Wednesday, June 15, 2011

Professional Update and July CCDE Practice Exams

I'm back! What do I mean by this? After several months at a new job in the financial industry, my family and I decided that relocating to the New York metropolitan area wasn't for us. I regret not having figured that out before taking the new job, but I believe this was one of those decisions that couldn't have been made until it became real. Sincere "Thank You's" go out to everyone at my former employer. What an incredible group of people.

"I'm back" means that I intend to post here considerably more often than I have so far this year. I won't be telling stories of my time in finance due to the sensitivity of that industry, but I'm sure I can find interesting things to say. "I'm back" also means I will again be offering my CCDE Practice Exams (details below). It also means that I am returning to my previous employer. They were gracious to invite me back to work, and I am excited to return.


July CCDE Practice Exams

I have been asked by several CCDE candidates to offer my practice exams earlier in the testing cycle. It was my expectation that most candidates would like to use this offering as a refresher at the end of their study plan. It turns out many would like to begin their studies with an exam to determine where they stand in relation to the test. I hadn't thought of this, but it makes perfect sense to me. Therefore, I am offering my CCDE Practice Exam sessions in July, three months prior to the October 21st testing date. I am considering offering the sessions again in September/October for procrastinators as well :) This will also make the training available to candidates who are attending Cisco Live the week of July 10th.

The Enterprise-focused practice exam review session is scheduled for Wednesday, July 13th at 5pm ET. The Service Provider-focused session will be on Saturday, July 16th at 12pm ET (noon). Links to the registration pages for each session, and for the combination of the two can be found below:

Enterprise Registration Page

Service Provider Registration Page

Combo (both exams at a discounted price)


If you have any questions, please email me at jeremy@filliben.com. I look forward to your participation, and I'm excited to be returning to blogging!

Jeremy

Monday, March 14, 2011

April CCDE Practice Exams

My CCDE practice exam offering has been quite successful. Participant feedback has been extremely positive, and I greatly appreciate the constructive suggestions that have been provided. I've made several updates to the materials, and I look forward to delivering these exams again in April.

Upcoming CCDE Practice Exams

Registration is now open for these practice exams in April 2011. The Enterprise exam will be delivered to participants on Wednesday, April 6th 2011. The review session is scheduled for Saturday, April 9th @ 12pm (noon) EDT. The Service Provider exam will be delivered to participants on Wednesday,April 13th and the review will take place on Saturday, April 16th @ 12pm (noon) EDT. If you are interested in the offering, but the dates listed above do not work for you, let me know. We can work to find an alternate date/time to complete the review session.

The Registration pages for this offering can be found at:

Enterprise Exam

Service Provider Exam

Combo (Both exams at a discounted cost)

Please email me with any questions you may have (jeremy@filliben.com). As before, the first hour of the review session will include my presentation and an open discussion on the CCDE Practice Exam (format, testing experience, future developments). The CCDE program is constantly being updated, and I do my best to keep this information up to date. Former CCDE Practice Exam participants are invited to join this session to receive updated information.

Other News

I'm sorry I haven't been posting to this blog regularly. My new job has been a bit like drinking from a fire hose. There is a ton of new information to process. In addition I've been working to contribute as much as possible to my new company's projects. Once things settle down a bit I intend to resume writing.

Tuesday, December 21, 2010

Professional Life Update – Winter 2010

Life hasn’t slowed down for the holidays in 2010!  Earlier this week I accepted a job with a new employer.  I greatly enjoyed my time with Genworth Financial, and I’ll miss working with my team and the larger IT department there.  It was a wonderful 6+ years with Genworth, and nearly a decade within the General Electric organization.

I expect that my new position will keep me very busy.  For this reason, I have bowed out of teaching CCDE courses for CCBOOTCAMP.  I want to thank Dawn & Dawn, as well as Brad, for their guidance and support in 2010.  I grew quite a bit as both an engineer and communicator through my experiences with CCBOOTCAMP, and if my schedule ever allows it, I would be happy to teach again in the future.

So as not to leave CCDE candidates ‘high and dry’, I will continue to offer my CCDE Practical practice exams on a regular schedule.  Details about the January offering can be found at http://www.jeremyfilliben.com/2010/12/january-ccde-practice-exams.html.  The response has been very positive, and I am looking forward to working with the candidates in a few weeks.

Even with the new responsibilities, I intend to post occasionally to this blog and keep active in various other social media universes.  Here’s a rundown of how/why I use various tools.  I thought this might be helpful to the community.  At the very least it’ll explain why I ignore most Facebook friend requests :)

Twitter – I’m on twitter @jfilliben.  I use this primarily for networking industry purposes.  I view twitter as a river of information, sort of like CNN Headline News or ESPN News. I don’t have time to watch it all day, but when I have a moment, I like to see what’s going on in the networking world.  I’m fairly good at keeping track of DMs and replies, but I miss a lot of the daily chatter.

LinkedIn – I’m on LinkedIn under the username jfilliben.  I have been a fan of LinkedIn for a long time.  In my early career I moved  from job to job fairly frequently, as did many others.  LinkedIn became a way to keep in contact with my former co-workers and to keep an eye out for new opportunities.  I am open to linking with anyone who has an interest or need to contact me.  I do not see a significant downside to linking with people who I don’t know terribly well.  I suppose this can serve as a warning to anyone in my LinkedIn network.. Just because I’m linked to someone doesn’t mean I would vouch for them.

