# -*- coding: utf-8 mode: python -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 __all__ = ( 'Webapp', 'webapp_matcher', 'webappname_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 ################################################################################ # Webapp def match_webapp(qwebapp, object): if withpath(qwebapp): # webapp avec chemin return qwebapp in object.get('webapp', ()) else: # nom de webapp return qwebapp in object.get('webappname', ()) def webapp_matcher(qwebapp): return lambda object: match_webapp(qwebapp, object) def match_webappname(qwebapp, object): qwebapp = path.basename(qwebapp) return qwebapp in object.get('webappname', ()) def webappname_matcher(qwebapp): return lambda object: match_webappname(qwebapp, object) class Webapp(Object): ATTRS = XT(Object, values=pathP, webapp=mpathP, webappdir=pathP, basedir=pathP, dirspec=fileP) def _resolve(self, catalog): if self.oid == '*': return default = catalog.get(self.otype, '*', None, False) webapps = self.get('webapp', []) basedir = self.get('basedir', None) if basedir is not None: webapps.extend(self.resolve_basedir(basedir, dirs=True)) dirspec = self.get('dirspec', None) if dirspec is not None: webapps.extend(self.resolve_filespec(dirspec, dirs=True)) if webapps: # générer webappdir et webappname à partir de webapp webappdirs = [path.dirname(webapp) for webapp in webapps] if webappdirs: webappdirs = self.webappdir = ulistof(webappdirs) webappnames = [path.basename(webapp) for webapp in webapps] if webappnames: webappnames = self.webappname = ulistof(webappnames) else: # générer webapps à partir de webappdir et webappname webappdirs = self.get('webappdir', ()) if not webappdirs and default is not None: webappdirs = default.get('webappdir', ()) if webappdirs: webappdirs = self.webappdir = ulistof(webappdirs) webappnames = self.get('webappname', ()) if not webappnames: webappnames = [self.oid] if webappnames: webappnames = self.webappname = ulistof(webappnames) if webappdirs: webapps = [] for webappname in webappnames: found = [] for webappdir in webappdirs: webapp = path.join(webappdir, webappname) if path.exists(webapp): found.append(webapp) break if not found: found = [path.join(webappdirs[0], webappname)] webapps.extend(found) else: webapps = webappnames if webapps: webapps = self.webapp = ulistof(webapps) if not self.values: self.values = webapps ################################################################################ # Actions def option_choice(yesoption, nooption): def func(value, *ignored): if istrue(value): return yesoption else: return nooption return func def pffprofile_support(value, fact, webapp): pffprofile = fact.get('pffprofile', None) if pffprofile is None: pffprofile = webapp.get('pffprofile', None) if pffprofile is None: return None if value == 'ensure': return ['--ensure-pffprofile', pffprofile[0]] elif value == 'set': return ['--set-pffprofile', pffprofile[0]] TOINST_ATTRS = { 'tomcat_profile': dict( option='--config-profile', ), 'catalina_base': dict( option='--catalina-base', ), 'tomcat_user': dict( option='--tomcat-user', ), 'tomcat_group': dict( option='--tomcat-group', ), 'tomcat_version': dict( option='--tomcat-version', ), 'manager_url': dict( option='--manager-url', ), 'manager_user': dict( option='--manager-user', ), 'manager_password': dict( option='--manager-password', ), 'wamap': dict( option='--wamap', multiple=True, flattensep=',', ), 'exclude': dict( option='--exclude', multiple=True, ), 'exclude_override': dict( option='--replace-excludes', multiple=True, ), 'protect': dict( option='--protect', multiple=True, ), 'rsync_option': dict( option='--rsync-option', multiple=True, ), 'rsync_option_override': dict( option='--replace-rsync-options', multiple=True, ), 'backup': dict( func=option_choice('--backup', '--no-backup'), ), 'restart': dict( func=option_choice('--restart', '--no-restart'), ), 'legacy_sort': dict( func=option_choice('--legacy-sort', None), ), 'pffprofile_support': dict( func=pffprofile_support, ), } def query_rtoinst(*args): """afficher la commande pour déployer avec la commande $1 la webapp $2 sur l'hôte $3 dans le profil $4 ou le profil pff $5 $1 doit valoir 'rtoinst' ou être un chemin vers ce script $2 peut être * un nom de webapp: toutes les webapps de ce nom sont sélectionnés * un chemin complet: si une webapp avec le chemin complet est trouvée, ne sélectinner que celle-là, sinon faire comme si on n'avait spécifié que le nom de la webapp * non spécifié: toutes les webapps devant être déployé sur l'hôte sont cherchées $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 webapp 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 webapps et des hôtes * toute autre valeur, e.g prod ou test: seuls les déploiements 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' $5 peut valoir * 'NONE': seuls les déploiement sans profils pff définis sont sélectionnés. * 'ALL' ou '': ne pas tenir compte du profil pff lors de la sélection des webapps et des hôtes. c'est la valeur par défaut. * toute autre valeur, e.g prod ou test: seuls les déploiements de ce profil pff sont sélectionnés. la webapp, ou l'hôte, ou les deux sont requis. le profil et le profil pff sont facultatifs. 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 toinst = tomcat_profile --config-profile catalina_base --catalina-base tomcat_user --tomcat-user tomcat_group --tomcat-group tomcat_version --tomcat-version manager_url --manager-url manager_user --manager-user manager_password --manager-password wamap --wamap exclude --exclude exclude_override --replace-excludes protect --protect rsync_option --rsync-option rsync_option_override --replace-rsync-options backup --backup / --no-backup restart --restart / --no-restart legacy_sort --legacy-sort pffprofile_support --ensure-pffprofile / --set-pffprofile """ rtoinst = args[0] if args[0:1] else None if rtoinst is not None and (rtoinst == 'rtoinst' or rtoinst.endswith('/rtoinst')): verb = 'rtoinst' else: raise ValueError("Le verbe est requis et doit valoir 'rtoinst'") qwebapp = 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' qpffprofile = args[4] if args[4:5] else 'ALL' supplattrs = args[5:] if not qwebapp and not qhost: raise ValueError("Il faut spécifier webapp et/ou host") if not qwebapp: webapps = None elif cwithpath(qwebapp): qwebapp = path.abspath(qwebapp) webapps = catalog.find_objects('webapp', expr=webapp_matcher(qwebapp)) if not webapps: webapps = catalog.find_objects('webapp', expr=webappname_matcher(qwebapp)) else: webapps = catalog.find_objects('webapp', expr=webappname_matcher(qwebapp)) 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) if qpffprofile == '': qpffprofile = 'ALL' qpffprofiles = flattenstr([qpffprofile]) if 'ALL' in qpffprofiles: qpffprofile = None else: expr = [] for qpffprofile in qpffprofiles: if qpffprofile == 'NONE': qpffprofile = NONE(EXISTS('pffprofile')) else: qpffprofile = dict(pffprofile=qpffprofile) expr.append(qpffprofile) qpffprofile = ANY(*expr) if qprofile is None and qpffprofile is None: expr = None elif qprofile is not None and qpffprofile is not None: expr = ALL(qprofile, qpffprofile) elif qprofile is not None: expr = qprofile elif qpffprofile is not None: expr = qpffprofile # webapps et hosts sont spécifiés if webapps is not None and hosts is not None: facts = catalog.find_facts( verb=verb, tsotype='webapp', tsexpr=dict(oid=[webapp.oid for webapp in webapps]), ttotype='host', ttexpr=dict(oid=[host.oid for host in hosts]), expr=expr, ) # Seuls les webapps sont spécifiés: chercher les hôtes elif webapps is not None: facts = catalog.find_facts( verb=verb, tsotype='webapp', tsexpr=dict(oid=[webapp.oid for webapp in webapps]), ttotype='host', expr=expr, ) # Seuls les hôtes sont spécifiés: chercher les webapps elif hosts is not None: facts = catalog.find_facts( verb=verb, tsotype='webapp', ttotype='host', ttexpr=dict(oid=[host.oid for host in hosts]), expr=expr, ) # 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 webapp in tsobjects: # construire les options de toinst. on prend les valeurs d'abord dans le # fait puis dans l'objet webapp. options = [] names = set(fact.attrs.keys()) names.update(webapp.attrs.keys()) for name in names: values = fact.get(name, None) factvalue = True if values is None: values = webapp.get(name, None) factvalue = False if values is None: # ne devrait pas se produire en principe continue if name in ('profile', 'pffprofile'): # les attributs de sélection du profil ont été déjà été traités # plus haut continue params = TOINST_ATTRS.get(name, None) if params is None: if factvalue: log.warning("ignoring %s option %s=%r", fact.verb, name, values) else: func = params.get('func', None) option = params.get('option', None) if func is not None: option = func(values[0], fact, webapp) 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 webapp.webapp: # préférer si possible le chemin fourni par l'utilisateur if withpath(qwebapp): w = qwebapp parts = [rtoinst, '--no-deploydb', '-yh', qshell(hs), qshell(w)] if options: parts.append('--') parts.extend(options) print ' '.join(parts)