micolous.id.au

The result of a blogging accident

Linux iSCSI COW Images, and Windows integration.

More stuff for RetroLAN PCs, yay!

I’ve now got all the RetroLAN PCs running out of a copy-on-write image. This means I now have a single 4GB “base” image with an installation of Windows XP, instead of having 10 of them. It also means it is very trivial to reset the machine’s disk images, which I achieved with a bit of scripting.

First up, I needed to setup the copy-on-write images through `device-mapper`. This program is designed to let you setup software RAID, but it also allows snapshotting disks to provide a backup of a device at a point in time. Unfortunately, `dmsetup` doesn’t support accessing file images, only actual devices, so I need to create some loopback devices first.

# setup loop0 as the "master" image, and mark it as read-only so no changes ever get written back.
losetup -r /dev/loop0 /store/master.img
# we need to get the size of the image in sectors for use later on.
cow_size=`blockdev --getsize /dev/loop0`

The next step is to create a blank image file and loopback device to commit changes to. For my setup, I created 200MB images. Be aware of things like disk-based caches and automatic defragmentation will use up the allocated space very quickly, and when you run out of space in this file further writes will not be allowed, and Windows will soon crash with a blue screen of death. (More on dealing with this in a bit)

# remove any existing COW image
rm /store/pc-1.cow# create a new 200MB image (it's actually comes out 5% more than stated, but it allows for overheads in the filesystem)
dd if=/dev/zero of=/store/pc-1.cow bs=1M count=200# now hook it to a loopback device
losetup /dev/loop1 /store/pc-1.cow

Now we have two devices, `/dev/loop0` which contains the master image, and `/dev/loop1` which will contain any changes made to the disk. The next step is to use dmsetup to create a `device mapper` which will layer the COW image ontop of the original image.

# create the cow!
echo "0 ${cow_size} snapshot /dev/loop0 /dev/loop1 p 64" | dmsetup create pc1

This will create a device called `/dev/mapper/pc1`. You can use it in your iSCSI setup with something like this in your `/etc/ietd.conf`:

Target iqn.2010-01.lan.someplace.iscsihost:pc1
        Lun 0 Path=/dev/mapper/pc1,Type=fileio

You can then start `ietd`.

Now the next part is writing a script to let you reset the COW images. `ietd` does not like you changing things while it is running, so you’ll need to make sure your script suspends all disk activity before blanking out the COW image. Something like this:

# disconnect the COW image #1
ietadm --op delete --tid 1 --lun 0# suspend dm activity
dmsetup suspend pc1# clear the cow cache
dd if=/dev/zero of=/dev/loop1# reload the cow table
master="/dev/loop0"
cow_size=`blockdev --getsize ${master}`
echo "0 ${cow_size} snapshot ${master} /dev/loop1 p 64" | dmsetup reload pc1# resume dm activity
dmsetup resume pc1# reconnect the COW image #1
ietadm --op new --tid 1 --lun 0 --params Path=/dev/mapper/pc1

You could change this script around so that it allows you to pass in a PC number as a command-line argument, and then hook in a CGI script that will call it to reset it remotely. In my setup, I’m using gPXE to boot from iSCSI, but it can also be used to access a URL via HTTP. So your CGI script reads in the request IP address, and if it is one of the resettable machines, it will reset it’s COW image. Then it sends back a gPXE script instructing it to run a DOS .com file that will reboot the computer. For this to work you need to have your CGI script execute the command as the superuser (root).

One of the things about the COWs is if you reset them or they become full while Windows is running, Windows will very soon crash, and refuse to boot up again. To better inform both yourself and users about the usage levels, you can read the information from `dmsetup`:

# dmsetup status pc8
0 8385930 snapshot 55296/409600

In this example, it is indicating that the full image is 8385930 sectors, and 55296 of 409600 sectors are used in the COW image. So I wrote a Python CGI script that reports this in a parsable fashion to clients:

