sean@4: """ sean@4: This software is part of "Bottledash" sean@4: author: sean engelhardt > sean.engelhardt@intevation.de sean@4: license: GNU >= V2. See LICENSE for details sean@4: """ sean@4: sean@3: from bottle import get, post, request, view, response, route sean@3: from bottle import template, run, static_file, error sean@19: import os.path, sys, configparser, functools, bottle sean@19: import logging sean@0: sean@3: CONFIG_FILE = "dash.conf" sean@8: PATH = os.path.abspath(os.path.dirname(sys.argv[0])) sean@8: CONFIG_PATH = PATH + "/" + CONFIG_FILE sean@8: sean@9: # Create a new list with absolute paths sean@9: MY_TEMPLATE_PATH = [ sean@10: os.path.abspath(os.path.join(os.path.dirname(__file__), './views')), sean@9: ] sean@9: sean@9: # Patch @view() so it uses the customized path list instead of the global one sean@9: view = functools.partial(bottle.view, template_lookup=MY_TEMPLATE_PATH) sean@9: sean@3: tiles = [] sean@7: settings = {} sean@3: sean@7: default_settings = configparser.ConfigParser() sean@19: default_settings['settings'] = {'show_top_bar': False, 'rows': 2} sean@19: sean@19: #logging: sean@19: sean@19: logger = logging.getLogger('myapp') sean@19: hdlr = logging.FileHandler(PATH + '/bottledash.log') sean@19: formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') sean@19: hdlr.setFormatter(formatter) sean@19: logger.addHandler(hdlr) sean@19: logger.setLevel(logging.INFO) sean@7: sean@20: ### debug function: prints the current status of the "tiles" in sean@20: ### the log-file sean@20: def log_tile_status(): sean@20: global tiles sean@20: sean@20: try: sean@20: for tile in tiles: sean@20: logger.info("found tile : " + str(tile)) sean@20: for option in tiles[0]: sean@20: logger.info(str(option) + ' : ' + str(tile[option])) sean@20: except KeyError: sean@20: pass sean@20: sean@20: ###read the config file. usually "dash.conf" sean@3: def read_config(): sean@20: global settings sean@8: # print(PATH) sean@3: sean@8: if os.path.isfile(CONFIG_PATH) == False: sean@3: write_default_config() sean@3: sean@20: # print("read existing config file...") sean@3: config = configparser.ConfigParser() sean@8: config.read(CONFIG_PATH) sean@3: sean@3: for section in config.items(): sean@3: if "tile" in section[0]: sean@3: tiles.append(section[1]) sean@3: sean@20: ###log read tiles sean@20: logger.info("------- read tiles --------") sean@20: log_tile_status() sean@20: sean@7: try: sean@7: if config["settings"]: sean@7: settings = config["settings"] sean@7: else: sean@7: settings = default_settings["settings"] sean@7: except KeyError: sean@7: settings = default_settings["settings"] sean@7: sean@7: ###write the default condfig file if there is none sean@3: def write_default_config(): sean@25: logger.info("No config-file found. create new config file") sean@8: file = open(CONFIG_PATH, "w") sean@3: file.write(""" sean@3: ### bottledash default configuration sean@3: ### created by sean engelhardt >sean.engelhardt@intevation.de sean@7: ### license: GNU GPL >= v2. See LICENSE for details sean@3: ### sean@3: ### Usage: sean@3: ### define the tiles for the dashboard in sections sean@7: sean@7: # Settings: sean@7: # example: sean@7: # -------------------- sean@7: # [settings] sean@32: # show_top_bar=False sean@29: # rows=3 sean@7: # -------------------- sean@7: # options: sean@7: # show_top_bar (True / False) [optional] sean@7: # If True, the dashboard will show the current date and the time on the top sean@7: # if False, the dashboard will not show a top-bar sean@29: # default: False sean@7: # hint: the top-bar has got a height of 5%! sean@29: # sean@29: # rows (integer-number) [recommended] sean@29: # the number or rows for the tiles. Not every number makes sence since sean@29: # you usually cannot scroll on the screen. This only works on the default sean@29: # tile template - it is always possivle to write an own template! sean@29: # example: sean@29: # If there are 4 tiles in 2 rows the reult would be the following: sean@29: # ▢▢ sean@29: # ▢▢ sean@29: # sean@29: # if there are 3 tiles in 3 rows the result would be the following sean@29: # ▢ sean@29: # ▢ sean@29: # ▢ sean@29: # sean@29: # if there re 12 tiles in 3 rows the reult would be the following sean@29: # ▢▢▢▢ sean@29: # ▢▢▢▢ sean@29: # ▢▢▢▢ sean@29: # sean@29: sean@7: sean@7: [settings] sean@14: show_top_bar=False sean@29: rows=3 sean@7: sean@7: # Tiles: sean@7: # example: sean@7: # -------------------- sean@7: # [tile1] sean@7: # type=mon sean@7: # source=192.168.0.2 sean@7: # status=up sean@7: # -------------------- sean@7: # options: sean@29: # type (mon / web_view) [required] sean@7: # tells the program what kind of tile you need. sean@7: # a "mon" tile can be used for IT infrastructure monitoring purposes sean@29: # a "web_view" tile can be used to display a chart sean@7: # default: - sean@7: # sean@7: # source ( or ) [required for mon-types] sean@7: # ONLY FOR MON-Type tiles! sean@7: # tells the tile which resource to watch sean@7: # ONLY FOR DEBUGGING PURPOSE - WILL BE REMOVED LATER sean@7: # simulates up and down events for mon-type-tiles sean@14: # sean@14: # div_name: (identifier) [required for d3js-types] sean@14: # sean@14: # script: (name of a script without extension) [required for d3js-types0] sean@14: # sean@3: [tile1] sean@3: type=mon sean@29: source=red2-Bottle-App sean@28: status=wait sean@3: sean@3: [tile2] sean@3: type=mon sean@29: source=Intevation_Homepage sean@28: status=wait sean@7: sean@7: [tile3] sean@7: type=mon sean@29: source=mpuls-jmd-demo sean@28: status=wait sean@7: sean@7: [tile4] sean@29: type=mon sean@29: source=aise-icn sean@29: status=wait sean@29: sean@29: [tile5] sean@29: type=mon sean@29: source=mpuls-s_bew_prev sean@29: status=wait sean@29: sean@29: [tile6] sean@25: type=web_view sean@14: div_name=techintern sean@14: script=display_issues_techintern sean@3: """) sean@3: file.close() sean@3: sean@3: ##Bottle sean@0: @route('/') sean@15: @view('bottledash_view') sean@0: def call_dashboard(): sean@7: return dict(tiles=tiles, settings=settings) sean@0: sean@20: #wait for post-request which shall inform the system about running services sean@19: @post('/updown') sean@19: def updown(): sean@20: global tiles sean@20: sean@19: service = request.forms.get('service') sean@19: status = request.forms.get('status') sean@20: try: sean@20: for tile in tiles: sean@20: if tile["source"] == str(service): sean@20: tile["status"] = str(status) sean@20: except KeyError: sean@21: logger.info("this tile got no source : " + str(tile)) sean@20: sean@19: sean@19: logger.info('------- new alert --------') sean@19: logger.info('Service : ' + str(service)) sean@19: logger.info('Status : ' + str(status)) sean@24: # log_tile_status() sean@19: sean@25: return ":: " + str(service) + " is " + str(status) + " !" sean@0: sean@21: sean@21: @post('/ask-systemstate') sean@21: def get_systemstate(): sean@21: global tiles sean@21: service = request.forms.get('service') sean@21: sean@25: # print("service: " + service) sean@21: sean@21: try: sean@21: for tile in tiles: sean@21: if tile["source"] == str(service): sean@21: # return "service : " + str(service) + " is " + str(tile["status"]) sean@21: return tile["status"] sean@21: except KeyError: sean@21: return "cannot find the service: " + str(service) sean@21: sean@21: return "did not found anything in my list for : " + service sean@21: sean@21: sean@21: @route('/static/') sean@21: def server_static(filename): sean@21: return static_file(filename, root=PATH + '/static/') sean@21: sean@0: @error(404) sean@0: def error404(error): sean@0: return 'Nothing here, sorry
404' sean@0: sean@3: read_config() sean@7: # print(tiles) sean@25: run(host='localhost', port=8080, debug=False, quiet=True)