recherche d'objets
This commit is contained in:
parent
38fdb21f91
commit
c8e13163d3
|
@ -13,6 +13,8 @@ SYSTEM_CONFDIR = '/var/local/deploy'
|
|||
# Diverses fonctions
|
||||
|
||||
def isseq(t):
|
||||
"""Tester si t est une séquence
|
||||
"""
|
||||
return isinstance(t, list) or isinstance(t, tuple) or isinstance(t, set)
|
||||
|
||||
def flatten(src, unique=True, clean=True, sep=','):
|
||||
|
@ -23,7 +25,6 @@ def flatten(src, unique=True, clean=True, sep=','):
|
|||
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 = []
|
||||
|
@ -37,6 +38,31 @@ def flatten(src, unique=True, clean=True, sep=','):
|
|||
dest.extend(items)
|
||||
return dest
|
||||
|
||||
def qshell(values):
|
||||
if isseq(values): return map(qshell, values)
|
||||
elif not values: return ''
|
||||
else: return "'%s'" % values.replace("'", "'\\''")
|
||||
|
||||
def strip_domain(a):
|
||||
if a.find('.') == -1: return a
|
||||
else: return a[:a.find('.')]
|
||||
|
||||
def match_host(a, b):
|
||||
"""Les hôtes sont matchés de façon particulière:
|
||||
NAME matche NAME et NAME.DOMAIN
|
||||
NAME. matche NAME
|
||||
NAME.DOMAIN matche NAME.DOMAIN
|
||||
"""
|
||||
a = a.lower()
|
||||
b = b.lower()
|
||||
if a == b: return True
|
||||
if a.find('.') == -1: # NAME
|
||||
return a == strip_domain(b)
|
||||
elif a.endswith('.'): # NAME.
|
||||
return a[:-1] == b
|
||||
else: # NAME.DOMAIN
|
||||
return a == b
|
||||
|
||||
################################################################################
|
||||
# Base de données
|
||||
|
||||
|
@ -47,6 +73,10 @@ class GenericObject(object):
|
|||
values) et des attributs (propriété attrs)
|
||||
"""
|
||||
|
||||
OTYPE = 'object'
|
||||
VALUESNAME = 'values'
|
||||
ATTRPREFIX = 'object_'
|
||||
|
||||
_id = None
|
||||
_values = None
|
||||
_attrs = None
|
||||
|
@ -59,7 +89,12 @@ class GenericObject(object):
|
|||
def get_id(self): return self._id
|
||||
id = property(get_id)
|
||||
|
||||
def get_values(self): return self._values
|
||||
def is_defaults(self):
|
||||
"""Cet objet contient-il les attributs par défaut pour le type?
|
||||
"""
|
||||
return self._id is None
|
||||
|
||||
def get_values(self): return tuple(self._values)
|
||||
values = property(get_values)
|
||||
def add_value(self, value):
|
||||
self._values.add(value)
|
||||
|
@ -110,6 +145,46 @@ class GenericObject(object):
|
|||
for value in values:
|
||||
self.modify_attr(name, value, method)
|
||||
|
||||
def invalid_match(self, match):
|
||||
return ValueError("%s: invalid match type")
|
||||
def match_values(self, values, match='any', reverse=False):
|
||||
"""Tester si cet objet a l'une des valeurs spécifiées (avec match=='any' qui est
|
||||
la valeur par défaut)
|
||||
"""
|
||||
if not isseq(values): values = [values]
|
||||
if reverse: Yes, No = False, True
|
||||
else: Yes, No = True, False
|
||||
if match == 'any':
|
||||
for value in values:
|
||||
if value in self._values: return Yes
|
||||
return No
|
||||
elif match == 'all':
|
||||
for value in values:
|
||||
if value not in self._values: return No
|
||||
return Yes
|
||||
else:
|
||||
raise self.invalid_match(match)
|
||||
def match_attrs(self, attrs, match='any', reverse=False):
|
||||
"""Tester si cet objet a un des attributs correspondant aux valeurs spécifiées
|
||||
(avec match=='any' qui est la valeur par défaut)
|
||||
"""
|
||||
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)
|
||||
|
||||
def _dump_id(self, indent):
|
||||
print "%s%s" % (indent, self.id)
|
||||
def _dump_values(self, indent, name):
|
||||
|
@ -126,13 +201,17 @@ class GenericObject(object):
|
|||
"""Afficher l'identifiant, les valeurs et les attributs de cet objet
|
||||
"""
|
||||
self._dump_id(indent)
|
||||
self._dump_values(indent, 'values')
|
||||
self._dump_values(indent, self.VALUESNAME)
|
||||
self._dump_attrs(indent)
|
||||
|
||||
class HostObject(GenericObject):
|
||||
"""Un hôte
|
||||
"""
|
||||
|
||||
OTYPE = 'host'
|
||||
VALUESNAME = 'hosts'
|
||||
ATTRPREFIX = 'host_'
|
||||
|
||||
@staticmethod
|
||||
def genid(host):
|
||||
"""Générer un identifiant à partir du nom d'hôte
|
||||
|
@ -151,15 +230,31 @@ class HostObject(GenericObject):
|
|||
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)
|
||||
def match_values(self, values, match='any', reverse=False):
|
||||
if not isseq(values): values = [values]
|
||||
if reverse: Yes, No = False, True
|
||||
else: Yes, No = True, False
|
||||
if match == 'any':
|
||||
for value in values:
|
||||
for host in self._values:
|
||||
if match_host(value, host): return Yes
|
||||
return No
|
||||
elif match == 'all':
|
||||
for value in values:
|
||||
for host in self._values:
|
||||
if not match_host(value, host): return No
|
||||
return Yes
|
||||
else:
|
||||
raise self.invalid_match(match)
|
||||
|
||||
class ModuleObject(GenericObject):
|
||||
"""Un module, uinst-allable
|
||||
"""
|
||||
|
||||
OTYPE = 'module'
|
||||
VALUESNAME = 'modules'
|
||||
ATTRPREFIX = 'module_'
|
||||
|
||||
@staticmethod
|
||||
def genid(module):
|
||||
"""Générér un identifiant à partir du nom du module
|
||||
|
@ -168,15 +263,14 @@ class ModuleObject(GenericObject):
|
|||
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):
|
||||
"""Un bundle Webobjects, woinst-allable
|
||||
"""
|
||||
|
||||
OTYPE = 'wobundle'
|
||||
VALUESNAME = 'wobundles'
|
||||
ATTRPREFIX = 'wobundle_'
|
||||
|
||||
@staticmethod
|
||||
def genid(wobundle):
|
||||
"""Générér un identifiant à partir du nom du bundle
|
||||
|
@ -186,15 +280,14 @@ class WobundleObject(GenericObject):
|
|||
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):
|
||||
"""Une webapp, toinst-allable
|
||||
"""
|
||||
|
||||
OTYPE = 'webapp'
|
||||
VALUESNAME = 'webapps'
|
||||
ATTRPREFIX = 'webapp_'
|
||||
|
||||
@staticmethod
|
||||
def genid(webapp):
|
||||
"""Générér un identifiant à partir du nom de la webapp
|
||||
|
@ -203,11 +296,6 @@ class WebappObject(GenericObject):
|
|||
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)
|
||||
|
||||
class GenericLink(object):
|
||||
"""Un lien générique
|
||||
|
||||
|
@ -552,13 +640,23 @@ class Database(object):
|
|||
"""
|
||||
return self._objects_ids_otype.keys()
|
||||
known_otypes = property(get_known_otypes)
|
||||
def get_objects(self, otype):
|
||||
def get_objects(self, otype, value=UNDEF, attrs=UNDEF):
|
||||
"""Obtenir tous les objets définis du type spécifié ou None si le type d'objets
|
||||
est invalide.
|
||||
est invalide. Si value ou attrs sont définis, filtrer sur ces valeurs.
|
||||
"""
|
||||
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]
|
||||
if otype is None:
|
||||
objects = []
|
||||
for objects_ids in self._objects_ids_otype.values():
|
||||
objects.extend(objects_ids.values())
|
||||
else:
|
||||
objects = self._objects_ids_otype.get(otype, None)
|
||||
if objects is None: return None
|
||||
objects = [objects[id] for id in objects if id is not None]
|
||||
return [
|
||||
object for object in objects if
|
||||
(value is UNDEF or object.match_values(value)) and
|
||||
(attrs is UNDEF or object.match_attrs(attrs))
|
||||
]
|
||||
def has_object(self, otype, id):
|
||||
"""Vérifier si l'objet avec l'identifiant spécifié est défini
|
||||
"""
|
||||
|
@ -1390,6 +1488,69 @@ class Parser(object):
|
|||
################################################################################
|
||||
# Programme principal
|
||||
|
||||
VALID_QUERY_TYPES = ('object', 'source', 'dest', 'link')
|
||||
def __fix_query_type(query_type):
|
||||
if query_type is None: return None
|
||||
query_type = query_type.lower()
|
||||
if query_type == 'o': query_type = 'object'
|
||||
elif query_type == 's': query_type = 'source'
|
||||
elif query_type == 'd': query_type = 'dest'
|
||||
elif query_type == 'l': query_type = 'link'
|
||||
if query_type not in VALID_QUERY_TYPES:
|
||||
raise ValuerError("%s: invalid query type")
|
||||
return query_type
|
||||
|
||||
def __resolve_format(o, query_type):
|
||||
format = o.format or 'shell'
|
||||
ovars, lvars = o.object_vars, o.link_vars
|
||||
if not ovars and not lvars:
|
||||
if query_type == 'object':
|
||||
ovars = True
|
||||
lvars = False
|
||||
elif query_type in ('source', 'dest'):
|
||||
ovars = True
|
||||
lvars = True
|
||||
elif query_type == 'link':
|
||||
ovars = False
|
||||
lvars = True
|
||||
ofunc, lfunc = o.object_func, o.link_func
|
||||
ivars = flatten(o.vars) or None
|
||||
return format, ovars, lvars, ofunc, lfunc, ivars
|
||||
|
||||
def print_var(name, values, format, ivars, is_values=False):
|
||||
if ivars is not None and name not in ivars: return
|
||||
if not isseq(values): values = [values]
|
||||
if format == 'shell':
|
||||
params = (name, " ".join(qshell(values)))
|
||||
if len(values) <= 1: print "%s=%s" % params
|
||||
else: print "%s=(%s)" % params
|
||||
return
|
||||
if not is_values: return
|
||||
if format == 'lines':
|
||||
for value in values:
|
||||
print value
|
||||
elif format == 'spaces':
|
||||
print " ".join(values)
|
||||
elif format == 'path':
|
||||
print ":".join(values)
|
||||
elif format == 'comma':
|
||||
print ",".join(values)
|
||||
else:
|
||||
raise ValueError("%s: invalid format" % format)
|
||||
|
||||
def print_objects(objects, query_type, o):
|
||||
if objects is None: return
|
||||
format, ovars, lvars, ofunc, lfunc, ivars = __resolve_format(o, query_type)
|
||||
if not ovars: return
|
||||
for o in objects:
|
||||
if o.is_defaults(): continue
|
||||
print_var('otype', o.OTYPE, format, ivars)
|
||||
print_var(o.VALUESNAME, o.values, format, ivars, True)
|
||||
for name, values in o.attrs.items():
|
||||
print_var('%s%s' % (o.ATTRPREFIX, name), values, format, ivars)
|
||||
if ofunc and format == 'shell':
|
||||
print ofunc
|
||||
|
||||
def run_qdd(o):
|
||||
# fichier de configuration
|
||||
confname = o.confname or 'deploy'
|
||||
|
@ -1409,7 +1570,7 @@ def run_qdd(o):
|
|||
|
||||
if o.action == 'nop':
|
||||
pass
|
||||
elif o.action == 'dump': #XXX
|
||||
elif o.action == 'dump':
|
||||
predicates = lexer.get_predicates()
|
||||
parser = Parser(db).parse(predicates)
|
||||
print "=== predicates"
|
||||
|
@ -1434,6 +1595,22 @@ def run_qdd(o):
|
|||
|
||||
elif o.action == 'query':
|
||||
parser = Parser(db).parse(lexer.get_predicates())
|
||||
query_type, object_type, link_type = __fix_query_type(o.query_type), o.object_type, o.link_type
|
||||
if query_type is None and object_type is None and link_type is None:
|
||||
query_type = 'dest'
|
||||
object_type = 'host'
|
||||
if o.profile is not None: profile = o.profile or None
|
||||
else: profile = UNDEF
|
||||
args = o.args or UNDEF
|
||||
if query_type == 'object':
|
||||
objects = db.get_objects(object_type, args)
|
||||
print_objects(objects, query_type, o)
|
||||
elif query_type == 'source':
|
||||
pass
|
||||
elif query_type == 'dest':
|
||||
pass
|
||||
elif query_type == 'link':
|
||||
pass
|
||||
|
||||
if __name__ == '__main__':
|
||||
from argparse import ArgumentParser, HelpFormatter
|
||||
|
@ -1482,29 +1659,42 @@ if __name__ == '__main__':
|
|||
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"")
|
||||
help=u"Spécifier le type d'information à afficher:\n"
|
||||
+ u">>>\n"
|
||||
+ u"* object pour des informations sur les objets,\n"
|
||||
+ u">>>\n"
|
||||
+ u"* source pour des informations sur les objets sources d'un lien,\n"
|
||||
+ u">>>\n"
|
||||
+ u"* dest pour des informations sur les objets destinations d'un lien,\n"
|
||||
+ u">>>\n"
|
||||
+ u"* link pour des informations sur un lien.\n"
|
||||
+ u">>>\n"
|
||||
+ u"La valeur par défaut est '-q dest -j host'")
|
||||
AP.add_argument('-j', '--object-type', dest='object_type',
|
||||
help=u"")
|
||||
help=u"Spécifier le type d'objet à chercher pour -q object|source|dest")
|
||||
AP.add_argument('-t', '--link-type', dest='link_type',
|
||||
help=u"")
|
||||
help=u"Spécifier le type de lien à chercher pour -q source|dest|link")
|
||||
AP.add_argument('-p', '--profile', dest='profile',
|
||||
help=u"Spécifier le profil de déploiement")
|
||||
help=u"Spécifier le profil de déploiement pour -q source|dest|link. Utiliser une valeur vide pour ne sélectionner que les liens sans profils. Ne pas spécifier l'option pour afficher tous les profils définis.")
|
||||
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 le format pour la sortie. La valeur par défaut est shell.\n"
|
||||
+ u">>>\n"
|
||||
+ u"* shell affiche les définitions de variables\n"
|
||||
+ u">>>\n"
|
||||
+ u"* lines affiches les valeurs à raison d'une par lignes. les attributs ne sont pas affichés.\n"
|
||||
+ u">>>\n"
|
||||
+ u"* spaces, path et comma affichent les valeurs séparées respectivement par un espace ' ', deux point ':' et une virgule ','. les attributs ne sont pas affichés.\n")
|
||||
AP.add_argument('-v', '--include-vars', action='append', 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.")
|
||||
help=u"Afficher uniquement les variables associées aux objets.")
|
||||
AP.add_argument('-l', '--link-vars', action='store_true', dest='link_vars',
|
||||
help=u"Afficher les variables associées aux liens.")
|
||||
help=u"Afficher uniquement 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.")
|
||||
AP.add_argument('args', nargs=REMAINDER)
|
||||
o = AP.parse_args()
|
||||
|
||||
run_qdd(o)
|
||||
|
|
Loading…
Reference in New Issue