nutools/lib/ulib/support/deploydb.py

1511 lines
57 KiB
Python
Raw Normal View History

#!/usr/bin/env python
# -*- coding: utf-8 mode: python -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
import os, sys, re
from os import path
from argparse import ArgumentParser, REMAINDER
from glob import glob
USER_CONFDIR = '~/etc/deploy'
SYSTEM_CONFDIR = '/var/local/deploy'
2017-06-18 11:57:38 +04:00
################################################################################
# Diverses fonctions
def isseq(t):
return isinstance(t, list) or isinstance(t, tuple) or isinstance(t, set)
2017-07-18 17:33:32 +04:00
def flatten(src, unique=True, clean=True, sep=','):
"""découper chaque élément du tableau src selon sep et les aplatir dans une
seule liste.
Si unique==True, supprimer les doublons.
Si clean==True, supprimer les valeurs vides et les espaces périphériques
e.g flatten(['a , b', 'c,']) --> ['a', 'b', 'c']
"""
if not isseq(src): src = [src]
dest = []
for items in src:
items = items.split(sep)
if clean: items = filter(None, map(lambda item: item.strip(), items))
if unique:
for item in items:
if item not in dest: dest.append(item)
else:
dest.extend(items)
return dest
################################################################################
# Base de données
class GenericObject(object):
2017-07-18 17:33:32 +04:00
"""Un objet générique
Un objet a un identifiant (propriété id), une liste de valeurs (propriété
values) et des attributs (propriété attrs)
"""
_id = None
_values = None
_attrs = None
def __init__(self, id):
self._id = id
2017-06-18 11:57:38 +04:00
self._values = set()
self._attrs = {}
def get_id(self): return self._id
id = property(get_id)
def get_values(self): return self._values
values = property(get_values)
def add_value(self, value):
2017-06-18 11:57:38 +04:00
self._values.add(value)
def get_attrs(self): return self._attrs
attrs = property(get_attrs)
2017-07-18 17:33:32 +04:00
def get_attr(self, name):
"""Obtenir la valeur d'un attribut ou None si l'attribut n'existe pas
"""
return self._attrs.get(name, None)
def set_attr(self, name, value):
2017-07-18 17:33:32 +04:00
"""Ajouter une valeur à la liste des valeurs d'un attribut si elle n'y est pas
déjà.
"""
if name not in self._attrs: self._attrs[name] = []
if value not in self._attrs[name]: self._attrs[name].append(value)
def reset_attr(self, name, value=None):
2017-07-18 17:33:32 +04:00
"""Recommencer à zéro la valeur d'un attribut
"""
if name in self._attrs: del self._attrs[name]
if value is not None: self.set_attr(name, value)
def add_attr(self, name, value):
2017-07-18 17:33:32 +04:00
"""Ajouter une valeur à la liste des valeurs d'un attribut
"""
if name not in self._attrs: self._attrs[name] = []
self._attrs[name].append(value)
def del_attr(self, name, value):
2017-07-18 17:33:32 +04:00
"""Supprimer une valeur d'un attribut
"""
if name not in self._attrs: return
self._attrs[name].remove(value)
if not self._attrs[name]: del self._attrs[name]
def modify_attr(self, name, value, method='set'):
2017-07-18 17:33:32 +04:00
"""Méthode générique pour modifier les valeurs d'un attribut
method peut valoir set, reset, add, del
"""
if method == 'set': self.set_attr(name, value)
elif method == 'reset': self.reset_attr(name, value)
elif method == 'add': self.add_attr(name, value)
elif method == 'del': self.del_attr(name, value)
2017-06-16 00:36:36 +04:00
def merge_attrs(self, other, method='set'):
2017-07-18 17:33:32 +04:00
"""Fusionner dans cet objet les attributs d'un autre objet, avec la méthode
spécifiée.
"""
for name, values in other.attrs.items():
for value in values:
self.modify_attr(name, value, method)
def _dump_id(self, indent):
print "%s%s" % (indent, self.id)
def _dump_values(self, indent, name):
print "%s %s: %s" % (indent, name, ' '.join(self.values))
def _dump_attrs(self, indent):
if self.attrs:
print "%s attrs:" % indent
for name, values in self.attrs.items():
if len(values) == 1:
print "%s %s=%s" % (indent, name, repr(values[0]))
else:
print "%s %s=(%s)" % (indent, name, ', '.join(map(repr, values)))
def dump(self, indent=''):
2017-07-18 17:33:32 +04:00
"""Afficher l'identifiant, les valeurs et les attributs de cet objet
"""
self._dump_id(indent)
2017-07-18 17:33:32 +04:00
self._dump_values(indent, 'values')
self._dump_attrs(indent)
class HostObject(GenericObject):
2017-07-18 17:33:32 +04:00
"""Un hôte
"""
@staticmethod
def genid(host):
2017-07-18 17:33:32 +04:00
"""Générer un identifiant à partir du nom d'hôte
e.g. MY-Host42.tld --> my_host42
"""
host = re.sub(r'\..*', '', host)
host = re.sub(r'[^a-zA-Z0-9]', '_', host)
host = host.lower()
return host
@staticmethod
def genib(id):
2017-07-18 17:33:32 +04:00
"""Générer un identifiant générique à partir d'un identifiant qualifié
e.g. my_host42 --> my_host
"""
if re.match(r'\d+$', id): return id
ib = re.sub(r'^(.*?)\d+$', r'\1', id)
return ib
def dump(self, indent=''):
self._dump_id(indent)
self._dump_values(indent, 'hosts')
self._dump_attrs(indent)
class ModuleObject(GenericObject):
2017-07-18 17:33:32 +04:00
"""Un module, uinst-allable
"""
@staticmethod
def genid(module):
2017-07-18 17:33:32 +04:00
"""Générér un identifiant à partir du nom du module
e.g. MY-Module --> MY_Module
"""
module = re.sub(r'[^a-zA-Z0-9]', '_', module)
return module
def dump(self, indent=''):
self._dump_id(indent)
self._dump_values(indent, 'modules')
self._dump_attrs(indent)
class WobundleObject(GenericObject):
2017-07-18 17:33:32 +04:00
"""Un bundle Webobjects, woinst-allable
"""
@staticmethod
def genid(wobundle):
2017-07-18 17:33:32 +04:00
"""Générér un identifiant à partir du nom du bundle
e.g. MY-App.woa --> MY_App
"""
wobundle = re.sub(r'\.(woa|framework)$', '', wobundle)
wobundle = re.sub(r'[^a-zA-Z0-9]', '_', wobundle)
return wobundle
def dump(self, indent=''):
self._dump_id(indent)
self._dump_values(indent, 'wobundles')
self._dump_attrs(indent)
class WebappObject(GenericObject):
2017-07-18 17:33:32 +04:00
"""Une webapp, toinst-allable
"""
@staticmethod
def genid(webapp):
2017-07-18 17:33:32 +04:00
"""Générér un identifiant à partir du nom de la webapp
e.g. MY-Webapp --> MY_Webapp
"""
webapp = re.sub(r'[^a-zA-Z0-9]', '_', webapp)
return webapp
def dump(self, indent=''):
self._dump_id(indent)
self._dump_values(indent, 'webapps')
self._dump_attrs(indent)
2017-06-16 00:36:36 +04:00
class GenericLink(object):
2017-07-18 17:33:32 +04:00
"""Un lien générique
Un lien est valide dans un profil (propriété profile), a un type d'objets de
départ (propriété ftype), un type d'objet d'arrivée (propriété ttype), un
ensemble d'objets de départ (propriété fos), un ensemble d'objets d'arrivée
(propriété tos), et des attributs (propriété attrs).
"""
2017-06-16 00:36:36 +04:00
_profile = None
_ftype = None
2017-06-18 11:57:38 +04:00
_fos = None
2017-06-16 00:36:36 +04:00
_ttype = None
_tos = None
_attrs = None
def __init__(self, ftype, ttype):
self._ftype = ftype
2017-06-18 11:57:38 +04:00
self._fos = set()
2017-06-16 00:36:36 +04:00
self._ttype = ttype
2017-06-18 11:57:38 +04:00
self._tos = set()
2017-06-16 00:36:36 +04:00
self._attrs = {}
2017-06-18 21:59:38 +04:00
def is_defaults(self):
2017-07-18 17:33:32 +04:00
"""Cet objet contient-il les attributs par défaut pour les liens provenant du
type d'objet ftype
"""
2017-06-18 21:59:38 +04:00
fos = list(self._fos)
return len(fos) == 1 and fos[0] is None
2017-06-16 00:36:36 +04:00
def get_profile(self): return self._profile
2017-06-18 21:59:38 +04:00
def set_profile(self, profile): self._profile = profile
2017-06-16 00:36:36 +04:00
profile = property(get_profile, set_profile)
def get_ftype(self): return self._ftype
ftype = property(get_ftype)
2017-06-18 11:57:38 +04:00
def get_fos(self): return self._fos
fos = property(get_fos)
def add_fo(self, fo):
self._fos.add(fo)
2017-06-16 00:36:36 +04:00
def get_ttype(self): return self._ttype
ttype = property(get_ttype)
def get_tos(self): return self._tos
tos = property(get_tos)
def add_to(self, to):
2017-06-18 11:57:38 +04:00
self._tos.add(to)
2017-06-16 00:36:36 +04:00
def get_attrs(self): return self._attrs
attrs = property(get_attrs)
2017-07-18 17:33:32 +04:00
def get_attr(self, name):
"""Obtenir la valeur d'un attribut ou None si l'attribut n'existe pas
"""
return self._attrs.get(name, None)
2017-06-16 00:36:36 +04:00
def set_attr(self, name, value):
2017-07-18 17:33:32 +04:00
"""Ajouter une valeur à la liste des valeurs d'un attribut si elle n'y est pas
déjà.
"""
2017-06-16 00:36:36 +04:00
if name not in self._attrs: self._attrs[name] = []
if value not in self._attrs[name]: self._attrs[name].append(value)
def reset_attr(self, name, value=None):
2017-07-18 17:33:32 +04:00
"""Recommencer à zéro la valeur d'un attribut
"""
2017-06-16 00:36:36 +04:00
if name in self._attrs: del self._attrs[name]
if value is not None: self.set_attr(name, value)
def add_attr(self, name, value):
2017-07-18 17:33:32 +04:00
"""Ajouter une valeur à la liste des valeurs d'un attribut
"""
2017-06-16 00:36:36 +04:00
if name not in self._attrs: self._attrs[name] = []
self._attrs[name].append(value)
def del_attr(self, name, value):
2017-07-18 17:33:32 +04:00
"""Supprimer une valeur d'un attribut
"""
2017-06-16 00:36:36 +04:00
if name not in self._attrs: return
self._attrs[name].remove(value)
if not self._attrs[name]: del self._attrs[name]
def modify_attr(self, name, value, method='set'):
2017-07-18 17:33:32 +04:00
"""Méthode générique pour modifier les valeurs d'un attribut
method peut valoir set, reset, add, del
"""
2017-06-16 00:36:36 +04:00
if method == 'set': self.set_attr(name, value)
elif method == 'reset': self.reset_attr(name, value)
elif method == 'add': self.add_attr(name, value)
elif method == 'del': self.del_attr(name, value)
def merge_attrs(self, other, method='set'):
2017-07-18 17:33:32 +04:00
"""Fusionner dans ce lien les attributs d'un autre lien, avec la méthode
spécifiée.
"""
2017-06-16 00:36:36 +04:00
for name, values in other.attrs.items():
for value in values:
self.modify_attr(name, value, method)
2017-07-18 17:47:58 +04:00
def invalid_match(self, match):
return ValueError("%s: invalid match type")
2017-06-18 21:59:38 +04:00
def match_profiles(self, profiles):
2017-07-18 17:33:32 +04:00
"""Tester si ce lien est dans l'un des profils spécifiés
Si profiles == None, alors l'utilisateur ne demande aucun profil en
particulier. Dans ce cas, la réponse est OUI.
Si profiles == [], alors l'utilisateur demande tous les liens pour
lesquels aucun profil n'est défini. Dans ce cas, la réponse est OUI si
ce lien ne définit aucun profil.
Sinon, profiles est une liste des profils recherchés. Dans ce cas, la
réponse n'est OUI que si le profil de ce lien fait partie des profils
spécifiés.
"""
if profiles is None: return True
2017-06-18 21:59:38 +04:00
if not isseq(profiles): profiles = [profiles]
2017-07-18 17:33:32 +04:00
if profiles == []: return self._profile is None
2017-06-18 21:59:38 +04:00
for profile in profiles:
if profile == self._profile: return True
return False
2017-07-18 17:47:58 +04:00
def match_fos(self, fos, match='any', reverse=False):
2017-07-18 17:33:32 +04:00
"""Tester si ce lien a l'un des objets spécifiés dans ses objets de départ (avec
match=='any' qui est la valeur par défaut)
"""
2017-06-18 11:57:38 +04:00
if not isseq(fos): fos = [fos]
2017-07-18 17:47:58 +04:00
if reverse: Yes, No = False, True
else: Yes, No = True, False
if match == 'any':
for fo in fos:
if fo in self._fos: return Yes
return No
elif match == 'all':
for fo in fos:
if fo not in self._fos: return No
return Yes
else:
raise self.invalid_match(match)
def match_tos(self, tos, match='any', reverse=False):
2017-07-18 17:33:32 +04:00
"""Tester si ce lien a l'un des objets spécifiés dans ses objets d'arrivée (avec
match=='any' qui est la valeur par défaut)
"""
2017-06-18 11:57:38 +04:00
if not isseq(tos): tos = [tos]
2017-07-18 17:47:58 +04:00
if reverse: Yes, No = False, True
else: Yes, No = True, False
if match == 'any':
for to in tos:
if to in self._tos: return Yes
return No
elif match == 'all':
for to in tos:
if to not in self._tos: return No
return Yes
else:
raise self.invalid_match(match)
def match_attrs(self, attrs, match='any', reverse=False):
2017-07-18 17:33:32 +04:00
"""Tester si ce lien a un des attributs correspondant aux valeurs spécifiées
(avec match=='any' qui est la valeur par défaut)
"""
2017-07-18 17:47:58 +04:00
if reverse: Yes, No = False, True
else: Yes, No = True, False
if match == 'any':
for name, value in attrs.items():
values = self._attrs.get(name, None)
if values is None: continue
if value in values: return Yes
return No
elif match == 'all':
for name, value in attrs.items():
values = self._attrs.get(name, None)
if values is None: return No
if value not in values: return No
return Yes
else:
raise self.invalid_match(match)
2017-06-18 11:57:38 +04:00
2017-06-16 00:36:36 +04:00
def _dump_profile(self, indent):
profile = self.profile or 'ALL'
print "%s profile: %s" % (indent, profile)
2017-06-18 11:57:38 +04:00
def _dump_fos(self, indent):
2017-06-18 21:59:38 +04:00
fos = [fo or '(default)' for fo in self.fos]
print "%s from: %s" % (indent, ' '.join(fos))
2017-06-16 00:36:36 +04:00
def _dump_tos(self, indent):
print "%s to: %s" % (indent, ' '.join(self.tos))
def _dump_attrs(self, indent):
if self.attrs:
print "%s attrs:" % indent
for name, values in self.attrs.items():
if len(values) == 1:
print "%s %s=%s" % (indent, name, repr(values[0]))
else:
print "%s %s=(%s)" % (indent, name, ', '.join(map(repr, values)))
def dump(self, indent=''):
2017-07-18 17:33:32 +04:00
"""Afficher le profil, les objets de départ, les objets d'arrivée, et les
attributs de ce lien
"""
2017-06-16 00:36:36 +04:00
print "%slink" % indent
self._dump_profile(indent)
2017-06-18 11:57:38 +04:00
self._dump_fos(indent)
2017-06-16 00:36:36 +04:00
self._dump_tos(indent)
self._dump_attrs(indent)
class UinstLink(GenericLink):
2017-07-18 17:33:32 +04:00
"""Un module déployé sur un hôte avec le type uinst:rsync
"""
2017-06-16 00:36:36 +04:00
def __init__(self):
super(UinstLink, self).__init__('module', 'host')
def dump(self, indent=''):
print "%suinst" % indent
self._dump_profile(indent)
2017-06-18 11:57:38 +04:00
self._dump_fos(indent)
2017-06-16 00:36:36 +04:00
self._dump_tos(indent)
self._dump_attrs(indent)
class RuinstLink(GenericLink):
2017-07-18 17:33:32 +04:00
"""Un module déployé sur un hôte avec ruinst
"""
2017-06-16 00:36:36 +04:00
def __init__(self):
2017-06-18 11:57:38 +04:00
super(RuinstLink, self).__init__('module', 'host')
2017-06-16 00:36:36 +04:00
def dump(self, indent=''):
2017-06-18 21:59:38 +04:00
print "%sruinst" % indent
self._dump_profile(indent)
self._dump_fos(indent)
self._dump_tos(indent)
self._dump_attrs(indent)
class RwoinstBundleLink(GenericLink):
2017-07-18 17:33:32 +04:00
"""Un bundle déployé sur un hôte avec rwoinst
"""
2017-06-18 21:59:38 +04:00
def __init__(self):
super(RwoinstBundleLink, self).__init__('wobundle', 'host')
def dump(self, indent=''):
print "%srwoinst bundle" % indent
self._dump_profile(indent)
self._dump_fos(indent)
self._dump_tos(indent)
self._dump_attrs(indent)
class RwoinstWebresLink(GenericLink):
2017-07-18 17:33:32 +04:00
"""Les resources web d'un bundle déployées sur un hôte avec rwoinst
"""
2017-06-18 21:59:38 +04:00
def __init__(self):
super(RwoinstWebresLink, self).__init__('wobundle', 'host')
def dump(self, indent=''):
print "%srwoinst webres" % indent
self._dump_profile(indent)
self._dump_fos(indent)
self._dump_tos(indent)
self._dump_attrs(indent)
class RtoinstLink(GenericLink):
2017-07-18 17:33:32 +04:00
"""Une webapp déployée sur un hôte avec rtoinst
"""
2017-06-18 21:59:38 +04:00
def __init__(self):
super(RtoinstLink, self).__init__('webapp', 'host')
def dump(self, indent=''):
print "%srtoinst" % indent
self._dump_profile(indent)
2017-06-18 11:57:38 +04:00
self._dump_fos(indent)
2017-06-16 00:36:36 +04:00
self._dump_tos(indent)
self._dump_attrs(indent)
2017-06-18 21:59:38 +04:00
class UNDEF(object):
__repr__ = __string__ = lambda self: 'UNDEF'
UNDEF = UNDEF()
class Database(object):
2017-07-18 17:33:32 +04:00
"""La base de données des objets et des liens
"""
_default_profile = None
_default_domain = None
2017-06-18 21:59:38 +04:00
_objects_ids_otype = None
_objects_classes = None
2017-06-18 21:59:38 +04:00
_links_ltype = None
2017-06-16 00:36:36 +04:00
_links_classes = None
def __init__(self):
2017-06-18 21:59:38 +04:00
self._objects_ids_otype = {}
self._objects_classes = {}
2017-06-18 21:59:38 +04:00
self._links_ltype = {}
2017-06-16 00:36:36 +04:00
self._links_classes = {}
def has_default_profile(self):
2017-07-18 17:33:32 +04:00
"""Vérifier si un profil par défaut a été spécifié
"""
return self._default_profile is not None
def get_default_profile(self):
2017-07-18 17:33:32 +04:00
"""Obtenir le profil par défaut, ou None si aucun profil par défaut n'a été
défini
"""
return self._default_profile
def set_default_profile(self, profile):
2017-07-18 17:33:32 +04:00
"""Spécifier le profil par défaut
Si un lien est spécifié sans profil, il est réputé défini dans le
profil par défaut.
"""
self._default_profile = profile or None
default_profile = property(get_default_profile, set_default_profile)
def has_default_domain(self):
2017-07-18 17:33:32 +04:00
"""Vérifier si un domaine par défaut a été spécifié
"""
return self._default_domain is not None
def get_default_domain(self):
2017-07-18 17:33:32 +04:00
"""Obtenir le domaine par défaut, ou None si aucun domaine par défaut n'a été
défini
"""
return self._default_domain
def set_default_domain(self, domain):
2017-07-18 17:33:32 +04:00
"""Spécifier le domaine par défaut
Si un hôte est spécifié sans domaine, il est réputé défini avec le
domaine par défaut.
"""
if domain is not None:
#XXX si le domaine a été corrigé, l'indiquer en warning
if domain.startswith('.'): domain = domain[1:]
if domain.endswith('.'): domain = domain[:-1]
self._default_domain = domain or None
default_domain = property(get_default_domain, set_default_domain)
def register_object(self, otype, oclass=None):
2017-07-18 17:33:32 +04:00
"""Enregistrer un type d'objet et la classe utilisée pour l'instancier.
Par défaut, le classe utilisé est GenericObject.
"""
2017-06-18 21:59:38 +04:00
if not self._objects_ids_otype.has_key(otype):
self._objects_ids_otype[otype] = {}
if oclass is None: oclass = GenericObject
self._objects_classes[otype] = oclass
2017-06-18 21:59:38 +04:00
def get_known_otypes(self):
2017-07-18 17:33:32 +04:00
"""Obtenir la liste des types d'objets connus
"""
2017-06-18 21:59:38 +04:00
return self._objects_ids_otype.keys()
known_otypes = property(get_known_otypes)
def get_objects(self, otype):
2017-07-18 17:33:32 +04:00
"""Obtenir tous les objets définis du type spécifié ou None si le type d'objets
est invalide.
"""
2017-06-18 21:59:38 +04:00
objects = self._objects_ids_otype.get(otype, None)
if objects is None: return None
return [objects[id] for id in objects if id is not None]
def has_object(self, otype, id):
2017-07-18 17:33:32 +04:00
"""Vérifier si l'objet avec l'identifiant spécifié est défini
"""
2017-06-18 21:59:38 +04:00
objects = self._objects_ids_otype.get(otype, None)
if objects is None: return False
return objects.has_key(id)
2017-07-18 17:33:32 +04:00
def get_object(self, otype, id, create=True):
"""Obtenir l'objet avec l'identifiant spécifié, ou None si l'objet n'existe pas.
Si create==True, l'objet est créé s'il n'existe pas, et cette méthode ne
retourne pas None.
"""
2017-06-18 21:59:38 +04:00
objects = self._objects_ids_otype.get(otype, None)
if objects is None: return None
object = objects.get(id, None)
2017-07-18 17:33:32 +04:00
if object is None and create:
object_class = self._objects_classes.get(otype, None)
if object_class is not None:
object = object_class(id)
2017-06-18 21:59:38 +04:00
self._objects_ids_otype[otype][id] = object
return object
2017-06-18 11:57:38 +04:00
def register_link(self, ltype, lclass):
2017-07-18 17:33:32 +04:00
"""Enregister un type de lien et la classe utilisée pour l'instancier.
Il n'y a pas de classe par défaut, il faut absolument spécifier une
classe valide.
"""
2017-06-18 21:59:38 +04:00
if not self._links_ltype.has_key(ltype):
self._links_ltype[ltype] = []
2017-06-16 00:36:36 +04:00
self._links_classes[ltype] = lclass
2017-06-18 21:59:38 +04:00
def get_known_ltypes(self):
2017-07-18 17:33:32 +04:00
"""Obtenir la liste des types de liens connus
"""
2017-06-18 21:59:38 +04:00
return self._links_ltype.keys()
known_ltypes = property(get_known_ltypes)
def get_links(self, ltype, profile=UNDEF, fo=UNDEF, to=UNDEF, attrs=UNDEF, create=False):
2017-07-18 17:33:32 +04:00
"""Obtenir les liens correspondant aux critères.
XXX compléter la doc
"""
2017-06-18 21:59:38 +04:00
links = self._links_ltype.get(ltype, None)
if links is None: return None
2017-06-18 11:57:38 +04:00
found = None
for link in links:
2017-06-18 21:59:38 +04:00
if (profile is UNDEF or link.match_profiles(profile)) \
and (fo is UNDEF or link.match_fos(fo)) \
and (to is UNDEF or link.match_tos(to)) \
and (attrs is UNDEF or link.match_attrs(attrs)):
2017-06-18 11:57:38 +04:00
if found is None: found = []
found.append(link)
if found is not None: return found
2017-06-18 21:59:38 +04:00
if not create: return []
# ici, create==True
if profile is UNDEF: profile = None
2017-06-18 11:57:38 +04:00
link = self._links_classes[ltype]()
2017-06-18 21:59:38 +04:00
link.profile = profile
if fo is not UNDEF:
2017-06-18 11:57:38 +04:00
if isseq(fo): fos = fo
else: fos = [fo]
for fo in fos:
link.add_fo(fo)
2017-06-18 21:59:38 +04:00
if to is not UNDEF:
2017-06-18 11:57:38 +04:00
if isseq(to): tos = to
else: tos = [to]
for to in tos:
link.add_to(to)
2017-06-18 21:59:38 +04:00
if attrs is not UNDEF:
2017-06-18 11:57:38 +04:00
for name, value in attrs.items():
link.set_attr(name, value)
links.append(link)
return [link]
2017-06-16 00:36:36 +04:00
################################################################################
# Analyse des fichiers de configuration
class EOL(object):
__repr__ = __string__ = lambda self: 'EOL'
EOL = EOL()
class EOF(object):
__repr__ = __string__ = lambda self: 'EOF'
EOF = EOF()
class Lexer(object):
file = None
lexems = None
_inf = None
_lcount = None
_line = None
def __init__(self, file, parse=True):
self.file = file
if parse: self.parse()
def next_line(self):
line = self._inf.readline()
if line == '': return None
if line.endswith("\r\n"): line = line[:-2]
elif line.endswith("\n"): line = line[:-1]
elif line.endswith("\r"): line = line[:-1]
self._lcount += 1
self._line = line
return line
def is_empty(self): return self._line == ''
def isa_comment(self): return self._line[:1] == '#'
def isa_squote(self): return self._line[:1] == "'"
def isa_dquote(self): return self._line[:1] == '"'
RE_SPACE = re.compile(r'\s+')
RE_COMMENT = re.compile(r'#.*')
def parse_ws(self):
mo = self.RE_SPACE.match(self._line)
if mo is not None:
self._line = self._line[mo.end(0):]
mo = self.RE_COMMENT.match(self._line)
if mo is not None:
self._line = self._line[mo.end(0):]
def isa_space(self): return self.RE_SPACE.match(self._line) is not None
def isa_comment(self): return self.RE_COMMENT.match(self._line) is not None
RE_SQUOTE = re.compile(r"'")
def parse_sstring(self):
slos = self._lcount
lexem = ''
self._line = self._line[1:]
mo = self.RE_SQUOTE.search(self._line)
while mo is None:
lexem += self._line
if self.next_line() is None:
raise ValueError("unterminated quoted string starting at line %i" % slos)
lexem += "\n"
mo = self.RE_SQUOTE.search(self._line)
lexem += self._line[0:mo.start(0)]
self._line = self._line[mo.end(0):]
return lexem
RE_DQUOTE = re.compile(r'"')
def parse_dstring(self):
slos = self._lcount
lexem = ''
self._line = self._line[1:]
mo = self.RE_DQUOTE.search(self._line)
while mo is None:
lexem += self._line
if self.next_line() is None:
raise ValueError("unterminated double-quoted string starting at line %i" % slos)
lexem += "\n"
mo = self.RE_DQUOTE.search(self._line)
lexem += self._line[0:mo.start(0)]
self._line = self._line[mo.end(0):]
lexem = lexem.replace('\\"', '"')
lexem = lexem.replace("\\'", "'")
lexem = lexem.replace('\\\\', '\\')
return lexem
RE_EOS = re.compile(r'''\s|(?<!\\)['"]''')
def parse_string(self):
mo = self.RE_EOS.search(self._line)
if mo is not None:
lexem = self._line[0:mo.start(0)]
self._line = self._line[mo.start(0):]
else:
lexem = self._line
self._line = ''
lexem = lexem.replace('\\"', '"')
lexem = lexem.replace("\\'", "'")
lexem = lexem.replace('\\\\', '\\')
return lexem
def parse(self):
if self.lexems is not None: return self.lexems
lexems = self.lexems = []
self._inf = open(self.file, 'rb')
self._lcount = 0
self._line = ''
try:
SOL = False
while True:
# Ignorer lignes vides et commentaires
self.parse_ws()
stop = False
while self.is_empty():
if SOL:
lexems.append(EOL)
SOL = False
if self.next_line() is None:
stop = True
break
self.parse_ws()
if stop: break
SOL = True
# Construire une chaine
lexem = ''
while True:
if self.is_empty(): break
elif self.isa_space(): break
if self.isa_squote(): lexem += self.parse_sstring()
elif self.isa_dquote(): lexem += self.parse_dstring()
else: lexem += self.parse_string()
lexems.append(lexem)
lexems.append(EOF)
return lexems
finally:
self._inf.close()
self._inf = None
return lexems
def get_predicates(self):
predicates = []
predicate = []
for lexem in self.parse():
if lexem is EOF: break
elif lexem is EOL:
predicates.append(predicate)
predicate = []
else:
predicate.append(lexem)
return predicates
RE_NAMETYPE = re.compile(r'(\S+):(\w+)$')
def split_namev(arg):
"spliter name[:type][method][=value]"
if '=' in arg:
name, value = arg.split('=', 1)
else:
name = arg
value = None
if name.endswith('%'):
name = name[:-1]
2017-06-15 02:24:17 +04:00
method = 'reset'
elif name.endswith('+'):
name = name[:-1]
method = 'add'
elif name.endswith('-'):
name = name[:-1]
method = 'del'
else:
2017-06-15 02:24:17 +04:00
method = 'set'
mo = RE_NAMETYPE.match(name)
if mo is not None:
name, type = mo.groups()
else:
type = None
return name, value, type, method
def split_nvalue(arg):
"spliter [name=]value"
if '=' in arg:
name, value = arg.split('=', 1)
else:
name = None
value = arg
return name, value
def split_nlist(arg):
"spliter [name=]values"
if '=' in arg:
name, values = arg.split('=', 1)
values = values.split(',')
else:
name = None
values = arg.split(',')
return name, values
class Parser(object):
db = None
groups = None
attr_otype = None
commands = None
def __init__(self, db=None):
if db is None: db = Database()
self.db = db
self.groups = {}
2017-07-18 17:33:32 +04:00
self.commands = {
'default_profile': self.handle_default_profile,
'default_domain': self.handle_default_domain,
'group': self.handle_group,
'attr': self.handle_attr,
}
self.__setup_hosts()
self.__setup_uinst()
self.__setup_woinst()
self.__setup_toinst()
2017-07-18 17:33:32 +04:00
self.__setup_query()
def parse(self, predicates):
for p in predicates:
cmd = p[0]
args = p[1:]
2017-07-18 17:33:32 +04:00
if cmd in self.commands: self.commands[cmd](*p)
else: raise ValueError("%s: unknown command" % cmd)
return self
def register_command(self, name, method):
self.commands[name] = method
############################################################################
def reset_group(self, otype):
2017-06-18 11:57:38 +04:00
self.groups[otype]['current'] = set()
def reset_group_maybe(self, otype):
if self.groups[otype]['type'] in ('defaults', 'once'):
self.reset_group(otype)
self.groups[otype]['type'] = 'once'
def add_group(self, otype, id):
2017-06-18 11:57:38 +04:00
self.groups[otype]['current'].add(id)
2017-07-18 17:33:32 +04:00
def handle_group(self, cmd, *args):
"""group otype [gtype]
gtype peut valoir:
- defaults: revenir à l'état initial, permettant de spécifier les
attributs et liens pour tous les hôtes définis à partir de cette ligne
2017-07-18 17:33:32 +04:00
- once: un nouveau groupe est défini à chaque nouvelle définition
d'objet. en d'autres termes, ne font partie du groupe que les objets
faisant partie d'une même définition. c'est la valeur par défaut
- until: définir un groupe qui va jusqu'à la prochaine définition de
groupe ou de lien.
"""
otype = args[0:1] and args[0] or 'host'
2017-06-15 02:24:17 +04:00
if otype not in self.groups:
raise ValueError('%s: invalid object type' % otype)
2017-07-18 17:33:32 +04:00
gtype = args[1:2] and args[1] or 'once'
self.groups[otype]['type'] = gtype
if gtype == 'defaults':
2017-06-18 11:57:38 +04:00
self.groups[otype]['current'] = set([None])
elif gtype in ('once', 'until'):
2017-06-18 11:57:38 +04:00
self.groups[otype]['current'] = set()
2017-06-15 02:24:17 +04:00
else:
raise ValueError('%s: invalid group type' % gtype)
self.attr_otype = otype
############################################################################
2017-07-18 17:33:32 +04:00
def handle_attr(self, cmd, *args):
otype = self.attr_otype
assert otype is not None, "attr_otype should not be None"
for nv in args:
name, value, type, method = split_namev(nv)
if value is None:
2017-06-15 02:24:17 +04:00
method = 'set'
value = '1'
elif type == 'path':
value = path.expanduser(value)
for id in self.groups[otype]['current']:
self.db.get_object(otype, id).modify_attr(name, value, method)
############################################################################
2017-07-18 17:33:32 +04:00
def handle_default_profile(self, cmd, *args):
if not args or not args[0]: profile = None
else: profile = args[0]
self.db.default_profile = profile
############################################################################
2017-07-18 17:33:32 +04:00
def handle_default_domain(self, cmd, *args):
if not args or not args[0]: domain = None
else: domain = args[0]
self.db.default_domain = domain
############################################################################
def __setup_hosts(self):
self.db.register_object('host', HostObject)
self.db.get_object('host', None)
self.groups['host'] = {}
2017-07-18 17:33:32 +04:00
self.handle_group('group', 'host', 'defaults')
self.register_command('host', self.handle_host)
def __fix_host(self, host):
if host.endswith('.'):
host = host[:-1]
elif '.' not in host and self.db.has_default_domain():
host = '%s.%s' % (host, self.db.default_domain)
return host
def handle_host(self, otype, *args):
"""host [id=]host,...
host -b [id=]basedir
host -d [id=]dirspec
"""
AP = ArgumentParser()
AP.add_argument('-b', '--basedir', action='append', dest='basedirs')
AP.add_argument('-d', '--dirspec', action='append', dest='dirspecs')
AP.add_argument('nvss', nargs=REMAINDER)
o = AP.parse_args(args)
# construire la liste des hôtes à traiter
nvss = []
if o.basedirs is not None:
for pb in o.basedirs:
p, b = split_nvalue(pb)
if p is None: prefix = ''
else: prefix = p + '='
basedir = path.expanduser(b)
nvss.extend([prefix + path.abspath(path.join(basedir, name))
for name in os.listdir(basedir)
if path.isdir(path.join(basedir, name))])
if o.dirspecs is not None:
for pd in o.dirspecs:
p, d = split_nvalue(pd)
if p is None: prefix = ''
else: prefix = p + '='
dirspec = path.expanduser(d)
nvss.extend([prefix + path.abspath(dir)
for dir in glob(dirspec)
if path.isdir(dir)])
nvss.extend(o.nvss)
# préparer la mise à jour du groupe courant
self.reset_group_maybe(otype)
self.attr_otype = otype
defaulto = self.db.get_object(otype, None)
# traiter les hôtes
for nvs in nvss:
name, values = split_nlist(nvs)
2017-06-18 08:53:34 +04:00
for value in values:
2017-06-15 02:24:17 +04:00
dir = None
2017-06-18 08:53:34 +04:00
if value:
if '/' in value:
dir, value = path.split(path.abspath(path.expanduser(value)))
value = self.__fix_host(value)
if name:
o = self.db.get_object(otype, name)
2017-06-18 08:53:34 +04:00
if value: o.add_value(value)
2017-06-16 00:36:36 +04:00
o.merge_attrs(defaulto)
if dir is not None: o.set_attr('dir', dir)
self.add_group(otype, name)
else:
2017-06-18 08:53:34 +04:00
id = HostObject.genid(value)
o = self.db.get_object(otype, id)
2017-06-18 08:53:34 +04:00
o.add_value(value)
2017-06-16 00:36:36 +04:00
o.merge_attrs(defaulto)
if dir is not None: o.set_attr('dir', dir)
self.add_group(otype, id)
ib = HostObject.genib(id)
o = self.db.get_object(otype, ib)
2017-06-18 08:53:34 +04:00
o.add_value(value)
2017-06-16 00:36:36 +04:00
o.merge_attrs(defaulto)
if dir is not None: o.set_attr('dir', dir)
self.add_group(otype, ib)
############################################################################
def __setup_uinst(self):
self.db.register_object('module', ModuleObject)
2017-06-16 00:36:36 +04:00
self.db.register_link('uinst', UinstLink)
self.db.register_link('ruinst', RuinstLink)
self.db.get_object('module', None)
self.groups['module'] = {}
2017-07-18 17:33:32 +04:00
self.handle_group('group', 'module', 'defaults')
self.register_command('module', self.handle_module)
self.register_command('uinst', self.handle_xuinst)
self.register_command('ruinst', self.handle_xuinst)
def __fix_module(self, module):
return module
def handle_module(self, otype, *args):
"""module [id=]path/to/module
module -b [id=]basedir
module -d [id=]dirspec
"""
AP = ArgumentParser()
AP.add_argument('-b', '--basedir', action='append', dest='basedirs')
AP.add_argument('-d', '--dirspec', action='append', dest='dirspecs')
AP.add_argument('nvss', nargs=REMAINDER)
o = AP.parse_args(args)
# construire la liste des modules à traiter
nvss = []
if o.basedirs is not None:
for pb in o.basedirs:
p, b = split_nvalue(pb)
if p is None: prefix = ''
else: prefix = p + '='
basedir = path.expanduser(b)
nvss.extend([prefix + path.abspath(path.join(basedir, name))
for name in os.listdir(basedir)
if path.isdir(path.join(basedir, name))])
if o.dirspecs is not None:
for pd in o.dirspecs:
p, d = split_nvalue(pd)
if p is None: prefix = ''
else: prefix = p + '='
dirspec = path.expanduser(d)
nvss.extend([prefix + path.abspath(dir)
for dir in glob(dirspec)
if path.isdir(dir)])
nvss.extend(o.nvss)
# préparer la mise à jour du groupe courant
self.reset_group_maybe(otype)
self.attr_otype = otype
defaulto = self.db.get_object(otype, None)
# traiter les modules
for nvs in nvss:
name, values = split_nlist(nvs)
2017-06-18 08:53:34 +04:00
for value in values:
dir = None
2017-06-18 08:53:34 +04:00
valuep = None
if value:
if '/' in value:
dir, value = path.split(path.abspath(path.expanduser(value)))
value = self.__fix_module(value)
if dir is not None:
2017-06-18 08:53:34 +04:00
valuep = path.join(dir, value)
if name:
o = self.db.get_object(otype, name)
2017-06-18 08:53:34 +04:00
if value: o.add_value(value)
2017-06-16 00:36:36 +04:00
o.merge_attrs(defaulto)
if dir is not None: o.set_attr('dir', dir)
2017-06-18 08:53:34 +04:00
if valuep is not None: o.set_attr('path', valuep)
self.add_group(otype, name)
else:
2017-06-18 08:53:34 +04:00
id = ModuleObject.genid(value)
o = self.db.get_object(otype, id)
2017-06-18 08:53:34 +04:00
if value: o.add_value(value)
2017-06-16 00:36:36 +04:00
o.merge_attrs(defaulto)
if dir is not None: o.set_attr('dir', dir)
2017-06-18 08:53:34 +04:00
if valuep is not None: o.set_attr('path', valuep)
self.add_group(otype, id)
2017-06-16 00:36:36 +04:00
def handle_xuinst(self, ltype, *args):
"""uinst -p profile attrs*
ruinst -p profile attrs*
2017-07-18 17:33:32 +04:00
usage de l'option -p:
pas d'option -p
définir les liens dans le profil par défaut. équivalent à -p '' s'il
n'y a pas de profil par défaut.
-p ''
définir les liens en ne les associant à aucun profil
-p PROFILE,...
définir les liens dans les profils spécifiés
2017-06-16 00:36:36 +04:00
"""
AP = ArgumentParser()
AP.add_argument('-p', '--profile', action='append', dest='profiles')
AP.add_argument('nvss', nargs=REMAINDER)
o = AP.parse_args(args)
2017-07-18 17:33:32 +04:00
if o.profiles is None: profiles = [self.db.default_profile]
else: profiles = flatten(o.profiles)
if profiles == []: profiles = [None]
2017-06-16 00:36:36 +04:00
for profile in profiles:
# préparer la mise à jour du groupe courant
2017-06-18 11:57:38 +04:00
currentls = self.db.get_links(ltype, profile, fo=self.groups['module']['current'], create=True)
2017-06-16 00:36:36 +04:00
if profile is not None:
2017-06-18 11:57:38 +04:00
globalls = self.db.get_links(ltype, None, fo=None, create=True)
2017-06-16 00:36:36 +04:00
for currentl in currentls:
2017-06-18 11:57:38 +04:00
for globall in globalls:
currentl.merge_attrs(globall)
defaultls = self.db.get_links(ltype, profile, fo=None, create=True)
2017-06-16 00:36:36 +04:00
for currentl in currentls:
2017-06-18 11:57:38 +04:00
for defaultl in defaultls:
currentl.merge_attrs(defaultl)
defaultl = defaultls[0]
2017-06-16 00:36:36 +04:00
# traiter les liens
2017-06-18 21:59:38 +04:00
for nvs in o.nvss:
2017-06-18 11:57:38 +04:00
name, value, type, method = split_namev(nvs)
2017-06-16 00:36:36 +04:00
if name == defaultl.ttype:
# définir des destinations du lien
2017-06-18 11:57:38 +04:00
if value is not None:
tos = split_nlist(nvs)[1]
for to in tos:
for currentl in currentls:
currentl.add_to(to)
2017-06-16 00:36:36 +04:00
else:
# définir un attribut du lien
2017-06-18 11:57:38 +04:00
#name, value, type, method = split_namev(nv)
2017-06-16 00:36:36 +04:00
if value is None:
method = 'set'
value = '1'
elif type == 'path':
value = path.expanduser(value)
2017-06-18 11:57:38 +04:00
for currentl in currentls:
currentl.modify_attr(name, value, method)
############################################################################
def __setup_woinst(self):
self.db.register_object('wobundle', WobundleObject)
2017-06-18 21:59:38 +04:00
self.db.register_link('rwoinst-bundle', RwoinstBundleLink)
self.db.register_link('rwoinst-webres', RwoinstWebresLink)
self.db.get_object('wobundle', None)
self.groups['wobundle'] = {}
2017-07-18 17:33:32 +04:00
self.handle_group('group', 'wobundle', 'defaults')
self.register_command('wobundle', self.handle_wobundle)
self.register_command('rwoinst', self.handle_rwoinst)
def __fix_wobundle(self, wobundle):
if path.splitext(wobundle)[1] not in ('.woa', '.framework'):
wobundle = wobundle + '.woa'
return wobundle
def handle_wobundle(self, otype, *args):
"""wobundle [id=]path/to/wobundle
wobundle -b [id=]basedir
wobundle -d [id=]dirspec
"""
AP = ArgumentParser()
AP.add_argument('-b', '--basedir', action='append', dest='basedirs')
AP.add_argument('-d', '--dirspec', action='append', dest='dirspecs')
AP.add_argument('nvss', nargs=REMAINDER)
o = AP.parse_args(args)
# construire la liste des wobundles à traiter
nvss = []
if o.basedirs is not None:
for pb in o.basedirs:
p, b = split_nvalue(pb)
if p is None: prefix = ''
else: prefix = p + '='
basedir = path.expanduser(b)
nvss.extend([prefix + path.abspath(path.join(basedir, name))
for name in os.listdir(basedir)
if path.isdir(path.join(basedir, name)) and path.splitext(name)[1] in ('.woa', '.framework')])
if o.dirspecs is not None:
for pd in o.dirspecs:
p, d = split_nvalue(pd)
if p is None: prefix = ''
else: prefix = p + '='
dirspec = path.expanduser(d)
nvss.extend([prefix + path.abspath(dir)
for dir in glob(dirspec)
if path.isdir(dir) and path.splitext(dir)[1] in ('.woa', '.framework')])
nvss.extend(o.nvss)
# préparer la mise à jour du groupe courant
self.reset_group_maybe(otype)
self.attr_otype = otype
defaulto = self.db.get_object(otype, None)
# traiter les wobundles
for nvs in nvss:
name, values = split_nlist(nvs)
2017-06-18 08:53:34 +04:00
for value in values:
dir = None
2017-06-18 08:53:34 +04:00
valuep = None
if value:
if '/' in value:
dir, value = path.split(path.abspath(path.expanduser(value)))
value = self.__fix_wobundle(value)
if dir is not None:
2017-06-18 08:53:34 +04:00
valuep = path.join(dir, value)
if name:
o = self.db.get_object(otype, name)
2017-06-18 08:53:34 +04:00
if value: o.add_value(value)
2017-06-16 00:36:36 +04:00
o.merge_attrs(defaulto)
if dir is not None: o.set_attr('dir', dir)
2017-06-18 08:53:34 +04:00
if valuep is not None: o.set_attr('path', valuep)
self.add_group(otype, name)
else:
2017-06-18 08:53:34 +04:00
id = WobundleObject.genid(value)
o = self.db.get_object(otype, id)
2017-06-18 08:53:34 +04:00
if value: o.add_value(value)
2017-06-16 00:36:36 +04:00
o.merge_attrs(defaulto)
if dir is not None: o.set_attr('dir', dir)
2017-06-18 08:53:34 +04:00
if valuep is not None: o.set_attr('path', valuep)
self.add_group(otype, id)
2017-06-18 21:59:38 +04:00
def handle_rwoinst(self, ltype, *args):
"""rwoinst bundle -p profile attrs*
rwoinst webres -p profile attrs*
"""
AP = ArgumentParser()
AP.add_argument('-p', '--profile', action='append', dest='profiles')
AP.add_argument('stype', nargs=1)
AP.add_argument('nvss', nargs=REMAINDER)
o = AP.parse_args(args)
2017-07-18 17:33:32 +04:00
if o.profiles is None: profiles = [self.db.default_profile]
else: profiles = flatten(o.profiles)
if profiles == []: profiles = [None]
2017-06-18 21:59:38 +04:00
ltype = "%s-%s" % (ltype, o.stype[0])
for profile in profiles:
# préparer la mise à jour du groupe courant
currentls = self.db.get_links(ltype, profile, fo=self.groups['wobundle']['current'], create=True)
if profile is not None:
globalls = self.db.get_links(ltype, None, fo=None, create=True)
for currentl in currentls:
for globall in globalls:
currentl.merge_attrs(globall)
defaultls = self.db.get_links(ltype, profile, fo=None, create=True)
for currentl in currentls:
for defaultl in defaultls:
currentl.merge_attrs(defaultl)
defaultl = defaultls[0]
# traiter les liens
for nvs in o.nvss:
name, value, type, method = split_namev(nvs)
if name == defaultl.ttype:
# définir des destinations du lien
if value is not None:
tos = split_nlist(nvs)[1]
for to in tos:
for currentl in currentls:
currentl.add_to(to)
else:
# définir un attribut du lien
#name, value, type, method = split_namev(nv)
if value is None:
method = 'set'
value = '1'
elif type == 'path':
value = path.expanduser(value)
for currentl in currentls:
currentl.modify_attr(name, value, method)
############################################################################
def __setup_toinst(self):
self.db.register_object('webapp', WebappObject)
2017-06-18 21:59:38 +04:00
self.db.register_link('rtoinst', RtoinstLink)
self.db.get_object('webapp', None)
self.groups['webapp'] = {}
2017-07-18 17:33:32 +04:00
self.handle_group('group', 'webapp', 'defaults')
self.register_command('webapp', self.handle_webapp)
self.register_command('rtoinst', self.handle_rtoinst)
def __fix_webapp(self, webapp):
return webapp
def handle_webapp(self, otype, *args):
"""webapp [id=]path/to/webapp
webapp -b [id=]basedir
webapp -d [id=]dirspec
"""
AP = ArgumentParser()
AP.add_argument('-b', '--basedir', action='append', dest='basedirs')
AP.add_argument('-d', '--dirspec', action='append', dest='dirspecs')
AP.add_argument('nvss', nargs=REMAINDER)
o = AP.parse_args(args)
# construire la liste des webapps à traiter
nvss = []
if o.basedirs is not None:
for pb in o.basedirs:
p, b = split_nvalue(pb)
if p is None: prefix = ''
else: prefix = p + '='
basedir = path.expanduser(b)
nvss.extend([prefix + path.abspath(path.join(basedir, name))
for name in os.listdir(basedir)
if path.isdir(path.join(basedir, name))])
if o.dirspecs is not None:
for pd in o.dirspecs:
p, d = split_nvalue(pd)
if p is None: prefix = ''
else: prefix = p + '='
dirspec = path.expanduser(d)
nvss.extend([prefix + path.abspath(dir)
for dir in glob(dirspec)
if path.isdir(dir)])
nvss.extend(o.nvss)
# préparer la mise à jour du groupe courant
self.reset_group_maybe(otype)
self.attr_otype = otype
defaulto = self.db.get_object(otype, None)
# traiter les webapps
for nvs in nvss:
name, values = split_nlist(nvs)
2017-06-18 08:53:34 +04:00
for value in values:
dir = None
2017-06-18 08:53:34 +04:00
valuep = None
if value:
if '/' in value:
dir, value = path.split(path.abspath(path.expanduser(value)))
value = self.__fix_webapp(value)
if dir is not None:
2017-06-18 08:53:34 +04:00
valuep = path.join(dir, value)
if name:
o = self.db.get_object(otype, name)
2017-06-18 08:53:34 +04:00
if value: o.add_value(value)
2017-06-16 00:36:36 +04:00
o.merge_attrs(defaulto)
if dir is not None: o.set_attr('dir', dir)
2017-06-18 08:53:34 +04:00
if valuep is not None: o.set_attr('path', valuep)
self.add_group(otype, name)
else:
2017-06-18 08:53:34 +04:00
id = WebappObject.genid(value)
o = self.db.get_object(otype, id)
2017-06-18 08:53:34 +04:00
if value: o.add_value(value)
2017-06-16 00:36:36 +04:00
o.merge_attrs(defaulto)
if dir is not None: o.set_attr('dir', dir)
2017-06-18 08:53:34 +04:00
if valuep is not None: o.set_attr('path', valuep)
self.add_group(otype, id)
2017-06-18 21:59:38 +04:00
def handle_rtoinst(self, ltype, *args):
"""rtoinst -p profile attrs*
"""
AP = ArgumentParser()
AP.add_argument('-p', '--profile', action='append', dest='profiles')
AP.add_argument('nvss', nargs=REMAINDER)
o = AP.parse_args(args)
2017-07-18 17:33:32 +04:00
if o.profiles is None: profiles = [self.db.default_profile]
else: profiles = flatten(o.profiles)
if profiles == []: profiles = [None]
2017-06-18 21:59:38 +04:00
for profile in profiles:
# préparer la mise à jour du groupe courant
currentls = self.db.get_links(ltype, profile, fo=self.groups['webapp']['current'], create=True)
if profile is not None:
globalls = self.db.get_links(ltype, None, fo=None, create=True)
for currentl in currentls:
for globall in globalls:
currentl.merge_attrs(globall)
defaultls = self.db.get_links(ltype, profile, fo=None, create=True)
for currentl in currentls:
for defaultl in defaultls:
currentl.merge_attrs(defaultl)
defaultl = defaultls[0]
# traiter les liens
for nvs in o.nvss:
name, value, type, method = split_namev(nvs)
if name == defaultl.ttype:
# définir des destinations du lien
if value is not None:
tos = split_nlist(nvs)[1]
for to in tos:
for currentl in currentls:
currentl.add_to(to)
else:
# définir un attribut du lien
#name, value, type, method = split_namev(nv)
if value is None:
method = 'set'
value = '1'
elif type == 'path':
value = path.expanduser(value)
for currentl in currentls:
currentl.modify_attr(name, value, method)
2017-07-18 17:33:32 +04:00
############################################################################
def __setup_query(self):
self.register_command('query', self.handle_query)
def handle_query(self, cmd, *args):
"""query config
"""
print "WARNING: query not implemented" #XXX
################################################################################
# Programme principal
def run_qdd(o):
# fichier de configuration
confname = o.confname or 'deploy'
if '/' in confname:
conf = path.abspath(confname)
elif path.exists('%s.conf' % confname):
conf = '%s.conf' % confname
else:
conf = confname
if not conf.endswith('.conf'): conf = '%s.conf' % conf
user_conf = path.join(path.expanduser(USER_CONFDIR), conf)
system_conf = path.join(SYSTEM_CONFDIR, conf)
if path.exists(user_conf): conf = user_conf
elif path.exists(system_conf): conf = system_conf
lexer = Lexer(conf)
db = Database()
if o.action == 'nop':
pass
elif o.action == 'dump': #XXX
predicates = lexer.get_predicates()
parser = Parser(db).parse(predicates)
print "=== predicates"
for p in predicates:
print ' '.join(map(repr, p))
2017-06-18 21:59:38 +04:00
otypes = db.known_otypes[:]
otypes.remove('host')
otypes.insert(0, 'host')
for otype in otypes:
objects = db.get_objects(otype)
if objects:
print "=== %s" % otype
for o in objects:
o.dump()
2017-06-18 21:59:38 +04:00
for ltype in db.known_ltypes:
links = db.get_links(ltype)
if links:
print "=== %s" % ltype
for link in links:
if link.is_defaults(): continue
link.dump(" ")
elif o.action == 'query':
parser = Parser(db).parse(lexer.get_predicates())
if __name__ == '__main__':
from argparse import ArgumentParser, HelpFormatter
if sys.argv[1:2] == ['--compat']:
# Avec l'argument --compat, désactiver la classe FancyHelpFormatter qui
# se base sur une API non documentée
sys.argv = sys.argv[0:1] + sys.argv[2:]
FancyHelpFormatter = HelpFormatter
else:
class FancyHelpFormatter(HelpFormatter):
"""Comme HelpFormatter, mais ne touche pas aux lignes qui commencent par les
caractères '>>>'. Cela permet de mixer du texte formaté et du texte non
formaté.
"""
def _fill_text(self, text, width, indent):
return ''.join([indent + line for line in text.splitlines(True)])
def _split_lines(self, text, width):
lines = ['']
for line in text.splitlines():
if line.startswith('>>>'):
lines.append(line)
lines.append('')
else:
lines[-1] += '\n' + line
lines = filter(None, lines)
texts = []
for line in lines:
if line.startswith('>>>'):
line = line[3:]
if line: texts.append(line)
else:
texts.extend(super(FancyHelpFormatter, self)._split_lines(line, width))
return texts
AP = ArgumentParser(
usage=u"%(prog)s --query FILTER",
description=__doc__,
formatter_class=FancyHelpFormatter,
)
AP.set_defaults(action='query')
AP.add_argument('-Q', '--query-action', action='store_const', dest='action', const='query',
help=u"Interroger la base de données. C'est l'option par défaut")
AP.add_argument('-N', '--nop-action', action='store_const', dest='action', const='nop',
help=u"Ne rien faire. Utile pour vérifier si le fichier ne contient pas d'erreur de syntaxe.")
AP.add_argument('-P', '--dump-action', action='store_const', dest='action', const='dump',
help=u"Afficher le contenu de la base de données.")
AP.add_argument('-c', '--config', dest='confname',
help=u"Spécifier le nom de la configuration à utiliser. Par défaut, utiliser le nom générique deploy.")
AP.add_argument('-q', '--query-type', dest='query_type',
help=u"")
AP.add_argument('-j', '--object-type', dest='object_type',
help=u"")
AP.add_argument('-t', '--link-type', dest='link_type',
help=u"")
AP.add_argument('-p', '--profile', dest='profile',
help=u"Spécifier le profil de déploiement")
AP.add_argument('-F', '--format', dest='format',
help=u"Spécifier le format pour la sortie. La valeur par défaut est shell.")
AP.add_argument('-v', '--include-vars', dest='vars', metavar='VARS...',
help=u"Spécifier les variables qui doivent être affichées. Par défaut, toutes les variables sont affichées.")
AP.add_argument('-o', '--object-vars', action='store_true', dest='object_vars',
help=u"Afficher les variables associées aux objets.")
AP.add_argument('-d', '--dest-vars', action='store_true', dest='dest_vars',
help=u"Afficher les variables associées aux hôtes destination.")
AP.add_argument('-l', '--link-vars', action='store_true', dest='link_vars',
help=u"Afficher les variables associées aux liens.")
AP.add_argument('--of', '--object-func', dest='object_func', metavar='FUNC',
help=u"Avec le format shell, spécifier le nom d'une fonction à afficher après les variables associées aux objets.")
AP.add_argument('--df', '--dest-func', dest='dest_func', metavar='FUNC',
help=u"Avec le format shell, spécifier le nom d'une fonction à afficher après les variables associées aux hôtes destination.")
AP.add_argument('--lf', '--link-func', dest='link_func', metavar='FUNC',
help=u"Avec le format shell, spécifier le nom d'une fonction à afficher après les variables associées aux liens.")
o = AP.parse_args()
run_qdd(o)