333 lines
13 KiB
Python
333 lines
13 KiB
Python
# -*- 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)
|