diff --git a/src/compute/service/__main__.py b/src/compute/service/__main__.py index e80681e177f0f0def3dbe75d76e7e65ceaca1e87..71db89c6536100f0099ffd1a1d49354c6e9fd615 100644 --- a/src/compute/service/__main__.py +++ b/src/compute/service/__main__.py @@ -20,6 +20,7 @@ from common.Settings import ( wait_for_environment_variables) from .ComputeService import ComputeService from .rest_server.RestServer import RestServer +from .rest_server.nbi_plugins.debug_api import register_debug_api from .rest_server.nbi_plugins.ietf_l2vpn import register_ietf_l2vpn terminate = threading.Event() @@ -57,6 +58,7 @@ def main(): grpc_service.start() rest_server = RestServer() + register_debug_api(rest_server) register_ietf_l2vpn(rest_server) rest_server.start() diff --git a/src/compute/service/rest_server/nbi_plugins/debug_api/Resources.py b/src/compute/service/rest_server/nbi_plugins/debug_api/Resources.py new file mode 100644 index 0000000000000000000000000000000000000000..a701fd5630d595faaa603178ddf5ff625a66c540 --- /dev/null +++ b/src/compute/service/rest_server/nbi_plugins/debug_api/Resources.py @@ -0,0 +1,158 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 flask.json import jsonify +from flask_restful import Resource +from common.proto.context_pb2 import ConnectionId, ContextId, DeviceId, Empty, LinkId, ServiceId, SliceId, TopologyId +from common.proto.policy_pb2 import PolicyRuleId +from common.tools.grpc.Tools import grpc_message_to_json +from common.tools.object_factory.Connection import json_connection_id +from common.tools.object_factory.Context import json_context_id +from common.tools.object_factory.Device import json_device_id +from common.tools.object_factory.Link import json_link_id +from common.tools.object_factory.PolicyRule import json_policy_rule_id +from common.tools.object_factory.Service import json_service_id +from common.tools.object_factory.Slice import json_slice_id +from common.tools.object_factory.Topology import json_topology_id +from context.client.ContextClient import ContextClient + + +def format_grpc_to_json(grpc_reply): + return jsonify(grpc_message_to_json(grpc_reply)) + +def grpc_connection_id(connection_uuid): + return ConnectionId(**json_connection_id(connection_uuid)) + +def grpc_context_id(context_uuid): + return ContextId(**json_context_id(context_uuid)) + +def grpc_device_id(device_uuid): + return DeviceId(**json_device_id(device_uuid)) + +def grpc_link_id(link_uuid): + return LinkId(**json_link_id(link_uuid)) + +def grpc_service_id(context_uuid, service_uuid): + return ServiceId(**json_service_id(service_uuid, context_id=json_context_id(context_uuid))) + +def grpc_slice_id(context_uuid, slice_uuid): + return SliceId(**json_slice_id(slice_uuid, context_id=json_context_id(context_uuid))) + +def grpc_topology_id(context_uuid, topology_uuid): + return TopologyId(**json_topology_id(topology_uuid, context_id=json_context_id(context_uuid))) + +def grpc_policy_rule_id(policy_rule_uuid): + return PolicyRuleId(**json_policy_rule_id(policy_rule_uuid)) + + +class _Resource(Resource): + def __init__(self) -> None: + super().__init__() + self.client = ContextClient() + +class ContextIds(_Resource): + def get(self): + return format_grpc_to_json(self.client.ListContextIds(Empty())) + +class Contexts(_Resource): + def get(self): + return format_grpc_to_json(self.client.ListContexts(Empty())) + +class Context(_Resource): + def get(self, context_uuid : str): + return format_grpc_to_json(self.client.GetContext(grpc_context_id(context_uuid))) + +class TopologyIds(_Resource): + def get(self, context_uuid : str): + return format_grpc_to_json(self.client.ListTopologyIds(grpc_context_id(context_uuid))) + +class Topologies(_Resource): + def get(self, context_uuid : str): + return format_grpc_to_json(self.client.ListTopologies(grpc_context_id(context_uuid))) + +class Topology(_Resource): + def get(self, context_uuid : str, topology_uuid : str): + return format_grpc_to_json(self.client.GetTopology(grpc_topology_id(context_uuid, topology_uuid))) + +class ServiceIds(_Resource): + def get(self, context_uuid : str): + return format_grpc_to_json(self.client.ListServiceIds(grpc_context_id(context_uuid))) + +class Services(_Resource): + def get(self, context_uuid : str): + return format_grpc_to_json(self.client.ListServices(grpc_context_id(context_uuid))) + +class Service(_Resource): + def get(self, context_uuid : str, service_uuid : str): + return format_grpc_to_json(self.client.GetService(grpc_service_id(context_uuid, service_uuid))) + +class SliceIds(_Resource): + def get(self, context_uuid : str): + return format_grpc_to_json(self.client.ListSliceIds(grpc_context_id(context_uuid))) + +class Slices(_Resource): + def get(self, context_uuid : str): + return format_grpc_to_json(self.client.ListSlices(grpc_context_id(context_uuid))) + +class Slice(_Resource): + def get(self, context_uuid : str, slice_uuid : str): + return format_grpc_to_json(self.client.GetSlice(grpc_slice_id(context_uuid, slice_uuid))) + +class DeviceIds(_Resource): + def get(self): + return format_grpc_to_json(self.client.ListDeviceIds(Empty())) + +class Devices(_Resource): + def get(self): + return format_grpc_to_json(self.client.ListDevices(Empty())) + +class Device(_Resource): + def get(self, device_uuid : str): + return format_grpc_to_json(self.client.GetDevice(grpc_device_id(device_uuid))) + +class LinkIds(_Resource): + def get(self): + return format_grpc_to_json(self.client.ListLinkIds(Empty())) + +class Links(_Resource): + def get(self): + return format_grpc_to_json(self.client.ListLinks(Empty())) + +class Link(_Resource): + def get(self, link_uuid : str): + return format_grpc_to_json(self.client.GetLink(grpc_link_id(link_uuid))) + +class ConnectionIds(_Resource): + def get(self, context_uuid : str, service_uuid : str): + return format_grpc_to_json(self.client.ListConnectionIds(grpc_service_id(context_uuid, service_uuid))) + +class Connections(_Resource): + def get(self, context_uuid : str, service_uuid : str): + return format_grpc_to_json(self.client.ListConnections(grpc_service_id(context_uuid, service_uuid))) + +class Connection(_Resource): + def get(self, connection_uuid : str): + return format_grpc_to_json(self.client.GetConnection(grpc_connection_id(connection_uuid))) + +class PolicyRuleIds(_Resource): + def get(self): + return format_grpc_to_json(self.client.ListPolicyRuleIds(Empty())) + +class PolicyRules(_Resource): + def get(self): + return format_grpc_to_json(self.client.ListPolicyRules(Empty())) + +class PolicyRule(_Resource): + def get(self, policy_rule_uuid : str): + return format_grpc_to_json(self.client.GetPolicyRule(grpc_policy_rule_id(policy_rule_uuid))) diff --git a/src/compute/service/rest_server/nbi_plugins/debug_api/__init__.py b/src/compute/service/rest_server/nbi_plugins/debug_api/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..4fca3b5346dba53d28c2790b3b251f5ec10a24a0 --- /dev/null +++ b/src/compute/service/rest_server/nbi_plugins/debug_api/__init__.py @@ -0,0 +1,65 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +# RFC 8466 - L2VPN Service Model (L2SM) +# Ref: https://datatracker.ietf.org/doc/html/rfc8466 + +from compute.service.rest_server.RestServer import RestServer +from .Resources import ( + Connection, ConnectionIds, Connections, Context, ContextIds, Contexts, Device, DeviceIds, Devices, Link, LinkIds, + Links, PolicyRule, PolicyRuleIds, PolicyRules, Service, ServiceIds, Services, Slice, SliceIds, Slices, Topologies, + Topology, TopologyIds) + +URL_PREFIX = '/api' + +# Use 'path' type in Service and Sink because service_uuid and link_uuid might contain char '/' and Flask is unable to +# recognize them in 'string' type. +RESOURCES = [ + # (endpoint_name, resource_class, resource_url) + ('api.context_ids', ContextIds, '/context_ids'), + ('api.contexts', Contexts, '/contexts'), + ('api.context', Context, '/context/<string:context_uuid>'), + + ('api.topology_ids', TopologyIds, '/context/<string:context_uuid>/topology_ids'), + ('api.topologies', Topologies, '/context/<string:context_uuid>/topologies'), + ('api.topology', Topology, '/context/<string:context_uuid>/topology/<string:topology_uuid>'), + + ('api.service_ids', ServiceIds, '/context/<string:context_uuid>/service_ids'), + ('api.services', Services, '/context/<string:context_uuid>/services'), + ('api.service', Service, '/context/<string:context_uuid>/service/<path:service_uuid>'), + + ('api.slice_ids', SliceIds, '/context/<string:context_uuid>/slice_ids'), + ('api.slices', Slices, '/context/<string:context_uuid>/slices'), + ('api.slice', Slice, '/context/<string:context_uuid>/slice/<path:slice_uuid>'), + + ('api.device_ids', DeviceIds, '/device_ids'), + ('api.devices', Devices, '/devices'), + ('api.device', Device, '/device/<string:device_uuid>'), + + ('api.link_ids', LinkIds, '/link_ids'), + ('api.links', Links, '/links'), + ('api.link', Link, '/link/<path:link_uuid>'), + + ('api.connection_ids', ConnectionIds, '/context/<string:context_uuid>/service/<path:service_uuid>/connection_ids'), + ('api.connections', Connections, '/context/<string:context_uuid>/service/<path:service_uuid>/connections'), + ('api.connection', Connection, '/connection/<path:connection_uuid>'), + + ('api.policyrule_ids', PolicyRuleIds, '/policyrule_ids'), + ('api.policyrules', PolicyRules, '/policyrules'), + ('api.policyrule', PolicyRule, '/policyrule/<string:policyrule_uuid>'), +] + +def register_debug_api(rest_server : RestServer): + for endpoint_name, resource_class, resource_url in RESOURCES: + rest_server.add_resource(resource_class, URL_PREFIX + resource_url, endpoint=endpoint_name) diff --git a/src/compute/tests/MockService_Dependencies.py b/src/compute/tests/MockService_Dependencies.py index 5ed9d4da9fa23ff43bbd72eb021ae4e4fecd9b9b..fbc4bd1a4956926151909d535e18bb244cdea97e 100644 --- a/src/compute/tests/MockService_Dependencies.py +++ b/src/compute/tests/MockService_Dependencies.py @@ -28,7 +28,7 @@ LOCAL_HOST = '127.0.0.1' SERVICE_CONTEXT = ServiceNameEnum.CONTEXT SERVICE_SERVICE = ServiceNameEnum.SERVICE -SERVICE_SLICE = ServiceNameEnum.SLICE +SERVICE_SLICE = ServiceNameEnum.SLICE class MockService_Dependencies(GenericGrpcService): # Mock Service implementing Context, Service and Slice to simplify unitary tests of Compute @@ -54,5 +54,5 @@ class MockService_Dependencies(GenericGrpcService): os.environ[get_env_var_name(SERVICE_SERVICE, ENVVAR_SUFIX_SERVICE_HOST )] = str(self.bind_address) os.environ[get_env_var_name(SERVICE_SERVICE, ENVVAR_SUFIX_SERVICE_PORT_GRPC)] = str(self.bind_port) - os.environ[get_env_var_name(SERVICE_SLICE, ENVVAR_SUFIX_SERVICE_HOST )] = str(self.bind_address) - os.environ[get_env_var_name(SERVICE_SLICE, ENVVAR_SUFIX_SERVICE_PORT_GRPC)] = str(self.bind_port) + os.environ[get_env_var_name(SERVICE_SLICE, ENVVAR_SUFIX_SERVICE_HOST )] = str(self.bind_address) + os.environ[get_env_var_name(SERVICE_SLICE, ENVVAR_SUFIX_SERVICE_PORT_GRPC)] = str(self.bind_port) diff --git a/src/compute/tests/PrepareTestScenario.py b/src/compute/tests/PrepareTestScenario.py index 06fb34f9ee7508f4bd6fa769da78c50eb78c3bb8..7ef99f4b1817247b4645ce6ff25b260f51706f54 100644 --- a/src/compute/tests/PrepareTestScenario.py +++ b/src/compute/tests/PrepareTestScenario.py @@ -17,6 +17,7 @@ from common.Constants import ServiceNameEnum from common.Settings import ( ENVVAR_SUFIX_SERVICE_HOST, ENVVAR_SUFIX_SERVICE_PORT_HTTP, get_env_var_name, get_service_port_http) from compute.service.rest_server.RestServer import RestServer +from compute.service.rest_server.nbi_plugins.debug_api import register_debug_api from compute.service.rest_server.nbi_plugins.ietf_l2vpn import register_ietf_l2vpn from compute.tests.MockService_Dependencies import MockService_Dependencies from tests.tools.mock_osm.MockOSM import MockOSM @@ -39,6 +40,7 @@ def mock_service(): @pytest.fixture(scope='session') def compute_service_rest(mock_service): # pylint: disable=redefined-outer-name _rest_server = RestServer() + register_debug_api(_rest_server) register_ietf_l2vpn(_rest_server) _rest_server.start() time.sleep(1) # bring time for the server to start diff --git a/src/compute/tests/test_debug_api.py b/src/compute/tests/test_debug_api.py new file mode 100644 index 0000000000000000000000000000000000000000..31d2049654d0449bbba486ad080ac7dfb1651bd0 --- /dev/null +++ b/src/compute/tests/test_debug_api.py @@ -0,0 +1,228 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 logging, os, pytest, requests, time, urllib +from typing import Tuple +from common.Constants import DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID, ServiceNameEnum +from common.proto.context_pb2 import Connection, Context, Device, Link, Service, Slice, Topology +from common.proto.policy_pb2 import PolicyRuleIdList, PolicyRuleId, PolicyRuleList, PolicyRule +from common.Settings import ( + ENVVAR_SUFIX_SERVICE_HOST, ENVVAR_SUFIX_SERVICE_PORT_GRPC, ENVVAR_SUFIX_SERVICE_PORT_HTTP, get_env_var_name, + get_service_baseurl_http, get_service_port_grpc, get_service_port_http) +from common.type_checkers.Assertions import ( + validate_connection, validate_connection_ids, validate_connections, validate_context, validate_context_ids, + validate_contexts, validate_device, validate_device_ids, validate_devices, validate_link, validate_link_ids, + validate_links, validate_service, validate_service_ids, validate_services, validate_topologies, validate_topology, + validate_topology_ids) +from context.client.ContextClient import ContextClient +from .MockService_Dependencies import MockService_Dependencies +from .Objects import ( + CONNECTION_R1_R3, CONNECTION_R1_R3_ID, CONNECTION_R1_R3_UUID, CONTEXT, CONTEXT_ID, DEVICE_R1, DEVICE_R1_ID, + DEVICE_R1_UUID, DEVICE_R2, DEVICE_R2_ID, DEVICE_R2_UUID, DEVICE_R3, DEVICE_R3_ID, DEVICE_R3_UUID, LINK_R1_R2, + LINK_R1_R2_ID, LINK_R1_R2_UUID, SERVICE_R1_R2, SERVICE_R1_R2_ID, SERVICE_R1_R2_UUID, SERVICE_R1_R3, + SERVICE_R1_R3_ID, SERVICE_R1_R3_UUID, SERVICE_R2_R3, SERVICE_R2_R3_ID, SERVICE_R2_R3_UUID, SLICE_R1_R3, TOPOLOGY, + TOPOLOGY_ID, POLICY_RULE, POLICY_RULE_ID, POLICY_RULE_UUID) + + +@pytest.fixture(scope='session') +def mock_service(): + _service = MockService_Dependencies(MOCKSERVICE_PORT) + _service.configure_env_vars() + _service.start() + yield _service + _service.stop() + + + +LOGGER = logging.getLogger(__name__) +LOGGER.setLevel(logging.DEBUG) + +LOCAL_HOST = '127.0.0.1' +GRPC_PORT = 10000 + int(get_service_port_grpc(ServiceNameEnum.CONTEXT)) # avoid privileged ports +HTTP_PORT = 10000 + int(get_service_port_http(ServiceNameEnum.CONTEXT)) # avoid privileged ports + +MOCKSERVICE_PORT = 10000 +DEVICE_SERVICE_PORT = MOCKSERVICE_PORT + get_service_port_grpc(ServiceNameEnum.DEVICE) # avoid privileged ports + +os.environ[get_env_var_name(ServiceNameEnum.CONTEXT, ENVVAR_SUFIX_SERVICE_HOST )] = str(LOCAL_HOST) +os.environ[get_env_var_name(ServiceNameEnum.CONTEXT, ENVVAR_SUFIX_SERVICE_PORT_GRPC)] = str(GRPC_PORT) +os.environ[get_env_var_name(ServiceNameEnum.CONTEXT, ENVVAR_SUFIX_SERVICE_PORT_HTTP)] = str(HTTP_PORT) + +@pytest.fixture(scope='session') +def context_service_grpc(): + _service = ContextService(context_s_mb[0], context_s_mb[1]) + _service.start() + yield _service + _service.stop() + +@pytest.fixture(scope='session') +def context_service_rest(): + database = context_db_mb[0] + _rest_server = RestServer() + for endpoint_name, resource_class, resource_url in RESOURCES: + _rest_server.add_resource(resource_class, resource_url, endpoint=endpoint_name, resource_class_args=(database,)) + _rest_server.start() + time.sleep(1) # bring time for the server to start + yield _rest_server + _rest_server.shutdown() + _rest_server.join() + +@pytest.fixture(scope='session') +def context_client_grpc(context_service_grpc : ContextService): # pylint: disable=redefined-outer-name + _client = ContextClient() + yield _client + _client.close() + +def test_populate_database(): + client = ContextClient(host=LOCAL_HOST, port=GRPC_PORT) + client.SetContext(Context(**CONTEXT)) + client.SetTopology(Topology(**TOPOLOGY)) + client.SetDevice(Device(**DEVICE_R1)) + client.SetDevice(Device(**DEVICE_R2)) + client.SetDevice(Device(**DEVICE_R3)) + client.SetLink(Link(**LINK_R1_R2)) + client.SetLink(Link(**LINK_R1_R3)) + client.SetLink(Link(**LINK_R2_R3)) + client.SetService(Service(**SERVICE_R1_R2)) + client.SetService(Service(**SERVICE_R1_R3)) + client.SetService(Service(**SERVICE_R2_R3)) + client.SetSlice(Slice(**SLICE_R1_R3)) + client.SetConnection(Connection(**CONNECTION_R1_R3)) + +def do_rest_request(url : str): + base_url = get_service_baseurl_http(ServiceNameEnum.CONTEXT) + request_url = 'http://{:s}:{:s}{:s}{:s}'.format(str(LOCAL_HOST), str(HTTP_PORT), str(base_url), url) + LOGGER.warning('Request: GET {:s}'.format(str(request_url))) + reply = requests.get(request_url) + LOGGER.warning('Reply: {:s}'.format(str(reply.text))) + assert reply.status_code == 200, 'Reply failed with code {}'.format(reply.status_code) + return reply.json() + + +def test_rest_get_context_ids(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + reply = do_rest_request('/context_ids') + validate_context_ids(reply) + +def test_rest_get_contexts(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + reply = do_rest_request('/contexts') + validate_contexts(reply) + +def test_rest_get_context(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) + reply = do_rest_request('/context/{:s}'.format(context_uuid)) + validate_context(reply) + +def test_rest_get_topology_ids(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) + reply = do_rest_request('/context/{:s}/topology_ids'.format(context_uuid)) + validate_topology_ids(reply) + +def test_rest_get_topologies(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) + reply = do_rest_request('/context/{:s}/topologies'.format(context_uuid)) + validate_topologies(reply) + +def test_rest_get_topology(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) + topology_uuid = urllib.parse.quote(DEFAULT_TOPOLOGY_UUID) + reply = do_rest_request('/context/{:s}/topology/{:s}'.format(context_uuid, topology_uuid)) + validate_topology(reply, num_devices=3, num_links=3) + +def test_rest_get_service_ids(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) + reply = do_rest_request('/context/{:s}/service_ids'.format(context_uuid)) + validate_service_ids(reply) + +def test_rest_get_services(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) + reply = do_rest_request('/context/{:s}/services'.format(context_uuid)) + validate_services(reply) + +def test_rest_get_service(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) + service_uuid = urllib.parse.quote(SERVICE_R1_R2_UUID, safe='') + reply = do_rest_request('/context/{:s}/service/{:s}'.format(context_uuid, service_uuid)) + validate_service(reply) + +def test_rest_get_slice_ids(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) + reply = do_rest_request('/context/{:s}/slice_ids'.format(context_uuid)) + #validate_slice_ids(reply) + +def test_rest_get_slices(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) + reply = do_rest_request('/context/{:s}/slices'.format(context_uuid)) + #validate_slices(reply) + +def test_rest_get_slice(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) + slice_uuid = urllib.parse.quote(SLICE_R1_R3_UUID, safe='') + reply = do_rest_request('/context/{:s}/slice/{:s}'.format(context_uuid, slice_uuid)) + #validate_slice(reply) + +def test_rest_get_device_ids(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + reply = do_rest_request('/device_ids') + validate_device_ids(reply) + +def test_rest_get_devices(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + reply = do_rest_request('/devices') + validate_devices(reply) + +def test_rest_get_device(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + device_uuid = urllib.parse.quote(DEVICE_R1_UUID, safe='') + reply = do_rest_request('/device/{:s}'.format(device_uuid)) + validate_device(reply) + +def test_rest_get_link_ids(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + reply = do_rest_request('/link_ids') + validate_link_ids(reply) + +def test_rest_get_links(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + reply = do_rest_request('/links') + validate_links(reply) + +def test_rest_get_link(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + link_uuid = urllib.parse.quote(LINK_R1_R2_UUID, safe='') + reply = do_rest_request('/link/{:s}'.format(link_uuid)) + validate_link(reply) + +def test_rest_get_connection_ids(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) + service_uuid = urllib.parse.quote(SERVICE_R1_R3_UUID, safe='') + reply = do_rest_request('/context/{:s}/service/{:s}/connection_ids'.format(context_uuid, service_uuid)) + validate_connection_ids(reply) + +def test_rest_get_connections(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) + service_uuid = urllib.parse.quote(SERVICE_R1_R3_UUID, safe='') + reply = do_rest_request('/context/{:s}/service/{:s}/connections'.format(context_uuid, service_uuid)) + validate_connections(reply) + +def test_rest_get_connection(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + connection_uuid = urllib.parse.quote(CONNECTION_R1_R3_UUID, safe='') + reply = do_rest_request('/connection/{:s}'.format(connection_uuid)) + validate_connection(reply) + +def test_rest_get_policyrule_ids(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + reply = do_rest_request('/policyrule_ids') + #validate_policyrule_ids(reply) + +def test_rest_get_policyrules(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + reply = do_rest_request('/policyrules') + #validate_policyrules(reply) + +def test_rest_get_policyrule(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + policyrule_uuid = urllib.parse.quote(POLICYRULE_UUID, safe='') + reply = do_rest_request('/policyrule/{:s}'.format(policyrule_uuid)) + #validate_policyrule(reply)