# -*- coding: utf-8 mode: python -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 __all__ = ( 'Deploydb', 'Host', 'host_matcher', 'hostname_matcher', ) import logging; log = logging.getLogger(__name__) import os, sys, socket, csv from os import path from .utils import * from .objects import XT, fileP, pathP, lowerP, Object, catalog ################################################################################ # Configuration de deploydb class Deploydb(Object): ATTRS = XT(Object, dir=pathP, file=fileP) ################################################################################ # Gestion des groupes class Group(Object): """Groupe d'objets liés. Lors du resolve, toutes les variables définies pour le groupe sont propagées aux objets liés si elles ne sont pas définies dans ces objets. dans l'exemple suivant: ~~~ group mymodules shared=all -module module1,module2 moduledir=~/wop/php -host host1,host2 domain=long.tld ~~~ la variable shared est initialisée pour module1,module2,host1,host2 alors que la variable moduledir ne concerne que module1,module2 et la variable domain ne concerne que host1,host2 """ __RESOLVE_FIRST__ = True ATTRS = XT(Object) def _resolve(self, catalog): for otype, links in self.links.items(): for link in links: object = link.resolve(catalog, resolve=False) object.set_defaults(link.attrs) object.set_defaults(self.attrs) ################################################################################ # Gestion des hôtes def withdomain(h): return '.' in h def fix_host(host, domain=None): if host.endswith('.'): host = host[:-1] elif domain and not withdomain(host): host = "%s.%s" % (host, domain) return host def strip_hostname(a): pos = a.find('.') if pos == -1: return None else: return a[pos + 1:] def strip_domain(a): pos = a.find('.') if pos == -1: return a else: return a[:pos] def match_host(qhost, object): qhost = lowerP.parse(qhost) if withdomain(qhost): # host avec chemin qhost = fix_host(qhost) return qhost in object.get('host', ()) else: # nom de host return qhost in object.get('hostname', ()) def host_matcher(qhost): return lambda object: match_host(qhost, object) def match_hostname(qhost, object): qhost = lowerP.parse(qhost) qhost = path.basename(qhost) return qhost in object.get('hostname', ()) def hostname_matcher(qhost): return lambda object: match_hostname(qhost, object) class Host(Object): ATTRS = XT(Object, values=lowerP, host=lowerP, hostname=lowerP, domain=lowerP, ip=None) def _resolve(self, catalog): if self.oid == '*': return default = catalog.get(self.otype, '*', None, False) hosts = self.get('host', []) hostnames = self.get('hostname', ()) domains = self.get('domain', ()) search_basedir = self.get('search_basedir', ('dirs',)) files = 'files' in search_basedir dirs = 'dirs' in search_basedir basedir = self.get('basedir', None) if basedir is not None: hostdirs = self.resolve_basedir(basedir, files=files, dirs=dirs) hosts.extend(map(path.basename, hostdirs)) dirspec = self.get('dirspec', None) if dirspec is not None: hostdirs = self.resolve_filespec(dirspec, dirs=True) hosts.extend(map(path.basename, hostdirs)) filespec = self.get('filespec', None) if filespec is not None: hostfiles = self.resolve_filespec(filespec, files=True) hosts.extend(map(path.basename, hostfiles)) if hosts: # générer hostname et domain à partir host if not domains: domains = set(map(strip_hostname, hosts)) domains = filter(lambda d: d is not None, domains) if not domains and default is not None: domains = default.get('domain', ()) domains = filter(None, domains) if domains: domains = self.domain = set(domains) hostnames = map(strip_domain, hostnames or hosts) if hostnames: hostnames = self.hostname = set(hostnames) if domains: tmphosts = [] for host in hosts: for domain in domains: tmphosts.append(fix_host(host, domain)) else: tmphosts = map(fix_host, hosts) hosts = self.host = set(tmphosts) else: # générer host à partir de hostname et domain if not domains and default is not None: domains = default.get('domain', ()) if domains: domains = self.domain = set(domains) if not hostnames: hostnames = [self.oid] hostnames = map(strip_domain, hostnames) if hostnames: self.hostname = hostnames if domains: hosts = [] for domain in domains: for hostname in hostnames: hosts.append('%s.%s' % (hostname, domain)) else: hosts = hostnames if hosts: hosts = self.host = set(hosts) ips = self.get('ip', []) if not ips: for host in hosts: try: hostnames, aliases, ipaddrs = socket.gethostbyname_ex(host) ips.extend(ipaddrs) except socket.herror, e: log.error("error resolving %s: %s, %s", host, e[0], e[1]) except socket.gaierror, e: log.error("error resolving %s: %s, %s", host, e[0], e[1]) if ips: ips = self.ip = set(ips) if not self.values: self.values = hosts def save_hosts(*args): """Ecrire les hôtes définis sous forme de liste csv, qu'il est possible d'exploiter avec 'deploydb loadcsv' plus ou moins équivalent à `save_objects host` mais les champs sont dans un ordre ergonomique (cette fonction a été écrite en premier, elle est gardée pour l'historique) """ # tout d'abord déterminer tous les attributs nécessaires headers = ['host', 'hostname', 'domain', 'ip'] hosts = catalog.find_objects('host') for host in hosts: for name in host.attrs.keys(): if name not in headers: headers.append(name) # ensuite les écrire rows = [] for host in hosts: if host.oid == '*': continue row = [host.otype, host.oid] for name in headers: row.append(';'.join(host.get(name, ()))) rows.append(row) headers[0:0] = ['otype', 'oid'] # écrire le résultat out = csv.writer(sys.stdout) out.writerow(headers) out.writerows(rows) ################################################################################ # Actions def save_objects(*args): """Ecrire les objets sous forme de liste csv, qu'il est possible d'exploiter avec 'deploydb loadcsv' usage: save_objects [otype [oids...]] """ otypes = listof(args[0] if args[0:1] else None, None) if otypes is not None: otypes = flattenstr(otypes) oids = args[1:] or None objects = catalog.find_objects(otypes, oids, create=False) # tout d'abord déterminer tous les attributs nécessaires headers = ['otype', 'oid'] for object in objects: for name in object.known_rw_attrs: if name not in headers: headers.append(name) for object in objects: for name in object.misc_attrs: if name not in headers: headers.append(name) # ensuite les écrire rows = [] for object in objects: row = [] for name in headers: row.append(';'.join(object.get(name, ()))) rows.append(row) # écrire le résultat out = csv.writer(sys.stdout) out.writerow(headers) out.writerows(rows) def query(*args): pass