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

PathComp Frontend:

- added test-deploy.sh script
- partial arrangement of GitLab CI/CD
- added configuration constants
- arranged Dockerfile
- corrected imports of proto Python files
- implemented composition of backend request
parent 9d31e1d1
Loading
Loading
Loading
Loading
+31 −6
Original line number Diff line number Diff line
@@ -13,15 +13,15 @@
# limitations under the License.

# Build, tag and push the Docker image to the GitLab registry
build pathcomp:
build pathcomp_frontend:
  variables:
    IMAGE_NAME: 'pathcomp' # name of the microservice
    IMAGE_NAME: 'pathcomp_frontend' # name of the microservice
    IMAGE_TAG: 'latest' # tag of the container image (production, development, etc)
  stage: build
  before_script:
    - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
  script:
    - docker build -t "$IMAGE_NAME:$IMAGE_TAG" -f ./src/$IMAGE_NAME/Dockerfile .
    - docker build -t "$IMAGE_NAME:$IMAGE_TAG" -f ./src/$IMAGE_NAME/frontend/Dockerfile .
    - docker tag "$IMAGE_NAME:$IMAGE_TAG" "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG"
    - docker push "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG"
  after_script:
@@ -32,9 +32,34 @@ build pathcomp:
    - changes:
      - src/common/**/*.py
      - proto/*.proto
      - src/$IMAGE_NAME/**/*.{py,in,yml}
      - src/$IMAGE_NAME/Dockerfile
      - src/$IMAGE_NAME/tests/*.py
      - src/$IMAGE_NAME/frontend/**/*.{py,in,yml}
      - src/$IMAGE_NAME/frontend/Dockerfile
      - src/$IMAGE_NAME/frontend/tests/*.py
      - manifests/${IMAGE_NAME}service.yaml
      - .gitlab-ci.yml

build pathcomp_backend:
  variables:
    IMAGE_NAME: 'pathcomp_backend' # name of the microservice
    IMAGE_TAG: 'latest' # tag of the container image (production, development, etc)
  stage: build
  before_script:
    - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
  script:
    - docker build -t "$IMAGE_NAME:$IMAGE_TAG" -f ./src/$IMAGE_NAME/backend/Dockerfile .
    - docker tag "$IMAGE_NAME:$IMAGE_TAG" "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG"
    - docker push "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG"
  after_script:
    - docker images --filter="dangling=true" --quiet | xargs -r docker rmi
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && ($CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH)'
    - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "develop"'
    - changes:
      - proto/*.proto
      - src/$IMAGE_NAME/.gitlab-ci.yml
      - src/$IMAGE_NAME/backend/**/*.{c,h,conf}
      - src/$IMAGE_NAME/backend/Makefile
      - src/$IMAGE_NAME/backend/Dockerfile
      - manifests/${IMAGE_NAME}service.yaml
      - .gitlab-ci.yml

+4 −0
Original line number Diff line number Diff line
@@ -11,3 +11,7 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

BACKEND_URL  = 'http://{:s}:{:d}/pathComp/api/v1/compRoute'
BACKEND_HOST = '172.28.0.2'
BACKEND_PORT = 8081
+2 −2
Original line number Diff line number Diff line
@@ -56,7 +56,7 @@ RUN find . -type f -exec sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' {} \;
# Create component sub-folders, get specific Python packages
RUN mkdir -p /var/teraflow/pathcomp
WORKDIR /var/teraflow/pathcomp
COPY src/pathcomp/requirements.in requirements.in
COPY src/pathcomp/frontend/requirements.in requirements.in
RUN pip-compile --quiet --output-file=requirements.txt requirements.in
RUN python3 -m pip install -r requirements.txt

@@ -66,4 +66,4 @@ COPY src/context/. context/
COPY src/pathcomp/. pathcomp/

# Start the service
ENTRYPOINT ["python", "-m", "pathcomp.service"]
ENTRYPOINT ["python", "-m", "pathcomp.frontend.service"]
+1 −1
Original line number Diff line number Diff line
@@ -15,7 +15,7 @@
from common.Constants import ServiceNameEnum
from common.Settings import get_service_port_grpc
from common.tools.service.GenericGrpcService import GenericGrpcService
from pathcomp.proto.pathcomp_pb2_grpc import add_PathCompServiceServicer_to_server
from common.proto.pathcomp_pb2_grpc import add_PathCompServiceServicer_to_server
from .PathCompServiceServicerImpl import PathCompServiceServicerImpl