#!/usr/bin/env pythonprint "Content-Type: text/plain\r\n\r"from os import environ
from subprocess import Popen, PIPEip = environ['REMOTE_ADDR']
d = int(ip.split(".")[3])
if d >= 41 and d <= 54:
  machine = d - 40
  try:
    p = Popen(('/sbin/dmsetup', 'status', 'retrocow%d' % machine), stdout=PIPE, stderr=PIPE)
    stdout, stderr = p.communicate()
    use, total = stdout.split(' ')[3].split('/')
    use, total = long(use), long(total)
    print "OK %d %d" % (use, total)
  except:
    print "ERR Problem getting parsing information"
else:
  # return error
  print "ERR Not a RetroLAN Machine"

For me, the RetroLAN machines have IPs ending in 41 to 54. That corresponds to their PC number (retro-01 to retro-14). This script outputs the following text file when requested via CGI if successful:

OK 55296 409600

This isn’t very usable for the end user, so I wrote a .NET 2.0 app called `CowBell` that runs in the system tray showing the percentage of COW usage, and notifies them if they’re over 75% usage (which means they will crash soon). I’ve made source code available for download, which you’ll need to modify `frmMain.cs` line 37 to include the path to where you put the CGI script, and uncomment the line otherwise it will not compile. If you make modifications to this program I do ask that you share your improvements with me.

This is the message that constantly pops up when you’re over 75% usage:

The application refreshes it’s status every 10 seconds, and if you right-click the icon it shows the COW usage in sectors.

Update 2010-10-05: Dealing with duplicate names

Something which I omitted from the original version of this post was how to deal with the duplicate names on the network. In my configuration, I’ve set the computer name PC-00 on the master iSCSI image.

I then setup a DNS and WINS server. On the DNS server, I created forward and reverse entries for each of the computers on iSCSI (eg: pc-01.example.lan). On the WINS server, I used Samba to make it proxy DNS (dns proxy = yes), but that shouldn’t be required as Windows will fall back to using DNS after failing WINS (and DNS is faster than WINS). The benefit to having the WINS server is for non-iSCSI clients register their names to it, so they can still be looked up without NetBIOS.

I configured the master image to use this WINS server and search domain (eg: example.lan), as DHCP options are ignored when booting from iSCSI with the exception of the DNS server. I then disabled NetBIOS over TCP/IP manually (see Microsoft Knowledge Base article 323357).

Now, Windows does not try to discover other hosts, or advertise it’s presence using NetBIOS. You can still connect to other clients via normal UNC names (eg: \\PC-01), and if your non-iSCSI clients have DNS entries created by DHCP or WINS configured, it will allow those to be discovered as well. Normally when Windows detects a duplicate computer name on the network it will pop up a notification - with these changes the computer name can be “wrong” according to Windows and it won’t matter or complain.

Mark Russinovich (of SysInternals fame, now working for Microsoft) wrote that duplicate SIDs for the most part don’t matter, so having duplicate SIDs isn’t really a problem either. Though I don’t run these clients on a domain - but according to the post that shouldn’t matter either.

If you’re using a script to perform an action based on the computer’s name, you’ll have to modify it to look up the computer’s DNS name from it’s IP address rather than getting it from Windows. I’ve included an example of this in an earlier post where I wrote a VBScript to change the wallpaper based on the computer’s name as reported by DNS. I use this with StreetGeek’s RetroLAN setup to show the computer’s hostname in the top right corner of the screen.

Something else you should be aware with in this configuration is your licensing requirements. All the machines will have the same product key. You may be able to use this setup if you have a Volume Licensing Agreement with Microsoft for Windows (where you have one key that is used for all installations of a Windows version in your organisation). Some organisations believe that having the same product key where the key is not a volume licensing key, even when your organisation holds enough licenses for that version of Windows, that it is illegal. I’m not a lawyer though, and you should seek legal advice about before deploying this.