From 7d368f62f06a37a92cf8d644842c338ba55b1d67 Mon Sep 17 00:00:00 2001 From: Kenny Woodson Date: Wed, 11 Feb 2015 10:38:47 -0500 Subject: initial commit of scp utilitiy --- bin/ansibleutil.py | 22 +++++- bin/oscp | 207 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 228 insertions(+), 1 deletion(-) create mode 100755 bin/oscp diff --git a/bin/ansibleutil.py b/bin/ansibleutil.py index b12b7b447..f5f699918 100644 --- a/bin/ansibleutil.py +++ b/bin/ansibleutil.py @@ -17,7 +17,8 @@ class AnsibleUtil(object): if args: cmd.extend(args) - env = {} + env = os.environ + p = subprocess.Popen(cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE, env=env) @@ -66,4 +67,23 @@ class AnsibleUtil(object): return inst_by_env + def get_hostnames(self, args=[]): + inv = self.get_inventory(args) + + import collections + hostnames = collections.defaultdict(list) + + for dns, host in inv['_meta']['hostvars'].items(): + hostnames['ec2_tag_Name'].append(host['ec2_id']) + + return hostnames + + def get_host_ids(self, args=[]): + inv = self.get_inventory(args) + + ids = {} + + for dns, host in inv['_meta']['hostvars'].items(): + ids['ec2_id'] = host + return ids diff --git a/bin/oscp b/bin/oscp new file mode 100755 index 000000000..22e218fe1 --- /dev/null +++ b/bin/oscp @@ -0,0 +1,207 @@ +#!/usr/bin/env python +# vim: expandtab:tabstop=4:shiftwidth=4 + +import argparse +import ansibleutil +import traceback +import sys +import os +import re + +class Oscp(object): + def __init__(self): + self.file_path = os.path.join(os.path.dirname(os.path.realpath(__file__))) + self.parse_cli_args() + + # parse host and user + self.process_host() + + print self.args + print self.host + print self.user + print self.path + sys.exit(0) + + self.ansible = ansibleutil.AnsibleUtil() + + # get a dict of host inventory + if self.args.list: + self.get_hosts() + else: + self.get_hosts(True) + + + if not self.args.list and not self.env: + print "Please specify an environment." + return + + if self.args.host == '' and not self.args.list: + self.parser.print_help() + return + + if self.args.debug: + print self.args + + # perform the scp + if self.args.list: + self.list_hosts() + else: + self.scp() + + def parse_cli_args(self): + parser = argparse.ArgumentParser(description='Openshift Online SSH Tool.') + 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('-r', action='store_true', default=False, + help='Recusrively copy files to or from destination.') + parser.add_argument('-o', '--ssh_opts', action='store', + help='options to pass to SSH.\n \ + "-oPort=22,TCPKeepAlive=yes"') + + parser.add_argument('src', default='') + parser.add_argument('dest',default='') + + self.args = parser.parse_args() + self.parser = parser + + + def process_host(self): + '''Determine host name and user name for SSH. + ''' + self.user = None + self.path = '' + + self.host = '' + if '@' in self.args.src: + self.host = self.args.src + else: + self.host = self.args.dest + + re_host = re.compile("(.*)@(.*)(:.*$)") + search = re_host.search(self.host) + if search: + # take the first? + self.user = search.groups()[0] + self.host = search.groups()[1] + self.path = search.groups()[2] + else: + print "Could not determine user and hostname." + return + + def get_hosts(self, cache_only=False): + '''Query our host inventory and return a dict where the format + equals: + + dict['environment'] = [{'servername' : {}}, ] + ''' + if cache_only: + self.host_inventory = self.ansible.build_host_dict(['--cache-only']) + else: + 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. + ''' + re_host = re.compile(self.host) + + results = [] + for hostname, server_info in self.host_inventory[self.env].items(): + if hostname.split(':')[0] == self.host: + # an exact match, return it! + return [(hostname, server_info)] + elif re_host.search(hostname): + results.append((hostname, server_info)) + + if 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 scp(self): + '''scp files to or from a specified host + ''' + try: + # shell args start with the program name in position 1 + scp_args = ['/usr/bin/scp'] + + if self.args.verbose: + scp_args.append('-v') + + if self.args.ssh_opts: + for arg in self.args.ssh_opts.split(","): + scp_args.append("-o%s" % arg) + + 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] + dns = server_info['ec2_public_dns_name'] + + scp_args.append(dns) + + #last argument + if self.args.command: + scp_args.append("%s" % self.args.command) + + print "Running: %s\n" % ' '.join(scp_args) + + os.execve('/usr/bin/scp', scp_args, os.environ) + except: + print traceback.print_exc() + print sys.exc_info() + + +if __name__ == '__main__': + oscp = Oscp() + -- cgit v1.2.3 From 603c79412df1b141230ec55f032ad086ada37097 Mon Sep 17 00:00:00 2001 From: Kenny Woodson Date: Wed, 11 Feb 2015 14:41:52 -0500 Subject: Checking in oscp. --- bin/ansibleutil.py | 25 +----------- bin/oscp | 115 ++++++++++++++++++++++++++++++----------------------- bin/ossh | 29 ++++++-------- 3 files changed, 81 insertions(+), 88 deletions(-) diff --git a/bin/ansibleutil.py b/bin/ansibleutil.py index f5f699918..07e41b42c 100644 --- a/bin/ansibleutil.py +++ b/bin/ansibleutil.py @@ -5,6 +5,7 @@ import sys import os import json import re +import collections class AnsibleUtil(object): def __init__(self): @@ -53,7 +54,7 @@ class AnsibleUtil(object): return groups - def build_host_dict(self, args=[]): + def build_host_dict_by_env(self, args=[]): inv = self.get_inventory(args) inst_by_env = {} @@ -63,27 +64,5 @@ class AnsibleUtil(object): host_id = "%s:%s" % (host['ec2_tag_Name'],host['ec2_id']) inst_by_env[host['ec2_tag_environment']][host_id] = host - return inst_by_env - - def get_hostnames(self, args=[]): - inv = self.get_inventory(args) - - import collections - hostnames = collections.defaultdict(list) - - for dns, host in inv['_meta']['hostvars'].items(): - hostnames['ec2_tag_Name'].append(host['ec2_id']) - - return hostnames - - def get_host_ids(self, args=[]): - inv = self.get_inventory(args) - - ids = {} - - for dns, host in inv['_meta']['hostvars'].items(): - ids['ec2_id'] = host - - return ids diff --git a/bin/oscp b/bin/oscp index 22e218fe1..463dadaef 100755 --- a/bin/oscp +++ b/bin/oscp @@ -16,12 +16,6 @@ class Oscp(object): # parse host and user self.process_host() - print self.args - print self.host - print self.user - print self.path - sys.exit(0) - self.ansible = ansibleutil.AnsibleUtil() # get a dict of host inventory @@ -30,16 +24,12 @@ class Oscp(object): else: self.get_hosts(True) - - if not self.args.list and not self.env: - print "Please specify an environment." - return - - if self.args.host == '' and not self.args.list: + if (self.args.src == '' or self.args.dest == '') and not self.args.list: self.parser.print_help() return if self.args.debug: + print self.host print self.args # perform the scp @@ -50,14 +40,16 @@ class Oscp(object): def parse_cli_args(self): parser = argparse.ArgumentParser(description='Openshift Online SSH Tool.') + parser.add_argument('-e', '--env', + action="store", help="Environment where this server exists.") 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('-r', action='store_true', default=False, - help='Recusrively copy files to or from destination.') + parser.add_argument('-r', '--recurse', action='store_true', default=False, + help='Recursively copy files to or from destination.') parser.add_argument('-o', '--ssh_opts', action='store', help='options to pass to SSH.\n \ "-oPort=22,TCPKeepAlive=yes"') @@ -72,26 +64,43 @@ class Oscp(object): def process_host(self): '''Determine host name and user name for SSH. ''' - self.user = None - self.path = '' + self.user = '' - self.host = '' - if '@' in self.args.src: + # is the first param passed a valid file? + if os.path.isfile(self.args.src) or os.path.isdir(self.args.src): + self.local_src = True + self.host = self.args.dest + else: + self.local_src = False self.host = self.args.src + + if '@' in self.host: + re_host = re.compile("(.*@)(.*)(:.*$)") else: - self.host = self.args.dest + re_host = re.compile("(.*)(:.*$)") - re_host = re.compile("(.*)@(.*)(:.*$)") search = re_host.search(self.host) + if search: - # take the first? - self.user = search.groups()[0] - self.host = search.groups()[1] - self.path = search.groups()[2] + if len(search.groups()) > 2: + self.user = search.groups()[0] + self.host = search.groups()[1] + self.path = search.groups()[2] + else: + self.host = search.groups()[0] + self.path = search.groups()[1] else: print "Could not determine user and hostname." return + if self.args.env: + self.env = self.args.env + elif "." in self.host: + self.host, self.env = self.host.split(".") + else: + print "HERE" + self.env = None + def get_hosts(self, cache_only=False): '''Query our host inventory and return a dict where the format equals: @@ -99,31 +108,28 @@ class Oscp(object): dict['environment'] = [{'servername' : {}}, ] ''' if cache_only: - self.host_inventory = self.ansible.build_host_dict(['--cache-only']) + self.host_inventory = self.ansible.build_host_dict_by_env(['--cache-only']) else: - self.host_inventory = self.ansible.build_host_dict() + self.host_inventory = self.ansible.build_host_dict_by_env() - def select_host(self, regex=False): + def select_host(self): '''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. ''' - re_host = re.compile(self.host) - results = [] - for hostname, server_info in self.host_inventory[self.env].items(): - if hostname.split(':')[0] == self.host: - # an exact match, return it! - return [(hostname, server_info)] - elif re_host.search(hostname): - results.append((hostname, server_info)) + for env in self.host_inventory.keys(): + for hostname, server_info in self.host_inventory[env].items(): + if hostname.split(':')[0] == self.host: + results.append((hostname, server_info)) + + # attempt to select the correct environment if specified + if self.env: + results = filter(lambda result: result[1]['ec2_tag_environment'] == self.env, results) if results: return results else: - print "Could not find specified host: %s in %s" % (self.host, self.env) + print "Could not find specified host: %s." % self.host # default - no results found. return None @@ -135,7 +141,7 @@ class Oscp(object): ''' if self.env: - results = self.select_host(True) + results = self.select_host() if len(results) == 1: hostname, server_info = results[0] sorted_keys = server_info.keys() @@ -172,27 +178,38 @@ class Oscp(object): if self.args.verbose: scp_args.append('-v') + if self.args.recurse: + scp_args.append('-r') + if self.args.ssh_opts: for arg in self.args.ssh_opts.split(","): scp_args.append("-o%s" % arg) - result = self.select_host() - if not result: + results = self.select_host() + + if self.args.debug: print results + + if not results: return # early exit, no results - if len(result) > 1: - self.list_hosts(10) + if len(results) > 1: + print "Multiple results found for %s." % self.host + for result in results: + print "{ec2_tag_Name:<35} {ec2_tag_environment:<5} {ec2_id:<10}".format(**result[1]) return # early exit, too many results # Assume we have one and only one. - hostname, server_info = result[0] + hostname, server_info = results[0] dns = server_info['ec2_public_dns_name'] - scp_args.append(dns) + host_str = "%s%s%s" % (self.user, dns, self.path) - #last argument - if self.args.command: - scp_args.append("%s" % self.args.command) + if self.local_src: + scp_args.append(self.args.src) + scp_args.append(host_str) + else: + scp_args.append(host_str) + scp_args.append(self.args.dest) print "Running: %s\n" % ' '.join(scp_args) diff --git a/bin/ossh b/bin/ossh index 0b48ca46c..d74244501 100755 --- a/bin/ossh +++ b/bin/ossh @@ -99,31 +99,28 @@ class Ossh(object): dict['servername'] = dns_name ''' if cache_only: - self.host_inventory = self.ansible.build_host_dict(['--cache-only']) + self.host_inventory = self.ansible.build_host_dict_by_env(['--cache-only']) else: - self.host_inventory = self.ansible.build_host_dict() + self.host_inventory = self.ansible.build_host_dict_by_env() - def select_host(self, regex=False): + def select_host(self): '''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. ''' - re_host = re.compile(self.host) - results = [] - for hostname, server_info in self.host_inventory[self.env].items(): - if hostname.split(':')[0] == self.host: - # an exact match, return it! - return [(hostname, server_info)] - elif re_host.search(hostname): - results.append((hostname, server_info)) + for env in self.host_inventory.keys(): + for hostname, server_info in self.host_inventory[env].items(): + if hostname.split(':')[0] == self.host: + results.append((hostname, server_info)) + + # attempt to select the correct environment if specified + if self.env: + results = filter(lambda result: result[1]['ec2_tag_environment'] == self.env, results) if results: return results else: - print "Could not find specified host: %s in %s" % (self.host, self.env) + print "Could not find specified host: %s." % self.host # default - no results found. return None @@ -135,7 +132,7 @@ class Ossh(object): ''' if self.env: - results = self.select_host(True) + results = self.select_host() if len(results) == 1: hostname, server_info = results[0] sorted_keys = server_info.keys() -- cgit v1.2.3 From cf65db9573ec99b0f4e2b599b6f158785602c230 Mon Sep 17 00:00:00 2001 From: Kenny Woodson Date: Wed, 11 Feb 2015 16:11:18 -0500 Subject: Updated oscp and ossh. --- bin/oscp | 7 ++----- bin/ossh | 17 +++++++---------- bin/ossh_bash_completion | 2 +- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/bin/oscp b/bin/oscp index 463dadaef..385e36732 100755 --- a/bin/oscp +++ b/bin/oscp @@ -54,8 +54,8 @@ class Oscp(object): help='options to pass to SSH.\n \ "-oPort=22,TCPKeepAlive=yes"') - parser.add_argument('src', default='') - parser.add_argument('dest',default='') + parser.add_argument('src', nargs='?', default='') + parser.add_argument('dest',nargs='?', default='') self.args = parser.parse_args() self.parser = parser @@ -89,9 +89,6 @@ class Oscp(object): else: self.host = search.groups()[0] self.path = search.groups()[1] - else: - print "Could not determine user and hostname." - return if self.args.env: self.env = self.args.env diff --git a/bin/ossh b/bin/ossh index d74244501..6b04cb46e 100755 --- a/bin/ossh +++ b/bin/ossh @@ -24,15 +24,10 @@ class Ossh(object): # parse host and user self.process_host() - if not self.args.list and not self.env: - print "Please specify an environment." - return - if self.args.host == '' and not self.args.list: self.parser.print_help() return - if self.args.debug: print self.args @@ -176,16 +171,18 @@ class Ossh(object): for arg in self.args.ssh_opts.split(","): ssh_args.append("-o%s" % arg) - result = self.select_host() - if not result: + results = self.select_host() + if not results: return # early exit, no results - if len(result) > 1: - self.list_hosts(10) + if len(results) > 1: + print "Multiple results found for %s." % self.host + for result in results: + print "{ec2_tag_Name:<35} {ec2_tag_environment:<5} {ec2_id:<10}".format(**result[1]) return # early exit, too many results # Assume we have one and only one. - hostname, server_info = result[0] + hostname, server_info = results[0] dns = server_info['ec2_public_dns_name'] ssh_args.append(dns) diff --git a/bin/ossh_bash_completion b/bin/ossh_bash_completion index 0d0bdb0e6..6a95ce6ee 100755 --- a/bin/ossh_bash_completion +++ b/bin/ossh_bash_completion @@ -15,4 +15,4 @@ _ossh() return 0 } -complete -F _ossh ossh +complete -F _ossh ossh oscp -- cgit v1.2.3 From 1505751e7846266285c0c517b8cb85cee17c6a5c Mon Sep 17 00:00:00 2001 From: Kenny Woodson Date: Thu, 12 Feb 2015 09:50:18 -0500 Subject: Cleanup before PR approval. --- bin/ansibleutil.py | 1 - bin/oscp | 1 - 2 files changed, 2 deletions(-) diff --git a/bin/ansibleutil.py b/bin/ansibleutil.py index 07e41b42c..b9f37726e 100644 --- a/bin/ansibleutil.py +++ b/bin/ansibleutil.py @@ -5,7 +5,6 @@ import sys import os import json import re -import collections class AnsibleUtil(object): def __init__(self): diff --git a/bin/oscp b/bin/oscp index 385e36732..a139ef38e 100755 --- a/bin/oscp +++ b/bin/oscp @@ -95,7 +95,6 @@ class Oscp(object): elif "." in self.host: self.host, self.env = self.host.split(".") else: - print "HERE" self.env = None def get_hosts(self, cache_only=False): -- cgit v1.2.3 From d751d4d53a73a7cf7096e55777b1a7c321bb4732 Mon Sep 17 00:00:00 2001 From: Kenny Woodson Date: Thu, 12 Feb 2015 10:57:58 -0500 Subject: Updated zsh completion to work with oscp --- bin/README_SHELL_COMPLETION | 12 ++++++++---- bin/ossh_zsh_completion | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/bin/README_SHELL_COMPLETION b/bin/README_SHELL_COMPLETION index e17b4b205..46ed7134c 100644 --- a/bin/README_SHELL_COMPLETION +++ b/bin/README_SHELL_COMPLETION @@ -1,8 +1,8 @@ -# ossh is an ssh replacement. +# completion is available for ossh/oscp - -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. +ossh/oscp 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. @@ -31,3 +31,7 @@ Once $fpath includes the _ossh_zsh_completion script then you should run `exec zsh`. This will then allow you to call `ossh host[TAB]` for a list of completions. +Before completing the final step, zsh keeps its own cache in +~/.zcompdump of the known functions and variables. In order to +refresh with new variables and completion arrays you might need +to `rm ~/.zcompdump` before running `exec zsh`. diff --git a/bin/ossh_zsh_completion b/bin/ossh_zsh_completion index f057ca8ce..6ab930dc4 100644 --- a/bin/ossh_zsh_completion +++ b/bin/ossh_zsh_completion @@ -1,4 +1,4 @@ -#compdef ossh +#compdef ossh oscp _ossh_known_hosts(){ if [[ -f ~/.ansible/tmp/multi_ec2_inventory.cache ]]; then -- cgit v1.2.3