nutools/lib/nulib/python/deploydb/woinst_module.py

333 lines
13 KiB
Python
Raw Normal View History

2018-04-26 23:19:17 +04:00
# -*- 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-, 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-, 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)