Facebook – I only joined Facebook because my wife told me to.  She handles the family social circle, so I don’t see a great need for me to be on it.  It was fun reconnecting with a few former classmates, but I still don’t see the point to it all.  I pretty much keep my work life and personal life separate, so I don’t normally “friend” professional associates on Facebook.  Occasionally I’ll “like” something work-related, like the Facebook LISP group.

Blogs – I write a professional one, but you already knew that.  Prior to creating this blog I used this URL in an unsuccessful campaign for a local political office, so occasionally you’ll see a reference to that in search engines.  I also started a blog for my surname, but I abandoned it awhile ago and never got around to deleting it.  It hosts a single picture of the family from 2008.

I read a large number of technical blogs, and that number grows weekly.  I primarily use RSS via Google Reader.  If a blog doesn’t do RSS, I can’t remember to follow it.  And if a blog only publishes summaries to RSS, the content better be great or I’ll unsubscribe.  Yes, I’m talking about Greg, Jeremy and Ivan here.  I don’t think there are any other technical blogs that I’m willing to click-thru to read.  I would list my favorite blogs, but then I would forget someone and feelings would be hurt.  Lately I’ve been adding some storage and blade server blogs to the list.  Hopefully it is just a phase :)

I also read a few dozen non-technical blogs, mostly in the areas of sports, finance, economics and politics.  In fact, I get most of my daily news from blogs and websites.  I haven’t subscribed to a paper newspaper (ever!) and I don’t care for local TV news.

As for how I read all this content… It’s about a 50/50 split between my Blackberry and my laptop.  I own a Kindle, but I only use it to read traditional books.  I’m sure I would love an iPad, but I already spend enough time looking at digital screens.  I don’t want another temptation.  The Blackberry is less-than-ideal for reading, and I will almost never ‘click-thru’ on it because of the poor web browser, but it meets my needs most of the time.

What Else? – I think that’s about it.  I don’t understand the interest in location-based apps like Foursquare.  Why would I care where other people are at the moment?  I’m busy enough on my own; I don’t want to force myself into even more interactions when I’m at the supermarket, etc.  I’ve seen it used to good effect during professional conferences like Cisco Live, but that’s about it.

I also don’t bother with the ‘next best’ versions of the above apps.  So no Google Buzz or Plaxo for me, thanks.  I have enough to read as it is, so I have not used Digg or its equivalents.  I did not get very involved in web chatting apps either, although I have accounts on Yahoo Messenger, AIM and Google Chat (is that what they call it?).

Thursday, December 9, 2010

January CCDE Practice Exams

Last month I presented my first CCDE practice exams to the public.  The offering was a success, and two of the participants went on the take the November CCDE Practical Exam.  Both indicated that the offering was a great help for their preparation.

 

Upcoming CCDE Practice Exams

Registration is now open for these practice exams in January 2011.  The Enterprise exam will be delivered to participants on Wednesday, January 5th.  The review session is scheduled for Saturday, January 8th @ 9am EST.  The Service Provider exam will be delivered to participants on Wednesday, January 12th and the review will take place on Saturday, January 15th @ 9am EST.  One item of feedback I received from the initial offering was that it would be nice to have more time between the practice exams and the real exam.  This schedule gives candidates four weeks to digest the information and reach out to me for follow-up guidance before taking the exam on February 15th.  I was also asked to move the review sessions to the weekend to accommodate busy work schedules.  If you are interested in the offering, but the dates listed above do not work for you, let me know.  We can work to find an alternate date/time to complete the review session.

The Registration pages for this offering can be found at:

Enterprise Exam

Service Provider Exam

Combo (Both exams at a discounted cost)

Please email me with any questions you may have (jeremy@filliben.com).   As before, the first hour of the review session will include my presentation and an open discussion on the CCDE Practice Exam (format, testing experience, future developments).  The CCDE program is constantly being updated, and I do my best to keep this information up to date.  Former CCDE Practice Exam participants are invited to join this session to receive updated information.

Other News

I regret to announce that I will not be teaching the upcoming live CCDE Practical Bootcamp offered by CCBootcamp.  After reviewing my work/life balance for 2010, and my already full professional calendar for 2011, I cannot find the time to participate in this program.  I wish CCBootcamp the best of luck with their CCDE training.  I am confident that they will find a wonderful instructor to take my place.

Friday, November 19, 2010

Python Texas Hold’em Simulator

Update

I finally got around to looking at this again, and I decided to re-write the program. I also posted the new version to GitHub. You can find it at https://github.com/jfilliben/poker-sim. Feel free to disregard the below info, except perhaps to learn a lot of things you shouldn't do with Python ;)


Original

In addition to my practical Python script for Cisco switch configuration verification, I wrote a purely impractical Texas Hold’em Simulator.  I enjoy the game, and it occurred to me that a poker simulator would be a fun way to explore Monte Carlo simulations.  The following program takes as an input a count of simulations to perform and two player hands, plus five community cards.  For any community card specified with “Xx”, it will randomly select a card and play out the hand.  At it’s simplest form, you can run it with one iteration and a fully-specified set of cards, and it will tell you which hand wins:
C:>python.exe pokersim.py 1 Ac7d 8sKd 2s3s4s4c4d
Total Hands: 1
Hand1: 1 Hand2: 0 Ties: 0
Hand1: 100.0% Hand2: 0.0% Ties: 0.0%

