diff --git a/manifests/.gitlab-ci.yml b/manifests/.gitlab-ci.yml index 0c0b1d4dc0783e1bf3158d0629558b4fedd355d6..9fc872ca61752d1be012aa4b866b3b592171e2cd 100644 --- a/manifests/.gitlab-ci.yml +++ b/manifests/.gitlab-ci.yml @@ -6,5 +6,4 @@ dependencies all: - kubectl version - kubectl get all - kubectl apply -f "manifests/prometheus.yaml" - - kubectl apply -f "manifests/redis.yaml" - kubectl get all diff --git a/manifests/contextservice.yaml b/manifests/contextservice.yaml index cf7da7e43fbfc02f5872745fc6f10f3cfcee6cb2..97bef301c27da42eedad02377b0ebecc73701b12 100644 --- a/manifests/contextservice.yaml +++ b/manifests/contextservice.yaml @@ -6,6 +6,7 @@ spec: selector: matchLabels: app: contextservice + replicas: 1 template: metadata: labels: @@ -13,6 +14,17 @@ spec: spec: terminationGracePeriodSeconds: 5 containers: + - name: redis + image: redis:6.2 + ports: + - containerPort: 6379 + resources: + requests: + cpu: 250m + memory: 512Mi + limits: + cpu: 700m + memory: 1024Mi - name: server image: registry.gitlab.com/teraflow-h2020/controller/context:latest imagePullPolicy: Always @@ -26,6 +38,8 @@ spec: value: "0" - name: LOG_LEVEL value: "DEBUG" + - name: POPULATE_FAKE_DATA + value: "true" readinessProbe: exec: command: ["/bin/grpc_health_probe", "-addr=:1010"] diff --git a/manifests/redis.yaml b/manifests/redis.yaml deleted file mode 100644 index 9aaebb1673637e6afc4fcf2d5887009f5d365a4d..0000000000000000000000000000000000000000 --- a/manifests/redis.yaml +++ /dev/null @@ -1,54 +0,0 @@ ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: redis -spec: - selector: - matchLabels: - app: redis - replicas: 1 - template: - metadata: - labels: - app: redis - version: v1 - spec: - containers: - - name: redis - image: redis:6.2 - ports: - - containerPort: 6379 ---- -apiVersion: v1 -kind: Service -metadata: - name: redis - labels: - app: redis -spec: - type: ClusterIP - selector: - app: redis - ports: - - name: redis - protocol: TCP - port: 6379 - targetPort: 6379 ---- -apiVersion: v1 -kind: Service -metadata: - name: redis-public - labels: - app: redis -spec: - type: NodePort - selector: - app: redis - ports: - - name: redis - protocol: TCP - port: 6379 - targetPort: 6379 ---- diff --git a/run_local_tests.sh b/run_local_tests.sh index 8302ea2ebdd3ead89c404c9c87f5d3d9e754a077..0987fd66854199905b1035df67ea9b4648845c3e 100755 --- a/run_local_tests.sh +++ b/run_local_tests.sh @@ -22,10 +22,8 @@ coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \ #coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \ # centralizedcybersecurity/tests/test_unitary.py -coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose --maxfail=1\ - context/tests/test_unitary_fast_hasher.py \ - context/tests/test_unitary_grpc.py \ - #context/tests/test_unitary_rest.py +coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \ + context/tests/test_unitary.py #coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \ # device/tests/test_unitary_driverapi.py \ diff --git a/src/common/type_checkers/Assertions.py b/src/common/type_checkers/Assertions.py index c7b87a671f88ab65768525c73dcf2b34361b579f..f11a059eb5463751a920b116e86438b7e1b13484 100644 --- a/src/common/type_checkers/Assertions.py +++ b/src/common/type_checkers/Assertions.py @@ -1,183 +1,294 @@ -def validate_empty(message): - assert type(message) is dict - assert len(message.keys()) == 0 +# ----- Enumerations --------------------------------------------------------------------------------------------------- +def validate_config_action_enum(message): + assert isinstance(message, str) + assert message in [ + 'CONFIGACTION_UNDEFINED', + 'CONFIGACTION_SET', + 'CONFIGACTION_DELETE', + ] + +def validate_device_driver_enum(message): + assert isinstance(message, str) + assert message in [ + 'DEVICEDRIVER_UNDEFINED', + 'DEVICEDRIVER_OPENCONFIG', + 'DEVICEDRIVER_TRANSPORT_API', + 'DEVICEDRIVER_P4', + 'DEVICEDRIVER_IETF_NETWORK_TOPOLOGY', + 'DEVICEDRIVER_ONF_TR_352', + ] + +def validate_device_operational_status_enum(message): + assert isinstance(message, str) + assert message in [ + 'DEVICEOPERATIONALSTATUS_UNDEFINED', + 'DEVICEOPERATIONALSTATUS_DISABLED', + 'DEVICEOPERATIONALSTATUS_ENABLED' + ] + +def validate_service_type_enum(message): + assert isinstance(message, str) + assert message in [ + 'SERVICETYPE_UNKNOWN', + 'SERVICETYPE_L3NM', + 'SERVICETYPE_L2NM', + 'SERVICETYPE_TAPI_CONNECTIVITY_SERVICE', + ] + +def validate_service_state_enum(message): + assert isinstance(message, str) + assert message in [ + 'SERVICESTATUS_UNDEFINED', + 'SERVICESTATUS_PLANNED', + 'SERVICESTATUS_ACTIVE', + 'SERVICESTATUS_PENDING_REMOVAL', + ] + + +# ----- Common --------------------------------------------------------------------------------------------------------- def validate_uuid(message, allow_empty=False): - assert type(message) is dict + assert isinstance(message, dict) assert len(message.keys()) == 1 assert 'uuid' in message - assert type(message['uuid']) is str + assert isinstance(message['uuid'], str) if allow_empty: return assert len(message['uuid']) > 1 +def validate_config_rule(message): + assert isinstance(message, dict) + assert len(message.keys()) == 3 + assert 'action' in message + validate_config_action_enum(message['action']) + assert 'resource_key' in message + assert isinstance(message['resource_key'], str) + assert 'resource_value' in message + assert isinstance(message['resource_value'], str) + +def validate_config_rules(message): + assert isinstance(message, dict) + assert len(message.keys()) == 1 + assert 'config_rules' in message + for config_rule in message['config_rules']: validate_config_rule(config_rule) + +def validate_constraint(message): + assert isinstance(message, dict) + assert len(message.keys()) == 2 + assert 'constraint_type' in message + assert isinstance(message['constraint_type'], str) + assert 'constraint_value' in message + assert isinstance(message['constraint_value'], str) + + +# ----- Identifiers ---------------------------------------------------------------------------------------------------- + def validate_context_id(message): - assert type(message) is dict + assert isinstance(message, dict) assert len(message.keys()) == 1 - assert 'contextUuid' in message - validate_uuid(message['contextUuid']) + assert 'context_uuid' in message + validate_uuid(message['context_uuid']) + +def validate_service_id(message, context_uuid=None): + assert isinstance(message, dict) + assert len(message.keys()) == 2 + assert 'context_id' in message + validate_context_id(message['context_id']) + if context_uuid is not None: assert message['context_id']['context_uuid']['uuid'] == context_uuid + assert 'service_uuid' in message + validate_uuid(message['service_uuid']) + +def validate_topology_id(message, context_uuid=None): + assert isinstance(message, dict) + assert len(message.keys()) == 2 + assert 'context_id' in message + validate_context_id(message['context_id']) + if context_uuid is not None: assert message['context_id']['context_uuid']['uuid'] == context_uuid + assert 'topology_uuid' in message + validate_uuid(message['topology_uuid']) def validate_device_id(message): - assert type(message) is dict + assert isinstance(message, dict) assert len(message.keys()) == 1 - assert 'device_id' in message - validate_uuid(message['device_id']) + assert 'device_uuid' in message + validate_uuid(message['device_uuid']) def validate_link_id(message): - assert type(message) is dict + assert isinstance(message, dict) assert len(message.keys()) == 1 - assert 'link_id' in message - validate_uuid(message['link_id']) + assert 'link_uuid' in message + validate_uuid(message['link_uuid']) + +def validate_endpoint_id(message): + assert isinstance(message, dict) + assert len(message.keys()) == 3 + assert 'topology_id' in message + validate_topology_id(message['topology_id']) + assert 'device_id' in message + validate_device_id(message['device_id']) + assert 'endpoint_uuid' in message + validate_uuid(message['endpoint_uuid']) -def validate_topology_id(message): - assert type(message) is dict - assert len(message.keys()) == 2 - assert 'contextId' in message - validate_context_id(message['contextId']) - assert 'topoId' in message - validate_uuid(message['topoId']) -def validate_device_config(message): - assert type(message) is dict +# ----- Lists of Identifiers ------------------------------------------------------------------------------------------- + +def validate_context_ids(message): + assert isinstance(message, dict) assert len(message.keys()) == 1 - assert 'device_config' in message - assert type(message['device_config']) is str + assert 'context_ids' in message + assert isinstance(message['context_ids'], list) + for context_id in message['context_ids']: validate_context_id(context_id) -def validate_device_operational_status(message): - assert type(message) is str - assert message in ['KEEP_STATE', 'ENABLED', 'DISABLED'] +def validate_service_ids(message, context_uuid=None): + assert isinstance(message, dict) + assert len(message.keys()) == 1 + assert 'service_ids' in message + assert isinstance(message['service_ids'], list) + for service_id in message['service_ids']: validate_service_id(service_id, context_uuid=context_uuid) -def validate_endpoint_id(message): - assert type(message) is dict +def validate_topology_ids(message, context_uuid=None): + assert isinstance(message, dict) + assert len(message.keys()) == 1 + assert 'topology_ids' in message + assert isinstance(message['topology_ids'], list) + for topology_id in message['topology_ids']: validate_topology_id(topology_id, context_uuid=context_uuid) + +def validate_device_ids(message): + assert isinstance(message, dict) + assert len(message.keys()) == 1 + assert 'device_ids' in message + assert isinstance(message['device_ids'], list) + for device_id in message['device_ids']: validate_device_id(device_id) + +def validate_link_ids(message): + assert isinstance(message, dict) + assert len(message.keys()) == 1 + assert 'link_ids' in message + assert isinstance(message['link_ids'], list) + for link_id in message['link_ids']: validate_link_id(link_id) + + +# ----- Objects -------------------------------------------------------------------------------------------------------- + +def validate_context(message): + assert isinstance(message, dict) assert len(message.keys()) == 3 - assert 'topoId' in message - validate_topology_id(message['topoId']) - assert 'dev_id' in message - validate_device_id(message['dev_id']) - assert 'port_id' in message - validate_uuid(message['port_id']) + assert 'context_id' in message + validate_context_id(message['context_id']) + context_uuid = message['context_id']['context_uuid']['uuid'] + assert 'service_ids' in message + assert isinstance(message['service_ids'], list) + for service_id in message['service_ids']: validate_service_id(service_id, context_uuid=context_uuid) + assert 'topology_ids' in message + assert isinstance(message['topology_ids'], list) + for topology_id in message['topology_ids']: validate_topology_id(topology_id, context_uuid=context_uuid) + +def validate_service_state(message): + assert isinstance(message, dict) + assert len(message.keys()) == 1 + assert 'service_status' in message + validate_service_state_enum(message['service_status']) + +def validate_service(message): + assert isinstance(message, dict) + assert len(message.keys()) == 6 + assert 'service_id' in message + validate_service_id(message['service_id']) + assert 'service_type' in message + validate_service_type_enum(message['service_type']) + assert 'service_endpoint_ids' in message + assert isinstance(message['service_endpoint_ids'], list) + for endpoint_id in message['service_endpoint_ids']: validate_endpoint_id(endpoint_id) + assert 'service_constraints' in message + assert isinstance(message['service_constraints'], list) + for constraint in message['service_constraints']: validate_constraint(constraint) + assert 'service_status' in message + validate_service_state(message['service_status']) + assert 'service_config' in message + validate_config_rules(message['service_config']) + +def validate_topology(message, num_devices=None, num_links=None): + assert isinstance(message, dict) + assert len(message.keys()) == 3 + assert 'topology_id' in message + validate_topology_id(message['topology_id']) + assert 'device_ids' in message + assert isinstance(message['device_ids'], list) + if num_devices is not None: assert len(message['device_ids']) == num_devices + for device_id in message['device_ids']: validate_device_id(device_id) + assert 'link_ids' in message + assert isinstance(message['link_ids'], list) + if num_links is not None: assert len(message['link_ids']) == num_links + for link_id in message['link_ids']: validate_link_id(link_id) def validate_endpoint(message): - assert type(message) is dict + assert isinstance(message, dict) assert len(message.keys()) == 2 - assert 'port_id' in message - validate_endpoint_id(message['port_id']) - assert 'port_type' in message - assert type(message['port_type']) is str + assert 'endpoint_id' in message + validate_endpoint_id(message['endpoint_id']) + assert 'endpoint_type' in message + assert isinstance(message['endpoint_type'], str) def validate_device(message): - assert type(message) is dict - assert len(message.keys()) == 5 + assert isinstance(message, dict) + assert len(message.keys()) == 6 assert 'device_id' in message validate_device_id(message['device_id']) assert 'device_type' in message - assert type(message['device_type']) is str + assert isinstance(message['device_type'], str) assert 'device_config' in message - validate_device_config(message['device_config']) - assert 'devOperationalStatus' in message - validate_device_operational_status(message['devOperationalStatus']) - assert 'endpointList' in message - assert type(message['endpointList']) is list - for endpoint in message['endpointList']: validate_endpoint(endpoint) + validate_config_rules(message['device_config']) + assert 'device_operational_status' in message + validate_device_operational_status_enum(message['device_operational_status']) + assert 'device_drivers' in message + assert isinstance(message['device_drivers'], list) + for driver in message['device_drivers']: validate_device_driver_enum(driver) + assert 'device_endpoints' in message + assert isinstance(message['device_endpoints'], list) + for endpoint in message['device_endpoints']: validate_endpoint(endpoint) def validate_link(message): - assert type(message) is dict + assert isinstance(message, dict) assert len(message.keys()) == 2 assert 'link_id' in message validate_link_id(message['link_id']) - assert 'endpointList' in message - assert type(message['endpointList']) is list - for endpoint_id in message['endpointList']: validate_endpoint_id(endpoint_id) - -def validate_topology(message): - assert type(message) is dict - assert len(message.keys()) > 0 - assert 'topoId' in message - validate_topology_id(message['topoId']) - assert 'device' in message - assert type(message['device']) is list - for device in message['device']: validate_device(device) - assert 'link' in message - assert type(message['link']) is list - for link in message['link']: validate_link(link) - -def validate_topology_is_empty(message): - validate_topology(message) - assert len(message['device']) == 0 - assert len(message['link']) == 0 - -def validate_topology_has_devices(message): - validate_topology(message) - assert len(message['device']) > 0 - -def validate_topology_has_links(message): - validate_topology(message) - assert len(message['link']) > 0 + assert 'link_endpoint_ids' in message + assert isinstance(message['link_endpoint_ids'], list) + for endpoint_id in message['link_endpoint_ids']: validate_endpoint_id(endpoint_id) -def validate_constraint(message): - assert type(message) is dict - assert len(message.keys()) == 2 - assert 'constraint_type' in message - assert type(message['constraint_type']) is str - assert 'constraint_value' in message - assert type(message['constraint_value']) is str -def validate_service_id(message): - assert type(message) is dict - assert len(message.keys()) == 2 - assert 'contextId' in message - validate_context_id(message['contextId']) - assert 'cs_id' in message - validate_uuid(message['cs_id']) +# ----- Lists of Objects ----------------------------------------------------------------------------------------------- -def validate_service_config(message): - assert type(message) is dict +def validate_contexts(message): + assert isinstance(message, dict) assert len(message.keys()) == 1 - assert 'serviceConfig' in message - assert type(message['serviceConfig']) is str + assert 'contexts' in message + assert isinstance(message['contexts'], list) + for context in message['contexts']: validate_context(context) -def validate_service_type(message): - assert type(message) is str - assert message in ['UNKNOWN', 'L3NM', 'L2NM', 'TAPI_CONNECTIVITY_SERVICE'] +def validate_services(message): + assert isinstance(message, dict) + assert len(message.keys()) == 1 + assert 'services' in message + assert isinstance(message['services'], list) + for service in message['services']: validate_service(service) -def validate_service_state_enum(message): - assert type(message) is str - assert message in ['PLANNED', 'ACTIVE', 'PENDING_REMOVAL'] +def validate_topologies(message): + assert isinstance(message, dict) + assert len(message.keys()) == 1 + assert 'topologies' in message + assert isinstance(message['topologies'], list) + for topology in message['topologies']: validate_topology(topology) -def validate_service_state(message): - assert type(message) is dict +def validate_devices(message): + assert isinstance(message, dict) assert len(message.keys()) == 1 - assert 'serviceState' in message - validate_service_state_enum(message['serviceState']) + assert 'devices' in message + assert isinstance(message['devices'], list) + for device in message['devices']: validate_device(device) -def validate_service(message): - assert type(message) is dict - assert len(message.keys()) == 6 - assert 'cs_id' in message - validate_service_id(message['cs_id']) - assert 'serviceType' in message - validate_service_type(message['serviceType']) - assert 'endpointList' in message - assert type(message['endpointList']) is list - for endpoint_id in message['endpointList']: validate_endpoint_id(endpoint_id) - assert 'constraint' in message - assert type(message['constraint']) is list - for constraint in message['constraint']: validate_constraint(constraint) - assert 'serviceState' in message - validate_service_state(message['serviceState']) - assert 'serviceConfig' in message - validate_service_config(message['serviceConfig']) - -def validate_service_list(message): - assert type(message) is dict - assert len(message.keys()) == 1 - assert 'cs' in message - assert type(message['cs']) is list - for cs in message['cs']: validate_service(cs) - -def validate_service_list_is_empty(message): - validate_service_list(message) - assert len(message['cs']) == 0 - -def validate_service_list_is_not_empty(message): - validate_service_list(message) - assert len(message['cs']) > 0 +def validate_links(message): + assert isinstance(message, dict) + assert len(message.keys()) == 1 + assert 'links' in message + assert isinstance(message['links'], list) + for link in message['links']: validate_link(link) diff --git a/src/context/Config.py b/src/context/Config.py index c4a21d97b63e17466bd7b47596cf0daa54ba4a64..9892899fdec0419ea2332547f6c9141a060d7a3e 100644 --- a/src/context/Config.py +++ b/src/context/Config.py @@ -1,7 +1,7 @@ import logging # General settings -LOG_LEVEL = logging.WARNING +LOG_LEVEL = logging.INFO # gRPC settings GRPC_SERVICE_PORT = 10100 diff --git a/src/context/service/__main__.py b/src/context/service/__main__.py index 979ee588d2790461937fa06b85106a6665f4197b..985a8e528d2cd08918c8e093adb38f54037c1354 100644 --- a/src/context/service/__main__.py +++ b/src/context/service/__main__.py @@ -11,7 +11,7 @@ from context.Config import ( from context.service.Populate import populate from context.service.grpc_server.ContextService import ContextService from context.service.rest_server.Server import Server -from context.service.rest_server.resources.Context import Context +from context.service.rest_server.Resources import RESOURCES terminate = threading.Event() logger = None @@ -31,6 +31,7 @@ def main(): restapi_base_url = get_setting('RESTAPI_BASE_URL', default=RESTAPI_BASE_URL ) metrics_port = get_setting('METRICS_PORT', default=METRICS_PORT ) populate_fake_data = get_setting('POPULATE_FAKE_DATA', default=POPULATE_FAKE_DATA ) + if isinstance(populate_fake_data, str): populate_fake_data = (populate_fake_data.upper() in {'T', '1', 'TRUE'}) logging.basicConfig(level=log_level) logger = logging.getLogger(__name__) @@ -55,7 +56,8 @@ def main(): grpc_service.start() rest_server = Server(port=restapi_service_port, base_url=restapi_base_url) - rest_server.add_resource(Context, '/context', endpoint='api.context', resource_class_args=(database,)) + 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() if populate_fake_data: diff --git a/src/context/service/grpc_server/ContextService.py b/src/context/service/grpc_server/ContextService.py index 544dcad424589ac3c9b9e2eee0be47ea2c467611..ab7653e37d318d0bfeea4a60213206d391c0dfda 100644 --- a/src/context/service/grpc_server/ContextService.py +++ b/src/context/service/grpc_server/ContextService.py @@ -29,8 +29,8 @@ class ContextService: self.server = None def start(self): - self.endpoint = '{:s}:{:s}'.format(self.address, self.port) - LOGGER.debug('Starting Service (tentative endpoint: {:s}, max_workers: {:s})...'.format( + self.endpoint = '{:s}:{:s}'.format(self.address, str(self.port)) + LOGGER.info('Starting Service (tentative endpoint: {:s}, max_workers: {:s})...'.format( str(self.endpoint), str(self.max_workers))) self.pool = futures.ThreadPoolExecutor(max_workers=self.max_workers) diff --git a/src/context/service/grpc_server/ContextServiceServicerImpl.py b/src/context/service/grpc_server/ContextServiceServicerImpl.py index f39c7773917898efbfdfbb083d3630292710c4a3..d8f7b648b4b919cc61330f236195c444f550ede1 100644 --- a/src/context/service/grpc_server/ContextServiceServicerImpl.py +++ b/src/context/service/grpc_server/ContextServiceServicerImpl.py @@ -71,26 +71,33 @@ class ContextServiceServicerImpl(ContextServiceServicer): @safe_and_metered_rpc_method(METRICS, LOGGER) def SetContext(self, request: Context, context : grpc.ServicerContext) -> ContextId: context_uuid = request.context_id.context_uuid.uuid - result : Tuple[ContextModel, bool] = update_or_create_object( - self.database, ContextModel, context_uuid, {'context_uuid': context_uuid}) - db_context, updated = result for i,topology_id in enumerate(request.topology_ids): topology_context_uuid = topology_id.context_id.context_uuid.uuid - topology_uuid = topology_id.topology_uuid.uuid if topology_context_uuid != context_uuid: raise InvalidArgumentException( 'request.topology_ids[{:d}].context_id.context_uuid.uuid'.format(i), topology_context_uuid, ['should be == {:s}({:s})'.format('request.context_id.context_uuid.uuid', context_uuid)]) - get_object(self.database, TopologyModel, [context_uuid, topology_uuid]) # just to confirm it exists for i,service_id in enumerate(request.service_ids): service_context_uuid = service_id.context_id.context_uuid.uuid - service_uuid = service_id.service_uuid.uuid if service_context_uuid != context_uuid: raise InvalidArgumentException( 'request.service_ids[{:d}].context_id.context_uuid.uuid'.format(i), service_context_uuid, ['should be == {:s}({:s})'.format('request.context_id.context_uuid.uuid', context_uuid)]) + + result : Tuple[ContextModel, bool] = update_or_create_object( + self.database, ContextModel, context_uuid, {'context_uuid': context_uuid}) + db_context, updated = result + + for i,topology_id in enumerate(request.topology_ids): + topology_context_uuid = topology_id.context_id.context_uuid.uuid + topology_uuid = topology_id.topology_uuid.uuid + get_object(self.database, TopologyModel, [context_uuid, topology_uuid]) # just to confirm it exists + + for i,service_id in enumerate(request.service_ids): + service_context_uuid = service_id.context_id.context_uuid.uuid + service_uuid = service_id.service_uuid.uuid get_object(self.database, ServiceModel, [context_uuid, service_uuid]) # just to confirm it exists event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE @@ -221,6 +228,14 @@ class ContextServiceServicerImpl(ContextServiceServicer): def SetDevice(self, request: Device, context : grpc.ServicerContext) -> DeviceId: device_uuid = request.device_id.device_uuid.uuid + for i,endpoint in enumerate(request.device_endpoints): + endpoint_device_uuid = endpoint.endpoint_id.device_id.device_uuid.uuid + if len(endpoint_device_uuid) == 0: endpoint_device_uuid = device_uuid + if device_uuid != endpoint_device_uuid: + raise InvalidArgumentException( + 'request.device_endpoints[{:d}].device_id.device_uuid.uuid'.format(i), endpoint_device_uuid, + ['should be == {:s}({:s})'.format('request.device_id.device_uuid.uuid', device_uuid)]) + running_config_result = set_config(self.database, device_uuid, 'running', request.device_config.config_rules) db_running_config = running_config_result[0][0] @@ -238,10 +253,6 @@ class ContextServiceServicerImpl(ContextServiceServicer): endpoint_uuid = endpoint.endpoint_id.endpoint_uuid.uuid endpoint_device_uuid = endpoint.endpoint_id.device_id.device_uuid.uuid if len(endpoint_device_uuid) == 0: endpoint_device_uuid = device_uuid - if device_uuid != endpoint_device_uuid: - raise InvalidArgumentException( - 'request.device_endpoints[{:d}].device_id.device_uuid.uuid'.format(i), endpoint_device_uuid, - ['should be == {:s}({:s})'.format('request.device_id.device_uuid.uuid', device_uuid)]) str_endpoint_key = key_to_str([device_uuid, endpoint_uuid]) endpoint_attributes = { @@ -426,6 +437,14 @@ class ContextServiceServicerImpl(ContextServiceServicer): context_uuid = request.service_id.context_id.context_uuid.uuid db_context : ContextModel = get_object(self.database, ContextModel, context_uuid) + for i,endpoint_id in enumerate(request.service_endpoint_ids): + endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid + if len(endpoint_topology_context_uuid) > 0 and context_uuid != endpoint_topology_context_uuid: + raise InvalidArgumentException( + 'request.service_endpoint_ids[{:d}].topology_id.context_id.context_uuid.uuid'.format(i), + endpoint_topology_context_uuid, + ['should be == {:s}({:s})'.format('request.service_id.context_id.context_uuid.uuid', context_uuid)]) + service_uuid = request.service_id.service_uuid.uuid str_service_key = key_to_str([context_uuid, service_uuid]) @@ -453,12 +472,6 @@ class ContextServiceServicerImpl(ContextServiceServicer): endpoint_topology_uuid = endpoint_id.topology_id.topology_uuid.uuid endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid - if len(endpoint_topology_context_uuid) > 0 and context_uuid != endpoint_topology_context_uuid: - raise InvalidArgumentException( - 'request.service_endpoint_ids[{:d}].topology_id.context_id.context_uuid.uuid'.format(i), - endpoint_topology_context_uuid, - ['should be == {:s}({:s})'.format('request.service_id.context_id.context_uuid.uuid', context_uuid)]) - str_endpoint_key = key_to_str([endpoint_device_uuid, endpoint_uuid]) if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0: str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid]) diff --git a/src/context/service/rest_server/Resources.py b/src/context/service/rest_server/Resources.py new file mode 100644 index 0000000000000000000000000000000000000000..54a21ed3984ced399d82f72b7ca7f77e447f0459 --- /dev/null +++ b/src/context/service/rest_server/Resources.py @@ -0,0 +1,130 @@ +from flask.json import jsonify +from flask_restful import Resource +from google.protobuf.json_format import MessageToDict +from common.message_broker.Factory import LOGGER +from common.orm.Database import Database +from context.proto.context_pb2 import ContextId, DeviceId, Empty, LinkId, ServiceId, TopologyId +from context.service.grpc_server.ContextServiceServicerImpl import ContextServiceServicerImpl + +def grpc_context_id(context_uuid): + return ContextId(**{ + 'context_uuid': {'uuid': context_uuid} + }) + +def grpc_topology_id(context_uuid, topology_uuid): + return TopologyId(**{ + 'context_id': {'context_uuid': {'uuid': context_uuid}}, + 'topology_uuid': {'uuid': topology_uuid} + }) + +def grpc_service_id(context_uuid, service_uuid): + return ServiceId(**{ + 'context_id': {'context_uuid': {'uuid': context_uuid}}, + 'service_uuid': {'uuid': service_uuid} + }) + +def grpc_device_id(device_uuid): + return DeviceId(**{ + 'device_uuid': {'uuid': device_uuid} + }) + +def grpc_link_id(link_uuid): + return LinkId(**{ + 'link_uuid': {'uuid': link_uuid} + }) + +def format_grpc_to_json(grpc_reply): + return jsonify(MessageToDict( + grpc_reply, including_default_value_fields=True, preserving_proto_field_name=True, + use_integers_for_enums=False)) + +class _Resource(Resource): + def __init__(self, database : Database) -> None: + super().__init__() + self.database = database + self.servicer = ContextServiceServicerImpl(self.database, None) + +class ContextIds(_Resource): + def get(self): + return format_grpc_to_json(self.servicer.ListContextIds(Empty(), None)) + +class Contexts(_Resource): + def get(self): + return format_grpc_to_json(self.servicer.ListContexts(Empty(), None)) + +class Context(_Resource): + def get(self, context_uuid : str): + return format_grpc_to_json(self.servicer.GetContext(grpc_context_id(context_uuid), None)) + +class TopologyIds(_Resource): + def get(self, context_uuid : str): + return format_grpc_to_json(self.servicer.ListTopologyIds(grpc_context_id(context_uuid), None)) + +class Topologies(_Resource): + def get(self, context_uuid : str): + return format_grpc_to_json(self.servicer.ListTopologies(grpc_context_id(context_uuid), None)) + +class Topology(_Resource): + def get(self, context_uuid : str, topology_uuid : str): + return format_grpc_to_json(self.servicer.GetTopology(grpc_topology_id(context_uuid, topology_uuid), None)) + +class ServiceIds(_Resource): + def get(self, context_uuid : str): + return format_grpc_to_json(self.servicer.ListServiceIds(grpc_context_id(context_uuid), None)) + +class Services(_Resource): + def get(self, context_uuid : str): + return format_grpc_to_json(self.servicer.ListServices(grpc_context_id(context_uuid), None)) + +class Service(_Resource): + def get(self, context_uuid : str, service_uuid : str): + return format_grpc_to_json(self.servicer.GetService(grpc_service_id(context_uuid, service_uuid), None)) + +class DeviceIds(_Resource): + def get(self): + return format_grpc_to_json(self.servicer.ListDeviceIds(Empty(), None)) + +class Devices(_Resource): + def get(self): + return format_grpc_to_json(self.servicer.ListDevices(Empty(), None)) + +class Device(_Resource): + def get(self, device_uuid : str): + return format_grpc_to_json(self.servicer.GetDevice(grpc_device_id(device_uuid), None)) + +class LinkIds(_Resource): + def get(self): + return format_grpc_to_json(self.servicer.ListLinkIds(Empty(), None)) + +class Links(_Resource): + def get(self): + return format_grpc_to_json(self.servicer.ListLinks(Empty(), None)) + +class Link(_Resource): + def get(self, link_uuid : str): + return format_grpc_to_json(self.servicer.GetLink(grpc_link_id(link_uuid), None)) + +# 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.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>'), +] diff --git a/src/context/service/rest_server/Server.py b/src/context/service/rest_server/Server.py index 16badfce8c84f058aeaeac79993ada726a17f06a..3095d77c48e756dd0c5d655b06a2b0625bcc89d5 100644 --- a/src/context/service/rest_server/Server.py +++ b/src/context/service/rest_server/Server.py @@ -15,6 +15,8 @@ class Server(threading.Thread): self.host = host self.port = port self.base_url = base_url + self.srv = None + self.ctx = None self.app = Flask(__name__) self.api = Api(self.app, prefix=self.base_url) diff --git a/src/context/service/rest_server/resources/Context.py b/src/context/service/rest_server/resources/Context.py deleted file mode 100644 index 557654924bd838c557751f7b941953988f7bc7a2..0000000000000000000000000000000000000000 --- a/src/context/service/rest_server/resources/Context.py +++ /dev/null @@ -1,15 +0,0 @@ -from flask.json import jsonify -from flask_restful import Resource -from google.protobuf.json_format import MessageToDict -from common.orm.Database import Database -from context.proto.context_pb2 import Empty -from context.service.grpc_server.ContextServiceServicerImpl import ContextServiceServicerImpl - -class Context(Resource): - def __init__(self, database : Database) -> None: - super().__init__() - self.database = database - - def get(self): - servicer = ContextServiceServicerImpl(self.database, None) - return jsonify(MessageToDict(servicer.ListContexts(Empty(), None))) diff --git a/src/context/service/rest_server/resources/__init__.py b/src/context/service/rest_server/resources/__init__.py deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/context/tests/example_objects.py b/src/context/tests/example_objects.py index 9fb48f038f23ebddb89fe3608899bf579aca0e49..81339c04e1fe77667bd41179f3fa0813c5fc69df 100644 --- a/src/context/tests/example_objects.py +++ b/src/context/tests/example_objects.py @@ -18,7 +18,11 @@ def endpoint(topology_id, device_id, endpoint_uuid, endpoint_type): ## use "deepcopy" to prevent propagating forced changes during tests CONTEXT_ID = {'context_uuid': {'uuid': DEFAULT_CONTEXT_UUID}} -CONTEXT = {'context_id': deepcopy(CONTEXT_ID)} +CONTEXT = { + 'context_id': deepcopy(CONTEXT_ID), + 'topology_ids': [], + 'service_ids': [], +} TOPOLOGY_ID = { 'context_id': deepcopy(CONTEXT_ID), diff --git a/src/context/tests/test_unitary_grpc.py b/src/context/tests/test_unitary.py similarity index 71% rename from src/context/tests/test_unitary_grpc.py rename to src/context/tests/test_unitary.py index 6581b60b3192dae9c2750e2a7abb356a6361ffd2..db706948fc46bf8397a3894ecd62e212301d88c8 100644 --- a/src/context/tests/test_unitary_grpc.py +++ b/src/context/tests/test_unitary.py @@ -1,4 +1,4 @@ -import copy, grpc, logging, pytest, threading +import copy, grpc, logging, pytest, requests, threading, time, urllib from queue import Queue from typing import Tuple from common.Constants import DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID @@ -6,13 +6,23 @@ from common.orm.Database import Database from common.orm.Factory import get_database_backend, BackendEnum as DatabaseBackendEnum from common.message_broker.Factory import get_messagebroker_backend, BackendEnum as MessageBrokerBackendEnum from common.message_broker.MessageBroker import MessageBroker -from context.Config import GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD +from common.type_checkers.Assertions import ( + 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.Config import ( + GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD, RESTAPI_SERVICE_PORT, RESTAPI_BASE_URL) from context.client.ContextClient import ContextClient from context.proto.context_pb2 import ( Context, ContextEvent, ContextId, Device, DeviceEvent, DeviceId, DeviceOperationalStatusEnum, Empty, EventTypeEnum, Link, LinkEvent, LinkId, Service, ServiceEvent, ServiceId, ServiceStatusEnum, ServiceTypeEnum, Topology, TopologyEvent, TopologyId) +from context.service.database.Tools import ( + FASTHASHER_DATA_ACCEPTED_FORMAT, FASTHASHER_ITEM_ACCEPTED_FORMAT, fast_hasher) from context.service.grpc_server.ContextService import ContextService +from context.service.Populate import populate +from context.service.rest_server.Server import Server as RestServer +from context.service.rest_server.Resources import RESOURCES from .example_objects import ( CONTEXT, CONTEXT_ID, DEVICE1, DEVICE1_ID, DEVICE1_UUID, DEVICE2, DEVICE2_ID, DEVICE2_UUID, LINK_DEV1_DEV2, LINK_DEV1_DEV2_ID, LINK_DEV1_DEV2_UUID, SERVICE_DEV1_DEV2, SERVICE_DEV1_DEV2_ID, SERVICE_DEV1_DEV2_UUID, TOPOLOGY, @@ -21,7 +31,8 @@ from .example_objects import ( LOGGER = logging.getLogger(__name__) LOGGER.setLevel(logging.DEBUG) -GRPC_PORT = 10000 + GRPC_SERVICE_PORT # avoid privileged ports +GRPC_PORT = 10000 + GRPC_SERVICE_PORT # avoid privileged ports +RESTAPI_PORT = 10000 + RESTAPI_SERVICE_PORT # avoid privileged ports REDIS_CONFIG = { 'REDIS_SERVICE_HOST': '10.1.7.194', @@ -45,7 +56,7 @@ def context_db_mb(request) -> Tuple[Database, MessageBroker]: _message_broker.terminate() @pytest.fixture(scope='session') -def context_service(context_db_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name +def context_service_grpc(context_db_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name _service = ContextService( context_db_mb[0], context_db_mb[1], port=GRPC_PORT, max_workers=GRPC_MAX_WORKERS, grace_period=GRPC_GRACE_PERIOD) @@ -54,21 +65,40 @@ def context_service(context_db_mb : Tuple[Database, MessageBroker]): # pylint: d _service.stop() @pytest.fixture(scope='session') -def context_client(context_service : ContextService): # pylint: disable=redefined-outer-name +def context_service_rest(context_db_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name + database = context_db_mb[0] + _rest_server = RestServer(port=RESTAPI_PORT, base_url=RESTAPI_BASE_URL) + 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(address='127.0.0.1', port=GRPC_PORT) yield _client _client.close() +def do_rest_request(url : str): + request_url = 'http://127.0.0.1:{:s}{:s}{:s}'.format(str(RESTAPI_PORT), str(RESTAPI_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() class EventsCollector: - def __init__(self, context_client : ContextClient) -> None: # pylint: disable=redefined-outer-name + def __init__(self, context_client_grpc : ContextClient) -> None: # pylint: disable=redefined-outer-name self._events_queue = Queue() - self._context_stream = context_client.GetContextEvents(Empty()) - self._topology_stream = context_client.GetTopologyEvents(Empty()) - self._device_stream = context_client.GetDeviceEvents(Empty()) - self._link_stream = context_client.GetLinkEvents(Empty()) - self._service_stream = context_client.GetServiceEvents(Empty()) + self._context_stream = context_client_grpc.GetContextEvents(Empty()) + self._topology_stream = context_client_grpc.GetTopologyEvents(Empty()) + self._device_stream = context_client_grpc.GetDeviceEvents(Empty()) + self._link_stream = context_client_grpc.GetLinkEvents(Empty()) + self._service_stream = context_client_grpc.GetServiceEvents(Empty()) self._context_thread = threading.Thread(target=self._collect, args=(self._context_stream ,), daemon=False) self._topology_thread = threading.Thread(target=self._collect, args=(self._topology_stream,), daemon=False) @@ -108,8 +138,10 @@ class EventsCollector: self._service_thread.join() -def test_context( - context_client : ContextClient, # pylint: disable=redefined-outer-name +# ----- Test gRPC methods ---------------------------------------------------------------------------------------------- + +def test_grpc_context( + context_client_grpc : ContextClient, # pylint: disable=redefined-outer-name context_db_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name context_database = context_db_mb[0] @@ -117,20 +149,20 @@ def test_context( context_database.clear_all() # ----- Initialize the EventsCollector ----------------------------------------------------------------------------- - events_collector = EventsCollector(context_client) + events_collector = EventsCollector(context_client_grpc) events_collector.start() # ----- Get when the object does not exist ------------------------------------------------------------------------- with pytest.raises(grpc.RpcError) as e: - context_client.GetContext(ContextId(**CONTEXT_ID)) + context_client_grpc.GetContext(ContextId(**CONTEXT_ID)) assert e.value.code() == grpc.StatusCode.NOT_FOUND assert e.value.details() == 'Context({:s}) not found'.format(DEFAULT_CONTEXT_UUID) # ----- List when the object does not exist ------------------------------------------------------------------------ - response = context_client.ListContextIds(Empty()) + response = context_client_grpc.ListContextIds(Empty()) assert len(response.context_ids) == 0 - response = context_client.ListContexts(Empty()) + response = context_client_grpc.ListContexts(Empty()) assert len(response.contexts) == 0 # ----- Dump state of database before create the object ------------------------------------------------------------ @@ -142,9 +174,31 @@ def test_context( assert len(db_entries) == 0 # ----- Create the object ------------------------------------------------------------------------------------------ - response = context_client.SetContext(Context(**CONTEXT)) + response = context_client_grpc.SetContext(Context(**CONTEXT)) assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID + with pytest.raises(grpc.RpcError) as e: + WRONG_TOPOLOGY_ID = copy.deepcopy(TOPOLOGY_ID) + WRONG_TOPOLOGY_ID['context_id']['context_uuid']['uuid'] = 'wrong-context-uuid' + WRONG_CONTEXT = copy.deepcopy(CONTEXT) + WRONG_CONTEXT['topology_ids'].append(WRONG_TOPOLOGY_ID) + context_client_grpc.SetContext(Context(**WRONG_CONTEXT)) + assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT + msg = 'request.topology_ids[0].context_id.context_uuid.uuid(wrong-context-uuid) is invalid; '\ + 'should be == request.context_id.context_uuid.uuid(admin)' + assert e.value.details() == msg + + with pytest.raises(grpc.RpcError) as e: + WRONG_SERVICE_ID = copy.deepcopy(SERVICE_DEV1_DEV2_ID) + WRONG_SERVICE_ID['context_id']['context_uuid']['uuid'] = 'wrong-context-uuid' + WRONG_CONTEXT = copy.deepcopy(CONTEXT) + WRONG_CONTEXT['service_ids'].append(WRONG_SERVICE_ID) + context_client_grpc.SetContext(Context(**WRONG_CONTEXT)) + assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT + msg = 'request.service_ids[0].context_id.context_uuid.uuid(wrong-context-uuid) is invalid; '\ + 'should be == request.context_id.context_uuid.uuid(admin)' + assert e.value.details() == msg + # ----- Check create event ----------------------------------------------------------------------------------------- event = events_collector.get_event(block=True) assert isinstance(event, ContextEvent) @@ -152,7 +206,7 @@ def test_context( assert event.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID # ----- Update the object ------------------------------------------------------------------------------------------ - response = context_client.SetContext(Context(**CONTEXT)) + response = context_client_grpc.SetContext(Context(**CONTEXT)) assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID # ----- Check update event ----------------------------------------------------------------------------------------- @@ -170,24 +224,24 @@ def test_context( assert len(db_entries) == 2 # ----- Get when the object exists --------------------------------------------------------------------------------- - response = context_client.GetContext(ContextId(**CONTEXT_ID)) + response = context_client_grpc.GetContext(ContextId(**CONTEXT_ID)) assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID assert len(response.topology_ids) == 0 assert len(response.service_ids) == 0 # ----- List when the object exists -------------------------------------------------------------------------------- - response = context_client.ListContextIds(Empty()) + response = context_client_grpc.ListContextIds(Empty()) assert len(response.context_ids) == 1 assert response.context_ids[0].context_uuid.uuid == DEFAULT_CONTEXT_UUID - response = context_client.ListContexts(Empty()) + response = context_client_grpc.ListContexts(Empty()) assert len(response.contexts) == 1 assert response.contexts[0].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID assert len(response.contexts[0].topology_ids) == 0 assert len(response.contexts[0].service_ids) == 0 # ----- Remove the object ------------------------------------------------------------------------------------------ - context_client.RemoveContext(ContextId(**CONTEXT_ID)) + context_client_grpc.RemoveContext(ContextId(**CONTEXT_ID)) # ----- Check remove event ----------------------------------------------------------------------------------------- event = events_collector.get_event(block=True) @@ -207,8 +261,8 @@ def test_context( assert len(db_entries) == 0 -def test_topology( - context_client : ContextClient, # pylint: disable=redefined-outer-name +def test_grpc_topology( + context_client_grpc : ContextClient, # pylint: disable=redefined-outer-name context_db_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name context_database = context_db_mb[0] @@ -216,11 +270,11 @@ def test_topology( context_database.clear_all() # ----- Initialize the EventsCollector ----------------------------------------------------------------------------- - events_collector = EventsCollector(context_client) + events_collector = EventsCollector(context_client_grpc) events_collector.start() # ----- Prepare dependencies for the test and capture related events ----------------------------------------------- - response = context_client.SetContext(Context(**CONTEXT)) + response = context_client_grpc.SetContext(Context(**CONTEXT)) assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID event = events_collector.get_event(block=True) @@ -230,15 +284,15 @@ def test_topology( # ----- Get when the object does not exist ------------------------------------------------------------------------- with pytest.raises(grpc.RpcError) as e: - context_client.GetTopology(TopologyId(**TOPOLOGY_ID)) + context_client_grpc.GetTopology(TopologyId(**TOPOLOGY_ID)) assert e.value.code() == grpc.StatusCode.NOT_FOUND assert e.value.details() == 'Topology({:s}/{:s}) not found'.format(DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID) # ----- List when the object does not exist ------------------------------------------------------------------------ - response = context_client.ListTopologyIds(ContextId(**CONTEXT_ID)) + response = context_client_grpc.ListTopologyIds(ContextId(**CONTEXT_ID)) assert len(response.topology_ids) == 0 - response = context_client.ListTopologies(ContextId(**CONTEXT_ID)) + response = context_client_grpc.ListTopologies(ContextId(**CONTEXT_ID)) assert len(response.topologies) == 0 # ----- Dump state of database before create the object ------------------------------------------------------------ @@ -250,10 +304,15 @@ def test_topology( assert len(db_entries) == 2 # ----- Create the object ------------------------------------------------------------------------------------------ - response = context_client.SetTopology(Topology(**TOPOLOGY)) + response = context_client_grpc.SetTopology(Topology(**TOPOLOGY)) assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + CONTEXT_WITH_TOPOLOGY = copy.deepcopy(CONTEXT) + CONTEXT_WITH_TOPOLOGY['topology_ids'].append(TOPOLOGY_ID) + response = context_client_grpc.SetContext(Context(**CONTEXT_WITH_TOPOLOGY)) + assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # ----- Check create event ----------------------------------------------------------------------------------------- event = events_collector.get_event(block=True) assert isinstance(event, TopologyEvent) @@ -261,8 +320,13 @@ def test_topology( assert event.topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID assert event.topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + event = events_collector.get_event(block=True) + assert isinstance(event, ContextEvent) + assert event.event.event_type == EventTypeEnum.EVENTTYPE_UPDATE + assert event.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # ----- Update the object ------------------------------------------------------------------------------------------ - response = context_client.SetTopology(Topology(**TOPOLOGY)) + response = context_client_grpc.SetTopology(Topology(**TOPOLOGY)) assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID @@ -282,19 +346,19 @@ def test_topology( assert len(db_entries) == 5 # ----- Get when the object exists --------------------------------------------------------------------------------- - response = context_client.GetTopology(TopologyId(**TOPOLOGY_ID)) + response = context_client_grpc.GetTopology(TopologyId(**TOPOLOGY_ID)) assert response.topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID assert response.topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID assert len(response.device_ids) == 0 assert len(response.link_ids) == 0 # ----- List when the object exists -------------------------------------------------------------------------------- - response = context_client.ListTopologyIds(ContextId(**CONTEXT_ID)) + response = context_client_grpc.ListTopologyIds(ContextId(**CONTEXT_ID)) assert len(response.topology_ids) == 1 assert response.topology_ids[0].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID assert response.topology_ids[0].topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID - response = context_client.ListTopologies(ContextId(**CONTEXT_ID)) + response = context_client_grpc.ListTopologies(ContextId(**CONTEXT_ID)) assert len(response.topologies) == 1 assert response.topologies[0].topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID assert response.topologies[0].topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID @@ -302,8 +366,8 @@ def test_topology( assert len(response.topologies[0].link_ids) == 0 # ----- Remove the object ------------------------------------------------------------------------------------------ - context_client.RemoveTopology(TopologyId(**TOPOLOGY_ID)) - context_client.RemoveContext(ContextId(**CONTEXT_ID)) + context_client_grpc.RemoveTopology(TopologyId(**TOPOLOGY_ID)) + context_client_grpc.RemoveContext(ContextId(**CONTEXT_ID)) # ----- Check remove event ----------------------------------------------------------------------------------------- event = events_collector.get_event(block=True) @@ -329,8 +393,8 @@ def test_topology( assert len(db_entries) == 0 -def test_device( - context_client : ContextClient, # pylint: disable=redefined-outer-name +def test_grpc_device( + context_client_grpc : ContextClient, # pylint: disable=redefined-outer-name context_db_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name context_database = context_db_mb[0] @@ -338,14 +402,14 @@ def test_device( context_database.clear_all() # ----- Initialize the EventsCollector ----------------------------------------------------------------------------- - events_collector = EventsCollector(context_client) + events_collector = EventsCollector(context_client_grpc) events_collector.start() # ----- Prepare dependencies for the test and capture related events ----------------------------------------------- - response = context_client.SetContext(Context(**CONTEXT)) + response = context_client_grpc.SetContext(Context(**CONTEXT)) assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID - response = context_client.SetTopology(Topology(**TOPOLOGY)) + response = context_client_grpc.SetTopology(Topology(**TOPOLOGY)) assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID @@ -362,15 +426,15 @@ def test_device( # ----- Get when the object does not exist ------------------------------------------------------------------------- with pytest.raises(grpc.RpcError) as e: - context_client.GetDevice(DeviceId(**DEVICE1_ID)) + context_client_grpc.GetDevice(DeviceId(**DEVICE1_ID)) assert e.value.code() == grpc.StatusCode.NOT_FOUND assert e.value.details() == 'Device({:s}) not found'.format(DEVICE1_UUID) # ----- List when the object does not exist ------------------------------------------------------------------------ - response = context_client.ListDeviceIds(Empty()) + response = context_client_grpc.ListDeviceIds(Empty()) assert len(response.device_ids) == 0 - response = context_client.ListDevices(Empty()) + response = context_client_grpc.ListDevices(Empty()) assert len(response.devices) == 0 # ----- Dump state of database before create the object ------------------------------------------------------------ @@ -382,7 +446,16 @@ def test_device( assert len(db_entries) == 5 # ----- Create the object ------------------------------------------------------------------------------------------ - response = context_client.SetDevice(Device(**DEVICE1)) + with pytest.raises(grpc.RpcError) as e: + WRONG_DEVICE = copy.deepcopy(DEVICE1) + WRONG_DEVICE['device_endpoints'][0]['endpoint_id']['device_id']['device_uuid']['uuid'] = 'wrong-device-uuid' + context_client_grpc.SetDevice(Device(**WRONG_DEVICE)) + assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT + msg = 'request.device_endpoints[0].device_id.device_uuid.uuid(wrong-device-uuid) is invalid; '\ + 'should be == request.device_id.device_uuid.uuid(DEV1)' + assert e.value.details() == msg + + response = context_client_grpc.SetDevice(Device(**DEVICE1)) assert response.device_uuid.uuid == DEVICE1_UUID # ----- Check create event ----------------------------------------------------------------------------------------- @@ -392,7 +465,7 @@ def test_device( assert event.device_id.device_uuid.uuid == DEVICE1_UUID # ----- Update the object ------------------------------------------------------------------------------------------ - response = context_client.SetDevice(Device(**DEVICE1)) + response = context_client_grpc.SetDevice(Device(**DEVICE1)) assert response.device_uuid.uuid == DEVICE1_UUID # ----- Check update event ----------------------------------------------------------------------------------------- @@ -410,7 +483,7 @@ def test_device( assert len(db_entries) == 25 # ----- Get when the object exists --------------------------------------------------------------------------------- - response = context_client.GetDevice(DeviceId(**DEVICE1_ID)) + response = context_client_grpc.GetDevice(DeviceId(**DEVICE1_ID)) assert response.device_id.device_uuid.uuid == DEVICE1_UUID assert response.device_type == 'packet-router' assert len(response.device_config.config_rules) == 3 @@ -419,11 +492,11 @@ def test_device( assert len(response.device_endpoints) == 3 # ----- List when the object exists -------------------------------------------------------------------------------- - response = context_client.ListDeviceIds(Empty()) + response = context_client_grpc.ListDeviceIds(Empty()) assert len(response.device_ids) == 1 assert response.device_ids[0].device_uuid.uuid == DEVICE1_UUID - response = context_client.ListDevices(Empty()) + response = context_client_grpc.ListDevices(Empty()) assert len(response.devices) == 1 assert response.devices[0].device_id.device_uuid.uuid == DEVICE1_UUID assert response.devices[0].device_type == 'packet-router' @@ -435,7 +508,7 @@ def test_device( # ----- Create object relation ------------------------------------------------------------------------------------- TOPOLOGY_WITH_DEVICE = copy.deepcopy(TOPOLOGY) TOPOLOGY_WITH_DEVICE['device_ids'].append(DEVICE1_ID) - response = context_client.SetTopology(Topology(**TOPOLOGY_WITH_DEVICE)) + response = context_client_grpc.SetTopology(Topology(**TOPOLOGY_WITH_DEVICE)) assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID @@ -447,7 +520,7 @@ def test_device( assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID # ----- Check relation was created --------------------------------------------------------------------------------- - response = context_client.GetTopology(TopologyId(**TOPOLOGY_ID)) + response = context_client_grpc.GetTopology(TopologyId(**TOPOLOGY_ID)) assert response.topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID assert response.topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID assert len(response.device_ids) == 1 @@ -463,9 +536,9 @@ def test_device( assert len(db_entries) == 25 # ----- Remove the object ------------------------------------------------------------------------------------------ - context_client.RemoveDevice(DeviceId(**DEVICE1_ID)) - context_client.RemoveTopology(TopologyId(**TOPOLOGY_ID)) - context_client.RemoveContext(ContextId(**CONTEXT_ID)) + context_client_grpc.RemoveDevice(DeviceId(**DEVICE1_ID)) + context_client_grpc.RemoveTopology(TopologyId(**TOPOLOGY_ID)) + context_client_grpc.RemoveContext(ContextId(**CONTEXT_ID)) # ----- Check remove event ----------------------------------------------------------------------------------------- event = events_collector.get_event(block=True) @@ -496,8 +569,8 @@ def test_device( assert len(db_entries) == 0 -def test_link( - context_client : ContextClient, # pylint: disable=redefined-outer-name +def test_grpc_link( + context_client_grpc : ContextClient, # pylint: disable=redefined-outer-name context_db_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name context_database = context_db_mb[0] @@ -505,21 +578,21 @@ def test_link( context_database.clear_all() # ----- Initialize the EventsCollector ----------------------------------------------------------------------------- - events_collector = EventsCollector(context_client) + events_collector = EventsCollector(context_client_grpc) events_collector.start() # ----- Prepare dependencies for the test and capture related events ----------------------------------------------- - response = context_client.SetContext(Context(**CONTEXT)) + response = context_client_grpc.SetContext(Context(**CONTEXT)) assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID - response = context_client.SetTopology(Topology(**TOPOLOGY)) + response = context_client_grpc.SetTopology(Topology(**TOPOLOGY)) assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID - response = context_client.SetDevice(Device(**DEVICE1)) + response = context_client_grpc.SetDevice(Device(**DEVICE1)) assert response.device_uuid.uuid == DEVICE1_UUID - response = context_client.SetDevice(Device(**DEVICE2)) + response = context_client_grpc.SetDevice(Device(**DEVICE2)) assert response.device_uuid.uuid == DEVICE2_UUID event = events_collector.get_event(block=True) @@ -545,15 +618,15 @@ def test_link( # ----- Get when the object does not exist ------------------------------------------------------------------------- with pytest.raises(grpc.RpcError) as e: - context_client.GetLink(LinkId(**LINK_DEV1_DEV2_ID)) + context_client_grpc.GetLink(LinkId(**LINK_DEV1_DEV2_ID)) assert e.value.code() == grpc.StatusCode.NOT_FOUND assert e.value.details() == 'Link({:s}) not found'.format(LINK_DEV1_DEV2_UUID) # ----- List when the object does not exist ------------------------------------------------------------------------ - response = context_client.ListLinkIds(Empty()) + response = context_client_grpc.ListLinkIds(Empty()) assert len(response.link_ids) == 0 - response = context_client.ListLinks(Empty()) + response = context_client_grpc.ListLinks(Empty()) assert len(response.links) == 0 # ----- Dump state of database before create the object ------------------------------------------------------------ @@ -565,7 +638,7 @@ def test_link( assert len(db_entries) == 38 # ----- Create the object ------------------------------------------------------------------------------------------ - response = context_client.SetLink(Link(**LINK_DEV1_DEV2)) + response = context_client_grpc.SetLink(Link(**LINK_DEV1_DEV2)) assert response.link_uuid.uuid == LINK_DEV1_DEV2_UUID # ----- Check create event ----------------------------------------------------------------------------------------- @@ -575,7 +648,7 @@ def test_link( assert event.link_id.link_uuid.uuid == LINK_DEV1_DEV2_UUID # ----- Update the object ------------------------------------------------------------------------------------------ - response = context_client.SetLink(Link(**LINK_DEV1_DEV2)) + response = context_client_grpc.SetLink(Link(**LINK_DEV1_DEV2)) assert response.link_uuid.uuid == LINK_DEV1_DEV2_UUID # ----- Check update event ----------------------------------------------------------------------------------------- @@ -593,16 +666,16 @@ def test_link( assert len(db_entries) == 48 # ----- Get when the object exists --------------------------------------------------------------------------------- - response = context_client.GetLink(LinkId(**LINK_DEV1_DEV2_ID)) + response = context_client_grpc.GetLink(LinkId(**LINK_DEV1_DEV2_ID)) assert response.link_id.link_uuid.uuid == LINK_DEV1_DEV2_UUID assert len(response.link_endpoint_ids) == 2 # ----- List when the object exists -------------------------------------------------------------------------------- - response = context_client.ListLinkIds(Empty()) + response = context_client_grpc.ListLinkIds(Empty()) assert len(response.link_ids) == 1 assert response.link_ids[0].link_uuid.uuid == LINK_DEV1_DEV2_UUID - response = context_client.ListLinks(Empty()) + response = context_client_grpc.ListLinks(Empty()) assert len(response.links) == 1 assert response.links[0].link_id.link_uuid.uuid == LINK_DEV1_DEV2_UUID assert len(response.links[0].link_endpoint_ids) == 2 @@ -610,7 +683,7 @@ def test_link( # ----- Create object relation ------------------------------------------------------------------------------------- TOPOLOGY_WITH_LINK = copy.deepcopy(TOPOLOGY) TOPOLOGY_WITH_LINK['link_ids'].append(LINK_DEV1_DEV2_ID) - response = context_client.SetTopology(Topology(**TOPOLOGY_WITH_LINK)) + response = context_client_grpc.SetTopology(Topology(**TOPOLOGY_WITH_LINK)) assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID @@ -622,7 +695,7 @@ def test_link( assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID # ----- Check relation was created --------------------------------------------------------------------------------- - response = context_client.GetTopology(TopologyId(**TOPOLOGY_ID)) + response = context_client_grpc.GetTopology(TopologyId(**TOPOLOGY_ID)) assert response.topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID assert response.topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID assert len(response.device_ids) == 2 @@ -639,11 +712,11 @@ def test_link( assert len(db_entries) == 48 # ----- Remove the object ------------------------------------------------------------------------------------------ - context_client.RemoveLink(LinkId(**LINK_DEV1_DEV2_ID)) - context_client.RemoveDevice(DeviceId(**DEVICE1_ID)) - context_client.RemoveDevice(DeviceId(**DEVICE2_ID)) - context_client.RemoveTopology(TopologyId(**TOPOLOGY_ID)) - context_client.RemoveContext(ContextId(**CONTEXT_ID)) + context_client_grpc.RemoveLink(LinkId(**LINK_DEV1_DEV2_ID)) + context_client_grpc.RemoveDevice(DeviceId(**DEVICE1_ID)) + context_client_grpc.RemoveDevice(DeviceId(**DEVICE2_ID)) + context_client_grpc.RemoveTopology(TopologyId(**TOPOLOGY_ID)) + context_client_grpc.RemoveContext(ContextId(**CONTEXT_ID)) # ----- Check remove event ----------------------------------------------------------------------------------------- event = events_collector.get_event(block=True) @@ -684,8 +757,8 @@ def test_link( assert len(db_entries) == 0 -def test_service( - context_client : ContextClient, # pylint: disable=redefined-outer-name +def test_grpc_service( + context_client_grpc : ContextClient, # pylint: disable=redefined-outer-name context_db_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name context_database = context_db_mb[0] @@ -693,21 +766,21 @@ def test_service( context_database.clear_all() # ----- Initialize the EventsCollector ----------------------------------------------------------------------------- - events_collector = EventsCollector(context_client) + events_collector = EventsCollector(context_client_grpc) events_collector.start() # ----- Prepare dependencies for the test and capture related events ----------------------------------------------- - response = context_client.SetContext(Context(**CONTEXT)) + response = context_client_grpc.SetContext(Context(**CONTEXT)) assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID - response = context_client.SetTopology(Topology(**TOPOLOGY)) + response = context_client_grpc.SetTopology(Topology(**TOPOLOGY)) assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID - response = context_client.SetDevice(Device(**DEVICE1)) + response = context_client_grpc.SetDevice(Device(**DEVICE1)) assert response.device_uuid.uuid == DEVICE1_UUID - response = context_client.SetDevice(Device(**DEVICE2)) + response = context_client_grpc.SetDevice(Device(**DEVICE2)) assert response.device_uuid.uuid == DEVICE2_UUID event = events_collector.get_event(block=True) @@ -733,15 +806,15 @@ def test_service( # ----- Get when the object does not exist ------------------------------------------------------------------------- with pytest.raises(grpc.RpcError) as e: - context_client.GetService(ServiceId(**SERVICE_DEV1_DEV2_ID)) + context_client_grpc.GetService(ServiceId(**SERVICE_DEV1_DEV2_ID)) assert e.value.code() == grpc.StatusCode.NOT_FOUND assert e.value.details() == 'Service({:s}/{:s}) not found'.format(DEFAULT_CONTEXT_UUID, SERVICE_DEV1_DEV2_UUID) # ----- List when the object does not exist ------------------------------------------------------------------------ - response = context_client.ListServiceIds(ContextId(**CONTEXT_ID)) + response = context_client_grpc.ListServiceIds(ContextId(**CONTEXT_ID)) assert len(response.service_ids) == 0 - response = context_client.ListServices(ContextId(**CONTEXT_ID)) + response = context_client_grpc.ListServices(ContextId(**CONTEXT_ID)) assert len(response.services) == 0 # ----- Dump state of database before create the object ------------------------------------------------------------ @@ -753,10 +826,25 @@ def test_service( assert len(db_entries) == 38 # ----- Create the object ------------------------------------------------------------------------------------------ - response = context_client.SetService(Service(**SERVICE_DEV1_DEV2)) + with pytest.raises(grpc.RpcError) as e: + WRONG_SERVICE = copy.deepcopy(SERVICE_DEV1_DEV2) + WRONG_SERVICE['service_endpoint_ids'][0]\ + ['topology_id']['context_id']['context_uuid']['uuid'] = 'wrong-context-uuid' + context_client_grpc.SetService(Service(**WRONG_SERVICE)) + assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT + msg = 'request.service_endpoint_ids[0].topology_id.context_id.context_uuid.uuid(wrong-context-uuid) is invalid; '\ + 'should be == request.service_id.context_id.context_uuid.uuid(admin)' + assert e.value.details() == msg + + response = context_client_grpc.SetService(Service(**SERVICE_DEV1_DEV2)) assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID assert response.service_uuid.uuid == SERVICE_DEV1_DEV2_UUID + CONTEXT_WITH_SERVICE = copy.deepcopy(CONTEXT) + CONTEXT_WITH_SERVICE['service_ids'].append(SERVICE_DEV1_DEV2_ID) + response = context_client_grpc.SetContext(Context(**CONTEXT_WITH_SERVICE)) + assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # ----- Check create event ----------------------------------------------------------------------------------------- event = events_collector.get_event(block=True) assert isinstance(event, ServiceEvent) @@ -764,8 +852,13 @@ def test_service( assert event.service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID assert event.service_id.service_uuid.uuid == SERVICE_DEV1_DEV2_UUID + event = events_collector.get_event(block=True) + assert isinstance(event, ContextEvent) + assert event.event.event_type == EventTypeEnum.EVENTTYPE_UPDATE + assert event.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # ----- Update the object ------------------------------------------------------------------------------------------ - response = context_client.SetService(Service(**SERVICE_DEV1_DEV2)) + response = context_client_grpc.SetService(Service(**SERVICE_DEV1_DEV2)) assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID assert response.service_uuid.uuid == SERVICE_DEV1_DEV2_UUID @@ -785,7 +878,7 @@ def test_service( assert len(db_entries) == 57 # ----- Get when the object exists --------------------------------------------------------------------------------- - response = context_client.GetService(ServiceId(**SERVICE_DEV1_DEV2_ID)) + response = context_client_grpc.GetService(ServiceId(**SERVICE_DEV1_DEV2_ID)) assert response.service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID assert response.service_id.service_uuid.uuid == SERVICE_DEV1_DEV2_UUID assert response.service_type == ServiceTypeEnum.SERVICETYPE_L3NM @@ -795,12 +888,12 @@ def test_service( assert len(response.service_config.config_rules) == 3 # ----- List when the object exists -------------------------------------------------------------------------------- - response = context_client.ListServiceIds(ContextId(**CONTEXT_ID)) + response = context_client_grpc.ListServiceIds(ContextId(**CONTEXT_ID)) assert len(response.service_ids) == 1 assert response.service_ids[0].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID assert response.service_ids[0].service_uuid.uuid == SERVICE_DEV1_DEV2_UUID - response = context_client.ListServices(ContextId(**CONTEXT_ID)) + response = context_client_grpc.ListServices(ContextId(**CONTEXT_ID)) assert len(response.services) == 1 assert response.services[0].service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID assert response.services[0].service_id.service_uuid.uuid == SERVICE_DEV1_DEV2_UUID @@ -811,11 +904,11 @@ def test_service( assert len(response.services[0].service_config.config_rules) == 3 # ----- Remove the object ------------------------------------------------------------------------------------------ - context_client.RemoveService(ServiceId(**SERVICE_DEV1_DEV2_ID)) - context_client.RemoveDevice(DeviceId(**DEVICE1_ID)) - context_client.RemoveDevice(DeviceId(**DEVICE2_ID)) - context_client.RemoveTopology(TopologyId(**TOPOLOGY_ID)) - context_client.RemoveContext(ContextId(**CONTEXT_ID)) + context_client_grpc.RemoveService(ServiceId(**SERVICE_DEV1_DEV2_ID)) + context_client_grpc.RemoveDevice(DeviceId(**DEVICE1_ID)) + context_client_grpc.RemoveDevice(DeviceId(**DEVICE2_ID)) + context_client_grpc.RemoveTopology(TopologyId(**TOPOLOGY_ID)) + context_client_grpc.RemoveContext(ContextId(**CONTEXT_ID)) # ----- Check remove event ----------------------------------------------------------------------------------------- event = events_collector.get_event(block=True) @@ -854,3 +947,113 @@ def test_service( LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover LOGGER.info('-----------------------------------------------------------') assert len(db_entries) == 0 + + +# ----- Test REST API methods ------------------------------------------------------------------------------------------ + +def test_rest_populate_database( + context_db_mb : Tuple[Database, MessageBroker], # pylint: disable=redefined-outer-name + context_service_grpc : ContextService # pylint: disable=redefined-outer-name + ): + database = context_db_mb[0] + database.clear_all() + populate('127.0.0.1', GRPC_PORT) + + +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('admin') + 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('admin') + 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('admin') + 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('admin') + topology_uuid = urllib.parse.quote('admin') + 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('admin') + 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('admin') + 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('admin') + service_uuid = urllib.parse.quote('SVC:DEV1/EP100-DEV2/EP100', safe='') + reply = do_rest_request('/context/{:s}/service/{:s}'.format(context_uuid, service_uuid)) + validate_service(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('DEV1', 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('DEV1/EP2 ==> DEV2/EP1', safe='') + reply = do_rest_request('/link/{:s}'.format(link_uuid)) + validate_link(reply) + + +# ----- Test misc. Context internal tools ------------------------------------------------------------------------------ + +def test_tools_fast_string_hasher(): + with pytest.raises(TypeError) as e: + fast_hasher(27) + assert str(e.value) == "data(27) must be " + FASTHASHER_DATA_ACCEPTED_FORMAT + ", found <class 'int'>" + + with pytest.raises(TypeError) as e: + fast_hasher({27}) + assert str(e.value) == "data({27}) must be " + FASTHASHER_DATA_ACCEPTED_FORMAT + ", found <class 'set'>" + + with pytest.raises(TypeError) as e: + fast_hasher({'27'}) + assert str(e.value) == "data({'27'}) must be " + FASTHASHER_DATA_ACCEPTED_FORMAT + ", found <class 'set'>" + + with pytest.raises(TypeError) as e: + fast_hasher([27]) + assert str(e.value) == "data[0](27) must be " + FASTHASHER_ITEM_ACCEPTED_FORMAT + ", found <class 'int'>" + + fast_hasher('hello-world') + fast_hasher('hello-world'.encode('UTF-8')) + fast_hasher(['hello', 'world']) + fast_hasher(('hello', 'world')) + fast_hasher(['hello'.encode('UTF-8'), 'world'.encode('UTF-8')]) + fast_hasher(('hello'.encode('UTF-8'), 'world'.encode('UTF-8'))) diff --git a/src/context/tests/test_unitary_fast_hasher.py b/src/context/tests/test_unitary_fast_hasher.py deleted file mode 100644 index 94ce3aebe21752c3311a38bb92a285aa83e337f5..0000000000000000000000000000000000000000 --- a/src/context/tests/test_unitary_fast_hasher.py +++ /dev/null @@ -1,30 +0,0 @@ -import logging, pytest -from context.service.database.Tools import ( - FASTHASHER_DATA_ACCEPTED_FORMAT, FASTHASHER_ITEM_ACCEPTED_FORMAT, fast_hasher) - -LOGGER = logging.getLogger(__name__) -LOGGER.setLevel(logging.DEBUG) - -def test_fast_hasher(): - with pytest.raises(TypeError) as e: - fast_hasher(27) - assert str(e.value) == "data(27) must be " + FASTHASHER_DATA_ACCEPTED_FORMAT + ", found <class 'int'>" - - with pytest.raises(TypeError) as e: - fast_hasher({27}) - assert str(e.value) == "data({27}) must be " + FASTHASHER_DATA_ACCEPTED_FORMAT + ", found <class 'set'>" - - with pytest.raises(TypeError) as e: - fast_hasher({'27'}) - assert str(e.value) == "data({'27'}) must be " + FASTHASHER_DATA_ACCEPTED_FORMAT + ", found <class 'set'>" - - with pytest.raises(TypeError) as e: - fast_hasher([27]) - assert str(e.value) == "data[0](27) must be " + FASTHASHER_ITEM_ACCEPTED_FORMAT + ", found <class 'int'>" - - fast_hasher('hello-world') - fast_hasher('hello-world'.encode('UTF-8')) - fast_hasher(['hello', 'world']) - fast_hasher(('hello', 'world')) - fast_hasher(['hello'.encode('UTF-8'), 'world'.encode('UTF-8')]) - fast_hasher(('hello'.encode('UTF-8'), 'world'.encode('UTF-8'))) diff --git a/src/context/tests/test_unitary_rest.py b/src/context/tests/test_unitary_rest.py deleted file mode 100644 index 01c58581a274ac74aa8162d06d4551fbed3a77fa..0000000000000000000000000000000000000000 --- a/src/context/tests/test_unitary_rest.py +++ /dev/null @@ -1,57 +0,0 @@ -import logging, pytest, requests, time -from google.protobuf.json_format import MessageToDict -from common.orm.Database import Database -from common.orm.Factory import get_database_backend, BackendEnum -from context.proto.context_pb2 import Context, Topology -from context.Config import RESTAPI_SERVICE_PORT, RESTAPI_BASE_URL -from context.service.rest_server.Server import Server -from context.service.rest_server.resources.Context import Context -#from .populate_database import populate_example - -LOGGER = logging.getLogger(__name__) -LOGGER.setLevel(logging.DEBUG) - -RESTAPI_PORT = 10000 + RESTAPI_SERVICE_PORT # avoid privileged ports - -SCENARIOS = [ - (BackendEnum.INMEMORY, {}), - #(BackendEnum.REDIS, { - # 'REDIS_SERVICE_HOST': '10.1.7.194', - # 'REDIS_SERVICE_PORT': 30283, - # 'REDIS_DATABASE_ID': 0, - #}), -] - -@pytest.fixture(scope='session', ids=[str(scenario[0].value) for scenario in SCENARIOS], params=SCENARIOS) -def context_database(request): - backend,settings = request.param - LOGGER.info('Running fixture with backend={}, settings={}...'.format(str(backend), str(settings))) - database_backend = get_database_backend(backend=backend, **settings) - _database = Database(database_backend) - return _database - -@pytest.fixture(scope='session') -def context_service_rest(context_database : Database): # pylint: disable=redefined-outer-name - _rest_server = Server(port=RESTAPI_PORT, base_url=RESTAPI_BASE_URL) - _rest_server.add_resource(Context, '/context', endpoint='api.context', resource_class_args=(context_database,)) - _rest_server.start() - time.sleep(1) # bring time for the server to start - yield _rest_server - _rest_server.shutdown() - _rest_server.join() - -def test_get_topology_completed_rest_api(context_service_rest : Server): # pylint: disable=redefined-outer-name - # should work - request_url = 'http://127.0.0.1:{}{}/context'.format(RESTAPI_PORT, RESTAPI_BASE_URL) - LOGGER.warning('Request: GET {}'.format(str(request_url))) - reply = requests.get(request_url) - LOGGER.warning('Reply: {}'.format(str(reply.text))) - assert reply.status_code == 200, 'Reply failed with code {}'.format(reply.status_code) - json_reply = reply.json() - topology = MessageToDict( - Topology(**json_reply['topologies'][0]), - including_default_value_fields=True, preserving_proto_field_name=True, - use_integers_for_enums=False) - validate_topology(topology) - validate_topology_has_devices(topology) - validate_topology_has_links(topology)