March 2010 Archives

"You Need To Learn Spanish"

During a recent stay at a hotel in Los Angeles, California, USA, I held the elevator door for a flight attendant from an international airline as she approached with two large pieces of luggage.  As she entered the elevator, she thanked me by saying "Gracias."  I asked which floor, to which she responded, "Tres."  It took me a moment, but I eventually translated it to three and pressed the button.  She then matter-of-factly stated, "You need to learn Spanish."  I shook my head and said, "No, I don't -- why?"  She replied, "Because it's easy.  Everybody should know Spanish."  I then asked if she was from Spain, to which she somewhat indignantly replied as she was stepping-out of the car, "No, Chile."

I'm dumbfounded by her attitude -- so much so that I don't really know what to say.  The fact is, I'm unapologetic about not knowing how to speak in her native tongue, or any other tongue for that matter.  I'm not opposed to being multilingual, but I am opposed to and take offense to a visitor to this country to expect me (or any other American) to speak their language.  I don't think I need to mention the hypocrisy of the stewardess' attitude if I were to demand the same of her if the situation were reversed.  Now, go back to Chile, young lady, and don't come back until you learn some manners!

Human History 101

For those that don't know about history, here is a condensed version:

Humans originally existed as members of small bands of nomadic hunters/gatherers. They lived on deer in the mountains during the summer and would go to the coast and live on fish and lobster in the winter.

The two most important events in all of history were the invention of beer and the invention of the wheel.  The wheel was invented to get man to the beer.  These were the foundation of modern civilization and together were the catalyst for the splitting of humanity into two distinct subgroups: liberals and conservatives.

Once beer was discovered, it required grain and that was the beginning of agriculture.  Neither the glass bottle nor aluminum can were invented yet, so while our early humans were sitting around waiting for them to be invented, they just stayed close to the brewery.  That's how villages were formed.

Some men spent their days tracking and killing animals to BBQ at night while they were drinking beer.  This was the beginning of what is known as the conservative movement.

Other men who were weaker and less-skilled at hunting learned to live off the conservatives by showing up for the nightly BBQs and doing the sewing, fetching, and hairdressing. This was the beginning of the liberal movement.

Some of these liberal men eventually evolved into women.  Those became known as girlie-men. Some noteworthy liberal achievements include the domestication of cats, the invention of group therapy, group hugs, and the concept of democratic voting to decide how to divide the meat and beer that conservatives provided.

Over the years conservatives came to be symbolized by the largest, most powerful land animal on earth, the elephant.  Liberals, conversely, are symbolized by the jackass.

Modern liberals like imported beer but most prefer white wine or imported bottled water.  They eat raw fish, but like their beef well done.  Sushi, tofu, and French food are standard liberal fare.  Another interesting evolutionary side note: most of their women have higher testosterone levels than their men. Most social workers, personal injury attorneys, journalists, dreamers in Hollywood, and group therapists are liberals. Liberals invented the designated hitter rule because it wasn't fair to make the pitcher also bat.

Conservatives drink domestic beer, mostly Bud Lite, Bud, or Miller. They eat red meat and still provide for their women. Conservatives are big-game hunters, rodeo cowboys, lumberjacks, construction workers, firemen, medical doctors, police officers, engineers, corporate executives, athletes, members of the military, airline pilots, and generally anyone who works productively.  Conservatives who own companies hire other conservatives who want to work for a living.

Liberals produce little or nothing. They like to govern the producers and decide what to do with the production.  Liberals believe Europeans are more enlightened than Americans. That is why most of the liberals remained in Europe when conservatives were coming to America.  They crept in after the Wild West was tamed and created a business of trying to get more for nothing.

End of lesson.

Hat tip: Joe G. (via an e-mail)

Rock-Solid Reliability

Rock-solid reliability.  That's something to which even the most Ritalin-addled, Kool-Aid drinking Microsoft "admin" would never be able to honestly claim to experience in their career.  In the UNIX world, however, it is commonplace and, at times, even noteworthy.  One of the database servers for which I am (was) responsible was migrated to new hardware and subsequently decommissioned.  Before powering-down the system, I captured a screenshot of the uptime data:
doors-uptime-redacted.pngIt was shutdown 1,132 (or so) days prior to move it from its original server room to the new datacenter.

