Commit d6b2243c authored by Lluis Gifre Renom's avatar Lluis Gifre Renom
Browse files

NBI Component:

- Extended PrepareTestScenario to support multiple REST methods
- Updated unitary tests accordingly
- Renamed test_unitary to test_ietf_l2vpn
- Cosmetic changes in CI/CD pipeline
- Fixed Python dependency versions in requirements file
parent 1f1aaf44
Loading
Loading
Loading
Loading
+17 −3
Original line number Diff line number Diff line
@@ -48,15 +48,29 @@ unit_test nbi:
    - build nbi
  before_script:
    - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
    - if docker network list | grep teraflowbridge; then echo "teraflowbridge is already created"; else docker network create -d bridge teraflowbridge; fi
    - if docker container ls | grep $IMAGE_NAME; then docker rm -f $IMAGE_NAME; else echo "$IMAGE_NAME image is not in the system"; fi
    - >
      if docker network list | grep teraflowbridge; then
        echo "teraflowbridge is already created";
      else
        docker network create -d bridge teraflowbridge;
      fi
    - >
      if docker container ls | grep $IMAGE_NAME; then
        docker rm -f $IMAGE_NAME;
      else
        echo "$IMAGE_NAME image is not in the system";
      fi
  script:
    - docker pull "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG"
    - docker run --name $IMAGE_NAME -d -p 9090:9090 -v "$PWD/src/$IMAGE_NAME/tests:/opt/results" --network=teraflowbridge $CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG
    - sleep 5
    - docker ps -a
    - docker logs $IMAGE_NAME
    - docker exec -i $IMAGE_NAME bash -c "coverage run -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_unitary.py $IMAGE_NAME/tests/test_ietf_network.py --junitxml=/opt/results/${IMAGE_NAME}_report.xml"
    - >
      docker exec -i $IMAGE_NAME bash -c "coverage run -m pytest --log-level=INFO --verbose
      $IMAGE_NAME/tests/test_ietf_l2vpn.py
      $IMAGE_NAME/tests/test_ietf_network.py
      --junitxml=/opt/results/${IMAGE_NAME}_report.xml"
    - docker exec -i $IMAGE_NAME bash -c "coverage report --include='${IMAGE_NAME}/*' --show-missing"
  coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/'
  after_script:
+1 −1
Original line number Diff line number Diff line
@@ -17,7 +17,7 @@ Flask==2.1.3
Flask-HTTPAuth==4.5.0
Flask-RESTful==0.3.9
jsonschema==4.4.0
pyang
pyang==2.6.0
git+https://github.com/robshakir/pyangbind.git
requests==2.27.1
werkzeug==2.3.7
+63 −7
Original line number Diff line number Diff line
@@ -12,8 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import logging, os, pytest, requests, time
from typing import Dict, List, Optional, Set, Union
import enum, logging, os, pytest, requests, time
from typing import Any, Dict, List, Optional, Set, Union
from common.Constants import ServiceNameEnum
from common.Settings import (
    ENVVAR_SUFIX_SERVICE_HOST, ENVVAR_SUFIX_SERVICE_PORT_HTTP,
@@ -79,17 +79,73 @@ def slice_client(mock_service : MockService_Dependencies): # pylint: disable=red
    yield _client
    _client.close()

class RestRequestMethod(enum.Enum):
    GET    = 'get'
    POST   = 'post'
    PUT    = 'put'
    DELETE = 'delete'

EXPECTED_STATUS_CODES : Set[int] = {
    requests.codes['OK'        ],
    requests.codes['CREATED'   ],
    requests.codes['ACCEPTED'  ],
    requests.codes['NO_CONTENT'],
}

def do_rest_request(
    url : str, logger : Optional[logging.Logger] = None, timeout=10, expected_status_codes : Set[int] = {200}
) -> Union[Dict, List]:
    method : RestRequestMethod, url : str, body : Optional[Any] = None, timeout : int = 10,
    allow_redirects : bool = True, expected_status_codes : Set[int] = EXPECTED_STATUS_CODES,
    logger : Optional[logging.Logger] = None
) -> Optional[Union[Dict, List]]:
    base_url = get_service_baseurl_http(ServiceNameEnum.NBI) or ''
    request_url = 'http://{:s}:{:s}@{:s}:{:d}{:s}{:s}'.format(
        USERNAME, PASSWORD, LOCAL_HOST, NBI_SERVICE_PORT, str(base_url), url
    )
    if logger is not None:
        logger.warning('Request: GET {:s}'.format(str(request_url)))
    reply = requests.get(request_url, timeout=timeout)
        msg = 'Request: {:s} {:s}'.format(str(method.value).upper(), str(request_url))
        if body is not None: msg += ' body={:s}'.format(str(body))
        logger.warning(msg)
    reply = requests.request(method.value, request_url, timeout=timeout, json=body, allow_redirects=allow_redirects)
    if logger is not None:
        logger.warning('Reply: {:s}'.format(str(reply.text)))
    assert reply.status_code in expected_status_codes, 'Reply failed with status code {:d}'.format(reply.status_code)
    return reply.json()

    if reply.content and len(reply.content) > 0: return reply.json()
    return None

