I recently donated a whole bunch of Dell server to ISC to help upgrade f.root-servers.net.


They all had Dell DRAC4 cards in them and I wanted to reset them to a known config before donating them.

Dell does provide some Linux software to talk to the cards, but is is mostly binary packages that run under RedHat Enterprise server or CentOS. Various folks have managed to make racadm and the other tools work under other distributions, but it is a headache to setup.


I needed to wipe and stage 50 servers or so and I wanted to be able to do it in as simple a manner as possiable. I didn't want to have to wipe the drives, install CentOS, wipe the DRAC card, then reinstall with the real OS, so I ended up writing the below code. It will talk to the DRAC card over the internal serial port and should run on any OS (including FreeBSD and netBSD) that run Python.






# $Revision:: 16                                           $


# $Date:: 2010-01-25 20:01:49 -0500 (Mon, 25 Jan 2010)     $


# $Author:: wkumari                                        $


# Copyright: Warren Kumari (This email address is being protected from spambots. You need JavaScript enabled to view it.) --  2010






This tries to connect to a DRAC4 card in a Dell server and


resets the password, IP, mask and gateway.


It assumes that the DRAC card will show up on /dev/ttyS1


and that you have pySerial installed.


It is neither pretty nor elegant, but I needed to reset the


DRAC card on a bunch ofservers and this just works.


It does no error checking, make completely brick your system,


may cuase early baldness, etc.




--ip: The IP address to set the DRAC to.


--mask: The netmask (dotted quad).


--gw: The gateway to use.




./rac_reset.py --ip= --mask= --gw=




import getopt


import sys


import time




import serial




print ('Unable to import pySerial module.\n'


'On Ubuntu (and similar) you may be able to fix this with:\n'


'apt-get install python-serial')




PORT = '/dev/ttyS1'


FILTER=''.join([(len(repr(chr(x)))==3) and chr(x) or '.' for x in range(256)])


OK_STRING = '\x02\x60\x0A\x00\x00\x00\x00\x00\x00\x00\x96\x03'


CALVIN_MD5 = 'e6e66b8981c1030d5650da159e79539a'


def rac_connect():


s = serial.Serial(PORT, timeout = 10)


if s.isOpen():


print 'Connected to: %s' % s.portstr




print "Unable to open: %s", s.portstr




return s


def xmit(str):




print 'Going to send: %s' % str






data = ''


if s.inWaiting:


data = data + s.read(s.inWaiting())




while s.inWaiting():


data = data + s.read(s.inWaiting())




if data:


if data == OK_STRING:


print 'Success!'




print "Got an error: %s\n" % dump(data, len(data))




print 'Got no reply!'




# And give the serial port a bit to settle down.




def dump(src, length):


"""Prints the input in hex notation."""


N=0; result=''


while src:


s,src = src[:length],src[length:]


hexa = ' '.join(["%02X"%ord(x) for x in s])


s = s.translate(FILTER)


result += "%04X   %-*s   %s\n" % (N, length*3, hexa, s)




return result


def checksum(str):


"""Takes a string an calculates the RAC checksum.


The RAC checksum seems to be made by adding all


the charaters in the string mod 256 and then


negating that..




str: A string that we want the checksum for.


Returns: A char to be appended to the str to


make the checksum correct.




a = 0


for char in str:


a = a + ord (char)


a = a % 256


checksum = 256 - a


return chr(checksum)


def make_str(command, cmd_no):


"""Returns a string suitable to be handed to the RAC.


It looks like the DRAC expects a struct with various


bits filled in with, um, something. This takes a


command (like "racdump") and returns it embedded in


the struct.




command: A string containing a command (e.g: "racdump")


cmd_no : An integer, how many cammands we have run. It


appears that the DRAC *may* uses this so that it can


have multiple outstanding commands. Looks like not


actually used.




A string, suitable to be passed to the DRAC socket. Includes




PREFIX = "\2"


PAD = "\0"


SUFFIX = "\3"


length = chr(len(command) + 6)


command_str = ("P" + length + PAD + chr(cmd_no) + \


command + PAD)


str  = (PREFIX + command_str)


str = str + checksum(command_str) + SUFFIX


return str


def usage():


print '%s' % __doc__


if __name__ == "__main__":




opts, args = getopt.getopt(sys.argv[1:], "hi:m:g:v",


["help", "ip=", 'mask=', 'gw='])


except getopt.GetoptError, err:


print 'Option %s' % err




verbose = False


ip = ''


mask = ''


gw = ''


for o, a in opts:


if o == "-v":


verbose = True


elif o in ("-h", "--help"):






elif o in ("-i", "--ip"):


ip = a


elif o in ("-m", "--mask"):


mask = a


elif o in ("-g", "--gw"):


gw = a




assert False, "unhandled option"


if not ip or not mask or not gw:


print 'Error: must supply --ip, --mask and --gw!'




print 'Welcome to the RAC reset utility.'


print 'This will reset the DRAC4 root password to "calvin",'


print 'the IP to %s, the mask to %s and the gateway to %s\n' % (


ip, mask, gw)


#  ip = raw_input('Please enter IP: ')


#  mask = raw_input('Please enter mask: ')


#  gw = raw_input('Please enter gateway: ')


cmd_no = 0


xmit(make_str('setoid -g cfgUserAdmin -o cfgUserAdminPassword -i 1 %s' % CALVIN_MD5, cmd_no))


xmit(make_str('setoid -g cfgLanNetworking -o cfgNicGateway %s' % gw.strip(),cmd_no))


xmit(make_str('setoid -g cfgLanNetworking -o cfgNicNetmask %s' % mask.strip(),cmd_no))


xmit(make_str('setoid -g cfgLanNetworking -o cfgNicIpAddress %s' % ip.strip(), cmd_no))


print 'Done!'


Here is a direct link: rac_reset.py