Friday, December 20, 2019

Get MAC Address for a Pi Cluster

There are a lot of ways to get the MAC address for a Raspberry Pi. For the Super Pi there were a lot of Pi, 1050 to be specific (two of them are dead on arrival and we disabled 24 of them to get the exact number we wanted, 1024), we looked at the DHCP log files. Roy and Mike spent a few days doing this. Think about it, if you spend one minute times 1050, that's 17.5 hours! I'm surprised they didn't go crazy but they seemed to be having fun. For the Mini Super Pi, I need to know the order.

At this point I'm going to digress a little. When building a large Raspberry Pi cluster at some point it's worth network booting. I'd say after 8 the cost of network booting is worth it. Just the time and expense of buying and cloning eight network cards. Network booting a Pi isn't terribly difficult, it's doing it consistently with a lot of them that's the problem. The netboot on a Pi times out after a short period of time, it isn't implemented 100% to spec, I could go on but these are things you can find out with some research. Although it's buried so if people want me to document these issues let me know in the comments. If there are any problems netbooting the Pi can be dead in a big cluster with no way to reboot it except reboot the entire thing. It helps to have a static IP address. Best way of doing this is by using DHCP (yeah, that's confusing) but map the MAC address to an IP address. Oh, also if you have enough Raspberry Pi, the MAC address may not be unique. We have two Pi with the same MAC addresses. Most clusters won't run into this. Also, network booting while not difficult requires the use of overlay file system. The problem comes when combining the two which is rather difficult. I got overlay file system working. Roy merged netboot and overlay and Vijay did this with Oracle Linux. There are a few other options netboot and redirect all writes to separate files and hope you got all them, create an NFS mount for each Pi (yeah, no). The only downside to netboot + overlay file system is you can't run Docker, which means you can't run K8 because Docker uses overlay and you can't use overlay on top of overlay.

OK, back to the reason for this blog post. My new system for obtaining the MAC address is as follows:

1. Install Raspbian on an SD card. I used belenaEtcher on Mac just because it's easy.
2. Copy the following Python script to the Pi and call it

import socket
import time
import threading
import datetime
import uuid
import random

# This is needed so we get a network interface

# Global variables
MacAddress = hex(uuid.getnode())

if MacAddress.endswith('L'):
    MacAddress = MacAddress[:-1]

client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
client.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)

server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
server.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
hostname = socket.gethostname()    
IPAddr = socket.gethostbyname(hostname)    
print("Your Computer Name is:" + hostname)    
print("Your Computer IP Address is:" + IPAddr)

def SendMessage(portmessage):
    data = message
    endodeddata = data.encode()
    server.sendto(endodeddata, ('<broadcast>', port))
    print("message sent!" + data)

while True:
    SendMessage(3333, MacAddress)

3. Create a cron job:

sudo crontab -e

4. Add this line to the end of the file:

@reboot python /home/pi/ &

5. Copy the following Python script to your desktop and run it. I'm running it in Visual Studio Code.

import datetime
import os
import signal
import socket
import threading
import time
import re
import uuid

if == 'nt':
    signal.signal(signal.SIGINT, signal.SIG_DFL)

server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
server.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)

hostname = socket.gethostname()
IPAddr = socket.gethostbyname(hostname)
print("Your Computer Name is: {0}\n".format(hostname))
print("Your Computer IP Address is: {0}\n".format(IPAddr))

def waitForPosts():

addresses = {}

class Listen(threading.Thread):
    def __init__(selfport):
        self.port = port
        self.UIDCount = 0

    def run(self):
        client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
        client.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)

        while True:
            data, addr = client.recvfrom(1024)
            mac = data
            if mac not in addresses:
                addresses[mac] = mac
                val = mac.decode()
                print (':'.join(re.findall('..''%012x' % int(val, 16)))) 

publish_thread = Listen(3333)


Note: If you decide you want to remove the dictionary in the code above to allow duplicate MAC addresses to print out, which his a great debugging tool and since UDP is not guaranteed is a perfectly fine way to go, then you might want to remove duplicates. To do this in Visual Studio Code this is one way I found to do it:

  1. Control+F
  2. Toggle "Replace mode"
  3. Toggle "Use Regular Expression" (the icon with the .* symbol)
  4. In the search field, type ((^[^\S$]*?(?=\S)(?:.*)+$)[\S\s]*?)^\2$(?:\n)?
  5. In the "replace with" field, type $1
  6. Click the Replace All button ("Replace All").
6. So now insert the SD card, plug the Pi into network, and power and wait. The output on your desktop will be a list of your MAC addresses in a known order and it is easy to know if one is dead and which one.