PostgreSQL INET/CIDR Functionality

Some time ago, I thought I needed to be able to programmatically calculate properly aligned networks containing the netmask in dotted-quad (as opposed to CIDR) format (i.e. 192.168.1.0/255.255.255.0). That need is [thankfully] all but a distant memory, but as I was perusing the 8.4.3 documentation this afternoon, I glanced at the network address functions and operators and recalled some of the difficulties I encountered when coding the initial utility in Perl. I thought about exercising some of those network address functions by redoing it in PostgreSQL.

The Helper Functions

The first task was to create a function to calculate the decimal (BIGINT) equivalent of a properly formatted IPv4 address. PostgreSQL's INET and CIDR datatypes are ideal for this because of the address validation they provide (which makes life significantly easier).

CREATE OR REPLACE FUNCTION inet_to_longip(v_t INET)
RETURNS BIGINT AS /*{{{*/
$inet_to_longip$
DECLARE
    t TEXT[];
    i BIGINT;

BEGIN
    t := REGEXP_SPLIT_TO_ARRAY(HOST(v_t), E'\\.');
    i := (t[1]::BIGINT << 24) + (t[2]::BIGINT << 16) +
            (t[3]::BIGINT << 8) + t[4]::BIGINT;
    RETURN i;
END;
$inet_to_longip$ LANGUAGE plpgsql STRICT IMMUTABLE; /*}}}*/
Once we can obtain the decimal (BIGINT) version of the IP address, we need to be able to return it to an INET or CIDR datatype. I couldn't find in the documentation where each of the two datatypes was simply a string, but that's what I discovered as I naively concatenated all the numbers and dots into one. In the interest of full disclosure, I found the basis of this function on the obsolete Wiki of an apparently defunct Linux firewall project that stored the rules in a PostgreSQL v7.4 database.
CREATE OR REPLACE FUNCTION longip_to_inet(v_i BIGINT)
RETURNS INET AS /*{{{*/
$longip_to_inet$
DECLARE
    t INET;

BEGIN
    t = ((v_i >> 24) & 255::BIGINT) || '.' ||
        ((v_i >> 16) & 255::BIGINT) || '.' ||
        ((v_i >> 8)  & 255::BIGINT) || '.' ||
        (v_i         & 255::BIGINT);
    RETURN t;
END;
$longip_to_inet$ LANGUAGE plpgsql STRICT IMMUTABLE; /*}}}*/
The last required piece is a function to determine the most significant bit of what will become the new netmask in the resultant properly-aligned network.
CREATE OR REPLACE FUNCTION netmask_msb(v_i BIGINT)
RETURNS INTEGER AS /*{{{*/
$netmask_msb$
DECLARE
    n INTEGER;

BEGIN
    FOR n IN REVERSE 31 .. 1 LOOP
        IF (v_i & (1 << n)) > 0 THEN
            RETURN n;
        END IF;
    END LOOP;
    RETURN n;
END;
$netmask_msb$ LANGUAGE plpgsql STRICT IMMUTABLE; /*}}}*/

A Bonus Function

As an added bonus, I decided to write a quick function to determine the natural class of the network. Years ago, there was no such thing as CIDR, and this was as granular (with classes A, B, and C, anyway) as a block of IP addresses could be.

CREATE OR REPLACE FUNCTION natural_class(v_t INET)
RETURNS CHAR AS /*{{{*/
$natural_class$
DECLARE
    o1 BIGINT := inet_to_longip(v_t) >> 28;

BEGIN
    CASE
        WHEN o1 >> 3 =  0 THEN RETURN('A');
        WHEN o1 >> 2 =  2 THEN RETURN('B');
        WHEN o1 >> 1 =  6 THEN RETURN('C');
        WHEN o1      = 14 THEN RETURN('D');
        WHEN o1      = 15 THEN RETURN('E');
    END CASE;
    RETURN NULL;
