diff --git a/.gitignore b/.gitignore index 4fbcd09..d96acf3 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,5 @@ MANIFEST .venv*/ .conda*/ .python-version + +batfish-data/ diff --git a/Dockerfile b/Dockerfile index 1ef248e..8c1a853 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ FROM python:3.10.7 COPY / /tmp/alto RUN rm -rf /tmp/alto/.git && \ - pip install redis geoip2 && \ + pip install redis geoip2 pybatfish && \ pip install /tmp/alto && \ rm -rf /tmp/alto diff --git a/README.rst b/README.rst index eaa0c85..d688a92 100644 --- a/README.rst +++ b/README.rst @@ -77,6 +77,7 @@ required packages: $ pip3 install . $ pip3 install redis $ gunicorn -b 0.0.0.0:8000 --reload --preload --capture-output --error-logfile /tmp/openalto-error.log --access-logfile /tmp/openalto-access.log alto.server.northbound.wsgi -D + $ python3 -m alto.agent.manage --pid /tmp start -c etc/batfish.json -D batfish $ python3 -m alto.agent.manage --pid /tmp start -c etc/lg-agent.json -D cernlg $ python3 -m alto.agent.manage --pid /tmp start -c etc/cric-agent.json -D cric $ python3 -m alto.agent.manage --pid /tmp start -c etc/geoip-delegate-agent.json -D geoip diff --git a/docker-batfish/Dockerfile b/docker-batfish/Dockerfile new file mode 100644 index 0000000..a464dd6 --- /dev/null +++ b/docker-batfish/Dockerfile @@ -0,0 +1,14 @@ +FROM batfish/allinone:latest + +COPY . /tmp/alto +COPY ./docker-batfish/start.sh start.sh +RUN chmod +x start.sh +RUN apt-get install -y python3 python3-pip +RUN rm -rf /tmp/alto/.git && \ + pip install redis geoip2 pybatfish && \ + pip install /tmp/alto && \ + rm -rf /tmp/alto + +EXPOSE 8000 + +ENTRYPOINT [ "./start.sh" ] diff --git a/docker-batfish/start.sh b/docker-batfish/start.sh new file mode 100644 index 0000000..1d7eb9e --- /dev/null +++ b/docker-batfish/start.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# Start batfish-agent +python3 -m alto.agent.manage --pid /tmp start -c /etc/batfish-agent.json -D batfish & + +# Start batfish-server +./wrapper.sh & + +# Wait for any process to exit +wait -n + +# Exit with status of process that exited first +exit $? diff --git a/docker-compose.yml b/docker-compose.yml index 283a45e..7f7230e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,3 +1,4 @@ +version: '2.2' services: gateway: image: nginx @@ -31,6 +32,15 @@ services: entrypoint: python command: ["-m", "alto.agent.manage", "--pid", "/tmp", "start", "-c", "/etc/cric-agent.json", "-D", "cric"] network_mode: "service:alto-frontend" + alto-batfish-server-agent: + build: + context: . + dockerfile: docker-batfish/Dockerfile + volumes: + - ./etc/batfish-data:/data + - ./etc/batfish-agent.json:/etc/batfish-agent.json + - ./etc/alto.conf:/opt/alto/etc/alto.conf + network_mode: "service:alto-frontend" alto-db: image: redis network_mode: "service:alto-frontend" diff --git a/etc/batfish-agent.json b/etc/batfish-agent.json new file mode 100644 index 0000000..6777a91 --- /dev/null +++ b/etc/batfish-agent.json @@ -0,0 +1,5 @@ +{ + "namespace": "default", + "agent_class": "alto.agent.batfish.BatfishAgent", + "refresh_interval": 300 +} \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index a551c0f..0d8cc84 100644 --- a/setup.cfg +++ b/setup.cfg @@ -51,6 +51,7 @@ install_requires = dataclasses; python_version<="3.6" importlib-metadata; python_version<"3.8" requests + pybatfish pytricia Django djangorestframework diff --git a/src/alto/agent/batfish.py b/src/alto/agent/batfish.py new file mode 100644 index 0000000..fb34cd5 --- /dev/null +++ b/src/alto/agent/batfish.py @@ -0,0 +1,45 @@ +import logging +from pybatfish.client.session import Session +from pybatfish.datamodel import * +from pybatfish.datamodel.answer import * +from pybatfish.datamodel.flow import * +import time + +from alto.server.components.datasource import DBInfo, DataSourceAgent +from alto.server.components.db import ForwardingRule, Match, Action + +class BatfishAgent(DataSourceAgent): + """ + Class of data source agent for looking glass server. + """ + + def __init__(self, dbinfo: DBInfo, name: str, namespace='default', **cfg): + super().__init__(dbinfo, name, namespace) + + self.refresh_interval = cfg.get('refresh_interval', None) + + logging.info("Loading databases") + self.db = [ self.request_db(t) for t in ['forwarding', 'endpoint']] + + self.bf = Session(host="localhost") + + self.bf.init_snapshot('/data/live', name='live', overwrite=True) + self.bf.q.initIssues().answer() + + def update(self): + fib_trans = self.db[0].new_transaction() + results = self.bf.q.routes().answer().frame() + logging.info("RESULTS*****************************************" + str(results)) + for index in results.index: + pkt_match = Match(results["Network"][index]) + action = Action(results["Next_Hop_IP"][index]) + rule = ForwardingRule(pkt_match, action) + fib_trans.add_rule(results["Node"][index], rule) + fib_trans.commit() + + def run(self): + if self.refresh_interval is None: + self.refresh_interval = 60 + while True: + self.update() + time.sleep(self.refresh_interval)