un peu de nettoyage
This commit is contained in:
parent
5e0924aeb5
commit
be0ec1c3ab
39
TODO.md
39
TODO.md
|
@ -1,3 +1,42 @@
|
||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
|
* faire la différence entre "pas de profil défini" (=aucun profil) et
|
||||||
|
"définition par défaut pour les profils" (si pas de définition plus précise
|
||||||
|
pour un profil, prendre celle-là)
|
||||||
|
|
||||||
|
* mettre à jour l'algorithme pour la prise en compte du type de groupe.
|
||||||
|
peut-être garder en mémoire la dernière commande ou le dernier type de
|
||||||
|
commande.
|
||||||
|
|
||||||
|
* il faut distinguer: définitions par défaut globales, définitions par défaut
|
||||||
|
pour le groupe, définition locale
|
||||||
|
~~~
|
||||||
|
host dh=dest-host.univ.run
|
||||||
|
|
||||||
|
group module defaults
|
||||||
|
attr odef=value
|
||||||
|
ruinst ldef=value
|
||||||
|
ruinst -p PROFILE pldef=value
|
||||||
|
|
||||||
|
# group #1
|
||||||
|
group module once
|
||||||
|
module MyModule
|
||||||
|
attr ovar=value
|
||||||
|
ruinst ldef2=value
|
||||||
|
ruinst -p PROFILE pldef2=value
|
||||||
|
ruinst host=dh lvar=value
|
||||||
|
|
||||||
|
# group #2
|
||||||
|
module OtherModule
|
||||||
|
ruinst host=dh
|
||||||
|
~~~
|
||||||
|
dans l'exemple ci-dessus:
|
||||||
|
* odef est un attribut global à tous les modules
|
||||||
|
* ovar est spécifique à MyModule
|
||||||
|
* ldef est un attribut global à tous les liens
|
||||||
|
* pldef est un attribut global à tous les liens dans le profil PROFILE
|
||||||
|
* ldef2 est attribut global à tous les liens du groupe #1
|
||||||
|
* pldef2 est un attribut global à tous les liens du groupe #1 dans le profil PROFILE
|
||||||
|
* lvar est spécifique au lien MyModule --> dh
|
||||||
|
|
||||||
-*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary
|
-*- coding: utf-8 mode: markdown -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8:noeol:binary
|
|
@ -9,20 +9,44 @@ from glob import glob
|
||||||
USER_CONFDIR = '~/etc/deploy'
|
USER_CONFDIR = '~/etc/deploy'
|
||||||
SYSTEM_CONFDIR = '/var/local/deploy'
|
SYSTEM_CONFDIR = '/var/local/deploy'
|
||||||
|
|
||||||
#XXX faire la différence entre "pas de profil défini" (=aucun profil) et
|
|
||||||
# "définition par défaut pour les profils" (si pas de définition plus précise
|
|
||||||
# pour un profil, prendre celle-là)
|
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# Diverses fonctions
|
# Diverses fonctions
|
||||||
|
|
||||||
def isseq(t):
|
def isseq(t):
|
||||||
return isinstance(t, list) or isinstance(t, tuple) or isinstance(t, set)
|
return isinstance(t, list) or isinstance(t, tuple) or isinstance(t, set)
|
||||||
|
|
||||||
|
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
|
# Base de données
|
||||||
|
|
||||||
class GenericObject(object):
|
class GenericObject(object):
|
||||||
|
"""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
|
_id = None
|
||||||
_values = None
|
_values = None
|
||||||
_attrs = None
|
_attrs = None
|
||||||
|
@ -43,26 +67,45 @@ class GenericObject(object):
|
||||||
def get_attrs(self): return self._attrs
|
def get_attrs(self): return self._attrs
|
||||||
attrs = property(get_attrs)
|
attrs = property(get_attrs)
|
||||||
|
|
||||||
def get_attr(self, name): return self._attrs.get(name, None)
|
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):
|
def set_attr(self, name, value):
|
||||||
|
"""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 name not in self._attrs: self._attrs[name] = []
|
||||||
if value not in self._attrs[name]: self._attrs[name].append(value)
|
if value not in self._attrs[name]: self._attrs[name].append(value)
|
||||||
def reset_attr(self, name, value=None):
|
def reset_attr(self, name, value=None):
|
||||||
|
"""Recommencer à zéro la valeur d'un attribut
|
||||||
|
"""
|
||||||
if name in self._attrs: del self._attrs[name]
|
if name in self._attrs: del self._attrs[name]
|
||||||
if value is not None: self.set_attr(name, value)
|
if value is not None: self.set_attr(name, value)
|
||||||
def add_attr(self, name, value):
|
def add_attr(self, name, value):
|
||||||
|
"""Ajouter une valeur à la liste des valeurs d'un attribut
|
||||||
|
"""
|
||||||
if name not in self._attrs: self._attrs[name] = []
|
if name not in self._attrs: self._attrs[name] = []
|
||||||
self._attrs[name].append(value)
|
self._attrs[name].append(value)
|
||||||
def del_attr(self, name, value):
|
def del_attr(self, name, value):
|
||||||
|
"""Supprimer une valeur d'un attribut
|
||||||
|
"""
|
||||||
if name not in self._attrs: return
|
if name not in self._attrs: return
|
||||||
self._attrs[name].remove(value)
|
self._attrs[name].remove(value)
|
||||||
if not self._attrs[name]: del self._attrs[name]
|
if not self._attrs[name]: del self._attrs[name]
|
||||||
def modify_attr(self, name, value, method='set'):
|
def modify_attr(self, name, value, method='set'):
|
||||||
|
"""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)
|
if method == 'set': self.set_attr(name, value)
|
||||||
elif method == 'reset': self.reset_attr(name, value)
|
elif method == 'reset': self.reset_attr(name, value)
|
||||||
elif method == 'add': self.add_attr(name, value)
|
elif method == 'add': self.add_attr(name, value)
|
||||||
elif method == 'del': self.del_attr(name, value)
|
elif method == 'del': self.del_attr(name, value)
|
||||||
def merge_attrs(self, other, method='set'):
|
def merge_attrs(self, other, method='set'):
|
||||||
|
"""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 name, values in other.attrs.items():
|
||||||
for value in values:
|
for value in values:
|
||||||
self.modify_attr(name, value, method)
|
self.modify_attr(name, value, method)
|
||||||
|
@ -80,20 +123,30 @@ class GenericObject(object):
|
||||||
else:
|
else:
|
||||||
print "%s %s=(%s)" % (indent, name, ', '.join(map(repr, values)))
|
print "%s %s=(%s)" % (indent, name, ', '.join(map(repr, values)))
|
||||||
def dump(self, indent=''):
|
def dump(self, indent=''):
|
||||||
|
"""Afficher l'identifiant, les valeurs et les attributs de cet objet
|
||||||
|
"""
|
||||||
self._dump_id(indent)
|
self._dump_id(indent)
|
||||||
|
self._dump_values(indent, 'values')
|
||||||
self._dump_attrs(indent)
|
self._dump_attrs(indent)
|
||||||
|
|
||||||
class HostObject(GenericObject):
|
class HostObject(GenericObject):
|
||||||
|
"""Un hôte
|
||||||
|
"""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def genid(host):
|
def genid(host):
|
||||||
"MY-Host42.self --> my_host42"
|
"""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'\..*', '', host)
|
||||||
host = re.sub(r'[^a-zA-Z0-9]', '_', host)
|
host = re.sub(r'[^a-zA-Z0-9]', '_', host)
|
||||||
host = host.lower()
|
host = host.lower()
|
||||||
return host
|
return host
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def genib(id):
|
def genib(id):
|
||||||
"my_host42 --> my_host"
|
"""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
|
if re.match(r'\d+$', id): return id
|
||||||
ib = re.sub(r'^(.*?)\d+$', r'\1', id)
|
ib = re.sub(r'^(.*?)\d+$', r'\1', id)
|
||||||
return ib
|
return ib
|
||||||
|
@ -104,9 +157,14 @@ class HostObject(GenericObject):
|
||||||
self._dump_attrs(indent)
|
self._dump_attrs(indent)
|
||||||
|
|
||||||
class ModuleObject(GenericObject):
|
class ModuleObject(GenericObject):
|
||||||
|
"""Un module, uinst-allable
|
||||||
|
"""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def genid(module):
|
def genid(module):
|
||||||
"MY-Module --> MY_Module"
|
"""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)
|
module = re.sub(r'[^a-zA-Z0-9]', '_', module)
|
||||||
return module
|
return module
|
||||||
|
|
||||||
|
@ -116,9 +174,14 @@ class ModuleObject(GenericObject):
|
||||||
self._dump_attrs(indent)
|
self._dump_attrs(indent)
|
||||||
|
|
||||||
class WobundleObject(GenericObject):
|
class WobundleObject(GenericObject):
|
||||||
|
"""Un bundle Webobjects, woinst-allable
|
||||||
|
"""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def genid(wobundle):
|
def genid(wobundle):
|
||||||
"MY-App.woa --> MY_App"
|
"""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'\.(woa|framework)$', '', wobundle)
|
||||||
wobundle = re.sub(r'[^a-zA-Z0-9]', '_', wobundle)
|
wobundle = re.sub(r'[^a-zA-Z0-9]', '_', wobundle)
|
||||||
return wobundle
|
return wobundle
|
||||||
|
@ -129,9 +192,14 @@ class WobundleObject(GenericObject):
|
||||||
self._dump_attrs(indent)
|
self._dump_attrs(indent)
|
||||||
|
|
||||||
class WebappObject(GenericObject):
|
class WebappObject(GenericObject):
|
||||||
|
"""Une webapp, toinst-allable
|
||||||
|
"""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def genid(webapp):
|
def genid(webapp):
|
||||||
"MY-Webapp --> MY_Webapp"
|
"""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)
|
webapp = re.sub(r'[^a-zA-Z0-9]', '_', webapp)
|
||||||
return webapp
|
return webapp
|
||||||
|
|
||||||
|
@ -141,6 +209,14 @@ class WebappObject(GenericObject):
|
||||||
self._dump_attrs(indent)
|
self._dump_attrs(indent)
|
||||||
|
|
||||||
class GenericLink(object):
|
class GenericLink(object):
|
||||||
|
"""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).
|
||||||
|
"""
|
||||||
|
|
||||||
_profile = None
|
_profile = None
|
||||||
_ftype = None
|
_ftype = None
|
||||||
_fos = None
|
_fos = None
|
||||||
|
@ -156,6 +232,9 @@ class GenericLink(object):
|
||||||
self._attrs = {}
|
self._attrs = {}
|
||||||
|
|
||||||
def is_defaults(self):
|
def is_defaults(self):
|
||||||
|
"""Cet objet contient-il les attributs par défaut pour les liens provenant du
|
||||||
|
type d'objet ftype
|
||||||
|
"""
|
||||||
fos = list(self._fos)
|
fos = list(self._fos)
|
||||||
return len(fos) == 1 and fos[0] is None
|
return len(fos) == 1 and fos[0] is None
|
||||||
|
|
||||||
|
@ -180,46 +259,89 @@ class GenericLink(object):
|
||||||
def get_attrs(self): return self._attrs
|
def get_attrs(self): return self._attrs
|
||||||
attrs = property(get_attrs)
|
attrs = property(get_attrs)
|
||||||
|
|
||||||
def get_attr(self, name): return self._attrs.get(name, None)
|
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):
|
def set_attr(self, name, value):
|
||||||
|
"""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 name not in self._attrs: self._attrs[name] = []
|
||||||
if value not in self._attrs[name]: self._attrs[name].append(value)
|
if value not in self._attrs[name]: self._attrs[name].append(value)
|
||||||
def reset_attr(self, name, value=None):
|
def reset_attr(self, name, value=None):
|
||||||
|
"""Recommencer à zéro la valeur d'un attribut
|
||||||
|
"""
|
||||||
if name in self._attrs: del self._attrs[name]
|
if name in self._attrs: del self._attrs[name]
|
||||||
if value is not None: self.set_attr(name, value)
|
if value is not None: self.set_attr(name, value)
|
||||||
def add_attr(self, name, value):
|
def add_attr(self, name, value):
|
||||||
|
"""Ajouter une valeur à la liste des valeurs d'un attribut
|
||||||
|
"""
|
||||||
if name not in self._attrs: self._attrs[name] = []
|
if name not in self._attrs: self._attrs[name] = []
|
||||||
self._attrs[name].append(value)
|
self._attrs[name].append(value)
|
||||||
def del_attr(self, name, value):
|
def del_attr(self, name, value):
|
||||||
|
"""Supprimer une valeur d'un attribut
|
||||||
|
"""
|
||||||
if name not in self._attrs: return
|
if name not in self._attrs: return
|
||||||
self._attrs[name].remove(value)
|
self._attrs[name].remove(value)
|
||||||
if not self._attrs[name]: del self._attrs[name]
|
if not self._attrs[name]: del self._attrs[name]
|
||||||
def modify_attr(self, name, value, method='set'):
|
def modify_attr(self, name, value, method='set'):
|
||||||
|
"""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)
|
if method == 'set': self.set_attr(name, value)
|
||||||
elif method == 'reset': self.reset_attr(name, value)
|
elif method == 'reset': self.reset_attr(name, value)
|
||||||
elif method == 'add': self.add_attr(name, value)
|
elif method == 'add': self.add_attr(name, value)
|
||||||
elif method == 'del': self.del_attr(name, value)
|
elif method == 'del': self.del_attr(name, value)
|
||||||
def merge_attrs(self, other, method='set'):
|
def merge_attrs(self, other, method='set'):
|
||||||
|
"""Fusionner dans ce lien les attributs d'un autre lien, avec la méthode
|
||||||
|
spécifiée.
|
||||||
|
"""
|
||||||
for name, values in other.attrs.items():
|
for name, values in other.attrs.items():
|
||||||
for value in values:
|
for value in values:
|
||||||
self.modify_attr(name, value, method)
|
self.modify_attr(name, value, method)
|
||||||
|
|
||||||
def match_profiles(self, profiles):
|
def match_profiles(self, profiles):
|
||||||
|
"""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
|
||||||
if not isseq(profiles): profiles = [profiles]
|
if not isseq(profiles): profiles = [profiles]
|
||||||
|
if profiles == []: return self._profile is None
|
||||||
for profile in profiles:
|
for profile in profiles:
|
||||||
if profile == self._profile: return True
|
if profile == self._profile: return True
|
||||||
return False
|
return False
|
||||||
def match_fos(self, fos, match='any'):
|
def match_fos(self, fos, match='any'):
|
||||||
|
"""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)
|
||||||
|
"""
|
||||||
if not isseq(fos): fos = [fos]
|
if not isseq(fos): fos = [fos]
|
||||||
for fo in fos:
|
for fo in fos:
|
||||||
if fo in self._fos: return True
|
if fo in self._fos: return True
|
||||||
return False
|
return False
|
||||||
def match_tos(self, tos, match='any'):
|
def match_tos(self, tos, match='any'):
|
||||||
|
"""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)
|
||||||
|
"""
|
||||||
if not isseq(tos): tos = [tos]
|
if not isseq(tos): tos = [tos]
|
||||||
for to in tos:
|
for to in tos:
|
||||||
if to in self._tos: return True
|
if to in self._tos: return True
|
||||||
return False
|
return False
|
||||||
def match_attrs(self, attrs, match='any'):
|
def match_attrs(self, attrs, match='any'):
|
||||||
|
"""Tester si ce lien a un des attributs correspondant aux valeurs spécifiées
|
||||||
|
(avec match=='any' qui est la valeur par défaut)
|
||||||
|
"""
|
||||||
for name, value in attrs.items():
|
for name, value in attrs.items():
|
||||||
values = self._attrs.get(name, None)
|
values = self._attrs.get(name, None)
|
||||||
if values is not None:
|
if values is not None:
|
||||||
|
@ -243,6 +365,9 @@ class GenericLink(object):
|
||||||
else:
|
else:
|
||||||
print "%s %s=(%s)" % (indent, name, ', '.join(map(repr, values)))
|
print "%s %s=(%s)" % (indent, name, ', '.join(map(repr, values)))
|
||||||
def dump(self, indent=''):
|
def dump(self, indent=''):
|
||||||
|
"""Afficher le profil, les objets de départ, les objets d'arrivée, et les
|
||||||
|
attributs de ce lien
|
||||||
|
"""
|
||||||
print "%slink" % indent
|
print "%slink" % indent
|
||||||
self._dump_profile(indent)
|
self._dump_profile(indent)
|
||||||
self._dump_fos(indent)
|
self._dump_fos(indent)
|
||||||
|
@ -250,6 +375,9 @@ class GenericLink(object):
|
||||||
self._dump_attrs(indent)
|
self._dump_attrs(indent)
|
||||||
|
|
||||||
class UinstLink(GenericLink):
|
class UinstLink(GenericLink):
|
||||||
|
"""Un module déployé sur un hôte avec le type uinst:rsync
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(UinstLink, self).__init__('module', 'host')
|
super(UinstLink, self).__init__('module', 'host')
|
||||||
|
|
||||||
|
@ -261,6 +389,9 @@ class UinstLink(GenericLink):
|
||||||
self._dump_attrs(indent)
|
self._dump_attrs(indent)
|
||||||
|
|
||||||
class RuinstLink(GenericLink):
|
class RuinstLink(GenericLink):
|
||||||
|
"""Un module déployé sur un hôte avec ruinst
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(RuinstLink, self).__init__('module', 'host')
|
super(RuinstLink, self).__init__('module', 'host')
|
||||||
|
|
||||||
|
@ -272,6 +403,9 @@ class RuinstLink(GenericLink):
|
||||||
self._dump_attrs(indent)
|
self._dump_attrs(indent)
|
||||||
|
|
||||||
class RwoinstBundleLink(GenericLink):
|
class RwoinstBundleLink(GenericLink):
|
||||||
|
"""Un bundle déployé sur un hôte avec rwoinst
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(RwoinstBundleLink, self).__init__('wobundle', 'host')
|
super(RwoinstBundleLink, self).__init__('wobundle', 'host')
|
||||||
|
|
||||||
|
@ -283,6 +417,9 @@ class RwoinstBundleLink(GenericLink):
|
||||||
self._dump_attrs(indent)
|
self._dump_attrs(indent)
|
||||||
|
|
||||||
class RwoinstWebresLink(GenericLink):
|
class RwoinstWebresLink(GenericLink):
|
||||||
|
"""Les resources web d'un bundle déployées sur un hôte avec rwoinst
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(RwoinstWebresLink, self).__init__('wobundle', 'host')
|
super(RwoinstWebresLink, self).__init__('wobundle', 'host')
|
||||||
|
|
||||||
|
@ -294,6 +431,9 @@ class RwoinstWebresLink(GenericLink):
|
||||||
self._dump_attrs(indent)
|
self._dump_attrs(indent)
|
||||||
|
|
||||||
class RtoinstLink(GenericLink):
|
class RtoinstLink(GenericLink):
|
||||||
|
"""Une webapp déployée sur un hôte avec rtoinst
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(RtoinstLink, self).__init__('webapp', 'host')
|
super(RtoinstLink, self).__init__('webapp', 'host')
|
||||||
|
|
||||||
|
@ -309,6 +449,9 @@ class UNDEF(object):
|
||||||
UNDEF = UNDEF()
|
UNDEF = UNDEF()
|
||||||
|
|
||||||
class Database(object):
|
class Database(object):
|
||||||
|
"""La base de données des objets et des liens
|
||||||
|
"""
|
||||||
|
|
||||||
_default_profile = None
|
_default_profile = None
|
||||||
_default_domain = None
|
_default_domain = None
|
||||||
|
|
||||||
|
@ -325,18 +468,38 @@ class Database(object):
|
||||||
self._links_classes = {}
|
self._links_classes = {}
|
||||||
|
|
||||||
def has_default_profile(self):
|
def has_default_profile(self):
|
||||||
|
"""Vérifier si un profil par défaut a été spécifié
|
||||||
|
"""
|
||||||
return self._default_profile is not None
|
return self._default_profile is not None
|
||||||
def get_default_profile(self):
|
def get_default_profile(self):
|
||||||
|
"""Obtenir le profil par défaut, ou None si aucun profil par défaut n'a été
|
||||||
|
défini
|
||||||
|
"""
|
||||||
return self._default_profile
|
return self._default_profile
|
||||||
def set_default_profile(self, profile):
|
def set_default_profile(self, profile):
|
||||||
|
"""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
|
self._default_profile = profile or None
|
||||||
default_profile = property(get_default_profile, set_default_profile)
|
default_profile = property(get_default_profile, set_default_profile)
|
||||||
|
|
||||||
def has_default_domain(self):
|
def has_default_domain(self):
|
||||||
|
"""Vérifier si un domaine par défaut a été spécifié
|
||||||
|
"""
|
||||||
return self._default_domain is not None
|
return self._default_domain is not None
|
||||||
def get_default_domain(self):
|
def get_default_domain(self):
|
||||||
|
"""Obtenir le domaine par défaut, ou None si aucun domaine par défaut n'a été
|
||||||
|
défini
|
||||||
|
"""
|
||||||
return self._default_domain
|
return self._default_domain
|
||||||
def set_default_domain(self, domain):
|
def set_default_domain(self, domain):
|
||||||
|
"""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:
|
if domain is not None:
|
||||||
#XXX si le domaine a été corrigé, l'indiquer en warning
|
#XXX si le domaine a été corrigé, l'indiquer en warning
|
||||||
if domain.startswith('.'): domain = domain[1:]
|
if domain.startswith('.'): domain = domain[1:]
|
||||||
|
@ -345,26 +508,42 @@ class Database(object):
|
||||||
default_domain = property(get_default_domain, set_default_domain)
|
default_domain = property(get_default_domain, set_default_domain)
|
||||||
|
|
||||||
def register_object(self, otype, oclass=None):
|
def register_object(self, otype, oclass=None):
|
||||||
|
"""Enregistrer un type d'objet et la classe utilisée pour l'instancier.
|
||||||
|
|
||||||
|
Par défaut, le classe utilisé est GenericObject.
|
||||||
|
"""
|
||||||
if not self._objects_ids_otype.has_key(otype):
|
if not self._objects_ids_otype.has_key(otype):
|
||||||
self._objects_ids_otype[otype] = {}
|
self._objects_ids_otype[otype] = {}
|
||||||
if oclass is None: oclass = GenericObject
|
if oclass is None: oclass = GenericObject
|
||||||
self._objects_classes[otype] = oclass
|
self._objects_classes[otype] = oclass
|
||||||
def get_known_otypes(self):
|
def get_known_otypes(self):
|
||||||
|
"""Obtenir la liste des types d'objets connus
|
||||||
|
"""
|
||||||
return self._objects_ids_otype.keys()
|
return self._objects_ids_otype.keys()
|
||||||
known_otypes = property(get_known_otypes)
|
known_otypes = property(get_known_otypes)
|
||||||
def get_objects(self, otype):
|
def get_objects(self, otype):
|
||||||
|
"""Obtenir tous les objets définis du type spécifié ou None si le type d'objets
|
||||||
|
est invalide.
|
||||||
|
"""
|
||||||
objects = self._objects_ids_otype.get(otype, None)
|
objects = self._objects_ids_otype.get(otype, None)
|
||||||
if objects is None: return None
|
if objects is None: return None
|
||||||
return [objects[id] for id in objects if id is not None]
|
return [objects[id] for id in objects if id is not None]
|
||||||
def has_object(self, otype, id):
|
def has_object(self, otype, id):
|
||||||
|
"""Vérifier si l'objet avec l'identifiant spécifié est défini
|
||||||
|
"""
|
||||||
objects = self._objects_ids_otype.get(otype, None)
|
objects = self._objects_ids_otype.get(otype, None)
|
||||||
if objects is None: return False
|
if objects is None: return False
|
||||||
return objects.has_key(id)
|
return objects.has_key(id)
|
||||||
def get_object(self, otype, id):
|
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.
|
||||||
|
"""
|
||||||
objects = self._objects_ids_otype.get(otype, None)
|
objects = self._objects_ids_otype.get(otype, None)
|
||||||
if objects is None: return None
|
if objects is None: return None
|
||||||
object = objects.get(id, None)
|
object = objects.get(id, None)
|
||||||
if object is None:
|
if object is None and create:
|
||||||
object_class = self._objects_classes.get(otype, None)
|
object_class = self._objects_classes.get(otype, None)
|
||||||
if object_class is not None:
|
if object_class is not None:
|
||||||
object = object_class(id)
|
object = object_class(id)
|
||||||
|
@ -372,13 +551,24 @@ class Database(object):
|
||||||
return object
|
return object
|
||||||
|
|
||||||
def register_link(self, ltype, lclass):
|
def register_link(self, ltype, lclass):
|
||||||
|
"""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.
|
||||||
|
"""
|
||||||
if not self._links_ltype.has_key(ltype):
|
if not self._links_ltype.has_key(ltype):
|
||||||
self._links_ltype[ltype] = []
|
self._links_ltype[ltype] = []
|
||||||
self._links_classes[ltype] = lclass
|
self._links_classes[ltype] = lclass
|
||||||
def get_known_ltypes(self):
|
def get_known_ltypes(self):
|
||||||
|
"""Obtenir la liste des types de liens connus
|
||||||
|
"""
|
||||||
return self._links_ltype.keys()
|
return self._links_ltype.keys()
|
||||||
known_ltypes = property(get_known_ltypes)
|
known_ltypes = property(get_known_ltypes)
|
||||||
def get_links(self, ltype, profile=UNDEF, fo=UNDEF, to=UNDEF, attrs=UNDEF, create=False):
|
def get_links(self, ltype, profile=UNDEF, fo=UNDEF, to=UNDEF, attrs=UNDEF, create=False):
|
||||||
|
"""Obtenir les liens correspondant aux critères.
|
||||||
|
|
||||||
|
XXX compléter la doc
|
||||||
|
"""
|
||||||
links = self._links_ltype.get(ltype, None)
|
links = self._links_ltype.get(ltype, None)
|
||||||
if links is None: return None
|
if links is None: return None
|
||||||
|
|
||||||
|
@ -616,22 +806,24 @@ class Parser(object):
|
||||||
if db is None: db = Database()
|
if db is None: db = Database()
|
||||||
self.db = db
|
self.db = db
|
||||||
self.groups = {}
|
self.groups = {}
|
||||||
self.commands = {}
|
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_hosts()
|
||||||
self.__setup_uinst()
|
self.__setup_uinst()
|
||||||
self.__setup_woinst()
|
self.__setup_woinst()
|
||||||
self.__setup_toinst()
|
self.__setup_toinst()
|
||||||
|
self.__setup_query()
|
||||||
|
|
||||||
def parse(self, predicates):
|
def parse(self, predicates):
|
||||||
for p in predicates:
|
for p in predicates:
|
||||||
cmd = p[0]
|
cmd = p[0]
|
||||||
args = p[1:]
|
args = p[1:]
|
||||||
if cmd == 'default_profile': self.handle_default_profile(args)
|
if cmd in self.commands: self.commands[cmd](*p)
|
||||||
elif cmd == 'default_domain': self.handle_default_domain(args)
|
else: raise ValueError("%s: unknown command" % cmd)
|
||||||
elif cmd == 'group': self.handle_group(args)
|
|
||||||
elif cmd == 'attr': self.handle_attr(args)
|
|
||||||
elif cmd == 'host': self.handle_host(*p)
|
|
||||||
elif cmd in self.commands: self.commands[cmd](*p)
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def register_command(self, name, method):
|
def register_command(self, name, method):
|
||||||
|
@ -647,18 +839,21 @@ class Parser(object):
|
||||||
def add_group(self, otype, id):
|
def add_group(self, otype, id):
|
||||||
self.groups[otype]['current'].add(id)
|
self.groups[otype]['current'].add(id)
|
||||||
|
|
||||||
def handle_group(self, args):
|
def handle_group(self, cmd, *args):
|
||||||
"""group otype gtype
|
"""group otype [gtype]
|
||||||
gtype peut valoir:
|
gtype peut valoir:
|
||||||
- defaults: revenir à l'état initial, permettant de spécifier les
|
- defaults: revenir à l'état initial, permettant de spécifier les
|
||||||
attributs et liens pour tous les hôtes définis à partir de cette ligne
|
attributs et liens pour tous les hôtes définis à partir de cette ligne
|
||||||
- once: un nouveau groupe est défini à chaque nouvelle ligne 'group'
|
- once: un nouveau groupe est défini à chaque nouvelle définition
|
||||||
- until: définir un groupe qui va jusqu'à la prochaine ligne 'group'
|
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'
|
otype = args[0:1] and args[0] or 'host'
|
||||||
if otype not in self.groups:
|
if otype not in self.groups:
|
||||||
raise ValueError('%s: invalid object type' % otype)
|
raise ValueError('%s: invalid object type' % otype)
|
||||||
gtype = args[1:2] and args[1] or 'until'
|
gtype = args[1:2] and args[1] or 'once'
|
||||||
self.groups[otype]['type'] = gtype
|
self.groups[otype]['type'] = gtype
|
||||||
if gtype == 'defaults':
|
if gtype == 'defaults':
|
||||||
self.groups[otype]['current'] = set([None])
|
self.groups[otype]['current'] = set([None])
|
||||||
|
@ -669,7 +864,7 @@ class Parser(object):
|
||||||
self.attr_otype = otype
|
self.attr_otype = otype
|
||||||
|
|
||||||
############################################################################
|
############################################################################
|
||||||
def handle_attr(self, args):
|
def handle_attr(self, cmd, *args):
|
||||||
otype = self.attr_otype
|
otype = self.attr_otype
|
||||||
assert otype is not None, "attr_otype should not be None"
|
assert otype is not None, "attr_otype should not be None"
|
||||||
for nv in args:
|
for nv in args:
|
||||||
|
@ -683,13 +878,13 @@ class Parser(object):
|
||||||
self.db.get_object(otype, id).modify_attr(name, value, method)
|
self.db.get_object(otype, id).modify_attr(name, value, method)
|
||||||
|
|
||||||
############################################################################
|
############################################################################
|
||||||
def handle_default_profile(self, args):
|
def handle_default_profile(self, cmd, *args):
|
||||||
if not args or not args[0]: profile = None
|
if not args or not args[0]: profile = None
|
||||||
else: profile = args[0]
|
else: profile = args[0]
|
||||||
self.db.default_profile = profile
|
self.db.default_profile = profile
|
||||||
|
|
||||||
############################################################################
|
############################################################################
|
||||||
def handle_default_domain(self, args):
|
def handle_default_domain(self, cmd, *args):
|
||||||
if not args or not args[0]: domain = None
|
if not args or not args[0]: domain = None
|
||||||
else: domain = args[0]
|
else: domain = args[0]
|
||||||
self.db.default_domain = domain
|
self.db.default_domain = domain
|
||||||
|
@ -699,7 +894,8 @@ class Parser(object):
|
||||||
self.db.register_object('host', HostObject)
|
self.db.register_object('host', HostObject)
|
||||||
self.db.get_object('host', None)
|
self.db.get_object('host', None)
|
||||||
self.groups['host'] = {}
|
self.groups['host'] = {}
|
||||||
self.handle_group(['host', 'defaults'])
|
self.handle_group('group', 'host', 'defaults')
|
||||||
|
self.register_command('host', self.handle_host)
|
||||||
def __fix_host(self, host):
|
def __fix_host(self, host):
|
||||||
if host.endswith('.'):
|
if host.endswith('.'):
|
||||||
host = host[:-1]
|
host = host[:-1]
|
||||||
|
@ -779,7 +975,7 @@ class Parser(object):
|
||||||
self.db.register_link('ruinst', RuinstLink)
|
self.db.register_link('ruinst', RuinstLink)
|
||||||
self.db.get_object('module', None)
|
self.db.get_object('module', None)
|
||||||
self.groups['module'] = {}
|
self.groups['module'] = {}
|
||||||
self.handle_group(['module', 'defaults'])
|
self.handle_group('group', 'module', 'defaults')
|
||||||
self.register_command('module', self.handle_module)
|
self.register_command('module', self.handle_module)
|
||||||
self.register_command('uinst', self.handle_xuinst)
|
self.register_command('uinst', self.handle_xuinst)
|
||||||
self.register_command('ruinst', self.handle_xuinst)
|
self.register_command('ruinst', self.handle_xuinst)
|
||||||
|
@ -852,13 +1048,25 @@ class Parser(object):
|
||||||
def handle_xuinst(self, ltype, *args):
|
def handle_xuinst(self, ltype, *args):
|
||||||
"""uinst -p profile attrs*
|
"""uinst -p profile attrs*
|
||||||
ruinst -p profile attrs*
|
ruinst -p profile attrs*
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
"""
|
"""
|
||||||
AP = ArgumentParser()
|
AP = ArgumentParser()
|
||||||
AP.add_argument('-p', '--profile', action='append', dest='profiles')
|
AP.add_argument('-p', '--profile', action='append', dest='profiles')
|
||||||
AP.add_argument('nvss', nargs=REMAINDER)
|
AP.add_argument('nvss', nargs=REMAINDER)
|
||||||
o = AP.parse_args(args)
|
o = AP.parse_args(args)
|
||||||
profiles = o.profiles or [None]
|
|
||||||
#XXX spliter profiles sur ','
|
if o.profiles is None: profiles = [self.db.default_profile]
|
||||||
|
else: profiles = flatten(o.profiles)
|
||||||
|
if profiles == []: profiles = [None]
|
||||||
for profile in profiles:
|
for profile in profiles:
|
||||||
# préparer la mise à jour du groupe courant
|
# préparer la mise à jour du groupe courant
|
||||||
currentls = self.db.get_links(ltype, profile, fo=self.groups['module']['current'], create=True)
|
currentls = self.db.get_links(ltype, profile, fo=self.groups['module']['current'], create=True)
|
||||||
|
@ -900,7 +1108,7 @@ class Parser(object):
|
||||||
self.db.register_link('rwoinst-webres', RwoinstWebresLink)
|
self.db.register_link('rwoinst-webres', RwoinstWebresLink)
|
||||||
self.db.get_object('wobundle', None)
|
self.db.get_object('wobundle', None)
|
||||||
self.groups['wobundle'] = {}
|
self.groups['wobundle'] = {}
|
||||||
self.handle_group(['wobundle', 'defaults'])
|
self.handle_group('group', 'wobundle', 'defaults')
|
||||||
self.register_command('wobundle', self.handle_wobundle)
|
self.register_command('wobundle', self.handle_wobundle)
|
||||||
self.register_command('rwoinst', self.handle_rwoinst)
|
self.register_command('rwoinst', self.handle_rwoinst)
|
||||||
def __fix_wobundle(self, wobundle):
|
def __fix_wobundle(self, wobundle):
|
||||||
|
@ -980,9 +1188,11 @@ class Parser(object):
|
||||||
AP.add_argument('stype', nargs=1)
|
AP.add_argument('stype', nargs=1)
|
||||||
AP.add_argument('nvss', nargs=REMAINDER)
|
AP.add_argument('nvss', nargs=REMAINDER)
|
||||||
o = AP.parse_args(args)
|
o = AP.parse_args(args)
|
||||||
|
|
||||||
|
if o.profiles is None: profiles = [self.db.default_profile]
|
||||||
|
else: profiles = flatten(o.profiles)
|
||||||
|
if profiles == []: profiles = [None]
|
||||||
ltype = "%s-%s" % (ltype, o.stype[0])
|
ltype = "%s-%s" % (ltype, o.stype[0])
|
||||||
profiles = o.profiles or [None]
|
|
||||||
#XXX spliter profiles sur ','
|
|
||||||
for profile in profiles:
|
for profile in profiles:
|
||||||
# préparer la mise à jour du groupe courant
|
# préparer la mise à jour du groupe courant
|
||||||
currentls = self.db.get_links(ltype, profile, fo=self.groups['wobundle']['current'], create=True)
|
currentls = self.db.get_links(ltype, profile, fo=self.groups['wobundle']['current'], create=True)
|
||||||
|
@ -1023,7 +1233,7 @@ class Parser(object):
|
||||||
self.db.register_link('rtoinst', RtoinstLink)
|
self.db.register_link('rtoinst', RtoinstLink)
|
||||||
self.db.get_object('webapp', None)
|
self.db.get_object('webapp', None)
|
||||||
self.groups['webapp'] = {}
|
self.groups['webapp'] = {}
|
||||||
self.handle_group(['webapp', 'defaults'])
|
self.handle_group('group', 'webapp', 'defaults')
|
||||||
self.register_command('webapp', self.handle_webapp)
|
self.register_command('webapp', self.handle_webapp)
|
||||||
self.register_command('rtoinst', self.handle_rtoinst)
|
self.register_command('rtoinst', self.handle_rtoinst)
|
||||||
def __fix_webapp(self, webapp):
|
def __fix_webapp(self, webapp):
|
||||||
|
@ -1099,8 +1309,10 @@ class Parser(object):
|
||||||
AP.add_argument('-p', '--profile', action='append', dest='profiles')
|
AP.add_argument('-p', '--profile', action='append', dest='profiles')
|
||||||
AP.add_argument('nvss', nargs=REMAINDER)
|
AP.add_argument('nvss', nargs=REMAINDER)
|
||||||
o = AP.parse_args(args)
|
o = AP.parse_args(args)
|
||||||
profiles = o.profiles or [None]
|
|
||||||
#XXX spliter profiles sur ','
|
if o.profiles is None: profiles = [self.db.default_profile]
|
||||||
|
else: profiles = flatten(o.profiles)
|
||||||
|
if profiles == []: profiles = [None]
|
||||||
for profile in profiles:
|
for profile in profiles:
|
||||||
# préparer la mise à jour du groupe courant
|
# préparer la mise à jour du groupe courant
|
||||||
currentls = self.db.get_links(ltype, profile, fo=self.groups['webapp']['current'], create=True)
|
currentls = self.db.get_links(ltype, profile, fo=self.groups['webapp']['current'], create=True)
|
||||||
|
@ -1135,6 +1347,15 @@ class Parser(object):
|
||||||
for currentl in currentls:
|
for currentl in currentls:
|
||||||
currentl.modify_attr(name, value, method)
|
currentl.modify_attr(name, value, method)
|
||||||
|
|
||||||
|
############################################################################
|
||||||
|
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
|
# Programme principal
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue