I have a 10 node containerlab topology which is integrated with Serf. Currently, it does the following through some scripts;
- deploy the containerlab nodes
- Assign IP addresses for the nodes
- Copy necessary files to each node and generating a configuration file for the Serf agent.
- Start Serf agents
- Join nodes to the cluster using Serf
Everything works so far. The issue I am facing is how to get the coordinates. as per docs I found that we can use get-coordinate
command. However, I am unable to make it work. I tried to run serf get-coordinate inside the running container but it says command not found. I also tried to run it as curl -X POST -d '{"Node":"serf1"}' http://10.0.1.11:7373/v1/agent/get-coordinate
which gives curl: (52) Empty reply from server. When I check the logs I found that it says
[INFO] agent.ipc: Accepted client: 10.0.1.11:52128
[ERR] agent.ipc: failed to decode request header: msgpack decode error [pos 1]: only encoded map or array can be decoded into a struct
So I think I am doing something wrong here which I failed to understand so far.
So asper further readings I came across RPC protocol. I think I need to use handshake
and get-coordinate
commands. So I tried to install MsgPack and tried with some code even from chatgpt but still it doesn’t work. I am unable to understand why this keeps getting failed. I have RPC in each node (0.0.0.0:7373). Honestly I just want to find the most simple way to get the coordinates from these running serf nodes. I kindly seek any advices or guidance on this.
The code I was using:
I tried so many times but getting same errors and I am out of options.
import socket
import msgpack
def send_rpc_request(command, body=None):
"""
Sends an RPC request to the Serf agent.
:param command: The command to execute (e.g., "get-coordinate" or "handshake").
:param body: The request body (e.g., {"Node": "serf1"} or {"Version": 1}).
:return: The response from the Serf RPC server.
"""
# RPC server address and port
rpc_address = "172.20.20.2" # IP address of the Serf node
rpc_port = 7373
# Create the request header
header = {
"Command": command,
"Seq": 0 # Sequence number for tracking requests
}
# Serialize the header and body using MessagePack
payload = msgpack.packb(header)
if body:
payload += msgpack.packb(body)
# Create a TCP connection to the RPC server
try:
with socket.create_connection((rpc_address, rpc_port)) as sock:
# Send the serialized request (Handshake first)
print(f"Sending handshake to {rpc_address}:{rpc_port} with body: {body}")
sock.sendall(payload)
# Receive and deserialize the handshake response
response = sock.recv(4096)
print(f"Raw response: {response}") # Print raw response for debugging
unpacked_response = msgpack.unpackb(response, strict_map_key=False)
# Check if handshake was successful
if unpacked_response.get('Error', '') == "":
print(f"Handshake successful: {unpacked_response}")
else:
print(f"Handshake failed: {unpacked_response}")
return None # No point continuing if handshake fails
# Now send the actual command (get-coordinate) after handshake is successful
print(f"Sending '{command}' request for node {body['Node']}")
# Ensure proper format for the request body
coordinate_payload = msgpack.packb({
"Command": "get-coordinate",
"Seq": 1 # New sequence number for this request
})
# Check that the body parameter has the correct format
coordinate_body = {"Node": body["Node"]}
# Double-check the Node parameter is formatted correctly
print(f"Sending get-coordinate request body: {coordinate_body}")
coordinate_payload += msgpack.packb(coordinate_body)
# Send the get-coordinate request
sock.sendall(coordinate_payload) # Send the second request for coordinates
# Receive and deserialize the response for get-coordinate
response = sock.recv(4096)
unpacked_response = msgpack.unpackb(response, strict_map_key=False)
return unpacked_response
except Exception as e:
return {"Error": str(e)}
# Step 1: Send handshake with version information
handshake_response = send_rpc_request("handshake", {"Version": 1}) # First, send the handshake
if handshake_response:
print("Handshake Response:", handshake_response)
# Step 2: Send the get-coordinate request for "serf2" only if handshake is successful
coordinate_response = send_rpc_request("get-coordinate", {"Node": "serf2"})
print("Coordinate Response:", coordinate_response)
The Response:
python3 script.py
Sending handshake to 172.20.20.2:7373 with body: {'Version': 1}
Raw response: b'x82xa5Errorxa0xa3Seqx00'
Handshake successful: {'Error': '', 'Seq': 0}
Handshake Response: {'Error': "'Node'"}
Sending handshake to 172.20.20.2:7373 with body: {'Node': 'serf2'}
Raw response: b'x82xa5Errorxb2Handshake requiredxa3Seqx00'
Handshake failed: {'Error': 'Handshake required', 'Seq': 0}
Coordinate Response: None