implémenter la gestion des objets modules, wobundle, webapp

This commit is contained in:
Jephté Clain 2017-06-15 23:20:38 +04:00
parent ffe5f715c1
commit 02c5f91b78
1 changed files with 387 additions and 49 deletions

View File

@ -12,34 +12,23 @@ SYSTEM_CONFDIR = '/var/local/deploy'
################################################################################ ################################################################################
# Base de données # Base de données
class HostObject(object): class GenericObject(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 _id = None
_hosts = None _values = None
_attrs = None _attrs = None
def __init__(self, id): def __init__(self, id):
self._id = id self._id = id
self._hosts = [] self._values = []
self._attrs = {} self._attrs = {}
def get_id(self): return self._id def get_id(self): return self._id
id = property(get_id) id = property(get_id)
def get_hosts(self): return self._hosts def get_values(self): return self._values
hosts = property(get_hosts) values = property(get_values)
def add_host(self, host): def add_value(self, value):
if host not in self._hosts: self._hosts.append(host) if value not in self._values: self._values.append(value)
def get_attrs(self): return self._attrs def get_attrs(self): return self._attrs
attrs = property(get_attrs) attrs = property(get_attrs)
@ -68,9 +57,11 @@ class HostObject(object):
for value in values: for value in values:
self.modify_attr(name, value, method) self.modify_attr(name, value, method)
def dump(self, indent=''): def _dump_id(self, indent):
print "%s%s" % (indent, self.id) print "%s%s" % (indent, self.id)
print "%s hosts: %s" % (indent, ' '.join(self.hosts)) def _dump_values(self, indent, name):
print "%s %s: %s" % (indent, name, ' '.join(self.values))
def _dump_attrs(self, indent):
if self.attrs: if self.attrs:
print "%s attrs:" % indent print "%s attrs:" % indent
for name, values in self.attrs.items(): for name, values in self.attrs.items():
@ -78,6 +69,66 @@ class HostObject(object):
print "%s %s=%s" % (indent, name, repr(values[0])) print "%s %s=%s" % (indent, name, repr(values[0]))
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=''):
self._dump_id(indent)
self._dump_attrs(indent)
class HostObject(GenericObject):
@staticmethod
def genid(host):
"MY-Host42.self --> 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):
"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):
@staticmethod
def genid(module):
"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):
@staticmethod
def genid(wobundle):
"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):
@staticmethod
def genid(webapp):
"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)
class Database(object): class Database(object):
_known_profiles = None _known_profiles = None
@ -93,7 +144,7 @@ class Database(object):
self._objects_classes = {} self._objects_classes = {}
def get_known_profiles(self): def get_known_profiles(self):
return self.known_profiles.keys() return self._known_profiles.keys()
known_profiles = property(get_known_profiles) known_profiles = property(get_known_profiles)
def has_default_profile(self): def has_default_profile(self):
@ -117,10 +168,14 @@ class Database(object):
self._default_domain = domain or None self._default_domain = domain or None
default_domain = property(get_default_domain, set_default_domain) default_domain = property(get_default_domain, set_default_domain)
def register_object(self, otype, oclass): def register_object(self, otype, oclass=None):
if not self._objects.has_key(otype): if not self._objects.has_key(otype):
self._objects[otype] = {} self._objects[otype] = {}
if oclass is None: oclass = GenericObject
self._objects_classes[otype] = oclass self._objects_classes[otype] = oclass
def get_known_objects(self):
return self._objects.keys()
known_objects = property(get_known_objects)
def get_objects(self, otype): def get_objects(self, otype):
objects = self._objects.get(otype, None) objects = self._objects.get(otype, None)
if objects is None: return None if objects is None: return None
@ -289,7 +344,9 @@ class Lexer(object):
predicate.append(lexem) predicate.append(lexem)
return predicates return predicates
def split_scalar(arg): RE_NAMETYPE = re.compile(r'(\S+):(\w+)$')
def split_namev(arg):
"spliter name[:type][method][=value]"
if '=' in arg: if '=' in arg:
name, value = arg.split('=', 1) name, value = arg.split('=', 1)
else: else:
@ -306,9 +363,24 @@ def split_scalar(arg):
method = 'del' method = 'del'
else: else:
method = 'set' method = 'set'
return name, method, value mo = RE_NAMETYPE.match(name)
if mo is not None:
name, type = mo.groups()
else:
type = None
return name, value, type, method
def split_list(arg): 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: if '=' in arg:
name, values = arg.split('=', 1) name, values = arg.split('=', 1)
values = values.split(',') values = values.split(',')
@ -322,11 +394,17 @@ class Parser(object):
groups = None groups = None
attr_otype = None attr_otype = None
commands = None
def __init__(self, db=None): def __init__(self, db=None):
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.__setup_hosts() self.__setup_hosts()
self.__setup_uinst()
self.__setup_woinst()
self.__setup_toinst()
def parse(self, predicates): def parse(self, predicates):
for p in predicates: for p in predicates:
@ -337,8 +415,12 @@ 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)
elif cmd in self.commands: self.commands[cmd](*p)
return self return self
def register_command(self, name, method):
self.commands[name] = method
############################################################################ ############################################################################
def reset_group(self, otype): def reset_group(self, otype):
self.groups[otype]['current'] = {} self.groups[otype]['current'] = {}
@ -368,16 +450,19 @@ class Parser(object):
self.groups[otype]['current'] = {} self.groups[otype]['current'] = {}
else: else:
raise ValueError('%s: invalid group type' % gtype) raise ValueError('%s: invalid group type' % gtype)
self.attr_otype = otype
############################################################################ ############################################################################
def handle_attr(self, args): def handle_attr(self, 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:
name, method, value = split_scalar(nv) name, value, type, method = split_namev(nv)
if value is None: if value is None:
method = 'set' method = 'set'
value = '1' value = '1'
elif type == 'path':
value = path.expanduser(value)
for id in self.groups[otype]['current']: for id in self.groups[otype]['current']:
self.db.get_object(otype, id).modify_attr(name, value, method) self.db.get_object(otype, id).modify_attr(name, value, method)
@ -399,7 +484,6 @@ class Parser(object):
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(['host', 'defaults'])
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]
@ -409,6 +493,8 @@ class Parser(object):
def handle_host(self, args): def handle_host(self, args):
"""host [id=]host,... """host [id=]host,...
host -b [id=]basedir
host -d [id=]dirspec
""" """
AP = ArgumentParser() AP = ArgumentParser()
AP.add_argument('-b', '--basedir', action='append', dest='basedirs') AP.add_argument('-b', '--basedir', action='append', dest='basedirs')
@ -418,23 +504,31 @@ class Parser(object):
# construire la liste des hôtes à traiter # construire la liste des hôtes à traiter
nvss = [] nvss = []
if o.basedirs is not None: if o.basedirs is not None:
for basedir in [path.expanduser(basedir) for basedir in o.basedirs]: for pb in o.basedirs:
nvss.extend([path.abspath(path.join(basedir, name)) 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) for name in os.listdir(basedir)
if path.isdir(path.join(basedir, name))]) if path.isdir(path.join(basedir, name))])
if o.dirspecs is not None: if o.dirspecs is not None:
for dirspec in [path.expanduser(dirspec) for dirspec in o.dirspecs]: for pd in o.dirspecs:
nvss.extend([path.abspath(dir) 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) for dir in glob(dirspec)
if path.isdir(dir)]) if path.isdir(dir)])
nvss.extend(o.nvss) nvss.extend(o.nvss)
# 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.db.get_object('host', None) defaulto = 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_nlist(nvs)
for host in values: for host in values:
dir = None dir = None
if host: if host:
@ -442,26 +536,263 @@ 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.db.get_object('host', name) o = self.db.get_object('host', name)
if host: ho.add_host(host) if host: o.add_value(host)
ho.copy_attrs(default_ho) o.copy_attrs(defaulto)
if dir is not None: ho.reset_attr('dir', dir) if dir is not None: o.set_attr('dir', dir)
self.add_group('host', name) self.add_group('host', name)
else: else:
id = HostObject.genid(host) id = HostObject.genid(host)
ho = self.db.get_object('host', id) o = self.db.get_object('host', id)
ho.add_host(host) o.add_value(host)
ho.copy_attrs(default_ho) o.copy_attrs(defaulto)
if dir is not None: ho.reset_attr('dir', dir) if dir is not None: o.set_attr('dir', dir)
self.add_group('host', id) self.add_group('host', id)
ib = HostObject.genib(id) ib = HostObject.genib(id)
ho = self.db.get_object('host', ib) o = self.db.get_object('host', ib)
ho.add_host(host) o.add_value(host)
ho.copy_attrs(default_ho) o.copy_attrs(defaulto)
if dir is not None: ho.reset_attr('dir', dir) if dir is not None: o.set_attr('dir', dir)
self.add_group('host', ib) self.add_group('host', ib)
############################################################################
def __setup_uinst(self):
self.db.register_object('module', ModuleObject)
self.db.get_object('module', None)
self.groups['module'] = {}
self.handle_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, cmd, *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('module')
self.attr_otype = 'module'
defaulto = self.db.get_object('module', None)
# traiter les modules
for nvs in nvss:
name, values = split_nlist(nvs)
for module in values:
dir = None
modulep = None
if module:
if '/' in module:
dir, module = path.split(path.abspath(path.expanduser(module)))
module = self.__fix_module(module)
if dir is not None:
modulep = path.join(dir, module)
if name:
o = self.db.get_object('module', name)
if module: o.add_value(module)
o.copy_attrs(defaulto)
if dir is not None: o.set_attr('dir', dir)
if modulep is not None: o.set_attr('path', modulep)
self.add_group('module', name)
else:
id = ModuleObject.genid(module)
o = self.db.get_object('module', id)
if module: o.add_value(module)
o.copy_attrs(defaulto)
if dir is not None: o.set_attr('dir', dir)
if modulep is not None: o.set_attr('path', modulep)
self.add_group('module', id)
def handle_xuinst(self, cmd, *args):
pass
############################################################################
def __setup_woinst(self):
self.db.register_object('wobundle', WobundleObject)
self.db.get_object('wobundle', None)
self.groups['wobundle'] = {}
self.handle_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, cmd, *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('wobundle')
self.attr_otype = 'wobundle'
defaulto = self.db.get_object('wobundle', None)
# traiter les wobundles
for nvs in nvss:
name, values = split_nlist(nvs)
for wobundle in values:
dir = None
wobundlep = None
if wobundle:
if '/' in wobundle:
dir, wobundle = path.split(path.abspath(path.expanduser(wobundle)))
wobundle = self.__fix_wobundle(wobundle)
if dir is not None:
wobundlep = path.join(dir, wobundle)
if name:
o = self.db.get_object('wobundle', name)
if wobundle: o.add_value(wobundle)
o.copy_attrs(defaulto)
if dir is not None: o.set_attr('dir', dir)
if wobundlep is not None: o.set_attr('path', wobundlep)
self.add_group('wobundle', name)
else:
id = WobundleObject.genid(wobundle)
o = self.db.get_object('wobundle', id)
if wobundle: o.add_value(wobundle)
o.copy_attrs(defaulto)
if dir is not None: o.set_attr('dir', dir)
if wobundlep is not None: o.set_attr('path', wobundlep)
self.add_group('wobundle', id)
def handle_rwoinst(self, cmd, *args):
pass
############################################################################
def __setup_toinst(self):
self.db.register_object('webapp', WebappObject)
self.db.get_object('webapp', None)
self.groups['webapp'] = {}
self.handle_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, cmd, *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('webapp')
self.attr_otype = 'webapp'
defaulto = self.db.get_object('webapp', None)
# traiter les webapps
for nvs in nvss:
name, values = split_nlist(nvs)
for webapp in values:
dir = None
webappp = None
if webapp:
if '/' in webapp:
dir, webapp = path.split(path.abspath(path.expanduser(webapp)))
webapp = self.__fix_webapp(webapp)
if dir is not None:
webappp = path.join(dir, webapp)
if name:
o = self.db.get_object('webapp', name)
if webapp: o.add_value(webapp)
o.copy_attrs(defaulto)
if dir is not None: o.set_attr('dir', dir)
if webappp is not None: o.set_attr('path', webappp)
self.add_group('webapp', name)
else:
id = WebappObject.genid(webapp)
o = self.db.get_object('webapp', id)
if webapp: o.add_value(webapp)
o.copy_attrs(defaulto)
if dir is not None: o.set_attr('dir', dir)
if webappp is not None: o.set_attr('path', webappp)
self.add_group('webapp', id)
def handle_rtoinst(self, cmd, *args):
pass
################################################################################
# Programme principal
def run_qdd(o): def run_qdd(o):
# fichier de configuration # fichier de configuration
confname = o.confname or 'deploy' confname = o.confname or 'deploy'
@ -487,9 +818,16 @@ def run_qdd(o):
print "=== predicates" print "=== predicates"
for p in predicates: for p in predicates:
print ' '.join(map(repr, p)) print ' '.join(map(repr, p))
print "=== hosts" otypes = db.known_objects
for ho in db.get_objects('host'): otypes.remove('host')
ho.dump() otypes.insert(0, 'host')
for otype in otypes:
objects = db.get_objects(otype)
if objects:
print "=== %s" % otype
for o in objects:
o.dump()
elif o.action == 'query': elif o.action == 'query':
parser = Parser(db).parse(lexer.get_predicates()) parser = Parser(db).parse(lexer.get_predicates())