diff --git a/rsd_lib/resources/v2_1/event_service/event_destination.py b/rsd_lib/resources/v2_1/event_service/event_destination.py new file mode 100644 index 0000000..562b18f --- /dev/null +++ b/rsd_lib/resources/v2_1/event_service/event_destination.py @@ -0,0 +1,102 @@ +# Copyright 2019 Intel, 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 jsonschema import validate +import logging + +from sushy.resources import base + +from rsd_lib import base as rsd_lib_base +from rsd_lib.resources.v2_1.event_service import schemas \ + as event_service_schemas + + +LOG = logging.getLogger(__name__) + + +class HttpHeaderPropertyCollectionField(base.ListField): + """HttpHeaderProperty field + + The value of the HTTP header is the property value. The header name is + the property name. + """ + + pattern = base.Field("Pattern") + + type = base.Field("Type") + + +class EventDestination(rsd_lib_base.ResourceBase): + """EventDestination resource class + + An Event Destination desribes the target of an event subscription, + including the types of events subscribed and context to provide to the + target in the Event payload. + """ + + destination = base.Field("Destination") + """The URI of the destination Event Service.""" + + event_types = base.Field("EventTypes") + """This property shall contain the types of events that shall be sent to + the desination. + """ + + context = base.Field("Context") + """A client-supplied string that is stored with the event destination + subscription. + """ + + protocol = base.Field("Protocol") + """The protocol type of the event connection.""" + + http_headers = HttpHeaderPropertyCollectionField("HttpHeaders") + """This is for setting HTTP headers, such as authorization information. + This object will be null on a GET. + """ + + message_ids = base.Field("MessageIds") + """A list of MessageIds that the service will only send.""" + + def delete(self): + """Delete this event subscription""" + self._conn.delete(self._path) + + +class EventDestinationCollection(rsd_lib_base.ResourceCollectionBase): + @property + def _resource_type(self): + return EventDestination + + def create_event_subscription(self, event_subscription_req): + """Create a new event subscription + + :param event_subscription_req: JSON for event subscription + :returns: The uri of the new event subscription + """ + target_uri = self._path + + validate( + event_subscription_req, + event_service_schemas.event_subscription_req_schema, + ) + + resp = self._conn.post(target_uri, data=event_subscription_req) + event_subscription_url = resp.headers["Location"] + LOG.info("event subscription created at %s", event_subscription_url) + + return event_subscription_url[ + event_subscription_url.find(self._path): + ] diff --git a/rsd_lib/resources/v2_1/event_service/event_service.py b/rsd_lib/resources/v2_1/event_service/event_service.py index 06b6a31..8dc3b74 100644 --- a/rsd_lib/resources/v2_1/event_service/event_service.py +++ b/rsd_lib/resources/v2_1/event_service/event_service.py @@ -16,72 +16,57 @@ from sushy.resources import base from sushy import utils -from rsd_lib import common as rsd_lib_common -from rsd_lib.resources.v2_1.event_service import event_subscription +from rsd_lib import base as rsd_lib_base +from rsd_lib.resources.v2_1.event_service import event_destination from rsd_lib import utils as rsd_lib_utils -class EventService(base.ResourceBase): - identity = base.Field("Id", required=True) - """The event service identity""" +class EventService(rsd_lib_base.ResourceBase): + """EventService resource class - name = base.Field("Name") - """The event service name""" - - description = base.Field("Description") - """The description of event service""" - - status = rsd_lib_common.StatusField('Status') - """The event service status""" + The Event Service resource contains properties for managing event + subcriptions and generates the events sent to subscribers. The + resource has links to the actual collection of subscriptions (called + Event Destinations). + """ service_enabled = base.Field("ServiceEnabled", adapter=bool) - """Whether the event service is enabled""" + """This indicates whether this service is enabled.""" - delivery_retry_attempts = base.Field("DeliveryRetryAttempts", - adapter=rsd_lib_utils.num_or_none) + delivery_retry_attempts = base.Field( + "DeliveryRetryAttempts", adapter=rsd_lib_utils.num_or_none + ) """This is the number of attempts an event posting is retried before the - subscription is terminated + subscription is terminated. """ delivery_retry_interval_seconds = base.Field( - "DeliveryRetryIntervalSeconds", adapter=rsd_lib_utils.num_or_none) + "DeliveryRetryIntervalSeconds", adapter=rsd_lib_utils.num_or_none + ) """This represents the number of seconds between retry attempts for sending any given Event """ event_types_for_subscription = base.Field("EventTypesForSubscription") - """These are the types of Events that can be subscribed to. Available - event types: - - StatusChange - The status of this resource has changed - - ResourceUpdated - The value of this resource has been updated - - ResourceAdded - A resource has been added - - ResourceRemoved - A resource has been removed - - Alert - A condition exists which requires attention + """This is the types of Events that can be subscribed to.""" + + status = rsd_lib_base.StatusField("Status") + """This indicates the known state of the resource, such as if it is + enabled. """ - def __init__(self, connector, identity, redfish_version=None): - """A class representing a EventService - - :param connector: A Connector instance - :param identity: The identity of the EventService resource - :param redfish_version: The version of RedFish. Used to construct - the object according to schema of the given version. - """ - super(EventService, self).__init__(connector, identity, - redfish_version) - - def _get_subscriptions_collection_path(self): - """Helper function to find the EventSubscriptionCollection path""" - return utils.get_sub_resource_path_by(self, 'Subscriptions') + # TODO(linyang): Add Action Field @property @utils.cache_it def subscriptions(self): - """Property to provide reference to `EventSubscriptionCollection` + """Property to provide reference to `EventDestinationCollection` instance - It is calculated once when it is queried for the first time. On - refresh, this property is reset. + It is calculated once when it is queried for the first time. On + refresh, this property is reset. """ - return event_subscription.EventSubscriptionCollection( - self._conn, self._get_subscriptions_collection_path(), - redfish_version=self.redfish_version) + return event_destination.EventDestinationCollection( + self._conn, + utils.get_sub_resource_path_by(self, "Subscriptions"), + redfish_version=self.redfish_version, + ) diff --git a/rsd_lib/resources/v2_1/event_service/event_subscription.py b/rsd_lib/resources/v2_1/event_service/event_subscription.py deleted file mode 100644 index 62c6e37..0000000 --- a/rsd_lib/resources/v2_1/event_service/event_subscription.py +++ /dev/null @@ -1,121 +0,0 @@ -# Copyright 2019 Intel, 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 jsonschema import validate -import logging - -from sushy.resources import base - -from rsd_lib.resources.v2_1.event_service import schemas as \ - event_service_schemas - - -LOG = logging.getLogger(__name__) - - -class OemField(base.CompositeField): - pass - - -class EventSubscription(base.ResourceBase): - identity = base.Field("Id") - """The Event Subscription id""" - - name = base.Field("Name") - """The Event Subscription name""" - - description = base.Field("Description") - """The Event Subscription description""" - - oem = OemField("Oem") - """The Event Subscription oem""" - - destination = base.Field("Destination") - """The URI of the destination Event Service""" - - event_types = base.Field("EventTypes") - """These are the types of Events that can be subscribed to. Available - event types: - - StatusChange - The status of this resource has changed - - ResourceUpdated - The value of this resource has been updated. - - ResourceAdded - A resource has been added - - ResourceRemoved - A resource has been removed - - Alert - A condition exists which requires attention - """ - - context = base.Field("Context") - """A client-supplied string that is stored with the event destination - subscription - """ - - protocol = base.Field("Protocol") - """The protocol type of the event connection. Available protocols: - - "Redfish" - event type shall adhere to that defined in the \ - Redfish specification. - """ - - http_headers = base.Field("HttpHeaders") - """This property shall contain an object consisting of the names and values - of of HTTP header to be included with every event POST to the Event - Destination - """ - - origin_resources = base.Field("OriginResources") - """A list of resources for which the service will send events specified in - EventTypes array. Empty array or NULL is interpreted as subscription for - all resources and assets in subsystem. - """ - - message_ids = base.Field("MessageIds") - """A list of MessageIds that the service will send""" - - def delete(self): - """Delete this event subscription""" - self._conn.delete(self._path) - - -class EventSubscriptionCollection(base.ResourceCollectionBase): - @property - def _resource_type(self): - return EventSubscription - - def __init__(self, connector, path, redfish_version=None): - """A class representing a Event Subscription Collection - - :param connector: A Connector instance - :param path: The canonical path to the Event Subscription collection - resource - :param redfish_version: The version of RedFish. Used to construct - the object according to schema of the given version. - """ - super(EventSubscriptionCollection, self).__init__(connector, path, - redfish_version) - - def create_event_subscription(self, event_subscription_req): - """Create a new event subscription - - :param event_subscription_req: JSON for event subscription - :returns: The uri of the new event subscription - """ - target_uri = self._path - - validate(event_subscription_req, - event_service_schemas.event_subscription_req_schema) - - resp = self._conn.post(target_uri, data=event_subscription_req) - event_subscription_url = resp.headers['Location'] - LOG.info("event subscription created at %s", event_subscription_url) - - return event_subscription_url[event_subscription_url.find(self._path):] diff --git a/rsd_lib/resources/v2_1/event_service/schemas.py b/rsd_lib/resources/v2_1/event_service/schemas.py index f431ec1..3c6538b 100644 --- a/rsd_lib/resources/v2_1/event_service/schemas.py +++ b/rsd_lib/resources/v2_1/event_service/schemas.py @@ -14,32 +14,32 @@ # under the License. event_subscription_req_schema = { - 'type': 'object', - 'properties': { - 'Name': {'type': 'string'}, - 'Destination': {'type': 'string'}, - 'EventTypes': { - 'type': 'array', - 'items': { - 'type': 'string', - 'enum': ['ResourceAdded', 'ResourceRemoved', 'StatusChange', - 'ResourceUpdated', 'Alert'] - } + "type": "object", + "properties": { + "Name": {"type": "string"}, + "Destination": {"type": "string"}, + "EventTypes": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "ResourceAdded", + "ResourceRemoved", + "StatusChange", + "ResourceUpdated", + "Alert", + ], + }, }, - 'Context': {'type': 'string'}, - 'Protocol': { - 'type': 'string', - 'enum': ['Redfish'] + "Context": {"type": "string"}, + "Protocol": {"type": "string", "enum": ["Redfish"]}, + "OriginResources": { + "type": "array", + "items": { + "type": "object", + "properties": {"@odata.id": {"type": "string"}}, + }, }, - 'OriginResources': { - 'type': 'array', - 'items': { - 'type': 'object', - 'properties': { - '@odata.id': {'type': 'string'} - }, - } - } }, - 'additionalProperties': False + "additionalProperties": False, } diff --git a/rsd_lib/tests/unit/json_samples/v2_1/event_subscription.json b/rsd_lib/tests/unit/json_samples/v2_1/event_destination.json similarity index 100% rename from rsd_lib/tests/unit/json_samples/v2_1/event_subscription.json rename to rsd_lib/tests/unit/json_samples/v2_1/event_destination.json diff --git a/rsd_lib/tests/unit/json_samples/v2_1/event_subscription_collection.json b/rsd_lib/tests/unit/json_samples/v2_1/event_destination_collection.json similarity index 100% rename from rsd_lib/tests/unit/json_samples/v2_1/event_subscription_collection.json rename to rsd_lib/tests/unit/json_samples/v2_1/event_destination_collection.json diff --git a/rsd_lib/tests/unit/resources/v2_1/event_service/test_event_destination.py b/rsd_lib/tests/unit/resources/v2_1/event_service/test_event_destination.py new file mode 100644 index 0000000..2879f6d --- /dev/null +++ b/rsd_lib/tests/unit/resources/v2_1/event_service/test_event_destination.py @@ -0,0 +1,168 @@ +# Copyright 2019 Intel, 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 json +import jsonschema +import mock +import testtools + +from rsd_lib.resources.v2_1.event_service import event_destination +from rsd_lib.tests.unit.fakes import request_fakes + + +class EventSubscriptionTestCase(testtools.TestCase): + def setUp(self): + super(EventSubscriptionTestCase, self).setUp() + self.conn = mock.Mock() + with open( + "rsd_lib/tests/unit/json_samples/v2_1/event_destination.json", "r" + ) as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + + self.event_destination_inst = event_destination.EventDestination( + self.conn, + "/redfish/v1/EventService/Subscriptions/1", + redfish_version="1.0.2", + ) + + def test__parse_attributes(self): + self.event_destination_inst._parse_attributes() + self.assertEqual("1", self.event_destination_inst.identity) + self.assertEqual( + "EventSubscription 1", self.event_destination_inst.name + ) + self.assertEqual( + "EventSubscription", self.event_destination_inst.description + ) + self.assertEqual( + "http://192.168.1.1/Destination1", + self.event_destination_inst.destination, + ) + self.assertEqual( + ["ResourceAdded", "ResourceRemoved"], + self.event_destination_inst.event_types, + ) + self.assertEqual("My Event", self.event_destination_inst.context) + self.assertEqual("Redfish", self.event_destination_inst.protocol) + + def test_delete(self): + self.event_destination_inst.delete() + self.event_destination_inst._conn.delete.assert_called_once_with( + self.event_destination_inst.path + ) + + +class EventSubscriptionCollectionTestCase(testtools.TestCase): + def setUp(self): + super(EventSubscriptionCollectionTestCase, self).setUp() + self.conn = mock.Mock() + with open( + "rsd_lib/tests/unit/json_samples/v2_1/" + "event_destination_collection.json", + "r", + ) as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + + self.conn.post.return_value = request_fakes.fake_request_post( + None, + headers={ + "Location": "https://localhost:8443/redfish/v1/" + "EventService/Subscriptions/2" + }, + ) + + self.event_subscription_col = event_destination.\ + EventDestinationCollection( + self.conn, + "/redfish/v1/EventService/" "Subscriptions", + redfish_version="1.0.2", + ) + + def test__parse_attributes(self): + self.event_subscription_col._parse_attributes() + self.assertEqual("1.0.2", self.event_subscription_col.redfish_version) + self.assertEqual( + "Event Subscriptions Collection", self.event_subscription_col.name + ) + self.assertEqual( + ("/redfish/v1/EventService/Subscriptions/1",), + self.event_subscription_col.members_identities, + ) + + @mock.patch.object(event_destination, "EventDestination", autospec=True) + def test_get_member(self, mock_event_subscription): + self.event_subscription_col.get_member( + "/redfish/v1/EventService/Subscriptions/1" + ) + mock_event_subscription.assert_called_once_with( + self.event_subscription_col._conn, + "/redfish/v1/EventService/Subscriptions/1", + redfish_version=self.event_subscription_col.redfish_version, + ) + + @mock.patch.object(event_destination, "EventDestination", autospec=True) + def test_get_members(self, mock_event_subscription): + members = self.event_subscription_col.get_members() + mock_event_subscription.assert_called_once_with( + self.event_subscription_col._conn, + "/redfish/v1/EventService/Subscriptions/1", + redfish_version=self.event_subscription_col.redfish_version, + ) + self.assertIsInstance(members, list) + self.assertEqual(1, len(members)) + + def test_create_subscription_reqs(self): + reqs = { + "Name": "EventSubscription 1", + "Destination": "EventSubscription", + "EventTypes": ["ResourceAdded", "ResourceRemoved"], + "Context": "My Event", + "Protocol": "Redfish", + "OriginResources": [{"@odata.id": "/redfish/v1/Systems/1"}], + } + + result = self.event_subscription_col.create_event_subscription(reqs) + self.event_subscription_col._conn.post.assert_called_once_with( + "/redfish/v1/EventService/Subscriptions", data=reqs + ) + self.assertEqual(result, "/redfish/v1/EventService/Subscriptions/2") + + def test_create_subscription_invalid_reqs(self): + reqs = { + "Name": "EventSubscription 1", + "Destination": "EventSubscription", + "EventTypes": ["ResourceAdded", "ResourceRemoved"], + "Context": "My Event", + "Protocol": "Redfish", + "OriginResources": [{"@odata.id": "/redfish/v1/Systems/1"}], + } + + # Wrong format + event_subscription_req = reqs.copy() + event_subscription_req.update({"Context": True}) + self.assertRaises( + jsonschema.exceptions.ValidationError, + self.event_subscription_col.create_event_subscription, + event_subscription_req, + ) + + # Wrong additional fields + event_subscription_req = reqs.copy() + event_subscription_req["Additional"] = "AdditionalField" + self.assertRaises( + jsonschema.exceptions.ValidationError, + self.event_subscription_col.create_event_subscription, + event_subscription_req, + ) diff --git a/rsd_lib/tests/unit/resources/v2_1/event_service/test_event_service.py b/rsd_lib/tests/unit/resources/v2_1/event_service/test_event_service.py index 1ccbb8f..6624654 100644 --- a/rsd_lib/tests/unit/resources/v2_1/event_service/test_event_service.py +++ b/rsd_lib/tests/unit/resources/v2_1/event_service/test_event_service.py @@ -17,94 +17,105 @@ import json import mock import testtools -from sushy import exceptions - +from rsd_lib.resources.v2_1.event_service import event_destination from rsd_lib.resources.v2_1.event_service import event_service -from rsd_lib.resources.v2_1.event_service import event_subscription class EventServiceTestCase(testtools.TestCase): def setUp(self): super(EventServiceTestCase, self).setUp() self.conn = mock.Mock() - with open('rsd_lib/tests/unit/json_samples/v2_1/event_service.json', - 'r') as f: + with open( + "rsd_lib/tests/unit/json_samples/v2_1/event_service.json", "r" + ) as f: self.conn.get.return_value.json.return_value = json.loads(f.read()) self.event_service_inst = event_service.EventService( - self.conn, '/redfish/v1/EventService', - redfish_version='1.0.2') + self.conn, "/redfish/v1/EventService", redfish_version="1.0.2" + ) def test__parse_attributes(self): self.event_service_inst._parse_attributes() - self.assertEqual('EventService', self.event_service_inst.identity) - self.assertEqual('Event Service', self.event_service_inst.name) - self.assertEqual('Event Service', self.event_service_inst.description) - self.assertEqual('Enabled', self.event_service_inst.status.state) - self.assertEqual('OK', self.event_service_inst.status.health) + self.assertEqual("EventService", self.event_service_inst.identity) + self.assertEqual("Event Service", self.event_service_inst.name) + self.assertEqual("Event Service", self.event_service_inst.description) + self.assertEqual("Enabled", self.event_service_inst.status.state) + self.assertEqual("OK", self.event_service_inst.status.health) self.assertEqual(None, self.event_service_inst.status.health_rollup) self.assertEqual(True, self.event_service_inst.service_enabled) self.assertEqual(3, self.event_service_inst.delivery_retry_attempts) self.assertEqual( - 60, self.event_service_inst.delivery_retry_interval_seconds) + 60, self.event_service_inst.delivery_retry_interval_seconds + ) self.assertEqual( - ["StatusChange", "ResourceUpdated", "ResourceAdded", - "ResourceRemoved", "Alert"], - self.event_service_inst.event_types_for_subscription) - - def test__get_subscriptions_collection_path(self): - expected = '/redfish/v1/EventService/Subscriptions' - result = self.event_service_inst._get_subscriptions_collection_path() - self.assertEqual(expected, result) - - def test__get_subscriptions_collection_path_missing_attr(self): - self.event_service_inst._json.pop('Subscriptions') - with self.assertRaisesRegex( - exceptions.MissingAttributeError, 'attribute Subscriptions'): - self.event_service_inst._get_subscriptions_collection_path() + [ + "StatusChange", + "ResourceUpdated", + "ResourceAdded", + "ResourceRemoved", + "Alert", + ], + self.event_service_inst.event_types_for_subscription, + ) def test_subscriptions(self): # | GIVEN | self.conn.get.return_value.json.reset_mock() - with open('rsd_lib/tests/unit/json_samples/v2_1/' - 'event_subscription_collection.json', 'r') as f: + with open( + "rsd_lib/tests/unit/json_samples/v2_1/" + "event_destination_collection.json", + "r", + ) as f: self.conn.get.return_value.json.return_value = json.loads(f.read()) # | WHEN | actual_subscriptions = self.event_service_inst.subscriptions # | THEN | - self.assertIsInstance(actual_subscriptions, - event_subscription.EventSubscriptionCollection) + self.assertIsInstance( + actual_subscriptions, event_destination.EventDestinationCollection + ) self.conn.get.return_value.json.assert_called_once_with() # reset mock self.conn.get.return_value.json.reset_mock() # | WHEN & THEN | # tests for same object on invoking subsequently - self.assertIs(actual_subscriptions, - self.event_service_inst.subscriptions) + self.assertIs( + actual_subscriptions, self.event_service_inst.subscriptions + ) self.conn.get.return_value.json.assert_not_called() def test_event_subscriptions_on_refresh(self): # | GIVEN | - with open('rsd_lib/tests/unit/json_samples/v2_1/' - 'event_subscription_collection.json', 'r') as f: + with open( + "rsd_lib/tests/unit/json_samples/v2_1/" + "event_destination_collection.json", + "r", + ) as f: self.conn.get.return_value.json.return_value = json.loads(f.read()) # | WHEN & THEN | - self.assertIsInstance(self.event_service_inst.subscriptions, - event_subscription.EventSubscriptionCollection) + self.assertIsInstance( + self.event_service_inst.subscriptions, + event_destination.EventDestinationCollection, + ) # On refreshing the event_service instance... - with open('rsd_lib/tests/unit/json_samples/v2_1/' - 'event_service.json', 'r') as f: + with open( + "rsd_lib/tests/unit/json_samples/v2_1/" "event_service.json", "r" + ) as f: self.conn.get.return_value.json.return_value = json.loads(f.read()) self.event_service_inst.invalidate() self.event_service_inst.refresh(force=False) # | GIVEN | - with open('rsd_lib/tests/unit/json_samples/v2_1/' - 'event_subscription_collection.json', 'r') as f: + with open( + "rsd_lib/tests/unit/json_samples/v2_1/" + "event_destination_collection.json", + "r", + ) as f: self.conn.get.return_value.json.return_value = json.loads(f.read()) # | WHEN & THEN | - self.assertIsInstance(self.event_service_inst.subscriptions, - event_subscription.EventSubscriptionCollection) + self.assertIsInstance( + self.event_service_inst.subscriptions, + event_destination.EventDestinationCollection, + ) diff --git a/rsd_lib/tests/unit/resources/v2_1/event_service/test_event_subscription.py b/rsd_lib/tests/unit/resources/v2_1/event_service/test_event_subscription.py deleted file mode 100644 index 55b3be3..0000000 --- a/rsd_lib/tests/unit/resources/v2_1/event_service/test_event_subscription.py +++ /dev/null @@ -1,155 +0,0 @@ -# Copyright 2019 Intel, 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 json -import jsonschema -import mock -import testtools - -from rsd_lib.resources.v2_1.event_service import event_subscription -from rsd_lib.tests.unit.fakes import request_fakes - - -class EventSubscriptionTestCase(testtools.TestCase): - def setUp(self): - super(EventSubscriptionTestCase, self).setUp() - self.conn = mock.Mock() - with open( - 'rsd_lib/tests/unit/json_samples/v2_1/event_subscription.json', - 'r') as f: - self.conn.get.return_value.json.return_value = json.loads(f.read()) - - self.event_subscription_inst = event_subscription.EventSubscription( - self.conn, '/redfish/v1/EventService/Subscriptions/1', - redfish_version='1.0.2') - - def test__parse_attributes(self): - self.event_subscription_inst._parse_attributes() - self.assertEqual('1', self.event_subscription_inst.identity) - self.assertEqual('EventSubscription 1', - self.event_subscription_inst.name) - self.assertEqual('EventSubscription', - self.event_subscription_inst.description) - self.assertEqual('http://192.168.1.1/Destination1', - self.event_subscription_inst.destination) - self.assertEqual(['ResourceAdded', 'ResourceRemoved'], - self.event_subscription_inst.event_types) - self.assertEqual("My Event", self.event_subscription_inst.context) - self.assertEqual("Redfish", self.event_subscription_inst.protocol) - - def test_delete(self): - self.event_subscription_inst.delete() - self.event_subscription_inst._conn.delete.assert_called_once_with( - self.event_subscription_inst.path) - - -class EventSubscriptionCollectionTestCase(testtools.TestCase): - def setUp(self): - super(EventSubscriptionCollectionTestCase, self).setUp() - self.conn = mock.Mock() - with open( - 'rsd_lib/tests/unit/json_samples/v2_1/' - 'event_subscription_collection.json', 'r') as f: - self.conn.get.return_value.json.return_value = json.loads(f.read()) - - self.conn.post.return_value = request_fakes.fake_request_post( - None, headers={"Location": "https://localhost:8443/redfish/v1/" - "EventService/Subscriptions/2"}) - - self.event_subscription_col = event_subscription. \ - EventSubscriptionCollection(self.conn, - '/redfish/v1/EventService/' - 'Subscriptions', - redfish_version='1.0.2') - - def test__parse_attributes(self): - self.event_subscription_col._parse_attributes() - self.assertEqual('1.0.2', self.event_subscription_col.redfish_version) - self.assertEqual('Event Subscriptions Collection', - self.event_subscription_col.name) - self.assertEqual(('/redfish/v1/EventService/Subscriptions/1',), - self.event_subscription_col.members_identities) - - @mock.patch.object(event_subscription, 'EventSubscription', autospec=True) - def test_get_member(self, mock_event_subscription): - self.event_subscription_col.get_member( - '/redfish/v1/EventService/Subscriptions/1') - mock_event_subscription.assert_called_once_with( - self.event_subscription_col._conn, - '/redfish/v1/EventService/Subscriptions/1', - redfish_version=self.event_subscription_col.redfish_version) - - @mock.patch.object(event_subscription, 'EventSubscription', autospec=True) - def test_get_members(self, mock_event_subscription): - members = self.event_subscription_col.get_members() - mock_event_subscription.assert_called_once_with( - self.event_subscription_col._conn, - '/redfish/v1/EventService/Subscriptions/1', - redfish_version=self.event_subscription_col.redfish_version) - self.assertIsInstance(members, list) - self.assertEqual(1, len(members)) - - def test_create_subscription_reqs(self): - reqs = { - 'Name': 'EventSubscription 1', - 'Destination': 'EventSubscription', - 'EventTypes': [ - 'ResourceAdded', - 'ResourceRemoved' - ], - 'Context': 'My Event', - 'Protocol': 'Redfish', - 'OriginResources': [{ - '@odata.id': '/redfish/v1/Systems/1' - }] - } - - result = self.event_subscription_col.create_event_subscription(reqs) - self.event_subscription_col._conn.post.assert_called_once_with( - '/redfish/v1/EventService/Subscriptions', - data=reqs) - self.assertEqual(result, - '/redfish/v1/EventService/Subscriptions/2') - - def test_create_subscription_invalid_reqs(self): - reqs = { - 'Name': 'EventSubscription 1', - 'Destination': 'EventSubscription', - 'EventTypes': [ - 'ResourceAdded', - 'ResourceRemoved' - ], - 'Context': 'My Event', - 'Protocol': 'Redfish', - 'OriginResources': [{ - '@odata.id': '/redfish/v1/Systems/1' - }] - } - - # Wrong format - event_subscription_req = reqs.copy() - event_subscription_req.update({'Context': True}) - self.assertRaises( - jsonschema.exceptions.ValidationError, - self.event_subscription_col.create_event_subscription, - event_subscription_req) - - # Wrong additional fields - event_subscription_req = reqs.copy() - event_subscription_req['Additional'] = 'AdditionalField' - self.assertRaises( - jsonschema.exceptions.ValidationError, - self.event_subscription_col.create_event_subscription, - event_subscription_req)