# -*- coding: utf-8 mode: python -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 __all__ = ( 'Module', 'module_matcher', 'modulename_matcher', ) import logging; log = logging.getLogger(__name__) import os, sys from os import path from .utils import * from .expr import * from .objects import XT, fileP, pathP, mpathP, lowerP, Object, catalog from .parser import Parser from .base_module import withdomain, host_matcher, hostname_matcher ################################################################################ # Module def match_module(qmodule, object): if withpath(qmodule): # module avec chemin return qmodule in object.get('module', ()) else: # nom de module return qmodule in object.get('modulename', ()) def module_matcher(qmodule): return lambda object: match_module(qmodule, object) def match_modulename(qmodule, object): qmodule = path.basename(qmodule) return qmodule in object.get('modulename', ()) def modulename_matcher(qmodule): return lambda object: match_modulename(qmodule, object) class Module(Object): ATTRS = XT(Object, values=pathP, module=mpathP, moduledir=pathP, basedir=pathP, dirspec=fileP) def _resolve(self, catalog): if self.oid == '*': return default = catalog.get(self.otype, '*', None, False) modules = self.get('module', []) basedir = self.get('basedir', None) if basedir is not None: modules.extend(self.resolve_basedir(basedir, dirs=True)) dirspec = self.get('dirspec', None) if dirspec is not None: modules.extend(self.resolve_filespec(dirspec, dirs=True)) if modules: # générer moduledir et modulename à partir de module moduledirs = [path.dirname(module) for module in modules] if moduledirs: moduledirs = self.moduledir = ulistof(moduledirs) modulenames = [path.basename(module) for module in modules] if modulenames: modulenames = self.modulename = ulistof(modulenames) else: # générer modules à partir de moduledir et modulename moduledirs = self.get('moduledir', ()) if not moduledirs and default is not None: moduledirs = default.get('moduledir', ()) if moduledirs: moduledirs = self.moduledir = ulistof(moduledirs) modulenames = self.get('modulename', ()) if not modulenames: modulenames = [self.oid] if modulenames: modulenames = self.modulename = ulistof(modulenames) if moduledirs: modules = [] for modulename in modulenames: found = [] for moduledir in moduledirs: module = path.join(moduledir, modulename) if path.exists(module): found.append(module) break if not found: found = [path.join(moduledirs[0], modulename)] modules.extend(found) else: modules = modulenames if modules: modules = self.module = ulistof(modules) if not self.values: self.values = modules ################################################################################ # Actions def query_xuinst(*args): """afficher la commande pour déployer avec la commande $1 le module $2 sur l'hôte $3 dans le profil $4 $1 peut valoir 'ruinst' ou 'uinst' ou être un chemin vers l'un de ces deux scripts $2 peut être * un nom de module: tout les modules de ce nom sont sélectionnés * un chemin complet: si un module avec le chemin complet est trouvé, ne sélectinner que celui-là, sinon faire comme si on n'avait spécifié que le nom du module * non spécifié: tout les modules devant être déployé sur l'hôte sont cherchés $3 peut être * un nom d'hôte: tous les hôtes de ce nom sont sélectionés * un nom d'hôte pleinement qualifié: si le nom d'hôte pleinement qualifié est trouvé, ne sélectionner que celui-là, sinon faire comme si on n'avait spécifié que le nom d'hôte * non spécifié: tous les hôtes vers lequel doit être déployé le module sont cherchés $4 peut valoir * 'NONE': seuls les déploiements sans profils définis sont sélectionnés. c'est la valeur par défaut. * 'ALL' ou '': ne pas tenir compte du profil lors de la sélection des modules et des hôtes * toute autre valeur, e.g prod ou test: seuls les déploiement de ce profil sont sélectionnés Il est possible de spécifier plusieurs profils en les séparant par des virgules. Par exemple, 'prod,NONE' permet de sélectionner les déploiements sans profil ou dans le profil 'prod' le module, ou l'hôte, ou les deux sont requis. le profil est facultatif. Les valeurs $5..$* sont des définitions d'attributs utilisées pour mettre à jour les faits trouvés. Ces définitions sont utilisés comme argument de uinst. """ xuinst = args[0] if args[0:1] else None if xuinst is None: verb = None elif xuinst == 'ruinst' or xuinst.endswith('/ruinst'): verb = 'ruinst' elif xuinst == 'uinst' or xuinst.endswith('/uinst'): verb = 'uinst' else: verb = None if verb is None: raise ValueError("Le verbe est requis et doit être 'uinst' ou 'ruinst'") qmodule = args[1:2] and args[1] or None qhost = args[2:3] and args[2] or None qprofile = args[3] if args[3:4] else 'NONE' supplattrs = args[4:] if not qmodule and not qhost: raise ValueError("Il faut spécifier module et/ou host") if not qmodule: modules = None elif cwithpath(qmodule): qmodule = path.abspath(qmodule) modules = catalog.find_objects('module', expr=module_matcher(qmodule)) if not modules: modules = catalog.find_objects('module', expr=modulename_matcher(qmodule)) else: modules = catalog.find_objects('module', expr=modulename_matcher(qmodule)) if not qhost: hosts = None else: if cwithpath(qhost): qhost = path.basename(path.abspath(qhost)) if withdomain(qhost): hosts = catalog.find_objects('host', expr=host_matcher(qhost)) if not hosts: hosts = catalog.find_objects('host', expr=hostname_matcher(qhost)) else: hosts = catalog.find_objects('host', expr=hostname_matcher(qhost)) if qprofile == '': qprofile = 'ALL' qprofiles = flattenstr([qprofile]) if 'ALL' in qprofiles: qprofile = None else: expr = [] for qprofile in qprofiles: if qprofile == 'NONE': qprofile = NONE(EXISTS('profile')) else: qprofile = dict(profile=qprofile) expr.append(qprofile) qprofile = ANY(*expr) # modules et hosts sont spécifiés if modules is not None and hosts is not None: facts = catalog.find_facts( verb=verb, tsotype='module', tsexpr=dict(oid=[module.oid for module in modules]), ttotype='host', ttexpr=dict(oid=[host.oid for host in hosts]), expr=qprofile, ) # Seuls les modules sont spécifiés: chercher les hôtes elif modules is not None: facts = catalog.find_facts( verb=verb, tsotype='module', tsexpr=dict(oid=[module.oid for module in modules]), ttotype='host', expr=qprofile, ) # Seuls les hôtes sont spécifiés: chercher les modules elif hosts is not None: facts = catalog.find_facts( verb=verb, tsotype='module', ttotype='host', ttexpr=dict(oid=[host.oid for host in hosts]), expr=qprofile, ) # afficher la commande if supplattrs: parser = Parser() for fact, tsobjects, ttobjects in facts: hs = flattenseq([host.host for host in ttobjects]) ms = flattenseq([module.module for module in tsobjects]) if supplattrs: parser.parse_attrs(supplattrs, fact) vars = [] for name, values in fact.attrs.items(): vars.append("%s=%s" % (name, qshell(':'.join(values)))) for m in ms: # préférer si possible le chemin fourni par l'utilisateur if withpath(qmodule): m = qmodule if fact.verb == 'uinst': # chaque hôte est traité à part avec uinst:rsync for h in hs: parts = [xuinst, '--no-deploydb', '-yh', qshell(h), qshell(m)] if vars: parts.extend(['--', ' '.join(vars)]) print ' '.join(parts) elif fact.verb == 'ruinst': hs = ':'.join(hs) parts = [xuinst, '--no-deploydb', '-h', qshell(hs), qshell(m), '--', '-y'] if vars: parts.append(' '.join(vars)) print ' '.join(parts)