--- func/commonconfig.py | 10 +++- func/overlord/client.py | 184 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 191 insertions(+), 3 deletions(-) diff --git a/func/commonconfig.py b/func/commonconfig.py index 3823d39..c71138d 100644 --- a/func/commonconfig.py +++ b/func/commonconfig.py @@ -29,10 +29,18 @@ class FuncdConfig(BaseConfig): minion_name = Option('') method_log_dir = Option("/var/log/func/methods/") + ca_file = Option('') + cert_file = Option('') + key_file = Option('') class OverlordConfig(BaseConfig): socket_timeout = FloatOption(0) backend = Option('conf') group_db = Option('') - + key_file = Option('') + cert_file = Option('') + ca_file = Option('') + puppet_minions = BoolOption(False) + puppet_inventory = Option('/var/lib/puppet/ssl/ca/inventory.txt') + puppet_crl = Option('/var/lib/puppet/ssl/ca/ca_crl.pem') diff --git a/func/overlord/client.py b/func/overlord/client.py index e011929..b0631e7 100644 --- a/func/overlord/client.py +++ b/func/overlord/client.py @@ -18,6 +18,10 @@ import sys import glob import os import time +import shlex +import subprocess +import re +import fnmatch import func.yaml as yaml from certmaster.commonconfig import CMConfig @@ -221,6 +225,176 @@ class Minions(object): return False +class PuppetMinions(object): + def __init__(self, spec, port=51234, + noglobs=None, verbose=None, + just_fqdns=False, groups_backend="conf", + delegate=False, minionmap={},exclude_spec=None,**kwargs): + + # open inventory.txt + # for each CN (uniqued) in there + # open the ca_crl.pem file - if the serial of the CN shows up in there + # remove those from the list of hosts + + self.spec = spec + self.port = port + self.noglobs = noglobs + self.verbose = verbose + self.just_fqdns = just_fqdns + self.delegate = delegate + self.minionmap = minionmap + self.exclude_spec = exclude_spec + + self.cm_config = read_config(CONFIG_FILE, CMConfig) + self.overlord_config = read_config(OVERLORD_CONFIG_FILE, OverlordConfig) + self.cm_config = read_config(CONFIG_FILE, CMConfig) + self.group_class = groups.Groups(backend=groups_backend,**kwargs) + + #lets make them sets so we dont loop again and again + self.all_hosts = set() + self.all_certs = set() + self.all_urls = [] + + def _get_new_hosts(self): + self.new_hosts = self._get_group_hosts(self.spec) + return self.new_hosts + + def _get_group_hosts(self,spec): + return self.group_class.get_hosts_by_group_glob(spec) + + def _get_hosts_for_specs(self,seperate_gloobs): + """ + Gets the hosts and certs for proper spec + """ + tmp_hosts = set() + tmp_certs = set() + for each_gloob in seperate_gloobs: + if each_gloob.startswith('@'): + continue + h,c = self._get_hosts_for_spec(each_gloob) + tmp_hosts = tmp_hosts.union(h) + tmp_certs = tmp_certs.union(c) + + return tmp_hosts,tmp_certs + + def _get_hosts_for_spec(self,each_gloob): + """ + Pull only for specified spec + """ + #these will be returned + tmp_certs = set() + tmp_hosts = set() + + # revoked certs + revoked_serials = self._return_revoked_serials(self.overlord_config.puppet_crl) + # get all hosts + if os.access(self.overlord_config.puppet_inventory, os.R_OK): + fo = open(self.overlord_config.puppet_inventory, 'r') + host_inv = {} + time_format = '%Y-%m-%dT%H:%M:%S%Z' + now = time.time() + for line in fo.readlines(): + if re.match('\s*(#|$)', line): + continue + (serial, before, after, cn) = line.split() + if int(serial, 16) in revoked_serials: + continue + before = time.strftime('%s', time.strptime(before, time_format)) + if now < int(before): + continue + after = time.strftime('%s', time.strptime(after, time_format)) + if now > int(after): + continue + + hn = cn.replace('/CN=','') + hn = hn.replace('\n','') + if hn in host_inv: + if host_inv[hn] > serial: + continue + host_inv[hn] = serial + + for hostname in host_inv.keys(): + if fnmatch.fnmatch(hostname, each_gloob): + tmp_hosts.add(hostname) + # don't return certs path - just hosts + + return tmp_hosts,tmp_certs + + def _return_revoked_serials(self, crlfile): + call = '/usr/bin/openssl crl -text -noout -in %s' % crlfile + call = shlex.split(call) + serials = [] + (res,err) = subprocess.Popen(call, stdout=subprocess.PIPE).communicate() + for line in res.split('\n'): + if line.find('Serial Number:') == -1: + continue + (crap, serial) = line.split(':') + serial = serial.strip() + serial = int(serial, 16) + serials.append(serial) + return serials + + def get_hosts_for_spec(self,spec): + """ + Be careful when editting that method it will be used + also by groups api to pull machines to have better + glob control there ... + """ + return self._get_hosts_for_spec(spec)[0] + + + + def _get_all_hosts(self): + """ + Gets hosts that are included and excluded by user + a better orm like spec so user may say + func "*" --exclude "www.*;@mygroup" ... + """ + included_part = self._get_hosts_for_specs(self.spec.split(";")+self.new_hosts) + self.all_certs=self.all_certs.union(included_part[1]) + self.all_hosts=self.all_hosts.union(included_part[0]) + #excluded ones + if self.exclude_spec: + #get first groups ypu dont want to run : + group_exclude = self._get_group_hosts(self.exclude_spec) + excluded_part = self._get_hosts_for_specs(self.exclude_spec.split(";")+group_exclude) + self.all_certs = self.all_certs.difference(excluded_part[1]) + self.all_hosts = self.all_hosts.difference(excluded_part[0]) + + + + def get_all_hosts(self): + """ + Get current host list + """ + self._get_new_hosts() + self._get_all_hosts() + + #we keep it all the time as a set so + return list(self.all_hosts) + + def get_urls(self): + self._get_new_hosts() + self._get_all_hosts() + for host in self.all_hosts: + if not self.just_fqdns: + self.all_urls.append("https://%s:%s" % (host, self.port)) + else: + self.all_urls.append(host) + + if self.verbose and len(self.all_urls) == 0: + sys.stderr.write("no hosts matched\n") + + return self.all_urls + + # FIXME: hmm, dont like this bit of the api... -al; + def is_minion(self): + self.get_urls() + if len(self.all_urls) > 0: + return True + return False + + # does the hostnamegoo actually expand to anything? def is_minion(minion_string): @@ -292,8 +466,14 @@ class Overlord(object): #overlord_query stuff self.overlord_query = OverlordQuery() - - self.minions_class = Minions(self.server_spec, port=self.port, noglobs=self.noglobs, verbose=self.verbose,exclude_spec=self.exclude_spec) + if self.overlord_config.puppet_minions: + mc = PuppetMinions + else: + mc = Minions + + self.minions_class = mc(self.server_spec, port=self.port, + noglobs=self.noglobs, verbose=self.verbose, + exclude_spec=self.exclude_spec) self.minions = self.minions_class.get_urls() if len(self.minions) == 0: raise Func_Client_Exception, 'Can\'t find any minions matching \"%s\". ' % self.server_spec -- 1.6.6 _______________________________________________ Func-list mailing list Func-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/func-list