From fe7d30b762357ac4ec1fe2b173320d463267ac82 Mon Sep 17 00:00:00 2001 From: Kenny Woodson Date: Wed, 4 Feb 2015 12:06:41 -0500 Subject: Renamed ossh.py and added bash completion function --- bin/COMPLETION_SETUP | 21 +++++ bin/ansibleutil.py | 4 - bin/ossh | 235 +++++++++++++++++++++++++++++++++++++++++++++++ bin/ossh.py | 228 --------------------------------------------- bin/ossh_bash_completion | 18 ++++ 5 files changed, 274 insertions(+), 232 deletions(-) create mode 100644 bin/COMPLETION_SETUP create mode 100755 bin/ossh delete mode 100755 bin/ossh.py create mode 100755 bin/ossh_bash_completion (limited to 'bin') diff --git a/bin/COMPLETION_SETUP b/bin/COMPLETION_SETUP new file mode 100644 index 000000000..c11a2dfff --- /dev/null +++ b/bin/COMPLETION_SETUP @@ -0,0 +1,21 @@ +# ossh is an ssh replacement. + +Ossh uses a dynamic inventory cache in order to lookup hostnames and translate them +to something meaningful such as an IP address or dns name. + +This allows us to treat our servers as cattle and not as pets. + +In order to setup bash completion, source the following script: +/path/to/repository/openshift-online-ansible/bin/ossh_bash_completion + +This bash_completion script will look at the cached version of your +multi_ec2 results in ~/.ansible/tmp/. It will then parse a few +host.env out of the json and return them to be completable. + +If you have not run the ossh command and it has not laid down +a cache file the completions will not be available. + +You can populate the cache by running `ossh --list`. This +will populate the cache file and the completions should +become available. + diff --git a/bin/ansibleutil.py b/bin/ansibleutil.py index 26fb25493..ed35ef8f9 100644 --- a/bin/ansibleutil.py +++ b/bin/ansibleutil.py @@ -57,10 +57,6 @@ class AnsibleUtil(object): for dns, host in inv['_meta']['hostvars'].items(): if host['ec2_tag_environment'] not in inst_by_env: inst_by_env[host['ec2_tag_environment']] = {} - - #if inst_by_env[host['ec2_tag_environment']][host['ec2_tag_Name']]: - #raise Exception('Duplicate ec2_tag_Name found: %s' % host['ec2_tag_Name']) - host_id = "%s:%s" % (host['ec2_tag_Name'],host['ec2_id']) inst_by_env[host['ec2_tag_environment']][host_id] = host diff --git a/bin/ossh b/bin/ossh new file mode 100755 index 000000000..2fa4bde37 --- /dev/null +++ b/bin/ossh @@ -0,0 +1,235 @@ +#!/usr/bin/env python + +import pdb +import argparse +import ansibleutil +import traceback +import sys +import os +import re + + +# use dynamic inventory +# list instances +# symlinked to ~/bin +# list instances that match pattern +# python! + +# list environment stuff as well +# 3 states: +# - an exact match; return result +# - a partial match; return all regex results +# - no match; None + +class Ossh(object): + def __init__(self): + self.file_path = os.path.join(os.path.dirname(os.path.realpath(__file__))) + self.parse_cli_args() + + if self.args.host == '' and not self.args.list: + self.parser.print_help() + return + + self.ansible = ansibleutil.AnsibleUtil() + + self.host_inventory = self.get_hosts() + + + if self.args.debug: + print self.args + + # get a dict of host inventory + self.get_hosts() + + # parse host and user + self.process_host() + + # perform the SSH + if self.args.list: + self.list_hosts() + else: + self.ssh() + + def parse_cli_args(self): + parser = argparse.ArgumentParser(description='Openshift Online SSH Tool.') + parser.add_argument('-r', '--random', action="store", + help="Choose a random host") + parser.add_argument('-e', '--env', action="store", + help="Which environment to search for the host ") + parser.add_argument('-d', '--debug', default=False, + action="store_true", help="debug mode") + parser.add_argument('-v', '--verbose', default=False, + action="store_true", help="Verbose?") + parser.add_argument('--list', default=False, + action="store_true", help="list out hosts") + parser.add_argument('-c', '--command', action='store', + help='Command to run on remote host') + parser.add_argument('-l', '--login_name', action='store', + help='User in which to ssh as') + + parser.add_argument('-o', '--ssh_opts', action='store', + help='options to pass to SSH.\n \ + "-o ForwardX11 yes"') + parser.add_argument('host', nargs='?', default='') + + self.args = parser.parse_args() + self.parser = parser + + + def process_host(self): + '''Determine host name and user name for SSH. + ''' + self.env = None + + re_env = re.compile('\.(int|stg|prod|ops)') + search = re_env.search(self.args.host) + if self.args.env: + self.env = self.args.env + elif search: + # take the first? + self.env = search.groups()[0] + + # remove env from hostname command line arg if found + if search: + self.args.host = re_env.split(self.args.host)[0] + + # parse username if passed + if '@' in self.args.host: + self.user, self.host = self.args.host.split('@') + else: + self.host = self.args.host + if self.args.login_name: + self.user = self.args.login_name + else: + self.user = os.environ['USER'] + + + + def get_hosts(self): + '''Query our host inventory and return a dict where the format + equals: + + dict['servername'] = dns_name + ''' + # TODO: perform a numerical sort on these hosts + # and display them + self.host_inventory = self.ansible.build_host_dict() + + def select_host(self, regex=False): + '''select host attempts to match the host specified + on the command line with a list of hosts. + + if regex is specified then we will attempt to match + all *{host_string}* equivalents. + ''' +# list environment stuff as well +# 3 states: +# - an exact match; return result +# - a partial match; return all regex results +# - no match; None + re_host = re.compile(self.host) + + exact = [] + results = [] + for hostname, server_info in self.host_inventory[self.env].items(): + if hostname.split(':')[0] == self.host: + exact.append((hostname, server_info)) + break + elif re_host.search(hostname): + results.append((hostname, server_info)) + + if exact: + return exact + elif results: + return results + else: + print "Could not find specified host: %s in %s" % (self.host, self.env) + + # default - no results found. + return None + + + def list_hosts(self, limit=None): + '''Function to print out the host inventory. + + Takes a single parameter to limit the number of hosts printed. + ''' + + if self.env: + results = self.select_host(True) + if len(results) == 1: + hostname, server_info = results[0] + sorted_keys = server_info.keys() + sorted_keys.sort() + for key in sorted_keys: + print '{0:<35} {1}'.format(key, server_info[key]) + else: + for host_id, server_info in results[:limit]: + name = server_info['ec2_tag_Name'] + ec2_id = server_info['ec2_id'] + ip = server_info['ec2_ip_address'] + print '{ec2_tag_Name:<35} {ec2_tag_environment:<8} {ec2_id:<15} {ec2_ip_address}'.format(**server_info) + + if limit: + print + print 'Showing only the first %d results...' % limit + print + + else: + for env, host_ids in self.host_inventory.items(): + for host_id, server_info in host_ids.items(): + name = server_info['ec2_tag_Name'] + ec2_id = server_info['ec2_id'] + ip = server_info['ec2_ip_address'] + print '{ec2_tag_Name:<35} {ec2_tag_environment:<5} {ec2_id:<15} {ec2_ip_address}'.format(**server_info) + + def ssh(self): + '''SSH to a specified host + ''' + try: + cmd = '/usr/bin/ssh' + ssh_args = [cmd, '-l%s' % self.user] + #ssh_args = [cmd, ] + + if self.args.verbose: + ssh_args.append('-vvv') + + if self.args.ssh_opts: + ssh_args.append("-o%s" % self.args.ssh_opts) + + result = self.select_host() + if not result: + return # early exit, no results + + if len(result) > 1: + self.list_hosts(10) + return # early exit, too many results + + # Assume we have one and only one. + hostname, server_info = result[0] + ip = server_info['ec2_ip_address'] + + ssh_args.append(ip) + + #last argument + if self.args.command: + ssh_args.append("%s" % self.args.command) + + if self.args.debug: + print "SSH to %s in %s as %s" % (hostname, self.env, self.user) + + print "Running: %s\n" % ' '.join(ssh_args) + + os.execve('/usr/bin/ssh', ssh_args, os.environ) + except: + print traceback.print_exc() + print sys.exc_info() + + +def main(): + ossh = Ossh() + + +if __name__ == '__main__': + main() + diff --git a/bin/ossh.py b/bin/ossh.py deleted file mode 100755 index d0f4a1475..000000000 --- a/bin/ossh.py +++ /dev/null @@ -1,228 +0,0 @@ -#!/usr/bin/env python - -import pdb -import argparse -import ansibleutil -import traceback -import sys -import os -import re - - -# use dynamic inventory -# list instances -# symlinked to ~/bin -# list instances that match pattern -# python! - -# list environment stuff as well -# 3 states: -# - an exact match; return result -# - a partial match; return all regex results -# - no match; None - -class Ossh(object): - def __init__(self): - self.file_path = os.path.join(os.path.dirname(os.path.realpath(__file__))) - self.parse_cli_args() - self.ansible = ansibleutil.AnsibleUtil() - - self.host_inventory = self.get_hosts() - - - if self.args.debug: - print self.args - - # get a dict of host inventory - self.get_hosts() - - # parse host and user - self.process_host() - - # perform the SSH - if self.args.list: - self.list_hosts() - else: - self.ssh() - - def parse_cli_args(self): - parser = argparse.ArgumentParser(description='Openshift Online SSH Tool.') - parser.add_argument('-r', '--random', action="store", - help="Choose a random host") - parser.add_argument('-e', '--env', action="store", - help="Which environment to search for the host ") - parser.add_argument('-d', '--debug', default=False, - action="store_true", help="debug mode") - parser.add_argument('-v', '--verbose', default=False, - action="store_true", help="Verbose?") - parser.add_argument('--list', default=False, - action="store_true", help="list out hosts") - parser.add_argument('host') - parser.add_argument('-c', '--command', action='store', - help='Command to run on remote host') - parser.add_argument('-l', '--login_name', action='store', - help='User in which to ssh as') - - parser.add_argument('-o', '--ssh_opts', action='store', - help='options to pass to SSH.\n \ - "-o ForwardX11 yes"') - - self.args = parser.parse_args() - - def process_host(self): - '''Determine host name and user name for SSH. - ''' - self.env = None - - re_env = re.compile('\.(int|stg|prod|ops)') - search = re_env.search(self.args.host) - if self.args.env: - self.env = self.args.env - elif search: - # take the first? - self.env = search.groups()[0] - - # remove env from hostname command line arg if found - if search: - self.args.host = re_env.split(self.args.host)[0] - - # parse username if passed - if '@' in self.args.host: - self.user, self.host = self.args.host.split('@') - else: - self.host = self.args.host - if self.args.login_name: - self.user = self.args.login_name - else: - self.user = os.environ['USER'] - - - - def get_hosts(self): - '''Query our host inventory and return a dict where the format - equals: - - dict['servername'] = dns_name - ''' - # TODO: perform a numerical sort on these hosts - # and display them - self.host_inventory = self.ansible.build_host_dict() - - def select_host(self, regex=False): - '''select host attempts to match the host specified - on the command line with a list of hosts. - - if regex is specified then we will attempt to match - all *{host_string}* equivalents. - ''' -# list environment stuff as well -# 3 states: -# - an exact match; return result -# - a partial match; return all regex results -# - no match; None - re_host = re.compile(self.host) - - exact = [] - results = [] - for hostname, server_info in self.host_inventory[self.env].items(): - if hostname.split(':')[0] == self.host: - exact.append((hostname, server_info)) - break - elif re_host.search(hostname): - results.append((hostname, server_info)) - - if exact: - return exact - elif results: - return results - else: - print "Could not find specified host: %s in %s" % (self.host, self.env) - - # default - no results found. - return None - - - def list_hosts(self, limit=None): - '''Function to print out the host inventory. - - Takes a single parameter to limit the number of hosts printed. - ''' - - if self.env: - results = self.select_host(True) - if len(results) == 1: - hostname, server_info = results[0] - sorted_keys = server_info.keys() - sorted_keys.sort() - for key in sorted_keys: - print '{0:<35} {1}'.format(key, server_info[key]) - else: - for host_id, server_info in results[:limit]: - name = server_info['ec2_tag_Name'] - ec2_id = server_info['ec2_id'] - ip = server_info['ec2_ip_address'] - print '{ec2_tag_Name:<35} {ec2_tag_environment:<8} {ec2_id:<15} {ec2_ip_address}'.format(**server_info) - - if limit: - print - print 'Showing only the first %d results...' % limit - print - - else: - for env, host_ids in self.host_inventory.items(): - for host_id, server_info in host_ids.items(): - name = server_info['ec2_tag_Name'] - ec2_id = server_info['ec2_id'] - ip = server_info['ec2_ip_address'] - print '{ec2_tag_Name:<35} {ec2_tag_environment:<5} {ec2_id:<15} {ec2_ip_address}'.format(**server_info) - - def ssh(self): - '''SSH to a specified host - ''' - try: - cmd = '/usr/bin/ssh' - ssh_args = [cmd, '-l%s' % self.user] - #ssh_args = [cmd, ] - - if self.args.verbose: - ssh_args.append('-vvv') - - if self.args.ssh_opts: - ssh_args.append("-o%s" % self.args.ssh_opts) - - result = self.select_host() - if not result: - return # early exit, no results - - if len(result) > 1: - self.list_hosts(10) - return # early exit, too many results - - # Assume we have one and only one. - hostname, server_info = result[0] - ip = server_info['ec2_ip_address'] - - ssh_args.append(ip) - - #last argument - if self.args.command: - ssh_args.append("%s" % self.args.command) - - if self.args.debug: - print "SSH to %s in %s as %s" % (hostname, self.env, self.user) - - print "Running: %s\n" % ' '.join(ssh_args) - - os.execve('/usr/bin/ssh', ssh_args, os.environ) - except: - print traceback.print_exc() - print sys.exc_info() - - -def main(): - ossh = Ossh() - - -if __name__ == '__main__': - main() - diff --git a/bin/ossh_bash_completion b/bin/ossh_bash_completion new file mode 100755 index 000000000..0d0bdb0e6 --- /dev/null +++ b/bin/ossh_bash_completion @@ -0,0 +1,18 @@ +__ossh_known_hosts(){ + if [[ -f ~/.ansible/tmp/multi_ec2_inventory.cache ]]; then + /usr/bin/python -c 'import json,os; z = json.loads(open("%s"%os.path.expanduser("~/.ansible/tmp/multi_ec2_inventory.cache")).read()); print "\n".join(["%s.%s" % (host["ec2_tag_Name"],host["ec2_tag_environment"]) for dns, host in z["_meta"]["hostvars"].items()])' + fi +} + +_ossh() +{ + local cur prev known_hosts + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD-1]}" + known_hosts="$(__ossh_known_hosts)" + COMPREPLY=( $(compgen -W "${known_hosts}" -- ${cur})) + + return 0 +} +complete -F _ossh ossh -- cgit v1.2.3