root/trunk/bcfg2/src/lib/Server/Plugins/Metadata.py

Revision 5592, 31.5 KB (checked in by desai, 18 hours ago)

Metadata: cache session information for all clients that aren't address resolved

  • Property svn:eol-style set to native
  • Property cvs2svn:cvs-rev set to 1.29
  • Property svn:keywords set to Date Author Id Revision
Line 
1'''This file stores persistent metadata for the Bcfg2 Configuration Repository'''
2__revision__ = '$Revision$'
3
4import copy
5import fcntl
6import lxml.etree
7import socket
8import time
9import Bcfg2.Server.Plugin
10
11class MetadataConsistencyError(Exception):
12    '''This error gets raised when metadata is internally inconsistent'''
13    pass
14
15class MetadataRuntimeError(Exception):
16    '''This error is raised when the metadata engine is called prior to reading enough data'''
17    pass
18
19class ClientMetadata(object):
20    '''This object contains client metadata'''
21    def __init__(self, client, profile, groups, bundles,
22                 aliases, addresses, categories, uuid, password, query):
23        self.hostname = client
24        self.profile = profile
25        self.bundles = bundles
26        self.aliases = aliases
27        self.addresses = addresses
28        self.groups = groups
29        self.categories = categories
30        self.uuid = uuid
31        self.password = password
32        self.connectors = []
33        self.query = query
34
35    def inGroup(self, group):
36        '''Test to see if client is a member of group'''
37        return group in self.groups
38
39class MetadataQuery(object):
40    def __init__(self, by_name, get_clients, by_groups, by_profiles, all_groups):
41        # resolver is set later
42        self.by_name = by_name
43        self.names_by_groups = by_groups
44        self.names_by_profiles = by_profiles
45        self.all_clients = get_clients
46        self.all_groups = all_groups
47
48    def by_groups(self, groups):
49        return [self.by_name(name) for name in self.names_by_groups(groups)]
50
51    def by_profiles(self, profiles):
52        return [self.by_name(name) for name in self.names_by_profiles(profiles)]
53
54    def all(self):
55        return [self.by_name(name) for name in self.all_clients()]
56
57class Metadata(Bcfg2.Server.Plugin.Plugin,
58               Bcfg2.Server.Plugin.Metadata,
59               Bcfg2.Server.Plugin.Statistics):
60    '''This class contains data for bcfg2 server metadata'''
61    __version__ = '$Id$'
62    __author__ = 'bcfg-dev@mcs.anl.gov'
63    name = "Metadata"
64
65    def __init__(self, core, datastore, watch_clients=True):
66        Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
67        Bcfg2.Server.Plugin.Metadata.__init__(self)
68        Bcfg2.Server.Plugin.Statistics.__init__(self)
69        if watch_clients:
70            try:
71                core.fam.AddMonitor("%s/%s" % (self.data, "groups.xml"), self)
72                core.fam.AddMonitor("%s/%s" % (self.data, "clients.xml"), self)
73            except:
74                raise Bcfg2.Server.Plugin.PluginInitError
75        self.states = {}
76        if watch_clients:
77            self.states = {"groups.xml":False, "clients.xml":False}
78        self.addresses = {}
79        self.auth = dict()
80        self.clients = {}
81        self.aliases = {}
82        self.groups = {}
83        self.cgroups = {}
84        self.public = []
85        self.private = []
86        self.profiles = []
87        self.categories = {}
88        self.bad_clients = {}
89        self.uuid = {}
90        self.secure = []
91        self.floating = []
92        self.passwords = {}
93        self.session_cache = {}
94        self.clientdata = None
95        self.clientdata_original = None
96        self.default = None
97        self.pdirty = False
98        self.extra = {'groups.xml':[], 'clients.xml':[]}
99        self.password = core.password
100        self.query = MetadataQuery(core.build_metadata,
101                                   lambda:self.clients.keys(),
102                                   self.get_client_names_by_groups,
103                                   self.get_client_names_by_profiles,
104                                   self.get_all_group_names)
105
106    @classmethod
107    def init_repo(cls, repo, groups, os_selection, clients):
108        path = '%s/%s' % (repo, cls.name)
109        cls.make_path(path)
110        open("%s/Metadata/groups.xml" %
111             repo, "w").write(groups % os_selection)
112        open("%s/Metadata/clients.xml" %
113             repo, "w").write(clients % socket.getfqdn())
114
115    def get_groups(self):
116        '''return groups xml tree'''
117        groups_tree = lxml.etree.parse(self.data + "/groups.xml")
118        root = groups_tree.getroot()
119        return root
120
121    def search_group(self, group_name, tree):
122        '''find a group'''
123        for node in tree.findall("//Group"):
124            if node.get("name") == group_name:
125                return node
126            for child in node:
127                if child.tag == "Alias" and child.attrib["name"] == group_name:
128                    return node
129        return None
130
131    def add_group(self, group_name, attribs):
132        '''add group to groups.xml'''
133        tree = lxml.etree.parse(self.data + "/groups.xml")
134        root = tree.getroot()
135        element = lxml.etree.Element("Group", name=group_name)
136        for key, val in attribs.iteritems():
137            element.set(key, val)
138        node = self.search_group(group_name, tree)
139        if node != None:
140            self.logger.error("Group \"%s\" already exists" % (group_name))
141            raise MetadataConsistencyError
142        root.append(element)
143        group_tree = open(self.data + "/groups.xml","w")
144        fd = group_tree.fileno()
145        while True:
146            try:
147                fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
148            except IOError:
149                continue
150            else:
151                break
152        tree.write(group_tree)
153        fcntl.lockf(fd, fcntl.LOCK_UN)
154        group_tree.close()
155
156    def update_group(self, group_name, attribs):
157        '''Update a groups attributes'''
158        tree = lxml.etree.parse(self.data + "/groups.xml")
159        root = tree.getroot()
160        node = self.search_group(group_name, tree)
161        if node == None:
162            self.logger.error("Group \"%s\" not found" % (group_name))
163            raise MetadataConsistencyError
164        node.attrib.update(attribs)
165        group_tree = open(self.data + "/groups.xml","w")
166        fd = group_tree.fileno()
167        while True:
168            try:
169                fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
170            except IOError:
171                continue
172            else:
173                break
174        tree.write(group_tree)
175        fcntl.lockf(fd, fcntl.LOCK_UN)
176        group_tree.close()
177
178    def remove_group(self, group_name):
179        '''Remove a group'''
180        tree = lxml.etree.parse(self.data + "/groups.xml")
181        root = tree.getroot()
182        node = self.search_group(group_name, tree)
183        if node == None:
184            self.logger.error("Client \"%s\" not found" % (group_name))
185            raise MetadataConsistencyError
186        root.remove(node)
187        group_tree = open(self.data + "/groups.xml","w")
188        fd = group_tree.fileno()
189        while True:
190            try:
191                fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
192            except IOError:
193                continue
194            else:
195                break
196        tree.write(group_tree)
197        fcntl.lockf(fd, fcntl.LOCK_UN)
198        group_tree.close()
199
200    def add_bundle(self, bundle_name):
201        '''add bundle to groups.xml'''
202        tree = lxml.etree.parse(self.data + "/groups.xml")
203        root = tree.getroot()
204        element = lxml.etree.Element("Bundle", name=bundle_name)
205        node = self.search_group(bundle_name, tree)
206        if node != None:
207            self.logger.error("Bundle \"%s\" already exists" % (bundle_name))
208            raise MetadataConsistencyError
209        root.append(element)
210        group_tree = open(self.data + "/groups.xml","w")
211        fd = group_tree.fileno()
212        while True:
213            try:
214                fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
215            except IOError:
216                continue
217            else:
218                break
219        tree.write(group_tree)
220        fcntl.lockf(fd, fcntl.LOCK_UN)
221        group_tree.close()
222
223    def remove_bundle(self, bundle_name):
224        '''Remove a bundle'''
225        tree = lxml.etree.parse(self.data + "/groups.xml")
226        root = tree.getroot()
227        node = self.search_group(bundle_name, tree)
228        if node == None:
229            self.logger.error("Bundle \"%s\" not found" % (bundle_name))
230            raise MetadataConsistencyError
231        root.remove(node)
232        group_tree = open(self.data + "/groups.xml","w")
233        fd = group_tree.fileno()
234        while True:
235            try:
236                fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
237            except IOError:
238                continue
239            else:
240                break
241        tree.write(group_tree)
242        fcntl.lockf(fd, fcntl.LOCK_UN)
243        group_tree.close()
244
245    def search_client(self, client_name, tree):
246        '''find a client'''
247        for node in tree.findall("//Client"):
248            if node.get("name") == client_name:
249                return node
250            for child in node:
251                if child.tag == "Alias" and child.attrib["name"] == client_name:
252                    return node
253        return None
254
255    def add_client(self, client_name, attribs):
256        '''add client to clients.xml'''
257        tree = lxml.etree.parse(self.data + "/clients.xml")
258        root = tree.getroot()
259        element = lxml.etree.Element("Client", name=client_name)
260        for key, val in attribs.iteritems():
261            element.set(key, val)
262        node = self.search_client(client_name, tree)
263        if node != None:
264            self.logger.error("Client \"%s\" already exists" % (client_name))
265            raise MetadataConsistencyError
266        root.append(element)
267        client_tree = open(self.data + "/clients.xml","w")
268        fd = client_tree.fileno()
269        while True:
270            try:
271                fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
272            except IOError:
273                continue
274            else:
275                break
276        tree.write(client_tree)
277        fcntl.lockf(fd, fcntl.LOCK_UN)
278        client_tree.close()
279
280    def update_client(self, client_name, attribs):
281        '''Update a clients attributes'''
282        tree = lxml.etree.parse(self.data + "/clients.xml")
283        root = tree.getroot()
284        node = self.search_client(client_name, tree)
285        if node == None:
286            self.logger.error("Client \"%s\" not found" % (client_name))
287            raise MetadataConsistencyError
288        node.attrib.update(attribs)
289        client_tree = open(self.data + "/clients.xml","w")
290        fd = client_tree.fileno()
291        while True:
292            try:
293                fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
294            except IOError:
295                continue
296            else:
297                break
298        tree.write(client_tree)
299        fcntl.lockf(fd, fcntl.LOCK_UN)
300        client_tree.close()
301
302    def HandleEvent(self, event):
303        '''Handle update events for data files'''
304        filename = event.filename.split('/')[-1]
305        if filename in ['groups.xml', 'clients.xml']:
306            dest = filename
307        elif filename in reduce(lambda x, y:x+y, self.extra.values()):
308            if event.code2str() == 'exists':
309                return
310            dest = [key for key, value in self.extra.iteritems() if filename in value][0]
311        else:
312            return
313        if event.code2str() == 'endExist':
314            return
315        try:
316            xdata = lxml.etree.parse("%s/%s" % (self.data, dest))
317        except lxml.etree.XMLSyntaxError:
318            self.logger.error('Failed to parse %s' % (dest))
319            return
320        included = [ent.get('href') for ent in \
321                    xdata.findall('./{http://www.w3.org/2001/XInclude}include')]
322        xdata_original = copy.deepcopy(xdata)
323        if included:
324            for name in included:
325                if name not in self.extra[dest]:
326                    self.core.fam.AddMonitor("%s/%s" % (self.data, name), self)
327                    self.extra[dest].append(name)
328            try:
329                xdata.xinclude()
330            except lxml.etree.XIncludeError:
331                self.logger.error("Failed to process XInclude for file %s" % dest)
332
333        if dest == 'clients.xml':
334            self.clients = {}
335            self.aliases = {}
336            self.raliases = {}
337            self.bad_clients = {}
338            self.secure = []
339            self.floating = []
340            self.addresses = {}
341            self.raddresses = {}
342            self.clientdata_original = xdata_original
343            self.clientdata = xdata
344            for client in xdata.findall('.//Client'):
345                clname = client.get('name').lower()
346                if 'address' in client.attrib:
347                    caddr = client.get('address')
348                    if caddr in self.addresses:
349                        self.addresses[caddr].append(clname)
350                    else:
351                        self.addresses[caddr] = [clname]
352                    if clname not in self.raddresses:
353                        self.raddresses[clname] = set()
354                    self.raddresses[clname].add(caddr)
355                if 'auth' in client.attrib:
356                    self.auth[client.get('name')] = client.get('auth',
357                                                               'cert+password')
358                if 'uuid' in client.attrib:
359                    self.uuid[client.get('uuid')] = clname
360                if client.get('secure', 'false') == 'true':
361                    self.secure.append(clname)
362                if client.get('location', 'fixed') == 'floating':
363                    self.floating.append(clname)
364                if 'password' in client.attrib:
365                    self.passwords[clname] = client.get('password')
366                for alias in [alias for alias in client.findall('Alias')\
367                              if 'address' in alias.attrib]:
368                    if alias.get('address') in self.addresses:
369                        self.addresses[alias.get('address')].append(clname)
370                    else:
371                        self.addresses[alias.get('address')] = [clname]
372                    if clname not in self.raddresses:
373                        self.raddresses[clname] = set()
374                    self.raddresses[clname].add(alias.get('address'))
375                self.clients.update({clname: client.get('profile')})
376                [self.aliases.update({alias.get('name'): clname}) \
377                 for alias in client.findall('Alias')]
378                self.raliases[clname] = set()
379                [self.raliases[clname].add(alias.get('name')) for alias \
380                 in client.findall('Alias')]
381        elif dest == 'groups.xml':
382            self.public = []
383            self.private = []
384            self.profiles = []
385            self.groups = {}
386            grouptmp = {}
387            self.categories = {}
388            for group in xdata.xpath('//Groups/Group') \
389                    + xdata.xpath('Group'):
390                grouptmp[group.get('name')] = tuple([[item.get('name') for item in group.findall(spec)]
391                                                     for spec in ['./Bundle', './Group']])
392                grouptmp[group.get('name')][1].append(group.get('name'))
393                if group.get('default', 'false') == 'true':
394                    self.default = group.get('name')
395                if group.get('profile', 'false') == 'true':
396                    self.profiles.append(group.get('name'))
397                if group.get('public', 'false') == 'true':
398                    self.public.append(group.get('name'))
399                elif group.get('public', 'true') == 'false':
400                    self.private.append(group.get('name'))
401                if 'category' in group.attrib:
402                    self.categories[group.get('name')] = group.get('category')
403            for group in grouptmp:
404                # self.groups[group] => (bundles, groups, categories)
405                self.groups[group] = (set(), set(), {})
406                tocheck = [group]
407                group_cat = self.groups[group][2]
408                while tocheck:
409                    now = tocheck.pop()
410                    self.groups[group][1].add(now)
411                    if now in grouptmp:
412                        (bundles, groups) = grouptmp[now]
413                        for ggg in [ggg for ggg in groups if ggg not in self.groups[group][1]]:
414                            if ggg not in self.categories or \
415                                   self.categories[ggg] not in self.groups[group][2]:
416                                self.groups[group][1].add(ggg)
417                                tocheck.append(ggg)
418                                if ggg in self.categories:
419                                    group_cat[self.categories[ggg]] = ggg
420                            elif ggg in self.categories:
421                                self.logger.info("Group %s: %s cat-suppressed %s" % \
422                                                 (group,
423                                                  group_cat[self.categories[ggg]],
424                                                  ggg))
425                        [self.groups[group][0].add(bund) for bund in bundles]
426        self.states[dest] = True
427        if False not in self.states.values():
428            # check that all client groups are real and complete
429            real = self.groups.keys()
430            for client in self.clients.keys():
431                if self.clients[client] not in self.profiles:
432                    self.logger.error("Client %s set as nonexistent or incomplete group %s" \
433                                      % (client, self.clients[client]))
434                    self.logger.error("Removing client mapping for %s" % (client))
435                    self.bad_clients[client] = self.clients[client]
436                    del self.clients[client]
437            for bclient in self.bad_clients.keys():
438                if self.bad_clients[bclient] in self.profiles:
439                    self.logger.info("Restored profile mapping for client %s" % bclient)
440                    self.clients[bclient] = self.bad_clients[bclient]
441                    del self.bad_clients[bclient]
442
443    def set_profile(self, client, profile, addresspair):
444        '''Set group parameter for provided client'''
445        self.logger.info("Asserting client %s profile to %s" % (client, profile))
446        if False in self.states.values():
447            raise MetadataRuntimeError
448        if profile not in self.public:
449            self.logger.error("Failed to set client %s to private group %s" % (client, profile))
450            raise MetadataConsistencyError
451        if client in self.clients:
452            self.logger.info("Changing %s group from %s to %s" % (client, self.clients[client], profile))
453            cli = self.clientdata_original.xpath('.//Client[@name="%s"]' % (client))
454            cli[0].set('profile', profile)
455        else:
456            self.logger.info("Creating new client: %s, profile %s" % \
457                             (client, profile))
458            if addresspair in self.session_cache:
459                # we are working with a uuid'd client
460                lxml.etree.SubElement(self.clientdata_original.getroot(),
461                                      'Client', name=client,
462                                      uuid=client, profile=profile,
463                                      address=addresspair[0])
464            else:
465                lxml.etree.SubElement(self.clientdata_original.getroot(),
466                                      'Client', name=client,
467                                      profile=profile)
468        self.clients[client] = profile
469        self.write_back_clients()
470
471    def write_back_clients(self):
472        '''Write changes to client.xml back to disk'''
473        try:
474            datafile = open("%s/%s" % (self.data, 'clients.xml'), 'w')
475        except IOError:
476            self.logger.error("Failed to write clients.xml")
477            raise MetadataRuntimeError
478        fd = datafile.fileno()
479        while self.locked(fd) == True:
480            pass
481        dataroot = self.clientdata_original.getroot()
482        if hasattr(dataroot, 'iter'):
483            items = dataroot.iter()
484        else:
485            items = dataroot.getchildren()
486        for item in items:
487            # no items have text data of any sort
488            item.tail = None
489            item.text = None
490        datafile.write(lxml.etree.tostring(dataroot, pretty_print=True))
491        fcntl.lockf(fd, fcntl.LOCK_UN)
492        datafile.close()
493
494    def locked(self, fd):
495        try:
496            fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
497        except IOError:
498            return True
499        return False
500
501    def resolve_client(self, addresspair):
502        '''Lookup address locally or in DNS to get a hostname'''
503        #print self.session_cache
504        if addresspair in self.session_cache:
505            (stamp, uuid) = self.session_cache[addresspair]
506            if time.time() - stamp < 60:
507                return self.uuid[uuid]
508        address = addresspair[0]
509        if address in self.addresses:
510            if len(self.addresses[address]) != 1:
511                self.logger.error("Address %s has multiple reverse assignments; a uuid must be used" % (address))
512                raise MetadataConsistencyError
513            return self.addresses[address][0]
514        try:
515            cname = socket.gethostbyaddr(address)[0].lower()
516            if cname in self.aliases:
517                return self.aliases[cname]
518            return cname
519        except socket.herror:
520            warning = "address resolution error for %s" % (address)
521            self.logger.warning(warning)
522            raise MetadataConsistencyError
523
524    def get_initial_metadata(self, client):
525        '''Return the metadata for a given client'''
526        client = client.lower()
527        if client in self.aliases:
528            client = self.aliases[client]
529        if client in self.clients:
530            profile = self.clients[client]
531            (bundles, groups, categories) = self.groups[profile]
532        else:
533            if self.default == None:
534                self.logger.error("Cannot set group for client %s; no default group set" % (client))
535                raise MetadataConsistencyError
536            self.set_profile(client, self.default, (None, None))
537            profile = self.default
538            [bundles, groups, categories] = self.groups[self.default]
539        aliases = self.raliases.get(client, set())
540        addresses = self.raddresses.get(client, set())
541        newgroups = set(groups)
542        newbundles = set(bundles)
543        newcategories = {}
544        newcategories.update(categories)
545        if client in self.passwords:
546            password = self.passwords[client]
547        else:
548            password = None
549        uuids = [item for item, value in self.uuid.iteritems() if value == client]
550        if uuids:
551            uuid = uuids[0]
552        else:
553            uuid = None
554        for group in self.cgroups.get(client, []):
555            if group in self.groups:
556                nbundles, ngroups, ncategories = self.groups[group]
557            else:
558                nbundles, ngroups, ncategories = ([], [group], {})
559            [newbundles.add(b) for b in nbundles if b not in newbundles]
560            [newgroups.add(g) for g in ngroups if g not in newgroups]
561            newcategories.update(ncategories)
562        return ClientMetadata(client, profile, newgroups, newbundles, aliases,
563                              addresses, newcategories, uuid, password, self.query)
564
565    def get_all_group_names(self):
566        all_groups = set()
567        [all_groups.update(g[1]) for g in self.groups.values()]
568        return all_groups
569
570    def get_client_names_by_profiles(self, profiles):
571        return [client for client, profile in self.clients.iteritems() \
572                if profile in profiles]
573
574    def get_client_names_by_groups(self, groups):
575        gprofiles = [profile for profile in self.profiles if \
576                     self.groups[profile][1].issuperset(groups)]
577        return self.get_client_names_by_profiles(gprofiles)
578
579    def merge_additional_groups(self, imd, groups):
580        for group in groups:
581            if group in self.categories and \
582                   self.categories[group] in imd.categories:
583                continue
584            nb, ng, _ = self.groups.get(group, (list(), [group], dict()))
585            for b in nb:
586                if b not in imd.bundles:
587                    imd.bundles.add(b)
588            for g in ng:
589                if g not in imd.groups:
590                    if g in self.categories and \
591                       self.categories[g] in imd.categories:
592                        continue
593                    if g in self.private:
594                        self.logger.error("Refusing to add dynamic membership in private group %s for client %s" % (g, imd.hostname))
595                        continue
596                    imd.groups.add(g)
597
598    def merge_additional_data(self, imd, source, data):
599        if not hasattr(imd, source):
600            setattr(imd, source, data)
601            imd.connectors.append(source)
602
603    def validate_client_address(self, client, addresspair):
604        '''Check address against client'''
605        address = addresspair[0]
606        if client in self.floating:
607            self.debug_log("Client %s is floating" % client)
608            return True
609        if address in self.addresses:
610            if client in self.addresses[address]:
611                self.debug_log("Client %s matches address %s" % (client, address))
612                return True
613            else:
614                self.logger.error("Got request for non-float client %s from %s" \
615                                  % (client, address))
616                return False
617        resolved = self.resolve_client(addresspair)
618        if resolved == client:
619            return True
620        else:
621            self.logger.error("Got request for %s from incorrect address %s" \
622                              % (client, address))
623            self.logger.error("Resolved to %s" % resolved)
624            return False
625
626    def AuthenticateConnection(self, cert, user, password, address):
627        '''This function checks auth creds'''
628        if cert:
629            id_method = 'cert'
630            certinfo = dict([x[0] for x in cert['subject']])
631            # look at cert.cN
632            client = certinfo['commonName']
633            self.debug_log("Got cN %s; using as client name" % client)
634            auth_type = self.auth.get(client, 'cert+password')
635        elif user == 'root':
636            id_method = 'address'
637            try:
638                client = self.resolve_client(address)
639            except MetadataConsistencyError:
640                self.logger.error("Client %s failed to resolve; metadata problem" % (address[0]))
641                return False
642        else:
643            id_method = 'uuid'
644            # user maps to client
645            if user not in self.uuid:
646                client = user
647                self.uuid[user] = user
648            else:
649                client = self.uuid[user]
650
651        # we have the client name
652        self.debug_log("Authenticating client %s" % client)
653
654        # next we validate the address
655        if id_method == 'uuid':
656            addr_is_valid = True
657        else:
658            addr_is_valid = self.validate_client_address(client, address)
659
660        if not addr_is_valid:
661            return False
662
663        if id_method == 'cert' and auth_type != 'cert+password':
664            # we are done if cert+password not required
665            return True
666
667        if client not in self.passwords:
668            if client in self.secure:
669                self.logger.error("Client %s in secure mode but has no password" % (address[0]))
670                return False
671            if password != self.password:
672                self.logger.error("Client %s used incorrect global password" % (address[0]))
673                return False
674        if client not in self.secure:
675            if client in self.passwords:
676                plist = [self.password, self.passwords[client]]
677            else:
678                plist = [self.password]
679            if password not in plist:
680                self.logger.error("Client %s failed to use either allowed password" % \
681                                  (address[0]))
682                return False
683        else:
684            # client in secure mode and has a client password
685            if password != self.passwords[client]:
686                self.logger.error("Client %s failed to use client password in secure mode" % \
687                                  (address[0]))
688                return False
689
690        if id_method != 'address':
691            # populate the session cache
692            self.session_cache[address] = (time.time(), user)
693        return True
694
695    def process_statistics(self, meta, _):
696        '''Hook into statistics interface to toggle clients in bootstrap mode'''
697        client = meta.hostname
698        if client in self.auth and self.auth[client] == 'bootstrap':
699            self.logger.info("Asserting client %s auth mode to cert" % client)
700            cli = self.clientdata_original.xpath('.//Client[@name="%s"]' \
701                                                 % (client))
702            cli[0].set('auth', 'cert')
703            self.write_back_clients()
704
705    def viz(self, hosts, bundles, key, colors):
706        '''admin mode viz support'''
707        groups_tree = lxml.etree.parse(self.data + "/groups.xml")
708        groups = groups_tree.getroot()
709        categories = {'default':'grey83'}
710        instances = {}
711        viz_str = ""
712        egroups = groups.findall("Group") + groups.findall('.//Groups/Group')
713        for group in egroups:
714            if not group.get('category') in categories:
715                categories[group.get('category')] = colors.pop()
716            group.set('color', categories[group.get('category')])
717        if None in categories:
718            del categories[None]
719        if hosts:
720            clients = self.clients
721            for client, profile in clients.iteritems():
722                if profile in instances:
723                    instances[profile].append(client)
724                else:
725                    instances[profile] = [client]
726            for profile, clist in instances.iteritems():
727                clist.sort()
728                viz_str += '''\t"%s-instances" [ label="%s", shape="record" ];\n''' \
729                    % (profile, '|'.join(clist))
730                viz_str += '''\t"%s-instances" -> "group-%s";\n''' \
731                    % (profile, profile)
732        if bundles:
733            bundles = []
734            [bundles.append(bund.get('name')) \
735                 for bund in groups.findall('.//Bundle') \
736                 if bund.get('name') not in bundles]
737            bundles.sort()
738            for bundle in bundles:
739                viz_str += '''\t"bundle-%s" [ label="%s", shape="septagon"];\n''' \
740                    % (bundle, bundle)
741        gseen = []
742        for group in egroups:
743            if group.get('profile', 'false') == 'true':
744                style = "filled, bold"
745            else:
746                style = "filled"
747            gseen.append(group.get('name'))
748            viz_str += '\t"group-%s" [label="%s", style="%s", fillcolor=%s];\n' % \
749                (group.get('name'), group.get('name'), style, group.get('color'))
750            if bundles:
751                for bundle in group.findall('Bundle'):
752                    viz_str += '\t"group-%s" -> "bundle-%s";\n' % \
753                        (group.get('name'), bundle.get('name'))
754        gfmt = '\t"group-%s" [label="%s", style="filled", fillcolor="grey83"];\n'
755        for group in egroups:
756            for parent in group.findall('Group'):
757                if parent.get('name') not in gseen:
758                    viz_str += gfmt % (parent.get('name'), parent.get('name'))
759                    gseen.append(parent.get("name"))
760                viz_str += '\t"group-%s" -> "group-%s" ;\n' % \
761                    (group.get('name'), parent.get('name'))
762        if key:
763            for category in categories:
764                viz_str += '''\t"''' + category + '''" [label="''' + category + \
765                    '''", shape="record", style="filled", fillcolor=''' + \
766                    categories[category] + '''];\n'''
767        return viz_str
Note: See TracBrowser for help on using the browser.