blog/content/post/a-raspberry-pi-bmc.md

133 lines
11 KiB
Markdown

+++
class = "post"
date = "2017-02-10T01:35:38-05:00"
tags = []
title = "Build A Raspberry Pi BMC"
type = "post"
weight = 1
draft = true
+++
IPMI BMCs are pretty ubiquitous in the datacenter and enterprise computing, because in a warehouse full of computers, finding and walking up to one just to reset it or check its console is quite daunting. The same goes for a home server: it may just be in my basement, but in a closed-up rack it becomes a huge hassle to manage a machine without IPMI. I try to get it on every motherboard I buy, but currently my Ceph nodes are running with motherboards that lack built-in IPMI. After an incident with one machine while I was on vacation, I finally decided that they needed remote management, and I decided to make my own BMC for them rather than waste money on replacement (IPMI-capable) motherboards.
## Enter the Raspberry Pi
If you don't know what it is, the Rapberry Pi is a small single-board computer, featuring an ARM SOC, Ethernet, USB, video, audio, and most importantly, GPIO, powered by MicroUSB and running the Debian distribution '[Raspbian](https://www.raspberrypi.org/downloads/raspbian/)'. The GPIO pins allow, with a simple utility or Python library, one to control or read information from various devices, including a serial console interface. These features make the Raspberry Pi a perfect fit for a BMC, and really not that far from the "real" BMCs found in most server-grade motherboards.
(Pictured: A Raspberry Pi 1 model B)
![Raspberry Pi](/images/rpibmc/rpi-1b.jpg)
## The hardware
### Power - thanks ATX!
One of the main ideas behind a BMC is that it should be accessible even if the host system is off. So we need some sort of power independent of the host state. The first thought when using a Raspberry Pi as a BMC would be to power it from some sort of external USB power brick, but that's messy: another cable running out of the case that must be dealt with, and another power brick. Luckily for us however, the ATX power supply standard has a solution!
The purple wire on a standard 24-pin ATX connector provides a standby +5V power supply, rated in the spec for up to 50mA but in reality today often supporting close to 1A. This is more than enough to run the motherboard standby power as well as a Raspberry Pi. This has the added bennefit of working just like a real BMC: when the system is unplugged, the BMC also turns off, and turns back on as soon as power is reapplied. With that in mind, I made a simple "adapter" out of a piece of solid-core CAT5 cable, which is inserted directly into the ATX motherboard connector, with the other end attached to a standard MicroUSB cable. The result is consistent, reliable, in-case power for the BMC without any trickery.
(Pictured: The power interface to the motherboard, attached to a MicroUSB cable)
[Picture - power stuff]
### Serial - USB or TTL?
The most critical component of the BMC, at least to me, is the ability to manage the host system without requiring a connected monitor+keyboard. Luckily just about every motherboard, and especially "server-grade" motherboards, still feature one or two DB9 COM ports. With some BIOS configurations, they will almost always support redirecting console output through the serial port, giving us exactly what we need for console access. However getting that console into the Raspberry Pi is a bit tricky.
The most common way to do this is to use a USB to Serial adapter, but like the original power suggestion, it's messy in terms of cabling. Enter the TTL serial port on the Raspberry Pi. Built in to [GPIO pins 14 and 15](http://codeandlife.com/2012/07/01/raspberry-pi-serial-console-with-max3232cpe/), the Raspberry Pi features a TTL 3.3V serial interface. Normally you would use this to get a console to the Raspberry Pi itself, but for our purposes, a simple flip of the TXD and RXD lines and some [reconfiguration of the Raspbian image](http://www.hobbytronics.co.uk/raspberry-pi-serial-port), we can use this interface to directly communicate with the host system.
This method does require a special chip, as you can see in the first link above. Luckily, the converter chips can be had for about $1.50 for 5 on eBay from the right Chinese sellers. And with the tiny little board and some small jumpers, we can connect the chip not to the rear DB9 port, but directly to the motherboard COM2 header; no messy crossover cables or USB to serial adapters! The resulting device is `/dev/ttyAMA0` in Raspbian and works flawlessly with your terminal emulator of choice; I'm using screen to allow persistence and clean disconnection without terminating the serial session (more on that later!)
(Pictured: the MAX3232 signal converter board)
![MAX3232 Serial boards](/images/rpibmc/max3232-boards.jpg)
### GPIO to rule them all
No BMC would be complete without two more functions: controlling and determining the power state of the system. This basic functionality allows you to, for instance, hard reset a crashed machine, or start it back up after a power failure. No more running for the box to press the power button!
The Raspberry Pi's GPIO pins provide a nice simple method for interfacing with the raw system management headers on any motherboard. For determining the power state, a GPIO pin (in my case, pin 21) is connected with a small resistor directly to the Power LED connector on the motherboard. When this pin is high, we know the system is online; when it's low, we know it's offline. Dead-simple.
The power and reset switches are a little more complex. While you can direct the GPIO directly to the switch headers, this will not work as you would expect, and could in fact risk blowing out your motherboard by sending 3.3V into the switch headers. The solution is to use a transistor: connect the gate pin of the transistor to your GPIO pin, and the anode and cathode pins to the switch header. The result is an electrically controlled switch, which turns on when the GPIO is set high, and turns off again when the GPIO is set low. You can now safely control the power and reset switches with your Raspberry Pi.
(Pictured: the wiring layout for a first-generation model-B Raspberry Pi BMC and the breadboad [sans serial])
![GPIO pinout](/images/rpibmc/rpi-1b-gpio.png)
![My wiring diagram](/images/rpibmc/wiring-diagram-sans-serial.jpg)
### Cabling it up
To keep everything neat, I used a through-hole solderable breadboard to mount all the components and keep everything neat. Header pins let us interface directly between the motherboard, Raspberry Pi, and the chassis buttons and LEDs using female-female jumper cables, again keeping everything nice and neat and easy to understand. The required components include two transitiors, two resistors, and a couple jumper wires. The transitors let us control the power and reset switches with a GPIO pin, while the resistors keep us from frying the LEDs (and the Raspberry Pi).
(Pictured: the cabling of the Raspberry Pi BMC)
![My wiring diagram](/images/rpibmc/finished-product.jpg)
## The software
Most traditional BMCs are managed via a (terrible) Web UI, which is often prone to breakage or other failures. And often these BMCs are incredibly complex, featuring dozens of menus and gigantic Flash monstrosities just to view their state. By using the Raspberry Pi, a seeming limitation - SSH-only access - becomes a benefit: it becomes trivial to connect to the BMC from a terminal, check the power state or serial console, turn on/off/reset a host, and then disconnect. No crummy web page or point-and-click menu system!
The software side started out as a basic Raspbian system, however I wanted to make it a little more "BMC-like", in a sense stripped-down and easy-to-use with a small set of commands. I started by writing a simple "shell" emulator in BASH, and using a constant loop and hostname prompt along with `stty` to keep it focused and running. Each command triggers a function which performs its specific job and then returns to the "shell". While `bash` is available also, it should rarely be needed.
The actual software doing the heavy lifting is a combination of `screen`, to view the host system serial console, and the `gpio` utility by WiringPi (Debian package `wiringpi`). The `screen` session is configured to start automatically at BMC boot to ensure all serial output is captured and stored for later analysis - a major problem with the (few) SSH-based BMCs I've tried! The `gpio` program makes writing and reading the GPIO pins simple and easy, returning 0 or 1 for the low/high states and easily writing states. By writing the BMB shell in `bash`, I was able to get all the flexibility I wanted without any programming overhead; the whole thing is under 200 lines.
(Pictured: an example session with `bmc.sh`)
![Shell example](/images/rpibmc/bmcshell-sample.png)
For example, here is the `powersw` function, which manages the power button for the `powersw` and `kill` commands. By introducing a delay, we can even simulate a power-button force-off event for the `kill` task and avoid messing with the raw power:
```
powersw() {
if [ "$1" == "hard" ]; then
delay=10
echo -n "Holding power switch... "
else
delay=0
echo -n "Pressing power switch... "
fi
gpio mode 1 out
gpio write 1 1
sleep $delay
gpio write 1 0
sleep 1
echo "done."
}
```
Simply by setting the GPIO high, we simulate a button press, and then by setting the GPIO low, we release the simulated button. The result is a very convincing replica of the standard IPMI/BMC power control functionality. Another feature is to obtain the current system power status (shown multiple times in the image above). This function reads instead of writes the GPIO, in this case the front power LED, and reports the result, colour and all:
```
readpower() {
gpio mode 2 in
powerstate_raw=$(gpio read 2)
if [ "${powerstate_raw}" -eq 1 ]; then
powerstate="\e[32mOn\e[0m"
else
powerstate="\e[31mOff\e[0m"
fi
}
```
The entire code of the BMC "shell" can be found on my [GitHub](https://github.com/joshuaboniface/rpibmc), and is called via a small hack to the `/etc/passwd` file for the `bmc` user, starting by default and terminating the SSH session on exit; to set this when creating a new user, simply pass `useradd` the `-S /path/to/bmc.sh` flag instead:
```
bmc:x:0:0:bmc:/home/bmc:/bin/bmc.sh
```
```
useradd -g sudo -G gpio -S /bin/bmc.sh bmc
```
Finally we're able to set the host system's name (for display when logging in) via the file `/etc/bmchost`, which makes deploying an image and setting the name trivial.
## Conclusion
I hope you've found this post interesting and useful - if you have some IPMI-less systems you want to manage remotely, I definitely recommend you try it out. So far my testing has been extremely positive; for under $30 (if you can find the Raspberry Pi for the right price), you can build youself out-of-band BMC management for any motherboard you can find. And the remote management makes even the most irritating host messups ("Oh broke my udev network rules, again!?") trivial to recover from. A small price to pay for the peace of mind of being able to manage your system from almost anywhere, even a cruise ship!
(Pictured: what you might have to do without a Raspberry Pi BMC!)
![Txt from a ship](/images/rpibmc/txt-from-a-ship.png)
If you have any questions or comments, shoot me an e-mail!