Technical Musings

Wednesday, February 6, 2013

OSX SSH Terminal Console Coloring, Redux 2.0

Something about my previous take on this breaks autocompletion in OSX in a weird way: After I finish my first SSH session, tab completion no longer autocompletes. It does provide suggestions as a list, but will not complete the line I'm typing.
I've tried all kinds of things to get this to work, and here's my latest take: simply create a bash script and create an ssh alias to it.
/Users/USERNAME/ash.sh:
#!/bin/bash
ARGS="$@"
A="${ARGS}"
IFS=" "
set -- "$ARGS"
ARGSARRAY=( $@ )
FQDN="${ARGSARRAY[0]}"
IFS="."
set -- "${FQDN}"
FQDNARRAY=( $@ )
HOST="${FQDNARRAY[0]}"
DOMAIN="${FQDNARRAY[1]}.${FQDNARRAY[2]}"
IFS="-"
set -- "${HOST}"
MYARRAY=( $@ )
SERVERTYPE="${MYARRAY[0]}"
ENVNAME="${MYARRAY[1]}"
if [ "${ENVNAME}" = "pro" ]; then
    if [ "${serverType}" = "p19" ]; then
        PROFILE="Basic Green"
    else
        PROFILE="Basic Black"
    fi
elif [ "${ENVNAME}" = "qa" ]; then
    PROFILE="Basic Grey"
elif [ "${ENVNAME}" = "stage" ]; then
    PROFILE="Man Page"
elif [ "${ENVNAME}" = "shadow" ]; then
    PROFILE="Basic Blue"
elif [ "${DOMAIN}" = "test.info" ];then
    PROFILE="Basic Grey"
elif [ "${DOMAIN}" = "test.net" ];then
    PROFILE="Basic Black"
else
    PROFILE="Basic"
fi 
echo "tell app \"Terminal\" to set current settings of \
 first window to settings set \"${PROFILE}\"" | osascript
/opt/local/bin/ssh "${A}"

echo "tell app \"Terminal\" to set current settings of \ 
 first window to settings set \"Basic\"" | osascript 
.profile:
alias ssh='/Users/USERNAME/ash.sh'
This isn't perfect; it doesn't color my csshX sessions, and seems to mess up scp file autocompletion (you use that?). But it's reliable, you don't have to remember anything but 'ssh', and you can use other ssh parameters, as long as the hostname is the first argument.

Wednesday, January 30, 2013

'yum update' that excludes Puppet managed files

I have many machines that I manage with Puppet. If you have more than one machine, you need to use a tool like it. It does a great job of managing the configuration of packages that we use directly or are direct dependencies. But we do not put every package that is installed on a box in Puppet; I don't think anyone does this. On our boxes we have around 500 packages installed. I imagine some environment would have the time to review each rpm to ensure it was compatible, and then change it's version in Puppet, ensuring that no dependencies for that package have changed. A much more realistic approach is to just update a test machine to the latest, and then test out your application, only reviewing the changes for the packages that are direct dependencies. Even better would be to have yum update all packages except the packages you have specified in Puppet. You want all the basic upgrades, like a kernel update, without changing that version of ruby you have been using.

How to do this?

First, run puppet a puppet no operation test, to update the local catalog:

/usr/bin/puppet agent --onetime --ignorecache --server ${puppet_master} \
 --no-daemonize --verbose --detailed-exitcodes \
 --logdest /var/log/puppet/puppet.log --noop --test

Then build up a list of packages that are controlled by Puppet, and exclude them from a 'yum update' command:

packages=$(grep "reference:\ \"Package" /var/lib/puppet/client_yaml/catalog/*.yaml \
| awk -F"[" '{print $2}' \
| awk -F"]" '{print $1}')

exclude=$(for package in ${packages};do echo -n " -x ${package}";done); 

yum update ${exclude}

You could use a similar approach with other package managers like apt.

Found the data when I checked out puppet-ls

Friday, January 25, 2013

OSX SSH Terminal Console Coloring, Redux

UPDATE: http://technicalmusings.blogspot.com/2013/02/something-about-my-previous-take-on.html
I've come up with a much better, simpler recipe to color my OSX Terminal session depending on the host I'm ssh-ing into. First, take a function with some cool BASH-only splitting and arrays and add that I found a way to address the current session in Applescript. Then add a one line way to enable bash ssh auto-complete when using the function. Ensure you pass all args to SSH so you can tunnel, etc. Finish with another line to switch the colors back to some default when you disconnect. Add this to your .profile, and voila!

This function assumes a certain host naming scheme, and that you have a Terminal profile for each environment.  I just copied 'Basic', and changed the background colors.  If you can't parse your server names, you need a better naming scheme ;)

ash() {
    ARGS="$@"
    IFS=" "
    set -- $ARGS 
    ARGSARRAY=( $@ )
    HOST=${ARGSARRAY[0]}
    IFS="-"
    set -- ${HOST}
    MYARRAY=( $@ )
    SERVERTYPE=${MYARRAY[0]}
    ENVNAME=${MYARRAY[1]}
    if [ "${ENVNAME}" = "pro" ]; then
        if [ "${serverType}" = "p19" ]; then
            PROFILE="Basic Green"
        else
            PROFILE="Basic Black"
        fi
    elif [ "${ENVNAME}" = "qa" ]; then
       PROFILE="Basic Grey"
    elif [ "${ENVNAME}" = "stage" ]; then
        PROFILE="Man Page"
    elif [ "${ENVNAME}" = "shadow" ]; then
        PROFILE="Basic Blue"
    else
        PROFILE="Basic"
    fi 
    echo "tell app \"Terminal\" to set current settings of first window to settings set \"${PROFILE}\"" | osascript
    ssh "${ARGS}" 

    echo "tell app \"Terminal\" to set current settings of first window to settings set \"Basic\"" | osascript 
}
complete -o default -o nospace -F _ssh ash
 
so:
$ ash test-pro-wxy01.test.com
 
will ssh to test-pro-wxy01.test.com and set the background to black, and when I logout, the background will be set to white.

gist: https://gist.github.com/4638756

One limitation currently, is that the hostname must be the first argument.

Another slight disadvantage is that you have to type something other than 'ssh'.  You could rename the ssh binary to something else, and then name the function 'ssh'.   Don't just name the function 'ssh' w/out renaming the binary (try it and find out why!)

Ideas taken from everywhere, including: https://raw.github.com/c3w/ash/master/ash

Monday, February 6, 2012

SSH LocalCommand / OSX Terminal Colors

UPDATE: http://technicalmusings.blogspot.com/2013/01/osx-ssh-session-coloring-redux.html If you're like me, you work in many different computing environments: home, test, QA, and production.  And it's REALLY important not to get them mixed up.  I've always used a color scheme in my ssh/console windows to help me differentiate each.   I've manually set the color of each console before I log in, and try not to reuse a console for different environments.  It's kind of a pain, and if I'm brain dead enough to not notice what host I'm logged into, chances are high that I'm not going to set the colors correctly either.  So I created an OSX (I'm on 10.7) AppleScript to automatically change the Terminal colors for each of my windows/tabs to match the environment that console is logged into.

To trigger the AppleScript, I added it to an until-now-unknown to me ssh client setting: LocalCommand.  It will run a command locally each time you make an ssh connection.

To setup the trigger, you must add the following lines into ~/.ssh/config:
(Note: it's important to add the & at the end of LocalCommand to put the command in background so it can wait for Terminal to connect)
 Host *
  
   PermitLocalCommand yes
  
   LocalCommand /Users/MYNAME/applescript/term_tab_style.scpt &
  

UPDATE: And this in /etc/ssh_config:
  PermitLocalCommand yes  

The AppleScript goes through all open Terminal windows/tabs and checks the title.  This is normally set to the user@hostname, and I have a naming convention 'purpose'-'env'-'location'-'number'.  Applescript has very limited parsing capabilites; I found the functions I used here to split the hostname up and get the environment name.  You can then set the Terminal window/tab to whatever Profile you desire.

The code for term_tab_style.scpt.  Copy this code into wherever you specified the LocalCommand in ~/.ssh/config and don't forget to run 'chmod u+x ~/term_tab_style.scpt'.

 #!/usr/bin/osascript
  
 on run argv

   delay 1
  
   tell application "Terminal"
     repeat with w from 1 to count windows
       repeat with t from 1 to count tabs of window w
         set title to custom title of tab t of window w  
         try
           set myArray to my theSplit(title, "@")
           set userName to my getArrayValue(myArray, 1)
           set restName to my getArrayValue(myArray, 2)
           set myArray to my theSplit(restName, "-")
           set envName to my getArrayValue(myArray, 2)
           if envName is "pro" then
             #display dialog ("" & envName)
                set current settings of tab t of window w to (first settings set whose name is "Basic Blue")
           else if envName is "qa" then
                set current settings of tab t of window w to (first settings set whose name is "Basic")
           end if
         end try
       end repeat
     end repeat
   end tell
  
 end run
  
 on theSplit(theString, theDelimiter) 
   -- save delimiters to restore old settings
   set oldDelimiters to AppleScript's text item delimiters
   -- set delimiters to delimiter to be used
   set AppleScript's text item delimiters to theDelimiter
   -- create the array
   set theArray to every text item of theString  
   -- restore the old setting  
   set AppleScript's text item delimiters to oldDelimiters  
   -- return the result  
   return theArray 
 end theSplit
  
 on getArrayValue(array, location)  
   -- very important -- The list index starts at 1 not 0  
   return item location in array  
 end getArrayValue
  

Thursday, January 12, 2012

Python JSON one-liner

I've seen the python one-liner to parse and pretty-print json:

cat test.json | python -mjson.tool

But I still have to grep/awk to get the value.  Here's a python one-liner to get a json value:

curl -s -u user:password "http://test.json.output" | python -c "import json,sys;input=json.load(sys.stdin);print (input.get('messages'))"

A reasonably short, pure python way to do this is with the requests library, but that's not built into python, and I find one-liners are only useful if they work across a lot of systems as-is:

import json,requests
r = requests.get("http://test.json.output/api/", auth=('user','pass'))
o = json.loads(r.content)
print (o.get('messages'))

Tuesday, December 6, 2011

Wireshark on OS X

I run Wireshark (formerly Ethereal) on OSX, but by default only root has rights to the ethernet devices.  So either you run it as root, which is a security risk, or you give your own user the rights.  The devices (/dev/bf*) that are used are recreated every boot, so just 'chown'ing them won't do.

First add your user to the wheel group:


dscl . append /Groups/wheel GroupMembership 'username'

The run this command:

sudo chmod g+rw /dev/bpf*; open /Applications/Wireshark.app

I added that command to my .profile file as an alias for convenience:

alias wireshark='sudo chmod g+rw /dev/bpf*; open /Applications/Wireshark.app'

MSudo: Mac OSX Graphical SUDO

UPDATE: I've created a git repository for this: https://github.com/dgulino/msudo

I develop a lot on OSX and there are times I need to run a GUI app as root, like a gui editor of a systems file, or running Wireshark. I could just run it from a terminal using "sudo", but that's not very cool. There is the Pseudo app, which is quite cool, and only $15, but I figured it wouldn't be too hard to script something together. Well, Many Hours Later, I've got something! An AppleScript droplet that can be added to your dock and when you drag another App on it, a GUI popup will ask for your password, and voila!  You have a GUI app running as root.

Open Applications..Utilities..AppleScriptEditor, paste this below, and then save it as MSudo.app, as an Application.  Then drag it onto your Dock

Open
on open {filename}
     set p to POSIX path of filename
     set myArray to my theSplit(p, "/")
     set numItems to (count myArray) - 1
     set AppNameFull to getArrayValue(myArray, numItems)
     set myArray to my theSplit(AppNameFull, ".")
     set numItems to (count myArray) - 1
     set AppName to getArrayValue(myArray, 1)
     do shell script p & "/Contents/MacOS/" & AppName with administrator privileges
end open
on theSplit(theString, theDelimiter)
     -- save delimiters to restore old settings
     set oldDelimiters to AppleScript's text item delimiters
     -- set delimiters to delimiter to be used
set AppleScript's text item delimiters to theDelimiter
     -- create the array
     set theArray to every text item of theString
     -- restore the old setting
     set AppleScript's text item delimiters to oldDelimiters
     -- return the result
     return theArray
end theSplit
on getArrayValue(array, location)
     -- very important -- The list index starts at 1 not 0
     return item location in array
end getArrayValue

Update 2012/03/12: You can add a nice icon for this: http://stackoverflow.com/questions/8371790/how-to-set-icon-on-file-or-directory-using-cli-on-os-x http://icons.iconarchive.com/icons/iconshock/super-heros/128/superman-icon.png