Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V0.3.3 #36

Merged
merged 3 commits into from
Mar 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ name: Docker build and push tag

on:
push:
# branches:
# branches:
# - main
# - V0.3*
tags: [ "*_pr", "*_tag", "*_build" ]
tags: [ "*_pr", "*_prerelease", "*_pre-release" ]
# label:
# branches: [ "main" ]

Expand All @@ -14,7 +14,7 @@ env:
REGISTRY: docker.io
# github.repository as <account>/<repo>
IMAGE_NAME: kronos443/nutcase

jobs:
build:
runs-on: ubuntu-latest
Expand Down
21 changes: 16 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@
<h2 id="introduction">A Network UPS Tools (NUT) and APC daemon exporter to pass data to Prometheus and any JSON compatible applications</h2>
<h3 id="key-features">Key features</h3>

* A graphic interface showing key data over time with diagnostic information. <a href="https://github.com/ArthurMitchell42/nutcase/discussions/19"><img src="https://img.shields.io/badge/New_in_V0.3.0_Beta_1-8A2BE2"></a>
* A drop-in replacement for other Prometheus scrapers such as HON95/prometheus-nut-exporter.
* A graphic interface showing key data over time with diagnostic information. <a href="https://github.com/ArthurMitchell42/nutcase/discussions/19"><img src="https://img.shields.io/badge/New_in_V0.3.0-8A2BE2"></a>
* Supports pulling data from NUT and APC servers and formatting the UPS status for the [Prometheus](https://prometheus.io/) logging system
* Supports formatting the UPS status as JSON for use with the beautiful [HomePage](https://gethomepage.dev/) app.
* The JSON output can be used with [Uptime Kuma](https://github.com/louislam/uptime-kuma) and other reporting, alarming and monitoring apps.
* Provides diagnostic information and usage information.
* Supports APC apcupsd servers for graphic display **and** metric scraping - **Use one Prometheus and Grafana dashboard for all servers.**
* Supports filtering of JSON elements to support simple monitoring apps. See [Filtering the JSON](https://github.com/ArthurMitchell42/nutcase/wiki/Using-the-JSON-returned-by-NUTCase#filtering-the-json-) <a href="https://github.com/ArthurMitchell42/nutcase/discussions/19"><img src="https://img.shields.io/badge/New_in_V0.3.0_Beta_4-8A2BE2"></a>
* Supports filtering of JSON elements to support simple monitoring apps. See [Filtering the JSON](https://github.com/ArthurMitchell42/nutcase/wiki/Using-the-JSON-returned-by-NUTCase#filtering-the-json-) <a href="https://github.com/ArthurMitchell42/nutcase/discussions/19"><img src="https://img.shields.io/badge/New_in_V0.3.0-8A2BE2"></a>

![image](https://github.com/ArthurMitchell42/nutcase/assets/82239494/6fbfa4d8-7cbc-4882-9e8e-ac3907e70d9a)

Expand All @@ -33,7 +34,7 @@ NUTCase sits between any nuber of UPS servers, either NUT or APC, and converts t
The JSON is ideal for monitoring or display on dashboard systems such as HomePage.

<h3 id="contents">Contents</h3>

- [Introduction](#introduction)
- [Key features](#key-features)
- [Contents](#contents)
Expand Down Expand Up @@ -73,7 +74,7 @@ The Wiki for usage information and advice:

<h3 id="architectures">Supported Architectures</h3>
<p>
Currently supports 'AMD64', 'ARM64 (ARM64V8)' (suitable for running on docker under Raspberry PI with a 64-bit OS such as Raspberry PI OS 64-bit and Ubuntu 64-bit)'
Currently supports 'AMD64', 'ARM64 (ARM64V8)' (suitable for running on docker under Raspberry PI with a 64-bit OS such as Raspberry PI OS 64-bit and Ubuntu 64-bit)'
<br>

<table>
Expand Down Expand Up @@ -105,6 +106,16 @@ Currently supports 'AMD64', 'ARM64 (ARM64V8)' (suitable for running on docker un
</thead>
<tbody>
<tr>
<td align="left">7/3/2024</td>
<td align="left">0.3.3</td>
<td align="left">Add a check for release updates on the GUI, prep-work for next major release.</td>
</tr>
<tr>
<td align="left">22/2/2024</td>
<td align="left">0.3.2</td>
<td align="left">Correct re-work of cl-count miss reading data</td>
</tr>
<tr>
<td align="left">19/2/2024</td>
<td align="left">0.3.1</td>
<td align="left">Fix for start crash Re missing config file</td>
Expand Down Expand Up @@ -180,7 +191,7 @@ To setup the NUTCase docker container:

<h3 id="nutcase-useage">NUTCase Usage</h3>

For details on useage please see [Running the NUTCase container](https://github.com/ArthurMitchell42/nutcase/wiki/Running-the-NUTCase-container).
For details on useage please see [Running the NUTCase container](https://github.com/ArthurMitchell42/nutcase/wiki/Running-the-NUTCase-container).

[Contents](#contents)

Expand Down
69 changes: 69 additions & 0 deletions nutcase/app/app/api/api_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import json
import urllib
from urllib.error import URLError, HTTPError
from http import HTTPStatus

from flask import current_app

HTML_Update = '''<a href="{url}" target="_blank" class="text-decoration-none text-small badge rounded-pill {style}">{text}</a>'''
HTML_Note = 'bg-success text-light'
HTML_Warn = 'bg-warning text-dark'
HTML_Alert = 'bg-danger text-light'

# ==================================================================================================
# Call a named WebHook
# ==================================================================================================
def Get_Update_String():
Result = ""
try:
URL = current_app.config['GITHUB_API_URL'] + "releases"
Response = urllib.request.urlopen(URL)
HTTP_Code = Response.getcode()
if HTTP_Code != HTTPStatus.OK:
current_app.logger.warning("Github returned {}: {}".format(
HTTP_Code, HTTPStatus(HTTP_Code).name))
return Result
try:
Page_JSON = json.loads(Response.read())
current_app.logger.debugv("JSON Returned by Github {}".format( json.dumps(Page_JSON, indent=4)))

if len(Page_JSON) > 0:
Current_Version = "v" + current_app.config['APP_VERSION']
for rel in Page_JSON:
Tag = rel['tag_name'].lower()
if Tag == Current_Version or \
Tag.startswith(Current_Version + "_"):
break
elif rel['draft'] is True:
continue
elif rel['prerelease'] is False:
Link = rel['html_url']
Ver_Text = rel['tag_name']
if "security" in rel['body'].lower() or "urgent" in rel['body'].lower():
Style_Text = HTML_Alert
else:
Style_Text = HTML_Warn
Result = HTML_Update.format(url=Link, style=Style_Text, text=Ver_Text)
break
elif rel['prerelease'] is True:
Link = rel['html_url']
Ver_Text = rel['tag_name']
Body = rel['body'].lower()
if "security" in Body or "urgent" in Body:
Style_Text = HTML_Alert
else:
Style_Text = HTML_Note
Result = HTML_Update.format(url=Link, style=Style_Text, text=Ver_Text)
continue
except json.decoder.JSONDecodeError as Error:
current_app.logger.warning("JSON Returned by Github could not be parsed {}".format(Error))
except HTTPError as Error:
if hasattr(Error, 'reason'):
current_app.logger.warning("Failed to call Github API. Reason: {}".format(Error.reason))
if hasattr(Error, 'code'):
current_app.logger.warning("Github couldn't fulfill request Error: {}".format(
Error.code))
except URLError as Error:
if hasattr(Error, 'reason'):
current_app.logger.warning("Failed to call Github API. Reason: {}".format(Error.reason))
return Result
42 changes: 42 additions & 0 deletions nutcase/app/app/api/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from app.utils import scrape
from app.utils import gui_data_format
from app.api import api_utils

# ====================================================================================
# Serve the end-point /api/status
Expand Down Expand Up @@ -58,3 +59,44 @@ def route_default():
break

return Result

# ====================================================================================
# Serve the end-point /api/events - Returns JSON for the event icons
# on the tool bar.
# ====================================================================================
@bp.route('/events')
def route_events():
Result = {}
Overflow = "99+"
if current_app.config['APP_STATUS_FLAGS']['info'] > 100:
Result['info_flag_val'] = Overflow
elif current_app.config['APP_STATUS_FLAGS']['info'] > 0:
Result['info_flag_val'] = str(current_app.config['APP_STATUS_FLAGS']['info'])
else:
Result['info_flag_val'] = ""
if current_app.config['APP_STATUS_FLAGS']['warning'] > 100:
Result['warn_flag_val'] = Overflow
elif current_app.config['APP_STATUS_FLAGS']['warning'] > 0:
Result['warn_flag_val'] = str(current_app.config['APP_STATUS_FLAGS']['warning'])
else:
Result['warn_flag_val'] = ""
if current_app.config['APP_STATUS_FLAGS']['alert'] > 100:
Result['alert_flag_val'] = Overflow
elif current_app.config['APP_STATUS_FLAGS']['alert'] > 0:
Result['alert_flag_val'] = str(current_app.config['APP_STATUS_FLAGS']['alert'])
else:
Result['alert_flag_val'] = ""

return Result

# ====================================================================================
# Serve the end-point /api/events - Returns JSON for the event icons
# on the tool bar.
# ====================================================================================
@bp.route('/appupdate')
def route_appupdate():
Result = {}

Result['app_update'] = api_utils.Get_Update_String()

return Result
94 changes: 76 additions & 18 deletions nutcase/app/app/templates/bootstrap/bs5_base_top_navbar.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,16 @@

<!-- Top nav bar -->
<aside id="id-nav-bar">
<nav class="navbar navbar-expand-md bg-body-tertiary">
<a class="navbar-brand px-3" href="https://github.com/ArthurMitchell42/nutcase"><img src="/static/favicon.ico" style="width:22px;height:22px;"> {{ config['APP_NAME'] }} <span class="fs-6 text-muted">- V{{ config['APP_VERSION'] }} {% if config['CONFIG_SET'] != 'Prd' %} {{ config['CONFIG_SET'] }} {% endif %}</span></a>
<nav class="navbar navbar-expand-md bg-body-tertiary">
<a class="navbar-brand px-3" href="https://github.com/ArthurMitchell42/nutcase"><img src="/static/favicon.ico" style="width:22px;height:22px;"> {{ config['APP_NAME'] }} <span class="fs-6 text-muted">- V{{ config['APP_VERSION'] }} {% if config['CONFIG_SET'] != 'Prd' %} {{ config['CONFIG_SET'] }} {% endif %}</span></a><span id="ph_update"></span>
<div id="ph_debug"></div>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse justify-content-end" id="navbarNavDropdown">
<ul class="navbar-nav">
<li class="nav-item" data-bs-toggle="tooltip_navbar" data-bs-placement="bottom" data-bs-html="true" data-bs-title="<small>Click for warning</small>">
<a class="nav-link d-none" data-bs-toggle="modal" data-bs-target="#warningModal" role="button" id="icon-navbar-warning"><i class="bi bi-exclamation-triangle-fill text-warning"></i></a>
<a class="nav-link d-none" data-bs-toggle="modal" data-bs-target="#warningModal" role="button" id="icon-navbar-warning"><i class="bi bi-exclamation-triangle-fill text-warning"></i></a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Expand All @@ -66,7 +66,7 @@
<a class="nav-link" href="{{ url_for('main.route_log') }}" data-bs-toggle="tooltip_navbar" data-bs-placement="bottom" data-bs-html="true" data-bs-title="<small>Log files</small>"><i class="bi bi-clipboard2-pulse-fill"></i></a>
</li>
<li class="nav-item" data-bs-toggle="tooltip_navbar" data-bs-placement="bottom" data-bs-html="true" data-bs-title="<small>Details</small>">
<a class="nav-link" data-bs-toggle="collapse" href="#info-data" role="button"><i class="bi bi-info-circle-fill"></i></a>
<a class="nav-link" data-bs-toggle="collapse" href="#info-data" role="button"><i class="bi bi-info-circle-fill"></i></a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://github.com/ArthurMitchell42/nutcase/wiki" data-bs-toggle="tooltip_navbar" data-bs-placement="bottom" data-bs-html="true" data-bs-title="<small>Manual (GitHub)</small>"><i class="bi bi-github"></i></a>
Expand All @@ -75,7 +75,7 @@
<a class="nav-link" href="{{ url_for('main.route_help') }}"><i class="bi bi-book"></i></a>
</li>
<li class="nav-item border-end border-secondary-subtle" data-bs-toggle="tooltip_navbar" data-bs-placement="bottom" data-bs-html="true" data-bs-title="<small>Page help</small>">
<a class="nav-link" data-bs-toggle="modal" data-bs-target="#helpModal" role="button"><i class="bi bi-question-circle-fill"></i></a>
<a class="nav-link" data-bs-toggle="modal" data-bs-target="#helpModal" role="button"><i class="bi bi-question-circle-fill"></i></a>
</li>


Expand All @@ -84,11 +84,11 @@
<span class="nav-link">
<i class="bi bi-info-circle position-relative header-icons">
{% if config['APP_STATUS_FLAGS'] %}
{% if config['APP_STATUS_FLAGS'].info > 0 %}
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill header-icon-pill bg-success">
{{ config['APP_STATUS_FLAGS'].info }}
<!-- { % if config['APP_STATUS_FLAGS'].info > 0 %} -->
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill header-icon-pill bg-success" id="info-flag-val">
{% if config['APP_STATUS_FLAGS'].info > 0 %}{{ config['APP_STATUS_FLAGS'].info }}{% endif %}
</span>
{% endif %}
<!-- { % endif %} -->
{% endif %}
</i>
</span>
Expand All @@ -99,11 +99,11 @@
<span class="nav-link">
<i class="bi bi-exclamation-triangle position-relative header-icons">
{% if config['APP_STATUS_FLAGS'] %}
{% if config['APP_STATUS_FLAGS'].warning > 0 %}
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill header-icon-pill bg-warning" style="font-size: 11px;color: black;">
{{ config['APP_STATUS_FLAGS'].warning }}
<!-- { % if config['APP_STATUS_FLAGS'].warning > 0 %} -->
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill header-icon-pill bg-warning" id="warn_flag_val" style="font-size: 11px;color: black;">
{% if config['APP_STATUS_FLAGS'].warning > 0 %}{{ config['APP_STATUS_FLAGS'].warning }}{% endif %}
</span>
{% endif %}
<!-- { % endif %} -->
{% endif %}
</i>
</span>
Expand All @@ -114,11 +114,11 @@
<span class="nav-link">
<i class="bi bi-bell position-relative header-icons">
{% if config['APP_STATUS_FLAGS'] %}
{% if config['APP_STATUS_FLAGS'].alert > 0 %}
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill header-icon-pill bg-danger" style="font-size: 11px;">
{{ config['APP_STATUS_FLAGS'].alert }}
<!-- { % if config['APP_STATUS_FLAGS'].alert > 0 %} -->
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill header-icon-pill bg-danger" id="alert_flag_val" style="font-size: 11px;">
{% if config['APP_STATUS_FLAGS'].alert > 0 %}{{ config['APP_STATUS_FLAGS'].alert }}{% endif %}
</span>
{% endif %}
<!-- { % endif %} -->
{% endif %}
</i>
</span>
Expand Down Expand Up @@ -174,7 +174,7 @@
<!-- { { moment.lang(g.locale) } } -->

<script type="text/javascript" src="/static/jquery/jquery.min.js"></script>
<script type="text/javascript" src="/static/popper/popper.min.js"></script>
<script type="text/javascript" src="/static/popper/popper.min.js"></script>
<script type="text/javascript" src="/static/bootstrap/js/bootstrap.min.js"></script>
<!-- <script type="text/javascript" src="/static/feather/feather.js"></script>
<script>feather.replace()</script>-->
Expand Down Expand Up @@ -209,13 +209,71 @@
// .always(function() {
// console.log( "complete" );
// });

API_URL = "/api/appupdate"
$.getJSON( API_URL, function(data) {
document.getElementById("ph_update").innerHTML = data.app_update
})
.done(function(data) {
// console.log( "B5_Base devices success" );
// document.getElementById("ph-device-menu").innerHTML = data.device_menu
})
.fail(function() {
console.log("No update info after API call " + API_URL)
// document.getElementById("ph-main-status").innerHTML = HTML_No_Default
return
});
// .always(function() {
// console.log( "complete" );
// });

})

//======================================
// Enable all tool tips
const tooltipTriggerList_navbar = document.querySelectorAll('[data-bs-toggle="tooltip_navbar"]')
const tooltipList_navbar = [...tooltipTriggerList_navbar].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl))

</script>

<script>
//=================================================
// Update the current data
//=================================================
function updateEventIcons() {
API_Call = '/api/events';

$.getJSON(API_Call, function(data){
if (Object.keys(data).length == 0) {
console.log( "Call to " + API_Call + " returned no data" );
//document.getElementById("ph-main-status").innerHTML = HTML_No_Server_Comms
//heartBeat("error")
return
}
//heartBeat("beat")
//trigger_time_bar()
})
.done(function(data) {
if (Object.keys(data).length == 0) { return }

document.getElementById("info-flag-val").innerHTML = data.info_flag_val;
document.getElementById("warn_flag_val").innerHTML = data.warn_flag_val;
document.getElementById("alert_flag_val").innerHTML = data.alert_flag_val;
})
.fail(function() {
console.log( "Call to " + API_Call + " failed" );
//document.getElementById("ph-main-status").innerHTML = HTML_No_Nutcase_Comms
//heartBeat("error")
});
//.always(function() {
// console.log( "complete" );
// trigger_time_bar()
//});
}

setInterval('updateEventIcons()', 1000 * 10); // Units: ms


</script>

{% block script %}{% endblock %}
Expand Down
2 changes: 1 addition & 1 deletion nutcase/app/app/templates/main/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ <h5 class="modal-title" id="warningLabel">Warning</h5>
runtime_chart_obj.update();
})
.fail(function() {
console.log( "Call /api/status failed" );
console.log( "Call to " + API_Call + " failed" );
document.getElementById("ph-main-status").innerHTML = HTML_No_Nutcase_Comms
heartBeat("error")
});
Expand Down
3 changes: 2 additions & 1 deletion nutcase/app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ class Config(object):
# Core info
# ==================================================================
APP_NAME = 'NUTCase'
APP_VERSION = '0.3.2'
APP_VERSION = '0.3.3'
GITHUB_API_URL = "https://api.github.com/repos/ArthurMitchell42/nutcase/"

# ==================================================================
# Configuration file
Expand Down
Loading
Loading