Creating a two-node CentOS 6 cluster with floating IP using Pacemaker and Corosync

These are my old instructions for creating a Linux cluster with floating IP on versions of CentOS prior to 6.4.

If you’re using CentOS 6.4 or higher, you need my updated post for creating a cluster with CMAN and Pacemaker.

Installation and configuration

Install the required packages and prepare the configuration file:

yum install pacemaker
cp /etc/corosync/corosync.conf.example /etc/corosync/corosync.conf

Change bindnetaddr to your network address (e.g. in
/etc/corosync/corosync.conf. Create /etc/corosync/service.d/pcmk and put
the Pacemaker startup config into it:

service {
        name: pacemaker
        ver: 1 

Start corosync and pacemaker:

service corosync start
service pacemaker start

Make sure they start on boot:

chkconfig corosync on
chkconfig pacemaker on

Repeat all of the above on the second node. Then disable STONITH on the primary node:

crm configure property stonith-enabled=false

Add a floating IP on the primary node (change IP address and netmask as

crm configure primitive ClusterIP ocf:heartbeat:IPaddr2 params ip= cidr_netmask=24 op monitor interval=30s

Disable quorum on the primary node (necessary for running only two nodes):

crm configure property no-quorum-policy=ignore

Prevent resources from moving back after a node recovers:

crm configure rsc_defaults resource-stickiness=100

Use the commands in the Administration section below to ensure that the
floating IP is on the correct machine – if this is not the case then
temporarily shut down Pacemaker on whichever machine has the floating IP so
that it moves across to the desired node:

service pacemaker stop


Display the current configuration:

crm configure show

Status monitor:

crm status

Show which node has the cluster IP:

crm resource status ClusterIP

Reset the config to default:

cibadmin -E --force

Verify that there are no problems with the current config:

crm_verify -L

Creating a two-node CentOS 6 cluster with floating IP using CMAN and Pacemaker

Originally I was using Heartbeat to create two-node Linux clusters with floating IPs, but when Heartbeat stopped being developed I needed to figure out how to use Corosync and Pacemaker for this instead. Somewhat annoyingly, Linux HA stuff has changed yet again in CentOS 6.4, so now it’s necessary to use CMAN and Pacemaker instead.

This is quite a lot more in-depth than the simple configuration that was originally required for Heartbeat. Anyway, based on my recent experiences, here’s a very quick guide for if you find yourself in a similar situation. This works for me on CentOS 6.4 and higher, but it won’t work on earlier versions of CentOS.

If you’re looking for the old instructions for creating a cluster with Pacemaker and Corosync, they’re here.

Installation and initial configuration

Install the required packages on both machines:

yum install pacemaker
cp /etc/corosync/corosync.conf.example /etc/corosync/corosync.conf

Set up and configure the cluster on the primary machine, changing newcluster , and as needed:

ccs -f /etc/cluster/cluster.conf --createcluster newcluster
ccs -f /etc/cluster/cluster.conf --addnode
ccs -f /etc/cluster/cluster.conf --addnode
ccs -f /etc/cluster/cluster.conf --addfencedev pcmk agent=fence_pcmk
ccs -f /etc/cluster/cluster.conf --addmethod pcmk-redirect
ccs -f /etc/cluster/cluster.conf --addmethod pcmk-redirect
ccs -f /etc/cluster/cluster.conf --addfenceinst pcmk pcmk-redirect
ccs -f /etc/cluster/cluster.conf --addfenceinst pcmk pcmk-redirect

Copy /etc/cluster/cluster.conf from the primary machine to the secondary machine in the cluster.

It’s necessary to turn off quorum checking, so do this on both machines:

echo "CMAN_QUORUM_TIMEOUT=0" >> /etc/sysconfig/cman

Start the services

Start up the services on both machines:

service cman start
service pacemaker start

Make sure both services start on reboot:

chkconfig cman on
chkconfig pacemaker on

Configure and create floating IP

Configure the cluster on the primary machine:

pcs property set stonith-enabled=false
pcs property set no-quorum-policy=ignore

Create the floating IP on the primary machine, changing the IP address and server name as needed:

pcs resource create livefrontendIP0 ocf:heartbeat:IPaddr2 ip= cidr_netmask=32 op monitor interval=30s
pcs constraint location livefrontendIP0 prefers

Cluster administration

To monitor the status of the cluster:

pcs status

To show the full cluster configuration:

pcs config

Emerging from the formless void…

Dicepeople - Let There Be Light

What was previously my own electronic music project Dicepeople has now grown into an exciting audiovisual collaboration with the talented and enthusiastic Rafael Filomeno.

Our first performance together with music and visuals is happening this Friday 6th September at The Nest in Dalston, London.

If you’re in London and free on Friday evening, come along. It should be something quite special.

The Facebook event is here and you need to add yourself to the free guest list here (it only takes a minute).

Creating a Bash-based web server on my new Raspberry Pi

Recently I decided to correct my oversight of not having obtained a Raspberry Pi, so I purchased the Model B version along with the nice clear case and named it “Colossus”.

Never having owned an Acorn Archimedes (I was an Amiga man) the first thing I did was play with RISC OS for a while. Having got that out of the way, I installed Raspbian Linux and decided to have some fun creating a demo web page showing continuously updating statistics from the Pi.

I ended up using a Bash script to generate a single page of HTML incorporating an embedded image (a lovely iPhoneography photo I took of Colossus). I then piped this script through Netcat to create a single-threaded web server. And I then ran all of that several times, each instance on a different port, to create a multithreaded web server which I named “Hydra”.

I needed something at the front end to distribute incoming HTTP request on port 80 to Hydra’s multiple ports, and a very simple command utilising the handy Pen load balancer did the trick for that.

The finished web site can be seen in all its geeky glory at

Warpvision – (Motion)

“Warpvision presents an Artificial Intelligence video – (Motion) – electronic listening music for the eyes”

I was lucky enough to see Warpvision’s fantastic “(Motion)” at its launch party at the Institute of Contemporary Arts in 1994 and I’ve loved it ever since. Sadly Warp have never released it on DVD, so I thought I’d upload my VHS rip of this memorable audiovisual production which features music from Warp Records’ seminal “Artificial Intelligence II” compilation, including various classic IDM artists, combined with retro 90s computer animation.

Video by David Slade and Phil Wolstenholme: (Nanotechnics.1); (Mirage); (Corpus Porpoise Posthumous Non Polhemus); (Lifespan); (Sim)(biotics).

Audio: Scanner – Untitled; Link – Arcadian (Global Communication Remix); Beaumont Hannant – Utuba; Polygon Window – Polygon Window; Richard H. Kirk – Reality Net; Speedy J – Symmetry; Mark Franklin – Release To The System (Beaumont Hannant Remix); Autechre – Basscadet (Bcdtmx).

© & ℗ 1994 Warpvision

New website for my system administration and infrastructure consultancy business

I became a self employed system administrator in 2011 and (touch wood) my freelance infrastructure business has been going fairly well so far. I thought it was about time I built a marketing website for my business, and I wanted to create a simple, professional-looking site without having to spend too much time building it.

After some research I ended up using the excellent Initializr to produce a template website built from HTML5 Boilerplate (H5BP) and Twitter Bootstrap. H5BP provides a solid HTML5 core with optimisations for JavaScript, CSS, Apache, Google Analytics, etc., and Twitter Bootstrap provides a simple but very powerful HTML/CSS layout framework which cleverly handles things like rendering on mobile devices so that you don’t have to worry about it.

It was very easy to modify the template and build the simple marketing website I wanted. The end result isn’t the most advanced design ever created, and it’s instantly recognisable as a Bootstrap site to anyone else who’s used it, but that’s OK. I’m just trying to market my business, not win design awards. I’d certainly recommend Initializr to anyone else who understands HTML and CSS and wants to get a decent site up and running as quickly as possible.

The end result can be seen at

Bash script to extract photos from vCard files

If you ever find yourself wanting to extract the contact photos from vCard/VCF files then this may be the script for you.

I know that this works on a vCard export of multiple contacts from the Contacts app in OS X Mountain Lion. It’s possible you might have to tweak it a bit for vCard files from other sources. This script also resizes the exported photos to 201×201 pixels. If you want to modify this conversion then just change the “convert” line near the bottom accordingly. If you don’t want to resize the photos then simply delete this line and the bit where it checks for the presence of ImageMagick (or comment these bits out). If you do want to resize the images then you’ll need to have ImageMagick installed (brew install imagemagick if you’re on a Mac with Homebrew installed, which it really ought to be). You’ll also need to make sure you have base64 installed, which it probably already will be. Note that with more recent versions of OS X, you need to change base64 -d in the script to base64 -D with a capital D (thanks to Interuptic for the tip).

If you’re copying and pasting this script, make sure you replace the ^M near the bottom of the script with a literal ^M (normally you type Ctrl-V then Ctrl-M to get a literal ^M). Alternatively, grab the script from GitHub.

The script will take the vCard file you specify as input and create a subdirectory called photos containing the photos from your contacts:


progname=$(basename $0)
usage="Usage:t$progname -f vCard_file nt$progname -h"

while getopts "f:h" options ; do
  case $options in
    f) vcard_file=$OPTARG ;;
    h) echo -e $usage ; exit ;;

if [ ! "$vcard_file" ] ; then
  echo -e $usage
  exit 1

if [[ ! $(head -1 $vcard_file) =~ "BEGIN:VCARD" ]] ; then
  echo "$vcard_file does not appear to be a vCard file"
  exit 1

if ! which base64 > /dev/null ; then
  echo "base64 is not installed"
  exit 1

if ! which convert > /dev/null ; then
  echo "ImageMagick is not installed"
  echo "Either install it or comment out the ImageMagick stuff"
  exit 1

if [ -d photos ] ; then
  echo "photos directory already exists"
  exit 1

if ! mkdir photos ; then
  echo "Failed to create photos directory"
  exit 1


echo -ne "Extracting photos"
cat $vcard_file | while read line ; do
  if [[ $line =~ $regex1 ]] ; then
    filename=$(echo $line | awk -F '[:;]' '{printf("%s%s",$3,$2)}' | sed 's/[^a-zA-Z]//g')
    echo -ne "."
    cat /dev/null > photos/$filename.tmp
  elif [[ $line =~ $regex2 ]] ; then
    echo $line | sed 's/PHOTO;ENCODING=b;TYPE=JPEG://' >> photos/$filename.tmp
  elif [[ ! $line =~ $regex3 ]] ; then
    echo $line >> photos/$filename.tmp
echo -ne " donen"

echo -ne "Removing empty photos..."
find photos -empty -exec rm -f {} +
echo -ne " donen"

echo -ne "Converting photos"
cd photos
for file in *.tmp ; do
  filename=$(echo $file | awk -F '.' '{print $1}')
  echo -ne "."
  cat $file | tr -d 'n' | tr -d '^M' | base64 -d > $filename.jpg
  convert $filename.jpg -resize 201x201 $filename.jpg
  rm -f $file
echo -ne " donen"

Get a continuously updating display of client IP addresses on a web server using X-Forwarded-For

Sometimes it’s desirable to have a continuously updating display of the IP addresses which are hitting a web server, with an indication of how many times each IP address has made a request. This may be because you suspect a DoS or DDoS attack, or there may appear to be some other odd activity, or you may simply be curious. If a web server is sitting directly on the Internet then it’s possible to do this fairly easily with a tool such as netstat. Often, however, a web server is behind an ELB or another type of load balancer, which means that if you try to use netstat then you’ll just see the load balancer’s IP address, not the address of the client which made the request. But if your load balancer is passing the X-Forwarded-For header (as it really ought to be) then you can use this header instead of the client IP to get a continuously updating display.

First, you’ll need to make sure the tools ngrep and watch are installed. Then copy and paste the following Bash script (replacing eth0 with the correct interface if it’s not eth0) (or grab it from GitHub):


ngrep -il -d eth0 -W byline "x-forwarded-for" "port 80" | grep -i x-forwarded-for | 
  awk -F '[., ]' '{printf( "%s.%s.%s.%sn", $2,$3,$4,$5 );}' > /tmp/ngrep.tmp &

watch -tn 10 'cat /tmp/ngrep.tmp | sort -n | uniq -c | sort -nr | head -30'

When you run this script you should see a continuously updating display which looks something like this:


It may start to slow down after a while. If that happens then just hit CTRL-C and run it again.

Python script to convert ICS-exported List from Reminders app to plaintext

I make extensive use of the Reminders app* in OS X to keep track of tasks and to-do items, and I wanted a way to export a list of reminders to plaintext, so I knocked up a quick Python script to take an ICS file exported from a List in Reminders (which you can do from the File menu) and output it in plaintext. If this is something you find yourself needing to do then this might work for you.

Grab the script from GitHub or copy and paste it below:

# -*- coding: utf-8 -*-

import re
from optparse import OptionParser

parser = OptionParser()
parser.add_option("-f", "--file", dest="filename", metavar="FILE", 
  help="read reminders from ICS FILE")
parser.add_option("-c", "--include-completed", action="store_true", 
  dest="include_completed", help="include completed tasks")
(options, args) = parser.parse_args()

if not options.filename:
  print "No filename specified; -h for help"

with open(options.filename) as file:
  ics_export = file.readlines()

todos = []
for ics_line in ics_export:
  if "STATUS:COMPLETED" in ics_line and options.include_completed is None:
    exclude = True
  elif "STATUS:" in ics_line:
    exclude = False
  if "SUMMARY:" in ics_line and exclude is False:
    bits = re.split(":", ics_line)

todos.sort(key=lambda y: y.lower())
for todo in todos:
  print "•", todo

* Synced to my iPhone and other Macs via ownCloud on my Linux server, but that’s a story for another day.

An Open Letter to BT

Dear BT,

All I wanted you to do was move my number from my old flat to my new flat and give me a SIM in time for me to pass it to my broadband provider so that I didn’t have any loss in broadband service during my move. What followed was weeks of diabolical communication and total and utter incompetence, leading to an enormous amount of stress, hassle and wasted time both for me and for my broadband provider.

As soon as possible I will be getting an alternative broadband solution which doesn’t require a BT line so that I don’t have to use BT ever again. You’re a truly awful company, I don’t ever want to have to deal with you again either for personal or professional use, and I’ve been making sure all my colleagues, friends and family know how utterly useless and incompetent you are.

Kind regards,

Matt Brock.