Commit 715cdd02 authored by Lluis Gifre Renom's avatar Lluis Gifre Renom
Browse files

End-to-end Test - Ryu-OpenFlow:

- Added HTTP server to Mininet to accept ping requests between hosts
- Updated CI/CD pipeline
parent a7407358
Loading
Loading
Loading
Loading
+17 −4
Original line number Diff line number Diff line
@@ -165,7 +165,7 @@ end2end_test ryu-openflow:
    # Deploy Mininet
    #  --ulimit memlock=-1:-1 --ulimit nofile=65536:65536 --ulimit nproc=65536:65536
    - >
      docker run --detach --name mininet --network ryu-of-br --ip 172.254.252.11
      docker run --detach --name mininet --network ryu-of-br --ip 172.254.252.11 --publish 5000
      --privileged --volume /lib/modules:/lib/modules
      "${CI_REGISTRY_IMAGE}/${TEST_NAME}-mininet:${IMAGE_TAG}"

@@ -254,12 +254,25 @@ end2end_test ryu-openflow:
    - docker exec mininet ovs-ofctl dump-flows s4
    - docker exec mininet ovs-ofctl dump-flows s5

    # Run end-to-end test: test no connectivity with ping
    - export TEST_H1_H2=$(curl -s http://172.254.252.10:5000/ping?source=h1&target=h2&count=3)
    - echo $TEST_H1_H2
    - echo $TEST_H1_H2 | grep -E '3 packets transmitted, 0 received, 100\% packet loss'

    - export TEST_H1_H3=$(curl -s http://172.254.252.10:5000/ping?source=h1&target=h3&count=3)
    - echo $TEST_H1_H3
    - echo $TEST_H1_H3 | grep -E '3 packets transmitted, 0 received, 100\% packet loss'

    - export TEST_H1_H4=$(curl -s http://172.254.252.10:5000/ping?source=h1&target=h4&count=3)
    - echo $TEST_H1_H4
    - echo $TEST_H1_H4 | grep -E '3 packets transmitted, 0 received, 100\% packet loss'

    ## Run end-to-end test: configure service IETF
    #- >
    #  docker run -t --rm --name ${TEST_NAME} --network=host 
    #  --volume "$PWD/tfs_runtime_env_vars.sh:/var/teraflow/tfs_runtime_env_vars.sh"
    #  --volume "$PWD/src/tests/${TEST_NAME}:/opt/results"
    #  ${CI_REGISTRY_IMAGE}/${TEST_NAME}:${IMAGE_TAG} /var/teraflow/run-service-ietf-create.sh
    #  "${CI_REGISTRY_IMAGE}/${TEST_NAME}-test:${IMAGE_TAG}" /var/teraflow/run-service-ietf-create.sh

    # Dump configuration of the switches (OpenFlow rules configured) (after configure IETF service)
    - docker exec mininet bash -c "ovs-vsctl show"
@@ -294,7 +307,7 @@ end2end_test ryu-openflow:
    #  docker run -t --rm --name ${TEST_NAME} --network=host 
    #  --volume "$PWD/tfs_runtime_env_vars.sh:/var/teraflow/tfs_runtime_env_vars.sh"
    #  --volume "$PWD/src/tests/${TEST_NAME}:/opt/results"
    #  ${CI_REGISTRY_IMAGE}/${TEST_NAME}:${IMAGE_TAG} /var/teraflow/run-service-ietf-remove.sh
    #  "${CI_REGISTRY_IMAGE}/${TEST_NAME}-test:${IMAGE_TAG}" /var/teraflow/run-service-ietf-remove.sh

    # Dump configuration of the switches (OpenFlow rules configured) (after deconfigure IETF service)
    - docker exec mininet bash -c "ovs-vsctl show"
@@ -309,7 +322,7 @@ end2end_test ryu-openflow:
      docker run -t --rm --name ${TEST_NAME} --network=host 
      --volume "$PWD/tfs_runtime_env_vars.sh:/var/teraflow/tfs_runtime_env_vars.sh"
      --volume "$PWD/src/tests/${TEST_NAME}:/opt/results"
      ${CI_REGISTRY_IMAGE}/${TEST_NAME}:${IMAGE_TAG} /var/teraflow/run-cleanup.sh
      "${CI_REGISTRY_IMAGE}/${TEST_NAME}-test:${IMAGE_TAG}" /var/teraflow/run-cleanup.sh

  after_script:
    # Dump configuration of the switches (OpenFlow rules configured) (on after_script)
+1 −1
Original line number Diff line number Diff line
@@ -31,6 +31,6 @@ RUN apt-get --yes --quiet --quiet update && \
COPY src/tests/ryu-openflow/mininet/custom_pentagon_topology.py /opt/custom_pentagon_topology.py
COPY src/tests/ryu-openflow/mininet/mininet-entrypoint.sh /mininet-entrypoint.sh

EXPOSE 6633 6653 6640
EXPOSE 6633 6653 6640 5000

ENTRYPOINT ["/mininet-entrypoint.sh"]
+98 −8
Original line number Diff line number Diff line
@@ -13,11 +13,13 @@
# limitations under the License.


import time
import json, re, socketserver, threading, time
from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import parse_qs, urlparse
from mininet.topo import Topo
from mininet.net import Mininet
from mininet.node import RemoteController
from mininet.cli import CLI
#from mininet.cli import CLI
from mininet.link import TCLink

class PentagonTopo(Topo):
@@ -44,6 +46,85 @@ class PentagonTopo(Topo):
        self.addLink(h3, sw5)
        self.addLink(h4, sw5)


def parse_ping_output(output: str):
    match = re.search(r'(\d+) packets transmitted, (\d+) received', output)
    if not match:
        return 0, 0, 0.0
    transmitted = int(match.group(1))
    received = int(match.group(2))
    ratio = (received / transmitted) if transmitted else 0.0
    return transmitted, received, ratio


def build_ping_handler(net: Mininet):
    """
    Create a HTTP request handler that performs pings between hosts.
    """
    class PingHandler(BaseHTTPRequestHandler):
        def log_message(self, *args, **kwargs):
            # Silence default stdout logging.
            return

        def _send_json(self, status: int, payload: dict):
            body = json.dumps(payload).encode()
            self.send_response(status)
            self.send_header('Content-Type', 'application/json')
            self.send_header('Content-Length', str(len(body)))
            self.end_headers()
            self.wfile.write(body)

        def do_GET(self):
            parsed = urlparse(self.path)
            if parsed.path != '/ping':
                self._send_json(404, {'error': 'not found'})
                return

            params = parse_qs(parsed.query)
            source = params.get('source', [None])[0]
            target = params.get('target', [None])[0]
            count_val = params.get('count', ['3'])[0]

            if not source or not target:
                self._send_json(400, {'error': 'source and target query parameters are required'})
                return

            try:
                count = int(count_val)
                if count <= 0:
                    raise ValueError()
            except ValueError:
                self._send_json(400, {'error': 'count must be a positive integer'})
                return

            try:
                src_host = net.get(source)
                dst_host = net.get(target)
            except KeyError as exc:
                self._send_json(404, {'error': f'Unknown host: {exc}'})
                return

            destination_ip = dst_host.IP()
            output = src_host.cmd(f'ping -c {count} {destination_ip}')
            transmitted, received, ratio = parse_ping_output(output)

            self._send_json(200, {
                'source': source,
                'target': target,
                'destination_ip': destination_ip,
                'count': count,
                'packets_transmitted': transmitted,
                'packets_received': received,
                'success_ratio': ratio,
                'raw_output': output
            })

    return PingHandler


class ThreadingHTTPServer(socketserver.ThreadingMixIn, HTTPServer):
    daemon_threads = True

if __name__ == '__main__':
    topo = PentagonTopo()
    ctrl = RemoteController('ryu', ip='172.254.252.10', port=6653)
@@ -56,10 +137,19 @@ if __name__ == '__main__':
    net.start()
    net.staticArp()

    ping_handler = build_http_handler(net)
    http_server = ThreadingHTTPServer(('0.0.0.0', 5000), ping_handler)
    server_thread = threading.Thread(target=http_server.serve_forever, daemon=True)
    server_thread.start()

    print('Custom Pentagon Topology is up with static ARP.')
    #CLI(net)
    print('HTTP ping server running at http://0.0.0.0:5000/ping?source=h1&target=h2&count=3')

    try:
        while True:
            time.sleep(60)
    #while True:
    #time.sleep(60)
    #net.stop()
    except KeyboardInterrupt:
        print('Stopping Mininet...')
        http_server.shutdown()
        http_server.server_close()
        net.stop()