END;
$natural_class$ LANGUAGE plpgsql STRICT IMMUTABLE; /*}}}*/
Now that we have the ability to convert the addresses in both directions and do some minor bit-banging to determine what the new netmask should be, let's put it all together. For my purposes, all I needed was the new network and netmask in dotted-quad format. Below, you'll find a few more pieces of information that may or may not be of interest. I can definitely see a use for outputting the data in binary format.
CREATE OR REPLACE FUNCTION align(  /*{{{*/
    IN v_t1 INET, IN v_t2 INET,
    OUT oldstart INET, OUT oldend INET, OUT oldhosts INTEGER,
    OUT newstart INET, OUT newend INET, OUT newhosts INTEGER,
    OUT cidrmask INT,  OUT dqmask INET) RETURNS RECORD AS
$align$
DECLARE
    v_oldstart INET := HOST(v_t1);
    v_oldend   INET := HOST(v_t2);
    --
    v_oldstart_long BIGINT := inet_to_longip(v_oldstart);
    v_oldend_long   BIGINT := inet_to_longip(v_oldend);
    ipmask          BIGINT := v_oldstart_long # v_oldend_long;
    maskmsb         INTEGER := netmask_msb(ipmask);
    new_ipmask      BIGINT := (1 << (maskmsb + 1)) - 1;
    v_newstart_long BIGINT := v_oldstart_long & ~new_ipmask;
    v_newend_long   BIGINT := v_oldend_long | new_ipmask;
    v_newstart INET := longip_to_inet(v_newstart_long);
    v_newend   INET := longip_to_inet(v_newend_long);
    v_newnet   INET :=SET_MASKLEN(v_newstart::cidr,
                           32 - (maskmsb + 1));

BEGIN
    if (v_t1 > v_t2) THEN
        RAISE EXCEPTION
           'Starting address must be lower than ending address';
    END IF;

    oldstart := v_oldstart;
    oldend   := v_oldend;
    oldhosts := v_t2 - v_t1 + 1;
    newstart := v_newstart;
    newend   := v_newend;
    newhosts := v_newend - v_newstart + 1;
    cidrmask := MASKLEN(v_newnet);
    dqmask   := NETMASK(v_newnet);
    RETURN;
END;
$align$ LANGUAGE plpgsql STRICT IMMUTABLE; /*}}}*/

Next Step(s)

There are probably a few inefficiencies in the code I've presented in this tutorial of sorts. I'll leave finding and fixing them (and the binary output) as an exercise for the reader. Admittedly, some of the functionality could be made easier by creating casts to go between the two data formats.

Conclusion

That's all there is to it. Now, when you're trying to configure a VPN device that will be installed at a remote location and all the information you have is information from the receptionist via voicemail (e.g. "Our allowable numbers from the internet people are sixty-six point eighty-eight point one hundred and two point thirty-three to sixty-six point eighty-eight point one hundred and two point thirty-seven), you have access to enough information to mostly configure it before you ship it ahead of your trip to wherever that device may be going (hopefully someplace warm).

Let me know what you think.

Just over a year ago, I chose to use Movable Type for my blogging software because of its support for PostgreSQL.  As of this afternoon, after performing some preliminary investigation into whether or not I should upgrade, I discovered that, much to my dismay, version 5 of Movable Type no longer supports PostgreSQL as a database backend.  Six Apart states that they now will support only MySQL in the open-source version, and adding Oracle and MSSQL to their enterprise version.  Oracle, I understand.  I also understand MSSQL, but still have difficulty understanding how so many will entrust their enterprise-level anything to anything Microsoft.  As for MySQL (and in a somewhat similar manner to MSSQL), however, I cannot help but ask why?  Why choose a product that is technologically inferior to PostgreSQL?  Why choose a product that will possibly be mired in licensing issues or, worse, may not be around in its current incarnation in the near future?  Why are so many attracted to MySQL when in all my experience (admittedly minimal) it is nothing more than a toy, a training "database?"

About this Archive

This page is an archive of entries from March 2010 listed from newest to oldest.

October 2009 is the previous archive.

June 2010 is the next archive.

Find recent content on the main index or look in the archives to find all content.

Categories