239 lines
8.9 KiB
Python
239 lines
8.9 KiB
Python
# -*- coding: utf-8 mode: python -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
|
|
|
|
__all__ = (
|
|
'Module',
|
|
'module_matcher', 'modulename_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
|
|
|
|
################################################################################
|
|
# Module
|
|
|
|
def match_module(qmodule, object):
|
|
if withpath(qmodule): # module avec chemin
|
|
return qmodule in object.get('module', ())
|
|
else: # nom de module
|
|
return qmodule in object.get('modulename', ())
|
|
def module_matcher(qmodule):
|
|
return lambda object: match_module(qmodule, object)
|
|
|
|
def match_modulename(qmodule, object):
|
|
qmodule = path.basename(qmodule)
|
|
return qmodule in object.get('modulename', ())
|
|
def modulename_matcher(qmodule):
|
|
return lambda object: match_modulename(qmodule, object)
|
|
|
|
class Module(Object):
|
|
ATTRS = XT(Object,
|
|
values=pathP, module=mpathP, moduledir=pathP,
|
|
basedir=pathP, dirspec=fileP)
|
|
|
|
def _resolve(self, catalog):
|
|
if self.oid == '*': return
|
|
default = catalog.get(self.otype, '*', None, False)
|
|
|
|
modules = self.get('module', [])
|
|
basedir = self.get('basedir', None)
|
|
if basedir is not None:
|
|
modules.extend(self.resolve_basedir(basedir, dirs=True))
|
|
dirspec = self.get('dirspec', None)
|
|
if dirspec is not None:
|
|
modules.extend(self.resolve_filespec(dirspec, dirs=True))
|
|
|
|
if modules:
|
|
# générer moduledir et modulename à partir de module
|
|
moduledirs = [path.dirname(module) for module in modules]
|
|
if moduledirs: moduledirs = self.moduledir = ulistof(moduledirs)
|
|
|
|
modulenames = [path.basename(module) for module in modules]
|
|
if modulenames: modulenames = self.modulename = ulistof(modulenames)
|
|
|
|
else:
|
|
# générer modules à partir de moduledir et modulename
|
|
moduledirs = self.get('moduledir', ())
|
|
if not moduledirs and default is not None:
|
|
moduledirs = default.get('moduledir', ())
|
|
if moduledirs: moduledirs = self.moduledir = ulistof(moduledirs)
|
|
|
|
modulenames = self.get('modulename', ())
|
|
if not modulenames: modulenames = [self.oid]
|
|
if modulenames: modulenames = self.modulename = ulistof(modulenames)
|
|
|
|
if moduledirs:
|
|
modules = []
|
|
for modulename in modulenames:
|
|
found = []
|
|
for moduledir in moduledirs:
|
|
module = path.join(moduledir, modulename)
|
|
if path.exists(module):
|
|
found.append(module)
|
|
break
|
|
if not found:
|
|
found = [path.join(moduledirs[0], modulename)]
|
|
modules.extend(found)
|
|
else:
|
|
modules = modulenames
|
|
if modules: modules = self.module = ulistof(modules)
|
|
|
|
if not self.values:
|
|
self.values = modules
|
|
|
|
################################################################################
|
|
# Actions
|
|
|
|
def query_xuinst(*args):
|
|
"""afficher la commande pour déployer avec la commande $1 le module $2 sur
|
|
l'hôte $3 dans le profil $4
|
|
|
|
$1 peut valoir 'ruinst' ou 'uinst' ou être un chemin vers l'un de ces deux
|
|
scripts
|
|
|
|
$2 peut être
|
|
* un nom de module: tout les modules de ce nom sont sélectionnés
|
|
* un chemin complet: si un module 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 module
|
|
* non spécifié: tout les modules 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 module 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
|
|
modules 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 module, 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
|
|
uinst.
|
|
"""
|
|
xuinst = args[0] if args[0:1] else None
|
|
if xuinst is None:
|
|
verb = None
|
|
elif xuinst == 'ruinst' or xuinst.endswith('/ruinst'):
|
|
verb = 'ruinst'
|
|
elif xuinst == 'uinst' or xuinst.endswith('/uinst'):
|
|
verb = 'uinst'
|
|
else:
|
|
verb = None
|
|
if verb is None:
|
|
raise ValueError("Le verbe est requis et doit être 'uinst' ou 'ruinst'")
|
|
qmodule = 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 qmodule and not qhost:
|
|
raise ValueError("Il faut spécifier module et/ou host")
|
|
|
|
if not qmodule:
|
|
modules = None
|
|
elif cwithpath(qmodule):
|
|
qmodule = path.abspath(qmodule)
|
|
modules = catalog.find_objects('module', expr=module_matcher(qmodule))
|
|
if not modules:
|
|
modules = catalog.find_objects('module', expr=modulename_matcher(qmodule))
|
|
else:
|
|
modules = catalog.find_objects('module', expr=modulename_matcher(qmodule))
|
|
|
|
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)
|
|
|
|
# modules et hosts sont spécifiés
|
|
if modules is not None and hosts is not None:
|
|
facts = catalog.find_facts(
|
|
verb=verb,
|
|
tsotype='module', tsexpr=dict(oid=[module.oid for module in modules]),
|
|
ttotype='host', ttexpr=dict(oid=[host.oid for host in hosts]),
|
|
expr=qprofile,
|
|
)
|
|
|
|
# Seuls les modules sont spécifiés: chercher les hôtes
|
|
elif modules is not None:
|
|
facts = catalog.find_facts(
|
|
verb=verb,
|
|
tsotype='module', tsexpr=dict(oid=[module.oid for module in modules]),
|
|
ttotype='host',
|
|
expr=qprofile,
|
|
)
|
|
|
|
# Seuls les hôtes sont spécifiés: chercher les modules
|
|
elif hosts is not None:
|
|
facts = catalog.find_facts(
|
|
verb=verb,
|
|
tsotype='module',
|
|
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])
|
|
ms = flattenseq([module.module for module 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 m in ms:
|
|
# préférer si possible le chemin fourni par l'utilisateur
|
|
if withpath(qmodule): m = qmodule
|
|
if fact.verb == 'uinst':
|
|
# chaque hôte est traité à part avec uinst:rsync
|
|
for h in hs:
|
|
parts = [xuinst, '--no-deploydb', '-yh', qshell(h), qshell(m)]
|
|
if vars:
|
|
parts.extend(['--', ' '.join(vars)])
|
|
print ' '.join(parts)
|
|
elif fact.verb == 'ruinst':
|
|
hs = ':'.join(hs)
|
|
parts = [xuinst, '--no-deploydb', '-h', qshell(hs), qshell(m), '--', '-y']
|
|
if vars: parts.append(' '.join(vars))
|
|
print ' '.join(parts)
|