Or you can leave a couple cards ‘blank’ and see what random turn and river cards will provide:
C:>python.exe pokersim.py 1000 Ac7d 8sKd 2s3sXxXxXx
Total Hands: 1000
Hand1: 612 Hand2: 385 Ties: 3
Hand1: 61.2% Hand2: 38.5% Ties: 0.3%
(note, this program uses a Monte Carlo simulation, so there is a measure of randomness to the results.  If you run the same test multiple times, you will almost always get slightly different results.  And of course, the more iterations you choose to do, the more accurate the results)
Here is the script.  Again, any comments would be appreciated.  If there is a more efficient way to accomplish portions of this program, I’d love to hear of them.  And if you spot any errors, please let me know.  As a disclaimer, there is no guarantee that the results of this script are accurate.  I don’t use this for profit in any way, it was purely a thought exercise for me.
Run it with no options (or “——help”) to get a brief description of the options.
----------
pokersim.py:
#
# pokersim.py - Runs a Monte Carlo simulation of two Texas Hold'em hands
#               with user-specified (or random) community cards
#
# Work to be done:
# Add exhaustive search?
# Compare speed of copy.deepcopy and hand_copy()
# Add input checking for user input
#
import sys
import random
def hand_copy(cards):
#
# Replace copy.deepcopy with specific copy function to speed things up
# (presumably, but not tested)
#
    results = []
    for i, v in enumerate(cards):
        results.append(cards[i])
    return results
def legal_hand(cards):
#
# Returns 1 if hand is legal
# Returns 0 if hand is illegal (two of same card)
#
    for i, v in enumerate(cards):
        if cards.count(v) > 1: return 0
        elif cards.count([-1, -1]): return 0
    return 1
def valid_card(card):
#
# Returns 1 if card is a valid card in text format (rank in (A-2),
#  suit in (c, d, h, s) or wildcard (Xx)
# Returns 0 if card is invalid
#
    if card[0] in ("X", "x", "A", "a", "K", "k", "Q", "q", "J", "j"\
    , "T", "t", "9", "8", "7", "6", "5", "4", "3", "2"):
        if card[1] in ("x", "X", "c", "C", "d", "D", "h", "H", "s", "S"):
            return 1
    else: return 0
def readable_hand(cards):
#
# Returns a readable version of a set of cards
#
    string = ""
    for i, v in enumerate(cards):
        if v[0] == 0: string += "2"
        elif v[0] == 1: string += "3"
        elif v[0] == 2: string += "4"
        elif v[0] == 3: string += "5"
        elif v[0] == 4: string += "6"
        elif v[0] == 5: string += "7"
        elif v[0] == 6: string += "8"
        elif v[0] == 7: string += "9"
        elif v[0] == 8: string += "T"
        elif v[0] == 9: string += "J"
        elif v[0] == 10: string += "Q"
        elif v[0] == 11: string += "K"
        elif v[0] == 12: string += "A"
        elif v[0] == -1: string += "X"
        if v[1] == 0: string += "c"
        elif v[1] == 1: string += "d"
        elif v[1] == 2: string += "h"
        elif v[1] == 3: string += "s"
        elif v[1] == -1: string += "x"
    return string
def hand_to_numeric(cards):
#
# Converts alphanumeric hand to numeric values for easier comparisons
# Also sorts cards based on rank
#
    result = []
    for i, v in enumerate(cards):
        currentcard = [0, 0]
        if cards[i][0] == "2": currentcard[0] = 0
        elif cards[i][0] == "3": currentcard[0] = 1
        elif cards[i][0] == "4": currentcard[0] = 2
        elif cards[i][0] == "5": currentcard[0] = 3
        elif cards[i][0] == "6": currentcard[0] = 4
        elif cards[i][0] == "7": currentcard[0] = 5
        elif cards[i][0] == "8": currentcard[0] = 6
        elif cards[i][0] == "9": currentcard[0] = 7
        elif cards[i][0] in ("t","T"): currentcard[0] = 8
        elif cards[i][0] in ("j","J"): currentcard[0] = 9
        elif cards[i][0] in ("q","Q"): currentcard[0] = 10
        elif cards[i][0] in ("k","K"): currentcard[0] = 11
        elif cards[i][0] in ("a","A"): currentcard[0] = 12
        elif cards[i][0] in ("x","X"): currentcard[0] = -1
        if cards[i][1] in ("c","C"): currentcard[1] = 0
        elif cards[i][1] in ("d","D"): currentcard[1] = 1
        elif cards[i][1] in ("h","H"): currentcard[1] = 2
        elif cards[i][1] in ("s","S"): currentcard[1] = 3
        elif cards[i][1] in ("x","X"): currentcard[1] = -1
        result.append(currentcard)
    result.sort()
    result.reverse()
    return result
def check_flush(hand):
# Return 0 if not true
# Return 1 if true
#
# Initialization
#
    hand_suit = []
    hand_suit.append(hand[0][1])
    hand_suit.append(hand[1][1])
    hand_suit.append(hand[2][1])
    hand_suit.append(hand[3][1])
    hand_suit.append(hand[4][1])
    for i in range(0, 4):
        if hand_suit.count(i) == 5: return 1
    return 0
def check_straight(hand):
# Return 0 if not true
# Return 1 if true
    if hand[0][0] == (hand[1][0] + 1) == (hand[2][0] + 2) == (hand[3][0] + 3)\
    == (hand[4][0] + 4): return 1
    elif (hand[0][0] == 12) and (hand[1][0] == 3) and (hand[2][0] == 2)\
    and (hand[3][0] == 1) and (hand[4][0] == 0): return 1
    return 0
