Friday, March 30, 2012

Stupid Unix Tricks... Part Two (Remote Command Execution using SSH)



So, let's say that you wanted to have a script on your dbTier that will reach out to your appsTier and shut down the applications. Maybe this is your system-level shutdown script so that when the Unix administrator shuts down the dbTier, everything is shut down nice and neat like...

For the purpose of this exercise, we're going to need to assume that the APPS password is known to the script (how you do that might be the subject of another blog posting). We're also going to assume that the Unix environment is set automatically (and without prompting) on the remote system.

So, how do you do it?

Well, first you have to set up ssh pre-shared keys. This will allow you to login without being asked for a password. (See my earlier posting: Password-less Login Using SSH Pre-Shared Keys)

Once that is configured, you can use a command like this:

ssh applmgr@myappstier.mydomain "cd ${ADMIN_SCRIPTS_HOME};./adstpall.sh apps/${APPSPW}" 2>&1 |tee -a ${LOG}

A few things here. First, you'll notice that I'm actually executing TWO commands remotely. The "cd" to change directories and then the adstpall.sh script (the semicolon allows me to do that in Unix). Secondly, there are environment variables. Here's the thing about those environment variables. In the command above, they are NOT evaluated on the target system. They are evaluated locally on the SOURCE system. If you want to use variables that are local to the target, you're going to have to "escape" them.

For example, this one will use a variable evaluated on the source machine:

ssh applmgr@myappstier.mydomain "echo ${CONTEXT_NAME}"

And this one will use a variable evaluated on the target machine:

ssh applmgr@myappstier.mydomain 'echo ${CONTEXT_NAME}'

Similarly, you can evaluate a variable on the target by “escaping” it:

ssh applmgr@myappstier.mydomain "echo \${CONTEXT_NAME}"

At one client, their standard is to use a script that wraps around the standard "oraenv" to set their environment variables. As a result, every time they log in, they are greeted with a prompt asking them to choose their environment.

This raised an interesting problem for some of the automated processes we were trying to deploy. The automation was driven from a remote box and would need to ssh over to a target box and issue commands. So, how do we configure the environment so that a user logging in interactively is prompted and one issuing a command remotely through ssh isn't? Well, it turns out that, on Linux at least, that remote command doesn't get assigned a TTY. So, we've made a change to the .bash_profile on the target node that looks something like this:

if tty | fgrep pts ; then
#
# Normal, interactive logins
#
export ORAENV_ASK=YES
else
#
# Human-less logins (ssh "command")
# (Suppress output and bypass prompting for oracle environment)
#
export ORAENV_ASK=NO
fi

Now, let's assume you want to be a little more elaborate. You want to clean up extraneous output and capture the results of the command in your logfile (represented by the environment variable ${LOG}):

ssh applmgr@myappstier.mydomain ". ./.bash_profile 2>&1 1>/dev/null;cd ${ADMIN_SCRIPTS_HOME};./adstpall.sh apps/${APPSPW}" 2>&1 |tee -a ${LOG}

Or, maybe you'd like to do something in SQL*Plus on a remote system?

ssh applmgr@myappstier.mydomain “. ./.bash_profile 2>&1 1>/dev/null;sqlplus apps/${APPSPW}” <&1 1>>${LOG}
select sysdate from dual;
EOF

This will redirect stderr to stdout, and send both to your logfile (${LOG}). Pay close attention to the line containing the EOF. It has to be the ONLY thing on the line (not even a trailing space!)

James

No comments:

Post a Comment