détacher la base de données du parser

This commit is contained in:
Jephté Clain 2017-06-15 21:11:58 +04:00
parent 5a3504e141
commit ffe5f715c1
1 changed files with 167 additions and 108 deletions

View File

@ -9,6 +9,141 @@ from glob import glob
USER_CONFDIR = '~/etc/deploy' USER_CONFDIR = '~/etc/deploy'
SYSTEM_CONFDIR = '/var/local/deploy' SYSTEM_CONFDIR = '/var/local/deploy'
################################################################################
# Base de données
class HostObject(object):
@staticmethod
def genid(host):
host = re.sub(r'\..*', '', host)
host = re.sub(r'[^a-zA-Z0-9]', '_', host)
host = host.lower()
return host
@staticmethod
def genib(id):
if re.match(r'\d+$', id): return id
ib = re.sub(r'^(.*?)\d+$', r'\1', id)
return ib
_id = None
_hosts = None
_attrs = None
def __init__(self, id):
self._id = id
self._hosts = []
self._attrs = {}
def get_id(self): return self._id
id = property(get_id)
def get_hosts(self): return self._hosts
hosts = property(get_hosts)
def add_host(self, host):
if host not in self._hosts: self._hosts.append(host)
def get_attrs(self): return self._attrs
attrs = property(get_attrs)
def get_attr(self, name): return self._attrs.get(name, None)
def set_attr(self, name, value):
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):
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):
if name not in self._attrs: self._attrs[name] = []
self._attrs[name].append(value)
def del_attr(self, name, value):
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'):
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 copy_attrs(self, other, method='set'):
for name, values in other.attrs.items():
for value in values:
self.modify_attr(name, value, method)
def dump(self, indent=''):
print "%s%s" % (indent, self.id)
print "%s hosts: %s" % (indent, ' '.join(self.hosts))
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)))
class Database(object):
_known_profiles = None
_default_profile = None
_default_domain = None
_objects = None
_objects_classes = None
def __init__(self):
self._known_profiles = {}
self._objects = {}
self._objects_classes = {}
def get_known_profiles(self):
return self.known_profiles.keys()
known_profiles = property(get_known_profiles)
def has_default_profile(self):
return self._default_profile is not None
def get_default_profile(self):
return self._default_profile
def set_default_profile(self, profile):
if profile: self._known_profiles[profile] = True
self._default_profile = profile or None
default_profile = property(get_default_profile, set_default_profile)
def has_default_domain(self):
return self._default_domain is not None
def get_default_domain(self):
return self._default_domain
def set_default_domain(self, domain):
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):
if not self._objects.has_key(otype):
self._objects[otype] = {}
self._objects_classes[otype] = oclass
def get_objects(self, otype):
objects = self._objects.get(otype, None)
if objects is None: return None
return [objects[id] for id in objects if id is not None]
objects = property(get_objects)
def has_object(self, otype, id):
objects = self._objects.get(otype, None)
if objects is None: return False
return objects.has_key(id)
def get_object(self, otype, id):
objects = self._objects.get(otype, None)
if objects is None: return None
object = objects.get(id, None)
if object is None:
object_class = self._objects_classes.get(otype, None)
if object_class is not None:
object = object_class(id)
self._objects[otype][id] = object
return object
################################################################################
# Analyse des fichiers de configuration
class EOL(object): class EOL(object):
__repr__ = __string__ = lambda self: 'EOL' __repr__ = __string__ = lambda self: 'EOL'
EOL = EOL() EOL = EOL()
@ -154,47 +289,6 @@ class Lexer(object):
predicate.append(lexem) predicate.append(lexem)
return predicates return predicates
class HostObject(object):
id = None
dir = None
hosts = None
attrs = None
def __init__(self, id):
self.id = id
self.hosts = []
self.attrs = {}
def get_id(self): return self.id
def get_dir(self): return self.dir
def set_dir(self, dir): self.dir = dir
def get_hosts(self): return self.hosts
def add_host(self, host):
if host not in self.hosts: self.hosts.append(host)
def get_attrs(self): return self.attrs
def get_attr(self, name): return self.attrs.get(name, None)
def set_attr(self, name, value):
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):
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):
if name not in self.attrs: self.attrs[name] = []
self.attrs[name].append(value)
def del_attr(self, name, value):
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'):
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 copy_attrs(self, other, method='set'):
for name, values in other.attrs.items():
for value in values:
self.modify_attr(name, value, method)
def split_scalar(arg): def split_scalar(arg):
if '=' in arg: if '=' in arg:
name, value = arg.split('=', 1) name, value = arg.split('=', 1)
@ -224,27 +318,18 @@ def split_list(arg):
return name, values return name, values
class Parser(object): class Parser(object):
lexer = None db = None
predicates = None
known_profiles = None
default_profile = None
default_domain = None
objects = None
groups = None groups = None
attr_otype = None attr_otype = None
def __init__(self, lexer, parse=True): def __init__(self, db=None):
self.lexer = lexer if db is None: db = Database()
self.objects = {} self.db = db
self.groups = {} self.groups = {}
self.__setup_hosts() self.__setup_hosts()
if parse: self.parse()
def parse(self): def parse(self, predicates):
self.predicates = self.lexer.get_predicates() for p in predicates:
for p in self.predicates:
cmd = p[0] cmd = p[0]
args = p[1:] args = p[1:]
if cmd == 'default_profile': self.handle_default_profile(args) if cmd == 'default_profile': self.handle_default_profile(args)
@ -252,10 +337,7 @@ class Parser(object):
elif cmd == 'group': self.handle_group(args) elif cmd == 'group': self.handle_group(args)
elif cmd == 'attr': self.handle_attr(args) elif cmd == 'attr': self.handle_attr(args)
elif cmd == 'host': self.handle_host(args) elif cmd == 'host': self.handle_host(args)
return self
def get_objects(self, otype):
objects = self.objects[otype]
return [objects[id] for id in objects if id is not None]
############################################################################ ############################################################################
def reset_group(self, otype): def reset_group(self, otype):
@ -297,54 +379,33 @@ class Parser(object):
method = 'set' method = 'set'
value = '1' value = '1'
for id in self.groups[otype]['current']: for id in self.groups[otype]['current']:
self.objects[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, args):
if not args or not args[0]: if not args or not args[0]: profile = None
profile = None else: profile = args[0]
else: self.db.default_profile = profile
profile = args[0]
if self.known_profiles is None: self.known_profiles = {}
self.known_profiles[profile] = True
self.default_profile = profile
############################################################################ ############################################################################
def handle_default_domain(self, args): def handle_default_domain(self, args):
if not args or not args[0]: if not args or not args[0]: domain = None
domain = None else: domain = args[0]
else: self.db.default_domain = domain
domain = args[0]
self.default_domain = domain
############################################################################ ############################################################################
def __setup_hosts(self): def __setup_hosts(self):
self.objects['host'] = {} self.db.register_object('host', HostObject)
self.__host_object(None) self.db.get_object('host', None)
self.groups['host'] = {} self.groups['host'] = {}
self.handle_group(['host', 'defaults']) self.handle_group(['host', 'defaults'])
self.attr_otype = 'host' self.attr_otype = 'host'
def __fix_host(self, host): def __fix_host(self, host):
if host.endswith('.'): if host.endswith('.'):
host = host[:-1] host = host[:-1]
elif '.' not in host and self.default_domain: elif '.' not in host and self.db.has_default_domain():
host = '%s.%s' % (host, self.default_domain) host = '%s.%s' % (host, self.db.default_domain)
return host return host
def __hostid(self, host):
host = re.sub(r'\..*', '', host)
host = re.sub(r'[^a-zA-Z0-9]', '_', host)
host = host.lower()
return host
def __hostib(self, id):
if re.match(r'\d+$', id): return id
ib = re.sub(r'^(.*?)\d+$', r'\1', id)
return ib
def __host_object(self, id):
ho = self.objects['host'].get(id, None)
if ho is None:
ho = HostObject(id)
self.objects['host'][id] = ho
return ho
def handle_host(self, args): def handle_host(self, args):
"""host [id=]host,... """host [id=]host,...
@ -370,7 +431,7 @@ class Parser(object):
# préparer la mise à jour du groupe courant # préparer la mise à jour du groupe courant
self.reset_group_maybe('host') self.reset_group_maybe('host')
self.attr_otype = 'host' self.attr_otype = 'host'
default_ho = self.__host_object(None) default_ho = self.db.get_object('host', None)
# traiter les hôtes # traiter les hôtes
for nvs in nvss: for nvs in nvss:
name, values = split_list(nvs) name, values = split_list(nvs)
@ -381,20 +442,21 @@ class Parser(object):
dir, host = path.split(path.abspath(path.expanduser(host))) dir, host = path.split(path.abspath(path.expanduser(host)))
host = self.__fix_host(host) host = self.__fix_host(host)
if name: if name:
ho = self.__host_object(name) ho = self.db.get_object('host', name)
if host: ho.add_host(host) if host: ho.add_host(host)
ho.copy_attrs(default_ho) ho.copy_attrs(default_ho)
if dir is not None: ho.reset_attr('dir', dir) if dir is not None: ho.reset_attr('dir', dir)
self.add_group('host', name) self.add_group('host', name)
else: else:
id = self.__hostid(host) id = HostObject.genid(host)
ho = self.__host_object(id) ho = self.db.get_object('host', id)
ho.add_host(host) ho.add_host(host)
ho.copy_attrs(default_ho) ho.copy_attrs(default_ho)
if dir is not None: ho.reset_attr('dir', dir) if dir is not None: ho.reset_attr('dir', dir)
self.add_group('host', id) self.add_group('host', id)
ib = self.__hostib(id)
ho = self.__host_object(ib) ib = HostObject.genib(id)
ho = self.db.get_object('host', ib)
ho.add_host(host) ho.add_host(host)
ho.copy_attrs(default_ho) ho.copy_attrs(default_ho)
if dir is not None: ho.reset_attr('dir', dir) if dir is not None: ho.reset_attr('dir', dir)
@ -415,24 +477,21 @@ def run_qdd(o):
if path.exists(user_conf): conf = user_conf if path.exists(user_conf): conf = user_conf
elif path.exists(system_conf): conf = system_conf elif path.exists(system_conf): conf = system_conf
lexer = Lexer(conf) lexer = Lexer(conf)
db = Database()
if o.action == 'nop': if o.action == 'nop':
pass pass
elif o.action == 'dump': #XXX elif o.action == 'dump': #XXX
parser = Parser(lexer) predicates = lexer.get_predicates()
parser = Parser(db).parse(predicates)
print "=== predicates" print "=== predicates"
for p in parser.predicates: for p in predicates:
print ' '.join(map(repr, p)) print ' '.join(map(repr, p))
print "=== hosts" print "=== hosts"
for ho in parser.get_objects('host'): for ho in db.get_objects('host'):
id, dir, hosts = ho.get_id(), ho.get_dir(), ho.get_hosts() ho.dump()
attrs = ho.get_attrs()
print id
if dir: print " dir=%s" % dir
print " hosts=%r" % hosts
if attrs: print " attrs=%r" % attrs
elif o.action == 'query': elif o.action == 'query':
parser = Parser(lexer) parser = Parser(db).parse(lexer.get_predicates())
if __name__ == '__main__': if __name__ == '__main__':
from argparse import ArgumentParser, HelpFormatter from argparse import ArgumentParser, HelpFormatter