Whatever is your database technology, architecture, whether you are with a Cloud vendor, multi Cloud, on-premises or a mix of all of that, you are really likely to have the below way of connecting to your servers:
This is good from a security perspective indeed but it may be a bit more problematic when you want to:
The most approaching simple and efficient tool I know (may be there are others) to achieve this kind of thing is dcli but this tool is Exadata specific. Also, dcli does not allow to execute a script in sudo on the remote server -- as well a few other things I will describe below in yal's features.
- 1/ ssh to a jump server as a non privileged user (fdenis for me for example)
- 2/ ssh to a server as a non privileged user (opc for example in Oracle Cloud)
- 3/ sudo to a more privileged user (oracle or grid in the Oracle world for example)
- 4/ do something
This is good from a security perspective indeed but it may be a bit more problematic when you want to:
- Execute some commands on each server or a subset of those servers
- Execute a script on each server or a subset of those servers
- Copy a file on each server or a subset of those servers
- . . . well . . . efficiently do your admin job . . .
The most approaching simple and efficient tool I know (may be there are others) to achieve this kind of thing is dcli but this tool is Exadata specific. Also, dcli does not allow to execute a script in sudo on the remote server -- as well a few other things I will describe below in yal's features.
YAL
So, one more time when something does not exist or does not fit my needs, I used my favorite and standard vi to make this standard shell script yal.sh aka Yet Another Launcher with the goal of being able to easily achieve what I described above. The only requirement is to have SSH keys deployed from the jump server to the target server (it is actually not a requirement but being prompted for a password at every server connection would be painful).
A first example
Let's start with a first example and just execute the "uptime" and the "id" commands on a set of servers:
[fdenis@jump]$ ./yal.sh -c server1,server2 -l opc -b "uptime;id" BEGIN on server1 : Thu Sep 12 02:56:01 UTC 2019 02:56:01 up 125 days, 14:41, 0 users, load average: 2.04, 2.36, 2.25 uid=54322(opc) gid=54323(opc) groups=54323(opc) END on server1 : Thu Sep 12 02:56:05 UTC 2019 ELAPSED : 7 seconds BEGIN on server2 : Thu Sep 12 02:56:08 UTC 2019 02:56:08 up 125 days, 14:38, 0 users, load average: 3.30, 2.38, 2.09 uid=54322(opc) gid=54323(opc) groups=54323(opc) END on server2 : Thu Sep 12 02:56:11 UTC 2019 ELAPSED : 6 seconds [fdenis@jump]$A first explanation of the options used:
- -c: a comma separated list of target servers
- -l: the user to use to login to the target servers
- -b: commands to execute "before". Before what ? before copying or executing a script as I will show below. There is also a -a option to execute a command "after" a script is executed or copied on a target server
HEADER="echo BEGIN on \`hostname -s\` : \`date\`" # Header to print before execution on target FOOTER="echo END\ \ \ on \`hostname -s\` : \`date\`" # Footer to print after execution on a target SHOW_ELAPSED="yes" # Show the elapsed time
Target servers
The previous example shows that -c is used to select a comma separated list of target servers; you can also use a -g option to specify a file containing a list of files:
[fdenis@jump]$ cat alist server1 server2 [fdenis@jump]$ ./yal.sh -g alist -l opc -b "uptime;id" BEGIN on server1 : Thu Sep 12 03:11:38 UTC 2019 03:11:38 up 125 days, 14:57, 0 users, load average: 3.89, 2.88, 2.48 uid=54322(opc) gid=54323(opc) groups=54323(opc) END on server1 : Thu Sep 12 03:11:41 UTC 2019 ELAPSED : 6 seconds BEGIN on server2 : Thu Sep 12 03:11:44 UTC 2019 03:11:44 up 125 days, 14:53, 0 users, load average: 2.46, 2.21, 2.15 uid=54322(opc) gid=54323(opc) groups=54323(opc) END on server2 : Thu Sep 12 03:11:47 UTC 2019 ELAPSED : 6 seconds [fdenis@jump]$
sudo
As described earlier, you may not want to run your commands / copy / execute your scripts with the user you use to login as this one is most likely a non-privileged user; you may want to sudo to do so; yal can do that by specifying the user you want to execute with using the -e option:
[fdenis@jump]$ ./yal.sh -g alist -l opc -b "uptime;id" -e oracle BEGIN on server1 : Thu Sep 12 03:15:55 UTC 2019 03:15:55 up 125 days, 15:01, 0 users, load average: 1.88, 2.26, 2.32 uid=101(oracle) gid=1001(oinstall) groups=1001(oinstall),1002(dbaoper),1003(dba),1006(asmdba) END on server1 : Thu Sep 12 03:15:58 UTC 2019 ELAPSED : 6 seconds BEGIN on server2 : Thu Sep 12 03:16:02 UTC 2019 03:16:02 up 125 days, 14:58, 0 users, load average: 1.76, 1.92, 2.05 uid=101(oracle) gid=1001(oinstall) groups=1001(oinstall),1002(dbaoper),1003(dba),1006(asmdba) END on server2 : Thu Sep 12 03:16:05 UTC 2019 ELAPSED : 6 seconds [fdenis@jump]$On older systems, you may not be able to sudo through a script as no terminal is allocated here and you would then receive a sudo: sorry, you must have a tty to run sudo. error. This behavior has been acknowledged by Red Hat and Red Hat has fixed the issue as very well explained in this post. So you would basically face this issue if your /etc/sudoers still contains:
Defaults requiretty
Execute a script
yal can indeed execute a script (with the -x option) on each specified target server. The script has to be on your jump server, where you execute yal, not on the target serverl; yal will then:
- Copy the script on the target server (in /tmp by default, this can be modified with the -d option)
- Execute the script on the target server
- Remove the script from the target server
- Connect as user opc
- Sudo to user oracle
- Execute what is specified in the -b option before executing the script
- Execute the rac-status.sh script
- Execute what is specified in the -a option after executing the script
- Show everything server by server with the elapsed time on each server
[fdenis@jump]$ ./yal.sh -g alist -l opc -b "echo before" -e oracle -a "echo after" -x rac-status.sh BEGIN on server1 : Thu Sep 12 03:32:41 UTC 2019 before Script rac-status.sh successfully copied to /tmp/rac-status.sh71743 Cluster blablacluster Listener | Port | server1 | Type | ------------------------------------------------------------- LISTENER | TCP:1521 | Online | Listener | LISTENER_SCAN1| TCP:1521 | - | SCAN | ------------------------------------------------------------- DB | Version | server1 | DB Type | ------------------------------------------------------------- a_database | 11.2.0.4 (1) | Open | SINGLE (P) | ------------------------------------------------------------- ORACLE_HOME references listed in the Version column 1 : /u01/app/oracle/product/11.2.0.4/dbhome_1 oracle oinstall Script /tmp/rac-status.sh71743 removed successfully after END on server1 : Thu Sep 12 03:32:50 UTC 2019 ELAPSED : 13 seconds BEGIN on server2 : Thu Sep 12 03:32:54 UTC 2019 before Script rac-status.sh successfully copied to /tmp/rac-status.sh71743 Cluster anothercluster Listener | Port | server2 | Type | ------------------------------------------------------------- LISTENER | TCP:1521 | Online | Listener | LISTENER_SCAN1| TCP:1521 | - | SCAN | ------------------------------------------------------------- DB | Version | server2 | DB Type | ------------------------------------------------------------- myprod_db | 12.1.0.2 (1) | Open | SINGLE (P) | ------------------------------------------------------------- ORACLE_HOME references listed in the Version column 1 : /u01/app/oracle/product/12.1.0.2/dbhome_1 oracle oinstall Script /tmp/rac-status.sh71743 removed successfully after END on server2 : Thu Sep 12 03:33:02 UTC 2019 ELAPSED : 11 secondsNote that are shown the copies and deletion of the temporary files used to execute the script; you can deactivate that with the -q option (quiet).
Options to execute scripts
As you may want to use options to the scripts you execute, I made the -O option (upper case letter o as lower case o is already taken):
[fdenis@jump]$ ./yal.sh -g alist -l opc -e oracle -x rac-status.sh -O "-n -d" -qNote that you'll have to double quote your script options not to mess up with the yal options.
Copy a file
You can also simply copy a file to target servers using the -f option and the -d option to specify the target directory:
[fdenis@jump]$ ./yal.sh -g alist -l opc -b "ls -ltr /tmp/asmdu.sh" -a "ls -ltr /tmp/asmdu.sh" -f asmdu.sh -d /tmp BEGIN on server1 : Thu Sep 12 03:46:57 UTC 2019 ls: cannot access /tmp/asmdu.sh: No such file or directory Script asmdu.sh successfully copied to /tmp/asmdu.sh -rwxr--r-- 1 opc opc 12292 Sep 12 03:47 /tmp/asmdu.sh END on server1 : Thu Sep 12 03:47:04 UTC 2019 ELAPSED : 9 seconds BEGIN on server2 : Thu Sep 12 03:47:07 UTC 2019 ls: cannot access /tmp/asmdu.sh: No such file or directory Script asmdu.sh successfully copied to /tmp/asmdu.sh -rwxr--r-- 1 opc opc 12292 Sep 12 03:47 /tmp/asmdu.sh END on server2 : Thu Sep 12 03:47:14 UTC 2019 ELAPSED : 10 seconds [fdenis@jump]$
SSH options
To best adapt to your environment and the potential SSH security in place in your environments, you can change the SSH and SCP options using the -s and -S options
A config file
You can also specify your favorite options in a config file named .yal.config by default to avoid repeting them every time you use a command line:
[fdenis@jump]$ cat .yal.config SSH_OPTIONS="-qT -i /tmp/fred.pub" USER_TO_EXEC = root # comment [fdenis@jump]$Note that a value assigned to a parameter in the config file overwrites the default but is overwritten by a command line option. Consider the config file as your defaults.
Show the options
As you may end up with many options in your command line, I made a -o option to show (and only show, not execute anything) the options of the current command line:
[fdenis@jump]$ ./yal.sh -g alist -l opc -b "ls -ltr /tmp/asmdu.sh" -e oracle -a "ls -ltr /tmp/asmdu.sh" -f asmdu.sh -d /tmp -o Option | Value | ---------------------------------------------- -a: After | ls -ltr /tmp/asmdu.sh | -b: Before | ls -ltr /tmp/asmdu.sh | -c: Servers List | server1,server2 | -d: Dest | /tmp | -e: User to exec | oracle | -f: File to copy | asmdu.sh | -g: Group File | alist | -l: User to login | opc | -q: Quiet | NO | -s: SSH Options | -qT | -S: SCP Options | -q | -x: Script to exec | | Config File | .yal.config | ---------------------------------------------- [fdenis@jump]$
-h for help and -V for the script version
Feel free to use -h to show the script help and -V to show the script version.
yal instead of dcli ?
I am a big fan of dcli so I used the same options names for yal and added more that I found nice to have and I must admint that I sometimes use yal instead of dcli on Exadata but to each his own :)
Get the code
I hope you'll be enjoying yal as much as I do !
Hi Fred, I have a question for you...
ReplyDeleteI have a server with three instances and I use alias to set the profile of the instance where I want to connect.
That's the situation... I want to connect to the server and create a restore point for each instance, but I must set the profile before connect to sqlplus for each instance.
How can I do that?
Hi Diego,
DeleteSomething like that should work:
for X in alias1 alias2 alias3
do
. $X
sqlplus / as sysdba << END_SQL
create restore point blabla ;
END_SQL
done
Let me know,
Fred
Hi Fred, thanks for answering.
DeleteI think i don't explain my question well...
I need to connect on various servers where some of them have two or more instances (where I use alias to connect on each instance), so I want to use yal.sh to do that...
Is it clear for you?
Thanks in advance.
OK, I got it now.
DeleteLet's first make a "do_something.sh" script that does what you want on each instance:
$ cat do_something.sh
#!/bin/bash
for I in `cat /etc/oratab | grep "^[A-Z]" | awk -F":" '{print $1}'`
do
. oraenv <<< $I
sqlplus / as sysdba << END_SQL
create restore point whatever ;
END_SQL
done
Then you can launch it on each server you want using yal:
$ ./yal.sh -c server1,server2,server3 -l oracle -x do_something.sh
Let me know,
Fred
Or if you prefer to rely on the running instances and not on oratab, use:
Deletefor I in `ps -ef | grep pmon_[aA-zZ] | grep -v grep | awk '{print $NF}' | cut -d_ -f3,4`
do
. . .
done
Hi Fred, amazing script! Is it possible to set my own variables? I don't have SSH keys deployed, instead, I use sshpass so I don't have to type the password over and over again. Thanks!
ReplyDeleteHi,
DeleteYes, you can use a config file or command line options; I describe this in the blog. It is -s for your SSH options or SSH_OPTIONS in the config file.
For your old Solaris issue, please contact me by email or linkedin messaging to troubleshoot.
Thanks,
Fred
Hi Fred,
ReplyDeleteOne more time great script you developed.
I change your script to add differents OS_users on SERVER_LIST file and working like a charm.
Thanks
Thanks, I was thinking of implementing that as well :) will do soon.
Delete