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

224 lines
8.2 KiB
Python

# -*- coding: utf-8 mode: python -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
__all__ = (
'Wyapp',
'wyapp_matcher', 'wyappname_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
################################################################################
# Wyapp
def match_wyapp(qwyapp, object):
if withpath(qwyapp): # wyapp avec chemin
return qwyapp in object.get('wyapp', ())
else: # nom de wyapp
return qwyapp in object.get('wyappname', ())
def wyapp_matcher(qwyapp):
return lambda object: match_wyapp(qwyapp, object)
def match_wyappname(qwyapp, object):
qwyapp = path.basename(qwyapp)
return qwyapp in object.get('wyappname', ())
def wyappname_matcher(qwyapp):
return lambda object: match_wyappname(qwyapp, object)
class Wyapp(Object):
ATTRS = XT(Object,
values=pathP, wyapp=mpathP, wyappdir=pathP,
basedir=pathP, dirspec=fileP)
def _resolve(self, catalog):
if self.oid == '*': return
default = catalog.get(self.otype, '*', None, False)
wyapps = self.get('wyapp', [])
basedir = self.get('basedir', None)
if basedir is not None:
wyapps.extend(self.resolve_basedir(basedir, dirs=True))
dirspec = self.get('dirspec', None)
if dirspec is not None:
wyapps.extend(self.resolve_filespec(dirspec, dirs=True))
if wyapps:
# générer wyappdir et wyappname à partir de wyapp
wyappdirs = [path.dirname(wyapp) for wyapp in wyapps]
if wyappdirs: wyappdirs = self.wyappdir = ulistof(wyappdirs)
wyappnames = [path.basename(wyapp) for wyapp in wyapps]
if wyappnames: wyappnames = self.wyappname = ulistof(wyappnames)
else:
# générer wyapps à partir de wyappdir et wyappname
wyappdirs = self.get('wyappdir', ())
if not wyappdirs and default is not None:
wyappdirs = default.get('wyappdir', ())
if wyappdirs: wyappdirs = self.wyappdir = ulistof(wyappdirs)
wyappnames = self.get('wyappname', ())
if not wyappnames: wyappnames = [self.oid]
if wyappnames: wyappnames = self.wyappname = ulistof(wyappnames)
if wyappdirs:
wyapps = []
for wyappname in wyappnames:
found = []
for wyappdir in wyappdirs:
wyapp = path.join(wyappdir, wyappname)
if path.exists(wyapp):
found.append(wyapp)
break
if not found:
found = [path.join(wyappdirs[0], wyappname)]
wyapps.extend(found)
else:
wyapps = wyappnames
if wyapps: wyapps = self.wyapp = ulistof(wyapps)
if not self.values:
self.values = wyapps
################################################################################
# Actions
def query_rwyinst(*args):
"""afficher la commande pour déployer avec la commande $1 le wyapp $2 sur
l'hôte $3 dans le profil $4
$1 doit valoir 'rwyinst' ou être un chemin vers ce script
$2 peut être
* un nom de wyapp: tout les wyapps de ce nom sont sélectionnés
* un chemin complet: si un wyapp 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 wyapp
* non spécifié: tout les wyapps 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 wyapp 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
wyapps 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 wyapp, 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
wyinst.
"""
rwyinst = args[0] if args[0:1] else None
if rwyinst is not None and (rwyinst == 'rwyinst' or rwyinst.endswith('/rwyinst')):
verb = 'rwyinst'
else:
raise ValueError("Le verbe est requis et doit valoir 'rwyinst'")
qwyapp = 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 qwyapp and not qhost:
raise ValueError("Il faut spécifier wyapp et/ou host")
if not qwyapp:
wyapps = None
elif cwithpath(qwyapp):
qwyapp = path.abspath(qwyapp)
wyapps = catalog.find_objects('wyapp', expr=wyapp_matcher(qwyapp))
if not wyapps:
wyapps = catalog.find_objects('wyapp', expr=wyappname_matcher(qwyapp))
else:
wyapps = catalog.find_objects('wyapp', expr=wyappname_matcher(qwyapp))
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)
# wyapps et hosts sont spécifiés
if wyapps is not None and hosts is not None:
facts = catalog.find_facts(
verb=verb,
tsotype='wyapp', tsexpr=dict(oid=[wyapp.oid for wyapp in wyapps]),
ttotype='host', ttexpr=dict(oid=[host.oid for host in hosts]),
expr=qprofile,
)
# Seuls les wyapps sont spécifiés: chercher les hôtes
elif wyapps is not None:
facts = catalog.find_facts(
verb=verb,
tsotype='wyapp', tsexpr=dict(oid=[wyapp.oid for wyapp in wyapps]),
ttotype='host',
expr=qprofile,
)
# Seuls les hôtes sont spécifiés: chercher les wyapps
elif hosts is not None:
facts = catalog.find_facts(
verb=verb,
tsotype='wyapp',
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])
ws = flattenseq([wyapp.wyapp for wyapp 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 w in ws:
# préférer si possible le chemin fourni par l'utilisateur
if withpath(qwyapp): w = qwyapp
hs = ':'.join(hs)
parts = [xwyinst, '--no-deploydb', '-h', qshell(hs), qshell(w), '--', '-y']
if vars: parts.append(' '.join(vars))
print ' '.join(parts)