Tuesday, August 9, 2011

Clearing my mental hang-ups about Perl

The script below does some simple things to demonstrate to myself how a number of things work in Perl. Simple things, but ones that I sometimes have to stop and think twice about.

use Config::Auto;

# Test Config::Auto output, which should be a reference
my $ca = Config::Auto->new(
source => "test.conf",
format => "equal"
);

my $config = $ca->parse;

print "\$config is a " . ref($config) . " reference\n" if (ref $config);
print "c_one undefined\n" unless (defined $config->{'c_one'});
print "c_two undefined\n" unless (defined $config->{'c_two'});
print "c_three undefined\n" unless (defined $config->{'c_three'});
print "c_one non-existent\n" unless (exists $config->{'c_one'});
print "c_two non-existent\n" unless (exists $config->{'c_two'});
print "c_three non-existent\n" unless (exists $config->{'c_three'});


# Test a non-referenced hash
my %harsh=(
'one' => 1,
'two',
);
print "\$harsh is a " . ref($harsh) . " reference\n" if (ref $harsh);
print "one undefined\n" unless (defined $harsh{'one'});
print "two undefined\n" unless (defined $harsh{'two'});
print "three undefined\n" unless (defined $harsh{'three'});
print "one non-existent\n" unless (exists $harsh{'one'});
print "two non-existent\n" unless (exists $harsh{'two'});
print "three non-existent\n" unless (exists $harsh{'three'});

# Test array interpolation
my @array = ("one", "two");
print "\@array interpolated is: @array\n\@array not interpolated is " . @array . "\n\$\#array is $#array\n";

The test.conf file referred to above contains the following:

c_one = 1
c_two =

The following output is generated by the script:

$config is a HASH reference
c_three undefined
c_three non-existent
two undefined
three undefined
three non-existent
@array interpolated is: one two
@array not interpolated is 2
$#array is 1

Thursday, August 4, 2011

Renaming user-defined ZFS properties

Here is a short script I cooked up to rename the namespace of all the ZFS user-defined properties on a host. Useful if you need to change com.foo:beans to com.bar:beans for more than a couple of properties on a couple of filesystems.

#!/usr/local/bin/perl

#
# This script looks at the properties for every zfs filesystem and snapshot on
# a server and changes every occurance of a property in in namespace $old to
# namespace $old.
#

use strict;

my ($old, $new) = ("com.foo", "com.bar");
my $overwrite = "yes"; # overwrite existing $new properties
my $localonly = "yes"; # do not move inherited properties

for my $fs ( `zfs list -Ho name` ) {
chomp $fs;
&do_rename($fs);
}

for my $snap ( `zfs list -Ho name -t snapshot` ) {
chomp $snap;
&do_rename($snap);
}

sub do_rename {
#print "Renaming $old to $new on $_[0]\n";
# get list of properties with $old
for my $prop ( `zfs get -Ho property,source,value all $_[0] | grep $old` ) {
$prop =~ m/$old:([a-z._:][a-z.\-_:]*)\t([a-z0-9\/\- ]+)\t(.*)\n/;
#print "Examining property $old:$1 on $_[0] with value $3\n";
my ($suffix, $value) = ($1, $3);
# local check, if enabled
next if (( $localonly eq "yes" ) and ( $2 ne "local" ));
next if (( $overwrite eq "no" ) and ( &check_exists($_[0],"$old:$suffix") == 0 ));
print "Setting $new:$suffix=$value and inheriting (erasing) $old:$suffix on $_[0]\n";
( system("zfs set $new:$suffix=$value $_[0]") == 0 ) or die "Error during zfs set operation";
( system("zfs inherit $old:$suffix $_[0]") == 0 ) or die "Error during zfs inherit operation";
}
}

sub check_exists {
# Return 0 if the specified property [1] exists on object [0]
if ( system("zfs get -Ho value $_[1] $_[0] | grep -qE '^-\$'") == 0 ) {
# DNE
return 1;
} else {
print "$_[1] exists on $_[0]!\n";
return 0;
}
}

Monday, August 1, 2011

Undocumented zfs command

In the process of tracking down a ZFS mount problem (btw, a zpool will not mount automatically if the mount point exists), I realized that my ZFS swap volume was not available to FreeBSD. After some poking around, I found the /etc/rc.d/zvol script, which led me to the 'zfs volinit' command. This command is not documented in the man page or command usage, but it seems to be the goo that gets volumes going. Also, set the org.freebsd:swap=on property on your swap volume, to have it started automatically by the rc scripts.

