July 2009


I’m sure I’m not the first person to discover this, but it has come up twice in the past few days (once for the code review tool I maintain, and the other for a project-to-be-named later), so I thought I’d toss it out as interesting.

Let’s say you’re writing an app that needs to process TF changesets. You might pull some changesets with a history query …

changesets = VersionControl.QueryHistory("$/Project", VersionSpec.Latest, 0, RecursionType.Full, null, 1, VersionSpec.Latest, int.MaxValue, true, true, true);

… or you might grab the changeset directly …

changeset = VersionControl.GetChangeset(12345);

Regardless, you now have a shiny Changeset object that will tell you all about what happened at that point in time. So you start looking through the changes in the changeset, and you see some Adds, some Renames, some Deletes, some Rename | Edits, etc. So now you can present a list to the user with the things that got updated, one item per change in the changeset. Except…

I found that I need to pay attention to more than just the change type. For example, if you have a directory with a bunch of deleted files, and you move the directory, the changeset for the directory move will include a change with ChangeType.Rename for every file under that directory, including deleted files! The only indication you’ll get is that the change.Item will have a DeletionId value of something other than 0.

I saw this issue on the code review tool when a user asked why an apparent rename was giving a diff of two empty files. The answer was that the file had been deleted before the rename, so it didn’t really exist on either side of the rename. It was just dragged along. So, I should have checked DeletionId!

Also because of this behavior, the project-to-be-named-later ended up adding files to a directory that weren’t actually supposed to be there. Before I write the new version of files in the changeset, I check DeletionId… if it’s not 0, then I ignore the file.

So, if you’re consuming TF changesets, and you want to ignore files that were deleted in the past, be sure to check the change.Item.DeletionId property!

In my earlier post on getting a Ubuntu VM running on VPC, I neglected one important and usually insignificant detail: how to disable udev for the VM’s NICs.

The problem with udev is that it effectively disables the VM’s NIC under certain conditions. To understand why, it helps to make some observations about the various modules involved.

Udev is the device manager for Linux 2.6 systems. It is responsible for populating /dev with only the devices that are present. It provides notification to other software when devices are changed. And, most importantly for this blog post, it provides “persistent naming for devices when they move around the device tree.” So, if you move your USB cabling and routers around, your machine should still be able to identify, say, your printer. Or if you add a new NIC and your system detects it before your existing eth0, your old NIC will still be eth0 and your new one will be eth1. This is pretty awesome, and it helps resolve some of the problems I remember having with old PCs, trying to guess which device name maps to which physical device.

On Hyper-V (or Virtual Server, or Virtual PC), NICs are usually virtual, so they get assigned virtual MAC addresses. The default configuration is to let Windows pick the MAC address. These MAC addresses are typically stable, unless you do something like recreate the NIC or move the VM to a new host.

The scenario that this blog post is concerned about is this: you set up a VM on one host, and then you move it to another host. You might move it for any number of reasons: you set it up on VPC and want to permanently host it on Hyper-V, or your Hyper-V hosting situation changes and you need to migrate VMs around, or whatever. Following the move, your VM is no longer on the network! Bad news.

So, when you’re setting up your VM, you need to consider how udev will interact with Hyper-V. Some possible solutions include assigning a static MAC address to the NIC, or making udev smart enough to realize that if the old NIC went away and a new one is in its place, that it should use the new one in place of the old one. My solution is to make udev ignore eth*, since I usually am configuring a Linux VM to run some particular service, and I will not be changing the network configuration in any significant way. My solution is likely to be affected by upgrades because it modifies files that are generated by scripts.

And now, to the point of this post: how to make udev ignore your eth* devices:

  1. Open /etc/udev/rules.d/75-persistent-net-generator.rules in your favorite text editor.
  2. On line 21, where it says KERNEL!="eth*|ath*… remove eth*|. For example, on my system, I start with
    KERNEL!="eth*|ath*|wlan*[0-9]|ra*|sta*|ctc*|lcs*|hsi*", GOTO="persistent_net_generator_end"
    and end up with
    KERNEL!="ath*|wlan*[0-9]|ra*|sta*|ctc*|lcs*|hsi*", GOTO="persistent_net_generator_end"
  3. If udev has already renamed your nic, you’ll also need to open /etc/udev/rules.d/70-persistent-net.rules and remove any eth* entries you find.

This should work on any Linux with udev (kernel >= 2.6). Line 21 is the right one to change on Ubuntu 8.10 and 8.04.2 systems that I’ve configured this way.