def check_straightflush(hand):
# Return 0 if not true
# Return 1 if true
    if check_flush(hand) and check_straight(hand): return 1
    return 0
def check_fourofakind(hand):
# Return 0 if not true
# Return 1 if true
# Also returns rank of four of a kind card and rank of fifth card
# (garbage value if no four of a kind)
    hand_rank = []
    hand_rank.append(hand[0][0])
    hand_rank.append(hand[1][0])
    hand_rank.append(hand[2][0])
    hand_rank.append(hand[3][0])
    hand_rank.append(hand[4][0])
    for value in range (0, 13):
        if hand_rank.count(value) == 4:
            for n in range (0, 13):
                if hand_rank.count(n) == 1: return 1, value, n
    return 0, 13, 13
def check_fullhouse(hand):
# Return 0 if not true
# Return 1 if true
# Also returns rank of three of a kind card and two of a kind card
# (garbage values if no full house)
    hand_rank = []
    hand_rank.append(hand[0][0])
    hand_rank.append(hand[1][0])
    hand_rank.append(hand[2][0])
    hand_rank.append(hand[3][0])
    hand_rank.append(hand[4][0])
    for value in range(0, 13):
        if hand_rank.count(value) == 3:
            for n in range(0, 13):
                if hand_rank.count(n) == 2: return 1, value, n
    return 0, 13, 13
def check_threeofakind(hand):
# Return 0 if not true
# Return 1 if true
# Also returns rank of three of a kind card and remaining two cards
# (garbage values if no three of a kind)
    hand_rank = []
    hand_rank.append(hand[0][0])
    hand_rank.append(hand[1][0])
    hand_rank.append(hand[2][0])
    hand_rank.append(hand[3][0])
    hand_rank.append(hand[4][0])
    for value in range(0, 13):
        if hand_rank.count(value) == 3:
            for n in range(0, 13):
                if hand_rank.count(n) == 1:
                    for m in range(n+1, 13):
                        if hand_rank.count(m) == 1: return 1, value, [m, n]
    return 0, 13, [13, 13]
def check_twopair(hand):
# Return 0 if not true
# Return 1 if true
# Also returns ranks of paired cards and remaining card
# (garbage values if no two pair)
    value = 0
    hand_rank = []
    hand_rank.append(hand[0][0])
    hand_rank.append(hand[1][0])
    hand_rank.append(hand[2][0])
    hand_rank.append(hand[3][0])
    hand_rank.append(hand[4][0])
    for value in range(0, 13):
        if hand_rank.count(value) == 2:
            for n in range(value+1, 13):
                if hand_rank.count(n) == 2:
                    for m in range(0, 13):
                        if hand_rank.count(m) == 1: return 1, [n, value], m
    return 0, [13, 13], 13
def check_onepair(hand):
# Return 0 if not true
# Return 1 if true
# Also returns ranks of paired cards and remaining three cards
# (garbage values if no pair)
    hand_rank = []
    hand_rank.append(hand[0][0])
    hand_rank.append(hand[1][0])
    hand_rank.append(hand[2][0])
    hand_rank.append(hand[3][0])
    hand_rank.append(hand[4][0])
    for value in range(0, 13):
        if hand_rank.count(value) == 2:
            for n in range (0, 13):
                if hand_rank.count(n) == 1:
                    for m in range(n+1, 13):
                        if hand_rank.count(m) == 1:
                            for o in range (m+1, 13):
                                if hand_rank.count(o) == 1: return 1, value, [o, m, n]
    return 0, 13, [13, 13, 13]
def highest_card(hand1, hand2):
# Return 0 if hand1 is higher
# Return 1 if hand2 is higher
# Return 2 if equal
#
# Initialization
#
    hand1_rank = []
    hand1_rank.append(hand1[0][0])
    hand1_rank.append(hand1[1][0])
    hand1_rank.append(hand1[2][0])
    hand1_rank.append(hand1[3][0])
    hand1_rank.append(hand1[4][0])
    hand2_rank = []
    hand2_rank.append(hand2[0][0])
    hand2_rank.append(hand2[1][0])
    hand2_rank.append(hand2[2][0])
    hand2_rank.append(hand2[3][0])
    hand2_rank.append(hand2[4][0])
#
# Compare
#
    if hand1_rank > hand2_rank: return 0
    elif hand1_rank < hand2_rank: return 1
    return 2
def highest_card_straight(hand1, hand2):
# Return 0 if hand1 is higher
# Return 1 if hand2 is higher
# Return 2 if equal
#
# Compare second card first (to account for Ace low straights)
# if equal, we could have Ace low straight, so compare first card. 
# If first card is Ace, that is the lower straight
#
    if hand1[1][0] > hand2[1][0]: return 0
    elif hand1[1][0] < hand2[1][0]: return 1
    elif hand1[0][0] > hand2[0][0]: return 1
    elif hand1[0][0] < hand2[0][0]: return 0
    return 2
def compare_hands(hand1, hand2):
#
# Compare two hands
# Return 0 if hand1 is better
# Return 1 if hand2 is better
# Return 2 if equal
#
#
# Initialization
#
    result1 = []
    result2 = []
#
# Check for straight flush
#
    if check_straightflush(hand1):
        if check_straightflush(hand2):
            return(highest_card_straight(hand1, hand2))
        else: return 0
    elif check_straightflush(hand2): return 1