def do_rest_get_request(
    url : str, body : Optional[Any] = None, timeout : int = 10,
    allow_redirects : bool = True, expected_status_codes : Set[int] = EXPECTED_STATUS_CODES,
    logger : Optional[logging.Logger] = None
) -> Union[Dict, List]:
    return do_rest_request(
        RestRequestMethod.GET, url, body=body, timeout=timeout, allow_redirects=allow_redirects,
        expected_status_codes=expected_status_codes, logger=logger
    )

def do_rest_post_request(url : str, body : Optional[Any] = None, timeout : int = 10,
    allow_redirects : bool = True, expected_status_codes : Set[int] = EXPECTED_STATUS_CODES,
    logger : Optional[logging.Logger] = None
) -> Union[Dict, List]:
    return do_rest_request(
        RestRequestMethod.POST, url, body=body, timeout=timeout, allow_redirects=allow_redirects,
        expected_status_codes=expected_status_codes, logger=logger
    )

def do_rest_put_request(url : str, body : Optional[Any] = None, timeout : int = 10,
    allow_redirects : bool = True, expected_status_codes : Set[int] = EXPECTED_STATUS_CODES,
    logger : Optional[logging.Logger] = None
) -> Union[Dict, List]:
    return do_rest_request(
        RestRequestMethod.PUT, url, body=body, timeout=timeout, allow_redirects=allow_redirects,
        expected_status_codes=expected_status_codes, logger=logger
    )

def do_rest_delete_request(url : str, body : Optional[Any] = None, timeout : int = 10,
    allow_redirects : bool = True, expected_status_codes : Set[int] = EXPECTED_STATUS_CODES,
    logger : Optional[logging.Logger] = None
) -> Union[Dict, List]:
    return do_rest_request(
        RestRequestMethod.DELETE, url, body=body, timeout=timeout, allow_redirects=allow_redirects,
        expected_status_codes=expected_status_codes, logger=logger
    )
+30 −38
Original line number Diff line number Diff line
@@ -12,27 +12,29 @@
# 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
import logging, os, pytest, time, urllib
from common.Constants import DEFAULT_CONTEXT_NAME, DEFAULT_TOPOLOGY_NAME, 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)
    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 nbi.tests.PrepareTestScenario import do_rest_get_request
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)
    TOPOLOGY_ID, POLICY_RULE, POLICY_RULE_ID, POLICY_RULE_UUID
)


@pytest.fixture(scope='session')
@@ -100,129 +102,119 @@ def test_populate_database():
    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')
    reply = do_rest_get_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')
    reply = do_rest_get_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_NAME)
    reply = do_rest_request('/context/{:s}'.format(context_uuid))
    reply = do_rest_get_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_NAME)
    reply = do_rest_request('/context/{:s}/topology_ids'.format(context_uuid))
    reply = do_rest_get_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_NAME)
    reply = do_rest_request('/context/{:s}/topologies'.format(context_uuid))
    reply = do_rest_get_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_NAME)
    topology_uuid = urllib.parse.quote(DEFAULT_TOPOLOGY_NAME)
    reply = do_rest_request('/context/{:s}/topology/{:s}'.format(context_uuid, topology_uuid))
    reply = do_rest_get_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_NAME)
    reply = do_rest_request('/context/{:s}/service_ids'.format(context_uuid))
    reply = do_rest_get_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_NAME)
    reply = do_rest_request('/context/{:s}/services'.format(context_uuid))
    reply = do_rest_get_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_NAME)
    service_uuid = urllib.parse.quote(SERVICE_R1_R2_UUID, safe='')
    reply = do_rest_request('/context/{:s}/service/{:s}'.format(context_uuid, service_uuid))
    reply = do_rest_get_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_NAME)
    reply = do_rest_request('/context/{:s}/slice_ids'.format(context_uuid))
    reply = do_rest_get_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_NAME)
    reply = do_rest_request('/context/{:s}/slices'.format(context_uuid))
    reply = do_rest_get_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_NAME)
    slice_uuid = urllib.parse.quote(SLICE_R1_R3_UUID, safe='')
    reply = do_rest_request('/context/{:s}/slice/{:s}'.format(context_uuid, slice_uuid))
    reply = do_rest_get_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')
    reply = do_rest_get_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')
    reply = do_rest_get_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))
    reply = do_rest_get_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')
    reply = do_rest_get_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')
    reply = do_rest_get_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))
    reply = do_rest_get_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_NAME)
    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))
    reply = do_rest_get_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_NAME)
    service_uuid = urllib.parse.quote(SERVICE_R1_R3_UUID, safe='')
    reply = do_rest_request('/context/{:s}/service/{:s}/connections'.format(context_uuid, service_uuid))
    reply = do_rest_get_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))
    reply = do_rest_get_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')
    reply = do_rest_get_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')
    reply = do_rest_get_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))
    reply = do_rest_get_request('/policyrule/{:s}'.format(policyrule_uuid))
    #validate_policyrule(reply)
Loading