Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rpc handlers #1

Merged
merged 53 commits into from Jul 26, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
0147143
More PEP8 cleanups and removal of junk comments
Jul 9, 2013
d4e20ba
Split out callbacks again
Jul 9, 2013
f434a93
Moved utility functions to their own file
Jul 9, 2013
0a1e41c
Fix sshmap shell command
dwighthubbard Jul 9, 2013
72fc204
Fix sshmap shell command
dwighthubbard Jul 9, 2013
25a9fe3
Fix sshmap shell command
dwighthubbard Jul 9, 2013
7fbf355
Another attempt at fixing sudo
dwighthubbard Jul 9, 2013
b301666
Remove extra passwords and prompts
Jul 9, 2013
5ed4c59
Remove apssword prompt when using sudo
Jul 9, 2013
09f3b6b
Add additional prompt check
Jul 9, 2013
6e61f8b
Add debug prints
Jul 9, 2013
9fe02aa
Update version
Jul 9, 2013
ef9136f
Try and fix sudo hanging again
Jul 9, 2013
96cdf51
Undo more changes
Jul 9, 2013
713089a
Update version
Jul 9, 2013
0d2da05
Add back the code to skip the sudo password and prompts
Jul 9, 2013
e77e25c
Forgot to set it back to the result.err
Jul 9, 2013
84c37ff
Clean up old python2 format print in dump
Jul 10, 2013
8e4fdc2
Moved default global variables out of the main script file
Jul 10, 2013
283b378
PEP8 cleanups
Jul 10, 2013
932fc7c
Add unittest
Jul 10, 2013
95900ae
Remove incomplete test
Jul 10, 2013
750610a
Fix permissions
Jul 10, 2013
41d5655
Set file mode to write
Jul 10, 2013
c4af386
Add sudo test
Jul 10, 2013
0ed6cac
Remove wrong assert
Jul 10, 2013
19abe17
Fix assert statement
Jul 10, 2013
b465f23
Fix command execution
Jul 10, 2013
597581b
Add sudo script test
Jul 10, 2013
6269b35
PEP8 cleanups
Jul 10, 2013
0a5880e
Start adding rpc and unit tests
Jul 22, 2013
0dcaf0b
Added missing .gitignore
Jul 22, 2013
553f067
Add missign import to __init__.py
Jul 22, 2013
31826fe
Another attempt at fixing sudo
dwighthubbard Jul 22, 2013
bc1d1ca
runner additions/changes
dwighthubbard Jul 23, 2013
915b71f
pep8 cleanup
Jul 23, 2013
0e8ddc9
Merge remote-tracking branch 'origin/rpc_handlers' into rpc_handlers
Jul 23, 2013
56793a0
pep8 cleanup
Jul 23, 2013
9933c59
Use a runner
Jul 23, 2013
5f3ed33
Fix the test
Jul 23, 2013
e435504
Fix imports
Jul 23, 2013
e58a1d7
Have the remove passwords code ignore blank lines
Jul 24, 2013
ba01265
Remove the sudo prompt message from the stdout stream when using sudo
Jul 24, 2013
8be72be
Update version
Jul 24, 2013
6099684
Remove disabled code and PEP8 cleanups
Jul 24, 2013
d344538
Update version
Jul 24, 2013
cca24d9
Pep8 cleanups
Jul 24, 2013
7fe4ef6
Update version
Jul 24, 2013
e929474
Change the env variable for the sudo password
Jul 24, 2013
6132c5e
Add some from imports to pull in callbacks into their old name in the…
Jul 25, 2013
c60613e
Update version
Jul 25, 2013
d97f064
Move backwards compat stuff to __init__.py
Jul 25, 2013
fb5487b
Remove junk .idea directory
Jul 26, 2013
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
41 changes: 41 additions & 0 deletions .gitignore
@@ -0,0 +1,41 @@
*.py[cod]

# C extensions
*.so

# Packages
*.egg
*.egg-info
dist
build
eggs
parts
bin
var
sdist
develop-eggs
.installed.cfg
lib
lib64

# Installer logs
pip-log.txt

# Unit test / coverage reports
.coverage
.tox
nosetests.xml

# Translations
*.mo

# Mr Developer
.mr.developer.cfg
.project
.pydevproject

# Pycharm
.idea

# Backup files
*~
12 changes: 0 additions & 12 deletions LICENSE.txt

This file was deleted.

2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -19,7 +19,7 @@

