diff --git a/ffmap-modify.py b/ffmap-modify.py new file mode 100755 index 0000000..ebffbc1 --- /dev/null +++ b/ffmap-modify.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Knotendaten manuell ändern +Das ist z.B. für ausgeschaltete Knoten interessant, die nur +temporär nicht zur Verfügung stehen. Die können ausgeblendet +werden. +Das ist besser als löschen, weil so die Statistik nicht +verschwindet + +Änderungsprotokoll +================== + +Version Datum Änderung(en) von +-------- ----------- ------------------------------------------------------ ---- +1.0 2017-08-03 Programm in das ffmap-backend Projekt integriert tho + +""" + +import argparse +import configparser +import json +import os +import sys +import glob + +# Einstellungen werden in folgender Reihenfolge verarbeitet +# später gesetzte Werte überschreiben frühere +# 1. im Programm hart codiert +# 2. aus der zentralen Konfigurationsdatei gelesen +# 3. als Kommandozeilenoptionen angegeben +cfg = { + 'cfgfile': '/etc/ffmap/ffmap.cfg', + 'logfile': '/var/log/ffmap.log', + 'loglevel': 2, + 'dest_dir': '/var/lib/ffmap/mapdata', + 'nodedb': '/var/lib/ffmap/nodedb', + 'imgpath': '/var/www/meshviewer/stats/img' +} + +roles_defined = ('node', 'temp', 'mobile', 'offloader', 'service', 'test', 'gate', 'plan', 'hidden') + +def main(cfg): + + # Pfade zu den beteiligten Dateien + nodes_fn = os.path.join(cfg['dest_dir'], 'nodes.json') + nodelist_fn = os.path.join(cfg['dest_dir'], 'nodelist.json') + + # 1. Knotendaten (NodeDB) + # 1.1 Daten laden + try: + with open(nodes_fn, 'r') as nodedb_handle: + nodedb = json.load(nodedb_handle) + except IOError: + print("Error reading nodedb file %s" % nodes_fn) + nodedb = {'nodes': dict()} + # 1.2 Knoten bearbeiten + changed = False + for n in cfg['nodeid']: + if n in nodedb['nodes']: + print("Modify %s in nodedb" % n) + if 'role' in cfg and cfg['role'] in roles_defined: + try: + oldrole = nodedb['nodes'][n]['nodeinfo']['system']['role'] + except KeyError: + oldrole = '' + print(" - change role from '%s' to '%s'" % (oldrole, cfg['role'])) + nodedb['nodes'][n]['nodeinfo']['system']['role'] = cfg['role'] + changed = True + if 'location' in cfg: + print(" - remove location") + # del nodedb['nodes'][n]['nodeinfo']['location'] + changed = True + else: + print("Node %s not found in nodedb" % n) + # 1.3 Geänderte Daten zurückschreiben + if changed: + try: + with open(nodes_fn, 'w') as nodedb_handle: + json.dump(nodedb, nodedb_handle) + except IOError: + print("Error writing nodedb file %s" % nodes_fn) + + # 2. Knotenliste (NodeList) + try: + with open(nodelist_fn, 'r') as nodelist_handle: + nodelist = json.load(nodelist_handle) + except IOError: + print("Error reading nodelist file %s" % nodelist_fn) + nodelist = {'nodelist': dict()} + # 2.1 Knoten bearbeiten + changed = False + ixlist = [] + for nodeid in cfg['nodeid']: + found = False + for ix, node in enumerate(nodelist['nodes']): + if node['id'] == nodeid: + found = True + break + if found: + print("Modify %s in nodelist" % nodeid) + if 'role' in cfg and cfg['role'] in roles_defined: + try: + oldrole = nodelist['nodes'][ix]['role'] + except KeyError: + oldrole = '' + print(" - change role from '%s' to '%s'" % (oldrole, cfg['role'])) + nodelist['nodes'][ix]['role'] = cfg['role'] + if 'location' in cfg: + print(" - remove location") + try: + #del nodelist['nodes'][ix]['position'] + pass + except KeyError: + pass + changed = True + else: + print ("Node %s not found in nodelist" % nodeid) + # 2.3 Geänderte Daten zurückschreiben + if changed: + try: + with open(nodelist_fn, 'w') as nodelist_handle: + json.dump(nodelist, nodelist_handle) + except IOError: + print("Error writing nodelist file %s" % nodelist_fn) + +if __name__ == "__main__": + + # Optionen von der Kommandozeile lesen + parser = argparse.ArgumentParser() + + parser.add_argument('-c', '--config', action='store', + help='Configuration file') + + parser.add_argument('-d', '--dest-dir', action='store', + help='Directory with JSON data files', + required=False) + + parser.add_argument('-i', '--nodeid', metavar='ID', action='store', + nargs='+', required=True, + help='Node id to modify') + + parser.add_argument('-l', '--location', action='store_true', + help='Clear location information (hides node)', + required=False) + + parser.add_argument('-r', '--role', action='store', + help='Set new role', + required=False) + + # TODO + # Optionen was genau gemacht werden soll + # -p Position entfernen, Knoten wird nicht mehr angezeigt + # -r Rolle einstellen + + options = vars(parser.parse_args()) + + + # Konfigurationsdatei einlesen + if options['config']: + cfg['cfgfile'] = options['config'] + config = configparser.ConfigParser(cfg) + # config.read liefert eine Liste der geparsten Dateien + # zurück. Wenn sie leer ist, war z.B. die Datei nicht + # vorhanden + if config.read(cfg['cfgfile']): + if 'global' in config: + cfg['logfile'] = config['global']['logfile'] + cfg['loglevel'] = config['global']['loglevel'] + cfg['dest_dir'] = config['global']['dest_dir'] + else: + print('Config file %s not parsed' % cfg['cfgfile']) + + # Optionen von der Kommandozeile haben höchste Priorität + cfg['nodeid'] = options['nodeid'] + if options['dest_dir']: + cfg['dest_dir'] = options['dest_dir'] + if options['location']: + cfg['location'] = True + if options['role']: + cfg['role'] = options['role'] + + # Alles initialisiert, auf geht's + main(cfg)