#
# Check for four of a kind
#
    result1 = check_fourofakind(hand1)
    result2 = check_fourofakind(hand2)
    if result1[0] == 1:
        if result2[0] == 1:
            if result1[1] > result2[1]: return 0
            elif result1[1] < result2[1]: return 1
            elif result1[2] > result2[2]: return 0
            elif result1[2] < result2[2]: return 1
            else: return 2
        else: return 0
    elif result2[0] == 1: return 1
#
# Check for full house
#
    result1 = check_fullhouse(hand1)
    result2 = check_fullhouse(hand2)
    if result1[0] == 1:
        if result2[0] == 1:
            if result1[1] > result2[1]: return 0
            elif result1[1] < result2[1]: return 1
            elif result1[2] > result2[2]: return 0
            elif result1[2] < result2[2]: return 1
            else: return 2
        else: return 0
    elif result2[0] == 1: return 1
#
# Check for flush
#
    if check_flush(hand1):
        if check_flush(hand2):
            return(highest_card(hand1, hand2))
        else: return 0
    elif check_flush(hand2): return 1
#
# Check for straight
#
    if check_straight(hand1):
        if check_straight(hand2):
            temp = highest_card_straight(hand1, hand2)
            return temp
        else: return 0
    elif check_straight(hand2): return 1
#
# Check for three of a kind
#
    result1 = check_threeofakind(hand1)
    result2 = check_threeofakind(hand2)
    if result1[0] == 1:
        if result2[0] == 1:
            if result1[1] > result2[1]: return 0
            elif result1[1] < result2[1]: return 1
            elif result1[2] > result2[2]: return 0
            elif result1[2] < result2[2]: return 1
            else: return 2
        else: return 0
    elif result2[0] == 1: return 1
#
# Check for two pair
#
    result1 = check_twopair(hand1)
    result2 = check_twopair(hand2)
    if result1[0] == 1:
        if result2[0] == 1:
            if result1[1] > result2[1]: return 0
            elif result1[1] < result2[1]: return 1
            elif result1[2] > result2[2]: return 0
            elif result1[2] < result2[2]: return 1
            else: return 2
        else: return 0
    elif result2[0] == 1: return 1
#
# Check for one pair
#
    result1 = check_onepair(hand1)
    result2 = check_onepair(hand2)
    if result1[0] == 1:
        if result2[0] == 1:
            if result1[1] > result2[1]: return 0
            elif result1[1] < result2[1]: return 1
            elif result1[2] > result2[2]: return 0
            elif result1[2] < result2[2]: return 1
            else: return 2
        else: return 0
    elif result2[0] == 1: return 1
    return (highest_card(hand1, hand2))
def bestfive(hand, community):
#
# Takes hand and community cards in numeric form and returns best five cards
#
    currentbest = hand_copy(community)
    currentbest.sort()
    currentbest.reverse()
    m = 0
#
# Compare current best to five cards including only one player card
#
    for m in range (0, 2):
        for n in range (0, 5):
            comparehand = hand_copy(community)
            comparehand[n] = hand[m]
            comparehand.sort()
            comparehand.reverse()
            if compare_hands(currentbest, comparehand) == 1:
                currentbest = hand_copy(comparehand)
#
# Compare current best to five cards including both player cards
#
    for m in range (0, 5):
        for n in range (m+1, 5):
            comparehand = hand_copy(community)
            comparehand[m] = hand[0]
            comparehand[n] = hand[1]
            comparehand.sort()
            comparehand.reverse()
            if compare_hands(currentbest, comparehand) == 1:
                currentbest = hand_copy(comparehand)
    return currentbest
#
# Main Program Body
#
#
# Initialization
#
hand1 = []
handnum1 = []
best_hand1 = []
hand2 = []
handnum2 = []
best_hand2 = []
community = []
communitytemp = []
totals = [0,0,0]
iterations = 0
#
# Process command-line arguments
#
if (len(sys.argv) == 1) or (sys.argv[1] in ("-h", "--help")):
        sys.exit("\n\
First input is number of iterations to run the Monte Carlo simulation\n\
Input cards in format [RANK][SUIT], as in Ace Clubs + Four Diamonds = Ac4d)\n\
Input should be two cards for player 1, two cards for player 2 and five community cards\n\
Wildcards should be written as Xx (capital X for rank, lower-case x for suit)\n\
Wildcards should be placed at the end of the community hand\n\n\
--help: This message\n")
else:
    iterations = int(sys.argv[1])
    if iterations < 1: iterations = 1
    if valid_card(sys.argv[2][0:2]): hand1.append(sys.argv[2][0:2])
    else: sys.exit("Player 1 Card 1 Invalid")
    if valid_card(sys.argv[2][2:4]): hand1.append(sys.argv[2][2:4])
    else: sys.exit("Player 1 Card 2 Invalid")
    if valid_card(sys.argv[3][0:2]): hand2.append(sys.argv[3][0:2])
    else: sys.exit("Player 2 Card 1 Invalid")
    if valid_card(sys.argv[3][2:4]): hand2.append(sys.argv[3][2:4])
    else: sys.exit("Player 2 Card 2 Invalid")
    if valid_card(sys.argv[4][0:2]): community.append(sys.argv[4][0:2])
    else: sys.exit("Community Card 1 Invalid")
    if valid_card(sys.argv[4][2:4]): community.append(sys.argv[4][2:4])
    else: sys.exit("Community Card 2 Invalid")
    if valid_card(sys.argv[4][4:6]): community.append(sys.argv[4][4:6])
    else: sys.exit("Community Card 3 Invalid")
    if valid_card(sys.argv[4][6:8]): community.append(sys.argv[4][6:8])
    else: sys.exit("Community Card 4 Invalid")
    if valid_card(sys.argv[4][8:10]): community.append(sys.argv[4][8:10])
    else: sys.exit("Community Card 5 Invalid")
