L10 Blog

L10 Blog

Controlling Server Hardware With Go

Using IPMI and Go to never leave the couch!

Controlling Server Hardware With Go

Intelligent Platform Management Interface (IPMI) is built into most servers to allow remote management, generally for use when you're not in the same physical location of the server. In 2020, I purchased 4 used servers off of Craigslist to set up my own home lab. One of these servers runs a plex media server which hosts a variety of media that I can stream to my TV or even my phone when I'm away.

2020-12-16-033603369.jpg

My home lab is less than 100 steps from my couch and TV, where I'm writing this article at, and yet I decided that was too far to walk to turn my server on and off. Enter IPMI. I discovered my Supermicro server had 2 IP addresses, one I knew was used to connect via SSH and exposed the plex media server but the other was a mystery. This second IP address stayed online even when the server was turned off which lead me to investigate what was going on there. Visiting the IP address in the browser showed a login page and after entering some default credentials I discovered it was an IPMI.

image.png image.png

After seeing that I could turn my server on and off through a webpage, I decided that I didn't want to log in and deal with the ancient Java application that was running with many, many iframes. I decided to write a program to be able to do this from the command line and eventually integrate it into my own mobile app. Unsurprisingly, there was already a Go IPMI library written by VMware. After creating an IPMI connection, the code to execute a command against the server is less than 20 lines of code.

package iipmi

import (
    "github.com/cenkalti/backoff/v4"
    "github.com/vmware/goipmi"
)

func Execute(conn *ipmi.Connection, ctl ipmi.ChassisControl) error {
    client, err := ipmi.NewClient(conn)
    if err != nil {
        return err
    }
    if err := client.Open(); err != nil {
        return err
    }
    defer client.Close()

    return backoff.Retry(func() error { return client.Control(ctl) }, backoff.NewExponentialBackOff())
}

I added exponential backoff retry logic to help with spotty connections or IPMI being, well, IPMI. Setting up the IPMI connection is just as simple:

package cmd

import (
    "github.com/spf13/cobra"
    ipmi "github.com/vmware/goipmi"
    "ipmi-grpc/pkg/iipmi"
)

var startupCmd = &cobra.Command{
    Use: "startup",
    RunE: func(cmd *cobra.Command, args []string) error {
        conn := &ipmi.Connection{Interface: "lan"}
        conn.Path, _ = cmd.Flags().GetString("path")
        conn.Hostname, _ = cmd.Flags().GetString("hostname")
        conn.Port, _ = cmd.Flags().GetInt("port")
        conn.Username, _ = cmd.Flags().GetString("username")
        conn.Password, _ = cmd.Flags().GetString("password")

        return iipmi.Execute(conn, ipmi.ControlPowerUp)
    },
}

func init() {
    startupCmd.Flags().String("path", "", "IPMI path")
    startupCmd.Flags().String("hostname", "", "IPMI hostname")
    startupCmd.Flags().Int("port", 623, "IPMI port")
    startupCmd.Flags().String("username", "ADMIN", "IPMI username")
    startupCmd.Flags().String("password", "ADMIN", "IPMI password")

    _ = deviceIdCmd.MarkFlagRequired("hostname")

    rootCmd.AddCommand(startupCmd)
}

This is the cobra command to start up the server with a simple ./ipmi-grpc startup --hostname 192.168.37.113.

I used this as an excuse to write something in Go and learn more about IPMI. Hardware meeting software is one of the most interesting parts of programming to me. When I run this command and the fans start spinning in my office it brings a smile to my face knowing that I did that using code.

 
Share this
Proudly part of