diff --git a/ansible-devnet/genesis/validator-config.yaml b/ansible-devnet/genesis/validator-config.yaml index d6c9dd2..4d7405c 100644 --- a/ansible-devnet/genesis/validator-config.yaml +++ b/ansible-devnet/genesis/validator-config.yaml @@ -66,4 +66,12 @@ validators: ip: "46.62.163.74" quic: 9001 metricsPort: 8081 + count: 1 + + - name: "ethlambda_0" + privkey: "299550529a79bc2dce003747c52fb0639465c893e00b0440ac66144d625e066a" + enrFields: + ip: "TBD" + quic: 9001 + metricsPort: 8081 count: 1 \ No newline at end of file diff --git a/ansible/playbooks/deploy-single-node.yml b/ansible/playbooks/deploy-single-node.yml index 39a7285..116a3e5 100644 --- a/ansible/playbooks/deploy-single-node.yml +++ b/ansible/playbooks/deploy-single-node.yml @@ -76,7 +76,15 @@ - grandine - deploy +- name: Deploy Ethlambda node + include_role: + name: ethlambda + when: client_type == "ethlambda" + tags: + - ethlambda + - deploy + - name: Fail if unknown client type fail: - msg: "Unknown client type '{{ client_type }}' for node '{{ node_name }}'. Expected: zeam, ream, qlean, lantern, lighthouse or grandine" - when: client_type not in ["zeam", "ream", "qlean", "lantern", "lighthouse", "grandine"] + msg: "Unknown client type '{{ client_type }}' for node '{{ node_name }}'. Expected: zeam, ream, qlean, lantern, lighthouse, grandine or ethlambda" + when: client_type not in ["zeam", "ream", "qlean", "lantern", "lighthouse", "grandine", "ethlambda"] diff --git a/ansible/roles/ethlambda/defaults/main.yml b/ansible/roles/ethlambda/defaults/main.yml new file mode 100644 index 0000000..fc8589d --- /dev/null +++ b/ansible/roles/ethlambda/defaults/main.yml @@ -0,0 +1,7 @@ +--- +# Default variables for ethlambda role +# Note: These are fallback defaults. Actual values are extracted from client-cmds/ethlambda-cmd.sh +# in the tasks/main.yml file. These defaults are used if extraction fails. + +ethlambda_docker_image: "ghcr.io/lambdaclass/ethlambda:latest" +deployment_mode: docker # docker or binary diff --git a/ansible/roles/ethlambda/tasks/main.yml b/ansible/roles/ethlambda/tasks/main.yml new file mode 100644 index 0000000..3c063fa --- /dev/null +++ b/ansible/roles/ethlambda/tasks/main.yml @@ -0,0 +1,100 @@ +--- +# Ethlambda role: Deploy and manage Ethlambda nodes +# Converts client-cmds/ethlambda-cmd.sh logic to Ansible tasks + +- name: Extract docker image from client-cmd.sh + shell: | + # Extract the first word (docker image) from node_docker line + # playbook_dir points to ansible/playbooks, go up two levels to reach project root + project_root="$(cd '{{ playbook_dir }}/../..' && pwd)" + grep -E '^node_docker=' "$project_root/client-cmds/ethlambda-cmd.sh" | head -1 | sed -E 's/.*node_docker="([^ "]+).*/\1/' + register: ethlambda_docker_image_raw + changed_when: false + delegate_to: localhost + run_once: true + +- name: Extract deployment mode from client-cmd.sh + shell: | + # Extract the value from node_setup line + # playbook_dir points to ansible/playbooks, go up two levels to reach project root + project_root="$(cd '{{ playbook_dir }}/../..' && pwd)" + grep -E '^node_setup=' "$project_root/client-cmds/ethlambda-cmd.sh" | head -1 | sed -E 's/.*node_setup="([^"]+)".*/\1/' + register: ethlambda_deployment_mode_raw + changed_when: false + delegate_to: localhost + run_once: true + +- name: Set docker image and deployment mode from client-cmd.sh + set_fact: + ethlambda_docker_image: "{{ ethlambda_docker_image_raw.stdout | trim | default('ghcr.io/lambdaclass/ethlambda:latest') }}" + deployment_mode: "{{ ethlambda_deployment_mode_raw.stdout | trim | default('docker') }}" + +- name: Extract node configuration from validator-config.yaml + shell: | + yq eval ".validators[] | select(.name == \"{{ node_name }}\") | .{{ item }}" "{{ genesis_dir }}/validator-config.yaml" + register: ethlambda_node_config + changed_when: false + loop: + - enrFields.quic + - metricsPort + when: node_name is defined + +- name: Set node ports + set_fact: + ethlambda_quic_port: "{{ ethlambda_node_config.results[0].stdout }}" + ethlambda_metrics_port: "{{ ethlambda_node_config.results[1].stdout }}" + when: ethlambda_node_config is defined + +- name: Ensure node key file exists + stat: + path: "{{ genesis_dir }}/{{ node_name }}.key" + register: node_key_stat + +- name: Debug node key file check + debug: + msg: "Checking for key file at {{ genesis_dir }}/{{ node_name }}.key - exists: {{ node_key_stat.stat.exists | default('undefined') }}" + +- name: Fail if node key file is missing + fail: + msg: "Node key file {{ node_name }}.key not found in {{ genesis_dir }}" + when: not (node_key_stat.stat.exists | default(false)) + +- name: Clean node data directory + file: + path: "{{ data_dir }}/{{ node_name }}" + state: absent + when: clean_data | default(false) | bool + +- name: Create node data directory + file: + path: "{{ data_dir }}/{{ node_name }}" + state: directory + mode: "0755" + +- name: Deploy Ethlambda node using Docker + block: + - name: Stop existing Ethlambda container (if any) + command: docker rm -f {{ node_name }} + register: ethlambda_stop + failed_when: false + changed_when: ethlambda_stop.rc == 0 + + - name: Start Ethlambda container + command: >- + docker run -d + --pull=always + --name {{ node_name }} + --restart unless-stopped + --network host + -v {{ genesis_dir }}:/config:ro + -v {{ data_dir }}/{{ node_name }}:/data + {{ ethlambda_docker_image }} + --custom-network-config-dir /config + --gossipsub-port {{ ethlambda_quic_port }} + --node-id {{ node_name }} + --node-key /config/{{ node_name }}.key + --metrics-address 0.0.0.0 + --metrics-port {{ ethlambda_metrics_port }} + register: ethlambda_container + changed_when: ethlambda_container.rc == 0 + when: deployment_mode == 'docker' diff --git a/client-cmds/ethlambda-cmd.sh b/client-cmds/ethlambda-cmd.sh new file mode 100644 index 0000000..6ad8f83 --- /dev/null +++ b/client-cmds/ethlambda-cmd.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +#-----------------------ethlambda setup---------------------- + +binary_path="$scriptDir/../ethlambda/target/release/ethlambda" + +# Command when running as binary +node_binary="$binary_path \ + --custom-network-config-dir $configDir \ + --gossipsub-port $quicPort \ + --node-id $item \ + --node-key $configDir/$item.key \ + --metrics-address 0.0.0.0 \ + --metrics-port $metricsPort" + +# Command when running as docker container +node_docker="ghcr.io/lambdaclass/ethlambda:latest \ + --custom-network-config-dir /config \ + --gossipsub-port $quicPort \ + --node-id $item \ + --node-key /config/$item.key \ + --metrics-address 0.0.0.0 \ + --metrics-port $metricsPort" + +node_setup="docker" diff --git a/generate-ansible-inventory.sh b/generate-ansible-inventory.sh index c8bd385..e36044e 100755 --- a/generate-ansible-inventory.sh +++ b/generate-ansible-inventory.sh @@ -58,6 +58,8 @@ all: hosts: {} grandine_nodes: hosts: {} + ethlambda_nodes: + hosts: {} EOF # Extract node information from validator-config.yaml @@ -65,7 +67,7 @@ nodes=($(yq eval '.validators[].name' "$VALIDATOR_CONFIG")) # Process each node and generate inventory entries for node_name in "${nodes[@]}"; do - # Extract client type (zeam, ream, qlean, lantern, lighthouse, grandine) + # Extract client type (zeam, ream, qlean, lantern, lighthouse, grandine, ethlambda) IFS='_' read -r -a elements <<< "$node_name" client_type="${elements[0]}" group_name="${client_type}_nodes" diff --git a/local-devnet/genesis/validator-config.yaml b/local-devnet/genesis/validator-config.yaml index 9e1a058..7f99d48 100644 --- a/local-devnet/genesis/validator-config.yaml +++ b/local-devnet/genesis/validator-config.yaml @@ -48,7 +48,7 @@ validators: quic: 9004 metricsPort: 8084 count: 1 - + - name: "lighthouse_0" # node id a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2 # peer id 16Uiu2HAm7TYVs6qvDKnrovd9m4vvRikc4HPXm1WyLumKSe5fHxBv @@ -66,4 +66,15 @@ validators: ip: "127.0.0.1" quic: 9006 metricsPort: 8086 - count: 1 \ No newline at end of file + count: 1 + + - name: "ethlambda_0" + # node id a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2 + # peer id 16Uiu2HAmPV5jU62WtmDkCEmfq1jzbBDkGbHNsDN78gJyvmv2TuC5 + privkey: "299550529a79bc2dce003747c52fb0639465c893e00b0440ac66144d625e066a" + # verify /ip4/127.0.0.1/udp/9007/quic-v1/p2p/16Uiu2HAmPV5jU62WtmDkCEmfq1jzbBDkGbHNsDN78gJyvmv2TuC5 + enrFields: + ip: "127.0.0.1" + quic: 9007 + metricsPort: 8087 + count: 1 diff --git a/run-ansible.sh b/run-ansible.sh index a613417..5f4eabd 100755 --- a/run-ansible.sh +++ b/run-ansible.sh @@ -58,7 +58,7 @@ fi # Update inventory with SSH key file and user if provided if command -v yq &> /dev/null; then # Get all remote host groups (zeam_nodes, ream_nodes, qlean_nodes, lantern_nodes, lighthouse_nodes) - for group in zeam_nodes ream_nodes qlean_nodes lantern_nodes lighthouse_nodes grandine_nodes; do + for group in zeam_nodes ream_nodes qlean_nodes lantern_nodes lighthouse_nodes grandine_nodes ethlambda_nodes; do # Get all hosts in this group hosts=$(yq eval ".all.children.$group.hosts | keys | .[]" "$INVENTORY_FILE" 2>/dev/null || echo "") for host in $hosts; do