handnum1 = hand_to_numeric(hand1)
handnum2 = hand_to_numeric(hand2)
#
#
# Monte Carlo Simulation
#
#
for n in range (0, iterations):
    communitytemp = hand_to_numeric(community)
    while not legal_hand(handnum1 + handnum2 + communitytemp):
        for i, v in enumerate(community):
            if community[i][0] in ("X", "x"):
                communitytemp[i] = [random.randrange(0,13), random.randrange(0,4)]
    best_hand1 = bestfive(handnum1, communitytemp)
    best_hand2 = bestfive(handnum2, communitytemp)
    totals[compare_hands(best_hand1, best_hand2)] += 1
print "\nTotal Hands: " + str(totals[0]+totals[1]+totals[2])
print "Hand1: " + str(totals[0]) + " Hand2: " + str(totals[1]) + " Ties: " + str(totals[2])
print "Hand1: " + str(round((100*(totals[0])/((totals[0]+totals[1]+totals[2])+0.0)), 2))\
+ "% Hand2: " + str(round(((100*totals[1])/((totals[0]+totals[1]+totals[2])+0.0)), 2))\
+ "% Ties: " + str(round(((100*totals[2])/((totals[0]+totals[1]+totals[2])+0.0)), 2)) + "%"

Switchport Verification Script

A commenter on my previous post directed me to the module CiscoConfParse which would have likely made this a much easier exercise.  Even so, this was a great exercise for me to go through, as it allowed me to learn a new programming language.
I’ll still post my solution to this problem, along with the supporting files.  The first file below is the Python script.  Running it with a –h option will show you the proper format for the parameters.  The supporting file “verification.txt” is also listed below.  It describes the format to use for the verification seed file.
If you happen to see anything wrong with this script, or room for improvement, please share your thoughts via comment or email.  I am always looking to learn, and I know there is room for improvement in my Python programming!
Jeremy

--------------------------
configcheck.py:
#
# configcheck.py - A script that verifies the existence or
#   absence of specific configuration lines in a Cisco switch config
#
import os
import re
import sys
import getopt
#
# Checks router interface configuration against a base config
#
def get_config_snmp():
    pass
def get_config_file(filename):
    """getconfig loads a router config and returns an array with individual lines"""
    if os.path.isfile(filename) == 0:
        sys.exit("ERROR: File does not exist")
    contents = []
    with open(filename, 'r') as file:
        for a in file: contents.append(a)
    file.closed
#
# Clear '/r', '/n', ' ' characters from file (to allow interchangeability between Unix/Windows)
#
    for x in range(0, len(contents)):
        if len(contents[x]) > 1:
            while contents[x][-1:] in ('\r', '\n', ' '): contents[x] = contents[x][:-1]
    return contents
def gethostname(config):
    """gethostname retrieves the device hostname from configuration file"""
    n = 0
    for n in range (0, len(config)):
        if len(config[n-1]) > 9:
            if config[n-1][:8] == "hostname":
                return config[n-1][9:]
    return "No Hostname Found"
def check_interfaces(config, int_type, verify_config):
    """check_interfaces retrieves interface configuration\
from the config and sends it to verify"""
    n = -1
    while n <= (len(config) - 2):
        n += 1
        if len(config[n]) > 9:
            if config[n][:9] == "interface":
                interface = []
                interface.append(config[n])
                n += 1
                while config[n][0] == ' ':
                    interface.append(config[n])
                    n += 1
                if re.search(int_type, interface[1]):
                    verify(interface, verify_config)
def verify(interface, verify_config):
    """verify takes an interface config and a list of required statements\
and verifies that each is present"""
    s = "\n\n! The Following Interface is Missing the Command(s) Below: \n\n"\
    + interface[0] + '\n' + interface[1]
    error = 0
    for a in verify_config:
        if a[1] == "!":
           b = " " + a[2:] + " "
           if interface.count(b):
               error += 1
               if b[1:2] == "no":
                   s += " " + b[3:] + '\n'
               else:
                   s += " no" + b + '\n'
        elif not interface.count(a):
            error += 1
            s += a + '\n'
#Print Error Interfaces
    if error: print s
#
# Main Program Body
#
#
# Process command-line arguments
#
config = []
verify_file = "verification.txt"
try:
    opts, args = getopt.getopt(sys.argv[1:], "hs:f:v:", ["help", "snmp=", "file=", "verify="])
except getopt.GetoptError, err:
    # print help information and exit:
    print str(err) # will print something like "option -a not recognized"
    sys.exit(2)
for o, a in opts:
    if o in ("-h", "--help"):
        sys.exit("\nOptions:\n\
-s, --snmp: Use SNMP to retrieve configuration (not implemented in this version)\n\
-f, --file: Load configuration from local file\n\
-v, --verify: Load verification file from local file (default is verification.txt)\n\
-h, --help: This message")
    elif o in ("-v", "--verify"):
        verify_file = a
    elif o in ("-s", "--snmp"):
        config = get_config_snmp(a)
        break
    elif o in ("-f", "--file"):
        config = get_config_file(a)
        break
    else:
        assert False, "unhandled option"