Wednesday, July 27, 2011

I/O errors on zfs import?

I've been working on freshening up our backup scripts and I noticed that every time I do a ZFS import, the kernel logs I/O errors on RAID volumes, completely separate from the actual volume I want to import, including already mounted ZFS pools and UFS filesystems. For what it's worth, this behavior was occurring on a Dell r610, with an attached MD1000.

Jul 25 13:59:54 leopard kernel: mfi0: I/O error, status= 12 scsi_status= 0
Jul 25 13:59:54 leopard kernel: mfi0: sense error 0, sense_key 0, asc 0, ascq 0
Jul 25 13:59:54 leopard kernel: mfid1: hard error cmd=read 0-255

It was suggested that I should update the firmware on the disks, so this morning I went and updated all the disks, and the PERC 6/E. Voila! No more I/O errors on import.

For reference, here is the link to the firmware download I used. It is a windows executable that allows you to generate a bootable USB key that contains the firmware updater for the disks. I also used the underlying DOS environment to apply the firmware update for the PERC 6/E, and our brand new PERC H800 that came from Dell with ancient firmware.

Tuesday, June 14, 2011

Apple DNS cache, and a bourne revelation [to me]

I learned a couple of interesting things today. The first is that Mac OS X, or at least some applications, cache DNS results. This is irritating, but fortunately there is a way to clear out the cache. Run the following command in a terminal to clear out the cache.

dscacheutil -flushcache

On the FreeBSD side of life, I had a minor revelation related to bourne (sh) scripting. I have occasionally wondered if it was possible to build the name of a variable dynamically. When I've wondered aloud, the answer I've received has always been that this is not possible. Today, I was reading through /etc/network.subr and I happened upon this bit of code.

if [ -n "${static_routes}" ]; then
for i in ${static_routes}; do
eval route_args=\$route_${i}
route add ${route_args}
done
fi

Like a beam of light from Heaven, I suddenly realized that this little eval statement is the answer to my hopes. It allows you to build a variable name on the fly!

Friday, April 22, 2011

FreeBSD CARP+BRIDGE+VLAN=BAD

Bridge, good. VLANs, great. CARP, awesome. BRIDGE+VLAN+CARP, pwned. We decided to purchase a pair of atom-based systems for use as the office firewalls. The only thing we weren't completely pleased with was the single Ethernet port. Given that we have been using vlans elsewhere on the network, we didn't expect that there would be any problems using vlans for all interfaces. It turned out to be a big box of pain. In addition to routing problems, we also appear to have aggravated a bug that hangs the system.

VLAN+CARP is a fairly common configuration on firewalls. Our office and DC LANs use the same subnet and are bridged over an OpenVPN tunnel. Trying to incorporate VLAN+CARP into into a bridge seems to cause problems. This diagram illustrates our logical network setup.



After a lot of trial and error, a number of conclusions were drawn.

  • Routing over the bridge doesn't work the same when using vlans. The VPN server pushes a route to our production network when clients connect. When the office firewall was using a physical Ethernet interface for the LAN, this route would refer to the LAN interface as the outgoing interface for this connection. This seems counter-intuitive, but it worked just fine. When the Ethernet LAN interface was replaced with a vlan, the tap (VPN) interface was referenced by the route to production. This seemed to be more logical, except that the Production network became unreachable.
  • After some troubleshooting, I figured out that access to the production network could be fixed by adding a static route to the DC firewall (next-hop to production network) pointing out the tap interface. This seemed to allow traffic to flow smoothly to production.
  • Adding CARP into the above configuration caused the firewall to hang randomly. There seemed to be no indication of a crash, no excessive resource use or network traffic.
  • Routing traffic between tagged vlans and the underlying physical interface may be problematic. This was an earlier configuration I tried, and it seemed to have issues. However, at the time I had not identified CARP as the source of the system hangs, so this may be a non-issue.

The routing issue was reported in a PR that can be found here. The routing tables mentioned above can be found here.

Sunday, April 17, 2011

Enabling Audigy SPDIF in Windows 7

Reinstalled Windows 7 the other day and spent an hour figuring out how to enable digital audio output for my SoundBlaster Audigy Platinum. When you open the Sound properties you will see "Speakers" and "SPDIF Out" listed as playback devices. Futz with SPDIF all you want, but you won't get any audio output from digital. I finally opened the properties for "Speakers" and found the magic option under the "Custom" tab. The only option listed is a check box for "Digital Output Only". Click that box, hit apply, and enjoy digital output.