%PDF- %PDF-
Direktori : /usr/lib/python2.7/site-packages/salt/roster/ |
Current File : //usr/lib/python2.7/site-packages/salt/roster/sshconfig.py |
# -*- coding: utf-8 -*- ''' Parses roster entries out of Host directives from SSH config .. code-block:: bash salt-ssh --roster sshconfig '*' -r "echo hi" ''' from __future__ import absolute_import, print_function, unicode_literals # Import python libs import os import collections import fnmatch import re # Import Salt libs import salt.utils.files import salt.utils.stringutils from salt.ext import six import logging log = logging.getLogger(__name__) _SSHConfRegex = collections.namedtuple('_SSHConfRegex', ['target_field', 'pattern']) _ROSTER_FIELDS = ( _SSHConfRegex(target_field='user', pattern=r'\s+User (.*)'), _SSHConfRegex(target_field='port', pattern=r'\s+Port (.*)'), _SSHConfRegex(target_field='priv', pattern=r'\s+IdentityFile (.*)'), ) def _get_ssh_config_file(opts): ''' :return: Path to the .ssh/config file - usually <home>/.ssh/config ''' ssh_config_file = opts.get('ssh_config_file') if not os.path.isfile(ssh_config_file): raise IOError('Cannot find SSH config file') if not os.access(ssh_config_file, os.R_OK): raise IOError('Cannot access SSH config file: {}'.format(ssh_config_file)) return ssh_config_file def parse_ssh_config(lines): ''' Parses lines from the SSH config to create roster targets. :param lines: Individual lines from the ssh config file :return: Dictionary of targets in similar style to the flat roster ''' # transform the list of individual lines into a list of sublists where each # sublist represents a single Host definition hosts = [] for line in lines: line = salt.utils.stringutils.to_unicode(line) if not line or line.startswith('#'): continue elif line.startswith('Host '): hosts.append([]) hosts[-1].append(line) # construct a dictionary of Host names to mapped roster properties targets = collections.OrderedDict() for host_data in hosts: target = collections.OrderedDict() hostnames = host_data[0].split()[1:] for line in host_data[1:]: for field in _ROSTER_FIELDS: match = re.match(field.pattern, line) if match: target[field.target_field] = match.group(1) for hostname in hostnames: targets[hostname] = target # apply matching for glob hosts wildcard_targets = [] non_wildcard_targets = [] for target in targets.keys(): if '*' in target or '?' in target: wildcard_targets.append(target) else: non_wildcard_targets.append(target) for pattern in wildcard_targets: for candidate in non_wildcard_targets: if fnmatch.fnmatch(candidate, pattern): targets[candidate].update(targets[pattern]) del targets[pattern] # finally, update the 'host' to refer to its declaration in the SSH config # so that its connection parameters can be utilized for target in targets: targets[target]['host'] = target return targets def targets(tgt, tgt_type='glob', **kwargs): ''' Return the targets from the flat yaml file, checks opts for location but defaults to /etc/salt/roster ''' ssh_config_file = _get_ssh_config_file(__opts__) with salt.utils.files.fopen(ssh_config_file, 'r') as fp: all_minions = parse_ssh_config([line.rstrip() for line in fp]) rmatcher = RosterMatcher(all_minions, tgt, tgt_type) matched = rmatcher.targets() return matched class RosterMatcher(object): ''' Matcher for the roster data structure ''' def __init__(self, raw, tgt, tgt_type): self.tgt = tgt self.tgt_type = tgt_type self.raw = raw def targets(self): ''' Execute the correct tgt_type routine and return ''' try: return getattr(self, 'ret_{0}_minions'.format(self.tgt_type))() except AttributeError: return {} def ret_glob_minions(self): ''' Return minions that match via glob ''' minions = {} for minion in self.raw: if fnmatch.fnmatch(minion, self.tgt): data = self.get_data(minion) if data: minions[minion] = data return minions def get_data(self, minion): ''' Return the configured ip ''' if isinstance(self.raw[minion], six.string_types): return {'host': self.raw[minion]} if isinstance(self.raw[minion], dict): return self.raw[minion] return False