print "\n\n\nHostname (from configuration file): " + str(gethostname(config))
#
# Cycle through verification.txt to retrieve individual verification configuration
#
contents = []
with open(verify_file, 'r') as file:
    for a in file:
        if len(a) >= 4:
            if a[0] == '[':
                int_type = a[1:5]
            elif a[0] == ' ':
#
# Clear '/r', '/n', ' ' characters from file (to allow interchangeability between Unix/Windows)
#
                while a[-1:] in ('\r', '\n', ' '): a = a[:-1]
                contents.append(a)
            elif a[0] == "#":
                pass
        elif a[0] == "#":
            pass
        else:
            check_interfaces(config, int_type, contents)
            contents = []
file.closed
------------------------------------------------------
verification.txt:
#
# Comments begin with "#"
# Commands that must not be in the switchport configuration should begin with " !"
# Each interface type is headed with [XXXX]
# There is to be a blank line between each type
#
[ENDU]
switchport access vlan 10
switchport mode access
!mls qos trust dscp
service-policy input END-USER-IN
ip dhcp snooping limit rate 100
spanning-tree portfast
spanning-tree bpduguard enable
no logging event link-status
no snmp trap link-status
load-interval 30
switchport voice vlan 12
[SRVR]
!mls qos trust dscp
service-policy input SERVER-IN
ip dhcp snooping limit rate 100
spanning-tree portfast
spanning-tree bpduguard enable
logging event link-status
no snmp trap link-status
load-interval 30
[MPBX]
switchport mode access
mls qos trust dscp
flowcontrol receive desired
flowcontrol send off
ip dhcp snooping limit rate 100
logging event link-status
snmp trap link-status
load-interval 30
-------------
And to be complete, here is a sample switch configuration to use
sampleswitch.log:
!
hostname SampleSwitch
!
interface GigabitEthernet1/2
description ENDU; User A - Properly Configured
switchport
switchport access vlan 10
switchport mode access
switchport voice vlan 12
no logging event link-status
load-interval 30
wrr-queue bandwidth 5 25 70
wrr-queue queue-limit 5 25 40
wrr-queue random-detect min-threshold 1 80 100 100 100 100 100 100 100
wrr-queue random-detect min-threshold 2 80 100 100 100 100 100 100 100
wrr-queue random-detect min-threshold 3 50 60 70 80 90 100 100 100
wrr-queue random-detect max-threshold 1 100 100 100 100 100 100 100 100
wrr-queue random-detect max-threshold 2 100 100 100 100 100 100 100 100
wrr-queue random-detect max-threshold 3 60 70 80 90 100 100 100 100
wrr-queue cos-map 1 1 1
wrr-queue cos-map 2 1 0
wrr-queue cos-map 3 1 4
wrr-queue cos-map 3 2 2
wrr-queue cos-map 3 3 3
wrr-queue cos-map 3 4 6
wrr-queue cos-map 3 5 7
mls qos trust dscp
flowcontrol receive desired
flowcontrol send off
spanning-tree portfast
spanning-tree bpduguard enable
service-policy input END-USER-IN
ip dhcp snooping limit rate 100
no snmp trap link-status
!
interface GigabitEthernet1/3
description SRVR; Server A - Properly Configured
switchport
switchport access vlan 15
switchport mode access
logging event link-status
load-interval 30
wrr-queue bandwidth 5 25 70
wrr-queue queue-limit 5 25 40
wrr-queue random-detect min-threshold 1 80 100 100 100 100 100 100 100
wrr-queue random-detect min-threshold 2 80 100 100 100 100 100 100 100
wrr-queue random-detect min-threshold 3 50 60 70 80 90 100 100 100
wrr-queue random-detect max-threshold 1 100 100 100 100 100 100 100 100
wrr-queue random-detect max-threshold 2 100 100 100 100 100 100 100 100
wrr-queue random-detect max-threshold 3 60 70 80 90 100 100 100 100
wrr-queue cos-map 1 1 1
wrr-queue cos-map 2 1 0
wrr-queue cos-map 3 1 4
wrr-queue cos-map 3 2 2
wrr-queue cos-map 3 3 3
wrr-queue cos-map 3 4 6
wrr-queue cos-map 3 5 7
mls qos trust dscp
flowcontrol receive desired
flowcontrol send off
spanning-tree portfast
spanning-tree bpduguard enable
service-policy input SERVER-IN
ip dhcp snooping limit rate 100
no snmp trap link-status
!
interface GigabitEthernet1/4
description SRVR; Purposely Misconfigured
switchport
switchport trunk encapsulation dot1q
switchport mode trunk
logging event link-status
load-interval 30
wrr-queue bandwidth 5 25 70
wrr-queue queue-limit 5 25 40
wrr-queue random-detect min-threshold 1 80 100 100 100 100 100 100 100
wrr-queue random-detect min-threshold 2 80 100 100 100 100 100 100 100
wrr-queue random-detect min-threshold 3 50 60 70 80 90 100 100 100
wrr-queue random-detect max-threshold 1 100 100 100 100 100 100 100 100
wrr-queue random-detect max-threshold 2 100 100 100 100 100 100 100 100
wrr-queue random-detect max-threshold 3 60 70 80 90 100 100 100 100
wrr-queue cos-map 1 1 1
wrr-queue cos-map 2 1 0
wrr-queue cos-map 3 1 4
wrr-queue cos-map 3 2 2
wrr-queue cos-map 3 3 3
wrr-queue cos-map 3 4 6
wrr-queue cos-map 3 5 7
mls qos trust dscp
flowcontrol receive desired
flowcontrol send off
spanning-tree portfast
spanning-tree bpduguard enable
ip dhcp snooping trust
!
interface GigabitEthernet1/5
description ENDU; Purposely Misconfigured
switchport
switchport access vlan 30
switchport mode access
switchport voice vlan 125
no logging event link-status
load-interval 30
wrr-queue bandwidth 5 25 70
wrr-queue queue-limit 5 25 40
wrr-queue random-detect min-threshold 1 80 100 100 100 100 100 100 100
wrr-queue random-detect min-threshold 2 80 100 100 100 100 100 100 100
wrr-queue random-detect min-threshold 3 50 60 70 80 90 100 100 100
wrr-queue random-detect max-threshold 1 100 100 100 100 100 100 100 100
wrr-queue random-detect max-threshold 2 100 100 100 100 100 100 100 100
wrr-queue random-detect max-threshold 3 60 70 80 90 100 100 100 100
wrr-queue cos-map 1 1 1
wrr-queue cos-map 2 1 0
wrr-queue cos-map 3 1 4
wrr-queue cos-map 3 2 2
wrr-queue cos-map 3 3 3
wrr-queue cos-map 3 4 6
wrr-queue cos-map 3 5 7
snmp trap mac-notification change added
mls qos trust dscp
flowcontrol receive desired
flowcontrol send off
spanning-tree portfast
spanning-tree bpduguard enable
service-policy input END-USER-IN
ip dhcp snooping limit rate 100

