# -*- coding: utf-8 mode: python -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 __all__ = ( 'Wobundle', 'wobundle_matcher', 'wobundlename_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 ################################################################################ # Wobundle def match_wobundle(qwobundle, object): if withpath(qwobundle): # wobundle avec chemin return qwobundle in object.get('wobundle', ()) else: # nom de wobundle return qwobundle in object.get('wobundlename', ()) def wobundle_matcher(qwobundle): return lambda object: match_wobundle(qwobundle, object) def match_wobundlename(qwobundle, object): qwobundle = path.basename(qwobundle) return qwobundle in object.get('wobundlename', ()) def wobundlename_matcher(qwobundle): return lambda object: match_wobundlename(qwobundle, object) class Wobundle(Object): ATTRS = XT(Object, values=pathP, wobundle=mpathP, wobundledir=pathP, basedir=pathP, dirspec=fileP) FILESPECS = ['*.woa', '*.framework'] def _resolve(self, catalog): if self.oid == '*': return default = catalog.get(self.otype, '*', None, False) wobundles = self.get('wobundle', []) basedir = self.get('basedir', None) if basedir is not None: wobundles.extend(self.resolve_basedir(basedir, dirs=True, filespec=self.FILESPECS)) dirspec = self.get('dirspec', None) if dirspec is not None: wobundles.extend(self.resolve_filespec(dirspec, dirs=True)) if wobundles: # générer wobundledir et wobundlename à partir de wobundle wobundledirs = [path.dirname(wobundle) for wobundle in wobundles] if wobundledirs: wobundledirs = self.wobundledir = ulistof(wobundledirs) wobundlenames = [path.basename(wobundle) for wobundle in wobundles] if wobundlenames: wobundlenames = self.wobundlename = ulistof(wobundlenames) else: # générer wobundles à partir de wobundledir et wobundlename wobundledirs = self.get('wobundledir', ()) if not wobundledirs and default is not None: wobundledirs = default.get('wobundledir', ()) if wobundledirs: wobundledirs = self.wobundledir = ulistof(wobundledirs) wobundlenames = self.get('wobundlename', ()) if not wobundlenames: wobundlenames = ['%s.woa' % self.oid] if wobundlenames: wobundlenames = self.wobundlename = ulistof(wobundlenames) if wobundledirs: wobundles = [] for wobundlename in wobundlenames: found = [] for wobundledir in wobundledirs: wobundle = path.join(wobundledir, wobundlename) if path.exists(wobundle): found.append(wobundle) break if not found: found = [path.join(wobundledirs[0], wobundlename)] wobundles.extend(found) else: wobundles = wobundlenames if wobundles: wobundles = self.wobundle = ulistof(wobundles) if not self.values: self.values = wobundles ################################################################################ # Actions def option_choice(yesoption, nooption): def func(value): if istrue(value): return yesoption else: return nooption return func def what_choice(value): if value in ('bundle',): return '--bundle' elif value in ('webres',): return '--webres' else: log.warning("invalid what value %s", value) return None def restart_choice(value): if istrue(value) or value in ('restart', 'stop-start'): return '--stop-start' elif value in ('bounce',): return '--bounce' elif isfalse(value) or value in ('no-restart',): return '--no-restart' else: log.warning("invalid restart value %s", value) return None WOINST_ATTRS = { 'prefix': dict( func=option_choice('--prefix', '--no-prefix'), ), 'what': dict( func=what_choice, ), 'tag': dict( func=option_choice('--tag', '--no-tag'), ), 'dbconfig': dict( option='--active-dbconfig', ), 'dbconfig_map': dict( option='--active-dbconfig-map', multiple=True, flattensep=',', ), 'restart': dict( func=restart_choice, ), 'exec': dict( option='--exec', multiple=True, ), } def query_rwoinst(*args): """afficher la commande pour déployer avec la commande $1 le wobundle $2 sur l'hôte $3 dans le profil $4 $1 doit valoir 'rwoinst' ou être un chemin vers ce script $2 peut être * un nom de wobundle: tout les wobundles de ce nom sont sélectionnés * un chemin complet: si un wobundle 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 wobundle * non spécifié: tout les wobundles 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 wobundle 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 wobundles 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, 'NONE,prod' permet de sélectionner les déploiements sans profil ou dans le profil 'prod' le wobundle, 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. Les mappings suivants sont supportés: = attribut = = option de woinst = what --bundle / --webres dbconfig --active-dbconfig dbconfig_map --active-dbconfig-map restart --stop-start / --bounce exec --exec tag --tag / --no-tag prefix --prefix / --no-prefix destdir HTDOCSDIR= Les autres définitions sont utilisées comme argument de woinst, pour définir les valeurs des préfixes. """ rwoinst = args[0] if args[0:1] else None if rwoinst is not None and (rwoinst == 'rwoinst' or rwoinst.endswith('/rwoinst')): verb = 'rwoinst' else: raise ValueError("Le verbe est requis et doit valoir 'rwoinst'") qwobundle = 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 qwobundle and not qhost: raise ValueError("Il faut spécifier wobundle et/ou host") if not qwobundle: wobundles = None elif cwithpath(qwobundle): qwobundle = path.abspath(qwobundle) wobundles = catalog.find_objects('wobundle', expr=wobundle_matcher(qwobundle)) if not wobundles: wobundles = catalog.find_objects('wobundle', expr=wobundlename_matcher(qwobundle)) else: wobundles = catalog.find_objects('wobundle', expr=wobundlename_matcher(qwobundle)) 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) # wobundles et hosts sont spécifiés if wobundles is not None and hosts is not None: facts = catalog.find_facts( verb=verb, tsotype='wobundle', tsexpr=dict(oid=[wobundle.oid for wobundle in wobundles]), ttotype='host', ttexpr=dict(oid=[host.oid for host in hosts]), expr=qprofile, ) # Seuls les wobundles sont spécifiés: chercher les hôtes elif wobundles is not None: facts = catalog.find_facts( verb=verb, tsotype='wobundle', tsexpr=dict(oid=[wobundle.oid for wobundle in wobundles]), ttotype='host', expr=qprofile, ) # Seuls les hôtes sont spécifiés: chercher les wobundles elif hosts is not None: facts = catalog.find_facts( verb=verb, tsotype='wobundle', 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: if supplattrs: parser.parse_attrs(supplattrs, fact) hs = ':'.join(flattenseq([host.host for host in ttobjects])) for wobundle in tsobjects: # construire les options de woinst. on prend les valeurs d'abord # dans le fait puis dans l'objet wobundle. options = [] vars = [] names = set(fact.attrs.keys()) names.update(wobundle.attrs.keys()) for name in names: values = fact.get(name, None) factvalue = True if values is None: values = wobundle.get(name, None) factvalue = False if values is None: # ne devrait pas se produire en principe continue if name in ('profile',): # les attributs de sélection du profil ont été déjà été traités # plus haut continue elif name == 'destdir': name = 'HTDOCSDIR' params = WOINST_ATTRS.get(name, None) if params is None: if factvalue: # les variables spécifiques ne sont prise que dans le # fait. vars.append("%s=%s" % (name, qshell(':'.join(values)))) else: func = params.get('func', None) option = params.get('option', None) if func is not None: option = func(values[0]) if option is not None: options.extend(listof(option)) elif option is not None: if params.get('multiple', False): flattensep = params.get('flattensep', None) if flattensep is not None: values = flattenstr(values, flattensep) for value in values: options.append(option) options.append(qshell(value)) else: options.append(option) options.append(qshell(values[0])) else: raise ValueError("missing option key for attribute %s" % name) for w in wobundle.wobundle: # préférer si possible le chemin fourni par l'utilisateur if withpath(qwobundle): w = qwobundle parts = [rwoinst, '--no-deploydb', '-yh', qshell(hs), qshell(w)] if options or vars: parts.append('--') if options: parts.extend(options) if vars: parts.extend(vars) print ' '.join(parts)