From fe9103c4a7217e8adf5d03eb2b5a8188b11af41b Mon Sep 17 00:00:00 2001 From: Alexandr Popov Date: Thu, 30 Oct 2025 22:43:25 +0300 Subject: [PATCH] Changes before the presentation at HL2025: * Add README.md with quick start guide * Add demo env configuration files and instructions * Added a conscious error about the absence of JQ in the tools * Fixed dependencies: Added PyYAML as a required dependency * Fixed a bug in accessing the timeout parameter * Fixed typo in evpn-connector config sample --- README.md | 136 ++++++++++++++++++- docs/demo/CONFIGURE.md | 62 +++++++++ docs/demo/compute1/evpn_connector.cfg | 39 ++++++ docs/demo/compute1/gobgp.conf | 15 ++ docs/demo/compute1/logging.yaml | 32 +++++ docs/demo/compute1/vm_conf/vm1.json | 11 ++ docs/demo/compute1/vm_conf/vm2.json | 11 ++ docs/demo/compute2/evpn_connector.cfg | 39 ++++++ docs/demo/compute2/gobgp.conf | 15 ++ docs/demo/compute2/logging.yaml | 32 +++++ docs/demo/compute2/vm_conf/vm3.json | 11 ++ etc/evpn_connector/evpn_connector.cfg.sample | 2 +- evpn_connector/common/metrics.py | 2 +- requirements.txt | 1 + tools/create_clients_dp.sh | 4 + 15 files changed, 409 insertions(+), 3 deletions(-) create mode 100644 docs/demo/CONFIGURE.md create mode 100644 docs/demo/compute1/evpn_connector.cfg create mode 100644 docs/demo/compute1/gobgp.conf create mode 100644 docs/demo/compute1/logging.yaml create mode 100644 docs/demo/compute1/vm_conf/vm1.json create mode 100644 docs/demo/compute1/vm_conf/vm2.json create mode 100644 docs/demo/compute2/evpn_connector.cfg create mode 100644 docs/demo/compute2/gobgp.conf create mode 100644 docs/demo/compute2/logging.yaml create mode 100644 docs/demo/compute2/vm_conf/vm3.json diff --git a/README.md b/README.md index b93358b..32fce87 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,137 @@ # evpn-connector -The main task of this daemon is to interact with the gobgp and ovs to provide a connection using the EVPN protocol +[![Python Version](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/) +[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE) + +A service for automating the management distributed switch or router based on EVPN standards + + +## Key Features + +* **EVPN Standards Support:** + * RFC 7432: BGP MPLS-Based Ethernet VPN + * RFC 8365: Network Virtualization Overlay + * RFC 7988: Ingress Replication Tunnels in Multicast VPN + * RFC 9135: Integrated Routing and Bridging in Ethernet VPN + * RFC 9136: IP Prefix Advertisement in Ethernet VPN +* **Hardware Integration:** Interaction with the hardware switch and routers via EVPN +* **GoBGP Integration:** Interaction with the gobgpd daemon to pull or push EVPN annouces +* **OpenvSwitch Management:** Direct management of bridges and flows within the OpenvSwitch system +* **Reconciliation Loop Design:** Built on a closed-loop control architecture to ensure fault tolerance and reduce the impact of external factors +* **Configurability:** Flexible setup via INI daemon configuration files and JSON clients configuration files + +## Documentation +The details of the EVPN's operation and evpn-connector daemon workflow are described in the [presentation](https://vkvideo.ru/video-164978780_456239752) (in Russian only). + +## Installation & Quick Start + +### Prerequisites + +* **Python 3.8 +* **System Dependencies:** + * `gobgp` (for interaction via BGP within the EVPN control plane) + * `openvswitch-switch` (for interaction via VXLAN within the EVPN data plane) +* **Permissions:** Requires `root` privileges for interaction with OpenvSwitch + + +1. **Install dependencies:** + + On Ubuntu/Debian: + ```bash + sudo apt update + sudo apt install openvswitch-switch openvswitch-common gobgpd + ``` + On CentOS/RHEL: + ```bash + sudo yum install gobgp openvswitch + # or for newer versions: + # sudo dnf install gobgp openvswitch + ``` +2. **(Optional) Install latest GoBGP from binary release:** + ```bash + wget https://github.com/osrg/gobgp/releases/download/v3.34.0/gobgp_3.34.0_linux_amd64.tar.gz + mkdir gobgp + tar -xf gobgp_3.34.0_linux_amd64.tar.gz -C gobgp/ + ``` +3. **(Recommended) Create a virtual environment:** + Recommended installation gobgp versions >= v3.34 + ```bash + python3 -m venv evpn + source evpn/bin/activate + pip install --upgrade pip setuptools + ``` +4. **Install evpn-connector:** + ```bash + pip install evpn-connector + ``` +### Configuration + +Before the first run, you need to create a configuration file. + +1. **GoBGP config:** Copy the example configuration file and adapt it to your environment. + ```bash + cp etc/gobgpd/gobgp.conf.sample /etc/gobgpd/gobgp.conf + ``` + It is necessary to configure gobgp so that all nodes can exchange announces with afi-safi **"l2evpn-evpn"** + +2. **evpn-connector config:** Copy the example configuration file and adapt it to your environment. + ```bash + mkdir /etc/evpn_connector/ + cp etc/evpn_connector/logging.yaml /etc/evpn_connector/logging.yaml + cp etc/evpn_connector/evpn_connector.cfg.sample /etc/evpn_connector/evpn_connector.conf + ``` +3. **Edit `/etc/evpn_connector/evpn_connector.conf`:** Specify the necessary parameters: + * `[gobgp] section`: Settings for connecting to the GoBGP daemon + * `[gobgp] source_ip`: Source IP address for all VXLAN packets + * `[ovs] section`: Settings for OpenvSwitch dataplane parameters + * `[ovs] switch_name`: Name of switch created in OvS + * `[daemon] section`: Settings for evpn_connector daemon + * `[daemon] configs_dir`: Path to client configs + +4. **Client configs:** Create clients configs. Example config: + * For L2 connectivity + ```json + { + "cfg_type": "l2", // Config type for L2 connectivity use "l2" + "mac": "36:e7:a5:7e:0c:81", // MAC address of client + "ip": "10.0.0.1", // IP address of client + "vni": 10, // VXLAN segment identifier + "ofport": 1000, // OpenFlow port number in current OpenvSwitch switch + "type": "flat", // OpenvSwitch port type. May be "flat" and "vlan" + "tag": 0, // OpenvSwitch port segment identifier. Ignored on "flat" + "imp_rt": ["65000:10"], // List of imported BGP Route Targets + "exp_rt": ["65000:10"] // List of exported BGP Route Targets + } + ``` + * For L3 connectivity + ```json + { + "cfg_type": "l3", // Config type for L3 connectivity use "l3" + "mac": "36:e7:a5:7e:0c:81", // MAC address of client + "routes": ["10.0.0.1/32"], // List of CIDR prefixes for this client + "vni": 10, // VXLAN segment identifier + "ofport": 1000, // OpenFlow port number in current OpenvSwitch switch + "type": "flat", // OpenvSwitch port type. May be "flat" and "vlan" + "tag": 0, // OpenvSwitch port segment identifier. Ignored on "flat" + "imp_rt": ["65000:10"], // List of imported BGP Route Targets + "exp_rt": ["65000:10"] // List of exported BGP Route Targets + } + ``` + Need create json config for all clients in **configs_dir** + ```bash + mkdir /var/lib/evpn_connector/client_configs/ + vim /var/lib/evpn_connector/client_configs/vm1.json + ``` +### Running the Service + +Start the service by specifying the path to your configuration file: + +1. **Run GoBGP:** + ```bash + sudo gobgpd -f /etc/gobgpd/gobgp.conf + ``` +2. **Run evpn-connector** + ```bash + source evpn/bin/activate + evpn-connector --config-file ~/evpn/config/evpn_connector.cfg --daemon-configs_dir "/var/lib/evpn_connector/client_configs/" + ``` diff --git a/docs/demo/CONFIGURE.md b/docs/demo/CONFIGURE.md new file mode 100644 index 0000000..50f7c7b --- /dev/null +++ b/docs/demo/CONFIGURE.md @@ -0,0 +1,62 @@ +Configuring demo env: + +1. Download Debian 12 nocloud image: + ```bash + wget https://cdimage.debian.org/images/cloud/bookworm/latest/debian-12-nocloud-amd64.qcow2 + ``` +2. Run virtual machines via virt-manager or qemu: + ``` + Hostname: compute1, ip_address: 10.10.10.1 + Hostname: compute2, ip_address: 10.10.10.2 + ``` +3. Install packages: + ```bash + sudo apt update + sudo apt install build-essential zlib1g-dev libffi-dev libssl-dev libbz2-dev libreadline-dev libsqlite3-dev liblzma-dev libncurses-dev screen tcpdump openvswitch-switch openvswitch-common jq curl git wget + ``` +4. Install pyenv: + ```bash + curl -fsSL https://pyenv.run | bash + ``` +5. Add to end of `.bashrc` and reenter to bash: + ```bash + export PYENV_ROOT="$HOME/.pyenv" + [[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH" + eval "$(pyenv init - bash)" + export PATH="$HOME/gobgp:$PATH" + bold=$(tput bold) + red=$(tput setaf 1) + green=$(tput setaf 2) + blue=$(tput setaf 4) + reset=$(tput sgr0) + PS1='\[$red\]\h\[$reset\]:\[$bold\]\w\[$reset\]\$ ' + ``` +6. Install python3.8 for evpn-connector: + ```bash + pyenv install 3.8.20 + pyenv global system 3.8 + ``` +7. Create venv for evpn-connector and activate: + ```bash + python3.8 -m venv ~/evpn/ + source evpn/bin/activate + ``` +8. Install evpn-connector from pip + ```bash + pip install --upgrade pip setuptools + pip install evpn-connector + ``` +9. Download and install GoBGP from binary: + ```bash + wget https://github.com/osrg/gobgp/releases/download/v3.34.0/gobgp_3.34.0_linux_amd64.tar.gz + mkdir gobgp + tar -xf gobgp_3.34.0_linux_amd64.tar.gz -C gobgp/ + rm gobgp_3.34.0_linux_amd64.tar.gz + ``` +10. Clone evpn-connector git repo: + ```bash + git clone https://github.com/vktechdev/evpn_connector + ``` +11. Copy configs for all daemons from repo (for each compute host separately): + ```bash + cp -r evpn_connector/docs/source/demo/compute1/* ~/ diff --git a/docs/demo/compute1/evpn_connector.cfg b/docs/demo/compute1/evpn_connector.cfg new file mode 100644 index 0000000..7254424 --- /dev/null +++ b/docs/demo/compute1/evpn_connector.cfg @@ -0,0 +1,39 @@ +[DEFAULT] +verbose = True +debug = True + +[logging] +config = logging.yaml + +[agent] +sync_period = 10 + +[victoria_metrics] +enabled = False +host = "127.0.0.1" +port = 8126 +prefix = "apps.evpn_connector.dev" +obsender_host = "sprut.compute.i" + +[sentry] +enabled = False +dsn = "http://01b92236b6514577812280415d631354@192.168.220.220:9000/2" +traces_sample_rate = 1.0 +env = devenv + +[gobgp] +gobgp_channel = "localhost:50051" +grpc_timeout_sec = 100 +# Don't change this param without gobgp restart +source_ip = "10.10.10.1" +as_number = 1 + +[ovs] +switch_name = "evpn" +vxlan_udp_port = 4789 +enable_sudo = True +ovs_vsctl_bin_path="/usr/bin/ovs-vsctl" +ovs_ofctl_bin_path="/usr/bin/ovs-ofctl" + +[anycast] +anycast_status_file = /tmp/anycast_status_file diff --git a/docs/demo/compute1/gobgp.conf b/docs/demo/compute1/gobgp.conf new file mode 100644 index 0000000..7891450 --- /dev/null +++ b/docs/demo/compute1/gobgp.conf @@ -0,0 +1,15 @@ +[global.config] +as = 65100 +router-id = "10.10.10.1" +local-address-list = [ "10.10.10.1" ] + +[[neighbors]] + [neighbors.config] + peer-as = 65200 + neighbor-address = "10.10.10.2" + [neighbors.ebgp-multihop.config] + enabled = true + multihop-ttl = 7 + [[neighbors.afi-safis]] + [neighbors.afi-safis.config] + afi-safi-name = "l2vpn-evpn" diff --git a/docs/demo/compute1/logging.yaml b/docs/demo/compute1/logging.yaml new file mode 100644 index 0000000..0907640 --- /dev/null +++ b/docs/demo/compute1/logging.yaml @@ -0,0 +1,32 @@ +version: 1 +formatters: + aardvark: + format: '%(asctime)15s.%(msecs)03d %(processName)s pid:%(process)d tid:%(thread)d %(levelname)s %(name)s:%(lineno)d %(message)s' + datefmt: '%Y-%m-%dT%H:%M:%S' +# this one should only be used by handlers that support topology_id attribute +# and since it may be used by multiple modules, actual_module attribute +# should also be added (module will give a short name, pathname is too long) + +handlers: + console: + class : logging.StreamHandler + formatter: aardvark + stream : ext://sys.stdout + +loggers: +# by default all existing loggers are disabled upon the application +# of this config. To re-enable a logger and it's childer just add it +# to the loggers section with any even empty fields. + obsender: + handlers: [console] + level: WARNING + propagate: False + + loopster: + level: WARNING + + evpn_connector: {} + +root: + handlers: [console] + level: INFO diff --git a/docs/demo/compute1/vm_conf/vm1.json b/docs/demo/compute1/vm_conf/vm1.json new file mode 100644 index 0000000..b63547d --- /dev/null +++ b/docs/demo/compute1/vm_conf/vm1.json @@ -0,0 +1,11 @@ +{ +"cfg_type": "l3", +"mac": "36:e7:a5:7e:0c:81", +"routes": ["172.16.0.1/32"], +"vni": 10, +"ofport": 1001, +"type": "flat", +"tag": 0, +"imp_rt": ["65000:10"], +"exp_rt": ["65000:10"] +} diff --git a/docs/demo/compute1/vm_conf/vm2.json b/docs/demo/compute1/vm_conf/vm2.json new file mode 100644 index 0000000..33e322d --- /dev/null +++ b/docs/demo/compute1/vm_conf/vm2.json @@ -0,0 +1,11 @@ +{ +"cfg_type": "l3", +"mac": "36:e7:a5:7e:0c:82", +"routes": ["172.16.0.2/32"], +"vni": 10, +"ofport": 1002, +"type": "flat", +"tag": 0, +"imp_rt": ["65000:10"], +"exp_rt": ["65000:10"] +} diff --git a/docs/demo/compute2/evpn_connector.cfg b/docs/demo/compute2/evpn_connector.cfg new file mode 100644 index 0000000..a5fbf27 --- /dev/null +++ b/docs/demo/compute2/evpn_connector.cfg @@ -0,0 +1,39 @@ +[DEFAULT] +verbose = True +debug = True + +[logging] +config = logging.yaml + +[agent] +sync_period = 10 + +[victoria_metrics] +enabled = False +host = "127.0.0.1" +port = 8126 +prefix = "apps.evpn_connector.dev" +obsender_host = "sprut.compute.i" + +[sentry] +enabled = False +dsn = "http://01b92236b6514577812280415d631354@192.168.220.220:9000/2" +traces_sample_rate = 1.0 +env = devenv + +[gobgp] +gobgp_channel = "localhost:50051" +grpc_timeout_sec = 100 +# Don't change this param without gobgp restart +source_ip = "10.10.10.2" +as_number = 1 + +[ovs] +switch_name = "evpn" +vxlan_udp_port = 4789 +enable_sudo = True +ovs_vsctl_bin_path="/usr/bin/ovs-vsctl" +ovs_ofctl_bin_path="/usr/bin/ovs-ofctl" + +[anycast] +anycast_status_file = /tmp/anycast_status_file diff --git a/docs/demo/compute2/gobgp.conf b/docs/demo/compute2/gobgp.conf new file mode 100644 index 0000000..f9aded5 --- /dev/null +++ b/docs/demo/compute2/gobgp.conf @@ -0,0 +1,15 @@ +[global.config] +as = 65200 +router-id = "10.10.10.2" +local-address-list = [ "10.10.10.2" ] + +[[neighbors]] + [neighbors.config] + peer-as = 65100 + neighbor-address = "10.10.10.1" + [neighbors.ebgp-multihop.config] + enabled = true + multihop-ttl = 7 + [[neighbors.afi-safis]] + [neighbors.afi-safis.config] + afi-safi-name = "l2vpn-evpn" diff --git a/docs/demo/compute2/logging.yaml b/docs/demo/compute2/logging.yaml new file mode 100644 index 0000000..0907640 --- /dev/null +++ b/docs/demo/compute2/logging.yaml @@ -0,0 +1,32 @@ +version: 1 +formatters: + aardvark: + format: '%(asctime)15s.%(msecs)03d %(processName)s pid:%(process)d tid:%(thread)d %(levelname)s %(name)s:%(lineno)d %(message)s' + datefmt: '%Y-%m-%dT%H:%M:%S' +# this one should only be used by handlers that support topology_id attribute +# and since it may be used by multiple modules, actual_module attribute +# should also be added (module will give a short name, pathname is too long) + +handlers: + console: + class : logging.StreamHandler + formatter: aardvark + stream : ext://sys.stdout + +loggers: +# by default all existing loggers are disabled upon the application +# of this config. To re-enable a logger and it's childer just add it +# to the loggers section with any even empty fields. + obsender: + handlers: [console] + level: WARNING + propagate: False + + loopster: + level: WARNING + + evpn_connector: {} + +root: + handlers: [console] + level: INFO diff --git a/docs/demo/compute2/vm_conf/vm3.json b/docs/demo/compute2/vm_conf/vm3.json new file mode 100644 index 0000000..8929d25 --- /dev/null +++ b/docs/demo/compute2/vm_conf/vm3.json @@ -0,0 +1,11 @@ +{ +"cfg_type": "l3", +"mac": "36:e7:a5:7e:0c:83", +"routes": ["172.16.0.3/32"], +"vni": 10, +"ofport": 1003, +"type": "flat", +"tag": 0, +"imp_rt": ["65000:10"], +"exp_rt": ["65000:10"] +} diff --git a/etc/evpn_connector/evpn_connector.cfg.sample b/etc/evpn_connector/evpn_connector.cfg.sample index e319d89..a5fbf27 100644 --- a/etc/evpn_connector/evpn_connector.cfg.sample +++ b/etc/evpn_connector/evpn_connector.cfg.sample @@ -24,7 +24,7 @@ env = devenv [gobgp] gobgp_channel = "localhost:50051" grpc_timeout_sec = 100 -# Not change this params without gobgp restart +# Don't change this param without gobgp restart source_ip = "10.10.10.2" as_number = 1 diff --git a/evpn_connector/common/metrics.py b/evpn_connector/common/metrics.py index 5a4060a..8e0a953 100644 --- a/evpn_connector/common/metrics.py +++ b/evpn_connector/common/metrics.py @@ -29,7 +29,7 @@ def construct_event_sender(app_name, logger=None, timeout=None): """ if timeout is None: - timeout = CONF.metrics.obsender_timeout + timeout = 2.0 common_kwargs = { "app_name": app_name, diff --git a/requirements.txt b/requirements.txt index 97427ad..7ad7ae3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,3 +13,4 @@ sentry-sdk==1.5.2;python_version<'3.8' # BSD sentry-sdk==1.6.0;python_version>='3.8' # BSD loopster>=2.14.4,<3.0.0 # Apache-2.0 obsender>=5.0.0,<6.0.0 # Apache-2.0 +pyyaml>=6.0 # MIT diff --git a/tools/create_clients_dp.sh b/tools/create_clients_dp.sh index 07dcb51..b38c62f 100755 --- a/tools/create_clients_dp.sh +++ b/tools/create_clients_dp.sh @@ -29,6 +29,10 @@ if [ -z "$(which ovs-vsctl)" ]; then log "Need install openvswitch package" "ERROR" exit 1 fi +if [ -z "$(which jq)" ]; then + log "Need install jq package" "ERROR" + exit 1 +fi if [[ "$(whoami)" != "root" && ! "$DEBUG" ]]; then log "Please run as root" "ERROR" exit 1