Tuesday, November 16, 2010

Python Programming and the Network Engineer

I’ve recently rediscovered my interest in computer programming.  As I mentioned before, I once was an avid programmer.  In my youth I hacked around in C++ and x86 assembler, making interesting little programs that didn’t accomplish much, but which gave me a great sense of accomplishment.  In high school, I was a member of the third place team in a national computer programming competition.  One of my teammates is now a software engineer at Google, so maybe I wasn’t exactly the star of the show, but my contributions seemed important at the time.  :)
My college years went a long way toward suppressing any interest I had in programming computers.  The rote, repetitive routine of programming compilers, operating systems, database engines, etc, drove me directly into a career of infrastructure work.  I have no professional regrets, but I do sort of miss the thrill of writing working code.  There is a great sense of satisfaction derived from formulating an idea and seeing it through from a blank Notepad++ document to a working, debugged applicati0n.
A couple of months ago I was faced with a project that resurrected my interest in programming.  My team and I have been working steadily toward standardizing our network deployments.  Standardization is a key requirement to effectively managing a large network with a small number of engineers.  Prior to this effort, such things as VLAN numbering and port-level configurations varied between our locations, even when two offices were roughly similar in size.  One of the causes of this was the melding of several different IT organizations when my employer was created.  We also acquired several smaller companies since our genesis in 2004.
To accomplish our switchport standardization, we use the description field to identify the type of device that is connected to a specific switchport.  If a PBX is connected to a port, the description field would be something like:
interface Gigabit1/1
description PBX; Name_of_PBX

and if the port is connected to an end user, we would use:
interface Gigabit1/2
description USR; John Doe – patch-panel 1A

The text after the semi-colon is free-form, and can be defined on a site-by-site basis to be whatever is relevant to the local support team.
PBX ports require a different set of configuration statements than USR ports.  For one example, we need a “DSCP trust” statement on the PBX port, while we remark USR traffic with a specific QoS policy using the “service-policy input USR-POLICY” command.
Our standardization effort is now largely complete.  Our latest challenge is making sure our configurations stay standardized.  VLAN identifiers are easy, since it is quite painful to change them.  Switchport configurations are a very different story.  It is quite easy for a switchport to be configured incorrectly but still work in a suboptimal fashion.  For example, if the wrong QoS marking policy is installed, basic testing will work (ping, etc), but under load there will likely be performance issues.  Even if the proper template is used to activate a port, it can be difficult to prevent that port from being reused for another purpose.
My solution to this issue was to implement an audit process that would take a baseline configuration and compare it to our production switch configurations.  I first reached out to our primary network tools provider, Solarwinds.  Their current feature provides some of the features I need, such as verifying that specific lines exist in the configuration.  But they cannot yet do context-sensitive configuration checking.  Other vendors (such as Netcordia) could perform this task, but I had recently championed a network tools consolidation project, so it would be a bit hypocritical of me to request funding for a new tool.  I decided to learn a scripting language to try and tackle this need.  A friend suggested that Python or Perl would fit the need nicely, so I chose Python (for no particularly good reason) and began learning.
I was quite surprised as how easily I learned the language and was able to solve my problem.  I spent a total of eight hours from the time I settled on Python until I had a working prototype.  The small investment of time is primarily due to the ease of use of the Python language, and not my programming acumen.
The entire script is a few hundred lines, including comments, and it meets all of my needs.  I wouldn’t say it is terribly user friendly, and I have a laundry list of additional features and ‘opportunities for improvement’, but I am happy with the result.  If I can find the time (and there is interest), I’ll clean it up and publish it in separate blog post.  It is generic enough that other organizations can probably use it without a lot of rewriting.