class PathCompService(GenericGrpcService):
+52 −15
Original line number Diff line number Diff line
@@ -12,14 +12,16 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import grpc, json, logging, requests, uuid
from typing import List
import grpc, logging, uuid
from common.proto.context_pb2 import Connection, Empty, EndPointId
from common.proto.pathcomp_pb2 import PathCompReply, PathCompRequest
from common.proto.pathcomp_pb2_grpc import PathCompServiceServicer
from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method
from common.tools.grpc.Tools import grpc_message_to_json, grpc_message_to_json_string
from context.client.ContextClient import ContextClient
from pathcomp.frontend.Config import BACKEND_HOST, BACKEND_PORT, BACKEND_URL
from pathcomp.frontend.service.tools.ComposeRequest import compose_device, compose_link, compose_service

LOGGER = logging.getLogger(__name__)

@@ -36,28 +38,63 @@ class PathCompServiceServicerImpl(PathCompServiceServicer):
    def Compute(self, request : PathCompRequest, context : grpc.ServicerContext) -> PathCompReply:
        LOGGER.info('[Compute] begin ; request = {:s}'.format(grpc_message_to_json_string(request)))

        algorithm = request.WhichOneof('algorithm')
        if algorithm == 'shortest_path':
            # no attributes
            pass
        elif algorithm == 'k_shortest_path':
            k_inspection = request.k_shortest_path.k_inspection
            k_return = request.k_shortest_path.k_return
        else:
            raise NotImplementedError('Unsupported Algorithm: {:s}'.format(str(algorithm)))

        context_client = ContextClient()

        algorithm = {'id': 'KSP', 'sync': False, 'k_paths': k_return}
        service_list = [
            compose_service(grpc_service, algorithm)
            for grpc_service in request.services
        ]

        # TODO: consider filtering resources

        grpc_contexts = context_client.ListContexts(Empty())
        #grpc_contexts = context_client.ListContexts(Empty())
        #for grpc_context in grpc_contexts.contexts:
        #    # TODO: add context to request
        #    grpc_topologies = context_client.ListTopologies(grpc_context.context_id)
        #    for grpc_topology in grpc_topologies.topologies:    #pylint: disable=unused-variable
        #        # TODO: add topology to request
        #        pass

        grpc_devices = context_client.ListDevices(Empty())
        device_list = [
            compose_device(grpc_device)
            for grpc_device in grpc_devices.devices
        ]

        grpc_links = context_client.ListLinks(Empty())
        for grpc_context in grpc_contexts.contexts:
            # TODO: add context to request
            grpc_topologies = context_client.ListTopologies(grpc_context.context_id)
            for grpc_topology in grpc_topologies.topologies:    #pylint: disable=unused-variable
                # TODO: add topology to request
                pass
        for grpc_device in grpc_devices.devices:                #pylint: disable=unused-variable
            # TODO: add device to request
            pass
        for grpc_link in grpc_links.links:                      #pylint: disable=unused-variable
            # TODO: add link to request
            pass
        link_list = [
            compose_link(grpc_link)
            for grpc_link in grpc_links.links
        ]

        request = {
            'serviceList': service_list,
            'deviceList' : device_list,
            'linkList'   : link_list,
        }

        backend_url = BACKEND_URL.format(BACKEND_HOST, BACKEND_PORT)
        reply = requests.post(backend_url, json=request)
        if reply.status_code not in {requests.codes.ok}:
            raise Exception('Backend error({:s}) for request({:s})'.format(
                str(reply.content.decode('UTF-8')), json.dumps(request, sort_keys=True)))
        LOGGER.info('status_code={:s} reply={:s}'.format(
            str(reply.status_code), str(reply.content.decode('UTF-8'))))



        reply = PathCompReply()
        # TODO: issue path computation request
        # TODO: compose reply populating reply.services and reply.connections

        for service in request.services:
Loading