class UserSSHKeys(dict): """ List of user configured SSH keys to process """ def __init__(self, authorized_keys=DEFAULT_AUTHORIZED_KEYS): self.log = Logger().default_stream self.__parse_user_keyfiles() self.authorized_keys = AuthorizedKeys(authorized_keys) def __parse_user_keyfiles(self): """ Parses any normal user keyfiles in ~/.ssh directory automatically. These keys are marked by default not to be automatically loaded: to enable, add key path to ~/.ssh/sshkeys.conf """ user_sshdir = os.path.expanduser('~/.ssh') if not os.path.isdir(user_sshdir): return paths = filter(lambda x: os.path.isfile(x), [ os.path.join(user_sshdir, x) for x in filter( lambda x: x not in SSH_CONFIG_FILES and os.path.splitext(x)[1] != '.pub', os.listdir(user_sshdir)) ]) for path in paths: try: sshkey = SSHKeyFile(self, path) except SSHKeyError, emsg: self.log.debug(emsg) continue self[sshkey.path] = sshkey
class KnownHosts(list): """ Parser for OpenSSH known_hosts file contents """ def __init__(self, path=DEFAULT_KNOWN_HOSTS, fingerprint_hash=None): self.log = Logger().default_stream self.path = path self.fingerprint_hash = fingerprint_hash self.load() def load(self): """ Load known hosts file, discarding earlier data in the object. """ del self[0:len(self)] if not os.path.isfile(self.path): self.log.debug('No such file: {}'.format(self.path)) return for line in [l.rstrip() for l in open(self.path, 'r').readlines()]: if line.startswith('#') or line.strip() == '': continue # Strip list of hosts from line hosts, key = line.split(None, 1) hosts = hosts.split(',') try: key = KnownHostsEntry(key) if key not in self: self.append(key) else: key = self[self.index(key)] key.add_hosts(hosts) except SSHKeyError: pass def save(self, path=None): """Save known hosts Saves known hosts file, merging same keys to single line """ if path is None: path = self.path try: with open(path, 'w') as fd: for entry in self: fd.write('{}\n'.format(entry)) except Exception as e: raise SSHKeyError('Error writing {}: {}'.format(path, e)) def find_host_key(self, value): """Find key for hostname """ for key in self: if value in key.hosts: return key return None
class AuthorizedKeys(list): """ Parser for OpenSSH authorized_keys file contents """ def __init__(self, path=DEFAULT_AUTHORIZED_KEYS): self.log = Logger().default_stream self.path = path self.load() def load(self): """ Load authorized keys file, discarding earlier data in the object. SSH v1 authorized_keys line: options, bits, exponent, modulus, comment) SSH v2 authorized_keys line: options, keytype, base64-encoded key, comment)' See man sshd for details. """ self.__delslice__(0, len(self)) if not os.path.isfile(self.path): self.log.debug('No such file: {0}'.format(self.path)) return for line in [l.rstrip() for l in open(self.path, 'r').readlines()]: if line.startswith('#') or line.strip() == '': continue try: self.append(OpenSSHPublicKey(line)) except SSHKeyError: pass
class KnownHosts(list): """ Parser for OpenSSH known_hosts file contents """ def __init__(self, path=DEFAULT_KNOWN_HOSTS, fingerprint_hash=None): self.log = Logger().default_stream self.path = path self.fingerprint_hash = fingerprint_hash self.load() def load(self): """ Load known hosts file, discarding earlier data in the object. """ del self[0:len(self)] if not os.path.isfile(self.path): self.log.debug('No such file: {}'.format(self.path)) return for line in [l.rstrip() for l in open(self.path, 'r').readlines()]: if line.startswith('#') or line.strip() == '': continue # Strip list of hosts from line hosts, key = line.split(None, 1) hosts = hosts.split(',') try: key = KnownHostsEntry(key) if key not in self: self.append(key) else: key = self[self.index(key)] key.add_hosts(hosts) except SSHKeyError: pass def save(self, path=None): """Save known hosts Saves known hosts file, merging same keys to single line """ if path is None: path = self.path try: with open(path, 'w') as fd: for entry in self: fd.write('{}\n'.format(entry)) except Exception as e: raise SSHKeyError('Error writing {}: {}'.format(path, e)) def find_host_key(self, value): """Find key for hostname """ for key in self: if value in key.hosts: return key return None
class AuthorizedKeys(list): """ Parser for OpenSSH authorized_keys file contents """ def __init__(self, path=DEFAULT_AUTHORIZED_KEYS): self.log = Logger().default_stream self.path = path self.load() def load(self): """ Load authorized keys file, discarding earlier data in the object. SSH v1 authorized_keys line: options, bits, exponent, modulus, comment) SSH v2 authorized_keys line: options, keytype, base64-encoded key, comment)' See man sshd for details. """ self.__delslice__(0, len(self)) if not os.path.isfile(self.path): self.log.debug('No such file: {0}'.format(self.path)) return for line in [l.rstrip() for l in open(self.path, 'r').readlines()]: if line.startswith('#') or line.strip() == '': continue try: self.append(OpenSSHPublicKey(line)) except SSHKeyError: pass
class SSHConfig(dict): def __init__(self, path=None): self.defaults = {} self.patterns = [] self.log = Logger().default_stream self.path = path is not None and path or os.path.expanduser( '~/.ssh/config') self.reload() def reload(self): self.clear() if not os.path.isfile(self.path): self.log.debug('No such file: {}'.format(self.path)) return with open(self.path, 'r') as fd: host = None for line in [x.strip() for x in fd.readlines()]: if line == '' or line.startswith('#'): continue if line[:5] == 'Host ': host = SSHConfigHostPattern(self, line[5:]) for pattern in host.patterns: self[pattern] = host self.patterns.append(host) else: host.parse(line) if '*' in self.keys(): self.defaults.update(self.pop('*').items()) def keys(self): return [k for k in sorted(super(SSHConfig, self).keys())] def items(self): return [(k, self[k]) for k in self.keys()] def values(self): return [self[k] for k in self.keys()]
class SSHConfig(dict): def __init__(self, path=None): self.defaults = {} self.patterns = [] self.log = Logger().default_stream self.path = path is not None and path or os.path.expanduser('~/.ssh/config') self.reload() def reload(self): self.clear() if not os.path.isfile(self.path): self.log.debug('No such file: {}'.format(self.path)) return with open(self.path, 'r') as fd: host = None for line in [x.strip() for x in fd.readlines()]: if line == '' or line.startswith('#'): continue if line[:5] == 'Host ': host = SSHConfigHostPattern(self, line[5:]) for pattern in host.patterns: self[pattern] = host self.patterns.append(host) else: host.parse(line) if '*' in self.keys(): self.defaults.update(self.pop('*').items()) def keys(self): return [k for k in sorted(super(SSHConfig, self).keys())] def items(self): return [(k, self[k]) for k in self.keys()] def values(self): return [self[k] for k in self.keys()]
class SSHConfig(dict): def __init__(self, path=None): self.defaults = {} self.log = Logger().default_stream self.path = path is not None and path or os.path.expanduser('~/.ssh/config') self.reload() def reload(self): self.clear() if not os.path.isfile(self.path): self.log.debug('No such file: %s' % self.path) return with open(self.path, 'r') as fd: host = None for l in [x.strip() for x in fd.readlines()]: if l=='' or l.startswith('#'): continue if l[:5]=='Host ': host = SSHConfigHost(self, l[5:]) self[host.name] = host else: host.parse(l) if '*' in self.keys(): self.defaults.update(self.pop('*').items()) def keys(self): return [k for k in sorted(dict.keys(self))] def items(self): return [(k, self[k]) for k in self.keys()] def values(self): return [self[k] for k in self.keys()]
class UserSSHKeys(dict): """ List of user configured SSH keys to process """ def __init__(self, authorized_keys=DEFAULT_AUTHORIZED_KEYS): self.log = Logger().default_stream self.__parse_user_keyfiles() self.authorized_keys = AuthorizedKeys(authorized_keys) def __parse_user_keyfiles(self): """ Parses any normal user keyfiles in ~/.ssh directory automatically. These keys are marked by default not to be automatically loaded: to enable, add key path to ~/.ssh/sshkeys.conf """ user_sshdir = os.path.expanduser('~/.ssh') if not os.path.isdir(user_sshdir): return paths = filter(lambda x: os.path.isfile(x), [os.path.join(user_sshdir, x) for x in filter(lambda x: x not in SSH_CONFIG_FILES and os.path.splitext(x)[1]!='.pub', os.listdir(user_sshdir) )] ) for path in paths: try: sshkey = SSHKeyFile(self, path) except SSHKeyError, emsg: self.log.debug(emsg) continue self[sshkey.path] = sshkey
class RsyncCommand(object): """ Wrapper to execute rsync to target nicely from python """ def __init__(self, src, dst, flags=DEFAULT_RSYNC_FLAGS, output_format=DEFAULT_OUTPUT_FORMAT): self.log = Logger('rsync').default_stream self.src = src self.dst = dst self.flags = flags self.output_format = output_format cmd = CommandPathCache().which('rsync') if cmd is None: raise RsyncError('No such command: rsync') self.command = [cmd] + flags + [ self.output_format, '{0}'.format(src), '{0}'.format(dst) ] def __str__(self): return ' '.join(self.command) def run(self, verbose=False): """ Run the rsync command specific in __init__() """ if not verbose and self.log.level == logging.DEBUG: verbose = True try: p = Popen(self.command, stdin=PIPE, stdout=PIPE, stderr=PIPE) self.log.debug('Running: {0}'.format(self)) rval = None while rval is None: if verbose: while True: line = p.stdout.readline() if line == '': break sys.stdout.write('{0}\n'.format(line.rstrip())) time.sleep(0.2) rval = p.poll() self.log.debug('Return code: {0}'.format(rval)) if rval != 0: raise RsyncError('Error running command {0}: {1}'.format( self, p.stderr.read())) except KeyboardInterrupt: self.log.debug('Rsync interrupted') raise KeyboardInterrupt
class RsyncCommand(object): """ Wrapper to execute rsync to target nicely from python """ def __init__(self, src, dst, flags=DEFAULT_RSYNC_FLAGS, output_format=DEFAULT_OUTPUT_FORMAT): self.log = Logger('rsync').default_stream self.src = src self.dst = dst self.flags = flags self.output_format = output_format cmd = CommandPathCache().which('rsync') if cmd is None: raise RsyncError('No such command: rsync') self.command = [cmd] + flags + [self.output_format, '{0}'.format(src), '{0}'.format(dst)] def __str__(self): return ' '.join(self.command) def run(self, verbose=False): """ Run the rsync command specific in __init__() """ if not verbose and self.log.level == logging.DEBUG: verbose = True try: p = Popen(self.command, stdin=PIPE, stdout=PIPE, stderr=PIPE) self.log.debug('Running: {0}'.format(self)) rval = None while rval is None: if verbose: while True: line = p.stdout.readline() if line == '': break sys.stdout.write('{0}\n'.format(line.rstrip())) time.sleep(0.2) rval = p.poll() self.log.debug('Return code: {0}'.format(rval)) if rval != 0: raise RsyncError('Error running command {0}: {1}'.format(self, p.stderr.read())) except KeyboardInterrupt: self.log.debug('Rsync interrupted') raise KeyboardInterrupt
class UserSSHKeys(dict): """ List of user configured SSH keys to process """ def __init__(self, authorized_keys=DEFAULT_AUTHORIZED_KEYS): self.log = Logger().default_stream self.__parse_user_keyfiles() self.authorized_keys = AuthorizedKeys(authorized_keys) def __parse_user_keyfiles(self): """ Parses any normal user keyfiles in ~/.ssh directory automatically. These keys are marked by default not to be automatically loaded: to enable, add key path to ~/.ssh/sshkeys.conf """ user_sshdir = os.path.expanduser('~/.ssh') if not os.path.isdir(user_sshdir): return paths = [] for filename in os.listdir(user_sshdir): if filename in SSH_CONFIG_FILES or os.path.splitext(filename)[1] != '.pub': continue path = os.path.join(user_sshdir, filename) if os.path.isfile(path): paths.append(path) for path in paths: try: sshkey = SSHKeyFile(self, path) except SSHKeyError as e: self.log.debug(e) continue self[sshkey.path] = sshkey def read_config(self, path): """ Read a key configuration file, containing list of keys outside ~/.ssh directory to process. """ if not os.path.isfile(path): raise SSHKeyError('No such file: {0}'.format(path)) try: for l in [l.rstrip() for l in open(path, 'r').readlines()]: sshkey = SSHKeyFile(self, os.path.expandvars(os.path.expanduser(l))) if sshkey.path not in self.keys(): self[sshkey.path] = sshkey self[sshkey.path].autoload = True except IOError as e: raise SSHKeyError('Error loading {0}: {1}'.format(path, e)) except OSError as e: raise SSHKeyError('Error loading {0}: {1}'.format(path, e)) @property def loaded_keys(self): """ Return fingerprint, key mapping of keys loaded to ssh-agent """ keys = {} cmd = ['ssh-add', '-l'] p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) (stdout, stderr) = [x.decode('utf-8') for x in p.communicate()] if p.returncode == 1: l = stdout.split('\n')[0].strip() if l == 'The agent has no identities.': return keys if p.returncode != 0: raise SSHKeyError('Error listing loaded SSH agent keys') for l in [l.rstrip() for l in stdout.split('\n')[:-1]]: data = parse_public_key_line_pattern(l) if not data: raise SSHKeyError('Error parsing agent key list line {0}'.format(l)) keys[data['fingerprint']] = data return keys @property def available(self): return [k for k in self.values() if k.available] def keys(self): """ Return key filenames as sorted list """ return sorted(super(UserSSHKeys, self).keys()) def items(self): """ Return (filename, key) value pairs sorted with self.keys() """ return [(k, self[k]) for k in self.keys()] def values(self): """ Return keys sorted with self.keys() """ return [self[k] for k in self.keys()] def fix_permissions(self, directory_permissions=SSH_DIR_PERMS, file_permissions=SSH_FILE_PERMS): """ Fix permissions in ~/.ssh to match permissions set by parameters. All files must be writable by user to use this function. Default values: directory_permissions Directory permissions, by default '0700' file_permissions File permissions, bt default '0600' """ ssh_dir = os.path.expanduser('~/.ssh') dperm = int(directory_permissions, 8) fperm = int(file_permissions, 8) if not os.path.isdir(ssh_dir): self.log.debug('No such directory: {0}'.format(ssh_dir)) return for (root, dirs, files) in os.walk(ssh_dir): if stat.S_IMODE(os.stat(root).st_mode) != dperm: self.log.debug('Fixing permissions for directory {0}'.format(root)) os.chmod(root, dperm) for f in [os.path.join(root, f) for f in files]: if stat.S_IMODE(os.stat(f).st_mode) != fperm: self.log.debug('Fixing permissions for file {0}'.format(f)) os.chmod(f, fperm) def load_keys(self, keys): """Load given keys Load given keys to SSH agent. Checks if key was already loaded and skips if it was. Keys can be either paths or SSHKeyFile instances. """ paths = [] for key in keys: if isinstance(key, SSHKeyFile): if not key.is_loaded: paths.append(key.path) elif isinstance(key, str): paths.append(key) if paths: self.log.debug('Loading {0:d} keys to SSH agent'.format(len(paths))) cmd = [ 'ssh-add'] + paths p = Popen(cmd, stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr) p.wait() else: self.log.debug('All SSH keys were already loaded to SSH agent')
class ServerConfigFile(object): """ Parser for configuration file describing servers and their operating systems. """ def __init__(self, path): self.operating_systems = [] self.servers = [] self.path = path self.log = Logger().default_stream if os.path.isfile(self.path): self.load() @property def osnames(self): return [x.name for x in self.operating_systems] def load(self): self.operating_systems = [] self.servers = [] try: config = ConfigObj(self.path) except ValueError as e: raise ValueError("Error parsing {0}: {1}".format(self.path, e)) osgroup = None for key, section in config.items(): if "commands" in section: if key in self.servers: raise ValueError("Duplicate OS group name: {0}".format(key)) osgroup = OperatingSystemGroup(self, key, **section) self.operating_systems.append(osgroup) self.servers.extend(osgroup.servers) else: if osgroup is None: raise ValueError("Server in configuration file but no OS group defined yet") self.servers.append(Server(osgroup, section)) return def save(self): config = ConfigObj() for osgroup in self.operating_systems: section = {"commands": osgroup.update_commands} if osgroup.description is not None and osgroup.description != "": section["description"] = osgroup.description if osgroup.servers: section["servers"] = osgroup.servers if osgroup.connect_command != DEFAULT_CONNECT_COMMAND: section["connect"] = osgroup.connect_command if osgroup.command_separator != DEFAULT_COMMAND_SEPARATOR: section["command_separator"] = osgroup.command_separator config[osgroup.name] = section for server in osgroup.servers: if server.description: config[server.name] = {"description": server.description} self.log.debug("Saving configuration to {0}".format(self.path)) config.write(outfile=open(self.path, "w")) def match_os(self, name): for os in self.operating_systems: if os.name == name: return os raise ValueError("Unknown OS: {0}".format(name))
class SNMPAgent(Script): """SNMP Agent implementation SNMP agent daemon for net-snmpd pass_persist mode Example usage: agent = SNMPAgent('.1.2.3.4') tree = agent.register_tree('1.2.3.4.1') tree.register('1.2.3.4.1.1', 'integer', 1) tree.register('1.2.3.4.1.2', 'integer', 2) agent.register_tree('1.2.3.4.2') agent.register('1.2.3.4.2.1', 'integer', 1) agent.register('1.2.3.4.2.4', 'integer', 4) agent.register('1.2.3.4.2.3', 'integer', 3) agent.register('1.2.3.4.2.2', 'integer', 2) tree = agent.register_tree('1.2.3.4.3') tree.register('1.2.3.4.3.1.1', 'integer', 1) tree.add_values('string', [x for x in string.digits[2:]]) tree = agent.register_tree('1.2.3.4.4') tree.add_prefix_map([ { 'oid': '1.2.3.4.4.1', 'key': 'string', 'values': [x for x in string.letters[:10]]}, { 'oid': '1.2.3.4.4.2', 'key': 'integer', 'values': [x for x in string.digits[:10]]}, ]) agent.run() """ def __init__(self, oid, reload_interval=60): Script.__init__(self) self.log = Logger('snmp').default_stream self.reload_interval = reload_interval self.last_reload = None self.add_argument('-g', '--get', help='SNMP GET request') self.add_argument('-n', '--next', help='SNMP GET request') self.add_argument('-t', '--tree', action='store_true', help='Show OID tree') self.tree = Tree(oid) def __SIGHUP__(self, signum, frame): """ Signal handler to reload configuration. Note this requires also the IOError processing in main input loop below """ self.log.debug('Reloading from signal') self.reload() def register_tree(self, oid): return self.tree.add(Tree(oid)) def register(self, oid, key, value): return self.tree.add(Item(oid, key, value)) def GET(self, oid): """GET from agent Return given OID from agent tree or None """ try: return self.tree.GET(oid) except SNMPError, emsg: self.log.debug(emsg) return None
class PlaybookRunnerCallbacks(callbacks.PlaybookRunnerCallbacks): """Playbook runner callbacks Override version of ansible.callbacks.PlaybookRunnerCallbacks that only logs to default logger with debug messages, not actually doing anything else. """ def __init__(self, stats, verbose=None): callbacks.PlaybookRunnerCallbacks.__init__(self, stats, verbose) self.log = Logger().default_stream def on_unreachable(self, host, results): self.log.debug('host unreachable %s %s' % (host, results)) def on_failed(self, host, results, ignore_errors=False): self.log.debug('host failed %s %s' % (host, results)) def on_ok(self, host, host_result): self.log.debug('host ok %s %s' % (host, host_result)) def on_skipped(self, host, item=None): self.log.debug('skip %s item %s' % (host, item)) def on_no_hosts(self): self.log.debug('no hosts') def on_async_poll(self, host, res, jid, clock): self.log.debug('async poll %s' % host) def on_async_ok(self, host, res, jid): self.log.debug('async ok %s' % host) def on_async_failed(self, host, res, jid): self.log.debug('async failed %s' % host) def on_file_diff(self, host, diff): self.log.debug('file diff %s' % host)
class UserSSHKeys(dict): """ List of user configured SSH keys to process """ def __init__(self, authorized_keys=DEFAULT_AUTHORIZED_KEYS): self.log = Logger().default_stream self.__parse_user_keyfiles() self.authorized_keys = AuthorizedKeys(authorized_keys) def __parse_user_keyfiles(self): """ Parses any normal user keyfiles in ~/.ssh directory automatically. These keys are marked by default not to be automatically loaded: to enable, add key path to ~/.ssh/sshkeys.conf """ user_sshdir = os.path.expanduser('~/.ssh') if not os.path.isdir(user_sshdir): return paths = [] for filename in os.listdir(user_sshdir): if filename in SSH_CONFIG_FILES or os.path.splitext( filename)[1] != '.pub': continue path = os.path.join(user_sshdir, filename) if os.path.isfile(path): paths.append(path) for path in paths: try: sshkey = SSHKeyFile(self, path) except SSHKeyError as e: self.log.debug(e) continue self[sshkey.path] = sshkey def read_config(self, path): """ Read a key configuration file, containing list of keys outside ~/.ssh directory to process. """ if not os.path.isfile(path): raise SSHKeyError('No such file: {}'.format(path)) try: for l in [l.rstrip() for l in open(path, 'r').readlines()]: sshkey = SSHKeyFile(self, os.path.expandvars(os.path.expanduser(l))) if sshkey.path not in self.keys(): self[sshkey.path] = sshkey self[sshkey.path].autoload = True except IOError as e: raise SSHKeyError('Error loading {}: {}'.format(path, e)) except OSError as e: raise SSHKeyError('Error loading {}: {}'.format(path, e)) @property def loaded_keys(self): """ Return fingerprint, key mapping of keys loaded to ssh-agent """ keys = {} cmd = ['ssh-add', '-l'] p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) (stdout, stderr) = [str(x, 'utf-8') for x in p.communicate()] if p.returncode == 1: line = stdout.split('\n')[0].strip() if line == 'The agent has no identities.': return keys if p.returncode != 0: raise SSHKeyError('Error listing loaded SSH agent keys') for line in [line.rstrip() for line in stdout.split('\n')[:-1]]: data = parse_public_key_line_pattern(line) if not data: raise SSHKeyError( 'Error parsing agent key list line {}'.format(line)) keys[data['fingerprint']] = data return keys @property def available(self): return [k for k in self.values() if k.available] def keys(self): """ Return key filenames as sorted list """ return sorted(super(UserSSHKeys, self).keys()) def items(self): """ Return (filename, key) value pairs sorted with self.keys() """ return [(k, self[k]) for k in self.keys()] def values(self): """ Return keys sorted with self.keys() """ return [self[k] for k in self.keys()] def fix_permissions(self, directory_permissions=SSH_DIR_PERMS, file_permissions=SSH_FILE_PERMS): """ Fix permissions in ~/.ssh to match permissions set by parameters. All files must be writable by user to use this function. Default values: directory_permissions Directory permissions, by default '0700' file_permissions File permissions, bt default '0600' """ ssh_dir = os.path.expanduser('~/.ssh') dperm = int(directory_permissions, 8) fperm = int(file_permissions, 8) if not os.path.isdir(ssh_dir): self.log.debug('No such directory: {}'.format(ssh_dir)) return for (root, _dirs, files) in os.walk(ssh_dir): if stat.S_IMODE(os.stat(root).st_mode) != dperm: self.log.debug( 'Fixing permissions for directory {}'.format(root)) os.chmod(root, dperm) for f in [os.path.join(root, f) for f in files]: if stat.S_IMODE(os.stat(f).st_mode) != fperm: self.log.debug('Fixing permissions for file {}'.format(f)) os.chmod(f, fperm) def load_keys(self, keys): """Load given keys Load given keys to SSH agent. Checks if key was already loaded and skips if it was. Keys can be either paths or SSHKeyFile instances. """ paths = [] for key in keys: if isinstance(key, SSHKeyFile): if not key.is_loaded: paths.append(key.path) elif isinstance(key, str): paths.append(key) if paths: self.log.debug('Loading {0:d} keys to SSH agent'.format( len(paths))) cmd = ['ssh-add'] + paths p = Popen(cmd, stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr) p.wait() else: self.log.debug('All SSH keys were already loaded to SSH agent')
class ServerConfigFile(object): """ Parser for configuration file describing servers and their operating systems. """ def __init__(self, path): self.operating_systems = [] self.servers = [] self.path = path self.log = Logger().default_stream if os.path.isfile(self.path): self.load() @property def osnames(self): return [x.name for x in self.operating_systems] def load(self): self.operating_systems = [] self.servers = [] try: config = ConfigObj(self.path) except ValueError as e: raise ValueError('Error parsing {0}: {1}'.format(self.path, e)) osgroup = None for key, section in config.items(): if 'commands' in section: if key in self.servers: raise ValueError( 'Duplicate OS group name: {0}'.format(key)) osgroup = OperatingSystemGroup(self, key, **section) self.operating_systems.append(osgroup) self.servers.extend(osgroup.servers) else: if osgroup is None: raise ValueError( 'Server in configuration file but no OS group defined yet' ) self.servers.append(Server(osgroup, section)) return def save(self): config = ConfigObj() for osgroup in self.operating_systems: section = { 'commands': osgroup.update_commands, } if osgroup.description is not None and osgroup.description != '': section['description'] = osgroup.description if osgroup.servers: section['servers'] = osgroup.servers if osgroup.connect_command != DEFAULT_CONNECT_COMMAND: section['connect'] = osgroup.connect_command if osgroup.command_separator != DEFAULT_COMMAND_SEPARATOR: section['command_separator'] = osgroup.command_separator config[osgroup.name] = section for server in osgroup.servers: if server.description: config[server.name] = {'description': server.description} self.log.debug('Saving configuration to {0}'.format(self.path)) config.write(outfile=open(self.path, 'w')) def match_os(self, name): for operating_system in self.operating_systems: if operating_system.name == name: return operating_system raise ValueError('Unknown OS: {0}'.format(name))
class PlaybookCallbacks(callbacks.PlaybookCallbacks): """Playbook callbacks Override version of ansible.callbacks.PlaybookCallbacks that only logs to default logger with debug messages, not actually doing anything else. Please note that callback on_vars_prompt is NOT overridden, so if your code asks for variables we will use the standard chatty query version! """ def __init__(self, verbose=False): callbacks.PlaybookCallbacks.__init__(self, verbose) self.log = Logger().default_stream def on_start(self): self.log.debug('starting playbook') def on_notify(self, host, handler): self.log.debug('playbook notification') def on_no_hosts_remaining(self): self.log.debug('playbook no hosts remaining') def on_task_start(self, name, is_conditional): self.log.debug('playbook starting task "%s"' % name) def on_setup(self): self.log.debug('playbook setup') def on_import_for_host(self, host, imported_file): self.log.debug('playbook importing for host %s' % host) def on_not_import_for_host(self, host, missing_file): self.log.debug('playbook not importing for host %s' % host) def on_play_start(self, name): self.log.debug('playbook start play %s' % name) def on_no_hosts_matched(self): raise RunnerError('No hosts matched') def on_stats(self, stats): self.log.debug('playbook statistics %s' % stats)
class OperatingSystemGroup(object): """ Group of operating systems in configuration file """ def __init__(self, config, name, **kwargs): self.log = Logger().default_stream self.name = name self.description = name self.connect_command = DEFAULT_CONNECT_COMMAND self.command_separator = DEFAULT_COMMAND_SEPARATOR self.update_commands = [] self.servers = [] for k in ( 'description', 'command_separator', ): if k in kwargs: setattr(self, k, kwargs[k]) if 'connect' in kwargs: self.connect_command = kwargs['connect'] if 'commands' in kwargs: self.update_commands = kwargs['commands'] if 'servers' in kwargs: names = kwargs['servers'] if not isinstance(names, list): names = [names] for name in names: self.servers.append(Server(self, name)) self.modified = False def __repr__(self): return '%s: %s (%d servers)' % (self.name,self.description,len(self.servers)) @property def command_separator(self): return self._command_separator @command_separator.setter def command_separator(self, value): self._command_separator = value self.modified = True @property def connect_command(self): return self._connect_command @connect_command.setter def connect_command(self, value): if isinstance(value, basestring): value = value.split() self._connect_command = value self.modified = True @property def update_command(self): return self._update_commands @update_command.setter def update_commands(self, value): if isinstance(value, basestring): value = [value] self._update_commands = value self.modified = True @property def descripion(self): return self._descripion @descripion.setter def descripion(self, value): self._descripion = value self.modified = True def add_server(self, name): if name in [s.name for s in self.servers]: self.log.debug('Error adding: server already in group: %s' % name) return self.servers.append(Server(self, name)) self.modified = True def remove_server(self,name): try: server = [s for s in self.servers if s.name==name][0] self.servers.remove(server) self.modified = True except IndexError: self.log.debug('Error removing: server not in group: %s' % name) return
class OIDPrefix(object): """Sortable OID base class Base class for OID prefixes, sortable in OID order """ def __init__(self, oid): self.log = Logger('snmp').default_stream self.oid = self.__format_oid__(oid) self.oid_string = self.__format_oid_string__(oid) self._parent = None self._next = None def __format_oid_string__(self, oid): return '.%s' % '.'.join(str(x) for x in self.__format_oid__(oid)) def __format_oid__(self, oid): """Format OID Format OID string to validated OID list Return list of integeres """ if isinstance(oid, basestring): try: oid = oid.strip('.').split('.') except ValueError: raise ValueError('Invalid OID: %s' % oid) return self.__validate_oid__(oid) def __validate_oid__(self, oid): """Validate OID and return Validate list of integers to be a valid OID Returns value if OK """ try: oid = [int(x) for x in oid] for x in oid: if x<=0: raise ValueError except ValueError: raise ValueError('Invalid OID: %s' % oid) return oid def __invalid__(self, message): """Invalid request Log error and return None """ self.log.debug('%s' % message) return None def __cmp__(self, other): """Compare oid prefix items Compares OID prefixes """ if isinstance(other, basestring): oid = self.__format_oid__(other) elif isinstance(other, OIDPrefix): oid = other.oid else: raise ValueError('Error comparing OIDPrefix to %d' % type(other)) if len(oid) != len(self.oid): return cmp(len(self.oid), len(oid)) for i in range(0, len(oid)): if oid[i] != self.oid[i]: return cmp(self.oid[i], oid[i]) return cmp(self.oid, oid) def __eq__(self, oid): return self.__cmp__(oid) == 0 def __ne__(self, oid): return self.__cmp__(oid) != 0 def __lt__(self, oid): return self.__cmp__(oid) < 0 def __le__(self, oid): return self.__cmp__(oid) <= 0 def __gt__(self, oid): return self.__cmp__(oid) > 0 def __ge__(self, oid): return self.__cmp__(oid) >= 0 @property def get_response(self): return None @property def next_response(self): return None @property def parent(self): return self._parent @parent.setter def parent(self, value): self._parent = value @property def next(self): return self._next @next.setter def next(self, value): self._next = value def match(self, other): """Match other OID Match OID prefixes. Default function checks if self.oid == other.oid Override in child class (for example, trees match tree prefix) """ return self == other
class PlaybookCallbacks(callbacks.PlaybookCallbacks): """Playbook callbacks Override version of ansible.callbacks.PlaybookCallbacks that only logs to default logger with debug messages, not actually doing anything else. Please note that callback on_vars_prompt is NOT overridden, so if your code asks for variables we will use the standard chatty query version! """ def __init__(self, verbose=False): callbacks.PlaybookCallbacks.__init__(self, verbose) self.log = Logger().default_stream def on_start(self): self.log.debug('starting playbook') def on_notify(self, host, handler): self.log.debug('playbook notification') def on_no_hosts_remaining(self): self.log.debug('playbook no hosts remaining') def on_task_start(self, name, is_conditional): self.log.debug('playbook starting task "%s"' % name) def on_setup(self): self.log.debug('playbook setup') def on_import_for_host(self, host, imported_file): self.log.debug('playbook importing for host %s' % host) def on_not_import_for_host(self, host, missing_file): self.log.debug('playbook not importing for host %s' % host) def on_play_start(self, name): self.log.debug('playbook start play %s' % name) def on_no_hosts_matched(self): raise RunnerError('No hosts matched') def on_stats(self, stats): self.log.debug('playbook statistics %s' % stats)
""" entry = ARINReverseIP(address) url = URL_TEMPLATE % {'address': entry.address} headers = {'Accept': 'application/arin.whoisrws-v1+json' } res = requests.get(url, headers=headers) if res.status_code != 200: raise WhoisError('Error fetching URL %s' % url) try: data = json.loads(res.content) except ValueError, emsg: raise WhoisError('Error parsing response: %s' % res.content) if 'net' not in data: logger.debug('DATA: %s' % res.content) raise WhoisError('Did not receive expected data: missing net section') net = data['net'] assert isinstance(net, dict) for key in ( 'handle', 'name', 'comment', 'ref', 'version', ): if key in net: setattr(entry, key, net[key]) for key, field in DATE_FIELD_MAP.items(): try: setattr(entry, key, net[field]) except KeyError: continue blocks = net['netBlocks']['netBlock']
class OIDPrefix(object): """Sortable OID base class Base class for OID prefixes, sortable in OID order """ def __init__(self, oid): self.log = Logger('snmp').default_stream self.oid = self.__format_oid__(oid) self.oid_string = self.__format_oid_string__(oid) self._parent = None self._next = None def __format_oid_string__(self, oid): return '.%s' % '.'.join(str(x) for x in self.__format_oid__(oid)) def __format_oid__(self, oid): """Format OID Format OID string to validated OID list Return list of integeres """ if isinstance(oid, basestring): try: oid = oid.strip('.').split('.') except ValueError: raise ValueError('Invalid OID: %s' % oid) return self.__validate_oid__(oid) def __validate_oid__(self, oid): """Validate OID and return Validate list of integers to be a valid OID Returns value if OK """ try: oid = [int(x) for x in oid] for x in oid: if x <= 0: raise ValueError except ValueError: raise ValueError('Invalid OID: %s' % oid) return oid def __invalid__(self, message): """Invalid request Log error and return None """ self.log.debug('%s' % message) return None def __cmp__(self, other): """Compare oid prefix items Compares OID prefixes """ if isinstance(other, basestring): oid = self.__format_oid__(other) elif isinstance(other, OIDPrefix): oid = other.oid else: raise ValueError('Error comparing OIDPrefix to %d' % type(other)) if len(oid) != len(self.oid): return cmp(len(self.oid), len(oid)) for i in range(0, len(oid)): if oid[i] != self.oid[i]: return cmp(self.oid[i], oid[i]) return cmp(self.oid, oid) def __eq__(self, oid): return self.__cmp__(oid) == 0 def __ne__(self, oid): return self.__cmp__(oid) != 0 def __lt__(self, oid): return self.__cmp__(oid) < 0 def __le__(self, oid): return self.__cmp__(oid) <= 0 def __gt__(self, oid): return self.__cmp__(oid) > 0 def __ge__(self, oid): return self.__cmp__(oid) >= 0 @property def get_response(self): return None @property def next_response(self): return None @property def parent(self): return self._parent @parent.setter def parent(self, value): self._parent = value @property def next(self): return self._next @next.setter def next(self, value): self._next = value def match(self, other): """Match other OID Match OID prefixes. Default function checks if self.oid == other.oid Override in child class (for example, trees match tree prefix) """ return self == other
class OperatingSystemGroup(object): """ Group of operating systems in configuration file """ def __init__(self, config, name, **kwargs): self.log = Logger().default_stream self.name = name self.description = name self.connect_command = DEFAULT_CONNECT_COMMAND self.command_separator = DEFAULT_COMMAND_SEPARATOR self.update_commands = [] self.servers = [] for k in ('description', 'command_separator'): if k in kwargs: setattr(self, k, kwargs[k]) if 'connect' in kwargs: self.connect_command = kwargs['connect'] if 'commands' in kwargs: self.update_commands = kwargs['commands'] if 'servers' in kwargs: names = kwargs['servers'] if not isinstance(names, list): names = [names] for name in names: self.servers.append(Server(self, name)) self.modified = False def __repr__(self): return '{0}: {1} ({2:d} servers)'.format(self.name, self.description, len(self.servers)) @property def command_separator(self): return self._command_separator @command_separator.setter def command_separator(self, value): self._command_separator = value self.modified = True @property def connect_command(self): return self._connect_command @connect_command.setter def connect_command(self, value): if isinstance(value, str): value = value.split() self._connect_command = value self.modified = True @property def update_command(self): return self._update_commands @update_command.setter def update_commands(self, value): if isinstance(value, str): value = [value] self._update_commands = value self.modified = True @property def descripion(self): return self._descripion @descripion.setter def descripion(self, value): self._descripion = value self.modified = True def add_server(self, name): if name in [s.name for s in self.servers]: self.log.debug( 'Error adding: server already in group: {0}'.format(name)) return self.servers.append(Server(self, name)) self.modified = True def remove_server(self, name): try: server = [s for s in self.servers if s.name == name][0] self.servers.remove(server) self.modified = True except IndexError: self.log.debug( 'Error removing: server not in group: {0}'.format(name)) return
class SNMPAgent(Script): """SNMP Agent implementation SNMP agent daemon for net-snmpd pass_persist mode Example usage: agent = SNMPAgent('.1.2.3.4') tree = agent.register_tree('1.2.3.4.1') tree.register('1.2.3.4.1.1', 'integer', 1) tree.register('1.2.3.4.1.2', 'integer', 2) agent.register_tree('1.2.3.4.2') agent.register('1.2.3.4.2.1', 'integer', 1) agent.register('1.2.3.4.2.4', 'integer', 4) agent.register('1.2.3.4.2.3', 'integer', 3) agent.register('1.2.3.4.2.2', 'integer', 2) tree = agent.register_tree('1.2.3.4.3') tree.register('1.2.3.4.3.1.1', 'integer', 1) tree.add_values('string', [x for x in string.digits[2:]]) tree = agent.register_tree('1.2.3.4.4') tree.add_prefix_map([ { 'oid': '1.2.3.4.4.1', 'key': 'string', 'values': [x for x in string.letters[:10]]}, { 'oid': '1.2.3.4.4.2', 'key': 'integer', 'values': [x for x in string.digits[:10]]}, ]) agent.run() """ def __init__(self, oid, reload_interval=60): super(SNMPAgent, self).__init__() self.log = Logger('snmp').default_stream self.reload_interval = reload_interval self.last_reload = None self.args = None self.add_argument('-g', '--get', help='SNMP GET request') self.add_argument('-n', '--next', help='SNMP GET request') self.add_argument('-t', '--tree', action='store_true', help='Show OID tree') self.tree = Tree(oid) def __SIGHUP__(self, signum, frame): """ Signal handler to reload configuration. Note this requires also the IOError processing in main input loop below """ self.log.debug('Reloading from signal') self.reload() def parse_args(self): if self.args is None: self.args = super(SNMPAgent, self).parse_args() return self.args def register_tree(self, oid): return self.tree.add(Tree(oid)) def register(self, oid, key, value): return self.tree.add(Item(oid, key, value)) def GET(self, oid): """GET from agent Return given OID from agent tree or None """ try: return self.tree.GET(oid) except SNMPError as e: self.log.debug(e) return None def NEXT(self, oid): """NEXT from agent Return given NEXT from agent tree or None """ try: return self.tree.NEXT(oid) except SNMPError as e: self.log.debug(e) return None def SET(self, oid, value): """SET registered OID Set value for registered OID in agent tree. Note: By default items registered are readonly. """ return self.tree.SET(oid, value) def clear(self): return self.tree.clear() def reload(self): """ This method must be implemented in a child class. It is used to reload the SNMP tree data from files, if possible. """ raise NotImplementedError('You must implement reload in child class') def run(self): """SNMP Agent main loop Main loop to execute for agent. You can either run this in: - 'pass' mode: get/next for single OID by passing in OptionParser 'options' value 'get' or 'next' (snmpd.conf 'pass' agent) - 'pass_persist' mode: without any options, in which case the loop acts as permanent snmpd pass_persist agent. """ self.args = self.parse_args() signal.signal(signal.SIGHUP, self.__SIGHUP__) if self.args.tree: self.message(self.tree.oid_string) elif self.args.get: entry = self.GET(self.args.get) if entry is not None: sys.stdout.write('{0}\n'.format(entry.get_response)) elif self.args.next: entry = self.NEXT(self.args.next) if entry is not None: sys.stdout.write('{0}\n'.format(entry.next_response)) if self.args.tree or self.args.get or self.args.next: self.exit(0) EOF = '' self.log.debug('Starting SNMP agent for OID {0}'.format(self.tree.oid)) while True: ready = select.select([sys.stdin], [], [], 0.2)[0] if not ready: if self.last_reload is not None: since_reload = time.time() - self.last_reload if since_reload > self.reload_interval: self.reload() self.last_reload = time.time() else: self.last_reload = time.time() continue try: cmd = sys.stdin.readline() if cmd == EOF: break if cmd.strip() == '': self.log.debug('Received agent shutdown') break cmd = cmd.rstrip().lower() if cmd == 'quit': return if cmd == 'reload': self.reload() if cmd == 'ping': sys.stdout.write('PONG\n') if cmd in ('set', 'get', 'getnext'): oid = sys.stdin.readline() if oid == EOF: break oid = oid.rstrip() if cmd == 'set': value = sys.stdin.readline() if value == EOF: break result = self.SET(oid, value) sys.stdout.write('{0}\n'.format(result)) elif cmd == 'get': entry = self.GET(oid) if entry is not None: sys.stdout.write('{0}\n'.format(entry.get_response)) else: sys.stdout.write('NONE\n') elif cmd == 'getnext': entry = self.NEXT(oid) if entry is not None: sys.stdout.write('{0}\n'.format(entry.next_response)) else: sys.stdout.write('NONE\n') else: self.log.debug('Unknown command (should be get/set/getnext)') sys.stdout.flush() except IOError as e: # we get EINTR with SIGHUP and we can ignore it if e[0]==errno.EINTR: continue self.log.debug('IOError: {0}'.format(e)) return except KeyboardInterrupt: # Interactive mode, user interrupted self.log.debug('Quitting...') return
class Server(object): """ Configuration for one server """ def __init__(self, osgroup, name, description=None): self.log = Logger().default_stream self.osgroup = osgroup self.name = name self.description = description def __repr__(self): if self.description is not None: return '{0} ({1})'.format(self.name, self.description) else: return '{0}'.format(self.name) def __eq__(self, other): if isinstance(other, str): return self.name == other return self.name == other.name def __ne__(self, other): if isinstance(other, str): return self.name != other return self.name != other.name def __gt__(self, other): if isinstance(other, str): return self.name > other return self.name > other.name def __lt__(self, other): if isinstance(other, str): return self.name < other return self.name < other.name def __ge__(self, other): if isinstance(other, str): return self.name >= other return self.name >= other.name def __le__(self, other): if isinstance(other, str): return self.name <= other return self.name <= other.name @property def connect_command(self): return [ x == 'SERVER' and self.name or x for x in self.osgroup.connect_command ] def check_output(self, command): """ Wrapper to execute and check output of a command without interactive console actions Splits string automatically to words so pass a list if you need to escape stuff properly. """ if not isinstance(command, list): command = [command] cmd = self.connect_command + command p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) (stdout, stderr) = p.communicate() return p.returncode, stdout.rstrip('\n'), stderr.rstrip('\n') def shell(self, command): """ Wrapper to run interactive shell command on the host, using current terminal for stdin, stdout and stderr Splits string automatically to words so pass a list if you need to escape stuff properly. Returns command return code """ if not isinstance(command, list): command = [command] cmd = self.connect_command + command p = Popen(cmd, stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr) p.wait() return p.returncode def update(self): """ Wrapper to call correct update commands for this host """ if not self.osgroup.update_commands: self.log.debug('No update commands for OS {0}'.format( self.osgroup.name)) return self.log.debug("Running: {0} '{1}'".format( ' '.join(self.connect_command), self.osgroup.command_separator.join(self.osgroup.update_commands))) cmd = self.connect_command + [ self.osgroup.command_separator.join(self.osgroup.update_commands) ] p = Popen(cmd, stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr) p.wait() return p.returncode def remove(self): """Remove from configuration Remove this server from configuration """ return self.osgroup.remove_server(self)
class AuthorizedKeys(dict): """ Parser for OpenSSH authorized_keys file contents """ def __init__(self, path=DEFAULT_AUTHORIZED_KEYS): self.log = Logger().default_stream self.path = path self.load() def load(self): """ Load authorized keys file, discarding earlier data in the object. SSH v1 authorized_keys line: options, bits, exponent, modulus, comment) SSH v2 authorized_keys line: options, keytype, base64-encoded key, comment)' See man sshd for details. """ self.clear() if not os.path.isfile(self.path): self.log.debug('No such file: {0}'.format(self.path)) return for l in [l.rstrip() for l in open(self.path, 'r').readlines()]: if l.startswith('#') or l.strip() == '': continue entry = {} parts = l.split() if parts[0][0] in string.digits and len(parts) == 4: entry['keyformat'] = 1 entry['bits'] = int(parts[0]) entry['exponent'] = parts[1] entry['modulus'] = parts[2] entry['comment'] = parts[3] entry['options'] = None elif parts[0] in ( 'ssh-dsa', 'ssh-rsa', ): entry['keyformat'] = 2 entry['keytype'] = parts[0] entry['key_base64'] = parts[1] entry['comment'] = len(parts) > 2 and parts[2] or '' entry['options'] = None else: entry['options'] = parts[0] parts = parts[1:] if not parts: continue if parts[0] in ( 'ssh-dsa', 'ssh-rsa', ) and len(parts) == 3: entry['keyformat'] = 2 entry['keytype'] = parts[0] entry['key_base64'] = parts[1] entry['comment'] = parts[2] elif parts[0] in string.digits: entry['keyformat'] = 1 entry['keyformat'] = 1 entry['bits'] = int(parts[0]) entry['exponent'] = parts[1] entry['modulus'] = parts[2] entry['comment'] = parts[3]
class SNMPAgent(Script): """SNMP Agent implementation SNMP agent daemon for net-snmpd pass_persist mode Example usage: agent = SNMPAgent('.1.2.3.4') tree = agent.register_tree('1.2.3.4.1') tree.register('1.2.3.4.1.1', 'integer', 1) tree.register('1.2.3.4.1.2', 'integer', 2) agent.register_tree('1.2.3.4.2') agent.register('1.2.3.4.2.1', 'integer', 1) agent.register('1.2.3.4.2.4', 'integer', 4) agent.register('1.2.3.4.2.3', 'integer', 3) agent.register('1.2.3.4.2.2', 'integer', 2) tree = agent.register_tree('1.2.3.4.3') tree.register('1.2.3.4.3.1.1', 'integer', 1) tree.add_values('string', [x for x in string.digits[2:]]) tree = agent.register_tree('1.2.3.4.4') tree.add_prefix_map([ { 'oid': '1.2.3.4.4.1', 'key': 'string', 'values': [x for x in string.letters[:10]]}, { 'oid': '1.2.3.4.4.2', 'key': 'integer', 'values': [x for x in string.digits[:10]]}, ]) agent.run() """ def __init__(self, oid, reload_interval=60): Script.__init__(self) self.log = Logger('snmp').default_stream self.reload_interval = reload_interval self.last_reload = None self.add_argument('-g', '--get', help='SNMP GET request') self.add_argument('-n', '--next', help='SNMP GET request') self.add_argument('-t', '--tree', action='store_true', help='Show OID tree') self.tree = Tree(oid) def __SIGHUP__(self, signum, frame): """ Signal handler to reload configuration. Note this requires also the IOError processing in main input loop below """ self.log.debug('Reloading from signal') self.reload() def register_tree(self, oid): return self.tree.add(Tree(oid)) def register(self, oid, key, value): return self.tree.add(Item(oid, key, value)) def GET(self, oid): """GET from agent Return given OID from agent tree or None """ try: return self.tree.GET(oid) except SNMPError, emsg: self.log.debug(emsg) return None
class AuthorizedKeys(dict): """ Parser for OpenSSH authorized_keys file contents """ def __init__(self, path=DEFAULT_AUTHORIZED_KEYS): self.log = Logger().default_stream self.path = path self.load() def load(self): """ Load authorized keys file, discarding earlier data in the object. SSH v1 authorized_keys line: options, bits, exponent, modulus, comment) SSH v2 authorized_keys line: options, keytype, base64-encoded key, comment)' See man sshd for details. """ self.clear() if not os.path.isfile(self.path): self.log.debug('No such file: %s' % self.path) return for l in [l.rstrip() for l in open(self.path, 'r').readlines()]: if l.startswith('#') or l.strip() == '': continue entry = {} parts = l.split() if parts[0][0] in string.digits and len(parts) == 4: entry['keyformat'] = 1 entry['bits'] = int(parts[0]) entry['exponent'] = parts[1] entry['modulus'] = parts[2] entry['comment'] = parts[3] entry['options'] = None elif parts[0] in ( 'ssh-dsa', 'ssh-rsa', ): entry['keyformat'] = 2 entry['keytype'] = parts[0] entry['key_base64'] = parts[1] entry['comment'] = len(parts) > 2 and parts[2] or '' entry['options'] = None else: entry['options'] = parts[0] parts = parts[1:] if not parts: continue if parts[0] in ( 'ssh-dsa', 'ssh-rsa', ) and len(parts) == 3: entry['keyformat'] = 2 entry['keytype'] = parts[0] entry['key_base64'] = parts[1] entry['comment'] = parts[2] elif parts[0] in string.digits: entry['keyformat'] = 1 entry['keyformat'] = 1 entry['bits'] = int(parts[0]) entry['exponent'] = parts[1] entry['modulus'] = parts[2] entry['comment'] = parts[3]
class Server(object): """ Configuration for one server """ def __init__(self, osgroup, name, description=None): self.log = Logger().default_stream self.osgroup = osgroup self.name = name self.description = description def __repr__(self): if self.description is not None: return '%s (%s)' % (self.name, self.description) else: return '%s' % self.name def __cmp__(self, other): if isinstance(other, basestring): return cmp(self.name, other) return cmp(self, other) def __eq__(self, other): return self.__cmp__(other) == 0 def __ne__(self, other): return self.__cmp__(other) != 0 def __gt__(self, other): return self.__cmp__(other) > 0 def __gte__(self, other): return self.__cmp__(other) >= 0 def __lt__(self, other): return self.__cmp__(other) < 0 def __lte__(self, other): return self.__cmp__(other) <= 0 @property def connect_command(self): return [x=='SERVER' and self.name or x for x in self.osgroup.connect_command] def check_output(self, command): """ Wrapper to execute and check output of a command without interactive console actions Splits string automatically to words so pass a list if you need to escape stuff properly. """ if not isinstance(command, list): command = [command] cmd = self.connect_command + command p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) (stdout,stderr) = p.communicate() return p.returncode, stdout.rstrip('\n'), stderr.rstrip('\n') def shell(self, command): """ Wrapper to run interactive shell command on the host, using current terminal for stdin, stdout and stderr Splits string automatically to words so pass a list if you need to escape stuff properly. Returns command return code """ if not isinstance(command, list): command = [command] cmd = self.connect_command + command p = Popen(cmd, stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr) p.wait() return p.returncode def update(self): """ Wrapper to call correct update commands for this host """ if not self.osgroup.update_commands: self.log.debug('No update commands for OS %s' % self.osgroup.name) return self.log.debug("Running: %s '%s'" % ( ' '.join(self.connect_command), self.osgroup.command_separator.join(self.osgroup.update_commands) )) cmd = self.connect_command + [ self.osgroup.command_separator.join(self.osgroup.update_commands) ] p = Popen(cmd, stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr) p.wait() return p.returncode def remove(self): """Remove from configuration Remove this server from configuration """ return self.osgroup.remove_server(self)
class PlaybookRunnerCallbacks(callbacks.PlaybookRunnerCallbacks): """Playbook runner callbacks Override version of ansible.callbacks.PlaybookRunnerCallbacks that only logs to default logger with debug messages, not actually doing anything else. """ def __init__(self, stats, verbose=None): callbacks.PlaybookRunnerCallbacks.__init__(self, stats, verbose) self.log = Logger().default_stream def on_unreachable(self, host, results): self.log.debug('host unreachable %s %s' % (host, results)) def on_failed(self, host, results, ignore_errors=False): self.log.debug('host failed %s %s' % (host, results)) def on_ok(self, host, host_result): self.log.debug('host ok %s %s' % (host, host_result)) def on_skipped(self, host, item=None): self.log.debug('skip %s item %s' % (host, item)) def on_no_hosts(self): self.log.debug('no hosts') def on_async_poll(self, host, res, jid, clock): self.log.debug('async poll %s' % host) def on_async_ok(self, host, res, jid): self.log.debug('async ok %s' % host) def on_async_failed(self, host, res, jid): self.log.debug('async failed %s' % host) def on_file_diff(self, host, diff): self.log.debug('file diff %s' % host)