setup(
name="sshmap",
version="0.5.8",
version="0.5.50",
author="Dwight Hubbard",
author_email="dhubbard@yahoo-inc.com",
url="https://github.com/dwighthubbard/sshmap",
Expand Down
21 changes: 18 additions & 3 deletions sshmap/__init__.py 100644 → 100755
@@ -1,13 +1,28 @@
#Copyright (c) 2012 Yahoo! Inc. All rights reserved.
#Copyright (c) 2012-2013 Yahoo! Inc. All rights reserved.
#Licensed under the Apache License, Version 2.0 (the "License");
#you may not use this file except in compliance with the License.
#You may obtain a copy of the License at

#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License. See accompanying LICENSE file.
from sshmap import *

#from sshmap import run, run_command, ssh_result, ssh_results, fastSSHClient
import sshmap
import callback
import utility
import runner

# For backwards compatibility
from callback import summarize_failures as callback_summarize_failures
from callback import aggregate_output as callback_aggregate_output
from callback import exec_command as callback_exec_command
from callback import filter_match as callback_filter_match
from callback import status_count as callback_status_count

# The actual used sshmap functions
from sshmap import run, run_command
228 changes: 228 additions & 0 deletions sshmap/callback.py
@@ -0,0 +1,228 @@
#Copyright (c) 2012 Yahoo! Inc. All rights reserved.
#Licensed under the Apache License, Version 2.0 (the "License");
#you may not use this file except in compliance with the License.
#You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License. See accompanying LICENSE file.
__author__ = 'dhubbard'
"""
sshmap built in callback handlers
"""
import os
import sys
import hashlib
import json
import stat
import base64
import subprocess

import utility


# Filter callback handlers
def flowthrough(result):
"""
Builtin Callback, return the raw data passed

>>> result=flowthrough(ssh_result(["output"], ["error"],"foo", 0))
>>> result.dump()
foo output error 0 0 None
"""
return result


def summarize_failures(result):
"""
Builtin Callback, put a summary of failures into parm
"""
failures = result.setting('failures')
if not failures:
result.parm['failures'] = []
failures = []
if result.ssh_retcode:
failures.append(result.host)
result.parm['failures'] = failures
return result


def exec_command(result):
"""
Builtin Callback, pass the results to a command/script
:param result:
"""
script = result.setting("callback_script")
if not script:
return result
utility.status_clear()
result_out, result_err = subprocess.Popen(
script + " " + result.host,
shell=True,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
).communicate(
result.out_string() + result.err_string()
)
result.out = [result_out]
result.err = [result_err]
print(result.out_string())
return result


def aggregate_output(result):
""" Builtin Callback, Aggregate identical results """
aggregate_hosts = result.setting('aggregate_hosts')
if not aggregate_hosts:
aggregate_hosts = {}
collapsed_output = result.setting('collapsed_output')
if not collapsed_output:
collapsed_output = {}
h = hashlib.md5()
h.update(result.out_string())
h.update(result.err_string())
if result.ssh_retcode:
h.update(result.ssh_error_message())
digest = h.hexdigest()
if digest in aggregate_hosts.keys():
aggregate_hosts[digest].append(result.host)
else:
aggregate_hosts[digest] = [result.host]
if result.ssh_retcode:
error = []
if result.err:
error = result.err
error.append(result.ssh_error_message())
collapsed_output[digest] = (result.out, error)
else:
collapsed_output[digest] = (result.out, result.err)
result.parm['aggregate_hosts'] = aggregate_hosts
if collapsed_output:
result.parm['collapsed_output'] = collapsed_output
return result


def filter_match(result):
"""
Builtin Callback, remove all output if the string is not found in the
output
similar to grep
:param result:
"""
if result.out_string().find(result.setting('match')) == -1 and \
result.err_string().find(result.setting('match')) == -1:
result.out = ''
result.err = ''
return result


def filter_json(result):
"""
Builtin Callback, change stdout to json

>>> result=filter_json(ssh_result(["output"], ["error"],"foo", 0))
>>> result.dump()
foo [["output"], ["error"], 0] error 0 0 None
"""
result.out = [json.dumps((result.out, result.err, result.retcode))]
return result


def filter_base64(result):
"""
Builtin Callback, base64 encode the info in out and err streams
"""
result.out = [base64.b64encode(result.out_string)]
result.err = [base64.b64encode(result.err_string)]
return result


#Status callback handlers
def status_count(result):
"""
Builtin Callback, show the count complete/remaining
:param result:
"""
# The master process inserts the status into the
# total_host_count and completed_host_count variables
sys.stderr.write('\x1b[0G\x1b[0K%s/%s' % (
result.setting('completed_host_count'),
result.setting('total_host_count')))
sys.stderr.flush()
return result


#Output callback handlers
def output_prefix_host(result):
"""
Builtin Callback, print the output with the hostname: prefixed to each line
:param result:
hostname: out

>>> result=sshmap.callback.output_prefix_host(ssh_result(['out'],['err'], 'hostname', 0))
>>> result.dump()
"""
output = []
error = []
utility.status_clear()
# If summarize_failures option is set don't print ssh errors inline
if result.setting('summarize_failed') and result.ssh_retcode:
return result
if result.setting('print_rc'):
rc = ' SSH_Returncode: %d\tCommand_Returncode: %d' % (
result.ssh_retcode, result.retcode)
else:
rc = ''
if result.ssh_retcode:
print >> sys.stderr, '%s: %s' % (result.host, result.ssh_error_message())
error = ['%s: %s' % (result.host, result.ssh_error_message())]
if len(result.out_string()):
for line in result.out:
if line:
print '%s:%s %s' % (result.host, rc, line.strip())
output.append('%s:%s %s\n' % (result.host, rc, line.strip()))
if len(result.err_string()):
for line in result.err:
if line:
print >> sys.stderr, '%s:%s %s' % (result.host, rc, line.strip())
error.append('%s:%s Error: %s\n' % (result.host, rc, line.strip()))
if result.setting('output'):
if not len(result.out_string()) and not len(result.err_string()) and not result.setting(
'only_output') and result.setting('print_rc'):
print '%s:%s' % (result.host, rc)
sys.stdout.flush()
sys.stderr.flush()
result.out = output
result.err = error
return result


def read_conf(key=None, prompt=True):
""" Read settings from the config file
:param key:
:param prompt:
"""
try:
conf = json.load(open(os.path.expanduser('~/.sshmap.conf'), 'r'))
except IOError:
conf = sshmap.conf_defaults
if key:
try:
return conf[key].encode('ascii')
except KeyError:
pass
else:
return conf
if key and prompt:
conf[key] = raw_input(sshmap.conf_desc[key] + ': ')
fh = open(os.path.expanduser('~/.sshmap2.conf'), 'w')
os.fchmod(fh.fileno(), stat.S_IRUSR | stat.S_IWUSR)
json.dump(conf, fh)
fh.close()
return conf[key]
else:
return None
67 changes: 67 additions & 0 deletions sshmap/defaults.py
@@ -0,0 +1,67 @@
#Copyright (c) 2012 Yahoo! Inc. All rights reserved.
#Licensed under the Apache License, Version 2.0 (the "License");
#you may not use this file except in compliance with the License.
#You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License. See accompanying LICENSE file.
"""
Default Values for sshmap
"""
__author__ = 'dhubbard'
import os

# Defaults
JOB_MAX = 100
# noinspection PyBroadException
try:
for line in open('/proc/%d/limits' % os.getpid(), 'r').readlines():
if line.startswith('Max processes'):
JOB_MAX = int(line.strip().split()[2]) / 4
except:
pass

# Return code values
RUN_OK = 0
RUN_FAIL_AUTH = 1
RUN_FAIL_TIMEOUT = 2
RUN_FAIL_CONNECT = 3
RUN_FAIL_SSH = 4
RUN_SUDO_PROMPT = 5
RUN_FAIL_UNKNOWN = 6
RUN_FAIL_NOPASSWORD = 7
RUN_FAIL_BADPASSWORD = 8

# Text return codes
RUN_CODES = ['Ok', 'Authentication Error', 'Timeout', 'SSH Connection Failed',
'SSH Failure',
'Sudo did not send a password prompt', 'Connection refused',
'Sudo password required',
'Invalid sudo password']

# Configuration file field descriptions
conf_desc = {
"username": "IRC Server username",
"password": "IRC Server password",
"channel": "sshmap",
}

# Configuration file defaults
conf_defaults = {
"address": "chat.freenode.net",
"port": "6667",
"use_ssl": False,
}

sudo_message = [
'We trust you have received the usual lecture from the local System',
'Administrator. It usually boils down to these three things:',
'#1) Respect the privacy of others.',
'#2) Think before you type.',
'#3) With great power comes great responsibility.'
]