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

239 lines
8.9 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__ = (
'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-, 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-, 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)