From e6ed81c0919ecc662741b6581529abde6157db05 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Fri, 14 Feb 2020 09:36:18 +0100 Subject: [PATCH 001/884] fix Helpers.get_distro for Debian sid https://github.com/HenriWahl/Nagstamon/issues/616 --- Nagstamon/Helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/Helpers.py b/Nagstamon/Helpers.py index f4e6ca66e..d4fb749db 100644 --- a/Nagstamon/Helpers.py +++ b/Nagstamon/Helpers.py @@ -454,7 +454,7 @@ def get_distro(): for property in os_release_file.read_text().splitlines(): key, value = property.split('=', 1) os_release_dict[key] = value.strip('"').strip("'") - return (os_release_dict['ID'], os_release_dict['VERSION_ID'], os_release_dict['NAME']) + return (os_release_dict.get('ID'), os_release_dict.get('VERSION_ID'), os_release_dict.get('NAME')) else: return False else: From f1b74f39d0f9fda4358b1165bdd970f5ac9eaf0c Mon Sep 17 00:00:00 2001 From: momiji <momiji@users.noreply.github.com> Date: Sat, 29 Feb 2020 17:01:28 +0100 Subject: [PATCH 002/884] Return exception instead of exiting --- Nagstamon/thirdparty/zabbix_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/thirdparty/zabbix_api.py b/Nagstamon/thirdparty/zabbix_api.py index 00108348d..c776b5417 100644 --- a/Nagstamon/thirdparty/zabbix_api.py +++ b/Nagstamon/thirdparty/zabbix_api.py @@ -276,7 +276,7 @@ def do_request(self, json_obj): jobj = json.loads(reads.decode('utf-8')) except ValueError as msg: print ("unable to decode. returned string: %s" % reads) - sys.exit(-1) + raise ZabbixAPIException("Unable to decode answer") self.debug(logging.DEBUG, "Response Body: " + str(jobj)) self.id += 1 From 0fa988e0f19cbf802a4672d656e722328732640c Mon Sep 17 00:00:00 2001 From: momiji <momiji@users.noreply.github.com> Date: Sat, 29 Feb 2020 17:07:19 +0100 Subject: [PATCH 003/884] change login to repeat until success --- Nagstamon/Servers/Zabbix.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Nagstamon/Servers/Zabbix.py b/Nagstamon/Servers/Zabbix.py index d2d7c7d68..13393c064 100644 --- a/Nagstamon/Servers/Zabbix.py +++ b/Nagstamon/Servers/Zabbix.py @@ -84,9 +84,12 @@ def __init__(self, **kwds): def _login(self): try: - self.zapi = ZabbixAPI(server=self.monitor_url, path="", log_level=self.log_level, - validate_certs=self.validate_certs) - self.zapi.login(self.username, self.password) + # create ZabbixAPI if not yet created + if self.zapi is None: + self.zapi = ZabbixAPI(server=self.monitor_url, path="", log_level=self.log_level, validate_certs=self.validate_certs) + # login if not yet logged in, or if login was refused previously + if not self.zapi.logged_in(): + self.zapi.login(self.username, self.password) except ZabbixAPIException: result, error = self.Error(sys.exc_info()) return Result(result=result, error=error) @@ -112,8 +115,7 @@ def _get_status(self): nagitems = {"services": [], "hosts": []} # Create URLs for the configured filters - if self.zapi is None: - self._login() + self._login() try: # ========================================= @@ -458,8 +460,7 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi for s in all_services: triggerids.append(s) - if self.zapi is None: - self._login() + self._login() for triggerid in triggerids: events = [] @@ -544,4 +545,4 @@ def nagiosify_service(self, service): if (" on " or " is ") in service: for separator in [" on ", " is "]: service = service.split(separator)[0] - return(service) \ No newline at end of file + return(service) From 8ab8683b266d95e80286ffbf4ac81f98d0831d11 Mon Sep 17 00:00:00 2001 From: Stephan Schwarz <schwarz@e-spirit.com> Date: Fri, 10 Apr 2020 12:35:53 +0200 Subject: [PATCH 004/884] * added prometheus integration (untested) --- Nagstamon/Servers/Prometheus.py | 174 ++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 Nagstamon/Servers/Prometheus.py diff --git a/Nagstamon/Servers/Prometheus.py b/Nagstamon/Servers/Prometheus.py new file mode 100644 index 000000000..6aabb098c --- /dev/null +++ b/Nagstamon/Servers/Prometheus.py @@ -0,0 +1,174 @@ +# encoding: utf-8 + +# Nagstamon - Nagios status monitor for your desktop +# Copyright (C) 2008-2014 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +# Initial implementation by Stephan Schwarz (@stearz) +# +# This Server class connects against Prometheus. +# The monitor URL in the setup should be something like +# http://prometheus.example.com +# +# Status/TODOs: +# +# * Currently we can only fetch and display alerts from Prometheus. +# In the future it would be great to be able to interract with +# alertmanager, so that managing alerts (e.g. silencing) is possible + +import sys +import urllib.request, urllib.parse, urllib.error +import copy +import pprint +import json + +from datetime import datetime, timedelta +from ast import literal_eval + +from Nagstamon.Config import conf +from Nagstamon.Objects import (GenericHost, + GenericService, + Result) +from Nagstamon.Servers.Generic import GenericServer +from Nagstamon.Helpers import (HumanReadableDurationFromSeconds, + webbrowser_open) + + +class PrometheusServer(GenericServer): + """ + special treatment for Prometheus API + """ + TYPE = 'Prometheus' + + # Prometheus actions are limited to visiting the monitor for now + MENU_ACTIONS = ['Monitor'] + BROWSER_URLS= {'monitor': '$MONITOR$/alerts'} + + + def init_HTTP(self): + """ + things to do if HTTP is not initialized + """ + GenericServer.init_HTTP(self) + + # prepare for JSON + self.session.headers.update({'Accept': 'application/json', + 'Content-Type': 'application/json'}) + + + def init_config(self): + """ + dummy init_config, called at thread start + """ + pass + + + def get_start_end(self, host): + """ + Set a default of starttime of "now" and endtime is "now + 24 hours" + directly from web interface + """ + start = datetime.now() + end = datetime.now() + timedelta(hours=24) + + return str(start.strftime("%Y-%m-%d %H:%M:%S")), str(end.strftime("%Y-%m-%d %H:%M:%S")) + + + def _set_downtime(self, host, service, author, comment, fixed, start_time, end_time, hours, minutes): + """ + to be implemented in a future release + """ + pass + + + def _get_status(self): + """ + Get status from Prometheus Server + """ + # get all alerts from the API server + try: + result = self.FetchURL(self.monitor_url + "/api/v1/alerts", giveback="raw") + data, error, status_code = json.loads(result.result), result.error, result.status_code + + # check if any error occured + errors_occured = self.check_for_error(data, error, status_code) + # if there are errors return them + if errors_occured != False: + return(errors_occured) + + if conf.debug_mode: + self.Debug(server=self.get_name(), debug="Fetched JSON: " + pprint.pformat(data)) + +### Debug Ausgaben nutzen und dann geht's weiter + + for alert in data["alerts"]: + self.new_hosts[alert["labels"]["job"] = GenericHost() + self.new_hosts[alert["labels"]["job"]].name = str(alert["labels"]["job"]) + self.new_hosts[alert["labels"]["job"]].server = self.name + +# self.new_hosts[alert["labels"]["job"]].status = str(alert["severity"].upper()) +# self.new_hosts[alert["labels"]["job"]].status_type = str(alert["state_type"]) +# self.new_hosts[alert["labels"]["job"]].last_check = datetime.fromtimestamp(int(alert["last_check"])).strftime("%Y-%m-%d %H:%M:%S %z") +# self.new_hosts[alert["labels"]["job"]].duration = HumanReadableDurationFromSeconds(alert["state_duration"]) +# self.new_hosts[alert["labels"]["job"]].attempt = alert["current_check_attempt"]+ "/" + alert["max_check_attempts"] +# self.new_hosts[alert["labels"]["job"]].status_information = alert["value"].replace("\n", " ") + +# # if host is in downtime add it to known maintained hosts +# if alert['downtime'] != "0": +# self.new_hosts[alert["labels"]["job"]].scheduled_downtime = True +# #if host.has_key("acknowledged"): +# if 'acknowledged' in host: +# self.new_hosts[alert["labels"]["job"]].acknowledged = True +# #if host.has_key("flapping"): +# if 'flapping' in host: +# self.new_hosts[alert["labels"]["job"]].flapping = True + + #services + self.new_hosts[alert["labels"]["job"]].services[alert["labels"]["alertname"]] = PrometheusService() + self.new_hosts[alert["labels"]["job"]].services[alert["labels"]["alertname"]].host = str(alert["labels"]["job"]) + self.new_hosts[alert["labels"]["job"]].services[alert["labels"]["alertname"]].name = services[alert["labels"]["alertname"]] + self.new_hosts[alert["labels"]["job"]].services[alert["labels"]["alertname"]].server = self.name + + # states come in lower case from Opsview + self.new_hosts[alert["labels"]["job"]].services[alert["labels"]["alertname"]].status = services[alert["severity"]].upper() +# self.new_hosts[alert["labels"]["job"]].services[alert["labels"]["alertname"]].status_type = service["state_type"] +# self.new_hosts[alert["labels"]["job"]].services[alert["labels"]["alertname"]].last_check = datetime.fromtimestamp(int(service["last_check"])).strftime("%Y-%m-%d %H:%M:%S %z") +# self.new_hosts[alert["labels"]["job"]].services[alert["labels"]["alertname"]].duration = HumanReadableDurationFromSeconds(service["state_duration"]) +# self.new_hosts[alert["labels"]["job"]].services[alert["labels"]["alertname"]].attempt = service["current_check_attempt"]+ "/" + service["max_check_attempts"] + self.new_hosts[alert["labels"]["job"]].services[alert["labels"]["alertname"]].status_information = service[alert["value"]].replace("\n", " ") +# if service['downtime'] != '0': +# self.new_hosts[alert["labels"]["job"]].services[alert["labels"]["alertname"]].scheduled_downtime = True +# #if service.has_key("acknowledged"): +# if 'acknowledged' in service: +# self.new_hosts[alert["labels"]["job"]].services[alert["labels"]["alertname"]].acknowledged = True +# #f service.has_key("flapping"): +# if 'flapping' in service: +# self.new_hosts[alert["labels"]["job"]].services[alert["labels"]["alertname"]].flapping = True +# # extra opsview id for service, needed for submitting check results +# self.new_hosts[alert["labels"]["job"]].services[alert["labels"]["alertname"]].service_object_id = service["service_object_id"] + + except: + # set checking flag back to False + self.isChecking = False + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) + + #dummy return in case all is OK + return Result() + + + def open_monitor_webpage(self, host, service): + webbrowser_open('%s' % (self.monitor_url)) From ff0a60a038a65fe7206c1c74b81516d349ac636f Mon Sep 17 00:00:00 2001 From: Stephan Schwarz <schwarz@e-spirit.com> Date: Sun, 12 Apr 2020 11:57:10 +0200 Subject: [PATCH 005/884] * added PrometheusServer into __init__.py --- Nagstamon/Servers/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Servers/__init__.py b/Nagstamon/Servers/__init__.py index 3ce14ca98..f2483b675 100644 --- a/Nagstamon/Servers/__init__.py +++ b/Nagstamon/Servers/__init__.py @@ -41,6 +41,7 @@ from Nagstamon.Servers.Monitos4x import Monitos4xServer from Nagstamon.Servers.SnagView3 import SnagViewServer from Nagstamon.Servers.Sensu import SensuServer +from Nagstamon.Servers.Prometheus import PrometheusServer from Nagstamon.Config import conf @@ -196,9 +197,10 @@ def create_server(server=None): # moved registration process here because of circular dependencies -for server in (CentreonServer, IcingaServer, IcingaWeb2Server, MultisiteServer, NagiosServer, +for server in (CentreonServer, IcingaServer, IcingaWeb2Server, MultisiteServer, NagiosSrver, Op5MonitorServer, OpsviewServer, ThrukServer, ZabbixServer, SensuServer, - LivestatusServer, ZenossServer, Monitos3Server, Monitos4xServer, SnagViewServer): + LivestatusServer, ZenossServer, Monitos3Server, Monitos4xServer, SnagViewServer, + PrometheusServer): register_server(server) # create servers From 4386e0d6e5e63ed3f72fce47f54db2df34eaab1b Mon Sep 17 00:00:00 2001 From: Stephan Schwarz <schwarz@e-spirit.com> Date: Tue, 14 Apr 2020 14:33:14 +0200 Subject: [PATCH 006/884] * removed commented lines --- Nagstamon/Servers/Prometheus.py | 37 +-------------------------------- Nagstamon/Servers/__init__.py | 2 +- 2 files changed, 2 insertions(+), 37 deletions(-) diff --git a/Nagstamon/Servers/Prometheus.py b/Nagstamon/Servers/Prometheus.py index 6aabb098c..e4447b09f 100644 --- a/Nagstamon/Servers/Prometheus.py +++ b/Nagstamon/Servers/Prometheus.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2014 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2020 Henri Wahl <h.wahl@ifw-dresden.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -112,53 +112,18 @@ def _get_status(self): if conf.debug_mode: self.Debug(server=self.get_name(), debug="Fetched JSON: " + pprint.pformat(data)) -### Debug Ausgaben nutzen und dann geht's weiter - for alert in data["alerts"]: self.new_hosts[alert["labels"]["job"] = GenericHost() self.new_hosts[alert["labels"]["job"]].name = str(alert["labels"]["job"]) self.new_hosts[alert["labels"]["job"]].server = self.name -# self.new_hosts[alert["labels"]["job"]].status = str(alert["severity"].upper()) -# self.new_hosts[alert["labels"]["job"]].status_type = str(alert["state_type"]) -# self.new_hosts[alert["labels"]["job"]].last_check = datetime.fromtimestamp(int(alert["last_check"])).strftime("%Y-%m-%d %H:%M:%S %z") -# self.new_hosts[alert["labels"]["job"]].duration = HumanReadableDurationFromSeconds(alert["state_duration"]) -# self.new_hosts[alert["labels"]["job"]].attempt = alert["current_check_attempt"]+ "/" + alert["max_check_attempts"] -# self.new_hosts[alert["labels"]["job"]].status_information = alert["value"].replace("\n", " ") - -# # if host is in downtime add it to known maintained hosts -# if alert['downtime'] != "0": -# self.new_hosts[alert["labels"]["job"]].scheduled_downtime = True -# #if host.has_key("acknowledged"): -# if 'acknowledged' in host: -# self.new_hosts[alert["labels"]["job"]].acknowledged = True -# #if host.has_key("flapping"): -# if 'flapping' in host: -# self.new_hosts[alert["labels"]["job"]].flapping = True - - #services self.new_hosts[alert["labels"]["job"]].services[alert["labels"]["alertname"]] = PrometheusService() self.new_hosts[alert["labels"]["job"]].services[alert["labels"]["alertname"]].host = str(alert["labels"]["job"]) self.new_hosts[alert["labels"]["job"]].services[alert["labels"]["alertname"]].name = services[alert["labels"]["alertname"]] self.new_hosts[alert["labels"]["job"]].services[alert["labels"]["alertname"]].server = self.name - # states come in lower case from Opsview self.new_hosts[alert["labels"]["job"]].services[alert["labels"]["alertname"]].status = services[alert["severity"]].upper() -# self.new_hosts[alert["labels"]["job"]].services[alert["labels"]["alertname"]].status_type = service["state_type"] -# self.new_hosts[alert["labels"]["job"]].services[alert["labels"]["alertname"]].last_check = datetime.fromtimestamp(int(service["last_check"])).strftime("%Y-%m-%d %H:%M:%S %z") -# self.new_hosts[alert["labels"]["job"]].services[alert["labels"]["alertname"]].duration = HumanReadableDurationFromSeconds(service["state_duration"]) -# self.new_hosts[alert["labels"]["job"]].services[alert["labels"]["alertname"]].attempt = service["current_check_attempt"]+ "/" + service["max_check_attempts"] self.new_hosts[alert["labels"]["job"]].services[alert["labels"]["alertname"]].status_information = service[alert["value"]].replace("\n", " ") -# if service['downtime'] != '0': -# self.new_hosts[alert["labels"]["job"]].services[alert["labels"]["alertname"]].scheduled_downtime = True -# #if service.has_key("acknowledged"): -# if 'acknowledged' in service: -# self.new_hosts[alert["labels"]["job"]].services[alert["labels"]["alertname"]].acknowledged = True -# #f service.has_key("flapping"): -# if 'flapping' in service: -# self.new_hosts[alert["labels"]["job"]].services[alert["labels"]["alertname"]].flapping = True -# # extra opsview id for service, needed for submitting check results -# self.new_hosts[alert["labels"]["job"]].services[alert["labels"]["alertname"]].service_object_id = service["service_object_id"] except: # set checking flag back to False diff --git a/Nagstamon/Servers/__init__.py b/Nagstamon/Servers/__init__.py index f2483b675..dbef006c1 100644 --- a/Nagstamon/Servers/__init__.py +++ b/Nagstamon/Servers/__init__.py @@ -197,7 +197,7 @@ def create_server(server=None): # moved registration process here because of circular dependencies -for server in (CentreonServer, IcingaServer, IcingaWeb2Server, MultisiteServer, NagiosSrver, +for server in (CentreonServer, IcingaServer, IcingaWeb2Server, MultisiteServer, NagiosServer, Op5MonitorServer, OpsviewServer, ThrukServer, ZabbixServer, SensuServer, LivestatusServer, ZenossServer, Monitos3Server, Monitos4xServer, SnagViewServer, PrometheusServer): From b453643fc0ccef16efeae9b215aae45b3ceeae14 Mon Sep 17 00:00:00 2001 From: Stephan Schwarz <schwarz@e-spirit.com> Date: Wed, 15 Apr 2020 22:23:16 +0200 Subject: [PATCH 007/884] * working version --- Nagstamon/Config.py | 2 +- Nagstamon/Servers/Prometheus.py | 73 +++++- Nagstamon/Servers/__init__.py | 424 ++++++++++++++++---------------- 3 files changed, 273 insertions(+), 226 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index beb146db0..5d6227835 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -123,7 +123,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.5-20200113' + VERSION = '3.5-20200415' WEBSITE = 'https://nagstamon.ifw-dresden.de' COPYRIGHT = '©2008-2020 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/Servers/Prometheus.py b/Nagstamon/Servers/Prometheus.py index e4447b09f..314ca19c3 100644 --- a/Nagstamon/Servers/Prometheus.py +++ b/Nagstamon/Servers/Prometheus.py @@ -35,7 +35,9 @@ import pprint import json -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone +import dateutil.parser + from ast import literal_eval from Nagstamon.Config import conf @@ -46,6 +48,12 @@ from Nagstamon.Helpers import (HumanReadableDurationFromSeconds, webbrowser_open) +class PrometheusService(GenericService): + """ + add Prometheus specific service property to generic service class + """ + service_object_id = "" + class PrometheusServer(GenericServer): """ @@ -87,6 +95,26 @@ def get_start_end(self, host): return str(start.strftime("%Y-%m-%d %H:%M:%S")), str(end.strftime("%Y-%m-%d %H:%M:%S")) + def _get_duration(self, timestring): + """ + calculates the duration (delta) from Prometheus' activeAt (ISO8601 format) until now + an returns a human friendly string + """ + time_object = dateutil.parser.parse(timestring) + duration = datetime.now(timezone.utc) - time_object + h = int(duration.seconds / 3600) + m = int(duration.seconds % 3600 / 60) + s = int(duration.seconds % 60) + if duration.days > 0: + return "%sd %sh %02dm %02ds" % (duration.days, h, m, s) + elif h > 0: + return "%sh %02dm %02ds" % (h, m, s) + elif m > 0: + return "%02dm %02ds" % (m, s) + else: + return "%02ds" % (s) + + def _set_downtime(self, host, service, author, comment, fixed, start_time, end_time, hours, minutes): """ to be implemented in a future release @@ -112,18 +140,37 @@ def _get_status(self): if conf.debug_mode: self.Debug(server=self.get_name(), debug="Fetched JSON: " + pprint.pformat(data)) - for alert in data["alerts"]: - self.new_hosts[alert["labels"]["job"] = GenericHost() - self.new_hosts[alert["labels"]["job"]].name = str(alert["labels"]["job"]) - self.new_hosts[alert["labels"]["job"]].server = self.name - - self.new_hosts[alert["labels"]["job"]].services[alert["labels"]["alertname"]] = PrometheusService() - self.new_hosts[alert["labels"]["job"]].services[alert["labels"]["alertname"]].host = str(alert["labels"]["job"]) - self.new_hosts[alert["labels"]["job"]].services[alert["labels"]["alertname"]].name = services[alert["labels"]["alertname"]] - self.new_hosts[alert["labels"]["job"]].services[alert["labels"]["alertname"]].server = self.name - - self.new_hosts[alert["labels"]["job"]].services[alert["labels"]["alertname"]].status = services[alert["severity"]].upper() - self.new_hosts[alert["labels"]["job"]].services[alert["labels"]["alertname"]].status_information = service[alert["value"]].replace("\n", " ") + for alert in data["data"]["alerts"]: + if conf.debug_mode: + self.Debug(server=self.get_name(), debug="Processing Alert: " + pprint.pformat(alert)) + + if alert["labels"]["severity"] != "none": + if "pod_name" in alert["labels"]: + hostname = alert["labels"]["pod_name"] + elif "namespace" in alert["labels"]: + hostname = alert["labels"]["namespace"] + else: + hostname = "unknown" + + self.new_hosts[hostname] = GenericHost() + self.new_hosts[hostname].name = str(hostname) + self.new_hosts[hostname].server = self.name + + if "alertname" in alert["labels"]: + servicename = alert["labels"]["alertname"] + else: + servicename = "unknown" + + self.new_hosts[hostname].services[servicename] = PrometheusService() + self.new_hosts[hostname].services[servicename].host = str(hostname) + self.new_hosts[hostname].services[servicename].name = servicename + self.new_hosts[hostname].services[servicename].server = self.name + + self.new_hosts[hostname].services[servicename].status = alert["labels"]["severity"].upper() + self.new_hosts[hostname].services[servicename].last_check = "n/a" + self.new_hosts[hostname].services[servicename].attempt = alert["state"].upper() + self.new_hosts[hostname].services[servicename].duration = str(self._get_duration(alert["activeAt"])) + self.new_hosts[hostname].services[servicename].status_information = alert["annotations"]["message"].replace("\n", " ") except: # set checking flag back to False diff --git a/Nagstamon/Servers/__init__.py b/Nagstamon/Servers/__init__.py index dbef006c1..1fb36c5cc 100644 --- a/Nagstamon/Servers/__init__.py +++ b/Nagstamon/Servers/__init__.py @@ -1,212 +1,212 @@ -# encoding: utf-8 - -# Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2020 Henri Wahl <h.wahl@ifw-dresden.de> et al. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - -"""Module Servers""" - -import urllib.request -import urllib.error -import urllib.parse - -from collections import OrderedDict - -# load all existing server types -from Nagstamon.Servers.Nagios import NagiosServer -from Nagstamon.Servers.Centreon import CentreonServer -from Nagstamon.Servers.Icinga import IcingaServer -from Nagstamon.Servers.IcingaWeb2 import IcingaWeb2Server -from Nagstamon.Servers.Multisite import MultisiteServer -from Nagstamon.Servers.op5Monitor import Op5MonitorServer -from Nagstamon.Servers.Opsview import OpsviewServer -from Nagstamon.Servers.Thruk import ThrukServer -from Nagstamon.Servers.Zabbix import ZabbixServer -from Nagstamon.Servers.Livestatus import LivestatusServer -from Nagstamon.Servers.Zenoss import ZenossServer -from Nagstamon.Servers.Monitos3 import Monitos3Server -from Nagstamon.Servers.Monitos4x import Monitos4xServer -from Nagstamon.Servers.SnagView3 import SnagViewServer -from Nagstamon.Servers.Sensu import SensuServer -from Nagstamon.Servers.Prometheus import PrometheusServer - -from Nagstamon.Config import conf - -from Nagstamon.Helpers import STATES - -# dictionary for servers -servers = OrderedDict() - -# contains dict with available server classes -# key is type of server, value is server class -# used for automatic config generation -# and holding this information in one place -SERVER_TYPES = OrderedDict() - - -def register_server(server): - """ - Once new server class is created, should be registered with this function - for being visible in config and accessible in application. - """ - if server.TYPE not in SERVER_TYPES: - SERVER_TYPES[server.TYPE] = server - - -def get_enabled_servers(): - """ - list of enabled servers which connections outside should be used to check - """ - return([x for x in servers.values() if x.enabled is True]) - - -def get_worst_status(): - """ - get worst status of all servers - """ - worst_status = 'UP' - for server in get_enabled_servers(): - worst_status_current = server.get_worst_status_current() - if STATES.index(worst_status_current) > STATES.index(worst_status): - worst_status = worst_status_current - del worst_status_current - return worst_status - - -def get_status_count(): - """ - get all states of all servers and count them - """ - state_count = {'UNKNOWN': 0, - 'INFORMATION': 0, - 'WARNING': 0, - 'AVERAGE': 0, - 'HIGH': 0, - 'CRITICAL': 0, - 'DISASTER': 0, - 'UNREACHABLE': 0, - 'DOWN': 0} - for server in get_enabled_servers(): - state_count['UNKNOWN'] += server.unknown - state_count['INFORMATION'] += server.information - state_count['WARNING'] += server.warning - state_count['AVERAGE'] += server.average - state_count['HIGH'] += server.high - state_count['CRITICAL'] += server.critical - state_count['DISASTER'] += server.disaster - state_count['UNREACHABLE'] += server.unreachable - state_count['DOWN'] += server.down - - return(state_count) - - -def get_errors(): - """ - find out if any server has any error, used by statusbar error label - """ - for server in get_enabled_servers(): - if server.has_error: - return True - break - - # return default value - return False - - -def create_server(server=None): - # create Server from config - if server.type not in SERVER_TYPES: - print(('Server type not supported: %s' % server.type)) - return - # give argument servername so CentreonServer could use it for initializing MD5 cache - new_server = SERVER_TYPES[server.type](name=server.name) - new_server.type = server.type - new_server.enabled = server.enabled - new_server.monitor_url = server.monitor_url - new_server.monitor_cgi_url = server.monitor_cgi_url - new_server.username = server.username - new_server.password = server.password - new_server.use_proxy = server.use_proxy - new_server.use_proxy_from_os = server.use_proxy_from_os - new_server.proxy_address = server.proxy_address - new_server.proxy_username = server.proxy_username - new_server.proxy_password = server.proxy_password - new_server.authentication = server.authentication - new_server.timeout = server.timeout - - # SSL/TLS - new_server.ignore_cert = server.ignore_cert - new_server.custom_cert_use = server.custom_cert_use - new_server.custom_cert_ca_file = server.custom_cert_ca_file - - # if password is not to be saved ask for it at startup - if (server.enabled is True and server.save_password is False and - server.use_autologin is False): - new_server.refresh_authentication = True - - # Special FX - # Centreon - new_server.use_autologin = server.use_autologin - new_server.autologin_key = server.autologin_key - - # Icinga - new_server.use_display_name_host = server.use_display_name_host - new_server.use_display_name_service = server.use_display_name_service - - # IcingaWeb2 - new_server.no_cookie_auth = server.no_cookie_auth - - # Checkmk Multisite - new_server.force_authuser = server.force_authuser - new_server.checkmk_view_hosts = server.checkmk_view_hosts - new_server.checkmk_view_services = server.checkmk_view_services - - # OP5 api filters - new_server.host_filter = server.host_filter - new_server.service_filter = server.service_filter - - # Zabbix - new_server.use_description_name_service = server.use_description_name_service - - # server's individual preparations for HTTP connections (for example cookie creation) - # is done in GetStatus() method of monitor - if server.enabled is True: - new_server.enabled = True - - # start with high thread counter so server update thread does not have to wait - new_server.thread_counter = conf.update_interval_seconds - - # debug - if conf.debug_mode is True: - new_server.Debug(server=server.name, debug="Created server.") - - return new_server - - -# moved registration process here because of circular dependencies -for server in (CentreonServer, IcingaServer, IcingaWeb2Server, MultisiteServer, NagiosServer, - Op5MonitorServer, OpsviewServer, ThrukServer, ZabbixServer, SensuServer, - LivestatusServer, ZenossServer, Monitos3Server, Monitos4xServer, SnagViewServer, - PrometheusServer): - register_server(server) - -# create servers -for server in conf.servers.values(): - created_server = create_server(server) - if created_server is not None: - servers[server.name] = created_server - # for the next time no auth needed - servers[server.name].refresh_authentication = False +# encoding: utf-8 + +# Nagstamon - Nagios status monitor for your desktop +# Copyright (C) 2008-2020 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +"""Module Servers""" + +import urllib.request +import urllib.error +import urllib.parse + +from collections import OrderedDict + +# load all existing server types +from Nagstamon.Servers.Nagios import NagiosServer +from Nagstamon.Servers.Centreon import CentreonServer +from Nagstamon.Servers.Icinga import IcingaServer +from Nagstamon.Servers.IcingaWeb2 import IcingaWeb2Server +from Nagstamon.Servers.Multisite import MultisiteServer +from Nagstamon.Servers.op5Monitor import Op5MonitorServer +from Nagstamon.Servers.Opsview import OpsviewServer +from Nagstamon.Servers.Thruk import ThrukServer +from Nagstamon.Servers.Zabbix import ZabbixServer +from Nagstamon.Servers.Livestatus import LivestatusServer +from Nagstamon.Servers.Zenoss import ZenossServer +from Nagstamon.Servers.Monitos3 import Monitos3Server +from Nagstamon.Servers.Monitos4x import Monitos4xServer +from Nagstamon.Servers.SnagView3 import SnagViewServer +from Nagstamon.Servers.Sensu import SensuServer +from Nagstamon.Servers.Prometheus import PrometheusServer + +from Nagstamon.Config import conf + +from Nagstamon.Helpers import STATES + +# dictionary for servers +servers = OrderedDict() + +# contains dict with available server classes +# key is type of server, value is server class +# used for automatic config generation +# and holding this information in one place +SERVER_TYPES = OrderedDict() + + +def register_server(server): + """ + Once new server class is created, should be registered with this function + for being visible in config and accessible in application. + """ + if server.TYPE not in SERVER_TYPES: + SERVER_TYPES[server.TYPE] = server + + +def get_enabled_servers(): + """ + list of enabled servers which connections outside should be used to check + """ + return([x for x in servers.values() if x.enabled is True]) + + +def get_worst_status(): + """ + get worst status of all servers + """ + worst_status = 'UP' + for server in get_enabled_servers(): + worst_status_current = server.get_worst_status_current() + if STATES.index(worst_status_current) > STATES.index(worst_status): + worst_status = worst_status_current + del worst_status_current + return worst_status + + +def get_status_count(): + """ + get all states of all servers and count them + """ + state_count = {'UNKNOWN': 0, + 'INFORMATION': 0, + 'WARNING': 0, + 'AVERAGE': 0, + 'HIGH': 0, + 'CRITICAL': 0, + 'DISASTER': 0, + 'UNREACHABLE': 0, + 'DOWN': 0} + for server in get_enabled_servers(): + state_count['UNKNOWN'] += server.unknown + state_count['INFORMATION'] += server.information + state_count['WARNING'] += server.warning + state_count['AVERAGE'] += server.average + state_count['HIGH'] += server.high + state_count['CRITICAL'] += server.critical + state_count['DISASTER'] += server.disaster + state_count['UNREACHABLE'] += server.unreachable + state_count['DOWN'] += server.down + + return(state_count) + + +def get_errors(): + """ + find out if any server has any error, used by statusbar error label + """ + for server in get_enabled_servers(): + if server.has_error: + return True + break + + # return default value + return False + + +def create_server(server=None): + # create Server from config + if server.type not in SERVER_TYPES: + print(('Server type not supported: %s' % server.type)) + return + # give argument servername so CentreonServer could use it for initializing MD5 cache + new_server = SERVER_TYPES[server.type](name=server.name) + new_server.type = server.type + new_server.enabled = server.enabled + new_server.monitor_url = server.monitor_url + new_server.monitor_cgi_url = server.monitor_cgi_url + new_server.username = server.username + new_server.password = server.password + new_server.use_proxy = server.use_proxy + new_server.use_proxy_from_os = server.use_proxy_from_os + new_server.proxy_address = server.proxy_address + new_server.proxy_username = server.proxy_username + new_server.proxy_password = server.proxy_password + new_server.authentication = server.authentication + new_server.timeout = server.timeout + + # SSL/TLS + new_server.ignore_cert = server.ignore_cert + new_server.custom_cert_use = server.custom_cert_use + new_server.custom_cert_ca_file = server.custom_cert_ca_file + + # if password is not to be saved ask for it at startup + if (server.enabled is True and server.save_password is False and + server.use_autologin is False): + new_server.refresh_authentication = True + + # Special FX + # Centreon + new_server.use_autologin = server.use_autologin + new_server.autologin_key = server.autologin_key + + # Icinga + new_server.use_display_name_host = server.use_display_name_host + new_server.use_display_name_service = server.use_display_name_service + + # IcingaWeb2 + new_server.no_cookie_auth = server.no_cookie_auth + + # Checkmk Multisite + new_server.force_authuser = server.force_authuser + new_server.checkmk_view_hosts = server.checkmk_view_hosts + new_server.checkmk_view_services = server.checkmk_view_services + + # OP5 api filters + new_server.host_filter = server.host_filter + new_server.service_filter = server.service_filter + + # Zabbix + new_server.use_description_name_service = server.use_description_name_service + + # server's individual preparations for HTTP connections (for example cookie creation) + # is done in GetStatus() method of monitor + if server.enabled is True: + new_server.enabled = True + + # start with high thread counter so server update thread does not have to wait + new_server.thread_counter = conf.update_interval_seconds + + # debug + if conf.debug_mode is True: + new_server.Debug(server=server.name, debug="Created server.") + + return new_server + + +# moved registration process here because of circular dependencies +for server in (CentreonServer, IcingaServer, IcingaWeb2Server, MultisiteServer, NagiosServer, + Op5MonitorServer, OpsviewServer, ThrukServer, ZabbixServer, SensuServer, + LivestatusServer, ZenossServer, Monitos3Server, Monitos4xServer, SnagViewServer, + PrometheusServer): + register_server(server) + +# create servers +for server in conf.servers.values(): + created_server = create_server(server) + if created_server is not None: + servers[server.name] = created_server + # for the next time no auth needed + servers[server.name].refresh_authentication = False From b73aefb5d05f51d66b48a17576893bf7fd1ff7d7 Mon Sep 17 00:00:00 2001 From: Justin La Sotten <justin.lasotten@gmail.com> Date: Tue, 21 Apr 2020 17:35:51 -0400 Subject: [PATCH 008/884] Adding support for SOCKS5 Proxy When using a SOCKS5 proxy, simply set the URL to '<socks scheme>://<host>:<port>'. Adds the following SOCKS schemes: socks5, socks5h This requires SOCKS support in the Requests module using the pysocks module. Added new dependencies for Debian and Redhat Tested in Debian --- Nagstamon/Servers/Generic.py | 2 +- build/debian/control | 2 +- build/redhat/nagstamon.spec | 1 + setup.py | 1 + 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index 9b8dee7b0..c0e046e58 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -297,7 +297,7 @@ def proxify(self, requester): host_port = ''.join(proxy_address_parts[1:]) # use only valid schemes - if scheme.lower() in ('http:', 'https:'): + if scheme.lower() in ('http:', 'https:', 'socks5:', 'socks5h:'): # merge proxy URL proxy_url = '{0}//{1}{2}'.format(scheme, user_pass, host_port) # fill session.proxies for both protocols diff --git a/build/debian/control b/build/debian/control index 88aebcffe..4c765f93a 100644 --- a/build/debian/control +++ b/build/debian/control @@ -13,7 +13,7 @@ Vcs-Browser: https://codeload.github.com/HenriWahl/Nagstamon/zip/master Package: nagstamon Architecture: all -Depends: ${python3:Depends}, ${misc:Depends}, python3-pkg-resources, python3-bs4, python3-lxml, python3-pyqt5, python3-pyqt5.qtsvg, python3-pyqt5.qtmultimedia, libqt5multimedia5-plugins, python3-requests, python3-requests-kerberos, python3-psutil, python3-dbus.mainloop.pyqt5, python3-keyring +Depends: ${python3:Depends}, ${misc:Depends}, python3-pkg-resources, python3-bs4, python3-lxml, python3-pyqt5, python3-pyqt5.qtsvg, python3-pyqt5.qtmultimedia, libqt5multimedia5-plugins, python3-requests, python3-requests-kerberos, python3-psutil, python3-dbus.mainloop.pyqt5, python3-keyring, python3-socks Description: Nagios status monitor which takes place in systray or on desktop Nagstamon is a Nagios status monitor which takes place in systray or on desktop (GNOME, KDE) as floating statusbar to inform you in diff --git a/build/redhat/nagstamon.spec b/build/redhat/nagstamon.spec index 112a59b8b..03cf93ca7 100644 --- a/build/redhat/nagstamon.spec +++ b/build/redhat/nagstamon.spec @@ -22,6 +22,7 @@ Requires: python3-cryptography Requires: python3-keyring Requires: python3-lxml Requires: python3-psutil +Requires: python3-pysocks Requires: python3-qt5 Requires: python3-requests Requires: python3-requests-kerberos diff --git a/setup.py b/setup.py index e27f83078..f794b2065 100644 --- a/setup.py +++ b/setup.py @@ -87,6 +87,7 @@ 'python3-keyring ' 'python3-lxml ' 'python3-psutil ' + 'python3-pysocks ' 'python3-qt5 ' 'python3-requests ' 'python3-requests-kerberos ' From cfa9691dcf6a6b80964f1b4a91f74e09231e81ec Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Wed, 29 Apr 2020 16:00:22 +0200 Subject: [PATCH 009/884] fix version 20200429 --- Nagstamon/Config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 5d6227835..168a4cc18 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -123,7 +123,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.5-20200415' + VERSION = '3.5-20200429' WEBSITE = 'https://nagstamon.ifw-dresden.de' COPYRIGHT = '©2008-2020 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' From 6c75058d64541ad7ce55300b96a65460486227b1 Mon Sep 17 00:00:00 2001 From: Stephan Schwarz <schwarz@e-spirit.com> Date: Wed, 13 May 2020 17:22:55 +0200 Subject: [PATCH 010/884] fixed crashes caused by undefined urls in prometheus integration --- Nagstamon/Servers/Prometheus.py | 42 +++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Servers/Prometheus.py b/Nagstamon/Servers/Prometheus.py index 314ca19c3..8970acae6 100644 --- a/Nagstamon/Servers/Prometheus.py +++ b/Nagstamon/Servers/Prometheus.py @@ -28,7 +28,17 @@ # * Currently we can only fetch and display alerts from Prometheus. # In the future it would be great to be able to interract with # alertmanager, so that managing alerts (e.g. silencing) is possible - +# +# Release Notes: +# +# [1.0.0] - 2020-04-20: +# * added: +# Inital version +# +# [1.0.1] - 2020-05-13: +# * fixed: +# Nagstamon crashes due to missing url handling +# import sys import urllib.request, urllib.parse, urllib.error import copy @@ -63,7 +73,12 @@ class PrometheusServer(GenericServer): # Prometheus actions are limited to visiting the monitor for now MENU_ACTIONS = ['Monitor'] - BROWSER_URLS= {'monitor': '$MONITOR$/alerts'} + BROWSER_URLS= { + 'monitor': '$MONITOR$/alerts', + 'hosts': '$MONITOR$/targets', + 'services': '$MONITOR$/service-discovery', + 'history': '$MONITOR$/graph' + } def init_HTTP(self): @@ -184,3 +199,26 @@ def _get_status(self): def open_monitor_webpage(self, host, service): webbrowser_open('%s' % (self.monitor_url)) + + def open_monitor(self, host, service=''): + ''' + open monitor from tablewidget context menu + ''' + + if conf.debug_mode: + self.Debug(server=self.get_name(), host=host, service=service, + debug='Open host/service monitor web page ' + + self.monitor_url + '/graph?g0.range_input=1h&g0.expr=' + + urllib.parse.quote('ALERTS{alertname="' + service + '",pod_name="' + host + '"}', safe='') + '&g0.tab=1') + webbrowser_open(self.monitor_url + '/graph?g0.range_input=1h&g0.expr=' + + urllib.parse.quote('ALERTS{alertname="' + service + '",pod_name="' + host + '"}', safe='') + '&g0.tab=1') + + def open_monitor_webpage(self): + ''' + open monitor from systray/toparea context menu + ''' + + if conf.debug_mode: + self.Debug(server=self.get_name(), + debug='Open monitor web page ' + self.monitor_url) + webbrowser_open(self.monitor_url) \ No newline at end of file From 8cf8f8a43e1559c89b866b28014247e59e0331fb Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Fri, 15 May 2020 10:52:44 +0200 Subject: [PATCH 011/884] testing release 20200515 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 11 ++--------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 168a4cc18..22baa5dbf 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -123,7 +123,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.5-20200429' + VERSION = '3.5-20200515' WEBSITE = 'https://nagstamon.ifw-dresden.de' COPYRIGHT = '©2008-2020 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 90627fdd3..eb1c79d1a 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,14 +1,7 @@ -nagstamon (3.5-20200113) unstable; urgency=low +nagstamon (3.5-20200515) unstable; urgency=low * New upstream - - Centreon fix for acknowledgement when using autologin key - - IcingaWeb2 fix support older setups - - fix popup in systray mode on Windows - - improved mode switching - - better support for high resolution displays - - build support for Python 3.8 - - improved Windows installer - -- Henri Wahl <h.wahl@ifw-dresden.de> Wed, 8 Jan 2020 10:00:00 +0100 + -- Henri Wahl <h.wahl@ifw-dresden.de> Fri, 15 May 2020 10:00:00 +0100 nagstamon (3.4.1) stable; urgency=low * New upstream From 8b4e453f8b689f2cc15f5f5a5cce96cde32c459f Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Wed, 27 May 2020 09:34:44 +0200 Subject: [PATCH 012/884] testing release 20200527 with added dependency python3-dateutil --- Nagstamon/Config.py | 2 +- build/debian/changelog | 2 +- build/debian/control | 2 +- build/redhat/nagstamon.spec | 1 + 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 22baa5dbf..9b90471d7 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -123,7 +123,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.5-20200515' + VERSION = '3.5-20200527' WEBSITE = 'https://nagstamon.ifw-dresden.de' COPYRIGHT = '©2008-2020 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index eb1c79d1a..b0a46817b 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.5-20200515) unstable; urgency=low +nagstamon (3.5-20200527) unstable; urgency=low * New upstream -- Henri Wahl <h.wahl@ifw-dresden.de> Fri, 15 May 2020 10:00:00 +0100 diff --git a/build/debian/control b/build/debian/control index 4c765f93a..0ee67205d 100644 --- a/build/debian/control +++ b/build/debian/control @@ -13,7 +13,7 @@ Vcs-Browser: https://codeload.github.com/HenriWahl/Nagstamon/zip/master Package: nagstamon Architecture: all -Depends: ${python3:Depends}, ${misc:Depends}, python3-pkg-resources, python3-bs4, python3-lxml, python3-pyqt5, python3-pyqt5.qtsvg, python3-pyqt5.qtmultimedia, libqt5multimedia5-plugins, python3-requests, python3-requests-kerberos, python3-psutil, python3-dbus.mainloop.pyqt5, python3-keyring, python3-socks +Depends: ${python3:Depends}, ${misc:Depends}, python3-dateutil, python3-pkg-resources, python3-bs4, python3-lxml, python3-pyqt5, python3-pyqt5.qtsvg, python3-pyqt5.qtmultimedia, libqt5multimedia5-plugins, python3-requests, python3-requests-kerberos, python3-psutil, python3-dbus.mainloop.pyqt5, python3-keyring, python3-socks Description: Nagios status monitor which takes place in systray or on desktop Nagstamon is a Nagios status monitor which takes place in systray or on desktop (GNOME, KDE) as floating statusbar to inform you in diff --git a/build/redhat/nagstamon.spec b/build/redhat/nagstamon.spec index 03cf93ca7..8fac01e40 100644 --- a/build/redhat/nagstamon.spec +++ b/build/redhat/nagstamon.spec @@ -19,6 +19,7 @@ Requires: python3 Requires: python3-beautifulsoup4 Requires: python3-crypto Requires: python3-cryptography +Requires: python3-dateutil Requires: python3-keyring Requires: python3-lxml Requires: python3-psutil From 37909886bef65071623cba964354b84ce6646076 Mon Sep 17 00:00:00 2001 From: Stephan Schwarz <schwarz@e-spirit.com> Date: Sun, 7 Jun 2020 21:22:38 +0200 Subject: [PATCH 013/884] * fixed errors with unset fields and unexpected values --- Nagstamon/Servers/Prometheus.py | 23 ++++++++++++++++++----- README.md | 1 + 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/Nagstamon/Servers/Prometheus.py b/Nagstamon/Servers/Prometheus.py index 8970acae6..6889de462 100644 --- a/Nagstamon/Servers/Prometheus.py +++ b/Nagstamon/Servers/Prometheus.py @@ -31,14 +31,19 @@ # # Release Notes: # -# [1.0.0] - 2020-04-20: -# * added: -# Inital version +# [1.0.2] - 2020-06-07: +# * fixed: +# Missing message field in alert stopped integration from working +# Alerts with an unknown severity were not shown # # [1.0.1] - 2020-05-13: # * fixed: # Nagstamon crashes due to missing url handling # +# [1.0.0] - 2020-04-20: +# * added: +# Inital version +# import sys import urllib.request, urllib.parse, urllib.error import copy @@ -181,11 +186,19 @@ def _get_status(self): self.new_hosts[hostname].services[servicename].name = servicename self.new_hosts[hostname].services[servicename].server = self.name - self.new_hosts[hostname].services[servicename].status = alert["labels"]["severity"].upper() + if ((alert["labels"]["severity"].upper() == "CRITICAL") or (alert["labels"]["severity"].upper() == "WARNING")): + self.new_hosts[hostname].services[servicename].status = alert["labels"]["severity"].upper() + else: + self.new_hosts[hostname].services[servicename].status = "UNKNOWN" + self.new_hosts[hostname].services[servicename].last_check = "n/a" self.new_hosts[hostname].services[servicename].attempt = alert["state"].upper() self.new_hosts[hostname].services[servicename].duration = str(self._get_duration(alert["activeAt"])) - self.new_hosts[hostname].services[servicename].status_information = alert["annotations"]["message"].replace("\n", " ") + + try: + self.new_hosts[hostname].services[servicename].status_information = alert["annotations"]["message"].replace("\n", " ") + except KeyError: + self.new_hosts[hostname].services[servicename].status_information = "" except: # set checking flag back to False diff --git a/README.md b/README.md index b65416b7a..a05ca632b 100644 --- a/README.md +++ b/README.md @@ -25,5 +25,6 @@ Successfully tested monitors include: - Zenoss – experimental - monitos 3 - experimental - SNAG-View3 - experimental + - Prometheus - experimental See https://nagstamon.ifw-dresden.de for further information. From 7ac99241fa1c31bcf9ae0cabca0b15438bf98d66 Mon Sep 17 00:00:00 2001 From: Stephan Schwarz <schwarz@e-spirit.com> Date: Tue, 9 Jun 2020 22:02:05 +0200 Subject: [PATCH 014/884] * backend now uses mapping to assign first matching label to hostname and servicename --- Nagstamon/Servers/Prometheus.py | 106 ++++++++++++++++++++------------ 1 file changed, 65 insertions(+), 41 deletions(-) diff --git a/Nagstamon/Servers/Prometheus.py b/Nagstamon/Servers/Prometheus.py index 6889de462..375c58328 100644 --- a/Nagstamon/Servers/Prometheus.py +++ b/Nagstamon/Servers/Prometheus.py @@ -31,6 +31,12 @@ # # Release Notes: # +# [1.1.0] - 2020-06-09: +# * fixed: +# Some more errors with unset fields from Prometheus +# * added: +# Feature to let users decide what labels gets mapped to servicename and hostname +# # [1.0.2] - 2020-06-07: # * fixed: # Missing message field in alert stopped integration from working @@ -46,22 +52,18 @@ # import sys import urllib.request, urllib.parse, urllib.error -import copy import pprint import json from datetime import datetime, timedelta, timezone import dateutil.parser -from ast import literal_eval - from Nagstamon.Config import conf from Nagstamon.Objects import (GenericHost, GenericService, Result) from Nagstamon.Servers.Generic import GenericServer -from Nagstamon.Helpers import (HumanReadableDurationFromSeconds, - webbrowser_open) +from Nagstamon.Helpers import webbrowser_open class PrometheusService(GenericService): """ @@ -78,7 +80,7 @@ class PrometheusServer(GenericServer): # Prometheus actions are limited to visiting the monitor for now MENU_ACTIONS = ['Monitor'] - BROWSER_URLS= { + BROWSER_URLS = { 'monitor': '$MONITOR$/alerts', 'hosts': '$MONITOR$/targets', 'services': '$MONITOR$/service-discovery', @@ -160,45 +162,68 @@ def _get_status(self): if conf.debug_mode: self.Debug(server=self.get_name(), debug="Fetched JSON: " + pprint.pformat(data)) + # TODO: fetch these strings from GUI + map_to_hostname = "pod_name,namespace" + map_to_servicename = "alertname" + for alert in data["data"]["alerts"]: if conf.debug_mode: self.Debug(server=self.get_name(), debug="Processing Alert: " + pprint.pformat(alert)) - if alert["labels"]["severity"] != "none": - if "pod_name" in alert["labels"]: - hostname = alert["labels"]["pod_name"] - elif "namespace" in alert["labels"]: - hostname = alert["labels"]["namespace"] - else: - hostname = "unknown" - - self.new_hosts[hostname] = GenericHost() - self.new_hosts[hostname].name = str(hostname) - self.new_hosts[hostname].server = self.name - - if "alertname" in alert["labels"]: - servicename = alert["labels"]["alertname"] - else: - servicename = "unknown" - - self.new_hosts[hostname].services[servicename] = PrometheusService() - self.new_hosts[hostname].services[servicename].host = str(hostname) - self.new_hosts[hostname].services[servicename].name = servicename - self.new_hosts[hostname].services[servicename].server = self.name - - if ((alert["labels"]["severity"].upper() == "CRITICAL") or (alert["labels"]["severity"].upper() == "WARNING")): + # try to map hostname + try: + hostname = "unknown" + for hostkey in map_to_hostname.split(','): + if hostkey in alert["labels"]: + hostname = alert["labels"][hostkey] + break + except KeyError: + hostname = "unknown" + + # try to map servicename + try: + servicename = "unknown" + for servicekey in map_to_servicename.split(','): + if servicekey in alert["labels"]: + servicename = alert["labels"][servicekey] + break + except KeyError: + hostname = "unknown" + + self.new_hosts[hostname] = GenericHost() + self.new_hosts[hostname].name = str(hostname) + self.new_hosts[hostname].server = self.name + self.new_hosts[hostname].services[servicename] = PrometheusService() + self.new_hosts[hostname].services[servicename].host = str(hostname) + self.new_hosts[hostname].services[servicename].name = servicename + self.new_hosts[hostname].services[servicename].server = self.name + + try: + if ((alert["labels"]["severity"].upper() == "CRITICAL") or + (alert["labels"]["severity"].upper() == "WARNING")): self.new_hosts[hostname].services[servicename].status = alert["labels"]["severity"].upper() else: self.new_hosts[hostname].services[servicename].status = "UNKNOWN" + except KeyError: + self.new_hosts[hostname].services[servicename].status = "UNKNOWN" - self.new_hosts[hostname].services[servicename].last_check = "n/a" + self.new_hosts[hostname].services[servicename].last_check = "n/a" + try: self.new_hosts[hostname].services[servicename].attempt = alert["state"].upper() - self.new_hosts[hostname].services[servicename].duration = str(self._get_duration(alert["activeAt"])) + except KeyError: + self.new_hosts[hostname].services[servicename].attempt = "UNKNOWN" + + try: + self.new_hosts[hostname].services[servicename].duration = \ + str(self._get_duration(alert["activeAt"])) + except KeyError: + self.new_hosts[hostname].services[servicename].duration = "UNKNOWN" - try: - self.new_hosts[hostname].services[servicename].status_information = alert["annotations"]["message"].replace("\n", " ") - except KeyError: - self.new_hosts[hostname].services[servicename].status_information = "" + try: + self.new_hosts[hostname].services[servicename].status_information = \ + alert["annotations"]["message"].replace("\n", " ") + except KeyError: + self.new_hosts[hostname].services[servicename].status_information = "" except: # set checking flag back to False @@ -210,9 +235,6 @@ def _get_status(self): return Result() - def open_monitor_webpage(self, host, service): - webbrowser_open('%s' % (self.monitor_url)) - def open_monitor(self, host, service=''): ''' open monitor from tablewidget context menu @@ -221,10 +243,12 @@ def open_monitor(self, host, service=''): if conf.debug_mode: self.Debug(server=self.get_name(), host=host, service=service, debug='Open host/service monitor web page ' + - self.monitor_url + '/graph?g0.range_input=1h&g0.expr=' + - urllib.parse.quote('ALERTS{alertname="' + service + '",pod_name="' + host + '"}', safe='') + '&g0.tab=1') + self.monitor_url + '/graph?g0.range_input=1h&g0.expr=' + + urllib.parse.quote('ALERTS{alertname="' + service + '",pod_name="' + + host + '"}', safe='') + '&g0.tab=1') webbrowser_open(self.monitor_url + '/graph?g0.range_input=1h&g0.expr=' + - urllib.parse.quote('ALERTS{alertname="' + service + '",pod_name="' + host + '"}', safe='') + '&g0.tab=1') + urllib.parse.quote('ALERTS{alertname="' + service + '",pod_name="' + + host + '"}', safe='') + '&g0.tab=1') def open_monitor_webpage(self): ''' From e7253956b14fd4b1a030fbef2b485a1d57f0554a Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Wed, 10 Jun 2020 10:29:45 +0200 Subject: [PATCH 015/884] updated version for next testing release --- Nagstamon/Config.py | 2 +- build/debian/changelog | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 9b90471d7..886f3c9a2 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -123,7 +123,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.5-20200527' + VERSION = '3.5-20200610' WEBSITE = 'https://nagstamon.ifw-dresden.de' COPYRIGHT = '©2008-2020 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index b0a46817b..d86bc7fb0 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.5-20200527) unstable; urgency=low +nagstamon (3.5-20200610) unstable; urgency=low * New upstream -- Henri Wahl <h.wahl@ifw-dresden.de> Fri, 15 May 2020 10:00:00 +0100 From 41467a8df94235033314d324871993131f8239ae Mon Sep 17 00:00:00 2001 From: Stefan Schwarz <ssz@bitsbeats.com> Date: Wed, 10 Jun 2020 15:11:34 +0200 Subject: [PATCH 016/884] support non-kubernetes alerts and a bit of cleanup --- Nagstamon/Servers/Prometheus.py | 161 ++++++++++++++++---------------- 1 file changed, 80 insertions(+), 81 deletions(-) diff --git a/Nagstamon/Servers/Prometheus.py b/Nagstamon/Servers/Prometheus.py index 6889de462..108bb699c 100644 --- a/Nagstamon/Servers/Prometheus.py +++ b/Nagstamon/Servers/Prometheus.py @@ -45,7 +45,9 @@ # Inital version # import sys -import urllib.request, urllib.parse, urllib.error +import urllib.request +import urllib.parse +import urllib.error import copy import pprint import json @@ -63,32 +65,32 @@ from Nagstamon.Helpers import (HumanReadableDurationFromSeconds, webbrowser_open) + class PrometheusService(GenericService): """ - add Prometheus specific service property to generic service class + add Prometheus specific service property to generic service class """ service_object_id = "" class PrometheusServer(GenericServer): """ - special treatment for Prometheus API + special treatment for Prometheus API """ TYPE = 'Prometheus' # Prometheus actions are limited to visiting the monitor for now MENU_ACTIONS = ['Monitor'] - BROWSER_URLS= { + BROWSER_URLS = { 'monitor': '$MONITOR$/alerts', 'hosts': '$MONITOR$/targets', 'services': '$MONITOR$/service-discovery', 'history': '$MONITOR$/graph' } - def init_HTTP(self): """ - things to do if HTTP is not initialized + things to do if HTTP is not initialized """ GenericServer.init_HTTP(self) @@ -96,29 +98,27 @@ def init_HTTP(self): self.session.headers.update({'Accept': 'application/json', 'Content-Type': 'application/json'}) - def init_config(self): """ - dummy init_config, called at thread start + dummy init_config, called at thread start """ pass - def get_start_end(self, host): """ - Set a default of starttime of "now" and endtime is "now + 24 hours" + Set a default of starttime of "now" and endtime is "now + 24 hours" directly from web interface """ start = datetime.now() end = datetime.now() + timedelta(hours=24) - return str(start.strftime("%Y-%m-%d %H:%M:%S")), str(end.strftime("%Y-%m-%d %H:%M:%S")) - + return (str(start.strftime("%Y-%m-%d %H:%M:%S")), + str(end.strftime("%Y-%m-%d %H:%M:%S"))) def _get_duration(self, timestring): """ - calculates the duration (delta) from Prometheus' activeAt (ISO8601 format) until now - an returns a human friendly string + calculates the duration (delta) from Prometheus' activeAt (ISO8601 + format) until now an returns a human friendly string """ time_object = dateutil.parser.parse(timestring) duration = datetime.now(timezone.utc) - time_object @@ -134,71 +134,82 @@ def _get_duration(self, timestring): else: return "%02ds" % (s) - - def _set_downtime(self, host, service, author, comment, fixed, start_time, end_time, hours, minutes): + def _set_downtime(self, host, service, author, comment, fixed, start_time, + end_time, hours, minutes): """ - to be implemented in a future release + to be implemented in a future release """ pass - def _get_status(self): """ - Get status from Prometheus Server + Get status from Prometheus Server """ # get all alerts from the API server try: - result = self.FetchURL(self.monitor_url + "/api/v1/alerts", giveback="raw") - data, error, status_code = json.loads(result.result), result.error, result.status_code + result = self.FetchURL(self.monitor_url + "/api/v1/alerts", + giveback="raw") + data = json.loads(result.result) + error = result.error + status_code = result.status_code # check if any error occured errors_occured = self.check_for_error(data, error, status_code) - # if there are errors return them - if errors_occured != False: + if errors_occured is not False: return(errors_occured) if conf.debug_mode: - self.Debug(server=self.get_name(), debug="Fetched JSON: " + pprint.pformat(data)) + self.Debug(server=self.get_name(), + debug="Fetched JSON: " + pprint.pformat(data)) for alert in data["data"]["alerts"]: if conf.debug_mode: - self.Debug(server=self.get_name(), debug="Processing Alert: " + pprint.pformat(alert)) - - if alert["labels"]["severity"] != "none": - if "pod_name" in alert["labels"]: - hostname = alert["labels"]["pod_name"] - elif "namespace" in alert["labels"]: - hostname = alert["labels"]["namespace"] - else: - hostname = "unknown" - + self.Debug( + server=self.get_name(), + debug="Processing Alert: " + pprint.pformat(alert) + ) + + labels = alert.get("labels", {}) + + # skip alerts with none severity + severity = labels.get("severity", "UNKNOWN").upper() + if severity == "NONE": + continue + + if "pod_name" in labels: + hostname = labels["pod_name"] + elif "namespace" in labels: + hostname = labels["namespace"] + elif "instance" in labels: + hostname = labels["instance"] + else: + hostname = "unknown" + servicename = labels.get("alertname", "unknown") + + service = PrometheusService() + service.host = str(hostname) + service.name = servicename + service.server = self.name + service.status = severity + service.last_check = "n/a" + service.attempt = alert.get("state", "firirng") + service.duration = str(self._get_duration(alert["activeAt"])) + + annotations = alert.get("annotations", {}) + status_information = "" + if "message" in annotations: + status_information = annotations["message"] + if "summary" in annotations: + status_information = annotations["summary"] + if "descriptions" in annotations: + status_information = annotations["description"] + service.status_information = status_information + + if hostname not in self.new_hosts: self.new_hosts[hostname] = GenericHost() self.new_hosts[hostname].name = str(hostname) self.new_hosts[hostname].server = self.name - - if "alertname" in alert["labels"]: - servicename = alert["labels"]["alertname"] - else: - servicename = "unknown" - - self.new_hosts[hostname].services[servicename] = PrometheusService() - self.new_hosts[hostname].services[servicename].host = str(hostname) - self.new_hosts[hostname].services[servicename].name = servicename - self.new_hosts[hostname].services[servicename].server = self.name - - if ((alert["labels"]["severity"].upper() == "CRITICAL") or (alert["labels"]["severity"].upper() == "WARNING")): - self.new_hosts[hostname].services[servicename].status = alert["labels"]["severity"].upper() - else: - self.new_hosts[hostname].services[servicename].status = "UNKNOWN" - - self.new_hosts[hostname].services[servicename].last_check = "n/a" - self.new_hosts[hostname].services[servicename].attempt = alert["state"].upper() - self.new_hosts[hostname].services[servicename].duration = str(self._get_duration(alert["activeAt"])) - - try: - self.new_hosts[hostname].services[servicename].status_information = alert["annotations"]["message"].replace("\n", " ") - except KeyError: - self.new_hosts[hostname].services[servicename].status_information = "" + self.new_hosts[hostname].services[servicename] = service except: # set checking flag back to False @@ -206,32 +217,20 @@ def _get_status(self): result, error = self.Error(sys.exc_info()) return Result(result=result, error=error) - #dummy return in case all is OK + # dummy return in case all is OK return Result() - def open_monitor_webpage(self, host, service): + """ + open monitor from tablewidget context menu + """ webbrowser_open('%s' % (self.monitor_url)) def open_monitor(self, host, service=''): - ''' - open monitor from tablewidget context menu - ''' - - if conf.debug_mode: - self.Debug(server=self.get_name(), host=host, service=service, - debug='Open host/service monitor web page ' + - self.monitor_url + '/graph?g0.range_input=1h&g0.expr=' + - urllib.parse.quote('ALERTS{alertname="' + service + '",pod_name="' + host + '"}', safe='') + '&g0.tab=1') - webbrowser_open(self.monitor_url + '/graph?g0.range_input=1h&g0.expr=' + - urllib.parse.quote('ALERTS{alertname="' + service + '",pod_name="' + host + '"}', safe='') + '&g0.tab=1') - - def open_monitor_webpage(self): - ''' - open monitor from systray/toparea context menu - ''' - - if conf.debug_mode: - self.Debug(server=self.get_name(), - debug='Open monitor web page ' + self.monitor_url) - webbrowser_open(self.monitor_url) \ No newline at end of file + """ + open monitor for alert + """ + url = '%s/graph?g0.range_input=1h&g0.expr=%s' + url = url % (self.monitor_url, + urllib.parse.quote('ALERTS{alertname="%s"}' % service)) + webbrowser_open(url) From 23c56235502861c9c3f414b45b9a23736be3d9bc Mon Sep 17 00:00:00 2001 From: Stephan Schwarz <schwarz@e-spirit.com> Date: Thu, 11 Jun 2020 21:56:44 +0200 Subject: [PATCH 017/884] * fixed missing updates after merge --- build/debian/changelog | 11 ++--------- build/debian/control | 2 +- build/redhat/nagstamon.spec | 1 + 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/build/debian/changelog b/build/debian/changelog index 90627fdd3..d86bc7fb0 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,14 +1,7 @@ -nagstamon (3.5-20200113) unstable; urgency=low +nagstamon (3.5-20200610) unstable; urgency=low * New upstream - - Centreon fix for acknowledgement when using autologin key - - IcingaWeb2 fix support older setups - - fix popup in systray mode on Windows - - improved mode switching - - better support for high resolution displays - - build support for Python 3.8 - - improved Windows installer - -- Henri Wahl <h.wahl@ifw-dresden.de> Wed, 8 Jan 2020 10:00:00 +0100 + -- Henri Wahl <h.wahl@ifw-dresden.de> Fri, 15 May 2020 10:00:00 +0100 nagstamon (3.4.1) stable; urgency=low * New upstream diff --git a/build/debian/control b/build/debian/control index 4c765f93a..0ee67205d 100644 --- a/build/debian/control +++ b/build/debian/control @@ -13,7 +13,7 @@ Vcs-Browser: https://codeload.github.com/HenriWahl/Nagstamon/zip/master Package: nagstamon Architecture: all -Depends: ${python3:Depends}, ${misc:Depends}, python3-pkg-resources, python3-bs4, python3-lxml, python3-pyqt5, python3-pyqt5.qtsvg, python3-pyqt5.qtmultimedia, libqt5multimedia5-plugins, python3-requests, python3-requests-kerberos, python3-psutil, python3-dbus.mainloop.pyqt5, python3-keyring, python3-socks +Depends: ${python3:Depends}, ${misc:Depends}, python3-dateutil, python3-pkg-resources, python3-bs4, python3-lxml, python3-pyqt5, python3-pyqt5.qtsvg, python3-pyqt5.qtmultimedia, libqt5multimedia5-plugins, python3-requests, python3-requests-kerberos, python3-psutil, python3-dbus.mainloop.pyqt5, python3-keyring, python3-socks Description: Nagios status monitor which takes place in systray or on desktop Nagstamon is a Nagios status monitor which takes place in systray or on desktop (GNOME, KDE) as floating statusbar to inform you in diff --git a/build/redhat/nagstamon.spec b/build/redhat/nagstamon.spec index 03cf93ca7..8fac01e40 100644 --- a/build/redhat/nagstamon.spec +++ b/build/redhat/nagstamon.spec @@ -19,6 +19,7 @@ Requires: python3 Requires: python3-beautifulsoup4 Requires: python3-crypto Requires: python3-cryptography +Requires: python3-dateutil Requires: python3-keyring Requires: python3-lxml Requires: python3-psutil From b780578901cbd26a5de780cbfa4475ff40760ba9 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Fri, 12 Jun 2020 10:20:47 +0200 Subject: [PATCH 018/884] merged Prometheus, version 20200612 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 6ecb76237..59a75c026 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -123,7 +123,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.5-20200611' + VERSION = '3.5-20200612' WEBSITE = 'https://nagstamon.ifw-dresden.de' COPYRIGHT = '©2008-2020 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index d86bc7fb0..63983b2a6 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.5-20200610) unstable; urgency=low +nagstamon (3.5-20200612) unstable; urgency=low * New upstream -- Henri Wahl <h.wahl@ifw-dresden.de> Fri, 15 May 2020 10:00:00 +0100 From adbcfbe1f8817c8d75bb5c49a4849ce04d1a1bcc Mon Sep 17 00:00:00 2001 From: Stephan Schwarz <schwarz@e-spirit.com> Date: Fri, 12 Jun 2020 14:33:02 +0200 Subject: [PATCH 019/884] Added configuration for Prometheus mappings --- Nagstamon/Config.py | 4 + Nagstamon/QUI/__init__.py | 8 +- Nagstamon/QUI/settings_server.py | 211 +++++++++++++----------- Nagstamon/QUI/settings_server.ui | 270 +++++++++++++++++-------------- Nagstamon/Servers/Prometheus.py | 17 +- Nagstamon/Servers/__init__.py | 5 + 6 files changed, 288 insertions(+), 227 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 6ecb76237..6d1e1b888 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -952,6 +952,10 @@ def __init__(self): # Zabbix "Item Description" as "Service Name" self.use_description_name_service = False + # Prometheus mappings + self.map_to_hostname = "pod_name,namespace,instance" + self.map_to_servicename = "alertname" + self.map_to_status_information = "message,summary,description" class Action(object): """ diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 545d08832..e952c4fef 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -5747,7 +5747,13 @@ def __init__(self, dialog): self.ui.label_service_filter: ['op5Monitor'], self.ui.label_host_filter: ['op5Monitor'], self.ui.label_monitor_site: ['Sensu'], - self.ui.input_lineedit_monitor_site: ['Sensu']} + self.ui.input_lineedit_monitor_site: ['Sensu'], + self.ui.label_map_to_hostname: ['Prometheus'], + self.ui.input_lineedit_map_to_hostname: ['Prometheus'], + self.ui.label_map_to_servicename: ['Prometheus'], + self.ui.input_lineedit_map_to_servicename: ['Prometheus'], + self.ui.label_map_to_status_information: ['Prometheus'], + self.ui.input_lineedit_map_to_status_information: ['Prometheus']} # to be used when selecting authentication method Kerberos self.AUTHENTICATION_WIDGETS = [ diff --git a/Nagstamon/QUI/settings_server.py b/Nagstamon/QUI/settings_server.py index 8562b8569..6144a55a5 100644 --- a/Nagstamon/QUI/settings_server.py +++ b/Nagstamon/QUI/settings_server.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'settings_server.ui' # -# Created by: PyQt5 UI code generator 5.13.1 +# Created by: PyQt5 UI code generator 5.14.2 # # WARNING! All changes made in this file will be lost! @@ -173,38 +173,47 @@ def setupUi(self, settings_server): self.gridLayout_3.setHorizontalSpacing(10) self.gridLayout_3.setVerticalSpacing(5) self.gridLayout_3.setObjectName("gridLayout_3") + self.label_monitor_site = QtWidgets.QLabel(self.groupbox_options) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_monitor_site.sizePolicy().hasHeightForWidth()) + self.label_monitor_site.setSizePolicy(sizePolicy) + self.label_monitor_site.setObjectName("label_monitor_site") + self.gridLayout_3.addWidget(self.label_monitor_site, 8, 1, 1, 1) + self.input_checkbox_force_authuser = QtWidgets.QCheckBox(self.groupbox_options) + self.input_checkbox_force_authuser.setObjectName("input_checkbox_force_authuser") + self.gridLayout_3.addWidget(self.input_checkbox_force_authuser, 20, 1, 1, 3) + self.label_map_to_hostname = QtWidgets.QLabel(self.groupbox_options) + self.label_map_to_hostname.setObjectName("label_map_to_hostname") + self.gridLayout_3.addWidget(self.label_map_to_hostname, 13, 1, 1, 1) self.input_checkbox_use_display_name_host = QtWidgets.QCheckBox(self.groupbox_options) self.input_checkbox_use_display_name_host.setObjectName("input_checkbox_use_display_name_host") - self.gridLayout_3.addWidget(self.input_checkbox_use_display_name_host, 13, 0, 1, 4) - spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.gridLayout_3.addItem(spacerItem1, 5, 3, 1, 1) - self.horizontalLayout_timeout_seconds = QtWidgets.QHBoxLayout() - self.horizontalLayout_timeout_seconds.setSpacing(5) - self.horizontalLayout_timeout_seconds.setObjectName("horizontalLayout_timeout_seconds") - self.input_spinbox_timeout = QtWidgets.QSpinBox(self.groupbox_options) - self.input_spinbox_timeout.setObjectName("input_spinbox_timeout") - self.horizontalLayout_timeout_seconds.addWidget(self.input_spinbox_timeout) - self.label_timeout_sec = QtWidgets.QLabel(self.groupbox_options) - self.label_timeout_sec.setObjectName("label_timeout_sec") - self.horizontalLayout_timeout_seconds.addWidget(self.label_timeout_sec) - self.gridLayout_3.addLayout(self.horizontalLayout_timeout_seconds, 6, 2, 1, 1) - self.input_checkbox_use_autologin = QtWidgets.QCheckBox(self.groupbox_options) - self.input_checkbox_use_autologin.setObjectName("input_checkbox_use_autologin") - self.gridLayout_3.addWidget(self.input_checkbox_use_autologin, 7, 0, 1, 4) - self.input_checkbox_use_description_name_service = QtWidgets.QCheckBox(self.groupbox_options) - self.input_checkbox_use_description_name_service.setObjectName("input_checkbox_use_description_name_service") - self.gridLayout_3.addWidget(self.input_checkbox_use_description_name_service, 15, 0, 1, 4) - spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.gridLayout_3.addItem(spacerItem2, 6, 3, 1, 1) - self.input_checkbox_use_display_name_service = QtWidgets.QCheckBox(self.groupbox_options) - self.input_checkbox_use_display_name_service.setObjectName("input_checkbox_use_display_name_service") - self.gridLayout_3.addWidget(self.input_checkbox_use_display_name_service, 14, 0, 1, 4) - self.label_custom_ca_file = QtWidgets.QLabel(self.groupbox_options) - self.label_custom_ca_file.setObjectName("label_custom_ca_file") - self.gridLayout_3.addWidget(self.label_custom_ca_file, 2, 0, 1, 1) + self.gridLayout_3.addWidget(self.input_checkbox_use_display_name_host, 17, 1, 1, 3) self.input_checkbox_ignore_cert = QtWidgets.QCheckBox(self.groupbox_options) self.input_checkbox_ignore_cert.setObjectName("input_checkbox_ignore_cert") - self.gridLayout_3.addWidget(self.input_checkbox_ignore_cert, 0, 0, 1, 4) + self.gridLayout_3.addWidget(self.input_checkbox_ignore_cert, 0, 1, 1, 3) + self.input_checkbox_use_display_name_service = QtWidgets.QCheckBox(self.groupbox_options) + self.input_checkbox_use_display_name_service.setObjectName("input_checkbox_use_display_name_service") + self.gridLayout_3.addWidget(self.input_checkbox_use_display_name_service, 18, 1, 1, 3) + self.input_checkbox_no_cookie_auth = QtWidgets.QCheckBox(self.groupbox_options) + self.input_checkbox_no_cookie_auth.setObjectName("input_checkbox_no_cookie_auth") + self.gridLayout_3.addWidget(self.input_checkbox_no_cookie_auth, 16, 1, 1, 3) + self.label_autologin_key = QtWidgets.QLabel(self.groupbox_options) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_autologin_key.sizePolicy().hasHeightForWidth()) + self.label_autologin_key.setSizePolicy(sizePolicy) + self.label_autologin_key.setObjectName("label_autologin_key") + self.gridLayout_3.addWidget(self.label_autologin_key, 9, 1, 1, 1) + self.input_combobox_authentication = QtWidgets.QComboBox(self.groupbox_options) + self.input_combobox_authentication.setObjectName("input_combobox_authentication") + self.gridLayout_3.addWidget(self.input_combobox_authentication, 5, 2, 1, 1) + self.input_lineedit_autologin_key = QtWidgets.QLineEdit(self.groupbox_options) + self.input_lineedit_autologin_key.setText("") + self.input_lineedit_autologin_key.setObjectName("input_lineedit_autologin_key") + self.gridLayout_3.addWidget(self.input_lineedit_autologin_key, 9, 2, 1, 2) self.horizontalLayout = QtWidgets.QHBoxLayout() self.horizontalLayout.setObjectName("horizontalLayout") self.input_lineedit_custom_cert_ca_file = QtWidgets.QLineEdit(self.groupbox_options) @@ -214,6 +223,19 @@ def setupUi(self, settings_server): self.button_choose_custom_cert_ca_file.setObjectName("button_choose_custom_cert_ca_file") self.horizontalLayout.addWidget(self.button_choose_custom_cert_ca_file) self.gridLayout_3.addLayout(self.horizontalLayout, 2, 2, 1, 2) + self.label_service_filter = QtWidgets.QLabel(self.groupbox_options) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_service_filter.sizePolicy().hasHeightForWidth()) + self.label_service_filter.setSizePolicy(sizePolicy) + self.label_service_filter.setObjectName("label_service_filter") + self.gridLayout_3.addWidget(self.label_service_filter, 11, 1, 1, 1) + self.label_custom_ca_file = QtWidgets.QLabel(self.groupbox_options) + self.label_custom_ca_file.setObjectName("label_custom_ca_file") + self.gridLayout_3.addWidget(self.label_custom_ca_file, 2, 1, 1, 1) + spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.gridLayout_3.addItem(spacerItem1, 5, 3, 1, 1) self.groupbox_checkmk_views = QtWidgets.QGroupBox(self.groupbox_options) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) sizePolicy.setHorizontalStretch(0) @@ -241,31 +263,50 @@ def setupUi(self, settings_server): self.button_checkmk_view_services_reset = QtWidgets.QPushButton(self.groupbox_checkmk_views) self.button_checkmk_view_services_reset.setObjectName("button_checkmk_view_services_reset") self.gridLayout_4.addWidget(self.button_checkmk_view_services_reset, 1, 2, 1, 1) - self.gridLayout_3.addWidget(self.groupbox_checkmk_views, 17, 0, 1, 4) + self.gridLayout_3.addWidget(self.groupbox_checkmk_views, 21, 1, 1, 3) + self.input_lineedit_map_to_servicename = QtWidgets.QLineEdit(self.groupbox_options) + self.input_lineedit_map_to_servicename.setObjectName("input_lineedit_map_to_servicename") + self.gridLayout_3.addWidget(self.input_lineedit_map_to_servicename, 14, 2, 1, 2) + self.input_checkbox_use_autologin = QtWidgets.QCheckBox(self.groupbox_options) + self.input_checkbox_use_autologin.setObjectName("input_checkbox_use_autologin") + self.gridLayout_3.addWidget(self.input_checkbox_use_autologin, 7, 1, 1, 3) + self.input_lineedit_map_to_hostname = QtWidgets.QLineEdit(self.groupbox_options) + self.input_lineedit_map_to_hostname.setObjectName("input_lineedit_map_to_hostname") + self.gridLayout_3.addWidget(self.input_lineedit_map_to_hostname, 13, 2, 1, 2) + spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.gridLayout_3.addItem(spacerItem2, 6, 3, 1, 1) + self.horizontalLayout_timeout_seconds = QtWidgets.QHBoxLayout() + self.horizontalLayout_timeout_seconds.setSpacing(5) + self.horizontalLayout_timeout_seconds.setObjectName("horizontalLayout_timeout_seconds") + self.input_spinbox_timeout = QtWidgets.QSpinBox(self.groupbox_options) + self.input_spinbox_timeout.setObjectName("input_spinbox_timeout") + self.horizontalLayout_timeout_seconds.addWidget(self.input_spinbox_timeout) + self.label_timeout_sec = QtWidgets.QLabel(self.groupbox_options) + self.label_timeout_sec.setObjectName("label_timeout_sec") + self.horizontalLayout_timeout_seconds.addWidget(self.label_timeout_sec) + self.gridLayout_3.addLayout(self.horizontalLayout_timeout_seconds, 6, 2, 1, 1) + self.label_map_to_servicename = QtWidgets.QLabel(self.groupbox_options) + self.label_map_to_servicename.setObjectName("label_map_to_servicename") + self.gridLayout_3.addWidget(self.label_map_to_servicename, 14, 1, 1, 1) + self.input_checkbox_custom_cert_use = QtWidgets.QCheckBox(self.groupbox_options) + self.input_checkbox_custom_cert_use.setObjectName("input_checkbox_custom_cert_use") + self.gridLayout_3.addWidget(self.input_checkbox_custom_cert_use, 1, 1, 1, 2) self.label_auth_type = QtWidgets.QLabel(self.groupbox_options) self.label_auth_type.setObjectName("label_auth_type") - self.gridLayout_3.addWidget(self.label_auth_type, 5, 0, 1, 1) - self.input_lineedit_host_filter = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_host_filter.setText("") - self.input_lineedit_host_filter.setObjectName("input_lineedit_host_filter") - self.gridLayout_3.addWidget(self.input_lineedit_host_filter, 10, 2, 1, 2) + self.gridLayout_3.addWidget(self.label_auth_type, 5, 1, 1, 1) + self.input_lineedit_monitor_site = QtWidgets.QLineEdit(self.groupbox_options) + self.input_lineedit_monitor_site.setObjectName("input_lineedit_monitor_site") + self.gridLayout_3.addWidget(self.input_lineedit_monitor_site, 8, 2, 1, 1) + self.label_map_to_status_information = QtWidgets.QLabel(self.groupbox_options) + self.label_map_to_status_information.setObjectName("label_map_to_status_information") + self.gridLayout_3.addWidget(self.label_map_to_status_information, 15, 1, 1, 1) + self.input_lineedit_map_to_status_information = QtWidgets.QLineEdit(self.groupbox_options) + self.input_lineedit_map_to_status_information.setObjectName("input_lineedit_map_to_status_information") + self.gridLayout_3.addWidget(self.input_lineedit_map_to_status_information, 15, 2, 1, 2) self.input_lineedit_service_filter = QtWidgets.QLineEdit(self.groupbox_options) self.input_lineedit_service_filter.setText("") self.input_lineedit_service_filter.setObjectName("input_lineedit_service_filter") self.gridLayout_3.addWidget(self.input_lineedit_service_filter, 11, 2, 1, 2) - self.input_checkbox_force_authuser = QtWidgets.QCheckBox(self.groupbox_options) - self.input_checkbox_force_authuser.setObjectName("input_checkbox_force_authuser") - self.gridLayout_3.addWidget(self.input_checkbox_force_authuser, 16, 0, 1, 4) - self.input_lineedit_autologin_key = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_autologin_key.setText("") - self.input_lineedit_autologin_key.setObjectName("input_lineedit_autologin_key") - self.gridLayout_3.addWidget(self.input_lineedit_autologin_key, 9, 2, 1, 2) - self.input_combobox_authentication = QtWidgets.QComboBox(self.groupbox_options) - self.input_combobox_authentication.setObjectName("input_combobox_authentication") - self.gridLayout_3.addWidget(self.input_combobox_authentication, 5, 2, 1, 1) - self.label_timeout = QtWidgets.QLabel(self.groupbox_options) - self.label_timeout.setObjectName("label_timeout") - self.gridLayout_3.addWidget(self.label_timeout, 6, 0, 1, 1) self.label_host_filter = QtWidgets.QLabel(self.groupbox_options) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) @@ -273,40 +314,17 @@ def setupUi(self, settings_server): sizePolicy.setHeightForWidth(self.label_host_filter.sizePolicy().hasHeightForWidth()) self.label_host_filter.setSizePolicy(sizePolicy) self.label_host_filter.setObjectName("label_host_filter") - self.gridLayout_3.addWidget(self.label_host_filter, 10, 0, 1, 1) - self.label_service_filter = QtWidgets.QLabel(self.groupbox_options) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_service_filter.sizePolicy().hasHeightForWidth()) - self.label_service_filter.setSizePolicy(sizePolicy) - self.label_service_filter.setObjectName("label_service_filter") - self.gridLayout_3.addWidget(self.label_service_filter, 11, 0, 1, 1) - self.input_checkbox_custom_cert_use = QtWidgets.QCheckBox(self.groupbox_options) - self.input_checkbox_custom_cert_use.setObjectName("input_checkbox_custom_cert_use") - self.gridLayout_3.addWidget(self.input_checkbox_custom_cert_use, 1, 0, 1, 3) - self.label_autologin_key = QtWidgets.QLabel(self.groupbox_options) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_autologin_key.sizePolicy().hasHeightForWidth()) - self.label_autologin_key.setSizePolicy(sizePolicy) - self.label_autologin_key.setObjectName("label_autologin_key") - self.gridLayout_3.addWidget(self.label_autologin_key, 9, 0, 1, 1) - self.input_checkbox_no_cookie_auth = QtWidgets.QCheckBox(self.groupbox_options) - self.input_checkbox_no_cookie_auth.setObjectName("input_checkbox_no_cookie_auth") - self.gridLayout_3.addWidget(self.input_checkbox_no_cookie_auth, 12, 0, 1, 4) - self.label_monitor_site = QtWidgets.QLabel(self.groupbox_options) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_monitor_site.sizePolicy().hasHeightForWidth()) - self.label_monitor_site.setSizePolicy(sizePolicy) - self.label_monitor_site.setObjectName("label_monitor_site") - self.gridLayout_3.addWidget(self.label_monitor_site, 8, 0, 1, 1) - self.input_lineedit_monitor_site = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_monitor_site.setObjectName("input_lineedit_monitor_site") - self.gridLayout_3.addWidget(self.input_lineedit_monitor_site, 8, 2, 1, 1) + self.gridLayout_3.addWidget(self.label_host_filter, 10, 1, 1, 1) + self.label_timeout = QtWidgets.QLabel(self.groupbox_options) + self.label_timeout.setObjectName("label_timeout") + self.gridLayout_3.addWidget(self.label_timeout, 6, 1, 1, 1) + self.input_lineedit_host_filter = QtWidgets.QLineEdit(self.groupbox_options) + self.input_lineedit_host_filter.setText("") + self.input_lineedit_host_filter.setObjectName("input_lineedit_host_filter") + self.gridLayout_3.addWidget(self.input_lineedit_host_filter, 10, 2, 1, 2) + self.input_checkbox_use_description_name_service = QtWidgets.QCheckBox(self.groupbox_options) + self.input_checkbox_use_description_name_service.setObjectName("input_checkbox_use_description_name_service") + self.gridLayout_3.addWidget(self.input_checkbox_use_description_name_service, 19, 1, 1, 3) self.gridLayout.addWidget(self.groupbox_options, 28, 0, 1, 4) self.retranslateUi(settings_server) @@ -351,26 +369,29 @@ def retranslateUi(self, settings_server): self.input_checkbox_save_password.setText(_translate("settings_server", "Save password")) self.input_checkbox_show_options.setText(_translate("settings_server", "Show more options")) self.groupbox_options.setTitle(_translate("settings_server", "Options:")) + self.label_monitor_site.setText(_translate("settings_server", "Monitor Site:")) + self.input_checkbox_force_authuser.setText(_translate("settings_server", "Only show permitted hosts for \"see all\" users (1.4.0i1 or newer)")) + self.label_map_to_hostname.setText(_translate("settings_server", "Map to hostname:")) self.input_checkbox_use_display_name_host.setText(_translate("settings_server", "Use display name as host name")) - self.label_timeout_sec.setText(_translate("settings_server", "seconds")) - self.input_checkbox_use_autologin.setText(_translate("settings_server", "Use autologin")) - self.input_checkbox_use_description_name_service.setText(_translate("settings_server", "Use description as service name")) - self.input_checkbox_use_display_name_service.setText(_translate("settings_server", "Use display name as service name")) - self.label_custom_ca_file.setText(_translate("settings_server", "Custom CA file: ")) self.input_checkbox_ignore_cert.setText(_translate("settings_server", "Ignore SSL/TLS certificate")) + self.input_checkbox_use_display_name_service.setText(_translate("settings_server", "Use display name as service name")) + self.input_checkbox_no_cookie_auth.setText(_translate("settings_server", "Do not use cookie authentication")) + self.label_autologin_key.setText(_translate("settings_server", "Autologin Key:")) self.button_choose_custom_cert_ca_file.setText(_translate("settings_server", "Choose file...")) + self.label_service_filter.setText(_translate("settings_server", "Service filter:")) + self.label_custom_ca_file.setText(_translate("settings_server", "Custom CA file: ")) self.groupbox_checkmk_views.setTitle(_translate("settings_server", "Views:")) self.label_checkmk_view_hosts.setText(_translate("settings_server", "Hosts:")) self.label_checkmk_view_services.setText(_translate("settings_server", "Services:")) self.button_checkmk_view_hosts_reset.setText(_translate("settings_server", "Reset")) self.button_checkmk_view_services_reset.setText(_translate("settings_server", "Reset")) - self.label_auth_type.setText(_translate("settings_server", "Authentication:")) - self.input_checkbox_force_authuser.setText(_translate("settings_server", "Only show permitted hosts for \"see all\" users (1.4.0i1 or newer)")) - self.label_timeout.setText(_translate("settings_server", "Timeout:")) - self.label_host_filter.setText(_translate("settings_server", "Host filter:")) - self.label_service_filter.setText(_translate("settings_server", "Service filter:")) + self.input_checkbox_use_autologin.setText(_translate("settings_server", "Use autologin")) + self.label_timeout_sec.setText(_translate("settings_server", "seconds")) + self.label_map_to_servicename.setText(_translate("settings_server", "Map to servicename:")) self.input_checkbox_custom_cert_use.setText(_translate("settings_server", "Use custom CA file")) - self.label_autologin_key.setText(_translate("settings_server", "Autologin Key:")) - self.input_checkbox_no_cookie_auth.setText(_translate("settings_server", "Do not use cookie authentication")) - self.label_monitor_site.setText(_translate("settings_server", "Monitor Site:")) + self.label_auth_type.setText(_translate("settings_server", "Authentication:")) self.input_lineedit_monitor_site.setText(_translate("settings_server", "Site 1")) + self.label_map_to_status_information.setText(_translate("settings_server", "Map to status_information:")) + self.label_host_filter.setText(_translate("settings_server", "Host filter:")) + self.label_timeout.setText(_translate("settings_server", "Timeout:")) + self.input_checkbox_use_description_name_service.setText(_translate("settings_server", "Use description as service name")) diff --git a/Nagstamon/QUI/settings_server.ui b/Nagstamon/QUI/settings_server.ui index 395f93483..32d91d66c 100644 --- a/Nagstamon/QUI/settings_server.ui +++ b/Nagstamon/QUI/settings_server.ui @@ -344,88 +344,81 @@ <property name="verticalSpacing"> <number>5</number> </property> - <item row="13" column="0" colspan="4"> - <widget class="QCheckBox" name="input_checkbox_use_display_name_host"> + <item row="8" column="1"> + <widget class="QLabel" name="label_monitor_site"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> <property name="text"> - <string>Use display name as host name</string> + <string>Monitor Site:</string> </property> </widget> </item> - <item row="5" column="3"> - <spacer name="horizontalSpacer_2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item row="6" column="2"> - <layout class="QHBoxLayout" name="horizontalLayout_timeout_seconds"> - <property name="spacing"> - <number>5</number> + <item row="20" column="1" colspan="3"> + <widget class="QCheckBox" name="input_checkbox_force_authuser"> + <property name="text"> + <string>Only show permitted hosts for "see all" users (1.4.0i1 or newer)</string> </property> - <item> - <widget class="QSpinBox" name="input_spinbox_timeout"/> - </item> - <item> - <widget class="QLabel" name="label_timeout_sec"> - <property name="text"> - <string>seconds</string> - </property> - </widget> - </item> - </layout> + </widget> </item> - <item row="7" column="0" colspan="4"> - <widget class="QCheckBox" name="input_checkbox_use_autologin"> + <item row="13" column="1"> + <widget class="QLabel" name="label_map_to_hostname"> <property name="text"> - <string>Use autologin</string> + <string>Map to hostname:</string> </property> </widget> </item> - <item row="15" column="0" colspan="4"> - <widget class="QCheckBox" name="input_checkbox_use_description_name_service"> + <item row="17" column="1" colspan="3"> + <widget class="QCheckBox" name="input_checkbox_use_display_name_host"> <property name="text"> - <string>Use description as service name</string> + <string>Use display name as host name</string> </property> </widget> </item> - <item row="6" column="3"> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> + <item row="0" column="1" colspan="3"> + <widget class="QCheckBox" name="input_checkbox_ignore_cert"> + <property name="text"> + <string>Ignore SSL/TLS certificate</string> </property> - </spacer> + </widget> </item> - <item row="14" column="0" colspan="4"> + <item row="18" column="1" colspan="3"> <widget class="QCheckBox" name="input_checkbox_use_display_name_service"> <property name="text"> <string>Use display name as service name</string> </property> </widget> </item> - <item row="2" column="0"> - <widget class="QLabel" name="label_custom_ca_file"> + <item row="16" column="1" colspan="3"> + <widget class="QCheckBox" name="input_checkbox_no_cookie_auth"> <property name="text"> - <string>Custom CA file: </string> + <string>Do not use cookie authentication</string> </property> </widget> </item> - <item row="0" column="0" colspan="4"> - <widget class="QCheckBox" name="input_checkbox_ignore_cert"> + <item row="9" column="1"> + <widget class="QLabel" name="label_autologin_key"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> <property name="text"> - <string>Ignore SSL/TLS certificate</string> + <string>Autologin Key:</string> + </property> + </widget> + </item> + <item row="5" column="2"> + <widget class="QComboBox" name="input_combobox_authentication"/> + </item> + <item row="9" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_autologin_key"> + <property name="text"> + <string/> </property> </widget> </item> @@ -443,7 +436,40 @@ </item> </layout> </item> - <item row="17" column="0" colspan="4"> + <item row="11" column="1"> + <widget class="QLabel" name="label_service_filter"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Service filter:</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLabel" name="label_custom_ca_file"> + <property name="text"> + <string>Custom CA file: </string> + </property> + </widget> + </item> + <item row="5" column="3"> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="21" column="1" colspan="3"> <widget class="QGroupBox" name="groupbox_checkmk_views"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> @@ -492,86 +518,96 @@ </layout> </widget> </item> - <item row="5" column="0"> - <widget class="QLabel" name="label_auth_type"> + <item row="14" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_map_to_servicename"/> + </item> + <item row="7" column="1" colspan="3"> + <widget class="QCheckBox" name="input_checkbox_use_autologin"> <property name="text"> - <string>Authentication:</string> + <string>Use autologin</string> </property> </widget> </item> - <item row="10" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_host_filter"> - <property name="text"> - <string/> + <item row="13" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_map_to_hostname"/> + </item> + <item row="6" column="3"> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> </property> - </widget> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> </item> - <item row="11" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_service_filter"> - <property name="text"> - <string/> + <item row="6" column="2"> + <layout class="QHBoxLayout" name="horizontalLayout_timeout_seconds"> + <property name="spacing"> + <number>5</number> </property> - </widget> + <item> + <widget class="QSpinBox" name="input_spinbox_timeout"/> + </item> + <item> + <widget class="QLabel" name="label_timeout_sec"> + <property name="text"> + <string>seconds</string> + </property> + </widget> + </item> + </layout> </item> - <item row="16" column="0" colspan="4"> - <widget class="QCheckBox" name="input_checkbox_force_authuser"> + <item row="14" column="1"> + <widget class="QLabel" name="label_map_to_servicename"> <property name="text"> - <string>Only show permitted hosts for "see all" users (1.4.0i1 or newer)</string> + <string>Map to servicename:</string> </property> </widget> </item> - <item row="9" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_autologin_key"> + <item row="1" column="1" colspan="2"> + <widget class="QCheckBox" name="input_checkbox_custom_cert_use"> <property name="text"> - <string/> + <string>Use custom CA file</string> </property> </widget> </item> - <item row="5" column="2"> - <widget class="QComboBox" name="input_combobox_authentication"/> - </item> - <item row="6" column="0"> - <widget class="QLabel" name="label_timeout"> + <item row="5" column="1"> + <widget class="QLabel" name="label_auth_type"> <property name="text"> - <string>Timeout:</string> + <string>Authentication:</string> </property> </widget> </item> - <item row="10" column="0"> - <widget class="QLabel" name="label_host_filter"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> + <item row="8" column="2"> + <widget class="QLineEdit" name="input_lineedit_monitor_site"> <property name="text"> - <string>Host filter:</string> + <string>Site 1</string> </property> </widget> </item> - <item row="11" column="0"> - <widget class="QLabel" name="label_service_filter"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> + <item row="15" column="1"> + <widget class="QLabel" name="label_map_to_status_information"> <property name="text"> - <string>Service filter:</string> + <string>Map to status_information:</string> </property> </widget> </item> - <item row="1" column="0" colspan="3"> - <widget class="QCheckBox" name="input_checkbox_custom_cert_use"> + <item row="15" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_map_to_status_information"/> + </item> + <item row="11" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_service_filter"> <property name="text"> - <string>Use custom CA file</string> + <string/> </property> </widget> </item> - <item row="9" column="0"> - <widget class="QLabel" name="label_autologin_key"> + <item row="10" column="1"> + <widget class="QLabel" name="label_host_filter"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> <horstretch>0</horstretch> @@ -579,34 +615,28 @@ </sizepolicy> </property> <property name="text"> - <string>Autologin Key:</string> + <string>Host filter:</string> </property> </widget> </item> - <item row="12" column="0" colspan="4"> - <widget class="QCheckBox" name="input_checkbox_no_cookie_auth"> + <item row="6" column="1"> + <widget class="QLabel" name="label_timeout"> <property name="text"> - <string>Do not use cookie authentication</string> + <string>Timeout:</string> </property> </widget> </item> - <item row="8" column="0"> - <widget class="QLabel" name="label_monitor_site"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> + <item row="10" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_host_filter"> <property name="text"> - <string>Monitor Site:</string> + <string/> </property> </widget> </item> - <item row="8" column="2"> - <widget class="QLineEdit" name="input_lineedit_monitor_site"> + <item row="19" column="1" colspan="3"> + <widget class="QCheckBox" name="input_checkbox_use_description_name_service"> <property name="text"> - <string>Site 1</string> + <string>Use description as service name</string> </property> </widget> </item> diff --git a/Nagstamon/Servers/Prometheus.py b/Nagstamon/Servers/Prometheus.py index aba523940..d208f8930 100644 --- a/Nagstamon/Servers/Prometheus.py +++ b/Nagstamon/Servers/Prometheus.py @@ -31,12 +31,12 @@ # # Release Notes: # -# [1.1.0] - 2020-06-11: +# [1.1.0] - 2020-06-12: # * fixed: # Some more errors with unset fields from Prometheus # * added: -# Backend feature preparation to be able to configure which labels get mapped to servicename and hostname... -# ...and which annotations get mapped to status_information +# Feature for configuring which labels get mapped to servicename and hostname... +# ...and which annotations get mapped to status_information # # [1.0.2] - 2020-06-07: # * fixed: @@ -165,11 +165,6 @@ def _get_status(self): self.Debug(server=self.get_name(), debug="Fetched JSON: " + pprint.pformat(data)) - # TODO: fetch these strings from GUI - map_to_hostname = "pod_name,namespace,instance" - map_to_servicename = "alertname" - map_to_status_information = "message,summary,description" - for alert in data["data"]["alerts"]: if conf.debug_mode: self.Debug( @@ -185,13 +180,13 @@ def _get_status(self): continue hostname = "unknown" - for host_label in map_to_hostname.split(','): + for host_label in self.map_to_hostname.split(','): if host_label in labels: hostname = labels.get(host_label) break servicename = "unknown" - for service_label in map_to_servicename.split(','): + for service_label in self.map_to_servicename.split(','): if service_label in labels: servicename = labels.get(service_label) break @@ -207,7 +202,7 @@ def _get_status(self): annotations = alert.get("annotations", {}) status_information = "" - for status_information_label in map_to_status_information.split(','): + for status_information_label in self.map_to_status_information.split(','): if status_information_label in annotations: status_information = annotations.get(status_information_label) break diff --git a/Nagstamon/Servers/__init__.py b/Nagstamon/Servers/__init__.py index 1fb36c5cc..234436227 100644 --- a/Nagstamon/Servers/__init__.py +++ b/Nagstamon/Servers/__init__.py @@ -181,6 +181,11 @@ def create_server(server=None): # Zabbix new_server.use_description_name_service = server.use_description_name_service + # Prometheus + new_server.map_to_hostname = server.map_to_hostname + new_server.map_to_servicename = server.map_to_servicename + new_server.map_to_status_information = server.map_to_status_information + # server's individual preparations for HTTP connections (for example cookie creation) # is done in GetStatus() method of monitor if server.enabled is True: From aebbd37928e2d672e0f078206f0f5e9e9c927a46 Mon Sep 17 00:00:00 2001 From: Stephan Schwarz <schwarz@e-spirit.com> Date: Fri, 12 Jun 2020 14:40:24 +0200 Subject: [PATCH 020/884] increased version string --- Nagstamon/Config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 6d1e1b888..41cb9c679 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -123,7 +123,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.5-20200611' + VERSION = '3.5-20200612' WEBSITE = 'https://nagstamon.ifw-dresden.de' COPYRIGHT = '©2008-2020 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' From 168ebd2b4d125e46312e3bac2a718de6c816d046 Mon Sep 17 00:00:00 2001 From: Varac <varac@varac.net> Date: Sat, 13 Jun 2020 12:21:43 +0200 Subject: [PATCH 021/884] Detect OS release case-insensitive On some ubuntu systems, os_release_dict returns `ubuntu` instead of `Ubuntu`, so we compare case-insensitive. --- Nagstamon/Helpers.py | 6 ++++-- build/build.py | 5 ++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Nagstamon/Helpers.py b/Nagstamon/Helpers.py index d4fb749db..abebc5b7e 100644 --- a/Nagstamon/Helpers.py +++ b/Nagstamon/Helpers.py @@ -454,11 +454,13 @@ def get_distro(): for property in os_release_file.read_text().splitlines(): key, value = property.split('=', 1) os_release_dict[key] = value.strip('"').strip("'") - return (os_release_dict.get('ID'), os_release_dict.get('VERSION_ID'), os_release_dict.get('NAME')) + return (os_release_dict.get('ID').lower(), + os_release_dict.get('VERSION_ID').lower(), + os_release_dict.get('NAME').lower()) else: return False else: - return platform.dist() + return platform.dist().lower() # depending on column different functions have to be used diff --git a/build/build.py b/build/build.py index 49c00c5df..33cd36d94 100644 --- a/build/build.py +++ b/build/build.py @@ -212,8 +212,8 @@ def rpmmain(): DISTS = { 'debian': debmain, - 'Ubuntu': debmain, - 'LinuxMint': debmain, + 'ubuntu': debmain, + 'linuxmint': debmain, 'fedora': rpmmain } @@ -223,7 +223,6 @@ def rpmmain(): elif platform.system() == 'Darwin': macmain() else: - #dist = platform.dist()[0] dist = get_distro()[0] if dist in DISTS: DISTS[dist]() From da00b57c55f52987305e8d3f75d8045e8c10d639 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Mon, 15 Jun 2020 10:26:00 +0200 Subject: [PATCH 022/884] merged last changes, version 20200615 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 41cb9c679..4111f6f8d 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -123,7 +123,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.5-20200612' + VERSION = '3.5-20200615' WEBSITE = 'https://nagstamon.ifw-dresden.de' COPYRIGHT = '©2008-2020 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 63983b2a6..1fb3ac0e9 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.5-20200612) unstable; urgency=low +nagstamon (3.5-20200615) unstable; urgency=low * New upstream -- Henri Wahl <h.wahl@ifw-dresden.de> Fri, 15 May 2020 10:00:00 +0100 From ddf4a9a4ef2f0d5a338bee6327fbabfa0ee497c3 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Mon, 15 Jun 2020 16:40:59 +0200 Subject: [PATCH 023/884] fix for platform.dist with lower() --- Nagstamon/Helpers.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Helpers.py b/Nagstamon/Helpers.py index abebc5b7e..b9a196468 100644 --- a/Nagstamon/Helpers.py +++ b/Nagstamon/Helpers.py @@ -460,10 +460,12 @@ def get_distro(): else: return False else: - return platform.dist().lower() + # fix for non-working build on Debian<10 + dist_name, dist_version, dist_id = platform.dist() + return dist_name.lower(), dist_version, dist_id - # depending on column different functions have to be used +# depending on column different functions have to be used # 0 + 1 are column "Hosts", 1 + 2 are column "Service" due to extra font flag pictograms SORT_COLUMNS_FUNCTIONS = {0: compare_host, 1: compare_host, From 60a0005110a5f9b5bd4ff4882efaea3d00a2161c Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Fri, 19 Jun 2020 22:14:01 +0200 Subject: [PATCH 024/884] add python3-dateutil to setup.py --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index f794b2065..1c72d7a68 100644 --- a/setup.py +++ b/setup.py @@ -84,6 +84,7 @@ 'python3-beautifulsoup4 ' 'python3-crypto ' 'python3-cryptography ' + 'python3-dateutil ' 'python3-keyring ' 'python3-lxml ' 'python3-psutil ' From 78a501afec33d0dc79fd38614ee115dbe272544a Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Wed, 8 Jul 2020 12:39:20 +0200 Subject: [PATCH 025/884] applied IcingaWeb2.py fix from https://github.com/HenriWahl/Nagstamon/issues/653 --- Nagstamon/Config.py | 2 +- Nagstamon/Servers/IcingaWeb2.py | 6 ++++-- build/debian/changelog | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 4111f6f8d..f36c121ac 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -123,7 +123,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.5-20200615' + VERSION = '3.5-20200708' WEBSITE = 'https://nagstamon.ifw-dresden.de' COPYRIGHT = '©2008-2020 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/Servers/IcingaWeb2.py b/Nagstamon/Servers/IcingaWeb2.py index 9185d1b03..a293108cb 100644 --- a/Nagstamon/Servers/IcingaWeb2.py +++ b/Nagstamon/Servers/IcingaWeb2.py @@ -230,7 +230,8 @@ def _get_status(self): self.new_hosts[host_name].attempt = "HARD" # extra duration needed for calculation - duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(h['host_last_state_change'])) + last_change = h['host_last_state_change'] if h['host_last_state_change'] is not None else 0 + duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(last_change)) self.new_hosts[host_name].duration = strfdelta(duration, '{days}d {hours}h {minutes}m {seconds}s') del h, host_name @@ -328,7 +329,8 @@ def _get_status(self): self.new_hosts[host_name].services[service_name].attempt = "HARD" # extra duration needed for calculation - duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(s['service_last_state_change'])) + last_change = s['service_last_state_change'] if s['service_last_state_change'] is not None else 0 + duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(last_change)) self.new_hosts[host_name].services[service_name].duration = strfdelta(duration, '{days}d {hours}h {minutes}m {seconds}s') del s, host_name, service_name diff --git a/build/debian/changelog b/build/debian/changelog index 1fb3ac0e9..a362d7416 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.5-20200615) unstable; urgency=low +nagstamon (3.5-20200708) unstable; urgency=low * New upstream -- Henri Wahl <h.wahl@ifw-dresden.de> Fri, 15 May 2020 10:00:00 +0100 From 9dbcbe4dbc0ffebc25869090e74f2797cb8260a3 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Tue, 28 Jul 2020 16:50:08 +0200 Subject: [PATCH 026/884] avoid annoying action context menu bug --- Nagstamon/QUI/__init__.py | 98 +++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 46 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index e952c4fef..8689d674a 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -1579,7 +1579,8 @@ def show_window(self, event=None): else: vbox.button_fix_tls_error.hide() - if not conf.fullscreen and not conf.windowed: + if not conf.fullscreen and \ + not conf.windowed: # theory... width, height, x, y = self.calculate_size() # ...and practice @@ -1658,7 +1659,9 @@ def update_window(self): """ redraw window content, to be effective only when window is shown """ - if self.is_shown or conf.fullscreen or (conf.windowed and self.is_shown): + if self.is_shown or \ + conf.fullscreen or \ + (conf.windowed and self.is_shown): self.show_window() @pyqtSlot() @@ -4195,50 +4198,53 @@ def fill_data_array(self, sort_column, sort_order): self.info = {'hosts_flags_column_needed': False, 'services_flags_column_needed': False, } - # avoid race condition when waiting for password dialog - if len(QBRUSHES[0]) > 0: - # cruising the whole nagitems structure - for category in ('hosts', 'services'): - for state in self.server.nagitems_filtered[category].values(): - for item in state: - self.data_array.append(list(item.get_columns(HEADERS))) - - # hash for freshness comparison - hash = item.get_hash() - - if item.is_host(): - if hash in self.server.events_history and \ - self.server.events_history[hash] is True: - # second item in las data_array line is host flags - self.data_array[-1][1] += 'N' - else: - if hash in self.server.events_history and \ - self.server.events_history[hash] is True: - # fourth item in las data_array line is service flags - self.data_array[-1][3] += 'N' - # add text color as QBrush from status - self.data_array[-1].append(QBRUSHES[len(self.data_array) % 2][COLORS[item.status] + 'text']) - # add background color as QBrush from status - self.data_array[-1].append( - QBRUSHES[len(self.data_array) % 2][COLORS[item.status] + 'background']) - # add text color name for sorting data - self.data_array[-1].append(COLORS[item.status] + 'text') - # add background color name for sorting data - self.data_array[-1].append(COLORS[item.status] + 'background') - - # check if hosts and services flags should be shown - if self.data_array[-1][1] != '': - self.info['hosts_flags_column_needed'] = True - if self.data_array[-1][3] != '': - self.info['services_flags_column_needed'] = True - - self.data_array[-1].append('X') - - # sort data before it gets transmitted to treeview model - self.sort_data_array(self.sort_column, self.sort_order, False) - - # give sorted data to model - self.worker_data_array_filled.emit(self.data_array, self.info) + # only refresh table if there is no popup opened + print(APP.activePopupWidget()) + if not APP.activePopupWidget(): + # avoid race condition when waiting for password dialog + if len(QBRUSHES[0]) > 0: + # cruising the whole nagitems structure + for category in ('hosts', 'services'): + for state in self.server.nagitems_filtered[category].values(): + for item in state: + self.data_array.append(list(item.get_columns(HEADERS))) + + # hash for freshness comparison + hash = item.get_hash() + + if item.is_host(): + if hash in self.server.events_history and \ + self.server.events_history[hash] is True: + # second item in las data_array line is host flags + self.data_array[-1][1] += 'N' + else: + if hash in self.server.events_history and \ + self.server.events_history[hash] is True: + # fourth item in las data_array line is service flags + self.data_array[-1][3] += 'N' + # add text color as QBrush from status + self.data_array[-1].append(QBRUSHES[len(self.data_array) % 2][COLORS[item.status] + 'text']) + # add background color as QBrush from status + self.data_array[-1].append( + QBRUSHES[len(self.data_array) % 2][COLORS[item.status] + 'background']) + # add text color name for sorting data + self.data_array[-1].append(COLORS[item.status] + 'text') + # add background color name for sorting data + self.data_array[-1].append(COLORS[item.status] + 'background') + + # check if hosts and services flags should be shown + if self.data_array[-1][1] != '': + self.info['hosts_flags_column_needed'] = True + if self.data_array[-1][3] != '': + self.info['services_flags_column_needed'] = True + + self.data_array[-1].append('X') + + # sort data before it gets transmitted to treeview model + self.sort_data_array(self.sort_column, self.sort_order, False) + + # give sorted data to model + self.worker_data_array_filled.emit(self.data_array, self.info) @pyqtSlot(int, int, bool) def sort_data_array(self, sort_column, sort_order, header_clicked=False): From 48ce7bd51230c7668f46d7e5a171b41726d19a4d Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Tue, 28 Jul 2020 16:52:02 +0200 Subject: [PATCH 027/884] remove debug info --- Nagstamon/QUI/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 8689d674a..7adc3b237 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -4199,7 +4199,6 @@ def fill_data_array(self, sort_column, sort_order): 'services_flags_column_needed': False, } # only refresh table if there is no popup opened - print(APP.activePopupWidget()) if not APP.activePopupWidget(): # avoid race condition when waiting for password dialog if len(QBRUSHES[0]) > 0: From 31e3cf8f95432f57a0e0a2ab7f9f7dbcd87fbdbb Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Tue, 28 Jul 2020 16:53:57 +0200 Subject: [PATCH 028/884] version 20200728 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index f36c121ac..d31f1f01e 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -123,7 +123,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.5-20200708' + VERSION = '3.5-20200728' WEBSITE = 'https://nagstamon.ifw-dresden.de' COPYRIGHT = '©2008-2020 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index a362d7416..216ce60f2 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.5-20200708) unstable; urgency=low +nagstamon (3.5-20200728) unstable; urgency=low * New upstream - -- Henri Wahl <h.wahl@ifw-dresden.de> Fri, 15 May 2020 10:00:00 +0100 + -- Henri Wahl <h.wahl@ifw-dresden.de> Tue, 28 Jul 2020 17:00:00 +0100 nagstamon (3.4.1) stable; urgency=low * New upstream From c0d0bd302fb35c4aaa6f79c9d8dce270836aef52 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Wed, 29 Jul 2020 11:09:48 +0200 Subject: [PATCH 029/884] added requirements.txt --- requirements.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 000000000..fffa8de01 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,11 @@ +beautifulsoup4 +keyring==10.5.1 +lxml +psutil +pypiwin32 +pyqt5==5.13.2 +pysocks +python-dateutil +requests +requests-kerberos +setuptools==44.1.1 \ No newline at end of file From 00884922f390bf806284e983da5a9a318337b724 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Wed, 29 Jul 2020 11:24:18 +0200 Subject: [PATCH 030/884] added CONTRIBUTE.md --- CONTRIBUTE.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 CONTRIBUTE.md diff --git a/CONTRIBUTE.md b/CONTRIBUTE.md new file mode 100644 index 000000000..e889a0ea8 --- /dev/null +++ b/CONTRIBUTE.md @@ -0,0 +1,10 @@ +Contribution +============ + +Of course every contribution is welcome - without contributors Nagstamon never would have been what it is today. + +- Some basic information is already available at https://nagstamon.ifw-dresden.de/docs/requirements/. + +- The Python packages required to run Nagstamon are listed in https://github.com/HenriWahl/Nagstamon/blob/master/requirements.txt. + +- Building a binary release is easily done by running https://github.com/HenriWahl/Nagstamon/blob/master/build/build.py From 70af699dedd5b20e06fd07f08ee1fef1b21c5659 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Wed, 29 Jul 2020 12:33:33 +0200 Subject: [PATCH 031/884] modified IcingaWeb2.py according to https://github.com/HenriWahl/Nagstamon/issues/653#issuecomment-665543703 --- Nagstamon/Servers/IcingaWeb2.py | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/Nagstamon/Servers/IcingaWeb2.py b/Nagstamon/Servers/IcingaWeb2.py index a293108cb..9bdc40188 100644 --- a/Nagstamon/Servers/IcingaWeb2.py +++ b/Nagstamon/Servers/IcingaWeb2.py @@ -230,10 +230,12 @@ def _get_status(self): self.new_hosts[host_name].attempt = "HARD" # extra duration needed for calculation - last_change = h['host_last_state_change'] if h['host_last_state_change'] is not None else 0 - duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(last_change)) - self.new_hosts[host_name].duration = strfdelta(duration, '{days}d {hours}h {minutes}m {seconds}s') - + if h['host_last_state_change'] is not None: + last_change = h['host_last_state_change'] if h['host_last_state_change'] is not None else 0 + duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(last_change)) + self.new_hosts[host_name].duration = strfdelta(duration,'{days}d {hours}h {minutes}m {seconds}s') + else: + self.new_hosts[host_name].duration = 'n/a' del h, host_name except: import traceback @@ -287,17 +289,8 @@ def _get_status(self): # acknowledge needs host_description and no display name self.new_hosts[host_name].real_name = s['host_name'] - #if self.use_display_name_host == False: - # # legacy Icinga adjustments - # if 'service_description' in s: service_name = s['service_description'] - # elif 'description' in s: service_name = s['description'] - # elif 'service' in s: service_name = s['service'] - #else: - # service_name = s['service_display_name'] - # regarding to https://github.com/HenriWahl/Nagstamon/issues/400 Icinga2 needs no legacy adjustments service_name = s['service_display_name'] - # if a service does not exist create its object if not service_name in self.new_hosts[host_name].services: self.new_hosts[host_name].services[service_name] = GenericService() @@ -329,10 +322,13 @@ def _get_status(self): self.new_hosts[host_name].services[service_name].attempt = "HARD" # extra duration needed for calculation - last_change = s['service_last_state_change'] if s['service_last_state_change'] is not None else 0 - duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(last_change)) - self.new_hosts[host_name].services[service_name].duration = strfdelta(duration, '{days}d {hours}h {minutes}m {seconds}s') - + if s['service_last_state_change'] is not None: + last_change = s['service_last_state_change'] if s['service_last_state_change'] is not None else 0 + duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(last_change)) + self.new_hosts[host_name].services[service_name].duration = strfdelta(duration, '{days}d {hours}h {minutes}m {seconds}s') + else: + self.new_hosts[host_name].services[service_name].duration = 'n/a' + del s, host_name, service_name except: From 7b6e4316fd872392a00ccd5c880ece717e83954b Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Thu, 30 Jul 2020 15:25:34 +0200 Subject: [PATCH 032/884] checkmk do not force login with invalid credentials --- Nagstamon/QUI/__init__.py | 1 + Nagstamon/Servers/Generic.py | 3 +++ Nagstamon/Servers/Multisite.py | 16 +++++++++------- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 7adc3b237..eae2a00dc 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -6637,6 +6637,7 @@ def ok(self): self.server.username = self.ui.input_lineedit_username.text() self.server.password = self.ui.input_lineedit_password.text() + self.server.refresh_authentication = False # store password if it should be saved if self.ui.input_checkbox_save_password.isChecked(): diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index c0e046e58..dc4d7400a 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -182,6 +182,9 @@ def __init__(self, **kwds): # flag which tells GUI if there is an TLS problem self.tls_error = False + # counter for login attempts - have to be threaten differently by every monitoring server type + self.login_count = 0 + # to handle Icinga versions this information is necessary, might be of future use for others too self.version = '' diff --git a/Nagstamon/Servers/Multisite.py b/Nagstamon/Servers/Multisite.py index 8490e3456..f8a75182f 100644 --- a/Nagstamon/Servers/Multisite.py +++ b/Nagstamon/Servers/Multisite.py @@ -116,7 +116,7 @@ def init_HTTP(self): 'PEND': 'PENDING', } - if self.CookieAuth: + if self.CookieAuth and not self.refresh_authentication: # get cookie to access Checkmk web interface if 'cookies' in dir(self.session): if len(self.session.cookies) == 0: @@ -144,13 +144,12 @@ def _is_auth_in_cookies(self): return True return False - def _get_url(self, url): result = self.FetchURL(url, 'raw') content, error, status_code = result.result, result.error, result.status_code if error != '' or status_code >= 400: - raise MultisiteError(True, Result(result=content, + raise MultisiteError(True, Result(result=content, error=error, status_code=status_code)) @@ -163,7 +162,7 @@ def _get_url(self, url): raise MultisiteError(False, Result(result='\n'.join(c[1:]), error=c[0], status_code=status_code)) - + elif content.startswith('ERROR:'): raise MultisiteError(True, Result(result=content, error=content, @@ -175,7 +174,7 @@ def _get_url(self, url): self.refresh_authentication = True return '' - # looks like cookieauth + # looks like cookieauth elif content.startswith('<'): self.CookieAuth = True # if first attempt login and then try to get data again @@ -186,8 +185,12 @@ def _get_url(self, url): if content.startswith('<'): return '' - return eval(content) + # if finally still some <HTML> is sent this looks like a new login due to password change + if content.startswith('<'): + self.refresh_authentication = True + return '' + return eval(content) def _get_cookie_login(self): """ @@ -298,7 +301,6 @@ def _get_status(self): # Add filters to the url which should only be applied to the service request if conf.filter_services_on_unreachable_hosts == True: - ###url_params += '&hst2=0' # thanks to https://github.com/HenriWahl/Nagstamon/issues/510 url_params += '&hst0=On&hst1=On' From b5b64437f5f523c0f30fe24bf42d8bc88c3c862a Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Thu, 30 Jul 2020 16:40:30 +0200 Subject: [PATCH 033/884] nagios/icinga1 do not force login with invalid credentials --- Nagstamon/QUI/__init__.py | 3 ++- Nagstamon/Servers/Generic.py | 20 ++++++++++---------- Nagstamon/Servers/Icinga.py | 6 +++--- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index eae2a00dc..c7f9a63da 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -4104,7 +4104,8 @@ def get_status(self): self.change_label_status.emit('Refreshing...', '') # get status from server instance if connection was already possible and no TLS error - if not self.server.tls_error: + if not self.server.tls_error and \ + not self.server.refresh_authentication: status = self.server.GetStatus() else: # dummy status result diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index dc4d7400a..ad26cff94 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -248,18 +248,20 @@ def init_config(self): 'soft': self.monitor_cgi_url + '/status.cgi?hostgroup=all&style=hostdetail&hoststatustypes=12&hostprops=524288&limit=0'} def init_HTTP(self): - ''' - partly not constantly working Basic Authorization requires extra Authorization headers, - different between various server types - ''' - if self.session is None: + """ + partly not constantly working Basic Authorization requires extra Authorization headers, + different between various server types + """ + if self.refresh_authentication: + self.session = None + return False + elif self.session is None: self.session = requests.Session() self.session.headers['User-Agent'] = self.USER_AGENT # support for different authentication types if self.authentication == 'basic': # basic authentication - # self.session.auth = (self.username, self.password) self.session.auth = requests.auth.HTTPBasicAuth(self.username, self.password) elif self.authentication == 'digest': self.session.auth = requests.auth.HTTPDigestAuth(self.username, self.password) @@ -276,6 +278,7 @@ def init_HTTP(self): # add proxy information self.proxify(self.session) + return True def proxify(self, requester): ''' @@ -1448,7 +1451,7 @@ def FetchURL(self, url, giveback='obj', cgi_data=None, no_auth=False, multipart= # use session only for connections to monitor servers, other requests like looking for updates # should go out without credentials - if no_auth is False: + if no_auth is False and not self.refresh_authentication: # check if there is really a session if not self.session: self.reset_HTTP() @@ -1476,9 +1479,6 @@ def FetchURL(self, url, giveback='obj', cgi_data=None, no_auth=False, multipart= # add proxy information if necessary self.proxify(temporary_session) - # no need to check TLS validity for temporary sessions like update check - ###temporary_session.verify = False - # most requests come without multipart/form-data if multipart is False: if cgi_data is None: diff --git a/Nagstamon/Servers/Icinga.py b/Nagstamon/Servers/Icinga.py index 802fd3818..98901cecd 100644 --- a/Nagstamon/Servers/Icinga.py +++ b/Nagstamon/Servers/Icinga.py @@ -53,9 +53,9 @@ def init_HTTP(self): """ GenericServer.init_HTTP(self) - if not "Referer" in self.session.headers: + if not 'Referer' in self.session.headers: # to execute actions since Icinga 1.11 a Referer Header is necessary - self.session.headers["Referer"] = self.monitor_cgi_url + "/cmd.cgi" + self.session.headers['Referer'] = self.monitor_cgi_url + '/cmd.cgi' def get_server_version(self): @@ -72,7 +72,7 @@ def get_server_version(self): if tacraw.startswith('<'): self.json = False tacsoup = BeautifulSoup(tacraw, 'html.parser') - self.version = tacsoup.find('a', { 'class' : 'homepageURL' }) + self.version = tacsoup.find('a', { 'class': 'homepageURL' }) # only extract version if HTML seemed to be OK if 'contents' in self.version.__dict__: self.version = self.version.contents[0].split('Icinga ')[1] From 34173c51485b884a3213172cd58c331c7a670314 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Thu, 30 Jul 2020 15:25:34 +0200 Subject: [PATCH 034/884] checkmk do not force login with invalid credentials --- Nagstamon/QUI/__init__.py | 1 + Nagstamon/Servers/Generic.py | 3 +++ Nagstamon/Servers/Multisite.py | 16 +++++++++------- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 7adc3b237..eae2a00dc 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -6637,6 +6637,7 @@ def ok(self): self.server.username = self.ui.input_lineedit_username.text() self.server.password = self.ui.input_lineedit_password.text() + self.server.refresh_authentication = False # store password if it should be saved if self.ui.input_checkbox_save_password.isChecked(): diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index c0e046e58..dc4d7400a 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -182,6 +182,9 @@ def __init__(self, **kwds): # flag which tells GUI if there is an TLS problem self.tls_error = False + # counter for login attempts - have to be threaten differently by every monitoring server type + self.login_count = 0 + # to handle Icinga versions this information is necessary, might be of future use for others too self.version = '' diff --git a/Nagstamon/Servers/Multisite.py b/Nagstamon/Servers/Multisite.py index 8490e3456..f8a75182f 100644 --- a/Nagstamon/Servers/Multisite.py +++ b/Nagstamon/Servers/Multisite.py @@ -116,7 +116,7 @@ def init_HTTP(self): 'PEND': 'PENDING', } - if self.CookieAuth: + if self.CookieAuth and not self.refresh_authentication: # get cookie to access Checkmk web interface if 'cookies' in dir(self.session): if len(self.session.cookies) == 0: @@ -144,13 +144,12 @@ def _is_auth_in_cookies(self): return True return False - def _get_url(self, url): result = self.FetchURL(url, 'raw') content, error, status_code = result.result, result.error, result.status_code if error != '' or status_code >= 400: - raise MultisiteError(True, Result(result=content, + raise MultisiteError(True, Result(result=content, error=error, status_code=status_code)) @@ -163,7 +162,7 @@ def _get_url(self, url): raise MultisiteError(False, Result(result='\n'.join(c[1:]), error=c[0], status_code=status_code)) - + elif content.startswith('ERROR:'): raise MultisiteError(True, Result(result=content, error=content, @@ -175,7 +174,7 @@ def _get_url(self, url): self.refresh_authentication = True return '' - # looks like cookieauth + # looks like cookieauth elif content.startswith('<'): self.CookieAuth = True # if first attempt login and then try to get data again @@ -186,8 +185,12 @@ def _get_url(self, url): if content.startswith('<'): return '' - return eval(content) + # if finally still some <HTML> is sent this looks like a new login due to password change + if content.startswith('<'): + self.refresh_authentication = True + return '' + return eval(content) def _get_cookie_login(self): """ @@ -298,7 +301,6 @@ def _get_status(self): # Add filters to the url which should only be applied to the service request if conf.filter_services_on_unreachable_hosts == True: - ###url_params += '&hst2=0' # thanks to https://github.com/HenriWahl/Nagstamon/issues/510 url_params += '&hst0=On&hst1=On' From 9e3b3523eb60a37fe81c19d4eb2bde2884e505a5 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Thu, 30 Jul 2020 16:40:30 +0200 Subject: [PATCH 035/884] nagios/icinga1 do not force login with invalid credentials --- Nagstamon/QUI/__init__.py | 3 ++- Nagstamon/Servers/Generic.py | 20 ++++++++++---------- Nagstamon/Servers/Icinga.py | 6 +++--- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index eae2a00dc..c7f9a63da 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -4104,7 +4104,8 @@ def get_status(self): self.change_label_status.emit('Refreshing...', '') # get status from server instance if connection was already possible and no TLS error - if not self.server.tls_error: + if not self.server.tls_error and \ + not self.server.refresh_authentication: status = self.server.GetStatus() else: # dummy status result diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index dc4d7400a..ad26cff94 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -248,18 +248,20 @@ def init_config(self): 'soft': self.monitor_cgi_url + '/status.cgi?hostgroup=all&style=hostdetail&hoststatustypes=12&hostprops=524288&limit=0'} def init_HTTP(self): - ''' - partly not constantly working Basic Authorization requires extra Authorization headers, - different between various server types - ''' - if self.session is None: + """ + partly not constantly working Basic Authorization requires extra Authorization headers, + different between various server types + """ + if self.refresh_authentication: + self.session = None + return False + elif self.session is None: self.session = requests.Session() self.session.headers['User-Agent'] = self.USER_AGENT # support for different authentication types if self.authentication == 'basic': # basic authentication - # self.session.auth = (self.username, self.password) self.session.auth = requests.auth.HTTPBasicAuth(self.username, self.password) elif self.authentication == 'digest': self.session.auth = requests.auth.HTTPDigestAuth(self.username, self.password) @@ -276,6 +278,7 @@ def init_HTTP(self): # add proxy information self.proxify(self.session) + return True def proxify(self, requester): ''' @@ -1448,7 +1451,7 @@ def FetchURL(self, url, giveback='obj', cgi_data=None, no_auth=False, multipart= # use session only for connections to monitor servers, other requests like looking for updates # should go out without credentials - if no_auth is False: + if no_auth is False and not self.refresh_authentication: # check if there is really a session if not self.session: self.reset_HTTP() @@ -1476,9 +1479,6 @@ def FetchURL(self, url, giveback='obj', cgi_data=None, no_auth=False, multipart= # add proxy information if necessary self.proxify(temporary_session) - # no need to check TLS validity for temporary sessions like update check - ###temporary_session.verify = False - # most requests come without multipart/form-data if multipart is False: if cgi_data is None: diff --git a/Nagstamon/Servers/Icinga.py b/Nagstamon/Servers/Icinga.py index 802fd3818..98901cecd 100644 --- a/Nagstamon/Servers/Icinga.py +++ b/Nagstamon/Servers/Icinga.py @@ -53,9 +53,9 @@ def init_HTTP(self): """ GenericServer.init_HTTP(self) - if not "Referer" in self.session.headers: + if not 'Referer' in self.session.headers: # to execute actions since Icinga 1.11 a Referer Header is necessary - self.session.headers["Referer"] = self.monitor_cgi_url + "/cmd.cgi" + self.session.headers['Referer'] = self.monitor_cgi_url + '/cmd.cgi' def get_server_version(self): @@ -72,7 +72,7 @@ def get_server_version(self): if tacraw.startswith('<'): self.json = False tacsoup = BeautifulSoup(tacraw, 'html.parser') - self.version = tacsoup.find('a', { 'class' : 'homepageURL' }) + self.version = tacsoup.find('a', { 'class': 'homepageURL' }) # only extract version if HTML seemed to be OK if 'contents' in self.version.__dict__: self.version = self.version.contents[0].split('Icinga ')[1] From 956ea7716651b7462bd9341f6617ae93b3af0fb7 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Fri, 31 Jul 2020 10:52:00 +0200 Subject: [PATCH 036/884] version 20200731 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index d31f1f01e..6c562880a 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -123,7 +123,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.5-20200728' + VERSION = '3.5-20200731' WEBSITE = 'https://nagstamon.ifw-dresden.de' COPYRIGHT = '©2008-2020 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 216ce60f2..bc3f3110f 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.5-20200728) unstable; urgency=low +nagstamon (3.5-20200731) unstable; urgency=low * New upstream - -- Henri Wahl <h.wahl@ifw-dresden.de> Tue, 28 Jul 2020 17:00:00 +0100 + -- Henri Wahl <h.wahl@ifw-dresden.de> Fri, 31 Jul 2020 17:00:00 +0100 nagstamon (3.4.1) stable; urgency=low * New upstream From 9a1a80b13314fe26ca4f3e7a7da156f75d70b016 Mon Sep 17 00:00:00 2001 From: shastry <vinayshastry@gmail.com> Date: Wed, 5 Aug 2020 10:40:46 +0530 Subject: [PATCH 037/884] Toggle popup on system tray middle click. --- Nagstamon/QUI/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 7adc3b237..234607607 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -424,7 +424,7 @@ def icon_clicked(self, reason): # self.show_menu.emit() # only react on left mouse click on OSX # elif reason == (QSystemTrayIcon.Trigger or QSystemTrayIcon.DoubleClick): - if reason == (QSystemTrayIcon.Trigger or QSystemTrayIcon.DoubleClick): + if reason in (QSystemTrayIcon.Trigger, QSystemTrayIcon.DoubleClick, QSystemTrayIcon.MiddleClick): # when green icon is displayed and no popwin is about to po up show at least menu if get_worst_status() == 'UP' and OS == OS_DARWIN: self.menu.show_at_cursor() From 835eea094be30ccd5c2fa600e69a1906a3c349a2 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Thu, 30 Jul 2020 15:25:34 +0200 Subject: [PATCH 038/884] checkmk do not force login with invalid credentials (cherry picked from commit 7b6e4316fd872392a00ccd5c880ece717e83954b) --- Nagstamon/QUI/__init__.py | 1 + Nagstamon/Servers/Generic.py | 3 +++ Nagstamon/Servers/Multisite.py | 16 +++++++++------- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 7adc3b237..eae2a00dc 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -6637,6 +6637,7 @@ def ok(self): self.server.username = self.ui.input_lineedit_username.text() self.server.password = self.ui.input_lineedit_password.text() + self.server.refresh_authentication = False # store password if it should be saved if self.ui.input_checkbox_save_password.isChecked(): diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index c0e046e58..dc4d7400a 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -182,6 +182,9 @@ def __init__(self, **kwds): # flag which tells GUI if there is an TLS problem self.tls_error = False + # counter for login attempts - have to be threaten differently by every monitoring server type + self.login_count = 0 + # to handle Icinga versions this information is necessary, might be of future use for others too self.version = '' diff --git a/Nagstamon/Servers/Multisite.py b/Nagstamon/Servers/Multisite.py index 8490e3456..f8a75182f 100644 --- a/Nagstamon/Servers/Multisite.py +++ b/Nagstamon/Servers/Multisite.py @@ -116,7 +116,7 @@ def init_HTTP(self): 'PEND': 'PENDING', } - if self.CookieAuth: + if self.CookieAuth and not self.refresh_authentication: # get cookie to access Checkmk web interface if 'cookies' in dir(self.session): if len(self.session.cookies) == 0: @@ -144,13 +144,12 @@ def _is_auth_in_cookies(self): return True return False - def _get_url(self, url): result = self.FetchURL(url, 'raw') content, error, status_code = result.result, result.error, result.status_code if error != '' or status_code >= 400: - raise MultisiteError(True, Result(result=content, + raise MultisiteError(True, Result(result=content, error=error, status_code=status_code)) @@ -163,7 +162,7 @@ def _get_url(self, url): raise MultisiteError(False, Result(result='\n'.join(c[1:]), error=c[0], status_code=status_code)) - + elif content.startswith('ERROR:'): raise MultisiteError(True, Result(result=content, error=content, @@ -175,7 +174,7 @@ def _get_url(self, url): self.refresh_authentication = True return '' - # looks like cookieauth + # looks like cookieauth elif content.startswith('<'): self.CookieAuth = True # if first attempt login and then try to get data again @@ -186,8 +185,12 @@ def _get_url(self, url): if content.startswith('<'): return '' - return eval(content) + # if finally still some <HTML> is sent this looks like a new login due to password change + if content.startswith('<'): + self.refresh_authentication = True + return '' + return eval(content) def _get_cookie_login(self): """ @@ -298,7 +301,6 @@ def _get_status(self): # Add filters to the url which should only be applied to the service request if conf.filter_services_on_unreachable_hosts == True: - ###url_params += '&hst2=0' # thanks to https://github.com/HenriWahl/Nagstamon/issues/510 url_params += '&hst0=On&hst1=On' From 3db3aeb1aab3c134dd6fc3a5a41354df1ea250a4 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Thu, 30 Jul 2020 16:40:30 +0200 Subject: [PATCH 039/884] nagios/icinga1 do not force login with invalid credentials (cherry picked from commit b5b64437f5f523c0f30fe24bf42d8bc88c3c862a) --- Nagstamon/QUI/__init__.py | 3 ++- Nagstamon/Servers/Generic.py | 20 ++++++++++---------- Nagstamon/Servers/Icinga.py | 6 +++--- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index eae2a00dc..c7f9a63da 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -4104,7 +4104,8 @@ def get_status(self): self.change_label_status.emit('Refreshing...', '') # get status from server instance if connection was already possible and no TLS error - if not self.server.tls_error: + if not self.server.tls_error and \ + not self.server.refresh_authentication: status = self.server.GetStatus() else: # dummy status result diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index dc4d7400a..ad26cff94 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -248,18 +248,20 @@ def init_config(self): 'soft': self.monitor_cgi_url + '/status.cgi?hostgroup=all&style=hostdetail&hoststatustypes=12&hostprops=524288&limit=0'} def init_HTTP(self): - ''' - partly not constantly working Basic Authorization requires extra Authorization headers, - different between various server types - ''' - if self.session is None: + """ + partly not constantly working Basic Authorization requires extra Authorization headers, + different between various server types + """ + if self.refresh_authentication: + self.session = None + return False + elif self.session is None: self.session = requests.Session() self.session.headers['User-Agent'] = self.USER_AGENT # support for different authentication types if self.authentication == 'basic': # basic authentication - # self.session.auth = (self.username, self.password) self.session.auth = requests.auth.HTTPBasicAuth(self.username, self.password) elif self.authentication == 'digest': self.session.auth = requests.auth.HTTPDigestAuth(self.username, self.password) @@ -276,6 +278,7 @@ def init_HTTP(self): # add proxy information self.proxify(self.session) + return True def proxify(self, requester): ''' @@ -1448,7 +1451,7 @@ def FetchURL(self, url, giveback='obj', cgi_data=None, no_auth=False, multipart= # use session only for connections to monitor servers, other requests like looking for updates # should go out without credentials - if no_auth is False: + if no_auth is False and not self.refresh_authentication: # check if there is really a session if not self.session: self.reset_HTTP() @@ -1476,9 +1479,6 @@ def FetchURL(self, url, giveback='obj', cgi_data=None, no_auth=False, multipart= # add proxy information if necessary self.proxify(temporary_session) - # no need to check TLS validity for temporary sessions like update check - ###temporary_session.verify = False - # most requests come without multipart/form-data if multipart is False: if cgi_data is None: diff --git a/Nagstamon/Servers/Icinga.py b/Nagstamon/Servers/Icinga.py index 802fd3818..98901cecd 100644 --- a/Nagstamon/Servers/Icinga.py +++ b/Nagstamon/Servers/Icinga.py @@ -53,9 +53,9 @@ def init_HTTP(self): """ GenericServer.init_HTTP(self) - if not "Referer" in self.session.headers: + if not 'Referer' in self.session.headers: # to execute actions since Icinga 1.11 a Referer Header is necessary - self.session.headers["Referer"] = self.monitor_cgi_url + "/cmd.cgi" + self.session.headers['Referer'] = self.monitor_cgi_url + '/cmd.cgi' def get_server_version(self): @@ -72,7 +72,7 @@ def get_server_version(self): if tacraw.startswith('<'): self.json = False tacsoup = BeautifulSoup(tacraw, 'html.parser') - self.version = tacsoup.find('a', { 'class' : 'homepageURL' }) + self.version = tacsoup.find('a', { 'class': 'homepageURL' }) # only extract version if HTML seemed to be OK if 'contents' in self.version.__dict__: self.version = self.version.contents[0].split('Icinga ')[1] From 6411d523f377d33c35c9f9ad50082b15e0ecb216 Mon Sep 17 00:00:00 2001 From: shastry <vinayshastry@gmail.com> Date: Wed, 5 Aug 2020 10:40:46 +0530 Subject: [PATCH 040/884] Toggle popup on system tray middle click. (cherry picked from commit 9a1a80b13314fe26ca4f3e7a7da156f75d70b016) --- Nagstamon/QUI/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index c7f9a63da..a53e1f69f 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -424,7 +424,7 @@ def icon_clicked(self, reason): # self.show_menu.emit() # only react on left mouse click on OSX # elif reason == (QSystemTrayIcon.Trigger or QSystemTrayIcon.DoubleClick): - if reason == (QSystemTrayIcon.Trigger or QSystemTrayIcon.DoubleClick): + if reason in (QSystemTrayIcon.Trigger, QSystemTrayIcon.DoubleClick, QSystemTrayIcon.MiddleClick): # when green icon is displayed and no popwin is about to po up show at least menu if get_worst_status() == 'UP' and OS == OS_DARWIN: self.menu.show_at_cursor() From d94759f062052a4e60d66d7e1da300b021e71239 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Fri, 31 Jul 2020 10:52:00 +0200 Subject: [PATCH 041/884] version 20200731 (cherry picked from commit 956ea7716651b7462bd9341f6617ae93b3af0fb7) --- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index d31f1f01e..6c562880a 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -123,7 +123,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.5-20200728' + VERSION = '3.5-20200731' WEBSITE = 'https://nagstamon.ifw-dresden.de' COPYRIGHT = '©2008-2020 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 216ce60f2..bc3f3110f 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.5-20200728) unstable; urgency=low +nagstamon (3.5-20200731) unstable; urgency=low * New upstream - -- Henri Wahl <h.wahl@ifw-dresden.de> Tue, 28 Jul 2020 17:00:00 +0100 + -- Henri Wahl <h.wahl@ifw-dresden.de> Fri, 31 Jul 2020 17:00:00 +0100 nagstamon (3.4.1) stable; urgency=low * New upstream From 582edb0ab2b874d6ed635a06795719d0750d8335 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Wed, 5 Aug 2020 15:23:57 +0200 Subject: [PATCH 042/884] os-specific requirements.txt files --- requirements-linux.txt | 10 ++++++++++ requirements-macos.txt | 10 ++++++++++ requirements.txt => requirements-windows.txt | 0 3 files changed, 20 insertions(+) create mode 100644 requirements-linux.txt create mode 100644 requirements-macos.txt rename requirements.txt => requirements-windows.txt (100%) diff --git a/requirements-linux.txt b/requirements-linux.txt new file mode 100644 index 000000000..884d2de66 --- /dev/null +++ b/requirements-linux.txt @@ -0,0 +1,10 @@ +beautifulsoup4 +keyring==10.5.1 +lxml +psutil +pyqt5==5.13.2 +pysocks +python-dateutil +requests +requests-kerberos +setuptools==44.1.1 \ No newline at end of file diff --git a/requirements-macos.txt b/requirements-macos.txt new file mode 100644 index 000000000..3af2108f7 --- /dev/null +++ b/requirements-macos.txt @@ -0,0 +1,10 @@ +beautifulsoup4 +keyring==10.5.1 +lxml +psutil +pyqt5==5.13.2 +pysocks +python-dateutil +requests +requests-gssapi +setuptools==44.1.1 \ No newline at end of file diff --git a/requirements.txt b/requirements-windows.txt similarity index 100% rename from requirements.txt rename to requirements-windows.txt From 1d8c363dd13af38594a630c95b399a05e2266cc3 Mon Sep 17 00:00:00 2001 From: Oliver Gerlich <oliver.gerlich@gmx.de> Date: Mon, 17 Aug 2020 21:59:54 +0200 Subject: [PATCH 043/884] fix AttributeError for Qt.AA_EnableHighDpiScaling with Qt 5.5 This property was introduced with Qt 5.6. --- Nagstamon/QUI/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index a53e1f69f..72c611b4d 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -114,7 +114,10 @@ DBUS_AVAILABLE = False # enable HighDPI-awareness to avoid https://github.com/HenriWahl/Nagstamon/issues/618 -QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True) +try: + QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True) +except AttributeError: + pass QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True) # global application instance APP = QApplication(sys.argv) From 638c7c7a22156ca7e542725d32dbbc71f2f008a9 Mon Sep 17 00:00:00 2001 From: Simon Cadman <src@niftiestsoftware.com> Date: Wed, 19 Aug 2020 15:14:19 +0100 Subject: [PATCH 044/884] Fix issue #655 - by returning 'unknown' as VERSION_ID if value missing using .get method of dict --- Nagstamon/Helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/Helpers.py b/Nagstamon/Helpers.py index b9a196468..2d30cd685 100644 --- a/Nagstamon/Helpers.py +++ b/Nagstamon/Helpers.py @@ -455,7 +455,7 @@ def get_distro(): key, value = property.split('=', 1) os_release_dict[key] = value.strip('"').strip("'") return (os_release_dict.get('ID').lower(), - os_release_dict.get('VERSION_ID').lower(), + os_release_dict.get('VERSION_ID', 'unknown').lower(), os_release_dict.get('NAME').lower()) else: return False From 2801370d9390d5598db34cfc1ed5922f73e8e3f7 Mon Sep 17 00:00:00 2001 From: Lars Michelsen <lm@tribe29.com> Date: Thu, 8 Oct 2020 08:20:06 +0200 Subject: [PATCH 045/884] Align Multisite connector license The file is published under the same conditions as the the rest of Nagstamon. Fixes #667. --- Nagstamon/Servers/Multisite.py | 43 +++++++++++++++------------------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/Nagstamon/Servers/Multisite.py b/Nagstamon/Servers/Multisite.py index f8a75182f..4e974ea06 100644 --- a/Nagstamon/Servers/Multisite.py +++ b/Nagstamon/Servers/Multisite.py @@ -1,29 +1,24 @@ -# -*- encoding: utf-8; py-indent-offset: 4 -*- -# +------------------------------------------------------------------+ -# | ____ _ _ __ __ _ __ | -# | / ___| |__ ___ ___| | __ | \/ | |/ / | -# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / | -# | | |___| | | | __/ (__| < | | | | . \ | -# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ | -# | | -# | Copyright Mathias Kettner 2010 mk@mathias-kettner.de | -# | lm@mathias-kettner.de | -# +------------------------------------------------------------------+ +# encoding: utf-8 + +# Nagstamon - Nagios status monitor for your desktop +# Copyright (C) 2008-2020 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# The official homepage is at http://mathias-kettner.de/check_mk. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# Checkmk is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by -# the Free Software Foundation in version 2. Checkmk is distributed -# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with- -# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. See the GNU General Public License for more de- -# ails. You should have received a copy of the GNU General Public -# License along with GNU Make; see the file COPYING. If not, write -# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, -# Boston, MA 02110-1301 USA. - -# hax0rized by: lm@mathias-kettner.de +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +# The initial implementation was contributed to the Nagstamon project +# by tribe29 GmbH. import sys import urllib.request, urllib.parse, urllib.error From b569028bbf39fd296add2a49f4764bef228f783c Mon Sep 17 00:00:00 2001 From: Stefan Schwarz <ssz@bitsbeats.com> Date: Thu, 8 Oct 2020 11:08:05 +0200 Subject: [PATCH 046/884] get_distro should always contain an 3 item tuple --- Nagstamon/Helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/Helpers.py b/Nagstamon/Helpers.py index 2d30cd685..5faface71 100644 --- a/Nagstamon/Helpers.py +++ b/Nagstamon/Helpers.py @@ -458,7 +458,7 @@ def get_distro(): os_release_dict.get('VERSION_ID', 'unknown').lower(), os_release_dict.get('NAME').lower()) else: - return False + return '', '', '' else: # fix for non-working build on Debian<10 dist_name, dist_version, dist_id = platform.dist() From 2fc77672179ac47be9b5de7707469e077615b87c Mon Sep 17 00:00:00 2001 From: Stefan Schwarz <ssz@bitsbeats.com> Date: Thu, 8 Oct 2020 11:24:37 +0200 Subject: [PATCH 047/884] try pathlib get resource path --- Nagstamon/Config.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 6c562880a..b88de7a98 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -1064,3 +1064,6 @@ def __init__(self, **kwds): if os.path.exists(path): RESOURCES = path break + else: + from pathlib import Path + RESOURCES = str(Path(__file__).parent.absolute().joinpath('resources')) From 7b1760348861680d2e411436430e9350650b3be2 Mon Sep 17 00:00:00 2001 From: Jan Kantert <jan-github2@kantert.net> Date: Tue, 20 Oct 2020 12:51:10 +0200 Subject: [PATCH 048/884] add filter for unreachable services --- Nagstamon/Config.py | 1 + Nagstamon/Objects.py | 1 + Nagstamon/QUI/settings_main.py | 18 ++++++++++++++++-- Nagstamon/QUI/settings_main.ui | 7 +++++++ Nagstamon/Servers/Generic.py | 6 ++++++ Nagstamon/Servers/IcingaWeb2.py | 8 ++++++-- 6 files changed, 37 insertions(+), 4 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 6c562880a..9634579cc 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -160,6 +160,7 @@ def __init__(self): self.default_sort_order = 'descending' self.filter_all_down_hosts = False self.filter_all_unreachable_hosts = False + self.filter_all_unreachable_services = False self.filter_all_flapping_hosts = False self.filter_all_unknown_services = False self.filter_all_information_services = False diff --git a/Nagstamon/Objects.py b/Nagstamon/Objects.py index 2445086fc..5f3eb10df 100644 --- a/Nagstamon/Objects.py +++ b/Nagstamon/Objects.py @@ -147,6 +147,7 @@ class GenericService(GenericObject): def __init__(self): GenericObject.__init__(self) + self.unreachable = False def get_host_name(self): return str(self.host) diff --git a/Nagstamon/QUI/settings_main.py b/Nagstamon/QUI/settings_main.py index 3d5bfced7..bfd7f9d26 100644 --- a/Nagstamon/QUI/settings_main.py +++ b/Nagstamon/QUI/settings_main.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'settings_main.ui' # -# Created by: PyQt5 UI code generator 5.13.1 +# Created by: PyQt5 UI code generator 5.13.2 # # WARNING! All changes made in this file will be lost! @@ -408,9 +408,12 @@ def setupUi(self, settings_main): self.input_checkbox_filter_all_unreachable_hosts = QtWidgets.QCheckBox(self.groupbox_filters) self.input_checkbox_filter_all_unreachable_hosts.setObjectName("input_checkbox_filter_all_unreachable_hosts") self.gridLayout_5.addWidget(self.input_checkbox_filter_all_unreachable_hosts, 1, 0, 1, 1) + self.input_checkbox_filter_all_unreachable_services = QtWidgets.QCheckBox(self.groupbox_filters) + self.input_checkbox_filter_all_unreachable_services.setObjectName("input_checkbox_filter_all_unreachable_services") + self.gridLayout_5.addWidget(self.input_checkbox_filter_all_unreachable_services, 2, 0, 1, 1) self.input_checkbox_filter_all_flapping_hosts = QtWidgets.QCheckBox(self.groupbox_filters) self.input_checkbox_filter_all_flapping_hosts.setObjectName("input_checkbox_filter_all_flapping_hosts") - self.gridLayout_5.addWidget(self.input_checkbox_filter_all_flapping_hosts, 2, 0, 1, 1) + self.gridLayout_5.addWidget(self.input_checkbox_filter_all_flapping_hosts, 3, 0, 1, 1) self.input_checkbox_filter_all_critical_services = QtWidgets.QCheckBox(self.groupbox_filters) self.input_checkbox_filter_all_critical_services.setObjectName("input_checkbox_filter_all_critical_services") self.gridLayout_5.addWidget(self.input_checkbox_filter_all_critical_services, 4, 0, 1, 1) @@ -1500,6 +1503,7 @@ def retranslateUi(self, settings_main): self.groupbox_filters.setTitle(_translate("settings_main", "Filter out the following:")) self.input_checkbox_filter_all_down_hosts.setText(_translate("settings_main", "All down hosts")) self.input_checkbox_filter_all_unreachable_hosts.setText(_translate("settings_main", "All unreachable hosts")) + self.input_checkbox_filter_all_unreachable_services.setText(_translate("settings_main", "All unreachable services")) self.input_checkbox_filter_all_flapping_hosts.setText(_translate("settings_main", "All flapping hosts")) self.input_checkbox_filter_all_critical_services.setText(_translate("settings_main", "All critical services")) self.input_checkbox_filter_acknowledged_hosts_services.setText(_translate("settings_main", "Acknowledged hosts and services")) @@ -1642,3 +1646,13 @@ def retranslateUi(self, settings_main): self.label_acknowledge_submit_result.setTitle(_translate("settings_main", "Submit check result:")) self.label_defaults_submit_check_result_comment.setText(_translate("settings_main", "Comment:")) self.tabs.setTabText(self.tabs.indexOf(self.tab_defaults), _translate("settings_main", "Defaults")) + + +if __name__ == "__main__": + import sys + app = QtWidgets.QApplication(sys.argv) + settings_main = QtWidgets.QDialog() + ui = Ui_settings_main() + ui.setupUi(settings_main) + settings_main.show() + sys.exit(app.exec_()) diff --git a/Nagstamon/QUI/settings_main.ui b/Nagstamon/QUI/settings_main.ui index 4478d2c40..279cd966a 100644 --- a/Nagstamon/QUI/settings_main.ui +++ b/Nagstamon/QUI/settings_main.ui @@ -1052,6 +1052,13 @@ </widget> </item> <item row="2" column="0"> + <widget class="QCheckBox" name="input_checkbox_filter_all_unreachable_services"> + <property name="text"> + <string>All unreachable services</string> + </property> + </widget> + </item> + <item row="3" column="0"> <widget class="QCheckBox" name="input_checkbox_filter_all_flapping_hosts"> <property name="text"> <string>All flapping hosts</string> diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index ad26cff94..9f8fa7808 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -1081,6 +1081,12 @@ def GetStatus(self, output=None): service.name)) service.visible = False + if conf.filter_all_unreachable_services is True and service.unreachable is True: + if conf.debug_mode: + self.Debug(server=self.get_name(), debug='Filter: UNREACHABLE ' + str(host.name) + ';' + str( + service.name)) + service.visible = False + # Checkmk and OP5 do not show the status_type so their host.status_type will be empty if service.status_type != '': if conf.filter_services_in_soft_state is True and service.status_type == 'soft': diff --git a/Nagstamon/Servers/IcingaWeb2.py b/Nagstamon/Servers/IcingaWeb2.py index 9bdc40188..650c6e69a 100644 --- a/Nagstamon/Servers/IcingaWeb2.py +++ b/Nagstamon/Servers/IcingaWeb2.py @@ -122,8 +122,8 @@ def _get_status(self): # define CGI URLs for hosts and services if self.cgiurl_hosts == self.cgiurl_services == self.cgiurl_monitoring_health == None: # services (unknown, warning or critical?) - self.cgiurl_services = {'hard': self.monitor_cgi_url + '/monitoring/list/services?service_state>0&service_state<=3&service_state_type=1&addColumns=service_last_check&format=json', \ - 'soft': self.monitor_cgi_url + '/monitoring/list/services?service_state>0&service_state<=3&service_state_type=0&addColumns=service_last_check&format=json'} + self.cgiurl_services = {'hard': self.monitor_cgi_url + '/monitoring/list/services?service_state>0&service_state<=3&service_state_type=1&addColumns=service_last_check,service_is_reachable&format=json', \ + 'soft': self.monitor_cgi_url + '/monitoring/list/services?service_state>0&service_state<=3&service_state_type=0&addColumns=service_last_check,service_is_reachable&format=json'} # hosts (up or down or unreachable) self.cgiurl_hosts = {'hard': self.monitor_cgi_url + '/monitoring/list/hosts?host_state>0&host_state<=2&host_state_type=1&addColumns=host_last_check&format=json', \ 'soft': self.monitor_cgi_url + '/monitoring/list/hosts?host_state>0&host_state<=2&host_state_type=0&addColumns=host_last_check&format=json'} @@ -307,6 +307,10 @@ def _get_status(self): self.new_hosts[host_name].services[service_name].acknowledged = bool(int(s['service_acknowledged'])) self.new_hosts[host_name].services[service_name].scheduled_downtime = bool(int(s['service_in_downtime'])) self.new_hosts[host_name].services[service_name].status_type = status_type + self.new_hosts[host_name].services[service_name].unreachable = s['service_is_reachable'] == '0' + + if self.new_hosts[host_name].services[service_name].unreachable: + self.new_hosts[host_name].services[service_name].status_information += " (SERVICE UNREACHABLE)" # extra Icinga properties to solve https://github.com/HenriWahl/Nagstamon/issues/192 # acknowledge needs service_description and no display name From dafe24cfe20a1e1d3359dce6b701b5441c7aa87d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=8E=A0=E1=8E=A1=2E=20=D0=85=CF=B5rg=CF=B5=20=D1=B4ictor?= <ser@users.noreply.github.com> Date: Fri, 6 Nov 2020 18:19:09 +0700 Subject: [PATCH 049/884] Added Icinga2 API. Partially Closes #647 --- Nagstamon/Servers/Icinga2API.py | 233 ++++++++++++++++++++++++++++++++ Nagstamon/Servers/__init__.py | 3 +- requirements.txt | 4 +- 3 files changed, 238 insertions(+), 2 deletions(-) create mode 100644 Nagstamon/Servers/Icinga2API.py diff --git a/Nagstamon/Servers/Icinga2API.py b/Nagstamon/Servers/Icinga2API.py new file mode 100644 index 000000000..629531b78 --- /dev/null +++ b/Nagstamon/Servers/Icinga2API.py @@ -0,0 +1,233 @@ +# encoding: utf-8 + +# Nagstamon - Nagios status monitor for your desktop +# Copyright (C) 2008-2014 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +import arrow +import logging +import sys +import urllib3 +from icinga2api.client import Client + +from Nagstamon.Config import conf +from Nagstamon.Servers.Generic import GenericServer +from Nagstamon.Objects import (GenericHost, GenericService, Result) + +log = logging.getLogger('Icinga2API.py') +urllib3.disable_warnings() + + +class Icinga2APIServer(GenericServer): + """ + object of Icinga2 server API + """ + TYPE = 'Icinga2API' + + # ICINGA2API does not provide a web interface for humans + MENU_ACTIONS = [] + BROWSER_URLS = {} + + # Helpers + iapi = None + SERVICE_SEVERITY_CODE_TEXT_MAP = dict() + HOST_SEVERITY_CODE_TEXT_MAP = dict() + + def __init__(self, **kwds): + """ + Prepare all urls needed by nagstamon and icinga + """ + GenericServer.__init__(self, **kwds) + + self.url = conf.servers[self.get_name()].monitor_url + self.username = conf.servers[self.get_name()].username + self.password = conf.servers[self.get_name()].password + + self.SERVICE_SEVERITY_CODE_TEXT_MAP = { + 0: 'OK', + 1: 'WARNING', + 2: 'CRITICAL', + 3: 'UNKNOWN' + } + self.HOST_SEVERITY_CODE_TEXT_MAP = { + 0: 'UP', + 1: 'DOWN', + 2: 'UNREACHABLE' + } + + def _ilogin(self): + """ + Initialize HTTP connection to icinga api + """ + try: + self.iapi = Client(url=self.url, + username=self.username, + password=self.password, + ) + except Exception as e: + log.exception(e) + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) + + def _insert_service_to_hosts(self, service: GenericService): + """ + We want to create hosts for faulty services as GenericService requires + that logic. + """ + service_host = service.get_host_name() + if service_host not in self.new_hosts: + self.new_hosts[service_host] = GenericHost() + self.new_hosts[service_host].name = service_host + self.new_hosts[service_host].site = service.site + self.new_hosts[service_host].services[service.name] = service + + def _get_status(self): + """ + Get status from Icinga Server and translate it into Nagstamon magic + generic array + """ + # new_hosts dictionary + self.new_hosts = dict() + + # hosts - the down ones + try: + # We ask icinga for hosts which are not doing well + hosts = self._get_host_events() + for host in hosts: + host_name = host['attrs']['name'] + if host_name not in self.new_hosts: + self.new_hosts[host_name] = GenericHost() + self.new_hosts[host_name].name = host_name + self.new_hosts[host_name].site = self.name + try: + self.new_hosts[host_name].status = self.HOST_SEVERITY_CODE_TEXT_MAP.get(host['attrs']['state']) + except KeyError: + self.new_hosts[host_name].status = 'UNKNOWN' + if int(host['attrs']['state_type']) > 0: # if state is not SOFT, icinga does not report attempts properly + self.new_hosts[host_name].attempt = "{}/{}".format( + int(host['attrs']['max_check_attempts']), + int(host['attrs']['max_check_attempts'])) + else: + self.new_hosts[host_name].attempt = "{}/{}".format( + int(host['attrs']['check_attempt']), + int(host['attrs']['max_check_attempts'])) + self.new_hosts[host_name].last_check = arrow.get(host['attrs']['last_check']).humanize() + self.new_hosts[host_name].duration = arrow.get(host['attrs']['previous_state_change']).humanize() + self.new_hosts[host_name].status_information = host['attrs']['last_check_result']['output'] + self.new_hosts[host_name].passiveonly = not(host['attrs']['enable_active_checks']) + self.new_hosts[host_name].notifications_disabled = not(host['attrs']['enable_notifications']) + self.new_hosts[host_name].flapping = host['attrs']['flapping'] + self.new_hosts[host_name].acknowledged = host['attrs']['acknowledgement'] + self.new_hosts[host_name].status_type = {0: "soft", 1: "hard"}[host['attrs']['state_type']] + del host_name + del hosts + + except Exception as e: + # set checking flag back to False + self.isChecking = False + result, error = self.Error(sys.exc_info()) + log.exception(e) + return Result(result=result, error=error) + + # services + try: + services = self._get_service_events() + for service in services: + new_service = GenericService() + new_service.host = service['attrs']['host_name'] + new_service.name = service['attrs']['name'] + try: + new_service.status = self.SERVICE_SEVERITY_CODE_TEXT_MAP.get(service['attrs']['state']) + except KeyError: + new_service.status = 'UNKNOWN' + if int(service['attrs']['state_type']) > 0: # if state is not SOFT, icinga does not report attempts properly + new_service.attempt = "{}/{}".format( + int(service['attrs']['max_check_attempts']), + int(service['attrs']['max_check_attempts'])) + else: + new_service.attempt = "{}/{}".format( + int(service['attrs']['check_attempt']), + int(service['attrs']['max_check_attempts'])) + new_service.last_check = arrow.get(service['attrs']['last_check']).humanize() + new_service.duration = arrow.get(service['attrs']['previous_state_change']).humanize() + new_service.status_information = service['attrs']['last_check_result']['output'] + new_service.passiveonly = not(service['attrs']['enable_active_checks']) + new_service.notifications_disabled = not(service['attrs']['enable_notifications']) + new_service.flapping = service['attrs']['flapping'] + new_service.acknowledged = service['attrs']['acknowledgement'] + new_service.status_type = {0: "soft", 1: "hard"}[service['attrs']['state_type']] + self._insert_service_to_hosts(new_service) + del services + + except Exception as e: + log.exception(e) + # set checking flag back to False + self.isChecking = False + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) + + # dummy return in case all is OK + return Result() + + def _get_service_events(self): + """ + Suck faulty service events from API + """ + if self.iapi is None: + self._ilogin() + filters = 'service.state!=ServiceOK' + events = self.iapi.objects.list('Service', filters=filters) + return events + + def _get_host_events(self): + """ + Suck faulty hosts from API + """ + if self.iapi is None: + self._ilogin() + filters = 'host.state!=0' + events = self.iapi.objects.list('Host', filters=filters) + return events + + def _set_recheck(self, host, service): + """ + Please check again Icinga! + """ + pass + + def _set_acknowledge(self, host, service, author, comment, sticky, + notify, persistent, all_services=[]): + ''' + Send acknowledge to monitor server + ''' + pass + + def _set_submit_check_result(self, host, service, state, comment, + check_output, performance_data): + ''' + Submit check results + ''' + pass + + def _set_downtime(self, host, service, author, comment, fixed, start_time, + end_time, hours, minutes): + """ + Submit downtime + """ + pass + + +0 diff --git a/Nagstamon/Servers/__init__.py b/Nagstamon/Servers/__init__.py index 234436227..d8ac9715b 100644 --- a/Nagstamon/Servers/__init__.py +++ b/Nagstamon/Servers/__init__.py @@ -30,6 +30,7 @@ from Nagstamon.Servers.Centreon import CentreonServer from Nagstamon.Servers.Icinga import IcingaServer from Nagstamon.Servers.IcingaWeb2 import IcingaWeb2Server +from Nagstamon.Servers.Icinga2API import Icinga2APIServer from Nagstamon.Servers.Multisite import MultisiteServer from Nagstamon.Servers.op5Monitor import Op5MonitorServer from Nagstamon.Servers.Opsview import OpsviewServer @@ -202,7 +203,7 @@ def create_server(server=None): # moved registration process here because of circular dependencies -for server in (CentreonServer, IcingaServer, IcingaWeb2Server, MultisiteServer, NagiosServer, +for server in (CentreonServer, IcingaServer, IcingaWeb2Server, Icinga2APIServer, MultisiteServer, NagiosServer, Op5MonitorServer, OpsviewServer, ThrukServer, ZabbixServer, SensuServer, LivestatusServer, ZenossServer, Monitos3Server, Monitos4xServer, SnagViewServer, PrometheusServer): diff --git a/requirements.txt b/requirements.txt index fffa8de01..270c2d216 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,6 @@ +arrow beautifulsoup4 +icinga2api keyring==10.5.1 lxml psutil @@ -8,4 +10,4 @@ pysocks python-dateutil requests requests-kerberos -setuptools==44.1.1 \ No newline at end of file +setuptools==44.1.1 From b1c9780f6ace52d9df6bf6b455cefd5359df3448 Mon Sep 17 00:00:00 2001 From: Stephan Schwarz <schwarz@e-spirit.com> Date: Sun, 8 Nov 2020 14:43:08 +0100 Subject: [PATCH 050/884] added support for alertmanager --- Nagstamon/QUI/__init__.py | 12 +-- Nagstamon/Servers/Alertmanager.py | 152 ++++++++++++++++++++++++++++++ Nagstamon/Servers/Prometheus.py | 10 +- Nagstamon/Servers/__init__.py | 5 +- README.md | 1 + 5 files changed, 165 insertions(+), 15 deletions(-) create mode 100644 Nagstamon/Servers/Alertmanager.py diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 72c611b4d..338f4d117 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -5757,12 +5757,12 @@ def __init__(self, dialog): self.ui.label_host_filter: ['op5Monitor'], self.ui.label_monitor_site: ['Sensu'], self.ui.input_lineedit_monitor_site: ['Sensu'], - self.ui.label_map_to_hostname: ['Prometheus'], - self.ui.input_lineedit_map_to_hostname: ['Prometheus'], - self.ui.label_map_to_servicename: ['Prometheus'], - self.ui.input_lineedit_map_to_servicename: ['Prometheus'], - self.ui.label_map_to_status_information: ['Prometheus'], - self.ui.input_lineedit_map_to_status_information: ['Prometheus']} + self.ui.label_map_to_hostname: ['Prometheus','Alertmanager'], + self.ui.input_lineedit_map_to_hostname: ['Prometheus','Alertmanager'], + self.ui.label_map_to_servicename: ['Prometheus','Alertmanager'], + self.ui.input_lineedit_map_to_servicename: ['Prometheus','Alertmanager'], + self.ui.label_map_to_status_information: ['Prometheus','Alertmanager'], + self.ui.input_lineedit_map_to_status_information: ['Prometheus','Alertmanager']} # to be used when selecting authentication method Kerberos self.AUTHENTICATION_WIDGETS = [ diff --git a/Nagstamon/Servers/Alertmanager.py b/Nagstamon/Servers/Alertmanager.py new file mode 100644 index 000000000..2f50ede3d --- /dev/null +++ b/Nagstamon/Servers/Alertmanager.py @@ -0,0 +1,152 @@ +# encoding: utf-8 + +# Nagstamon - Nagios status monitor for your desktop +# Copyright (C) 2008-2020 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +# Initial implementation by Stephan Schwarz (@stearz) +# +# This Server class connects against Prometheus' Alertmanager. +# The monitor URL in the setup should be something like +# http://alertmanager.example.com +# +# Release Notes: +# +# [1.0.0] - 2020-11-08: +# * added: +# Inital version +# +import sys +import urllib.request +import urllib.parse +import urllib.error +import pprint +import json + +from datetime import datetime, timedelta, timezone +import dateutil.parser + +from Nagstamon.Config import conf +from Nagstamon.Objects import (GenericHost, + GenericService, + Result) +from Nagstamon.Servers.Generic import GenericServer +from Nagstamon.Servers.Prometheus import PrometheusServer,PrometheusService +from Nagstamon.Helpers import webbrowser_open + + +class AlertmanagerService(PrometheusService): + """ + add Alertmanager specific service property to generic service class + """ + service_object_id = "" + + +class AlertmanagerServer(PrometheusServer): + """ + special treatment for Alertmanager API + """ + TYPE = 'Alertmanager' + + # Alertmanager actions are limited to visiting the monitor for now + MENU_ACTIONS = ['Monitor'] + BROWSER_URLS = { + 'monitor': '$MONITOR$/#/alerts', + 'hosts': '$MONITOR$/#/alerts', + 'services': '$MONITOR$/#/alerts', + 'history': '$MONITOR$/#/alerts' + } + + API_PATH_ALERTS = "/api/v2/alerts" + + def _get_status(self): + """ + Get status from Alertmanager Server + """ + # get all alerts from the API server + try: + result = self.FetchURL(self.monitor_url + self.API_PATH_ALERTS, + giveback="raw") + data = json.loads(result.result) + error = result.error + status_code = result.status_code + + # check if any error occured + errors_occured = self.check_for_error(data, error, status_code) + if errors_occured is not False: + return(errors_occured) + + if conf.debug_mode: + self.Debug(server=self.get_name(), + debug="Fetched JSON: " + pprint.pformat(data)) + + for alert in data: + if conf.debug_mode: + self.Debug( + server=self.get_name(), + debug="Processing Alert: " + pprint.pformat(alert) + ) + + labels = alert.get("labels", {}) + + # skip alerts with none severity + severity = labels.get("severity", "UNKNOWN").upper() + if severity == "NONE": + continue + + hostname = "unknown" + for host_label in self.map_to_hostname.split(','): + if host_label in labels: + hostname = labels.get(host_label) + break + + servicename = "unknown" + for service_label in self.map_to_servicename.split(','): + if service_label in labels: + servicename = labels.get(service_label) + break + + service = PrometheusService() + service.host = str(hostname) + service.name = servicename + service.server = self.name + service.status = severity + service.last_check = str(self._get_duration(alert["updatedAt"])) + service.attempt = alert.get("state", "firirng") + service.duration = str(self._get_duration(alert["startsAt"])) + + annotations = alert.get("annotations", {}) + status_information = "" + for status_information_label in self.map_to_status_information.split(','): + if status_information_label in annotations: + status_information = annotations.get(status_information_label) + break + service.status_information = status_information + + if hostname not in self.new_hosts: + self.new_hosts[hostname] = GenericHost() + self.new_hosts[hostname].name = str(hostname) + self.new_hosts[hostname].server = self.name + self.new_hosts[hostname].services[servicename] = service + + except: + # set checking flag back to False + self.isChecking = False + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) + + # dummy return in case all is OK + return Result() diff --git a/Nagstamon/Servers/Prometheus.py b/Nagstamon/Servers/Prometheus.py index d208f8930..329dff50b 100644 --- a/Nagstamon/Servers/Prometheus.py +++ b/Nagstamon/Servers/Prometheus.py @@ -23,12 +23,6 @@ # The monitor URL in the setup should be something like # http://prometheus.example.com # -# Status/TODOs: -# -# * Currently we can only fetch and display alerts from Prometheus. -# In the future it would be great to be able to interract with -# alertmanager, so that managing alerts (e.g. silencing) is possible -# # Release Notes: # # [1.1.0] - 2020-06-12: @@ -91,6 +85,8 @@ class PrometheusServer(GenericServer): 'history': '$MONITOR$/graph' } + API_PATH_ALERTS = "/api/v1/alerts" + def init_HTTP(self): """ things to do if HTTP is not initialized @@ -150,7 +146,7 @@ def _get_status(self): """ # get all alerts from the API server try: - result = self.FetchURL(self.monitor_url + "/api/v1/alerts", + result = self.FetchURL(self.monitor_url + self.API_PATH_ALERTS, giveback="raw") data = json.loads(result.result) error = result.error diff --git a/Nagstamon/Servers/__init__.py b/Nagstamon/Servers/__init__.py index 234436227..b7d2611b5 100644 --- a/Nagstamon/Servers/__init__.py +++ b/Nagstamon/Servers/__init__.py @@ -42,6 +42,7 @@ from Nagstamon.Servers.SnagView3 import SnagViewServer from Nagstamon.Servers.Sensu import SensuServer from Nagstamon.Servers.Prometheus import PrometheusServer +from Nagstamon.Servers.Alertmanager import AlertmanagerServer from Nagstamon.Config import conf @@ -181,7 +182,7 @@ def create_server(server=None): # Zabbix new_server.use_description_name_service = server.use_description_name_service - # Prometheus + # Prometheus & Alertmanager new_server.map_to_hostname = server.map_to_hostname new_server.map_to_servicename = server.map_to_servicename new_server.map_to_status_information = server.map_to_status_information @@ -205,7 +206,7 @@ def create_server(server=None): for server in (CentreonServer, IcingaServer, IcingaWeb2Server, MultisiteServer, NagiosServer, Op5MonitorServer, OpsviewServer, ThrukServer, ZabbixServer, SensuServer, LivestatusServer, ZenossServer, Monitos3Server, Monitos4xServer, SnagViewServer, - PrometheusServer): + PrometheusServer, AlertmanagerServer): register_server(server) # create servers diff --git a/README.md b/README.md index a05ca632b..9eb5a77bb 100644 --- a/README.md +++ b/README.md @@ -26,5 +26,6 @@ Successfully tested monitors include: - monitos 3 - experimental - SNAG-View3 - experimental - Prometheus - experimental + - Alertmanager - experimental See https://nagstamon.ifw-dresden.de for further information. From 6d67bdab4f76cff39329c0f8d63b6ad51cd4762d Mon Sep 17 00:00:00 2001 From: Stephan Schwarz <schwarz@e-spirit.com> Date: Sun, 8 Nov 2020 16:44:24 +0100 Subject: [PATCH 051/884] fixed macosversion missing in info.plist --- Nagstamon/resources/Info.plist | 4 ++++ build/build.py | 8 ++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Nagstamon/resources/Info.plist b/Nagstamon/resources/Info.plist index 7f8d49d16..98bd4e28e 100644 --- a/Nagstamon/resources/Info.plist +++ b/Nagstamon/resources/Info.plist @@ -20,5 +20,9 @@ <string>0</string> <key>CFBundlePackageType</key> <string>APPL</string> + <key>CFBundleVersion</key> + <string>+++VERSION+++</string> + <key>CFBundleShortVersionString</key> + <string>+++VERSION+++</string> </dict> </plist> \ No newline at end of file diff --git a/build/build.py b/build/build.py index 33cd36d94..1a9dfe31e 100644 --- a/build/build.py +++ b/build/build.py @@ -139,8 +139,12 @@ def macmain(): # go back to build directory os.chdir(CURRENT_DIR) - # use own modified Info.plist for Retina compatibility - shutil.copyfile('../Nagstamon/resources/Info.plist', '../dist/Nagstamon.app/Contents/Info.plist') + # template own modified Info.plist for Retina compatibility + #shutil.copyfile('../Nagstamon/resources/Info.plist', '../dist/Nagstamon.app/Contents/Info.plist') + with open("../Nagstamon/resources/Info.plist", "rt") as fin: + with open("../dist/Nagstamon.app/Contents/Info.plist", "wt") as fout: + for line in fin: + fout.write(line.replace('+++VERSION+++', VERSION)) # create staging DMG folder for later compressing of DMG shutil.rmtree('Nagstamon {0} Staging DMG'.format(VERSION), ignore_errors=True) From 476480290ca34d1b9331ed7170f2f76875c6a39d Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Tue, 10 Nov 2020 11:53:12 +0100 Subject: [PATCH 052/884] version 20201110 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 6c562880a..21984a583 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -123,7 +123,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.5-20200731' + VERSION = '3.5-20201110' WEBSITE = 'https://nagstamon.ifw-dresden.de' COPYRIGHT = '©2008-2020 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index bc3f3110f..e6f4bc870 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.5-20200731) unstable; urgency=low +nagstamon (3.5-20201110) unstable; urgency=low * New upstream -- Henri Wahl <h.wahl@ifw-dresden.de> Fri, 31 Jul 2020 17:00:00 +0100 From ae08f2b0097e2ee6a881fc00e402019a0c589039 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=8E=A0=E1=8E=A1=2E=20=D0=85=CF=B5rg=CF=B5=20=D1=B4ictor?= <ser@users.noreply.github.com> Date: Thu, 12 Nov 2020 21:14:10 +0700 Subject: [PATCH 053/884] Conditional access to Icinga2 API --- Nagstamon/Servers/__init__.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/Nagstamon/Servers/__init__.py b/Nagstamon/Servers/__init__.py index d8ac9715b..47efce8b4 100644 --- a/Nagstamon/Servers/__init__.py +++ b/Nagstamon/Servers/__init__.py @@ -25,12 +25,18 @@ from collections import OrderedDict +# we need this for uncommon modules +try: + import icinga2api + icinga2api_is_available = True +except: + pass + # load all existing server types from Nagstamon.Servers.Nagios import NagiosServer from Nagstamon.Servers.Centreon import CentreonServer from Nagstamon.Servers.Icinga import IcingaServer from Nagstamon.Servers.IcingaWeb2 import IcingaWeb2Server -from Nagstamon.Servers.Icinga2API import Icinga2APIServer from Nagstamon.Servers.Multisite import MultisiteServer from Nagstamon.Servers.op5Monitor import Op5MonitorServer from Nagstamon.Servers.Opsview import OpsviewServer @@ -43,11 +49,14 @@ from Nagstamon.Servers.SnagView3 import SnagViewServer from Nagstamon.Servers.Sensu import SensuServer from Nagstamon.Servers.Prometheus import PrometheusServer - from Nagstamon.Config import conf from Nagstamon.Helpers import STATES +# conditional load if module is available +if icinga2api_is_available is True: + from Nagstamon.Servers.Icinga2API import Icinga2APIServer + # dictionary for servers servers = OrderedDict() @@ -203,10 +212,14 @@ def create_server(server=None): # moved registration process here because of circular dependencies -for server in (CentreonServer, IcingaServer, IcingaWeb2Server, Icinga2APIServer, MultisiteServer, NagiosServer, - Op5MonitorServer, OpsviewServer, ThrukServer, ZabbixServer, SensuServer, - LivestatusServer, ZenossServer, Monitos3Server, Monitos4xServer, SnagViewServer, - PrometheusServer): +servers_list = [CentreonServer, IcingaServer, IcingaWeb2Server, Icinga2APIServer, MultisiteServer, NagiosServer, + Op5MonitorServer, OpsviewServer, ThrukServer, ZabbixServer, SensuServer, + LivestatusServer, ZenossServer, Monitos3Server, Monitos4xServer, SnagViewServer, + PrometheusServer] +# we use these servers conditionally if modules are available only +if icinga2api_is_available is True: + servers_list.append(Icinga2APIServer) +for server in servers_list: register_server(server) # create servers From 39e270e1fac00f381f89ece1382ac6a2c9eeb92b Mon Sep 17 00:00:00 2001 From: Stephan Schwarz <schwarz@e-spirit.com> Date: Fri, 27 Nov 2020 11:47:04 +0100 Subject: [PATCH 054/884] Alertmanager now supports filtering of suppressed alerts --- Nagstamon/Servers/Alertmanager.py | 33 ++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/Nagstamon/Servers/Alertmanager.py b/Nagstamon/Servers/Alertmanager.py index 2f50ede3d..e879836b5 100644 --- a/Nagstamon/Servers/Alertmanager.py +++ b/Nagstamon/Servers/Alertmanager.py @@ -25,6 +25,10 @@ # # Release Notes: # +# [1.0.1] - 2020-11-27: +# * added: +# Support for hiding suppressed alerts with the scheduled downtime filter +# # [1.0.0] - 2020-11-08: # * added: # Inital version @@ -125,9 +129,21 @@ def _get_status(self): service.server = self.name service.status = severity service.last_check = str(self._get_duration(alert["updatedAt"])) - service.attempt = alert.get("state", "firirng") + + if "status" in alert: + service.attempt = alert["status"].get("state", "unknown") + else: + service.attempt = "unknown" + + if service.attempt == "suppressed": + service.scheduled_downtime = True + service.duration = str(self._get_duration(alert["startsAt"])) + # Alertmanager specific extensions + service.generatorURL = alert.get("generatorURL", {}) + service.fingerprint = alert.get("fingerprint", {}) + annotations = alert.get("annotations", {}) status_information = "" for status_information_label in self.map_to_status_information.split(','): @@ -150,3 +166,18 @@ def _get_status(self): # dummy return in case all is OK return Result() + + def open_monitor(self, host, service=''): + """ + open monitor for alert + """ + url = self.monitor_url + webbrowser_open(url) + + + def _set_downtime(self, host, service, author, comment, fixed, start_time, + end_time, hours, minutes): + """ + to be implemented in a future release + """ + pass From ffddb2d5847cc6d89b1e1ccd0719226a93064a58 Mon Sep 17 00:00:00 2001 From: Stephan Schwarz <schwarz@e-spirit.com> Date: Fri, 27 Nov 2020 11:47:54 +0100 Subject: [PATCH 055/884] fixed icinga2api_is_available build problems --- Nagstamon/Servers/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Servers/__init__.py b/Nagstamon/Servers/__init__.py index 289365e5a..47c948851 100644 --- a/Nagstamon/Servers/__init__.py +++ b/Nagstamon/Servers/__init__.py @@ -30,7 +30,7 @@ import icinga2api icinga2api_is_available = True except: - pass + icinga2api_is_available = False # load all existing server types from Nagstamon.Servers.Nagios import NagiosServer @@ -214,10 +214,10 @@ def create_server(server=None): # moved registration process here because of circular dependencies -servers_list = [CentreonServer, IcingaServer, IcingaWeb2Server, Icinga2APIServer, MultisiteServer, NagiosServer, +servers_list = [CentreonServer, IcingaServer, IcingaWeb2Server, MultisiteServer, NagiosServer, Op5MonitorServer, OpsviewServer, ThrukServer, ZabbixServer, SensuServer, LivestatusServer, ZenossServer, Monitos3Server, Monitos4xServer, SnagViewServer, - PrometheusServer, AlertmanagerServe] + PrometheusServer, AlertmanagerServer] # we use these servers conditionally if modules are available only if icinga2api_is_available is True: servers_list.append(Icinga2APIServer) From c8a65626a789d77c770cd023d0262cecae885b9a Mon Sep 17 00:00:00 2001 From: Benoit Poulet <benoit.poulet@businessdecision.com> Date: Tue, 15 Dec 2020 11:51:11 +0100 Subject: [PATCH 056/884] Update to Centreon 20.10 And fixes for previous version who have been modified by the editor. --- Nagstamon/Servers/Centreon.py | 240 ++++++++++++++++++++-------------- 1 file changed, 145 insertions(+), 95 deletions(-) diff --git a/Nagstamon/Servers/Centreon.py b/Nagstamon/Servers/Centreon.py index c53764b38..e530d1895 100644 --- a/Nagstamon/Servers/Centreon.py +++ b/Nagstamon/Servers/Centreon.py @@ -133,10 +133,19 @@ def init_HTTP(self): 'hosts': '$MONITOR$/main.php?p=20202', 'services': '$MONITOR$/main.php?p=20201', 'history': '$MONITOR$/main.php?p=203'} - elif re.search('18\.10\.[0-9]', raw_versioncheck) or re.search('19\.(04|10)\.[0-9]', raw_versioncheck): + elif re.search('18\.10\.[0-9]', raw_versioncheck): self.centreon_version = 18.10 if conf.debug_mode is True: - self.Debug(server=self.get_name(), debug='Centreon version detected : 18.10 <=> 19.10') + self.Debug(server=self.get_name(), debug='Centreon version detected : 18.10') + # URLs for browser shortlinks/buttons on popup window + self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?', + 'hosts': '$MONITOR$/main.php?p=20202', + 'services': '$MONITOR$/main.php?p=20201', + 'history': '$MONITOR$/main.php?p=203'} + elif re.search('19\.(04|10)\.[0-9]', raw_versioncheck) or re.search('20\.(04|10)\.[0-9]', raw_versioncheck): + self.centreon_version = 19.04 + if conf.debug_mode is True: + self.Debug(server=self.get_name(), debug='Centreon version detected : 19.04 <=> 20.10') # URLs for browser shortlinks/buttons on popup window self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?', 'hosts': '$MONITOR$/main.php?p=20202', @@ -144,9 +153,9 @@ def init_HTTP(self): 'history': '$MONITOR$/main.php?p=203'} else: # unsupported version or unable do determine - self.centreon_version = 18.10 + self.centreon_version = 19.04 if conf.debug_mode is True: - self.Debug(server=self.get_name(), debug='Centreon version unknown : supposed to be >= 18.10') + self.Debug(server=self.get_name(), debug='Centreon version unknown : supposed to be >= 19.04') # URLs for browser shortlinks/buttons on popup window self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?', 'hosts': '$MONITOR$/main.php?p=20202&o=hpb', @@ -189,8 +198,8 @@ def open_monitor(self, host, service=''): if m: service = m.group('rsd') webbrowser_open(self.urls_centreon['main'] + '?' + urllib.parse.urlencode({'p':20201,'o':'svcd','host_name':'_Module_Meta','service_description':service}) + auth ) - # Centreon 18.10 - elif self.centreon_version in [18.10]: + # Starting from Centreon 18.10 + else: m = re.search(r'^.+ \((?P<rsd>.+)\)$', service) if m: service = m.group('rsd') @@ -198,21 +207,21 @@ def open_monitor(self, host, service=''): # must be a host if service is empty elif service == '': - if self.centreon_version in [2.7, 2.8]: + if self.centreon_version < 2.7: + webbrowser_open(self.urls_centreon['main'] + '?' + urllib.parse.urlencode({'p':201,'o':'hd', 'host_name':host}) + auth ) + elif self.centreon_version in [2.7, 2.8]: webbrowser_open(self.urls_centreon['main'] + '?' + urllib.parse.urlencode({'p':20202,'o':'hd', 'host_name':host}) + auth ) - elif self.centreon_version in [18.10]: - webbrowser_open(self.urls_centreon['main_with_frames'] + '?' + urllib.parse.urlencode({'p':20202,'o':'hd', 'host_name':host}) + auth ) else: - webbrowser_open(self.urls_centreon['main'] + '?' + urllib.parse.urlencode({'p':201,'o':'hd', 'host_name':host}) + auth ) + webbrowser_open(self.urls_centreon['main_with_frames'] + '?' + urllib.parse.urlencode({'p':20202,'o':'hd', 'host_name':host}) + auth ) # so it's a service else: - if self.centreon_version in [2.7, 2.8]: + if self.centreon_version < 2.7: + webbrowser_open(self.urls_centreon['main'] + '?' + urllib.parse.urlencode({'p':202, 'o':'svcd', 'host_name':host, 'service_description':service}) + auth ) + elif self.centreon_version in [2.7, 2.8]: webbrowser_open(self.urls_centreon['main'] + '?' + urllib.parse.urlencode({'p':20201,'o':'svcd', 'host_name':host, 'service_description':service}) + auth ) - elif self.centreon_version in [18.10]: - webbrowser_open(self.urls_centreon['main_with_frames'] + '?' + urllib.parse.urlencode({'p':20201,'o':'svcd', 'host_name':host, 'service_description':service}) + auth ) else: - webbrowser_open(self.urls_centreon['main'] + '?' + urllib.parse.urlencode({'p':202, 'o':'svcd', 'host_name':host, 'service_description':service}) + auth ) + webbrowser_open(self.urls_centreon['main_with_frames'] + '?' + urllib.parse.urlencode({'p':20201,'o':'svcd', 'host_name':host, 'service_description':service}) + auth ) def _get_sid(self): @@ -231,17 +240,17 @@ def _get_sid(self): raw = self.FetchURL(self.monitor_cgi_url + '/index.php?p=101&autologin=1&useralias=' + self.username + '&token=' + self.autologin_key, giveback='raw') if conf.debug_mode == True: self.Debug(server=self.get_name(), debug='Autologin : ' + self.username + ' : ' + self.autologin_key) - # Gathering of the token who will be used to interact with Centreon - page = self.FetchURL(self.monitor_cgi_url + '/main.get.php') - if self.centreon_version in [18.10, 2.8, 2.7, 2.66]: + # Gathering of the token who will be used to interact with Centreon (start with 2.66) + if self.centreon_version >= 2.66 and self.centreon_version < 19.04: + page = self.FetchURL(self.monitor_cgi_url + '/main.get.php') self.centreon_token = page.result.find('input', {'name': "centreon_token"})['value'] # Password auth else: login = self.FetchURL(self.monitor_cgi_url + '/index.php') if login.error == '' and login.status_code == 200: - # Centreon > 2.6.6 implement a token - if self.centreon_version in [18.10, 2.8, 2.7, 2.66]: + # Centreon >= 2.6.6 implement a token + if self.centreon_version >= 2.66 and self.centreon_version <= 19.04: form = login.result.find('form') form_inputs = {} # Need to catch the centreon_token for login to work @@ -252,7 +261,7 @@ def _get_sid(self): form_inputs['password'] = self.password # fire up login button with all needed data raw = self.FetchURL(self.monitor_cgi_url + '/index.php', cgi_data=form_inputs) - elif self.centreon_version == 2.3456: + else: login_data = {"useralias" : self.username, "password" : self.password, "submit" : "Login"} raw = self.FetchURL(self.monitor_cgi_url + "/index.php",cgi_data=login_data, giveback="raw") if conf.debug_mode == True: @@ -261,7 +270,8 @@ def _get_sid(self): sid = self.session.cookies['PHPSESSID'] if conf.debug_mode == True: self.Debug(server=self.get_name(), debug='SID : ' + sid) - self.Debug(server=self.get_name(), debug='Centreon Token : ' + self.centreon_token) + if self.centreon_version >= 2.66 and self.centreon_version < 19.04: + self.Debug(server=self.get_name(), debug='Centreon Token : ' + self.centreon_token) # those broker urls would not be changing too often so this check migth be done here if self.first_login: self._get_xml_path(sid) @@ -282,7 +292,7 @@ def get_start_end(self, host): try: # It's not possible since 18.10 to get date from the webinterface # because it's set in javascript - if self.centreon_version in [2.3456, 2.66, 2.7, 2.8]: + if self.centreon_version < 18.10: cgi_data = {'o':'ah',\ 'host_name':host} if self.centreon_version < 2.7: @@ -377,7 +387,7 @@ def GetHost(self, host): return Result(result=address) - def _get_xml_path(self,sid): + def _get_xml_path(self, sid): ''' Find out where this instance of Centreon is publishing the status XMLs Centreon 2.6 + ndo/c.broker - /include/monitoring/status/Hosts/xml/{ndo,broker}/hostXML.php according to configuration @@ -385,9 +395,8 @@ def _get_xml_path(self,sid): Centreon 2.8 + c.broker - /include/monitoring/status/Hosts/xml/hostXML.php regexping HTML for Javascript ''' - if self.centreon_version == 2.3456 or self.centreon_version == 2.66: + if self.centreon_version <= 2.66: # 2.6 support NDO and C. Broker, we must check which one is used - # cgi_data = {'p':201, 'sid':self.SID} cgi_data = {'p':201, 'sid':sid} result = self.FetchURL(self.monitor_cgi_url + '/main.php', cgi_data=cgi_data, giveback='raw') raw, error = result.result, result.error @@ -464,15 +473,16 @@ def _define_url(self): 'external_cmd_cmdPopup': self.monitor_cgi_url + '/include/monitoring/external_cmd/cmdPopup.php', 'keepAlive': self.monitor_cgi_url + '/api/internal.php?object=centreon_keepalive&action=keepAlive' } + if self.centreon_version < 2.7: self.urls_centreon = urls_centreon_2_2 elif self.centreon_version == 2.7: self.urls_centreon = urls_centreon_2_7 elif self.centreon_version == 2.8: self.urls_centreon = urls_centreon_2_8 - elif self.centreon_version == 18.10: + # 18.10 and beyond + elif self.centreon_version >= 18.10: self.urls_centreon = urls_centreon_18_10 - # print IP in debug mode if conf.debug_mode == True: self.Debug(server=self.get_name(), debug='URLs defined for Centreon %s' % (self.centreon_version)) @@ -550,6 +560,7 @@ def _get_host_and_service_id(self, host, service): except: return '','' + def _get_status(self): ''' Get status from Centreon Server @@ -563,17 +574,19 @@ def _get_status(self): return result # services (unknown, warning or critical?) - if self.centreon_version in [2.7, 2.8, 18.10]: - nagcgiurl_services = self.urls_centreon['xml_services'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'svcpb', 'p':20201, 'nc':0, 'criticality':0, 'statusService':'svcpb', 'sSetOrderInMemory':1, 'sid':self.SID}) - else: + if self.centreon_version < 2.7: nagcgiurl_services = self.urls_centreon['xml_services'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'svcpb', 'sort_type':'status', 'sid':self.SID}) + else: + nagcgiurl_services = self.urls_centreon['xml_services'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'svcpb', 'p':20201, 'nc':0, 'criticality':0, 'statusService':'svcpb', 'sSetOrderInMemory':1, 'sid':self.SID}) # hosts (up or down or unreachable) # define hosts xml URL, because of inconsistant url - if self.centreon_version in [2.7, 2.8, 18.10]: + if self.centreon_version < 2.7: + nagcgiurl_hosts = self.urls_centreon['xml_hosts'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'hpb', 'sort_type':'status', 'sid':self.SID}) + elif self.centreon_version >= 2.7 and self.centreon_version < 19.04: nagcgiurl_hosts = self.urls_centreon['xml_hosts'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'hpb', 'p':20202, 'criticality':0, 'statusHost':'hpb', 'sSetOrderInMemory':1, 'sid':self.SID}) else: - nagcgiurl_hosts = self.urls_centreon['xml_hosts'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'hpb', 'sort_type':'status', 'sid':self.SID}) + nagcgiurl_hosts = self.urls_centreon['xml_hosts'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'hpb', 'p':20202, 'criticality':0, 'statusHost':'hpb', 'sSetOrderInMemory':1}) # hosts - mostly the down ones # unfortunately the hosts status page has a different structure so @@ -701,7 +714,7 @@ def _get_status(self): error='Bad session ID', status_code=status_code) - # In Centreon 2.8, Meta are merge with regular services + # In Centreon 2.8, Meta are merged with regular services if self.centreon_version < 2.8: # define meta-services xml URL if self.centreon_version == 2.7: @@ -844,12 +857,12 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi if self.centreon_version < 2.7: cgi_data['p'] = '20105' cgi_data['o'] = 'hpb' - elif self.centreon_version in [2.7, 2.8, 18.10]: + else: cgi_data['p'] = '20202' cgi_data['o'] = 'hpb' cgi_data['centreon_token'] = self.centreon_token - # running remote cgi command, also possible with GET method + # Post raw = self.FetchURL(self.urls_centreon['main'], cgi_data=cgi_data, giveback='raw') del raw @@ -872,15 +885,14 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi 'service_description': s, 'force_check': '1', 'persistent': int(persistent), - # follewing not needed in 18.10, requiered in wich version ? + # following not needed in 18.10, required in wich version ? 'persistant': int(persistent), 'sticky': int(sticky), 'o': 'svcd', 'en': '1'} if self.centreon_version < 2.7: cgi_data['p'] = '20215' - # elif self.centreon_version == 2.7 or self.centreon_version == 2.8: - elif self.centreon_version in [18.10, 2.8, 2.7]: + else: cgi_data['p'] = '20201' cgi_data['centreon_token'] = self.centreon_token @@ -898,12 +910,7 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi elif self.centreon_version in [2.8, 18.10]: cgi_data['service_description'] = rsd - # debug - redondant avec le FetchURL qui donne les données - if conf.debug_mode == True: - self.Debug(server=self.get_name(), host=host, service=s, debug=self.urls_centreon['main'] + '?' + urllib.parse.urlencode(cgi_data)) - - # running remote cgi command with GET method, for some strange reason only working if - # giveback is 'raw' + # POST, for some strange reason only working if giveback is 'raw' raw = self.FetchURL(self.urls_centreon['main'], cgi_data=cgi_data, giveback='raw') del raw except: @@ -942,16 +949,30 @@ def _set_recheck(self, host, service): # service @ host host_id, service_id = self._get_host_and_service_id(host, service) - # fill and encode CGI data - cgi_data = urllib.parse.urlencode({'cmd':'service_schedule_check', 'actiontype':1,\ - 'host_id':host_id, 'service_id':service_id, 'sid':self.SID}) - - url = self.urls_centreon['xml_serviceSendCommand'] + '?' + cgi_data - del host_id, service_id + # Starting from 19.04 this must be in POST + if self.centreon_version < 19.04: + # fill and encode URL data + cgi_data = urllib.parse.urlencode({'cmd':'service_schedule_check', 'actiontype':1,\ + 'host_id':host_id, 'service_id':service_id, 'sid':self.SID}) - # execute POST request - raw = self.FetchURL(url, giveback='raw') - del raw + url = self.urls_centreon['xml_serviceSendCommand'] + '?' + cgi_data + del host_id, service_id + else: + cgi_data = {'cmd': 'service_schedule_check', + 'host_id': host_id, + 'service_id': service_id, + 'actiontype': '0'} + del host_id, service_id + + if self.centreon_version < 19.04: + # execute GET request + raw = self.FetchURL(url, giveback='raw') + del raw + else: + # running remote cgi command with POST method, for some strange reason only working if + # giveback is 'raw' + raw = self.FetchURL(self.urls_centreon['xml_serviceSendCommand'], cgi_data=cgi_data, giveback='raw') + del raw except: self.Error(sys.exc_info()) @@ -969,53 +990,82 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t else: fixed = 'false' + # Host downtime if service == '': - # So it is a host downtime - cgi_data = {'cmd':75,\ - 'duration':duration,\ - 'duration_scale':'m',\ - 'start':start_time,\ - 'end':end_time,\ - 'comment':comment,\ - 'fixed':fixed,\ - 'downtimehostservice':'true',\ - 'author':author,\ - 'sid':self.SID,\ - 'select['+host+']':1} - - # debug - if conf.debug_mode == True: - self.Debug(server=self.get_name(), host=host, debug=self.urls_centreon['external_cmd_cmdPopup'] + '?' + urllib.parse.urlencode(cgi_data)) - + if self.centreon_version < 19.04: + cgi_data = {'cmd':75,\ + 'duration':duration,\ + 'duration_scale':'m',\ + 'start':start_time,\ + 'end':end_time,\ + 'comment':comment,\ + 'fixed':fixed,\ + 'downtimehostservice':'true',\ + 'author':author,\ + 'sid':self.SID,\ + 'select['+host+']':1 + } + # Params has changed starting from 19.04 + else: + cgi_data = {'cmd':75, + 'duration':duration, + 'duration_scale':'m', + 'comment':comment, + 'start':start_time, + 'end':end_time, + 'host_or_centreon_time':0, + 'fixed':fixed, + 'downtimehostservice':'true', + 'author':author, + 'resources':'["'+host+'"]' + } + + # Service downtime else: - # It is a service downtime - # Centreon 2.8 only, in case of a meta-service, extract the 'rsd' field from the service name : if host == '_Module_Meta' and self.centreon_version in [2.8, 18.10]: m = re.search(r'^.+ \((?P<rsd>.+)\)$', service) if m: rsd = m.group('rsd') service = rsd - - cgi_data = {'cmd':74,\ - 'duration':duration,\ - 'duration_scale':'m',\ - 'start':start_time,\ - 'end':end_time,\ - 'comment':comment,\ - 'fixed':fixed,\ - 'downtimehostservice':0,\ - 'author':author,\ - 'sid':self.SID,\ - 'select['+host+';'+service+']':1} - - # debug - if conf.debug_mode == True: - self.Debug(server=self.get_name(), host=host, service=service, debug=self.urls_centreon['external_cmd_cmdPopup'] + '?' + urllib.parse.urlencode(cgi_data)) - - # This request must be done in a GET, so just encode the parameters and fetch - raw = self.FetchURL(self.urls_centreon['external_cmd_cmdPopup'] + '?' + urllib.parse.urlencode(cgi_data), giveback="raw") - del raw + if self.centreon_version < 19.04: + cgi_data = {'cmd':74,\ + 'duration':duration,\ + 'duration_scale':'m',\ + 'start':start_time,\ + 'end':end_time,\ + 'comment':comment,\ + 'fixed':fixed,\ + 'downtimehostservice':0,\ + 'author':author,\ + 'sid':self.SID,\ + 'select['+host+';'+service+']':1 + } + + # Params has changed starting from 19.04 + else: + cgi_data = {'cmd':74, + 'duration':duration, + 'duration_scale':'m', + 'comment':comment, + 'start':start_time, + 'end':end_time, + 'host_or_centreon_time':0, + 'fixed':fixed, + 'downtimehostservice':0, + 'author':author, + 'resources':'["'+host+'%3B'+service+'"]' + } + + if self.centreon_version < 19.04: + # This request must be done in a GET, so just encode the parameters and fetch + raw = self.FetchURL(self.urls_centreon['external_cmd_cmdPopup'] + '?' + urllib.parse.urlencode(cgi_data), giveback="raw") + del raw + # Starting from 19.04, must be POST + else: + # Do it in POST + raw = self.FetchURL(self.urls_centreon['external_cmd_cmdPopup'], cgi_data=cgi_data, giveback='raw') + del raw except: self.Error(sys.exc_info()) @@ -1027,12 +1077,12 @@ def _check_session(self): if 'url_centreon' not in self.__dict__: self.init_config() try: - if self.centreon_version == 18.10: + if self.centreon_version >= 18.10: result = self.FetchURL(self.urls_centreon['keepAlive'], giveback='raw') - raw, error, status_code = result.result, result.error, result.status_code + self.raw, self.error, self.status_code = result.result, result.error, result.status_code # Return 200 & null a session is open if conf.debug_mode == True: - self.Debug(server=self.get_name(), debug='Session status : ' + raw) + self.Debug(server=self.get_name(), debug='Session status : ' + self.raw + ', http code : ' + str(self.status_code)) # 401 if no valid session is present if self.status_code == 401: self.SID = self._get_sid().result From 193ea9053a546900a5cf634d3a9d2d9a09d51b78 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Tue, 22 Dec 2020 14:14:36 +0100 Subject: [PATCH 057/884] try to fix https://github.com/HenriWahl/Nagstamon/issues/687 --- Nagstamon/Config.py | 2 +- Nagstamon/Servers/Zabbix.py | 4 +++- requirements-linux.txt | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 8797d4a37..cc141952a 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -28,6 +28,7 @@ import datetime from urllib.parse import quote from collections import OrderedDict +from pathlib import Path # older Kubuntu has trouble with keyring # see https://github.com/HenriWahl/Nagstamon/issues/447 @@ -1065,5 +1066,4 @@ def __init__(self, **kwds): RESOURCES = path break else: - from pathlib import Path RESOURCES = str(Path(__file__).parent.absolute().joinpath('resources')) diff --git a/Nagstamon/Servers/Zabbix.py b/Nagstamon/Servers/Zabbix.py index 13393c064..12b4b9142 100644 --- a/Nagstamon/Servers/Zabbix.py +++ b/Nagstamon/Servers/Zabbix.py @@ -165,9 +165,11 @@ def _get_status(self): # Zabbix backend data 'hostid': host['hostid'], 'site': '', - 'address': host['interfaces'][0]['ip'], + # 'address': host['interfaces'][0]['ip'], } + # try to fix https://github.com/HenriWahl/Nagstamon/issues/687 + n['address'] = host['interfaces'][0]['ip'] if len(host['interfaces']) > 0 else '' if host['maintenance_status'] == '1': n['scheduled_downtime'] = True diff --git a/requirements-linux.txt b/requirements-linux.txt index 884d2de66..6ba327a64 100644 --- a/requirements-linux.txt +++ b/requirements-linux.txt @@ -2,7 +2,7 @@ beautifulsoup4 keyring==10.5.1 lxml psutil -pyqt5==5.13.2 +pyqt5==5.15.2 pysocks python-dateutil requests From 24d310612770ba72c18edb6399313d6ebb46664c Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Tue, 22 Dec 2020 14:21:31 +0100 Subject: [PATCH 058/884] try to fix https://github.com/HenriWahl/Nagstamon/issues/687 --- Nagstamon/Config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index cc141952a..9d40a5f0f 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -124,7 +124,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.5-20201110' + VERSION = '3.5-20201222' WEBSITE = 'https://nagstamon.ifw-dresden.de' COPYRIGHT = '©2008-2020 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' From 52a1d6a91e8324f950d4a43e748344d26dc599c7 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Tue, 22 Dec 2020 15:32:40 +0100 Subject: [PATCH 059/884] fixed Debian changelog --- build/debian/changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/debian/changelog b/build/debian/changelog index e6f4bc870..3fdb72730 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.5-20201110) unstable; urgency=low +nagstamon (3.5-20201222) unstable; urgency=low * New upstream -- Henri Wahl <h.wahl@ifw-dresden.de> Fri, 31 Jul 2020 17:00:00 +0100 From 7040193bd2178ee053eff6d887890121141c7781 Mon Sep 17 00:00:00 2001 From: Sven Nierlein <sven@nierlein.de> Date: Wed, 3 Feb 2021 11:19:45 +0100 Subject: [PATCH 060/884] Thruk: fix login against latest release Cleaning up login handling. It is not required to detect cookie auth, it should simply work both ways. Changes tested against latest Thruk and back till v2.10 which is already more than 5 years old. Tested with and without cookie auth. --- Nagstamon/Servers/Thruk.py | 75 ++++++++++++++------------------------ 1 file changed, 27 insertions(+), 48 deletions(-) diff --git a/Nagstamon/Servers/Thruk.py b/Nagstamon/Servers/Thruk.py index a8c9aab8e..e9566196a 100644 --- a/Nagstamon/Servers/Thruk.py +++ b/Nagstamon/Servers/Thruk.py @@ -19,6 +19,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA from Nagstamon.Servers.Generic import GenericServer +from Nagstamon.Config import conf import sys import json import datetime @@ -63,41 +64,21 @@ class ThrukServer(GenericServer): def __init__(self, **kwds): GenericServer.__init__(self, **kwds) - # flag for newer cookie authentication - self.CookieAuth = False - - - def reset_HTTP(self): - """ - brute force reset by GenericServer disturbs logging in into Thruk - """ - # only reset session if Thruks 2 cookies are there - try: - # only reset session if Thruks 2 cookies are there - if len(self.session.cookies) > 1: - self.session = None - except: - self.Error(sys.exc_info()) - def init_HTTP(self): """ partly not constantly working Basic Authorization requires extra Autorization headers, different between various server types """ - GenericServer.init_HTTP(self) + if self.session is None: + GenericServer.init_HTTP(self) - # only if cookies are needed - if self.CookieAuth: - # get cookie to access Thruk web interface - # Thruk first send a test cookie, later an auth cookie - if len(self.session.cookies) < 2: - # get cookie from login page via url retrieving as with other urls - try: - # login and get cookie - self.login() - except: - self.Error(sys.exc_info()) + # get cookie from login page via url retrieving as with other urls + try: + # login and get cookie + self.login() + except: + self.Error(sys.exc_info()) def init_config(self): @@ -121,26 +102,25 @@ def init_config(self): "active_checks_enabled,notifications_enabled,is_flapping,"\ "acknowledged,scheduled_downtime_depth,state_type" - # get cookie from login page via url retrieving as with other urls - try: - # login and get cookie - self.login() - - if len(self.session.cookies) > 0: - self.CookieAuth = True - except: - self.Error(sys.exc_info()) - - def login(self): """ use pure session instead of FetchURL to get Thruk session """ - self.session.post(self.monitor_cgi_url + '/login.cgi?', + if self.session is None: + self.refresh_authentication = False + GenericServer.init_HTTP(self) + + # set thruk test cookie to in order to directly login + self.session.cookies.set('thruk_test', '***') + req = self.session.post(self.monitor_cgi_url + '/login.cgi?', data={'login': self.get_username(), 'password': self.get_password(), - 'submit': 'Login', - 'referer': ''}) + 'submit': 'Login'}) + if conf.debug_mode == True: + self.Debug(server=self.get_name(), debug='Login status: ' + req.url + ' http code : ' + str(req.status_code)) + if req.status_code != 200: + self.refresh_authentication = True + return Result(result=None, error="Login failed") def _get_status(self): @@ -159,16 +139,16 @@ def _get_status(self): jsonraw, error, status_code = copy.deepcopy(result.result),\ copy.deepcopy(result.error),\ result.status_code - + # check if any error occured errors_occured = self.check_for_error(jsonraw, error, status_code) # if there are errors return them if errors_occured != False: - return(errors_occured) + return(errors_occured) # in case basic auth did not work try form login cookie based login if jsonraw.startswith("<"): - self.CookieAuth = True + self.refresh_authentication = True return Result(result=None, error="Login failed") # in case JSON is not empty evaluate it @@ -213,10 +193,10 @@ def _get_status(self): # if there are errors return them if errors_occured != False: return(errors_occured) - + # in case basic auth did not work try form login cookie based login if jsonraw.startswith("<"): - self.CookieAuth = True + self.refresh_authentication = True return Result(result=None, error="Login failed") # in case JSON is not empty evaluate it @@ -260,4 +240,3 @@ def _get_status(self): # dummy return in case all is OK return Result() - From fe3d7584194c74d393b8fda74a33ac52fb184f3b Mon Sep 17 00:00:00 2001 From: dizixagi <dominik.zagol@ti-cahn.org> Date: Sun, 7 Feb 2021 19:47:30 +0100 Subject: [PATCH 061/884] fixing acknowleded services filter in zabbix --- Nagstamon/Servers/Generic.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index ad26cff94..0020e2dda 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -1088,7 +1088,8 @@ def GetStatus(self, output=None): self.Debug(server=self.get_name(), debug='Filter: SOFT STATE ' + str(host.name) + ';' + str(service.name)) service.visible = False - else: + # fix for https://github.com/HenriWahl/Nagstamon/issues/654 + elif self.TYPE.startswith("Zabbix"): if len(service.attempt) < 3: service.visible = True elif len(service.attempt) == 3: From ec4ee6b2fc3f3b297cb9fbb70c38b0433739e8a7 Mon Sep 17 00:00:00 2001 From: dizixagi <dominik.zagol@ti-cahn.org> Date: Sun, 7 Feb 2021 23:52:48 +0100 Subject: [PATCH 062/884] Revert "fixing acknowleded services filter in zabbix" This reverts commit fe3d7584194c74d393b8fda74a33ac52fb184f3b. --- Nagstamon/Servers/Generic.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index 0020e2dda..ad26cff94 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -1088,8 +1088,7 @@ def GetStatus(self, output=None): self.Debug(server=self.get_name(), debug='Filter: SOFT STATE ' + str(host.name) + ';' + str(service.name)) service.visible = False - # fix for https://github.com/HenriWahl/Nagstamon/issues/654 - elif self.TYPE.startswith("Zabbix"): + else: if len(service.attempt) < 3: service.visible = True elif len(service.attempt) == 3: From c698a33e6b33eeceeb4a784c5b2e2e5da06747ce Mon Sep 17 00:00:00 2001 From: dizixagi <dominik.zagol@ti-cahn.org> Date: Sun, 7 Feb 2021 23:55:46 +0100 Subject: [PATCH 063/884] fixing acknowledged services filter in zabbix #2 --- Nagstamon/Servers/Generic.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index ad26cff94..4b2352ca2 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -1088,7 +1088,8 @@ def GetStatus(self, output=None): self.Debug(server=self.get_name(), debug='Filter: SOFT STATE ' + str(host.name) + ';' + str(service.name)) service.visible = False - else: + # fix for https://github.com/HenriWahl/Nagstamon/issues/654 + elif not self.TYPE.startswith("Zabbix"): if len(service.attempt) < 3: service.visible = True elif len(service.attempt) == 3: From 0f3a8761cf7932d76746cb9875ad5930fbf49e09 Mon Sep 17 00:00:00 2001 From: dizixagi <dominik.zagol@ti-cahn.org> Date: Mon, 8 Feb 2021 00:57:26 +0100 Subject: [PATCH 064/884] Problem based zabbix pooling --- Nagstamon/QUI/__init__.py | 2 +- Nagstamon/Servers/ZabbixProblemBased.py | 169 ++++++++++++++++++++++++ Nagstamon/Servers/__init__.py | 3 +- 3 files changed, 172 insertions(+), 2 deletions(-) create mode 100644 Nagstamon/Servers/ZabbixProblemBased.py diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 338f4d117..c58d8a1c9 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -5635,7 +5635,7 @@ def toggle_zabbix_widgets(self): use_zabbix = False for server in servers.values(): if server.enabled: - if server.type == 'Zabbix': + if server.type.startswith('Zabbix'): use_zabbix = True break # remove extra Zabbix options diff --git a/Nagstamon/Servers/ZabbixProblemBased.py b/Nagstamon/Servers/ZabbixProblemBased.py new file mode 100644 index 000000000..7cf988460 --- /dev/null +++ b/Nagstamon/Servers/ZabbixProblemBased.py @@ -0,0 +1,169 @@ +# -*- encoding: utf-8; py-indent-offset: 4 -*- +# +# Zabbix.py based on Checkmk Multisite.py + +import sys +import urllib.parse +import time +import logging +import datetime + +from Nagstamon.Helpers import (HumanReadableDurationFromTimestamp, + webbrowser_open) +from Nagstamon.Config import conf +from Nagstamon.Objects import (GenericHost, + GenericService, + Result) +from Nagstamon.Servers.Generic import GenericServer +from Nagstamon.thirdparty.zabbix_api import (ZabbixAPI, + ZabbixAPIException) + +class ZabbixProblemBasedServer(GenericServer): + + TYPE = 'ZabbixProblemBased' + zapi = None + + if conf.debug_mode is True: + log_level = logging.DEBUG + else: + log_level = logging.WARNING + + def __init__(self, **kwds): + GenericServer.__init__(self, **kwds) + + self.statemap = { + '0': 'OK', + '1': 'INFORMATION', + '2': 'WARNING', + '3': 'AVERAGE', + '4': 'HIGH', + '5': 'DISASTER'} + + # Entries for monitor default actions in context menu + self.MENU_ACTIONS = [] + # URLs for browser shortlinks/buttons on popup window + self.BROWSER_URLS = {'monitor': '$MONITOR$', + 'hosts': '$MONITOR-CGI$/hosts.php?ddreset=1', + 'services': '$MONITOR-CGI$/zabbix.php?action=problem.view&fullscreen=0&page=1&filter_show=3&filter_set=1', + 'history': '$MONITOR-CGI$/zabbix.php?action=problem.view&fullscreen=0&page=1&filter_show=2&filter_set=1'} + + self.username = conf.servers[self.get_name()].username + self.password = conf.servers[self.get_name()].password + self.ignore_cert = conf.servers[self.get_name()].ignore_cert + self.validate_certs = not self.ignore_cert + + + def _login(self): + try: + # create ZabbixAPI if not yet created + if self.zapi is None: + self.zapi = ZabbixAPI(server=self.monitor_url, path="", log_level=self.log_level, validate_certs=self.validate_certs) + # login if not yet logged in, or if login was refused previously + if not self.zapi.logged_in(): + self.zapi.login(self.username, self.password) + except ZabbixAPIException: + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) + + def _get_status(self): + """ + Get status from Zabbix Server + """ + + # Create URLs for the configured filters + self._login() + + # ========================================= + # problems + # ========================================= + problems = [] + try: + #Get all current problems (trigger based) + problems = self.zapi.problem.get({'recent': "False"}) + + for problem in problems: + + #get trigger which rose current problem + trigger = self.zapi.trigger.get({'triggerids': problem['objectid'], + 'monitored': True, + 'active': True, + 'selectHosts': ['hostid', 'name', 'maintenance_status', + 'available', 'error', 'errors_from', + 'ipmi_available', 'ipmi_error', 'ipmi_errors_from', + 'jmx_available', 'jmx_error', 'jmx_errors_from', + 'snmp_available', 'snmp_error', 'snmp_errors_from'], + 'selectItems': ['key_', 'lastclock']}) + + + #problems on disabled/maintenanced/deleted hosts don't have triggers + #have to to that becouse of how zabbix houesekeeping service worke + #API reports past problems for hosts that no longer exist + if not trigger: + continue + + service_id = problem['eventid'] + host_id = trigger[0]['hosts'][0]['hostid'] + + #new host to report, we only need to do that at first problem for that host + if host_id not in self.new_hosts: + self.new_hosts[host_id] = GenericHost() + self.new_hosts[host_id].name = trigger[0]['hosts'][0]['name'] + + #host has active maintenance period + if trigger[0]['hosts'][0]['maintenance_status'] == "1": + self.new_hosts[host_id].scheduled_downtime = True + + #host not available via agent + if trigger[0]['hosts'][0]['available'] == "2": + self.new_hosts[host_id].status = "DOWN" + self.new_hosts[host_id].status_information = trigger[0]['hosts'][0]['error'] + self.new_hosts[host_id].duration = HumanReadableDurationFromTimestamp(trigger[0]['hosts'][0]['errors_from']) + + #host not available via ipmi + if trigger[0]['hosts'][0]['ipmi_available'] == "2": + self.new_hosts[host_id].status = "DOWN" + self.new_hosts[host_id].status_information = trigger[0]['hosts'][0]['ipmi_error'] + self.new_hosts[host_id].duration = HumanReadableDurationFromTimestamp(trigger[0]['hosts'][0]['ipmi_errors_from']) + + #host not available via jmx + if trigger[0]['hosts'][0]['jmx_available'] == "2": + self.new_hosts[host_id].status = "DOWN" + self.new_hosts[host_id].status_information = trigger[0]['hosts'][0]['jmx_error'] + self.new_hosts[host_id].duration = HumanReadableDurationFromTimestamp(trigger[0]['hosts'][0]['jmx_errors_from']) + + #host not available via snmp + if trigger[0]['hosts'][0]['snmp_available'] == "2": + self.new_hosts[host_id].status = "DOWN" + self.new_hosts[host_id].status_information = trigger[0]['hosts'][0]['snmp_error'] + self.new_hosts[host_id].duration = HumanReadableDurationFromTimestamp(trigger[0]['hosts'][0]['snmp_errors_from']) + + #service to report + self.new_hosts[host_id].services[service_id] = GenericService() + self.new_hosts[host_id].services[service_id].host = trigger[0]['hosts'][0]['name'] + self.new_hosts[host_id].services[service_id].status = self.statemap.get(problem['severity'], problem['severity']) + self.new_hosts[host_id].services[service_id].duration = HumanReadableDurationFromTimestamp(problem['clock']) + self.new_hosts[host_id].services[service_id].name = trigger[0]['items'][0]['key_'] + self.new_hosts[host_id].services[service_id].last_check = time.strftime("%d/%m/%Y %H:%M:%S", time.localtime(int(trigger[0]['items'][0]['lastclock']))) + + #we add opdata to status information just like in zabbix GUI + if problem["opdata"] != "": + self.new_hosts[host_id].services[service_id].status_information = problem['name'] + " (" + problem["opdata"] + ")" + else: + self.new_hosts[host_id].services[service_id].status_information = problem['name'] + + #service is acknowledged + if problem['acknowledged'] == "1": + self.new_hosts[host_id].services[service_id].acknowledged = True + + except (ZabbixAPIException): + # set checking flag back to False + self.isChecking = False + result, error = self.Error(sys.exc_info()) + print(sys.exc_info()) + return Result(result=result, error=error) + + return Result() + + # Disable set_recheck (nosense in Zabbix) + def set_recheck(self, info_dict): + pass diff --git a/Nagstamon/Servers/__init__.py b/Nagstamon/Servers/__init__.py index 47c948851..7d667fb3f 100644 --- a/Nagstamon/Servers/__init__.py +++ b/Nagstamon/Servers/__init__.py @@ -42,6 +42,7 @@ from Nagstamon.Servers.Opsview import OpsviewServer from Nagstamon.Servers.Thruk import ThrukServer from Nagstamon.Servers.Zabbix import ZabbixServer +from Nagstamon.Servers.ZabbixProblemBased import ZabbixProblemBasedServer from Nagstamon.Servers.Livestatus import LivestatusServer from Nagstamon.Servers.Zenoss import ZenossServer from Nagstamon.Servers.Monitos3 import Monitos3Server @@ -217,7 +218,7 @@ def create_server(server=None): servers_list = [CentreonServer, IcingaServer, IcingaWeb2Server, MultisiteServer, NagiosServer, Op5MonitorServer, OpsviewServer, ThrukServer, ZabbixServer, SensuServer, LivestatusServer, ZenossServer, Monitos3Server, Monitos4xServer, SnagViewServer, - PrometheusServer, AlertmanagerServer] + PrometheusServer, AlertmanagerServer, ZabbixProblemBasedServer] # we use these servers conditionally if modules are available only if icinga2api_is_available is True: servers_list.append(Icinga2APIServer) From bff8d27f5de4b6bc83a91d4faef16124e8b97a3d Mon Sep 17 00:00:00 2001 From: dizixagi <dominik.zagol@ti-cahn.org> Date: Thu, 11 Feb 2021 17:44:47 +0100 Subject: [PATCH 065/884] typo in "recent" parameter adding "skipdependet" to hide problems that are dependant on others --- Nagstamon/Servers/ZabbixProblemBased.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Nagstamon/Servers/ZabbixProblemBased.py b/Nagstamon/Servers/ZabbixProblemBased.py index 7cf988460..abaa147a3 100644 --- a/Nagstamon/Servers/ZabbixProblemBased.py +++ b/Nagstamon/Servers/ZabbixProblemBased.py @@ -79,7 +79,7 @@ def _get_status(self): problems = [] try: #Get all current problems (trigger based) - problems = self.zapi.problem.get({'recent': "False"}) + problems = self.zapi.problem.get({'recent': False}) for problem in problems: @@ -87,6 +87,7 @@ def _get_status(self): trigger = self.zapi.trigger.get({'triggerids': problem['objectid'], 'monitored': True, 'active': True, + 'skipDependent': True, 'selectHosts': ['hostid', 'name', 'maintenance_status', 'available', 'error', 'errors_from', 'ipmi_available', 'ipmi_error', 'ipmi_errors_from', From 0ab7e3ec53f435dd7fc935142e282919652ade10 Mon Sep 17 00:00:00 2001 From: dizixagi <dominik.zagol@ti-cahn.org> Date: Thu, 11 Feb 2021 17:49:18 +0100 Subject: [PATCH 066/884] typos in comments --- Nagstamon/Servers/ZabbixProblemBased.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Servers/ZabbixProblemBased.py b/Nagstamon/Servers/ZabbixProblemBased.py index abaa147a3..25898cce1 100644 --- a/Nagstamon/Servers/ZabbixProblemBased.py +++ b/Nagstamon/Servers/ZabbixProblemBased.py @@ -70,7 +70,7 @@ def _get_status(self): Get status from Zabbix Server """ - # Create URLs for the configured filters + # Login to ZabbixAPI self._login() # ========================================= @@ -96,8 +96,8 @@ def _get_status(self): 'selectItems': ['key_', 'lastclock']}) - #problems on disabled/maintenanced/deleted hosts don't have triggers - #have to to that becouse of how zabbix houesekeeping service worke + #problems on disabled/maintenance/deleted hosts don't have triggers + #have to do that because of how zabbix housekeeping service work #API reports past problems for hosts that no longer exist if not trigger: continue From 77516beab74ad627200aa983eab6e089b914e3d9 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Wed, 3 Mar 2021 08:06:22 +0100 Subject: [PATCH 067/884] added more version check options --- Nagstamon/Config.py | 5 +++++ Nagstamon/QUI/__init__.py | 18 ++++++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 9d40a5f0f..38bfd2e37 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -128,14 +128,19 @@ class AppInfo(object): WEBSITE = 'https://nagstamon.ifw-dresden.de' COPYRIGHT = '©2008-2020 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' + # dict of servers to offer for downloads if an update is available + DOWNLOAD_SERVERS = {'nagstamon.de': 'https://github.com/HenriWahl/Nagstamon/releases', + 'nagstamon.ifw-dresden.de': 'https://nagstamon.ifw-dresden.de/download'} # version URL depends on version string if 'alpha' in VERSION.lower() or \ 'beta' in VERSION.lower() or \ 'rc' in VERSION.lower() or \ '-' in VERSION.lower(): VERSION_URL = WEBSITE + '/version/unstable' + VERSION_PATH = '/version/unstable' else: VERSION_URL = WEBSITE + '/version/stable' + VERSION_PATH = '/version/stable' class Config(object): diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index c58d8a1c9..d76c6f956 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -6922,15 +6922,25 @@ def check(self): # default latest version is 'unavailable' latest_version = 'unavailable' - message = 'Cannot reach version check at <a href={0}>{0}</<a>.'.format(AppInfo.VERSION_URL) # find at least one server which allows to get version information for server in enabled_servers: + for download_server, download_url in AppInfo.DOWNLOAD_SERVERS.items(): - # retrieve VERSION_URL without auth information - response = server.FetchURL(AppInfo.VERSION_URL, giveback='raw', no_auth=True) + message = 'Cannot reach version check at <a href={0}>{0}</<a>.'.format(f'https://{download_server}{AppInfo.VERSION_PATH}') - # stop searching if some valid information has been found + # retrieve VERSION_URL without auth information + response = server.FetchURL(f'https://{download_server}{AppInfo.VERSION_PATH}', + giveback='raw', + no_auth=True) + # stop searching the available download URLs + if response.error == "" and not response.result.startswith('<'): + latest_version = response.result.strip() + break + # ignore TLS error in case it was caused by requesting latest version + server.tls_error = False + + # stop searching via enabled servers if response.error == "" and not response.result.startswith('<'): latest_version = response.result.strip() break From 38a306fbdba63eccf58a64352f070efa6fbabe9d Mon Sep 17 00:00:00 2001 From: Sven Nierlein <sven@nierlein.de> Date: Tue, 9 Mar 2021 15:30:48 +0100 Subject: [PATCH 068/884] thruk: implement autologin keys Signed-off-by: Sven Nierlein <sven@nierlein.de> --- Nagstamon/Config.py | 2 +- Nagstamon/QUI/__init__.py | 16 ++++++++-------- Nagstamon/Servers/Thruk.py | 33 +++++++++++++++++++++------------ 3 files changed, 30 insertions(+), 21 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 38bfd2e37..d5d60475f 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -475,7 +475,7 @@ def _LoadServersMultipleConfig(self): elif servers[server].proxy_password != "": servers[server].proxy_password = self.DeObfuscate(servers[server].proxy_password) - # do only deobfuscating if any autologin_key is set - will be only Centreon + # do only deobfuscating if any autologin_key is set - will be only Centreon/Thruk if 'autologin_key' in servers[server].__dict__.keys(): if len(servers[server].__dict__['autologin_key']) > 0: servers[server].autologin_key = self.DeObfuscate(servers[server].autologin_key) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index d76c6f956..5f70a090c 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -5742,9 +5742,9 @@ def __init__(self, dialog): self.VOLATILE_WIDGETS = { self.ui.label_monitor_cgi_url: ['Nagios', 'Icinga', 'Thruk', 'Sensu'], self.ui.input_lineedit_monitor_cgi_url: ['Nagios', 'Icinga', 'Thruk', 'Sensu'], - self.ui.input_checkbox_use_autologin: ['Centreon', 'monitos4x'], - self.ui.input_lineedit_autologin_key: ['Centreon', 'monitos4x'], - self.ui.label_autologin_key: ['Centreon', 'monitos4x'], + self.ui.input_checkbox_use_autologin: ['Centreon', 'monitos4x', 'Thruk'], + self.ui.input_lineedit_autologin_key: ['Centreon', 'monitos4x', 'Thruk'], + self.ui.label_autologin_key: ['Centreon', 'monitos4x', 'Thruk'], self.ui.input_checkbox_no_cookie_auth: ['IcingaWeb2', 'Sensu'], self.ui.input_checkbox_use_display_name_host: ['Icinga', 'IcingaWeb2'], self.ui.input_checkbox_use_display_name_service: ['Icinga', 'IcingaWeb2'], @@ -6278,7 +6278,7 @@ def __init__(self, dialog): self.VOLATILE_WIDGETS = { self.ui.input_checkbox_use_expire_time: ['IcingaWeb2'], self.ui.input_datetime_expire_time: ['IcingaWeb2'] - } + } def initialize(self, server=None, host=[], service=[]): @@ -6327,10 +6327,10 @@ def initialize(self, server=None, host=[], service=[]): for widget, server_types in self.VOLATILE_WIDGETS.items(): if self.server.TYPE in server_types: widget.show() - self.toggle_toggles() + self.toggle_toggles() else: widget.hide() - + # Adjust to current size if items are hidden in menu # Otherwise it will get confused and chop off text self.ui.options_groupbox.adjustSize() @@ -6597,7 +6597,7 @@ def initialize(self): if self.server is not None: self.window.setWindowTitle('Authenticate {0}'.format(self.server.name)) - if self.server.type == 'Centreon': + if self.server.type in ['Centreon', 'Thruk']: self.ui.input_checkbox_use_autologin.show() self.ui.input_lineedit_autologin_key.show() self.ui.input_lineedit_autologin_key.show() @@ -6652,7 +6652,7 @@ def ok(self): conf.SaveMultipleConfig('servers', 'server') # Centreon - if self.server.type == 'Centreon': + if self.server.type in ['Centreon', 'Thruk']: if self.ui.input_checkbox_use_autologin: conf.servers[self.server.name].use_autologin = self.ui.input_checkbox_use_autologin.isChecked() conf.servers[self.server.name].autologin_key = self.ui.input_lineedit_autologin_key.text() diff --git a/Nagstamon/Servers/Thruk.py b/Nagstamon/Servers/Thruk.py index e9566196a..978ec2608 100644 --- a/Nagstamon/Servers/Thruk.py +++ b/Nagstamon/Servers/Thruk.py @@ -76,7 +76,8 @@ def init_HTTP(self): # get cookie from login page via url retrieving as with other urls try: # login and get cookie - self.login() + if self.session is None or self.session.cookies.get('thruk_auth') is None: + self.login() except: self.Error(sys.exc_info()) @@ -110,17 +111,25 @@ def login(self): self.refresh_authentication = False GenericServer.init_HTTP(self) - # set thruk test cookie to in order to directly login - self.session.cookies.set('thruk_test', '***') - req = self.session.post(self.monitor_cgi_url + '/login.cgi?', - data={'login': self.get_username(), - 'password': self.get_password(), - 'submit': 'Login'}) - if conf.debug_mode == True: - self.Debug(server=self.get_name(), debug='Login status: ' + req.url + ' http code : ' + str(req.status_code)) - if req.status_code != 200: - self.refresh_authentication = True - return Result(result=None, error="Login failed") + if self.use_autologin is True: + req = self.session.post(self.monitor_cgi_url + '/user.cgi?', data={}, headers={'X-Thruk-Auth-Key':self.autologin_key.strip()}) + if conf.debug_mode == True: + self.Debug(server=self.get_name(), debug='Auto Login status: ' + req.url + ' http code : ' + str(req.status_code)) + if req.status_code != 200: + self.refresh_authentication = True + return Result(result=None, error="Login failed") + else: + # set thruk test cookie to in order to directly login + self.session.cookies.set('thruk_test', '***') + req = self.session.post(self.monitor_cgi_url + '/login.cgi?', + data={'login': self.get_username(), + 'password': self.get_password(), + 'submit': 'Login'}) + if conf.debug_mode == True: + self.Debug(server=self.get_name(), debug='Login status: ' + req.url + ' http code : ' + str(req.status_code)) + if req.status_code != 200: + self.refresh_authentication = True + return Result(result=None, error="Login failed") def _get_status(self): From fbd3317c0448d2b9283551814927f78d243e0c86 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Tue, 16 Mar 2021 21:58:18 +0100 Subject: [PATCH 069/884] added more version check options --- Nagstamon/QUI/__init__.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index d76c6f956..cf4814a3c 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -6926,9 +6926,8 @@ def check(self): # find at least one server which allows to get version information for server in enabled_servers: for download_server, download_url in AppInfo.DOWNLOAD_SERVERS.items(): - + # dummy message just in case version check does not work message = 'Cannot reach version check at <a href={0}>{0}</<a>.'.format(f'https://{download_server}{AppInfo.VERSION_PATH}') - # retrieve VERSION_URL without auth information response = server.FetchURL(f'https://{download_server}{AppInfo.VERSION_PATH}', giveback='raw', @@ -6937,7 +6936,7 @@ def check(self): if response.error == "" and not response.result.startswith('<'): latest_version = response.result.strip() break - # ignore TLS error in case it was caused by requesting latest version + # ignore TLS error in case it was caused by requesting latest version - not important for monitoring server.tls_error = False # stop searching via enabled servers From 1ffe2f2bc2fd467fd702b6cb4e94d3e8ed5242e7 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Tue, 16 Mar 2021 22:02:02 +0100 Subject: [PATCH 070/884] version 20210316 --- Nagstamon/Config.py | 4 ++-- build/debian/changelog | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index d5d60475f..7b861b361 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -124,9 +124,9 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.5-20201222' + VERSION = '3.5-20210316' WEBSITE = 'https://nagstamon.ifw-dresden.de' - COPYRIGHT = '©2008-2020 Henri Wahl et al.' + COPYRIGHT = '©2008-2021 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' # dict of servers to offer for downloads if an update is available DOWNLOAD_SERVERS = {'nagstamon.de': 'https://github.com/HenriWahl/Nagstamon/releases', diff --git a/build/debian/changelog b/build/debian/changelog index 3fdb72730..41ed20068 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.5-20201222) unstable; urgency=low +nagstamon (3.5-20210316) unstable; urgency=low * New upstream -- Henri Wahl <h.wahl@ifw-dresden.de> Fri, 31 Jul 2020 17:00:00 +0100 From f2684ee33b4f142163a056bed1099f1901718339 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Tue, 16 Mar 2021 22:05:39 +0100 Subject: [PATCH 071/884] experimenting with GitHub Actions --- .github/workflows/build-release-devel.yml | 93 ++++++++++++++++++++++ .github/workflows/build-release-stable.yml | 91 +++++++++++++++++++++ build/docker/Dockerfile-debian | 29 +++++++ build/docker/Dockerfile-fedora-33 | 25 ++++++ build/requirements/linux.txt | 10 +++ build/requirements/macos.txt | 11 +++ build/requirements/windows.txt | 15 ++++ 7 files changed, 274 insertions(+) create mode 100644 .github/workflows/build-release-devel.yml create mode 100644 .github/workflows/build-release-stable.yml create mode 100644 build/docker/Dockerfile-debian create mode 100644 build/docker/Dockerfile-fedora-33 create mode 100644 build/requirements/linux.txt create mode 100644 build/requirements/macos.txt create mode 100644 build/requirements/windows.txt diff --git a/.github/workflows/build-release-devel.yml b/.github/workflows/build-release-devel.yml new file mode 100644 index 000000000..13c16826f --- /dev/null +++ b/.github/workflows/build-release-devel.yml @@ -0,0 +1,93 @@ +name: build-release-devel +on: + push: + tags-ignore: 'v*' + branches: '**' +env: + python_win_version: 3.9.2 + python_win_path: c:/hostedtoolcache/windows/python/ +jobs: + debian: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + - uses: actions/upload-artifact@v2 + with: + path: build/*.deb + retention-days: 1 + + fedora-33: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + - uses: actions/upload-artifact@v2 + with: + path: build/*.rpm + retention-days: 1 + + macos: + runs-on: macos-latest + steps: + - uses: actions/checkout@v2 + - run: pip3 install --no-warn-script-location -r build/requirements/macos.txt + - run: cd ${{ github.workspace }}/build; python3 build.py + env: + PYTHONPATH: ${{ github.workspace }} + - uses: actions/upload-artifact@v2 + with: + path: build/*.dmg + retention-days: 1 + + windows-32: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - run: cinst -y --allow-empty-checksums --no-progress --x86 python3 --version=${{ env.python_win_version }} + - run: cinst -y --allow-empty-checksums --no-progress innosetup + - run: ${{ env.python_win_path }}/${{ env.python_win_version }}/x86/python.exe -m pip install --no-warn-script-location -r build/requirements/windows.txt + - run: cd ${{ github.workspace }}/build; ${{ env.python_win_path }}/${{ env.python_win_version }}/x86/python.exe build.py + env: + PYTHONPATH: ${{ github.workspace }} + - uses: actions/upload-artifact@v2 + with: + path: | + build/dist/*.zip + build/dist/*.exe + retention-days: 1 + + windows-64: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - run: cinst -y --allow-empty-checksums --no-progress python3 --version=${{ env.python_win_version }} + - run: cinst -y --allow-empty-checksums --no-progress innosetup + - run: ${{ env.python_win_path }}/${{ env.python_win_version }}/x64/python.exe -m pip install --no-warn-script-location -r build/requirements/windows.txt + - run: cd ${{ github.workspace }}/build; ${{ env.python_win_path }}/${{ env.python_win_version }}/x64/python.exe build.py + env: + PYTHONPATH: ${{ github.workspace }} + - uses: actions/upload-artifact@v2 + with: + path: | + build/dist/*.zip + build/dist/*.exe + retention-days: 1 + + upload-release: + runs-on: ubuntu-latest + needs: [debian, fedora-33, macos, windows-32, windows-64] + steps: + - uses: actions/download-artifact@v2 + - run: cd artifact && md5sum *agstamon* > md5sums.txt + - run: cd artifact && sha256sum *agstamon* > sha256sums.txt + - uses: marvinpinto/action-automatic-releases@latest + with: + repo_token: "${{ secrets.GITHUB_TOKEN }}" + automatic_release_tag: "latest" + prerelease: true + title: "Development Build" + files: | + artifact/* diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml new file mode 100644 index 000000000..565d11634 --- /dev/null +++ b/.github/workflows/build-release-stable.yml @@ -0,0 +1,91 @@ +name: build-release-stable +on: + push: + tags: 'v*' +env: + python_win_version: 3.9.2 + python_win_path: c:/hostedtoolcache/windows/python/ +jobs: + debian: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + - uses: actions/upload-artifact@v2 + with: + path: build/*.deb + retention-days: 1 + + fedora-33: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + - uses: actions/upload-artifact@v2 + with: + path: build/*.rpm + retention-days: 1 + + macos: + runs-on: macos-latest + steps: + - uses: actions/checkout@v2 + - run: pip3 install --no-warn-script-location -r build/requirements/macos.txt + - run: cd ${{ github.workspace }}/build; python3 build.py + env: + PYTHONPATH: ${{ github.workspace }} + - uses: actions/upload-artifact@v2 + with: + path: build/*.dmg + retention-days: 1 + + windows-32: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - run: cinst -y --allow-empty-checksums --no-progress --x86 python3 --version=${{ env.python_win_version }} + - run: cinst -y --allow-empty-checksums --no-progress innosetup + - run: ${{ env.python_win_path }}/${{ env.python_win_version }}/x86/python.exe -m pip install --no-warn-script-location -r build/requirements/windows.txt + - run: cd ${{ github.workspace }}/build; ${{ env.python_win_path }}/${{ env.python_win_version }}/x86/python.exe build.py + env: + PYTHONPATH: ${{ github.workspace }} + - uses: actions/upload-artifact@v2 + with: + path: | + build/dist/*.zip + build/dist/*.exe + retention-days: 1 + + windows-64: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - run: cinst -y --allow-empty-checksums --no-progress python3 --version=${{ env.python_win_version }} + - run: cinst -y --allow-empty-checksums --no-progress innosetup + - run: ${{ env.python_win_path }}/${{ env.python_win_version }}/x64/python.exe -m pip install --no-warn-script-location -r build/requirements/windows.txt + - run: cd ${{ github.workspace }}/build; ${{ env.python_win_path }}/${{ env.python_win_version }}/x64/python.exe build.py + env: + PYTHONPATH: ${{ github.workspace }} + - uses: actions/upload-artifact@v2 + with: + path: | + build/dist/*.zip + build/dist/*.exe + retention-days: 1 + + upload-release: + runs-on: ubuntu-latest + needs: [debian, fedora-33, macos, windows-32, windows-64] + steps: + - uses: actions/download-artifact@v2 + - run: cd artifact && md5sum *agstamon* > md5sums.txt + - run: cd artifact && sha256sum *agstamon* > sha256sums.txt + - uses: marvinpinto/action-automatic-releases@latest + with: + repo_token: "${{ secrets.GITHUB_TOKEN }}" + prerelease: false + draft: true + files: | + artifact/* diff --git a/build/docker/Dockerfile-debian b/build/docker/Dockerfile-debian new file mode 100644 index 000000000..7566d9eda --- /dev/null +++ b/build/docker/Dockerfile-debian @@ -0,0 +1,29 @@ +FROM debian:8 +LABEL maintainer=henri@nagstamon.de + +RUN apt -y update +RUN apt -y install apt-utils + +# python3-pysocks in debian:8 becomes python3-socks in later versions +RUN apt -y install debhelper \ + fakeroot \ + git \ + make \ + libqt5multimedia5-plugins \ + python3-bs4 \ + python3-dateutil \ + python3-dbus.mainloop.pyqt5 \ + python3-keyring \ + python3-lxml \ + python3-pkg-resources \ + python3-psutil \ + python3-pyqt5 \ + python3-pyqt5.qtsvg \ + python3-pyqt5.qtmultimedia \ + python3-requests \ + python3-requests-kerberos \ + python3-setuptools \ + python3-pysocks + +CMD cd /nagstamon/build && \ + /usr/bin/python3 build.py \ No newline at end of file diff --git a/build/docker/Dockerfile-fedora-33 b/build/docker/Dockerfile-fedora-33 new file mode 100644 index 000000000..d45e40f40 --- /dev/null +++ b/build/docker/Dockerfile-fedora-33 @@ -0,0 +1,25 @@ +FROM fedora:33 +LABEL maintainer=henri@nagstamon.de + +RUN dnf -y install desktop-file-utils \ + git \ + python3 \ + python3-beautifulsoup4 \ + python3-crypto \ + python3-cryptography \ + python3-dateutil \ + python3-devel \ + python3-keyring \ + python3-lxml \ + python3-psutil \ + python3-qt5 \ + python3-qt5-devel \ + python3-requests \ + python3-requests-kerberos \ + python3-SecretStorage \ + qt5-qtsvg \ + qt5-qtmultimedia \ + rpm-build + +CMD cd /nagstamon/build && \ + /usr/bin/python3 build.py diff --git a/build/requirements/linux.txt b/build/requirements/linux.txt new file mode 100644 index 000000000..6ba327a64 --- /dev/null +++ b/build/requirements/linux.txt @@ -0,0 +1,10 @@ +beautifulsoup4 +keyring==10.5.1 +lxml +psutil +pyqt5==5.15.2 +pysocks +python-dateutil +requests +requests-kerberos +setuptools==44.1.1 \ No newline at end of file diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt new file mode 100644 index 000000000..f47eed3c6 --- /dev/null +++ b/build/requirements/macos.txt @@ -0,0 +1,11 @@ +beautifulsoup4 +keyring==10.5.1 +lxml +psutil +pyinstaller +pyqt5==5.15.3 +pysocks +python-dateutil +requests +requests-gssapi +setuptools==44.1.1 \ No newline at end of file diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt new file mode 100644 index 000000000..a9e4adc28 --- /dev/null +++ b/build/requirements/windows.txt @@ -0,0 +1,15 @@ +arrow +beautifulsoup4 +icinga2api +keyring==10.5.1 +lxml +psutil +pyinstaller +pypiwin32 +pyqt5==5.15.3 +pysocks +python-dateutil +requests +requests-kerberos +setuptools==44.1.1 +wheel From 6f10b1dbb437b9434162215bfd207848a29284db Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Tue, 16 Mar 2021 22:21:44 +0100 Subject: [PATCH 072/884] build-release-devel.yml -> build-release-latest.yml --- .../{build-release-devel.yml => build-release-latest.yml} | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) rename .github/workflows/{build-release-devel.yml => build-release-latest.yml} (98%) diff --git a/.github/workflows/build-release-devel.yml b/.github/workflows/build-release-latest.yml similarity index 98% rename from .github/workflows/build-release-devel.yml rename to .github/workflows/build-release-latest.yml index 13c16826f..9dbe35085 100644 --- a/.github/workflows/build-release-devel.yml +++ b/.github/workflows/build-release-latest.yml @@ -1,4 +1,4 @@ -name: build-release-devel +name: build-release-latest on: push: tags-ignore: 'v*' @@ -88,6 +88,5 @@ jobs: repo_token: "${{ secrets.GITHUB_TOKEN }}" automatic_release_tag: "latest" prerelease: true - title: "Development Build" files: | artifact/* From 923d381edad29d29ef61e2427cb09ab907ba7d3e Mon Sep 17 00:00:00 2001 From: CK Kitisopakul <ckitisopakul@integradev.com.au> Date: Tue, 23 Mar 2021 10:09:29 +1100 Subject: [PATCH 073/884] Add SensuGo --- Nagstamon/QUI/__init__.py | 4 +- Nagstamon/Servers/SensuGo.py | 111 ++++++++++++++++++++++++++++ Nagstamon/Servers/__init__.py | 5 +- Nagstamon/thirdparty/sensugo_api.py | 75 +++++++++++++++++++ 4 files changed, 191 insertions(+), 4 deletions(-) create mode 100644 Nagstamon/Servers/SensuGo.py create mode 100644 Nagstamon/thirdparty/sensugo_api.py diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 1c3f82f5e..022eb0844 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -5740,8 +5740,8 @@ def __init__(self, dialog): # these widgets are shown or hidden depending on server type properties # the servers listed at each widget do need them self.VOLATILE_WIDGETS = { - self.ui.label_monitor_cgi_url: ['Nagios', 'Icinga', 'Thruk', 'Sensu'], - self.ui.input_lineedit_monitor_cgi_url: ['Nagios', 'Icinga', 'Thruk', 'Sensu'], + self.ui.label_monitor_cgi_url: ['Nagios', 'Icinga', 'Thruk', 'Sensu', 'SensuGo'], + self.ui.input_lineedit_monitor_cgi_url: ['Nagios', 'Icinga', 'Thruk', 'Sensu', 'SensuGo'], self.ui.input_checkbox_use_autologin: ['Centreon', 'monitos4x', 'Thruk'], self.ui.input_lineedit_autologin_key: ['Centreon', 'monitos4x', 'Thruk'], self.ui.label_autologin_key: ['Centreon', 'monitos4x', 'Thruk'], diff --git a/Nagstamon/Servers/SensuGo.py b/Nagstamon/Servers/SensuGo.py new file mode 100644 index 000000000..f72b933be --- /dev/null +++ b/Nagstamon/Servers/SensuGo.py @@ -0,0 +1,111 @@ +import sys +import traceback +from Nagstamon.Servers.Generic import GenericServer +from Nagstamon.Objects import (GenericHost, GenericService, Result) +from Nagstamon.Config import conf +from Nagstamon.thirdparty.sensugo_api import SensuGoAPI, SensuGoAPIException +from Nagstamon.Helpers import HumanReadableDurationFromTimestamp +from time import time +from datetime import datetime + +NAMESPACE_SEPARATOR = ' ||| ' +class SensuGoServer(GenericServer): + TYPE = 'SensuGo' + MENU_ACTIONS = ['Acknowledge'] + + _authentication = 'basic' + _api_url = '' + _sensugo_api = None + + def __init__(self, **kwds): + GenericServer.__init__(self, **kwds) + self._api_url = conf.servers[self.get_name()].monitor_cgi_url + self.reset_HTTP() + + def init_HTTP(self): + GenericServer.init_HTTP(self) + self._setup_sensugo_api() + + def reset_HTTP(self): + self._sensugo_api = SensuGoAPI(self._api_url) + + def _setup_sensugo_api(self): + if not self._sensugo_api.has_acquired_token(): + if self.custom_cert_use: + verify = self.custom_cert_ca_file + else: + verify = not self.ignore_cert + + try: + self._sensugo_api.auth(self.username, self.password, verify) + except Exception: + self.Error(sys.exc_info()) + + def _get_status(self): + try: + response_code, events = self._sensugo_api.get_all_events() + self._create_services(events) + except Exception: + result, error = self.Error(sys.exc_info()) + print(traceback.format_exc()) + return Result(result=result, error=error) + return Result() + + def _create_services(self, events): + for event in events: + service = self._parse_event_to_service(event) + self._insert_service_to_hosts(service) + + def _parse_event_to_service(self, event): + service = GenericService() + namespace_host = event['entity']['metadata']['namespace'] + NAMESPACE_SEPARATOR + event['entity']['metadata']['name'] + service.hostid = namespace_host + service.host = namespace_host + service.name = event['check']['metadata']['name'] + service.status = SensuGoAPI.parse_check_status(event['check']['status']) + service.last_check = datetime.fromtimestamp(int(event['timestamp'])).strftime('%Y-%m-%d %H:%M:%S') + service.duration = self._duration_since(event['check']['last_ok']) + service.status_information = event['check']['output'] + service.acknowledged = event['check']['is_silenced'] + service.notifications_disabled = event['check']['is_silenced'] + service.attempt = str(event['check']['occurrences']) + '/1' + service.passiveonly = event['check']['publish'] + service.flapping = False + service.scheduled_downtime = False + return service + + def _insert_service_to_hosts(self, service: GenericService): + service_host = service.get_host_name() + if service_host not in self.new_hosts: + self.new_hosts[service_host] = GenericHost() + self.new_hosts[service_host].name = service_host + self.new_hosts[service_host].site = service.site + + self.new_hosts[service_host].services[service.name] = service + + def _duration_since(self, timestamp): + if (timestamp == 0) or (timestamp > time()): + duration_text = 'n/a' + else: + duration_text = HumanReadableDurationFromTimestamp(timestamp) + return duration_text + + def set_acknowledge(self, info_dict): + namespace = self._extract_namespace(info_dict['host']) + + silenece_args = { + 'metadata': { + 'name': info_dict['service'], + 'namespace': namespace + }, + 'expire': -1, + 'expire_on_resolve': True, + 'creator': info_dict['author'], + 'reason': info_dict['comment'], + 'check': info_dict['service'] + } + self._sensugo_api.create_or_update_silence(silenece_args) + + def _extract_namespace(self, host_column: str): + return host_column.split(NAMESPACE_SEPARATOR)[0] + diff --git a/Nagstamon/Servers/__init__.py b/Nagstamon/Servers/__init__.py index 7d667fb3f..7d8456b54 100644 --- a/Nagstamon/Servers/__init__.py +++ b/Nagstamon/Servers/__init__.py @@ -49,6 +49,7 @@ from Nagstamon.Servers.Monitos4x import Monitos4xServer from Nagstamon.Servers.SnagView3 import SnagViewServer from Nagstamon.Servers.Sensu import SensuServer +from Nagstamon.Servers.SensuGo import SensuGoServer from Nagstamon.Servers.Prometheus import PrometheusServer from Nagstamon.Servers.Alertmanager import AlertmanagerServer @@ -217,8 +218,8 @@ def create_server(server=None): # moved registration process here because of circular dependencies servers_list = [CentreonServer, IcingaServer, IcingaWeb2Server, MultisiteServer, NagiosServer, Op5MonitorServer, OpsviewServer, ThrukServer, ZabbixServer, SensuServer, - LivestatusServer, ZenossServer, Monitos3Server, Monitos4xServer, SnagViewServer, - PrometheusServer, AlertmanagerServer, ZabbixProblemBasedServer] + SensuGoServer, LivestatusServer, ZenossServer, Monitos3Server, Monitos4xServer, + SnagViewServer, PrometheusServer, AlertmanagerServer, ZabbixProblemBasedServer] # we use these servers conditionally if modules are available only if icinga2api_is_available is True: servers_list.append(Icinga2APIServer) diff --git a/Nagstamon/thirdparty/sensugo_api.py b/Nagstamon/thirdparty/sensugo_api.py new file mode 100644 index 000000000..3253ac557 --- /dev/null +++ b/Nagstamon/thirdparty/sensugo_api.py @@ -0,0 +1,75 @@ +import json +import logging +import requests +from requests.auth import HTTPBasicAuth + +logger = logging.getLogger(__name__) + +class SensuGoAPIException(Exception): + pass + +class SensuGoAPI(object): + _base_api_url = '' + _refresh_token = '' + _session = None + + GOOD_RESPONSE_CODES = (200, 201, 202, 204) + + SEVERITY_CHECK_STATUS = { + 0: 'OK', + 1: 'WARNING', + 2: 'CRITICAL', + 3: 'UNKNOWN' + } + + def __init__(self, base_api_url): + self._base_api_url = base_api_url + self._session = requests.Session() + + def auth(self, username, password, verify): + response = self._session.get( + f'{self._base_api_url}/auth', + verify=verify, + auth=HTTPBasicAuth(username, password)) + + self._update_local_tokens(response) + + def _refresh_access_token(self): + response = self._session.post( + f'{self._base_api_url}/auth/token', + json={'refresh_token': self._refresh_token}) + + self._update_local_tokens(response) + + def _update_local_tokens(self, response): + if response.status_code in SensuGoAPI.GOOD_RESPONSE_CODES: + access_token = response.json()['access_token'] + self._session.headers.update({ + 'Authorization': f'Bearer {access_token}'}) + + self._refresh_token = response.json()['refresh_token'] + else: + logger.error(f'Response code: {response.status_code} {response.text}') + raise SensuGoAPIException('API returned bad request') + + def get_all_events(self): + self._refresh_access_token() + response = self._session.get(f'{self._base_api_url}/api/core/v2/events') + return (response.status_code, response.json()) + + def has_acquired_token(self): + return self._refresh_token != '' + + @staticmethod + def parse_check_status(status_code): + status = SensuGoAPI.SEVERITY_CHECK_STATUS[3] + if status_code in SensuGoAPI.SEVERITY_CHECK_STATUS: + status = SensuGoAPI.SEVERITY_CHECK_STATUS[status_code] + return status + + def create_or_update_silence(self, kwargs): + namespace = kwargs['metadata']['namespace'] + check = kwargs['metadata']['name'] + silence_api = f'/api/core/v2/namespaces/{namespace}/silenced/{check}' + self._session.put(self._base_api_url + silence_api, json=kwargs) + From ad3daddbb99ae01d5560e82e6b4a9b021dcf0a3b Mon Sep 17 00:00:00 2001 From: Emanuel Haupt <ehaupt@critical.ch> Date: Tue, 23 Mar 2021 17:50:47 +0100 Subject: [PATCH 074/884] Fix build with Python 3.8 and onward on FreeBSD Obtained from: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=254502 --- setup.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setup.py b/setup.py index 1c72d7a68..1991e8713 100644 --- a/setup.py +++ b/setup.py @@ -36,6 +36,9 @@ if OS not in ['Windows', 'Darwin']: if OS == 'Linux': DIST, DIST_VERSION, DIST_NAME = get_distro() + # platform.dist() returns "('', '', '')" on FreeBSD + elif OS == 'FreeBSD': + DIST, DIST_VERSION, DIST_NAME = ('', '', '') else: DIST, DIST_VERSION, DIST_NAME = platform.dist() NAME = NAME.lower() From 303b986ea5d36513863e67218509f56e489479bd Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Fri, 26 Mar 2021 16:37:27 +0100 Subject: [PATCH 075/884] changed URL --- Nagstamon/Config.py | 4 ++-- build/debian/changelog | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 7b861b361..ebd3b6828 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -124,8 +124,8 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.5-20210316' - WEBSITE = 'https://nagstamon.ifw-dresden.de' + VERSION = '3.5-20210326' + WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2021 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' # dict of servers to offer for downloads if an update is available diff --git a/build/debian/changelog b/build/debian/changelog index 41ed20068..a99eaf68e 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.5-20210316) unstable; urgency=low +nagstamon (3.5-20210326) unstable; urgency=low * New upstream -- Henri Wahl <h.wahl@ifw-dresden.de> Fri, 31 Jul 2020 17:00:00 +0100 From 09fd5f5ec7a0b7f47056d24343105b6d2e06f69c Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Fri, 26 Mar 2021 17:31:29 +0100 Subject: [PATCH 076/884] modified workflow yaml --- .github/workflows/build-release-stable.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index 565d11634..e448f26ef 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -86,6 +86,7 @@ jobs: with: repo_token: "${{ secrets.GITHUB_TOKEN }}" prerelease: false + automatic_release_tag: true draft: true files: | artifact/* From 0a6d87ed5addd74c05a05021c4e7e6ddca98cb7b Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Fri, 26 Mar 2021 17:45:20 +0100 Subject: [PATCH 077/884] modified workflow yaml with version --- .github/workflows/build-release-stable.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index e448f26ef..33accbc3a 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -82,11 +82,13 @@ jobs: - uses: actions/download-artifact@v2 - run: cd artifact && md5sum *agstamon* > md5sums.txt - run: cd artifact && sha256sum *agstamon* > sha256sums.txt + - run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/} + id: release_version - uses: marvinpinto/action-automatic-releases@latest with: repo_token: "${{ secrets.GITHUB_TOKEN }}" + automatic_release_tag: "${{ steps.release_version.outputs.VERSION }}" prerelease: false - automatic_release_tag: true draft: true files: | artifact/* From 5e0c3b7bec1dff88681a5682338aff5a65593f3e Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Fri, 26 Mar 2021 18:13:12 +0100 Subject: [PATCH 078/884] modified stable workflow yaml without version --- .github/workflows/build-release-stable.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index 33accbc3a..565d11634 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -82,12 +82,9 @@ jobs: - uses: actions/download-artifact@v2 - run: cd artifact && md5sum *agstamon* > md5sums.txt - run: cd artifact && sha256sum *agstamon* > sha256sums.txt - - run: echo ::set-output name=VERSION::${GITHUB_REF#refs/tags/} - id: release_version - uses: marvinpinto/action-automatic-releases@latest with: repo_token: "${{ secrets.GITHUB_TOKEN }}" - automatic_release_tag: "${{ steps.release_version.outputs.VERSION }}" prerelease: false draft: true files: | From b8dce0a45fdef69764985e5f12203f5acf14fa0f Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Fri, 26 Mar 2021 18:21:54 +0100 Subject: [PATCH 079/884] nagstamon.de into README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9eb5a77bb..d130038a3 100644 --- a/README.md +++ b/README.md @@ -28,4 +28,4 @@ Successfully tested monitors include: - Prometheus - experimental - Alertmanager - experimental -See https://nagstamon.ifw-dresden.de for further information. +See https://nagstamon.de for further information. From f7d2c7e4fc5de5f7864c0203cefec0986271514b Mon Sep 17 00:00:00 2001 From: Paul Wolneykien <manowar@altlinux.org> Date: Tue, 30 Mar 2021 13:10:15 +0300 Subject: [PATCH 080/884] Fix: Initialize `message` to prevent check uninitialized error Signed-off-by: Paul Wolneykien <manowar@altlinux.org> --- Nagstamon/QUI/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 022eb0844..0d31fdb2d 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -6953,6 +6953,8 @@ def check(self): 'Get it at <a href={1}>{1}</a>.'.format(latest_version, AppInfo.WEBSITE + '/download') else: message = '' + else: + message = '' # check if there is anything to tell if message != '': From 5dba1e15cbfc7c04ff04e92d6b52ad4043cdf364 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Tue, 30 Mar 2021 19:18:00 +0200 Subject: [PATCH 081/884] release 20210330, hoping to become 3.6 --- ChangeLog | 18 ++++++++++++++++++ Nagstamon/Config.py | 2 +- Nagstamon/QUI/__init__.py | 12 ++---------- build/debian/changelog | 19 ++++++++++++++++--- 4 files changed, 37 insertions(+), 14 deletions(-) diff --git a/ChangeLog b/ChangeLog index bd04a3be0..2fc71b4ba 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +nagstamon (3.5-20210330) unstable; urgency=low + * New upstream + - added Prometheus support + - added SensuGo support + - added support for SOCKS5 proxy + - added display of macOS version + - added Thruk autologin + - update for Centreon 20.10 + - fixes for Zabbix + - fix for IcingaWeb2 + - fix for Nagios/Icinga login with invalid credentials + - fixes Thruk login + - fixes context menu bug + - fix for HighDPI + - fix for distro detection + + -- Henri Wahl <h.wahl@ifw-dresden.de> Tue, 30 Mar 2021 17:00:00 +0100 + nagstamon (3.4.1) stable; urgency=low * New upstream - Centreon fix for acknowledgement when using autologin key diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index ebd3b6828..9592ec970 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -124,7 +124,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.5-20210326' + VERSION = '3.5-20210330' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2021 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 0d31fdb2d..7b58891d1 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -6878,11 +6878,6 @@ def show_message(self, message): """ self.version_info_retrieved.emit() - # QMessageBox.information(self.parent, - # 'Nagstamon version check', - # message, - # QMessageBox.Ok) - # attempt to solve https://github.com/HenriWahl/Nagstamon/issues/303 # might be working this time if statuswindow.is_shown: @@ -6920,8 +6915,9 @@ def check(self): # get servers to be used for checking version enabled_servers = get_enabled_servers() - # default latest version is 'unavailable' + # default latest version is 'unavailable' and message empty latest_version = 'unavailable' + message = '' # find at least one server which allows to get version information for server in enabled_servers: @@ -6951,10 +6947,6 @@ def check(self): elif latest_version != AppInfo.VERSION: message = 'The new version <b>Nagstamon {0}</b> is available.<p>' \ 'Get it at <a href={1}>{1}</a>.'.format(latest_version, AppInfo.WEBSITE + '/download') - else: - message = '' - else: - message = '' # check if there is anything to tell if message != '': diff --git a/build/debian/changelog b/build/debian/changelog index a99eaf68e..4f39ffbf5 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,20 @@ -nagstamon (3.5-20210326) unstable; urgency=low +nagstamon (3.5-20210330) unstable; urgency=low * New upstream - - -- Henri Wahl <h.wahl@ifw-dresden.de> Fri, 31 Jul 2020 17:00:00 +0100 + - added Prometheus support + - added SensuGo support + - added support for SOCKS5 proxy + - added display of macOS version + - added Thruk autologin + - update for Centreon 20.10 + - fixes for Zabbix + - fix for IcingaWeb2 + - fix for Nagios/Icinga login with invalid credentials + - fixes Thruk login + - fixes context menu bug + - fix for HighDPI + - fix for distro detection + + -- Henri Wahl <h.wahl@ifw-dresden.de> Tue, 30 Mar 2021 17:00:00 +0100 nagstamon (3.4.1) stable; urgency=low * New upstream From 1ae11a1c254965ce7c9aeb8f9eb45a23f655d7e3 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Fri, 2 Apr 2021 17:34:30 +0200 Subject: [PATCH 082/884] prepare 3.6 --- ChangeLog | 18 ++++++++++++++++++ Nagstamon/Config.py | 6 +++--- Nagstamon/QUI/__init__.py | 9 +++++---- Nagstamon/QUI/dialog_about.py | 2 +- Nagstamon/QUI/dialog_about.ui | 2 +- Nagstamon/Servers/Alertmanager.py | 2 +- Nagstamon/Servers/Generic.py | 2 +- Nagstamon/Servers/Livestatus.py | 2 +- Nagstamon/Servers/Monitos3.py | 2 +- Nagstamon/Servers/Multisite.py | 2 +- Nagstamon/Servers/Opsview.py | 2 +- Nagstamon/Servers/Prometheus.py | 2 +- Nagstamon/Servers/__init__.py | 7 ++++--- Nagstamon/setup.py | 2 +- Nagstamon/thirdparty/__init__.py | 2 +- README.md | 2 +- build/build.py | 2 +- build/debian/changelog | 19 ++++++++++++++++--- nagstacli.py | 2 +- nagstamon.py | 2 +- setup.py | 5 ++++- 21 files changed, 65 insertions(+), 29 deletions(-) diff --git a/ChangeLog b/ChangeLog index bd04a3be0..e26ac09da 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +nagstamon (3.6) unstable; urgency=low + * New upstream + - added Prometheus support + - added SensuGo support + - added support for SOCKS5 proxy + - added display of macOS version + - added Thruk autologin + - update for Centreon 20.10 + - fixes for Zabbix + - fix for IcingaWeb2 + - fix for Nagios/Icinga login with invalid credentials + - fixes Thruk login + - fixes context menu bug + - fix for HighDPI + - fix for distro detection + + -- Henri Wahl <h.wahl@ifw-dresden.de> Fri, 02 Apr 2021 17:00:00 +0100 + nagstamon (3.4.1) stable; urgency=low * New upstream - Centreon fix for acknowledgement when using autologin key diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 7b861b361..bf03d6ade 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -2,7 +2,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2020 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <h.wahl@ifw-dresden.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -124,8 +124,8 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.5-20210316' - WEBSITE = 'https://nagstamon.ifw-dresden.de' + VERSION = '3.6-rc1' + WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2021 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' # dict of servers to offer for downloads if an update is available diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 1c3f82f5e..776f42842 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -1,6 +1,6 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2020 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <h.wahl@ifw-dresden.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -5740,8 +5740,8 @@ def __init__(self, dialog): # these widgets are shown or hidden depending on server type properties # the servers listed at each widget do need them self.VOLATILE_WIDGETS = { - self.ui.label_monitor_cgi_url: ['Nagios', 'Icinga', 'Thruk', 'Sensu'], - self.ui.input_lineedit_monitor_cgi_url: ['Nagios', 'Icinga', 'Thruk', 'Sensu'], + self.ui.label_monitor_cgi_url: ['Nagios', 'Icinga', 'Thruk', 'Sensu', 'SensuGo'], + self.ui.input_lineedit_monitor_cgi_url: ['Nagios', 'Icinga', 'Thruk', 'Sensu', 'SensuGo'], self.ui.input_checkbox_use_autologin: ['Centreon', 'monitos4x', 'Thruk'], self.ui.input_lineedit_autologin_key: ['Centreon', 'monitos4x', 'Thruk'], self.ui.label_autologin_key: ['Centreon', 'monitos4x', 'Thruk'], @@ -6920,8 +6920,9 @@ def check(self): # get servers to be used for checking version enabled_servers = get_enabled_servers() - # default latest version is 'unavailable' + # default latest version is 'unavailable' and message empty latest_version = 'unavailable' + message = '' # find at least one server which allows to get version information for server in enabled_servers: diff --git a/Nagstamon/QUI/dialog_about.py b/Nagstamon/QUI/dialog_about.py index a46ae31bd..c50d53926 100644 --- a/Nagstamon/QUI/dialog_about.py +++ b/Nagstamon/QUI/dialog_about.py @@ -87,7 +87,7 @@ def retranslateUi(self, dialog_about): dialog_about.setWindowTitle(_translate("dialog_about", "About Nagstamon")) self.label_nagstamon.setText(_translate("dialog_about", "Nagstamon x")) self.label_nagstamon_long.setText(_translate("dialog_about", "Nagios status monitor")) - self.label_copyright.setText(_translate("dialog_about", "©2008-2020 Henri Wahl et al.")) + self.label_copyright.setText(_translate("dialog_about", "©2008-2021 Henri Wahl et al.")) self.label_website.setText(_translate("dialog_about", "https://nagstamon.ifw-dresden.de")) self.label_footnote.setText(_translate("dialog_about", "Footnote")) self.tabs.setTabText(self.tabs.indexOf(self.tab_about), _translate("dialog_about", "About")) diff --git a/Nagstamon/QUI/dialog_about.ui b/Nagstamon/QUI/dialog_about.ui index c25096341..f32401974 100644 --- a/Nagstamon/QUI/dialog_about.ui +++ b/Nagstamon/QUI/dialog_about.ui @@ -75,7 +75,7 @@ <item> <widget class="QLabel" name="label_copyright"> <property name="text"> - <string>©2008-2020 Henri Wahl et al.</string> + <string>©2008-2021 Henri Wahl et al.</string> </property> <property name="alignment"> <set>Qt::AlignCenter</set> diff --git a/Nagstamon/Servers/Alertmanager.py b/Nagstamon/Servers/Alertmanager.py index e879836b5..ff8d9cc19 100644 --- a/Nagstamon/Servers/Alertmanager.py +++ b/Nagstamon/Servers/Alertmanager.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2020 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <h.wahl@ifw-dresden.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index 4b2352ca2..1ede59f29 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2020 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <h.wahl@ifw-dresden.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Livestatus.py b/Nagstamon/Servers/Livestatus.py index 988e0846c..c1ee48a15 100644 --- a/Nagstamon/Servers/Livestatus.py +++ b/Nagstamon/Servers/Livestatus.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2020 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <h.wahl@ifw-dresden.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Monitos3.py b/Nagstamon/Servers/Monitos3.py index c384b3d84..4197ff096 100644 --- a/Nagstamon/Servers/Monitos3.py +++ b/Nagstamon/Servers/Monitos3.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2020 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <h.wahl@ifw-dresden.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Multisite.py b/Nagstamon/Servers/Multisite.py index 4e974ea06..ce0863dbd 100644 --- a/Nagstamon/Servers/Multisite.py +++ b/Nagstamon/Servers/Multisite.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2020 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <h.wahl@ifw-dresden.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Opsview.py b/Nagstamon/Servers/Opsview.py index b093c3cb8..550c49415 100644 --- a/Nagstamon/Servers/Opsview.py +++ b/Nagstamon/Servers/Opsview.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2020 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <h.wahl@ifw-dresden.de> et al. # # Based on https://github.com/duncs/Nagstamon by @duncs # diff --git a/Nagstamon/Servers/Prometheus.py b/Nagstamon/Servers/Prometheus.py index 329dff50b..afae53d69 100644 --- a/Nagstamon/Servers/Prometheus.py +++ b/Nagstamon/Servers/Prometheus.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2020 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <h.wahl@ifw-dresden.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/__init__.py b/Nagstamon/Servers/__init__.py index 7d667fb3f..975013d71 100644 --- a/Nagstamon/Servers/__init__.py +++ b/Nagstamon/Servers/__init__.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2020 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <h.wahl@ifw-dresden.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -49,6 +49,7 @@ from Nagstamon.Servers.Monitos4x import Monitos4xServer from Nagstamon.Servers.SnagView3 import SnagViewServer from Nagstamon.Servers.Sensu import SensuServer +from Nagstamon.Servers.SensuGo import SensuGoServer from Nagstamon.Servers.Prometheus import PrometheusServer from Nagstamon.Servers.Alertmanager import AlertmanagerServer @@ -217,8 +218,8 @@ def create_server(server=None): # moved registration process here because of circular dependencies servers_list = [CentreonServer, IcingaServer, IcingaWeb2Server, MultisiteServer, NagiosServer, Op5MonitorServer, OpsviewServer, ThrukServer, ZabbixServer, SensuServer, - LivestatusServer, ZenossServer, Monitos3Server, Monitos4xServer, SnagViewServer, - PrometheusServer, AlertmanagerServer, ZabbixProblemBasedServer] + SensuGoServer, LivestatusServer, ZenossServer, Monitos3Server, Monitos4xServer, + SnagViewServer, PrometheusServer, AlertmanagerServer, ZabbixProblemBasedServer] # we use these servers conditionally if modules are available only if icinga2api_is_available is True: servers_list.append(Icinga2APIServer) diff --git a/Nagstamon/setup.py b/Nagstamon/setup.py index 7fde70fda..5f93922f1 100644 --- a/Nagstamon/setup.py +++ b/Nagstamon/setup.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2020 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <h.wahl@ifw-dresden.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/thirdparty/__init__.py b/Nagstamon/thirdparty/__init__.py index 2113fdd3b..0de46dc69 100644 --- a/Nagstamon/thirdparty/__init__.py +++ b/Nagstamon/thirdparty/__init__.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2020 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <h.wahl@ifw-dresden.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/README.md b/README.md index 9eb5a77bb..d130038a3 100644 --- a/README.md +++ b/README.md @@ -28,4 +28,4 @@ Successfully tested monitors include: - Prometheus - experimental - Alertmanager - experimental -See https://nagstamon.ifw-dresden.de for further information. +See https://nagstamon.de for further information. diff --git a/build/build.py b/build/build.py index 1a9dfe31e..5943a1809 100644 --- a/build/build.py +++ b/build/build.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2020 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <h.wahl@ifw-dresden.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/build/debian/changelog b/build/debian/changelog index 41ed20068..72de4f7b4 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,20 @@ -nagstamon (3.5-20210316) unstable; urgency=low +nagstamon (3.6) unstable; urgency=low * New upstream - - -- Henri Wahl <h.wahl@ifw-dresden.de> Fri, 31 Jul 2020 17:00:00 +0100 + - added Prometheus support + - added SensuGo support + - added support for SOCKS5 proxy + - added display of macOS version + - added Thruk autologin + - update for Centreon 20.10 + - fixes for Zabbix + - fix for IcingaWeb2 + - fix for Nagios/Icinga login with invalid credentials + - fixes Thruk login + - fixes context menu bug + - fix for HighDPI + - fix for distro detection + + -- Henri Wahl <h.wahl@ifw-dresden.de> Fri, 02 Apr 2021 17:00:00 +0100 nagstamon (3.4.1) stable; urgency=low * New upstream diff --git a/nagstacli.py b/nagstacli.py index eb8cf703f..979bf26c9 100755 --- a/nagstacli.py +++ b/nagstacli.py @@ -2,7 +2,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2020 Henri Wahl <h.wahl@ifw-dresden.de> et al. Maik Lüdeke <m.luedeke@ifw-dresden.de> +# Copyright (C) 2008-2021 Henri Wahl <h.wahl@ifw-dresden.de> et al. Maik Lüdeke <m.luedeke@ifw-dresden.de> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/nagstamon.py b/nagstamon.py index 64c7bfd47..2dc849964 100755 --- a/nagstamon.py +++ b/nagstamon.py @@ -2,7 +2,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2020 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <h.wahl@ifw-dresden.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/setup.py b/setup.py index 1c72d7a68..f1d4a84e1 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2020 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <h.wahl@ifw-dresden.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -36,6 +36,9 @@ if OS not in ['Windows', 'Darwin']: if OS == 'Linux': DIST, DIST_VERSION, DIST_NAME = get_distro() + # platform.dist() returns "('', '', '')" on FreeBSD + elif OS == 'FreeBSD': + DIST, DIST_VERSION, DIST_NAME = ('', '', '') else: DIST, DIST_VERSION, DIST_NAME = platform.dist() NAME = NAME.lower() From c713b85c547a4e555ea078b660f139ea802cc0cd Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Fri, 2 Apr 2021 17:41:46 +0200 Subject: [PATCH 083/884] added fedora-32 --- .github/workflows/build-release-latest.yml | 13 ++++++++++- build/docker/Dockerfile-fedora-32 | 25 ++++++++++++++++++++++ build/docker/Dockerfile-fedora-33 | 2 +- 3 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 build/docker/Dockerfile-fedora-32 diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 9dbe35085..a66d8705b 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -18,6 +18,17 @@ jobs: path: build/*.deb retention-days: 1 + fedora-32: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + - uses: actions/upload-artifact@v2 + with: + path: build/*.rpm + retention-days: 1 + fedora-33: runs-on: ubuntu-latest steps: @@ -78,7 +89,7 @@ jobs: upload-release: runs-on: ubuntu-latest - needs: [debian, fedora-33, macos, windows-32, windows-64] + needs: [debian, fedora-32, fedora-33, macos, windows-32, windows-64] steps: - uses: actions/download-artifact@v2 - run: cd artifact && md5sum *agstamon* > md5sums.txt diff --git a/build/docker/Dockerfile-fedora-32 b/build/docker/Dockerfile-fedora-32 new file mode 100644 index 000000000..d45e40f40 --- /dev/null +++ b/build/docker/Dockerfile-fedora-32 @@ -0,0 +1,25 @@ +FROM fedora:33 +LABEL maintainer=henri@nagstamon.de + +RUN dnf -y install desktop-file-utils \ + git \ + python3 \ + python3-beautifulsoup4 \ + python3-crypto \ + python3-cryptography \ + python3-dateutil \ + python3-devel \ + python3-keyring \ + python3-lxml \ + python3-psutil \ + python3-qt5 \ + python3-qt5-devel \ + python3-requests \ + python3-requests-kerberos \ + python3-SecretStorage \ + qt5-qtsvg \ + qt5-qtmultimedia \ + rpm-build + +CMD cd /nagstamon/build && \ + /usr/bin/python3 build.py diff --git a/build/docker/Dockerfile-fedora-33 b/build/docker/Dockerfile-fedora-33 index d45e40f40..9890b4dd2 100644 --- a/build/docker/Dockerfile-fedora-33 +++ b/build/docker/Dockerfile-fedora-33 @@ -1,4 +1,4 @@ -FROM fedora:33 +FROM fedora:32 LABEL maintainer=henri@nagstamon.de RUN dnf -y install desktop-file-utils \ From f27f93df6e3d2f9e95e7f2ba50524b93962a8d89 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Sat, 3 Apr 2021 13:51:59 +0200 Subject: [PATCH 084/884] removed requirements-*.txt --- requirements-linux.txt | 10 ---------- requirements-macos.txt | 10 ---------- requirements-windows.txt | 13 ------------- 3 files changed, 33 deletions(-) delete mode 100644 requirements-linux.txt delete mode 100644 requirements-macos.txt delete mode 100644 requirements-windows.txt diff --git a/requirements-linux.txt b/requirements-linux.txt deleted file mode 100644 index 6ba327a64..000000000 --- a/requirements-linux.txt +++ /dev/null @@ -1,10 +0,0 @@ -beautifulsoup4 -keyring==10.5.1 -lxml -psutil -pyqt5==5.15.2 -pysocks -python-dateutil -requests -requests-kerberos -setuptools==44.1.1 \ No newline at end of file diff --git a/requirements-macos.txt b/requirements-macos.txt deleted file mode 100644 index 3af2108f7..000000000 --- a/requirements-macos.txt +++ /dev/null @@ -1,10 +0,0 @@ -beautifulsoup4 -keyring==10.5.1 -lxml -psutil -pyqt5==5.13.2 -pysocks -python-dateutil -requests -requests-gssapi -setuptools==44.1.1 \ No newline at end of file diff --git a/requirements-windows.txt b/requirements-windows.txt deleted file mode 100644 index 270c2d216..000000000 --- a/requirements-windows.txt +++ /dev/null @@ -1,13 +0,0 @@ -arrow -beautifulsoup4 -icinga2api -keyring==10.5.1 -lxml -psutil -pypiwin32 -pyqt5==5.13.2 -pysocks -python-dateutil -requests -requests-kerberos -setuptools==44.1.1 From b305ba0a1620c2e3af1b569a9e582cfd161f065e Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Sat, 3 Apr 2021 15:23:32 +0200 Subject: [PATCH 085/884] fixed fedora Dockerfiles --- build/docker/Dockerfile-fedora-32 | 2 +- build/docker/Dockerfile-fedora-33 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/docker/Dockerfile-fedora-32 b/build/docker/Dockerfile-fedora-32 index d45e40f40..9890b4dd2 100644 --- a/build/docker/Dockerfile-fedora-32 +++ b/build/docker/Dockerfile-fedora-32 @@ -1,4 +1,4 @@ -FROM fedora:33 +FROM fedora:32 LABEL maintainer=henri@nagstamon.de RUN dnf -y install desktop-file-utils \ diff --git a/build/docker/Dockerfile-fedora-33 b/build/docker/Dockerfile-fedora-33 index 9890b4dd2..d45e40f40 100644 --- a/build/docker/Dockerfile-fedora-33 +++ b/build/docker/Dockerfile-fedora-33 @@ -1,4 +1,4 @@ -FROM fedora:32 +FROM fedora:33 LABEL maintainer=henri@nagstamon.de RUN dnf -y install desktop-file-utils \ From b848d70b08066258721c70a7c52757b6cc870bee Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Sun, 4 Apr 2021 01:06:11 +0200 Subject: [PATCH 086/884] updated nagstamon.de --- CONTRIBUTE.md | 2 +- COPYRIGHT | 2 +- Nagstamon/Config.py | 2 +- Nagstamon/Helpers.py | 2 +- Nagstamon/Objects.py | 2 +- Nagstamon/QUI/__init__.py | 2 +- Nagstamon/QUI/dialog_about.py | 2 +- Nagstamon/QUI/dialog_about.ui | 2 +- Nagstamon/Servers/Alertmanager.py | 2 +- Nagstamon/Servers/Centreon.py | 2 +- Nagstamon/Servers/Generic.py | 2 +- Nagstamon/Servers/Icinga.py | 2 +- Nagstamon/Servers/Icinga2API.py | 2 +- Nagstamon/Servers/IcingaWeb2.py | 2 +- Nagstamon/Servers/Livestatus.py | 2 +- Nagstamon/Servers/Monitos3.py | 2 +- Nagstamon/Servers/Monitos4x.py | 2 +- Nagstamon/Servers/Multisite.py | 2 +- Nagstamon/Servers/Nagios.py | 2 +- Nagstamon/Servers/Opsview.py | 2 +- Nagstamon/Servers/Prometheus.py | 2 +- Nagstamon/Servers/SnagView3.py | 2 +- Nagstamon/Servers/Thruk.py | 2 +- Nagstamon/Servers/__init__.py | 2 +- Nagstamon/Servers/op5Monitor.py | 2 +- Nagstamon/__init__.py | 2 +- Nagstamon/resources/nagstamon.appdata.xml | 16 ++++++++-------- Nagstamon/resources/nagstamon.rst | 6 +++--- Nagstamon/setup.py | 8 ++++---- Nagstamon/thirdparty/__init__.py | 2 +- build/build.py | 2 +- build/debian/control | 2 +- build/redhat/nagstamon.spec | 2 +- nagstacli.py | 2 +- nagstamon.py | 2 +- setup.py | 8 ++++---- 36 files changed, 51 insertions(+), 51 deletions(-) diff --git a/CONTRIBUTE.md b/CONTRIBUTE.md index e889a0ea8..b68fbb21b 100644 --- a/CONTRIBUTE.md +++ b/CONTRIBUTE.md @@ -3,7 +3,7 @@ Contribution Of course every contribution is welcome - without contributors Nagstamon never would have been what it is today. -- Some basic information is already available at https://nagstamon.ifw-dresden.de/docs/requirements/. +- Some basic information is already available at https://nagstamon.de/documentation. - The Python packages required to run Nagstamon are listed in https://github.com/HenriWahl/Nagstamon/blob/master/requirements.txt. diff --git a/COPYRIGHT b/COPYRIGHT index 9348c2c78..37be493f0 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -1 +1 @@ -Copyright (C) 2009 Henri Wahl <h.wahl@ifw-dresden.de> +Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index bf03d6ade..294eb8659 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -2,7 +2,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Helpers.py b/Nagstamon/Helpers.py index 5faface71..ff4656f38 100644 --- a/Nagstamon/Helpers.py +++ b/Nagstamon/Helpers.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2014 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Objects.py b/Nagstamon/Objects.py index 2445086fc..5d0bc970b 100644 --- a/Nagstamon/Objects.py +++ b/Nagstamon/Objects.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2014 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index bad907190..57421d489 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -1,6 +1,6 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/QUI/dialog_about.py b/Nagstamon/QUI/dialog_about.py index c50d53926..4f5db628d 100644 --- a/Nagstamon/QUI/dialog_about.py +++ b/Nagstamon/QUI/dialog_about.py @@ -88,7 +88,7 @@ def retranslateUi(self, dialog_about): self.label_nagstamon.setText(_translate("dialog_about", "Nagstamon x")) self.label_nagstamon_long.setText(_translate("dialog_about", "Nagios status monitor")) self.label_copyright.setText(_translate("dialog_about", "©2008-2021 Henri Wahl et al.")) - self.label_website.setText(_translate("dialog_about", "https://nagstamon.ifw-dresden.de")) + self.label_website.setText(_translate("dialog_about", "https://nagstamon.de")) self.label_footnote.setText(_translate("dialog_about", "Footnote")) self.tabs.setTabText(self.tabs.indexOf(self.tab_about), _translate("dialog_about", "About")) self.tabs.setTabText(self.tabs.indexOf(self.tab_credits), _translate("dialog_about", "Credits")) diff --git a/Nagstamon/QUI/dialog_about.ui b/Nagstamon/QUI/dialog_about.ui index f32401974..9a01dfd36 100644 --- a/Nagstamon/QUI/dialog_about.ui +++ b/Nagstamon/QUI/dialog_about.ui @@ -85,7 +85,7 @@ <item> <widget class="QLabel" name="label_website"> <property name="text"> - <string>https://nagstamon.ifw-dresden.de</string> + <string>https://nagstamon.de</string> </property> <property name="alignment"> <set>Qt::AlignCenter</set> diff --git a/Nagstamon/Servers/Alertmanager.py b/Nagstamon/Servers/Alertmanager.py index ff8d9cc19..a0f4770a1 100644 --- a/Nagstamon/Servers/Alertmanager.py +++ b/Nagstamon/Servers/Alertmanager.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Centreon.py b/Nagstamon/Servers/Centreon.py index e530d1895..d17d3801b 100644 --- a/Nagstamon/Servers/Centreon.py +++ b/Nagstamon/Servers/Centreon.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2014 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index 1ede59f29..41d2bc4f0 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Icinga.py b/Nagstamon/Servers/Icinga.py index 98901cecd..b8f55a01e 100644 --- a/Nagstamon/Servers/Icinga.py +++ b/Nagstamon/Servers/Icinga.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2014 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Icinga2API.py b/Nagstamon/Servers/Icinga2API.py index 629531b78..344a71044 100644 --- a/Nagstamon/Servers/Icinga2API.py +++ b/Nagstamon/Servers/Icinga2API.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2014 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/IcingaWeb2.py b/Nagstamon/Servers/IcingaWeb2.py index 9bdc40188..3f57263b0 100644 --- a/Nagstamon/Servers/IcingaWeb2.py +++ b/Nagstamon/Servers/IcingaWeb2.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2014 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Livestatus.py b/Nagstamon/Servers/Livestatus.py index c1ee48a15..c95548ec6 100644 --- a/Nagstamon/Servers/Livestatus.py +++ b/Nagstamon/Servers/Livestatus.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Monitos3.py b/Nagstamon/Servers/Monitos3.py index 4197ff096..8f8dede9e 100644 --- a/Nagstamon/Servers/Monitos3.py +++ b/Nagstamon/Servers/Monitos3.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Monitos4x.py b/Nagstamon/Servers/Monitos4x.py index 38846239b..33e1f6a91 100644 --- a/Nagstamon/Servers/Monitos4x.py +++ b/Nagstamon/Servers/Monitos4x.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2014 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Multisite.py b/Nagstamon/Servers/Multisite.py index ce0863dbd..2bdc35e83 100644 --- a/Nagstamon/Servers/Multisite.py +++ b/Nagstamon/Servers/Multisite.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Nagios.py b/Nagstamon/Servers/Nagios.py index 9d2ef1499..7ade7a777 100644 --- a/Nagstamon/Servers/Nagios.py +++ b/Nagstamon/Servers/Nagios.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2014 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Opsview.py b/Nagstamon/Servers/Opsview.py index 550c49415..7b2afdc5d 100644 --- a/Nagstamon/Servers/Opsview.py +++ b/Nagstamon/Servers/Opsview.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. # # Based on https://github.com/duncs/Nagstamon by @duncs # diff --git a/Nagstamon/Servers/Prometheus.py b/Nagstamon/Servers/Prometheus.py index afae53d69..1971a85db 100644 --- a/Nagstamon/Servers/Prometheus.py +++ b/Nagstamon/Servers/Prometheus.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/SnagView3.py b/Nagstamon/Servers/SnagView3.py index a8a4ec124..57baba534 100644 --- a/Nagstamon/Servers/SnagView3.py +++ b/Nagstamon/Servers/SnagView3.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2014 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Thruk.py b/Nagstamon/Servers/Thruk.py index 978ec2608..f3401d43d 100644 --- a/Nagstamon/Servers/Thruk.py +++ b/Nagstamon/Servers/Thruk.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2014 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. # Thruk additions copyright by dcec@Github # # This program is free software; you can redistribute it and/or modify diff --git a/Nagstamon/Servers/__init__.py b/Nagstamon/Servers/__init__.py index 975013d71..0309f4c89 100644 --- a/Nagstamon/Servers/__init__.py +++ b/Nagstamon/Servers/__init__.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/op5Monitor.py b/Nagstamon/Servers/op5Monitor.py index 11e47d26f..419c599da 100644 --- a/Nagstamon/Servers/op5Monitor.py +++ b/Nagstamon/Servers/op5Monitor.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2014 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/__init__.py b/Nagstamon/__init__.py index 90e8597bd..e8dc8e5ca 100644 --- a/Nagstamon/__init__.py +++ b/Nagstamon/__init__.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2014 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/resources/nagstamon.appdata.xml b/Nagstamon/resources/nagstamon.appdata.xml index 5e968d820..42d75394e 100644 --- a/Nagstamon/resources/nagstamon.appdata.xml +++ b/Nagstamon/resources/nagstamon.appdata.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright 2016 Henri Wahl <h.wahl@ifw-dresden.de> --> +<!-- Copyright 2016 Henri Wahl <henri@nagstamon.de> --> <application> <id type="desktop">nagstamon.desktop</id> <metadata_license>GFDL-1.3</metadata_license> @@ -32,12 +32,12 @@ </p> </description> <screenshots> - <screenshot type="default" width="624" height="351">https://nagstamon.ifw-dresden.de/files-nagstamon/appdata/nagstamon-appdata-01.png</screenshot> - <screenshot width="624" height="351">https://nagstamon.ifw-dresden.de/files-nagstamon/appdata/nagstamon-appdata-02.png</screenshot> - <screenshot width="624" height="351">https://nagstamon.ifw-dresden.de/files-nagstamon/appdata/nagstamon-appdata-03.png</screenshot> - <screenshot width="624" height="351">https://nagstamon.ifw-dresden.de/files-nagstamon/appdata/nagstamon-appdata-04.png</screenshot> - <screenshot width="624" height="351">https://nagstamon.ifw-dresden.de/files-nagstamon/appdata/nagstamon-appdata-05.png</screenshot> + <screenshot type="default" width="624" height="351">https://nagstamon.de/files-nagstamon/appdata/nagstamon-appdata-01.png</screenshot> + <screenshot width="624" height="351">https://nagstamon.de/files-nagstamon/appdata/nagstamon-appdata-02.png</screenshot> + <screenshot width="624" height="351">https://nagstamon.de/files-nagstamon/appdata/nagstamon-appdata-03.png</screenshot> + <screenshot width="624" height="351">https://nagstamon.de/files-nagstamon/appdata/nagstamon-appdata-04.png</screenshot> + <screenshot width="624" height="351">https://nagstamon.de/files-nagstamon/appdata/nagstamon-appdata-05.png</screenshot> </screenshots> - <url type="homepage">https://nagstamon.ifw-dresden.de</url> - <updatecontact>h.wahl@ifw-dresden.de</updatecontact> + <url type="homepage">https://nagstamon.de</url> + <updatecontact>contact@nagstamon.de</updatecontact> </application> diff --git a/Nagstamon/resources/nagstamon.rst b/Nagstamon/resources/nagstamon.rst index 20fcdc663..f3803f1aa 100644 --- a/Nagstamon/resources/nagstamon.rst +++ b/Nagstamon/resources/nagstamon.rst @@ -6,8 +6,8 @@ Nagstamon Nagios status monitor which takes place in systray or on desktop ---------------------------------------------------------------- -:Author: This manual page has been written by Carl Chenet <chaica@ohmytux.com> and updated by Henri Wahl <h.wahl@ifw-dresden.de> -:Date: 2016-09-05 +:Author: This manual page has been written by Carl Chenet <chaica@ohmytux.com> and updated by Henri Wahl <henri@nagstamon.de> +:Date: 2021-04-04 :Version: 2.0 :Manual section: 1 :Copyright: This manual page is licensed under the GPL-2 license. @@ -24,5 +24,5 @@ The command can optionally take one argument giving the path to an alternate con RESSOURCES ========== -https://nagstamon.ifw-dresden.de +https://nagstamon.de diff --git a/Nagstamon/setup.py b/Nagstamon/setup.py index 5f93922f1..1cc059e26 100644 --- a/Nagstamon/setup.py +++ b/Nagstamon/setup.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -99,9 +99,9 @@ long_description='Nagstamon is a Nagios status monitor which takes place in systray or on desktop (GNOME, KDE, Windows) as floating statusbar to inform you in realtime about the status of your Nagios and derivatives monitored network. It allows to connect to multiple Nagios, Icinga, Opsview, Op5Monitor, Checkmk/Multisite, Centreon and Thruk servers.', classifiers=CLASSIFIERS, author='Henri Wahl', - author_email='h.wahl@ifw-dresden.de', - url='https://nagstamon.ifw-dresden.de', - download_url='https://nagstamon.ifw-dresden.de/download', + author_email='henri@nagstamon.de', + url='https://nagstamon.de', + download_url='https://nagstamon.de/download', scripts=['nagstamon.py'], packages=['Nagstamon', 'Nagstamon.Server', 'Nagstamon.thirdparty'], package_dir={'Nagstamon': 'Nagstamon'}, diff --git a/Nagstamon/thirdparty/__init__.py b/Nagstamon/thirdparty/__init__.py index 0de46dc69..2c44c5cf4 100644 --- a/Nagstamon/thirdparty/__init__.py +++ b/Nagstamon/thirdparty/__init__.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/build/build.py b/build/build.py index 5943a1809..edc0ab8df 100644 --- a/build/build.py +++ b/build/build.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/build/debian/control b/build/debian/control index 0ee67205d..491871f04 100644 --- a/build/debian/control +++ b/build/debian/control @@ -7,7 +7,7 @@ Uploaders: Carl Chenet <chaica@ohmytux.com> Build-Depends: debhelper (>= 9), python3 (>= 3.4), quilt (>= 0.63) Build-Depends-Indep: python-support Standards-Version: 3.9.2 -Homepage: https://nagstamon.ifw-dresden.de +Homepage: https://nagstamon.de Vcs-Git: git://github.com/HenriWahl/Nagstamon.git Vcs-Browser: https://codeload.github.com/HenriWahl/Nagstamon/zip/master diff --git a/build/redhat/nagstamon.spec b/build/redhat/nagstamon.spec index 8fac01e40..5c8a7163a 100644 --- a/build/redhat/nagstamon.spec +++ b/build/redhat/nagstamon.spec @@ -8,7 +8,7 @@ Release: 0.1.%{gitdate}git%{shortcommit}%{?dist} Summary: Nagios status monitor for desktop License: GPLv2+ -URL: https://nagstamon.ifw-dresden.de +URL: https://nagstamon.de Source0: https://github.com/HenriWahl/Nagstamon/archive/%{commit}/nagstamon-%{commit}.tar.gz BuildArch: noarch diff --git a/nagstacli.py b/nagstacli.py index 979bf26c9..cc398156c 100755 --- a/nagstacli.py +++ b/nagstacli.py @@ -2,7 +2,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <h.wahl@ifw-dresden.de> et al. Maik Lüdeke <m.luedeke@ifw-dresden.de> +# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. Maik Lüdeke <m.luedeke@ifw-dresden.de> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/nagstamon.py b/nagstamon.py index 2dc849964..233aee5d8 100755 --- a/nagstamon.py +++ b/nagstamon.py @@ -2,7 +2,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/setup.py b/setup.py index f1d4a84e1..8143612e4 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <h.wahl@ifw-dresden.de> et al. +# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -107,9 +107,9 @@ long_description='Nagstamon is a Nagios status monitor which takes place in systray or on desktop (GNOME, KDE, Windows) as floating statusbar to inform you in realtime about the status of your Nagios and derivatives monitored network. It allows to connect to multiple Nagios, Icinga, Opsview, Op5Monitor, Checkmk/Multisite, Centreon and Thruk servers.', classifiers=CLASSIFIERS, author='Henri Wahl', - author_email='h.wahl@ifw-dresden.de', - url='https://nagstamon.ifw-dresden.de', - download_url='https://nagstamon.ifw-dresden.de/files-nagstamon/stable/', + author_email='henri@nagstamon.de', + url='https://nagstamon.de', + download_url='https://nagstamon.de/download', scripts=[NAGSTAMON_SCRIPT], packages=['Nagstamon', 'Nagstamon.QUI', From 9a55cf61f001c6b2d68e601c5d1460f43dcb3af8 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Sun, 4 Apr 2021 01:07:24 +0200 Subject: [PATCH 087/884] added job repo-fedora --- .github/workflows/build-release-latest.yml | 24 +++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index a66d8705b..a91d612d3 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -87,7 +87,29 @@ jobs: build/dist/*.exe retention-days: 1 - upload-release: + repo-fedora: + runs-on: ubuntu-latest + needs: [fedora-32, fedora-33] + steps: + # get binaries created by other jobs + - uses: actions/download-artifact@v2 + # organize SSH deploy key for nagstamon-repo + - run: mkdir ~/.ssh + - run: echo "${{ secrets.NAGSTAMON_REPO_KEY }}" > ~/.ssh/id_ed25519 + - run: chmod -R go-rwx ~/.ssh + # get and prepare nagstamon-repo + - run: git clone git@github.com:HenriWahl/nagstamon-repo.git + - run: rm -rf ${{ env.repo_dir }}/fedora + - run: mkdir -p ${{ env.repo_dir }}/fedora/latest + # copy *.rpm files into nagstamon-repo + - run: cp -r artifact/*.rpm ${{ env.repo_dir }}/fedora/latest + # create rpm repo via Fedora container + - run: docker run --rm -v $PWD/${{ env.repo_dir }}/fedora/latest:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo" + # commit and push new binaries to nagstamon-repo + - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" + - run: cd ${{ env.repo_dir }} && git add . && git commit -am "new testing repo" && git push + + github-release: runs-on: ubuntu-latest needs: [debian, fedora-32, fedora-33, macos, windows-32, windows-64] steps: From 041904c9c1bef419811f26ac4994c80c3acc3004 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Sun, 4 Apr 2021 01:14:46 +0200 Subject: [PATCH 088/884] added missing repo_dir --- .github/workflows/build-release-latest.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index a91d612d3..ccd01c9c7 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -6,6 +6,7 @@ on: env: python_win_version: 3.9.2 python_win_path: c:/hostedtoolcache/windows/python/ + repo_dir: nagstamon-repo/docs jobs: debian: runs-on: ubuntu-latest From 805047cd92325f2b9a5174e38b8e722ff52409a3 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Sun, 4 Apr 2021 11:52:53 +0200 Subject: [PATCH 089/884] 3.6-rc2 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 294eb8659..4104792f5 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -124,7 +124,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.6-rc1' + VERSION = '3.6-rc2' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2021 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 5473d4e2a..d30bf0ca6 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.6) stable; urgency=low +nagstamon (3.6-rc2) stable; urgency=low * New upstream - added Prometheus support - added SensuGo support From 264d37813f024ca81d6d5b6a69184ed2bfdb25b6 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Mon, 5 Apr 2021 22:35:14 +0200 Subject: [PATCH 090/884] switched repo creation to nagstamon-jekyll --- .github/workflows/build-release-latest.yml | 16 +++++++++------- .github/workflows/build-release-stable.yml | 22 ++++++++++++++++++++++ 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index ccd01c9c7..63ab7409c 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -3,10 +3,12 @@ on: push: tags-ignore: 'v*' branches: '**' + env: python_win_version: 3.9.2 python_win_path: c:/hostedtoolcache/windows/python/ - repo_dir: nagstamon-repo/docs + repo_dir: nagstamon-jekyll/docs/repo + jobs: debian: runs-on: ubuntu-latest @@ -96,19 +98,19 @@ jobs: - uses: actions/download-artifact@v2 # organize SSH deploy key for nagstamon-repo - run: mkdir ~/.ssh - - run: echo "${{ secrets.NAGSTAMON_REPO_KEY }}" > ~/.ssh/id_ed25519 + - run: echo "${{ secrets.NAGSTAMON_REPO_KEY_WEB }}" > ~/.ssh/id_ed25519 - run: chmod -R go-rwx ~/.ssh - # get and prepare nagstamon-repo - - run: git clone git@github.com:HenriWahl/nagstamon-repo.git - - run: rm -rf ${{ env.repo_dir }}/fedora + # get and prepare nagstamon-jekyll + - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git + - run: rm -rf ${{ env.repo_dir }}/fedora/latest - run: mkdir -p ${{ env.repo_dir }}/fedora/latest - # copy *.rpm files into nagstamon-repo + # copy *.rpm files into nagstamon-jekyll - run: cp -r artifact/*.rpm ${{ env.repo_dir }}/fedora/latest # create rpm repo via Fedora container - run: docker run --rm -v $PWD/${{ env.repo_dir }}/fedora/latest:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo" # commit and push new binaries to nagstamon-repo - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - - run: cd ${{ env.repo_dir }} && git add . && git commit -am "new testing repo" && git push + - run: cd ${{ env.repo_dir }} && git add . && git commit -am "new latest repo" && git push github-release: runs-on: ubuntu-latest diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index 565d11634..e9fddc80d 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -2,9 +2,12 @@ name: build-release-stable on: push: tags: 'v*' + env: python_win_version: 3.9.2 python_win_path: c:/hostedtoolcache/windows/python/ + repo_dir: nagstamon-jekyll/docs/repo + jobs: debian: runs-on: ubuntu-latest @@ -75,6 +78,25 @@ jobs: build/dist/*.exe retention-days: 1 + repo-fedora: + runs-on: ubuntu-latest + needs: [fedora-33] + steps: + # get binaries created by other jobs + - uses: actions/download-artifact@v2 + # organize SSH deploy key for nagstamon-repo + - run: mkdir ~/.ssh + - run: echo "${{ secrets.NAGSTAMON_REPO_KEY_WEB }}" > ~/.ssh/id_ed25519 + - run: chmod -R go-rwx ~/.ssh + # get and prepare nagstamon-repo + - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git + - run: rm -rf ${{ env.repo_dir }}/fedora/?? + # copy *.rpm files into nagstamon-jekyll and create rpm repo via Fedora container + - run: for noarch_rpm in artifact/*.noarch.rpm; do version=$(echo $noarch_rpm | python3 -c "file=input(); print(file.split('fedora')[1].split('-')[0])"); mkdir -p mkdir -p ${{ env.repo_dir }}/fedora/$version; cp -r artifact/*.fedora$version-*.rpm ${{ env.repo_dir }}/fedora/$version; docker run --rm -v $PWD/${{ env.repo_dir }}/fedora/$version:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo"; done + # commit and push new binaries to nagstamon-repo + - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" + - run: cd ${{ env.repo_dir }} && git add . && git commit -am "new stable repo" && git push + upload-release: runs-on: ubuntu-latest needs: [debian, fedora-33, macos, windows-32, windows-64] From 22702dbcc515659db0d14d42d38b818d9eb5d2a6 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Mon, 5 Apr 2021 22:47:48 +0200 Subject: [PATCH 091/884] updates Windows Python version to 3.9.4 --- .github/workflows/build-release-latest.yml | 2 +- .github/workflows/build-release-stable.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 63ab7409c..92fcbf487 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -5,7 +5,7 @@ on: branches: '**' env: - python_win_version: 3.9.2 + python_win_version: 3.9.4 python_win_path: c:/hostedtoolcache/windows/python/ repo_dir: nagstamon-jekyll/docs/repo diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index e9fddc80d..9f1308840 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -4,7 +4,7 @@ on: tags: 'v*' env: - python_win_version: 3.9.2 + python_win_version: 3.9.4 python_win_path: c:/hostedtoolcache/windows/python/ repo_dir: nagstamon-jekyll/docs/repo From 8842e95bf5461e34f023a6ae80c775377e7ce0ee Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Mon, 5 Apr 2021 22:52:41 +0200 Subject: [PATCH 092/884] added fedora-34 --- .github/workflows/build-release-latest.yml | 15 +++++++++++-- .github/workflows/build-release-stable.yml | 26 ++++++++++++++++++++-- build/docker/Dockerfile-fedora-34 | 25 +++++++++++++++++++++ 3 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 build/docker/Dockerfile-fedora-34 diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 92fcbf487..d985df7c4 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -43,6 +43,17 @@ jobs: path: build/*.rpm retention-days: 1 + fedora-34: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + - uses: actions/upload-artifact@v2 + with: + path: build/*.rpm + retention-days: 1 + macos: runs-on: macos-latest steps: @@ -92,7 +103,7 @@ jobs: repo-fedora: runs-on: ubuntu-latest - needs: [fedora-32, fedora-33] + needs: [fedora-32, fedora-33, fedora-34] steps: # get binaries created by other jobs - uses: actions/download-artifact@v2 @@ -114,7 +125,7 @@ jobs: github-release: runs-on: ubuntu-latest - needs: [debian, fedora-32, fedora-33, macos, windows-32, windows-64] + needs: [debian, fedora-32, fedora-33, fedora-34, macos, windows-32, windows-64] steps: - uses: actions/download-artifact@v2 - run: cd artifact && md5sum *agstamon* > md5sums.txt diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index 9f1308840..c5e226142 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -20,6 +20,17 @@ jobs: path: build/*.deb retention-days: 1 + fedora-32: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + - uses: actions/upload-artifact@v2 + with: + path: build/*.rpm + retention-days: 1 + fedora-33: runs-on: ubuntu-latest steps: @@ -31,6 +42,17 @@ jobs: path: build/*.rpm retention-days: 1 + fedora-34: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + - uses: actions/upload-artifact@v2 + with: + path: build/*.rpm + retention-days: 1 + macos: runs-on: macos-latest steps: @@ -80,7 +102,7 @@ jobs: repo-fedora: runs-on: ubuntu-latest - needs: [fedora-33] + needs: [fedora-32, fedora-33, fedora-34] steps: # get binaries created by other jobs - uses: actions/download-artifact@v2 @@ -99,7 +121,7 @@ jobs: upload-release: runs-on: ubuntu-latest - needs: [debian, fedora-33, macos, windows-32, windows-64] + needs: [debian, fedora-32, fedora-33, fedora-34, macos, windows-32, windows-64] steps: - uses: actions/download-artifact@v2 - run: cd artifact && md5sum *agstamon* > md5sums.txt diff --git a/build/docker/Dockerfile-fedora-34 b/build/docker/Dockerfile-fedora-34 new file mode 100644 index 000000000..5b28a9647 --- /dev/null +++ b/build/docker/Dockerfile-fedora-34 @@ -0,0 +1,25 @@ +FROM fedora:34 +LABEL maintainer=henri@nagstamon.de + +RUN dnf -y install desktop-file-utils \ + git \ + python3 \ + python3-beautifulsoup4 \ + python3-crypto \ + python3-cryptography \ + python3-dateutil \ + python3-devel \ + python3-keyring \ + python3-lxml \ + python3-psutil \ + python3-qt5 \ + python3-qt5-devel \ + python3-requests \ + python3-requests-kerberos \ + python3-SecretStorage \ + qt5-qtsvg \ + qt5-qtmultimedia \ + rpm-build + +CMD cd /nagstamon/build && \ + /usr/bin/python3 build.py From ed87ea11fbccb94dee98375079226ab2f08165b5 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Mon, 5 Apr 2021 22:57:45 +0200 Subject: [PATCH 093/884] downgrade Windows Python version to 3.9.2 - chocolatey python path seems to have been changed? --- .github/workflows/build-release-latest.yml | 2 +- .github/workflows/build-release-stable.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index d985df7c4..b8355d4d2 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -5,7 +5,7 @@ on: branches: '**' env: - python_win_version: 3.9.4 + python_win_version: 3.9.2 python_win_path: c:/hostedtoolcache/windows/python/ repo_dir: nagstamon-jekyll/docs/repo diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index c5e226142..68fcbeda3 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -4,7 +4,7 @@ on: tags: 'v*' env: - python_win_version: 3.9.4 + python_win_version: 3.9.2 python_win_path: c:/hostedtoolcache/windows/python/ repo_dir: nagstamon-jekyll/docs/repo From 65cacac07ba6621c13373a795f20d04b81232c8c Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Mon, 5 Apr 2021 23:16:04 +0200 Subject: [PATCH 094/884] updates Windows Python version to 3.9.4 - 2nd attempt --- .github/workflows/build-release-latest.yml | 23 ++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index b8355d4d2..4669605e5 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -5,8 +5,7 @@ on: branches: '**' env: - python_win_version: 3.9.2 - python_win_path: c:/hostedtoolcache/windows/python/ + python_win_version: 3.9.4 repo_dir: nagstamon-jekyll/docs/repo jobs: @@ -71,10 +70,12 @@ jobs: runs-on: windows-latest steps: - uses: actions/checkout@v2 - - run: cinst -y --allow-empty-checksums --no-progress --x86 python3 --version=${{ env.python_win_version }} - - run: cinst -y --allow-empty-checksums --no-progress innosetup - - run: ${{ env.python_win_path }}/${{ env.python_win_version }}/x86/python.exe -m pip install --no-warn-script-location -r build/requirements/windows.txt - - run: cd ${{ github.workspace }}/build; ${{ env.python_win_path }}/${{ env.python_win_version }}/x86/python.exe build.py + - uses: actions/setup-python@v2 + with: + python-version: ${{ env.python_win_version }} + architecture: x86 + - run: python -m pip install --no-warn-script-location -r build/requirements/windows.txt + - run: cd ${{ github.workspace }}/build; python build.py env: PYTHONPATH: ${{ github.workspace }} - uses: actions/upload-artifact@v2 @@ -88,10 +89,12 @@ jobs: runs-on: windows-latest steps: - uses: actions/checkout@v2 - - run: cinst -y --allow-empty-checksums --no-progress python3 --version=${{ env.python_win_version }} - - run: cinst -y --allow-empty-checksums --no-progress innosetup - - run: ${{ env.python_win_path }}/${{ env.python_win_version }}/x64/python.exe -m pip install --no-warn-script-location -r build/requirements/windows.txt - - run: cd ${{ github.workspace }}/build; ${{ env.python_win_path }}/${{ env.python_win_version }}/x64/python.exe build.py + - uses: actions/setup-python@v2 + with: + python-version: ${{ env.python_win_version }} + architecture: x64 + - run: python -m pip install --no-warn-script-location -r build/requirements/windows.txt + - run: cd ${{ github.workspace }}/build; python build.py env: PYTHONPATH: ${{ github.workspace }} - uses: actions/upload-artifact@v2 From 11f1c6fabecdfee70db979ca8f3b079983acb340 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Tue, 6 Apr 2021 11:45:20 +0200 Subject: [PATCH 095/884] ready for 3.6 --- ChangeLog | 2 +- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index e26ac09da..70f920e34 100644 --- a/ChangeLog +++ b/ChangeLog @@ -14,7 +14,7 @@ nagstamon (3.6) unstable; urgency=low - fix for HighDPI - fix for distro detection - -- Henri Wahl <h.wahl@ifw-dresden.de> Fri, 02 Apr 2021 17:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Sun, 05 Apr 2021 17:00:00 +0100 nagstamon (3.4.1) stable; urgency=low * New upstream diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 4104792f5..0558ef2cf 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -124,7 +124,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.6-rc2' + VERSION = '3.6' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2021 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index d30bf0ca6..19deab1ac 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.6-rc2) stable; urgency=low +nagstamon (3.6) stable; urgency=low * New upstream - added Prometheus support - added SensuGo support @@ -14,7 +14,7 @@ nagstamon (3.6-rc2) stable; urgency=low - fix for HighDPI - fix for distro detection - -- Henri Wahl <h.wahl@ifw-dresden.de> Fri, 31 Jul 2020 17:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Sun, 05 Apr 2021 17:00:00 +0100 nagstamon (3.4.1) stable; urgency=low * New upstream From f3077d0bbe1ea20bc5a06f26d75b91450c8ac2fd Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Tue, 6 Apr 2021 12:03:12 +0200 Subject: [PATCH 096/884] ready for 3.6 - fixed build-release-stable.yml --- .github/workflows/build-release-stable.yml | 23 ++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index 68fcbeda3..9db4efe08 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -4,8 +4,7 @@ on: tags: 'v*' env: - python_win_version: 3.9.2 - python_win_path: c:/hostedtoolcache/windows/python/ + python_win_version: 3.9.4 repo_dir: nagstamon-jekyll/docs/repo jobs: @@ -70,10 +69,12 @@ jobs: runs-on: windows-latest steps: - uses: actions/checkout@v2 - - run: cinst -y --allow-empty-checksums --no-progress --x86 python3 --version=${{ env.python_win_version }} - - run: cinst -y --allow-empty-checksums --no-progress innosetup - - run: ${{ env.python_win_path }}/${{ env.python_win_version }}/x86/python.exe -m pip install --no-warn-script-location -r build/requirements/windows.txt - - run: cd ${{ github.workspace }}/build; ${{ env.python_win_path }}/${{ env.python_win_version }}/x86/python.exe build.py + - uses: actions/setup-python@v2 + with: + python-version: ${{ env.python_win_version }} + architecture: x86 + - run: python -m pip install --no-warn-script-location -r build/requirements/windows.txt + - run: cd ${{ github.workspace }}/build; python build.py env: PYTHONPATH: ${{ github.workspace }} - uses: actions/upload-artifact@v2 @@ -87,10 +88,12 @@ jobs: runs-on: windows-latest steps: - uses: actions/checkout@v2 - - run: cinst -y --allow-empty-checksums --no-progress python3 --version=${{ env.python_win_version }} - - run: cinst -y --allow-empty-checksums --no-progress innosetup - - run: ${{ env.python_win_path }}/${{ env.python_win_version }}/x64/python.exe -m pip install --no-warn-script-location -r build/requirements/windows.txt - - run: cd ${{ github.workspace }}/build; ${{ env.python_win_path }}/${{ env.python_win_version }}/x64/python.exe build.py + - uses: actions/setup-python@v2 + with: + python-version: ${{ env.python_win_version }} + architecture: x64 + - run: python -m pip install --no-warn-script-location -r build/requirements/windows.txt + - run: cd ${{ github.workspace }}/build; python build.py env: PYTHONPATH: ${{ github.workspace }} - uses: actions/upload-artifact@v2 From 83921c0e8d54a893356a8e5f1398dc7f09da4800 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Tue, 6 Apr 2021 13:17:33 +0200 Subject: [PATCH 097/884] semver.org compatible version 3.6.0 --- ChangeLog | 2 +- Nagstamon/Config.py | 2 +- build/debian/changelog | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 70f920e34..f3927e19e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,4 @@ -nagstamon (3.6) unstable; urgency=low +nagstamon (3.6.0) unstable; urgency=low * New upstream - added Prometheus support - added SensuGo support diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 0558ef2cf..95f51658e 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -124,7 +124,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.6' + VERSION = '3.6.0' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2021 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 19deab1ac..8b609f491 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.6) stable; urgency=low +nagstamon (3.6.0) stable; urgency=low * New upstream - added Prometheus support - added SensuGo support From 6ded049b819e49de4fade3e1df3c86f4bf7daf4d Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Wed, 7 Apr 2021 10:28:02 +0200 Subject: [PATCH 098/884] new latest version --- ChangeLog | 5 +++++ Nagstamon/Config.py | 2 +- build/debian/changelog | 5 +++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index f3927e19e..549ce843b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +nagstamon (3.7-20210407) unstable; urgency=low + * New upstream + + -- Henri Wahl <henri@nagstamon.de> Tue, 07 Apr 2021 19:00:00 +0100 + nagstamon (3.6.0) unstable; urgency=low * New upstream - added Prometheus support diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 95f51658e..1d75e515f 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -124,7 +124,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.6.0' + VERSION = '3.7-20210407' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2021 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 8b609f491..64418363b 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,3 +1,8 @@ +nagstamon (3.7-20210407) unstable; urgency=low + * New upstream + + -- Henri Wahl <henri@nagstamon.de> Tue, 07 Apr 2021 19:00:00 +0100 + nagstamon (3.6.0) stable; urgency=low * New upstream - added Prometheus support From c9f64fb064a9ad75219baf652f2227914f4e005c Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Wed, 7 Apr 2021 10:29:12 +0200 Subject: [PATCH 099/884] added @stearz to CREDITS --- Nagstamon/resources/CREDITS | 1 + 1 file changed, 1 insertion(+) diff --git a/Nagstamon/resources/CREDITS b/Nagstamon/resources/CREDITS index d425d3594..f5f7895f7 100644 --- a/Nagstamon/resources/CREDITS +++ b/Nagstamon/resources/CREDITS @@ -59,6 +59,7 @@ Sandro Tosi,<br> Sidney Harrell <a href=https://github.com/sidharrellr>@sidharrell</a>,<br> Simon Oxwell <a href=https://github.com/soxwellfb>@soxwellfb</a>,<br> Stefano Stella <a href=https://github.com/mprenditore>@mprenditore</a>,<br> +Stephan Schwarz, <a href=https://github.com/stearz>@stearz</a>,<br> Sven Nierlein <a href=https://github.com/sni>@sni</a>,<br> Thomas Gelf <a href=https://github.com/Thomas-Gelf>@Thomas-Gelf</a>,<br> Tobias Scheerbaum,<br> From 47995b7831700cf84c9a90fc57d469d7d5f63e46 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Wed, 7 Apr 2021 22:45:31 +0200 Subject: [PATCH 100/884] added @stearz to CREDITS --- Nagstamon/resources/CREDITS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/resources/CREDITS b/Nagstamon/resources/CREDITS index f5f7895f7..972ac2b26 100644 --- a/Nagstamon/resources/CREDITS +++ b/Nagstamon/resources/CREDITS @@ -51,9 +51,9 @@ Patrick Cernko,<br> Pawel Połewicz,<br> Robin <a href=https://github.com/DeadHunter>@DeadHunter</a>,<br> Robin Sonefors,<br> -<a href=https://github.com/rwha>@rwha</a>, <br> +<a href=https://github.com/rwha>@rwha</a>,<br> Rym Rabehi <a href=https://github.com/RymRabehi>@RymRabehi</a>,<br> -<a href=https://github.com/Sakerdotes>@Sakerdotes</a>, <br> +<a href=https://github.com/Sakerdotes>@Sakerdotes</a>,<br> Salvatore LaMendola <a href=https://github.com/vt0r>@vt0r</a>,<br> Sandro Tosi,<br> Sidney Harrell <a href=https://github.com/sidharrellr>@sidharrell</a>,<br> From 1ba545304b5fce724ea7d88688c78d599e4fcf1b Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Thu, 8 Apr 2021 11:55:38 +0200 Subject: [PATCH 101/884] try to fix https://github.com/HenriWahl/Nagstamon/issues/708 --- ChangeLog | 2 +- Nagstamon/Config.py | 2 +- Nagstamon/Servers/Zabbix.py | 8 ++++---- build/debian/changelog | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ChangeLog b/ChangeLog index 549ce843b..90644addc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,4 @@ -nagstamon (3.7-20210407) unstable; urgency=low +nagstamon (3.7-20210408) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Tue, 07 Apr 2021 19:00:00 +0100 diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 1d75e515f..5c087e298 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -124,7 +124,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.7-20210407' + VERSION = '3.7-20210408' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2021 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/Servers/Zabbix.py b/Nagstamon/Servers/Zabbix.py index 12b4b9142..56980fc5a 100644 --- a/Nagstamon/Servers/Zabbix.py +++ b/Nagstamon/Servers/Zabbix.py @@ -184,19 +184,19 @@ def _get_status(self): # n['status'] = 'UNREACHABLE' # n['status_information'] = 'Host agents in unknown state' # n['duration'] = 'Unknown' - if host['ipmi_available'] == '2': + if host.get('ipmi_available', '0') == '2': n['status'] = 'DOWN' n['status_information'] = host['ipmi_error'] n['duration'] = HumanReadableDurationFromTimestamp(host['ipmi_errors_from']) - if host['snmp_available'] == '2': + if host.get('snmp_available', '0') == '2': n['status'] = 'DOWN' n['status_information'] = host['snmp_error'] n['duration'] = HumanReadableDurationFromTimestamp(host['snmp_errors_from']) - if host['jmx_available'] == '2': + if host.get('jmx_available', '0') == '2': n['status'] = 'DOWN' n['status_information'] = host['jmx_error'] n['duration'] = HumanReadableDurationFromTimestamp(host['jmx_errors_from']) - if host['available'] == '2': + if host.get('available', '0') == '2': n['status'] = 'DOWN' n['status_information'] = host['error'] n['duration'] = HumanReadableDurationFromTimestamp(host['errors_from']) diff --git a/build/debian/changelog b/build/debian/changelog index 64418363b..c20583bbc 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.7-20210407) unstable; urgency=low +nagstamon (3.7-20210408) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Tue, 07 Apr 2021 19:00:00 +0100 From bbf4351b2673a273d7ac45a98e46eb0d28307056 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Thu, 8 Apr 2021 13:20:14 +0200 Subject: [PATCH 102/884] try to fix https://github.com/HenriWahl/Nagstamon/issues/708 for ZabbixProblemBased too --- Nagstamon/Servers/ZabbixProblemBased.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Servers/ZabbixProblemBased.py b/Nagstamon/Servers/ZabbixProblemBased.py index 25898cce1..58eebfa92 100644 --- a/Nagstamon/Servers/ZabbixProblemBased.py +++ b/Nagstamon/Servers/ZabbixProblemBased.py @@ -121,19 +121,19 @@ def _get_status(self): self.new_hosts[host_id].duration = HumanReadableDurationFromTimestamp(trigger[0]['hosts'][0]['errors_from']) #host not available via ipmi - if trigger[0]['hosts'][0]['ipmi_available'] == "2": + if trigger[0]['hosts'][0].get('ipmi_available', '0') == "2": self.new_hosts[host_id].status = "DOWN" self.new_hosts[host_id].status_information = trigger[0]['hosts'][0]['ipmi_error'] self.new_hosts[host_id].duration = HumanReadableDurationFromTimestamp(trigger[0]['hosts'][0]['ipmi_errors_from']) #host not available via jmx - if trigger[0]['hosts'][0]['jmx_available'] == "2": + if trigger[0]['hosts'][0].get('jmx_available', '0') == "2": self.new_hosts[host_id].status = "DOWN" self.new_hosts[host_id].status_information = trigger[0]['hosts'][0]['jmx_error'] self.new_hosts[host_id].duration = HumanReadableDurationFromTimestamp(trigger[0]['hosts'][0]['jmx_errors_from']) #host not available via snmp - if trigger[0]['hosts'][0]['snmp_available'] == "2": + if trigger[0]['hosts'][0].get('snmp_available', '0') == "2": self.new_hosts[host_id].status = "DOWN" self.new_hosts[host_id].status_information = trigger[0]['hosts'][0]['snmp_error'] self.new_hosts[host_id].duration = HumanReadableDurationFromTimestamp(trigger[0]['hosts'][0]['snmp_errors_from']) From 9829a1162183c7c2d4e2f23e92dc3a8184040539 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Thu, 8 Apr 2021 11:55:38 +0200 Subject: [PATCH 103/884] try to fix https://github.com/HenriWahl/Nagstamon/issues/708 --- ChangeLog | 2 +- Nagstamon/Config.py | 2 +- Nagstamon/Servers/Zabbix.py | 8 ++++---- build/debian/changelog | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ChangeLog b/ChangeLog index 549ce843b..90644addc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,4 @@ -nagstamon (3.7-20210407) unstable; urgency=low +nagstamon (3.7-20210408) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Tue, 07 Apr 2021 19:00:00 +0100 diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 1d75e515f..5c087e298 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -124,7 +124,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.7-20210407' + VERSION = '3.7-20210408' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2021 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/Servers/Zabbix.py b/Nagstamon/Servers/Zabbix.py index 12b4b9142..56980fc5a 100644 --- a/Nagstamon/Servers/Zabbix.py +++ b/Nagstamon/Servers/Zabbix.py @@ -184,19 +184,19 @@ def _get_status(self): # n['status'] = 'UNREACHABLE' # n['status_information'] = 'Host agents in unknown state' # n['duration'] = 'Unknown' - if host['ipmi_available'] == '2': + if host.get('ipmi_available', '0') == '2': n['status'] = 'DOWN' n['status_information'] = host['ipmi_error'] n['duration'] = HumanReadableDurationFromTimestamp(host['ipmi_errors_from']) - if host['snmp_available'] == '2': + if host.get('snmp_available', '0') == '2': n['status'] = 'DOWN' n['status_information'] = host['snmp_error'] n['duration'] = HumanReadableDurationFromTimestamp(host['snmp_errors_from']) - if host['jmx_available'] == '2': + if host.get('jmx_available', '0') == '2': n['status'] = 'DOWN' n['status_information'] = host['jmx_error'] n['duration'] = HumanReadableDurationFromTimestamp(host['jmx_errors_from']) - if host['available'] == '2': + if host.get('available', '0') == '2': n['status'] = 'DOWN' n['status_information'] = host['error'] n['duration'] = HumanReadableDurationFromTimestamp(host['errors_from']) diff --git a/build/debian/changelog b/build/debian/changelog index 64418363b..c20583bbc 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.7-20210407) unstable; urgency=low +nagstamon (3.7-20210408) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Tue, 07 Apr 2021 19:00:00 +0100 From 214ab431f8151b831e84bdc91c5e12194f9dc210 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Thu, 8 Apr 2021 13:20:14 +0200 Subject: [PATCH 104/884] try to fix https://github.com/HenriWahl/Nagstamon/issues/708 for ZabbixProblemBased too --- Nagstamon/Servers/ZabbixProblemBased.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Servers/ZabbixProblemBased.py b/Nagstamon/Servers/ZabbixProblemBased.py index 25898cce1..58eebfa92 100644 --- a/Nagstamon/Servers/ZabbixProblemBased.py +++ b/Nagstamon/Servers/ZabbixProblemBased.py @@ -121,19 +121,19 @@ def _get_status(self): self.new_hosts[host_id].duration = HumanReadableDurationFromTimestamp(trigger[0]['hosts'][0]['errors_from']) #host not available via ipmi - if trigger[0]['hosts'][0]['ipmi_available'] == "2": + if trigger[0]['hosts'][0].get('ipmi_available', '0') == "2": self.new_hosts[host_id].status = "DOWN" self.new_hosts[host_id].status_information = trigger[0]['hosts'][0]['ipmi_error'] self.new_hosts[host_id].duration = HumanReadableDurationFromTimestamp(trigger[0]['hosts'][0]['ipmi_errors_from']) #host not available via jmx - if trigger[0]['hosts'][0]['jmx_available'] == "2": + if trigger[0]['hosts'][0].get('jmx_available', '0') == "2": self.new_hosts[host_id].status = "DOWN" self.new_hosts[host_id].status_information = trigger[0]['hosts'][0]['jmx_error'] self.new_hosts[host_id].duration = HumanReadableDurationFromTimestamp(trigger[0]['hosts'][0]['jmx_errors_from']) #host not available via snmp - if trigger[0]['hosts'][0]['snmp_available'] == "2": + if trigger[0]['hosts'][0].get('snmp_available', '0') == "2": self.new_hosts[host_id].status = "DOWN" self.new_hosts[host_id].status_information = trigger[0]['hosts'][0]['snmp_error'] self.new_hosts[host_id].duration = HumanReadableDurationFromTimestamp(trigger[0]['hosts'][0]['snmp_errors_from']) From 0a46d57a1614b793a35b9d4d7f7c12af8a63dec5 Mon Sep 17 00:00:00 2001 From: Stephan Schwarz <stearz@gmx.de> Date: Sat, 10 Apr 2021 23:14:51 +0200 Subject: [PATCH 105/884] Added better debug output as requested in #709 --- Nagstamon/Servers/Alertmanager.py | 39 +++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/Nagstamon/Servers/Alertmanager.py b/Nagstamon/Servers/Alertmanager.py index a0f4770a1..9dcc366f9 100644 --- a/Nagstamon/Servers/Alertmanager.py +++ b/Nagstamon/Servers/Alertmanager.py @@ -25,6 +25,10 @@ # # Release Notes: # +# [1.0.2] - 2021-04-10: +# * added: +# Better debug output +# # [1.0.1] - 2020-11-27: # * added: # Support for hiding suppressed alerts with the scheduled downtime filter @@ -80,10 +84,22 @@ def _get_status(self): """ Get status from Alertmanager Server """ + if conf.debug_mode: + self.Debug(server=self.get_name(),debug="detection config (map_to_status_information): '" + str(self.map_to_status_information) + "'") + self.Debug(server=self.get_name(),debug="detection config (map_to_hostname): '" + str(self.map_to_hostname) + "'") + self.Debug(server=self.get_name(),debug="detection config (map_to_servicename): '" + str(self.map_to_servicename) + "'") + # get all alerts from the API server try: result = self.FetchURL(self.monitor_url + self.API_PATH_ALERTS, giveback="raw") + + if conf.debug_mode: + self.Debug(server=self.get_name(),debug="received status code '" + str(result.status_code) + "' with this content in result.result: \n\ +-----------------------------------------------------------------------------------------------------------------------------\n\ +" + result.result + "\ +-----------------------------------------------------------------------------------------------------------------------------") + data = json.loads(result.result) error = result.error status_code = result.status_code @@ -93,36 +109,44 @@ def _get_status(self): if errors_occured is not False: return(errors_occured) - if conf.debug_mode: - self.Debug(server=self.get_name(), - debug="Fetched JSON: " + pprint.pformat(data)) - for alert in data: if conf.debug_mode: self.Debug( server=self.get_name(), - debug="Processing Alert: " + pprint.pformat(alert) + debug="processing alert with fingerprint '" + alert['fingerprint'] + "':" ) labels = alert.get("labels", {}) # skip alerts with none severity severity = labels.get("severity", "UNKNOWN").upper() + if severity == "NONE": + if conf.debug_mode: + self.Debug(server=self.get_name(),debug="[" + alert['fingerprint'] + "]: detected severity from labels '" + severity + "' -> skipping alert") continue + if conf.debug_mode: + self.Debug(server=self.get_name(),debug="[" + alert['fingerprint'] + "]: detected severity from labels '" + severity + "'") + hostname = "unknown" for host_label in self.map_to_hostname.split(','): if host_label in labels: hostname = labels.get(host_label) break + if conf.debug_mode: + self.Debug(server=self.get_name(),debug="[" + alert['fingerprint'] + "]: detected hostname from labels: '" + hostname + "'") + servicename = "unknown" for service_label in self.map_to_servicename.split(','): if service_label in labels: servicename = labels.get(service_label) break + if conf.debug_mode: + self.Debug(server=self.get_name(),debug="[" + alert['fingerprint'] + "]: detected servicename from labels: '" + servicename + "'") + service = PrometheusService() service.host = str(hostname) service.name = servicename @@ -137,6 +161,11 @@ def _get_status(self): if service.attempt == "suppressed": service.scheduled_downtime = True + if conf.debug_mode: + self.Debug(server=self.get_name(),debug="[" + alert['fingerprint'] + "]: detected status: '" + service.attempt + "' -> interpreting as silenced") + else: + if conf.debug_mode: + self.Debug(server=self.get_name(),debug="[" + alert['fingerprint'] + "]: detected status: '" + service.attempt + "'") service.duration = str(self._get_duration(alert["startsAt"])) From 27d3af2f9faefb681d5d31d0b5d33bb4153a3ffd Mon Sep 17 00:00:00 2001 From: nxmyoz <vda@bitsbeats.com> Date: Tue, 13 Apr 2021 15:48:09 +0200 Subject: [PATCH 106/884] Implement _set_downtime method. --- Nagstamon/Servers/Alertmanager.py | 42 ++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/Nagstamon/Servers/Alertmanager.py b/Nagstamon/Servers/Alertmanager.py index 9dcc366f9..d29abbcd7 100644 --- a/Nagstamon/Servers/Alertmanager.py +++ b/Nagstamon/Servers/Alertmanager.py @@ -43,6 +43,7 @@ import urllib.error import pprint import json +import requests from datetime import datetime, timedelta, timezone import dateutil.parser @@ -70,7 +71,7 @@ class AlertmanagerServer(PrometheusServer): TYPE = 'Alertmanager' # Alertmanager actions are limited to visiting the monitor for now - MENU_ACTIONS = ['Monitor'] + MENU_ACTIONS = ['Monitor', 'Downtime'] BROWSER_URLS = { 'monitor': '$MONITOR$/#/alerts', 'hosts': '$MONITOR$/#/alerts', @@ -79,6 +80,7 @@ class AlertmanagerServer(PrometheusServer): } API_PATH_ALERTS = "/api/v2/alerts" + API_PATH_SILENCES = "/api/v2/silences" def _get_status(self): """ @@ -203,10 +205,38 @@ def open_monitor(self, host, service=''): url = self.monitor_url webbrowser_open(url) - def _set_downtime(self, host, service, author, comment, fixed, start_time, end_time, hours, minutes): - """ - to be implemented in a future release - """ - pass + + # Get local TZ + LOCAL_TIMEZONE = datetime.now(timezone(timedelta(0))).astimezone().tzinfo + + # Convert local dates to UTC + start_time_dt = dateutil.parser.parse(start_time).replace(tzinfo=LOCAL_TIMEZONE).astimezone(timezone.utc).isoformat() + end_time_dt = dateutil.parser.parse(end_time).replace(tzinfo=LOCAL_TIMEZONE).astimezone(timezone.utc).isoformat() + + # API Spec: https://github.com/prometheus/alertmanager/blob/master/api/v2/openapi.yaml + silence_data = { + "matchers": [ + { + "name": "instance", + "value": host, + "isRegex": False, + "isEqual": False + }, + { + "name": "alertname", + "value": service, + "isRegex": False, + "isEqual": False + } + ], + "startsAt": start_time_dt, + "endsAt": end_time_dt, + "createdBy": author, + "comment": comment + } + + post = requests.post(self.monitor_url + self.API_PATH_SILENCES, json=silence_data) + + #silence_id = post.json()["silenceID"] \ No newline at end of file From c0d16e6f1e01a57e1473ed5fbaae1061324618d0 Mon Sep 17 00:00:00 2001 From: nxmyoz <vda@bitsbeats.com> Date: Tue, 13 Apr 2021 16:01:20 +0200 Subject: [PATCH 107/884] Add a newline at the end. --- Nagstamon/Servers/Alertmanager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/Servers/Alertmanager.py b/Nagstamon/Servers/Alertmanager.py index d29abbcd7..0970dc6a8 100644 --- a/Nagstamon/Servers/Alertmanager.py +++ b/Nagstamon/Servers/Alertmanager.py @@ -239,4 +239,4 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, post = requests.post(self.monitor_url + self.API_PATH_SILENCES, json=silence_data) - #silence_id = post.json()["silenceID"] \ No newline at end of file + #silence_id = post.json()["silenceID"] From 37ee146b3268af070849f72cb820b0d22cdb0011 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Wed, 14 Apr 2021 20:48:30 +0200 Subject: [PATCH 108/884] version 3.7-20210414 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 5c087e298..5a2498e8b 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -124,7 +124,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.7-20210408' + VERSION = '3.7-20210414' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2021 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index c20583bbc..36f1c36e0 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.7-20210408) unstable; urgency=low +nagstamon (3.7-20210414) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Tue, 07 Apr 2021 19:00:00 +0100 From c9b281bc557425fc9473e89fd7a4a0ad535f2b42 Mon Sep 17 00:00:00 2001 From: matgn <22295201+matgn@users.noreply.github.com> Date: Mon, 19 Apr 2021 19:23:37 +0200 Subject: [PATCH 109/884] omit port number for hostname --- Nagstamon/Servers/Alertmanager.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Nagstamon/Servers/Alertmanager.py b/Nagstamon/Servers/Alertmanager.py index 0970dc6a8..1e7f1bb27 100644 --- a/Nagstamon/Servers/Alertmanager.py +++ b/Nagstamon/Servers/Alertmanager.py @@ -44,6 +44,7 @@ import pprint import json import requests +import re from datetime import datetime, timedelta, timezone import dateutil.parser @@ -135,6 +136,9 @@ def _get_status(self): for host_label in self.map_to_hostname.split(','): if host_label in labels: hostname = labels.get(host_label) + m = re.match(r"(.*):[0-9]+", hostname) + if m: + hostname = m.group(1) break if conf.debug_mode: From 591358b489bbe262c5017a3d87fd4d8d0cb86c53 Mon Sep 17 00:00:00 2001 From: matgn <22295201+matgn@users.noreply.github.com> Date: Tue, 20 Apr 2021 21:56:44 +0200 Subject: [PATCH 110/884] add filter for alertmanager request url --- Nagstamon/Config.py | 1 + Nagstamon/QUI/__init__.py | 4 +++- Nagstamon/QUI/settings_server.py | 7 +++++++ Nagstamon/QUI/settings_server.ui | 10 ++++++++++ Nagstamon/Servers/Alertmanager.py | 13 +++++++++---- Nagstamon/Servers/__init__.py | 1 + 6 files changed, 31 insertions(+), 5 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 5c087e298..8b1afce29 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -962,6 +962,7 @@ def __init__(self): self.map_to_hostname = "pod_name,namespace,instance" self.map_to_servicename = "alertname" self.map_to_status_information = "message,summary,description" + self.alertmanager_filter = '' class Action(object): """ diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 57421d489..7d75dbb25 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -5762,7 +5762,9 @@ def __init__(self, dialog): self.ui.label_map_to_servicename: ['Prometheus','Alertmanager'], self.ui.input_lineedit_map_to_servicename: ['Prometheus','Alertmanager'], self.ui.label_map_to_status_information: ['Prometheus','Alertmanager'], - self.ui.input_lineedit_map_to_status_information: ['Prometheus','Alertmanager']} + self.ui.input_lineedit_map_to_status_information: ['Prometheus','Alertmanager'], + self.ui.input_lineedit_alertmanager_filter: ['Alertmanager'], + self.ui.label_alertmanager_filter: ['Alertmanager']} # to be used when selecting authentication method Kerberos self.AUTHENTICATION_WIDGETS = [ diff --git a/Nagstamon/QUI/settings_server.py b/Nagstamon/QUI/settings_server.py index 6144a55a5..8cacf79ad 100644 --- a/Nagstamon/QUI/settings_server.py +++ b/Nagstamon/QUI/settings_server.py @@ -325,6 +325,12 @@ def setupUi(self, settings_server): self.input_checkbox_use_description_name_service = QtWidgets.QCheckBox(self.groupbox_options) self.input_checkbox_use_description_name_service.setObjectName("input_checkbox_use_description_name_service") self.gridLayout_3.addWidget(self.input_checkbox_use_description_name_service, 19, 1, 1, 3) + self.label_alertmanager_filter = QtWidgets.QLabel(self.groupbox_options) + self.label_alertmanager_filter.setObjectName("label_alertmanager_filter") + self.gridLayout_3.addWidget(self.label_alertmanager_filter, 12, 1, 1, 1) + self.input_lineedit_alertmanager_filter = QtWidgets.QLineEdit(self.groupbox_options) + self.input_lineedit_alertmanager_filter.setObjectName("input_lineedit_alertmanager_filter") + self.gridLayout_3.addWidget(self.input_lineedit_alertmanager_filter, 12, 2, 1, 2) self.gridLayout.addWidget(self.groupbox_options, 28, 0, 1, 4) self.retranslateUi(settings_server) @@ -395,3 +401,4 @@ def retranslateUi(self, settings_server): self.label_host_filter.setText(_translate("settings_server", "Host filter:")) self.label_timeout.setText(_translate("settings_server", "Timeout:")) self.input_checkbox_use_description_name_service.setText(_translate("settings_server", "Use description as service name")) + self.label_alertmanager_filter.setText(_translate("settings_server", "Filter:")) diff --git a/Nagstamon/QUI/settings_server.ui b/Nagstamon/QUI/settings_server.ui index 32d91d66c..8a6e49896 100644 --- a/Nagstamon/QUI/settings_server.ui +++ b/Nagstamon/QUI/settings_server.ui @@ -640,6 +640,16 @@ </property> </widget> </item> + <item row="12" column="1"> + <widget class="QLabel" name="label_alertmanager_filter"> + <property name="text"> + <string>Filter:</string> + </property> + </widget> + </item> + <item row="12" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_alertmanager_filter"/> + </item> </layout> </widget> </item> diff --git a/Nagstamon/Servers/Alertmanager.py b/Nagstamon/Servers/Alertmanager.py index 1e7f1bb27..45cb949e9 100644 --- a/Nagstamon/Servers/Alertmanager.py +++ b/Nagstamon/Servers/Alertmanager.py @@ -82,6 +82,7 @@ class AlertmanagerServer(PrometheusServer): API_PATH_ALERTS = "/api/v2/alerts" API_PATH_SILENCES = "/api/v2/silences" + API_FILTERS = '?filter=' def _get_status(self): """ @@ -91,12 +92,16 @@ def _get_status(self): self.Debug(server=self.get_name(),debug="detection config (map_to_status_information): '" + str(self.map_to_status_information) + "'") self.Debug(server=self.get_name(),debug="detection config (map_to_hostname): '" + str(self.map_to_hostname) + "'") self.Debug(server=self.get_name(),debug="detection config (map_to_servicename): '" + str(self.map_to_servicename) + "'") + self.Debug(server=self.get_name(),debug="detection config (alertmanager_filter): '" + str(self.alertmanager_filter) + "'") # get all alerts from the API server try: - result = self.FetchURL(self.monitor_url + self.API_PATH_ALERTS, - giveback="raw") - + if self.alertmanager_filter != '': + result = self.FetchURL(self.monitor_url + self.API_PATH_ALERTS + self.API_FILTERS + self.alertmanager_filter, + giveback="raw") + else: + result = self.FetchURL(self.monitor_url + self.API_PATH_ALERTS, + giveback="raw") if conf.debug_mode: self.Debug(server=self.get_name(),debug="received status code '" + str(result.status_code) + "' with this content in result.result: \n\ -----------------------------------------------------------------------------------------------------------------------------\n\ @@ -172,7 +177,7 @@ def _get_status(self): else: if conf.debug_mode: self.Debug(server=self.get_name(),debug="[" + alert['fingerprint'] + "]: detected status: '" + service.attempt + "'") - + service.duration = str(self._get_duration(alert["startsAt"])) # Alertmanager specific extensions diff --git a/Nagstamon/Servers/__init__.py b/Nagstamon/Servers/__init__.py index 0309f4c89..c48816873 100644 --- a/Nagstamon/Servers/__init__.py +++ b/Nagstamon/Servers/__init__.py @@ -196,6 +196,7 @@ def create_server(server=None): new_server.use_description_name_service = server.use_description_name_service # Prometheus & Alertmanager + new_server.alertmanager_filter = server.alertmanager_filter new_server.map_to_hostname = server.map_to_hostname new_server.map_to_servicename = server.map_to_servicename new_server.map_to_status_information = server.map_to_status_information From f5362b3ed48d9713ea880b33125eb77e369527a0 Mon Sep 17 00:00:00 2001 From: Kevin Konrad <kevin.konrad@unicepta.com> Date: Thu, 29 Apr 2021 23:55:28 +0200 Subject: [PATCH 111/884] add acknowledging Alertmanager-alerts to Nagstamon - add menu option to acknowledge alerts - allow and enforce setting the expiry time (this parameter is required for Alertmanager) - allow setting the default expiry time in the settings - enable filtering acknowledged Alertmanager-alerts --- Nagstamon/QUI/__init__.py | 30 +++++++++------- Nagstamon/QUI/dialog_acknowledge.py | 9 +++-- Nagstamon/QUI/dialog_acknowledge.ui | 3 ++ Nagstamon/Servers/Alertmanager.py | 54 +++++++++++++++++++++++++++-- Nagstamon/Servers/Generic.py | 2 +- Nagstamon/Servers/Prometheus.py | 1 + 6 files changed, 80 insertions(+), 19 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 57421d489..92519a5d0 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -4571,7 +4571,7 @@ def __init__(self): # check if special widgets have to be shown self.server.edited.connect(self.settings.toggle_zabbix_widgets) self.server.edited.connect(self.settings.toggle_op5monitor_widgets) - self.server.edited.connect(self.settings.toggle_icingaweb2_widgets) + self.server.edited.connect(self.settings.toggle_expire_time_widgets) class Dialog(QObject): @@ -4952,7 +4952,7 @@ def __init__(self, dialog): self.ui.input_checkbox_re_groups_reverse] # ...and another... - self.ICINGAWEB2_WIDGETS = [self.ui.input_checkbox_defaults_acknowledge_expire, + self.EXPIRE_TIME_WIDGETS = [self.ui.input_checkbox_defaults_acknowledge_expire, self.ui.label_expire_in, self.ui.label_expire_in_hours, self.ui.label_expire_in_minutes, @@ -5038,7 +5038,7 @@ def show(self, tab=0): # hide them and thus be able to fix size if no extra Zabbix/Op5Monitor/IcingaWeb2 widgets are shown self.toggle_zabbix_widgets() self.toggle_op5monitor_widgets() - self.toggle_icingaweb2_widgets() + self.toggle_expire_time_widgets() # small workaround for timestamp trick to avoid flickering # if the 'Settings' button was clicked too fast the timestamp difference @@ -5672,21 +5672,21 @@ def toggle_op5monitor_widgets(self): widget.hide() @pyqtSlot() - def toggle_icingaweb2_widgets(self): + def toggle_expire_time_widgets(self): """ - Depending on the existence of an enabled IcingaWeb2 monitor the IcingaWeb2 widgets are shown or hidden + Depending on the existence of an enabled IcingaWeb2 or Alertmanager monitor the expire_time widgets are shown or hidden """ - use_icingaweb2 = False + use_expire_time = False for server in servers.values(): if server.enabled: - if server.type == 'IcingaWeb2': - use_icingaweb2 = True + if server.type in ['IcingaWeb2', 'Alertmanager']: + use_expire_time = True break - if use_icingaweb2: - for widget in self.ICINGAWEB2_WIDGETS: + if use_expire_time: + for widget in self.EXPIRE_TIME_WIDGETS: widget.show() else: - for widget in self.ICINGAWEB2_WIDGETS: + for widget in self.EXPIRE_TIME_WIDGETS: widget.hide() @pyqtSlot() @@ -6277,9 +6277,11 @@ def __init__(self, dialog): self.VOLATILE_WIDGETS = { self.ui.input_checkbox_use_expire_time: ['IcingaWeb2'], - self.ui.input_datetime_expire_time: ['IcingaWeb2'] + self.ui.input_datetime_expire_time: ['IcingaWeb2', 'Alertmanager'] } + self.FORCE_DATETIME_EXPIRE_TIME = ['Alertmanager'] + def initialize(self, server=None, host=[], service=[]): # store server, host and service to be used for OK button evaluation @@ -6330,6 +6332,8 @@ def initialize(self, server=None, host=[], service=[]): self.toggle_toggles() else: widget.hide() + if self.server.TYPE in self.FORCE_DATETIME_EXPIRE_TIME: + self.ui.input_datetime_expire_time.show() # Adjust to current size if items are hidden in menu # Otherwise it will get confused and chop off text @@ -6351,7 +6355,7 @@ def ok(self): if s.host in self.host_list: all_services.append(s.name) - if self.ui.input_checkbox_use_expire_time.isChecked(): + if self.ui.input_checkbox_use_expire_time.isChecked() or self.server.TYPE in self.FORCE_DATETIME_EXPIRE_TIME: # Format used in UI # 2019-11-01T18:17:39 expire_datetime = self.ui.input_datetime_expire_time.dateTime().toString("yyyy-MM-ddTHH:mm:ss") diff --git a/Nagstamon/QUI/dialog_acknowledge.py b/Nagstamon/QUI/dialog_acknowledge.py index f719cca95..39816fc11 100644 --- a/Nagstamon/QUI/dialog_acknowledge.py +++ b/Nagstamon/QUI/dialog_acknowledge.py @@ -2,12 +2,15 @@ # Form implementation generated from reading ui file 'dialog_acknowledge.ui' # -# Created by: PyQt5 UI code generator 5.10.1 +# Created by: PyQt5 UI code generator 5.15.4 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. + from PyQt5 import QtCore, QtGui, QtWidgets + class Ui_dialog_acknowledge(object): def setupUi(self, dialog_acknowledge): dialog_acknowledge.setObjectName("dialog_acknowledge") @@ -108,5 +111,5 @@ def retranslateUi(self, dialog_acknowledge): self.input_checkbox_persistent_comment.setText(_translate("dialog_acknowledge", "Persistent comment")) self.input_checkbox_acknowledge_all_services.setText(_translate("dialog_acknowledge", "Acknowledge all services on host")) self.input_checkbox_use_expire_time.setText(_translate("dialog_acknowledge", "Expire acknowledgement")) + self.input_datetime_expire_time.setToolTip(_translate("dialog_acknowledge", "Expiry time")) self.input_label_description.setText(_translate("dialog_acknowledge", "description - set by QUI.py")) - diff --git a/Nagstamon/QUI/dialog_acknowledge.ui b/Nagstamon/QUI/dialog_acknowledge.ui index 8eb32e4e7..e39226667 100644 --- a/Nagstamon/QUI/dialog_acknowledge.ui +++ b/Nagstamon/QUI/dialog_acknowledge.ui @@ -124,6 +124,9 @@ <property name="enabled"> <bool>true</bool> </property> + <property name="toolTip"> + <string>Expiry time</string> + </property> <property name="buttonSymbols"> <enum>QAbstractSpinBox::UpDownArrows</enum> </property> diff --git a/Nagstamon/Servers/Alertmanager.py b/Nagstamon/Servers/Alertmanager.py index 1e7f1bb27..0af0f9fd1 100644 --- a/Nagstamon/Servers/Alertmanager.py +++ b/Nagstamon/Servers/Alertmanager.py @@ -45,7 +45,9 @@ import json import requests import re +import time +from collections import OrderedDict from datetime import datetime, timedelta, timezone import dateutil.parser @@ -57,7 +59,6 @@ from Nagstamon.Servers.Prometheus import PrometheusServer,PrometheusService from Nagstamon.Helpers import webbrowser_open - class AlertmanagerService(PrometheusService): """ add Alertmanager specific service property to generic service class @@ -72,7 +73,7 @@ class AlertmanagerServer(PrometheusServer): TYPE = 'Alertmanager' # Alertmanager actions are limited to visiting the monitor for now - MENU_ACTIONS = ['Monitor', 'Downtime'] + MENU_ACTIONS = ['Monitor', 'Downtime', 'Acknowledge'] BROWSER_URLS = { 'monitor': '$MONITOR$/#/alerts', 'hosts': '$MONITOR$/#/alerts', @@ -120,6 +121,7 @@ def _get_status(self): ) labels = alert.get("labels", {}) + state = alert.get("status", {"state": "active"})["state"] # skip alerts with none severity severity = labels.get("severity", "UNKNOWN").upper() @@ -158,6 +160,8 @@ def _get_status(self): service.name = servicename service.server = self.name service.status = severity + service.labels = labels + service.acknowledged = state == "suppressed" service.last_check = str(self._get_duration(alert["updatedAt"])) if "status" in alert: @@ -244,3 +248,49 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, post = requests.post(self.monitor_url + self.API_PATH_SILENCES, json=silence_data) #silence_id = post.json()["silenceID"] + + + # Overwrite function from generic server to add expire_time value + def set_acknowledge(self, info_dict): + ''' + different monitors might have different implementations of _set_acknowledge + ''' + if info_dict['acknowledge_all_services'] is True: + all_services = info_dict['all_services'] + else: + all_services = [] + + # Make sure expire_time is set + #if not info_dict['expire_time']: + # info_dict['expire_time'] = None + + self._set_acknowledge(info_dict['host'], + info_dict['service'], + info_dict['author'], + info_dict['comment'], + info_dict['sticky'], + info_dict['notify'], + info_dict['persistent'], + all_services, + info_dict['expire_time']) + + + def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=[], expire_time=None): + alert = self.hosts[host].services[service] + + cgi_data = {} + cgi_data["matchers"] = [] + for name, value in alert.labels.items(): + cgi_data["matchers"].append({ + "name": name, + "value": value, + "isRegex": False + }) + cgi_data["startsAt"] = datetime.utcfromtimestamp(time.time()).isoformat() + cgi_data["endsAt"] = expire_time or cgi_data["startAt"] + cgi_data["comment"] = comment or "Nagstamon silence" + cgi_data["createdBy"] = author or "Nagstamon" + cgi_data = json.dumps(cgi_data) + + result = self.FetchURL(self.monitor_url + self.API_PATH_SILENCES, giveback="raw", cgi_data=cgi_data) + return result diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index 41d2bc4f0..072780370 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -1432,7 +1432,7 @@ def FetchURL(self, url, giveback='obj', cgi_data=None, no_auth=False, multipart= cgi_data_log = cgi_data self.Debug(server=self.get_name(), debug='FetchURL: ' + url + ' CGI Data: ' + str(cgi_data_log)) - if OS == OS_DARWIN and not self.cacert_path.is_file(): + if OS == OS_DARWIN and self.cacert_path and not self.cacert_path.is_file(): # pyinstaller temp folder seems to be emptied completely after a while # so the directories containing the resources have to be recreated too self.cacert_path.parent.mkdir(exist_ok=True) diff --git a/Nagstamon/Servers/Prometheus.py b/Nagstamon/Servers/Prometheus.py index 1971a85db..f68863967 100644 --- a/Nagstamon/Servers/Prometheus.py +++ b/Nagstamon/Servers/Prometheus.py @@ -68,6 +68,7 @@ class PrometheusService(GenericService): add Prometheus specific service property to generic service class """ service_object_id = "" + labels = {} class PrometheusServer(GenericServer): From 306ef301b9d51850947f774d818c30391024266f Mon Sep 17 00:00:00 2001 From: Kevin Konrad <kevin.konrad@unicepta.com> Date: Mon, 3 May 2021 12:16:07 +0200 Subject: [PATCH 112/884] hide UI elements not used by Alertmanager, fix time conversion bug --- Nagstamon/QUI/__init__.py | 27 ++++++++++++++++++++++++++- Nagstamon/Servers/Alertmanager.py | 18 ++++++++++++------ 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 92519a5d0..13badc402 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -6275,9 +6275,34 @@ def __init__(self, dialog): self.ui.input_checkbox_use_expire_time: [self.ui.input_datetime_expire_time] } + NOT_PROMETHEUS_OR_ALERTMANAGER = [ + 'Centreon', + 'Generic', + 'Icinga', + 'Icinga2API', + 'IcingaWeb2', + 'Livestatus', + 'Monitos3', + 'Monitos4x', + 'Multisite', + 'Nagios', + 'Opsview', + 'Sensu', + 'SensuGo', + 'SnagView3', + 'Thruk', + 'Zabbix', + 'ZabbixProblemBased', + 'Zenoss' + ] + self.VOLATILE_WIDGETS = { self.ui.input_checkbox_use_expire_time: ['IcingaWeb2'], - self.ui.input_datetime_expire_time: ['IcingaWeb2', 'Alertmanager'] + self.ui.input_datetime_expire_time: ['IcingaWeb2', 'Alertmanager'], + self.ui.input_checkbox_sticky_acknowledgement: NOT_PROMETHEUS_OR_ALERTMANAGER, + self.ui.input_checkbox_send_notification: NOT_PROMETHEUS_OR_ALERTMANAGER, + self.ui.input_checkbox_persistent_comment: NOT_PROMETHEUS_OR_ALERTMANAGER, + self.ui.input_checkbox_acknowledge_all_services: NOT_PROMETHEUS_OR_ALERTMANAGER } self.FORCE_DATETIME_EXPIRE_TIME = ['Alertmanager'] diff --git a/Nagstamon/Servers/Alertmanager.py b/Nagstamon/Servers/Alertmanager.py index 0af0f9fd1..4a8aa8081 100644 --- a/Nagstamon/Servers/Alertmanager.py +++ b/Nagstamon/Servers/Alertmanager.py @@ -59,6 +59,7 @@ from Nagstamon.Servers.Prometheus import PrometheusServer,PrometheusService from Nagstamon.Helpers import webbrowser_open + class AlertmanagerService(PrometheusService): """ add Alertmanager specific service property to generic service class @@ -84,6 +85,13 @@ class AlertmanagerServer(PrometheusServer): API_PATH_ALERTS = "/api/v2/alerts" API_PATH_SILENCES = "/api/v2/silences" + @staticmethod + def timestring_to_utc(timestring): + local_time = datetime.now(timezone(timedelta(0))).astimezone().tzinfo + parsed_time = dateutil.parser.parse(timestring) + utc_time = parsed_time.replace(tzinfo=local_time).astimezone(timezone.utc) + return utc_time.isoformat() + def _get_status(self): """ Get status from Alertmanager Server @@ -216,12 +224,9 @@ def open_monitor(self, host, service=''): def _set_downtime(self, host, service, author, comment, fixed, start_time, end_time, hours, minutes): - # Get local TZ - LOCAL_TIMEZONE = datetime.now(timezone(timedelta(0))).astimezone().tzinfo - # Convert local dates to UTC - start_time_dt = dateutil.parser.parse(start_time).replace(tzinfo=LOCAL_TIMEZONE).astimezone(timezone.utc).isoformat() - end_time_dt = dateutil.parser.parse(end_time).replace(tzinfo=LOCAL_TIMEZONE).astimezone(timezone.utc).isoformat() + start_time_dt = self.timestring_to_utc(start_time) + end_time_dt = self.timestring_to_utc(end_time) # API Spec: https://github.com/prometheus/alertmanager/blob/master/api/v2/openapi.yaml silence_data = { @@ -277,6 +282,7 @@ def set_acknowledge(self, info_dict): def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=[], expire_time=None): alert = self.hosts[host].services[service] + endsAt = self.timestring_to_utc(expire_time) cgi_data = {} cgi_data["matchers"] = [] @@ -287,7 +293,7 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi "isRegex": False }) cgi_data["startsAt"] = datetime.utcfromtimestamp(time.time()).isoformat() - cgi_data["endsAt"] = expire_time or cgi_data["startAt"] + cgi_data["endsAt"] = endsAt or cgi_data["startAt"] cgi_data["comment"] = comment or "Nagstamon silence" cgi_data["createdBy"] = author or "Nagstamon" cgi_data = json.dumps(cgi_data) From b3cd8ad601a0a7f6b3993ee55b327d0f3d63db18 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Mon, 3 May 2021 22:01:29 +0200 Subject: [PATCH 113/884] version 20210503 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index af03d5241..0d90f6f9d 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -124,7 +124,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.7-20210414' + VERSION = '3.7-20210503' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2021 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 36f1c36e0..4cfbdcec3 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.7-20210414) unstable; urgency=low +nagstamon (3.7-20210503) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Tue, 07 Apr 2021 19:00:00 +0100 From 8fa7bb0f88a61d4e2a465caefd22be3b6cece913 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Tue, 11 May 2021 21:07:52 +0200 Subject: [PATCH 114/884] version 20210511 - try to fix #726 --- Nagstamon/Config.py | 2 +- Nagstamon/Servers/IcingaWeb2.py | 2 +- build/debian/changelog | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 0d90f6f9d..0baf78cbc 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -124,7 +124,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.7-20210503' + VERSION = '3.7-20210511' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2021 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/Servers/IcingaWeb2.py b/Nagstamon/Servers/IcingaWeb2.py index 0e3493e78..5fa10512f 100644 --- a/Nagstamon/Servers/IcingaWeb2.py +++ b/Nagstamon/Servers/IcingaWeb2.py @@ -92,7 +92,7 @@ def init_HTTP(self): """ GenericServer.init_HTTP(self) - if not 'Referer' in self.session.headers: + if self.session and not 'Referer' in self.session.headers: self.session.headers['Referer'] = self.monitor_cgi_url + '/icingaweb2/monitoring' # normally cookie out will be used diff --git a/build/debian/changelog b/build/debian/changelog index 4cfbdcec3..da8d2d5ca 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.7-20210503) unstable; urgency=low +nagstamon (3.7-202105011) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Tue, 07 Apr 2021 19:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Tue, 11 May 2021 19:00:00 +0100 nagstamon (3.6.0) stable; urgency=low * New upstream From 3bf3aba304702842d35d7555e8200a072736d9f8 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Wed, 12 May 2021 16:58:02 +0200 Subject: [PATCH 115/884] version 20210512 - fix for secretstorage --- Nagstamon/Config.py | 4 ++-- build/debian/changelog | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 0baf78cbc..1313d50ce 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -124,7 +124,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.7-20210511' + VERSION = '3.7-20210512' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2021 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' @@ -709,7 +709,7 @@ def KeyringAvailable(self): # import secretstorage module as dependency of keyring - # if not available keyring won't work import secretstorage - if ("SecretService") in dir(keyring.backends) and not (keyring.get_keyring() is None): + if keyring.get_keyring(): return True else: return False diff --git a/build/debian/changelog b/build/debian/changelog index da8d2d5ca..0cd39b47f 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.7-202105011) unstable; urgency=low +nagstamon (3.7-202105012) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Tue, 11 May 2021 19:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Wed, 12 May 2021 19:00:00 +0100 nagstamon (3.6.0) stable; urgency=low * New upstream From 0d0404b6ca0adbb9aa0c788a54c7925b5e4e963f Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Thu, 13 May 2021 00:07:49 +0200 Subject: [PATCH 116/884] try to support newer keyring and PyQt 5.15.4 --- build/requirements/linux.txt | 4 ++-- build/requirements/macos.txt | 4 ++-- build/requirements/windows.txt | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build/requirements/linux.txt b/build/requirements/linux.txt index 6ba327a64..81ace9e9e 100644 --- a/build/requirements/linux.txt +++ b/build/requirements/linux.txt @@ -1,8 +1,8 @@ beautifulsoup4 -keyring==10.5.1 +keyring lxml psutil -pyqt5==5.15.2 +pyqt5==5.15.4 pysocks python-dateutil requests diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index f47eed3c6..24d9d15e7 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -1,9 +1,9 @@ beautifulsoup4 -keyring==10.5.1 +keyring lxml psutil pyinstaller -pyqt5==5.15.3 +pyqt5==5.15.4 pysocks python-dateutil requests diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index a9e4adc28..f4ef0f973 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -1,12 +1,12 @@ arrow beautifulsoup4 icinga2api -keyring==10.5.1 +keyring lxml psutil pyinstaller pypiwin32 -pyqt5==5.15.3 +pyqt5==5.15.4 pysocks python-dateutil requests From 6419c1f5eefe2f63435390e068925cb7eea96932 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Thu, 13 May 2021 00:11:45 +0200 Subject: [PATCH 117/884] set system keyring true default --- Nagstamon/Config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 1313d50ce..f6dc60116 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -305,7 +305,7 @@ def __init__(self): # use_system_keyring is checked and defined some lines later after config file was read self.keyring_available = False # setting for keyring usage - self.use_system_keyring = False + self.use_system_keyring = True # Special FX # Centreon From 52ab387dc0a102015ac1551b941580d924a4449d Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Thu, 13 May 2021 00:17:51 +0200 Subject: [PATCH 118/884] set system keyring true default part II --- Nagstamon/Config.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index f6dc60116..9e6d07cfa 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -304,8 +304,11 @@ def __init__(self): # internal flag to determine if keyring is available at all - defaults to False # use_system_keyring is checked and defined some lines later after config file was read self.keyring_available = False - # setting for keyring usage - self.use_system_keyring = True + # setting for keyring usage - might cause trouble on Linux so disable it there as default to avoid crash at start + if OS in OS_NON_LINUX: + self.use_system_keyring = True + else: + self.use_system_keyring = False # Special FX # Centreon From d883c18f0d9909bb9843905875d3d13d40594595 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Thu, 13 May 2021 00:44:32 +0200 Subject: [PATCH 119/884] set system keyring true default part III - older Qt5 for macOS --- build/requirements/macos.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index 24d9d15e7..b97cc3ca3 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -3,7 +3,7 @@ keyring lxml psutil pyinstaller -pyqt5==5.15.4 +pyqt5==5.15.3 pysocks python-dateutil requests From 845073646a85defad5816ba40d0521e4c8f9434a Mon Sep 17 00:00:00 2001 From: Guillaume Rousse <guillaume.rousse@renater.fr> Date: Fri, 14 May 2021 12:54:35 +0200 Subject: [PATCH 120/884] initial support for ECP authentication --- Nagstamon/Config.py | 3 +++ Nagstamon/QUI/__init__.py | 2 +- Nagstamon/Servers/Generic.py | 7 +++++++ Nagstamon/Servers/__init__.py | 3 +++ 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 0baf78cbc..eedff11ae 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -115,6 +115,7 @@ 'proxy_password', 'autologin_key', 'custom_cert_ca_file', + 'idp_ecp_endpoint', 'monitor_site' ] @@ -930,6 +931,8 @@ def __init__(self): self.custom_cert_use = False self.custom_cert_ca_file = '' + self.idp_ecp_endpoint = 'https://idp/idp/profile/SAML2/SOAP/ECP' + # special FX # Centreon autologin self.use_autologin = False diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index e4a0f7f9d..4bde1fe76 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -5787,7 +5787,7 @@ def __init__(self, dialog): self.ui.button_choose_custom_cert_ca_file.clicked.connect(self.choose_custom_cert_ca_file) # fill authentication combobox - self.ui.input_combobox_authentication.addItems(['Basic', 'Digest', 'Kerberos']) + self.ui.input_combobox_authentication.addItems(['Basic', 'Digest', 'Kerberos', 'ECP']) # detect change of server type which leads to certain options shown or hidden self.ui.input_combobox_type.activated.connect(self.toggle_type) diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index 6f75fffa3..63762228a 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -62,6 +62,11 @@ except ImportError: from requests_kerberos import HTTPKerberosAuth as HTTPSKerberos +try: + from requests_ecp import HTTPECPAuth +except: + pass + # disable annoying SubjectAltNameWarning warnings try: from requests.packages.urllib3.exceptions import SubjectAltNameWarning @@ -265,6 +270,8 @@ def init_HTTP(self): self.session.auth = requests.auth.HTTPBasicAuth(self.username, self.password) elif self.authentication == 'digest': self.session.auth = requests.auth.HTTPDigestAuth(self.username, self.password) + elif self.authentication == 'ecp': + self.session.auth = HTTPECPAuth(self.idp_ecp_endpoint, username=self.username, password=self.password) elif self.authentication == 'kerberos': self.session.auth = HTTPSKerberos() diff --git a/Nagstamon/Servers/__init__.py b/Nagstamon/Servers/__init__.py index c48816873..94a3ee935 100644 --- a/Nagstamon/Servers/__init__.py +++ b/Nagstamon/Servers/__init__.py @@ -166,6 +166,9 @@ def create_server(server=None): new_server.custom_cert_use = server.custom_cert_use new_server.custom_cert_ca_file = server.custom_cert_ca_file + # ECP authentication + new_server.idp_ecp_endpoint = server.idp_ecp_endpoint + # if password is not to be saved ask for it at startup if (server.enabled is True and server.save_password is False and server.use_autologin is False): From c376792ce19686c389fc4c8332dc0923eb5315e7 Mon Sep 17 00:00:00 2001 From: Guillaume Rousse <guillaume.rousse@renater.fr> Date: Fri, 14 May 2021 13:25:40 +0200 Subject: [PATCH 121/884] UI support for configuring ECP authentication --- Nagstamon/QUI/__init__.py | 11 ++ Nagstamon/QUI/settings_server.py | 216 +++++++++++++------------ Nagstamon/QUI/settings_server.ui | 266 ++++++++++++++++--------------- 3 files changed, 261 insertions(+), 232 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 4bde1fe76..b3d7e206a 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -5774,6 +5774,10 @@ def __init__(self, dialog): self.ui.input_lineedit_password, self.ui.input_checkbox_save_password] + self.AUTHENTICATION_ECP_WIDGETS = [ + self.ui.label_idp_ecp_endpoint, + self.ui.input_lineedit_idp_ecp_endpoint] + # fill default order fields combobox with monitor server types self.ui.input_combobox_type.addItems(sorted(SERVER_TYPES.keys(), key=str.lower)) # default to Nagios as it is the mostly used monitor server @@ -5824,6 +5828,13 @@ def toggle_authentication(self): for widget in self.AUTHENTICATION_WIDGETS: widget.show() + if self.ui.input_combobox_authentication.currentText() == 'ECP': + for widget in self.AUTHENTICATION_ECP_WIDGETS: + widget.show() + else: + for widget in self.AUTHENTICATION_ECP_WIDGETS: + widget.hide() + # after hiding authentication widgets dialog might shrink self.window.adjustSize() diff --git a/Nagstamon/QUI/settings_server.py b/Nagstamon/QUI/settings_server.py index 8cacf79ad..22ee5a793 100644 --- a/Nagstamon/QUI/settings_server.py +++ b/Nagstamon/QUI/settings_server.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- -# Form implementation generated from reading ui file 'settings_server.ui' +# Form implementation generated from reading ui file 'Nagstamon/QUI/settings_server.ui' # -# Created by: PyQt5 UI code generator 5.14.2 +# Created by: PyQt5 UI code generator 5.15.2 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. from PyQt5 import QtCore, QtGui, QtWidgets @@ -173,32 +174,17 @@ def setupUi(self, settings_server): self.gridLayout_3.setHorizontalSpacing(10) self.gridLayout_3.setVerticalSpacing(5) self.gridLayout_3.setObjectName("gridLayout_3") - self.label_monitor_site = QtWidgets.QLabel(self.groupbox_options) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_monitor_site.sizePolicy().hasHeightForWidth()) - self.label_monitor_site.setSizePolicy(sizePolicy) - self.label_monitor_site.setObjectName("label_monitor_site") - self.gridLayout_3.addWidget(self.label_monitor_site, 8, 1, 1, 1) - self.input_checkbox_force_authuser = QtWidgets.QCheckBox(self.groupbox_options) - self.input_checkbox_force_authuser.setObjectName("input_checkbox_force_authuser") - self.gridLayout_3.addWidget(self.input_checkbox_force_authuser, 20, 1, 1, 3) - self.label_map_to_hostname = QtWidgets.QLabel(self.groupbox_options) - self.label_map_to_hostname.setObjectName("label_map_to_hostname") - self.gridLayout_3.addWidget(self.label_map_to_hostname, 13, 1, 1, 1) - self.input_checkbox_use_display_name_host = QtWidgets.QCheckBox(self.groupbox_options) - self.input_checkbox_use_display_name_host.setObjectName("input_checkbox_use_display_name_host") - self.gridLayout_3.addWidget(self.input_checkbox_use_display_name_host, 17, 1, 1, 3) - self.input_checkbox_ignore_cert = QtWidgets.QCheckBox(self.groupbox_options) - self.input_checkbox_ignore_cert.setObjectName("input_checkbox_ignore_cert") - self.gridLayout_3.addWidget(self.input_checkbox_ignore_cert, 0, 1, 1, 3) - self.input_checkbox_use_display_name_service = QtWidgets.QCheckBox(self.groupbox_options) - self.input_checkbox_use_display_name_service.setObjectName("input_checkbox_use_display_name_service") - self.gridLayout_3.addWidget(self.input_checkbox_use_display_name_service, 18, 1, 1, 3) - self.input_checkbox_no_cookie_auth = QtWidgets.QCheckBox(self.groupbox_options) - self.input_checkbox_no_cookie_auth.setObjectName("input_checkbox_no_cookie_auth") - self.gridLayout_3.addWidget(self.input_checkbox_no_cookie_auth, 16, 1, 1, 3) + spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.gridLayout_3.addItem(spacerItem1, 5, 3, 1, 1) + self.input_checkbox_use_autologin = QtWidgets.QCheckBox(self.groupbox_options) + self.input_checkbox_use_autologin.setObjectName("input_checkbox_use_autologin") + self.gridLayout_3.addWidget(self.input_checkbox_use_autologin, 7, 1, 1, 3) + self.input_lineedit_map_to_servicename = QtWidgets.QLineEdit(self.groupbox_options) + self.input_lineedit_map_to_servicename.setObjectName("input_lineedit_map_to_servicename") + self.gridLayout_3.addWidget(self.input_lineedit_map_to_servicename, 14, 2, 1, 2) + self.input_lineedit_monitor_site = QtWidgets.QLineEdit(self.groupbox_options) + self.input_lineedit_monitor_site.setObjectName("input_lineedit_monitor_site") + self.gridLayout_3.addWidget(self.input_lineedit_monitor_site, 8, 2, 1, 1) self.label_autologin_key = QtWidgets.QLabel(self.groupbox_options) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) @@ -207,13 +193,13 @@ def setupUi(self, settings_server): self.label_autologin_key.setSizePolicy(sizePolicy) self.label_autologin_key.setObjectName("label_autologin_key") self.gridLayout_3.addWidget(self.label_autologin_key, 9, 1, 1, 1) - self.input_combobox_authentication = QtWidgets.QComboBox(self.groupbox_options) - self.input_combobox_authentication.setObjectName("input_combobox_authentication") - self.gridLayout_3.addWidget(self.input_combobox_authentication, 5, 2, 1, 1) - self.input_lineedit_autologin_key = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_autologin_key.setText("") - self.input_lineedit_autologin_key.setObjectName("input_lineedit_autologin_key") - self.gridLayout_3.addWidget(self.input_lineedit_autologin_key, 9, 2, 1, 2) + self.input_lineedit_service_filter = QtWidgets.QLineEdit(self.groupbox_options) + self.input_lineedit_service_filter.setText("") + self.input_lineedit_service_filter.setObjectName("input_lineedit_service_filter") + self.gridLayout_3.addWidget(self.input_lineedit_service_filter, 11, 2, 1, 2) + self.label_map_to_status_information = QtWidgets.QLabel(self.groupbox_options) + self.label_map_to_status_information.setObjectName("label_map_to_status_information") + self.gridLayout_3.addWidget(self.label_map_to_status_information, 15, 1, 1, 1) self.horizontalLayout = QtWidgets.QHBoxLayout() self.horizontalLayout.setObjectName("horizontalLayout") self.input_lineedit_custom_cert_ca_file = QtWidgets.QLineEdit(self.groupbox_options) @@ -223,19 +209,6 @@ def setupUi(self, settings_server): self.button_choose_custom_cert_ca_file.setObjectName("button_choose_custom_cert_ca_file") self.horizontalLayout.addWidget(self.button_choose_custom_cert_ca_file) self.gridLayout_3.addLayout(self.horizontalLayout, 2, 2, 1, 2) - self.label_service_filter = QtWidgets.QLabel(self.groupbox_options) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_service_filter.sizePolicy().hasHeightForWidth()) - self.label_service_filter.setSizePolicy(sizePolicy) - self.label_service_filter.setObjectName("label_service_filter") - self.gridLayout_3.addWidget(self.label_service_filter, 11, 1, 1, 1) - self.label_custom_ca_file = QtWidgets.QLabel(self.groupbox_options) - self.label_custom_ca_file.setObjectName("label_custom_ca_file") - self.gridLayout_3.addWidget(self.label_custom_ca_file, 2, 1, 1, 1) - spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.gridLayout_3.addItem(spacerItem1, 5, 3, 1, 1) self.groupbox_checkmk_views = QtWidgets.QGroupBox(self.groupbox_options) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) sizePolicy.setHorizontalStretch(0) @@ -264,17 +237,9 @@ def setupUi(self, settings_server): self.button_checkmk_view_services_reset.setObjectName("button_checkmk_view_services_reset") self.gridLayout_4.addWidget(self.button_checkmk_view_services_reset, 1, 2, 1, 1) self.gridLayout_3.addWidget(self.groupbox_checkmk_views, 21, 1, 1, 3) - self.input_lineedit_map_to_servicename = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_map_to_servicename.setObjectName("input_lineedit_map_to_servicename") - self.gridLayout_3.addWidget(self.input_lineedit_map_to_servicename, 14, 2, 1, 2) - self.input_checkbox_use_autologin = QtWidgets.QCheckBox(self.groupbox_options) - self.input_checkbox_use_autologin.setObjectName("input_checkbox_use_autologin") - self.gridLayout_3.addWidget(self.input_checkbox_use_autologin, 7, 1, 1, 3) - self.input_lineedit_map_to_hostname = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_map_to_hostname.setObjectName("input_lineedit_map_to_hostname") - self.gridLayout_3.addWidget(self.input_lineedit_map_to_hostname, 13, 2, 1, 2) - spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.gridLayout_3.addItem(spacerItem2, 6, 3, 1, 1) + self.label_timeout = QtWidgets.QLabel(self.groupbox_options) + self.label_timeout.setObjectName("label_timeout") + self.gridLayout_3.addWidget(self.label_timeout, 6, 1, 1, 1) self.horizontalLayout_timeout_seconds = QtWidgets.QHBoxLayout() self.horizontalLayout_timeout_seconds.setSpacing(5) self.horizontalLayout_timeout_seconds.setObjectName("horizontalLayout_timeout_seconds") @@ -285,28 +250,37 @@ def setupUi(self, settings_server): self.label_timeout_sec.setObjectName("label_timeout_sec") self.horizontalLayout_timeout_seconds.addWidget(self.label_timeout_sec) self.gridLayout_3.addLayout(self.horizontalLayout_timeout_seconds, 6, 2, 1, 1) - self.label_map_to_servicename = QtWidgets.QLabel(self.groupbox_options) - self.label_map_to_servicename.setObjectName("label_map_to_servicename") - self.gridLayout_3.addWidget(self.label_map_to_servicename, 14, 1, 1, 1) - self.input_checkbox_custom_cert_use = QtWidgets.QCheckBox(self.groupbox_options) - self.input_checkbox_custom_cert_use.setObjectName("input_checkbox_custom_cert_use") - self.gridLayout_3.addWidget(self.input_checkbox_custom_cert_use, 1, 1, 1, 2) self.label_auth_type = QtWidgets.QLabel(self.groupbox_options) self.label_auth_type.setObjectName("label_auth_type") self.gridLayout_3.addWidget(self.label_auth_type, 5, 1, 1, 1) - self.input_lineedit_monitor_site = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_monitor_site.setObjectName("input_lineedit_monitor_site") - self.gridLayout_3.addWidget(self.input_lineedit_monitor_site, 8, 2, 1, 1) - self.label_map_to_status_information = QtWidgets.QLabel(self.groupbox_options) - self.label_map_to_status_information.setObjectName("label_map_to_status_information") - self.gridLayout_3.addWidget(self.label_map_to_status_information, 15, 1, 1, 1) - self.input_lineedit_map_to_status_information = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_map_to_status_information.setObjectName("input_lineedit_map_to_status_information") - self.gridLayout_3.addWidget(self.input_lineedit_map_to_status_information, 15, 2, 1, 2) - self.input_lineedit_service_filter = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_service_filter.setText("") - self.input_lineedit_service_filter.setObjectName("input_lineedit_service_filter") - self.gridLayout_3.addWidget(self.input_lineedit_service_filter, 11, 2, 1, 2) + self.input_combobox_authentication = QtWidgets.QComboBox(self.groupbox_options) + self.input_combobox_authentication.setObjectName("input_combobox_authentication") + self.gridLayout_3.addWidget(self.input_combobox_authentication, 5, 2, 1, 1) + self.input_checkbox_custom_cert_use = QtWidgets.QCheckBox(self.groupbox_options) + self.input_checkbox_custom_cert_use.setObjectName("input_checkbox_custom_cert_use") + self.gridLayout_3.addWidget(self.input_checkbox_custom_cert_use, 1, 1, 1, 2) + self.input_checkbox_no_cookie_auth = QtWidgets.QCheckBox(self.groupbox_options) + self.input_checkbox_no_cookie_auth.setObjectName("input_checkbox_no_cookie_auth") + self.gridLayout_3.addWidget(self.input_checkbox_no_cookie_auth, 16, 1, 1, 3) + self.input_checkbox_force_authuser = QtWidgets.QCheckBox(self.groupbox_options) + self.input_checkbox_force_authuser.setObjectName("input_checkbox_force_authuser") + self.gridLayout_3.addWidget(self.input_checkbox_force_authuser, 20, 1, 1, 3) + self.input_lineedit_map_to_hostname = QtWidgets.QLineEdit(self.groupbox_options) + self.input_lineedit_map_to_hostname.setObjectName("input_lineedit_map_to_hostname") + self.gridLayout_3.addWidget(self.input_lineedit_map_to_hostname, 13, 2, 1, 2) + self.input_lineedit_host_filter = QtWidgets.QLineEdit(self.groupbox_options) + self.input_lineedit_host_filter.setText("") + self.input_lineedit_host_filter.setObjectName("input_lineedit_host_filter") + self.gridLayout_3.addWidget(self.input_lineedit_host_filter, 10, 2, 1, 2) + self.input_lineedit_alertmanager_filter = QtWidgets.QLineEdit(self.groupbox_options) + self.input_lineedit_alertmanager_filter.setObjectName("input_lineedit_alertmanager_filter") + self.gridLayout_3.addWidget(self.input_lineedit_alertmanager_filter, 12, 2, 1, 2) + self.input_checkbox_use_display_name_service = QtWidgets.QCheckBox(self.groupbox_options) + self.input_checkbox_use_display_name_service.setObjectName("input_checkbox_use_display_name_service") + self.gridLayout_3.addWidget(self.input_checkbox_use_display_name_service, 18, 1, 1, 3) + self.input_checkbox_ignore_cert = QtWidgets.QCheckBox(self.groupbox_options) + self.input_checkbox_ignore_cert.setObjectName("input_checkbox_ignore_cert") + self.gridLayout_3.addWidget(self.input_checkbox_ignore_cert, 0, 1, 1, 3) self.label_host_filter = QtWidgets.QLabel(self.groupbox_options) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) @@ -315,22 +289,55 @@ def setupUi(self, settings_server): self.label_host_filter.setSizePolicy(sizePolicy) self.label_host_filter.setObjectName("label_host_filter") self.gridLayout_3.addWidget(self.label_host_filter, 10, 1, 1, 1) - self.label_timeout = QtWidgets.QLabel(self.groupbox_options) - self.label_timeout.setObjectName("label_timeout") - self.gridLayout_3.addWidget(self.label_timeout, 6, 1, 1, 1) - self.input_lineedit_host_filter = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_host_filter.setText("") - self.input_lineedit_host_filter.setObjectName("input_lineedit_host_filter") - self.gridLayout_3.addWidget(self.input_lineedit_host_filter, 10, 2, 1, 2) + self.input_lineedit_map_to_status_information = QtWidgets.QLineEdit(self.groupbox_options) + self.input_lineedit_map_to_status_information.setObjectName("input_lineedit_map_to_status_information") + self.gridLayout_3.addWidget(self.input_lineedit_map_to_status_information, 15, 2, 1, 2) + self.label_monitor_site = QtWidgets.QLabel(self.groupbox_options) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_monitor_site.sizePolicy().hasHeightForWidth()) + self.label_monitor_site.setSizePolicy(sizePolicy) + self.label_monitor_site.setObjectName("label_monitor_site") + self.gridLayout_3.addWidget(self.label_monitor_site, 8, 1, 1, 1) + self.label_service_filter = QtWidgets.QLabel(self.groupbox_options) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_service_filter.sizePolicy().hasHeightForWidth()) + self.label_service_filter.setSizePolicy(sizePolicy) + self.label_service_filter.setObjectName("label_service_filter") + self.gridLayout_3.addWidget(self.label_service_filter, 11, 1, 1, 1) self.input_checkbox_use_description_name_service = QtWidgets.QCheckBox(self.groupbox_options) self.input_checkbox_use_description_name_service.setObjectName("input_checkbox_use_description_name_service") self.gridLayout_3.addWidget(self.input_checkbox_use_description_name_service, 19, 1, 1, 3) + self.label_map_to_hostname = QtWidgets.QLabel(self.groupbox_options) + self.label_map_to_hostname.setObjectName("label_map_to_hostname") + self.gridLayout_3.addWidget(self.label_map_to_hostname, 13, 1, 1, 1) + self.label_custom_ca_file = QtWidgets.QLabel(self.groupbox_options) + self.label_custom_ca_file.setObjectName("label_custom_ca_file") + self.gridLayout_3.addWidget(self.label_custom_ca_file, 2, 1, 1, 1) + self.input_checkbox_use_display_name_host = QtWidgets.QCheckBox(self.groupbox_options) + self.input_checkbox_use_display_name_host.setObjectName("input_checkbox_use_display_name_host") + self.gridLayout_3.addWidget(self.input_checkbox_use_display_name_host, 17, 1, 1, 3) + spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.gridLayout_3.addItem(spacerItem2, 6, 3, 1, 1) + self.label_map_to_servicename = QtWidgets.QLabel(self.groupbox_options) + self.label_map_to_servicename.setObjectName("label_map_to_servicename") + self.gridLayout_3.addWidget(self.label_map_to_servicename, 14, 1, 1, 1) + self.input_lineedit_autologin_key = QtWidgets.QLineEdit(self.groupbox_options) + self.input_lineedit_autologin_key.setText("") + self.input_lineedit_autologin_key.setObjectName("input_lineedit_autologin_key") + self.gridLayout_3.addWidget(self.input_lineedit_autologin_key, 9, 2, 1, 2) self.label_alertmanager_filter = QtWidgets.QLabel(self.groupbox_options) self.label_alertmanager_filter.setObjectName("label_alertmanager_filter") self.gridLayout_3.addWidget(self.label_alertmanager_filter, 12, 1, 1, 1) - self.input_lineedit_alertmanager_filter = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_alertmanager_filter.setObjectName("input_lineedit_alertmanager_filter") - self.gridLayout_3.addWidget(self.input_lineedit_alertmanager_filter, 12, 2, 1, 2) + self.label_idp_ecp_endpoint = QtWidgets.QLabel(self.groupbox_options) + self.label_idp_ecp_endpoint.setObjectName("label_idp_ecp_endpoint") + self.gridLayout_3.addWidget(self.label_idp_ecp_endpoint, 22, 1, 1, 1) + self.input_lineedit_idp_ecp_endpoint = QtWidgets.QLineEdit(self.groupbox_options) + self.input_lineedit_idp_ecp_endpoint.setObjectName("input_lineedit_idp_ecp_endpoint") + self.gridLayout_3.addWidget(self.input_lineedit_idp_ecp_endpoint, 22, 2, 1, 2) self.gridLayout.addWidget(self.groupbox_options, 28, 0, 1, 4) self.retranslateUi(settings_server) @@ -375,30 +382,31 @@ def retranslateUi(self, settings_server): self.input_checkbox_save_password.setText(_translate("settings_server", "Save password")) self.input_checkbox_show_options.setText(_translate("settings_server", "Show more options")) self.groupbox_options.setTitle(_translate("settings_server", "Options:")) - self.label_monitor_site.setText(_translate("settings_server", "Monitor Site:")) - self.input_checkbox_force_authuser.setText(_translate("settings_server", "Only show permitted hosts for \"see all\" users (1.4.0i1 or newer)")) - self.label_map_to_hostname.setText(_translate("settings_server", "Map to hostname:")) - self.input_checkbox_use_display_name_host.setText(_translate("settings_server", "Use display name as host name")) - self.input_checkbox_ignore_cert.setText(_translate("settings_server", "Ignore SSL/TLS certificate")) - self.input_checkbox_use_display_name_service.setText(_translate("settings_server", "Use display name as service name")) - self.input_checkbox_no_cookie_auth.setText(_translate("settings_server", "Do not use cookie authentication")) + self.input_checkbox_use_autologin.setText(_translate("settings_server", "Use autologin")) + self.input_lineedit_monitor_site.setText(_translate("settings_server", "Site 1")) self.label_autologin_key.setText(_translate("settings_server", "Autologin Key:")) + self.label_map_to_status_information.setText(_translate("settings_server", "Map to status_information:")) self.button_choose_custom_cert_ca_file.setText(_translate("settings_server", "Choose file...")) - self.label_service_filter.setText(_translate("settings_server", "Service filter:")) - self.label_custom_ca_file.setText(_translate("settings_server", "Custom CA file: ")) self.groupbox_checkmk_views.setTitle(_translate("settings_server", "Views:")) self.label_checkmk_view_hosts.setText(_translate("settings_server", "Hosts:")) self.label_checkmk_view_services.setText(_translate("settings_server", "Services:")) self.button_checkmk_view_hosts_reset.setText(_translate("settings_server", "Reset")) self.button_checkmk_view_services_reset.setText(_translate("settings_server", "Reset")) - self.input_checkbox_use_autologin.setText(_translate("settings_server", "Use autologin")) + self.label_timeout.setText(_translate("settings_server", "Timeout:")) self.label_timeout_sec.setText(_translate("settings_server", "seconds")) - self.label_map_to_servicename.setText(_translate("settings_server", "Map to servicename:")) - self.input_checkbox_custom_cert_use.setText(_translate("settings_server", "Use custom CA file")) self.label_auth_type.setText(_translate("settings_server", "Authentication:")) - self.input_lineedit_monitor_site.setText(_translate("settings_server", "Site 1")) - self.label_map_to_status_information.setText(_translate("settings_server", "Map to status_information:")) + self.input_checkbox_custom_cert_use.setText(_translate("settings_server", "Use custom CA file")) + self.input_checkbox_no_cookie_auth.setText(_translate("settings_server", "Do not use cookie authentication")) + self.input_checkbox_force_authuser.setText(_translate("settings_server", "Only show permitted hosts for \"see all\" users (1.4.0i1 or newer)")) + self.input_checkbox_use_display_name_service.setText(_translate("settings_server", "Use display name as service name")) + self.input_checkbox_ignore_cert.setText(_translate("settings_server", "Ignore SSL/TLS certificate")) self.label_host_filter.setText(_translate("settings_server", "Host filter:")) - self.label_timeout.setText(_translate("settings_server", "Timeout:")) + self.label_monitor_site.setText(_translate("settings_server", "Monitor Site:")) + self.label_service_filter.setText(_translate("settings_server", "Service filter:")) self.input_checkbox_use_description_name_service.setText(_translate("settings_server", "Use description as service name")) + self.label_map_to_hostname.setText(_translate("settings_server", "Map to hostname:")) + self.label_custom_ca_file.setText(_translate("settings_server", "Custom CA file: ")) + self.input_checkbox_use_display_name_host.setText(_translate("settings_server", "Use display name as host name")) + self.label_map_to_servicename.setText(_translate("settings_server", "Map to servicename:")) self.label_alertmanager_filter.setText(_translate("settings_server", "Filter:")) + self.label_idp_ecp_endpoint.setText(_translate("settings_server", "IdP ECP endpoint URL")) diff --git a/Nagstamon/QUI/settings_server.ui b/Nagstamon/QUI/settings_server.ui index 8a6e49896..797ca8c56 100644 --- a/Nagstamon/QUI/settings_server.ui +++ b/Nagstamon/QUI/settings_server.ui @@ -344,58 +344,33 @@ <property name="verticalSpacing"> <number>5</number> </property> - <item row="8" column="1"> - <widget class="QLabel" name="label_monitor_site"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Monitor Site:</string> - </property> - </widget> - </item> - <item row="20" column="1" colspan="3"> - <widget class="QCheckBox" name="input_checkbox_force_authuser"> - <property name="text"> - <string>Only show permitted hosts for "see all" users (1.4.0i1 or newer)</string> - </property> - </widget> - </item> - <item row="13" column="1"> - <widget class="QLabel" name="label_map_to_hostname"> - <property name="text"> - <string>Map to hostname:</string> + <item row="5" column="3"> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> </property> - </widget> - </item> - <item row="17" column="1" colspan="3"> - <widget class="QCheckBox" name="input_checkbox_use_display_name_host"> - <property name="text"> - <string>Use display name as host name</string> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> </property> - </widget> + </spacer> </item> - <item row="0" column="1" colspan="3"> - <widget class="QCheckBox" name="input_checkbox_ignore_cert"> + <item row="7" column="1" colspan="3"> + <widget class="QCheckBox" name="input_checkbox_use_autologin"> <property name="text"> - <string>Ignore SSL/TLS certificate</string> + <string>Use autologin</string> </property> </widget> </item> - <item row="18" column="1" colspan="3"> - <widget class="QCheckBox" name="input_checkbox_use_display_name_service"> - <property name="text"> - <string>Use display name as service name</string> - </property> - </widget> + <item row="14" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_map_to_servicename"/> </item> - <item row="16" column="1" colspan="3"> - <widget class="QCheckBox" name="input_checkbox_no_cookie_auth"> + <item row="8" column="2"> + <widget class="QLineEdit" name="input_lineedit_monitor_site"> <property name="text"> - <string>Do not use cookie authentication</string> + <string>Site 1</string> </property> </widget> </item> @@ -412,16 +387,20 @@ </property> </widget> </item> - <item row="5" column="2"> - <widget class="QComboBox" name="input_combobox_authentication"/> - </item> - <item row="9" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_autologin_key"> + <item row="11" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_service_filter"> <property name="text"> <string/> </property> </widget> </item> + <item row="15" column="1"> + <widget class="QLabel" name="label_map_to_status_information"> + <property name="text"> + <string>Map to status_information:</string> + </property> + </widget> + </item> <item row="2" column="2" colspan="2"> <layout class="QHBoxLayout" name="horizontalLayout"> <item> @@ -436,39 +415,6 @@ </item> </layout> </item> - <item row="11" column="1"> - <widget class="QLabel" name="label_service_filter"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Service filter:</string> - </property> - </widget> - </item> - <item row="2" column="1"> - <widget class="QLabel" name="label_custom_ca_file"> - <property name="text"> - <string>Custom CA file: </string> - </property> - </widget> - </item> - <item row="5" column="3"> - <spacer name="horizontalSpacer_2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> <item row="21" column="1" colspan="3"> <widget class="QGroupBox" name="groupbox_checkmk_views"> <property name="sizePolicy"> @@ -518,32 +464,13 @@ </layout> </widget> </item> - <item row="14" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_map_to_servicename"/> - </item> - <item row="7" column="1" colspan="3"> - <widget class="QCheckBox" name="input_checkbox_use_autologin"> + <item row="6" column="1"> + <widget class="QLabel" name="label_timeout"> <property name="text"> - <string>Use autologin</string> + <string>Timeout:</string> </property> </widget> </item> - <item row="13" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_map_to_hostname"/> - </item> - <item row="6" column="3"> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> <item row="6" column="2"> <layout class="QHBoxLayout" name="horizontalLayout_timeout_seconds"> <property name="spacing"> @@ -561,13 +488,16 @@ </item> </layout> </item> - <item row="14" column="1"> - <widget class="QLabel" name="label_map_to_servicename"> + <item row="5" column="1"> + <widget class="QLabel" name="label_auth_type"> <property name="text"> - <string>Map to servicename:</string> + <string>Authentication:</string> </property> </widget> </item> + <item row="5" column="2"> + <widget class="QComboBox" name="input_combobox_authentication"/> + </item> <item row="1" column="1" colspan="2"> <widget class="QCheckBox" name="input_checkbox_custom_cert_use"> <property name="text"> @@ -575,34 +505,44 @@ </property> </widget> </item> - <item row="5" column="1"> - <widget class="QLabel" name="label_auth_type"> + <item row="16" column="1" colspan="3"> + <widget class="QCheckBox" name="input_checkbox_no_cookie_auth"> <property name="text"> - <string>Authentication:</string> + <string>Do not use cookie authentication</string> </property> </widget> </item> - <item row="8" column="2"> - <widget class="QLineEdit" name="input_lineedit_monitor_site"> + <item row="20" column="1" colspan="3"> + <widget class="QCheckBox" name="input_checkbox_force_authuser"> <property name="text"> - <string>Site 1</string> + <string>Only show permitted hosts for "see all" users (1.4.0i1 or newer)</string> </property> </widget> </item> - <item row="15" column="1"> - <widget class="QLabel" name="label_map_to_status_information"> + <item row="13" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_map_to_hostname"/> + </item> + <item row="10" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_host_filter"> <property name="text"> - <string>Map to status_information:</string> + <string/> </property> </widget> </item> - <item row="15" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_map_to_status_information"/> + <item row="12" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_alertmanager_filter"/> </item> - <item row="11" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_service_filter"> + <item row="18" column="1" colspan="3"> + <widget class="QCheckBox" name="input_checkbox_use_display_name_service"> <property name="text"> - <string/> + <string>Use display name as service name</string> + </property> + </widget> + </item> + <item row="0" column="1" colspan="3"> + <widget class="QCheckBox" name="input_checkbox_ignore_cert"> + <property name="text"> + <string>Ignore SSL/TLS certificate</string> </property> </widget> </item> @@ -619,17 +559,32 @@ </property> </widget> </item> - <item row="6" column="1"> - <widget class="QLabel" name="label_timeout"> + <item row="15" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_map_to_status_information"/> + </item> + <item row="8" column="1"> + <widget class="QLabel" name="label_monitor_site"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> <property name="text"> - <string>Timeout:</string> + <string>Monitor Site:</string> </property> </widget> </item> - <item row="10" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_host_filter"> + <item row="11" column="1"> + <widget class="QLabel" name="label_service_filter"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> <property name="text"> - <string/> + <string>Service filter:</string> </property> </widget> </item> @@ -640,6 +595,54 @@ </property> </widget> </item> + <item row="13" column="1"> + <widget class="QLabel" name="label_map_to_hostname"> + <property name="text"> + <string>Map to hostname:</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLabel" name="label_custom_ca_file"> + <property name="text"> + <string>Custom CA file: </string> + </property> + </widget> + </item> + <item row="17" column="1" colspan="3"> + <widget class="QCheckBox" name="input_checkbox_use_display_name_host"> + <property name="text"> + <string>Use display name as host name</string> + </property> + </widget> + </item> + <item row="6" column="3"> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="14" column="1"> + <widget class="QLabel" name="label_map_to_servicename"> + <property name="text"> + <string>Map to servicename:</string> + </property> + </widget> + </item> + <item row="9" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_autologin_key"> + <property name="text"> + <string/> + </property> + </widget> + </item> <item row="12" column="1"> <widget class="QLabel" name="label_alertmanager_filter"> <property name="text"> @@ -647,8 +650,15 @@ </property> </widget> </item> - <item row="12" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_alertmanager_filter"/> + <item row="22" column="1"> + <widget class="QLabel" name="label_idp_ecp_endpoint"> + <property name="text"> + <string>IdP ECP endpoint URL</string> + </property> + </widget> + </item> + <item row="22" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_idp_ecp_endpoint"/> </item> </layout> </widget> From c9d6335783bcf643b00ef041e8661eff2beb3c30 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Fri, 14 May 2021 14:36:40 +0200 Subject: [PATCH 122/884] Fix for #730 --- Nagstamon/QUI/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index e4a0f7f9d..eaf4bafaf 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -6277,6 +6277,7 @@ def __init__(self, dialog): self.ui.input_checkbox_use_expire_time: [self.ui.input_datetime_expire_time] } + # somewhat clumsy... but well, One Day (TM)... NOT_PROMETHEUS_OR_ALERTMANAGER = [ 'Centreon', 'Generic', @@ -6286,7 +6287,7 @@ def __init__(self, dialog): 'Livestatus', 'Monitos3', 'Monitos4x', - 'Multisite', + 'Checkmk Multisite', 'Nagios', 'Opsview', 'Sensu', From bf342a99d584a4c2ca62f15726740ca5caae1d6b Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Thu, 13 May 2021 00:17:51 +0200 Subject: [PATCH 123/884] set system keyring true default part II (cherry picked from commit 52ab387dc0a102015ac1551b941580d924a4449d) --- Nagstamon/Config.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 1313d50ce..9e6d07cfa 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -304,8 +304,11 @@ def __init__(self): # internal flag to determine if keyring is available at all - defaults to False # use_system_keyring is checked and defined some lines later after config file was read self.keyring_available = False - # setting for keyring usage - self.use_system_keyring = False + # setting for keyring usage - might cause trouble on Linux so disable it there as default to avoid crash at start + if OS in OS_NON_LINUX: + self.use_system_keyring = True + else: + self.use_system_keyring = False # Special FX # Centreon From ea146b6c963bcc8cc7234d3dc0551279b9ae282d Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Sun, 16 May 2021 08:57:20 +0200 Subject: [PATCH 124/884] set system keyring true default part II (cherry picked from commit 52ab387dc0a102015ac1551b941580d924a4449d) --- build/requirements/macos.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index f47eed3c6..32e999084 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -3,6 +3,7 @@ keyring==10.5.1 lxml psutil pyinstaller +# PyQt5 5.15.4 is not working pyqt5==5.15.3 pysocks python-dateutil From e61b9d3f32744deea1e1ffae3666dc4fbf481e55 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Sun, 16 May 2021 09:13:07 +0200 Subject: [PATCH 125/884] less clumsiness for Prometheus/Alermanager in acknowledge dialog --- Nagstamon/Config.py | 2 +- Nagstamon/QUI/__init__.py | 25 ++++--------------------- build/debian/changelog | 4 ++-- 3 files changed, 7 insertions(+), 24 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 9e6d07cfa..d6439e58c 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -124,7 +124,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.7-20210512' + VERSION = '3.7-20210515' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2021 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index eaf4bafaf..d8af27eb2 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -6277,27 +6277,10 @@ def __init__(self, dialog): self.ui.input_checkbox_use_expire_time: [self.ui.input_datetime_expire_time] } - # somewhat clumsy... but well, One Day (TM)... - NOT_PROMETHEUS_OR_ALERTMANAGER = [ - 'Centreon', - 'Generic', - 'Icinga', - 'Icinga2API', - 'IcingaWeb2', - 'Livestatus', - 'Monitos3', - 'Monitos4x', - 'Checkmk Multisite', - 'Nagios', - 'Opsview', - 'Sensu', - 'SensuGo', - 'SnagView3', - 'Thruk', - 'Zabbix', - 'ZabbixProblemBased', - 'Zenoss' - ] + # still clumsy but better than negating the other server types + PROMETHEUS_OR_ALERTMANAGER = ['Alertmanager', + 'Prometheus'] + NOT_PROMETHEUS_OR_ALERTMANAGER = [x.TYPE for x in SERVER_TYPES.values() if x.TYPE not in PROMETHEUS_OR_ALERTMANAGER] self.VOLATILE_WIDGETS = { self.ui.input_checkbox_use_expire_time: ['IcingaWeb2'], diff --git a/build/debian/changelog b/build/debian/changelog index 0cd39b47f..46b6a9dc6 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.7-202105012) unstable; urgency=low +nagstamon (3.7-202105015) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Wed, 12 May 2021 19:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Sat, 15 May 2021 19:00:00 +0100 nagstamon (3.6.0) stable; urgency=low * New upstream From b323598e35f5cfc798ced576ca2f07af8e669263 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Sun, 16 May 2021 20:19:25 +0200 Subject: [PATCH 126/884] added build badge to README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index d130038a3..52ff6c945 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ Nagstamon ========= +[![build-release-latest](https://github.com/HenriWahl/Nagstamon/actions/workflows/build-release-latest.yml/badge.svg)](https://github.com/HenriWahl/Nagstamon/actions/workflows/build-release-latest.yml) + Nagstamon is a status monitor for the desktop. It connects to multiple Nagios, Icinga, Opsview, Centreon, Op5 Monitor/Ninja, Checkmk Multisite, Thruk and monitos monitoring servers. Experimental support is provided for Zabbix, Zenoss and Livestatus monitors. It resides in systray, as a floating statusbar or fullscreen at the desktop showing a brief summary of critical, warning, unknown, unreachable and down hosts and services. It pops up a detailed status overview when being touched by the mouse pointer. Connections to displayed hosts and services are easily established by context menu via SSH, RDP, VNC or any self defined actions. Users can be notified by sound. Hosts and services can be filtered by category and regular expressions. It is inspired by Nagios Checker for Firefox – just without an open Firefox window all the time to monitor the network. From a6ec46c323a7961d1715c78d83de588d9df15b1d Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Sun, 16 May 2021 20:21:24 +0200 Subject: [PATCH 127/884] added build badge to README.md part II --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 52ff6c945..608c66db7 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ Nagstamon ========= - +[![build-release-stable](https://github.com/HenriWahl/Nagstamon/actions/workflows/build-release-stable.yml/badge.svg)](https://github.com/HenriWahl/Nagstamon/actions/workflows/build-release-stable.yml) [![build-release-latest](https://github.com/HenriWahl/Nagstamon/actions/workflows/build-release-latest.yml/badge.svg)](https://github.com/HenriWahl/Nagstamon/actions/workflows/build-release-latest.yml) Nagstamon is a status monitor for the desktop. It connects to multiple Nagios, Icinga, Opsview, Centreon, Op5 Monitor/Ninja, Checkmk Multisite, Thruk and monitos monitoring servers. Experimental support is provided for Zabbix, Zenoss and Livestatus monitors. It resides in systray, as a floating statusbar or fullscreen at the desktop showing a brief summary of critical, warning, unknown, unreachable and down hosts and services. It pops up a detailed status overview when being touched by the mouse pointer. Connections to displayed hosts and services are easily established by context menu via SSH, RDP, VNC or any self defined actions. Users can be notified by sound. Hosts and services can be filtered by category and regular expressions. From 79a0e9279bdeec5afa111014e7f75e0b470adbc5 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Sun, 16 May 2021 20:31:05 +0200 Subject: [PATCH 128/884] build badges seem not to reflect build reality --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 608c66db7..d130038a3 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ Nagstamon ========= -[![build-release-stable](https://github.com/HenriWahl/Nagstamon/actions/workflows/build-release-stable.yml/badge.svg)](https://github.com/HenriWahl/Nagstamon/actions/workflows/build-release-stable.yml) -[![build-release-latest](https://github.com/HenriWahl/Nagstamon/actions/workflows/build-release-latest.yml/badge.svg)](https://github.com/HenriWahl/Nagstamon/actions/workflows/build-release-latest.yml) Nagstamon is a status monitor for the desktop. It connects to multiple Nagios, Icinga, Opsview, Centreon, Op5 Monitor/Ninja, Checkmk Multisite, Thruk and monitos monitoring servers. Experimental support is provided for Zabbix, Zenoss and Livestatus monitors. It resides in systray, as a floating statusbar or fullscreen at the desktop showing a brief summary of critical, warning, unknown, unreachable and down hosts and services. It pops up a detailed status overview when being touched by the mouse pointer. Connections to displayed hosts and services are easily established by context menu via SSH, RDP, VNC or any self defined actions. Users can be notified by sound. Hosts and services can be filtered by category and regular expressions. From b3128855ee9ab5fe7b70bf83f7cd8d8df655c86c Mon Sep 17 00:00:00 2001 From: Stephan Schwarz <stearz@gmx.de> Date: Tue, 18 May 2021 19:54:41 +0200 Subject: [PATCH 129/884] added tests to alertmanager added pylint for tests to work corrected test to unittest removed installation of unittest added requirements.txt removed requests-kerberos added dependency for requests-kerberos using sudo removed broken python versions --- .github/workflows/build-release-latest.yml | 28 +++ .github/workflows/build-release-stable.yml | 28 +++ .gitignore | 3 +- ChangeLog | 4 +- Nagstamon/Config.py | 2 +- Nagstamon/Servers/Alertmanager.py | 251 ++++++++++++--------- requirements.txt | 10 + tests/test_Alertmanager.py | 141 ++++++++++++ tests/test_Alertmanager_critical.json | 32 +++ tests/test_Alertmanager_skipped.json | 22 ++ tests/test_Alertmanager_suppressed.json | 34 +++ tests/test_Alertmanager_warning.json | 24 ++ 12 files changed, 472 insertions(+), 107 deletions(-) create mode 100644 requirements.txt create mode 100644 tests/test_Alertmanager.py create mode 100644 tests/test_Alertmanager_critical.json create mode 100644 tests/test_Alertmanager_skipped.json create mode 100644 tests/test_Alertmanager_suppressed.json create mode 100644 tests/test_Alertmanager_warning.json diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 4669605e5..92bdae7b9 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -9,6 +9,34 @@ env: repo_dir: nagstamon-jekyll/docs/repo jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.7, 3.9] + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + sudo apt-get install libkrb5-dev + python -m pip install --upgrade pip + pip install flake8 pytest pylint + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + # - name: Lint with flake8 + # run: | + # # stop the build if there are Python syntax errors or undefined names + # flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + # flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with unittest + run: | + python -m unittest tests/test_Alertmanager.py + debian: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index 9db4efe08..4853f5376 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -8,6 +8,34 @@ env: repo_dir: nagstamon-jekyll/docs/repo jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.7, 3.9] + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + sudo apt-get install libkrb5-dev + python -m pip install --upgrade pip + pip install flake8 pytest pylint + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + # - name: Lint with flake8 + # run: | + # # stop the build if there are Python syntax errors or undefined names + # flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + # flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with unittest + run: | + python -m unittest tests/test_Alertmanager.py + debian: runs-on: ubuntu-latest steps: diff --git a/.gitignore b/.gitignore index eecee72df..4638bd74b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,10 @@ *.pyc *~ .idea/ -.__pycache__/ +__pycache__/ *.swp nagstamon.conf/ .directory .metadata/ .vscode/ +dist/ diff --git a/ChangeLog b/ChangeLog index 90644addc..42d713f86 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ -nagstamon (3.7-20210408) unstable; urgency=low +nagstamon (3.7-20210517) unstable; urgency=low * New upstream + - added test section to GitHub actions workflows to enable testing + - added basic unit tests and linting to Alertmanager -- Henri Wahl <henri@nagstamon.de> Tue, 07 Apr 2021 19:00:00 +0100 diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index d6439e58c..d74ce6b34 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -124,7 +124,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.7-20210515' + VERSION = '3.7-20210518' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2021 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/Servers/Alertmanager.py b/Nagstamon/Servers/Alertmanager.py index d9d03c943..634798a90 100644 --- a/Nagstamon/Servers/Alertmanager.py +++ b/Nagstamon/Servers/Alertmanager.py @@ -25,6 +25,13 @@ # # Release Notes: # +# [1.1.0] - 2021-05-18: +# * changed: +# Using logging module for all outputs +# Some refactoring for testing support +# * added: +# Initial tests based on unittest and pylint (see tests/test_Alertmanager.py) +# # [1.0.2] - 2021-04-10: # * added: # Better debug output @@ -38,27 +45,35 @@ # Inital version # import sys -import urllib.request -import urllib.parse -import urllib.error -import pprint import json -import requests import re import time -from collections import OrderedDict from datetime import datetime, timedelta, timezone +import logging import dateutil.parser +import requests from Nagstamon.Config import conf -from Nagstamon.Objects import (GenericHost, - GenericService, - Result) -from Nagstamon.Servers.Generic import GenericServer -from Nagstamon.Servers.Prometheus import PrometheusServer,PrometheusService +from Nagstamon.Objects import (GenericHost, Result) +from Nagstamon.Servers.Prometheus import PrometheusServer, PrometheusService from Nagstamon.Helpers import webbrowser_open +# logging -------------------------------------------- +log = logging.getLogger('Alertmanager.py') +handler = logging.StreamHandler(sys.stdout) +if conf.debug_mode is True: + log_level = logging.DEBUG + handler.setLevel(logging.DEBUG) +else: + log_level = logging.INFO + handler.setLevel(logging.INFO) +log.setLevel(log_level) +formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') +handler.setFormatter(formatter) +log.addHandler(handler) +# ---------------------------------------------------- + class AlertmanagerService(PrometheusService): """ @@ -86,6 +101,13 @@ class AlertmanagerServer(PrometheusServer): API_PATH_SILENCES = "/api/v2/silences" API_FILTERS = '?filter=' + # vars specific to alertmanager class + map_to_hostname = '' + map_to_servicename = '' + map_to_status_information = '' + name = '' + alertmanager_filter = '' + @staticmethod def timestring_to_utc(timestring): local_time = datetime.now(timezone(timedelta(0))).astimezone().tzinfo @@ -93,29 +115,103 @@ def timestring_to_utc(timestring): utc_time = parsed_time.replace(tzinfo=local_time).astimezone(timezone.utc) return utc_time.isoformat() + + def _detect_from_labels(self, labels, config_label_list, default_value="", list_delimiter=","): + result = default_value + for each_label in config_label_list.split(list_delimiter): + if each_label in labels: + result = labels.get(each_label) + break + return result + + + def _process_alert(self, alert): + result = {} + + # Alertmanager specific extensions + generatorURL = alert.get("generatorURL", {}) + fingerprint = alert.get("fingerprint", {}) + log.debug("processing alert with fingerprint '%s':", fingerprint) + + labels = alert.get("labels", {}) + state = alert.get("status", {"state": "active"})["state"] + severity = labels.get("severity", "UNKNOWN").upper() + + # skip alerts with none severity + if severity == "NONE": + log.debug("[%s]: detected detected state '%s' and severity '%s' from labels -> skipping alert", fingerprint, state, severity) + return False + log.debug("[%s]: detected detected state '%s' and severity '%s' from labels", fingerprint, state, severity) + + hostname = self._detect_from_labels(labels,self.map_to_hostname,"unknown") + hostname = re.sub(':[0-9]+', '', hostname) + log.debug("[%s]: detected hostname from labels: '%s'", fingerprint, hostname) + + servicename = self._detect_from_labels(labels,self.map_to_servicename,"unknown") + log.debug("[%s]: detected servicename from labels: '%s'", fingerprint, servicename) + + if "status" in alert: + attempt = alert["status"].get("state", "unknown") + else: + attempt = "unknown" + + if attempt == "suppressed": + scheduled_downtime = True + acknowledged = True + log.debug("[%s]: detected status: '%s' -> interpreting as silenced", fingerprint, attempt) + else: + scheduled_downtime = False + acknowledged = False + log.debug("[%s]: detected status: '%s'", fingerprint, attempt) + + duration = str(self._get_duration(alert["startsAt"])) + + annotations = alert.get("annotations", {}) + status_information = self._detect_from_labels(annotations,self.map_to_status_information,'') + + result['host'] = str(hostname) + result['name'] = servicename + result['server'] = self.name + result['status'] = severity + result['labels'] = labels + result['last_check'] = str(self._get_duration(alert["updatedAt"])) + result['attempt'] = attempt + result['scheduled_downtime'] = scheduled_downtime + result['acknowledged'] = acknowledged + result['duration'] = duration + result['generatorURL'] = generatorURL + result['fingerprint'] = fingerprint + result['status_information'] = status_information + + return result + + def _get_status(self): """ Get status from Alertmanager Server """ - if conf.debug_mode: - self.Debug(server=self.get_name(),debug="detection config (map_to_status_information): '" + str(self.map_to_status_information) + "'") - self.Debug(server=self.get_name(),debug="detection config (map_to_hostname): '" + str(self.map_to_hostname) + "'") - self.Debug(server=self.get_name(),debug="detection config (map_to_servicename): '" + str(self.map_to_servicename) + "'") - self.Debug(server=self.get_name(),debug="detection config (alertmanager_filter): '" + str(self.alertmanager_filter) + "'") + + log.debug("detection config (map_to_status_information): '%s'", self.map_to_status_information) + log.debug("detection config (map_to_hostname): '%s'", self.map_to_hostname) + log.debug("detection config (map_to_servicename): '%s'", self.map_to_servicename) + log.debug("detection config (alertmanager_filter): '%s'", self.alertmanager_filter) # get all alerts from the API server try: if self.alertmanager_filter != '': - result = self.FetchURL(self.monitor_url + self.API_PATH_ALERTS + self.API_FILTERS + self.alertmanager_filter, - giveback="raw") + result = self.FetchURL(self.monitor_url + self.API_PATH_ALERTS + self.API_FILTERS + + self.alertmanager_filter, giveback="raw") else: result = self.FetchURL(self.monitor_url + self.API_PATH_ALERTS, giveback="raw") - if conf.debug_mode: - self.Debug(server=self.get_name(),debug="received status code '" + str(result.status_code) + "' with this content in result.result: \n\ + + if result.status_code == 200: + log.debug("received status code '%s' with this content in result.result: \n\ -----------------------------------------------------------------------------------------------------------------------------\n\ -" + result.result + "\ ------------------------------------------------------------------------------------------------------------------------------") +%s\ +-----------------------------------------------------------------------------------------------------------------------------", result.status_code, result.result) + else: + log.error("received status code '%s'", result.status_code) data = json.loads(result.result) error = result.error @@ -127,98 +223,44 @@ def _get_status(self): return(errors_occured) for alert in data: - if conf.debug_mode: - self.Debug( - server=self.get_name(), - debug="processing alert with fingerprint '" + alert['fingerprint'] + "':" - ) - - labels = alert.get("labels", {}) - state = alert.get("status", {"state": "active"})["state"] - - # skip alerts with none severity - severity = labels.get("severity", "UNKNOWN").upper() - - if severity == "NONE": - if conf.debug_mode: - self.Debug(server=self.get_name(),debug="[" + alert['fingerprint'] + "]: detected severity from labels '" + severity + "' -> skipping alert") - continue - - if conf.debug_mode: - self.Debug(server=self.get_name(),debug="[" + alert['fingerprint'] + "]: detected severity from labels '" + severity + "'") - - hostname = "unknown" - for host_label in self.map_to_hostname.split(','): - if host_label in labels: - hostname = labels.get(host_label) - m = re.match(r"(.*):[0-9]+", hostname) - if m: - hostname = m.group(1) - break - - if conf.debug_mode: - self.Debug(server=self.get_name(),debug="[" + alert['fingerprint'] + "]: detected hostname from labels: '" + hostname + "'") - - servicename = "unknown" - for service_label in self.map_to_servicename.split(','): - if service_label in labels: - servicename = labels.get(service_label) - break - - if conf.debug_mode: - self.Debug(server=self.get_name(),debug="[" + alert['fingerprint'] + "]: detected servicename from labels: '" + servicename + "'") + alert_data = self._process_alert(alert) + if not alert_data: + break service = PrometheusService() - service.host = str(hostname) - service.name = servicename - service.server = self.name - service.status = severity - service.labels = labels - service.acknowledged = state == "suppressed" - service.last_check = str(self._get_duration(alert["updatedAt"])) - - if "status" in alert: - service.attempt = alert["status"].get("state", "unknown") - else: - service.attempt = "unknown" - - if service.attempt == "suppressed": - service.scheduled_downtime = True - if conf.debug_mode: - self.Debug(server=self.get_name(),debug="[" + alert['fingerprint'] + "]: detected status: '" + service.attempt + "' -> interpreting as silenced") - else: - if conf.debug_mode: - self.Debug(server=self.get_name(),debug="[" + alert['fingerprint'] + "]: detected status: '" + service.attempt + "'") - - service.duration = str(self._get_duration(alert["startsAt"])) - - # Alertmanager specific extensions - service.generatorURL = alert.get("generatorURL", {}) - service.fingerprint = alert.get("fingerprint", {}) - - annotations = alert.get("annotations", {}) - status_information = "" - for status_information_label in self.map_to_status_information.split(','): - if status_information_label in annotations: - status_information = annotations.get(status_information_label) - break - service.status_information = status_information - - if hostname not in self.new_hosts: - self.new_hosts[hostname] = GenericHost() - self.new_hosts[hostname].name = str(hostname) - self.new_hosts[hostname].server = self.name - self.new_hosts[hostname].services[servicename] = service - - except: + service.host = alert_data['host'] + service.name = alert_data['name'] + service.server = alert_data['server'] + service.status = alert_data['status'] + service.labels = alert_data['labels'] + service.scheduled_downtime = alert_data['scheduled_downtime'] + service.acknowledged = alert_data['acknowledged'] + service.last_check = alert_data['last_check'] + service.attempt = alert_data['attempt'] + service.duration = alert_data['duration'] + + service.generatorURL = alert_data['generatorURL'] + service.fingerprint = alert_data['fingerprint'] + + service.status_information = alert_data['status_information'] + + if service.host not in self.new_hosts: + self.new_hosts[service.host] = GenericHost() + self.new_hosts[service.host].name = str(service.host) + self.new_hosts[service.host].server = self.name + self.new_hosts[service.host].services[service.name] = service + + except Exception as e: # set checking flag back to False self.isChecking = False result, error = self.Error(sys.exc_info()) + log.exception(e) return Result(result=result, error=error) # dummy return in case all is OK return Result() + def open_monitor(self, host, service=''): """ open monitor for alert @@ -226,6 +268,7 @@ def open_monitor(self, host, service=''): url = self.monitor_url webbrowser_open(url) + def _set_downtime(self, host, service, author, comment, fixed, start_time, end_time, hours, minutes): diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 000000000..3e9605734 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,10 @@ +beautifulsoup4 +bs4 +keyring +lxml +psutil +PyQt5 +PySocks +python-dateutil +requests +requests-kerberos diff --git a/tests/test_Alertmanager.py b/tests/test_Alertmanager.py new file mode 100644 index 000000000..a2a138ee4 --- /dev/null +++ b/tests/test_Alertmanager.py @@ -0,0 +1,141 @@ +import re +import sys +import json + +from pylint import lint + +import unittest +import logging +import Nagstamon +import Nagstamon.Servers.Alertmanager + +conf = {} +conf['debug_mode'] = True + +def read_file(filename): + ''' + Helper method for injecting json from files + ''' + with open(filename) as f: + return f.read() + + +class test_Alertmanager(unittest.TestCase): + + def test_lint_with_pylint(self): + with self.assertRaises(SystemExit) as cm: + lint.Run(['--errors-only','Nagstamon/Servers/Alertmanager.py']) + self.assertEqual(cm.exception.code, 0) + + def test_unit_alert_suppressed(self): + with open('tests/test_Alertmanager_suppressed.json') as json_file: + data = json.load(json_file) + + test_class = Nagstamon.Servers.Alertmanager.AlertmanagerServer() + test_class.map_to_hostname = 'instance,pod_name,namespace' + test_class.map_to_servicename = 'alertname' + test_class.map_to_status_information = 'message,summary,description' + + test_result = test_class._process_alert(data) + + self.assertEqual(test_result['attempt'], 'suppressed') + self.assertEqual(test_result['acknowledged'], True) + self.assertEqual(test_result['scheduled_downtime'], True) + self.assertEqual(test_result['host'], '127.0.0.1') + self.assertEqual(test_result['name'], 'Error') + self.assertEqual(test_result['server'], '') + self.assertEqual(test_result['status'], 'WARNING') + self.assertEqual(test_result['labels'], {"alertname":"Error","device":"murpel","endpoint":"metrics","instance":"127.0.0.1:9100","job":"node-exporter","namespace":"monitoring","pod":"monitoring-prometheus-node-exporter-4711","prometheus":"monitoring/monitoring-prometheus-oper-prometheus","service":"monitoring-prometheus-node-exporter","severity":"warning"}) + self.assertEqual(test_result['generatorURL'], 'http://localhost') + self.assertEqual(test_result['fingerprint'], '0ef7c4bd7a504b8d') + self.assertEqual(test_result['status_information'], 'Network interface "murpel" showing errors on node-exporter monitoring/monitoring-prometheus-node-exporter-4711') + + + def test_unit_alert_skipped(self): + with open('tests/test_Alertmanager_skipped.json') as json_file: + data = json.load(json_file) + + test_class = Nagstamon.Servers.Alertmanager.AlertmanagerServer() + test_class.map_to_hostname = 'instance,pod_name,namespace' + test_class.map_to_servicename = 'alertname' + test_class.map_to_status_information = 'message,summary,description' + + test_result = test_class._process_alert(data) + + self.assertEqual(test_result, False) + + + def test_unit_alert_warning(self): + with open('tests/test_Alertmanager_warning.json') as json_file: + data = json.load(json_file) + + test_class = Nagstamon.Servers.Alertmanager.AlertmanagerServer() + test_class.map_to_hostname = 'instance,pod_name,namespace' + test_class.map_to_servicename = 'alertname' + test_class.map_to_status_information = 'message,summary,description' + + test_result = test_class._process_alert(data) + + self.assertEqual(test_result['attempt'], 'active') + self.assertEqual(test_result['acknowledged'], False) + self.assertEqual(test_result['scheduled_downtime'], False) + self.assertEqual(test_result['host'], 'unknown') + self.assertEqual(test_result['name'], 'TargetDown') + self.assertEqual(test_result['server'], '') + self.assertEqual(test_result['status'], 'WARNING') + self.assertEqual(test_result['labels'], {"alertname": "TargetDown","job": "kubelet","prometheus": "monitoring/monitoring-prometheus-oper-prometheus","severity": "warning"}) + self.assertEqual(test_result['generatorURL'], 'http://localhost') + self.assertEqual(test_result['fingerprint'], '7be970c6e97b95c9') + self.assertEqual(test_result['status_information'], '66.6% of the kubelet targets are down.') + + + def test_unit_alert_critical(self): + with open('tests/test_Alertmanager_critical.json') as json_file: + data = json.load(json_file) + + test_class = Nagstamon.Servers.Alertmanager.AlertmanagerServer() + test_class.map_to_hostname = 'instance,pod_name,namespace' + test_class.map_to_servicename = 'alertname' + test_class.map_to_status_information = 'message,summary,description' + + test_result = test_class._process_alert(data) + + self.assertEqual(test_result['attempt'], 'active') + self.assertEqual(test_result['acknowledged'], False) + self.assertEqual(test_result['scheduled_downtime'], False) + self.assertEqual(test_result['host'], '127.0.0.1') + self.assertEqual(test_result['name'], 'Error') + self.assertEqual(test_result['server'], '') + self.assertEqual(test_result['status'], 'ERROR') + self.assertEqual(test_result['labels'], {"alertname":"Error","device":"murpel","endpoint":"metrics","instance":"127.0.0.1:9100","job":"node-exporter","namespace":"monitoring","pod":"monitoring-prometheus-node-exporter-4711","prometheus":"monitoring/monitoring-prometheus-oper-prometheus","service":"monitoring-prometheus-node-exporter","severity":"error"}) + self.assertEqual(test_result['generatorURL'], 'http://localhost') + self.assertEqual(test_result['fingerprint'], '0ef7c4bd7a504b8d') + self.assertEqual(test_result['status_information'], 'Network interface "murpel" showing errors on node-exporter monitoring/monitoring-prometheus-node-exporter-4711') + + + def test_unit_alert_critical_with_empty_maps(self): + with open('tests/test_Alertmanager_critical.json') as json_file: + data = json.load(json_file) + + test_class = Nagstamon.Servers.Alertmanager.AlertmanagerServer() + test_class.map_to_hostname = '' + test_class.map_to_servicename = '' + test_class.map_to_status_information = '' + + test_result = test_class._process_alert(data) + + self.assertEqual(test_result['attempt'], 'active') + self.assertEqual(test_result['acknowledged'], False) + self.assertEqual(test_result['scheduled_downtime'], False) + self.assertEqual(test_result['host'], 'unknown') + self.assertEqual(test_result['name'], 'unknown') + self.assertEqual(test_result['server'], '') + self.assertEqual(test_result['status'], 'ERROR') + self.assertEqual(test_result['labels'], {"alertname":"Error","device":"murpel","endpoint":"metrics","instance":"127.0.0.1:9100","job":"node-exporter","namespace":"monitoring","pod":"monitoring-prometheus-node-exporter-4711","prometheus":"monitoring/monitoring-prometheus-oper-prometheus","service":"monitoring-prometheus-node-exporter","severity":"error"}) + self.assertEqual(test_result['generatorURL'], 'http://localhost') + self.assertEqual(test_result['fingerprint'], '0ef7c4bd7a504b8d') + self.assertEqual(test_result['status_information'], '') + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_Alertmanager_critical.json b/tests/test_Alertmanager_critical.json new file mode 100644 index 000000000..633a167ad --- /dev/null +++ b/tests/test_Alertmanager_critical.json @@ -0,0 +1,32 @@ +{ + "annotations": { + "message": "Network interface \"murpel\" showing errors on node-exporter monitoring/monitoring-prometheus-node-exporter-4711" + }, + "endsAt": "1970-01-01T00:00:12.345Z", + "fingerprint": "0ef7c4bd7a504b8d", + "receivers": [ + { + "name": "null" + } + ], + "startsAt": "1970-01-01T00:00:01.345Z", + "status": { + "inhibitedBy": [], + "silencedBy": [], + "state": "active" + }, + "updatedAt": "1970-01-01T00:00:12.345Z", + "generatorURL": "http://localhost", + "labels": { + "alertname": "Error", + "device": "murpel", + "endpoint": "metrics", + "instance": "127.0.0.1:9100", + "job": "node-exporter", + "namespace": "monitoring", + "pod": "monitoring-prometheus-node-exporter-4711", + "prometheus": "monitoring/monitoring-prometheus-oper-prometheus", + "service": "monitoring-prometheus-node-exporter", + "severity": "error" + } +} \ No newline at end of file diff --git a/tests/test_Alertmanager_skipped.json b/tests/test_Alertmanager_skipped.json new file mode 100644 index 000000000..1df93d3a2 --- /dev/null +++ b/tests/test_Alertmanager_skipped.json @@ -0,0 +1,22 @@ +{ + "labels": { + "alertname": "Watchdog", + "prometheus": "monitoring/monitoring-prometheus-oper-prometheus", + "severity": "none" + }, + "annotations": { + "message": "This is an alert meant to ensure that the entire alerting pipeline is functional.\nThis alert is always firing, therefore it should always be firing in Alertmanager\nand always fire against a receiver. There are integrations with various notification\nmechanisms that send a notification when this alert is not firing. For example the\n\"DeadMansSnitch\" integration in PagerDuty.\n" + }, + "startsAt": "1970-01-01T00:00:00.520032135Z", + "endsAt": "1970-01-01T00:00:00.520032135Z", + "generatorURL": "http://localhost", + "status": { + "state": "active", + "silencedBy": [], + "inhibitedBy": [] + }, + "receivers": [ + "null" + ], + "fingerprint": "1b43ca4565de75d2" +} \ No newline at end of file diff --git a/tests/test_Alertmanager_suppressed.json b/tests/test_Alertmanager_suppressed.json new file mode 100644 index 000000000..f5ae0186b --- /dev/null +++ b/tests/test_Alertmanager_suppressed.json @@ -0,0 +1,34 @@ +{ + "annotations": { + "message": "Network interface \"murpel\" showing errors on node-exporter monitoring/monitoring-prometheus-node-exporter-4711" + }, + "endsAt": "1970-01-01T00:00:12.345Z", + "fingerprint": "0ef7c4bd7a504b8d", + "receivers": [ + { + "name": "null" + } + ], + "startsAt": "1970-01-01T00:00:01.345Z", + "status": { + "inhibitedBy": [], + "silencedBy": [ + "bb043288-42a0-4315-8bae-15cde1d7e239" + ], + "state": "suppressed" + }, + "updatedAt": "1970-01-01T00:00:12.345Z", + "generatorURL": "http://localhost", + "labels": { + "alertname": "Error", + "device": "murpel", + "endpoint": "metrics", + "instance": "127.0.0.1:9100", + "job": "node-exporter", + "namespace": "monitoring", + "pod": "monitoring-prometheus-node-exporter-4711", + "prometheus": "monitoring/monitoring-prometheus-oper-prometheus", + "service": "monitoring-prometheus-node-exporter", + "severity": "warning" + } +} \ No newline at end of file diff --git a/tests/test_Alertmanager_warning.json b/tests/test_Alertmanager_warning.json new file mode 100644 index 000000000..3142d490e --- /dev/null +++ b/tests/test_Alertmanager_warning.json @@ -0,0 +1,24 @@ +{ + "labels": { + "alertname": "TargetDown", + "job": "kubelet", + "prometheus": "monitoring/monitoring-prometheus-oper-prometheus", + "severity": "warning" + }, + "annotations": { + "message": "66.6% of the kubelet targets are down." + }, + "startsAt": "1970-01-01T00:00:00.520032135Z", + "endsAt": "1970-01-01T00:00:00.520032135Z", + "updatedAt": "1970-01-01T00:00:00.520032135Z", + "generatorURL": "http://localhost", + "status": { + "state": "active", + "silencedBy": [], + "inhibitedBy": [] + }, + "receivers": [ + "null" + ], + "fingerprint": "7be970c6e97b95c9" +} \ No newline at end of file From f8f6a029c92341ca72ef55fd9c3011fe31842b26 Mon Sep 17 00:00:00 2001 From: Guillaume Rousse <guillaume.rousse@renater.fr> Date: Fri, 21 May 2021 10:20:46 +0200 Subject: [PATCH 130/884] add requests-ecp dependency --- build/requirements/linux.txt | 3 ++- build/requirements/macos.txt | 3 ++- build/requirements/windows.txt | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/build/requirements/linux.txt b/build/requirements/linux.txt index 6ba327a64..12728e88c 100644 --- a/build/requirements/linux.txt +++ b/build/requirements/linux.txt @@ -7,4 +7,5 @@ pysocks python-dateutil requests requests-kerberos -setuptools==44.1.1 \ No newline at end of file +requests-ecp +setuptools==44.1.1 diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index f47eed3c6..88f5061aa 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -8,4 +8,5 @@ pysocks python-dateutil requests requests-gssapi -setuptools==44.1.1 \ No newline at end of file +requests-ecp +setuptools==44.1.1 diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index a9e4adc28..34add19a9 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -11,5 +11,6 @@ pysocks python-dateutil requests requests-kerberos +requests-ecp setuptools==44.1.1 wheel From 0c41df90bae6c2139f2e63fc1b9ade859a90c7ee Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Fri, 21 May 2021 12:25:19 +0200 Subject: [PATCH 131/884] Update changelog --- build/debian/changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/debian/changelog b/build/debian/changelog index 46b6a9dc6..7becc9cce 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.7-202105015) unstable; urgency=low +nagstamon (3.7-202105018) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Sat, 15 May 2021 19:00:00 +0100 From a18270092a4bdb3783d3094fc69f7aa1ade845b1 Mon Sep 17 00:00:00 2001 From: Guillaume Rousse <guillaume.rousse@renater.fr> Date: Fri, 21 May 2021 13:31:29 +0200 Subject: [PATCH 132/884] only allows ECP auth if support is available --- Nagstamon/QUI/__init__.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index b3d7e206a..98c1dc8c7 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -113,6 +113,13 @@ print('No DBus for desktop notification available.') DBUS_AVAILABLE = False +# check ECP authentication support availability +try: + from requests_ecp import HTTPECPAuth + ECP_AVAILABLE = True +except ImportError: + ECP_AVAILABLE = False + # enable HighDPI-awareness to avoid https://github.com/HenriWahl/Nagstamon/issues/618 try: QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True) @@ -5791,7 +5798,9 @@ def __init__(self, dialog): self.ui.button_choose_custom_cert_ca_file.clicked.connect(self.choose_custom_cert_ca_file) # fill authentication combobox - self.ui.input_combobox_authentication.addItems(['Basic', 'Digest', 'Kerberos', 'ECP']) + self.ui.input_combobox_authentication.addItems(['Basic', 'Digest', 'Kerberos']) + if ECP_AVAILABLE is True: + self.ui.input_combobox_authentication.addItems(['ECP']) # detect change of server type which leads to certain options shown or hidden self.ui.input_combobox_type.activated.connect(self.toggle_type) From 3c4c3166d78694cfa733c502226d6320083a4fb3 Mon Sep 17 00:00:00 2001 From: Sven Nierlein <sven@nierlein.de> Date: Wed, 2 Jun 2021 10:51:58 +0200 Subject: [PATCH 133/884] fix services on hosts with downtime filter when using thruk (fixes #658) --- Nagstamon/Servers/Thruk.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Nagstamon/Servers/Thruk.py b/Nagstamon/Servers/Thruk.py index f3401d43d..cb2b4a80b 100644 --- a/Nagstamon/Servers/Thruk.py +++ b/Nagstamon/Servers/Thruk.py @@ -96,7 +96,8 @@ def init_config(self): "notifications_enabled,acknowledged,state_type,"\ "scheduled_downtime_depth" # hosts (up or down or unreachable) - self.cgiurl_hosts = self.monitor_cgi_url + "/status.cgi?hostgroup=all&style=hostdetail&hoststatustypes=12&"\ + self.cgiurl_hosts = self.monitor_cgi_url + "/status.cgi?hostgroup=all&style=hostdetail&"\ + "dfl_s0_hoststatustypes=12&dfl_s1_hostprops=1&dfl_s2_hostprops=4&dfl_s3_hostprops=524288&&dfl_s4_hostprops=4096&dfl_s5_hostprop=16&"\ "view_mode=json&entries=all&"\ "columns=name,state,last_check,last_state_change,"\ "plugin_output,current_attempt,max_check_attempts,"\ From f235a1da838d56311ff05d353abf57529d6ad61c Mon Sep 17 00:00:00 2001 From: Stefano Stella <mprenditore@gmail.com> Date: Thu, 10 Jun 2021 18:36:53 +0200 Subject: [PATCH 134/884] Fix crash for zabbix 5.4 on triggers with no app --- Nagstamon/Servers/Zabbix.py | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/Nagstamon/Servers/Zabbix.py b/Nagstamon/Servers/Zabbix.py index 56980fc5a..dd55577cd 100644 --- a/Nagstamon/Servers/Zabbix.py +++ b/Nagstamon/Servers/Zabbix.py @@ -96,6 +96,8 @@ def _login(self): def getLastApp(self, this_item): if len(this_item) > 0: + if "applications" not in this_item[0]: + return "NO APP" last_app = len(this_item[0]['applications']) - 1 # use it to get the last application name if last_app > -1: return "%s" % this_item[0]['applications'][last_app]['name'] @@ -146,7 +148,7 @@ def _get_status(self): # ipmi_available IPMI: 0 -> No agents 1 -> available 2-> Agent access error # maintenance_status = 1 In maintenance for host in hosts: - + n = { 'host': host['host'], 'name': host['name'], @@ -200,7 +202,7 @@ def _get_status(self): n['status'] = 'DOWN' n['status_information'] = host['error'] n['duration'] = HumanReadableDurationFromTimestamp(host['errors_from']) - + # Zabbix shows OK hosts too - kick 'em! if not n['status'] == 'UP': # add dictionary full of information about this host item to nagitems @@ -278,10 +280,10 @@ def _get_status(self): 'withLastEventUnacknowledged': True, }) unack_trigger_ids = [u['triggerid'] for u in unack_triggers] - + for t in triggers: t['acknowledged'] = False if t['triggerid'] in unack_trigger_ids else True - + # get Application name for the trigger this_item = self.zapi.item.get( {'itemids': [t['items'][0]['itemid']], @@ -372,7 +374,7 @@ def _get_status(self): n['scheduled_downtime'] = True nagitems["services"].append(n) - + # after collection data in nagitems create objects of its informations # host objects contain service objects # if not created previously, host should be created with proper data @@ -387,7 +389,7 @@ def _get_status(self): new_service = n["triggerid"] if new_service not in self.new_hosts[key].services: self.new_hosts[key].services[new_service] = GenericService() - + self.new_hosts[key].services[new_service].host = n["hostname"] if len(n['hostname']) != 0 else n["host"] self.new_hosts[key].services[new_service].name = n["service"] self.new_hosts[key].services[new_service].status = n["status"] @@ -414,7 +416,7 @@ def _get_status(self): result, error = self.Error(sys.exc_info()) print(sys.exc_info()) return Result(result=result, error=error) - + return ret def _open_browser(self, url): @@ -457,7 +459,7 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi # Service column is storing current trigger id triggerids = [] triggerids.append(service) - + # acknowledge all problems (column services) on a host when told to do so for s in all_services: triggerids.append(s) @@ -473,13 +475,13 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi 'sortfield': 'clock', 'sortorder': 'DESC'}): # Get only current event status, but retrieving first row ordered by clock DESC - + # If event status is not "OK" (Still is an active problem), mark event to acknowledge/close if e['value'] != '0': events.append(e['eventid']) # Only take care of newest event, discard all next break - + # If events pending of acknowledge, execute ack if len(events) > 0: # If sticky is set then close only current event @@ -487,20 +489,20 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi if conf.debug_mode is True: self.Debug(server=self.get_name(), debug="Events to acknowledge: " + str(events) + " Close: " + str(close)) self.zapi.event.acknowledge({'eventids': events, 'message': '%s: %s' % (author, comment), 'action': close}) - + def _set_downtime(self, hostname, service, author, comment, fixed, start_time, end_time, hours, minutes): hostids = [ self.hosts[hostname].hostid ] - + date = datetime.datetime.strptime(start_time, "%Y-%m-%d %H:%M") stime = time.mktime(date.timetuple()) date = datetime.datetime.strptime(end_time, "%Y-%m-%d %H:%M") etime = time.mktime(date.timetuple()) - + if conf.debug_mode is True: self.Debug(server=self.get_name(), debug="Downtime for " + hostname + "[" + str(hostids[0]) + "] stime:" + str(stime) + " etime:" + str(etime)) - + self.zapi.maintenance.create({'hostids': hostids, 'name': comment, 'description': author, 'active_since': stime, 'active_till': etime, 'maintenance_type' : 0, "timeperiods": [ { "timeperiod_type": 0, "start_date": stime, "period": etime - stime } ]}) From dae4d5f10aa769962aa682aaa2abc19e1a1d883c Mon Sep 17 00:00:00 2001 From: Thomas Klausner <tk@giga.or.at> Date: Sun, 4 Jul 2021 14:44:15 +0200 Subject: [PATCH 135/884] Add NetBSD support to setup.py. --- setup.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setup.py b/setup.py index 8143612e4..44e065678 100644 --- a/setup.py +++ b/setup.py @@ -39,6 +39,9 @@ # platform.dist() returns "('', '', '')" on FreeBSD elif OS == 'FreeBSD': DIST, DIST_VERSION, DIST_NAME = ('', '', '') + # platform.dist() does not exist on NetBSD + elif OS == 'NetBSD': + DIST, DIST_VERSION, DIST_NAME = ('', '', '') else: DIST, DIST_VERSION, DIST_NAME = platform.dist() NAME = NAME.lower() From 9ac8912fa5e2ed206977871ae688a61f364439e3 Mon Sep 17 00:00:00 2001 From: Fabian <11352551+fabm3n@users.noreply.github.com> Date: Tue, 27 Jul 2021 22:50:00 +0200 Subject: [PATCH 136/884] Fix SnagView interpreting all up and ok as pending If i execute this in python: print(int(0 or 4)) It always prints the number 4. So all Hosts and Services which are Up or Ok will be skipped because Nagstamon thinks they are pending (Status 4). --- Nagstamon/Servers/SnagView3.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Servers/SnagView3.py b/Nagstamon/Servers/SnagView3.py index 57baba534..5164be381 100644 --- a/Nagstamon/Servers/SnagView3.py +++ b/Nagstamon/Servers/SnagView3.py @@ -198,7 +198,7 @@ def _get_status(self): h = dict(host) # Skip if Host is 'Pending' - if int(h['sv_host__nagios_status__current_state'] or 4) == 4: + if int(h['sv_host__nagios_status__current_state']) == 4: continue # host @@ -279,8 +279,8 @@ def _get_status(self): s = dict(service) # Skip if Host or Service is 'Pending' - if int(s['sv_service_status__nagios_status__current_state'] or 4) == 4 or int( - s['sv_host__nagios_status__current_state'] or 4) == 4: + if int(s['sv_service_status__nagios_status__current_state']) == 4 or int( + s['sv_host__nagios_status__current_state']) == 4: continue # host and service From cdb941a4de409f463c88cc107125c2d3019e2b08 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Mon, 9 Aug 2021 11:02:45 +0200 Subject: [PATCH 137/884] last commits might condense in a new version --- Nagstamon/Config.py | 2 +- build/requirements/macos.txt | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 0725cb163..29499708b 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.7-20210518' + VERSION = '3.7-20210809' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2021 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index 638f256d6..3cde90ae7 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -8,6 +8,7 @@ pyqt5==5.15.3 pysocks python-dateutil requests -requests-gssapi requests-ecp +# gssapi instead kerberos +requests-gssapi setuptools==44.1.1 From ec5ce8955b73ca1cf1cb6fa9c81939dabc8fb783 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Mon, 9 Aug 2021 11:25:12 +0200 Subject: [PATCH 138/884] try to fix https://github.com/HenriWahl/Nagstamon/issues/753 --- Nagstamon/Servers/Alertmanager.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Servers/Alertmanager.py b/Nagstamon/Servers/Alertmanager.py index 634798a90..c1d50e5e7 100644 --- a/Nagstamon/Servers/Alertmanager.py +++ b/Nagstamon/Servers/Alertmanager.py @@ -212,8 +212,11 @@ def _get_status(self): -----------------------------------------------------------------------------------------------------------------------------", result.status_code, result.result) else: log.error("received status code '%s'", result.status_code) - - data = json.loads(result.result) + # when result is not JSON catch it + try: + data = json.loads(result.result) + except json.decoder.JSONDecodeError: + data = '' error = result.error status_code = result.status_code From 44062c57b7f364a4aeb04dcf5e2cb1a25e523ff7 Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Mon, 9 Aug 2021 17:26:01 +0200 Subject: [PATCH 139/884] 20210809 --- Nagstamon/Servers/Alertmanager.py | 2 +- build/build.py | 1 - build/debian/changelog | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Nagstamon/Servers/Alertmanager.py b/Nagstamon/Servers/Alertmanager.py index c1d50e5e7..a544c8071 100644 --- a/Nagstamon/Servers/Alertmanager.py +++ b/Nagstamon/Servers/Alertmanager.py @@ -216,7 +216,7 @@ def _get_status(self): try: data = json.loads(result.result) except json.decoder.JSONDecodeError: - data = '' + data = {} error = result.error status_code = result.status_code diff --git a/build/build.py b/build/build.py index edc0ab8df..fb296f32a 100644 --- a/build/build.py +++ b/build/build.py @@ -140,7 +140,6 @@ def macmain(): os.chdir(CURRENT_DIR) # template own modified Info.plist for Retina compatibility - #shutil.copyfile('../Nagstamon/resources/Info.plist', '../dist/Nagstamon.app/Contents/Info.plist') with open("../Nagstamon/resources/Info.plist", "rt") as fin: with open("../dist/Nagstamon.app/Contents/Info.plist", "wt") as fout: for line in fin: diff --git a/build/debian/changelog b/build/debian/changelog index 7becc9cce..a91b5c431 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.7-202105018) unstable; urgency=low +nagstamon (3.7-20210809) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Sat, 15 May 2021 19:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Mon, 09 Aug 2021 19:00:00 +0100 nagstamon (3.6.0) stable; urgency=low * New upstream From a56c3dc3f13fc128f20e04d5bf1bb5aea3b264b3 Mon Sep 17 00:00:00 2001 From: hunsbea <aahunsberger@gmail.com> Date: Sat, 14 Aug 2021 00:41:32 +0800 Subject: [PATCH 140/884] fix dup downtime crash + add app --- Nagstamon/Servers/Zabbix.py | 40 ++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Servers/Zabbix.py b/Nagstamon/Servers/Zabbix.py index dd55577cd..4aba9ab1e 100644 --- a/Nagstamon/Servers/Zabbix.py +++ b/Nagstamon/Servers/Zabbix.py @@ -97,7 +97,14 @@ def _login(self): def getLastApp(self, this_item): if len(this_item) > 0: if "applications" not in this_item[0]: - return "NO APP" + if 'tags' in this_item[0]: + app = "NO APP" + for tag in this_item[0]['tags']: + if tag['tag'] == 'Application': + app = tag['value'] + return app + else: + return "NO APP" last_app = len(this_item[0]['applications']) - 1 # use it to get the last application name if last_app > -1: return "%s" % this_item[0]['applications'][last_app]['name'] @@ -288,6 +295,7 @@ def _get_status(self): this_item = self.zapi.item.get( {'itemids': [t['items'][0]['itemid']], 'output': ['itemid', 'hostid', 'name', 'lastvalue'], + 'selectTags': 'extend', 'selectApplications': 'extend'} ) t['application'] = self.getLastApp(this_item) @@ -492,6 +500,23 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi def _set_downtime(self, hostname, service, author, comment, fixed, start_time, end_time, hours, minutes): + # Check if there is an associated Application tag with this trigger/item + app = None + triggers = self.zapi.trigger.get({ + 'selectItems': ['itemid'], + 'output': ['triggerid'], + 'filter': {'triggerid': service}}) + #import pdb; pdb.set_trace() + if triggers and triggers[0]['items']: + items = self.zapi.item.get({ + 'itemids': [triggers[0]['items'][0]['itemid']], + 'output': ['itemid'], + 'selectTags': 'extend'}) + if items and items[0]['tags']: + for tag in items[0]['tags']: + if tag['tag'] == 'Application': + app = tag['value'] + hostids = [ self.hosts[hostname].hostid ] date = datetime.datetime.strptime(start_time, "%Y-%m-%d %H:%M") @@ -503,9 +528,18 @@ def _set_downtime(self, hostname, service, author, comment, fixed, start_time, e if conf.debug_mode is True: self.Debug(server=self.get_name(), debug="Downtime for " + hostname + "[" + str(hostids[0]) + "] stime:" + str(stime) + " etime:" + str(etime)) - self.zapi.maintenance.create({'hostids': hostids, 'name': comment, 'description': author, 'active_since': stime, 'active_till': etime, 'maintenance_type' : 0, "timeperiods": [ + body = {'hostids': hostids, 'name': comment, 'description': author, 'active_since': stime, 'active_till': etime, 'maintenance_type' : 0, "timeperiods": [ { "timeperiod_type": 0, "start_date": stime, "period": etime - stime } - ]}) + ]} + if app: + body['tags'] = [{'tag': 'Application', 'operator': 0, 'value': app}] + body['description'] += ' ' + body['name'] + body['name'] = f'{hostname} {app}' + + try: + self.zapi.maintenance.create(body) + except Already_Exists: + self.Debug(server=self.get_name(), debug=f"Maintanence with name {body['name']} already exists") def get_start_end(self, host): return time.strftime("%Y-%m-%d %H:%M"), time.strftime("%Y-%m-%d %H:%M", time.localtime(time.time() + 7200)) From 4d00bf49e623cdceebdf8e6425e187d16136d8ec Mon Sep 17 00:00:00 2001 From: hunsbea <aahunsberger@gmail.com> Date: Sat, 14 Aug 2021 09:54:43 +0800 Subject: [PATCH 141/884] allow zabbix to ack events without closing --- Nagstamon/Servers/Zabbix.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/Nagstamon/Servers/Zabbix.py b/Nagstamon/Servers/Zabbix.py index 4aba9ab1e..f9cec6c61 100644 --- a/Nagstamon/Servers/Zabbix.py +++ b/Nagstamon/Servers/Zabbix.py @@ -492,11 +492,25 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi # If events pending of acknowledge, execute ack if len(events) > 0: + # actions is a bitmask with values: + # 1 - close problem + # 2 - acknowledge event + # 4 - add message + # 8 - change severity + # 16 - unacknowledge event + actions = 0 # If sticky is set then close only current event - close = 1 if (triggerid == service and sticky is True) else 0 + if triggerid == service and sticky is True: + actions |= 1 + # The current Nagstamon menu items don't match up too well with the Zabbix actions, + # but perhaps "Persistent comment" is the closest thing to acknowledgement + if persistent: + actions |= 2 + if comment: + actions |= 4 if conf.debug_mode is True: - self.Debug(server=self.get_name(), debug="Events to acknowledge: " + str(events) + " Close: " + str(close)) - self.zapi.event.acknowledge({'eventids': events, 'message': '%s: %s' % (author, comment), 'action': close}) + self.Debug(server=self.get_name(), debug="Events to acknowledge: " + str(events) + " Close: " + str(actions)) + self.zapi.event.acknowledge({'eventids': events, 'message': comment, 'action': actions}) def _set_downtime(self, hostname, service, author, comment, fixed, start_time, end_time, hours, minutes): @@ -506,7 +520,7 @@ def _set_downtime(self, hostname, service, author, comment, fixed, start_time, e 'selectItems': ['itemid'], 'output': ['triggerid'], 'filter': {'triggerid': service}}) - #import pdb; pdb.set_trace() + if triggers and triggers[0]['items']: items = self.zapi.item.get({ 'itemids': [triggers[0]['items'][0]['itemid']], From 71390934819a8de61d8520347f857a8f352da5c0 Mon Sep 17 00:00:00 2001 From: hunsbea <aahunsberger@gmail.com> Date: Sat, 14 Aug 2021 23:19:19 +0800 Subject: [PATCH 142/884] fix zabbix crash closing unclosable event --- Nagstamon/Servers/Zabbix.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Servers/Zabbix.py b/Nagstamon/Servers/Zabbix.py index f9cec6c61..281961be3 100644 --- a/Nagstamon/Servers/Zabbix.py +++ b/Nagstamon/Servers/Zabbix.py @@ -500,8 +500,13 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi # 16 - unacknowledge event actions = 0 # If sticky is set then close only current event - if triggerid == service and sticky is True: - actions |= 1 + if triggerid == service and sticky: + # do not send the "Close" flag if this event does not allow manual closing + triggers = self.zapi.trigger.get({ + 'output': ['triggerid', 'manual_close'], + 'filter': {'triggerid': triggerid}}) + if not triggers or 'manual_close' not in triggers[0] or triggers[0]['manual_close'] == 1: + actions |= 1 # The current Nagstamon menu items don't match up too well with the Zabbix actions, # but perhaps "Persistent comment" is the closest thing to acknowledgement if persistent: From 3ff4b264dc8438185a0f658185ba6372bb54465d Mon Sep 17 00:00:00 2001 From: Henri Wahl <h.wahl@ifw-dresden.de> Date: Mon, 16 Aug 2021 00:42:44 +0200 Subject: [PATCH 143/884] 20210815 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 29499708b..2ebf1e5d7 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.7-20210809' + VERSION = '3.7-20210815' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2021 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index a91b5c431..530f694ce 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.7-20210809) unstable; urgency=low +nagstamon (3.7-20210815) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Mon, 09 Aug 2021 19:00:00 +0100 From 8b02100c599d163e7fdcb333689c1ead9aa4f56e Mon Sep 17 00:00:00 2001 From: hunsbea <aahunsberger@gmail.com> Date: Mon, 16 Aug 2021 10:57:14 +0800 Subject: [PATCH 144/884] zabbix account for expired maintenance --- Nagstamon/Servers/Zabbix.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Nagstamon/Servers/Zabbix.py b/Nagstamon/Servers/Zabbix.py index 281961be3..38004fb2b 100644 --- a/Nagstamon/Servers/Zabbix.py +++ b/Nagstamon/Servers/Zabbix.py @@ -553,8 +553,7 @@ def _set_downtime(self, hostname, service, author, comment, fixed, start_time, e if app: body['tags'] = [{'tag': 'Application', 'operator': 0, 'value': app}] body['description'] += ' ' + body['name'] - body['name'] = f'{hostname} {app}' - + body['name'] = f'{start_time} {hostname} {app}' try: self.zapi.maintenance.create(body) except Already_Exists: From 1083d93424cb2621b0f0cfc88e435f27d3063c2f Mon Sep 17 00:00:00 2001 From: Sam Edwards <sam@samedwards.ca> Date: Thu, 19 Aug 2021 15:22:35 -0700 Subject: [PATCH 145/884] Build macos release from static specfile with dynamic version --- Nagstamon/resources/Info.plist | 28 ------------------- build/Nagstamon-macos.spec | 50 ++++++++++++++++++++++++++++++++++ build/build.py | 25 +++-------------- 3 files changed, 54 insertions(+), 49 deletions(-) delete mode 100644 Nagstamon/resources/Info.plist create mode 100644 build/Nagstamon-macos.spec diff --git a/Nagstamon/resources/Info.plist b/Nagstamon/resources/Info.plist deleted file mode 100644 index 98bd4e28e..000000000 --- a/Nagstamon/resources/Info.plist +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> -<plist version="1.0"> -<dict> - <key>CFBundleExecutable</key> - <string>MacOS/Nagstamon</string> - <key>CFBundleIconFile</key> - <string>nagstamon.icns</string> - <key>CFBundleName</key> - <string>Nagstamon</string> - <key>CFBundleDisplayName</key> - <string>Nagstamon</string> - <key>CFBundleIdentifier</key> - <string>de.ifw-dresden.nagstamon</string> - <key>NSHighResolutionCapable</key> - <true/> - <key>NSRequiresAquaSystemAppearance</key> - <false/> - <key>LSBackgroundOnly</key> - <string>0</string> - <key>CFBundlePackageType</key> - <string>APPL</string> - <key>CFBundleVersion</key> - <string>+++VERSION+++</string> - <key>CFBundleShortVersionString</key> - <string>+++VERSION+++</string> -</dict> -</plist> \ No newline at end of file diff --git a/build/Nagstamon-macos.spec b/build/Nagstamon-macos.spec new file mode 100644 index 000000000..b909478c0 --- /dev/null +++ b/build/Nagstamon-macos.spec @@ -0,0 +1,50 @@ +# -*- mode: python ; coding: utf-8 -*- + +import os + +block_cipher = None + +a = Analysis(['../nagstamon.py'], + pathex=[], + binaries=[], + datas=[('../Nagstamon/resources', 'Nagstamon/resources')], + hiddenimports=[], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher, + noarchive=False) +pyz = PYZ(a.pure, a.zipped_data, + cipher=block_cipher) + +exe = EXE(pyz, + a.scripts, + a.binaries, + a.zipfiles, + a.datas, + [], + name='Nagstamon', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + upx_exclude=[], + runtime_tmpdir=None, + console=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, + icon='../Nagstamon/resources/nagstamon.icns') + +app = BUNDLE(exe, + name='Nagstamon.app', + icon='../Nagstamon/resources/nagstamon.icns', + bundle_identifier='de.ifw-dresden.nagstamon', + version=os.environ['NAGSTAMON_VERSION'], + info_plist={ + 'NSRequiresAquaSystemAppearance': False, + 'LSBackgroundOnly': False + }) diff --git a/build/build.py b/build/build.py index fb296f32a..fd09dae12 100644 --- a/build/build.py +++ b/build/build.py @@ -122,34 +122,17 @@ def macmain(): """ execute steps necessary for compilation of MacOS X binaries and .dmg file """ - # go one directory up and run pyinstaller - os.chdir('{0}{1}..'.format(CURRENT_DIR, os.sep)) + # can't pass --version to pyinstaller in spec mode, so export as env var + os.environ['NAGSTAMON_VERSION'] = VERSION # create one-file .app bundle by pyinstaller - subprocess.call(['/usr/local/bin/pyinstaller', - '--noconfirm', - '--add-data=Nagstamon/resources:Nagstamon/resources', - '--icon=Nagstamon/resources/nagstamon.icns', - '--name=Nagstamon', - '--osx-bundle-identifier=de.ifw-dresden.nagstamon', - '--windowed', - '--onefile', - 'nagstamon.py']) - - # go back to build directory - os.chdir(CURRENT_DIR) - - # template own modified Info.plist for Retina compatibility - with open("../Nagstamon/resources/Info.plist", "rt") as fin: - with open("../dist/Nagstamon.app/Contents/Info.plist", "wt") as fout: - for line in fin: - fout.write(line.replace('+++VERSION+++', VERSION)) + subprocess.call(['pyinstaller --noconfirm Nagstamon-macos.spec'], shell=True) # create staging DMG folder for later compressing of DMG shutil.rmtree('Nagstamon {0} Staging DMG'.format(VERSION), ignore_errors=True) # copy app bundle folder - shutil.move('../dist/Nagstamon.app', 'Nagstamon {0} Staging DMG/Nagstamon.app'.format(VERSION)) + shutil.move('dist/Nagstamon.app', 'Nagstamon {0} Staging DMG/Nagstamon.app'.format(VERSION)) # cleanup before new images get created for dmg_file in glob.iglob('*.dmg'): From a51e5413f701eaa4c950e977399d95669c141188 Mon Sep 17 00:00:00 2001 From: Stephan Schwarz <stearz@gmx.de> Date: Mon, 23 Aug 2021 16:03:44 +0200 Subject: [PATCH 146/884] alertmanager-1.2.0 --- .github/workflows/build-release-latest.yml | 4 +- .github/workflows/build-release-stable.yml | 4 +- .pylintrc | 2 + Nagstamon/Config.py | 7 +- Nagstamon/QUI/__init__.py | 11 +- Nagstamon/QUI/settings_server.py | 292 +++++++------- Nagstamon/QUI/settings_server.ui | 364 ++++++++++-------- Nagstamon/Servers/Alertmanager/CHANGELOG.md | 28 ++ Nagstamon/Servers/Alertmanager/LICENSE | 16 + Nagstamon/Servers/Alertmanager/README.md | 14 + .../__init__.py} | 253 ++++++------ Nagstamon/Servers/Alertmanager/helpers.py | 80 ++++ Nagstamon/Servers/Prometheus.py | 2 +- Nagstamon/Servers/__init__.py | 5 + tests/test_Alertmanager.py | 69 +++- tests/test_alertmanager_custom_severity.json | 32 ++ 16 files changed, 740 insertions(+), 443 deletions(-) create mode 100644 .pylintrc create mode 100644 Nagstamon/Servers/Alertmanager/CHANGELOG.md create mode 100644 Nagstamon/Servers/Alertmanager/LICENSE create mode 100644 Nagstamon/Servers/Alertmanager/README.md rename Nagstamon/Servers/{Alertmanager.py => Alertmanager/__init__.py} (59%) create mode 100644 Nagstamon/Servers/Alertmanager/helpers.py create mode 100644 tests/test_alertmanager_custom_severity.json diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 92bdae7b9..c60d3f43e 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -25,7 +25,7 @@ jobs: run: | sudo apt-get install libkrb5-dev python -m pip install --upgrade pip - pip install flake8 pytest pylint + pip install pytest pylint #flake8 if [ -f requirements.txt ]; then pip install -r requirements.txt; fi # - name: Lint with flake8 # run: | @@ -35,7 +35,7 @@ jobs: # flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - name: Test with unittest run: | - python -m unittest tests/test_Alertmanager.py + python -m unittest tests/test_*.py debian: runs-on: ubuntu-latest diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index 4853f5376..81ce9064e 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -24,7 +24,7 @@ jobs: run: | sudo apt-get install libkrb5-dev python -m pip install --upgrade pip - pip install flake8 pytest pylint + pip install pytest pylint #flake8 if [ -f requirements.txt ]; then pip install -r requirements.txt; fi # - name: Lint with flake8 # run: | @@ -34,7 +34,7 @@ jobs: # flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - name: Test with unittest run: | - python -m unittest tests/test_Alertmanager.py + python -m unittest tests/test_*.py debian: runs-on: ubuntu-latest diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 000000000..9e39fcf48 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,2 @@ +[MASTER] +disable=R,C,W \ No newline at end of file diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 2ebf1e5d7..aa933f075 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -965,11 +965,16 @@ def __init__(self): # Zabbix "Item Description" as "Service Name" self.use_description_name_service = False - # Prometheus mappings + # Prometheus/Alertmanager mappings self.map_to_hostname = "pod_name,namespace,instance" self.map_to_servicename = "alertname" self.map_to_status_information = "message,summary,description" + # Alertmanager mappings self.alertmanager_filter = '' + self.map_to_critical = 'critical,error' + self.map_to_warning = 'warning,warn' + self.map_to_unknown = 'unknown' + self.map_to_ok = 'ok' class Action(object): """ diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 40ca081a4..978176913 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -5770,8 +5770,17 @@ def __init__(self, dialog): self.ui.input_lineedit_map_to_servicename: ['Prometheus','Alertmanager'], self.ui.label_map_to_status_information: ['Prometheus','Alertmanager'], self.ui.input_lineedit_map_to_status_information: ['Prometheus','Alertmanager'], + self.ui.label_alertmanager_filter: ['Alertmanager'], self.ui.input_lineedit_alertmanager_filter: ['Alertmanager'], - self.ui.label_alertmanager_filter: ['Alertmanager']} + self.ui.label_map_to_ok: ['Alertmanager'], + self.ui.input_lineedit_map_to_ok: ['Alertmanager'], + self.ui.label_map_to_unknown: ['Alertmanager'], + self.ui.input_lineedit_map_to_unknown: ['Alertmanager'], + self.ui.label_map_to_warning: ['Alertmanager'], + self.ui.input_lineedit_map_to_warning: ['Alertmanager'], + self.ui.label_map_to_critical: ['Alertmanager'], + self.ui.input_lineedit_map_to_critical: ['Alertmanager'] + } # to be used when selecting authentication method Kerberos self.AUTHENTICATION_WIDGETS = [ diff --git a/Nagstamon/QUI/settings_server.py b/Nagstamon/QUI/settings_server.py index 22ee5a793..26e206c73 100644 --- a/Nagstamon/QUI/settings_server.py +++ b/Nagstamon/QUI/settings_server.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'Nagstamon/QUI/settings_server.ui' # -# Created by: PyQt5 UI code generator 5.15.2 +# Created by: PyQt5 UI code generator 5.15.4 # # WARNING: Any manual changes made to this file will be lost when pyuic5 is # run again. Do not edit this file unless you know what you are doing. @@ -174,17 +174,67 @@ def setupUi(self, settings_server): self.gridLayout_3.setHorizontalSpacing(10) self.gridLayout_3.setVerticalSpacing(5) self.gridLayout_3.setObjectName("gridLayout_3") - spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.gridLayout_3.addItem(spacerItem1, 5, 3, 1, 1) + self.horizontalLayout = QtWidgets.QHBoxLayout() + self.horizontalLayout.setObjectName("horizontalLayout") + self.input_lineedit_custom_cert_ca_file = QtWidgets.QLineEdit(self.groupbox_options) + self.input_lineedit_custom_cert_ca_file.setObjectName("input_lineedit_custom_cert_ca_file") + self.horizontalLayout.addWidget(self.input_lineedit_custom_cert_ca_file) + self.button_choose_custom_cert_ca_file = QtWidgets.QPushButton(self.groupbox_options) + self.button_choose_custom_cert_ca_file.setObjectName("button_choose_custom_cert_ca_file") + self.horizontalLayout.addWidget(self.button_choose_custom_cert_ca_file) + self.gridLayout_3.addLayout(self.horizontalLayout, 2, 2, 1, 2) self.input_checkbox_use_autologin = QtWidgets.QCheckBox(self.groupbox_options) self.input_checkbox_use_autologin.setObjectName("input_checkbox_use_autologin") self.gridLayout_3.addWidget(self.input_checkbox_use_autologin, 7, 1, 1, 3) - self.input_lineedit_map_to_servicename = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_map_to_servicename.setObjectName("input_lineedit_map_to_servicename") - self.gridLayout_3.addWidget(self.input_lineedit_map_to_servicename, 14, 2, 1, 2) - self.input_lineedit_monitor_site = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_monitor_site.setObjectName("input_lineedit_monitor_site") - self.gridLayout_3.addWidget(self.input_lineedit_monitor_site, 8, 2, 1, 1) + self.input_lineedit_alertmanager_filter = QtWidgets.QLineEdit(self.groupbox_options) + self.input_lineedit_alertmanager_filter.setObjectName("input_lineedit_alertmanager_filter") + self.gridLayout_3.addWidget(self.input_lineedit_alertmanager_filter, 12, 2, 1, 2) + self.label_map_to_hostname = QtWidgets.QLabel(self.groupbox_options) + self.label_map_to_hostname.setObjectName("label_map_to_hostname") + self.gridLayout_3.addWidget(self.label_map_to_hostname, 13, 1, 1, 1) + self.input_lineedit_map_to_hostname = QtWidgets.QLineEdit(self.groupbox_options) + self.input_lineedit_map_to_hostname.setObjectName("input_lineedit_map_to_hostname") + self.gridLayout_3.addWidget(self.input_lineedit_map_to_hostname, 13, 2, 1, 2) + self.input_checkbox_use_display_name_service = QtWidgets.QCheckBox(self.groupbox_options) + self.input_checkbox_use_display_name_service.setObjectName("input_checkbox_use_display_name_service") + self.gridLayout_3.addWidget(self.input_checkbox_use_display_name_service, 22, 1, 1, 3) + self.input_lineedit_autologin_key = QtWidgets.QLineEdit(self.groupbox_options) + self.input_lineedit_autologin_key.setText("") + self.input_lineedit_autologin_key.setObjectName("input_lineedit_autologin_key") + self.gridLayout_3.addWidget(self.input_lineedit_autologin_key, 9, 2, 1, 2) + self.input_lineedit_map_to_ok = QtWidgets.QLineEdit(self.groupbox_options) + self.input_lineedit_map_to_ok.setObjectName("input_lineedit_map_to_ok") + self.gridLayout_3.addWidget(self.input_lineedit_map_to_ok, 16, 2, 1, 2) + self.label_idp_ecp_endpoint = QtWidgets.QLabel(self.groupbox_options) + self.label_idp_ecp_endpoint.setObjectName("label_idp_ecp_endpoint") + self.gridLayout_3.addWidget(self.label_idp_ecp_endpoint, 26, 1, 1, 1) + self.label_alertmanager_filter = QtWidgets.QLabel(self.groupbox_options) + self.label_alertmanager_filter.setObjectName("label_alertmanager_filter") + self.gridLayout_3.addWidget(self.label_alertmanager_filter, 12, 1, 1, 1) + self.label_map_to_servicename = QtWidgets.QLabel(self.groupbox_options) + self.label_map_to_servicename.setObjectName("label_map_to_servicename") + self.gridLayout_3.addWidget(self.label_map_to_servicename, 14, 1, 1, 1) + self.input_checkbox_custom_cert_use = QtWidgets.QCheckBox(self.groupbox_options) + self.input_checkbox_custom_cert_use.setObjectName("input_checkbox_custom_cert_use") + self.gridLayout_3.addWidget(self.input_checkbox_custom_cert_use, 1, 1, 1, 2) + spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.gridLayout_3.addItem(spacerItem1, 6, 3, 1, 1) + self.input_lineedit_service_filter = QtWidgets.QLineEdit(self.groupbox_options) + self.input_lineedit_service_filter.setText("") + self.input_lineedit_service_filter.setObjectName("input_lineedit_service_filter") + self.gridLayout_3.addWidget(self.input_lineedit_service_filter, 11, 2, 1, 2) + self.label_timeout = QtWidgets.QLabel(self.groupbox_options) + self.label_timeout.setObjectName("label_timeout") + self.gridLayout_3.addWidget(self.label_timeout, 6, 1, 1, 1) + self.input_checkbox_force_authuser = QtWidgets.QCheckBox(self.groupbox_options) + self.input_checkbox_force_authuser.setObjectName("input_checkbox_force_authuser") + self.gridLayout_3.addWidget(self.input_checkbox_force_authuser, 24, 1, 1, 3) + self.input_lineedit_map_to_status_information = QtWidgets.QLineEdit(self.groupbox_options) + self.input_lineedit_map_to_status_information.setObjectName("input_lineedit_map_to_status_information") + self.gridLayout_3.addWidget(self.input_lineedit_map_to_status_information, 15, 2, 1, 2) + self.input_checkbox_no_cookie_auth = QtWidgets.QCheckBox(self.groupbox_options) + self.input_checkbox_no_cookie_auth.setObjectName("input_checkbox_no_cookie_auth") + self.gridLayout_3.addWidget(self.input_checkbox_no_cookie_auth, 20, 1, 1, 3) self.label_autologin_key = QtWidgets.QLabel(self.groupbox_options) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) @@ -193,22 +243,46 @@ def setupUi(self, settings_server): self.label_autologin_key.setSizePolicy(sizePolicy) self.label_autologin_key.setObjectName("label_autologin_key") self.gridLayout_3.addWidget(self.label_autologin_key, 9, 1, 1, 1) - self.input_lineedit_service_filter = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_service_filter.setText("") - self.input_lineedit_service_filter.setObjectName("input_lineedit_service_filter") - self.gridLayout_3.addWidget(self.input_lineedit_service_filter, 11, 2, 1, 2) - self.label_map_to_status_information = QtWidgets.QLabel(self.groupbox_options) - self.label_map_to_status_information.setObjectName("label_map_to_status_information") - self.gridLayout_3.addWidget(self.label_map_to_status_information, 15, 1, 1, 1) - self.horizontalLayout = QtWidgets.QHBoxLayout() - self.horizontalLayout.setObjectName("horizontalLayout") - self.input_lineedit_custom_cert_ca_file = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_custom_cert_ca_file.setObjectName("input_lineedit_custom_cert_ca_file") - self.horizontalLayout.addWidget(self.input_lineedit_custom_cert_ca_file) - self.button_choose_custom_cert_ca_file = QtWidgets.QPushButton(self.groupbox_options) - self.button_choose_custom_cert_ca_file.setObjectName("button_choose_custom_cert_ca_file") - self.horizontalLayout.addWidget(self.button_choose_custom_cert_ca_file) - self.gridLayout_3.addLayout(self.horizontalLayout, 2, 2, 1, 2) + spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.gridLayout_3.addItem(spacerItem2, 5, 3, 1, 1) + self.input_lineedit_idp_ecp_endpoint = QtWidgets.QLineEdit(self.groupbox_options) + self.input_lineedit_idp_ecp_endpoint.setObjectName("input_lineedit_idp_ecp_endpoint") + self.gridLayout_3.addWidget(self.input_lineedit_idp_ecp_endpoint, 26, 2, 1, 2) + self.label_auth_type = QtWidgets.QLabel(self.groupbox_options) + self.label_auth_type.setObjectName("label_auth_type") + self.gridLayout_3.addWidget(self.label_auth_type, 5, 1, 1, 1) + self.label_service_filter = QtWidgets.QLabel(self.groupbox_options) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_service_filter.sizePolicy().hasHeightForWidth()) + self.label_service_filter.setSizePolicy(sizePolicy) + self.label_service_filter.setObjectName("label_service_filter") + self.gridLayout_3.addWidget(self.label_service_filter, 11, 1, 1, 1) + self.horizontalLayout_timeout_seconds = QtWidgets.QHBoxLayout() + self.horizontalLayout_timeout_seconds.setSpacing(5) + self.horizontalLayout_timeout_seconds.setObjectName("horizontalLayout_timeout_seconds") + self.input_spinbox_timeout = QtWidgets.QSpinBox(self.groupbox_options) + self.input_spinbox_timeout.setObjectName("input_spinbox_timeout") + self.horizontalLayout_timeout_seconds.addWidget(self.input_spinbox_timeout) + self.label_timeout_sec = QtWidgets.QLabel(self.groupbox_options) + self.label_timeout_sec.setObjectName("label_timeout_sec") + self.horizontalLayout_timeout_seconds.addWidget(self.label_timeout_sec) + self.gridLayout_3.addLayout(self.horizontalLayout_timeout_seconds, 6, 2, 1, 1) + self.label_monitor_site = QtWidgets.QLabel(self.groupbox_options) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_monitor_site.sizePolicy().hasHeightForWidth()) + self.label_monitor_site.setSizePolicy(sizePolicy) + self.label_monitor_site.setObjectName("label_monitor_site") + self.gridLayout_3.addWidget(self.label_monitor_site, 8, 1, 1, 1) + self.input_checkbox_use_description_name_service = QtWidgets.QCheckBox(self.groupbox_options) + self.input_checkbox_use_description_name_service.setObjectName("input_checkbox_use_description_name_service") + self.gridLayout_3.addWidget(self.input_checkbox_use_description_name_service, 23, 1, 1, 3) + self.input_combobox_authentication = QtWidgets.QComboBox(self.groupbox_options) + self.input_combobox_authentication.setObjectName("input_combobox_authentication") + self.gridLayout_3.addWidget(self.input_combobox_authentication, 5, 2, 1, 1) self.groupbox_checkmk_views = QtWidgets.QGroupBox(self.groupbox_options) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) sizePolicy.setHorizontalStretch(0) @@ -236,51 +310,7 @@ def setupUi(self, settings_server): self.button_checkmk_view_services_reset = QtWidgets.QPushButton(self.groupbox_checkmk_views) self.button_checkmk_view_services_reset.setObjectName("button_checkmk_view_services_reset") self.gridLayout_4.addWidget(self.button_checkmk_view_services_reset, 1, 2, 1, 1) - self.gridLayout_3.addWidget(self.groupbox_checkmk_views, 21, 1, 1, 3) - self.label_timeout = QtWidgets.QLabel(self.groupbox_options) - self.label_timeout.setObjectName("label_timeout") - self.gridLayout_3.addWidget(self.label_timeout, 6, 1, 1, 1) - self.horizontalLayout_timeout_seconds = QtWidgets.QHBoxLayout() - self.horizontalLayout_timeout_seconds.setSpacing(5) - self.horizontalLayout_timeout_seconds.setObjectName("horizontalLayout_timeout_seconds") - self.input_spinbox_timeout = QtWidgets.QSpinBox(self.groupbox_options) - self.input_spinbox_timeout.setObjectName("input_spinbox_timeout") - self.horizontalLayout_timeout_seconds.addWidget(self.input_spinbox_timeout) - self.label_timeout_sec = QtWidgets.QLabel(self.groupbox_options) - self.label_timeout_sec.setObjectName("label_timeout_sec") - self.horizontalLayout_timeout_seconds.addWidget(self.label_timeout_sec) - self.gridLayout_3.addLayout(self.horizontalLayout_timeout_seconds, 6, 2, 1, 1) - self.label_auth_type = QtWidgets.QLabel(self.groupbox_options) - self.label_auth_type.setObjectName("label_auth_type") - self.gridLayout_3.addWidget(self.label_auth_type, 5, 1, 1, 1) - self.input_combobox_authentication = QtWidgets.QComboBox(self.groupbox_options) - self.input_combobox_authentication.setObjectName("input_combobox_authentication") - self.gridLayout_3.addWidget(self.input_combobox_authentication, 5, 2, 1, 1) - self.input_checkbox_custom_cert_use = QtWidgets.QCheckBox(self.groupbox_options) - self.input_checkbox_custom_cert_use.setObjectName("input_checkbox_custom_cert_use") - self.gridLayout_3.addWidget(self.input_checkbox_custom_cert_use, 1, 1, 1, 2) - self.input_checkbox_no_cookie_auth = QtWidgets.QCheckBox(self.groupbox_options) - self.input_checkbox_no_cookie_auth.setObjectName("input_checkbox_no_cookie_auth") - self.gridLayout_3.addWidget(self.input_checkbox_no_cookie_auth, 16, 1, 1, 3) - self.input_checkbox_force_authuser = QtWidgets.QCheckBox(self.groupbox_options) - self.input_checkbox_force_authuser.setObjectName("input_checkbox_force_authuser") - self.gridLayout_3.addWidget(self.input_checkbox_force_authuser, 20, 1, 1, 3) - self.input_lineedit_map_to_hostname = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_map_to_hostname.setObjectName("input_lineedit_map_to_hostname") - self.gridLayout_3.addWidget(self.input_lineedit_map_to_hostname, 13, 2, 1, 2) - self.input_lineedit_host_filter = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_host_filter.setText("") - self.input_lineedit_host_filter.setObjectName("input_lineedit_host_filter") - self.gridLayout_3.addWidget(self.input_lineedit_host_filter, 10, 2, 1, 2) - self.input_lineedit_alertmanager_filter = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_alertmanager_filter.setObjectName("input_lineedit_alertmanager_filter") - self.gridLayout_3.addWidget(self.input_lineedit_alertmanager_filter, 12, 2, 1, 2) - self.input_checkbox_use_display_name_service = QtWidgets.QCheckBox(self.groupbox_options) - self.input_checkbox_use_display_name_service.setObjectName("input_checkbox_use_display_name_service") - self.gridLayout_3.addWidget(self.input_checkbox_use_display_name_service, 18, 1, 1, 3) - self.input_checkbox_ignore_cert = QtWidgets.QCheckBox(self.groupbox_options) - self.input_checkbox_ignore_cert.setObjectName("input_checkbox_ignore_cert") - self.gridLayout_3.addWidget(self.input_checkbox_ignore_cert, 0, 1, 1, 3) + self.gridLayout_3.addWidget(self.groupbox_checkmk_views, 25, 1, 1, 3) self.label_host_filter = QtWidgets.QLabel(self.groupbox_options) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) @@ -289,55 +319,49 @@ def setupUi(self, settings_server): self.label_host_filter.setSizePolicy(sizePolicy) self.label_host_filter.setObjectName("label_host_filter") self.gridLayout_3.addWidget(self.label_host_filter, 10, 1, 1, 1) - self.input_lineedit_map_to_status_information = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_map_to_status_information.setObjectName("input_lineedit_map_to_status_information") - self.gridLayout_3.addWidget(self.input_lineedit_map_to_status_information, 15, 2, 1, 2) - self.label_monitor_site = QtWidgets.QLabel(self.groupbox_options) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_monitor_site.sizePolicy().hasHeightForWidth()) - self.label_monitor_site.setSizePolicy(sizePolicy) - self.label_monitor_site.setObjectName("label_monitor_site") - self.gridLayout_3.addWidget(self.label_monitor_site, 8, 1, 1, 1) - self.label_service_filter = QtWidgets.QLabel(self.groupbox_options) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_service_filter.sizePolicy().hasHeightForWidth()) - self.label_service_filter.setSizePolicy(sizePolicy) - self.label_service_filter.setObjectName("label_service_filter") - self.gridLayout_3.addWidget(self.label_service_filter, 11, 1, 1, 1) - self.input_checkbox_use_description_name_service = QtWidgets.QCheckBox(self.groupbox_options) - self.input_checkbox_use_description_name_service.setObjectName("input_checkbox_use_description_name_service") - self.gridLayout_3.addWidget(self.input_checkbox_use_description_name_service, 19, 1, 1, 3) - self.label_map_to_hostname = QtWidgets.QLabel(self.groupbox_options) - self.label_map_to_hostname.setObjectName("label_map_to_hostname") - self.gridLayout_3.addWidget(self.label_map_to_hostname, 13, 1, 1, 1) + self.input_checkbox_ignore_cert = QtWidgets.QCheckBox(self.groupbox_options) + self.input_checkbox_ignore_cert.setObjectName("input_checkbox_ignore_cert") + self.gridLayout_3.addWidget(self.input_checkbox_ignore_cert, 0, 1, 1, 3) + self.input_lineedit_map_to_servicename = QtWidgets.QLineEdit(self.groupbox_options) + self.input_lineedit_map_to_servicename.setObjectName("input_lineedit_map_to_servicename") + self.gridLayout_3.addWidget(self.input_lineedit_map_to_servicename, 14, 2, 1, 2) + self.input_lineedit_host_filter = QtWidgets.QLineEdit(self.groupbox_options) + self.input_lineedit_host_filter.setText("") + self.input_lineedit_host_filter.setObjectName("input_lineedit_host_filter") + self.gridLayout_3.addWidget(self.input_lineedit_host_filter, 10, 2, 1, 2) self.label_custom_ca_file = QtWidgets.QLabel(self.groupbox_options) self.label_custom_ca_file.setObjectName("label_custom_ca_file") self.gridLayout_3.addWidget(self.label_custom_ca_file, 2, 1, 1, 1) + self.label_map_to_status_information = QtWidgets.QLabel(self.groupbox_options) + self.label_map_to_status_information.setObjectName("label_map_to_status_information") + self.gridLayout_3.addWidget(self.label_map_to_status_information, 15, 1, 1, 1) + self.input_lineedit_monitor_site = QtWidgets.QLineEdit(self.groupbox_options) + self.input_lineedit_monitor_site.setObjectName("input_lineedit_monitor_site") + self.gridLayout_3.addWidget(self.input_lineedit_monitor_site, 8, 2, 1, 1) self.input_checkbox_use_display_name_host = QtWidgets.QCheckBox(self.groupbox_options) self.input_checkbox_use_display_name_host.setObjectName("input_checkbox_use_display_name_host") - self.gridLayout_3.addWidget(self.input_checkbox_use_display_name_host, 17, 1, 1, 3) - spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.gridLayout_3.addItem(spacerItem2, 6, 3, 1, 1) - self.label_map_to_servicename = QtWidgets.QLabel(self.groupbox_options) - self.label_map_to_servicename.setObjectName("label_map_to_servicename") - self.gridLayout_3.addWidget(self.label_map_to_servicename, 14, 1, 1, 1) - self.input_lineedit_autologin_key = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_autologin_key.setText("") - self.input_lineedit_autologin_key.setObjectName("input_lineedit_autologin_key") - self.gridLayout_3.addWidget(self.input_lineedit_autologin_key, 9, 2, 1, 2) - self.label_alertmanager_filter = QtWidgets.QLabel(self.groupbox_options) - self.label_alertmanager_filter.setObjectName("label_alertmanager_filter") - self.gridLayout_3.addWidget(self.label_alertmanager_filter, 12, 1, 1, 1) - self.label_idp_ecp_endpoint = QtWidgets.QLabel(self.groupbox_options) - self.label_idp_ecp_endpoint.setObjectName("label_idp_ecp_endpoint") - self.gridLayout_3.addWidget(self.label_idp_ecp_endpoint, 22, 1, 1, 1) - self.input_lineedit_idp_ecp_endpoint = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_idp_ecp_endpoint.setObjectName("input_lineedit_idp_ecp_endpoint") - self.gridLayout_3.addWidget(self.input_lineedit_idp_ecp_endpoint, 22, 2, 1, 2) + self.gridLayout_3.addWidget(self.input_checkbox_use_display_name_host, 21, 1, 1, 3) + self.input_lineedit_map_to_warning = QtWidgets.QLineEdit(self.groupbox_options) + self.input_lineedit_map_to_warning.setObjectName("input_lineedit_map_to_warning") + self.gridLayout_3.addWidget(self.input_lineedit_map_to_warning, 17, 2, 1, 2) + self.input_lineedit_map_to_critical = QtWidgets.QLineEdit(self.groupbox_options) + self.input_lineedit_map_to_critical.setObjectName("input_lineedit_map_to_critical") + self.gridLayout_3.addWidget(self.input_lineedit_map_to_critical, 18, 2, 1, 2) + self.input_lineedit_map_to_unknown = QtWidgets.QLineEdit(self.groupbox_options) + self.input_lineedit_map_to_unknown.setObjectName("input_lineedit_map_to_unknown") + self.gridLayout_3.addWidget(self.input_lineedit_map_to_unknown, 19, 2, 1, 2) + self.label_map_to_ok = QtWidgets.QLabel(self.groupbox_options) + self.label_map_to_ok.setObjectName("label_map_to_ok") + self.gridLayout_3.addWidget(self.label_map_to_ok, 16, 1, 1, 1) + self.label_map_to_warning = QtWidgets.QLabel(self.groupbox_options) + self.label_map_to_warning.setObjectName("label_map_to_warning") + self.gridLayout_3.addWidget(self.label_map_to_warning, 17, 1, 1, 1) + self.label_map_to_critical = QtWidgets.QLabel(self.groupbox_options) + self.label_map_to_critical.setObjectName("label_map_to_critical") + self.gridLayout_3.addWidget(self.label_map_to_critical, 18, 1, 1, 1) + self.label_map_to_unknown = QtWidgets.QLabel(self.groupbox_options) + self.label_map_to_unknown.setObjectName("label_map_to_unknown") + self.gridLayout_3.addWidget(self.label_map_to_unknown, 19, 1, 1, 1) self.gridLayout.addWidget(self.groupbox_options, 28, 0, 1, 4) self.retranslateUi(settings_server) @@ -382,31 +406,35 @@ def retranslateUi(self, settings_server): self.input_checkbox_save_password.setText(_translate("settings_server", "Save password")) self.input_checkbox_show_options.setText(_translate("settings_server", "Show more options")) self.groupbox_options.setTitle(_translate("settings_server", "Options:")) + self.button_choose_custom_cert_ca_file.setText(_translate("settings_server", "Choose file...")) self.input_checkbox_use_autologin.setText(_translate("settings_server", "Use autologin")) - self.input_lineedit_monitor_site.setText(_translate("settings_server", "Site 1")) + self.label_map_to_hostname.setText(_translate("settings_server", "Map to hostname:")) + self.input_checkbox_use_display_name_service.setText(_translate("settings_server", "Use display name as service name")) + self.label_idp_ecp_endpoint.setText(_translate("settings_server", "IdP ECP endpoint URL")) + self.label_alertmanager_filter.setText(_translate("settings_server", "Filter:")) + self.label_map_to_servicename.setText(_translate("settings_server", "Map to servicename:")) + self.input_checkbox_custom_cert_use.setText(_translate("settings_server", "Use custom CA file")) + self.label_timeout.setText(_translate("settings_server", "Timeout:")) + self.input_checkbox_force_authuser.setText(_translate("settings_server", "Only show permitted hosts for \"see all\" users (1.4.0i1 or newer)")) + self.input_checkbox_no_cookie_auth.setText(_translate("settings_server", "Do not use cookie authentication")) self.label_autologin_key.setText(_translate("settings_server", "Autologin Key:")) - self.label_map_to_status_information.setText(_translate("settings_server", "Map to status_information:")) - self.button_choose_custom_cert_ca_file.setText(_translate("settings_server", "Choose file...")) + self.label_auth_type.setText(_translate("settings_server", "Authentication:")) + self.label_service_filter.setText(_translate("settings_server", "Service filter:")) + self.label_timeout_sec.setText(_translate("settings_server", "seconds")) + self.label_monitor_site.setText(_translate("settings_server", "Monitor Site:")) + self.input_checkbox_use_description_name_service.setText(_translate("settings_server", "Use description as service name")) self.groupbox_checkmk_views.setTitle(_translate("settings_server", "Views:")) self.label_checkmk_view_hosts.setText(_translate("settings_server", "Hosts:")) self.label_checkmk_view_services.setText(_translate("settings_server", "Services:")) self.button_checkmk_view_hosts_reset.setText(_translate("settings_server", "Reset")) self.button_checkmk_view_services_reset.setText(_translate("settings_server", "Reset")) - self.label_timeout.setText(_translate("settings_server", "Timeout:")) - self.label_timeout_sec.setText(_translate("settings_server", "seconds")) - self.label_auth_type.setText(_translate("settings_server", "Authentication:")) - self.input_checkbox_custom_cert_use.setText(_translate("settings_server", "Use custom CA file")) - self.input_checkbox_no_cookie_auth.setText(_translate("settings_server", "Do not use cookie authentication")) - self.input_checkbox_force_authuser.setText(_translate("settings_server", "Only show permitted hosts for \"see all\" users (1.4.0i1 or newer)")) - self.input_checkbox_use_display_name_service.setText(_translate("settings_server", "Use display name as service name")) - self.input_checkbox_ignore_cert.setText(_translate("settings_server", "Ignore SSL/TLS certificate")) self.label_host_filter.setText(_translate("settings_server", "Host filter:")) - self.label_monitor_site.setText(_translate("settings_server", "Monitor Site:")) - self.label_service_filter.setText(_translate("settings_server", "Service filter:")) - self.input_checkbox_use_description_name_service.setText(_translate("settings_server", "Use description as service name")) - self.label_map_to_hostname.setText(_translate("settings_server", "Map to hostname:")) + self.input_checkbox_ignore_cert.setText(_translate("settings_server", "Ignore SSL/TLS certificate")) self.label_custom_ca_file.setText(_translate("settings_server", "Custom CA file: ")) + self.label_map_to_status_information.setText(_translate("settings_server", "Map to status_information:")) + self.input_lineedit_monitor_site.setText(_translate("settings_server", "Site 1")) self.input_checkbox_use_display_name_host.setText(_translate("settings_server", "Use display name as host name")) - self.label_map_to_servicename.setText(_translate("settings_server", "Map to servicename:")) - self.label_alertmanager_filter.setText(_translate("settings_server", "Filter:")) - self.label_idp_ecp_endpoint.setText(_translate("settings_server", "IdP ECP endpoint URL")) + self.label_map_to_ok.setText(_translate("settings_server", "Map to OK")) + self.label_map_to_warning.setText(_translate("settings_server", "Map to WARNING")) + self.label_map_to_critical.setText(_translate("settings_server", "Map to CRITICAL")) + self.label_map_to_unknown.setText(_translate("settings_server", "Map to UNKNOWN")) diff --git a/Nagstamon/QUI/settings_server.ui b/Nagstamon/QUI/settings_server.ui index 797ca8c56..a7b7a1ffe 100644 --- a/Nagstamon/QUI/settings_server.ui +++ b/Nagstamon/QUI/settings_server.ui @@ -344,8 +344,87 @@ <property name="verticalSpacing"> <number>5</number> </property> - <item row="5" column="3"> - <spacer name="horizontalSpacer_2"> + <item row="2" column="2" colspan="2"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLineEdit" name="input_lineedit_custom_cert_ca_file"/> + </item> + <item> + <widget class="QPushButton" name="button_choose_custom_cert_ca_file"> + <property name="text"> + <string>Choose file...</string> + </property> + </widget> + </item> + </layout> + </item> + <item row="7" column="1" colspan="3"> + <widget class="QCheckBox" name="input_checkbox_use_autologin"> + <property name="text"> + <string>Use autologin</string> + </property> + </widget> + </item> + <item row="12" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_alertmanager_filter"/> + </item> + <item row="13" column="1"> + <widget class="QLabel" name="label_map_to_hostname"> + <property name="text"> + <string>Map to hostname:</string> + </property> + </widget> + </item> + <item row="13" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_map_to_hostname"/> + </item> + <item row="22" column="1" colspan="3"> + <widget class="QCheckBox" name="input_checkbox_use_display_name_service"> + <property name="text"> + <string>Use display name as service name</string> + </property> + </widget> + </item> + <item row="9" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_autologin_key"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="16" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_map_to_ok"/> + </item> + <item row="26" column="1"> + <widget class="QLabel" name="label_idp_ecp_endpoint"> + <property name="text"> + <string>IdP ECP endpoint URL</string> + </property> + </widget> + </item> + <item row="12" column="1"> + <widget class="QLabel" name="label_alertmanager_filter"> + <property name="text"> + <string>Filter:</string> + </property> + </widget> + </item> + <item row="14" column="1"> + <widget class="QLabel" name="label_map_to_servicename"> + <property name="text"> + <string>Map to servicename:</string> + </property> + </widget> + </item> + <item row="1" column="1" colspan="2"> + <widget class="QCheckBox" name="input_checkbox_custom_cert_use"> + <property name="text"> + <string>Use custom CA file</string> + </property> + </widget> + </item> + <item row="6" column="3"> + <spacer name="horizontalSpacer"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> @@ -357,20 +436,34 @@ </property> </spacer> </item> - <item row="7" column="1" colspan="3"> - <widget class="QCheckBox" name="input_checkbox_use_autologin"> + <item row="11" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_service_filter"> <property name="text"> - <string>Use autologin</string> + <string/> </property> </widget> </item> - <item row="14" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_map_to_servicename"/> + <item row="6" column="1"> + <widget class="QLabel" name="label_timeout"> + <property name="text"> + <string>Timeout:</string> + </property> + </widget> </item> - <item row="8" column="2"> - <widget class="QLineEdit" name="input_lineedit_monitor_site"> + <item row="24" column="1" colspan="3"> + <widget class="QCheckBox" name="input_checkbox_force_authuser"> <property name="text"> - <string>Site 1</string> + <string>Only show permitted hosts for "see all" users (1.4.0i1 or newer)</string> + </property> + </widget> + </item> + <item row="15" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_map_to_status_information"/> + </item> + <item row="20" column="1" colspan="3"> + <widget class="QCheckBox" name="input_checkbox_no_cookie_auth"> + <property name="text"> + <string>Do not use cookie authentication</string> </property> </widget> </item> @@ -387,35 +480,83 @@ </property> </widget> </item> - <item row="11" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_service_filter"> + <item row="5" column="3"> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="26" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_idp_ecp_endpoint"/> + </item> + <item row="5" column="1"> + <widget class="QLabel" name="label_auth_type"> <property name="text"> - <string/> + <string>Authentication:</string> </property> </widget> </item> - <item row="15" column="1"> - <widget class="QLabel" name="label_map_to_status_information"> + <item row="11" column="1"> + <widget class="QLabel" name="label_service_filter"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> <property name="text"> - <string>Map to status_information:</string> + <string>Service filter:</string> </property> </widget> </item> - <item row="2" column="2" colspan="2"> - <layout class="QHBoxLayout" name="horizontalLayout"> + <item row="6" column="2"> + <layout class="QHBoxLayout" name="horizontalLayout_timeout_seconds"> + <property name="spacing"> + <number>5</number> + </property> <item> - <widget class="QLineEdit" name="input_lineedit_custom_cert_ca_file"/> + <widget class="QSpinBox" name="input_spinbox_timeout"/> </item> <item> - <widget class="QPushButton" name="button_choose_custom_cert_ca_file"> + <widget class="QLabel" name="label_timeout_sec"> <property name="text"> - <string>Choose file...</string> + <string>seconds</string> </property> </widget> </item> </layout> </item> - <item row="21" column="1" colspan="3"> + <item row="8" column="1"> + <widget class="QLabel" name="label_monitor_site"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Monitor Site:</string> + </property> + </widget> + </item> + <item row="23" column="1" colspan="3"> + <widget class="QCheckBox" name="input_checkbox_use_description_name_service"> + <property name="text"> + <string>Use description as service name</string> + </property> + </widget> + </item> + <item row="5" column="2"> + <widget class="QComboBox" name="input_combobox_authentication"/> + </item> + <item row="25" column="1" colspan="3"> <widget class="QGroupBox" name="groupbox_checkmk_views"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> @@ -464,88 +605,6 @@ </layout> </widget> </item> - <item row="6" column="1"> - <widget class="QLabel" name="label_timeout"> - <property name="text"> - <string>Timeout:</string> - </property> - </widget> - </item> - <item row="6" column="2"> - <layout class="QHBoxLayout" name="horizontalLayout_timeout_seconds"> - <property name="spacing"> - <number>5</number> - </property> - <item> - <widget class="QSpinBox" name="input_spinbox_timeout"/> - </item> - <item> - <widget class="QLabel" name="label_timeout_sec"> - <property name="text"> - <string>seconds</string> - </property> - </widget> - </item> - </layout> - </item> - <item row="5" column="1"> - <widget class="QLabel" name="label_auth_type"> - <property name="text"> - <string>Authentication:</string> - </property> - </widget> - </item> - <item row="5" column="2"> - <widget class="QComboBox" name="input_combobox_authentication"/> - </item> - <item row="1" column="1" colspan="2"> - <widget class="QCheckBox" name="input_checkbox_custom_cert_use"> - <property name="text"> - <string>Use custom CA file</string> - </property> - </widget> - </item> - <item row="16" column="1" colspan="3"> - <widget class="QCheckBox" name="input_checkbox_no_cookie_auth"> - <property name="text"> - <string>Do not use cookie authentication</string> - </property> - </widget> - </item> - <item row="20" column="1" colspan="3"> - <widget class="QCheckBox" name="input_checkbox_force_authuser"> - <property name="text"> - <string>Only show permitted hosts for "see all" users (1.4.0i1 or newer)</string> - </property> - </widget> - </item> - <item row="13" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_map_to_hostname"/> - </item> - <item row="10" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_host_filter"> - <property name="text"> - <string/> - </property> - </widget> - </item> - <item row="12" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_alertmanager_filter"/> - </item> - <item row="18" column="1" colspan="3"> - <widget class="QCheckBox" name="input_checkbox_use_display_name_service"> - <property name="text"> - <string>Use display name as service name</string> - </property> - </widget> - </item> - <item row="0" column="1" colspan="3"> - <widget class="QCheckBox" name="input_checkbox_ignore_cert"> - <property name="text"> - <string>Ignore SSL/TLS certificate</string> - </property> - </widget> - </item> <item row="10" column="1"> <widget class="QLabel" name="label_host_filter"> <property name="sizePolicy"> @@ -559,107 +618,88 @@ </property> </widget> </item> - <item row="15" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_map_to_status_information"/> - </item> - <item row="8" column="1"> - <widget class="QLabel" name="label_monitor_site"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> + <item row="0" column="1" colspan="3"> + <widget class="QCheckBox" name="input_checkbox_ignore_cert"> <property name="text"> - <string>Monitor Site:</string> + <string>Ignore SSL/TLS certificate</string> </property> </widget> </item> - <item row="11" column="1"> - <widget class="QLabel" name="label_service_filter"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> + <item row="14" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_map_to_servicename"/> + </item> + <item row="10" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_host_filter"> <property name="text"> - <string>Service filter:</string> + <string/> </property> </widget> </item> - <item row="19" column="1" colspan="3"> - <widget class="QCheckBox" name="input_checkbox_use_description_name_service"> + <item row="2" column="1"> + <widget class="QLabel" name="label_custom_ca_file"> <property name="text"> - <string>Use description as service name</string> + <string>Custom CA file: </string> </property> </widget> </item> - <item row="13" column="1"> - <widget class="QLabel" name="label_map_to_hostname"> + <item row="15" column="1"> + <widget class="QLabel" name="label_map_to_status_information"> <property name="text"> - <string>Map to hostname:</string> + <string>Map to status_information:</string> </property> </widget> </item> - <item row="2" column="1"> - <widget class="QLabel" name="label_custom_ca_file"> + <item row="8" column="2"> + <widget class="QLineEdit" name="input_lineedit_monitor_site"> <property name="text"> - <string>Custom CA file: </string> + <string>Site 1</string> </property> </widget> </item> - <item row="17" column="1" colspan="3"> + <item row="21" column="1" colspan="3"> <widget class="QCheckBox" name="input_checkbox_use_display_name_host"> <property name="text"> <string>Use display name as host name</string> </property> </widget> </item> - <item row="6" column="3"> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> + <item row="17" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_map_to_warning"/> </item> - <item row="14" column="1"> - <widget class="QLabel" name="label_map_to_servicename"> + <item row="18" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_map_to_critical"/> + </item> + <item row="19" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_map_to_unknown"/> + </item> + <item row="16" column="1"> + <widget class="QLabel" name="label_map_to_ok"> <property name="text"> - <string>Map to servicename:</string> + <string>Map to OK</string> </property> </widget> </item> - <item row="9" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_autologin_key"> + <item row="17" column="1"> + <widget class="QLabel" name="label_map_to_warning"> <property name="text"> - <string/> + <string>Map to WARNING</string> </property> </widget> </item> - <item row="12" column="1"> - <widget class="QLabel" name="label_alertmanager_filter"> + <item row="18" column="1"> + <widget class="QLabel" name="label_map_to_critical"> <property name="text"> - <string>Filter:</string> + <string>Map to CRITICAL</string> </property> </widget> </item> - <item row="22" column="1"> - <widget class="QLabel" name="label_idp_ecp_endpoint"> + <item row="19" column="1"> + <widget class="QLabel" name="label_map_to_unknown"> <property name="text"> - <string>IdP ECP endpoint URL</string> + <string>Map to UNKNOWN</string> </property> </widget> </item> - <item row="22" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_idp_ecp_endpoint"/> - </item> </layout> </widget> </item> diff --git a/Nagstamon/Servers/Alertmanager/CHANGELOG.md b/Nagstamon/Servers/Alertmanager/CHANGELOG.md new file mode 100644 index 000000000..ccabd24bc --- /dev/null +++ b/Nagstamon/Servers/Alertmanager/CHANGELOG.md @@ -0,0 +1,28 @@ +# Changelog + +[1.2.0] - 2021-07-22: + * changed: + Removed dependencies to the Prometheus integration + alertmanager is now a full module residing in its own directory + * added: + Support user defined severity values for critical or warning + TODO: Support for duplicate tupels of (hostname,servicename) + +[1.1.0] - 2021-05-18: + * changed: + Using logging module for all outputs + Some refactoring for testing support + * added: + Initial tests based on unittest and pylint (see tests/test_Alertmanager.py) + +[1.0.2] - 2021-04-10: + * added: + Better debug output + +[1.0.1] - 2020-11-27: + * added: + Support for hiding suppressed alerts with the scheduled downtime filter + +[1.0.0] - 2020-11-08: + * added: + Inital version \ No newline at end of file diff --git a/Nagstamon/Servers/Alertmanager/LICENSE b/Nagstamon/Servers/Alertmanager/LICENSE new file mode 100644 index 000000000..4c9f4588f --- /dev/null +++ b/Nagstamon/Servers/Alertmanager/LICENSE @@ -0,0 +1,16 @@ +Nagstamon - Nagios status monitor for your desktop +Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA \ No newline at end of file diff --git a/Nagstamon/Servers/Alertmanager/README.md b/Nagstamon/Servers/Alertmanager/README.md new file mode 100644 index 000000000..c30ab9fdd --- /dev/null +++ b/Nagstamon/Servers/Alertmanager/README.md @@ -0,0 +1,14 @@ +# alertmanager + +## description +The AlertmanagerServer and AlertmanagerService classes implement support for Prometheus' alertmanager. + +The monitor URL in the setup should be something like: +`http://prometheus.example.com:9093` + +What the integration does: + +It reads the alerts from the Alertmanager's REST API and tries to fit each alert into Nagstamon's GenericServer and GenericService objects. + +## author(s) +Initial implementation by Stephan Schwarz (@stearz) \ No newline at end of file diff --git a/Nagstamon/Servers/Alertmanager.py b/Nagstamon/Servers/Alertmanager/__init__.py similarity index 59% rename from Nagstamon/Servers/Alertmanager.py rename to Nagstamon/Servers/Alertmanager/__init__.py index a544c8071..0bf22a455 100644 --- a/Nagstamon/Servers/Alertmanager.py +++ b/Nagstamon/Servers/Alertmanager/__init__.py @@ -1,94 +1,44 @@ # encoding: utf-8 -# Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - -# Initial implementation by Stephan Schwarz (@stearz) -# -# This Server class connects against Prometheus' Alertmanager. -# The monitor URL in the setup should be something like -# http://alertmanager.example.com -# -# Release Notes: -# -# [1.1.0] - 2021-05-18: -# * changed: -# Using logging module for all outputs -# Some refactoring for testing support -# * added: -# Initial tests based on unittest and pylint (see tests/test_Alertmanager.py) -# -# [1.0.2] - 2021-04-10: -# * added: -# Better debug output -# -# [1.0.1] - 2020-11-27: -# * added: -# Support for hiding suppressed alerts with the scheduled downtime filter -# -# [1.0.0] - 2020-11-08: -# * added: -# Inital version -# import sys import json import re import time -from datetime import datetime, timedelta, timezone -import logging -import dateutil.parser +from datetime import datetime, timedelta + import requests from Nagstamon.Config import conf -from Nagstamon.Objects import (GenericHost, Result) -from Nagstamon.Servers.Prometheus import PrometheusServer, PrometheusService +from Nagstamon.Objects import (GenericHost,GenericService,Result) +from Nagstamon.Servers.Generic import GenericServer from Nagstamon.Helpers import webbrowser_open -# logging -------------------------------------------- -log = logging.getLogger('Alertmanager.py') -handler = logging.StreamHandler(sys.stdout) -if conf.debug_mode is True: - log_level = logging.DEBUG - handler.setLevel(logging.DEBUG) -else: - log_level = logging.INFO - handler.setLevel(logging.INFO) -log.setLevel(log_level) -formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') -handler.setFormatter(formatter) -log.addHandler(handler) -# ---------------------------------------------------- - - -class AlertmanagerService(PrometheusService): +from .helpers import (start_logging, + get_duration, + convert_timestring_to_utc, + detect_from_labels) + + +# TODO: support debug level switching while running +log = start_logging('alertmanager', conf.debug_mode) + + +class AlertmanagerService(GenericService): """ - add Alertmanager specific service property to generic service class + add alertmanager specific service property to generic service class """ service_object_id = "" + labels = {} -class AlertmanagerServer(PrometheusServer): +class AlertmanagerServer(GenericServer): """ - special treatment for Alertmanager API + special treatment for alertmanager API """ TYPE = 'Alertmanager' - # Alertmanager actions are limited to visiting the monitor for now + # alertmanager actions are limited to visiting the monitor for now MENU_ACTIONS = ['Monitor', 'Downtime', 'Acknowledge'] BROWSER_URLS = { 'monitor': '$MONITOR$/#/alerts', @@ -105,49 +55,86 @@ class AlertmanagerServer(PrometheusServer): map_to_hostname = '' map_to_servicename = '' map_to_status_information = '' + map_to_critical = '' + map_to_warning = '' + map_to_unknown = '' + map_to_ok = '' name = '' alertmanager_filter = '' - @staticmethod - def timestring_to_utc(timestring): - local_time = datetime.now(timezone(timedelta(0))).astimezone().tzinfo - parsed_time = dateutil.parser.parse(timestring) - utc_time = parsed_time.replace(tzinfo=local_time).astimezone(timezone.utc) - return utc_time.isoformat() + + def init_HTTP(self): + """ + things to do if HTTP is not initialized + """ + GenericServer.init_HTTP(self) + + # prepare for JSON + self.session.headers.update({'Accept': 'application/json', + 'Content-Type': 'application/json'}) - def _detect_from_labels(self, labels, config_label_list, default_value="", list_delimiter=","): - result = default_value - for each_label in config_label_list.split(list_delimiter): - if each_label in labels: - result = labels.get(each_label) - break - return result + def init_config(self): + """ + dummy init_config, called at thread start + """ + + + def get_start_end(self, host): + """ + Set a default of starttime of "now" and endtime is "now + 24 hours" + directly from web interface + """ + start = datetime.now() + end = datetime.now() + timedelta(hours=24) + return (str(start.strftime("%Y-%m-%d %H:%M:%S")), + str(end.strftime("%Y-%m-%d %H:%M:%S"))) + + def map_severity(self, the_severity): + """Maps a severity + + Args: + the_severity (str): The severity that should be mapped + + Returns: + str: The matched Nagstamon severity + """ + if the_severity in self.map_to_unknown.split(','): + return "UNKNOWN" + if the_severity in self.map_to_critical.split(','): + return "CRITICAL" + if the_severity in self.map_to_warning.split(','): + return "WARNING" + if the_severity in self.map_to_ok.split(','): + return "OK" + return the_severity.upper() def _process_alert(self, alert): result = {} - # Alertmanager specific extensions - generatorURL = alert.get("generatorURL", {}) + # alertmanager specific extensions + generator_url = alert.get("generatorURL", {}) fingerprint = alert.get("fingerprint", {}) log.debug("processing alert with fingerprint '%s':", fingerprint) labels = alert.get("labels", {}) state = alert.get("status", {"state": "active"})["state"] - severity = labels.get("severity", "UNKNOWN").upper() + severity = self.map_severity(labels.get("severity", "unknown")) # skip alerts with none severity if severity == "NONE": - log.debug("[%s]: detected detected state '%s' and severity '%s' from labels -> skipping alert", fingerprint, state, severity) + log.debug("[%s]: detected detected state '%s' and severity '%s' from labels \ + -> skipping alert", fingerprint, state, severity) return False - log.debug("[%s]: detected detected state '%s' and severity '%s' from labels", fingerprint, state, severity) + log.debug("[%s]: detected detected state '%s' and severity '%s' from labels", + fingerprint, state, severity) - hostname = self._detect_from_labels(labels,self.map_to_hostname,"unknown") - hostname = re.sub(':[0-9]+', '', hostname) + hostname = detect_from_labels(labels,self.map_to_hostname,"unknown") + hostname = re.sub(':[0-9]+', '', hostname) log.debug("[%s]: detected hostname from labels: '%s'", fingerprint, hostname) - servicename = self._detect_from_labels(labels,self.map_to_servicename,"unknown") + servicename = detect_from_labels(labels,self.map_to_servicename,"unknown") log.debug("[%s]: detected servicename from labels: '%s'", fingerprint, servicename) if "status" in alert: @@ -158,28 +145,29 @@ def _process_alert(self, alert): if attempt == "suppressed": scheduled_downtime = True acknowledged = True - log.debug("[%s]: detected status: '%s' -> interpreting as silenced", fingerprint, attempt) + log.debug("[%s]: detected status: '%s' -> interpreting as silenced", + fingerprint, attempt) else: scheduled_downtime = False acknowledged = False log.debug("[%s]: detected status: '%s'", fingerprint, attempt) - - duration = str(self._get_duration(alert["startsAt"])) + + duration = str(get_duration(alert["startsAt"])) annotations = alert.get("annotations", {}) - status_information = self._detect_from_labels(annotations,self.map_to_status_information,'') - + status_information = detect_from_labels(annotations,self.map_to_status_information,'') + result['host'] = str(hostname) result['name'] = servicename result['server'] = self.name result['status'] = severity result['labels'] = labels - result['last_check'] = str(self._get_duration(alert["updatedAt"])) + result['last_check'] = str(get_duration(alert["updatedAt"])) result['attempt'] = attempt result['scheduled_downtime'] = scheduled_downtime result['acknowledged'] = acknowledged result['duration'] = duration - result['generatorURL'] = generatorURL + result['generatorURL'] = generator_url result['fingerprint'] = fingerprint result['status_information'] = status_information @@ -188,13 +176,25 @@ def _process_alert(self, alert): def _get_status(self): """ - Get status from Alertmanager Server + Get status from alertmanager Server """ - - log.debug("detection config (map_to_status_information): '%s'", self.map_to_status_information) - log.debug("detection config (map_to_hostname): '%s'", self.map_to_hostname) - log.debug("detection config (map_to_servicename): '%s'", self.map_to_servicename) - log.debug("detection config (alertmanager_filter): '%s'", self.alertmanager_filter) + + log.debug("detection config (map_to_status_information): '%s'", + self.map_to_status_information) + log.debug("detection config (map_to_hostname): '%s'", + self.map_to_hostname) + log.debug("detection config (map_to_servicename): '%s'", + self.map_to_servicename) + log.debug("detection config (alertmanager_filter): '%s'", + self.alertmanager_filter) + log.debug("severity config (map_to_unknown): '%s'", + self.map_to_unknown) + log.debug("severity config (map_to_critical): '%s'", + self.map_to_critical) + log.debug("severity config (map_to_warning): '%s'", + self.map_to_warning) + log.debug("severity config (map_to_ok): '%s'", + self.map_to_ok) # get all alerts from the API server try: @@ -204,33 +204,31 @@ def _get_status(self): else: result = self.FetchURL(self.monitor_url + self.API_PATH_ALERTS, giveback="raw") - + if result.status_code == 200: log.debug("received status code '%s' with this content in result.result: \n\ ------------------------------------------------------------------------------------------------------------------------------\n\ -%s\ ------------------------------------------------------------------------------------------------------------------------------", result.status_code, result.result) + ---------------------------------------------------------------\n\ + %s\ + ---------------------------------------------------------------", + result.status_code, result.result) else: log.error("received status code '%s'", result.status_code) - # when result is not JSON catch it - try: - data = json.loads(result.result) - except json.decoder.JSONDecodeError: - data = {} + + data = json.loads(result.result) error = result.error status_code = result.status_code # check if any error occured errors_occured = self.check_for_error(data, error, status_code) if errors_occured is not False: - return(errors_occured) + return errors_occured for alert in data: alert_data = self._process_alert(alert) if not alert_data: break - service = PrometheusService() + service = AlertmanagerService() service.host = alert_data['host'] service.name = alert_data['name'] service.server = alert_data['server'] @@ -242,7 +240,7 @@ def _get_status(self): service.attempt = alert_data['attempt'] service.duration = alert_data['duration'] - service.generatorURL = alert_data['generatorURL'] + service.generator_url = alert_data['generatorURL'] service.fingerprint = alert_data['fingerprint'] service.status_information = alert_data['status_information'] @@ -253,16 +251,21 @@ def _get_status(self): self.new_hosts[service.host].server = self.name self.new_hosts[service.host].services[service.name] = service - except Exception as e: + except Exception as the_exception: # set checking flag back to False self.isChecking = False result, error = self.Error(sys.exc_info()) - log.exception(e) + log.exception(the_exception) return Result(result=result, error=error) # dummy return in case all is OK return Result() + def open_monitor_webpage(self, host, service): + """ + open monitor from tablewidget context menu + """ + webbrowser_open('%s' % (self.monitor_url)) def open_monitor(self, host, service=''): """ @@ -276,8 +279,8 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_time, hours, minutes): # Convert local dates to UTC - start_time_dt = self.timestring_to_utc(start_time) - end_time_dt = self.timestring_to_utc(end_time) + start_time_dt = convert_timestring_to_utc(start_time) + end_time_dt = convert_timestring_to_utc(end_time) # API Spec: https://github.com/prometheus/alertmanager/blob/master/api/v2/openapi.yaml silence_data = { @@ -331,9 +334,10 @@ def set_acknowledge(self, info_dict): info_dict['expire_time']) - def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=[], expire_time=None): + def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, + all_services=[], expire_time=None): alert = self.hosts[host].services[service] - endsAt = self.timestring_to_utc(expire_time) + ends_at = convert_timestring_to_utc(expire_time) cgi_data = {} cgi_data["matchers"] = [] @@ -344,10 +348,11 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi "isRegex": False }) cgi_data["startsAt"] = datetime.utcfromtimestamp(time.time()).isoformat() - cgi_data["endsAt"] = endsAt or cgi_data["startAt"] + cgi_data["endsAt"] = ends_at or cgi_data["startAt"] cgi_data["comment"] = comment or "Nagstamon silence" cgi_data["createdBy"] = author or "Nagstamon" cgi_data = json.dumps(cgi_data) - result = self.FetchURL(self.monitor_url + self.API_PATH_SILENCES, giveback="raw", cgi_data=cgi_data) + result = self.FetchURL(self.monitor_url + self.API_PATH_SILENCES, giveback="raw", + cgi_data=cgi_data) return result diff --git a/Nagstamon/Servers/Alertmanager/helpers.py b/Nagstamon/Servers/Alertmanager/helpers.py new file mode 100644 index 000000000..1895fe5b9 --- /dev/null +++ b/Nagstamon/Servers/Alertmanager/helpers.py @@ -0,0 +1,80 @@ +import sys +import logging +import dateutil.parser +from datetime import datetime, timedelta, timezone + +def start_logging(log_name, debug_mode): + logger = logging.getLogger(log_name) + handler = logging.StreamHandler(sys.stdout) + if debug_mode is True: + LOG_LEVEL = logging.DEBUG + handler.setLevel(logging.DEBUG) + else: + LOG_LEVEL = logging.INFO + handler.setLevel(logging.INFO) + logger.setLevel(LOG_LEVEL) + formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + handler.setFormatter(formatter) + logger.addHandler(handler) + return logger + + +def get_duration(timestring): + """ + calculates the duration (delta) from Prometheus' activeAt (ISO8601 + format) until now and returns a human friendly string + + Args: + timestring (string): An ISO8601 time string + + Returns: + string: A time string in human readable format + """ + time_object = dateutil.parser.parse(timestring) + duration = datetime.now(timezone.utc) - time_object + hour = int(duration.seconds / 3600) + minute = int(duration.seconds % 3600 / 60) + second = int(duration.seconds % 60) + if duration.days > 0: + return "%sd %sh %02dm %02ds" % (duration.days, hour, minute, second) + if hour > 0: + return "%sh %02dm %02ds" % (hour, minute, second) + if minute > 0: + return "%02dm %02ds" % (minute, second) + return "%02ds" % (second) + + +def convert_timestring_to_utc(timestring): + """Converts time string and returns time for timezone UTC in ISO format + + Args: + timestring (string): A time string + + Returns: + string: A time string in ISO format + """ + local_time = datetime.now(timezone(timedelta(0))).astimezone().tzinfo + parsed_time = dateutil.parser.parse(timestring) + utc_time = parsed_time.replace(tzinfo=local_time).astimezone(timezone.utc) + return utc_time.isoformat() + + +def detect_from_labels(labels, config_label_list, default_value="", list_delimiter=","): + """Returns the name of the label that first matched between `labels` and `config_label_list`. + If there has not been a match it returns an empty string. + + Args: + labels (list(str)): A list of string labels + config_label_list (str): A delimiter seperated list - Delimiter can be specified with `list_delimiter`. Default delimiter is ",". + default_value (str, optional): The value to return if there has not been a match. Defaults to "". + list_delimiter (str, optional): The delimiter used in the value of `config_label_list`. Defaults to ",". + + Returns: + str: The matched label name or an empty string if there was no match + """ + result = default_value + for each_label in config_label_list.split(list_delimiter): + if each_label in labels: + result = labels.get(each_label) + break + return result diff --git a/Nagstamon/Servers/Prometheus.py b/Nagstamon/Servers/Prometheus.py index f68863967..18c28e16b 100644 --- a/Nagstamon/Servers/Prometheus.py +++ b/Nagstamon/Servers/Prometheus.py @@ -21,7 +21,7 @@ # # This Server class connects against Prometheus. # The monitor URL in the setup should be something like -# http://prometheus.example.com +# http://prometheus.example.com:9090 # # Release Notes: # diff --git a/Nagstamon/Servers/__init__.py b/Nagstamon/Servers/__init__.py index 94a3ee935..c449abf67 100644 --- a/Nagstamon/Servers/__init__.py +++ b/Nagstamon/Servers/__init__.py @@ -203,6 +203,11 @@ def create_server(server=None): new_server.map_to_hostname = server.map_to_hostname new_server.map_to_servicename = server.map_to_servicename new_server.map_to_status_information = server.map_to_status_information + new_server.map_to_ok = server.map_to_ok + new_server.map_to_unknown = server.map_to_unknown + new_server.map_to_warning = server.map_to_warning + new_server.map_to_critical = server.map_to_critical + # server's individual preparations for HTTP connections (for example cookie creation) # is done in GetStatus() method of monitor diff --git a/tests/test_Alertmanager.py b/tests/test_Alertmanager.py index a2a138ee4..6f56e09a4 100644 --- a/tests/test_Alertmanager.py +++ b/tests/test_Alertmanager.py @@ -1,40 +1,33 @@ -import re -import sys import json from pylint import lint import unittest -import logging import Nagstamon import Nagstamon.Servers.Alertmanager conf = {} conf['debug_mode'] = True -def read_file(filename): - ''' - Helper method for injecting json from files - ''' - with open(filename) as f: - return f.read() - - -class test_Alertmanager(unittest.TestCase): +class test_alertmanager(unittest.TestCase): def test_lint_with_pylint(self): with self.assertRaises(SystemExit) as cm: - lint.Run(['--errors-only','Nagstamon/Servers/Alertmanager.py']) + lint.Run(['Nagstamon/Servers/Alertmanager']) self.assertEqual(cm.exception.code, 0) def test_unit_alert_suppressed(self): - with open('tests/test_Alertmanager_suppressed.json') as json_file: + with open('tests/test_alertmanager_suppressed.json') as json_file: data = json.load(json_file) test_class = Nagstamon.Servers.Alertmanager.AlertmanagerServer() test_class.map_to_hostname = 'instance,pod_name,namespace' test_class.map_to_servicename = 'alertname' test_class.map_to_status_information = 'message,summary,description' + test_class.map_to_unknwon = '' + test_class.map_to_critical = '' + test_class.map_to_warning = '' + test_class.map_to_ok = '' test_result = test_class._process_alert(data) @@ -52,7 +45,7 @@ def test_unit_alert_suppressed(self): def test_unit_alert_skipped(self): - with open('tests/test_Alertmanager_skipped.json') as json_file: + with open('tests/test_alertmanager_skipped.json') as json_file: data = json.load(json_file) test_class = Nagstamon.Servers.Alertmanager.AlertmanagerServer() @@ -66,13 +59,17 @@ def test_unit_alert_skipped(self): def test_unit_alert_warning(self): - with open('tests/test_Alertmanager_warning.json') as json_file: + with open('tests/test_alertmanager_warning.json') as json_file: data = json.load(json_file) test_class = Nagstamon.Servers.Alertmanager.AlertmanagerServer() test_class.map_to_hostname = 'instance,pod_name,namespace' test_class.map_to_servicename = 'alertname' test_class.map_to_status_information = 'message,summary,description' + test_class.map_to_unknwon = '' + test_class.map_to_critical = '' + test_class.map_to_warning = '' + test_class.map_to_ok = '' test_result = test_class._process_alert(data) @@ -90,13 +87,17 @@ def test_unit_alert_warning(self): def test_unit_alert_critical(self): - with open('tests/test_Alertmanager_critical.json') as json_file: + with open('tests/test_alertmanager_critical.json') as json_file: data = json.load(json_file) test_class = Nagstamon.Servers.Alertmanager.AlertmanagerServer() test_class.map_to_hostname = 'instance,pod_name,namespace' test_class.map_to_servicename = 'alertname' test_class.map_to_status_information = 'message,summary,description' + test_class.map_to_unknwon = '' + test_class.map_to_critical = '' + test_class.map_to_warning = '' + test_class.map_to_ok = '' test_result = test_class._process_alert(data) @@ -114,13 +115,17 @@ def test_unit_alert_critical(self): def test_unit_alert_critical_with_empty_maps(self): - with open('tests/test_Alertmanager_critical.json') as json_file: + with open('tests/test_alertmanager_critical.json') as json_file: data = json.load(json_file) test_class = Nagstamon.Servers.Alertmanager.AlertmanagerServer() test_class.map_to_hostname = '' test_class.map_to_servicename = '' test_class.map_to_status_information = '' + test_class.map_to_unknwon = '' + test_class.map_to_critical = '' + test_class.map_to_warning = '' + test_class.map_to_ok = '' test_result = test_class._process_alert(data) @@ -137,5 +142,33 @@ def test_unit_alert_critical_with_empty_maps(self): self.assertEqual(test_result['status_information'], '') + def test_unit_alert_custom_severity_critical(self): + with open('tests/test_alertmanager_custom_severity.json') as json_file: + data = json.load(json_file) + + test_class = Nagstamon.Servers.Alertmanager.AlertmanagerServer() + test_class.map_to_hostname = 'instance,pod_name,namespace' + test_class.map_to_servicename = 'alertname' + test_class.map_to_status_information = 'message,summary,description' + test_class.map_to_unknwon = 'unknown' + test_class.map_to_critical = 'error,rocketchat' + test_class.map_to_warning = 'warning' + test_class.map_to_ok = 'ok' + + test_result = test_class._process_alert(data) + + self.assertEqual(test_result['attempt'], 'active') + self.assertEqual(test_result['acknowledged'], False) + self.assertEqual(test_result['scheduled_downtime'], False) + self.assertEqual(test_result['host'], '127.0.0.1') + self.assertEqual(test_result['name'], 'Error') + self.assertEqual(test_result['server'], '') + self.assertEqual(test_result['status'], 'CRITICAL') + self.assertEqual(test_result['labels'], {"alertname":"Error","device":"murpel","endpoint":"metrics","instance":"127.0.0.1:9100","job":"node-exporter","namespace":"monitoring","pod":"monitoring-prometheus-node-exporter-4711","prometheus":"monitoring/monitoring-prometheus-oper-prometheus","service":"monitoring-prometheus-node-exporter","severity":"rocketchat"}) + self.assertEqual(test_result['generatorURL'], 'http://localhost') + self.assertEqual(test_result['fingerprint'], '0ef7c4bd7a504b8d') + self.assertEqual(test_result['status_information'], 'Network interface "murpel" showing errors on node-exporter monitoring/monitoring-prometheus-node-exporter-4711') + + if __name__ == '__main__': unittest.main() diff --git a/tests/test_alertmanager_custom_severity.json b/tests/test_alertmanager_custom_severity.json new file mode 100644 index 000000000..237dfd6c1 --- /dev/null +++ b/tests/test_alertmanager_custom_severity.json @@ -0,0 +1,32 @@ +{ + "annotations": { + "message": "Network interface \"murpel\" showing errors on node-exporter monitoring/monitoring-prometheus-node-exporter-4711" + }, + "endsAt": "1970-01-01T00:00:12.345Z", + "fingerprint": "0ef7c4bd7a504b8d", + "receivers": [ + { + "name": "null" + } + ], + "startsAt": "1970-01-01T00:00:01.345Z", + "status": { + "inhibitedBy": [], + "silencedBy": [], + "state": "active" + }, + "updatedAt": "1970-01-01T00:00:12.345Z", + "generatorURL": "http://localhost", + "labels": { + "alertname": "Error", + "device": "murpel", + "endpoint": "metrics", + "instance": "127.0.0.1:9100", + "job": "node-exporter", + "namespace": "monitoring", + "pod": "monitoring-prometheus-node-exporter-4711", + "prometheus": "monitoring/monitoring-prometheus-oper-prometheus", + "service": "monitoring-prometheus-node-exporter", + "severity": "rocketchat" + } +} \ No newline at end of file From 760fcbd8ddc65e692d5c0122bdc0b929f03dabea Mon Sep 17 00:00:00 2001 From: Stephan Schwarz <stearz@gmx.de> Date: Mon, 23 Aug 2021 16:09:13 +0200 Subject: [PATCH 147/884] re-fixed empty response in refactored module and bumped changelog and version strings --- ChangeLog | 2 +- Nagstamon/Servers/Alertmanager/CHANGELOG.md | 3 +-- Nagstamon/Servers/Alertmanager/__init__.py | 5 ++++- build/debian/changelog | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/ChangeLog b/ChangeLog index 42d713f86..47315a892 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,4 @@ -nagstamon (3.7-20210517) unstable; urgency=low +nagstamon (3.7-20210823) unstable; urgency=low * New upstream - added test section to GitHub actions workflows to enable testing - added basic unit tests and linting to Alertmanager diff --git a/Nagstamon/Servers/Alertmanager/CHANGELOG.md b/Nagstamon/Servers/Alertmanager/CHANGELOG.md index ccabd24bc..512affdad 100644 --- a/Nagstamon/Servers/Alertmanager/CHANGELOG.md +++ b/Nagstamon/Servers/Alertmanager/CHANGELOG.md @@ -1,12 +1,11 @@ # Changelog -[1.2.0] - 2021-07-22: +[1.2.0] - 2021-08-23: * changed: Removed dependencies to the Prometheus integration alertmanager is now a full module residing in its own directory * added: Support user defined severity values for critical or warning - TODO: Support for duplicate tupels of (hostname,servicename) [1.1.0] - 2021-05-18: * changed: diff --git a/Nagstamon/Servers/Alertmanager/__init__.py b/Nagstamon/Servers/Alertmanager/__init__.py index 0bf22a455..05263ef24 100644 --- a/Nagstamon/Servers/Alertmanager/__init__.py +++ b/Nagstamon/Servers/Alertmanager/__init__.py @@ -214,7 +214,10 @@ def _get_status(self): else: log.error("received status code '%s'", result.status_code) - data = json.loads(result.result) + try: + data = json.loads(result.result) + except json.decoder.JSONDecodeError: + data = {} error = result.error status_code = result.status_code diff --git a/build/debian/changelog b/build/debian/changelog index 530f694ce..00aad35fa 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.7-20210815) unstable; urgency=low +nagstamon (3.7-20210822) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Mon, 09 Aug 2021 19:00:00 +0100 From f9d1693b44fbea2a47572debba4de2224b47f470 Mon Sep 17 00:00:00 2001 From: Stephan Schwarz <stearz@gmx.de> Date: Mon, 23 Aug 2021 16:12:50 +0200 Subject: [PATCH 148/884] fixed case in test json files --- tests/test_alertmanager.py | 174 ++++++++++++++++++++++++ tests/test_alertmanager_critical.json | 32 +++++ tests/test_alertmanager_skipped.json | 22 +++ tests/test_alertmanager_suppressed.json | 34 +++++ tests/test_alertmanager_warning.json | 24 ++++ 5 files changed, 286 insertions(+) create mode 100644 tests/test_alertmanager.py create mode 100644 tests/test_alertmanager_critical.json create mode 100644 tests/test_alertmanager_skipped.json create mode 100644 tests/test_alertmanager_suppressed.json create mode 100644 tests/test_alertmanager_warning.json diff --git a/tests/test_alertmanager.py b/tests/test_alertmanager.py new file mode 100644 index 000000000..6f56e09a4 --- /dev/null +++ b/tests/test_alertmanager.py @@ -0,0 +1,174 @@ +import json + +from pylint import lint + +import unittest +import Nagstamon +import Nagstamon.Servers.Alertmanager + +conf = {} +conf['debug_mode'] = True + +class test_alertmanager(unittest.TestCase): + + def test_lint_with_pylint(self): + with self.assertRaises(SystemExit) as cm: + lint.Run(['Nagstamon/Servers/Alertmanager']) + self.assertEqual(cm.exception.code, 0) + + def test_unit_alert_suppressed(self): + with open('tests/test_alertmanager_suppressed.json') as json_file: + data = json.load(json_file) + + test_class = Nagstamon.Servers.Alertmanager.AlertmanagerServer() + test_class.map_to_hostname = 'instance,pod_name,namespace' + test_class.map_to_servicename = 'alertname' + test_class.map_to_status_information = 'message,summary,description' + test_class.map_to_unknwon = '' + test_class.map_to_critical = '' + test_class.map_to_warning = '' + test_class.map_to_ok = '' + + test_result = test_class._process_alert(data) + + self.assertEqual(test_result['attempt'], 'suppressed') + self.assertEqual(test_result['acknowledged'], True) + self.assertEqual(test_result['scheduled_downtime'], True) + self.assertEqual(test_result['host'], '127.0.0.1') + self.assertEqual(test_result['name'], 'Error') + self.assertEqual(test_result['server'], '') + self.assertEqual(test_result['status'], 'WARNING') + self.assertEqual(test_result['labels'], {"alertname":"Error","device":"murpel","endpoint":"metrics","instance":"127.0.0.1:9100","job":"node-exporter","namespace":"monitoring","pod":"monitoring-prometheus-node-exporter-4711","prometheus":"monitoring/monitoring-prometheus-oper-prometheus","service":"monitoring-prometheus-node-exporter","severity":"warning"}) + self.assertEqual(test_result['generatorURL'], 'http://localhost') + self.assertEqual(test_result['fingerprint'], '0ef7c4bd7a504b8d') + self.assertEqual(test_result['status_information'], 'Network interface "murpel" showing errors on node-exporter monitoring/monitoring-prometheus-node-exporter-4711') + + + def test_unit_alert_skipped(self): + with open('tests/test_alertmanager_skipped.json') as json_file: + data = json.load(json_file) + + test_class = Nagstamon.Servers.Alertmanager.AlertmanagerServer() + test_class.map_to_hostname = 'instance,pod_name,namespace' + test_class.map_to_servicename = 'alertname' + test_class.map_to_status_information = 'message,summary,description' + + test_result = test_class._process_alert(data) + + self.assertEqual(test_result, False) + + + def test_unit_alert_warning(self): + with open('tests/test_alertmanager_warning.json') as json_file: + data = json.load(json_file) + + test_class = Nagstamon.Servers.Alertmanager.AlertmanagerServer() + test_class.map_to_hostname = 'instance,pod_name,namespace' + test_class.map_to_servicename = 'alertname' + test_class.map_to_status_information = 'message,summary,description' + test_class.map_to_unknwon = '' + test_class.map_to_critical = '' + test_class.map_to_warning = '' + test_class.map_to_ok = '' + + test_result = test_class._process_alert(data) + + self.assertEqual(test_result['attempt'], 'active') + self.assertEqual(test_result['acknowledged'], False) + self.assertEqual(test_result['scheduled_downtime'], False) + self.assertEqual(test_result['host'], 'unknown') + self.assertEqual(test_result['name'], 'TargetDown') + self.assertEqual(test_result['server'], '') + self.assertEqual(test_result['status'], 'WARNING') + self.assertEqual(test_result['labels'], {"alertname": "TargetDown","job": "kubelet","prometheus": "monitoring/monitoring-prometheus-oper-prometheus","severity": "warning"}) + self.assertEqual(test_result['generatorURL'], 'http://localhost') + self.assertEqual(test_result['fingerprint'], '7be970c6e97b95c9') + self.assertEqual(test_result['status_information'], '66.6% of the kubelet targets are down.') + + + def test_unit_alert_critical(self): + with open('tests/test_alertmanager_critical.json') as json_file: + data = json.load(json_file) + + test_class = Nagstamon.Servers.Alertmanager.AlertmanagerServer() + test_class.map_to_hostname = 'instance,pod_name,namespace' + test_class.map_to_servicename = 'alertname' + test_class.map_to_status_information = 'message,summary,description' + test_class.map_to_unknwon = '' + test_class.map_to_critical = '' + test_class.map_to_warning = '' + test_class.map_to_ok = '' + + test_result = test_class._process_alert(data) + + self.assertEqual(test_result['attempt'], 'active') + self.assertEqual(test_result['acknowledged'], False) + self.assertEqual(test_result['scheduled_downtime'], False) + self.assertEqual(test_result['host'], '127.0.0.1') + self.assertEqual(test_result['name'], 'Error') + self.assertEqual(test_result['server'], '') + self.assertEqual(test_result['status'], 'ERROR') + self.assertEqual(test_result['labels'], {"alertname":"Error","device":"murpel","endpoint":"metrics","instance":"127.0.0.1:9100","job":"node-exporter","namespace":"monitoring","pod":"monitoring-prometheus-node-exporter-4711","prometheus":"monitoring/monitoring-prometheus-oper-prometheus","service":"monitoring-prometheus-node-exporter","severity":"error"}) + self.assertEqual(test_result['generatorURL'], 'http://localhost') + self.assertEqual(test_result['fingerprint'], '0ef7c4bd7a504b8d') + self.assertEqual(test_result['status_information'], 'Network interface "murpel" showing errors on node-exporter monitoring/monitoring-prometheus-node-exporter-4711') + + + def test_unit_alert_critical_with_empty_maps(self): + with open('tests/test_alertmanager_critical.json') as json_file: + data = json.load(json_file) + + test_class = Nagstamon.Servers.Alertmanager.AlertmanagerServer() + test_class.map_to_hostname = '' + test_class.map_to_servicename = '' + test_class.map_to_status_information = '' + test_class.map_to_unknwon = '' + test_class.map_to_critical = '' + test_class.map_to_warning = '' + test_class.map_to_ok = '' + + test_result = test_class._process_alert(data) + + self.assertEqual(test_result['attempt'], 'active') + self.assertEqual(test_result['acknowledged'], False) + self.assertEqual(test_result['scheduled_downtime'], False) + self.assertEqual(test_result['host'], 'unknown') + self.assertEqual(test_result['name'], 'unknown') + self.assertEqual(test_result['server'], '') + self.assertEqual(test_result['status'], 'ERROR') + self.assertEqual(test_result['labels'], {"alertname":"Error","device":"murpel","endpoint":"metrics","instance":"127.0.0.1:9100","job":"node-exporter","namespace":"monitoring","pod":"monitoring-prometheus-node-exporter-4711","prometheus":"monitoring/monitoring-prometheus-oper-prometheus","service":"monitoring-prometheus-node-exporter","severity":"error"}) + self.assertEqual(test_result['generatorURL'], 'http://localhost') + self.assertEqual(test_result['fingerprint'], '0ef7c4bd7a504b8d') + self.assertEqual(test_result['status_information'], '') + + + def test_unit_alert_custom_severity_critical(self): + with open('tests/test_alertmanager_custom_severity.json') as json_file: + data = json.load(json_file) + + test_class = Nagstamon.Servers.Alertmanager.AlertmanagerServer() + test_class.map_to_hostname = 'instance,pod_name,namespace' + test_class.map_to_servicename = 'alertname' + test_class.map_to_status_information = 'message,summary,description' + test_class.map_to_unknwon = 'unknown' + test_class.map_to_critical = 'error,rocketchat' + test_class.map_to_warning = 'warning' + test_class.map_to_ok = 'ok' + + test_result = test_class._process_alert(data) + + self.assertEqual(test_result['attempt'], 'active') + self.assertEqual(test_result['acknowledged'], False) + self.assertEqual(test_result['scheduled_downtime'], False) + self.assertEqual(test_result['host'], '127.0.0.1') + self.assertEqual(test_result['name'], 'Error') + self.assertEqual(test_result['server'], '') + self.assertEqual(test_result['status'], 'CRITICAL') + self.assertEqual(test_result['labels'], {"alertname":"Error","device":"murpel","endpoint":"metrics","instance":"127.0.0.1:9100","job":"node-exporter","namespace":"monitoring","pod":"monitoring-prometheus-node-exporter-4711","prometheus":"monitoring/monitoring-prometheus-oper-prometheus","service":"monitoring-prometheus-node-exporter","severity":"rocketchat"}) + self.assertEqual(test_result['generatorURL'], 'http://localhost') + self.assertEqual(test_result['fingerprint'], '0ef7c4bd7a504b8d') + self.assertEqual(test_result['status_information'], 'Network interface "murpel" showing errors on node-exporter monitoring/monitoring-prometheus-node-exporter-4711') + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_alertmanager_critical.json b/tests/test_alertmanager_critical.json new file mode 100644 index 000000000..633a167ad --- /dev/null +++ b/tests/test_alertmanager_critical.json @@ -0,0 +1,32 @@ +{ + "annotations": { + "message": "Network interface \"murpel\" showing errors on node-exporter monitoring/monitoring-prometheus-node-exporter-4711" + }, + "endsAt": "1970-01-01T00:00:12.345Z", + "fingerprint": "0ef7c4bd7a504b8d", + "receivers": [ + { + "name": "null" + } + ], + "startsAt": "1970-01-01T00:00:01.345Z", + "status": { + "inhibitedBy": [], + "silencedBy": [], + "state": "active" + }, + "updatedAt": "1970-01-01T00:00:12.345Z", + "generatorURL": "http://localhost", + "labels": { + "alertname": "Error", + "device": "murpel", + "endpoint": "metrics", + "instance": "127.0.0.1:9100", + "job": "node-exporter", + "namespace": "monitoring", + "pod": "monitoring-prometheus-node-exporter-4711", + "prometheus": "monitoring/monitoring-prometheus-oper-prometheus", + "service": "monitoring-prometheus-node-exporter", + "severity": "error" + } +} \ No newline at end of file diff --git a/tests/test_alertmanager_skipped.json b/tests/test_alertmanager_skipped.json new file mode 100644 index 000000000..1df93d3a2 --- /dev/null +++ b/tests/test_alertmanager_skipped.json @@ -0,0 +1,22 @@ +{ + "labels": { + "alertname": "Watchdog", + "prometheus": "monitoring/monitoring-prometheus-oper-prometheus", + "severity": "none" + }, + "annotations": { + "message": "This is an alert meant to ensure that the entire alerting pipeline is functional.\nThis alert is always firing, therefore it should always be firing in Alertmanager\nand always fire against a receiver. There are integrations with various notification\nmechanisms that send a notification when this alert is not firing. For example the\n\"DeadMansSnitch\" integration in PagerDuty.\n" + }, + "startsAt": "1970-01-01T00:00:00.520032135Z", + "endsAt": "1970-01-01T00:00:00.520032135Z", + "generatorURL": "http://localhost", + "status": { + "state": "active", + "silencedBy": [], + "inhibitedBy": [] + }, + "receivers": [ + "null" + ], + "fingerprint": "1b43ca4565de75d2" +} \ No newline at end of file diff --git a/tests/test_alertmanager_suppressed.json b/tests/test_alertmanager_suppressed.json new file mode 100644 index 000000000..f5ae0186b --- /dev/null +++ b/tests/test_alertmanager_suppressed.json @@ -0,0 +1,34 @@ +{ + "annotations": { + "message": "Network interface \"murpel\" showing errors on node-exporter monitoring/monitoring-prometheus-node-exporter-4711" + }, + "endsAt": "1970-01-01T00:00:12.345Z", + "fingerprint": "0ef7c4bd7a504b8d", + "receivers": [ + { + "name": "null" + } + ], + "startsAt": "1970-01-01T00:00:01.345Z", + "status": { + "inhibitedBy": [], + "silencedBy": [ + "bb043288-42a0-4315-8bae-15cde1d7e239" + ], + "state": "suppressed" + }, + "updatedAt": "1970-01-01T00:00:12.345Z", + "generatorURL": "http://localhost", + "labels": { + "alertname": "Error", + "device": "murpel", + "endpoint": "metrics", + "instance": "127.0.0.1:9100", + "job": "node-exporter", + "namespace": "monitoring", + "pod": "monitoring-prometheus-node-exporter-4711", + "prometheus": "monitoring/monitoring-prometheus-oper-prometheus", + "service": "monitoring-prometheus-node-exporter", + "severity": "warning" + } +} \ No newline at end of file diff --git a/tests/test_alertmanager_warning.json b/tests/test_alertmanager_warning.json new file mode 100644 index 000000000..3142d490e --- /dev/null +++ b/tests/test_alertmanager_warning.json @@ -0,0 +1,24 @@ +{ + "labels": { + "alertname": "TargetDown", + "job": "kubelet", + "prometheus": "monitoring/monitoring-prometheus-oper-prometheus", + "severity": "warning" + }, + "annotations": { + "message": "66.6% of the kubelet targets are down." + }, + "startsAt": "1970-01-01T00:00:00.520032135Z", + "endsAt": "1970-01-01T00:00:00.520032135Z", + "updatedAt": "1970-01-01T00:00:00.520032135Z", + "generatorURL": "http://localhost", + "status": { + "state": "active", + "silencedBy": [], + "inhibitedBy": [] + }, + "receivers": [ + "null" + ], + "fingerprint": "7be970c6e97b95c9" +} \ No newline at end of file From 587f6cd5f54ea0b74318cb5a411595531866b322 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 23 Aug 2021 21:05:01 +0200 Subject: [PATCH 149/884] 20210823 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index aa933f075..48c2e6fb1 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.7-20210815' + VERSION = '3.7-20210823' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2021 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 00aad35fa..3c03f22cd 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.7-20210822) unstable; urgency=low +nagstamon (3.7-20210823) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Mon, 09 Aug 2021 19:00:00 +0100 From 0912f5f34e2e3aace4c04a94524483f9a22da693 Mon Sep 17 00:00:00 2001 From: Stephan Schwarz <stearz@gmx.de> Date: Wed, 25 Aug 2021 12:01:08 +0200 Subject: [PATCH 150/884] fixed "No module named 'Nagstamon.Servers.Alertmanager'" --- Nagstamon/Servers/Alertmanager/__init__.py | 359 +----------------- .../Alertmanager/alertmanagerserver.py | 351 +++++++++++++++++ .../Alertmanager/alertmanagerservice.py | 8 + tests/test_Alertmanager.py | 174 --------- tests/test_Alertmanager_critical.json | 32 -- tests/test_Alertmanager_skipped.json | 22 -- tests/test_Alertmanager_suppressed.json | 34 -- tests/test_Alertmanager_warning.json | 24 -- 8 files changed, 361 insertions(+), 643 deletions(-) create mode 100644 Nagstamon/Servers/Alertmanager/alertmanagerserver.py create mode 100644 Nagstamon/Servers/Alertmanager/alertmanagerservice.py delete mode 100644 tests/test_Alertmanager.py delete mode 100644 tests/test_Alertmanager_critical.json delete mode 100644 tests/test_Alertmanager_skipped.json delete mode 100644 tests/test_Alertmanager_suppressed.json delete mode 100644 tests/test_Alertmanager_warning.json diff --git a/Nagstamon/Servers/Alertmanager/__init__.py b/Nagstamon/Servers/Alertmanager/__init__.py index 05263ef24..9736b7ab2 100644 --- a/Nagstamon/Servers/Alertmanager/__init__.py +++ b/Nagstamon/Servers/Alertmanager/__init__.py @@ -1,361 +1,6 @@ # encoding: utf-8 -import sys -import json -import re -import time +from .alertmanagerservice import AlertmanagerService +from .alertmanagerserver import AlertmanagerServer -from datetime import datetime, timedelta -import requests - -from Nagstamon.Config import conf -from Nagstamon.Objects import (GenericHost,GenericService,Result) -from Nagstamon.Servers.Generic import GenericServer -from Nagstamon.Helpers import webbrowser_open - -from .helpers import (start_logging, - get_duration, - convert_timestring_to_utc, - detect_from_labels) - - -# TODO: support debug level switching while running -log = start_logging('alertmanager', conf.debug_mode) - - -class AlertmanagerService(GenericService): - """ - add alertmanager specific service property to generic service class - """ - service_object_id = "" - labels = {} - - -class AlertmanagerServer(GenericServer): - """ - special treatment for alertmanager API - """ - TYPE = 'Alertmanager' - - # alertmanager actions are limited to visiting the monitor for now - MENU_ACTIONS = ['Monitor', 'Downtime', 'Acknowledge'] - BROWSER_URLS = { - 'monitor': '$MONITOR$/#/alerts', - 'hosts': '$MONITOR$/#/alerts', - 'services': '$MONITOR$/#/alerts', - 'history': '$MONITOR$/#/alerts' - } - - API_PATH_ALERTS = "/api/v2/alerts" - API_PATH_SILENCES = "/api/v2/silences" - API_FILTERS = '?filter=' - - # vars specific to alertmanager class - map_to_hostname = '' - map_to_servicename = '' - map_to_status_information = '' - map_to_critical = '' - map_to_warning = '' - map_to_unknown = '' - map_to_ok = '' - name = '' - alertmanager_filter = '' - - - def init_HTTP(self): - """ - things to do if HTTP is not initialized - """ - GenericServer.init_HTTP(self) - - # prepare for JSON - self.session.headers.update({'Accept': 'application/json', - 'Content-Type': 'application/json'}) - - - def init_config(self): - """ - dummy init_config, called at thread start - """ - - - def get_start_end(self, host): - """ - Set a default of starttime of "now" and endtime is "now + 24 hours" - directly from web interface - """ - start = datetime.now() - end = datetime.now() + timedelta(hours=24) - - return (str(start.strftime("%Y-%m-%d %H:%M:%S")), - str(end.strftime("%Y-%m-%d %H:%M:%S"))) - - def map_severity(self, the_severity): - """Maps a severity - - Args: - the_severity (str): The severity that should be mapped - - Returns: - str: The matched Nagstamon severity - """ - if the_severity in self.map_to_unknown.split(','): - return "UNKNOWN" - if the_severity in self.map_to_critical.split(','): - return "CRITICAL" - if the_severity in self.map_to_warning.split(','): - return "WARNING" - if the_severity in self.map_to_ok.split(','): - return "OK" - return the_severity.upper() - - def _process_alert(self, alert): - result = {} - - # alertmanager specific extensions - generator_url = alert.get("generatorURL", {}) - fingerprint = alert.get("fingerprint", {}) - log.debug("processing alert with fingerprint '%s':", fingerprint) - - labels = alert.get("labels", {}) - state = alert.get("status", {"state": "active"})["state"] - severity = self.map_severity(labels.get("severity", "unknown")) - - # skip alerts with none severity - if severity == "NONE": - log.debug("[%s]: detected detected state '%s' and severity '%s' from labels \ - -> skipping alert", fingerprint, state, severity) - return False - log.debug("[%s]: detected detected state '%s' and severity '%s' from labels", - fingerprint, state, severity) - - hostname = detect_from_labels(labels,self.map_to_hostname,"unknown") - hostname = re.sub(':[0-9]+', '', hostname) - log.debug("[%s]: detected hostname from labels: '%s'", fingerprint, hostname) - - servicename = detect_from_labels(labels,self.map_to_servicename,"unknown") - log.debug("[%s]: detected servicename from labels: '%s'", fingerprint, servicename) - - if "status" in alert: - attempt = alert["status"].get("state", "unknown") - else: - attempt = "unknown" - - if attempt == "suppressed": - scheduled_downtime = True - acknowledged = True - log.debug("[%s]: detected status: '%s' -> interpreting as silenced", - fingerprint, attempt) - else: - scheduled_downtime = False - acknowledged = False - log.debug("[%s]: detected status: '%s'", fingerprint, attempt) - - duration = str(get_duration(alert["startsAt"])) - - annotations = alert.get("annotations", {}) - status_information = detect_from_labels(annotations,self.map_to_status_information,'') - - result['host'] = str(hostname) - result['name'] = servicename - result['server'] = self.name - result['status'] = severity - result['labels'] = labels - result['last_check'] = str(get_duration(alert["updatedAt"])) - result['attempt'] = attempt - result['scheduled_downtime'] = scheduled_downtime - result['acknowledged'] = acknowledged - result['duration'] = duration - result['generatorURL'] = generator_url - result['fingerprint'] = fingerprint - result['status_information'] = status_information - - return result - - - def _get_status(self): - """ - Get status from alertmanager Server - """ - - log.debug("detection config (map_to_status_information): '%s'", - self.map_to_status_information) - log.debug("detection config (map_to_hostname): '%s'", - self.map_to_hostname) - log.debug("detection config (map_to_servicename): '%s'", - self.map_to_servicename) - log.debug("detection config (alertmanager_filter): '%s'", - self.alertmanager_filter) - log.debug("severity config (map_to_unknown): '%s'", - self.map_to_unknown) - log.debug("severity config (map_to_critical): '%s'", - self.map_to_critical) - log.debug("severity config (map_to_warning): '%s'", - self.map_to_warning) - log.debug("severity config (map_to_ok): '%s'", - self.map_to_ok) - - # get all alerts from the API server - try: - if self.alertmanager_filter != '': - result = self.FetchURL(self.monitor_url + self.API_PATH_ALERTS + self.API_FILTERS - + self.alertmanager_filter, giveback="raw") - else: - result = self.FetchURL(self.monitor_url + self.API_PATH_ALERTS, - giveback="raw") - - if result.status_code == 200: - log.debug("received status code '%s' with this content in result.result: \n\ - ---------------------------------------------------------------\n\ - %s\ - ---------------------------------------------------------------", - result.status_code, result.result) - else: - log.error("received status code '%s'", result.status_code) - - try: - data = json.loads(result.result) - except json.decoder.JSONDecodeError: - data = {} - error = result.error - status_code = result.status_code - - # check if any error occured - errors_occured = self.check_for_error(data, error, status_code) - if errors_occured is not False: - return errors_occured - - for alert in data: - alert_data = self._process_alert(alert) - if not alert_data: - break - - service = AlertmanagerService() - service.host = alert_data['host'] - service.name = alert_data['name'] - service.server = alert_data['server'] - service.status = alert_data['status'] - service.labels = alert_data['labels'] - service.scheduled_downtime = alert_data['scheduled_downtime'] - service.acknowledged = alert_data['acknowledged'] - service.last_check = alert_data['last_check'] - service.attempt = alert_data['attempt'] - service.duration = alert_data['duration'] - - service.generator_url = alert_data['generatorURL'] - service.fingerprint = alert_data['fingerprint'] - - service.status_information = alert_data['status_information'] - - if service.host not in self.new_hosts: - self.new_hosts[service.host] = GenericHost() - self.new_hosts[service.host].name = str(service.host) - self.new_hosts[service.host].server = self.name - self.new_hosts[service.host].services[service.name] = service - - except Exception as the_exception: - # set checking flag back to False - self.isChecking = False - result, error = self.Error(sys.exc_info()) - log.exception(the_exception) - return Result(result=result, error=error) - - # dummy return in case all is OK - return Result() - - def open_monitor_webpage(self, host, service): - """ - open monitor from tablewidget context menu - """ - webbrowser_open('%s' % (self.monitor_url)) - - def open_monitor(self, host, service=''): - """ - open monitor for alert - """ - url = self.monitor_url - webbrowser_open(url) - - - def _set_downtime(self, host, service, author, comment, fixed, start_time, - end_time, hours, minutes): - - # Convert local dates to UTC - start_time_dt = convert_timestring_to_utc(start_time) - end_time_dt = convert_timestring_to_utc(end_time) - - # API Spec: https://github.com/prometheus/alertmanager/blob/master/api/v2/openapi.yaml - silence_data = { - "matchers": [ - { - "name": "instance", - "value": host, - "isRegex": False, - "isEqual": False - }, - { - "name": "alertname", - "value": service, - "isRegex": False, - "isEqual": False - } - ], - "startsAt": start_time_dt, - "endsAt": end_time_dt, - "createdBy": author, - "comment": comment - } - - post = requests.post(self.monitor_url + self.API_PATH_SILENCES, json=silence_data) - - #silence_id = post.json()["silenceID"] - - - # Overwrite function from generic server to add expire_time value - def set_acknowledge(self, info_dict): - ''' - different monitors might have different implementations of _set_acknowledge - ''' - if info_dict['acknowledge_all_services'] is True: - all_services = info_dict['all_services'] - else: - all_services = [] - - # Make sure expire_time is set - #if not info_dict['expire_time']: - # info_dict['expire_time'] = None - - self._set_acknowledge(info_dict['host'], - info_dict['service'], - info_dict['author'], - info_dict['comment'], - info_dict['sticky'], - info_dict['notify'], - info_dict['persistent'], - all_services, - info_dict['expire_time']) - - - def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, - all_services=[], expire_time=None): - alert = self.hosts[host].services[service] - ends_at = convert_timestring_to_utc(expire_time) - - cgi_data = {} - cgi_data["matchers"] = [] - for name, value in alert.labels.items(): - cgi_data["matchers"].append({ - "name": name, - "value": value, - "isRegex": False - }) - cgi_data["startsAt"] = datetime.utcfromtimestamp(time.time()).isoformat() - cgi_data["endsAt"] = ends_at or cgi_data["startAt"] - cgi_data["comment"] = comment or "Nagstamon silence" - cgi_data["createdBy"] = author or "Nagstamon" - cgi_data = json.dumps(cgi_data) - - result = self.FetchURL(self.monitor_url + self.API_PATH_SILENCES, giveback="raw", - cgi_data=cgi_data) - return result diff --git a/Nagstamon/Servers/Alertmanager/alertmanagerserver.py b/Nagstamon/Servers/Alertmanager/alertmanagerserver.py new file mode 100644 index 000000000..175dc9a83 --- /dev/null +++ b/Nagstamon/Servers/Alertmanager/alertmanagerserver.py @@ -0,0 +1,351 @@ +import sys +import json +import re +import time + +from datetime import datetime, timedelta + +import requests + +from Nagstamon.Config import conf +from Nagstamon.Objects import (GenericHost,Result) +from Nagstamon.Servers.Generic import GenericServer +from Nagstamon.Helpers import webbrowser_open + +from .helpers import (start_logging, + get_duration, + convert_timestring_to_utc, + detect_from_labels) + +from .alertmanagerservice import AlertmanagerService + +# TODO: support debug level switching while running +log = start_logging('alertmanager', conf.debug_mode) + +class AlertmanagerServer(GenericServer): + """ + special treatment for alertmanager API + """ + TYPE = 'Alertmanager' + + # alertmanager actions are limited to visiting the monitor for now + MENU_ACTIONS = ['Monitor', 'Downtime', 'Acknowledge'] + BROWSER_URLS = { + 'monitor': '$MONITOR$/#/alerts', + 'hosts': '$MONITOR$/#/alerts', + 'services': '$MONITOR$/#/alerts', + 'history': '$MONITOR$/#/alerts' + } + + API_PATH_ALERTS = "/api/v2/alerts" + API_PATH_SILENCES = "/api/v2/silences" + API_FILTERS = '?filter=' + + # vars specific to alertmanager class + map_to_hostname = '' + map_to_servicename = '' + map_to_status_information = '' + map_to_critical = '' + map_to_warning = '' + map_to_unknown = '' + map_to_ok = '' + name = '' + alertmanager_filter = '' + + + def init_HTTP(self): + """ + things to do if HTTP is not initialized + """ + GenericServer.init_HTTP(self) + + # prepare for JSON + self.session.headers.update({'Accept': 'application/json', + 'Content-Type': 'application/json'}) + + + def init_config(self): + """ + dummy init_config, called at thread start + """ + + + def get_start_end(self, host): + """ + Set a default of starttime of "now" and endtime is "now + 24 hours" + directly from web interface + """ + start = datetime.now() + end = datetime.now() + timedelta(hours=24) + + return (str(start.strftime("%Y-%m-%d %H:%M:%S")), + str(end.strftime("%Y-%m-%d %H:%M:%S"))) + + def map_severity(self, the_severity): + """Maps a severity + + Args: + the_severity (str): The severity that should be mapped + + Returns: + str: The matched Nagstamon severity + """ + if the_severity in self.map_to_unknown.split(','): + return "UNKNOWN" + if the_severity in self.map_to_critical.split(','): + return "CRITICAL" + if the_severity in self.map_to_warning.split(','): + return "WARNING" + if the_severity in self.map_to_ok.split(','): + return "OK" + return the_severity.upper() + + def _process_alert(self, alert): + result = {} + + # alertmanager specific extensions + generator_url = alert.get("generatorURL", {}) + fingerprint = alert.get("fingerprint", {}) + log.debug("processing alert with fingerprint '%s':", fingerprint) + + labels = alert.get("labels", {}) + state = alert.get("status", {"state": "active"})["state"] + severity = self.map_severity(labels.get("severity", "unknown")) + + # skip alerts with none severity + if severity == "NONE": + log.debug("[%s]: detected detected state '%s' and severity '%s' from labels \ + -> skipping alert", fingerprint, state, severity) + return False + log.debug("[%s]: detected detected state '%s' and severity '%s' from labels", + fingerprint, state, severity) + + hostname = detect_from_labels(labels,self.map_to_hostname,"unknown") + hostname = re.sub(':[0-9]+', '', hostname) + log.debug("[%s]: detected hostname from labels: '%s'", fingerprint, hostname) + + servicename = detect_from_labels(labels,self.map_to_servicename,"unknown") + log.debug("[%s]: detected servicename from labels: '%s'", fingerprint, servicename) + + if "status" in alert: + attempt = alert["status"].get("state", "unknown") + else: + attempt = "unknown" + + if attempt == "suppressed": + scheduled_downtime = True + acknowledged = True + log.debug("[%s]: detected status: '%s' -> interpreting as silenced", + fingerprint, attempt) + else: + scheduled_downtime = False + acknowledged = False + log.debug("[%s]: detected status: '%s'", fingerprint, attempt) + + duration = str(get_duration(alert["startsAt"])) + + annotations = alert.get("annotations", {}) + status_information = detect_from_labels(annotations,self.map_to_status_information,'') + + result['host'] = str(hostname) + result['name'] = servicename + result['server'] = self.name + result['status'] = severity + result['labels'] = labels + result['last_check'] = str(get_duration(alert["updatedAt"])) + result['attempt'] = attempt + result['scheduled_downtime'] = scheduled_downtime + result['acknowledged'] = acknowledged + result['duration'] = duration + result['generatorURL'] = generator_url + result['fingerprint'] = fingerprint + result['status_information'] = status_information + + return result + + + def _get_status(self): + """ + Get status from alertmanager Server + """ + + log.debug("detection config (map_to_status_information): '%s'", + self.map_to_status_information) + log.debug("detection config (map_to_hostname): '%s'", + self.map_to_hostname) + log.debug("detection config (map_to_servicename): '%s'", + self.map_to_servicename) + log.debug("detection config (alertmanager_filter): '%s'", + self.alertmanager_filter) + log.debug("severity config (map_to_unknown): '%s'", + self.map_to_unknown) + log.debug("severity config (map_to_critical): '%s'", + self.map_to_critical) + log.debug("severity config (map_to_warning): '%s'", + self.map_to_warning) + log.debug("severity config (map_to_ok): '%s'", + self.map_to_ok) + + # get all alerts from the API server + try: + if self.alertmanager_filter != '': + result = self.FetchURL(self.monitor_url + self.API_PATH_ALERTS + self.API_FILTERS + + self.alertmanager_filter, giveback="raw") + else: + result = self.FetchURL(self.monitor_url + self.API_PATH_ALERTS, + giveback="raw") + + if result.status_code == 200: + log.debug("received status code '%s' with this content in result.result: \n\ + ---------------------------------------------------------------\n\ + %s\ + ---------------------------------------------------------------", + result.status_code, result.result) + else: + log.error("received status code '%s'", result.status_code) + + try: + data = json.loads(result.result) + except json.decoder.JSONDecodeError: + data = {} + error = result.error + status_code = result.status_code + + # check if any error occured + errors_occured = self.check_for_error(data, error, status_code) + if errors_occured is not False: + return errors_occured + + for alert in data: + alert_data = self._process_alert(alert) + if not alert_data: + break + + service = AlertmanagerService() + service.host = alert_data['host'] + service.name = alert_data['name'] + service.server = alert_data['server'] + service.status = alert_data['status'] + service.labels = alert_data['labels'] + service.scheduled_downtime = alert_data['scheduled_downtime'] + service.acknowledged = alert_data['acknowledged'] + service.last_check = alert_data['last_check'] + service.attempt = alert_data['attempt'] + service.duration = alert_data['duration'] + + service.generator_url = alert_data['generatorURL'] + service.fingerprint = alert_data['fingerprint'] + + service.status_information = alert_data['status_information'] + + if service.host not in self.new_hosts: + self.new_hosts[service.host] = GenericHost() + self.new_hosts[service.host].name = str(service.host) + self.new_hosts[service.host].server = self.name + self.new_hosts[service.host].services[service.name] = service + + except Exception as the_exception: + # set checking flag back to False + self.isChecking = False + result, error = self.Error(sys.exc_info()) + log.exception(the_exception) + return Result(result=result, error=error) + + # dummy return in case all is OK + return Result() + + def open_monitor_webpage(self, host, service): + """ + open monitor from tablewidget context menu + """ + webbrowser_open('%s' % (self.monitor_url)) + + def open_monitor(self, host, service=''): + """ + open monitor for alert + """ + url = self.monitor_url + webbrowser_open(url) + + + def _set_downtime(self, host, service, author, comment, fixed, start_time, + end_time, hours, minutes): + + # Convert local dates to UTC + start_time_dt = convert_timestring_to_utc(start_time) + end_time_dt = convert_timestring_to_utc(end_time) + + # API Spec: https://github.com/prometheus/alertmanager/blob/master/api/v2/openapi.yaml + silence_data = { + "matchers": [ + { + "name": "instance", + "value": host, + "isRegex": False, + "isEqual": False + }, + { + "name": "alertname", + "value": service, + "isRegex": False, + "isEqual": False + } + ], + "startsAt": start_time_dt, + "endsAt": end_time_dt, + "createdBy": author, + "comment": comment + } + + post = requests.post(self.monitor_url + self.API_PATH_SILENCES, json=silence_data) + + #silence_id = post.json()["silenceID"] + + + # Overwrite function from generic server to add expire_time value + def set_acknowledge(self, info_dict): + ''' + different monitors might have different implementations of _set_acknowledge + ''' + if info_dict['acknowledge_all_services'] is True: + all_services = info_dict['all_services'] + else: + all_services = [] + + # Make sure expire_time is set + #if not info_dict['expire_time']: + # info_dict['expire_time'] = None + + self._set_acknowledge(info_dict['host'], + info_dict['service'], + info_dict['author'], + info_dict['comment'], + info_dict['sticky'], + info_dict['notify'], + info_dict['persistent'], + all_services, + info_dict['expire_time']) + + + def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, + all_services=[], expire_time=None): + alert = self.hosts[host].services[service] + ends_at = convert_timestring_to_utc(expire_time) + + cgi_data = {} + cgi_data["matchers"] = [] + for name, value in alert.labels.items(): + cgi_data["matchers"].append({ + "name": name, + "value": value, + "isRegex": False + }) + cgi_data["startsAt"] = datetime.utcfromtimestamp(time.time()).isoformat() + cgi_data["endsAt"] = ends_at or cgi_data["startAt"] + cgi_data["comment"] = comment or "Nagstamon silence" + cgi_data["createdBy"] = author or "Nagstamon" + cgi_data = json.dumps(cgi_data) + + result = self.FetchURL(self.monitor_url + self.API_PATH_SILENCES, giveback="raw", + cgi_data=cgi_data) + return result diff --git a/Nagstamon/Servers/Alertmanager/alertmanagerservice.py b/Nagstamon/Servers/Alertmanager/alertmanagerservice.py new file mode 100644 index 000000000..f07fead59 --- /dev/null +++ b/Nagstamon/Servers/Alertmanager/alertmanagerservice.py @@ -0,0 +1,8 @@ +from Nagstamon.Objects import GenericService + +class AlertmanagerService(GenericService): + """ + add alertmanager specific service property to generic service class + """ + service_object_id = "" + labels = {} \ No newline at end of file diff --git a/tests/test_Alertmanager.py b/tests/test_Alertmanager.py deleted file mode 100644 index 6f56e09a4..000000000 --- a/tests/test_Alertmanager.py +++ /dev/null @@ -1,174 +0,0 @@ -import json - -from pylint import lint - -import unittest -import Nagstamon -import Nagstamon.Servers.Alertmanager - -conf = {} -conf['debug_mode'] = True - -class test_alertmanager(unittest.TestCase): - - def test_lint_with_pylint(self): - with self.assertRaises(SystemExit) as cm: - lint.Run(['Nagstamon/Servers/Alertmanager']) - self.assertEqual(cm.exception.code, 0) - - def test_unit_alert_suppressed(self): - with open('tests/test_alertmanager_suppressed.json') as json_file: - data = json.load(json_file) - - test_class = Nagstamon.Servers.Alertmanager.AlertmanagerServer() - test_class.map_to_hostname = 'instance,pod_name,namespace' - test_class.map_to_servicename = 'alertname' - test_class.map_to_status_information = 'message,summary,description' - test_class.map_to_unknwon = '' - test_class.map_to_critical = '' - test_class.map_to_warning = '' - test_class.map_to_ok = '' - - test_result = test_class._process_alert(data) - - self.assertEqual(test_result['attempt'], 'suppressed') - self.assertEqual(test_result['acknowledged'], True) - self.assertEqual(test_result['scheduled_downtime'], True) - self.assertEqual(test_result['host'], '127.0.0.1') - self.assertEqual(test_result['name'], 'Error') - self.assertEqual(test_result['server'], '') - self.assertEqual(test_result['status'], 'WARNING') - self.assertEqual(test_result['labels'], {"alertname":"Error","device":"murpel","endpoint":"metrics","instance":"127.0.0.1:9100","job":"node-exporter","namespace":"monitoring","pod":"monitoring-prometheus-node-exporter-4711","prometheus":"monitoring/monitoring-prometheus-oper-prometheus","service":"monitoring-prometheus-node-exporter","severity":"warning"}) - self.assertEqual(test_result['generatorURL'], 'http://localhost') - self.assertEqual(test_result['fingerprint'], '0ef7c4bd7a504b8d') - self.assertEqual(test_result['status_information'], 'Network interface "murpel" showing errors on node-exporter monitoring/monitoring-prometheus-node-exporter-4711') - - - def test_unit_alert_skipped(self): - with open('tests/test_alertmanager_skipped.json') as json_file: - data = json.load(json_file) - - test_class = Nagstamon.Servers.Alertmanager.AlertmanagerServer() - test_class.map_to_hostname = 'instance,pod_name,namespace' - test_class.map_to_servicename = 'alertname' - test_class.map_to_status_information = 'message,summary,description' - - test_result = test_class._process_alert(data) - - self.assertEqual(test_result, False) - - - def test_unit_alert_warning(self): - with open('tests/test_alertmanager_warning.json') as json_file: - data = json.load(json_file) - - test_class = Nagstamon.Servers.Alertmanager.AlertmanagerServer() - test_class.map_to_hostname = 'instance,pod_name,namespace' - test_class.map_to_servicename = 'alertname' - test_class.map_to_status_information = 'message,summary,description' - test_class.map_to_unknwon = '' - test_class.map_to_critical = '' - test_class.map_to_warning = '' - test_class.map_to_ok = '' - - test_result = test_class._process_alert(data) - - self.assertEqual(test_result['attempt'], 'active') - self.assertEqual(test_result['acknowledged'], False) - self.assertEqual(test_result['scheduled_downtime'], False) - self.assertEqual(test_result['host'], 'unknown') - self.assertEqual(test_result['name'], 'TargetDown') - self.assertEqual(test_result['server'], '') - self.assertEqual(test_result['status'], 'WARNING') - self.assertEqual(test_result['labels'], {"alertname": "TargetDown","job": "kubelet","prometheus": "monitoring/monitoring-prometheus-oper-prometheus","severity": "warning"}) - self.assertEqual(test_result['generatorURL'], 'http://localhost') - self.assertEqual(test_result['fingerprint'], '7be970c6e97b95c9') - self.assertEqual(test_result['status_information'], '66.6% of the kubelet targets are down.') - - - def test_unit_alert_critical(self): - with open('tests/test_alertmanager_critical.json') as json_file: - data = json.load(json_file) - - test_class = Nagstamon.Servers.Alertmanager.AlertmanagerServer() - test_class.map_to_hostname = 'instance,pod_name,namespace' - test_class.map_to_servicename = 'alertname' - test_class.map_to_status_information = 'message,summary,description' - test_class.map_to_unknwon = '' - test_class.map_to_critical = '' - test_class.map_to_warning = '' - test_class.map_to_ok = '' - - test_result = test_class._process_alert(data) - - self.assertEqual(test_result['attempt'], 'active') - self.assertEqual(test_result['acknowledged'], False) - self.assertEqual(test_result['scheduled_downtime'], False) - self.assertEqual(test_result['host'], '127.0.0.1') - self.assertEqual(test_result['name'], 'Error') - self.assertEqual(test_result['server'], '') - self.assertEqual(test_result['status'], 'ERROR') - self.assertEqual(test_result['labels'], {"alertname":"Error","device":"murpel","endpoint":"metrics","instance":"127.0.0.1:9100","job":"node-exporter","namespace":"monitoring","pod":"monitoring-prometheus-node-exporter-4711","prometheus":"monitoring/monitoring-prometheus-oper-prometheus","service":"monitoring-prometheus-node-exporter","severity":"error"}) - self.assertEqual(test_result['generatorURL'], 'http://localhost') - self.assertEqual(test_result['fingerprint'], '0ef7c4bd7a504b8d') - self.assertEqual(test_result['status_information'], 'Network interface "murpel" showing errors on node-exporter monitoring/monitoring-prometheus-node-exporter-4711') - - - def test_unit_alert_critical_with_empty_maps(self): - with open('tests/test_alertmanager_critical.json') as json_file: - data = json.load(json_file) - - test_class = Nagstamon.Servers.Alertmanager.AlertmanagerServer() - test_class.map_to_hostname = '' - test_class.map_to_servicename = '' - test_class.map_to_status_information = '' - test_class.map_to_unknwon = '' - test_class.map_to_critical = '' - test_class.map_to_warning = '' - test_class.map_to_ok = '' - - test_result = test_class._process_alert(data) - - self.assertEqual(test_result['attempt'], 'active') - self.assertEqual(test_result['acknowledged'], False) - self.assertEqual(test_result['scheduled_downtime'], False) - self.assertEqual(test_result['host'], 'unknown') - self.assertEqual(test_result['name'], 'unknown') - self.assertEqual(test_result['server'], '') - self.assertEqual(test_result['status'], 'ERROR') - self.assertEqual(test_result['labels'], {"alertname":"Error","device":"murpel","endpoint":"metrics","instance":"127.0.0.1:9100","job":"node-exporter","namespace":"monitoring","pod":"monitoring-prometheus-node-exporter-4711","prometheus":"monitoring/monitoring-prometheus-oper-prometheus","service":"monitoring-prometheus-node-exporter","severity":"error"}) - self.assertEqual(test_result['generatorURL'], 'http://localhost') - self.assertEqual(test_result['fingerprint'], '0ef7c4bd7a504b8d') - self.assertEqual(test_result['status_information'], '') - - - def test_unit_alert_custom_severity_critical(self): - with open('tests/test_alertmanager_custom_severity.json') as json_file: - data = json.load(json_file) - - test_class = Nagstamon.Servers.Alertmanager.AlertmanagerServer() - test_class.map_to_hostname = 'instance,pod_name,namespace' - test_class.map_to_servicename = 'alertname' - test_class.map_to_status_information = 'message,summary,description' - test_class.map_to_unknwon = 'unknown' - test_class.map_to_critical = 'error,rocketchat' - test_class.map_to_warning = 'warning' - test_class.map_to_ok = 'ok' - - test_result = test_class._process_alert(data) - - self.assertEqual(test_result['attempt'], 'active') - self.assertEqual(test_result['acknowledged'], False) - self.assertEqual(test_result['scheduled_downtime'], False) - self.assertEqual(test_result['host'], '127.0.0.1') - self.assertEqual(test_result['name'], 'Error') - self.assertEqual(test_result['server'], '') - self.assertEqual(test_result['status'], 'CRITICAL') - self.assertEqual(test_result['labels'], {"alertname":"Error","device":"murpel","endpoint":"metrics","instance":"127.0.0.1:9100","job":"node-exporter","namespace":"monitoring","pod":"monitoring-prometheus-node-exporter-4711","prometheus":"monitoring/monitoring-prometheus-oper-prometheus","service":"monitoring-prometheus-node-exporter","severity":"rocketchat"}) - self.assertEqual(test_result['generatorURL'], 'http://localhost') - self.assertEqual(test_result['fingerprint'], '0ef7c4bd7a504b8d') - self.assertEqual(test_result['status_information'], 'Network interface "murpel" showing errors on node-exporter monitoring/monitoring-prometheus-node-exporter-4711') - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/test_Alertmanager_critical.json b/tests/test_Alertmanager_critical.json deleted file mode 100644 index 633a167ad..000000000 --- a/tests/test_Alertmanager_critical.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "annotations": { - "message": "Network interface \"murpel\" showing errors on node-exporter monitoring/monitoring-prometheus-node-exporter-4711" - }, - "endsAt": "1970-01-01T00:00:12.345Z", - "fingerprint": "0ef7c4bd7a504b8d", - "receivers": [ - { - "name": "null" - } - ], - "startsAt": "1970-01-01T00:00:01.345Z", - "status": { - "inhibitedBy": [], - "silencedBy": [], - "state": "active" - }, - "updatedAt": "1970-01-01T00:00:12.345Z", - "generatorURL": "http://localhost", - "labels": { - "alertname": "Error", - "device": "murpel", - "endpoint": "metrics", - "instance": "127.0.0.1:9100", - "job": "node-exporter", - "namespace": "monitoring", - "pod": "monitoring-prometheus-node-exporter-4711", - "prometheus": "monitoring/monitoring-prometheus-oper-prometheus", - "service": "monitoring-prometheus-node-exporter", - "severity": "error" - } -} \ No newline at end of file diff --git a/tests/test_Alertmanager_skipped.json b/tests/test_Alertmanager_skipped.json deleted file mode 100644 index 1df93d3a2..000000000 --- a/tests/test_Alertmanager_skipped.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "labels": { - "alertname": "Watchdog", - "prometheus": "monitoring/monitoring-prometheus-oper-prometheus", - "severity": "none" - }, - "annotations": { - "message": "This is an alert meant to ensure that the entire alerting pipeline is functional.\nThis alert is always firing, therefore it should always be firing in Alertmanager\nand always fire against a receiver. There are integrations with various notification\nmechanisms that send a notification when this alert is not firing. For example the\n\"DeadMansSnitch\" integration in PagerDuty.\n" - }, - "startsAt": "1970-01-01T00:00:00.520032135Z", - "endsAt": "1970-01-01T00:00:00.520032135Z", - "generatorURL": "http://localhost", - "status": { - "state": "active", - "silencedBy": [], - "inhibitedBy": [] - }, - "receivers": [ - "null" - ], - "fingerprint": "1b43ca4565de75d2" -} \ No newline at end of file diff --git a/tests/test_Alertmanager_suppressed.json b/tests/test_Alertmanager_suppressed.json deleted file mode 100644 index f5ae0186b..000000000 --- a/tests/test_Alertmanager_suppressed.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "annotations": { - "message": "Network interface \"murpel\" showing errors on node-exporter monitoring/monitoring-prometheus-node-exporter-4711" - }, - "endsAt": "1970-01-01T00:00:12.345Z", - "fingerprint": "0ef7c4bd7a504b8d", - "receivers": [ - { - "name": "null" - } - ], - "startsAt": "1970-01-01T00:00:01.345Z", - "status": { - "inhibitedBy": [], - "silencedBy": [ - "bb043288-42a0-4315-8bae-15cde1d7e239" - ], - "state": "suppressed" - }, - "updatedAt": "1970-01-01T00:00:12.345Z", - "generatorURL": "http://localhost", - "labels": { - "alertname": "Error", - "device": "murpel", - "endpoint": "metrics", - "instance": "127.0.0.1:9100", - "job": "node-exporter", - "namespace": "monitoring", - "pod": "monitoring-prometheus-node-exporter-4711", - "prometheus": "monitoring/monitoring-prometheus-oper-prometheus", - "service": "monitoring-prometheus-node-exporter", - "severity": "warning" - } -} \ No newline at end of file diff --git a/tests/test_Alertmanager_warning.json b/tests/test_Alertmanager_warning.json deleted file mode 100644 index 3142d490e..000000000 --- a/tests/test_Alertmanager_warning.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "labels": { - "alertname": "TargetDown", - "job": "kubelet", - "prometheus": "monitoring/monitoring-prometheus-oper-prometheus", - "severity": "warning" - }, - "annotations": { - "message": "66.6% of the kubelet targets are down." - }, - "startsAt": "1970-01-01T00:00:00.520032135Z", - "endsAt": "1970-01-01T00:00:00.520032135Z", - "updatedAt": "1970-01-01T00:00:00.520032135Z", - "generatorURL": "http://localhost", - "status": { - "state": "active", - "silencedBy": [], - "inhibitedBy": [] - }, - "receivers": [ - "null" - ], - "fingerprint": "7be970c6e97b95c9" -} \ No newline at end of file From cea97df3ec9ccbc0b7613e47b71992f4f497e866 Mon Sep 17 00:00:00 2001 From: Stephan Schwarz <stearz@gmx.de> Date: Wed, 25 Aug 2021 12:08:51 +0200 Subject: [PATCH 151/884] fixed tests to import AlermanagerServer like Nagstamon does --- tests/test_alertmanager.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/tests/test_alertmanager.py b/tests/test_alertmanager.py index 6f56e09a4..d9501f553 100644 --- a/tests/test_alertmanager.py +++ b/tests/test_alertmanager.py @@ -3,8 +3,7 @@ from pylint import lint import unittest -import Nagstamon -import Nagstamon.Servers.Alertmanager +from Nagstamon.Servers.Alertmanager import AlertmanagerServer conf = {} conf['debug_mode'] = True @@ -20,7 +19,7 @@ def test_unit_alert_suppressed(self): with open('tests/test_alertmanager_suppressed.json') as json_file: data = json.load(json_file) - test_class = Nagstamon.Servers.Alertmanager.AlertmanagerServer() + test_class = AlertmanagerServer() test_class.map_to_hostname = 'instance,pod_name,namespace' test_class.map_to_servicename = 'alertname' test_class.map_to_status_information = 'message,summary,description' @@ -48,7 +47,7 @@ def test_unit_alert_skipped(self): with open('tests/test_alertmanager_skipped.json') as json_file: data = json.load(json_file) - test_class = Nagstamon.Servers.Alertmanager.AlertmanagerServer() + test_class = AlertmanagerServer() test_class.map_to_hostname = 'instance,pod_name,namespace' test_class.map_to_servicename = 'alertname' test_class.map_to_status_information = 'message,summary,description' @@ -62,7 +61,7 @@ def test_unit_alert_warning(self): with open('tests/test_alertmanager_warning.json') as json_file: data = json.load(json_file) - test_class = Nagstamon.Servers.Alertmanager.AlertmanagerServer() + test_class = AlertmanagerServer() test_class.map_to_hostname = 'instance,pod_name,namespace' test_class.map_to_servicename = 'alertname' test_class.map_to_status_information = 'message,summary,description' @@ -90,7 +89,7 @@ def test_unit_alert_critical(self): with open('tests/test_alertmanager_critical.json') as json_file: data = json.load(json_file) - test_class = Nagstamon.Servers.Alertmanager.AlertmanagerServer() + test_class = AlertmanagerServer() test_class.map_to_hostname = 'instance,pod_name,namespace' test_class.map_to_servicename = 'alertname' test_class.map_to_status_information = 'message,summary,description' @@ -118,7 +117,7 @@ def test_unit_alert_critical_with_empty_maps(self): with open('tests/test_alertmanager_critical.json') as json_file: data = json.load(json_file) - test_class = Nagstamon.Servers.Alertmanager.AlertmanagerServer() + test_class = AlertmanagerServer() test_class.map_to_hostname = '' test_class.map_to_servicename = '' test_class.map_to_status_information = '' @@ -146,7 +145,7 @@ def test_unit_alert_custom_severity_critical(self): with open('tests/test_alertmanager_custom_severity.json') as json_file: data = json.load(json_file) - test_class = Nagstamon.Servers.Alertmanager.AlertmanagerServer() + test_class = AlertmanagerServer() test_class.map_to_hostname = 'instance,pod_name,namespace' test_class.map_to_servicename = 'alertname' test_class.map_to_status_information = 'message,summary,description' From b2dfe3f971205123fe408a13658fc72126638c17 Mon Sep 17 00:00:00 2001 From: Stephan Schwarz <stearz@gmx.de> Date: Wed, 25 Aug 2021 13:37:35 +0200 Subject: [PATCH 152/884] bumped version strings --- ChangeLog | 2 +- Nagstamon/Config.py | 2 +- build/debian/changelog | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 47315a892..bf2f67401 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,4 @@ -nagstamon (3.7-20210823) unstable; urgency=low +nagstamon (3.7-20210825) unstable; urgency=low * New upstream - added test section to GitHub actions workflows to enable testing - added basic unit tests and linting to Alertmanager diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 48c2e6fb1..a2ff60470 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.7-20210823' + VERSION = '3.7-20210825' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2021 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 3c03f22cd..60c4cc1bb 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.7-20210823) unstable; urgency=low +nagstamon (3.7-20210825) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Mon, 09 Aug 2021 19:00:00 +0100 From b94234adb15032b1ede2eb2c58d779f157f792fd Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 26 Aug 2021 02:13:48 +0200 Subject: [PATCH 153/884] added fedora-35 --- .github/workflows/build-release-latest.yml | 11 ++++++++++ build/docker/Dockerfile-fedora-34 | 2 +- build/docker/Dockerfile-fedora-35 | 25 ++++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 build/docker/Dockerfile-fedora-35 diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index c60d3f43e..4b8264565 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -81,6 +81,17 @@ jobs: path: build/*.rpm retention-days: 1 + fedora-35: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + - uses: actions/upload-artifact@v2 + with: + path: build/*.rpm + retention-days: 1 + macos: runs-on: macos-latest steps: diff --git a/build/docker/Dockerfile-fedora-34 b/build/docker/Dockerfile-fedora-34 index 5b28a9647..01457f023 100644 --- a/build/docker/Dockerfile-fedora-34 +++ b/build/docker/Dockerfile-fedora-34 @@ -1,4 +1,4 @@ -FROM fedora:34 +FROM fedora:35 LABEL maintainer=henri@nagstamon.de RUN dnf -y install desktop-file-utils \ diff --git a/build/docker/Dockerfile-fedora-35 b/build/docker/Dockerfile-fedora-35 new file mode 100644 index 000000000..5b28a9647 --- /dev/null +++ b/build/docker/Dockerfile-fedora-35 @@ -0,0 +1,25 @@ +FROM fedora:34 +LABEL maintainer=henri@nagstamon.de + +RUN dnf -y install desktop-file-utils \ + git \ + python3 \ + python3-beautifulsoup4 \ + python3-crypto \ + python3-cryptography \ + python3-dateutil \ + python3-devel \ + python3-keyring \ + python3-lxml \ + python3-psutil \ + python3-qt5 \ + python3-qt5-devel \ + python3-requests \ + python3-requests-kerberos \ + python3-SecretStorage \ + qt5-qtsvg \ + qt5-qtmultimedia \ + rpm-build + +CMD cd /nagstamon/build && \ + /usr/bin/python3 build.py From cc999350b9a1466e87f9b133705165c3b1b633e2 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 26 Aug 2021 02:18:46 +0200 Subject: [PATCH 154/884] added test requirement --- .github/workflows/build-release-latest.yml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 4b8264565..e9336f824 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -39,6 +39,7 @@ jobs: debian: runs-on: ubuntu-latest + needs: test steps: - uses: actions/checkout@v2 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . @@ -50,6 +51,7 @@ jobs: fedora-32: runs-on: ubuntu-latest + needs: test steps: - uses: actions/checkout@v2 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . @@ -61,6 +63,7 @@ jobs: fedora-33: runs-on: ubuntu-latest + needs: test steps: - uses: actions/checkout@v2 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . @@ -72,6 +75,7 @@ jobs: fedora-34: runs-on: ubuntu-latest + needs: test steps: - uses: actions/checkout@v2 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . @@ -83,6 +87,7 @@ jobs: fedora-35: runs-on: ubuntu-latest + needs: test steps: - uses: actions/checkout@v2 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . @@ -94,6 +99,7 @@ jobs: macos: runs-on: macos-latest + needs: test steps: - uses: actions/checkout@v2 - run: pip3 install --no-warn-script-location -r build/requirements/macos.txt @@ -107,6 +113,7 @@ jobs: windows-32: runs-on: windows-latest + needs: test steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 @@ -126,6 +133,7 @@ jobs: windows-64: runs-on: windows-latest + needs: test steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 @@ -145,7 +153,7 @@ jobs: repo-fedora: runs-on: ubuntu-latest - needs: [fedora-32, fedora-33, fedora-34] + needs: [fedora-32, fedora-33, fedora-34, fedora-35] steps: # get binaries created by other jobs - uses: actions/download-artifact@v2 @@ -167,7 +175,7 @@ jobs: github-release: runs-on: ubuntu-latest - needs: [debian, fedora-32, fedora-33, fedora-34, macos, windows-32, windows-64] + needs: [debian, fedora-32, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] steps: - uses: actions/download-artifact@v2 - run: cd artifact && md5sum *agstamon* > md5sums.txt From af88edf24f43f0a693ab653839a554b892ccb977 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 26 Aug 2021 02:20:50 +0200 Subject: [PATCH 155/884] avoid duplicate requirements.txt --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index e9336f824..df01daca5 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -26,7 +26,7 @@ jobs: sudo apt-get install libkrb5-dev python -m pip install --upgrade pip pip install pytest pylint #flake8 - if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + if [ -f build/requirements/linux.txt ]; then pip install -r build/requirements/linux.txt; fi # - name: Lint with flake8 # run: | # # stop the build if there are Python syntax errors or undefined names From 230dbede52a38a0c4f5410c8f7080a3a0e24afee Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 26 Aug 2021 02:59:49 +0200 Subject: [PATCH 156/884] fixed Dockerfile-fedora35 --- build/docker/Dockerfile-fedora-35 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/docker/Dockerfile-fedora-35 b/build/docker/Dockerfile-fedora-35 index 5b28a9647..01457f023 100644 --- a/build/docker/Dockerfile-fedora-35 +++ b/build/docker/Dockerfile-fedora-35 @@ -1,4 +1,4 @@ -FROM fedora:34 +FROM fedora:35 LABEL maintainer=henri@nagstamon.de RUN dnf -y install desktop-file-utils \ From d332699434af597e100205b61347e88e5bb0d69c Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 26 Aug 2021 03:13:17 +0200 Subject: [PATCH 157/884] debug missing fedora-35 --- .github/workflows/build-release-latest.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index df01daca5..89468dfe5 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -80,6 +80,7 @@ jobs: - uses: actions/checkout@v2 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + - run: ls -lR - uses: actions/upload-artifact@v2 with: path: build/*.rpm @@ -92,6 +93,7 @@ jobs: - uses: actions/checkout@v2 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + - run: ls -lR - uses: actions/upload-artifact@v2 with: path: build/*.rpm @@ -153,7 +155,7 @@ jobs: repo-fedora: runs-on: ubuntu-latest - needs: [fedora-32, fedora-33, fedora-34, fedora-35] + needs: [debian, fedora-32, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] steps: # get binaries created by other jobs - uses: actions/download-artifact@v2 From adb65f84732a646a321b8d03f8c29ed519973c65 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 26 Aug 2021 14:04:44 +0200 Subject: [PATCH 158/884] workaround for Alertmanager caused problem while packaging - only considered as workaround to get a running package again --- Nagstamon/Config.py | 2 +- build/debian/changelog | 2 +- setup.py | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index a2ff60470..2ff6aaaee 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.7-20210825' + VERSION = '3.7-20210826' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2021 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 60c4cc1bb..c0ae00a3d 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.7-20210825) unstable; urgency=low +nagstamon (3.7-20210826) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Mon, 09 Aug 2021 19:00:00 +0100 diff --git a/setup.py b/setup.py index 44e065678..8f6af9d0f 100644 --- a/setup.py +++ b/setup.py @@ -117,6 +117,7 @@ packages=['Nagstamon', 'Nagstamon.QUI', 'Nagstamon.Servers', + 'Nagstamon.Servers.Alertmanager', 'Nagstamon.thirdparty', 'Nagstamon.thirdparty.Xlib', 'Nagstamon.thirdparty.Xlib.ext', From f606e7567db4b25e2072ffa99da7efec8df8a396 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 26 Aug 2021 14:15:25 +0200 Subject: [PATCH 159/884] fix for https://github.com/HenriWahl/Nagstamon/issues/766 --- Nagstamon/Helpers.py | 7 ++++--- build/docker/Dockerfile-fedora-34 | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Nagstamon/Helpers.py b/Nagstamon/Helpers.py index ff4656f38..ca02a4f75 100644 --- a/Nagstamon/Helpers.py +++ b/Nagstamon/Helpers.py @@ -451,9 +451,10 @@ def get_distro(): os_release_file = Path('/etc/os-release') if os_release_file.exists() and (os_release_file.is_file() or os_release_file.is_symlink()): os_release_dict = {} - for property in os_release_file.read_text().splitlines(): - key, value = property.split('=', 1) - os_release_dict[key] = value.strip('"').strip("'") + for line in os_release_file.read_text().splitlines(): + if not line.startswith('#'): + key, value = line.split('=', 1) + os_release_dict[key] = value.strip('"').strip("'") return (os_release_dict.get('ID').lower(), os_release_dict.get('VERSION_ID', 'unknown').lower(), os_release_dict.get('NAME').lower()) diff --git a/build/docker/Dockerfile-fedora-34 b/build/docker/Dockerfile-fedora-34 index 01457f023..5b28a9647 100644 --- a/build/docker/Dockerfile-fedora-34 +++ b/build/docker/Dockerfile-fedora-34 @@ -1,4 +1,4 @@ -FROM fedora:35 +FROM fedora:34 LABEL maintainer=henri@nagstamon.de RUN dnf -y install desktop-file-utils \ From eab37e23d29bd04ed2963584174bd0f8fcd9a7f0 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 26 Aug 2021 14:22:56 +0200 Subject: [PATCH 160/884] moved macos .spec file to folder --- Nagstamon/resources/nagstamon.1.gz | Bin 788 -> 788 bytes build/build.py | 2 +- .../nagstamon.spec} | 8 ++++---- 3 files changed, 5 insertions(+), 5 deletions(-) rename build/{Nagstamon-macos.spec => macos/nagstamon.spec} (83%) diff --git a/Nagstamon/resources/nagstamon.1.gz b/Nagstamon/resources/nagstamon.1.gz index 8f69a8979d98d82a6ba190d47bad27ebb2b4c34b..c7dabe3651570176a153007e10a709c7bf422fbb 100644 GIT binary patch delta 17 YcmbQjHieBtzMF$XpiMn-BL@#N041UW-2eap delta 17 YcmbQjHieBtzMF&NYobuxMh+fk04q8Kg#Z8m diff --git a/build/build.py b/build/build.py index fd09dae12..367cc13c8 100644 --- a/build/build.py +++ b/build/build.py @@ -126,7 +126,7 @@ def macmain(): os.environ['NAGSTAMON_VERSION'] = VERSION # create one-file .app bundle by pyinstaller - subprocess.call(['pyinstaller --noconfirm Nagstamon-macos.spec'], shell=True) + subprocess.call(['pyinstaller --noconfirm macos/nagstamon.spec'], shell=True) # create staging DMG folder for later compressing of DMG shutil.rmtree('Nagstamon {0} Staging DMG'.format(VERSION), ignore_errors=True) diff --git a/build/Nagstamon-macos.spec b/build/macos/nagstamon.spec similarity index 83% rename from build/Nagstamon-macos.spec rename to build/macos/nagstamon.spec index b909478c0..b45a81929 100644 --- a/build/Nagstamon-macos.spec +++ b/build/macos/nagstamon.spec @@ -4,10 +4,10 @@ import os block_cipher = None -a = Analysis(['../nagstamon.py'], +a = Analysis(['../../nagstamon.py'], pathex=[], binaries=[], - datas=[('../Nagstamon/resources', 'Nagstamon/resources')], + datas=[('../../Nagstamon/resources', '../Nagstamon/resources')], hiddenimports=[], hookspath=[], hooksconfig={}, @@ -37,11 +37,11 @@ exe = EXE(pyz, target_arch=None, codesign_identity=None, entitlements_file=None, - icon='../Nagstamon/resources/nagstamon.icns') + icon='../../Nagstamon/resources/nagstamon.icns') app = BUNDLE(exe, name='Nagstamon.app', - icon='../Nagstamon/resources/nagstamon.icns', + icon='../../Nagstamon/resources/nagstamon.icns', bundle_identifier='de.ifw-dresden.nagstamon', version=os.environ['NAGSTAMON_VERSION'], info_plist={ From 2185e115493556f1b826b9e39fb604813b19f5bc Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 26 Aug 2021 14:28:18 +0200 Subject: [PATCH 161/884] "if-no-files-found: error" added to upload artifacts --- .github/workflows/build-release-latest.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 89468dfe5..9023314c9 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -48,6 +48,7 @@ jobs: with: path: build/*.deb retention-days: 1 + if-no-files-found: error fedora-32: runs-on: ubuntu-latest @@ -60,6 +61,7 @@ jobs: with: path: build/*.rpm retention-days: 1 + if-no-files-found: error fedora-33: runs-on: ubuntu-latest @@ -72,6 +74,7 @@ jobs: with: path: build/*.rpm retention-days: 1 + if-no-files-found: error fedora-34: runs-on: ubuntu-latest @@ -85,6 +88,7 @@ jobs: with: path: build/*.rpm retention-days: 1 + if-no-files-found: error fedora-35: runs-on: ubuntu-latest @@ -98,6 +102,7 @@ jobs: with: path: build/*.rpm retention-days: 1 + if-no-files-found: error macos: runs-on: macos-latest @@ -112,6 +117,7 @@ jobs: with: path: build/*.dmg retention-days: 1 + if-no-files-found: error windows-32: runs-on: windows-latest @@ -132,6 +138,7 @@ jobs: build/dist/*.zip build/dist/*.exe retention-days: 1 + if-no-files-found: error windows-64: runs-on: windows-latest @@ -152,6 +159,7 @@ jobs: build/dist/*.zip build/dist/*.exe retention-days: 1 + if-no-files-found: error repo-fedora: runs-on: ubuntu-latest From 1ae1c4e89ef6fb24dcd6c72daadf6cbd350fcb89 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 26 Aug 2021 14:38:06 +0200 Subject: [PATCH 162/884] fiddling with build-release-latest.yml and upload artifacts --- .github/workflows/build-release-latest.yml | 3 ++- Nagstamon/resources/nagstamon.1.gz | Bin 788 -> 788 bytes 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 9023314c9..8563d3d35 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -100,7 +100,8 @@ jobs: - run: ls -lR - uses: actions/upload-artifact@v2 with: - path: build/*.rpm +# path: build/*.rpm + path: **/*.rpm retention-days: 1 if-no-files-found: error diff --git a/Nagstamon/resources/nagstamon.1.gz b/Nagstamon/resources/nagstamon.1.gz index c7dabe3651570176a153007e10a709c7bf422fbb..3d3297fe51276208e878a77c70ef65ab8fc4c58a 100644 GIT binary patch delta 15 WcmbQjHieB%zMF%iylW#HA2R?T9|OJs delta 15 WcmbQjHieB%zMF$Xplu@?A2R?Qp#xI@ From 7c39e1357edbef6bf35220cb6c6fb44a188443da Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 26 Aug 2021 14:40:27 +0200 Subject: [PATCH 163/884] fiddling with build-release-latest.yml and upload artifacts part II --- .github/workflows/build-release-latest.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 8563d3d35..f027bb48a 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -83,7 +83,7 @@ jobs: - uses: actions/checkout@v2 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon - - run: ls -lR + - run: ls -l build - uses: actions/upload-artifact@v2 with: path: build/*.rpm @@ -97,11 +97,11 @@ jobs: - uses: actions/checkout@v2 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon - - run: ls -lR + - run: ls -l build - uses: actions/upload-artifact@v2 with: # path: build/*.rpm - path: **/*.rpm + path: '**/*.rpm' retention-days: 1 if-no-files-found: error From a9d1d3e0fb2c54beb45f929a3dd1edfff461b10f Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 26 Aug 2021 14:43:57 +0200 Subject: [PATCH 164/884] debugging non-working Fedora-35 build action --- .github/workflows/build-release-latest.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index f027bb48a..9e12f52b1 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -98,6 +98,7 @@ jobs: - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon - run: ls -l build + - run: find . -name "*.rpm" - uses: actions/upload-artifact@v2 with: # path: build/*.rpm From 630359c4a65c474ca2f2045b282e80bb92ac93a2 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 26 Aug 2021 14:54:28 +0200 Subject: [PATCH 165/884] removed requirements.txt because they are in build/requirements --- Nagstamon/resources/nagstamon.1.gz | Bin 788 -> 788 bytes requirements.txt | 10 ---------- 2 files changed, 10 deletions(-) delete mode 100644 requirements.txt diff --git a/Nagstamon/resources/nagstamon.1.gz b/Nagstamon/resources/nagstamon.1.gz index 3d3297fe51276208e878a77c70ef65ab8fc4c58a..d5380cab085d11061caaa1136f9996d6d06ce2db 100644 GIT binary patch delta 15 WcmbQjHieB%zMF&NUGGLVK4t(S_yjlr delta 15 WcmbQjHieB%zMF%iylW#HA2R?T9|OJs diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 3e9605734..000000000 --- a/requirements.txt +++ /dev/null @@ -1,10 +0,0 @@ -beautifulsoup4 -bs4 -keyring -lxml -psutil -PyQt5 -PySocks -python-dateutil -requests -requests-kerberos From 46b3e2b89d87b1b687982978a3338c30c7db66b8 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 26 Aug 2021 14:59:44 +0200 Subject: [PATCH 166/884] seemingly out of memory problem --- .github/workflows/build-release-latest.yml | 26 +++++++++++----------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 9e12f52b1..f88f7881b 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -76,19 +76,19 @@ jobs: retention-days: 1 if-no-files-found: error - fedora-34: - runs-on: ubuntu-latest - needs: test - steps: - - uses: actions/checkout@v2 - - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon - - run: ls -l build - - uses: actions/upload-artifact@v2 - with: - path: build/*.rpm - retention-days: 1 - if-no-files-found: error +# fedora-34: +# runs-on: ubuntu-latest +# needs: test +# steps: +# - uses: actions/checkout@v2 +# - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . +# - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon +# - run: ls -l build +# - uses: actions/upload-artifact@v2 +# with: +# path: build/*.rpm +# retention-days: 1 +# if-no-files-found: error fedora-35: runs-on: ubuntu-latest From 4e999d0ab9247fde6214a1d2193f2596447a83e8 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 26 Aug 2021 15:01:13 +0200 Subject: [PATCH 167/884] seemingly out of memory problem part II --- .github/workflows/build-release-latest.yml | 36 +++++++++++----------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index f88f7881b..3a003e9a7 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -50,7 +50,20 @@ jobs: retention-days: 1 if-no-files-found: error - fedora-32: +# fedora-32: +# runs-on: ubuntu-latest +# needs: test +# steps: +# - uses: actions/checkout@v2 +# - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . +# - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon +# - uses: actions/upload-artifact@v2 +# with: +# path: build/*.rpm +# retention-days: 1 +# if-no-files-found: error + + fedora-33: runs-on: ubuntu-latest needs: test steps: @@ -63,33 +76,20 @@ jobs: retention-days: 1 if-no-files-found: error - fedora-33: + fedora-34: runs-on: ubuntu-latest needs: test steps: - uses: actions/checkout@v2 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + - run: ls -l build - uses: actions/upload-artifact@v2 with: path: build/*.rpm retention-days: 1 if-no-files-found: error -# fedora-34: -# runs-on: ubuntu-latest -# needs: test -# steps: -# - uses: actions/checkout@v2 -# - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . -# - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon -# - run: ls -l build -# - uses: actions/upload-artifact@v2 -# with: -# path: build/*.rpm -# retention-days: 1 -# if-no-files-found: error - fedora-35: runs-on: ubuntu-latest needs: test @@ -165,7 +165,7 @@ jobs: repo-fedora: runs-on: ubuntu-latest - needs: [debian, fedora-32, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] + needs: [debian, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] steps: # get binaries created by other jobs - uses: actions/download-artifact@v2 @@ -187,7 +187,7 @@ jobs: github-release: runs-on: ubuntu-latest - needs: [debian, fedora-32, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] + needs: [debian, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] steps: - uses: actions/download-artifact@v2 - run: cd artifact && md5sum *agstamon* > md5sums.txt From 1ecdfe1f6261e3ece3678fe5cd7ad0985faf5b90 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 26 Aug 2021 15:15:18 +0200 Subject: [PATCH 168/884] due to memory problem do not try to build for Fedora 35 --- .github/workflows/build-release-latest.yml | 44 +++++++++++----------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 3a003e9a7..c0926582e 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -50,20 +50,7 @@ jobs: retention-days: 1 if-no-files-found: error -# fedora-32: -# runs-on: ubuntu-latest -# needs: test -# steps: -# - uses: actions/checkout@v2 -# - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . -# - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon -# - uses: actions/upload-artifact@v2 -# with: -# path: build/*.rpm -# retention-days: 1 -# if-no-files-found: error - - fedora-33: + fedora-32: runs-on: ubuntu-latest needs: test steps: @@ -76,36 +63,47 @@ jobs: retention-days: 1 if-no-files-found: error - fedora-34: + fedora-33: runs-on: ubuntu-latest needs: test steps: - uses: actions/checkout@v2 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon - - run: ls -l build - uses: actions/upload-artifact@v2 with: path: build/*.rpm retention-days: 1 if-no-files-found: error - fedora-35: + fedora-34: runs-on: ubuntu-latest needs: test steps: - uses: actions/checkout@v2 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon - - run: ls -l build - - run: find . -name "*.rpm" - uses: actions/upload-artifact@v2 with: -# path: build/*.rpm - path: '**/*.rpm' + path: build/*.rpm retention-days: 1 if-no-files-found: error + # maybe Fedora 35 is not mature yet, but there is an out-of-memory-error when trying to build + # via GitHub Actions +# fedora-35: +# runs-on: ubuntu-latest +# needs: test +# steps: +# - uses: actions/checkout@v2 +# - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . +# - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon +# - uses: actions/upload-artifact@v2 +# with: +# path: build/*.rpm +# retention-days: 1 +# if-no-files-found: error + macos: runs-on: macos-latest needs: test @@ -165,7 +163,7 @@ jobs: repo-fedora: runs-on: ubuntu-latest - needs: [debian, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] + needs: [debian, fedora-32, fedora-33, fedora-34, macos, windows-32, windows-64] steps: # get binaries created by other jobs - uses: actions/download-artifact@v2 @@ -187,7 +185,7 @@ jobs: github-release: runs-on: ubuntu-latest - needs: [debian, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] + needs: [debian, fedora-32, fedora-33, fedora-34, macos, windows-32, windows-64] steps: - uses: actions/download-artifact@v2 - run: cd artifact && md5sum *agstamon* > md5sums.txt From f1c338d1e6199891697c086b483566f50119ab19 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 26 Aug 2021 15:29:29 +0200 Subject: [PATCH 169/884] try with single fedora-35 job --- .github/workflows/build-release-latest.yml | 28 +++++++++++----------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index c0926582e..19e7c9539 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -91,18 +91,18 @@ jobs: # maybe Fedora 35 is not mature yet, but there is an out-of-memory-error when trying to build # via GitHub Actions -# fedora-35: -# runs-on: ubuntu-latest -# needs: test -# steps: -# - uses: actions/checkout@v2 -# - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . -# - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon -# - uses: actions/upload-artifact@v2 -# with: -# path: build/*.rpm -# retention-days: 1 -# if-no-files-found: error + fedora-35: + runs-on: ubuntu-latest + needs: [ debian, fedora-32, fedora-33, fedora-34, macos, test, windows-32, windows-64 ] + steps: + - uses: actions/checkout@v2 + - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + - uses: actions/upload-artifact@v2 + with: + path: build/*.rpm + retention-days: 1 + if-no-files-found: error macos: runs-on: macos-latest @@ -163,7 +163,7 @@ jobs: repo-fedora: runs-on: ubuntu-latest - needs: [debian, fedora-32, fedora-33, fedora-34, macos, windows-32, windows-64] + needs: [debian, fedora-32, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] steps: # get binaries created by other jobs - uses: actions/download-artifact@v2 @@ -185,7 +185,7 @@ jobs: github-release: runs-on: ubuntu-latest - needs: [debian, fedora-32, fedora-33, fedora-34, macos, windows-32, windows-64] + needs: [debian, fedora-32, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] steps: - uses: actions/download-artifact@v2 - run: cd artifact && md5sum *agstamon* > md5sums.txt From a87c8b315a0f09302943b47334f87166226ff38c Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 26 Aug 2021 15:40:31 +0200 Subject: [PATCH 170/884] try with single fedora-35 job - failed too --- .github/workflows/build-release-latest.yml | 28 +++++++++++----------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 19e7c9539..672b0198c 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -91,18 +91,18 @@ jobs: # maybe Fedora 35 is not mature yet, but there is an out-of-memory-error when trying to build # via GitHub Actions - fedora-35: - runs-on: ubuntu-latest - needs: [ debian, fedora-32, fedora-33, fedora-34, macos, test, windows-32, windows-64 ] - steps: - - uses: actions/checkout@v2 - - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon - - uses: actions/upload-artifact@v2 - with: - path: build/*.rpm - retention-days: 1 - if-no-files-found: error +# fedora-35: +# runs-on: ubuntu-latest +# needs: [ debian, fedora-32, fedora-33, fedora-34, macos, test, windows-32, windows-64 ] +# steps: +# - uses: actions/checkout@v2 +# - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . +# - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon +# - uses: actions/upload-artifact@v2 +# with: +# path: build/*.rpm +# retention-days: 1 +# if-no-files-found: error macos: runs-on: macos-latest @@ -163,7 +163,7 @@ jobs: repo-fedora: runs-on: ubuntu-latest - needs: [debian, fedora-32, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] + needs: [debian, fedora-32, fedora-33, fedora-34, macos, windows-32, windows-64] steps: # get binaries created by other jobs - uses: actions/download-artifact@v2 @@ -185,7 +185,7 @@ jobs: github-release: runs-on: ubuntu-latest - needs: [debian, fedora-32, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] + needs: [debian, fedora-32, fedora-33, fedora-34, macos, windows-32, windows-64] steps: - uses: actions/download-artifact@v2 - run: cd artifact && md5sum *agstamon* > md5sums.txt From d797a0f05f00c428c6b1715f038f310a12050058 Mon Sep 17 00:00:00 2001 From: hunsbea <aahunsberger@gmail.com> Date: Sun, 29 Aug 2021 20:27:10 +0800 Subject: [PATCH 171/884] prefetch items to reduce refresh time --- Nagstamon/Servers/Zabbix.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/Nagstamon/Servers/Zabbix.py b/Nagstamon/Servers/Zabbix.py index 38004fb2b..1e59a6dc1 100644 --- a/Nagstamon/Servers/Zabbix.py +++ b/Nagstamon/Servers/Zabbix.py @@ -288,16 +288,21 @@ def _get_status(self): }) unack_trigger_ids = [u['triggerid'] for u in unack_triggers] + # prefetch items + all_item_ids = list(set([t['items'][0]['itemid'] for t in triggers])) + all_items = self.zapi.item.get( + {'itemids':all_item_ids, + 'output': ['itemid', 'hostid', 'name', 'lastvalue'], + 'selectTags': 'extend', + 'selectApplications': 'extend'} + ) + itemid_item_map = {i['itemid']: i for i in all_items} + for t in triggers: t['acknowledged'] = False if t['triggerid'] in unack_trigger_ids else True # get Application name for the trigger - this_item = self.zapi.item.get( - {'itemids': [t['items'][0]['itemid']], - 'output': ['itemid', 'hostid', 'name', 'lastvalue'], - 'selectTags': 'extend', - 'selectApplications': 'extend'} - ) + this_item = [itemid_item_map[t['items'][0]['itemid']]] t['application'] = self.getLastApp(this_item) try: t['lastvalue'] = this_item[0]['lastvalue'] @@ -505,7 +510,7 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi triggers = self.zapi.trigger.get({ 'output': ['triggerid', 'manual_close'], 'filter': {'triggerid': triggerid}}) - if not triggers or 'manual_close' not in triggers[0] or triggers[0]['manual_close'] == 1: + if not triggers or 'manual_close' not in triggers[0] or str(triggers[0]['manual_close']) == '1': actions |= 1 # The current Nagstamon menu items don't match up too well with the Zabbix actions, # but perhaps "Persistent comment" is the closest thing to acknowledgement From 16902bc4cccdc04c3ad0d5462574955cc3f03432 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 29 Aug 2021 21:57:08 +0200 Subject: [PATCH 172/884] Update Config.py --- Nagstamon/Config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 2ff6aaaee..6be618394 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.7-20210826' + VERSION = '3.7-20210829' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2021 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' From 633c4466a6e7b4c340d1d515962bf33d77f861b8 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 29 Aug 2021 21:57:32 +0200 Subject: [PATCH 173/884] Update changelog --- build/debian/changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/debian/changelog b/build/debian/changelog index c0ae00a3d..6b8924288 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.7-20210826) unstable; urgency=low +nagstamon (3.7-20210829) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Mon, 09 Aug 2021 19:00:00 +0100 From 324df2d5ced24368e7c4dd5b64a96bb41d312a00 Mon Sep 17 00:00:00 2001 From: hunsbea <aahunsberger@gmail.com> Date: Wed, 1 Sep 2021 19:43:04 +0800 Subject: [PATCH 174/884] zabbix trigger item race condition --- Nagstamon/Servers/Zabbix.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Nagstamon/Servers/Zabbix.py b/Nagstamon/Servers/Zabbix.py index 1e59a6dc1..b93816afd 100644 --- a/Nagstamon/Servers/Zabbix.py +++ b/Nagstamon/Servers/Zabbix.py @@ -302,7 +302,12 @@ def _get_status(self): t['acknowledged'] = False if t['triggerid'] in unack_trigger_ids else True # get Application name for the trigger - this_item = [itemid_item_map[t['items'][0]['itemid']]] + if t['items'][0]['itemid'] in itemid_item_map: + this_item = [itemid_item_map[t['items'][0]['itemid']]] + else: + # This else condition should never be hit, except in rare circumstances the trigger/item + # config is updated at the same time Nagstamon is pulling trigger/item config + this_item = [] t['application'] = self.getLastApp(this_item) try: t['lastvalue'] = this_item[0]['lastvalue'] From 381379c70cc870c14351cc9dad4d0884f48227ad Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Fri, 3 Sep 2021 22:54:46 +0200 Subject: [PATCH 175/884] versin 20210901 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 6be618394..078545355 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.7-20210829' + VERSION = '3.7-20210901' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2021 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 6b8924288..6defca379 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.7-20210829) unstable; urgency=low +nagstamon (3.7-20210901) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Mon, 09 Aug 2021 19:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Wed, 01 Sep 2021 19:00:00 +0100 nagstamon (3.6.0) stable; urgency=low * New upstream From 6722b6337df91e80f9a406e6f6d643d5fe89f977 Mon Sep 17 00:00:00 2001 From: Moritz Schlarb <schlarbm@uni-mainz.de> Date: Tue, 7 Sep 2021 19:49:03 +0000 Subject: [PATCH 176/884] Update nagstamon.1 Fix typo and homepage adress --- Nagstamon/resources/nagstamon.1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/resources/nagstamon.1 b/Nagstamon/resources/nagstamon.1 index 9a56f4c6c..c8915476f 100644 --- a/Nagstamon/resources/nagstamon.1 +++ b/Nagstamon/resources/nagstamon.1 @@ -38,9 +38,9 @@ nagstamon [alternate\-config] Nagstamon is a Nagios status monitor which takes place in systray or on desktop as floating statusbar to inform you in realtime about the status of your Nagios monitored network&. Nagstamon connects to multiple Nagios, Opsview, Icinga, Centreon, Op5Monitor, Checkmk Multisite and Thruk monitoring servers. Experimental support for Zabbix, Zenoss and Livestatus is included. .sp The command can optionally take one argument giving the path to an alternate configuration file. -.SH RESSOURCES +.SH RESOURCES .sp -\fI\%https://nagstamon.ifw\-dresden.de\fP +\fI\%https://nagstamon.de\fP .SH AUTHOR This manual page has been written by Carl Chenet <chaica@ohmytux.com> and updated by Henri Wahl <h.wahl@ifw-dresden.de> .SH COPYRIGHT From b86068a0b1db71bc63119657ddcb9225772f0bba Mon Sep 17 00:00:00 2001 From: Moritz Schlarb <schlarbm@uni-mainz.de> Date: Tue, 7 Sep 2021 19:50:23 +0000 Subject: [PATCH 177/884] Update nagstamon.desktop Add keywords to desktop file --- Nagstamon/resources/nagstamon.desktop | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Nagstamon/resources/nagstamon.desktop b/Nagstamon/resources/nagstamon.desktop index d4198fe30..4407ff6c7 100644 --- a/Nagstamon/resources/nagstamon.desktop +++ b/Nagstamon/resources/nagstamon.desktop @@ -5,6 +5,7 @@ Comment=Nagios status monitor Icon=nagstamon Exec=nagstamon Terminal=false -Categories=System;Monitor; +Categories=System;Monitor;GTK; +Keywords=system;monitor;remote; StartupNotify=true GenericName=Nagios status monitor for the desktop From cb4138a03b926d3f9f87bf2c22b4acdb0a322b60 Mon Sep 17 00:00:00 2001 From: Moritz Schlarb <schlarbm@uni-mainz.de> Date: Tue, 7 Sep 2021 19:57:40 +0000 Subject: [PATCH 178/884] Update nagstamon.rst --- Nagstamon/resources/nagstamon.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/resources/nagstamon.rst b/Nagstamon/resources/nagstamon.rst index f3803f1aa..526a2a015 100644 --- a/Nagstamon/resources/nagstamon.rst +++ b/Nagstamon/resources/nagstamon.rst @@ -22,7 +22,7 @@ Nagstamon is a Nagios status monitor which takes place in systray or on desktop The command can optionally take one argument giving the path to an alternate configuration file. -RESSOURCES +RESOURCES ========== https://nagstamon.de From dbad7c0f4e3c40fc76a699e33c1d11504bd8147e Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 9 Sep 2021 21:29:30 +0200 Subject: [PATCH 179/884] try to fix macOS start problem --- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- build/macos/nagstamon.spec | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 078545355..79ab6e29c 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.7-20210901' + VERSION = '3.7-20210908' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2021 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 6defca379..adf01a01a 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.7-20210901) unstable; urgency=low +nagstamon (3.7-20210908) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Wed, 01 Sep 2021 19:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Thu, 08 Sep 2021 19:00:00 +0100 nagstamon (3.6.0) stable; urgency=low * New upstream diff --git a/build/macos/nagstamon.spec b/build/macos/nagstamon.spec index b45a81929..eaedf06e8 100644 --- a/build/macos/nagstamon.spec +++ b/build/macos/nagstamon.spec @@ -7,7 +7,7 @@ block_cipher = None a = Analysis(['../../nagstamon.py'], pathex=[], binaries=[], - datas=[('../../Nagstamon/resources', '../Nagstamon/resources')], + datas=[('../../Nagstamon/resources', 'Nagstamon/resources')], hiddenimports=[], hookspath=[], hooksconfig={}, From 6498ed992108244340ac9901cc69e9d1d43c2bb8 Mon Sep 17 00:00:00 2001 From: dizixagi <dominik.zagol@ti-cahn.org> Date: Fri, 10 Sep 2021 23:08:40 +0200 Subject: [PATCH 180/884] Add support for Zabbix 5.4.0+ and move from urllib to requests - Support for Zabbix 5.4.0+ (API changes, multiple interfaces support) - Persistent connections via requests (reduce load on Zabbix www server) - No longer using thirdparty.zabbix_api module - Code cleanup --- Nagstamon/Servers/ZabbixProblemBased.py | 209 ++++++++++++++++-------- build/requirements/linux.txt | 1 + build/requirements/macos.txt | 1 + build/requirements/windows.txt | 1 + 4 files changed, 148 insertions(+), 64 deletions(-) diff --git a/Nagstamon/Servers/ZabbixProblemBased.py b/Nagstamon/Servers/ZabbixProblemBased.py index 58eebfa92..f7142e111 100644 --- a/Nagstamon/Servers/ZabbixProblemBased.py +++ b/Nagstamon/Servers/ZabbixProblemBased.py @@ -1,32 +1,104 @@ -# -*- encoding: utf-8; py-indent-offset: 4 -*- -# -# Zabbix.py based on Checkmk Multisite.py +# encoding: utf-8 import sys -import urllib.parse import time import logging -import datetime +import requests +from packaging.version import parse as parse_version -from Nagstamon.Helpers import (HumanReadableDurationFromTimestamp, - webbrowser_open) +from Nagstamon.Helpers import HumanReadableDurationFromTimestamp from Nagstamon.Config import conf -from Nagstamon.Objects import (GenericHost, - GenericService, - Result) +from Nagstamon.Objects import GenericHost, GenericService, Result from Nagstamon.Servers.Generic import GenericServer -from Nagstamon.thirdparty.zabbix_api import (ZabbixAPI, - ZabbixAPIException) + +class ZabbixLightApi(): + + logger = None + server_name = "Zabbix" + monitor_url = "http://127.0.0.1/api_jsonrpc.php" + validate_certs = False + r_session = None + + zbx_auth = None + zbx_req_id = 0 + + def __init__(self, server_name, monitor_url, validate_certs): + + self.server_name = server_name + self.monitor_url = monitor_url + "/api_jsonrpc.php" + self.validate_certs = validate_certs + + #persistent connections + self.r_session = requests.Session() + + #configure logging + self.logger = logging.getLogger('ZabbixLightApi_'+server_name) + console_logger = logging.StreamHandler() + console_logger_formatter = logging.Formatter('[%(name)s] %(levelname)s - %(message)s') + console_logger.setFormatter(console_logger_formatter) + self.logger.addHandler(console_logger) + if conf.debug_mode is True: + self.logger.setLevel(logging.DEBUG) + else: + self.logger.setLevel(logging.INFO) + self.logger.debug("ZabbixLightApi START!") + self.logger.debug("monitor_url = " + self.monitor_url) + + def do_request(self, method, params={}): + zabbix_rpc_obj = { + "jsonrpc": "2.0", + "method": method, + "params": params, + "auth": self.zbx_auth, + "id": self.zbx_req_id + } + self.zbx_req_id += 1 + + self.logger.debug("ZBX: > " + str(zabbix_rpc_obj)) + + try: + + response = self.r_session.post(self.monitor_url, json=zabbix_rpc_obj, verify=self.validate_certs) + + #we didnt get HTTP code 200 + if response.status_code != 200: + raise ZabbixLightApiException("Got return code - " + str(response.status_code)) + + #parse response json + response_json = response.json() + self.logger.debug("ZBX: < " + str(response_json)) + + #there was some kind of error during processing our request + if "error" in response_json.keys(): + raise ZabbixLightApiException("ZBX: < " + response_json["error"]["data"]) + + #zabbix returned garbage + if "result" not in response_json.keys(): + raise ZabbixLightApiException("ZBX: < no result object in response") + + #all other network related errors + except Exception as e: + raise ZabbixLightApiException(e) + + return response_json['result'] + + def logged_in(self): + if self.zbx_auth is None: + return False + else: + return True + + def login(self, username, password): + self.logger.debug("Login in as " + username) + self.zbx_auth = self.do_request('user.login', {'user': username, 'password': password}) + +class ZabbixLightApiException(Exception): + pass class ZabbixProblemBasedServer(GenericServer): TYPE = 'ZabbixProblemBased' - zapi = None - - if conf.debug_mode is True: - log_level = logging.DEBUG - else: - log_level = logging.WARNING + zlapi = None def __init__(self, **kwds): GenericServer.__init__(self, **kwds) @@ -49,42 +121,34 @@ def __init__(self, **kwds): self.username = conf.servers[self.get_name()].username self.password = conf.servers[self.get_name()].password - self.ignore_cert = conf.servers[self.get_name()].ignore_cert - self.validate_certs = not self.ignore_cert - - - def _login(self): - try: - # create ZabbixAPI if not yet created - if self.zapi is None: - self.zapi = ZabbixAPI(server=self.monitor_url, path="", log_level=self.log_level, validate_certs=self.validate_certs) - # login if not yet logged in, or if login was refused previously - if not self.zapi.logged_in(): - self.zapi.login(self.username, self.password) - except ZabbixAPIException: - result, error = self.Error(sys.exc_info()) - return Result(result=result, error=error) + self.validate_certs = not conf.servers[self.get_name()].ignore_cert def _get_status(self): """ Get status from Zabbix Server """ - - # Login to ZabbixAPI - self._login() - # ========================================= # problems # ========================================= problems = [] try: + #Are we logged in? + if self.zlapi is None: + self.zlapi = ZabbixLightApi(server_name=self.name, monitor_url=self.monitor_url, validate_certs=self.validate_certs) + self.zbx_version = self.zlapi.do_request("apiinfo.version", {}) + if not self.zlapi.logged_in(): + self.zlapi.login(self.username, self.password) + #Get all current problems (trigger based) - problems = self.zapi.problem.get({'recent': False}) + if conf.filter_acknowledged_hosts_services: + problems = self.zlapi.do_request("problem.get", {'recent': False, 'acknowledged': False}) + else: + problems = self.zlapi.do_request("problem.get", {'recent': False}) for problem in problems: #get trigger which rose current problem - trigger = self.zapi.trigger.get({'triggerids': problem['objectid'], + trigger = self.zlapi.do_request("trigger.get", {'triggerids': problem['objectid'], 'monitored': True, 'active': True, 'skipDependent': True, @@ -114,29 +178,47 @@ def _get_status(self): if trigger[0]['hosts'][0]['maintenance_status'] == "1": self.new_hosts[host_id].scheduled_downtime = True - #host not available via agent - if trigger[0]['hosts'][0]['available'] == "2": - self.new_hosts[host_id].status = "DOWN" - self.new_hosts[host_id].status_information = trigger[0]['hosts'][0]['error'] - self.new_hosts[host_id].duration = HumanReadableDurationFromTimestamp(trigger[0]['hosts'][0]['errors_from']) - - #host not available via ipmi - if trigger[0]['hosts'][0].get('ipmi_available', '0') == "2": - self.new_hosts[host_id].status = "DOWN" - self.new_hosts[host_id].status_information = trigger[0]['hosts'][0]['ipmi_error'] - self.new_hosts[host_id].duration = HumanReadableDurationFromTimestamp(trigger[0]['hosts'][0]['ipmi_errors_from']) - - #host not available via jmx - if trigger[0]['hosts'][0].get('jmx_available', '0') == "2": - self.new_hosts[host_id].status = "DOWN" - self.new_hosts[host_id].status_information = trigger[0]['hosts'][0]['jmx_error'] - self.new_hosts[host_id].duration = HumanReadableDurationFromTimestamp(trigger[0]['hosts'][0]['jmx_errors_from']) - - #host not available via snmp - if trigger[0]['hosts'][0].get('snmp_available', '0') == "2": - self.new_hosts[host_id].status = "DOWN" - self.new_hosts[host_id].status_information = trigger[0]['hosts'][0]['snmp_error'] - self.new_hosts[host_id].duration = HumanReadableDurationFromTimestamp(trigger[0]['hosts'][0]['snmp_errors_from']) + #old api shows host interfaces status in host object + if parse_version(self.zbx_version) < parse_version("5.4.0"): + + #host not available via agent + if trigger[0]['hosts'][0].get('available', '0') == "2": + self.new_hosts[host_id].status = "DOWN" + self.new_hosts[host_id].status_information = trigger[0]['hosts'][0]['error'] + self.new_hosts[host_id].duration = HumanReadableDurationFromTimestamp(trigger[0]['hosts'][0]['errors_from']) + + #host not available via ipmi + if trigger[0]['hosts'][0].get('ipmi_available', '0') == "2": + self.new_hosts[host_id].status = "DOWN" + self.new_hosts[host_id].status_information = trigger[0]['hosts'][0]['ipmi_error'] + self.new_hosts[host_id].duration = HumanReadableDurationFromTimestamp(trigger[0]['hosts'][0]['ipmi_errors_from']) + + #host not available via jmx + if trigger[0]['hosts'][0].get('jmx_available', '0') == "2": + self.new_hosts[host_id].status = "DOWN" + self.new_hosts[host_id].status_information = trigger[0]['hosts'][0]['jmx_error'] + self.new_hosts[host_id].duration = HumanReadableDurationFromTimestamp(trigger[0]['hosts'][0]['jmx_errors_from']) + + #host not available via snmp + if trigger[0]['hosts'][0].get('snmp_available', '0') == "2": + self.new_hosts[host_id].status = "DOWN" + self.new_hosts[host_id].status_information = trigger[0]['hosts'][0]['snmp_error'] + self.new_hosts[host_id].duration = HumanReadableDurationFromTimestamp(trigger[0]['hosts'][0]['snmp_errors_from']) + + #new api shows host interfaces status in hostinterfaces object + else: + + #get all host interfaces + hostinterfaces = self.zlapi.do_request("hostinterface.get", {"hostids": host_id}) + + #check them all and mark host as DOWN on first not available interface + for hostinterface in hostinterfaces: + if hostinterface.get('available', '0') == "2": + self.new_hosts[host_id].status = "DOWN" + self.new_hosts[host_id].status_information = hostinterface['error'] + self.new_hosts[host_id].duration = HumanReadableDurationFromTimestamp(hostinterface['errors_from']) + #we stop checking rest of interfaces + break #service to report self.new_hosts[host_id].services[service_id] = GenericService() @@ -156,11 +238,10 @@ def _get_status(self): if problem['acknowledged'] == "1": self.new_hosts[host_id].services[service_id].acknowledged = True - except (ZabbixAPIException): + except ZabbixLightApiException: # set checking flag back to False self.isChecking = False result, error = self.Error(sys.exc_info()) - print(sys.exc_info()) return Result(result=result, error=error) return Result() diff --git a/build/requirements/linux.txt b/build/requirements/linux.txt index 0e4b12158..1b5572b1a 100644 --- a/build/requirements/linux.txt +++ b/build/requirements/linux.txt @@ -9,3 +9,4 @@ requests requests-kerberos requests-ecp setuptools==44.1.1 +packaging diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index 3cde90ae7..3f42d1967 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -12,3 +12,4 @@ requests-ecp # gssapi instead kerberos requests-gssapi setuptools==44.1.1 +packaging diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index ecdfd22b1..c2f011743 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -14,3 +14,4 @@ requests-kerberos requests-ecp setuptools==44.1.1 wheel +packaging From 806129bbd2907300e0f1d1669743bb9c01b8f7f8 Mon Sep 17 00:00:00 2001 From: dizixagi <dominik.zagol@ti-cahn.org> Date: Sat, 11 Sep 2021 12:31:14 +0200 Subject: [PATCH 181/884] Switch from problematic packaging to old pkg_resources module that is included in setuptools --- Nagstamon/Servers/ZabbixProblemBased.py | 2 +- build/requirements/linux.txt | 1 - build/requirements/macos.txt | 1 - build/requirements/windows.txt | 1 - 4 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Nagstamon/Servers/ZabbixProblemBased.py b/Nagstamon/Servers/ZabbixProblemBased.py index f7142e111..ad6a5a292 100644 --- a/Nagstamon/Servers/ZabbixProblemBased.py +++ b/Nagstamon/Servers/ZabbixProblemBased.py @@ -4,7 +4,7 @@ import time import logging import requests -from packaging.version import parse as parse_version +from pkg_resources import parse_version from Nagstamon.Helpers import HumanReadableDurationFromTimestamp from Nagstamon.Config import conf diff --git a/build/requirements/linux.txt b/build/requirements/linux.txt index 1b5572b1a..0e4b12158 100644 --- a/build/requirements/linux.txt +++ b/build/requirements/linux.txt @@ -9,4 +9,3 @@ requests requests-kerberos requests-ecp setuptools==44.1.1 -packaging diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index 3f42d1967..3cde90ae7 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -12,4 +12,3 @@ requests-ecp # gssapi instead kerberos requests-gssapi setuptools==44.1.1 -packaging diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index c2f011743..ecdfd22b1 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -14,4 +14,3 @@ requests-kerberos requests-ecp setuptools==44.1.1 wheel -packaging From 202252981f9ec423689f480b469588b49da95389 Mon Sep 17 00:00:00 2001 From: dizixagi <dominik.zagol@ti-cahn.org> Date: Sat, 11 Sep 2021 12:32:44 +0200 Subject: [PATCH 182/884] More certain check for login and current API version --- Nagstamon/Servers/ZabbixProblemBased.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/Nagstamon/Servers/ZabbixProblemBased.py b/Nagstamon/Servers/ZabbixProblemBased.py index ad6a5a292..02e78a89d 100644 --- a/Nagstamon/Servers/ZabbixProblemBased.py +++ b/Nagstamon/Servers/ZabbixProblemBased.py @@ -44,14 +44,17 @@ def __init__(self, server_name, monitor_url, validate_certs): self.logger.debug("ZabbixLightApi START!") self.logger.debug("monitor_url = " + self.monitor_url) - def do_request(self, method, params={}): + def do_request(self, method, params={}, no_auth=False): zabbix_rpc_obj = { "jsonrpc": "2.0", "method": method, "params": params, - "auth": self.zbx_auth, "id": self.zbx_req_id } + + if no_auth == False: + zabbix_rpc_obj["auth"] = self.zbx_auth + self.zbx_req_id += 1 self.logger.debug("ZBX: > " + str(zabbix_rpc_obj)) @@ -86,7 +89,12 @@ def logged_in(self): if self.zbx_auth is None: return False else: - return True + is_auth=self.do_request("user.checkAuthentication", {"sessionid": self.zbx_auth}, no_auth=True) + if is_auth: + return True + else: + self.zbx_auth = None + return False def login(self, username, password): self.logger.debug("Login in as " + username) @@ -99,6 +107,7 @@ class ZabbixProblemBasedServer(GenericServer): TYPE = 'ZabbixProblemBased' zlapi = None + zbx_version = "" def __init__(self, **kwds): GenericServer.__init__(self, **kwds) @@ -135,11 +144,15 @@ def _get_status(self): #Are we logged in? if self.zlapi is None: self.zlapi = ZabbixLightApi(server_name=self.name, monitor_url=self.monitor_url, validate_certs=self.validate_certs) - self.zbx_version = self.zlapi.do_request("apiinfo.version", {}) + + #zabbix could get an upgrade between checks, we need to check version each time + self.zbx_version = self.zlapi.do_request("apiinfo.version", {}, no_auth=True) + + #check are we still logged in, if not, relogin if not self.zlapi.logged_in(): self.zlapi.login(self.username, self.password) - #Get all current problems (trigger based) + #Get all current problems (trigger based), no need to check acknowledged problems if they are filtered out (load reduce) if conf.filter_acknowledged_hosts_services: problems = self.zlapi.do_request("problem.get", {'recent': False, 'acknowledged': False}) else: From aeef908d42a3d740be9ca9d8ce9f838a11050360 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 14 Sep 2021 08:45:22 +0200 Subject: [PATCH 183/884] 20210914 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 79ab6e29c..7d60bd2f3 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.7-20210908' + VERSION = '3.7-20210914' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2021 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index adf01a01a..6491930e5 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.7-20210908) unstable; urgency=low +nagstamon (3.7-20210914) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Thu, 08 Sep 2021 19:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Tue, 14 Sep 2021 9:00:00 +0100 nagstamon (3.6.0) stable; urgency=low * New upstream From 57125fe0e6cc3360e01002cf16db96588a1901a9 Mon Sep 17 00:00:00 2001 From: glesys-andreas <58209007+glesys-andreas@users.noreply.github.com> Date: Wed, 15 Sep 2021 20:17:58 +0200 Subject: [PATCH 184/884] Fix issue when status_information is null Fix for issue: https://github.com/HenriWahl/Nagstamon/issues/773 When status_information is null, an exception is thrown and no lines are displayed --- Nagstamon/Servers/Icinga.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Nagstamon/Servers/Icinga.py b/Nagstamon/Servers/Icinga.py index b8f55a01e..ffaea2e1e 100644 --- a/Nagstamon/Servers/Icinga.py +++ b/Nagstamon/Servers/Icinga.py @@ -278,7 +278,8 @@ def _get_status_JSON(self): self.new_hosts[host_name].services[service_name].last_check = s['last_check'] self.new_hosts[host_name].services[service_name].duration = s['duration'] self.new_hosts[host_name].services[service_name].attempt = s['attempts'] - self.new_hosts[host_name].services[service_name].status_information = s['status_information'].replace('\n', ' ').strip() + if s['status_information']: + self.new_hosts[host_name].services[service_name].status_information = s['status_information'].replace('\n', ' ').strip() self.new_hosts[host_name].services[service_name].passiveonly = not(s['active_checks_enabled']) self.new_hosts[host_name].services[service_name].notifications_disabled = not(s['notifications_enabled']) self.new_hosts[host_name].services[service_name].flapping = s['is_flapping'] From 730f14320ccf9f6c288ae2643ea5c3f7ea4dfb2c Mon Sep 17 00:00:00 2001 From: "Artem Ovdiienko (Vendor)" <aovdiienko@godaddy.com> Date: Fri, 17 Sep 2021 12:33:46 +0300 Subject: [PATCH 185/884] https://github.com/HenriWahl/Nagstamon/issues/776 --- Nagstamon/Servers/Sensu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/Servers/Sensu.py b/Nagstamon/Servers/Sensu.py index ecd1cb9e8..84c09063f 100644 --- a/Nagstamon/Servers/Sensu.py +++ b/Nagstamon/Servers/Sensu.py @@ -133,7 +133,7 @@ def _get_status(self): new_service.site = '' new_service.status = '' try: - new_service.status = self.SEVERITY_CODE_TEXT_MAP.get(event_check['status']) + new_service.status = self.SEVERITY_CODE_TEXT_MAP[event_check['status']] except KeyError: new_service.status = 'UNKNOWN' last_check_time = datetime.utcfromtimestamp(int(event['timestamp'])) From 19d97aab13e636315cc0135f1fe0d22983baa4ef Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 23 Sep 2021 22:59:30 +0200 Subject: [PATCH 186/884] applied suggestion from https://github.com/HenriWahl/Nagstamon/issues/707 --- Nagstamon/Config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 7d60bd2f3..6b6cf9176 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -838,7 +838,7 @@ def _DefaultActions(self): defaultactions["Checkmk Edit host in WATO"] = Action(name="Checkmk Edit host in WATO", enabled=False, monitor_type="Checkmk Multisite", description="Edit host in WATO.", - string="$MONITOR$index.py?start_url=%2Fmonitor%2Fcheck_mk%2Fwato.py%3Fhost%3D$HOST$%26mode%3Dedit_host") + string="$MONITOR$/wato.py?host=$HOST$&mode=edit_host") defaultactions["Email"] = Action(name="Email", enabled=False, description="Send email to someone.", type="browser", string="mailto:servicedesk@my.org?subject=Monitor alert: $HOST$ - $SERVICE$ - $STATUS-INFO$&body=Please help!.%0d%0aBest regards from Nagstamon") From f6ab7413c3313f759fdb308e8f462fc082bacae7 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 23 Sep 2021 23:10:29 +0200 Subject: [PATCH 187/884] removed Linux static versions for pip packages in requirements --- build/requirements/linux.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/requirements/linux.txt b/build/requirements/linux.txt index 0e4b12158..c573aec97 100644 --- a/build/requirements/linux.txt +++ b/build/requirements/linux.txt @@ -2,10 +2,10 @@ beautifulsoup4 keyring lxml psutil -pyqt5==5.15.4 +pyqt5 pysocks python-dateutil requests requests-kerberos requests-ecp -setuptools==44.1.1 +setuptools From e10bf7e50ae076c5b676dbfd694579ff9905ee45 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 23 Sep 2021 23:10:29 +0200 Subject: [PATCH 188/884] fedora-35 --- .github/workflows/build-release-latest.yml | 30 ++++++++++------------ build/requirements/linux.txt | 4 +-- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 672b0198c..a9113218b 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -89,20 +89,18 @@ jobs: retention-days: 1 if-no-files-found: error - # maybe Fedora 35 is not mature yet, but there is an out-of-memory-error when trying to build - # via GitHub Actions -# fedora-35: -# runs-on: ubuntu-latest -# needs: [ debian, fedora-32, fedora-33, fedora-34, macos, test, windows-32, windows-64 ] -# steps: -# - uses: actions/checkout@v2 -# - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . -# - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon -# - uses: actions/upload-artifact@v2 -# with: -# path: build/*.rpm -# retention-days: 1 -# if-no-files-found: error + fedora-35: + runs-on: ubuntu-latest + needs: test + steps: + - uses: actions/checkout@v2 + - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + - uses: actions/upload-artifact@v2 + with: + path: build/*.rpm + retention-days: 1 + if-no-files-found: error macos: runs-on: macos-latest @@ -163,7 +161,7 @@ jobs: repo-fedora: runs-on: ubuntu-latest - needs: [debian, fedora-32, fedora-33, fedora-34, macos, windows-32, windows-64] + needs: [debian, fedora-32, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] steps: # get binaries created by other jobs - uses: actions/download-artifact@v2 @@ -185,7 +183,7 @@ jobs: github-release: runs-on: ubuntu-latest - needs: [debian, fedora-32, fedora-33, fedora-34, macos, windows-32, windows-64] + needs: [debian, fedora-32, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] steps: - uses: actions/download-artifact@v2 - run: cd artifact && md5sum *agstamon* > md5sums.txt diff --git a/build/requirements/linux.txt b/build/requirements/linux.txt index 0e4b12158..c573aec97 100644 --- a/build/requirements/linux.txt +++ b/build/requirements/linux.txt @@ -2,10 +2,10 @@ beautifulsoup4 keyring lxml psutil -pyqt5==5.15.4 +pyqt5 pysocks python-dateutil requests requests-kerberos requests-ecp -setuptools==44.1.1 +setuptools From 1ad6b81a0a6ca41a1e0f76411952e98b72e49b4a Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Mon, 25 Oct 2021 17:34:13 +0200 Subject: [PATCH 189/884] try to fix checkmk 2.0 acknownledge and downtime bugs --- Nagstamon/Servers/Multisite.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Nagstamon/Servers/Multisite.py b/Nagstamon/Servers/Multisite.py index 2bdc35e83..25ffe100b 100644 --- a/Nagstamon/Servers/Multisite.py +++ b/Nagstamon/Servers/Multisite.py @@ -525,6 +525,10 @@ def _get_transid(self, host, service): """ get transid for an action """ - transid = self.FetchURL(self.urls['transid'].replace('$HOST$', host).replace('$SERVICE$', service.replace(' ', '+')),\ + dummy2 = self.FetchURL(self.urls['transid'].replace('$HOST$', host).replace('$SERVICE$', service.replace(' ', '+')),\ + 'obj') + dummy = self.FetchURL(self.urls['transid'].replace('$HOST$', host).replace('$SERVICE$', service.replace(' ', '+')), + 'obj').result.find(attrs={'name' : '_transid'}) + transid = self.FetchURL(self.urls['transid'].replace('$HOST$', host).replace('$SERVICE$', service.replace(' ', '+')), 'obj').result.find(attrs={'name' : '_transid'})['value'] return transid From f26d5bc559b88b41be427974cd1220e630a37ba1 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 25 Oct 2021 21:54:55 +0200 Subject: [PATCH 190/884] macos-10.15 --- .github/workflows/build-release-latest.yml | 2 +- .github/workflows/build-release-stable.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index a9113218b..98c36d9b9 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -103,7 +103,7 @@ jobs: if-no-files-found: error macos: - runs-on: macos-latest + runs-on: macos-10.15 needs: test steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index 81ce9064e..b95915731 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -81,7 +81,7 @@ jobs: retention-days: 1 macos: - runs-on: macos-latest + runs-on: macos-10.15 steps: - uses: actions/checkout@v2 - run: pip3 install --no-warn-script-location -r build/requirements/macos.txt From 538e9040e0d2122eb94145b55088fcdc98a7a475 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 25 Oct 2021 23:00:58 +0200 Subject: [PATCH 191/884] add service in get_trans_id if empty --- .github/workflows/build-release-stable.yml | 15 +++++++++++++-- Nagstamon/Config.py | 2 +- Nagstamon/Servers/Multisite.py | 13 ++++++------- build/debian/changelog | 4 ++-- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index 81ce9064e..04911c678 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -80,6 +80,17 @@ jobs: path: build/*.rpm retention-days: 1 + fedora-35: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + - uses: actions/upload-artifact@v2 + with: + path: build/*.rpm + retention-days: 1 + macos: runs-on: macos-latest steps: @@ -133,7 +144,7 @@ jobs: repo-fedora: runs-on: ubuntu-latest - needs: [fedora-32, fedora-33, fedora-34] + needs: [debian, fedora-32, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] steps: # get binaries created by other jobs - uses: actions/download-artifact@v2 @@ -152,7 +163,7 @@ jobs: upload-release: runs-on: ubuntu-latest - needs: [debian, fedora-32, fedora-33, fedora-34, macos, windows-32, windows-64] + needs: [debian, fedora-32, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] steps: - uses: actions/download-artifact@v2 - run: cd artifact && md5sum *agstamon* > md5sums.txt diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 6b6cf9176..67a0821b1 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.7-20210914' + VERSION = '3.7-20211025' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2021 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/Servers/Multisite.py b/Nagstamon/Servers/Multisite.py index 25ffe100b..317f6a0f2 100644 --- a/Nagstamon/Servers/Multisite.py +++ b/Nagstamon/Servers/Multisite.py @@ -95,9 +95,9 @@ def init_HTTP(self): urllib.parse.urlencode({'start_url': 'view.py?view_name=hoststatus'}), # URLs do not need pythonic output because since werk #0766 API does not work with transid=-1 anymore # thus access to normal webinterface is used - 'api_host_act': self.monitor_url + '/view.py?_transid=-1&_do_actions=yes&_do_confirm=Yes!&view_name=hoststatus&filled_in=actions&lang=', - 'api_service_act': self.monitor_url + '/view.py?_transid=-1&_do_actions=yes&_do_confirm=Yes!&view_name=service&filled_in=actions&lang=', - 'api_svcprob_act': self.monitor_url + '/view.py?_transid=-1&_do_actions=yes&_do_confirm=Yes!&view_name=svcproblems&filled_in=actions&lang=', + 'api_host_act': self.monitor_url + '/view.py?_transid=-1&_do_actions=yes&_do_confirm=Confirm&view_name=hoststatus&filled_in=actions&lang=', + 'api_service_act': self.monitor_url + '/view.py?_transid=-1&_do_actions=yes&_do_confirm=Confirm&view_name=service&filled_in=actions&lang=', + 'api_svcprob_act': self.monitor_url + '/view.py?_transid=-1&_do_actions=yes&_do_confirm=Confirm&view_name=svcproblems&filled_in=actions&lang=', 'human_events': self.monitor_url + '/index.py?%s' % urllib.parse.urlencode({'start_url': 'view.py?view_name=events'}), 'transid': self.monitor_url + '/view.py?actions=yes&filled_in=actions&host=$HOST$&service=$SERVICE$&view_name=service' @@ -525,10 +525,9 @@ def _get_transid(self, host, service): """ get transid for an action """ - dummy2 = self.FetchURL(self.urls['transid'].replace('$HOST$', host).replace('$SERVICE$', service.replace(' ', '+')),\ - 'obj') - dummy = self.FetchURL(self.urls['transid'].replace('$HOST$', host).replace('$SERVICE$', service.replace(' ', '+')), - 'obj').result.find(attrs={'name' : '_transid'}) + # since Checkmk 2.0 it seems to be a problem if service is empty so fill it with a definitively existing one + if not service: + service = 'PING' transid = self.FetchURL(self.urls['transid'].replace('$HOST$', host).replace('$SERVICE$', service.replace(' ', '+')), 'obj').result.find(attrs={'name' : '_transid'})['value'] return transid diff --git a/build/debian/changelog b/build/debian/changelog index 6491930e5..f54effb0e 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.7-20210914) unstable; urgency=low +nagstamon (3.7-20211025) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Tue, 14 Sep 2021 9:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Mon, 25 Oct 2021 19:00:00 +0100 nagstamon (3.6.0) stable; urgency=low * New upstream From b0987e08505138de25fbd74d505840706b4357cb Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 25 Oct 2021 23:03:01 +0200 Subject: [PATCH 192/884] build without fedora-35 --- .github/workflows/build-release-latest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index a9113218b..d506cbcb5 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -161,7 +161,7 @@ jobs: repo-fedora: runs-on: ubuntu-latest - needs: [debian, fedora-32, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] + needs: [debian, fedora-32, fedora-33, fedora-34, macos, windows-32, windows-64] steps: # get binaries created by other jobs - uses: actions/download-artifact@v2 @@ -183,7 +183,7 @@ jobs: github-release: runs-on: ubuntu-latest - needs: [debian, fedora-32, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] + needs: [debian, fedora-32, fedora-33, fedora-34, macos, windows-32, windows-64] steps: - uses: actions/download-artifact@v2 - run: cd artifact && md5sum *agstamon* > md5sums.txt From 227db87a8f1f3a30a9444eeab09ea860220de18d Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 25 Oct 2021 23:06:40 +0200 Subject: [PATCH 193/884] Yes! --- Nagstamon/Servers/Multisite.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Servers/Multisite.py b/Nagstamon/Servers/Multisite.py index 317f6a0f2..eadd548e5 100644 --- a/Nagstamon/Servers/Multisite.py +++ b/Nagstamon/Servers/Multisite.py @@ -95,9 +95,9 @@ def init_HTTP(self): urllib.parse.urlencode({'start_url': 'view.py?view_name=hoststatus'}), # URLs do not need pythonic output because since werk #0766 API does not work with transid=-1 anymore # thus access to normal webinterface is used - 'api_host_act': self.monitor_url + '/view.py?_transid=-1&_do_actions=yes&_do_confirm=Confirm&view_name=hoststatus&filled_in=actions&lang=', - 'api_service_act': self.monitor_url + '/view.py?_transid=-1&_do_actions=yes&_do_confirm=Confirm&view_name=service&filled_in=actions&lang=', - 'api_svcprob_act': self.monitor_url + '/view.py?_transid=-1&_do_actions=yes&_do_confirm=Confirm&view_name=svcproblems&filled_in=actions&lang=', + 'api_host_act': self.monitor_url + '/view.py?_transid=-1&_do_actions=yes&_do_confirm=Yes!&view_name=hoststatus&filled_in=actions&lang=', + 'api_service_act': self.monitor_url + '/view.py?_transid=-1&_do_actions=yes&_do_confirm=Yes!&view_name=service&filled_in=actions&lang=', + 'api_svcprob_act': self.monitor_url + '/view.py?_transid=-1&_do_actions=yes&_do_confirm=Yes!&view_name=svcproblems&filled_in=actions&lang=', 'human_events': self.monitor_url + '/index.py?%s' % urllib.parse.urlencode({'start_url': 'view.py?view_name=events'}), 'transid': self.monitor_url + '/view.py?actions=yes&filled_in=actions&host=$HOST$&service=$SERVICE$&view_name=service' From a3e88d9f5d018928017d223975a2f7805eaaddbb Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 25 Oct 2021 23:58:08 +0200 Subject: [PATCH 194/884] fixed downtime problems in checkmk --- Nagstamon/Servers/Multisite.py | 50 ++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/Nagstamon/Servers/Multisite.py b/Nagstamon/Servers/Multisite.py index eadd548e5..e94ce988b 100644 --- a/Nagstamon/Servers/Multisite.py +++ b/Nagstamon/Servers/Multisite.py @@ -471,17 +471,45 @@ def _action(self, site, host, service, specific_params): def _set_downtime(self, host, service, author, comment, fixed, start_time, end_time, hours, minutes): - self._action(self.hosts[host].site, host, service, { - '_down_comment': author == self.username and comment or '%s: %s' % (author, comment), - '_down_flexible': fixed == 0 and 'on' or '', - '_down_custom': 'Custom+time+range', - '_down_from_date': start_time.split(' ')[0], - '_down_from_time': start_time.split(' ')[1], - '_down_to_date': end_time.split(' ')[0], - '_down_to_time': end_time.split(' ')[1], - '_down_duration': '%s:%s' % (hours, minutes), - 'actions': 'yes' - }) + # Checkmk needs downtime info in more explizit format + from_date = from_time = to_date = to_time = '' + try: + # might be more sophisticated, especially if there is a localized Checkmk web interface + from_date, from_time = start_time.split(' ') + from_year, from_month, from_day = from_date.split('-') + from_hour, from_min = from_time.split(':') + to_date, to_time = end_time.split(' ') + to_year, to_month, to_day = to_date.split('-') + to_hour, to_min = to_time.split(':') + + # let's try to push downtime info in all variants to server - somewhat holzhammery but well... + self._action(self.hosts[host].site, host, service, { + '_down_comment': author == self.username and comment or '%s: %s' % (author, comment), + '_down_flexible': fixed == 0 and 'on' or '', + '_down_custom': 'Custom+time+range', + '_down_from_date': from_date, + '_down_from_time': from_time, + '_down_to_date': to_date, + '_down_to_time': to_time, + '_down_duration': '%s:%s' % (hours, minutes), + '_down_from_year': from_year, + '_down_from_month': from_month, + '_down_from_day': from_day, + '_down_from_hour': from_hour, + '_down_from_min': from_min, + '_down_from_sec': '00', + '_down_to_year': to_year, + '_down_to_month': to_month, + '_down_to_day': to_day, + '_down_to_hour': to_hour, + '_down_to_min': to_min, + '_down_to_sec': '00', + 'actions': 'yes' + }) + except: + if conf.debug_mode: + self.Debug(server=self.get_name(), host=host, + debug='Invalid start/end date/time given') def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=[]): From e43dcc7f576132a1925e592a0c673bd7dcc90b27 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 26 Oct 2021 08:27:57 +0200 Subject: [PATCH 195/884] fix downtime for service --- Nagstamon/Servers/Multisite.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Nagstamon/Servers/Multisite.py b/Nagstamon/Servers/Multisite.py index e94ce988b..55b7c5015 100644 --- a/Nagstamon/Servers/Multisite.py +++ b/Nagstamon/Servers/Multisite.py @@ -471,8 +471,6 @@ def _action(self, site, host, service, specific_params): def _set_downtime(self, host, service, author, comment, fixed, start_time, end_time, hours, minutes): - # Checkmk needs downtime info in more explizit format - from_date = from_time = to_date = to_time = '' try: # might be more sophisticated, especially if there is a localized Checkmk web interface from_date, from_time = start_time.split(' ') @@ -483,7 +481,7 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t to_hour, to_min = to_time.split(':') # let's try to push downtime info in all variants to server - somewhat holzhammery but well... - self._action(self.hosts[host].site, host, service, { + params = { '_down_comment': author == self.username and comment or '%s: %s' % (author, comment), '_down_flexible': fixed == 0 and 'on' or '', '_down_custom': 'Custom+time+range', @@ -505,7 +503,11 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t '_down_to_min': to_min, '_down_to_sec': '00', 'actions': 'yes' - }) + } + # service needs extra parameter + if service: + params['_do_confirm_service_downtime'] = 'Schedule+downtime+for+1+service' + self._action(self.hosts[host].site, host, service, params) except: if conf.debug_mode: self.Debug(server=self.get_name(), host=host, From f23f8b45e9b58cf5f66afeb91804f7d112df633b Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Fri, 5 Nov 2021 00:08:53 +0100 Subject: [PATCH 196/884] fedroa-35 --- .github/workflows/build-release-latest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index c8170768a..98c36d9b9 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -161,7 +161,7 @@ jobs: repo-fedora: runs-on: ubuntu-latest - needs: [debian, fedora-32, fedora-33, fedora-34, macos, windows-32, windows-64] + needs: [debian, fedora-32, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] steps: # get binaries created by other jobs - uses: actions/download-artifact@v2 @@ -183,7 +183,7 @@ jobs: github-release: runs-on: ubuntu-latest - needs: [debian, fedora-32, fedora-33, fedora-34, macos, windows-32, windows-64] + needs: [debian, fedora-32, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] steps: - uses: actions/download-artifact@v2 - run: cd artifact && md5sum *agstamon* > md5sums.txt From f31da2b4d2b0a36d06d35c020f83db607d8481f5 Mon Sep 17 00:00:00 2001 From: Alan <alan.garcia@k-net.pro> Date: Thu, 11 Nov 2021 16:24:19 +0100 Subject: [PATCH 197/884] Allow to use display_name on services with Thruk --- Nagstamon/QUI/__init__.py | 2 +- Nagstamon/Servers/Thruk.py | 34 +++++++++++++++++++++++++++++++--- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 978176913..7b76d0627 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -5754,7 +5754,7 @@ def __init__(self, dialog): self.ui.label_autologin_key: ['Centreon', 'monitos4x', 'Thruk'], self.ui.input_checkbox_no_cookie_auth: ['IcingaWeb2', 'Sensu'], self.ui.input_checkbox_use_display_name_host: ['Icinga', 'IcingaWeb2'], - self.ui.input_checkbox_use_display_name_service: ['Icinga', 'IcingaWeb2'], + self.ui.input_checkbox_use_display_name_service: ['Icinga', 'IcingaWeb2', 'Thruk'], self.ui.input_checkbox_use_description_name_service: ['Zabbix'], self.ui.input_checkbox_force_authuser: ['Checkmk Multisite'], self.ui.groupbox_checkmk_views: ['Checkmk Multisite'], diff --git a/Nagstamon/Servers/Thruk.py b/Nagstamon/Servers/Thruk.py index cb2b4a80b..5a1dbe39d 100644 --- a/Nagstamon/Servers/Thruk.py +++ b/Nagstamon/Servers/Thruk.py @@ -24,8 +24,11 @@ import json import datetime import copy +import base64 +import urllib.parse from Nagstamon.Helpers import HumanReadableDurationFromTimestamp +from Nagstamon.Helpers import webbrowser_open from Nagstamon.Objects import (GenericHost, GenericService, Result) @@ -94,7 +97,7 @@ def init_config(self): "last_state_change,plugin_output,current_attempt,"\ "max_check_attempts,active_checks_enabled,is_flapping,"\ "notifications_enabled,acknowledged,state_type,"\ - "scheduled_downtime_depth" + "scheduled_downtime_depth,host_display_name,display_name" # hosts (up or down or unreachable) self.cgiurl_hosts = self.monitor_cgi_url + "/status.cgi?hostgroup=all&style=hostdetail&"\ "dfl_s0_hoststatustypes=12&dfl_s1_hostprops=1&dfl_s2_hostprops=4&dfl_s3_hostprops=524288&&dfl_s4_hostprops=4096&dfl_s5_hostprop=16&"\ @@ -102,7 +105,7 @@ def init_config(self): "columns=name,state,last_check,last_state_change,"\ "plugin_output,current_attempt,max_check_attempts,"\ "active_checks_enabled,notifications_enabled,is_flapping,"\ - "acknowledged,scheduled_downtime_depth,state_type" + "acknowledged,scheduled_downtime_depth,state_type,host_display_name,display_name" def login(self): """ @@ -133,6 +136,21 @@ def login(self): return Result(result=None, error="Login failed") + def open_monitor(self, host, service=''): + ''' + open monitor from tablewidget context menu + ''' + # only type is important so do not care of service '' in case of host monitor + if service == '': + url = self.monitor_cgi_url + '/extinfo.cgi?type=1&' + urllib.parse.urlencode( { 'host': host }) + else: + url = self.monitor_cgi_url + '/extinfo.cgi?type=2&' + urllib.parse.urlencode( { 'host': host, 'service': self.hosts[host].services[ base64.b64encode(service.encode()).decode() ].real_name }) + + if conf.debug_mode: + self.Debug(server=self.get_name(), host=host, service=service, + debug='Open host/service monitor web page {0}'.format(url)) + webbrowser_open(url) + def _get_status(self): """ Get status from Thruk Server @@ -226,7 +244,17 @@ def _get_status(self): # ##new_service = s["description"] self.new_hosts[s["host_name"]].services[s["description"]] = GenericService() self.new_hosts[s["host_name"]].services[s["description"]].host = s["host_name"] - self.new_hosts[s["host_name"]].services[s["description"]].name = s["description"] + + # If we want to use display_name for services + if self.use_display_name_service == False: + self.new_hosts[s["host_name"]].services[s["description"]].name = s["description"] + else: + self.new_hosts[s["host_name"]].services[s["description"]].name = s["display_name"] + # If we use display_name, we have to be able to get back the service real name with the display_name, so I add a entry + # But the display_name can be with any char, some are not allowed in Python, so I encode it + self.new_hosts[s["host_name"]].services[ base64.b64encode( self.new_hosts[s["host_name"]].services[s["description"]].name.encode()).decode() ] = GenericService() + self.new_hosts[s["host_name"]].services[ base64.b64encode( self.new_hosts[s["host_name"]].services[s["description"]].name.encode()).decode() ].real_name = s["description"] + self.new_hosts[s["host_name"]].services[s["description"]].server = self.name self.new_hosts[s["host_name"]].services[s["description"]].status = self.STATES_MAPPING["services"][s["state"]] self.new_hosts[s["host_name"]].services[s["description"]].last_check = datetime.datetime.fromtimestamp(int(s["last_check"])).isoformat(" ") From 17b918878d819df78ff13e2b58fe86a908e2951c Mon Sep 17 00:00:00 2001 From: Alan <alan.garcia@k-net.pro> Date: Fri, 12 Nov 2021 21:25:59 +0100 Subject: [PATCH 198/884] Fix ACK --- Nagstamon/Servers/Thruk.py | 48 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/Nagstamon/Servers/Thruk.py b/Nagstamon/Servers/Thruk.py index 5a1dbe39d..5fb17f707 100644 --- a/Nagstamon/Servers/Thruk.py +++ b/Nagstamon/Servers/Thruk.py @@ -18,6 +18,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +from collections import OrderedDict from Nagstamon.Servers.Generic import GenericServer from Nagstamon.Config import conf import sys @@ -151,6 +152,53 @@ def open_monitor(self, host, service=''): debug='Open host/service monitor web page {0}'.format(url)) webbrowser_open(url) + def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=[]): + ''' + send acknowledge to monitor server - might be different on every monitor type + ''' + + url = self.monitor_cgi_url + '/cmd.cgi' + + # the following flags apply to hosts and services + # + # according to sf.net bug #3304098 (https://sourceforge.net/tracker/?func=detail&atid=1101370&aid=3304098&group_id=236865) + # the send_notification-flag must not exist if it is set to 'off', otherwise + # the Nagios core interpretes it as set, regardless its real value + # + # for whatever silly reason Icinga depends on the correct order of submitted form items... + # see sf.net bug 3428844 + # + # Thanks to Icinga ORDER OF ARGUMENTS IS IMPORTANT HERE! + # + cgi_data = OrderedDict() + if service == '': + cgi_data['cmd_typ'] = '33' + else: + cgi_data['cmd_typ'] = '34' + cgi_data['cmd_mod'] = '2' + cgi_data['host'] = host + if service != '': + cgi_data['service'] = self.hosts[host].services[ base64.b64encode(service.encode()).decode() ].real_name + cgi_data['com_author'] = author + cgi_data['com_data'] = comment + cgi_data['btnSubmit'] = 'Commit' + if notify is True: + cgi_data['send_notification'] = 'on' + if persistent is True: + cgi_data['persistent'] = 'on' + if sticky is True: + cgi_data['sticky_ack'] = 'on' + + self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + + # acknowledge all services on a host + if len(all_services) > 0: + for s in all_services: + cgi_data['cmd_typ'] = '34' + cgi_data['service'] = self.hosts[host].services[ base64.b64encode(s.encode()).decode() ].real_name + self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + + def _get_status(self): """ Get status from Thruk Server From a6ace72e16d551fc079d66977340b9f43875155f Mon Sep 17 00:00:00 2001 From: Alan <fufroma@gmail.com> Date: Sat, 13 Nov 2021 10:08:49 +0100 Subject: [PATCH 199/884] Fix recheck and clipboard --- Nagstamon/Servers/Thruk.py | 89 +++++++++++++++++++++++++------------- 1 file changed, 58 insertions(+), 31 deletions(-) diff --git a/Nagstamon/Servers/Thruk.py b/Nagstamon/Servers/Thruk.py index 5fb17f707..730266589 100644 --- a/Nagstamon/Servers/Thruk.py +++ b/Nagstamon/Servers/Thruk.py @@ -25,7 +25,6 @@ import json import datetime import copy -import base64 import urllib.parse from Nagstamon.Helpers import HumanReadableDurationFromTimestamp @@ -145,7 +144,7 @@ def open_monitor(self, host, service=''): if service == '': url = self.monitor_cgi_url + '/extinfo.cgi?type=1&' + urllib.parse.urlencode( { 'host': host }) else: - url = self.monitor_cgi_url + '/extinfo.cgi?type=2&' + urllib.parse.urlencode( { 'host': host, 'service': self.hosts[host].services[ base64.b64encode(service.encode()).decode() ].real_name }) + url = self.monitor_cgi_url + '/extinfo.cgi?type=2&' + urllib.parse.urlencode( { 'host': host, 'service': self.hosts[host].services[ service ].real_name }) if conf.debug_mode: self.Debug(server=self.get_name(), host=host, service=service, @@ -178,7 +177,7 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi cgi_data['cmd_mod'] = '2' cgi_data['host'] = host if service != '': - cgi_data['service'] = self.hosts[host].services[ base64.b64encode(service.encode()).decode() ].real_name + cgi_data['service'] = self.hosts[host].services[ service ].real_name cgi_data['com_author'] = author cgi_data['com_data'] = comment cgi_data['btnSubmit'] = 'Commit' @@ -195,9 +194,40 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi if len(all_services) > 0: for s in all_services: cgi_data['cmd_typ'] = '34' - cgi_data['service'] = self.hosts[host].services[ base64.b64encode(s.encode()).decode() ].real_name + cgi_data['service'] = self.hosts[host].services[ s ].real_name self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + def _set_recheck(self, host, service): + if service != '': + if self.hosts[host].services[ service ].is_passive_only(): + # Do not check passive only checks + return + try: + # get start time from Nagios as HTML to use same timezone setting like the locally installed Nagios + result = self.FetchURL( + self.monitor_cgi_url + '/cmd.cgi?' + urllib.parse.urlencode({'cmd_typ': '96', 'host': host})) + self.start_time = dict(result.result.find(attrs={'name': 'start_time'}).attrs)['value'] + # decision about host or service - they have different URLs + if service == '': + # host + cmd_typ = '96' + service_name = '' + else: + # service @ host + cmd_typ = '7' + service_name = self.hosts[host].services[ service ].real_name + # ignore empty service in case of rechecking a host + cgi_data = urllib.parse.urlencode([('cmd_typ', cmd_typ), + ('cmd_mod', '2'), + ('host', host), + ('service', service_name), + ('start_time', self.start_time), + ('force_check', 'on'), + ('btnSubmit', 'Commit')]) + # execute POST request + self.FetchURL(self.monitor_cgi_url + '/cmd.cgi', giveback='raw', cgi_data=cgi_data) + except: + traceback.print_exc(file=sys.stdout) def _get_status(self): """ @@ -287,34 +317,31 @@ def _get_status(self): self.new_hosts[s["host_name"]].server = self.name self.new_hosts[s["host_name"]].status = "UP" + if self.use_display_name_service == True: + entry = s["display_name"] + else: + entry = s["description"] + # if a service does not exist create its object - if s["description"] not in self.new_hosts[s["host_name"]].services: - # ##new_service = s["description"] - self.new_hosts[s["host_name"]].services[s["description"]] = GenericService() - self.new_hosts[s["host_name"]].services[s["description"]].host = s["host_name"] - - # If we want to use display_name for services - if self.use_display_name_service == False: - self.new_hosts[s["host_name"]].services[s["description"]].name = s["description"] - else: - self.new_hosts[s["host_name"]].services[s["description"]].name = s["display_name"] - # If we use display_name, we have to be able to get back the service real name with the display_name, so I add a entry - # But the display_name can be with any char, some are not allowed in Python, so I encode it - self.new_hosts[s["host_name"]].services[ base64.b64encode( self.new_hosts[s["host_name"]].services[s["description"]].name.encode()).decode() ] = GenericService() - self.new_hosts[s["host_name"]].services[ base64.b64encode( self.new_hosts[s["host_name"]].services[s["description"]].name.encode()).decode() ].real_name = s["description"] - - self.new_hosts[s["host_name"]].services[s["description"]].server = self.name - self.new_hosts[s["host_name"]].services[s["description"]].status = self.STATES_MAPPING["services"][s["state"]] - self.new_hosts[s["host_name"]].services[s["description"]].last_check = datetime.datetime.fromtimestamp(int(s["last_check"])).isoformat(" ") - self.new_hosts[s["host_name"]].services[s["description"]].duration = HumanReadableDurationFromTimestamp(s["last_state_change"]) - self.new_hosts[s["host_name"]].services[s["description"]].attempt = "%s/%s" % (s["current_attempt"], s["max_check_attempts"]) - self.new_hosts[s["host_name"]].services[s["description"]].status_information = s["plugin_output"].replace("\n", " ").strip() - self.new_hosts[s["host_name"]].services[s["description"]].passiveonly = not(bool(int(s["active_checks_enabled"]))) - self.new_hosts[s["host_name"]].services[s["description"]].notifications_disabled = not(bool(int(s["notifications_enabled"]))) - self.new_hosts[s["host_name"]].services[s["description"]].flapping = not(bool(int(s["notifications_enabled"]))) - self.new_hosts[s["host_name"]].services[s["description"]].acknowledged = bool(int(s["acknowledged"])) - self.new_hosts[s["host_name"]].services[s["description"]].scheduled_downtime = bool(int(s["scheduled_downtime_depth"])) - self.new_hosts[s["host_name"]].services[s["description"]].status_type = {0: "soft", 1: "hard"}[s["state_type"]] + if entry not in self.new_hosts[s["host_name"]].services: + self.new_hosts[s["host_name"]].services[ entry ] = GenericService() + self.new_hosts[s["host_name"]].services[ entry ].host = s["host_name"] + + self.new_hosts[s["host_name"]].services[ entry ].name = entry + self.new_hosts[s["host_name"]].services[ entry ].real_name = s["description"] + + self.new_hosts[s["host_name"]].services[ entry ].server = self.name + self.new_hosts[s["host_name"]].services[ entry ].status = self.STATES_MAPPING["services"][s["state"]] + self.new_hosts[s["host_name"]].services[ entry ].last_check = datetime.datetime.fromtimestamp(int(s["last_check"])).isoformat(" ") + self.new_hosts[s["host_name"]].services[ entry ].duration = HumanReadableDurationFromTimestamp(s["last_state_change"]) + self.new_hosts[s["host_name"]].services[ entry ].attempt = "%s/%s" % (s["current_attempt"], s["max_check_attempts"]) + self.new_hosts[s["host_name"]].services[ entry ].status_information = s["plugin_output"].replace("\n", " ").strip() + self.new_hosts[s["host_name"]].services[ entry ].passiveonly = not(bool(int(s["active_checks_enabled"]))) + self.new_hosts[s["host_name"]].services[ entry ].notifications_disabled = not(bool(int(s["notifications_enabled"]))) + self.new_hosts[s["host_name"]].services[ entry ].flapping = not(bool(int(s["notifications_enabled"]))) + self.new_hosts[s["host_name"]].services[ entry ] .acknowledged = bool(int(s["acknowledged"])) + self.new_hosts[s["host_name"]].services[ entry ].scheduled_downtime = bool(int(s["scheduled_downtime_depth"])) + self.new_hosts[s["host_name"]].services[ entry ].status_type = {0: "soft", 1: "hard"}[s["state_type"]] del s except: import traceback From addcf7165aa0859c5faa54474062b2bbc4675129 Mon Sep 17 00:00:00 2001 From: Alan <fufroma@gmail.com> Date: Sat, 13 Nov 2021 11:01:53 +0100 Subject: [PATCH 200/884] Fix downtime --- Nagstamon/Servers/Thruk.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/Nagstamon/Servers/Thruk.py b/Nagstamon/Servers/Thruk.py index 730266589..d7cc6b157 100644 --- a/Nagstamon/Servers/Thruk.py +++ b/Nagstamon/Servers/Thruk.py @@ -229,6 +229,36 @@ def _set_recheck(self, host, service): except: traceback.print_exc(file=sys.stdout) + def _set_downtime(self, host, service, author, comment, fixed, start_time, end_time, hours, minutes): + ''' + finally send downtime command to monitor server + ''' + url = self.monitor_cgi_url + '/cmd.cgi' + + # for some reason Icinga is very fastidiuos about the order of CGI arguments, so please + # here we go... it took DAYS :-( + cgi_data = OrderedDict() + if service == '': + cgi_data['cmd_typ'] = '55' + else: + cgi_data['cmd_typ'] = '56' + cgi_data['cmd_mod'] = '2' + cgi_data['trigger'] = '0' + cgi_data['host'] = host + if service != '': + cgi_data['service'] = self.hosts[host].services[ service ].real_name + cgi_data['com_author'] = author + cgi_data['com_data'] = comment + cgi_data['fixed'] = fixed + cgi_data['start_time'] = start_time + cgi_data['end_time'] = end_time + cgi_data['hours'] = hours + cgi_data['minutes'] = minutes + cgi_data['btnSubmit'] = 'Commit' + + # running remote cgi command + self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + def _get_status(self): """ Get status from Thruk Server From 2c1ce797f91f5f2edcc5d298955216460e9a3024 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 15 Nov 2021 22:06:57 +0100 Subject: [PATCH 201/884] 20211115 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 67a0821b1..08e67f397 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.7-20211025' + VERSION = '3.7-20211115' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2021 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index f54effb0e..560a30466 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.7-20211025) unstable; urgency=low +nagstamon (3.7-20211115) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Mon, 25 Oct 2021 19:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Mon, 15 Nov 2021 19:00:00 +0100 nagstamon (3.6.0) stable; urgency=low * New upstream From 4e7c178fc941ae1c479ae1206915ccc9acc785a4 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 15 Nov 2021 22:30:18 +0100 Subject: [PATCH 202/884] prepare 3.8 --- ChangeLog | 15 ++++++++++----- Nagstamon/Config.py | 2 +- build/debian/changelog | 9 ++++++++- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/ChangeLog b/ChangeLog index bf2f67401..04c81dd74 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,9 +1,14 @@ -nagstamon (3.7-20210825) unstable; urgency=low +nagstamon (3.8.0) unstable; urgency=low * New upstream - - added test section to GitHub actions workflows to enable testing - - added basic unit tests and linting to Alertmanager - - -- Henri Wahl <henri@nagstamon.de> Tue, 07 Apr 2021 19:00:00 +0100 + - added alertmanager acknownledgment + - added ECP authentication + - added Zabbix 5.4+ support + - Checkmk 2.0 fixes + - Thruk fixes + - Zabbix fixes + - dependency updates + + -- Henri Wahl <henri@nagstamon.de> Mon, 15 Nov 2021 19:00:00 +0100 nagstamon (3.6.0) unstable; urgency=low * New upstream diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 08e67f397..48d65c582 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.7-20211115' + VERSION = '3.8.0' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2021 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 560a30466..0b5209c06 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,5 +1,12 @@ -nagstamon (3.7-20211115) unstable; urgency=low +nagstamon (3.8.0) unstable; urgency=low * New upstream + - added alertmanager acknownledgment + - added ECP authentication + - added Zabbix 5.4+ support + - Checkmk 2.0 fixes + - Thruk fixes + - Zabbix fixes + - dependency updates -- Henri Wahl <henri@nagstamon.de> Mon, 15 Nov 2021 19:00:00 +0100 From 7e97a63b134a8ede2b580012a1174e91186ce6a9 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 15 Nov 2021 22:49:20 +0100 Subject: [PATCH 203/884] fixed tests for stable 3.8 --- .github/workflows/build-release-stable.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index a1b3fa630..cd2cd848c 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -25,7 +25,7 @@ jobs: sudo apt-get install libkrb5-dev python -m pip install --upgrade pip pip install pytest pylint #flake8 - if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + if [ -f build/requirements/linux.txt ]; then pip install -r build/requirements/linux.txt; fi # - name: Lint with flake8 # run: | # # stop the build if there are Python syntax errors or undefined names From 20c03b77e9309e82ddcf63c4eb213f806bdd3434 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 15 Nov 2021 22:57:00 +0100 Subject: [PATCH 204/884] need test for stable --- .github/workflows/build-release-stable.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index cd2cd848c..842d5318e 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -38,6 +38,7 @@ jobs: debian: runs-on: ubuntu-latest + needs: test steps: - uses: actions/checkout@v2 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . @@ -49,6 +50,7 @@ jobs: fedora-32: runs-on: ubuntu-latest + needs: test steps: - uses: actions/checkout@v2 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . @@ -60,6 +62,7 @@ jobs: fedora-33: runs-on: ubuntu-latest + needs: test steps: - uses: actions/checkout@v2 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . @@ -71,6 +74,7 @@ jobs: fedora-34: runs-on: ubuntu-latest + needs: test steps: - uses: actions/checkout@v2 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . @@ -82,6 +86,7 @@ jobs: fedora-35: runs-on: ubuntu-latest + needs: test steps: - uses: actions/checkout@v2 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . @@ -93,6 +98,7 @@ jobs: macos: runs-on: macos-10.15 + needs: test steps: - uses: actions/checkout@v2 - run: pip3 install --no-warn-script-location -r build/requirements/macos.txt @@ -106,6 +112,7 @@ jobs: windows-32: runs-on: windows-latest + needs: test steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 @@ -125,6 +132,7 @@ jobs: windows-64: runs-on: windows-latest + needs: test steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 From c93f677a0830c17313e3a64b9a8c24268015ee54 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Tue, 23 Nov 2021 08:30:13 +0100 Subject: [PATCH 205/884] removed nagstamon.ifw-dresden.de as source of new version info --- .github/workflows/build-release-latest.yml | 2 +- Nagstamon/Config.py | 5 ++--- build/debian/changelog | 7 ++++++- build/requirements/windows.txt | 2 +- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 98c36d9b9..9c4aecfea 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -5,7 +5,7 @@ on: branches: '**' env: - python_win_version: 3.9.4 + python_win_version: 3.9.9 repo_dir: nagstamon-jekyll/docs/repo jobs: diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 48d65c582..d27500610 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,13 +125,12 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.8.0' + VERSION = '3.9-20211116' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2021 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' # dict of servers to offer for downloads if an update is available - DOWNLOAD_SERVERS = {'nagstamon.de': 'https://github.com/HenriWahl/Nagstamon/releases', - 'nagstamon.ifw-dresden.de': 'https://nagstamon.ifw-dresden.de/download'} + DOWNLOAD_SERVERS = {'nagstamon.de': 'https://github.com/HenriWahl/Nagstamon/releases'} # version URL depends on version string if 'alpha' in VERSION.lower() or \ 'beta' in VERSION.lower() or \ diff --git a/build/debian/changelog b/build/debian/changelog index 0b5209c06..d14b380d2 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,9 @@ -nagstamon (3.8.0) unstable; urgency=low +nagstamon (3.9-20211116) unstable; urgency=low + * New upstream + + -- Henri Wahl <henri@nagstamon.de> Tue, 16 Nov 2021 19:00:00 +0100 + +nagstamon (3.8.0) stable; urgency=low * New upstream - added alertmanager acknownledgment - added ECP authentication diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index ecdfd22b1..524068d81 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -4,7 +4,7 @@ icinga2api keyring lxml psutil -pyinstaller +pyinstaller==4.7 pypiwin32 pyqt5==5.15.4 pysocks From 542c8362ea88918d510c9df3b3e7cc9747f795ba Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Wed, 8 Dec 2021 07:21:18 +0100 Subject: [PATCH 206/884] try to fix IcingaWeb2.py cookie problem #790 --- Nagstamon/Config.py | 2 +- Nagstamon/Servers/IcingaWeb2.py | 4 ++-- build/debian/changelog | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index d27500610..6353390a8 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20211116' + VERSION = '3.9-20211208' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2021 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/Servers/IcingaWeb2.py b/Nagstamon/Servers/IcingaWeb2.py index 5fa10512f..7247cd610 100644 --- a/Nagstamon/Servers/IcingaWeb2.py +++ b/Nagstamon/Servers/IcingaWeb2.py @@ -95,9 +95,9 @@ def init_HTTP(self): if self.session and not 'Referer' in self.session.headers: self.session.headers['Referer'] = self.monitor_cgi_url + '/icingaweb2/monitoring' - # normally cookie out will be used + # normally cookie auth will be used if not self.no_cookie_auth: - if len(self.session.cookies) == 0: + if self.session.get('cookies') and len(self.session.cookies) == 0: # get login page, thus automatically a cookie login = self.FetchURL('{0}/authentication/login'.format(self.monitor_url)) if login.error == '' and login.status_code == 200: diff --git a/build/debian/changelog b/build/debian/changelog index d14b380d2..cfd3342e3 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.9-20211116) unstable; urgency=low +nagstamon (3.9-20211208) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Tue, 16 Nov 2021 19:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Wed, 08 Dec 2021 08:00:00 +0100 nagstamon (3.8.0) stable; urgency=low * New upstream From 910330062b5a625e0f5698a300cd9c951e0643dc Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Wed, 8 Dec 2021 14:12:49 +0100 Subject: [PATCH 207/884] next attempt to fix #790 --- Nagstamon/Servers/IcingaWeb2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/Servers/IcingaWeb2.py b/Nagstamon/Servers/IcingaWeb2.py index 7247cd610..bd0a316a1 100644 --- a/Nagstamon/Servers/IcingaWeb2.py +++ b/Nagstamon/Servers/IcingaWeb2.py @@ -97,7 +97,7 @@ def init_HTTP(self): # normally cookie auth will be used if not self.no_cookie_auth: - if self.session.get('cookies') and len(self.session.cookies) == 0: + if not self.session.get('cookies') or len(self.session.cookies) == 0: # get login page, thus automatically a cookie login = self.FetchURL('{0}/authentication/login'.format(self.monitor_url)) if login.error == '' and login.status_code == 200: From 16549c6860b51a93141d84881c6ad28c35d8581e Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 9 Dec 2021 11:33:12 +0100 Subject: [PATCH 208/884] Update IcingaWeb2.py --- Nagstamon/Servers/IcingaWeb2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/Servers/IcingaWeb2.py b/Nagstamon/Servers/IcingaWeb2.py index bd0a316a1..99a5f6495 100644 --- a/Nagstamon/Servers/IcingaWeb2.py +++ b/Nagstamon/Servers/IcingaWeb2.py @@ -97,7 +97,7 @@ def init_HTTP(self): # normally cookie auth will be used if not self.no_cookie_auth: - if not self.session.get('cookies') or len(self.session.cookies) == 0: + if 'cookies' not in dir(self.session) or len(self.session.cookies) == 0: # get login page, thus automatically a cookie login = self.FetchURL('{0}/authentication/login'.format(self.monitor_url)) if login.error == '' and login.status_code == 200: From bd7fb4707dbd4855465ab95dd19cdb15a4d17a04 Mon Sep 17 00:00:00 2001 From: "Kimmig, Simon - D0242573" <simon.kimmig@dm.de> Date: Mon, 7 Feb 2022 13:51:29 +0100 Subject: [PATCH 209/884] Some Beauty --- Nagstamon/Servers/Zabbix.py | 163 +++++++++++++++++++----------------- 1 file changed, 88 insertions(+), 75 deletions(-) diff --git a/Nagstamon/Servers/Zabbix.py b/Nagstamon/Servers/Zabbix.py index b93816afd..d8e28097b 100644 --- a/Nagstamon/Servers/Zabbix.py +++ b/Nagstamon/Servers/Zabbix.py @@ -34,7 +34,6 @@ def __init__(self, terminate, result): class ZabbixServer(GenericServer): - """ special treatment for Zabbix, taken from Check_MK Multisite JSON API """ @@ -71,7 +70,7 @@ def __init__(self, **kwds): self.BROWSER_URLS = {'monitor': '$MONITOR$', 'hosts': '$MONITOR-CGI$/hosts.php?ddreset=1', 'services': '$MONITOR-CGI$/zabbix.php?action=problem.view&fullscreen=0&page=1&filter_show=3&filter_set=1', - 'history': '$MONITOR-CGI$/zabbix.php?action=problem.view&fullscreen=0&page=1&filter_show=2&filter_set=1'} + 'history': '$MONITOR-CGI$/zabbix.php?action=problem.view&fullscreen=0&page=1&filter_show=2&filter_set=1'} self.username = conf.servers[self.get_name()].username self.password = conf.servers[self.get_name()].password @@ -86,7 +85,8 @@ def _login(self): try: # create ZabbixAPI if not yet created if self.zapi is None: - self.zapi = ZabbixAPI(server=self.monitor_url, path="", log_level=self.log_level, validate_certs=self.validate_certs) + self.zapi = ZabbixAPI(server=self.monitor_url, path="", log_level=self.log_level, + validate_certs=self.validate_certs) # login if not yet logged in, or if login was refused previously if not self.zapi.logged_in(): self.zapi.login(self.username, self.password) @@ -125,19 +125,22 @@ def _get_status(self): # Create URLs for the configured filters self._login() - + # print(self.name) try: # ========================================= # Hosts Zabbix API data # ========================================= hosts = [] try: - hosts = self.zapi.host.get({"output": ["hostid", "host", "name", "status", \ - "available", "error", "errors_from", \ - "snmp_available", "snmp_error", "snmp_errors_from", \ - "ipmi_available", "ipmi_error", "ipmi_errors_from", \ - "jmx_available", "jmx_error", "jmx_errors_from", \ - "maintenance_status", "maintenance_from"], "selectInterfaces": ["ip"], "filter": {}}) + hosts = self.zapi.host.get({"output": ["hostid", "host", "name", "status", + "available", "error", "errors_from", + "snmp_available", "snmp_error", "snmp_errors_from", + "ipmi_available", "ipmi_error", "ipmi_errors_from", + "jmx_available", "jmx_error", "jmx_errors_from", + "maintenance_status", "maintenance_from"], + "selectInterfaces": ["ip"], + "filter": {} + }) except (ZabbixError, ZabbixAPIException, APITimeout, Already_Exists): # set checking flag back to False self.isChecking = False @@ -160,7 +163,7 @@ def _get_status(self): 'host': host['host'], 'name': host['name'], 'server': self.name, - 'status': 'UP', # Host is OK by default + 'status': 'UP', # Host is OK by default 'last_check': 'n/a', 'duration': '', 'attempt': 'N/A', @@ -187,28 +190,28 @@ def _get_status(self): # filter services and hosts by "filter_hosts_services_disabled_notifications" n['notifications_disabled'] = True # Filter only hosts by filter "Host & services with disabled checks" - n['passiveonly'] = True + n['passiveonly'] = True # attempt to fix https://github.com/HenriWahl/Nagstamon/issues/535 # if host['available'] == '0' and host['snmp_available'] == '0' and host['ipmi_available'] == '0' and host['jmx_available'] == '0': # n['status'] = 'UNREACHABLE' # n['status_information'] = 'Host agents in unknown state' # n['duration'] = 'Unknown' if host.get('ipmi_available', '0') == '2': - n['status'] = 'DOWN' + n['status'] = 'DOWN' n['status_information'] = host['ipmi_error'] - n['duration'] = HumanReadableDurationFromTimestamp(host['ipmi_errors_from']) + n['duration'] = HumanReadableDurationFromTimestamp(host['ipmi_errors_from']) if host.get('snmp_available', '0') == '2': - n['status'] = 'DOWN' + n['status'] = 'DOWN' n['status_information'] = host['snmp_error'] - n['duration'] = HumanReadableDurationFromTimestamp(host['snmp_errors_from']) + n['duration'] = HumanReadableDurationFromTimestamp(host['snmp_errors_from']) if host.get('jmx_available', '0') == '2': - n['status'] = 'DOWN' + n['status'] = 'DOWN' n['status_information'] = host['jmx_error'] - n['duration'] = HumanReadableDurationFromTimestamp(host['jmx_errors_from']) + n['duration'] = HumanReadableDurationFromTimestamp(host['jmx_errors_from']) if host.get('available', '0') == '2': - n['status'] = 'DOWN' + n['status'] = 'DOWN' n['status_information'] = host['error'] - n['duration'] = HumanReadableDurationFromTimestamp(host['errors_from']) + n['duration'] = HumanReadableDurationFromTimestamp(host['errors_from']) # Zabbix shows OK hosts too - kick 'em! if not n['status'] == 'UP': @@ -217,10 +220,10 @@ def _get_status(self): # after collection data in nagitems create objects from its informations # host objects contain service objects - #key_host = n["host"] - key_host = n["name"] if len(n['name']) != 0 else n["host"]; + # key_host = n["host"] + key_host = n["name"] if len(n['name']) != 0 else n["host"] - #key_host = n["hostid"] + # key_host = n["hostid"] if key_host not in self.new_hosts: self.new_hosts[key_host] = GenericHost() self.new_hosts[key_host].hostid = n["hostid"] @@ -244,7 +247,6 @@ def _get_status(self): result, error = self.Error(sys.exc_info()) return Result(result=result, error=error) - # ========================================= # services # ========================================= @@ -265,15 +267,16 @@ def _get_status(self): # Get a list of all issues (AKA tripped triggers) # Zabbix 3+ returns array of objects triggers = self.zapi.trigger.get({'only_true': True, - 'skipDependent': True, - 'monitored': True, - 'active': True, - 'output': 'extend', - 'expandDescription': True, - 'selectHosts': ['hostid','host','name'], - 'selectItems': ['itemid','name','key_','lastvalue','state','lastclock'], # thats for zabbix api 2.0+ - 'filter': {'value': 1}, - }) + 'skipDependent': True, + 'monitored': True, + 'active': True, + 'output': 'extend', + 'expandDescription': True, + 'selectHosts': ['hostid', 'host', 'name'], + 'selectItems': ['itemid', 'name', 'key_', 'lastvalue', 'state', + 'lastclock'], # thats for zabbix api 2.0+ + 'filter': {'value': 1}, + }) # Do another query to find out which issues are Unacknowledged # Zabbix 3+ returns array of objects @@ -285,16 +288,16 @@ def _get_status(self): 'expandDescription': True, 'selectHosts': ['hostid'], 'withLastEventUnacknowledged': True, - }) + }) unack_trigger_ids = [u['triggerid'] for u in unack_triggers] # prefetch items all_item_ids = list(set([t['items'][0]['itemid'] for t in triggers])) all_items = self.zapi.item.get( - {'itemids':all_item_ids, - 'output': ['itemid', 'hostid', 'name', 'lastvalue'], - 'selectTags': 'extend', - 'selectApplications': 'extend'} + {'itemids': all_item_ids, + 'output': ['itemid', 'hostid', 'name', 'lastvalue'], + 'selectTags': 'extend', + 'selectApplications': 'extend'} ) itemid_item_map = {i['itemid']: i for i in all_items} @@ -310,10 +313,10 @@ def _get_status(self): this_item = [] t['application'] = self.getLastApp(this_item) try: - t['lastvalue'] = this_item[0]['lastvalue'] + t['lastvalue'] = this_item[0]['lastvalue'] except IndexError as e: self.Debug(server=self.get_name(), debug="ItemID '%s' has no values" % - t['items'][0]['itemid'], head='WARNING') + t['items'][0]['itemid'], head='WARNING') services.append(t) @@ -338,8 +341,9 @@ def _get_status(self): # UPDATE Zabbix api 3.0 doesn't but I didn't tried with older # so I left it status = self.statemap.get(service['priority'], service['priority']) - # self.Debug(server=self.get_name(), debug="SERVICE (" + service['application'] + ") STATUS: **" + status + "** PRIORITY: #" + service['priority']) - # self.Debug(server=self.get_name(), debug="-----======== SERVICE " + str(service)) + # self.Debug(server=self.get_name(), debug="SERVICE (" + service['application'] + ") STATUS: **" + + # status + "** PRIORITY: #" + service['priority']) self.Debug(server=self.get_name(), + # debug="-----======== SERVICE " + str(service)) if not status == 'OK': if not service['description'].endswith('...'): state = service['description'] @@ -362,8 +366,8 @@ def _get_status(self): 'host': '', 'hostname': '', 'service': service['triggerid'], - 'server': self.name, - 'status': status, + 'server': self.name, + 'status': status, # Putting service in attempt column allow to see it in GUI 'attempt': srvc, 'duration': HumanReadableDurationFromTimestamp(service['lastchange']), @@ -375,20 +379,20 @@ def _get_status(self): 'passiveonly': False, 'notifications_disabled': False, 'flapping': False, - 'acknowledged' : service['acknowledged'], + 'acknowledged': service['acknowledged'], 'scheduled_downtime': False, # Zabbix data 'triggerid': service['triggerid'], } - n['hostid'] = service['hosts'][0]['hostid'] - n['host'] = service['hosts'][0]['host'] + n['hostid'] = service['hosts'][0]['hostid'] + n['host'] = service['hosts'][0]['host'] n['hostname'] = service['hosts'][0]['name'] - key = n["hostname"] if len(n['hostname']) != 0 else n["host"]; - #key = n["hostid"]; + key = n["hostname"] if len(n['hostname']) != 0 else n["host"] + # key = n["hostid"]; - if self.new_hosts[key].scheduled_downtime == True: + if self.new_hosts[key].scheduled_downtime: n['scheduled_downtime'] = True nagitems["services"].append(n) @@ -408,25 +412,27 @@ def _get_status(self): if new_service not in self.new_hosts[key].services: self.new_hosts[key].services[new_service] = GenericService() - self.new_hosts[key].services[new_service].host = n["hostname"] if len(n['hostname']) != 0 else n["host"] - self.new_hosts[key].services[new_service].name = n["service"] - self.new_hosts[key].services[new_service].status = n["status"] + self.new_hosts[key].services[new_service].host = n["hostname"] if len(n['hostname']) != 0 else \ + n["host"] + self.new_hosts[key].services[new_service].name = n["service"] + self.new_hosts[key].services[new_service].status = n["status"] self.new_hosts[key].services[new_service].last_check = n["last_check"] - self.new_hosts[key].services[new_service].duration = n["duration"] - self.new_hosts[key].services[new_service].attempt = n["attempt"] + self.new_hosts[key].services[new_service].duration = n["duration"] + self.new_hosts[key].services[new_service].attempt = n["attempt"] self.new_hosts[key].services[new_service].status_information = n["status_information"] self.new_hosts[key].services[new_service].passiveonly = n["passiveonly"] self.new_hosts[key].services[new_service].notifications_disabled = n["notifications_disabled"] - self.new_hosts[key].services[new_service].flapping = n["flapping"] + self.new_hosts[key].services[new_service].flapping = n["flapping"] self.new_hosts[key].services[new_service].acknowledged = n["acknowledged"] self.new_hosts[key].services[new_service].scheduled_downtime = n["scheduled_downtime"] - self.new_hosts[key].services[new_service].site = n["site"] - self.new_hosts[key].services[new_service].address = self.new_hosts[key].address - self.new_hosts[key].services[new_service].command = n["command"] - self.new_hosts[key].services[new_service].hostid = n["hostid"] - self.new_hosts[key].services[new_service].triggerid = n["triggerid"] + self.new_hosts[key].services[new_service].site = n["site"] + self.new_hosts[key].services[new_service].address = self.new_hosts[key].address + self.new_hosts[key].services[new_service].command = n["command"] + self.new_hosts[key].services[new_service].hostid = n["hostid"] + self.new_hosts[key].services[new_service].triggerid = n["triggerid"] if conf.debug_mode is True: - self.Debug(server=self.get_name(), debug="Adding new service[" + new_service + "] **" + n['service'] + "**") + self.Debug(server=self.get_name(), + debug="Adding new service[" + new_service + "] **" + n['service'] + "**") except (ZabbixError, ZabbixAPIException): # set checking flag back to False @@ -472,7 +478,9 @@ def set_recheck(self, info_dict): def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=[]): if conf.debug_mode is True: - self.Debug(server=self.get_name(), debug="Set Acknowledge Host: " + host + " Service: " + service + " Sticky: " + str(sticky) + " persistent:" + str(persistent) + " All services: " + str(all_services)) + self.Debug(server=self.get_name(), + debug="Set Acknowledge Host: " + host + " Service: " + service + " Sticky: " + str( + sticky) + " persistent:" + str(persistent) + " All services: " + str(all_services)) # Service column is storing current trigger id triggerids = [] @@ -489,7 +497,7 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi for e in self.zapi.event.get({'triggerids': [triggerid], # from zabbix 2.2 should be used "objectids" instead of "triggerids" 'objectids': [triggerid], -# 'acknowledged': False, + # 'acknowledged': False, 'sortfield': 'clock', 'sortorder': 'DESC'}): # Get only current event status, but retrieving first row ordered by clock DESC @@ -524,7 +532,8 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi if comment: actions |= 4 if conf.debug_mode is True: - self.Debug(server=self.get_name(), debug="Events to acknowledge: " + str(events) + " Close: " + str(actions)) + self.Debug(server=self.get_name(), + debug="Events to acknowledge: " + str(events) + " Close: " + str(actions)) self.zapi.event.acknowledge({'eventids': events, 'message': comment, 'action': actions}) def _set_downtime(self, hostname, service, author, comment, fixed, start_time, end_time, hours, minutes): @@ -546,20 +555,24 @@ def _set_downtime(self, hostname, service, author, comment, fixed, start_time, e if tag['tag'] == 'Application': app = tag['value'] - hostids = [ self.hosts[hostname].hostid ] + hostids = [self.hosts[hostname].hostid] - date = datetime.datetime.strptime(start_time, "%Y-%m-%d %H:%M") + date = datetime.datetime.strptime(start_time, "%Y-%m-%d %H:%M") stime = time.mktime(date.timetuple()) - date = datetime.datetime.strptime(end_time, "%Y-%m-%d %H:%M") + date = datetime.datetime.strptime(end_time, "%Y-%m-%d %H:%M") etime = time.mktime(date.timetuple()) if conf.debug_mode is True: - self.Debug(server=self.get_name(), debug="Downtime for " + hostname + "[" + str(hostids[0]) + "] stime:" + str(stime) + " etime:" + str(etime)) - - body = {'hostids': hostids, 'name': comment, 'description': author, 'active_since': stime, 'active_till': etime, 'maintenance_type' : 0, "timeperiods": [ - { "timeperiod_type": 0, "start_date": stime, "period": etime - stime } - ]} + self.Debug(server=self.get_name(), + debug="Downtime for " + hostname + "[" + str(hostids[0]) + "] stime:" + str( + stime) + " etime:" + str(etime)) + + body = {'hostids': hostids, 'name': comment, 'description': author, 'active_since': stime, 'active_till': etime, + 'maintenance_type': 0, "timeperiods": [ + {"timeperiod_type": 0, "start_date": stime, "period": etime - stime} + ] + } if app: body['tags'] = [{'tag': 'Application', 'operator': 0, 'value': app}] body['description'] += ' ' + body['name'] @@ -583,7 +596,7 @@ def GetHost(self, host): return Result(result=host) ip = "" - address = host; + address = host try: if host in self.hosts: @@ -611,4 +624,4 @@ def nagiosify_service(self, service): if (" on " or " is ") in service: for separator in [" on ", " is "]: service = service.split(separator)[0] - return(service) + return service From 20964081bd913ecbf5b4d3c7db120b3e63581edb Mon Sep 17 00:00:00 2001 From: "Kimmig, Simon - D0242573" <simon.kimmig@dm.de> Date: Tue, 8 Feb 2022 14:24:32 +0100 Subject: [PATCH 210/884] Make Macros visible --- Nagstamon/Servers/Zabbix.py | 124 +++++++++++++++++++----------------- 1 file changed, 65 insertions(+), 59 deletions(-) diff --git a/Nagstamon/Servers/Zabbix.py b/Nagstamon/Servers/Zabbix.py index d8e28097b..1c1fc4500 100644 --- a/Nagstamon/Servers/Zabbix.py +++ b/Nagstamon/Servers/Zabbix.py @@ -270,54 +270,56 @@ def _get_status(self): 'skipDependent': True, 'monitored': True, 'active': True, - 'output': 'extend', - 'expandDescription': True, + 'output': ['triggerid', 'description', 'lastchange'], + # 'expandDescription': True, + # 'expandComment': True, + 'selectLastEvent': ['name', 'ns', 'clock', 'acknowledged', 'value', + 'severity'], 'selectHosts': ['hostid', 'host', 'name'], - 'selectItems': ['itemid', 'name', 'key_', 'lastvalue', 'state', - 'lastclock'], # thats for zabbix api 2.0+ + 'selectItems': ['name', 'lastvalue', 'state', 'lastclock'], + # thats for zabbix api 2.0+ 'filter': {'value': 1}, }) - # Do another query to find out which issues are Unacknowledged # Zabbix 3+ returns array of objects - unack_triggers = self.zapi.trigger.get({'only_true': True, - 'skipDependent': True, - 'monitored': True, - 'active': True, - 'output': ['triggerid'], - 'expandDescription': True, - 'selectHosts': ['hostid'], - 'withLastEventUnacknowledged': True, - }) - unack_trigger_ids = [u['triggerid'] for u in unack_triggers] - - # prefetch items - all_item_ids = list(set([t['items'][0]['itemid'] for t in triggers])) - all_items = self.zapi.item.get( - {'itemids': all_item_ids, - 'output': ['itemid', 'hostid', 'name', 'lastvalue'], - 'selectTags': 'extend', - 'selectApplications': 'extend'} - ) - itemid_item_map = {i['itemid']: i for i in all_items} + # unack_triggers = self.zapi.trigger.get({'only_true': True, + # 'skipDependent': True, + # 'monitored': True, + # 'active': True, + # 'output': ['triggerid'], + # #'expandDescription': True, + # #'expandComment': True, + # 'selectHosts': ['hostid'], + # 'withLastEventUnacknowledged': True, + # }) + # unack_trigger_ids = [u['triggerid'] for u in unack_triggers] + + # prefetch items - already in item -> last value + # all_item_ids = list(set([t['items'][0]['itemid'] for t in triggers])) + # all_items = self.zapi.item.get( + # {'itemids': all_item_ids, + # 'output': ['itemid', 'hostid', 'name', 'lastvalue'], + # 'selectTags': 'extend', + # 'selectApplications': 'extend'} + # ) + # itemid_item_map = {i['itemid']: i for i in all_items} for t in triggers: - t['acknowledged'] = False if t['triggerid'] in unack_trigger_ids else True - - # get Application name for the trigger - if t['items'][0]['itemid'] in itemid_item_map: - this_item = [itemid_item_map[t['items'][0]['itemid']]] - else: - # This else condition should never be hit, except in rare circumstances the trigger/item - # config is updated at the same time Nagstamon is pulling trigger/item config - this_item = [] - t['application'] = self.getLastApp(this_item) - try: - t['lastvalue'] = this_item[0]['lastvalue'] - except IndexError as e: - self.Debug(server=self.get_name(), debug="ItemID '%s' has no values" % - t['items'][0]['itemid'], head='WARNING') - + # # t['acknowledged'] = False if t['triggerid'] in unack_trigger_ids else True + # t['lastEvent'][''] + # # get Application name for the trigger + # if t['items'][0]['itemid'] in itemid_item_map: + # this_item = [itemid_item_map[t['items'][0]['itemid']]] + # else: + # # This else condition should never be hit, except in rare circumstances the trigger/item + # # config is updated at the same time Nagstamon is pulling trigger/item config + # this_item = [] + # t['application'] = self.getLastApp(this_item) + # try: + # t['lastvalue'] = this_item[0]['lastvalue'] + # except IndexError as e: + # self.Debug(server=self.get_name(), debug="ItemID '%s' has no values" % + # t['items'][0]['itemid'], head='WARNING') services.append(t) except ZabbixAPIException: @@ -340,15 +342,16 @@ def _get_status(self): # Zabbix probably shows OK services too - kick 'em! # UPDATE Zabbix api 3.0 doesn't but I didn't tried with older # so I left it - status = self.statemap.get(service['priority'], service['priority']) + # print(service) + status = self.statemap.get(service['lastEvent']['severity'], service['lastEvent']['severity']) # self.Debug(server=self.get_name(), debug="SERVICE (" + service['application'] + ") STATUS: **" + # status + "** PRIORITY: #" + service['priority']) self.Debug(server=self.get_name(), # debug="-----======== SERVICE " + str(service)) if not status == 'OK': - if not service['description'].endswith('...'): - state = service['description'] - else: - state = service['items'][0]['lastvalue'] + # if not service['description'].endswith('...'): + # state = service['description'] + # else: + # state = service['items'][0]['lastvalue'] # A trigger can be triggered by multiple items # Get last checking date of any of the items involved lastcheck = 0 @@ -356,22 +359,24 @@ def _get_status(self): if int(item['lastclock']) > lastcheck: lastcheck = int(item['lastclock']) - if self.use_description_name_service and \ - len(service['comments']) != 0: - srvc = self.nagiosify_service(service['comments']) - else: - srvc = service['application'] - + # if self.use_description_name_service and \ + # len(service['comments']) != 0: + # srvc = self.nagiosify_service(service['comments']) + # else: + # srvc = "Not Implemented" + status_information = "" + for item in service['items']: + status_information = item['name'] + ": " + item['lastvalue'] + ", " + status_information n = { 'host': '', 'hostname': '', - 'service': service['triggerid'], + 'service': service['lastEvent']['name'], 'server': self.name, 'status': status, # Putting service in attempt column allow to see it in GUI - 'attempt': srvc, + 'attempt': '', 'duration': HumanReadableDurationFromTimestamp(service['lastchange']), - 'status_information': state, + 'status_information': status_information, 'last_check': time.strftime("%d/%m/%Y %H:%M:%S", time.localtime(lastcheck)), 'site': '', 'command': 'zabbix', @@ -379,7 +384,7 @@ def _get_status(self): 'passiveonly': False, 'notifications_disabled': False, 'flapping': False, - 'acknowledged': service['acknowledged'], + 'acknowledged': bool(int(service['lastEvent']['acknowledged'])), 'scheduled_downtime': False, # Zabbix data 'triggerid': service['triggerid'], @@ -481,7 +486,8 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi self.Debug(server=self.get_name(), debug="Set Acknowledge Host: " + host + " Service: " + service + " Sticky: " + str( sticky) + " persistent:" + str(persistent) + " All services: " + str(all_services)) - + print("Set Acknowledge Host: " + host + " Service: " + service + " Sticky: " + str( + sticky) + " persistent:" + str(persistent) + " All services: " + str(all_services)) # Service column is storing current trigger id triggerids = [] triggerids.append(service) @@ -570,8 +576,8 @@ def _set_downtime(self, hostname, service, author, comment, fixed, start_time, e body = {'hostids': hostids, 'name': comment, 'description': author, 'active_since': stime, 'active_till': etime, 'maintenance_type': 0, "timeperiods": [ - {"timeperiod_type": 0, "start_date": stime, "period": etime - stime} - ] + {"timeperiod_type": 0, "start_date": stime, "period": etime - stime} + ] } if app: body['tags'] = [{'tag': 'Application', 'operator': 0, 'value': app}] From 17e99437dbb1413a70db9c74034200bc7308fc2e Mon Sep 17 00:00:00 2001 From: "Kimmig, Simon - D0242573" <simon.kimmig@dm.de> Date: Tue, 8 Feb 2022 14:34:32 +0100 Subject: [PATCH 211/884] Make Macros visible --- Nagstamon/Servers/Zabbix.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Nagstamon/Servers/Zabbix.py b/Nagstamon/Servers/Zabbix.py index 1c1fc4500..f4eadc25a 100644 --- a/Nagstamon/Servers/Zabbix.py +++ b/Nagstamon/Servers/Zabbix.py @@ -416,7 +416,6 @@ def _get_status(self): new_service = n["triggerid"] if new_service not in self.new_hosts[key].services: self.new_hosts[key].services[new_service] = GenericService() - self.new_hosts[key].services[new_service].host = n["hostname"] if len(n['hostname']) != 0 else \ n["host"] self.new_hosts[key].services[new_service].name = n["service"] @@ -464,7 +463,6 @@ def open_monitor(self, host, service=""): """ open monitor from treeview context menu """ - if service == "": url = self.urls['human_host'] + urllib.parse.urlencode( {'x': 'site=' + self.hosts[host].site + '&host=' + host}).replace('x=', '%26') @@ -486,8 +484,8 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi self.Debug(server=self.get_name(), debug="Set Acknowledge Host: " + host + " Service: " + service + " Sticky: " + str( sticky) + " persistent:" + str(persistent) + " All services: " + str(all_services)) - print("Set Acknowledge Host: " + host + " Service: " + service + " Sticky: " + str( - sticky) + " persistent:" + str(persistent) + " All services: " + str(all_services)) + # print("Set Acknowledge Host: " + host + " Service: " + service + " Sticky: " + str( + # sticky) + " persistent:" + str(persistent) + " All services: " + str(all_services)) # Service column is storing current trigger id triggerids = [] triggerids.append(service) @@ -503,7 +501,7 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi for e in self.zapi.event.get({'triggerids': [triggerid], # from zabbix 2.2 should be used "objectids" instead of "triggerids" 'objectids': [triggerid], - # 'acknowledged': False, + # 'acknowledged': False, 'sortfield': 'clock', 'sortorder': 'DESC'}): # Get only current event status, but retrieving first row ordered by clock DESC @@ -543,7 +541,6 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi self.zapi.event.acknowledge({'eventids': events, 'message': comment, 'action': actions}) def _set_downtime(self, hostname, service, author, comment, fixed, start_time, end_time, hours, minutes): - # Check if there is an associated Application tag with this trigger/item app = None triggers = self.zapi.trigger.get({ From 639b2426a125baac6ac298e666065c536c1f626f Mon Sep 17 00:00:00 2001 From: "Kimmig, Simon - D0242573" <simon.kimmig@dm.de> Date: Tue, 8 Feb 2022 15:55:58 +0100 Subject: [PATCH 212/884] Fix Ackn --- Nagstamon/Servers/Zabbix.py | 104 +++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 49 deletions(-) diff --git a/Nagstamon/Servers/Zabbix.py b/Nagstamon/Servers/Zabbix.py index f4eadc25a..47d59e265 100644 --- a/Nagstamon/Servers/Zabbix.py +++ b/Nagstamon/Servers/Zabbix.py @@ -273,7 +273,7 @@ def _get_status(self): 'output': ['triggerid', 'description', 'lastchange'], # 'expandDescription': True, # 'expandComment': True, - 'selectLastEvent': ['name', 'ns', 'clock', 'acknowledged', 'value', + 'selectLastEvent': ['eventid', 'name', 'ns', 'clock', 'acknowledged', 'value', 'severity'], 'selectHosts': ['hostid', 'host', 'name'], 'selectItems': ['name', 'lastvalue', 'state', 'lastclock'], @@ -387,7 +387,7 @@ def _get_status(self): 'acknowledged': bool(int(service['lastEvent']['acknowledged'])), 'scheduled_downtime': False, # Zabbix data - 'triggerid': service['triggerid'], + 'triggerid': service['lastEvent']['eventid'],, } n['hostid'] = service['hosts'][0]['hostid'] @@ -487,58 +487,64 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi # print("Set Acknowledge Host: " + host + " Service: " + service + " Sticky: " + str( # sticky) + " persistent:" + str(persistent) + " All services: " + str(all_services)) # Service column is storing current trigger id - triggerids = [] - triggerids.append(service) + services = [] + services.append(service) # acknowledge all problems (column services) on a host when told to do so for s in all_services: - triggerids.append(s) + services.append(s) self._login() - - for triggerid in triggerids: - events = [] - for e in self.zapi.event.get({'triggerids': [triggerid], - # from zabbix 2.2 should be used "objectids" instead of "triggerids" - 'objectids': [triggerid], - # 'acknowledged': False, - 'sortfield': 'clock', - 'sortorder': 'DESC'}): - # Get only current event status, but retrieving first row ordered by clock DESC - - # If event status is not "OK" (Still is an active problem), mark event to acknowledge/close - if e['value'] != '0': - events.append(e['eventid']) - # Only take care of newest event, discard all next - break - - # If events pending of acknowledge, execute ack - if len(events) > 0: - # actions is a bitmask with values: - # 1 - close problem - # 2 - acknowledge event - # 4 - add message - # 8 - change severity - # 16 - unacknowledge event - actions = 0 - # If sticky is set then close only current event - if triggerid == service and sticky: - # do not send the "Close" flag if this event does not allow manual closing - triggers = self.zapi.trigger.get({ - 'output': ['triggerid', 'manual_close'], - 'filter': {'triggerid': triggerid}}) - if not triggers or 'manual_close' not in triggers[0] or str(triggers[0]['manual_close']) == '1': - actions |= 1 - # The current Nagstamon menu items don't match up too well with the Zabbix actions, - # but perhaps "Persistent comment" is the closest thing to acknowledgement - if persistent: - actions |= 2 - if comment: - actions |= 4 - if conf.debug_mode is True: - self.Debug(server=self.get_name(), - debug="Events to acknowledge: " + str(events) + " Close: " + str(actions)) - self.zapi.event.acknowledge({'eventids': events, 'message': comment, 'action': actions}) + eventids=[] + get_host = self.hosts[host] + # Through all Services + for service in services: + # find Trigger ID + for host_service in get_host.services: + host_service = get_host.services[host_service] + if host_service.name == service: + eventids.append(host_service.triggerid) + + #for e in self.zapi.event.get({'triggerids': [triggerid], + # # from zabbix 2.2 should be used "objectids" instead of "triggerids" + # 'objectids': [triggerid], + # # 'acknowledged': False, + # 'sortfield': 'clock', + # 'sortorder': 'DESC'}): + # # Get only current event status, but retrieving first row ordered by clock DESC + # # If event status is not "OK" (Still is an active problem), mark event to acknowledge/close + # if e['value'] != '0': + # events.append(e['eventid']) + # # Only take care of newest event, discard all next + # break + + # If events pending of acknowledge, execute ack + if len(eventids) > 0: + # actions is a bitmask with values: + # 1 - close problem + # 2 - acknowledge event + # 4 - add message + # 8 - change severity + # 16 - unacknowledge event + actions = 0 + # If sticky is set then close only current event + # if triggerid == service and sticky: + # # do not send the "Close" flag if this event does not allow manual closing + # triggers = self.zapi.trigger.get({ + # 'output': ['triggerid', 'manual_close'], + # 'filter': {'triggerid': triggerid}}) + # if not triggers or 'manual_close' not in triggers[0] or str(triggers[0]['manual_close']) == '1': + # actions |= 1 + # The current Nagstamon menu items don't match up too well with the Zabbix actions, + # but perhaps "Persistent comment" is the closest thing to acknowledgement + if persistent: + actions |= 2 + if comment: + actions |= 4 + if conf.debug_mode is True: + self.Debug(server=self.get_name(), + debug="Events to acknowledge: " + str(eventids) + " Close: " + str(actions)) + self.zapi.event.acknowledge({'eventids': eventids, 'message': comment, 'action': actions}) def _set_downtime(self, hostname, service, author, comment, fixed, start_time, end_time, hours, minutes): # Check if there is an associated Application tag with this trigger/item From 2cab44cc5884ca5b736c1538f99d8d2ac3f2a168 Mon Sep 17 00:00:00 2001 From: "Kimmig, Simon - D0242573" <simon.kimmig@dm.de> Date: Tue, 8 Feb 2022 16:57:38 +0100 Subject: [PATCH 213/884] Fix Downtime --- Nagstamon/Servers/Zabbix.py | 65 ++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/Nagstamon/Servers/Zabbix.py b/Nagstamon/Servers/Zabbix.py index 47d59e265..939f3304a 100644 --- a/Nagstamon/Servers/Zabbix.py +++ b/Nagstamon/Servers/Zabbix.py @@ -387,7 +387,8 @@ def _get_status(self): 'acknowledged': bool(int(service['lastEvent']['acknowledged'])), 'scheduled_downtime': False, # Zabbix data - 'triggerid': service['lastEvent']['eventid'],, + 'triggerid': service['triggerid'], + 'eventid': service['lastEvent']['eventid'], } n['hostid'] = service['hosts'][0]['hostid'] @@ -434,6 +435,7 @@ def _get_status(self): self.new_hosts[key].services[new_service].command = n["command"] self.new_hosts[key].services[new_service].hostid = n["hostid"] self.new_hosts[key].services[new_service].triggerid = n["triggerid"] + self.new_hosts[key].services[new_service].eventid = n["eventid"] if conf.debug_mode is True: self.Debug(server=self.get_name(), debug="Adding new service[" + new_service + "] **" + n['service'] + "**") @@ -485,7 +487,7 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi debug="Set Acknowledge Host: " + host + " Service: " + service + " Sticky: " + str( sticky) + " persistent:" + str(persistent) + " All services: " + str(all_services)) # print("Set Acknowledge Host: " + host + " Service: " + service + " Sticky: " + str( - # sticky) + " persistent:" + str(persistent) + " All services: " + str(all_services)) + # sticky) + " persistent:" + str(persistent) + " All services: " + str(all_services)) # Service column is storing current trigger id services = [] services.append(service) @@ -503,7 +505,8 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi for host_service in get_host.services: host_service = get_host.services[host_service] if host_service.name == service: - eventids.append(host_service.triggerid) + eventids.append(host_service.eventid) + break #for e in self.zapi.event.get({'triggerids': [triggerid], # # from zabbix 2.2 should be used "objectids" instead of "triggerids" @@ -526,7 +529,7 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi # 4 - add message # 8 - change severity # 16 - unacknowledge event - actions = 0 + actions = 2 # If sticky is set then close only current event # if triggerid == service and sticky: # # do not send the "Close" flag if this event does not allow manual closing @@ -537,32 +540,36 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi # actions |= 1 # The current Nagstamon menu items don't match up too well with the Zabbix actions, # but perhaps "Persistent comment" is the closest thing to acknowledgement - if persistent: - actions |= 2 + # if persistent: + # actions |= 2 if comment: actions |= 4 if conf.debug_mode is True: self.Debug(server=self.get_name(), debug="Events to acknowledge: " + str(eventids) + " Close: " + str(actions)) + # print("Events to acknowledge: " + str(eventids) + " Close: " + str(actions)) self.zapi.event.acknowledge({'eventids': eventids, 'message': comment, 'action': actions}) def _set_downtime(self, hostname, service, author, comment, fixed, start_time, end_time, hours, minutes): # Check if there is an associated Application tag with this trigger/item - app = None - triggers = self.zapi.trigger.get({ - 'selectItems': ['itemid'], - 'output': ['triggerid'], - 'filter': {'triggerid': service}}) - - if triggers and triggers[0]['items']: - items = self.zapi.item.get({ - 'itemids': [triggers[0]['items'][0]['itemid']], - 'output': ['itemid'], - 'selectTags': 'extend'}) - if items and items[0]['tags']: - for tag in items[0]['tags']: - if tag['tag'] == 'Application': - app = tag['value'] + triggerid = None + for host_service in self.hosts[hostname].services: + if self.hosts[hostname].services[host_service].name == service: + triggerid = self.hosts[hostname].services[host_service].triggerid + break + # triggers = self.zapi.trigger.get({ + # 'selectItems': ['itemid'], + # 'output': ['triggerid'], + # 'filter': {'triggerid': service}}) + # if triggers and triggers[0]['items']: + # items = self.zapi.item.get({ + # 'itemids': [triggers[0]['items'][0]['itemid']], + # 'output': ['itemid'], + # 'selectTags': 'extend'}) + # if items and items[0]['tags']: + # for tag in items[0]['tags']: + # if tag['tag'] == 'Application': + # app = tag['value'] hostids = [self.hosts[hostname].hostid] @@ -574,18 +581,18 @@ def _set_downtime(self, hostname, service, author, comment, fixed, start_time, e if conf.debug_mode is True: self.Debug(server=self.get_name(), - debug="Downtime for " + hostname + "[" + str(hostids[0]) + "] stime:" + str( + debug="Downtime for " + hostname + "[" + str(hostids) + "] stime:" + str( stime) + " etime:" + str(etime)) - + # print("Downtime for " + hostname + "[" + str(hostids) + "] stime:" + str(stime) + " etime:" + str(etime)) body = {'hostids': hostids, 'name': comment, 'description': author, 'active_since': stime, 'active_till': etime, 'maintenance_type': 0, "timeperiods": [ - {"timeperiod_type": 0, "start_date": stime, "period": etime - stime} - ] + {"timeperiod_type": 0, "start_date": stime, "period": etime - stime} + ] } - if app: - body['tags'] = [{'tag': 'Application', 'operator': 0, 'value': app}] - body['description'] += ' ' + body['name'] - body['name'] = f'{start_time} {hostname} {app}' + if triggerid: + body['tags'] = [{'tag': 'triggerid', 'operator': 0, 'value': triggerid}] + body['description'] = body['description'] + '(Nagstamon): ' + comment + body['name'] = f'{hostname} - {service}' try: self.zapi.maintenance.create(body) except Already_Exists: From 5763a4415724a3285e2b8ad97bb0a9d4ca712a69 Mon Sep 17 00:00:00 2001 From: "Kimmig, Simon - D0242573" <simon.kimmig@dm.de> Date: Wed, 9 Feb 2022 12:44:45 +0100 Subject: [PATCH 214/884] Fix Downtime --- Nagstamon/Servers/Zabbix.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Servers/Zabbix.py b/Nagstamon/Servers/Zabbix.py index 939f3304a..5b6e96e44 100644 --- a/Nagstamon/Servers/Zabbix.py +++ b/Nagstamon/Servers/Zabbix.py @@ -139,6 +139,7 @@ def _get_status(self): "jmx_available", "jmx_error", "jmx_errors_from", "maintenance_status", "maintenance_from"], "selectInterfaces": ["ip"], + "with_triggers": True, "filter": {} }) except (ZabbixError, ZabbixAPIException, APITimeout, Already_Exists): @@ -275,7 +276,7 @@ def _get_status(self): # 'expandComment': True, 'selectLastEvent': ['eventid', 'name', 'ns', 'clock', 'acknowledged', 'value', 'severity'], - 'selectHosts': ['hostid', 'host', 'name'], + 'selectHosts': 'extend', #['hostid', 'host', 'name'], 'selectItems': ['name', 'lastvalue', 'state', 'lastclock'], # thats for zabbix api 2.0+ 'filter': {'value': 1}, @@ -592,7 +593,7 @@ def _set_downtime(self, hostname, service, author, comment, fixed, start_time, e if triggerid: body['tags'] = [{'tag': 'triggerid', 'operator': 0, 'value': triggerid}] body['description'] = body['description'] + '(Nagstamon): ' + comment - body['name'] = f'{hostname} - {service}' + body['name'] = f'{hostname}: {service}' try: self.zapi.maintenance.create(body) except Already_Exists: From 4c90f5512eb6bb2900e7bf0dba365534c1b39b8a Mon Sep 17 00:00:00 2001 From: "Kimmig, Simon - D0242573" <simon.kimmig@dm.de> Date: Wed, 9 Feb 2022 14:19:30 +0100 Subject: [PATCH 215/884] Restructure for Performance Improvements in Version 5.4 --- Nagstamon/Servers/Zabbix.py | 318 +++++++++++++++++------------------- 1 file changed, 150 insertions(+), 168 deletions(-) diff --git a/Nagstamon/Servers/Zabbix.py b/Nagstamon/Servers/Zabbix.py index 5b6e96e44..c9b2a1644 100644 --- a/Nagstamon/Servers/Zabbix.py +++ b/Nagstamon/Servers/Zabbix.py @@ -126,134 +126,13 @@ def _get_status(self): # Create URLs for the configured filters self._login() # print(self.name) - try: - # ========================================= - # Hosts Zabbix API data - # ========================================= - hosts = [] - try: - hosts = self.zapi.host.get({"output": ["hostid", "host", "name", "status", - "available", "error", "errors_from", - "snmp_available", "snmp_error", "snmp_errors_from", - "ipmi_available", "ipmi_error", "ipmi_errors_from", - "jmx_available", "jmx_error", "jmx_errors_from", - "maintenance_status", "maintenance_from"], - "selectInterfaces": ["ip"], - "with_triggers": True, - "filter": {} - }) - except (ZabbixError, ZabbixAPIException, APITimeout, Already_Exists): - # set checking flag back to False - self.isChecking = False - result, error = self.Error(sys.exc_info()) - return Result(result=result, error=error) - - # get All Hosts. - # 1. Store data in cache (to be used by events) - # 2. We store as faulty two kinds of hosts incidences: - # - Disabled hosts - # - Hosts with issues trying to connect to agent/service - # - In maintenance - # status = 1 -> Disabled - # available ZBX: 0 -> No agents 1 -> available 2-> Agent access error - # ipmi_available IPMI: 0 -> No agents 1 -> available 2-> Agent access error - # maintenance_status = 1 In maintenance - for host in hosts: - - n = { - 'host': host['host'], - 'name': host['name'], - 'server': self.name, - 'status': 'UP', # Host is OK by default - 'last_check': 'n/a', - 'duration': '', - 'attempt': 'N/A', - 'status_information': '', - # status flags - 'passiveonly': False, - 'notifications_disabled': False, - 'flapping': False, - 'acknowledged': False, - 'scheduled_downtime': False, - # Zabbix backend data - 'hostid': host['hostid'], - 'site': '', - # 'address': host['interfaces'][0]['ip'], - } - - # try to fix https://github.com/HenriWahl/Nagstamon/issues/687 - n['address'] = host['interfaces'][0]['ip'] if len(host['interfaces']) > 0 else '' - - if host['maintenance_status'] == '1': - n['scheduled_downtime'] = True - - if host['status'] == '1': - # filter services and hosts by "filter_hosts_services_disabled_notifications" - n['notifications_disabled'] = True - # Filter only hosts by filter "Host & services with disabled checks" - n['passiveonly'] = True - # attempt to fix https://github.com/HenriWahl/Nagstamon/issues/535 - # if host['available'] == '0' and host['snmp_available'] == '0' and host['ipmi_available'] == '0' and host['jmx_available'] == '0': - # n['status'] = 'UNREACHABLE' - # n['status_information'] = 'Host agents in unknown state' - # n['duration'] = 'Unknown' - if host.get('ipmi_available', '0') == '2': - n['status'] = 'DOWN' - n['status_information'] = host['ipmi_error'] - n['duration'] = HumanReadableDurationFromTimestamp(host['ipmi_errors_from']) - if host.get('snmp_available', '0') == '2': - n['status'] = 'DOWN' - n['status_information'] = host['snmp_error'] - n['duration'] = HumanReadableDurationFromTimestamp(host['snmp_errors_from']) - if host.get('jmx_available', '0') == '2': - n['status'] = 'DOWN' - n['status_information'] = host['jmx_error'] - n['duration'] = HumanReadableDurationFromTimestamp(host['jmx_errors_from']) - if host.get('available', '0') == '2': - n['status'] = 'DOWN' - n['status_information'] = host['error'] - n['duration'] = HumanReadableDurationFromTimestamp(host['errors_from']) - - # Zabbix shows OK hosts too - kick 'em! - if not n['status'] == 'UP': - # add dictionary full of information about this host item to nagitems - nagitems["hosts"].append(n) - - # after collection data in nagitems create objects from its informations - # host objects contain service objects - # key_host = n["host"] - key_host = n["name"] if len(n['name']) != 0 else n["host"] - - # key_host = n["hostid"] - if key_host not in self.new_hosts: - self.new_hosts[key_host] = GenericHost() - self.new_hosts[key_host].hostid = n["hostid"] - self.new_hosts[key_host].host = n["host"] - self.new_hosts[key_host].name = n["name"] - self.new_hosts[key_host].status = n["status"] - self.new_hosts[key_host].last_check = n["last_check"] - self.new_hosts[key_host].duration = n["duration"] - self.new_hosts[key_host].attempt = n["attempt"] - self.new_hosts[key_host].status_information = n["status_information"] - self.new_hosts[key_host].site = n["site"] - self.new_hosts[key_host].address = n["address"] - self.new_hosts[key_host].notifications_disabled = n["notifications_disabled"] - self.new_hosts[key_host].scheduled_downtime = n["scheduled_downtime"] - self.new_hosts[key_host].passiveonly = n["passiveonly"] - self.new_hosts[key_host].acknowledged = n["acknowledged"] - self.new_hosts[key_host].flapping = n["flapping"] - - except ZabbixError: - self.isChecking = False - result, error = self.Error(sys.exc_info()) - return Result(result=result, error=error) # ========================================= - # services + # Service # ========================================= services = [] try: - api_version = self.zapi.api_version() + api_version = int(''.join(self.zapi.api_version().split('.')[:-1])) # Make API Version smaller except ZabbixAPIException: # FIXME Is there a cleaner way to handle this? I just borrowed # this code from 80 lines ahead. -- AGV @@ -267,61 +146,21 @@ def _get_status(self): try: # Get a list of all issues (AKA tripped triggers) # Zabbix 3+ returns array of objects - triggers = self.zapi.trigger.get({'only_true': True, + services = self.zapi.trigger.get({'only_true': True, 'skipDependent': True, 'monitored': True, 'active': True, 'output': ['triggerid', 'description', 'lastchange'], # 'expandDescription': True, # 'expandComment': True, - 'selectLastEvent': ['eventid', 'name', 'ns', 'clock', 'acknowledged', 'value', - 'severity'], - 'selectHosts': 'extend', #['hostid', 'host', 'name'], + 'selectLastEvent': ['eventid', 'name', 'ns', 'clock', 'acknowledged', + 'value', 'severity'], + 'selectHosts': ["hostid", "host", "name", "status", "available", + "maintenance_status", "maintenance_from"], 'selectItems': ['name', 'lastvalue', 'state', 'lastclock'], # thats for zabbix api 2.0+ 'filter': {'value': 1}, }) - # Do another query to find out which issues are Unacknowledged - # Zabbix 3+ returns array of objects - # unack_triggers = self.zapi.trigger.get({'only_true': True, - # 'skipDependent': True, - # 'monitored': True, - # 'active': True, - # 'output': ['triggerid'], - # #'expandDescription': True, - # #'expandComment': True, - # 'selectHosts': ['hostid'], - # 'withLastEventUnacknowledged': True, - # }) - # unack_trigger_ids = [u['triggerid'] for u in unack_triggers] - - # prefetch items - already in item -> last value - # all_item_ids = list(set([t['items'][0]['itemid'] for t in triggers])) - # all_items = self.zapi.item.get( - # {'itemids': all_item_ids, - # 'output': ['itemid', 'hostid', 'name', 'lastvalue'], - # 'selectTags': 'extend', - # 'selectApplications': 'extend'} - # ) - # itemid_item_map = {i['itemid']: i for i in all_items} - - for t in triggers: - # # t['acknowledged'] = False if t['triggerid'] in unack_trigger_ids else True - # t['lastEvent'][''] - # # get Application name for the trigger - # if t['items'][0]['itemid'] in itemid_item_map: - # this_item = [itemid_item_map[t['items'][0]['itemid']]] - # else: - # # This else condition should never be hit, except in rare circumstances the trigger/item - # # config is updated at the same time Nagstamon is pulling trigger/item config - # this_item = [] - # t['application'] = self.getLastApp(this_item) - # try: - # t['lastvalue'] = this_item[0]['lastvalue'] - # except IndexError as e: - # self.Debug(server=self.get_name(), debug="ItemID '%s' has no values" % - # t['items'][0]['itemid'], head='WARNING') - services.append(t) except ZabbixAPIException: # FIXME Is there a cleaner way to handle this? I just borrowed @@ -339,6 +178,149 @@ def _get_status(self): service = e.result.content ret = e.result + # ========================================= + # Hosts Zabbix API data + # ========================================= + # Create Hostids for shorten Query + try: + hosts = [] + if api_version >= 54: # For Version 5.4 and higher + # Some performance improvement for 5.4 + hostids = [] + # get just involved Hosts. + for service in services: + for host in service["hosts"]: + hostids.append(host["hostid"]) + + try: + hosts = self.zapi.host.get({"output": ["hostid", "host", "name", "status", "available", + "maintenance_status", "maintenance_from"], + "hostids": hostids, + "selectInterfaces": ["ip"], + "filter": {} + }) + except (ZabbixError, ZabbixAPIException, APITimeout, Already_Exists): + # set checking flag back to False + self.isChecking = False + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) + else: + try: + # TODO: This Query can be removed when Zabbix Version 4 Support is dropped. + hosts = self.zapi.host.get({"output": ["hostid", "host", "name", "status", "available", + "error", "errors_from", # dropped in Version 5.4 + "snmp_available", "snmp_error", "snmp_errors_from", # dropped in Version 5.4 + "ipmi_available", "ipmi_error", "ipmi_errors_from", # dropped in Version 5.4 + "jmx_available", "jmx_error", "jmx_errors_from", # dropped in Version 5.4 + "maintenance_status", "maintenance_from"], + "selectInterfaces": ["ip"], + "filter": {} + }) + except (ZabbixError, ZabbixAPIException, APITimeout, Already_Exists): + # set checking flag back to False + self.isChecking = False + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) + # get All Hosts. + # 1. Store data in cache (to be used by events) + # 2. We store as faulty two kinds of hosts incidences: + # - Disabled hosts + # - Hosts with issues trying to connect to agent/service + # - In maintenance + # status = 1 -> Disabled + # available ZBX: 0 -> No agents 1 -> available 2-> Agent access error + # ipmi_available IPMI: 0 -> No agents 1 -> available 2-> Agent access error + # maintenance_status = 1 In maintenance + for host in hosts: + n = { + 'host': host['host'], + 'name': host['name'], + 'server': self.name, + 'status': 'UP', # Host is OK by default + 'last_check': 'n/a', + 'duration': '', + 'attempt': 'N/A', + 'status_information': '', + # status flags + 'passiveonly': False, + 'notifications_disabled': False, + 'flapping': False, + 'acknowledged': False, + 'scheduled_downtime': False, + # Zabbix backend data + 'hostid': host['hostid'], + 'site': '', + # 'address': host['interfaces'][0]['ip'], + } + + # try to fix https://github.com/HenriWahl/Nagstamon/issues/687 + # + n['address'] = host['interfaces'][0]['ip'] if len(host['interfaces']) > 0 else '' + + if host['maintenance_status'] == '1': + n['scheduled_downtime'] = True + + if host['status'] == '1': + # filter services and hosts by "filter_hosts_services_disabled_notifications" + n['notifications_disabled'] = True + # Filter only hosts by filter "Host & services with disabled checks" + n['passiveonly'] = True + # attempt to fix https://github.com/HenriWahl/Nagstamon/issues/535 + # TODO: This can be simplified if Zabbix Version 5.0 Support is dropped. + # if host['available'] == '0' and host['snmp_available'] == '0' and host['ipmi_available'] == '0' and host['jmx_available'] == '0': + # n['status'] = 'UNREACHABLE' + # n['status_information'] = 'Host agents in unknown state' + # n['duration'] = 'Unknown' + if host.get('ipmi_available', '0') == '2': + n['status'] = 'DOWN' + n['status_information'] = host['ipmi_error'] + n['duration'] = HumanReadableDurationFromTimestamp(host['ipmi_errors_from']) + if host.get('snmp_available', '0') == '2': + n['status'] = 'DOWN' + n['status_information'] = host['snmp_error'] + n['duration'] = HumanReadableDurationFromTimestamp(host['snmp_errors_from']) + if host.get('jmx_available', '0') == '2': + n['status'] = 'DOWN' + n['status_information'] = host['jmx_error'] + n['duration'] = HumanReadableDurationFromTimestamp(host['jmx_errors_from']) + if host.get('available', '0') == '2': + n['status'] = 'DOWN' + n['status_information'] = host['error'] + n['duration'] = HumanReadableDurationFromTimestamp(host['errors_from']) + # Zabbix shows OK hosts too - kick 'em! + if not n['status'] == 'UP': + # add dictionary full of information about this host item to nagitems + nagitems["hosts"].append(n) + + # after collection data in nagitems create objects from its informations + # host objects contain service objects + # key_host = n["host"] + key_host = n["name"] if len(n['name']) != 0 else n["host"] + + # key_host = n["hostid"] + if key_host not in self.new_hosts: + self.new_hosts[key_host] = GenericHost() + self.new_hosts[key_host].hostid = n["hostid"] + self.new_hosts[key_host].host = n["host"] + self.new_hosts[key_host].name = n["name"] + self.new_hosts[key_host].status = n["status"] + self.new_hosts[key_host].last_check = n["last_check"] + self.new_hosts[key_host].duration = n["duration"] + self.new_hosts[key_host].attempt = n["attempt"] + self.new_hosts[key_host].status_information = n["status_information"] + self.new_hosts[key_host].site = n["site"] + self.new_hosts[key_host].address = n["address"] + self.new_hosts[key_host].notifications_disabled = n["notifications_disabled"] + self.new_hosts[key_host].scheduled_downtime = n["scheduled_downtime"] + self.new_hosts[key_host].passiveonly = n["passiveonly"] + self.new_hosts[key_host].acknowledged = n["acknowledged"] + self.new_hosts[key_host].flapping = n["flapping"] + + except ZabbixError: + self.isChecking = False + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) + ### for service in services: # Zabbix probably shows OK services too - kick 'em! # UPDATE Zabbix api 3.0 doesn't but I didn't tried with older From e670b25096e32ba88128f74d3e1e6545e5e2bd7b Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 19 Feb 2022 13:48:16 +0100 Subject: [PATCH 216/884] https://github.com/HenriWahl/Nagstamon/issues/787 LooseVersion replacement --- COPYRIGHT | 2 +- Nagstamon/Config.py | 6 +++--- Nagstamon/Helpers.py | 2 +- Nagstamon/Objects.py | 2 +- Nagstamon/QUI/__init__.py | 2 +- Nagstamon/QUI/dialog_about.py | 2 +- Nagstamon/QUI/dialog_about.ui | 2 +- Nagstamon/Servers/Alertmanager/LICENSE | 2 +- Nagstamon/Servers/Centreon.py | 2 +- Nagstamon/Servers/Generic.py | 2 +- Nagstamon/Servers/Icinga.py | 13 +++++++++---- Nagstamon/Servers/Icinga2API.py | 2 +- Nagstamon/Servers/IcingaWeb2.py | 2 +- Nagstamon/Servers/Livestatus.py | 2 +- Nagstamon/Servers/Monitos3.py | 2 +- Nagstamon/Servers/Monitos4x.py | 2 +- Nagstamon/Servers/Multisite.py | 2 +- Nagstamon/Servers/Nagios.py | 2 +- Nagstamon/Servers/Opsview.py | 2 +- Nagstamon/Servers/Prometheus.py | 2 +- Nagstamon/Servers/SnagView3.py | 2 +- Nagstamon/Servers/Thruk.py | 2 +- Nagstamon/Servers/__init__.py | 2 +- Nagstamon/Servers/op5Monitor.py | 2 +- Nagstamon/__init__.py | 2 +- Nagstamon/setup.py | 2 +- Nagstamon/thirdparty/__init__.py | 2 +- build/build.py | 2 +- build/debian/changelog | 2 +- nagstacli.py | 2 +- nagstamon.py | 2 +- setup.py | 2 +- 32 files changed, 42 insertions(+), 37 deletions(-) diff --git a/COPYRIGHT b/COPYRIGHT index 37be493f0..3132671ff 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -1 +1 @@ -Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. +Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 6353390a8..3e0221f3c 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -2,7 +2,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -125,9 +125,9 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20211208' + VERSION = '3.9-20220219' WEBSITE = 'https://nagstamon.de' - COPYRIGHT = '©2008-2021 Henri Wahl et al.' + COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' # dict of servers to offer for downloads if an update is available DOWNLOAD_SERVERS = {'nagstamon.de': 'https://github.com/HenriWahl/Nagstamon/releases'} diff --git a/Nagstamon/Helpers.py b/Nagstamon/Helpers.py index ca02a4f75..f2dc20ec0 100644 --- a/Nagstamon/Helpers.py +++ b/Nagstamon/Helpers.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Objects.py b/Nagstamon/Objects.py index fcf4f3762..15a03c747 100644 --- a/Nagstamon/Objects.py +++ b/Nagstamon/Objects.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 7b76d0627..7c2682a4f 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -1,6 +1,6 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/QUI/dialog_about.py b/Nagstamon/QUI/dialog_about.py index 4f5db628d..8dd8865d6 100644 --- a/Nagstamon/QUI/dialog_about.py +++ b/Nagstamon/QUI/dialog_about.py @@ -87,7 +87,7 @@ def retranslateUi(self, dialog_about): dialog_about.setWindowTitle(_translate("dialog_about", "About Nagstamon")) self.label_nagstamon.setText(_translate("dialog_about", "Nagstamon x")) self.label_nagstamon_long.setText(_translate("dialog_about", "Nagios status monitor")) - self.label_copyright.setText(_translate("dialog_about", "©2008-2021 Henri Wahl et al.")) + self.label_copyright.setText(_translate("dialog_about", "©2008-2022 Henri Wahl et al.")) self.label_website.setText(_translate("dialog_about", "https://nagstamon.de")) self.label_footnote.setText(_translate("dialog_about", "Footnote")) self.tabs.setTabText(self.tabs.indexOf(self.tab_about), _translate("dialog_about", "About")) diff --git a/Nagstamon/QUI/dialog_about.ui b/Nagstamon/QUI/dialog_about.ui index 9a01dfd36..46d88357b 100644 --- a/Nagstamon/QUI/dialog_about.ui +++ b/Nagstamon/QUI/dialog_about.ui @@ -75,7 +75,7 @@ <item> <widget class="QLabel" name="label_copyright"> <property name="text"> - <string>©2008-2021 Henri Wahl et al.</string> + <string>©2008-2022 Henri Wahl et al.</string> </property> <property name="alignment"> <set>Qt::AlignCenter</set> diff --git a/Nagstamon/Servers/Alertmanager/LICENSE b/Nagstamon/Servers/Alertmanager/LICENSE index 4c9f4588f..d00a5ca36 100644 --- a/Nagstamon/Servers/Alertmanager/LICENSE +++ b/Nagstamon/Servers/Alertmanager/LICENSE @@ -1,5 +1,5 @@ Nagstamon - Nagios status monitor for your desktop -Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. +Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Centreon.py b/Nagstamon/Servers/Centreon.py index d17d3801b..3fb224500 100644 --- a/Nagstamon/Servers/Centreon.py +++ b/Nagstamon/Servers/Centreon.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index 63762228a..1c38c5e1e 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Icinga.py b/Nagstamon/Servers/Icinga.py index ffaea2e1e..7d75a0b52 100644 --- a/Nagstamon/Servers/Icinga.py +++ b/Nagstamon/Servers/Icinga.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -23,7 +23,6 @@ import json from bs4 import BeautifulSoup from collections import OrderedDict -from distutils.version import LooseVersion from Nagstamon.Servers.Generic import GenericServer from Nagstamon.Objects import (GenericHost, GenericService, Result) @@ -89,6 +88,13 @@ def get_server_version(self): else: self.refresh_authentication = True + def looseversion(self, version): + """ + Replacement for distutils.version.LooseVersion which got lost in newer Python + Fix for https://github.com/HenriWahl/Nagstamon/issues/787 + """ + # gives back a list with split version components - makes 2 method calls comparable + return [int(x) for x in version.split('.')] def _get_status(self): """ @@ -102,7 +108,7 @@ def _get_status(self): if self.version != '': # define CGI URLs for hosts and services depending on JSON-capable server version if self.cgiurl_hosts == self.cgiurl_services == None: - if LooseVersion(self.version) < LooseVersion('1.7'): + if self.looseversion(self.version) < self.looseversion('1.7'): # http://www.nagios-wiki.de/nagios/tips/host-_und_serviceproperties_fuer_status.cgi?s=servicestatustypes # services (unknown, warning or critical?) as dictionary, sorted by hard and soft state type self.cgiurl_services = {'hard': self.monitor_cgi_url + '/status.cgi?host=all&servicestatustypes=253&serviceprops=262144', \ @@ -680,5 +686,4 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi cgi_data['service'] = s self.FetchURL(url, giveback='raw', cgi_data=cgi_data) -0 diff --git a/Nagstamon/Servers/Icinga2API.py b/Nagstamon/Servers/Icinga2API.py index 344a71044..207e3f9cb 100644 --- a/Nagstamon/Servers/Icinga2API.py +++ b/Nagstamon/Servers/Icinga2API.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/IcingaWeb2.py b/Nagstamon/Servers/IcingaWeb2.py index 99a5f6495..c9a14565a 100644 --- a/Nagstamon/Servers/IcingaWeb2.py +++ b/Nagstamon/Servers/IcingaWeb2.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Livestatus.py b/Nagstamon/Servers/Livestatus.py index c95548ec6..c9a1f6c66 100644 --- a/Nagstamon/Servers/Livestatus.py +++ b/Nagstamon/Servers/Livestatus.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Monitos3.py b/Nagstamon/Servers/Monitos3.py index 8f8dede9e..18b383e93 100644 --- a/Nagstamon/Servers/Monitos3.py +++ b/Nagstamon/Servers/Monitos3.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Monitos4x.py b/Nagstamon/Servers/Monitos4x.py index 33e1f6a91..db6bd7d2f 100644 --- a/Nagstamon/Servers/Monitos4x.py +++ b/Nagstamon/Servers/Monitos4x.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Multisite.py b/Nagstamon/Servers/Multisite.py index 55b7c5015..c84e94516 100644 --- a/Nagstamon/Servers/Multisite.py +++ b/Nagstamon/Servers/Multisite.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Nagios.py b/Nagstamon/Servers/Nagios.py index 7ade7a777..68469bbd3 100644 --- a/Nagstamon/Servers/Nagios.py +++ b/Nagstamon/Servers/Nagios.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Opsview.py b/Nagstamon/Servers/Opsview.py index 7b2afdc5d..267430150 100644 --- a/Nagstamon/Servers/Opsview.py +++ b/Nagstamon/Servers/Opsview.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. # # Based on https://github.com/duncs/Nagstamon by @duncs # diff --git a/Nagstamon/Servers/Prometheus.py b/Nagstamon/Servers/Prometheus.py index 18c28e16b..3d66ae1f0 100644 --- a/Nagstamon/Servers/Prometheus.py +++ b/Nagstamon/Servers/Prometheus.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/SnagView3.py b/Nagstamon/Servers/SnagView3.py index 5164be381..46f4ce947 100644 --- a/Nagstamon/Servers/SnagView3.py +++ b/Nagstamon/Servers/SnagView3.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Thruk.py b/Nagstamon/Servers/Thruk.py index d7cc6b157..b4faf108a 100644 --- a/Nagstamon/Servers/Thruk.py +++ b/Nagstamon/Servers/Thruk.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. # Thruk additions copyright by dcec@Github # # This program is free software; you can redistribute it and/or modify diff --git a/Nagstamon/Servers/__init__.py b/Nagstamon/Servers/__init__.py index c449abf67..6d0f0bc3e 100644 --- a/Nagstamon/Servers/__init__.py +++ b/Nagstamon/Servers/__init__.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/op5Monitor.py b/Nagstamon/Servers/op5Monitor.py index 419c599da..13f61144c 100644 --- a/Nagstamon/Servers/op5Monitor.py +++ b/Nagstamon/Servers/op5Monitor.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/__init__.py b/Nagstamon/__init__.py index e8dc8e5ca..997747a19 100644 --- a/Nagstamon/__init__.py +++ b/Nagstamon/__init__.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/setup.py b/Nagstamon/setup.py index 1cc059e26..2e393e4cd 100644 --- a/Nagstamon/setup.py +++ b/Nagstamon/setup.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/thirdparty/__init__.py b/Nagstamon/thirdparty/__init__.py index 2c44c5cf4..f0f9b3d62 100644 --- a/Nagstamon/thirdparty/__init__.py +++ b/Nagstamon/thirdparty/__init__.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/build/build.py b/build/build.py index 367cc13c8..6c748064f 100644 --- a/build/build.py +++ b/build/build.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/build/debian/changelog b/build/debian/changelog index cfd3342e3..77c35bde1 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.9-20211208) unstable; urgency=low +nagstamon (3.9-20220219) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Wed, 08 Dec 2021 08:00:00 +0100 diff --git a/nagstacli.py b/nagstacli.py index cc398156c..e98013fcd 100755 --- a/nagstacli.py +++ b/nagstacli.py @@ -2,7 +2,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. Maik Lüdeke <m.luedeke@ifw-dresden.de> +# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. Maik Lüdeke <m.luedeke@ifw-dresden.de> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/nagstamon.py b/nagstamon.py index 233aee5d8..4cfb71cd4 100755 --- a/nagstamon.py +++ b/nagstamon.py @@ -2,7 +2,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/setup.py b/setup.py index 8f6af9d0f..6575075ae 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2021 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by From b35af9dadc06c1cee14d04983257d9c1adf5a289 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 22 Feb 2022 18:17:25 +0100 Subject: [PATCH 217/884] Python 3.9.10 --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 9c4aecfea..b415bd471 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -5,7 +5,7 @@ on: branches: '**' env: - python_win_version: 3.9.9 + python_win_version: 3.9.10 repo_dir: nagstamon-jekyll/docs/repo jobs: From f3cb7c7364d1ebda207c4f581484416be939b9b5 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 22 Feb 2022 18:19:57 +0100 Subject: [PATCH 218/884] less strict requirements --- build/requirements/windows.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index 524068d81..b50ea6a21 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -4,13 +4,13 @@ icinga2api keyring lxml psutil -pyinstaller==4.7 +pyinstaller pypiwin32 -pyqt5==5.15.4 +pyqt5 pysocks python-dateutil requests requests-kerberos requests-ecp -setuptools==44.1.1 +setuptools wheel From 963767b9432a6388c2242e420fae944bb6d2cea9 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 22 Feb 2022 20:51:19 +0100 Subject: [PATCH 219/884] python 3.10.2 for Windows --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index b415bd471..6226b30aa 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -5,7 +5,7 @@ on: branches: '**' env: - python_win_version: 3.9.10 + python_win_version: 3.10.2 repo_dir: nagstamon-jekyll/docs/repo jobs: From 437a7cf1413d535b074e4459208190af6b55efff Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 22 Feb 2022 23:00:56 +0100 Subject: [PATCH 220/884] windows-2019 --- .github/workflows/build-release-latest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 6226b30aa..ed622b249 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -118,7 +118,7 @@ jobs: if-no-files-found: error windows-32: - runs-on: windows-latest + runs-on: windows-2019 needs: test steps: - uses: actions/checkout@v2 @@ -139,7 +139,7 @@ jobs: if-no-files-found: error windows-64: - runs-on: windows-latest + runs-on: windows-2019 needs: test steps: - uses: actions/checkout@v2 From b0af47d6b746ed7810ee91ca4e1379a3b593ef75 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 22 Feb 2022 18:17:25 +0100 Subject: [PATCH 221/884] Python 3.9.10 --- .github/workflows/build-release-latest.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index ed622b249..b415bd471 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -5,7 +5,7 @@ on: branches: '**' env: - python_win_version: 3.10.2 + python_win_version: 3.9.10 repo_dir: nagstamon-jekyll/docs/repo jobs: @@ -118,7 +118,7 @@ jobs: if-no-files-found: error windows-32: - runs-on: windows-2019 + runs-on: windows-latest needs: test steps: - uses: actions/checkout@v2 @@ -139,7 +139,7 @@ jobs: if-no-files-found: error windows-64: - runs-on: windows-2019 + runs-on: windows-latest needs: test steps: - uses: actions/checkout@v2 From 8a5a8ab82f6f9607d35820edd1275e39b0c9e384 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 22 Feb 2022 23:00:56 +0100 Subject: [PATCH 222/884] windows-2019 --- .github/workflows/build-release-latest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index b415bd471..faf76d0c9 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -118,7 +118,7 @@ jobs: if-no-files-found: error windows-32: - runs-on: windows-latest + runs-on: windows-2019 needs: test steps: - uses: actions/checkout@v2 @@ -139,7 +139,7 @@ jobs: if-no-files-found: error windows-64: - runs-on: windows-latest + runs-on: windows-2019 needs: test steps: - uses: actions/checkout@v2 From 5a353ad5f8f88bbe6d7b33bcdfde85bf328fde21 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Wed, 23 Feb 2022 00:00:52 +0100 Subject: [PATCH 223/884] try to prevent gssapi --- .github/workflows/build-release-latest.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index faf76d0c9..1631e515c 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -118,6 +118,7 @@ jobs: if-no-files-found: error windows-32: + # better depend on stable build image runs-on: windows-2019 needs: test steps: @@ -127,6 +128,8 @@ jobs: python-version: ${{ env.python_win_version }} architecture: x86 - run: python -m pip install --no-warn-script-location -r build/requirements/windows.txt + # pretty hacky but no other idea to avoid gssapi beong installed which breaks requests-kerberos + - run: python -m pip uninstall gssapi - run: cd ${{ github.workspace }}/build; python build.py env: PYTHONPATH: ${{ github.workspace }} @@ -139,6 +142,7 @@ jobs: if-no-files-found: error windows-64: + # better depend on stable build image runs-on: windows-2019 needs: test steps: @@ -148,6 +152,8 @@ jobs: python-version: ${{ env.python_win_version }} architecture: x64 - run: python -m pip install --no-warn-script-location -r build/requirements/windows.txt + # pretty hacky but no other idea to avoid gssapi beong installed which breaks requests-kerberos + - run: python -m pip uninstall gssapi - run: cd ${{ github.workspace }}/build; python build.py env: PYTHONPATH: ${{ github.workspace }} From 22dca6f3878af287df9dff8137c41ef855923223 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 22 Feb 2022 18:19:57 +0100 Subject: [PATCH 224/884] -y --- .github/workflows/build-release-latest.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 1631e515c..3b8f116b8 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -128,8 +128,8 @@ jobs: python-version: ${{ env.python_win_version }} architecture: x86 - run: python -m pip install --no-warn-script-location -r build/requirements/windows.txt - # pretty hacky but no other idea to avoid gssapi beong installed which breaks requests-kerberos - - run: python -m pip uninstall gssapi + # pretty hacky but no other idea to avoid gssapi being installed which breaks requests-kerberos + - run: python -m pip uninstall -y gssapi - run: cd ${{ github.workspace }}/build; python build.py env: PYTHONPATH: ${{ github.workspace }} @@ -152,8 +152,8 @@ jobs: python-version: ${{ env.python_win_version }} architecture: x64 - run: python -m pip install --no-warn-script-location -r build/requirements/windows.txt - # pretty hacky but no other idea to avoid gssapi beong installed which breaks requests-kerberos - - run: python -m pip uninstall gssapi + # pretty hacky but no other idea to avoid gssapi being installed which breaks requests-kerberos + - run: python -m pip uninstall -y gssapi - run: cd ${{ github.workspace }}/build; python build.py env: PYTHONPATH: ${{ github.workspace }} From cd89c7eecfd0f9f2466f86e55872875cf6ab871d Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 19 Apr 2022 23:35:15 +0200 Subject: [PATCH 225/884] try without "self.refresh_authentication = True" in Checkmk --- Nagstamon/Config.py | 2 +- Nagstamon/QUI/__init__.py | 16 ++++---- Nagstamon/Servers/Generic.py | 72 +++++++++++++++++++++------------- Nagstamon/Servers/Multisite.py | 24 ++++++------ build/debian/changelog | 2 +- build/requirements/macos.txt | 2 +- 6 files changed, 69 insertions(+), 49 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 3e0221f3c..5322af95c 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20220219' + VERSION = '3.9-20220419' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 7c2682a4f..970b2e7be 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -4113,13 +4113,15 @@ def get_status(self): # reflect status retrieval attempt on server vbox label self.change_label_status.emit('Refreshing...', '') - # get status from server instance if connection was already possible and no TLS error - if not self.server.tls_error and \ - not self.server.refresh_authentication: - status = self.server.GetStatus() - else: - # dummy status result - status = Result() + # # get status from server instance if connection was already possible and no TLS error + # if not self.server.tls_error and \ + # not self.server.refresh_authentication: + # status = self.server.GetStatus() + # else: + # # dummy status result + # status = Result() + + status = self.server.GetStatus() # all is OK if no error info came back if self.server.status_description == '' and \ diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index 1c38c5e1e..e921e5289 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -254,39 +254,50 @@ def init_config(self): def init_HTTP(self): """ - partly not constantly working Basic Authorization requires extra Authorization headers, - different between various server types + initialize HTTP connection + should return a valid session if none exists yet + when reauthentication is needed the session should be removed """ if self.refresh_authentication: self.session = None return False elif self.session is None: - self.session = requests.Session() - self.session.headers['User-Agent'] = self.USER_AGENT - - # support for different authentication types - if self.authentication == 'basic': - # basic authentication - self.session.auth = requests.auth.HTTPBasicAuth(self.username, self.password) - elif self.authentication == 'digest': - self.session.auth = requests.auth.HTTPDigestAuth(self.username, self.password) - elif self.authentication == 'ecp': - self.session.auth = HTTPECPAuth(self.idp_ecp_endpoint, username=self.username, password=self.password) - elif self.authentication == 'kerberos': - self.session.auth = HTTPSKerberos() - - # default to check TLS validity - if self.ignore_cert: - self.session.verify = False - elif self.custom_cert_use: - self.session.verify = self.custom_cert_ca_file - else: - self.session.verify = True - - # add proxy information - self.proxify(self.session) + self.session = self.create_session() return True + def create_session(self): + """ + reusable session creation + partly not constantly working Basic Authorization requires extra Authorization headers, + different between various server types + """ + session = requests.Session() + session.headers['User-Agent'] = self.USER_AGENT + + # support for different authentication types + if self.authentication == 'basic': + # basic authentication + session.auth = requests.auth.HTTPBasicAuth(self.username, self.password) + elif self.authentication == 'digest': + session.auth = requests.auth.HTTPDigestAuth(self.username, self.password) + elif self.authentication == 'ecp': + session.auth = HTTPECPAuth(self.idp_ecp_endpoint, username=self.username, password=self.password) + elif self.authentication == 'kerberos': + session.auth = HTTPSKerberos() + + # default to check TLS validity + if self.ignore_cert: + session.verify = False + elif self.custom_cert_use: + session.verify = self.custom_cert_ca_file + else: + session.verify = True + + # add proxy information + self.proxify(session) + + return session + def proxify(self, requester): ''' add proxy information to session or single request @@ -1466,6 +1477,7 @@ def FetchURL(self, url, giveback='obj', cgi_data=None, no_auth=False, multipart= # use session only for connections to monitor servers, other requests like looking for updates # should go out without credentials if no_auth is False and not self.refresh_authentication: + # if no_auth is False: # check if there is really a session if not self.session: self.reset_HTTP() @@ -1512,7 +1524,10 @@ def FetchURL(self, url, giveback='obj', cgi_data=None, no_auth=False, multipart= del temporary_session except Exception: - traceback.print_exc(file=sys.stdout) + if conf.debug_mode: + #traceback.print_exc(file=sys.stdout) + #self.Debug(server=self.get_name(), debug=' '.join(sys.exc_info())) + self.Error(sys.exc_info()) result, error = self.Error(sys.exc_info()) if error.startswith('requests.exceptions.SSLError:'): self.tls_error = True @@ -1542,7 +1557,8 @@ def FetchURL(self, url, giveback='obj', cgi_data=None, no_auth=False, multipart= status_code=response.status_code) except Exception: - traceback.print_exc(file=sys.stdout) + #traceback.print_exc(file=sys.stdout) + self.Error(sys.exc_info()) result, error = self.Error(sys.exc_info()) return Result(result=result, error=error, status_code=response.status_code) diff --git a/Nagstamon/Servers/Multisite.py b/Nagstamon/Servers/Multisite.py index c84e94516..2cb4ec9fe 100644 --- a/Nagstamon/Servers/Multisite.py +++ b/Nagstamon/Servers/Multisite.py @@ -67,11 +67,10 @@ def __init__(self, **kwds): def init_HTTP(self): # general initialization - GenericServer.init_HTTP(self) + if not self.session: + GenericServer.init_HTTP(self) # Fix eventually missing tailing '/' in url - # if self.monitor_url[-1] != '/': - # self.monitor_url += '/' if self.monitor_url.endswith('/'): self.monitor_url.rstrip('/') @@ -120,6 +119,12 @@ def init_HTTP(self): elif self.session == None: # if no cookie yet login self._get_cookie_login() + elif self.CookieAuth and self.refresh_authentication: + if self.session is None: + self.session = self.create_session() + + # force re-auth + self._get_cookie_login() def init_config(self): @@ -133,7 +138,7 @@ def _is_auth_in_cookies(self): """ check if there is any valid auth session in cookies which has the name 'auth_<monitor_name>' """ - if not self.session == None: + if self.session: for cookie in self.session.cookies: if cookie.name.startswith('auth_'): return True @@ -164,10 +169,10 @@ def _get_url(self, url): status_code=status_code)) # in case of auth problem enable GUI auth part in popup - if self.CookieAuth == True and not self.session == None: - if not self._is_auth_in_cookies(): - self.refresh_authentication = True - return '' + #if self.CookieAuth and self.session is not None: + # if not self._is_auth_in_cookies(): + # self.refresh_authentication = True + # return '' # looks like cookieauth elif content.startswith('<'): @@ -202,9 +207,6 @@ def _get_cookie_login(self): # login and get cookie self.FetchURL(self.monitor_url + '/login.py', cgi_data=login_data, multipart=True) except: - import traceback - traceback.print_exc(file=sys.stdout) - self.Error(sys.exc_info()) diff --git a/build/debian/changelog b/build/debian/changelog index 77c35bde1..065e910ba 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.9-20220219) unstable; urgency=low +nagstamon (3.9-20220419) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Wed, 08 Dec 2021 08:00:00 +0100 diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index 3cde90ae7..a67d3af5e 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -11,4 +11,4 @@ requests requests-ecp # gssapi instead kerberos requests-gssapi -setuptools==44.1.1 +setuptools From 2e0136b1e645f96206687ddd7bbeea3bc4f28b3f Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Fri, 22 Apr 2022 22:24:29 +0200 Subject: [PATCH 226/884] fix for debug-less Zabbix filtering --- Nagstamon/Config.py | 2 +- Nagstamon/Servers/Generic.py | 32 ++++++++++++++++---------------- build/debian/changelog | 2 +- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 5322af95c..459cc3ace 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20220419' + VERSION = '3.9-20220422' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index e921e5289..942d73d23 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -1251,40 +1251,40 @@ def GetStatus(self, output=None): if conf.debug_mode: self.Debug(server=self.get_name(), debug="Filter: INFORMATION " + str(host.name) + ";" + str(service.name)) - service.visible = False - else: - self.nagitems_filtered["services"]["INFORMATION"].append(service) - self.information += 1 + service.visible = False + else: + self.nagitems_filtered["services"]["INFORMATION"].append(service) + self.information += 1 if service.status == "AVERAGE": if conf.filter_all_unknown_services is True: if conf.debug_mode: self.Debug(server=self.get_name(), debug="Filter: AVERAGE " + str(host.name) + ";" + str(service.name)) - service.visible = False - else: - self.nagitems_filtered["services"]["AVERAGE"].append(service) - self.average += 1 + service.visible = False + else: + self.nagitems_filtered["services"]["AVERAGE"].append(service) + self.average += 1 if service.status == "HIGH": if conf.filter_all_unknown_services is True: if conf.debug_mode: self.Debug(server=self.get_name(), debug="Filter: HIGH " + str(host.name) + ";" + str(service.name)) - service.visible = False - else: - self.nagitems_filtered["services"]["HIGH"].append(service) - self.high += 1 + service.visible = False + else: + self.nagitems_filtered["services"]["HIGH"].append(service) + self.high += 1 if service.status == "DISASTER": if conf.filter_all_unknown_services is True: if conf.debug_mode: self.Debug(server=self.get_name(), debug="Filter: DISASTER " + str(host.name) + ";" + str(service.name)) - service.visible = False - else: - self.nagitems_filtered["services"]["DISASTER"].append(service) - self.disaster += 1 + service.visible = False + else: + self.nagitems_filtered["services"]["DISASTER"].append(service) + self.disaster += 1 # Add service flags for status icons in treeview if service.acknowledged: diff --git a/build/debian/changelog b/build/debian/changelog index 065e910ba..b762a17b3 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.9-20220419) unstable; urgency=low +nagstamon (3.9-20220422) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Wed, 08 Dec 2021 08:00:00 +0100 From f9720deff8b39d63d0e3475b2dfc79f7bcb6c750 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 2 May 2022 21:57:19 +0200 Subject: [PATCH 227/884] un-*-ed imports of Qt modules --- Nagstamon/QUI/__init__.py | 182 +++++++++++++++++++++++--------------- 1 file changed, 111 insertions(+), 71 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 970b2e7be..93b007d90 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -16,31 +16,77 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA -import sys - -"""Module QUI""" -from PyQt5.QtWidgets import * -from PyQt5.QtGui import * -from PyQt5.QtCore import * -from PyQt5.QtSvg import * -from PyQt5.QtMultimedia import * - +import base64 +from collections import OrderedDict +import copy +from copy import deepcopy +import datetime import os import os.path -import urllib.parse -import subprocess import platform -import time import random -import copy -import base64 -import datetime +import subprocess +import sys +import time import traceback - from urllib.parse import quote -from collections import OrderedDict -from copy import deepcopy +from PyQt5.QtCore import pyqtSignal, \ + pyqtSlot, \ + QAbstractTableModel, \ + QByteArray, \ + QDateTime, \ + QModelIndex, \ + QObject, \ + QPoint, \ + QSignalMapper, \ + Qt, \ + QThread, \ + QTimer, \ + QUrl, \ + QVariant, \ + QXmlStreamReader +from PyQt5.QtGui import QBrush, \ + QColor, \ + QCursor, \ + QFont, \ + QFontDatabase, \ + QIcon, \ + QKeySequence, \ + QPainter, \ + QPalette, \ + QPixmap +from PyQt5.QtMultimedia import QMediaContent, \ + QMediaPlayer, \ + QMediaPlaylist +from PyQt5.QtSvg import QSvgRenderer, \ + QSvgWidget + +from PyQt5.QtWidgets import QAbstractItemView, \ + QAction, \ + QApplication, \ + QColorDialog, \ + QComboBox, \ + QDialog, \ + QFileDialog, \ + QFontDialog, \ + QHBoxLayout, \ + QHeaderView, \ + QListWidgetItem, \ + QMenu, \ + QMenuBar, \ + QMessageBox, \ + QLabel, \ + QPushButton, \ + QScrollArea, \ + QSizePolicy,\ + QSpacerItem, \ + QToolButton, \ + QTreeView, \ + QStyle, \ + QSystemTrayIcon, \ + QVBoxLayout, \ + QWidget from Nagstamon.Config import (Action, AppInfo, @@ -73,8 +119,6 @@ STATES_SOUND, SORT_COLUMNS_FUNCTIONS) -from Nagstamon.Objects import Result - # dialogs from Nagstamon.QUI.settings_main import Ui_settings_main from Nagstamon.QUI.settings_server import Ui_settings_server @@ -116,6 +160,7 @@ # check ECP authentication support availability try: from requests_ecp import HTTPECPAuth + ECP_AVAILABLE = True except ImportError: ECP_AVAILABLE = False @@ -302,15 +347,18 @@ def show_items(self): for item in range(self.count() - 1): self.itemAt(item).widget().show() + class QIconWithFilename(QIcon): """ extend QIcon with a filename property """ + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if type(args[0]) == str: self.filename = args[0] + class SystemTrayIcon(QSystemTrayIcon): """ Icon in system tray, works at least in Windows and OSX @@ -402,7 +450,8 @@ def create_icons(self): # replace dummy text and background colors with configured ones for line in svg_template_xml: line = line.replace('fill:#ff00ff', 'fill:' + conf.__dict__['color_' + state.lower() + '_text']) - line = line.replace('fill:#00ff00', 'fill:' + conf.__dict__['color_' + state.lower() + '_background']) + line = line.replace('fill:#00ff00', + 'fill:' + conf.__dict__['color_' + state.lower() + '_background']) svg_state_xml.append(line) # create XML stream of SVG @@ -422,7 +471,8 @@ def create_icons(self): # put pixmap into icon self.icons[state] = QIconWithFilename(svg_pixmap) - debug_queue.append('DEBUG: SystemTrayIcon created icon {} for state "{}"'.format(self.icons[state], state)) + debug_queue.append( + 'DEBUG: SystemTrayIcon created icon {} for state "{}"'.format(self.icons[state], state)) @pyqtSlot(QSystemTrayIcon.ActivationReason) def icon_clicked(self, reason): @@ -1312,7 +1362,7 @@ def set_mode(self): self.show_window() # fullscreen mode is rather buggy on everything other than OSX so just use a maximized window if OS == OS_DARWIN: - self.showFullScreen() + self.showFullScreen() else: self.show() self.showMaximized() @@ -1590,7 +1640,7 @@ def show_window(self, event=None): vbox.button_fix_tls_error.hide() if not conf.fullscreen and \ - not conf.windowed: + not conf.windowed: # theory... width, height, x, y = self.calculate_size() # ...and practice @@ -1638,9 +1688,9 @@ def show_window(self, event=None): # Thus we check again with this timer to catch missed mouse-outs. # causes trouble in Wayland so is disabled for it if conf.close_details_hover and \ - conf.statusbar_floating and\ - self.is_shown and\ - not DESKTOP_WAYLAND: + conf.statusbar_floating and \ + self.is_shown and \ + not DESKTOP_WAYLAND: self.periodically_check_window_under_mouse_and_hide() def periodically_check_window_under_mouse_and_hide(self): @@ -1656,9 +1706,9 @@ def hide_window_if_not_under_mouse(self): """ mouse_pos = QCursor.pos() # Check mouse cursor over window and an opened context menu or dropdown list - if self.geometry().contains(mouse_pos.x(), mouse_pos.y()) or\ - not APP.activePopupWidget() is None or \ - self.is_shown: + if self.geometry().contains(mouse_pos.x(), mouse_pos.y()) or \ + not APP.activePopupWidget() is None or \ + self.is_shown: return False self.hide_window() @@ -1670,8 +1720,8 @@ def update_window(self): redraw window content, to be effective only when window is shown """ if self.is_shown or \ - conf.fullscreen or \ - (conf.windowed and self.is_shown): + conf.fullscreen or \ + (conf.windowed and self.is_shown): self.show_window() @pyqtSlot() @@ -1795,7 +1845,7 @@ def calculate_size(self): # add available_y because it might vary on differently setup screens # calculate top-ness only if window is closed if conf.statusbar_floating: - #if self.is_shown is False: + # if self.is_shown is False: if self.y() < desktop.screenGeometry(self).height() // 2 + available_y: self.top = True else: @@ -1847,7 +1897,7 @@ def calculate_size(self): # when height is to large for current screen cut it if self.y() + self.height() - real_height < available_y: height = desktop.screenGeometry().height() - available_y - ( - desktop.screenGeometry().height() - (self.y() + self.height())) + desktop.screenGeometry().height() - (self.y() + self.height())) y = available_y else: height = real_height @@ -2137,10 +2187,10 @@ def raise_window_on_all_desktops(self): self.setWindowFlags(WINDOW_FLAGS) # again and again try to keep that statuswindow on top! - if OS == OS_WINDOWS and\ - not conf.fullscreen and\ - not conf.windowed and\ - APP.activePopupWidget() == None: + if OS == OS_WINDOWS and \ + not conf.fullscreen and \ + not conf.windowed and \ + APP.activePopupWidget() == None: try: # find out if no context menu is shown and thus would be # overlapped by statuswindow @@ -4113,14 +4163,6 @@ def get_status(self): # reflect status retrieval attempt on server vbox label self.change_label_status.emit('Refreshing...', '') - # # get status from server instance if connection was already possible and no TLS error - # if not self.server.tls_error and \ - # not self.server.refresh_authentication: - # status = self.server.GetStatus() - # else: - # # dummy status result - # status = Result() - status = self.server.GetStatus() # all is OK if no error info came back @@ -4235,7 +4277,8 @@ def fill_data_array(self, sort_column, sort_order): # fourth item in las data_array line is service flags self.data_array[-1][3] += 'N' # add text color as QBrush from status - self.data_array[-1].append(QBRUSHES[len(self.data_array) % 2][COLORS[item.status] + 'text']) + self.data_array[-1].append( + QBRUSHES[len(self.data_array) % 2][COLORS[item.status] + 'text']) # add background color as QBrush from status self.data_array[-1].append( QBRUSHES[len(self.data_array) % 2][COLORS[item.status] + 'background']) @@ -4962,12 +5005,11 @@ def __init__(self, dialog): # ...and another... self.EXPIRE_TIME_WIDGETS = [self.ui.input_checkbox_defaults_acknowledge_expire, - self.ui.label_expire_in, - self.ui.label_expire_in_hours, - self.ui.label_expire_in_minutes, - self.ui.input_spinbox_defaults_acknowledge_expire_duration_hours, - self.ui.input_spinbox_defaults_acknowledge_expire_duration_minutes] - + self.ui.label_expire_in, + self.ui.label_expire_in_hours, + self.ui.label_expire_in_minutes, + self.ui.input_spinbox_defaults_acknowledge_expire_duration_hours, + self.ui.input_spinbox_defaults_acknowledge_expire_duration_minutes] def initialize(self): # apply configuration values @@ -5162,10 +5204,10 @@ def ok(self): # when display mode was changed its the easiest to destroy the old status window and create a new one # store display_mode to decide if statuswindow has to be recreated if display_mode != str(conf.statusbar_floating) + \ - str(conf.icon_in_systray) + \ - str(conf.fullscreen) + \ - str(conf.fullscreen_display) + \ - str(conf.windowed): + str(conf.icon_in_systray) + \ + str(conf.fullscreen) + \ + str(conf.fullscreen_display) + \ + str(conf.windowed): # increase number of display changes for silly Windows-hides-statusbar-after-display-mode-change problem NUMBER_OF_DISPLAY_CHANGES += 1 @@ -5766,12 +5808,12 @@ def __init__(self, dialog): self.ui.label_host_filter: ['op5Monitor'], self.ui.label_monitor_site: ['Sensu'], self.ui.input_lineedit_monitor_site: ['Sensu'], - self.ui.label_map_to_hostname: ['Prometheus','Alertmanager'], - self.ui.input_lineedit_map_to_hostname: ['Prometheus','Alertmanager'], - self.ui.label_map_to_servicename: ['Prometheus','Alertmanager'], - self.ui.input_lineedit_map_to_servicename: ['Prometheus','Alertmanager'], - self.ui.label_map_to_status_information: ['Prometheus','Alertmanager'], - self.ui.input_lineedit_map_to_status_information: ['Prometheus','Alertmanager'], + self.ui.label_map_to_hostname: ['Prometheus', 'Alertmanager'], + self.ui.input_lineedit_map_to_hostname: ['Prometheus', 'Alertmanager'], + self.ui.label_map_to_servicename: ['Prometheus', 'Alertmanager'], + self.ui.input_lineedit_map_to_servicename: ['Prometheus', 'Alertmanager'], + self.ui.label_map_to_status_information: ['Prometheus', 'Alertmanager'], + self.ui.input_lineedit_map_to_status_information: ['Prometheus', 'Alertmanager'], self.ui.label_alertmanager_filter: ['Alertmanager'], self.ui.input_lineedit_alertmanager_filter: ['Alertmanager'], self.ui.label_map_to_ok: ['Alertmanager'], @@ -6306,12 +6348,13 @@ def __init__(self, dialog): self.TOGGLE_DEPS = { self.ui.input_checkbox_use_expire_time: [self.ui.input_datetime_expire_time] - } + } # still clumsy but better than negating the other server types PROMETHEUS_OR_ALERTMANAGER = ['Alertmanager', 'Prometheus'] - NOT_PROMETHEUS_OR_ALERTMANAGER = [x.TYPE for x in SERVER_TYPES.values() if x.TYPE not in PROMETHEUS_OR_ALERTMANAGER] + NOT_PROMETHEUS_OR_ALERTMANAGER = [x.TYPE for x in SERVER_TYPES.values() if + x.TYPE not in PROMETHEUS_OR_ALERTMANAGER] self.VOLATILE_WIDGETS = { self.ui.input_checkbox_use_expire_time: ['IcingaWeb2'], @@ -6320,11 +6363,10 @@ def __init__(self, dialog): self.ui.input_checkbox_send_notification: NOT_PROMETHEUS_OR_ALERTMANAGER, self.ui.input_checkbox_persistent_comment: NOT_PROMETHEUS_OR_ALERTMANAGER, self.ui.input_checkbox_acknowledge_all_services: NOT_PROMETHEUS_OR_ALERTMANAGER - } + } self.FORCE_DATETIME_EXPIRE_TIME = ['Alertmanager'] - def initialize(self, server=None, host=[], service=[]): # store server, host and service to be used for OK button evaluation self.server = server @@ -6382,7 +6424,6 @@ def initialize(self, server=None, host=[], service=[]): self.ui.options_groupbox.adjustSize() self.window.adjustSize() - def ok(self): """ acknowledge miserable host/service @@ -6404,8 +6445,6 @@ def ok(self): else: expire_datetime = None - - for line_number in range(len(self.host_list)): service = self.service_list[line_number] host = self.host_list[line_number] @@ -6969,7 +7008,8 @@ def check(self): for server in enabled_servers: for download_server, download_url in AppInfo.DOWNLOAD_SERVERS.items(): # dummy message just in case version check does not work - message = 'Cannot reach version check at <a href={0}>{0}</<a>.'.format(f'https://{download_server}{AppInfo.VERSION_PATH}') + message = 'Cannot reach version check at <a href={0}>{0}</<a>.'.format( + f'https://{download_server}{AppInfo.VERSION_PATH}') # retrieve VERSION_URL without auth information response = server.FetchURL(f'https://{download_server}{AppInfo.VERSION_PATH}', giveback='raw', From 5c23a3221973d858487b6bcbe5d38883ffd6fd4d Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 2 May 2022 22:14:29 +0200 Subject: [PATCH 228/884] started Qt version selector --- Nagstamon/QUI/__init__.py | 21 +++++------ Nagstamon/QUI/qt.py | 79 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 11 deletions(-) create mode 100644 Nagstamon/QUI/qt.py diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 93b007d90..2288eb28b 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -31,10 +31,11 @@ import traceback from urllib.parse import quote -from PyQt5.QtCore import pyqtSignal, \ +from .qt import pyqtSignal, \ pyqtSlot, \ QAbstractTableModel, \ QByteArray, \ + QBrush, \ QDateTime, \ QModelIndex, \ QObject, \ @@ -45,8 +46,7 @@ QTimer, \ QUrl, \ QVariant, \ - QXmlStreamReader -from PyQt5.QtGui import QBrush, \ + QXmlStreamReader, \ QColor, \ QCursor, \ QFont, \ @@ -55,14 +55,13 @@ QKeySequence, \ QPainter, \ QPalette, \ - QPixmap -from PyQt5.QtMultimedia import QMediaContent, \ + QPixmap, \ + QMediaContent, \ QMediaPlayer, \ - QMediaPlaylist -from PyQt5.QtSvg import QSvgRenderer, \ - QSvgWidget - -from PyQt5.QtWidgets import QAbstractItemView, \ + QMediaPlaylist, \ + QSvgRenderer, \ + QSvgWidget, \ + QAbstractItemView, \ QAction, \ QApplication, \ QColorDialog, \ @@ -79,7 +78,7 @@ QLabel, \ QPushButton, \ QScrollArea, \ - QSizePolicy,\ + QSizePolicy, \ QSpacerItem, \ QToolButton, \ QTreeView, \ diff --git a/Nagstamon/QUI/qt.py b/Nagstamon/QUI/qt.py new file mode 100644 index 000000000..5bbdefa7c --- /dev/null +++ b/Nagstamon/QUI/qt.py @@ -0,0 +1,79 @@ +# Nagstamon - Nagios status monitor for your desktop +# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +# Select Qt version based on installation found +# Prefer in this order: Pyside6 - PyQt6 - PyQt5 + +import sys + +if 'PyQt5' in sys.modules: + from PyQt5.QtCore import pyqtSignal, \ + pyqtSlot, \ + QAbstractTableModel, \ + QByteArray, \ + QDateTime, \ + QModelIndex, \ + QObject, \ + QPoint, \ + QSignalMapper, \ + Qt, \ + QThread, \ + QTimer, \ + QUrl, \ + QVariant, \ + QXmlStreamReader + from PyQt5.QtGui import QBrush, \ + QColor, \ + QCursor, \ + QFont, \ + QFontDatabase, \ + QIcon, \ + QKeySequence, \ + QPainter, \ + QPalette, \ + QPixmap + from PyQt5.QtMultimedia import QMediaContent, \ + QMediaPlayer, \ + QMediaPlaylist + from PyQt5.QtSvg import QSvgRenderer, \ + QSvgWidget + + from PyQt5.QtWidgets import QAbstractItemView, \ + QAction, \ + QApplication, \ + QColorDialog, \ + QComboBox, \ + QDialog, \ + QFileDialog, \ + QFontDialog, \ + QHBoxLayout, \ + QHeaderView, \ + QListWidgetItem, \ + QMenu, \ + QMenuBar, \ + QMessageBox, \ + QLabel, \ + QPushButton, \ + QScrollArea, \ + QSizePolicy, \ + QSpacerItem, \ + QToolButton, \ + QTreeView, \ + QStyle, \ + QSystemTrayIcon, \ + QVBoxLayout, \ + QWidget \ No newline at end of file From 92b135f232a883af8c1457864908ff5bec7099ad Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 2 May 2022 23:23:11 +0200 Subject: [PATCH 229/884] switch qt versions --- Nagstamon/QUI/__init__.py | 449 +++++++++++++++++---------------- Nagstamon/QUI/qt.py | 150 ++++++++++- Nagstamon/QUI/settings_main.py | 2 +- build/requirements/linux.txt | 2 + nagstamon.py | 2 +- 5 files changed, 369 insertions(+), 236 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 2288eb28b..d546842be 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -31,8 +31,8 @@ import traceback from urllib.parse import quote -from .qt import pyqtSignal, \ - pyqtSlot, \ +from .qt import Signal, \ + Slot, \ QAbstractTableModel, \ QByteArray, \ QBrush, \ @@ -45,7 +45,6 @@ QThread, \ QTimer, \ QUrl, \ - QVariant, \ QXmlStreamReader, \ QColor, \ QCursor, \ @@ -82,6 +81,7 @@ QSpacerItem, \ QToolButton, \ QTreeView, \ + QT_VERSION_MAJOR, \ QStyle, \ QSystemTrayIcon, \ QVBoxLayout, \ @@ -147,7 +147,7 @@ try: from dbus import (Interface, SessionBus) - from dbus.mainloop.pyqt5 import DBusQtMainLoop + from dbus.mainloop.glib import DBusQtMainLoop # flag to check later if DBus is available DBUS_AVAILABLE = True @@ -164,12 +164,14 @@ except ImportError: ECP_AVAILABLE = False -# enable HighDPI-awareness to avoid https://github.com/HenriWahl/Nagstamon/issues/618 -try: - QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True) -except AttributeError: - pass -QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True) +# since Qt6 HighDPI-awareness is default behaviour +if QT_VERSION_MAJOR < 6: + # enable HighDPI-awareness to avoid https://github.com/HenriWahl/Nagstamon/issues/618 + try: + QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True) + except AttributeError: + pass + QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True) # global application instance APP = QApplication(sys.argv) @@ -206,7 +208,8 @@ QBRUSHES = {0: {}, 1: {}} # dummy QVariant as empty return value for model data() -DUMMY_QVARIANT = QVariant() +#DUMMY_QVARIANT = QVariant() +DUMMY_QVARIANT = 'QVariant' # headers for tablewidgets HEADERS = OrderedDict([('host', {'header': 'Host', @@ -366,8 +369,8 @@ class SystemTrayIcon(QSystemTrayIcon): For some dark, very dark reason systray menu does NOT work in Windows if run on commandline as nagstamon.py - the binary .exe works """ - show_popwin = pyqtSignal() - hide_popwin = pyqtSignal() + show_popwin = Signal() + hide_popwin = Signal() # flag for displaying error icon in case of error error_shown = False @@ -416,7 +419,7 @@ def currentIconName(self): return '<none>' return str(curIcon) - @pyqtSlot(QMenu) + @Slot(QMenu) def set_menu(self, menu): """ create current menu for right clicks @@ -429,7 +432,7 @@ def set_menu(self, menu): if OS != OS_DARWIN: self.setContextMenu(self.menu) - @pyqtSlot() + @Slot() def create_icons(self): """ create icons from template, applying colors @@ -473,7 +476,7 @@ def create_icons(self): debug_queue.append( 'DEBUG: SystemTrayIcon created icon {} for state "{}"'.format(self.icons[state], state)) - @pyqtSlot(QSystemTrayIcon.ActivationReason) + @Slot(QSystemTrayIcon.ActivationReason) def icon_clicked(self, reason): """ evaluate mouse click @@ -494,7 +497,7 @@ def icon_clicked(self, reason): else: self.show_popwin.emit() - @pyqtSlot() + @Slot() def show_state(self): """ get worst status and display it in systray @@ -508,7 +511,7 @@ def show_state(self): else: self.setIcon(self.icons['ERROR']) - @pyqtSlot() + @Slot() def flash(self): """ send color inversion signal to labels @@ -527,7 +530,7 @@ def flash(self): # fire up a singleshot to reset color soon self.timer.singleShot(500, self.reset) - @pyqtSlot() + @Slot() def reset(self): """ tell labels to set original colors @@ -546,11 +549,11 @@ def reset(self): self.setIcon(self.current_icon) self.current_icon = None - @pyqtSlot() + @Slot() def set_error(self): self.error_shown = True - @pyqtSlot() + @Slot() def reset_error(self): self.error_shown = False @@ -562,12 +565,12 @@ class MenuAtCursor(QMenu): # flag to avoid too fast popping up menus available = True - is_shown = pyqtSignal(bool) + is_shown = Signal(bool) def __init__(self, parent=None): QMenu.__init__(self, parent=parent) - @pyqtSlot() + @Slot() def show_at_cursor(self): """ pop up at mouse pointer position, lock itself to avoid permamently popping menus on Windows @@ -578,7 +581,7 @@ def show_at_cursor(self): # tell the world that the menu will be shown self.is_shown.emit(True) # show menu - self.exec_(QPoint(x, y)) + self.exec(QPoint(x, y)) # tell world that menu will be closed self.is_shown.emit(False) del (x, y) @@ -589,7 +592,7 @@ class MenuContext(MenuAtCursor): class for universal context menu, used at systray icon and hamburger menu """ - menu_ready = pyqtSignal(QMenu) + menu_ready = Signal(QMenu) def __init__(self, parent=None): MenuAtCursor.__init__(self, parent=parent) @@ -610,7 +613,7 @@ def __init__(self, parent=None): self.initialize() - @pyqtSlot() + @Slot() def initialize(self): """ add actions and servers to menu @@ -743,7 +746,7 @@ class PushButton_Hamburger(Button): Pushbutton with menu for hamburger """ - pressed = pyqtSignal() + pressed = Signal() def __init__(self): # ##QPushButton.__init__(self) @@ -754,7 +757,7 @@ def mousePressEvent(self, event): self.pressed.emit() self.showMenu() - @pyqtSlot(QMenu) + @Slot(QMenu) def set_menu(self, menu): self.setMenu(menu) @@ -770,7 +773,7 @@ def __init__(self, text='', parent=None, server=None, url_type=''): self.server = server self.url_type = url_type - @pyqtSlot() + @Slot() def open_url(self): """ open URL from BROWSER_URLS in webbrowser @@ -795,7 +798,7 @@ class ComboBox_Servers(QComboBox): """ combobox which does lock statuswindow so it does not close when opening combobox """ - monitor_opened = pyqtSignal() + monitor_opened = Signal() # flag to avoid silly focusOutEvent freshly_opened = False @@ -819,7 +822,7 @@ def fill(self): self.addItem('Go to monitor...') self.addItems(sorted(conf.servers.keys(), key=str.lower)) - @pyqtSlot() + @Slot() def response(self): """ respnose to activated item in servers combobox @@ -839,14 +842,14 @@ class DraggableWidget(QWidget): Used to give various toparea and statusbar widgets draggability """ # yell if statusbar is moved - window_moved = pyqtSignal() + window_moved = Signal() # needed for popup after hover - mouse_entered = pyqtSignal() + mouse_entered = Signal() # needed for popup after click - mouse_pressed = pyqtSignal() - mouse_released = pyqtSignal() + mouse_pressed = Signal() + mouse_released = Signal() # keep state of right button pressed to avoid dragging and # unwanted repositioning of statuswindow @@ -854,7 +857,7 @@ class DraggableWidget(QWidget): # Maybe due to the later mixin usage, but somehow the pyqtSlot decorator is ignored here when used by NagstamonLogo # and DraggableLabel - # @pyqtSlot(QMenu) + # @Slot(QMenu) def set_menu(self, menu): self.menu = menu @@ -948,14 +951,14 @@ class DraggableLabel(QLabel, DraggableWidget): label with dragging capabilities used by toparea """ # yell if statusbar is moved - window_moved = pyqtSignal() + window_moved = Signal() # needed for popup after hover - mouse_entered = pyqtSignal() + mouse_entered = Signal() # needed for popup after click - mouse_pressed = pyqtSignal() - mouse_released = pyqtSignal() + mouse_pressed = Signal() + mouse_released = Signal() def __init__(self, text='', parent=None): QLabel.__init__(self, text, parent=parent) @@ -994,7 +997,7 @@ def __init__(self, text='', parent=None): self.set_color() dialogs.settings.changed.connect(self.set_color) - @pyqtSlot() + @Slot() def set_color(self): self.setStyleSheet('''padding-left: 1px; padding-right: 1px; @@ -1012,20 +1015,20 @@ class StatusWindow(QWidget): Either statusbar is shown or (toparea + scrolling area) """ # sent by .resize_window() - resizing = pyqtSignal() + resizing = Signal() # send when windows opens, e.g. for stopping notifications - showing = pyqtSignal() + showing = Signal() # send when window shrinks down to statusbar or closes - hiding = pyqtSignal() + hiding = Signal() # signal to be sent to all server workers to recheck all - recheck = pyqtSignal() + recheck = Signal() # signal to be sent to all treeview workers to clear server event history # after 'Refresh'-button has been pressed - clear_event_history = pyqtSignal() + clear_event_history = Signal() def __init__(self): """ @@ -1514,7 +1517,7 @@ def create_ServerVBoxes(self): self.sort_ServerVBoxes() - @pyqtSlot() + @Slot() def show_window_after_checking_for_clicking(self): """ being called after clicking statusbar - check if window should be showed @@ -1522,7 +1525,7 @@ def show_window_after_checking_for_clicking(self): if conf.popup_details_clicking: self.show_window() - @pyqtSlot() + @Slot() def show_window_after_checking_for_hover(self): """ being called after hovering over statusbar - check if window should be showed @@ -1530,7 +1533,7 @@ def show_window_after_checking_for_hover(self): if conf.popup_details_hover: self.show_window() - @pyqtSlot() + @Slot() def show_window_from_notification_bubble(self): """ show status window after button being clicked in notification bubble @@ -1540,7 +1543,7 @@ def show_window_from_notification_bubble(self): elif conf.icon_in_systray: self.show_window_systrayicon() - @pyqtSlot() + @Slot() def show_window_systrayicon(self): """ handle clicks onto systray icon @@ -1585,7 +1588,7 @@ def show_window_systrayicon(self): else: self.hide_window() - @pyqtSlot() + @Slot() def show_window(self, event=None): """ used to show status window when its appearance is triggered, also adjusts geometry @@ -1713,7 +1716,7 @@ def hide_window_if_not_under_mouse(self): self.hide_window() return True - @pyqtSlot() + @Slot() def update_window(self): """ redraw window content, to be effective only when window is shown @@ -1723,7 +1726,7 @@ def update_window(self): (conf.windowed and self.is_shown): self.show_window() - @pyqtSlot() + @Slot() def hide_window(self): """ hide window if not needed @@ -1767,7 +1770,7 @@ def hide_window(self): self.move(self.stored_x, self.stored_y) - @pyqtSlot() + @Slot() def correct_moving_position(self): """ correct position if moving and cursor started outside statusbar @@ -1959,14 +1962,14 @@ def resize_window(self, width, height, x, y): return True - @pyqtSlot() + @Slot() def move_timer(self): """ helper for move by QTimer.singleShot - attempt to avoid flickering on Windows """ self.move(self.move_to_x, self.move_to_y) - @pyqtSlot() + @Slot() def adjust_size(self): """ resize window if shown and needed @@ -1996,7 +1999,7 @@ def adjust_size(self): else: self.adjust_dummy_columns() - @pyqtSlot() + @Slot() def adjust_dummy_columns(self): """ calculate widest width of all server tables to hide dummy column at the widest one @@ -2022,7 +2025,7 @@ def adjust_dummy_columns(self): del (max_width, max_width_table) return True - @pyqtSlot() + @Slot() def store_position(self): """ store position for restoring it when hiding @@ -2110,7 +2113,7 @@ def store_position_to_conf(self): conf.position_width = self.width() conf.position_height = self.height() - @pyqtSlot(str, str) + @Slot(str, str) def show_message(self, msg_type, message): """ show message from other thread like MediaPlayer @@ -2122,14 +2125,14 @@ def show_message(self, msg_type, message): elif msg_type == 'information': return (QMessageBox.information(statuswindow, title, message)) - @pyqtSlot() + @Slot() def recheck_all(self): """ tell servers to recheck all hosts and services """ self.recheck.emit() - @pyqtSlot() + @Slot() def refresh(self): """ tell all enabled servers to refresh their information @@ -2145,7 +2148,7 @@ def refresh(self): # at thread counter server.thread_counter = conf.update_interval_seconds - @pyqtSlot(dict) + @Slot(dict) def desktop_notification(self, current_status_count): """ show desktop notification - must be called from same thread as DBus intialization @@ -2165,7 +2168,7 @@ def desktop_notification(self, current_status_count): except Exception: traceback.print_exc(file=sys.stdout) - @pyqtSlot() + @Slot() def raise_window_on_all_desktops(self): """ experimental workaround for floating-statusbar-only-on-one-virtual-desktop-after-a-while bug @@ -2238,7 +2241,7 @@ def close_debug_file(self): self.debug_file.close() self.debug_file = None - @pyqtSlot() + @Slot() def debug_loop(self): """ if debugging is enabled, poll debug_queue list and print/write its contents @@ -2276,15 +2279,15 @@ class Worker_Notification(QObject): """ # tell statusbar labels to flash - start_flash = pyqtSignal() - stop_flash = pyqtSignal() + start_flash = Signal() + stop_flash = Signal() # tell mediaplayer to load and play sound file - load_sound = pyqtSignal(str) - play_sound = pyqtSignal() + load_sound = Signal(str) + play_sound = Signal() # tell statuswindow to use desktop notification - desktop_notification = pyqtSignal(dict) + desktop_notification = Signal(dict) # flag about current notification state is_notifying = False @@ -2301,7 +2304,7 @@ class Worker_Notification(QObject): def __init__(self): QObject.__init__(self) - @pyqtSlot(str, str, str) + @Slot(str, str, str) def start(self, server_name, worst_status_diff, worst_status_current): """ start notification @@ -2420,7 +2423,7 @@ def start(self, server_name, worst_status_diff, worst_status_current): self.status_count = current_status_count del (current_status_count) - @pyqtSlot() + @Slot() def stop(self): """ stop notification if there is no need anymore @@ -2455,14 +2458,14 @@ class NagstamonLogo(QSvgWidget, DraggableWidget): SVG based logo, used for statusbar and toparea logos """ # yell if statusbar is moved - window_moved = pyqtSignal() + window_moved = Signal() # needed for popup after hover - mouse_entered = pyqtSignal() + mouse_entered = Signal() # needed for popup after click - mouse_pressed = pyqtSignal() - mouse_released = pyqtSignal() + mouse_pressed = Signal() + mouse_released = Signal() def __init__(self, file, width=None, height=None, parent=None): QSvgWidget.__init__(self, parent=parent) @@ -2486,11 +2489,11 @@ class StatusBar(QWidget): """ # send signal to statuswindow - resize = pyqtSignal() + resize = Signal() # needed to maintain flashing labels - labels_invert = pyqtSignal() - labels_reset = pyqtSignal() + labels_invert = Signal() + labels_reset = Signal() def __init__(self, parent=None): QWidget.__init__(self, parent=parent) @@ -2543,7 +2546,7 @@ def __init__(self, parent=None): self.adjust_size() - @pyqtSlot() + @Slot() def summarize_states(self): """ display summaries of states in statusbar @@ -2585,7 +2588,7 @@ def summarize_states(self): # tell statuswindow its size might be adjusted self.resize.emit() - @pyqtSlot() + @Slot() def flash(self): """ send color inversion signal to labels @@ -2596,7 +2599,7 @@ def flash(self): # fire up a singleshot to reset color soon self.timer.singleShot(500, self.reset) - @pyqtSlot() + @Slot() def reset(self): """ tell labels to set original colors @@ -2607,7 +2610,7 @@ def reset(self): # even later call itself to invert colors as flash self.timer.singleShot(500, self.flash) - @pyqtSlot() + @Slot() def adjust_size(self): """ apply new size of widgets, especially Nagstamon logo @@ -2636,7 +2639,7 @@ def adjust_size(self): # avoid flickerung/artefact by updating immediately self.summarize_states() - @pyqtSlot(str) + @Slot(str) def set_error(self, message): """ display error message if any error exists @@ -2644,7 +2647,7 @@ def set_error(self, message): self.label_message.setText(message) self.label_message.show() - @pyqtSlot() + @Slot() def reset_error(self): """ delete error message if there is no error @@ -2660,14 +2663,14 @@ class StatusBarLabel(DraggableLabel): """ # yell if statusbar is moved - window_moved = pyqtSignal() + window_moved = Signal() # needed for popup after hover - mouse_entered = pyqtSignal() + mouse_entered = Signal() # needed for popup after click - mouse_pressed = pyqtSignal() - mouse_released = pyqtSignal() + mouse_pressed = Signal() + mouse_released = Signal() def __init__(self, state, parent=None): DraggableLabel.__init__(self, parent=parent) @@ -2691,7 +2694,7 @@ def __init__(self, state, parent=None): # store state of label to access long state names in .summarize_states() self.state = state - @pyqtSlot() + @Slot() def invert(self): self.setStyleSheet('''padding-left: 1px; padding-right: 1px; @@ -2699,7 +2702,7 @@ def invert(self): % (conf.__dict__['color_%s_background' % (self.state.lower())], conf.__dict__['color_%s_text' % (self.state.lower())])) - @pyqtSlot() + @Slot() def reset(self): self.setStyleSheet('''padding-left: 1px; padding-right: 1px; @@ -2713,7 +2716,7 @@ class TopArea(QWidget): Top area of status window """ - mouse_entered = pyqtSignal() + mouse_entered = Signal() def __init__(self, parent=None): QWidget.__init__(self) @@ -2768,7 +2771,7 @@ def enterEvent(self, event): # unlock statuswindow if pointer touches statusbar self.mouse_entered.emit() - @pyqtSlot() + @Slot() def create_icons(self): """ create icons from template, applying colors @@ -2830,7 +2833,7 @@ class ServerStatusLabel(ClosingLabel): def __init__(self, parent=None): QLabel.__init__(self, parent=parent) - @pyqtSlot(str, str) + @Slot(str, str) def change(self, text, style=''): # store old text and stylesheet in case it needs to be reused self.text_old = self.text() @@ -2862,12 +2865,12 @@ def change(self, text, style=''): self.setText(text.split(' ')[0]) self.setToolTip(text) - @pyqtSlot() + @Slot() def reset(self): self.setStyleSheet(self.stylesheet_old) self.setText('') - @pyqtSlot() + @Slot() def restore(self): # restore text, used by recheck_all of tablewidget worker self.setStyleSheet(self.stylesheet_old) @@ -2879,13 +2882,13 @@ class ServerVBox(QVBoxLayout): one VBox per server containing buttons and hosts/services listview """ # used to update status label text like 'Connected-' - change_label_status = pyqtSignal(str, str) + change_label_status = Signal(str, str) # signal to submit server to authentication dialog - authenticate = pyqtSignal(str) + authenticate = Signal(str) - button_fix_tls_error_show = pyqtSignal() - button_fix_tls_error_hide = pyqtSignal() + button_fix_tls_error_show = Signal() + button_fix_tls_error_hide = Signal() def __init__(self, server, parent=None): QVBoxLayout.__init__(self, parent) @@ -2991,7 +2994,7 @@ def get_real_height(self): height += self.button_monitor.sizeHint().height() + 2 return height - @pyqtSlot() + @Slot() def show_all(self): """ show all items in server vbox except the table - not needed if empty @@ -3010,7 +3013,7 @@ def show_all(self): self.table.show() self.table.is_shown = True - @pyqtSlot() + @Slot() def show_only_header(self): """ show all items in server vbox except the table - not needed if empty or major connection problem @@ -3029,7 +3032,7 @@ def show_only_header(self): self.table.hide() self.table.is_shown = False - @pyqtSlot() + @Slot() def hide_all(self): """ hide all items in server vbox @@ -3049,7 +3052,7 @@ def hide_all(self): self.table.hide() self.table.is_shown = False - @pyqtSlot() + @Slot() def delete(self): """ delete VBox and its children @@ -3086,7 +3089,7 @@ def authenticate_server(self): """ self.authenticate.emit(self.server.name) - @pyqtSlot() + @Slot() def update_label(self): self.label.setText('<big><b> {0}@{1}</b></big>'.format(self.server.username, self.server.name)) # let label padding keep top and bottom space - apparently not necessary on OSX @@ -3094,7 +3097,7 @@ def update_label(self): self.label.setStyleSheet('''padding-top: {0}px; padding-bottom: {0}px;'''.format(SPACE)) - @pyqtSlot() + @Slot() def fix_tls_error(self): """ call dialogs.server.edit() with server name and showing extra options @@ -3109,7 +3112,7 @@ class Model(QAbstractTableModel): Model for storing status data to be presented in Treeview-table """ - model_data_array_filled = pyqtSignal() + model_data_array_filled = Signal() # list of lists for storage of status data data_array = list() @@ -3125,8 +3128,8 @@ class Model(QAbstractTableModel): dummy_qmodelindex = QModelIndex() # tell treeview if flags columns should be hidden or not - hosts_flags_column_needed = pyqtSignal(bool) - services_flags_column_needed = pyqtSignal(bool) + hosts_flags_column_needed = Signal(bool) + services_flags_column_needed = Signal(bool) def __init__(self, server, parent=None): QAbstractTableModel.__init__(self, parent=parent) @@ -3152,8 +3155,8 @@ def headerData(self, column, orientation, role): if role == Qt.DisplayRole: return (HEADERS_HEADERS[column]) - @pyqtSlot(list, dict) - # @pyqtSlot(list) + @Slot(list, dict) + # @Slot(list) def fill_data_array(self, data_array, info): """ fill data_array for model @@ -3226,23 +3229,23 @@ class TreeView(QTreeView): """ # tell global window that it should be resized - ready_to_resize = pyqtSignal() + ready_to_resize = Signal() # sent by refresh() for statusbar - refreshed = pyqtSignal() + refreshed = Signal() # tell worker to get status after a recheck has been solicited - recheck = pyqtSignal(dict) + recheck = Signal(dict) # tell notification that status of server has changed - status_changed = pyqtSignal(str, str, str) + status_changed = Signal(str, str, str) # action to be executed by worker # 2 values: action and host/service info - request_action = pyqtSignal(dict, dict) + request_action = Signal(dict, dict) # tell worker it should sort columns after someone pressed the column header - sort_data_array_for_columns = pyqtSignal(int, int, bool) + sort_data_array_for_columns = Signal(int, int, bool) def __init__(self, columncount, rowcount, sort_column, sort_order, server, parent=None): QTreeView.__init__(self, parent=parent) @@ -3384,14 +3387,14 @@ def __init__(self, columncount, rowcount, sort_column, sort_order, server, paren # connect signal for handling copy from keyboard - @pyqtSlot() + @Slot() def set_font(self): """ change font if it has been changed by settings """ self.setFont(FONT) - @pyqtSlot(bool) + @Slot(bool) def show_hosts_flags_column(self, value): """ show hosts flags column if needed @@ -3399,7 +3402,7 @@ def show_hosts_flags_column(self, value): """ self.setColumnHidden(1, not value) - @pyqtSlot(bool) + @Slot(bool) def show_services_flags_column(self, value): """ show service flags column if needed @@ -3431,7 +3434,7 @@ def get_real_width(self): width += self.columnWidth(column) return (width) - @pyqtSlot() + @Slot() def adjust_table(self): """ adjust table dimensions after filling it @@ -3520,7 +3523,7 @@ def keyPressEvent(self, event): return super(TreeView, self).keyPressEvent(event) - @pyqtSlot() + @Slot() def cell_clicked(self): """ Windows reacts differently to clicks into table cells than Linux and MacOSX @@ -3701,7 +3704,7 @@ def cell_clicked(self): else: self.action_menu.available = True - @pyqtSlot(str) + @Slot(str) def action_menu_custom_response(self, action): # avoid blocked context menu self.action_menu.available = True @@ -3752,7 +3755,7 @@ def action_menu_custom_response(self, action): # clean up del list_rows - @pyqtSlot() + @Slot() def action_response_decorator(method): """ decorate repeatedly called stuff @@ -3915,7 +3918,7 @@ def action_submit(self): service=miserable_service) dialogs.submit.show() - @pyqtSlot() + @Slot() def action_clipboard_action_host(self): """ copy host name to clipboard @@ -3940,7 +3943,7 @@ def action_clipboard_action_host(self): clipboard.setText(text) - @pyqtSlot() + @Slot() def action_clipboard_action_service(self): """ copy service name to clipboard @@ -3965,7 +3968,7 @@ def action_clipboard_action_service(self): clipboard.setText(text) - @pyqtSlot() + @Slot() def action_clipboard_action_statusinformation(self): """ copy status information to clipboard @@ -3989,7 +3992,7 @@ def action_clipboard_action_statusinformation(self): clipboard.setText(text) - @pyqtSlot() + @Slot() def action_clipboard_action_all(self): """ @@ -4033,7 +4036,7 @@ def action_clipboard_action_all(self): # copy text to clipboard clipboard.setText(text) - @pyqtSlot() + @Slot() def refresh(self): """ refresh status display @@ -4061,7 +4064,7 @@ def refresh(self): self.status_changed.emit(self.server.name, self.server.worst_status_diff, self.server.worst_status_current) - @pyqtSlot(int, Qt.SortOrder) + @Slot(int, Qt.SortOrder) def sort_columns(self, sort_column, sort_order): """ forward sorting task to worker @@ -4070,7 +4073,7 @@ def sort_columns(self, sort_column, sort_order): # intransmissible self.sort_data_array_for_columns.emit(int(sort_column), int(sort_order), True) - @pyqtSlot() + @Slot() def finish_worker_thread(self): """ attempt to shutdown thread cleanly @@ -4090,44 +4093,44 @@ class Worker(QObject): """ # send signal if monitor server has new status data - new_status = pyqtSignal() + new_status = Signal() # send signal if next cell can be filled - next_cell = pyqtSignal(int, int, str, str, str, list, str) + next_cell = Signal(int, int, str, str, str, list, str) # send signal if all cells are filled and table can be adjusted - table_ready = pyqtSignal() + table_ready = Signal() # send signal if ready to stop - finish = pyqtSignal() + finish = Signal() # send start and end of downtime - set_start_end = pyqtSignal(str, str) + set_start_end = Signal(str, str) # try to stop thread by evaluating this flag running = True # signal to be sent to slot "change" of ServerStatusLabel - change_label_status = pyqtSignal(str, str) + change_label_status = Signal(str, str) # signal to be sent to slot "restore" of ServerStatusLabel - restore_label_status = pyqtSignal() + restore_label_status = Signal() # send notification a stop message if problems vanished without being noticed - problems_vanished = pyqtSignal() + problems_vanished = Signal() # flag to keep recheck_all from being started more than once rechecking_all = False # signals to control error message in statusbar - show_error = pyqtSignal(str) - hide_error = pyqtSignal() + show_error = Signal(str) + hide_error = Signal() # sent to treeview with new data_array - worker_data_array_filled = pyqtSignal(list, dict) + worker_data_array_filled = Signal(list, dict) # sendt to treeview if data has been sorted by click on column header - data_array_sorted = pyqtSignal(list, dict) + data_array_sorted = Signal(list, dict) # keep track of last sorting column and order to pre-sort by it # start with sorting by host @@ -4148,7 +4151,7 @@ def __init__(self, parent=None, server=None, sort_column=0, sort_order=0): self.sort_column = sort_column self.sort_order = sort_order - @pyqtSlot() + @Slot() def get_status(self): """ check every second if thread still has to run @@ -4238,7 +4241,7 @@ def get_status(self): # tell treeview to finish worker_thread self.finish.emit() - @pyqtSlot(int, int) + @Slot(int, int) def fill_data_array(self, sort_column, sort_order): """ let worker do the dirty job of filling the array @@ -4300,7 +4303,7 @@ def fill_data_array(self, sort_column, sort_order): # give sorted data to model self.worker_data_array_filled.emit(self.data_array, self.info) - @pyqtSlot(int, int, bool) + @Slot(int, int, bool) def sort_data_array(self, sort_column, sort_order, header_clicked=False): """ sort list of lists in data_array depending on sort criteria @@ -4345,7 +4348,7 @@ def sort_data_array(self, sort_column, sort_order, header_clicked=False): self.last_sort_column_cached = self.sort_column - @pyqtSlot(dict) + @Slot(dict) def acknowledge(self, info_dict): """ slot waiting for 'acknowledge' signal from ok button from acknowledge dialog @@ -4358,7 +4361,7 @@ def acknowledge(self, info_dict): # pass dictionary to server's acknowledge machinery self.server.set_acknowledge(info_dict) - @pyqtSlot(dict) + @Slot(dict) def downtime(self, info_dict): """ slot waiting for 'downtime' signal from ok button from downtime dialog @@ -4371,7 +4374,7 @@ def downtime(self, info_dict): # pass dictionary to server's downtime machinery self.server.set_downtime(info_dict) - @pyqtSlot(dict) + @Slot(dict) def submit(self, info_dict): """ slot waiting for 'submit' signal from ok button from submit dialog @@ -4384,7 +4387,7 @@ def submit(self, info_dict): # pass dictionary to server's downtime machinery self.server.set_submit_check_result(info_dict) - @pyqtSlot(dict) + @Slot(dict) def recheck(self, info_dict): """ Slot to start server recheck method, getting signal from TableWidget context menu @@ -4401,7 +4404,7 @@ def recheck(self, info_dict): # call server recheck method self.server.set_recheck(info_dict) - @pyqtSlot() + @Slot() def recheck_all(self): """ call server.set_recheck for every single host/service @@ -4445,7 +4448,7 @@ def recheck_all(self): if conf.debug_mode: self.server.Debug(server=self.server.name, debug='Already rechecking all') - @pyqtSlot(str, str) + @Slot(str, str) def get_start_end(self, server_name, host): """ Investigates start and end time of a downtime asynchronously @@ -4456,7 +4459,7 @@ def get_start_end(self, server_name, host): # send start/end time to slot self.set_start_end.emit(start, end) - @pyqtSlot(dict, dict) + @Slot(dict, dict) def execute_action(self, action, info): """ runs action, may it be custom or included like the Checkmk Multisite actions @@ -4555,13 +4558,13 @@ def _URLify(self, string): """ return quote(string, ":/=?&@+") - @pyqtSlot() + @Slot() def unfresh_event_history(self): # set all flagged-as-fresh-events to un-fresh for event in self.server.events_history.keys(): self.server.events_history[event] = False - # @pyqtSlot(bool) + # @Slot(bool) # def track_action_menu(self, action_menu_shown): # self.action_menu_shown = action_menu_shown @@ -4630,7 +4633,7 @@ class Dialog(QObject): one single dialog """ # send signal e.g. to statuswindow if dialog pops up - show_dialog = pyqtSignal() + show_dialog = Signal() # dummy toggle dependencies TOGGLE_DEPS = {} @@ -4709,7 +4712,7 @@ def toggle_visibility(self, checkbox, widgets=[]): for widget in widgets: widget.hide() - @pyqtSlot(str) + @Slot(str) def toggle(self, checkbox): """ change state of depending widgets, slot for signals from checkboxes in UI @@ -4745,12 +4748,12 @@ def fill_list(self, listwidget, config): listitem.setForeground(self.GRAY) listwidget.addItem(listitem) - @pyqtSlot() + @Slot() def ok(self): # dummy OK treatment pass - @pyqtSlot() + @Slot() def cancel(self): """ as default closes dialog - might be refined, for example by settings dialog @@ -4764,13 +4767,13 @@ class for settings dialog """ # signal to be fired if OK button was clicked and new setting are applied - changed = pyqtSignal() + changed = Signal() # send signal if check for new version is wanted - check_for_new_version = pyqtSignal(bool, QWidget) + check_for_new_version = Signal(bool, QWidget) # used to tell debug loop it should start - start_debug_loop = pyqtSignal() + start_debug_loop = Signal() def __init__(self, dialog): Dialog.__init__(self, dialog) @@ -5104,9 +5107,9 @@ def show(self, tab=0): # reset window if only needs smaller screen estate self.window.adjustSize() - self.window.exec_() + self.window.exec() - @pyqtSlot() + @Slot() def show_new_server(self): """ opens settings and new server dialogs - used by dialogs.server_missing @@ -5114,14 +5117,14 @@ def show_new_server(self): # emulate button click self.ui.button_new_server.clicked.emit() - @pyqtSlot() + @Slot() def show_filters(self): """ opens filters settings after clicking button_filters in toparea """ self.show(tab=2) - @pyqtSlot() + @Slot() def show_defaults(self): """ opens default settings after clicking button in acknowledge/downtime dialog @@ -5249,7 +5252,7 @@ def ok(self): # see if there are any servers created and enabled check_servers() - @pyqtSlot() + @Slot() def cancel(self): """ check if there are any usable servers configured @@ -5257,28 +5260,28 @@ def cancel(self): self.window.close() check_servers() - @pyqtSlot() + @Slot() def new_server(self): """ create new server """ dialogs.server.new() - @pyqtSlot() + @Slot() def edit_server(self): """ edit existing server """ dialogs.server.edit() - @pyqtSlot() + @Slot() def copy_server(self): """ copy existing server """ dialogs.server.copy() - @pyqtSlot() + @Slot() def delete_server(self): """ delete server, stop its thread, remove from config and list @@ -5341,28 +5344,28 @@ def refresh_list(self, list_widget, list_conf, current=''): # activate currently created/edited server monitor item by first searching it in the list list_widget.setCurrentItem(list_widget.findItems(current, Qt.MatchExactly)[0]) - @pyqtSlot() + @Slot() def new_action(self): """ create new action """ dialogs.action.new() - @pyqtSlot() + @Slot() def edit_action(self): """ edit existing action """ dialogs.action.edit() - @pyqtSlot() + @Slot() def copy_action(self): """ copy existing action and edit it """ dialogs.action.copy() - @pyqtSlot() + @Slot() def delete_action(self): """ delete action remove from config and list @@ -5426,17 +5429,17 @@ def decoration_function(self): return (decoration_function) @choose_sound_file_decoration - @pyqtSlot() + @Slot() def choose_sound_file_warning(self): self.sound_file_type = 'warning' @choose_sound_file_decoration - @pyqtSlot() + @Slot() def choose_sound_file_critical(self): self.sound_file_type = 'critical' @choose_sound_file_decoration - @pyqtSlot() + @Slot() def choose_sound_file_down(self): self.sound_file_type = 'down' @@ -5461,17 +5464,17 @@ def decoration_function(self): return (decoration_function) @play_sound_file_decoration - @pyqtSlot() + @Slot() def play_sound_file_warning(self): self.sound_file_type = 'warning' @play_sound_file_decoration - @pyqtSlot() + @Slot() def play_sound_file_critical(self): self.sound_file_type = 'critical' @play_sound_file_decoration - @pyqtSlot() + @Slot() def play_sound_file_down(self): self.sound_file_type = 'down' @@ -5493,7 +5496,7 @@ def paint_colors(self): (conf.__dict__['color_%s_text' % (status)], (conf.__dict__['color_%s_background' % (status)]))) - @pyqtSlot() + @Slot() def colors_defaults(self): """ apply default colors to buttons @@ -5521,7 +5524,7 @@ def colors_defaults(self): self.ui.__dict__[label].setStyleSheet('color: %s; background: %s' % (color_text, color_background)) - @pyqtSlot(str) + @Slot(str) def color_chooser(self, item): """ open QColorDialog to choose a color and change it in settings dialog @@ -5575,7 +5578,7 @@ def paint_color_alternation(self): padding-bottom: 3px; '''.format(text, background)) - @pyqtSlot(int) + @Slot(int) def change_color_alternation(self, value): """ fill alternation level 1 labels with altered color @@ -5623,14 +5626,14 @@ def change_color_alternation(self, value): padding-bottom: 3px; '''.format(text, r, g, b)) - @pyqtSlot() + @Slot() def change_color_alternation_by_value(self): """ to be fired up when colors are reset """ self.change_color_alternation(self.ui.input_slider_grid_alternation_intensity.value()) - @pyqtSlot() + @Slot() def font_chooser(self): """ use font dialog to choose a font @@ -5638,7 +5641,7 @@ def font_chooser(self): self.font = QFontDialog.getFont(self.font, parent=self.window)[0] self.ui.label_font.setFont(self.font) - @pyqtSlot() + @Slot() def font_default(self): """ reset font to default font which was valid when Nagstamon was launched @@ -5646,14 +5649,14 @@ def font_default(self): self.ui.label_font.setFont(DEFAULT_FONT) self.font = DEFAULT_FONT - @pyqtSlot() + @Slot() def button_check_for_new_version_clicked(self): """ at this point start_mode for version check is definitively False """ self.check_for_new_version.emit(False, self.window) - @pyqtSlot() + @Slot() def choose_browser_executable(self): """ show dialog for selection of non-default browser @@ -5677,7 +5680,7 @@ def choose_browser_executable(self): if file != '': self.ui.input_lineedit_custom_browser.setText(file) - @pyqtSlot() + @Slot() def toggle_zabbix_widgets(self): """ Depending on the existence of an enabled Zabbix monitor the Zabbix widgets are shown or hidden @@ -5703,7 +5706,7 @@ def toggle_zabbix_widgets(self): for widget in self.ZABBIX_COLOR_INTENSITY_LABELS: widget.hide() - @pyqtSlot() + @Slot() def toggle_op5monitor_widgets(self): """ Depending on the existence of an enabled Op5Monitor monitor the Op5Monitor widgets are shown or hidden @@ -5721,7 +5724,7 @@ def toggle_op5monitor_widgets(self): for widget in self.OP5MONITOR_WIDGETS: widget.hide() - @pyqtSlot() + @Slot() def toggle_expire_time_widgets(self): """ Depending on the existence of an enabled IcingaWeb2 or Alertmanager monitor the expire_time widgets are shown or hidden @@ -5739,7 +5742,7 @@ def toggle_expire_time_widgets(self): for widget in self.EXPIRE_TIME_WIDGETS: widget.hide() - @pyqtSlot() + @Slot() def toggle_systray_icon_offset(self): """ Only show offset spinbox when offset is enabled @@ -5762,7 +5765,7 @@ class Dialog_Server(Dialog): """ # tell server has been edited - edited = pyqtSignal() + edited = Signal() def __init__(self, dialog): Dialog.__init__(self, dialog) @@ -5867,7 +5870,7 @@ def __init__(self, dialog): # mode needed for evaluate dialog after ok button pressed - defaults to 'new' self.mode = 'new' - @pyqtSlot(int) + @Slot(int) def toggle_type(self, server_type_index=0): # server_type_index is not needed - we get the server type from .currentText() # check if server type is listed in volatile widgets to decide if it has to be shown or hidden @@ -5877,7 +5880,7 @@ def toggle_type(self, server_type_index=0): else: widget.hide() - @pyqtSlot() + @Slot() def toggle_authentication(self): """ when authentication is changed to Kerberos then disable username/password as the are now useless @@ -5956,7 +5959,7 @@ def decoration_function(self, **kwargs): self.window.adjustSize() # self.window.show() - self.window.exec_() + self.window.exec() # give back decorated function return (decoration_function) @@ -6118,7 +6121,7 @@ def ok(self): # store server settings conf.SaveMultipleConfig('servers', 'server') - @pyqtSlot() + @Slot() def choose_custom_cert_ca_file(self): """ show dialog for selection of non-default browser @@ -6132,11 +6135,11 @@ def choose_custom_cert_ca_file(self): if file != '': self.ui.input_lineedit_custom_cert_ca_file.setText(file) - @pyqtSlot() + @Slot() def checkmk_view_hosts_reset(self): self.ui.input_lineedit_checkmk_view_hosts.setText('nagstamon_hosts') - @pyqtSlot() + @Slot() def checkmk_view_services_reset(self): self.ui.input_lineedit_checkmk_view_services.setText('nagstamon_svc') @@ -6225,7 +6228,7 @@ def decoration_function(self): self.window.adjustSize() # self.window.show() - self.window.exec_() + self.window.exec() # give back decorated function return (decoration_function) @@ -6340,7 +6343,7 @@ class Dialog_Acknowledge(Dialog): host_list = service_list = [] # tell worker to acknowledge some troublesome item - acknowledge = pyqtSignal(dict) + acknowledge = Signal(dict) def __init__(self, dialog): Dialog.__init__(self, dialog) @@ -6468,10 +6471,10 @@ class Dialog_Downtime(Dialog): """ # send signal to get start and end of a downtime asynchronously - get_start_end = pyqtSignal(str, str) + get_start_end = Signal(str, str) # signal to tell worker to commit downtime - downtime = pyqtSignal(dict) + downtime = Signal(dict) # store host and service to be used for OK button evaluation server = None @@ -6550,7 +6553,7 @@ def ok(self): 'hours': int(self.ui.input_spinbox_duration_hours.value()), 'minutes': int(self.ui.input_spinbox_duration_minutes.value())}) - pyqtSlot(str, str) + Slot(str, str) def set_start_end(self, start, end): """ @@ -6559,7 +6562,7 @@ def set_start_end(self, start, end): self.ui.input_lineedit_start_time.setText(start) self.ui.input_lineedit_end_time.setText(end) - pyqtSlot() + Slot() def set_type_fixed(self): """ @@ -6571,7 +6574,7 @@ def set_type_fixed(self): self.ui.input_spinbox_duration_hours.hide() self.ui.input_spinbox_duration_minutes.hide() - pyqtSlot() + Slot() def set_type_flexible(self): """ @@ -6592,7 +6595,7 @@ class Dialog_Submit(Dialog): server = None host = service = '' - submit = pyqtSignal(dict) + submit = Signal(dict) def __init__(self, dialog): Dialog.__init__(self, dialog) @@ -6669,7 +6672,7 @@ class Dialog_Authentication(Dialog): server = None # signal for telling server_vbox label to update - update = pyqtSignal(str) + update = Signal(str) def __init__(self, dialog): Dialog.__init__(self, dialog) @@ -6702,7 +6705,7 @@ def initialize(self): self.ui.input_lineedit_password.setText(self.server.password) self.ui.input_checkbox_save_password.setChecked(conf.servers[self.server.name].save_password) - @pyqtSlot(str) + @Slot(str) def show_auth_dialog(self, server): """ initialize and show authentication dialog @@ -6713,7 +6716,7 @@ def show_auth_dialog(self, server): if not statuswindow is None: statuswindow.hide_window() self.window.adjustSize() - self.window.exec_() + self.window.exec() def ok(self): """ @@ -6752,7 +6755,7 @@ def ok(self): # update server_vbox label self.update.emit(self.server.name) - @pyqtSlot() + @Slot() def toggle_autologin(self): """ toolge autologin option for Centreon @@ -6850,7 +6853,7 @@ def __init__(self, dialog): self.ui.tabs.setCurrentIndex(0) def show(self): - self.window.exec_() + self.window.exec() class MediaPlayer(QObject): @@ -6858,7 +6861,7 @@ class MediaPlayer(QObject): play media files for notification """ # needed to show error in a thread-safe way - send_message = pyqtSignal(str, str) + send_message = Signal(str, str) def __init__(self): QObject.__init__(self) @@ -6874,7 +6877,7 @@ def __init__(self): statuswindow.worker_notification.load_sound.connect(self.set_media) statuswindow.worker_notification.play_sound.connect(self.play) - @pyqtSlot(str) + @Slot(str) def set_media(self, media_file): """ Give media_file to player and if it is one of the default files check first if still exists @@ -6896,7 +6899,7 @@ def set_media(self, media_file): self.send_message.emit('warning', 'Sound file <b>\'{0}\'</b> not found for playback.'.format(media_file)) return False - @pyqtSlot() + @Slot() def play(self): # just play sound self.player.play() @@ -6909,9 +6912,9 @@ class CheckVersion(QObject): is_checking = False - version_info_retrieved = pyqtSignal() + version_info_retrieved = Signal() - @pyqtSlot(bool, QWidget) + @Slot(bool, QWidget) def check(self, start_mode=False, parent=None): if self.is_checking is False: @@ -6948,14 +6951,14 @@ def check(self, start_mode=False, parent=None): self.worker_thread.started.connect(self.worker.check) self.worker_thread.start(0) - @pyqtSlot() + @Slot() def reset_checking(self): """ reset checking flag to avoid QThread crashes """ self.is_checking = False - @pyqtSlot(str) + @Slot(str) def show_message(self, message): """ message dialog must be shown from GUI thread @@ -6985,9 +6988,9 @@ class Worker(QObject): check for new version in background """ # send signal if some version information is available - ready = pyqtSignal(str) + ready = Signal(str) - finished = pyqtSignal() + finished = Signal() def __init__(self): QObject.__init__(self) @@ -7049,7 +7052,7 @@ class DBus(QObject): Create connection to DBus for desktop notification for Linux/Unix """ - open_statuswindow = pyqtSignal() + open_statuswindow = Signal() # random ID needed because otherwise all instances of Nagstamon # will get commands by clicking on notification bubble via DBUS @@ -7188,7 +7191,7 @@ def get_screen_geometry(screen_number): return desktop.screenGeometry(0) -@pyqtSlot() +@Slot() def exit(): """ stop all child threads before quitting instance diff --git a/Nagstamon/QUI/qt.py b/Nagstamon/QUI/qt.py index 5bbdefa7c..17dbae5f9 100644 --- a/Nagstamon/QUI/qt.py +++ b/Nagstamon/QUI/qt.py @@ -20,9 +20,21 @@ import sys -if 'PyQt5' in sys.modules: - from PyQt5.QtCore import pyqtSignal, \ - pyqtSlot, \ +try: + from PySide6 import __version__ as QT_VERSION_STR +except ImportError: + try: + from PyQt6.QtCore import PYQT_VERSION_STR as QT_VERSION_STR + except ImportError: + try: + from PyQt6.QtCore import PYQT_VERSION_STR as QT_VERSION_STR + except ImportError: + sys.exit('Qt is missing') + + +if 'PySide6' in sys.modules: + from PySide6.QtCore import Signal, \ + Slot, \ QAbstractTableModel, \ QByteArray, \ QDateTime, \ @@ -34,9 +46,9 @@ QThread, \ QTimer, \ QUrl, \ - QVariant, \ QXmlStreamReader - from PyQt5.QtGui import QBrush, \ + from PySide6.QtGui import QAction, \ + QBrush, \ QColor, \ QCursor, \ QFont, \ @@ -46,14 +58,12 @@ QPainter, \ QPalette, \ QPixmap - from PyQt5.QtMultimedia import QMediaContent, \ + from PySide6.QtMultimedia import QMediaContent, \ QMediaPlayer, \ QMediaPlaylist - from PyQt5.QtSvg import QSvgRenderer, \ + from PySide6.QtSvg import QSvgRenderer, \ QSvgWidget - - from PyQt5.QtWidgets import QAbstractItemView, \ - QAction, \ + from PySide6.QtWidgets import QAbstractItemView, \ QApplication, \ QColorDialog, \ QComboBox, \ @@ -76,4 +86,122 @@ QStyle, \ QSystemTrayIcon, \ QVBoxLayout, \ - QWidget \ No newline at end of file + QWidget +# elif 'PyQt6' in sys.modules: +# # if 'PyQt6' in sys.modules: +# # PySide/PyQt compatibility +# from PyQt6.QtCore import pyqtSignal as Signal, \ +# pyqtSlot as Slot, \ +# PYQT_VERSION_STR as QT_VERSION_STR, \ +# QAbstractTableModel, \ +# QByteArray, \ +# QDateTime, \ +# QModelIndex, \ +# QObject, \ +# QPoint, \ +# QSignalMapper, \ +# Qt, \ +# QThread, \ +# QTimer, \ +# QUrl, \ +# QXmlStreamReader +# from PyQt6.QtGui import QAction, \ +# QBrush, \ +# QColor, \ +# QCursor, \ +# QFont, \ +# QFontDatabase, \ +# QIcon, \ +# QKeySequence, \ +# QPainter, \ +# QPalette, \ +# QPixmap +# from PyQt6.QtMultimedia import QMediaContent, \ +# QMediaPlayer, \ +# QMediaPlaylist +# from PyQt6.QtSvg import QSvgRenderer, \ +# QSvgWidget +# from PyQt6.QtWidgets import QAbstractItemView, \ +# QApplication, \ +# QColorDialog, \ +# QComboBox, \ +# QDialog, \ +# QFileDialog, \ +# QFontDialog, \ +# QHBoxLayout, \ +# QHeaderView, \ +# QListWidgetItem, \ +# QMenu, \ +# QMenuBar, \ +# QMessageBox, \ +# QLabel, \ +# QPushButton, \ +# QScrollArea, \ +# QSizePolicy, \ +# QSpacerItem, \ +# QToolButton, \ +# QTreeView, \ +# QStyle, \ +# QSystemTrayIcon, \ +# QVBoxLayout, \ +# QWidget +# elif 'PyQt5' in sys.modules: +# from PyQt5.QtCore import pyqtSignal, \ +# pyqtSlot, \ +# PYQT_VERSION_STR as QT_VERSION_STR, \ +# QAbstractTableModel, \ +# QByteArray, \ +# QDateTime, \ +# QModelIndex, \ +# QObject, \ +# QPoint, \ +# QSignalMapper, \ +# Qt, \ +# QThread, \ +# QTimer, \ +# QUrl, \ +# QXmlStreamReader +# from PyQt5.QtGui import QBrush, \ +# QColor, \ +# QCursor, \ +# QFont, \ +# QFontDatabase, \ +# QIcon, \ +# QKeySequence, \ +# QPainter, \ +# QPalette, \ +# QPixmap +# from PyQt5.QtMultimedia import QMediaContent, \ +# QMediaPlayer, \ +# QMediaPlaylist +# from PyQt5.QtSvg import QSvgRenderer, \ +# QSvgWidget +# +# from PyQt5.QtWidgets import QAbstractItemView, \ +# QAction, \ +# QApplication, \ +# QColorDialog, \ +# QComboBox, \ +# QDialog, \ +# QFileDialog, \ +# QFontDialog, \ +# QHBoxLayout, \ +# QHeaderView, \ +# QListWidgetItem, \ +# QMenu, \ +# QMenuBar, \ +# QMessageBox, \ +# QLabel, \ +# QPushButton, \ +# QScrollArea, \ +# QSizePolicy, \ +# QSpacerItem, \ +# QToolButton, \ +# QTreeView, \ +# QStyle, \ +# QSystemTrayIcon, \ +# QVBoxLayout, \ +# QWidget + +QT_VERSION_MAJOR, QT_VERSION_MINOR, QT_VERSION_BUGFIX = [int(x) for x in QT_VERSION_STR.split('.')] +print(QT_VERSION_MAJOR, QT_VERSION_MINOR, QT_VERSION_BUGFIX) \ No newline at end of file diff --git a/Nagstamon/QUI/settings_main.py b/Nagstamon/QUI/settings_main.py index bfd7f9d26..5208a31e1 100644 --- a/Nagstamon/QUI/settings_main.py +++ b/Nagstamon/QUI/settings_main.py @@ -1655,4 +1655,4 @@ def retranslateUi(self, settings_main): ui = Ui_settings_main() ui.setupUi(settings_main) settings_main.show() - sys.exit(app.exec_()) + sys.exit(app.exec()) diff --git a/build/requirements/linux.txt b/build/requirements/linux.txt index c573aec97..61af4cb7f 100644 --- a/build/requirements/linux.txt +++ b/build/requirements/linux.txt @@ -3,6 +3,8 @@ keyring lxml psutil pyqt5 +pyqt6 +pyside6 pysocks python-dateutil requests diff --git a/nagstamon.py b/nagstamon.py index 4cfb71cd4..f5130fbc0 100755 --- a/nagstamon.py +++ b/nagstamon.py @@ -58,7 +58,7 @@ if conf.check_for_new_version is True: check_version.check(start_mode=True, parent=statuswindow) - APP.exec_() + APP.exec() del(APP) sys.exit(0) From 44c8793aff0b8ef88606fb5a5c6ed653af8edf31 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 3 May 2022 23:12:33 +0200 Subject: [PATCH 230/884] QVARIANT --- Nagstamon/QUI/__init__.py | 84 ++------------ Nagstamon/QUI/qt.py | 234 +++++++++++++++++++------------------- 2 files changed, 128 insertions(+), 190 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index d546842be..9d1bdfe7e 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -31,61 +31,8 @@ import traceback from urllib.parse import quote -from .qt import Signal, \ - Slot, \ - QAbstractTableModel, \ - QByteArray, \ - QBrush, \ - QDateTime, \ - QModelIndex, \ - QObject, \ - QPoint, \ - QSignalMapper, \ - Qt, \ - QThread, \ - QTimer, \ - QUrl, \ - QXmlStreamReader, \ - QColor, \ - QCursor, \ - QFont, \ - QFontDatabase, \ - QIcon, \ - QKeySequence, \ - QPainter, \ - QPalette, \ - QPixmap, \ - QMediaContent, \ - QMediaPlayer, \ - QMediaPlaylist, \ - QSvgRenderer, \ - QSvgWidget, \ - QAbstractItemView, \ - QAction, \ - QApplication, \ - QColorDialog, \ - QComboBox, \ - QDialog, \ - QFileDialog, \ - QFontDialog, \ - QHBoxLayout, \ - QHeaderView, \ - QListWidgetItem, \ - QMenu, \ - QMenuBar, \ - QMessageBox, \ - QLabel, \ - QPushButton, \ - QScrollArea, \ - QSizePolicy, \ - QSpacerItem, \ - QToolButton, \ - QTreeView, \ - QT_VERSION_MAJOR, \ - QStyle, \ - QSystemTrayIcon, \ - QVBoxLayout, \ - QWidget +# for details of imports look into qt.py +from .qt import * from Nagstamon.Config import (Action, AppInfo, @@ -208,7 +155,6 @@ QBRUSHES = {0: {}, 1: {}} # dummy QVariant as empty return value for model data() -#DUMMY_QVARIANT = QVariant() DUMMY_QVARIANT = 'QVariant' # headers for tablewidgets @@ -3121,9 +3067,6 @@ class Model(QAbstractTableModel): row_count = 0 column_count = len(HEADERS_HEADERS) - # do not need to create everytime a new QVariant() object - dummy_return_qvariant = QVariant() - # dummy QModelIndex for dataChanged signal dummy_qmodelindex = QModelIndex() @@ -3188,39 +3131,36 @@ def data(self, index, role): overridden method for data delivery for treeview """ if role == Qt.DisplayRole: - return (self.data_array[index.row()][index.column()]) - # return(self.server.data[index.row()][index.column()]) + return self.data_array[index.row()][index.column()] elif role == Qt.ForegroundRole: - # return(self.data_array[index.row()][COLOR_INDEX['text'][index.column()]]) - return (self.data_array[index.row()][10]) + return(self.data_array[index.row()][10] elif role == Qt.BackgroundRole: # return(self.data_array[index.row()][COLOR_INDEX['background'][index.column()]]) - return (self.data_array[index.row()][11]) + return self.data_array[index.row()][11] elif role == Qt.FontRole: if index.column() == 1: - return (ICONS_FONT) + return ICONS_FONT elif index.column() == 3: - return (ICONS_FONT) + return ICONS_FONT else: - return (DUMMY_QVARIANT) - + return DUMMY_QVARIANT # provide icons via Qt.UserRole elif role == Qt.UserRole: # depending on host or service column return host or service icon list - return (self.data_array[index.row()][7 + index.column()]) + return self.data_array[index.row()][7 + index.column()] elif role == Qt.ToolTipRole: # only if tooltips are wanted show them, combining host + service + status_info if conf.show_tooltips: - return ('''<div style=white-space:pre;margin:3px;><b>{0}: {1}</b></div> + return '''<div style=white-space:pre;margin:3px;><b>{0}: {1}</b></div> {2}'''.format(self.data_array[index.row()][0], self.data_array[index.row()][2], - self.data_array[index.row()][8])) + self.data_array[index.row()][8]) else: - return (DUMMY_QVARIANT) + return DUMMY_QVARIANT class TreeView(QTreeView): diff --git a/Nagstamon/QUI/qt.py b/Nagstamon/QUI/qt.py index 17dbae5f9..c8075139c 100644 --- a/Nagstamon/QUI/qt.py +++ b/Nagstamon/QUI/qt.py @@ -31,8 +31,64 @@ except ImportError: sys.exit('Qt is missing') - -if 'PySide6' in sys.modules: +# as log as Qt6 does not work prefer PyQt5 +if 'PyQt5' in sys.modules: + from PyQt5.QtCore import pyqtSignal as Signal, \ + pyqtSlot as Slot, \ + PYQT_VERSION_STR as QT_VERSION_STR, \ + QAbstractTableModel, \ + QByteArray, \ + QDateTime, \ + QModelIndex, \ + QObject, \ + QPoint, \ + QSignalMapper, \ + Qt, \ + QThread, \ + QTimer, \ + QUrl, \ + QXmlStreamReader + from PyQt5.QtGui import QBrush, \ + QColor, \ + QCursor, \ + QFont, \ + QFontDatabase, \ + QIcon, \ + QKeySequence, \ + QPainter, \ + QPalette, \ + QPixmap + from PyQt5.QtMultimedia import QMediaContent, \ + QMediaPlayer, \ + QMediaPlaylist + from PyQt5.QtSvg import QSvgRenderer, \ + QSvgWidget + from PyQt5.QtWidgets import QAbstractItemView, \ + QAction, \ + QApplication, \ + QColorDialog, \ + QComboBox, \ + QDialog, \ + QFileDialog, \ + QFontDialog, \ + QHBoxLayout, \ + QHeaderView, \ + QListWidgetItem, \ + QMenu, \ + QMenuBar, \ + QMessageBox, \ + QLabel, \ + QPushButton, \ + QScrollArea, \ + QSizePolicy, \ + QSpacerItem, \ + QToolButton, \ + QTreeView, \ + QStyle, \ + QSystemTrayIcon, \ + QVBoxLayout, \ + QWidget +elif 'PySide6' in sys.modules: from PySide6.QtCore import Signal, \ Slot, \ QAbstractTableModel, \ @@ -87,121 +143,63 @@ QSystemTrayIcon, \ QVBoxLayout, \ QWidget -# elif 'PyQt6' in sys.modules: -# # if 'PyQt6' in sys.modules: -# # PySide/PyQt compatibility -# from PyQt6.QtCore import pyqtSignal as Signal, \ -# pyqtSlot as Slot, \ -# PYQT_VERSION_STR as QT_VERSION_STR, \ -# QAbstractTableModel, \ -# QByteArray, \ -# QDateTime, \ -# QModelIndex, \ -# QObject, \ -# QPoint, \ -# QSignalMapper, \ -# Qt, \ -# QThread, \ -# QTimer, \ -# QUrl, \ -# QXmlStreamReader -# from PyQt6.QtGui import QAction, \ -# QBrush, \ -# QColor, \ -# QCursor, \ -# QFont, \ -# QFontDatabase, \ -# QIcon, \ -# QKeySequence, \ -# QPainter, \ -# QPalette, \ -# QPixmap -# from PyQt6.QtMultimedia import QMediaContent, \ -# QMediaPlayer, \ -# QMediaPlaylist -# from PyQt6.QtSvg import QSvgRenderer, \ -# QSvgWidget -# from PyQt6.QtWidgets import QAbstractItemView, \ -# QApplication, \ -# QColorDialog, \ -# QComboBox, \ -# QDialog, \ -# QFileDialog, \ -# QFontDialog, \ -# QHBoxLayout, \ -# QHeaderView, \ -# QListWidgetItem, \ -# QMenu, \ -# QMenuBar, \ -# QMessageBox, \ -# QLabel, \ -# QPushButton, \ -# QScrollArea, \ -# QSizePolicy, \ -# QSpacerItem, \ -# QToolButton, \ -# QTreeView, \ -# QStyle, \ -# QSystemTrayIcon, \ -# QVBoxLayout, \ -# QWidget -# elif 'PyQt5' in sys.modules: -# from PyQt5.QtCore import pyqtSignal, \ -# pyqtSlot, \ -# PYQT_VERSION_STR as QT_VERSION_STR, \ -# QAbstractTableModel, \ -# QByteArray, \ -# QDateTime, \ -# QModelIndex, \ -# QObject, \ -# QPoint, \ -# QSignalMapper, \ -# Qt, \ -# QThread, \ -# QTimer, \ -# QUrl, \ -# QXmlStreamReader -# from PyQt5.QtGui import QBrush, \ -# QColor, \ -# QCursor, \ -# QFont, \ -# QFontDatabase, \ -# QIcon, \ -# QKeySequence, \ -# QPainter, \ -# QPalette, \ -# QPixmap -# from PyQt5.QtMultimedia import QMediaContent, \ -# QMediaPlayer, \ -# QMediaPlaylist -# from PyQt5.QtSvg import QSvgRenderer, \ -# QSvgWidget -# -# from PyQt5.QtWidgets import QAbstractItemView, \ -# QAction, \ -# QApplication, \ -# QColorDialog, \ -# QComboBox, \ -# QDialog, \ -# QFileDialog, \ -# QFontDialog, \ -# QHBoxLayout, \ -# QHeaderView, \ -# QListWidgetItem, \ -# QMenu, \ -# QMenuBar, \ -# QMessageBox, \ -# QLabel, \ -# QPushButton, \ -# QScrollArea, \ -# QSizePolicy, \ -# QSpacerItem, \ -# QToolButton, \ -# QTreeView, \ -# QStyle, \ -# QSystemTrayIcon, \ -# QVBoxLayout, \ -# QWidget +elif 'PyQt6' in sys.modules: + # PySide/PyQt compatibility + from PyQt6.QtCore import pyqtSignal as Signal, \ + pyqtSlot as Slot, \ + PYQT_VERSION_STR as QT_VERSION_STR, \ + QAbstractTableModel, \ + QByteArray, \ + QDateTime, \ + QModelIndex, \ + QObject, \ + QPoint, \ + QSignalMapper, \ + Qt, \ + QThread, \ + QTimer, \ + QUrl, \ + QXmlStreamReader + from PyQt6.QtGui import QAction, \ + QBrush, \ + QColor, \ + QCursor, \ + QFont, \ + QFontDatabase, \ + QIcon, \ + QKeySequence, \ + QPainter, \ + QPalette, \ + QPixmap + from PyQt6.QtMultimedia import QMediaContent, \ + QMediaPlayer, \ + QMediaPlaylist + from PyQt6.QtSvg import QSvgRenderer, \ + QSvgWidget + from PyQt6.QtWidgets import QAbstractItemView, \ + QApplication, \ + QColorDialog, \ + QComboBox, \ + QDialog, \ + QFileDialog, \ + QFontDialog, \ + QHBoxLayout, \ + QHeaderView, \ + QListWidgetItem, \ + QMenu, \ + QMenuBar, \ + QMessageBox, \ + QLabel, \ + QPushButton, \ + QScrollArea, \ + QSizePolicy, \ + QSpacerItem, \ + QToolButton, \ + QTreeView, \ + QStyle, \ + QSystemTrayIcon, \ + QVBoxLayout, \ + QWidget +# get int-ed version parts QT_VERSION_MAJOR, QT_VERSION_MINOR, QT_VERSION_BUGFIX = [int(x) for x in QT_VERSION_STR.split('.')] -print(QT_VERSION_MAJOR, QT_VERSION_MINOR, QT_VERSION_BUGFIX) \ No newline at end of file From 52ad1f36b1f217463dd14991e699554775009a40 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 3 May 2022 23:14:42 +0200 Subject: [PATCH 231/884] 3.9-20220503 --- Nagstamon/Config.py | 2 +- Nagstamon/QUI/__init__.py | 2 +- build/debian/changelog | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 459cc3ace..754d2f524 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20220422' + VERSION = '3.9-20220503' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 9d1bdfe7e..a2f177d6b 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -3134,7 +3134,7 @@ def data(self, index, role): return self.data_array[index.row()][index.column()] elif role == Qt.ForegroundRole: - return(self.data_array[index.row()][10] + return self.data_array[index.row()][10] elif role == Qt.BackgroundRole: # return(self.data_array[index.row()][COLOR_INDEX['background'][index.column()]]) diff --git a/build/debian/changelog b/build/debian/changelog index b762a17b3..220825891 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.9-20220422) unstable; urgency=low +nagstamon (3.9-20220503) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Wed, 08 Dec 2021 08:00:00 +0100 From bc527b07b66466dccf30ef991e5b1582f27f9f2e Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 3 May 2022 23:15:13 +0200 Subject: [PATCH 232/884] 3.9-20220503 --- build/requirements/linux.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/build/requirements/linux.txt b/build/requirements/linux.txt index 61af4cb7f..c573aec97 100644 --- a/build/requirements/linux.txt +++ b/build/requirements/linux.txt @@ -3,8 +3,6 @@ keyring lxml psutil pyqt5 -pyqt6 -pyside6 pysocks python-dateutil requests From b7739b519af167d87034115af463dba9ecae3a4b Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 3 May 2022 23:23:15 +0200 Subject: [PATCH 233/884] PyQt5 --- Nagstamon/QUI/__init__.py | 3 +++ Nagstamon/QUI/qt.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index a2f177d6b..1f0b9d486 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -6808,6 +6808,9 @@ def __init__(self): self.player = QMediaPlayer(parent=self) self.player.setVolume(100) + + # Qt6: https://doc-snapshots.qt.io/qt6-dev/qmediaplayer.html#setSource + self.playlist = QMediaPlaylist() self.player.setPlaylist(self.playlist) diff --git a/Nagstamon/QUI/qt.py b/Nagstamon/QUI/qt.py index c8075139c..08e7095e3 100644 --- a/Nagstamon/QUI/qt.py +++ b/Nagstamon/QUI/qt.py @@ -27,7 +27,7 @@ from PyQt6.QtCore import PYQT_VERSION_STR as QT_VERSION_STR except ImportError: try: - from PyQt6.QtCore import PYQT_VERSION_STR as QT_VERSION_STR + from PyQt5.QtCore import PYQT_VERSION_STR as QT_VERSION_STR except ImportError: sys.exit('Qt is missing') From 577ce390269ef7a4f1a098f24a6b2e505338c548 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 3 May 2022 23:34:32 +0200 Subject: [PATCH 234/884] removed fedora-32 support --- .github/workflows/build-release-latest.yml | 17 ++--------------- .github/workflows/build-release-stable.yml | 16 ++-------------- 2 files changed, 4 insertions(+), 29 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 3b8f116b8..d57b191c2 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -50,19 +50,6 @@ jobs: retention-days: 1 if-no-files-found: error - fedora-32: - runs-on: ubuntu-latest - needs: test - steps: - - uses: actions/checkout@v2 - - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon - - uses: actions/upload-artifact@v2 - with: - path: build/*.rpm - retention-days: 1 - if-no-files-found: error - fedora-33: runs-on: ubuntu-latest needs: test @@ -167,7 +154,7 @@ jobs: repo-fedora: runs-on: ubuntu-latest - needs: [debian, fedora-32, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] + needs: [debian, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] steps: # get binaries created by other jobs - uses: actions/download-artifact@v2 @@ -189,7 +176,7 @@ jobs: github-release: runs-on: ubuntu-latest - needs: [debian, fedora-32, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] + needs: [debian, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] steps: - uses: actions/download-artifact@v2 - run: cd artifact && md5sum *agstamon* > md5sums.txt diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index 842d5318e..4f4a89e66 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -48,18 +48,6 @@ jobs: path: build/*.deb retention-days: 1 - fedora-32: - runs-on: ubuntu-latest - needs: test - steps: - - uses: actions/checkout@v2 - - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon - - uses: actions/upload-artifact@v2 - with: - path: build/*.rpm - retention-days: 1 - fedora-33: runs-on: ubuntu-latest needs: test @@ -152,7 +140,7 @@ jobs: repo-fedora: runs-on: ubuntu-latest - needs: [debian, fedora-32, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] + needs: [debian, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] steps: # get binaries created by other jobs - uses: actions/download-artifact@v2 @@ -171,7 +159,7 @@ jobs: upload-release: runs-on: ubuntu-latest - needs: [debian, fedora-32, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] + needs: [debian, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] steps: - uses: actions/download-artifact@v2 - run: cd artifact && md5sum *agstamon* > md5sums.txt From 7db3e621c6e96cf8ab4fe9b6865d2591eeff5ca8 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 7 May 2022 17:05:12 +0200 Subject: [PATCH 235/884] PyQt6 loads --- Nagstamon/QUI/__init__.py | 21 +++-- Nagstamon/QUI/qt.py | 170 ++++++++++++++++++++------------------ 2 files changed, 102 insertions(+), 89 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 1f0b9d486..aeea51cde 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -34,6 +34,8 @@ # for details of imports look into qt.py from .qt import * +print(QT_VERSION_MAJOR) + from Nagstamon.Config import (Action, AppInfo, BOOLPOOL, @@ -66,7 +68,7 @@ SORT_COLUMNS_FUNCTIONS) # dialogs -from Nagstamon.QUI.settings_main import Ui_settings_main +#from Nagstamon.QUI.settings_main import Ui_settings_main from Nagstamon.QUI.settings_server import Ui_settings_server from Nagstamon.QUI.settings_action import Ui_settings_action from Nagstamon.QUI.dialog_acknowledge import Ui_dialog_acknowledge @@ -229,11 +231,10 @@ FONT = DEFAULT_FONT # add nagstamon.ttf with icons to fonts -FONTDATABASE = QFontDatabase() -FONTDATABASE.addApplicationFont('{0}{1}nagstamon.ttf'.format(RESOURCES, os.sep)) +QFontDatabase.addApplicationFont('{0}{1}nagstamon.ttf'.format(RESOURCES, os.sep)) # always stay in normal weight without any italic -ICONS_FONT = QFont('Nagstamon', FONT.pointSize() + 2, QFont.Normal, False) +ICONS_FONT = QFont('Nagstamon', FONT.pointSize() + 2, QFont.Weight.Normal, False) # completely silly but no other rescue for Windows-hides-statusbar-after-display-mode-change problem NUMBER_OF_DISPLAY_CHANGES = 0 @@ -250,7 +251,7 @@ # ## # WINDOW_FLAGS = Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool | Qt.BypassWindowManagerHint # ##else: # ## WINDOW_FLAGS = Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint | Qt.Tool -WINDOW_FLAGS = Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint | Qt.Tool +WINDOW_FLAGS = Qt.WindowType.WindowStaysOnTopHint | Qt.WindowType.FramelessWindowHint | Qt.WindowType.Tool # icon for dialogs ICON = QIcon('{0}{1}nagstamon.ico'.format(RESOURCES, os.sep)) @@ -981,7 +982,8 @@ def __init__(self): Status window combined from status bar and popup window """ # attempt with desktop as parent for window Qt.Tool - QWidget.__init__(self, parent=APP.desktop()) + #QWidget.__init__(self, parent=APP.desktop()) + QWidget.__init__(self) # immediately hide to avoid flicker on Windows and OSX self.hide() @@ -4517,6 +4519,7 @@ class for accessing all dialogs def __init__(self): # settings main dialog self.settings = Dialog_Settings(Ui_settings_main) + #self.settings = Dialog_Settings('settings_main') self.settings.initialize() # server settings dialog @@ -4584,13 +4587,14 @@ class Dialog(QObject): # names of widgets and their defaults WIDGET_NAMES = {} # style stuff used by settings dialog for servers/actions listwidget - GRAY = QBrush(Qt.gray) + GRAY = QBrush(Qt.GlobalColor.gray) def __init__(self, dialog): QObject.__init__(self) self.window = QDialog() self.ui = dialog() + self.ui.setupUi(self.window) # explicitly set window flags to avoid '?' button on Windows @@ -7199,7 +7203,8 @@ def is_modifier_pressed(): check_version = CheckVersion() # access to various desktop parameters -desktop = APP.desktop() +#desktop = APP.desktop() +desktop = QScreen # access to clipboard clipboard = APP.clipboard() diff --git a/Nagstamon/QUI/qt.py b/Nagstamon/QUI/qt.py index 08e7095e3..1afb74893 100644 --- a/Nagstamon/QUI/qt.py +++ b/Nagstamon/QUI/qt.py @@ -20,20 +20,20 @@ import sys +# Enough to handle with differences between PyQt5 + PyQt6, so PySide6 will be +# ignored right now +# by the little import the appropriate PyQt version will be loaded try: - from PySide6 import __version__ as QT_VERSION_STR + from PyQt6.QtCore import PYQT_VERSION_STR as QT_VERSION_STR except ImportError: try: - from PyQt6.QtCore import PYQT_VERSION_STR as QT_VERSION_STR + from PyQt5.QtCore import PYQT_VERSION_STR as QT_VERSION_STR except ImportError: - try: - from PyQt5.QtCore import PYQT_VERSION_STR as QT_VERSION_STR - except ImportError: - sys.exit('Qt is missing') + sys.exit('Qt is missing') -# as log as Qt6 does not work prefer PyQt5 -if 'PyQt5' in sys.modules: - from PyQt5.QtCore import pyqtSignal as Signal, \ +if 'PyQt6' in sys.modules: + # PySide/PyQt compatibility + from PyQt6.QtCore import pyqtSignal as Signal, \ pyqtSlot as Slot, \ PYQT_VERSION_STR as QT_VERSION_STR, \ QAbstractTableModel, \ @@ -48,62 +48,7 @@ QTimer, \ QUrl, \ QXmlStreamReader - from PyQt5.QtGui import QBrush, \ - QColor, \ - QCursor, \ - QFont, \ - QFontDatabase, \ - QIcon, \ - QKeySequence, \ - QPainter, \ - QPalette, \ - QPixmap - from PyQt5.QtMultimedia import QMediaContent, \ - QMediaPlayer, \ - QMediaPlaylist - from PyQt5.QtSvg import QSvgRenderer, \ - QSvgWidget - from PyQt5.QtWidgets import QAbstractItemView, \ - QAction, \ - QApplication, \ - QColorDialog, \ - QComboBox, \ - QDialog, \ - QFileDialog, \ - QFontDialog, \ - QHBoxLayout, \ - QHeaderView, \ - QListWidgetItem, \ - QMenu, \ - QMenuBar, \ - QMessageBox, \ - QLabel, \ - QPushButton, \ - QScrollArea, \ - QSizePolicy, \ - QSpacerItem, \ - QToolButton, \ - QTreeView, \ - QStyle, \ - QSystemTrayIcon, \ - QVBoxLayout, \ - QWidget -elif 'PySide6' in sys.modules: - from PySide6.QtCore import Signal, \ - Slot, \ - QAbstractTableModel, \ - QByteArray, \ - QDateTime, \ - QModelIndex, \ - QObject, \ - QPoint, \ - QSignalMapper, \ - Qt, \ - QThread, \ - QTimer, \ - QUrl, \ - QXmlStreamReader - from PySide6.QtGui import QAction, \ + from PyQt6.QtGui import QAction, \ QBrush, \ QColor, \ QCursor, \ @@ -113,13 +58,13 @@ QKeySequence, \ QPainter, \ QPalette, \ - QPixmap - from PySide6.QtMultimedia import QMediaContent, \ - QMediaPlayer, \ - QMediaPlaylist - from PySide6.QtSvg import QSvgRenderer, \ - QSvgWidget - from PySide6.QtWidgets import QAbstractItemView, \ + QPixmap, \ + QScreen + from PyQt6.QtMultimedia import QAudioOutput, \ + QMediaPlayer + from PyQt6.QtSvg import QSvgRenderer + from PyQt6.QtSvgWidgets import QSvgWidget + from PyQt6.QtWidgets import QAbstractItemView, \ QApplication, \ QColorDialog, \ QComboBox, \ @@ -143,9 +88,11 @@ QSystemTrayIcon, \ QVBoxLayout, \ QWidget -elif 'PyQt6' in sys.modules: - # PySide/PyQt compatibility - from PyQt6.QtCore import pyqtSignal as Signal, \ + # for later decision which differences have to be considered + QT_FLAVOR = 'PyQt6' + +elif 'PyQt5' in sys.modules: + from PyQt5.QtCore import pyqtSignal as Signal, \ pyqtSlot as Slot, \ PYQT_VERSION_STR as QT_VERSION_STR, \ QAbstractTableModel, \ @@ -160,8 +107,7 @@ QTimer, \ QUrl, \ QXmlStreamReader - from PyQt6.QtGui import QAction, \ - QBrush, \ + from PyQt5.QtGui import QBrush, \ QColor, \ QCursor, \ QFont, \ @@ -170,13 +116,15 @@ QKeySequence, \ QPainter, \ QPalette, \ - QPixmap - from PyQt6.QtMultimedia import QMediaContent, \ + QPixmap, \ + QScreen + from PyQt5.QtMultimedia import QMediaContent, \ QMediaPlayer, \ QMediaPlaylist - from PyQt6.QtSvg import QSvgRenderer, \ + from PyQt5.QtSvg import QSvgRenderer, \ QSvgWidget - from PyQt6.QtWidgets import QAbstractItemView, \ + from PyQt5.QtWidgets import QAbstractItemView, \ + QAction, \ QApplication, \ QColorDialog, \ QComboBox, \ @@ -200,6 +148,66 @@ QSystemTrayIcon, \ QVBoxLayout, \ QWidget + # for later decision which differences have to be considered + QT_FLAVOR = 'PyQt5' + +# elif 'PySide6' in sys.modules: +# from PySide6.QtCore import Signal, \ +# Slot, \ +# QAbstractTableModel, \ +# QByteArray, \ +# QDateTime, \ +# QModelIndex, \ +# QObject, \ +# QPoint, \ +# QSignalMapper, \ +# Qt, \ +# QThread, \ +# QTimer, \ +# QUrl, \ +# QXmlStreamReader +# from PySide6.QtGui import QAction, \ +# QBrush, \ +# QColor, \ +# QCursor, \ +# QFont, \ +# QFontDatabase, \ +# QIcon, \ +# QKeySequence, \ +# QPainter, \ +# QPalette, \ +# QPixmap, \ +# QScreen +# from PySide6.QtMultimedia import QAudioOutput, \ +# QMediaPlayer +# from PySide6.QtSvg import QSvgRenderer +# from PySide6.QtSvgWidgets import QSvgWidget +# from PySide6.QtWidgets import QAbstractItemView, \ +# QApplication, \ +# QColorDialog, \ +# QComboBox, \ +# QDialog, \ +# QFileDialog, \ +# QFontDialog, \ +# QHBoxLayout, \ +# QHeaderView, \ +# QListWidgetItem, \ +# QMenu, \ +# QMenuBar, \ +# QMessageBox, \ +# QLabel, \ +# QPushButton, \ +# QScrollArea, \ +# QSizePolicy, \ +# QSpacerItem, \ +# QToolButton, \ +# QTreeView, \ +# QStyle, \ +# QSystemTrayIcon, \ +# QVBoxLayout, \ +# QWidget +# # for later decision which differences have to be considered +# QT_FLAVOR = 'PySide6' # get int-ed version parts QT_VERSION_MAJOR, QT_VERSION_MINOR, QT_VERSION_BUGFIX = [int(x) for x in QT_VERSION_STR.split('.')] From 987f2960bf2323f6308e6d61ea37a325e96ff19e Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 7 May 2022 17:31:09 +0200 Subject: [PATCH 236/884] melt ui and window? --- Nagstamon/QUI/__init__.py | 14 +++++++------- Nagstamon/QUI/qt.py | 2 ++ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index aeea51cde..24c112c4f 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -4518,8 +4518,8 @@ class for accessing all dialogs def __init__(self): # settings main dialog - self.settings = Dialog_Settings(Ui_settings_main) - #self.settings = Dialog_Settings('settings_main') + #self.settings = Dialog_Settings(Ui_settings_main) + self.settings = Dialog_Settings('settings_main') self.settings.initialize() # server settings dialog @@ -4591,14 +4591,14 @@ class Dialog(QObject): def __init__(self, dialog): QObject.__init__(self) - self.window = QDialog() + #self.window = QDialog() - self.ui = dialog() - - self.ui.setupUi(self.window) + self.ui = uic.loadUi(f'Nagstamon/QUI/{dialog}.ui') + self.window = self.ui + #self.ui.setupUi(self.window) # explicitly set window flags to avoid '?' button on Windows - self.window.setWindowFlags(Qt.WindowCloseButtonHint) + self.window.setWindowFlags(Qt.WindowFlags.WindowCloseButtonHint) # hoping to avoid overly large dialogs self.window.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) diff --git a/Nagstamon/QUI/qt.py b/Nagstamon/QUI/qt.py index 1afb74893..8e9d5dcbc 100644 --- a/Nagstamon/QUI/qt.py +++ b/Nagstamon/QUI/qt.py @@ -88,6 +88,7 @@ QSystemTrayIcon, \ QVBoxLayout, \ QWidget + from PyQt6 import uic # for later decision which differences have to be considered QT_FLAVOR = 'PyQt6' @@ -148,6 +149,7 @@ QSystemTrayIcon, \ QVBoxLayout, \ QWidget + from PyQt5 import uic # for later decision which differences have to be considered QT_FLAVOR = 'PyQt5' From 4604d6da7f4c196aba1ab1e7a9c00e07792cb7d7 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 8 May 2022 10:56:33 +0200 Subject: [PATCH 237/884] first ui file run through --- Nagstamon/QUI/__init__.py | 31 ++++++++++++++++--------------- Nagstamon/QUI/qt.py | 9 +++------ 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 24c112c4f..ae1e4df24 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -4598,10 +4598,10 @@ def __init__(self, dialog): #self.ui.setupUi(self.window) # explicitly set window flags to avoid '?' button on Windows - self.window.setWindowFlags(Qt.WindowFlags.WindowCloseButtonHint) + self.window.setWindowFlags(Qt.WindowType.WindowCloseButtonHint) # hoping to avoid overly large dialogs - self.window.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) + self.window.setSizePolicy(QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Minimum) # set small titlebar icon self.window.setWindowIcon(ICON) @@ -4616,7 +4616,7 @@ def __init__(self, dialog): self.signalmapper_toggles = QSignalMapper() # try to get and keep focus - self.window.setWindowModality(Qt.ApplicationModal) + self.window.setWindowModality(Qt.WindowModality.ApplicationModal) def initialize(self): """ @@ -4680,7 +4680,7 @@ def toggle_toggles(self): checkbox.toggled.connect(self.window.adjustSize) # finally map signals with .sender() - [QWidget] is important! - self.signalmapper_toggles.mapped[str].connect(self.toggle) + self.signalmapper_toggles.mappedString[str].connect(self.toggle) def fill_list(self, listwidget, config): """ @@ -4857,23 +4857,23 @@ def __init__(self, dialog): # set folder and play symbols to choose and play buttons self.ui.button_choose_warning.setText('') - self.ui.button_choose_warning.setIcon(self.ui.button_play_warning.style().standardIcon(QStyle.SP_DirIcon)) + self.ui.button_choose_warning.setIcon(self.ui.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_DirIcon)) self.ui.button_play_warning.setText('') - self.ui.button_play_warning.setIcon(self.ui.button_play_warning.style().standardIcon(QStyle.SP_MediaPlay)) + self.ui.button_play_warning.setIcon(self.ui.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_MediaPlay)) self.ui.button_choose_critical.setText('') - self.ui.button_choose_critical.setIcon(self.ui.button_play_warning.style().standardIcon(QStyle.SP_DirIcon)) + self.ui.button_choose_critical.setIcon(self.ui.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_DirIcon)) self.ui.button_play_critical.setText('') - self.ui.button_play_critical.setIcon(self.ui.button_play_warning.style().standardIcon(QStyle.SP_MediaPlay)) + self.ui.button_play_critical.setIcon(self.ui.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_MediaPlay)) self.ui.button_choose_down.setText('') - self.ui.button_choose_down.setIcon(self.ui.button_play_warning.style().standardIcon(QStyle.SP_DirIcon)) + self.ui.button_choose_down.setIcon(self.ui.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_DirIcon)) self.ui.button_play_down.setText('') - self.ui.button_play_down.setIcon(self.ui.button_play_warning.style().standardIcon(QStyle.SP_MediaPlay)) + self.ui.button_play_down.setIcon(self.ui.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_MediaPlay)) # set browser file chooser icon and current custom browser path self.ui.button_choose_browser.setText('') - self.ui.button_choose_browser.setIcon(self.ui.button_play_warning.style().standardIcon(QStyle.SP_DirIcon)) + self.ui.button_choose_browser.setIcon(self.ui.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_DirIcon)) self.ui.input_lineedit_custom_browser.setText(conf.custom_browser) # connect choose browser button with file dialog self.ui.button_choose_browser.clicked.connect(self.choose_browser_executable) @@ -4903,7 +4903,7 @@ def __init__(self, dialog): self.ui.input_checkbox_grid_use_custom_intensity.clicked.connect(self.toggle_zabbix_widgets) # finally map signals with .sender() - [<type>] is important! - self.signalmapper_colors.mapped[str].connect(self.color_chooser) + self.signalmapper_colors.mappedString[str].connect(self.color_chooser) # connect slider to alternating colors self.ui.input_slider_grid_alternation_intensity.valueChanged.connect(self.change_color_alternation) @@ -5001,7 +5001,7 @@ def initialize(self): self.ui.input_combobox_default_sort_order.setCurrentText(conf.default_sort_order.title()) # fill combobox with screens for fullscreen - for display in range(desktop.screenCount()): + for display in range(len(desktop)): self.ui.input_combobox_fullscreen_display.addItem(str(display)) self.ui.input_combobox_fullscreen_display.setCurrentText(str(conf.fullscreen_display)) @@ -5545,7 +5545,7 @@ def change_color_alternation(self, value): .split(';\n')[0].split(': ')[1] # get background of level 0 label - background = label_0.palette().color(QPalette.Window) + background = label_0.palette().color(QPalette.ColorRole.Window) r, g, b, a = background.getRgb() # if label background is too dark lighten the color instead of darken it mor @@ -7204,7 +7204,8 @@ def is_modifier_pressed(): # access to various desktop parameters #desktop = APP.desktop() -desktop = QScreen +# desktop = QScreen +desktop = APP.screens() # access to clipboard clipboard = APP.clipboard() diff --git a/Nagstamon/QUI/qt.py b/Nagstamon/QUI/qt.py index 8e9d5dcbc..fc1d8b63a 100644 --- a/Nagstamon/QUI/qt.py +++ b/Nagstamon/QUI/qt.py @@ -58,8 +58,7 @@ QKeySequence, \ QPainter, \ QPalette, \ - QPixmap, \ - QScreen + QPixmap from PyQt6.QtMultimedia import QAudioOutput, \ QMediaPlayer from PyQt6.QtSvg import QSvgRenderer @@ -117,8 +116,7 @@ QKeySequence, \ QPainter, \ QPalette, \ - QPixmap, \ - QScreen + QPixmap from PyQt5.QtMultimedia import QMediaContent, \ QMediaPlayer, \ QMediaPlaylist @@ -178,8 +176,7 @@ # QKeySequence, \ # QPainter, \ # QPalette, \ -# QPixmap, \ -# QScreen +# QPixmap # from PySide6.QtMultimedia import QAudioOutput, \ # QMediaPlayer # from PySide6.QtSvg import QSvgRenderer From 057841f069fc7f97987bb73dd3252200531c1930 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 8 May 2022 11:51:22 +0200 Subject: [PATCH 238/884] first ui file run through part II --- Nagstamon/QUI/__init__.py | 84 ++++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 40 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index ae1e4df24..d7c8adc89 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -203,7 +203,7 @@ HEADERS_KEYS_HEADERS[item] = HEADERS[item]['header'] # sorting order for tablewidgets -SORT_ORDER = {'descending': 1, 'ascending': 0, 0: True, 1: False} +SORT_ORDER = {'descending': 1, 'ascending': 0, 0: Qt.SortOrder.DescendingOrder, 1: Qt.SortOrder.AscendingOrder} # bend columns 1 and 3 to 0 and 2 to avoid sorting the extra flag icons of hosts and services SORT_COLUMNS_INDEX = {0: 0, @@ -410,7 +410,7 @@ def create_icons(self): # pixmap to be painted on - arbitrarily choosen 128x128 px svg_pixmap = QPixmap(128, 128) # fill transparent backgound - svg_pixmap.fill(Qt.transparent) + svg_pixmap.fill(Qt.GlobalColor.transparent) # initiate painter which paints onto paintdevice pixmap svg_painter = QPainter(svg_pixmap) # render svg to pixmap @@ -939,8 +939,8 @@ class AllOKLabel(QLabel): def __init__(self, text='', parent=None): QLabel.__init__(self, text='OK', parent=parent) - self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) - self.setAlignment(Qt.AlignCenter) + self.setSizePolicy(QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.MinimumExpanding) + self.setAlignment(Qt.AlignmentFlag.AlignCenter) self.set_color() dialogs.settings.changed.connect(self.set_color) @@ -996,7 +996,7 @@ def __init__(self): APP.setQuitOnLastWindowClosed(False) # show tooltips even if popup window has no focus - self.setAttribute(Qt.WA_AlwaysShowToolTips) + self.setAttribute(Qt.WidgetAttribute.WA_AlwaysShowToolTips) if OS == OS_DARWIN: # avoid hiding window if it has no focus - necessary on OSX if using flag Qt.Tool @@ -1016,7 +1016,7 @@ def __init__(self): self.servers_scrollarea = QScrollArea(self) # scrollable area for server vboxes # avoid horizontal scrollbars - self.servers_scrollarea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + self.servers_scrollarea.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) self.servers_scrollarea_widget = QWidget( self.servers_scrollarea) # necessary widget to contain vbox for servers self.servers_scrollarea.hide() @@ -1118,7 +1118,7 @@ def __init__(self): self.worker_notification.moveToThread(self.worker_notification_thread) # start with priority 0 = lowest - self.worker_notification_thread.start(0) + self.worker_notification_thread.start(QThread.Priority.InheritPriority) self.create_ServerVBoxes() @@ -2419,7 +2419,7 @@ def __init__(self, file, width=None, height=None, parent=None): QSvgWidget.__init__(self, parent=parent) # either filepath or QByteArray for toparea logo self.load(file) - self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) + self.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed) # size needed for small Nagstamon logo in statusbar if width is not None and height is not None: self.setMinimumSize(width, height) @@ -2446,7 +2446,7 @@ class StatusBar(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent=parent) - self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) + self.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed) self.hbox = HBoxLayout(spacing=0, parent=parent) self.setLayout(self.hbox) @@ -2628,7 +2628,7 @@ def __init__(self, state, parent=None): % (conf.__dict__['color_%s_text' % (state.lower())], conf.__dict__['color_%s_background' % (state.lower())])) # just let labels grow as much as they need - self.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) + self.setSizePolicy(QSizePolicy.Policy.Maximum, QSizePolicy.Policy.Maximum) # hidden per default self.hide() @@ -2669,7 +2669,7 @@ class TopArea(QWidget): def __init__(self, parent=None): QWidget.__init__(self) self.hbox = HBoxLayout(spacing=SPACE, parent=self) # top HBox containing buttons - self.hbox.setSizeConstraint(QHBoxLayout.SetMinimumSize) + self.hbox.setSizeConstraint(QHBoxLayout.SizeConstraint.SetMinimumSize) self.icons = dict() self.create_icons() @@ -2678,7 +2678,7 @@ def __init__(self, parent=None): self.logo = NagstamonLogo(self.icons['nagstamon_logo_toparea'], width=150, height=42, parent=self) self.label_version = DraggableLabel(text=AppInfo.VERSION, parent=self) self.label_empty_space = DraggableLabel(text='', parent=self) - self.label_empty_space.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Ignored) + self.label_empty_space.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Ignored) self.combobox_servers = ComboBox_Servers(parent=self) self.button_filters = Button("Filters", parent=self) self.button_recheck_all = Button("Recheck all", parent=self) @@ -2726,7 +2726,7 @@ def create_icons(self): """ # get rgb values of current foreground color to be used for SVG icons (menu) - r, g, b, a = APP.palette().color(QPalette.Foreground).getRgb() + r, g, b, a = APP.palette().color(QPalette.ColorRole.Text).getRgb() for icon in 'nagstamon_logo_toparea', 'close', 'menu': # get template from file @@ -2749,7 +2749,7 @@ def create_icons(self): # pixmap to be painted on - arbitrarily choosen 128x128 px svg_pixmap = QPixmap(128, 128) # fill transparent backgound - svg_pixmap.fill(Qt.transparent) + svg_pixmap.fill(Qt.GlobalColor.transparent) # initiate painter which paints onto paintdevice pixmap svg_painter = QPainter(svg_pixmap) # render svg to pixmap @@ -2868,10 +2868,10 @@ def __init__(self, server, parent=None): # use label instead of spacer to be clickable self.label_stretcher = ClosingLabel('', parent=parent) - self.label_stretcher.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Expanding) + self.label_stretcher.setSizePolicy(QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.Expanding) self.label_status = ServerStatusLabel(parent=parent) - self.label_status.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding) + self.label_status.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Expanding) self.button_authenticate = QPushButton('Authenticate', parent=parent) @@ -3097,7 +3097,7 @@ def headerData(self, column, orientation, role): """ overridden method to get headers of columns """ - if role == Qt.DisplayRole: + if role == Qt.ItemDataRole.DisplayRole: return (HEADERS_HEADERS[column]) @Slot(list, dict) @@ -3197,8 +3197,8 @@ def __init__(self, columncount, rowcount, sort_column, sort_order, server, paren self.server = server # no handling of selection by treeview - self.setSelectionBehavior(QAbstractItemView.SelectRows) - self.setSelectionMode(QAbstractItemView.ExtendedSelection) + self.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows) + self.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection) # disable space at the left side self.setRootIsDecorated(False) @@ -3207,24 +3207,20 @@ def __init__(self, columncount, rowcount, sort_column, sort_order, server, paren self.setUniformRowHeights(True) # no scrollbars at tables because they will be scrollable by the global vertical scrollbar - self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) - self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) + self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) self.setAutoScroll(False) self.setSortingEnabled(True) - self.sortByColumn(0, Qt.AscendingOrder) + self.sortByColumn(0, Qt.SortOrder.AscendingOrder) - self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Expanding) + self.setSizePolicy(QSizePolicy.Policy.Ignored, QSizePolicy.Policy.Expanding) - self.header().setSectionResizeMode(QHeaderView.ResizeToContents) - self.header().setDefaultAlignment(Qt.AlignLeft) + self.header().setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents) self.header().setSortIndicatorShown(True) self.header().setStretchLastSection(True) - try: - self.header().setSortIndicator(sort_column, SORT_ORDER[self.sort_order]) - except Exception: - self.header().setSortIndicator(sort_column, SORT_ORDER[self.sort_order]) + self.header().setSortIndicator(sort_column, SORT_ORDER[self.sort_order]) # small method needed to tell worker which column and sort order to use self.header().sortIndicatorChanged.connect(self.sort_columns) @@ -3248,7 +3244,7 @@ def __init__(self, columncount, rowcount, sort_column, sort_order, server, paren # signalmapper for getting triggered actions self.signalmapper_action_menu = QSignalMapper() # connect menu to responder - self.signalmapper_action_menu.mapped[str].connect(self.action_menu_custom_response) + self.signalmapper_action_menu.mappedString[str].connect(self.action_menu_custom_response) # clipboard actions self.clipboard_menu = QMenu('Copy to clipboard', self) @@ -4523,19 +4519,23 @@ def __init__(self): self.settings.initialize() # server settings dialog - self.server = Dialog_Server(Ui_settings_server) + # self.server = Dialog_Server(Ui_settings_server) + self.server = Dialog_Server('settings_server') self.server.initialize() # action settings dialog - self.action = Dialog_Action(Ui_settings_action) + # self.action = Dialog_Action(Ui_settings_action) + self.action = Dialog_Action('settings_action') self.action.initialize() # acknowledge dialog for miserable item context menu - self.acknowledge = Dialog_Acknowledge(Ui_dialog_acknowledge) + # self.acknowledge = Dialog_Acknowledge(Ui_dialog_acknowledge) + self.acknowledge = Dialog_Acknowledge('dialog_acknowledge') self.acknowledge.initialize() # downtime dialog for miserable item context menu - self.downtime = Dialog_Downtime(Ui_dialog_downtime) + # self.downtime = Dialog_Downtime(Ui_dialog_downtime) + self.downtime = Dialog_Downtime('dialog_downtime') self.downtime.initialize() # open defaults settings on button click @@ -4545,22 +4545,26 @@ def __init__(self): self.acknowledge.ui.button_change_defaults_acknowledge.clicked.connect(self.acknowledge.window.close) # downtime dialog for miserable item context menu - self.submit = Dialog_Submit(Ui_dialog_submit) + #self.submit = Dialog_Submit(Ui_dialog_submit) + self.submit = Dialog_Submit('dialog_submit') self.submit.initialize() # authentication dialog for username/password - self.authentication = Dialog_Authentication(Ui_dialog_authentication) + #self.authentication = Dialog_Authentication(Ui_dialog_authentication) + self.authentication = Dialog_Authentication('dialog_authentication') self.authentication.initialize() # dialog for asking about disabled or not configured servers - self.server_missing = Dialog_Server_missing(Ui_dialog_server_missing) + #self.server_missing = Dialog_Server_missing(Ui_dialog_server_missing) + self.server_missing = Dialog_Server_missing('dialog_server_missing') self.server_missing.initialize() # open server creation dialog self.server_missing.ui.button_create_server.clicked.connect(self.settings.show_new_server) self.server_missing.ui.button_enable_server.clicked.connect(self.settings.show) # about dialog - self.about = Dialog_About(Ui_dialog_about) + #self.about = Dialog_About(Ui_dialog_about) + self.about = Dialog_About('dialog_about') # file chooser Dialog self.file_chooser = QFileDialog() @@ -5792,7 +5796,7 @@ def __init__(self, dialog): # set folder and play symbols to choose and play buttons self.ui.button_choose_custom_cert_ca_file.setText('') self.ui.button_choose_custom_cert_ca_file.setIcon( - self.ui.button_choose_custom_cert_ca_file.style().standardIcon(QStyle.SP_DirIcon)) + self.ui.button_choose_custom_cert_ca_file.style().standardIcon(QStyle.StandardPixmap.SP_DirIcon)) # connect choose custom cert CA file button with file dialog self.ui.button_choose_custom_cert_ca_file.clicked.connect(self.choose_custom_cert_ca_file) @@ -6770,7 +6774,7 @@ def __init__(self, dialog): # first add the logo on top - no idea how to achive in Qt Designer logo = QSvgWidget('{0}{1}nagstamon.svg'.format(RESOURCES, os.sep)) logo.setFixedSize(100, 100) - self.ui.vbox_about.insertWidget(1, logo, 0, Qt.AlignHCenter) + self.ui.vbox_about.insertWidget(1, logo, 0, Qt.AlignmentFlag.AlignHCenter) # update version information self.ui.label_nagstamon.setText('<h1>{0} {1}</h1>'.format(AppInfo.NAME, AppInfo.VERSION)) self.ui.label_nagstamon_long.setText('<h2>Nagios¹ status monitor for your desktop</2>') From aa99ffda1f77011ed398639d1a600c4c0dd6438b Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 8 May 2022 15:17:49 +0200 Subject: [PATCH 239/884] kind of at least runs a little bit --- Nagstamon/QUI/__init__.py | 76 +- Nagstamon/QUI/dialog_about.py | 96 - Nagstamon/QUI/dialog_acknowledge.py | 115 -- Nagstamon/QUI/dialog_authentication.py | 72 - Nagstamon/QUI/dialog_downtime.py | 99 - Nagstamon/QUI/dialog_server_missing.py | 60 - Nagstamon/QUI/dialog_submit.py | 154 -- Nagstamon/QUI/{ => files}/dialog_about.ui | 378 ++-- .../QUI/{ => files}/dialog_acknowledge.ui | 0 .../QUI/{ => files}/dialog_authentication.ui | 0 Nagstamon/QUI/{ => files}/dialog_downtime.ui | 0 .../QUI/{ => files}/dialog_server_missing.ui | 0 Nagstamon/QUI/{ => files}/dialog_submit.ui | 0 Nagstamon/QUI/{ => files}/settings_action.ui | 0 Nagstamon/QUI/{ => files}/settings_main.ui | 0 Nagstamon/QUI/{ => files}/settings_server.ui | 0 Nagstamon/QUI/settings_action.py | 261 --- Nagstamon/QUI/settings_main.py | 1658 ----------------- Nagstamon/QUI/settings_server.py | 440 ----- 19 files changed, 223 insertions(+), 3186 deletions(-) delete mode 100644 Nagstamon/QUI/dialog_about.py delete mode 100644 Nagstamon/QUI/dialog_acknowledge.py delete mode 100644 Nagstamon/QUI/dialog_authentication.py delete mode 100644 Nagstamon/QUI/dialog_downtime.py delete mode 100644 Nagstamon/QUI/dialog_server_missing.py delete mode 100644 Nagstamon/QUI/dialog_submit.py rename Nagstamon/QUI/{ => files}/dialog_about.ui (96%) rename Nagstamon/QUI/{ => files}/dialog_acknowledge.ui (100%) rename Nagstamon/QUI/{ => files}/dialog_authentication.ui (100%) rename Nagstamon/QUI/{ => files}/dialog_downtime.ui (100%) rename Nagstamon/QUI/{ => files}/dialog_server_missing.ui (100%) rename Nagstamon/QUI/{ => files}/dialog_submit.ui (100%) rename Nagstamon/QUI/{ => files}/settings_action.ui (100%) rename Nagstamon/QUI/{ => files}/settings_main.ui (100%) rename Nagstamon/QUI/{ => files}/settings_server.ui (100%) delete mode 100644 Nagstamon/QUI/settings_action.py delete mode 100644 Nagstamon/QUI/settings_main.py delete mode 100644 Nagstamon/QUI/settings_server.py diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index d7c8adc89..1f5004a06 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -67,17 +67,6 @@ STATES_SOUND, SORT_COLUMNS_FUNCTIONS) -# dialogs -#from Nagstamon.QUI.settings_main import Ui_settings_main -from Nagstamon.QUI.settings_server import Ui_settings_server -from Nagstamon.QUI.settings_action import Ui_settings_action -from Nagstamon.QUI.dialog_acknowledge import Ui_dialog_acknowledge -from Nagstamon.QUI.dialog_downtime import Ui_dialog_downtime -from Nagstamon.QUI.dialog_submit import Ui_dialog_submit -from Nagstamon.QUI.dialog_authentication import Ui_dialog_authentication -from Nagstamon.QUI.dialog_server_missing import Ui_dialog_server_missing -from Nagstamon.QUI.dialog_about import Ui_dialog_about - # only on X11/Linux thirdparty path should be added because it contains the Xlib module # needed to tell window manager via EWMH to keep Nagstamon window on all virtual desktops # TODO: test if X11 or Wayland is used @@ -433,7 +422,9 @@ def icon_clicked(self, reason): # self.show_menu.emit() # only react on left mouse click on OSX # elif reason == (QSystemTrayIcon.Trigger or QSystemTrayIcon.DoubleClick): - if reason in (QSystemTrayIcon.Trigger, QSystemTrayIcon.DoubleClick, QSystemTrayIcon.MiddleClick): + if reason in (QSystemTrayIcon.ActivationReason.Trigger, + QSystemTrayIcon.ActivationReason.DoubleClick, + QSystemTrayIcon.ActivationReason.MiddleClick): # when green icon is displayed and no popwin is about to po up show at least menu if get_worst_status() == 'UP' and OS == OS_DARWIN: self.menu.show_at_cursor() @@ -1000,7 +991,7 @@ def __init__(self): if OS == OS_DARWIN: # avoid hiding window if it has no focus - necessary on OSX if using flag Qt.Tool - self.setAttribute(Qt.WA_MacAlwaysShowToolWindow) + self.setAttribute(Qt.WidgetAttribute.WA_MacAlwaysShowToolWindow) self.setWindowTitle(AppInfo.NAME) self.setWindowIcon(QIcon('{0}{1}nagstamon.svg'.format(RESOURCES, os.sep))) @@ -1117,8 +1108,8 @@ def __init__(self): self.hiding.connect(self.worker_notification.stop) self.worker_notification.moveToThread(self.worker_notification_thread) - # start with priority 0 = lowest - self.worker_notification_thread.start(QThread.Priority.InheritPriority) + # start with low priority + self.worker_notification_thread.start(QThread.Priority.LowestPriority) self.create_ServerVBoxes() @@ -1169,8 +1160,8 @@ def __init__(self): self.worker_thread.started.connect(self.worker.debug_loop) # start debug loop by signal dialogs.settings.start_debug_loop.connect(self.worker.debug_loop) - # start with priority 0 = lowest - self.worker_thread.start(0) + # start with low priority + self.worker_thread.start(QThread.Priority.LowestPriority) # part of the stupid workaround for Qt-5.10-Windows-QSystemTrayIcon-madness self.connect_systrayicon() @@ -1243,7 +1234,7 @@ def set_mode(self): self.setWindowFlags(WINDOW_FLAGS) # show statusbar without being active, just floating - self.setAttribute(Qt.WA_ShowWithoutActivating) + self.setAttribute(Qt.WidgetAttribute.WA_ShowWithoutActivating) # necessary to be shown before Linux EWMH-mantra can be applied self.show() @@ -1274,7 +1265,7 @@ def set_mode(self): self.setWindowFlags(WINDOW_FLAGS) # show statusbar without being active, just floating - self.setAttribute(Qt.WA_ShowWithoutActivating) + self.setAttribute(Qt.WidgetAttribute.WA_ShowWithoutActivating) # yeah! systray! if OS == OS_WINDOWS: @@ -1306,7 +1297,7 @@ def set_mode(self): self.setWindowFlags(Qt.Widget | Qt.FramelessWindowHint) # show statusbar actively - self.setAttribute(Qt.WA_ShowWithoutActivating, False) + self.setAttribute(Qt.WidgetAttribute.WA_ShowWithoutActivating, False) # newer Qt5 seem to be better regarding fullscreen mode on non-OSX self.show_window() @@ -1337,7 +1328,7 @@ def set_mode(self): self.setWindowFlags(Qt.Widget) # show statusbar actively - self.setAttribute(Qt.WA_ShowWithoutActivating, False) + self.setAttribute(Qt.WidgetAttribute.WA_ShowWithoutActivating, False) # some maybe sensible default self.setMinimumSize(700, 300) @@ -1446,8 +1437,8 @@ def sort_ServerVBoxes(self): servers_vbox_new.addLayout(vboxes_dict[vbox]) # add expanding stretching item at the end for fullscreen beauty - servers_vbox_new.addSpacerItem(QSpacerItem(0, desktop.availableGeometry(self).height(), - QSizePolicy.Minimum, QSizePolicy.Expanding)) + servers_vbox_new.addSpacerItem(QSpacerItem(0, self.screen().availableGeometry().height(), + QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)) # switch to new servers_vbox self.servers_vbox = servers_vbox_new @@ -1508,10 +1499,10 @@ def show_window_systrayicon(self): icon_y = QCursor.pos().y() # get available desktop specs - available_width = desktop.availableGeometry(self).width() - available_height = desktop.availableGeometry(self).height() - available_x = desktop.availableGeometry(self).x() - available_y = desktop.availableGeometry(self).y() + available_width = self.screen().availableGeometry().width() + available_height = self.screen().availableGeometry().height() + available_x = self.screen().availableGeometry().x() + available_y = self.screen().availableGeometry().y() y = 0 @@ -3380,7 +3371,7 @@ def adjust_table(self): # force table to its maximal height, calculated by .get_real_height() self.setMinimumHeight(self.get_real_height()) self.setMaximumHeight(self.get_real_height()) - self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Maximum) + self.setSizePolicy(QSizePolicy.Policy.Ignored, QSizePolicy.Policy.Maximum) # after setting table whole window can be repainted self.ready_to_resize.emit() @@ -4597,7 +4588,7 @@ def __init__(self, dialog): QObject.__init__(self) #self.window = QDialog() - self.ui = uic.loadUi(f'Nagstamon/QUI/{dialog}.ui') + self.ui = uic.loadUi(f'Nagstamon/QUI/files/{dialog}.ui') self.window = self.ui #self.ui.setupUi(self.window) @@ -6815,18 +6806,18 @@ def __init__(self): QObject.__init__(self) self.player = QMediaPlayer(parent=self) - self.player.setVolume(100) + ##self.player.setVolume(100) # Qt6: https://doc-snapshots.qt.io/qt6-dev/qmediaplayer.html#setSource - self.playlist = QMediaPlaylist() - self.player.setPlaylist(self.playlist) + ##self.playlist = QMediaPlaylist() + ##self.player.setPlaylist(self.playlist) # let statuswindow show message - self.send_message.connect(statuswindow.show_message) + ##self.send_message.connect(statuswindow.show_message) # connect with statuswindow notification worker - statuswindow.worker_notification.load_sound.connect(self.set_media) - statuswindow.worker_notification.play_sound.connect(self.play) + ##statuswindow.worker_notification.load_sound.connect(self.set_media) + ##statuswindow.worker_notification.play_sound.connect(self.play) @Slot(str) def set_media(self, media_file): @@ -6900,7 +6891,7 @@ def check(self, start_mode=False, parent=None): self.worker.moveToThread(self.worker_thread) # run check when thread starts self.worker_thread.started.connect(self.worker.check) - self.worker_thread.start(0) + self.worker_thread.start(QThread.Priority.LowestPriority) @Slot() def reset_checking(self): @@ -6929,7 +6920,7 @@ def show_message(self, message): QMessageBox.Ok, parent, Qt.Dialog | Qt.MSWindowsFixedSizeDialogHint) - messagebox.setAttribute(Qt.WA_DeleteOnClose) + messagebox.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose) messagebox.setWindowModality(Qt.NonModal) messagebox.exec() @@ -7133,8 +7124,9 @@ def get_screen_geometry(screen_number): """ set screen for fullscreen """ - number_of_screens = desktop.screenCount() - for screen in range(number_of_screens + 1): + #number_of_screens = len(APP.screens()) + #for screen in range(number_of_screens + 1): + for screen in APP.screns(): if screen == screen_number: return desktop.screenGeometry(screen) @@ -7194,9 +7186,9 @@ def is_modifier_pressed(): check if (left) CTRL or Shift keys are pressed """ modifiers = APP.keyboardModifiers() - if modifiers == Qt.ControlModifier or \ - modifiers == Qt.ShiftModifier or \ - modifiers == (Qt.ControlModifier | Qt.ShiftModifier): + if modifiers == Qt.Modifier.CTRL or \ + modifiers == Qt.Modifier.SHIFT or \ + modifiers == (Qt.Modifier.CTRL | Qt.Modifier.SHIFT): del modifiers return True del modifiers diff --git a/Nagstamon/QUI/dialog_about.py b/Nagstamon/QUI/dialog_about.py deleted file mode 100644 index 8dd8865d6..000000000 --- a/Nagstamon/QUI/dialog_about.py +++ /dev/null @@ -1,96 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'dialog_about.ui' -# -# Created by: PyQt5 UI code generator 5.5.1 -# -# WARNING! All changes made in this file will be lost! - -from PyQt5 import QtCore, QtGui, QtWidgets - -class Ui_dialog_about(object): - def setupUi(self, dialog_about): - dialog_about.setObjectName("dialog_about") - dialog_about.resize(500, 300) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(dialog_about.sizePolicy().hasHeightForWidth()) - dialog_about.setSizePolicy(sizePolicy) - dialog_about.setSizeGripEnabled(True) - dialog_about.setModal(True) - self.gridLayout = QtWidgets.QGridLayout(dialog_about) - self.gridLayout.setObjectName("gridLayout") - self.tabs = QtWidgets.QTabWidget(dialog_about) - self.tabs.setTabShape(QtWidgets.QTabWidget.Rounded) - self.tabs.setObjectName("tabs") - self.tab_about = QtWidgets.QWidget() - self.tab_about.setObjectName("tab_about") - self.vbox_about = QtWidgets.QVBoxLayout(self.tab_about) - self.vbox_about.setObjectName("vbox_about") - spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.vbox_about.addItem(spacerItem) - self.label_nagstamon = QtWidgets.QLabel(self.tab_about) - self.label_nagstamon.setAlignment(QtCore.Qt.AlignCenter) - self.label_nagstamon.setObjectName("label_nagstamon") - self.vbox_about.addWidget(self.label_nagstamon) - self.label_nagstamon_long = QtWidgets.QLabel(self.tab_about) - self.label_nagstamon_long.setAlignment(QtCore.Qt.AlignCenter) - self.label_nagstamon_long.setObjectName("label_nagstamon_long") - self.vbox_about.addWidget(self.label_nagstamon_long) - self.label_copyright = QtWidgets.QLabel(self.tab_about) - self.label_copyright.setAlignment(QtCore.Qt.AlignCenter) - self.label_copyright.setObjectName("label_copyright") - self.vbox_about.addWidget(self.label_copyright) - self.label_website = QtWidgets.QLabel(self.tab_about) - self.label_website.setAlignment(QtCore.Qt.AlignCenter) - self.label_website.setObjectName("label_website") - self.vbox_about.addWidget(self.label_website) - spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.vbox_about.addItem(spacerItem1) - self.label_footnote = QtWidgets.QLabel(self.tab_about) - self.label_footnote.setAlignment(QtCore.Qt.AlignCenter) - self.label_footnote.setObjectName("label_footnote") - self.vbox_about.addWidget(self.label_footnote) - self.tabs.addTab(self.tab_about, "") - self.tab_credits = QtWidgets.QWidget() - self.tab_credits.setObjectName("tab_credits") - self.verticalLayout = QtWidgets.QVBoxLayout(self.tab_credits) - self.verticalLayout.setObjectName("verticalLayout") - self.textedit_credits = QtWidgets.QTextBrowser(self.tab_credits) - self.textedit_credits.setObjectName("textedit_credits") - self.verticalLayout.addWidget(self.textedit_credits) - self.tabs.addTab(self.tab_credits, "") - self.tab_license = QtWidgets.QWidget() - self.tab_license.setObjectName("tab_license") - self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.tab_license) - self.verticalLayout_3.setObjectName("verticalLayout_3") - self.textedit_license = QtWidgets.QTextEdit(self.tab_license) - self.textedit_license.setObjectName("textedit_license") - self.verticalLayout_3.addWidget(self.textedit_license) - self.tabs.addTab(self.tab_license, "") - self.gridLayout.addWidget(self.tabs, 0, 0, 1, 1) - self.buttonBox = QtWidgets.QDialogButtonBox(dialog_about) - self.buttonBox.setOrientation(QtCore.Qt.Horizontal) - self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Ok) - self.buttonBox.setObjectName("buttonBox") - self.gridLayout.addWidget(self.buttonBox, 1, 0, 1, 1) - - self.retranslateUi(dialog_about) - self.tabs.setCurrentIndex(0) - self.buttonBox.accepted.connect(dialog_about.accept) - self.buttonBox.rejected.connect(dialog_about.reject) - QtCore.QMetaObject.connectSlotsByName(dialog_about) - - def retranslateUi(self, dialog_about): - _translate = QtCore.QCoreApplication.translate - dialog_about.setWindowTitle(_translate("dialog_about", "About Nagstamon")) - self.label_nagstamon.setText(_translate("dialog_about", "Nagstamon x")) - self.label_nagstamon_long.setText(_translate("dialog_about", "Nagios status monitor")) - self.label_copyright.setText(_translate("dialog_about", "©2008-2022 Henri Wahl et al.")) - self.label_website.setText(_translate("dialog_about", "https://nagstamon.de")) - self.label_footnote.setText(_translate("dialog_about", "Footnote")) - self.tabs.setTabText(self.tabs.indexOf(self.tab_about), _translate("dialog_about", "About")) - self.tabs.setTabText(self.tabs.indexOf(self.tab_credits), _translate("dialog_about", "Credits")) - self.tabs.setTabText(self.tabs.indexOf(self.tab_license), _translate("dialog_about", "License")) - diff --git a/Nagstamon/QUI/dialog_acknowledge.py b/Nagstamon/QUI/dialog_acknowledge.py deleted file mode 100644 index 39816fc11..000000000 --- a/Nagstamon/QUI/dialog_acknowledge.py +++ /dev/null @@ -1,115 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'dialog_acknowledge.ui' -# -# Created by: PyQt5 UI code generator 5.15.4 -# -# WARNING: Any manual changes made to this file will be lost when pyuic5 is -# run again. Do not edit this file unless you know what you are doing. - - -from PyQt5 import QtCore, QtGui, QtWidgets - - -class Ui_dialog_acknowledge(object): - def setupUi(self, dialog_acknowledge): - dialog_acknowledge.setObjectName("dialog_acknowledge") - dialog_acknowledge.resize(475, 366) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(dialog_acknowledge.sizePolicy().hasHeightForWidth()) - dialog_acknowledge.setSizePolicy(sizePolicy) - dialog_acknowledge.setSizeGripEnabled(True) - dialog_acknowledge.setModal(True) - self.gridLayout = QtWidgets.QGridLayout(dialog_acknowledge) - self.gridLayout.setObjectName("gridLayout") - self.input_lineedit_comment = QtWidgets.QLineEdit(dialog_acknowledge) - self.input_lineedit_comment.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop) - self.input_lineedit_comment.setObjectName("input_lineedit_comment") - self.gridLayout.addWidget(self.input_lineedit_comment, 4, 0, 1, 1) - self.horizontalLayout = QtWidgets.QHBoxLayout() - self.horizontalLayout.setObjectName("horizontalLayout") - self.button_change_defaults_acknowledge = QtWidgets.QPushButton(dialog_acknowledge) - self.button_change_defaults_acknowledge.setObjectName("button_change_defaults_acknowledge") - self.horizontalLayout.addWidget(self.button_change_defaults_acknowledge) - self.button_box = QtWidgets.QDialogButtonBox(dialog_acknowledge) - self.button_box.setOrientation(QtCore.Qt.Horizontal) - self.button_box.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) - self.button_box.setObjectName("button_box") - self.horizontalLayout.addWidget(self.button_box) - self.gridLayout.addLayout(self.horizontalLayout, 8, 0, 1, 2) - self.options_groupbox = QtWidgets.QGroupBox(dialog_acknowledge) - self.options_groupbox.setEnabled(True) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.options_groupbox.sizePolicy().hasHeightForWidth()) - self.options_groupbox.setSizePolicy(sizePolicy) - self.options_groupbox.setMinimumSize(QtCore.QSize(0, 91)) - self.options_groupbox.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop) - self.options_groupbox.setObjectName("options_groupbox") - self.verticalLayout = QtWidgets.QVBoxLayout(self.options_groupbox) - self.verticalLayout.setObjectName("verticalLayout") - self.input_checkbox_sticky_acknowledgement = QtWidgets.QCheckBox(self.options_groupbox) - self.input_checkbox_sticky_acknowledgement.setObjectName("input_checkbox_sticky_acknowledgement") - self.verticalLayout.addWidget(self.input_checkbox_sticky_acknowledgement) - self.input_checkbox_send_notification = QtWidgets.QCheckBox(self.options_groupbox) - self.input_checkbox_send_notification.setObjectName("input_checkbox_send_notification") - self.verticalLayout.addWidget(self.input_checkbox_send_notification) - self.input_checkbox_persistent_comment = QtWidgets.QCheckBox(self.options_groupbox) - self.input_checkbox_persistent_comment.setObjectName("input_checkbox_persistent_comment") - self.verticalLayout.addWidget(self.input_checkbox_persistent_comment) - self.input_checkbox_acknowledge_all_services = QtWidgets.QCheckBox(self.options_groupbox) - self.input_checkbox_acknowledge_all_services.setObjectName("input_checkbox_acknowledge_all_services") - self.verticalLayout.addWidget(self.input_checkbox_acknowledge_all_services) - self.input_checkbox_use_expire_time = QtWidgets.QCheckBox(self.options_groupbox) - self.input_checkbox_use_expire_time.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu) - self.input_checkbox_use_expire_time.setToolTip("") - self.input_checkbox_use_expire_time.setObjectName("input_checkbox_use_expire_time") - self.verticalLayout.addWidget(self.input_checkbox_use_expire_time) - self.input_datetime_expire_time = QtWidgets.QDateTimeEdit(self.options_groupbox) - self.input_datetime_expire_time.setEnabled(True) - self.input_datetime_expire_time.setButtonSymbols(QtWidgets.QAbstractSpinBox.UpDownArrows) - self.input_datetime_expire_time.setCurrentSection(QtWidgets.QDateTimeEdit.HourSection) - self.input_datetime_expire_time.setCalendarPopup(True) - self.input_datetime_expire_time.setObjectName("input_datetime_expire_time") - self.verticalLayout.addWidget(self.input_datetime_expire_time) - self.gridLayout.addWidget(self.options_groupbox, 5, 0, 2, 2) - self.input_label_description = QtWidgets.QLabel(dialog_acknowledge) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.MinimumExpanding) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.input_label_description.sizePolicy().hasHeightForWidth()) - self.input_label_description.setSizePolicy(sizePolicy) - self.input_label_description.setMinimumSize(QtCore.QSize(0, 5)) - self.input_label_description.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop) - self.input_label_description.setObjectName("input_label_description") - self.gridLayout.addWidget(self.input_label_description, 0, 0, 1, 2) - spacerItem = QtWidgets.QSpacerItem(20, 0, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.gridLayout.addItem(spacerItem, 7, 0, 1, 1) - - self.retranslateUi(dialog_acknowledge) - self.button_box.accepted.connect(dialog_acknowledge.accept) - self.button_box.rejected.connect(dialog_acknowledge.reject) - QtCore.QMetaObject.connectSlotsByName(dialog_acknowledge) - dialog_acknowledge.setTabOrder(self.input_lineedit_comment, self.input_checkbox_sticky_acknowledgement) - dialog_acknowledge.setTabOrder(self.input_checkbox_sticky_acknowledgement, self.input_checkbox_send_notification) - dialog_acknowledge.setTabOrder(self.input_checkbox_send_notification, self.input_checkbox_persistent_comment) - dialog_acknowledge.setTabOrder(self.input_checkbox_persistent_comment, self.input_checkbox_acknowledge_all_services) - dialog_acknowledge.setTabOrder(self.input_checkbox_acknowledge_all_services, self.input_checkbox_use_expire_time) - dialog_acknowledge.setTabOrder(self.input_checkbox_use_expire_time, self.input_datetime_expire_time) - dialog_acknowledge.setTabOrder(self.input_datetime_expire_time, self.button_change_defaults_acknowledge) - - def retranslateUi(self, dialog_acknowledge): - _translate = QtCore.QCoreApplication.translate - dialog_acknowledge.setWindowTitle(_translate("dialog_acknowledge", "Acknowledge")) - self.button_change_defaults_acknowledge.setText(_translate("dialog_acknowledge", "Change acknowledgement defaults...")) - self.options_groupbox.setTitle(_translate("dialog_acknowledge", "Options")) - self.input_checkbox_sticky_acknowledgement.setText(_translate("dialog_acknowledge", "Sticky acknowledgement")) - self.input_checkbox_send_notification.setText(_translate("dialog_acknowledge", "Send notification")) - self.input_checkbox_persistent_comment.setText(_translate("dialog_acknowledge", "Persistent comment")) - self.input_checkbox_acknowledge_all_services.setText(_translate("dialog_acknowledge", "Acknowledge all services on host")) - self.input_checkbox_use_expire_time.setText(_translate("dialog_acknowledge", "Expire acknowledgement")) - self.input_datetime_expire_time.setToolTip(_translate("dialog_acknowledge", "Expiry time")) - self.input_label_description.setText(_translate("dialog_acknowledge", "description - set by QUI.py")) diff --git a/Nagstamon/QUI/dialog_authentication.py b/Nagstamon/QUI/dialog_authentication.py deleted file mode 100644 index 07f4e67ac..000000000 --- a/Nagstamon/QUI/dialog_authentication.py +++ /dev/null @@ -1,72 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'dialog_authentication.ui' -# -# Created by: PyQt5 UI code generator 5.5.1 -# -# WARNING! All changes made in this file will be lost! - -from PyQt5 import QtCore, QtGui, QtWidgets - -class Ui_dialog_authentication(object): - def setupUi(self, dialog_authentication): - dialog_authentication.setObjectName("dialog_authentication") - dialog_authentication.resize(350, 226) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(dialog_authentication.sizePolicy().hasHeightForWidth()) - dialog_authentication.setSizePolicy(sizePolicy) - dialog_authentication.setMinimumSize(QtCore.QSize(350, 0)) - dialog_authentication.setModal(True) - self.gridLayout = QtWidgets.QGridLayout(dialog_authentication) - self.gridLayout.setObjectName("gridLayout") - self.label_username = QtWidgets.QLabel(dialog_authentication) - self.label_username.setObjectName("label_username") - self.gridLayout.addWidget(self.label_username, 0, 0, 1, 1) - self.label_autologin_key = QtWidgets.QLabel(dialog_authentication) - self.label_autologin_key.setObjectName("label_autologin_key") - self.gridLayout.addWidget(self.label_autologin_key, 6, 0, 1, 1) - self.label_password = QtWidgets.QLabel(dialog_authentication) - self.label_password.setObjectName("label_password") - self.gridLayout.addWidget(self.label_password, 1, 0, 1, 1) - self.input_lineedit_autologin_key = QtWidgets.QLineEdit(dialog_authentication) - self.input_lineedit_autologin_key.setObjectName("input_lineedit_autologin_key") - self.gridLayout.addWidget(self.input_lineedit_autologin_key, 6, 1, 1, 1) - self.input_lineedit_username = QtWidgets.QLineEdit(dialog_authentication) - self.input_lineedit_username.setFrame(True) - self.input_lineedit_username.setObjectName("input_lineedit_username") - self.gridLayout.addWidget(self.input_lineedit_username, 0, 1, 1, 1) - self.input_lineedit_password = QtWidgets.QLineEdit(dialog_authentication) - self.input_lineedit_password.setMinimumSize(QtCore.QSize(200, 0)) - self.input_lineedit_password.setBaseSize(QtCore.QSize(0, 0)) - self.input_lineedit_password.setEchoMode(QtWidgets.QLineEdit.Password) - self.input_lineedit_password.setObjectName("input_lineedit_password") - self.gridLayout.addWidget(self.input_lineedit_password, 1, 1, 1, 1) - self.horizontalLayout = QtWidgets.QHBoxLayout() - self.horizontalLayout.setObjectName("horizontalLayout") - self.button_box = QtWidgets.QDialogButtonBox(dialog_authentication) - self.button_box.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) - self.button_box.setCenterButtons(False) - self.button_box.setObjectName("button_box") - self.horizontalLayout.addWidget(self.button_box) - self.gridLayout.addLayout(self.horizontalLayout, 7, 0, 1, 2) - self.input_checkbox_use_autologin = QtWidgets.QCheckBox(dialog_authentication) - self.input_checkbox_use_autologin.setObjectName("input_checkbox_use_autologin") - self.gridLayout.addWidget(self.input_checkbox_use_autologin, 4, 0, 1, 2) - self.input_checkbox_save_password = QtWidgets.QCheckBox(dialog_authentication) - self.input_checkbox_save_password.setObjectName("input_checkbox_save_password") - self.gridLayout.addWidget(self.input_checkbox_save_password, 3, 0, 1, 2) - - self.retranslateUi(dialog_authentication) - QtCore.QMetaObject.connectSlotsByName(dialog_authentication) - - def retranslateUi(self, dialog_authentication): - _translate = QtCore.QCoreApplication.translate - dialog_authentication.setWindowTitle(_translate("dialog_authentication", "Authentication")) - self.label_username.setText(_translate("dialog_authentication", "Username:")) - self.label_autologin_key.setText(_translate("dialog_authentication", "Autologin key:")) - self.label_password.setText(_translate("dialog_authentication", "Password:")) - self.input_checkbox_use_autologin.setText(_translate("dialog_authentication", "Use autologin")) - self.input_checkbox_save_password.setText(_translate("dialog_authentication", "Save password")) - diff --git a/Nagstamon/QUI/dialog_downtime.py b/Nagstamon/QUI/dialog_downtime.py deleted file mode 100644 index def35a9c5..000000000 --- a/Nagstamon/QUI/dialog_downtime.py +++ /dev/null @@ -1,99 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'dialog_downtime.ui' -# -# Created by: PyQt5 UI code generator 5.5.1 -# -# WARNING! All changes made in this file will be lost! - -from PyQt5 import QtCore, QtGui, QtWidgets - -class Ui_dialog_downtime(object): - def setupUi(self, dialog_downtime): - dialog_downtime.setObjectName("dialog_downtime") - dialog_downtime.resize(409, 294) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(dialog_downtime.sizePolicy().hasHeightForWidth()) - dialog_downtime.setSizePolicy(sizePolicy) - dialog_downtime.setSizeGripEnabled(True) - dialog_downtime.setModal(True) - self.gridLayout = QtWidgets.QGridLayout(dialog_downtime) - self.gridLayout.setObjectName("gridLayout") - self.label_start_time = QtWidgets.QLabel(dialog_downtime) - self.label_start_time.setObjectName("label_start_time") - self.gridLayout.addWidget(self.label_start_time, 2, 0, 1, 1) - spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.gridLayout.addItem(spacerItem, 13, 2, 1, 1) - self.input_label_description = QtWidgets.QLabel(dialog_downtime) - self.input_label_description.setObjectName("input_label_description") - self.gridLayout.addWidget(self.input_label_description, 0, 0, 1, 5) - self.horizontalLayout = QtWidgets.QHBoxLayout() - self.horizontalLayout.setObjectName("horizontalLayout") - self.button_change_defaults_downtime = QtWidgets.QPushButton(dialog_downtime) - self.button_change_defaults_downtime.setObjectName("button_change_defaults_downtime") - self.horizontalLayout.addWidget(self.button_change_defaults_downtime) - self.button_box = QtWidgets.QDialogButtonBox(dialog_downtime) - self.button_box.setOrientation(QtCore.Qt.Horizontal) - self.button_box.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) - self.button_box.setObjectName("button_box") - self.horizontalLayout.addWidget(self.button_box) - self.gridLayout.addLayout(self.horizontalLayout, 18, 0, 2, 5) - self.label_duration_hours = QtWidgets.QLabel(dialog_downtime) - self.label_duration_hours.setObjectName("label_duration_hours") - self.gridLayout.addWidget(self.label_duration_hours, 12, 2, 1, 1) - self.input_spinbox_duration_minutes = QtWidgets.QSpinBox(dialog_downtime) - self.input_spinbox_duration_minutes.setObjectName("input_spinbox_duration_minutes") - self.gridLayout.addWidget(self.input_spinbox_duration_minutes, 12, 3, 1, 1) - self.input_lineedit_comment = QtWidgets.QLineEdit(dialog_downtime) - self.input_lineedit_comment.setObjectName("input_lineedit_comment") - self.gridLayout.addWidget(self.input_lineedit_comment, 1, 0, 1, 5) - self.label_duration_minutes = QtWidgets.QLabel(dialog_downtime) - self.label_duration_minutes.setObjectName("label_duration_minutes") - self.gridLayout.addWidget(self.label_duration_minutes, 12, 4, 1, 1) - self.input_spinbox_duration_hours = QtWidgets.QSpinBox(dialog_downtime) - self.input_spinbox_duration_hours.setObjectName("input_spinbox_duration_hours") - self.gridLayout.addWidget(self.input_spinbox_duration_hours, 12, 1, 1, 1) - self.label_duration = QtWidgets.QLabel(dialog_downtime) - self.label_duration.setObjectName("label_duration") - self.gridLayout.addWidget(self.label_duration, 12, 0, 1, 1) - self.label_end_time = QtWidgets.QLabel(dialog_downtime) - self.label_end_time.setObjectName("label_end_time") - self.gridLayout.addWidget(self.label_end_time, 3, 0, 1, 1) - self.input_lineedit_end_time = QtWidgets.QLineEdit(dialog_downtime) - self.input_lineedit_end_time.setObjectName("input_lineedit_end_time") - self.gridLayout.addWidget(self.input_lineedit_end_time, 3, 1, 1, 4) - self.input_lineedit_start_time = QtWidgets.QLineEdit(dialog_downtime) - self.input_lineedit_start_time.setObjectName("input_lineedit_start_time") - self.gridLayout.addWidget(self.input_lineedit_start_time, 2, 1, 1, 4) - self.input_radiobutton_type_flexible = QtWidgets.QRadioButton(dialog_downtime) - self.input_radiobutton_type_flexible.setObjectName("input_radiobutton_type_flexible") - self.gridLayout.addWidget(self.input_radiobutton_type_flexible, 6, 0, 1, 1) - self.input_radiobutton_type_fixed = QtWidgets.QRadioButton(dialog_downtime) - self.input_radiobutton_type_fixed.setObjectName("input_radiobutton_type_fixed") - self.gridLayout.addWidget(self.input_radiobutton_type_fixed, 5, 0, 1, 1) - - self.retranslateUi(dialog_downtime) - self.button_box.accepted.connect(dialog_downtime.accept) - self.button_box.rejected.connect(dialog_downtime.reject) - QtCore.QMetaObject.connectSlotsByName(dialog_downtime) - dialog_downtime.setTabOrder(self.input_lineedit_comment, self.input_spinbox_duration_hours) - dialog_downtime.setTabOrder(self.input_spinbox_duration_hours, self.input_spinbox_duration_minutes) - dialog_downtime.setTabOrder(self.input_spinbox_duration_minutes, self.button_change_defaults_downtime) - - def retranslateUi(self, dialog_downtime): - _translate = QtCore.QCoreApplication.translate - dialog_downtime.setWindowTitle(_translate("dialog_downtime", "Downtime")) - self.label_start_time.setText(_translate("dialog_downtime", "Start time:")) - self.input_label_description.setText(_translate("dialog_downtime", "description - set by QUI.py")) - self.button_change_defaults_downtime.setText(_translate("dialog_downtime", "Change downtime defaults...")) - self.label_duration_hours.setText(_translate("dialog_downtime", "hours")) - self.label_duration_minutes.setText(_translate("dialog_downtime", "minutes")) - self.label_duration.setText(_translate("dialog_downtime", "Duration:")) - self.label_end_time.setText(_translate("dialog_downtime", "End time:")) - self.input_lineedit_end_time.setText(_translate("dialog_downtime", "n/a")) - self.input_lineedit_start_time.setText(_translate("dialog_downtime", "n/a")) - self.input_radiobutton_type_flexible.setText(_translate("dialog_downtime", "Flexible")) - self.input_radiobutton_type_fixed.setText(_translate("dialog_downtime", "Fixed")) - diff --git a/Nagstamon/QUI/dialog_server_missing.py b/Nagstamon/QUI/dialog_server_missing.py deleted file mode 100644 index f73b6fb51..000000000 --- a/Nagstamon/QUI/dialog_server_missing.py +++ /dev/null @@ -1,60 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'dialog_server_missing.ui' -# -# Created by: PyQt5 UI code generator 5.5.1 -# -# WARNING! All changes made in this file will be lost! - -from PyQt5 import QtCore, QtGui, QtWidgets - -class Ui_dialog_server_missing(object): - def setupUi(self, dialog_server_missing): - dialog_server_missing.setObjectName("dialog_server_missing") - dialog_server_missing.resize(813, 263) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(dialog_server_missing.sizePolicy().hasHeightForWidth()) - dialog_server_missing.setSizePolicy(sizePolicy) - dialog_server_missing.setMinimumSize(QtCore.QSize(350, 0)) - dialog_server_missing.setModal(True) - self.verticalLayout = QtWidgets.QVBoxLayout(dialog_server_missing) - self.verticalLayout.setObjectName("verticalLayout") - self.label_no_server_configured = QtWidgets.QLabel(dialog_server_missing) - self.label_no_server_configured.setWordWrap(True) - self.label_no_server_configured.setObjectName("label_no_server_configured") - self.verticalLayout.addWidget(self.label_no_server_configured) - self.label_no_server_enabled = QtWidgets.QLabel(dialog_server_missing) - self.label_no_server_enabled.setWordWrap(True) - self.label_no_server_enabled.setObjectName("label_no_server_enabled") - self.verticalLayout.addWidget(self.label_no_server_enabled) - self.horizontalLayout = QtWidgets.QHBoxLayout() - self.horizontalLayout.setObjectName("horizontalLayout") - self.button_enable_server = QtWidgets.QPushButton(dialog_server_missing) - self.button_enable_server.setObjectName("button_enable_server") - self.horizontalLayout.addWidget(self.button_enable_server) - self.button_create_server = QtWidgets.QPushButton(dialog_server_missing) - self.button_create_server.setObjectName("button_create_server") - self.horizontalLayout.addWidget(self.button_create_server) - self.button_ignore = QtWidgets.QPushButton(dialog_server_missing) - self.button_ignore.setObjectName("button_ignore") - self.horizontalLayout.addWidget(self.button_ignore) - self.button_exit = QtWidgets.QPushButton(dialog_server_missing) - self.button_exit.setObjectName("button_exit") - self.horizontalLayout.addWidget(self.button_exit) - self.verticalLayout.addLayout(self.horizontalLayout) - - self.retranslateUi(dialog_server_missing) - QtCore.QMetaObject.connectSlotsByName(dialog_server_missing) - - def retranslateUi(self, dialog_server_missing): - _translate = QtCore.QCoreApplication.translate - dialog_server_missing.setWindowTitle(_translate("dialog_server_missing", "Nagstamon")) - self.label_no_server_configured.setText(_translate("dialog_server_missing", "<html><head/><body><p>There are no configured servers yet.<br/></p></body></html>")) - self.label_no_server_enabled.setText(_translate("dialog_server_missing", "<html><head/><body><p>There are no servers enabled.<br/></p></body></html>")) - self.button_enable_server.setText(_translate("dialog_server_missing", "Enable server")) - self.button_create_server.setText(_translate("dialog_server_missing", "Create new server")) - self.button_ignore.setText(_translate("dialog_server_missing", "Ignore")) - self.button_exit.setText(_translate("dialog_server_missing", "Exit")) - diff --git a/Nagstamon/QUI/dialog_submit.py b/Nagstamon/QUI/dialog_submit.py deleted file mode 100644 index 925fe903b..000000000 --- a/Nagstamon/QUI/dialog_submit.py +++ /dev/null @@ -1,154 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'dialog_submit.ui' -# -# Created by: PyQt5 UI code generator 5.5.1 -# -# WARNING! All changes made in this file will be lost! - -from PyQt5 import QtCore, QtGui, QtWidgets - - -class Ui_dialog_submit(object): - - def setupUi(self, dialog_submit): - dialog_submit.setObjectName("dialog_submit") - dialog_submit.resize(473, 449) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(dialog_submit.sizePolicy().hasHeightForWidth()) - dialog_submit.setSizePolicy(sizePolicy) - dialog_submit.setMaximumSize(QtCore.QSize(16777215, 16777215)) - dialog_submit.setSizeGripEnabled(True) - dialog_submit.setModal(True) - self.gridLayout = QtWidgets.QGridLayout(dialog_submit) - self.gridLayout.setObjectName("gridLayout") - self.input_lineedit_performance_data = QtWidgets.QLineEdit(dialog_submit) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Maximum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.input_lineedit_performance_data.sizePolicy().hasHeightForWidth()) - self.input_lineedit_performance_data.setSizePolicy(sizePolicy) - self.input_lineedit_performance_data.setObjectName("input_lineedit_performance_data") - self.gridLayout.addWidget(self.input_lineedit_performance_data, 5, 2, 1, 1) - self.label_check_output = QtWidgets.QLabel(dialog_submit) - self.label_check_output.setObjectName("label_check_output") - self.gridLayout.addWidget(self.label_check_output, 3, 0, 1, 1, QtCore.Qt.AlignVCenter) - self.input_lineedit_check_output = QtWidgets.QLineEdit(dialog_submit) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Maximum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.input_lineedit_check_output.sizePolicy().hasHeightForWidth()) - self.input_lineedit_check_output.setSizePolicy(sizePolicy) - self.input_lineedit_check_output.setObjectName("input_lineedit_check_output") - self.gridLayout.addWidget(self.input_lineedit_check_output, 3, 2, 1, 1) - self.label_performance_data = QtWidgets.QLabel(dialog_submit) - self.label_performance_data.setObjectName("label_performance_data") - self.gridLayout.addWidget(self.label_performance_data, 5, 0, 1, 1, QtCore.Qt.AlignVCenter) - self.check_result_groupbox = QtWidgets.QGroupBox(dialog_submit) - self.check_result_groupbox.setObjectName("check_result_groupbox") - self.verticalLayout = QtWidgets.QVBoxLayout(self.check_result_groupbox) - self.verticalLayout.setObjectName("verticalLayout") - self.input_radiobutton_result_ok = QtWidgets.QRadioButton(self.check_result_groupbox) - self.input_radiobutton_result_ok.setChecked(True) - self.input_radiobutton_result_ok.setObjectName("input_radiobutton_result_ok") - self.verticalLayout.addWidget(self.input_radiobutton_result_ok) - self.input_radiobutton_result_up = QtWidgets.QRadioButton(self.check_result_groupbox) - self.input_radiobutton_result_up.setObjectName("input_radiobutton_result_up") - self.verticalLayout.addWidget(self.input_radiobutton_result_up) - self.input_radiobutton_result_information = QtWidgets.QRadioButton(self.check_result_groupbox) - self.input_radiobutton_result_information.setObjectName("input_radiobutton_result_information") - self.verticalLayout.addWidget(self.input_radiobutton_result_information) - self.input_radiobutton_result_warning = QtWidgets.QRadioButton(self.check_result_groupbox) - self.input_radiobutton_result_warning.setObjectName("input_radiobutton_result_warning") - self.verticalLayout.addWidget(self.input_radiobutton_result_warning) - self.input_radiobutton_result_average = QtWidgets.QRadioButton(self.check_result_groupbox) - self.input_radiobutton_result_average.setObjectName("input_radiobutton_result_average") - self.verticalLayout.addWidget(self.input_radiobutton_result_average) - self.input_radiobutton_result_high = QtWidgets.QRadioButton(self.check_result_groupbox) - self.input_radiobutton_result_high.setObjectName("input_radiobutton_result_high") - self.verticalLayout.addWidget(self.input_radiobutton_result_high) - self.input_radiobutton_result_critical = QtWidgets.QRadioButton(self.check_result_groupbox) - self.input_radiobutton_result_critical.setObjectName("input_radiobutton_result_critical") - self.verticalLayout.addWidget(self.input_radiobutton_result_critical) - self.input_radiobutton_result_disaster = QtWidgets.QRadioButton(self.check_result_groupbox) - self.input_radiobutton_result_disaster.setObjectName("input_radiobutton_result_disaster") - self.verticalLayout.addWidget(self.input_radiobutton_result_disaster) - self.input_radiobutton_result_unreachable = QtWidgets.QRadioButton(self.check_result_groupbox) - self.input_radiobutton_result_unreachable.setObjectName("input_radiobutton_result_unreachable") - self.verticalLayout.addWidget(self.input_radiobutton_result_unreachable) - self.input_radiobutton_result_unknown = QtWidgets.QRadioButton(self.check_result_groupbox) - self.input_radiobutton_result_unknown.setObjectName("input_radiobutton_result_unknown") - self.verticalLayout.addWidget(self.input_radiobutton_result_unknown) - self.input_radiobutton_result_down = QtWidgets.QRadioButton(self.check_result_groupbox) - self.input_radiobutton_result_down.setObjectName("input_radiobutton_result_down") - self.verticalLayout.addWidget(self.input_radiobutton_result_down) - self.gridLayout.addWidget(self.check_result_groupbox, 1, 0, 1, 4) - self.label_comment = QtWidgets.QLabel(dialog_submit) - self.label_comment.setObjectName("label_comment") - self.gridLayout.addWidget(self.label_comment, 7, 0, 1, 1, QtCore.Qt.AlignVCenter) - self.input_label_description = QtWidgets.QLabel(dialog_submit) - self.input_label_description.setObjectName("input_label_description") - self.gridLayout.addWidget(self.input_label_description, 0, 0, 1, 4) - self.horizontalLayout = QtWidgets.QHBoxLayout() - self.horizontalLayout.setObjectName("horizontalLayout") - self.button_change_defaults_submit_check_result = QtWidgets.QPushButton(dialog_submit) - self.button_change_defaults_submit_check_result.setObjectName("button_change_defaults_submit_check_result") - self.horizontalLayout.addWidget(self.button_change_defaults_submit_check_result) - self.button_box = QtWidgets.QDialogButtonBox(dialog_submit) - self.button_box.setOrientation(QtCore.Qt.Horizontal) - self.button_box.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel | QtWidgets.QDialogButtonBox.Ok) - self.button_box.setObjectName("button_box") - self.horizontalLayout.addWidget(self.button_box) - self.gridLayout.addLayout(self.horizontalLayout, 11, 0, 2, 4) - self.input_lineedit_comment = QtWidgets.QLineEdit(dialog_submit) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Maximum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.input_lineedit_comment.sizePolicy().hasHeightForWidth()) - self.input_lineedit_comment.setSizePolicy(sizePolicy) - self.input_lineedit_comment.setObjectName("input_lineedit_comment") - self.gridLayout.addWidget(self.input_lineedit_comment, 7, 2, 1, 1) - spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.gridLayout.addItem(spacerItem, 10, 0, 1, 1) - - self.retranslateUi(dialog_submit) - self.button_box.accepted.connect(dialog_submit.accept) - self.button_box.rejected.connect(dialog_submit.reject) - QtCore.QMetaObject.connectSlotsByName(dialog_submit) - dialog_submit.setTabOrder(self.input_radiobutton_result_ok, self.input_radiobutton_result_up) - dialog_submit.setTabOrder(self.input_radiobutton_result_ok, self.input_radiobutton_result_information) - dialog_submit.setTabOrder(self.input_radiobutton_result_information, self.input_radiobutton_result_warning) - dialog_submit.setTabOrder(self.input_radiobutton_result_warning, self.input_radiobutton_result_average) - dialog_submit.setTabOrder(self.input_radiobutton_result_average, self.input_radiobutton_result_high) - dialog_submit.setTabOrder(self.input_radiobutton_result_high, self.input_radiobutton_result_critical) - dialog_submit.setTabOrder(self.input_radiobutton_result_critical, self.input_radiobutton_result_disaster) - dialog_submit.setTabOrder(self.input_radiobutton_result_disaster, self.input_radiobutton_result_unreachable) - dialog_submit.setTabOrder(self.input_radiobutton_result_unreachable, self.input_radiobutton_result_unknown) - dialog_submit.setTabOrder(self.input_radiobutton_result_unknown, self.input_radiobutton_result_down) - dialog_submit.setTabOrder(self.input_radiobutton_result_down, self.input_lineedit_check_output) - dialog_submit.setTabOrder(self.input_lineedit_check_output, self.input_lineedit_performance_data) - dialog_submit.setTabOrder(self.input_lineedit_performance_data, self.input_lineedit_comment) - dialog_submit.setTabOrder(self.input_lineedit_comment, self.button_change_defaults_submit_check_result) - - def retranslateUi(self, dialog_submit): - _translate = QtCore.QCoreApplication.translate - dialog_submit.setWindowTitle(_translate("dialog_submit", "Submit check result")) - self.label_check_output.setText(_translate("dialog_submit", "Check output:")) - self.label_performance_data.setText(_translate("dialog_submit", "Performance data:")) - self.check_result_groupbox.setTitle(_translate("dialog_submit", "Check result")) - self.input_radiobutton_result_ok.setText(_translate("dialog_submit", "OK")) - self.input_radiobutton_result_up.setText(_translate("dialog_submit", "UP")) - self.input_radiobutton_result_information.setText(_translate("dialog_submit", "INFORMATION")) - self.input_radiobutton_result_warning.setText(_translate("dialog_submit", "WARNING")) - self.input_radiobutton_result_average.setText(_translate("dialog_submit", "AVERAGE")) - self.input_radiobutton_result_high.setText(_translate("dialog_submit", "HIGH")) - self.input_radiobutton_result_critical.setText(_translate("dialog_submit", "CRITICAL")) - self.input_radiobutton_result_disaster.setText(_translate("dialog_submit", "DISASTER")) - self.input_radiobutton_result_unreachable.setText(_translate("dialog_submit", "UNREACHABLE")) - self.input_radiobutton_result_unknown.setText(_translate("dialog_submit", "UNKNOWN")) - self.input_radiobutton_result_down.setText(_translate("dialog_submit", "DOWN")) - self.label_comment.setText(_translate("dialog_submit", "Comment:")) - self.input_label_description.setText(_translate("dialog_submit", "description - set by QUI.py")) - self.button_change_defaults_submit_check_result.setText(_translate("dialog_submit", "Change submit check result defaults...")) diff --git a/Nagstamon/QUI/dialog_about.ui b/Nagstamon/QUI/files/dialog_about.ui similarity index 96% rename from Nagstamon/QUI/dialog_about.ui rename to Nagstamon/QUI/files/dialog_about.ui index 46d88357b..35aefe52b 100644 --- a/Nagstamon/QUI/dialog_about.ui +++ b/Nagstamon/QUI/files/dialog_about.ui @@ -1,189 +1,189 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>dialog_about</class> - <widget class="QDialog" name="dialog_about"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>500</width> - <height>300</height> - </rect> - </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="windowTitle"> - <string>About Nagstamon</string> - </property> - <property name="sizeGripEnabled"> - <bool>true</bool> - </property> - <property name="modal"> - <bool>true</bool> - </property> - <layout class="QGridLayout" name="gridLayout"> - <item row="0" column="0"> - <widget class="QTabWidget" name="tabs"> - <property name="tabShape"> - <enum>QTabWidget::Rounded</enum> - </property> - <property name="currentIndex"> - <number>0</number> - </property> - <widget class="QWidget" name="tab_about"> - <attribute name="title"> - <string>About</string> - </attribute> - <layout class="QVBoxLayout" name="vbox_about"> - <item> - <spacer name="verticalSpacer_2"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>40</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QLabel" name="label_nagstamon"> - <property name="text"> - <string>Nagstamon x</string> - </property> - <property name="alignment"> - <set>Qt::AlignCenter</set> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="label_nagstamon_long"> - <property name="text"> - <string>Nagios status monitor</string> - </property> - <property name="alignment"> - <set>Qt::AlignCenter</set> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="label_copyright"> - <property name="text"> - <string>©2008-2022 Henri Wahl et al.</string> - </property> - <property name="alignment"> - <set>Qt::AlignCenter</set> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="label_website"> - <property name="text"> - <string>https://nagstamon.de</string> - </property> - <property name="alignment"> - <set>Qt::AlignCenter</set> - </property> - </widget> - </item> - <item> - <spacer name="verticalSpacer"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>40</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QLabel" name="label_footnote"> - <property name="text"> - <string>Footnote</string> - </property> - <property name="alignment"> - <set>Qt::AlignCenter</set> - </property> - </widget> - </item> - </layout> - </widget> - <widget class="QWidget" name="tab_credits"> - <attribute name="title"> - <string>Credits</string> - </attribute> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> - <widget class="QTextBrowser" name="textedit_credits"/> - </item> - </layout> - </widget> - <widget class="QWidget" name="tab_license"> - <attribute name="title"> - <string>License</string> - </attribute> - <layout class="QVBoxLayout" name="verticalLayout_3"> - <item> - <widget class="QTextEdit" name="textedit_license"/> - </item> - </layout> - </widget> - </widget> - </item> - <item row="1" column="0"> - <widget class="QDialogButtonBox" name="buttonBox"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="standardButtons"> - <set>QDialogButtonBox::Ok</set> - </property> - </widget> - </item> - </layout> - </widget> - <resources/> - <connections> - <connection> - <sender>buttonBox</sender> - <signal>accepted()</signal> - <receiver>dialog_about</receiver> - <slot>accept()</slot> - <hints> - <hint type="sourcelabel"> - <x>248</x> - <y>254</y> - </hint> - <hint type="destinationlabel"> - <x>157</x> - <y>274</y> - </hint> - </hints> - </connection> - <connection> - <sender>buttonBox</sender> - <signal>rejected()</signal> - <receiver>dialog_about</receiver> - <slot>reject()</slot> - <hints> - <hint type="sourcelabel"> - <x>316</x> - <y>260</y> - </hint> - <hint type="destinationlabel"> - <x>286</x> - <y>274</y> - </hint> - </hints> - </connection> - </connections> -</ui> +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>dialog_about</class> + <widget class="QDialog" name="dialog_about"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>500</width> + <height>300</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="windowTitle"> + <string>About Nagstamon</string> + </property> + <property name="sizeGripEnabled"> + <bool>true</bool> + </property> + <property name="modal"> + <bool>true</bool> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QTabWidget" name="tabs"> + <property name="tabShape"> + <enum>QTabWidget::Rounded</enum> + </property> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="tab_about"> + <attribute name="title"> + <string>About</string> + </attribute> + <layout class="QVBoxLayout" name="vbox_about"> + <item> + <spacer name="verticalSpacer_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="label_nagstamon"> + <property name="text"> + <string>Nagstamon x</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_nagstamon_long"> + <property name="text"> + <string>Nagios status monitor</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_copyright"> + <property name="text"> + <string>©2008-2022 Henri Wahl et al.</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_website"> + <property name="text"> + <string>https://nagstamon.de</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="label_footnote"> + <property name="text"> + <string>Footnote</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_credits"> + <attribute name="title"> + <string>Credits</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QTextBrowser" name="textedit_credits"/> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_license"> + <attribute name="title"> + <string>License</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QTextEdit" name="textedit_license"/> + </item> + </layout> + </widget> + </widget> + </item> + <item row="1" column="0"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>dialog_about</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>dialog_about</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/Nagstamon/QUI/dialog_acknowledge.ui b/Nagstamon/QUI/files/dialog_acknowledge.ui similarity index 100% rename from Nagstamon/QUI/dialog_acknowledge.ui rename to Nagstamon/QUI/files/dialog_acknowledge.ui diff --git a/Nagstamon/QUI/dialog_authentication.ui b/Nagstamon/QUI/files/dialog_authentication.ui similarity index 100% rename from Nagstamon/QUI/dialog_authentication.ui rename to Nagstamon/QUI/files/dialog_authentication.ui diff --git a/Nagstamon/QUI/dialog_downtime.ui b/Nagstamon/QUI/files/dialog_downtime.ui similarity index 100% rename from Nagstamon/QUI/dialog_downtime.ui rename to Nagstamon/QUI/files/dialog_downtime.ui diff --git a/Nagstamon/QUI/dialog_server_missing.ui b/Nagstamon/QUI/files/dialog_server_missing.ui similarity index 100% rename from Nagstamon/QUI/dialog_server_missing.ui rename to Nagstamon/QUI/files/dialog_server_missing.ui diff --git a/Nagstamon/QUI/dialog_submit.ui b/Nagstamon/QUI/files/dialog_submit.ui similarity index 100% rename from Nagstamon/QUI/dialog_submit.ui rename to Nagstamon/QUI/files/dialog_submit.ui diff --git a/Nagstamon/QUI/settings_action.ui b/Nagstamon/QUI/files/settings_action.ui similarity index 100% rename from Nagstamon/QUI/settings_action.ui rename to Nagstamon/QUI/files/settings_action.ui diff --git a/Nagstamon/QUI/settings_main.ui b/Nagstamon/QUI/files/settings_main.ui similarity index 100% rename from Nagstamon/QUI/settings_main.ui rename to Nagstamon/QUI/files/settings_main.ui diff --git a/Nagstamon/QUI/settings_server.ui b/Nagstamon/QUI/files/settings_server.ui similarity index 100% rename from Nagstamon/QUI/settings_server.ui rename to Nagstamon/QUI/files/settings_server.ui diff --git a/Nagstamon/QUI/settings_action.py b/Nagstamon/QUI/settings_action.py deleted file mode 100644 index 7a70d5f91..000000000 --- a/Nagstamon/QUI/settings_action.py +++ /dev/null @@ -1,261 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'settings_action.ui' -# -# Created by: PyQt5 UI code generator 5.12.2 -# -# WARNING! All changes made in this file will be lost! - -from PyQt5 import QtCore, QtGui, QtWidgets - - -class Ui_settings_action(object): - def setupUi(self, settings_action): - settings_action.setObjectName("settings_action") - settings_action.resize(752, 1017) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(settings_action.sizePolicy().hasHeightForWidth()) - settings_action.setSizePolicy(sizePolicy) - settings_action.setModal(True) - self.gridLayout = QtWidgets.QGridLayout(settings_action) - self.gridLayout.setObjectName("gridLayout") - self.input_checkbox_re_host_reverse = QtWidgets.QCheckBox(settings_action) - self.input_checkbox_re_host_reverse.setObjectName("input_checkbox_re_host_reverse") - self.gridLayout.addWidget(self.input_checkbox_re_host_reverse, 22, 5, 1, 1) - self.input_lineedit_re_attempt_pattern = QtWidgets.QLineEdit(settings_action) - self.input_lineedit_re_attempt_pattern.setObjectName("input_lineedit_re_attempt_pattern") - self.gridLayout.addWidget(self.input_lineedit_re_attempt_pattern, 28, 0, 1, 5) - self.input_lineedit_re_service_pattern = QtWidgets.QLineEdit(settings_action) - self.input_lineedit_re_service_pattern.setObjectName("input_lineedit_re_service_pattern") - self.gridLayout.addWidget(self.input_lineedit_re_service_pattern, 24, 0, 1, 5) - self.input_radiobutton_close_popwin = QtWidgets.QRadioButton(settings_action) - self.input_radiobutton_close_popwin.setObjectName("input_radiobutton_close_popwin") - self.gridLayout.addWidget(self.input_radiobutton_close_popwin, 37, 0, 1, 6) - self.input_checkbox_re_status_information_enabled = QtWidgets.QCheckBox(settings_action) - self.input_checkbox_re_status_information_enabled.setObjectName("input_checkbox_re_status_information_enabled") - self.gridLayout.addWidget(self.input_checkbox_re_status_information_enabled, 25, 0, 1, 6) - self.input_lineedit_re_status_information_pattern = QtWidgets.QLineEdit(settings_action) - self.input_lineedit_re_status_information_pattern.setObjectName("input_lineedit_re_status_information_pattern") - self.gridLayout.addWidget(self.input_lineedit_re_status_information_pattern, 26, 0, 1, 5) - self.input_radiobutton_leave_popwin_open = QtWidgets.QRadioButton(settings_action) - self.input_radiobutton_leave_popwin_open.setObjectName("input_radiobutton_leave_popwin_open") - self.gridLayout.addWidget(self.input_radiobutton_leave_popwin_open, 38, 0, 1, 6) - self.input_textedit_string = QtWidgets.QTextEdit(settings_action) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.input_textedit_string.sizePolicy().hasHeightForWidth()) - self.input_textedit_string.setSizePolicy(sizePolicy) - self.input_textedit_string.setObjectName("input_textedit_string") - self.gridLayout.addWidget(self.input_textedit_string, 6, 1, 1, 5) - self.input_checkbox_re_duration_enabled = QtWidgets.QCheckBox(settings_action) - self.input_checkbox_re_duration_enabled.setObjectName("input_checkbox_re_duration_enabled") - self.gridLayout.addWidget(self.input_checkbox_re_duration_enabled, 29, 0, 1, 6) - self.label_string = QtWidgets.QLabel(settings_action) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_string.sizePolicy().hasHeightForWidth()) - self.label_string.setSizePolicy(sizePolicy) - self.label_string.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop) - self.label_string.setObjectName("label_string") - self.gridLayout.addWidget(self.label_string, 6, 0, 1, 1) - self.button_box = QtWidgets.QDialogButtonBox(settings_action) - self.button_box.setOrientation(QtCore.Qt.Horizontal) - self.button_box.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) - self.button_box.setObjectName("button_box") - self.gridLayout.addWidget(self.button_box, 40, 0, 1, 6) - self.label_action_type = QtWidgets.QLabel(settings_action) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_action_type.sizePolicy().hasHeightForWidth()) - self.label_action_type.setSizePolicy(sizePolicy) - self.label_action_type.setObjectName("label_action_type") - self.gridLayout.addWidget(self.label_action_type, 1, 0, 1, 1) - self.label_monitor_type = QtWidgets.QLabel(settings_action) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_monitor_type.sizePolicy().hasHeightForWidth()) - self.label_monitor_type.setSizePolicy(sizePolicy) - self.label_monitor_type.setObjectName("label_monitor_type") - self.gridLayout.addWidget(self.label_monitor_type, 3, 0, 1, 1) - self.input_checkbox_filter_target_host = QtWidgets.QCheckBox(settings_action) - self.input_checkbox_filter_target_host.setObjectName("input_checkbox_filter_target_host") - self.gridLayout.addWidget(self.input_checkbox_filter_target_host, 13, 1, 1, 1) - self.input_checkbox_re_host_enabled = QtWidgets.QCheckBox(settings_action) - self.input_checkbox_re_host_enabled.setObjectName("input_checkbox_re_host_enabled") - self.gridLayout.addWidget(self.input_checkbox_re_host_enabled, 21, 0, 1, 6) - self.input_lineedit_re_host_pattern = QtWidgets.QLineEdit(settings_action) - self.input_lineedit_re_host_pattern.setObjectName("input_lineedit_re_host_pattern") - self.gridLayout.addWidget(self.input_lineedit_re_host_pattern, 22, 0, 1, 5) - self.label_status_popup = QtWidgets.QLabel(settings_action) - self.label_status_popup.setObjectName("label_status_popup") - self.gridLayout.addWidget(self.label_status_popup, 34, 0, 1, 1) - self.input_combobox_type = QtWidgets.QComboBox(settings_action) - self.input_combobox_type.setObjectName("input_combobox_type") - self.gridLayout.addWidget(self.input_combobox_type, 1, 1, 1, 2) - self.input_combobox_monitor_type = QtWidgets.QComboBox(settings_action) - self.input_combobox_monitor_type.setObjectName("input_combobox_monitor_type") - self.gridLayout.addWidget(self.input_combobox_monitor_type, 3, 1, 1, 2) - self.input_lineedit_re_groups_pattern = QtWidgets.QLineEdit(settings_action) - self.input_lineedit_re_groups_pattern.setObjectName("input_lineedit_re_groups_pattern") - self.gridLayout.addWidget(self.input_lineedit_re_groups_pattern, 32, 0, 1, 5) - self.input_checkbox_re_duration_reverse = QtWidgets.QCheckBox(settings_action) - self.input_checkbox_re_duration_reverse.setObjectName("input_checkbox_re_duration_reverse") - self.gridLayout.addWidget(self.input_checkbox_re_duration_reverse, 30, 5, 1, 1) - self.input_checkbox_filter_target_service = QtWidgets.QCheckBox(settings_action) - self.input_checkbox_filter_target_service.setObjectName("input_checkbox_filter_target_service") - self.gridLayout.addWidget(self.input_checkbox_filter_target_service, 14, 1, 1, 1) - self.input_checkbox_re_service_reverse = QtWidgets.QCheckBox(settings_action) - self.input_checkbox_re_service_reverse.setObjectName("input_checkbox_re_service_reverse") - self.gridLayout.addWidget(self.input_checkbox_re_service_reverse, 24, 5, 1, 1) - self.input_checkbox_re_service_enabled = QtWidgets.QCheckBox(settings_action) - self.input_checkbox_re_service_enabled.setObjectName("input_checkbox_re_service_enabled") - self.gridLayout.addWidget(self.input_checkbox_re_service_enabled, 23, 0, 1, 6) - self.input_lineedit_re_duration_pattern = QtWidgets.QLineEdit(settings_action) - self.input_lineedit_re_duration_pattern.setObjectName("input_lineedit_re_duration_pattern") - self.gridLayout.addWidget(self.input_lineedit_re_duration_pattern, 30, 0, 1, 5) - self.input_lineedit_description = QtWidgets.QLineEdit(settings_action) - self.input_lineedit_description.setObjectName("input_lineedit_description") - self.gridLayout.addWidget(self.input_lineedit_description, 5, 1, 1, 5) - self.input_lineedit_name = QtWidgets.QLineEdit(settings_action) - self.input_lineedit_name.setObjectName("input_lineedit_name") - self.gridLayout.addWidget(self.input_lineedit_name, 4, 1, 1, 5) - self.input_checkbox_re_groups_reverse = QtWidgets.QCheckBox(settings_action) - self.input_checkbox_re_groups_reverse.setObjectName("input_checkbox_re_groups_reverse") - self.gridLayout.addWidget(self.input_checkbox_re_groups_reverse, 32, 5, 1, 1) - self.label_target = QtWidgets.QLabel(settings_action) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_target.sizePolicy().hasHeightForWidth()) - self.label_target.setSizePolicy(sizePolicy) - self.label_target.setObjectName("label_target") - self.gridLayout.addWidget(self.label_target, 13, 0, 1, 1) - self.input_checkbox_recheck = QtWidgets.QCheckBox(settings_action) - self.input_checkbox_recheck.setObjectName("input_checkbox_recheck") - self.gridLayout.addWidget(self.input_checkbox_recheck, 39, 0, 1, 6) - self.input_checkbox_re_groups_enabled = QtWidgets.QCheckBox(settings_action) - self.input_checkbox_re_groups_enabled.setObjectName("input_checkbox_re_groups_enabled") - self.gridLayout.addWidget(self.input_checkbox_re_groups_enabled, 31, 0, 1, 6) - self.label_description = QtWidgets.QLabel(settings_action) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_description.sizePolicy().hasHeightForWidth()) - self.label_description.setSizePolicy(sizePolicy) - self.label_description.setObjectName("label_description") - self.gridLayout.addWidget(self.label_description, 5, 0, 1, 1) - self.label_name = QtWidgets.QLabel(settings_action) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_name.sizePolicy().hasHeightForWidth()) - self.label_name.setSizePolicy(sizePolicy) - self.label_name.setObjectName("label_name") - self.gridLayout.addWidget(self.label_name, 4, 0, 1, 1) - self.input_checkbox_re_attempt_enabled = QtWidgets.QCheckBox(settings_action) - self.input_checkbox_re_attempt_enabled.setObjectName("input_checkbox_re_attempt_enabled") - self.gridLayout.addWidget(self.input_checkbox_re_attempt_enabled, 27, 0, 1, 5) - self.input_checkbox_re_attempt_reverse = QtWidgets.QCheckBox(settings_action) - self.input_checkbox_re_attempt_reverse.setObjectName("input_checkbox_re_attempt_reverse") - self.gridLayout.addWidget(self.input_checkbox_re_attempt_reverse, 28, 5, 1, 1) - self.input_checkbox_re_status_information_reverse = QtWidgets.QCheckBox(settings_action) - self.input_checkbox_re_status_information_reverse.setObjectName("input_checkbox_re_status_information_reverse") - self.gridLayout.addWidget(self.input_checkbox_re_status_information_reverse, 26, 5, 1, 1) - self.input_checkbox_enabled = QtWidgets.QCheckBox(settings_action) - self.input_checkbox_enabled.setObjectName("input_checkbox_enabled") - self.gridLayout.addWidget(self.input_checkbox_enabled, 0, 0, 1, 6) - self.label_python_re = QtWidgets.QLabel(settings_action) - self.label_python_re.setOpenExternalLinks(True) - self.label_python_re.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) - self.label_python_re.setObjectName("label_python_re") - self.gridLayout.addWidget(self.label_python_re, 33, 0, 1, 6) - - self.retranslateUi(settings_action) - self.button_box.accepted.connect(settings_action.accept) - self.button_box.rejected.connect(settings_action.reject) - QtCore.QMetaObject.connectSlotsByName(settings_action) - settings_action.setTabOrder(self.input_checkbox_enabled, self.input_combobox_type) - settings_action.setTabOrder(self.input_combobox_type, self.input_combobox_monitor_type) - settings_action.setTabOrder(self.input_combobox_monitor_type, self.input_lineedit_name) - settings_action.setTabOrder(self.input_lineedit_name, self.input_lineedit_description) - settings_action.setTabOrder(self.input_lineedit_description, self.input_checkbox_filter_target_host) - settings_action.setTabOrder(self.input_checkbox_filter_target_host, self.input_checkbox_filter_target_service) - settings_action.setTabOrder(self.input_checkbox_filter_target_service, self.input_checkbox_re_host_enabled) - settings_action.setTabOrder(self.input_checkbox_re_host_enabled, self.input_lineedit_re_host_pattern) - settings_action.setTabOrder(self.input_lineedit_re_host_pattern, self.input_checkbox_re_host_reverse) - settings_action.setTabOrder(self.input_checkbox_re_host_reverse, self.input_checkbox_re_service_enabled) - settings_action.setTabOrder(self.input_checkbox_re_service_enabled, self.input_lineedit_re_service_pattern) - settings_action.setTabOrder(self.input_lineedit_re_service_pattern, self.input_checkbox_re_service_reverse) - settings_action.setTabOrder(self.input_checkbox_re_service_reverse, self.input_checkbox_re_status_information_enabled) - settings_action.setTabOrder(self.input_checkbox_re_status_information_enabled, self.input_lineedit_re_status_information_pattern) - settings_action.setTabOrder(self.input_lineedit_re_status_information_pattern, self.input_checkbox_re_status_information_reverse) - settings_action.setTabOrder(self.input_checkbox_re_status_information_reverse, self.input_checkbox_re_duration_enabled) - settings_action.setTabOrder(self.input_checkbox_re_duration_enabled, self.input_lineedit_re_duration_pattern) - settings_action.setTabOrder(self.input_lineedit_re_duration_pattern, self.input_checkbox_re_duration_reverse) - settings_action.setTabOrder(self.input_checkbox_re_duration_reverse, self.input_checkbox_re_groups_enabled) - settings_action.setTabOrder(self.input_checkbox_re_groups_enabled, self.input_radiobutton_close_popwin) - settings_action.setTabOrder(self.input_radiobutton_close_popwin, self.input_radiobutton_leave_popwin_open) - - def retranslateUi(self, settings_action): - _translate = QtCore.QCoreApplication.translate - settings_action.setWindowTitle(_translate("settings_action", "Dialog")) - self.input_checkbox_re_host_reverse.setText(_translate("settings_action", "reverse")) - self.input_radiobutton_close_popwin.setText(_translate("settings_action", "Close status popup window after action")) - self.input_checkbox_re_status_information_enabled.setText(_translate("settings_action", "Regular expressions for status informations")) - self.input_radiobutton_leave_popwin_open.setText(_translate("settings_action", "Leave status popup window open after action")) - self.input_textedit_string.setToolTip(_translate("settings_action", "Available variables for action strings:\n" -"\n" -"$HOST$ - host as in monitor\n" -"$SERVICE$ - service as in monitor\n" -"$MONITOR$ - monitor address\n" -"$MONITOR-CGI$ - monitor CGI address\n" -"$ADDRESS$ - address of host, delivered from connection method\n" -"$USERNAME$ - username on monitor\n" -"$STATUS-INFO$ - status information for host or service\n" -"$PASSWORD$ - username\'s password on monitor\n" -"$COMMENT-ACK$ - default acknowledge comment\n" -"$COMMENT-DOWN$ - default downtime comment\n" -"$COMMENT-SUBMIT$ - default submit check result comment\n" -"\n" -"$TRANSID$ - only useful for Checkmk as _transid=$TRANSID$")) - self.input_checkbox_re_duration_enabled.setText(_translate("settings_action", "Regular expressions for duration")) - self.label_string.setText(_translate("settings_action", "String:")) - self.label_action_type.setText(_translate("settings_action", "Action type:")) - self.label_monitor_type.setText(_translate("settings_action", "Monitor type:")) - self.input_checkbox_filter_target_host.setText(_translate("settings_action", "Host")) - self.input_checkbox_re_host_enabled.setText(_translate("settings_action", "Regular expressions for hosts")) - self.label_status_popup.setText(_translate("settings_action", "Status popup window:")) - self.input_combobox_type.setToolTip(_translate("settings_action", "Available action types:\n" -"\n" -"Browser:\n" -"Use given string as URL, evaluate variables and open it in your default browser, for example a graph page in monitor.\n" -"\n" -"Command:\n" -"Execute command as given in string and evaluate variables, for example to open SSH connection.\n" -"\n" -"URL:\n" -"Request given URL string in the background, for example to acknowledge a service with one click.\n" -"")) - self.input_checkbox_re_duration_reverse.setText(_translate("settings_action", "reverse")) - self.input_checkbox_filter_target_service.setText(_translate("settings_action", "Service")) - self.input_checkbox_re_service_reverse.setText(_translate("settings_action", "reverse")) - self.input_checkbox_re_service_enabled.setText(_translate("settings_action", "Regular expressions for services")) - self.input_checkbox_re_groups_reverse.setText(_translate("settings_action", "reverse")) - self.label_target.setText(_translate("settings_action", "Target:")) - self.input_checkbox_recheck.setText(_translate("settings_action", "Recheck after action to force result")) - self.input_checkbox_re_groups_enabled.setText(_translate("settings_action", "Regular expressions for groups")) - self.label_description.setText(_translate("settings_action", "Description:")) - self.label_name.setText(_translate("settings_action", "Name:")) - self.input_checkbox_re_attempt_enabled.setText(_translate("settings_action", "Regular expressions for attempt")) - self.input_checkbox_re_attempt_reverse.setText(_translate("settings_action", "reverse")) - self.input_checkbox_re_status_information_reverse.setText(_translate("settings_action", "reverse")) - self.input_checkbox_enabled.setText(_translate("settings_action", "Enabled")) - self.label_python_re.setText(_translate("settings_action", "<a href=http://docs.python.org/howto/regex.html>See Python Regular Expressions HOWTO for filtering details.</a>")) - - diff --git a/Nagstamon/QUI/settings_main.py b/Nagstamon/QUI/settings_main.py deleted file mode 100644 index 5208a31e1..000000000 --- a/Nagstamon/QUI/settings_main.py +++ /dev/null @@ -1,1658 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'settings_main.ui' -# -# Created by: PyQt5 UI code generator 5.13.2 -# -# WARNING! All changes made in this file will be lost! - - -from PyQt5 import QtCore, QtGui, QtWidgets - - -class Ui_settings_main(object): - def setupUi(self, settings_main): - settings_main.setObjectName("settings_main") - settings_main.resize(665, 1193) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(settings_main.sizePolicy().hasHeightForWidth()) - settings_main.setSizePolicy(sizePolicy) - settings_main.setMaximumSize(QtCore.QSize(16777215, 16777215)) - settings_main.setLocale(QtCore.QLocale(QtCore.QLocale.C, QtCore.QLocale.AnyCountry)) - settings_main.setModal(True) - self.verticalLayout = QtWidgets.QVBoxLayout(settings_main) - self.verticalLayout.setObjectName("verticalLayout") - self.tabs = QtWidgets.QTabWidget(settings_main) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.tabs.sizePolicy().hasHeightForWidth()) - self.tabs.setSizePolicy(sizePolicy) - self.tabs.setObjectName("tabs") - self.tab_servers = QtWidgets.QWidget() - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.tab_servers.sizePolicy().hasHeightForWidth()) - self.tab_servers.setSizePolicy(sizePolicy) - self.tab_servers.setObjectName("tab_servers") - self.gridLayout = QtWidgets.QGridLayout(self.tab_servers) - self.gridLayout.setContentsMargins(10, 10, 10, 10) - self.gridLayout.setSpacing(5) - self.gridLayout.setObjectName("gridLayout") - self.horizontalLayout_2 = QtWidgets.QHBoxLayout() - self.horizontalLayout_2.setContentsMargins(5, 5, 5, 0) - self.horizontalLayout_2.setSpacing(5) - self.horizontalLayout_2.setObjectName("horizontalLayout_2") - self.input_checkbox_debug_mode = QtWidgets.QCheckBox(self.tab_servers) - self.input_checkbox_debug_mode.setObjectName("input_checkbox_debug_mode") - self.horizontalLayout_2.addWidget(self.input_checkbox_debug_mode) - self.input_checkbox_debug_to_file = QtWidgets.QCheckBox(self.tab_servers) - self.input_checkbox_debug_to_file.setEnabled(True) - self.input_checkbox_debug_to_file.setObjectName("input_checkbox_debug_to_file") - self.horizontalLayout_2.addWidget(self.input_checkbox_debug_to_file) - self.input_lineedit_debug_file = QtWidgets.QLineEdit(self.tab_servers) - self.input_lineedit_debug_file.setObjectName("input_lineedit_debug_file") - self.horizontalLayout_2.addWidget(self.input_lineedit_debug_file) - self.gridLayout.addLayout(self.horizontalLayout_2, 3, 1, 1, 3) - self.verticalLayout_2 = QtWidgets.QVBoxLayout() - self.verticalLayout_2.setContentsMargins(0, 5, 0, 5) - self.verticalLayout_2.setSpacing(5) - self.verticalLayout_2.setObjectName("verticalLayout_2") - self.button_new_server = QtWidgets.QPushButton(self.tab_servers) - self.button_new_server.setObjectName("button_new_server") - self.verticalLayout_2.addWidget(self.button_new_server) - self.button_edit_server = QtWidgets.QPushButton(self.tab_servers) - self.button_edit_server.setObjectName("button_edit_server") - self.verticalLayout_2.addWidget(self.button_edit_server) - self.button_copy_server = QtWidgets.QPushButton(self.tab_servers) - self.button_copy_server.setObjectName("button_copy_server") - self.verticalLayout_2.addWidget(self.button_copy_server) - self.button_delete_server = QtWidgets.QPushButton(self.tab_servers) - self.button_delete_server.setObjectName("button_delete_server") - self.verticalLayout_2.addWidget(self.button_delete_server) - spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.verticalLayout_2.addItem(spacerItem) - self.gridLayout.addLayout(self.verticalLayout_2, 0, 3, 1, 1) - self.horizontalLayout_3 = QtWidgets.QHBoxLayout() - self.horizontalLayout_3.setContentsMargins(5, 5, 5, 5) - self.horizontalLayout_3.setSpacing(5) - self.horizontalLayout_3.setObjectName("horizontalLayout_3") - self.input_checkbox_use_system_keyring = QtWidgets.QCheckBox(self.tab_servers) - self.input_checkbox_use_system_keyring.setObjectName("input_checkbox_use_system_keyring") - self.horizontalLayout_3.addWidget(self.input_checkbox_use_system_keyring) - self.gridLayout.addLayout(self.horizontalLayout_3, 2, 1, 1, 3) - self.horizontalLayout = QtWidgets.QHBoxLayout() - self.horizontalLayout.setContentsMargins(5, 5, 5, 5) - self.horizontalLayout.setSpacing(5) - self.horizontalLayout.setObjectName("horizontalLayout") - self.label_update_interval_seconds = QtWidgets.QLabel(self.tab_servers) - self.label_update_interval_seconds.setObjectName("label_update_interval_seconds") - self.horizontalLayout.addWidget(self.label_update_interval_seconds) - self.input_spinbox_update_interval_seconds = QtWidgets.QSpinBox(self.tab_servers) - self.input_spinbox_update_interval_seconds.setMaximum(999) - self.input_spinbox_update_interval_seconds.setObjectName("input_spinbox_update_interval_seconds") - self.horizontalLayout.addWidget(self.input_spinbox_update_interval_seconds) - self.label_interval_seconds = QtWidgets.QLabel(self.tab_servers) - self.label_interval_seconds.setObjectName("label_interval_seconds") - self.horizontalLayout.addWidget(self.label_interval_seconds) - spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.horizontalLayout.addItem(spacerItem1) - self.gridLayout.addLayout(self.horizontalLayout, 1, 1, 1, 3) - self.horizontalLayout_4 = QtWidgets.QHBoxLayout() - self.horizontalLayout_4.setContentsMargins(5, 5, 5, 0) - self.horizontalLayout_4.setSpacing(5) - self.horizontalLayout_4.setObjectName("horizontalLayout_4") - self.input_checkbox_check_for_new_version = QtWidgets.QCheckBox(self.tab_servers) - self.input_checkbox_check_for_new_version.setObjectName("input_checkbox_check_for_new_version") - self.horizontalLayout_4.addWidget(self.input_checkbox_check_for_new_version) - spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.horizontalLayout_4.addItem(spacerItem2) - self.button_check_for_new_version_now = QtWidgets.QPushButton(self.tab_servers) - self.button_check_for_new_version_now.setObjectName("button_check_for_new_version_now") - self.horizontalLayout_4.addWidget(self.button_check_for_new_version_now) - self.gridLayout.addLayout(self.horizontalLayout_4, 4, 1, 1, 3) - self.list_servers = QtWidgets.QListWidget(self.tab_servers) - self.list_servers.setMinimumSize(QtCore.QSize(0, 200)) - self.list_servers.setMaximumSize(QtCore.QSize(16777215, 16777215)) - self.list_servers.setResizeMode(QtWidgets.QListView.Adjust) - self.list_servers.setObjectName("list_servers") - self.gridLayout.addWidget(self.list_servers, 0, 1, 1, 1) - spacerItem3 = QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.gridLayout.addItem(spacerItem3, 5, 1, 1, 1) - self.tabs.addTab(self.tab_servers, "") - self.tab_display = QtWidgets.QWidget() - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.tab_display.sizePolicy().hasHeightForWidth()) - self.tab_display.setSizePolicy(sizePolicy) - self.tab_display.setObjectName("tab_display") - self.gridLayout_2 = QtWidgets.QGridLayout(self.tab_display) - self.gridLayout_2.setContentsMargins(10, 10, 10, 10) - self.gridLayout_2.setSpacing(5) - self.gridLayout_2.setObjectName("gridLayout_2") - self.groupbox_display_size = QtWidgets.QGroupBox(self.tab_display) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.groupbox_display_size.sizePolicy().hasHeightForWidth()) - self.groupbox_display_size.setSizePolicy(sizePolicy) - self.groupbox_display_size.setObjectName("groupbox_display_size") - self.horizontalLayout_5 = QtWidgets.QHBoxLayout(self.groupbox_display_size) - self.horizontalLayout_5.setContentsMargins(10, 10, 10, 10) - self.horizontalLayout_5.setSpacing(5) - self.horizontalLayout_5.setObjectName("horizontalLayout_5") - self.gridLayout_14 = QtWidgets.QGridLayout() - self.gridLayout_14.setContentsMargins(5, 5, 5, 5) - self.gridLayout_14.setSpacing(5) - self.gridLayout_14.setObjectName("gridLayout_14") - self.input_radiobutton_short_display = QtWidgets.QRadioButton(self.groupbox_display_size) - self.input_radiobutton_short_display.setObjectName("input_radiobutton_short_display") - self.gridLayout_14.addWidget(self.input_radiobutton_short_display, 0, 1, 1, 1) - self.input_radiobutton_long_display = QtWidgets.QRadioButton(self.groupbox_display_size) - self.input_radiobutton_long_display.setObjectName("input_radiobutton_long_display") - self.gridLayout_14.addWidget(self.input_radiobutton_long_display, 0, 0, 1, 1) - self.horizontalLayout_5.addLayout(self.gridLayout_14) - self.gridLayout_2.addWidget(self.groupbox_display_size, 0, 0, 1, 1) - spacerItem4 = QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.gridLayout_2.addItem(spacerItem4, 11, 0, 1, 1) - self.groupbox_detailed_summary_popup = QtWidgets.QGroupBox(self.tab_display) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.groupbox_detailed_summary_popup.sizePolicy().hasHeightForWidth()) - self.groupbox_detailed_summary_popup.setSizePolicy(sizePolicy) - self.groupbox_detailed_summary_popup.setObjectName("groupbox_detailed_summary_popup") - self.gridLayout_3 = QtWidgets.QGridLayout(self.groupbox_detailed_summary_popup) - self.gridLayout_3.setContentsMargins(10, 10, 10, 10) - self.gridLayout_3.setSpacing(5) - self.gridLayout_3.setObjectName("gridLayout_3") - self.gridLayout_13 = QtWidgets.QGridLayout() - self.gridLayout_13.setContentsMargins(5, 5, 5, 5) - self.gridLayout_13.setSpacing(5) - self.gridLayout_13.setObjectName("gridLayout_13") - self.input_radiobutton_close_details_clicking = QtWidgets.QRadioButton(self.groupbox_detailed_summary_popup) - self.input_radiobutton_close_details_clicking.setObjectName("input_radiobutton_close_details_clicking") - self.buttongroup_close_statusbar = QtWidgets.QButtonGroup(settings_main) - self.buttongroup_close_statusbar.setObjectName("buttongroup_close_statusbar") - self.buttongroup_close_statusbar.addButton(self.input_radiobutton_close_details_clicking) - self.gridLayout_13.addWidget(self.input_radiobutton_close_details_clicking, 1, 1, 1, 1) - self.input_radiobutton_popup_details_clicking = QtWidgets.QRadioButton(self.groupbox_detailed_summary_popup) - self.input_radiobutton_popup_details_clicking.setObjectName("input_radiobutton_popup_details_clicking") - self.buttongroup_popup_statuswindow = QtWidgets.QButtonGroup(settings_main) - self.buttongroup_popup_statuswindow.setObjectName("buttongroup_popup_statuswindow") - self.buttongroup_popup_statuswindow.addButton(self.input_radiobutton_popup_details_clicking) - self.gridLayout_13.addWidget(self.input_radiobutton_popup_details_clicking, 1, 0, 1, 1) - self.input_radiobutton_popup_details_hover = QtWidgets.QRadioButton(self.groupbox_detailed_summary_popup) - self.input_radiobutton_popup_details_hover.setObjectName("input_radiobutton_popup_details_hover") - self.buttongroup_popup_statuswindow.addButton(self.input_radiobutton_popup_details_hover) - self.gridLayout_13.addWidget(self.input_radiobutton_popup_details_hover, 0, 0, 1, 1) - self.input_radiobutton_close_details_hover = QtWidgets.QRadioButton(self.groupbox_detailed_summary_popup) - self.input_radiobutton_close_details_hover.setObjectName("input_radiobutton_close_details_hover") - self.buttongroup_close_statusbar.addButton(self.input_radiobutton_close_details_hover) - self.gridLayout_13.addWidget(self.input_radiobutton_close_details_hover, 0, 1, 1, 1) - self.input_radiobutton_close_details_clicking_somewhere = QtWidgets.QRadioButton(self.groupbox_detailed_summary_popup) - self.input_radiobutton_close_details_clicking_somewhere.setObjectName("input_radiobutton_close_details_clicking_somewhere") - self.buttongroup_close_statusbar.addButton(self.input_radiobutton_close_details_clicking_somewhere) - self.gridLayout_13.addWidget(self.input_radiobutton_close_details_clicking_somewhere, 2, 1, 1, 1) - self.gridLayout_3.addLayout(self.gridLayout_13, 0, 0, 2, 2) - self.input_checkbox_highlight_new_events = QtWidgets.QCheckBox(self.groupbox_detailed_summary_popup) - self.input_checkbox_highlight_new_events.setObjectName("input_checkbox_highlight_new_events") - self.gridLayout_3.addWidget(self.input_checkbox_highlight_new_events, 6, 0, 1, 2) - self.input_checkbox_show_tooltips = QtWidgets.QCheckBox(self.groupbox_detailed_summary_popup) - self.input_checkbox_show_tooltips.setObjectName("input_checkbox_show_tooltips") - self.gridLayout_3.addWidget(self.input_checkbox_show_tooltips, 7, 0, 1, 2) - self.gridLayout_15 = QtWidgets.QGridLayout() - self.gridLayout_15.setContentsMargins(5, 5, 5, 5) - self.gridLayout_15.setSpacing(5) - self.gridLayout_15.setObjectName("gridLayout_15") - self.input_combobox_default_sort_field = QtWidgets.QComboBox(self.groupbox_detailed_summary_popup) - self.input_combobox_default_sort_field.setObjectName("input_combobox_default_sort_field") - self.gridLayout_15.addWidget(self.input_combobox_default_sort_field, 0, 1, 1, 1) - self.label_default_sort_field = QtWidgets.QLabel(self.groupbox_detailed_summary_popup) - self.label_default_sort_field.setObjectName("label_default_sort_field") - self.gridLayout_15.addWidget(self.label_default_sort_field, 0, 0, 1, 1) - self.label_default_sort_order = QtWidgets.QLabel(self.groupbox_detailed_summary_popup) - self.label_default_sort_order.setObjectName("label_default_sort_order") - self.gridLayout_15.addWidget(self.label_default_sort_order, 1, 0, 1, 1) - self.input_combobox_default_sort_order = QtWidgets.QComboBox(self.groupbox_detailed_summary_popup) - self.input_combobox_default_sort_order.setObjectName("input_combobox_default_sort_order") - self.gridLayout_15.addWidget(self.input_combobox_default_sort_order, 1, 1, 1, 1) - self.gridLayout_3.addLayout(self.gridLayout_15, 8, 0, 1, 1) - self.gridLayout_2.addWidget(self.groupbox_detailed_summary_popup, 10, 0, 1, 1) - self.groupbox_appearance = QtWidgets.QGroupBox(self.tab_display) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.groupbox_appearance.sizePolicy().hasHeightForWidth()) - self.groupbox_appearance.setSizePolicy(sizePolicy) - self.groupbox_appearance.setObjectName("groupbox_appearance") - self.gridLayout_17 = QtWidgets.QGridLayout(self.groupbox_appearance) - self.gridLayout_17.setContentsMargins(10, 10, 10, 10) - self.gridLayout_17.setSpacing(5) - self.gridLayout_17.setObjectName("gridLayout_17") - self.input_radiobutton_icon_in_systray = QtWidgets.QRadioButton(self.groupbox_appearance) - self.input_radiobutton_icon_in_systray.setObjectName("input_radiobutton_icon_in_systray") - self.gridLayout_17.addWidget(self.input_radiobutton_icon_in_systray, 1, 0, 1, 1) - self.input_checkbox_systray_offset_use = QtWidgets.QCheckBox(self.groupbox_appearance) - self.input_checkbox_systray_offset_use.setObjectName("input_checkbox_systray_offset_use") - self.gridLayout_17.addWidget(self.input_checkbox_systray_offset_use, 7, 0, 1, 2) - self.gridLayout_20 = QtWidgets.QGridLayout() - self.gridLayout_20.setObjectName("gridLayout_20") - self.label_fullscreen_display = QtWidgets.QLabel(self.groupbox_appearance) - self.label_fullscreen_display.setObjectName("label_fullscreen_display") - self.gridLayout_20.addWidget(self.label_fullscreen_display, 1, 0, 1, 1) - self.label_offset_statuswindow = QtWidgets.QLabel(self.groupbox_appearance) - self.label_offset_statuswindow.setObjectName("label_offset_statuswindow") - self.gridLayout_20.addWidget(self.label_offset_statuswindow, 0, 0, 1, 1) - self.input_spinbox_systray_offset = QtWidgets.QSpinBox(self.groupbox_appearance) - self.input_spinbox_systray_offset.setObjectName("input_spinbox_systray_offset") - self.gridLayout_20.addWidget(self.input_spinbox_systray_offset, 0, 1, 1, 1) - self.input_combobox_fullscreen_display = QtWidgets.QComboBox(self.groupbox_appearance) - self.input_combobox_fullscreen_display.setObjectName("input_combobox_fullscreen_display") - self.gridLayout_20.addWidget(self.input_combobox_fullscreen_display, 1, 1, 1, 1) - self.gridLayout_17.addLayout(self.gridLayout_20, 12, 0, 1, 1) - self.input_radiobutton_fullscreen = QtWidgets.QRadioButton(self.groupbox_appearance) - self.input_radiobutton_fullscreen.setObjectName("input_radiobutton_fullscreen") - self.gridLayout_17.addWidget(self.input_radiobutton_fullscreen, 5, 0, 1, 1) - self.input_radiobutton_statusbar_floating = QtWidgets.QRadioButton(self.groupbox_appearance) - self.input_radiobutton_statusbar_floating.setObjectName("input_radiobutton_statusbar_floating") - self.gridLayout_17.addWidget(self.input_radiobutton_statusbar_floating, 0, 0, 1, 1) - self.input_radiobutton_windowed = QtWidgets.QRadioButton(self.groupbox_appearance) - self.input_radiobutton_windowed.setObjectName("input_radiobutton_windowed") - self.gridLayout_17.addWidget(self.input_radiobutton_windowed, 4, 0, 1, 1) - self.gridLayout_2.addWidget(self.groupbox_appearance, 2, 0, 1, 1) - self.groupbox_font = QtWidgets.QGroupBox(self.tab_display) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.groupbox_font.sizePolicy().hasHeightForWidth()) - self.groupbox_font.setSizePolicy(sizePolicy) - self.groupbox_font.setObjectName("groupbox_font") - self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.groupbox_font) - self.verticalLayout_3.setContentsMargins(10, 10, 10, 10) - self.verticalLayout_3.setSpacing(5) - self.verticalLayout_3.setObjectName("verticalLayout_3") - self.verticalLayout_13 = QtWidgets.QVBoxLayout() - self.verticalLayout_13.setContentsMargins(5, 5, 5, 5) - self.verticalLayout_13.setSpacing(5) - self.verticalLayout_13.setObjectName("verticalLayout_13") - self.label_font = QtWidgets.QLabel(self.groupbox_font) - self.label_font.setFrameShape(QtWidgets.QFrame.Box) - self.label_font.setFrameShadow(QtWidgets.QFrame.Sunken) - self.label_font.setWordWrap(True) - self.label_font.setObjectName("label_font") - self.verticalLayout_13.addWidget(self.label_font) - self.horizontalLayout_6 = QtWidgets.QHBoxLayout() - self.horizontalLayout_6.setContentsMargins(5, 5, 5, 5) - self.horizontalLayout_6.setObjectName("horizontalLayout_6") - self.button_fontchooser = QtWidgets.QPushButton(self.groupbox_font) - self.button_fontchooser.setObjectName("button_fontchooser") - self.horizontalLayout_6.addWidget(self.button_fontchooser) - self.button_default_font = QtWidgets.QPushButton(self.groupbox_font) - self.button_default_font.setObjectName("button_default_font") - self.horizontalLayout_6.addWidget(self.button_default_font) - self.verticalLayout_13.addLayout(self.horizontalLayout_6) - self.verticalLayout_3.addLayout(self.verticalLayout_13) - self.gridLayout_2.addWidget(self.groupbox_font, 4, 0, 1, 1) - self.tabs.addTab(self.tab_display, "") - self.tab_filters = QtWidgets.QWidget() - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.tab_filters.sizePolicy().hasHeightForWidth()) - self.tab_filters.setSizePolicy(sizePolicy) - self.tab_filters.setObjectName("tab_filters") - self.gridLayout_4 = QtWidgets.QGridLayout(self.tab_filters) - self.gridLayout_4.setContentsMargins(10, 10, 10, 10) - self.gridLayout_4.setSpacing(5) - self.gridLayout_4.setObjectName("gridLayout_4") - self.input_checkbox_re_attempt_enabled = QtWidgets.QCheckBox(self.tab_filters) - self.input_checkbox_re_attempt_enabled.setObjectName("input_checkbox_re_attempt_enabled") - self.gridLayout_4.addWidget(self.input_checkbox_re_attempt_enabled, 17, 0, 1, 1) - self.input_checkbox_re_service_enabled = QtWidgets.QCheckBox(self.tab_filters) - self.input_checkbox_re_service_enabled.setObjectName("input_checkbox_re_service_enabled") - self.gridLayout_4.addWidget(self.input_checkbox_re_service_enabled, 8, 0, 1, 1) - self.layout_re_status_information = QtWidgets.QHBoxLayout() - self.layout_re_status_information.setContentsMargins(5, 5, 5, 5) - self.layout_re_status_information.setSpacing(5) - self.layout_re_status_information.setObjectName("layout_re_status_information") - self.input_lineedit_re_status_information_pattern = QtWidgets.QLineEdit(self.tab_filters) - self.input_lineedit_re_status_information_pattern.setObjectName("input_lineedit_re_status_information_pattern") - self.layout_re_status_information.addWidget(self.input_lineedit_re_status_information_pattern) - self.input_checkbox_re_status_information_reverse = QtWidgets.QCheckBox(self.tab_filters) - self.input_checkbox_re_status_information_reverse.setObjectName("input_checkbox_re_status_information_reverse") - self.layout_re_status_information.addWidget(self.input_checkbox_re_status_information_reverse) - self.gridLayout_4.addLayout(self.layout_re_status_information, 12, 0, 2, 1) - spacerItem5 = QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.gridLayout_4.addItem(spacerItem5, 24, 0, 1, 1) - self.layout_re_duration = QtWidgets.QHBoxLayout() - self.layout_re_duration.setContentsMargins(5, 5, 5, 5) - self.layout_re_duration.setSpacing(5) - self.layout_re_duration.setObjectName("layout_re_duration") - self.input_lineedit_re_duration_pattern = QtWidgets.QLineEdit(self.tab_filters) - self.input_lineedit_re_duration_pattern.setObjectName("input_lineedit_re_duration_pattern") - self.layout_re_duration.addWidget(self.input_lineedit_re_duration_pattern) - self.input_checkbox_re_duration_reverse = QtWidgets.QCheckBox(self.tab_filters) - self.input_checkbox_re_duration_reverse.setObjectName("input_checkbox_re_duration_reverse") - self.layout_re_duration.addWidget(self.input_checkbox_re_duration_reverse) - self.gridLayout_4.addLayout(self.layout_re_duration, 15, 0, 1, 1) - self.input_checkbox_re_status_information_enabled = QtWidgets.QCheckBox(self.tab_filters) - self.input_checkbox_re_status_information_enabled.setObjectName("input_checkbox_re_status_information_enabled") - self.gridLayout_4.addWidget(self.input_checkbox_re_status_information_enabled, 11, 0, 1, 1) - self.horizontalLayout_10 = QtWidgets.QHBoxLayout() - self.horizontalLayout_10.setContentsMargins(5, 5, 5, 5) - self.horizontalLayout_10.setSpacing(5) - self.horizontalLayout_10.setObjectName("horizontalLayout_10") - self.input_lineedit_re_service_pattern = QtWidgets.QLineEdit(self.tab_filters) - self.input_lineedit_re_service_pattern.setObjectName("input_lineedit_re_service_pattern") - self.horizontalLayout_10.addWidget(self.input_lineedit_re_service_pattern) - self.input_checkbox_re_service_reverse = QtWidgets.QCheckBox(self.tab_filters) - self.input_checkbox_re_service_reverse.setObjectName("input_checkbox_re_service_reverse") - self.horizontalLayout_10.addWidget(self.input_checkbox_re_service_reverse) - self.gridLayout_4.addLayout(self.horizontalLayout_10, 9, 0, 2, 1) - self.input_checkbox_re_groups_enabled = QtWidgets.QCheckBox(self.tab_filters) - self.input_checkbox_re_groups_enabled.setObjectName("input_checkbox_re_groups_enabled") - self.gridLayout_4.addWidget(self.input_checkbox_re_groups_enabled, 20, 0, 1, 1) - self.layout_re_attempt = QtWidgets.QHBoxLayout() - self.layout_re_attempt.setContentsMargins(5, 5, 5, 5) - self.layout_re_attempt.setSpacing(5) - self.layout_re_attempt.setObjectName("layout_re_attempt") - self.input_lineedit_re_attempt_pattern = QtWidgets.QLineEdit(self.tab_filters) - self.input_lineedit_re_attempt_pattern.setObjectName("input_lineedit_re_attempt_pattern") - self.layout_re_attempt.addWidget(self.input_lineedit_re_attempt_pattern) - self.input_checkbox_re_attempt_reverse = QtWidgets.QCheckBox(self.tab_filters) - self.input_checkbox_re_attempt_reverse.setObjectName("input_checkbox_re_attempt_reverse") - self.layout_re_attempt.addWidget(self.input_checkbox_re_attempt_reverse) - self.gridLayout_4.addLayout(self.layout_re_attempt, 18, 0, 1, 1) - self.input_checkbox_re_duration_enabled = QtWidgets.QCheckBox(self.tab_filters) - self.input_checkbox_re_duration_enabled.setObjectName("input_checkbox_re_duration_enabled") - self.gridLayout_4.addWidget(self.input_checkbox_re_duration_enabled, 14, 0, 1, 1) - self.layout_re_groups = QtWidgets.QHBoxLayout() - self.layout_re_groups.setContentsMargins(5, 5, 5, 5) - self.layout_re_groups.setSpacing(5) - self.layout_re_groups.setObjectName("layout_re_groups") - self.input_lineedit_re_groups_pattern = QtWidgets.QLineEdit(self.tab_filters) - self.input_lineedit_re_groups_pattern.setObjectName("input_lineedit_re_groups_pattern") - self.layout_re_groups.addWidget(self.input_lineedit_re_groups_pattern) - self.input_checkbox_re_groups_reverse = QtWidgets.QCheckBox(self.tab_filters) - self.input_checkbox_re_groups_reverse.setObjectName("input_checkbox_re_groups_reverse") - self.layout_re_groups.addWidget(self.input_checkbox_re_groups_reverse) - self.gridLayout_4.addLayout(self.layout_re_groups, 21, 0, 1, 1) - self.label_python_re = QtWidgets.QLabel(self.tab_filters) - self.label_python_re.setOpenExternalLinks(True) - self.label_python_re.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) - self.label_python_re.setObjectName("label_python_re") - self.gridLayout_4.addWidget(self.label_python_re, 23, 0, 1, 1) - self.input_checkbox_re_host_enabled = QtWidgets.QCheckBox(self.tab_filters) - self.input_checkbox_re_host_enabled.setObjectName("input_checkbox_re_host_enabled") - self.gridLayout_4.addWidget(self.input_checkbox_re_host_enabled, 3, 0, 1, 1) - self.groupbox_filters = QtWidgets.QGroupBox(self.tab_filters) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.groupbox_filters.sizePolicy().hasHeightForWidth()) - self.groupbox_filters.setSizePolicy(sizePolicy) - self.groupbox_filters.setObjectName("groupbox_filters") - self.gridLayout_5 = QtWidgets.QGridLayout(self.groupbox_filters) - self.gridLayout_5.setContentsMargins(10, 10, 10, 10) - self.gridLayout_5.setHorizontalSpacing(10) - self.gridLayout_5.setVerticalSpacing(5) - self.gridLayout_5.setObjectName("gridLayout_5") - self.input_checkbox_filter_all_down_hosts = QtWidgets.QCheckBox(self.groupbox_filters) - self.input_checkbox_filter_all_down_hosts.setObjectName("input_checkbox_filter_all_down_hosts") - self.gridLayout_5.addWidget(self.input_checkbox_filter_all_down_hosts, 0, 0, 1, 1) - self.input_checkbox_filter_all_unreachable_hosts = QtWidgets.QCheckBox(self.groupbox_filters) - self.input_checkbox_filter_all_unreachable_hosts.setObjectName("input_checkbox_filter_all_unreachable_hosts") - self.gridLayout_5.addWidget(self.input_checkbox_filter_all_unreachable_hosts, 1, 0, 1, 1) - self.input_checkbox_filter_all_unreachable_services = QtWidgets.QCheckBox(self.groupbox_filters) - self.input_checkbox_filter_all_unreachable_services.setObjectName("input_checkbox_filter_all_unreachable_services") - self.gridLayout_5.addWidget(self.input_checkbox_filter_all_unreachable_services, 2, 0, 1, 1) - self.input_checkbox_filter_all_flapping_hosts = QtWidgets.QCheckBox(self.groupbox_filters) - self.input_checkbox_filter_all_flapping_hosts.setObjectName("input_checkbox_filter_all_flapping_hosts") - self.gridLayout_5.addWidget(self.input_checkbox_filter_all_flapping_hosts, 3, 0, 1, 1) - self.input_checkbox_filter_all_critical_services = QtWidgets.QCheckBox(self.groupbox_filters) - self.input_checkbox_filter_all_critical_services.setObjectName("input_checkbox_filter_all_critical_services") - self.gridLayout_5.addWidget(self.input_checkbox_filter_all_critical_services, 4, 0, 1, 1) - self.input_checkbox_filter_acknowledged_hosts_services = QtWidgets.QCheckBox(self.groupbox_filters) - self.input_checkbox_filter_acknowledged_hosts_services.setObjectName("input_checkbox_filter_acknowledged_hosts_services") - self.gridLayout_5.addWidget(self.input_checkbox_filter_acknowledged_hosts_services, 0, 1, 1, 1) - self.input_checkbox_filter_hosts_services_disabled_notifications = QtWidgets.QCheckBox(self.groupbox_filters) - self.input_checkbox_filter_hosts_services_disabled_notifications.setObjectName("input_checkbox_filter_hosts_services_disabled_notifications") - self.gridLayout_5.addWidget(self.input_checkbox_filter_hosts_services_disabled_notifications, 1, 1, 1, 1) - self.input_checkbox_filter_hosts_services_disabled_checks = QtWidgets.QCheckBox(self.groupbox_filters) - self.input_checkbox_filter_hosts_services_disabled_checks.setObjectName("input_checkbox_filter_hosts_services_disabled_checks") - self.gridLayout_5.addWidget(self.input_checkbox_filter_hosts_services_disabled_checks, 2, 1, 1, 1) - self.input_checkbox_filter_hosts_services_maintenance = QtWidgets.QCheckBox(self.groupbox_filters) - self.input_checkbox_filter_hosts_services_maintenance.setObjectName("input_checkbox_filter_hosts_services_maintenance") - self.gridLayout_5.addWidget(self.input_checkbox_filter_hosts_services_maintenance, 3, 1, 1, 1) - self.input_checkbox_filter_services_on_acknowledged_hosts = QtWidgets.QCheckBox(self.groupbox_filters) - self.input_checkbox_filter_services_on_acknowledged_hosts.setObjectName("input_checkbox_filter_services_on_acknowledged_hosts") - self.gridLayout_5.addWidget(self.input_checkbox_filter_services_on_acknowledged_hosts, 4, 1, 1, 1) - self.input_checkbox_filter_services_on_down_hosts = QtWidgets.QCheckBox(self.groupbox_filters) - self.input_checkbox_filter_services_on_down_hosts.setObjectName("input_checkbox_filter_services_on_down_hosts") - self.gridLayout_5.addWidget(self.input_checkbox_filter_services_on_down_hosts, 5, 1, 1, 1) - self.input_checkbox_filter_all_high_services = QtWidgets.QCheckBox(self.groupbox_filters) - self.input_checkbox_filter_all_high_services.setObjectName("input_checkbox_filter_all_high_services") - self.gridLayout_5.addWidget(self.input_checkbox_filter_all_high_services, 13, 0, 1, 1) - self.input_checkbox_filter_all_disaster_services = QtWidgets.QCheckBox(self.groupbox_filters) - self.input_checkbox_filter_all_disaster_services.setObjectName("input_checkbox_filter_all_disaster_services") - self.gridLayout_5.addWidget(self.input_checkbox_filter_all_disaster_services, 11, 0, 1, 1) - self.input_checkbox_filter_all_flapping_services = QtWidgets.QCheckBox(self.groupbox_filters) - self.input_checkbox_filter_all_flapping_services.setObjectName("input_checkbox_filter_all_flapping_services") - self.gridLayout_5.addWidget(self.input_checkbox_filter_all_flapping_services, 5, 0, 1, 1) - self.input_checkbox_filter_all_unknown_services = QtWidgets.QCheckBox(self.groupbox_filters) - self.input_checkbox_filter_all_unknown_services.setObjectName("input_checkbox_filter_all_unknown_services") - self.gridLayout_5.addWidget(self.input_checkbox_filter_all_unknown_services, 6, 0, 1, 1) - self.input_checkbox_filter_all_warning_services = QtWidgets.QCheckBox(self.groupbox_filters) - self.input_checkbox_filter_all_warning_services.setObjectName("input_checkbox_filter_all_warning_services") - self.gridLayout_5.addWidget(self.input_checkbox_filter_all_warning_services, 7, 0, 1, 1) - self.input_checkbox_filter_all_information_services = QtWidgets.QCheckBox(self.groupbox_filters) - self.input_checkbox_filter_all_information_services.setObjectName("input_checkbox_filter_all_information_services") - self.gridLayout_5.addWidget(self.input_checkbox_filter_all_information_services, 9, 0, 1, 1) - self.input_checkbox_filter_all_average_services = QtWidgets.QCheckBox(self.groupbox_filters) - self.input_checkbox_filter_all_average_services.setObjectName("input_checkbox_filter_all_average_services") - self.gridLayout_5.addWidget(self.input_checkbox_filter_all_average_services, 10, 0, 1, 1) - self.input_checkbox_filter_services_on_hosts_in_maintenance = QtWidgets.QCheckBox(self.groupbox_filters) - self.input_checkbox_filter_services_on_hosts_in_maintenance.setObjectName("input_checkbox_filter_services_on_hosts_in_maintenance") - self.gridLayout_5.addWidget(self.input_checkbox_filter_services_on_hosts_in_maintenance, 6, 1, 1, 1) - self.input_checkbox_filter_services_on_unreachable_hosts = QtWidgets.QCheckBox(self.groupbox_filters) - self.input_checkbox_filter_services_on_unreachable_hosts.setObjectName("input_checkbox_filter_services_on_unreachable_hosts") - self.gridLayout_5.addWidget(self.input_checkbox_filter_services_on_unreachable_hosts, 7, 1, 1, 1) - self.input_checkbox_filter_hosts_in_soft_state = QtWidgets.QCheckBox(self.groupbox_filters) - self.input_checkbox_filter_hosts_in_soft_state.setObjectName("input_checkbox_filter_hosts_in_soft_state") - self.gridLayout_5.addWidget(self.input_checkbox_filter_hosts_in_soft_state, 9, 1, 1, 1) - self.input_checkbox_filter_services_in_soft_state = QtWidgets.QCheckBox(self.groupbox_filters) - self.input_checkbox_filter_services_in_soft_state.setObjectName("input_checkbox_filter_services_in_soft_state") - self.gridLayout_5.addWidget(self.input_checkbox_filter_services_in_soft_state, 10, 1, 1, 1) - self.gridLayout_4.addWidget(self.groupbox_filters, 1, 0, 1, 1) - self.horizontalLayout_9 = QtWidgets.QHBoxLayout() - self.horizontalLayout_9.setContentsMargins(5, 5, 5, 5) - self.horizontalLayout_9.setSpacing(5) - self.horizontalLayout_9.setObjectName("horizontalLayout_9") - self.input_lineedit_re_host_pattern = QtWidgets.QLineEdit(self.tab_filters) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.input_lineedit_re_host_pattern.sizePolicy().hasHeightForWidth()) - self.input_lineedit_re_host_pattern.setSizePolicy(sizePolicy) - self.input_lineedit_re_host_pattern.setObjectName("input_lineedit_re_host_pattern") - self.horizontalLayout_9.addWidget(self.input_lineedit_re_host_pattern) - self.input_checkbox_re_host_reverse = QtWidgets.QCheckBox(self.tab_filters) - self.input_checkbox_re_host_reverse.setObjectName("input_checkbox_re_host_reverse") - self.horizontalLayout_9.addWidget(self.input_checkbox_re_host_reverse) - self.gridLayout_4.addLayout(self.horizontalLayout_9, 4, 0, 2, 1) - self.tabs.addTab(self.tab_filters, "") - self.tab_actions = QtWidgets.QWidget() - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.tab_actions.sizePolicy().hasHeightForWidth()) - self.tab_actions.setSizePolicy(sizePolicy) - self.tab_actions.setObjectName("tab_actions") - self.gridLayout_6 = QtWidgets.QGridLayout(self.tab_actions) - self.gridLayout_6.setContentsMargins(10, 10, 10, 10) - self.gridLayout_6.setSpacing(5) - self.gridLayout_6.setObjectName("gridLayout_6") - self.list_actions = QtWidgets.QListWidget(self.tab_actions) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.list_actions.sizePolicy().hasHeightForWidth()) - self.list_actions.setSizePolicy(sizePolicy) - self.list_actions.setMinimumSize(QtCore.QSize(0, 200)) - self.list_actions.setResizeMode(QtWidgets.QListView.Adjust) - self.list_actions.setObjectName("list_actions") - self.gridLayout_6.addWidget(self.list_actions, 0, 0, 1, 1) - self.verticalLayout_4 = QtWidgets.QVBoxLayout() - self.verticalLayout_4.setContentsMargins(0, 5, 0, 5) - self.verticalLayout_4.setSpacing(5) - self.verticalLayout_4.setObjectName("verticalLayout_4") - self.button_new_action = QtWidgets.QPushButton(self.tab_actions) - self.button_new_action.setObjectName("button_new_action") - self.verticalLayout_4.addWidget(self.button_new_action) - self.button_edit_action = QtWidgets.QPushButton(self.tab_actions) - self.button_edit_action.setObjectName("button_edit_action") - self.verticalLayout_4.addWidget(self.button_edit_action) - self.button_copy_action = QtWidgets.QPushButton(self.tab_actions) - self.button_copy_action.setObjectName("button_copy_action") - self.verticalLayout_4.addWidget(self.button_copy_action) - self.button_delete_action = QtWidgets.QPushButton(self.tab_actions) - self.button_delete_action.setObjectName("button_delete_action") - self.verticalLayout_4.addWidget(self.button_delete_action) - spacerItem6 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.verticalLayout_4.addItem(spacerItem6) - self.gridLayout_6.addLayout(self.verticalLayout_4, 0, 2, 1, 1) - self.groupbox_connection_method = QtWidgets.QGroupBox(self.tab_actions) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.groupbox_connection_method.sizePolicy().hasHeightForWidth()) - self.groupbox_connection_method.setSizePolicy(sizePolicy) - self.groupbox_connection_method.setObjectName("groupbox_connection_method") - self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.groupbox_connection_method) - self.verticalLayout_5.setContentsMargins(10, 10, 10, 10) - self.verticalLayout_5.setSpacing(5) - self.verticalLayout_5.setObjectName("verticalLayout_5") - self.input_radiobutton_connect_by_host = QtWidgets.QRadioButton(self.groupbox_connection_method) - self.input_radiobutton_connect_by_host.setObjectName("input_radiobutton_connect_by_host") - self.verticalLayout_5.addWidget(self.input_radiobutton_connect_by_host) - self.input_radiobutton_connect_by_dns = QtWidgets.QRadioButton(self.groupbox_connection_method) - self.input_radiobutton_connect_by_dns.setObjectName("input_radiobutton_connect_by_dns") - self.verticalLayout_5.addWidget(self.input_radiobutton_connect_by_dns) - self.input_radiobutton_connect_by_ip = QtWidgets.QRadioButton(self.groupbox_connection_method) - self.input_radiobutton_connect_by_ip.setObjectName("input_radiobutton_connect_by_ip") - self.verticalLayout_5.addWidget(self.input_radiobutton_connect_by_ip) - self.gridLayout_6.addWidget(self.groupbox_connection_method, 1, 0, 1, 1) - spacerItem7 = QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.gridLayout_6.addItem(spacerItem7, 3, 0, 1, 1) - self.groupbox_browser = QtWidgets.QGroupBox(self.tab_actions) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.groupbox_browser.sizePolicy().hasHeightForWidth()) - self.groupbox_browser.setSizePolicy(sizePolicy) - self.groupbox_browser.setObjectName("groupbox_browser") - self.verticalLayout_14 = QtWidgets.QVBoxLayout(self.groupbox_browser) - self.verticalLayout_14.setContentsMargins(10, 10, 10, 10) - self.verticalLayout_14.setSpacing(5) - self.verticalLayout_14.setObjectName("verticalLayout_14") - self.input_radiobutton_use_default_browser = QtWidgets.QRadioButton(self.groupbox_browser) - self.input_radiobutton_use_default_browser.setObjectName("input_radiobutton_use_default_browser") - self.verticalLayout_14.addWidget(self.input_radiobutton_use_default_browser) - self.input_radiobutton_use_custom_browser = QtWidgets.QRadioButton(self.groupbox_browser) - self.input_radiobutton_use_custom_browser.setObjectName("input_radiobutton_use_custom_browser") - self.verticalLayout_14.addWidget(self.input_radiobutton_use_custom_browser) - self.groupbox_custom_browser = QtWidgets.QGroupBox(self.groupbox_browser) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.groupbox_custom_browser.sizePolicy().hasHeightForWidth()) - self.groupbox_custom_browser.setSizePolicy(sizePolicy) - self.groupbox_custom_browser.setObjectName("groupbox_custom_browser") - self.gridLayout_19 = QtWidgets.QGridLayout(self.groupbox_custom_browser) - self.gridLayout_19.setContentsMargins(10, 10, 10, 10) - self.gridLayout_19.setSpacing(5) - self.gridLayout_19.setObjectName("gridLayout_19") - self.input_lineedit_custom_browser = QtWidgets.QLineEdit(self.groupbox_custom_browser) - self.input_lineedit_custom_browser.setObjectName("input_lineedit_custom_browser") - self.gridLayout_19.addWidget(self.input_lineedit_custom_browser, 0, 0, 1, 1) - self.button_choose_browser = QtWidgets.QPushButton(self.groupbox_custom_browser) - self.button_choose_browser.setObjectName("button_choose_browser") - self.gridLayout_19.addWidget(self.button_choose_browser, 0, 1, 1, 1) - self.verticalLayout_14.addWidget(self.groupbox_custom_browser) - self.gridLayout_6.addWidget(self.groupbox_browser, 2, 0, 1, 1) - self.tabs.addTab(self.tab_actions, "") - self.tab_notifications = QtWidgets.QWidget() - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.tab_notifications.sizePolicy().hasHeightForWidth()) - self.tab_notifications.setSizePolicy(sizePolicy) - self.tab_notifications.setObjectName("tab_notifications") - self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.tab_notifications) - self.verticalLayout_6.setContentsMargins(10, 10, 10, 10) - self.verticalLayout_6.setSpacing(5) - self.verticalLayout_6.setObjectName("verticalLayout_6") - self.input_checkbox_notification = QtWidgets.QCheckBox(self.tab_notifications) - self.input_checkbox_notification.setObjectName("input_checkbox_notification") - self.verticalLayout_6.addWidget(self.input_checkbox_notification) - self.notification_groupbox = QtWidgets.QGroupBox(self.tab_notifications) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.notification_groupbox.sizePolicy().hasHeightForWidth()) - self.notification_groupbox.setSizePolicy(sizePolicy) - self.notification_groupbox.setObjectName("notification_groupbox") - self.verticalLayout_7 = QtWidgets.QVBoxLayout(self.notification_groupbox) - self.verticalLayout_7.setContentsMargins(10, 10, 10, 10) - self.verticalLayout_7.setSpacing(5) - self.verticalLayout_7.setObjectName("verticalLayout_7") - self.horizontalLayout_notifications = QtWidgets.QHBoxLayout() - self.horizontalLayout_notifications.setContentsMargins(0, 0, 0, 0) - self.horizontalLayout_notifications.setSpacing(5) - self.horizontalLayout_notifications.setObjectName("horizontalLayout_notifications") - self.input_checkbox_notify_if_warning = QtWidgets.QCheckBox(self.notification_groupbox) - self.input_checkbox_notify_if_warning.setObjectName("input_checkbox_notify_if_warning") - self.horizontalLayout_notifications.addWidget(self.input_checkbox_notify_if_warning) - self.input_checkbox_notify_if_critical = QtWidgets.QCheckBox(self.notification_groupbox) - self.input_checkbox_notify_if_critical.setObjectName("input_checkbox_notify_if_critical") - self.horizontalLayout_notifications.addWidget(self.input_checkbox_notify_if_critical) - self.input_checkbox_notify_if_unknown = QtWidgets.QCheckBox(self.notification_groupbox) - self.input_checkbox_notify_if_unknown.setObjectName("input_checkbox_notify_if_unknown") - self.horizontalLayout_notifications.addWidget(self.input_checkbox_notify_if_unknown) - self.input_checkbox_notify_if_unreachable = QtWidgets.QCheckBox(self.notification_groupbox) - self.input_checkbox_notify_if_unreachable.setObjectName("input_checkbox_notify_if_unreachable") - self.horizontalLayout_notifications.addWidget(self.input_checkbox_notify_if_unreachable) - self.input_checkbox_notify_if_down = QtWidgets.QCheckBox(self.notification_groupbox) - self.input_checkbox_notify_if_down.setObjectName("input_checkbox_notify_if_down") - self.horizontalLayout_notifications.addWidget(self.input_checkbox_notify_if_down) - spacerItem8 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.horizontalLayout_notifications.addItem(spacerItem8) - self.verticalLayout_7.addLayout(self.horizontalLayout_notifications) - self.horizontalLayout_notifications_zabbix = QtWidgets.QHBoxLayout() - self.horizontalLayout_notifications_zabbix.setObjectName("horizontalLayout_notifications_zabbix") - self.input_checkbox_notify_if_information = QtWidgets.QCheckBox(self.notification_groupbox) - self.input_checkbox_notify_if_information.setObjectName("input_checkbox_notify_if_information") - self.horizontalLayout_notifications_zabbix.addWidget(self.input_checkbox_notify_if_information) - self.input_checkbox_notify_if_disaster = QtWidgets.QCheckBox(self.notification_groupbox) - self.input_checkbox_notify_if_disaster.setObjectName("input_checkbox_notify_if_disaster") - self.horizontalLayout_notifications_zabbix.addWidget(self.input_checkbox_notify_if_disaster) - self.input_checkbox_notify_if_average = QtWidgets.QCheckBox(self.notification_groupbox) - self.input_checkbox_notify_if_average.setObjectName("input_checkbox_notify_if_average") - self.horizontalLayout_notifications_zabbix.addWidget(self.input_checkbox_notify_if_average) - self.input_checkbox_notify_if_high = QtWidgets.QCheckBox(self.notification_groupbox) - self.input_checkbox_notify_if_high.setObjectName("input_checkbox_notify_if_high") - self.horizontalLayout_notifications_zabbix.addWidget(self.input_checkbox_notify_if_high) - spacerItem9 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.horizontalLayout_notifications_zabbix.addItem(spacerItem9) - self.verticalLayout_7.addLayout(self.horizontalLayout_notifications_zabbix) - self.input_checkbox_notification_flashing = QtWidgets.QCheckBox(self.notification_groupbox) - self.input_checkbox_notification_flashing.setObjectName("input_checkbox_notification_flashing") - self.verticalLayout_7.addWidget(self.input_checkbox_notification_flashing) - self.input_checkbox_notification_desktop = QtWidgets.QCheckBox(self.notification_groupbox) - self.input_checkbox_notification_desktop.setObjectName("input_checkbox_notification_desktop") - self.verticalLayout_7.addWidget(self.input_checkbox_notification_desktop) - self.input_checkbox_notification_sound = QtWidgets.QCheckBox(self.notification_groupbox) - self.input_checkbox_notification_sound.setObjectName("input_checkbox_notification_sound") - self.verticalLayout_7.addWidget(self.input_checkbox_notification_sound) - self.notification_sounds_groupbox = QtWidgets.QGroupBox(self.notification_groupbox) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.notification_sounds_groupbox.sizePolicy().hasHeightForWidth()) - self.notification_sounds_groupbox.setSizePolicy(sizePolicy) - self.notification_sounds_groupbox.setObjectName("notification_sounds_groupbox") - self.gridLayout_16 = QtWidgets.QGridLayout(self.notification_sounds_groupbox) - self.gridLayout_16.setContentsMargins(10, 10, 10, 10) - self.gridLayout_16.setSpacing(5) - self.gridLayout_16.setObjectName("gridLayout_16") - self.notification_custom_sounds_groupbox = QtWidgets.QGroupBox(self.notification_sounds_groupbox) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.notification_custom_sounds_groupbox.sizePolicy().hasHeightForWidth()) - self.notification_custom_sounds_groupbox.setSizePolicy(sizePolicy) - self.notification_custom_sounds_groupbox.setObjectName("notification_custom_sounds_groupbox") - self.gridLayout_8 = QtWidgets.QGridLayout(self.notification_custom_sounds_groupbox) - self.gridLayout_8.setContentsMargins(10, 10, 10, 10) - self.gridLayout_8.setSpacing(5) - self.gridLayout_8.setObjectName("gridLayout_8") - self.button_choose_critical = QtWidgets.QPushButton(self.notification_custom_sounds_groupbox) - self.button_choose_critical.setObjectName("button_choose_critical") - self.gridLayout_8.addWidget(self.button_choose_critical, 1, 2, 1, 1) - self.button_play_warning = QtWidgets.QPushButton(self.notification_custom_sounds_groupbox) - self.button_play_warning.setObjectName("button_play_warning") - self.gridLayout_8.addWidget(self.button_play_warning, 0, 3, 1, 1) - self.label_notification_sound_options_custom_sounds_warning = QtWidgets.QLabel(self.notification_custom_sounds_groupbox) - self.label_notification_sound_options_custom_sounds_warning.setObjectName("label_notification_sound_options_custom_sounds_warning") - self.gridLayout_8.addWidget(self.label_notification_sound_options_custom_sounds_warning, 0, 0, 1, 1) - self.label_notification_sound_options_custom_sounds_critical = QtWidgets.QLabel(self.notification_custom_sounds_groupbox) - self.label_notification_sound_options_custom_sounds_critical.setObjectName("label_notification_sound_options_custom_sounds_critical") - self.gridLayout_8.addWidget(self.label_notification_sound_options_custom_sounds_critical, 1, 0, 1, 1) - self.button_choose_down = QtWidgets.QPushButton(self.notification_custom_sounds_groupbox) - self.button_choose_down.setObjectName("button_choose_down") - self.gridLayout_8.addWidget(self.button_choose_down, 2, 2, 1, 1) - self.button_choose_warning = QtWidgets.QPushButton(self.notification_custom_sounds_groupbox) - self.button_choose_warning.setObjectName("button_choose_warning") - self.gridLayout_8.addWidget(self.button_choose_warning, 0, 2, 1, 1) - self.button_play_critical = QtWidgets.QPushButton(self.notification_custom_sounds_groupbox) - self.button_play_critical.setObjectName("button_play_critical") - self.gridLayout_8.addWidget(self.button_play_critical, 1, 3, 1, 1) - self.label_notification_sound_options_custom_sounds_down = QtWidgets.QLabel(self.notification_custom_sounds_groupbox) - self.label_notification_sound_options_custom_sounds_down.setObjectName("label_notification_sound_options_custom_sounds_down") - self.gridLayout_8.addWidget(self.label_notification_sound_options_custom_sounds_down, 2, 0, 1, 1) - self.button_play_down = QtWidgets.QPushButton(self.notification_custom_sounds_groupbox) - self.button_play_down.setObjectName("button_play_down") - self.gridLayout_8.addWidget(self.button_play_down, 2, 3, 1, 1) - self.input_lineedit_notification_custom_sound_warning = QtWidgets.QLineEdit(self.notification_custom_sounds_groupbox) - self.input_lineedit_notification_custom_sound_warning.setObjectName("input_lineedit_notification_custom_sound_warning") - self.gridLayout_8.addWidget(self.input_lineedit_notification_custom_sound_warning, 0, 1, 1, 1) - self.input_lineedit_notification_custom_sound_critical = QtWidgets.QLineEdit(self.notification_custom_sounds_groupbox) - self.input_lineedit_notification_custom_sound_critical.setObjectName("input_lineedit_notification_custom_sound_critical") - self.gridLayout_8.addWidget(self.input_lineedit_notification_custom_sound_critical, 1, 1, 1, 1) - self.input_lineedit_notification_custom_sound_down = QtWidgets.QLineEdit(self.notification_custom_sounds_groupbox) - self.input_lineedit_notification_custom_sound_down.setObjectName("input_lineedit_notification_custom_sound_down") - self.gridLayout_8.addWidget(self.input_lineedit_notification_custom_sound_down, 2, 1, 1, 1) - self.gridLayout_16.addWidget(self.notification_custom_sounds_groupbox, 3, 0, 1, 2) - self.input_checkbox_notification_sound_repeat = QtWidgets.QCheckBox(self.notification_sounds_groupbox) - self.input_checkbox_notification_sound_repeat.setObjectName("input_checkbox_notification_sound_repeat") - self.gridLayout_16.addWidget(self.input_checkbox_notification_sound_repeat, 0, 0, 1, 2) - self.input_radiobutton_notification_default_sound = QtWidgets.QRadioButton(self.notification_sounds_groupbox) - self.input_radiobutton_notification_default_sound.setObjectName("input_radiobutton_notification_default_sound") - self.gridLayout_16.addWidget(self.input_radiobutton_notification_default_sound, 1, 0, 1, 2) - self.input_radiobutton_notification_custom_sound = QtWidgets.QRadioButton(self.notification_sounds_groupbox) - self.input_radiobutton_notification_custom_sound.setObjectName("input_radiobutton_notification_custom_sound") - self.gridLayout_16.addWidget(self.input_radiobutton_notification_custom_sound, 2, 0, 1, 2) - self.verticalLayout_7.addWidget(self.notification_sounds_groupbox) - self.input_checkbox_notification_actions = QtWidgets.QCheckBox(self.notification_groupbox) - self.input_checkbox_notification_actions.setObjectName("input_checkbox_notification_actions") - self.verticalLayout_7.addWidget(self.input_checkbox_notification_actions) - self.notification_actions_groupbox = QtWidgets.QGroupBox(self.notification_groupbox) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.notification_actions_groupbox.sizePolicy().hasHeightForWidth()) - self.notification_actions_groupbox.setSizePolicy(sizePolicy) - self.notification_actions_groupbox.setObjectName("notification_actions_groupbox") - self.gridLayout_11 = QtWidgets.QGridLayout(self.notification_actions_groupbox) - self.gridLayout_11.setContentsMargins(10, 10, 10, 10) - self.gridLayout_11.setSpacing(5) - self.gridLayout_11.setObjectName("gridLayout_11") - self.input_lineedit_notification_action_ok_string = QtWidgets.QLineEdit(self.notification_actions_groupbox) - self.input_lineedit_notification_action_ok_string.setObjectName("input_lineedit_notification_action_ok_string") - self.gridLayout_11.addWidget(self.input_lineedit_notification_action_ok_string, 3, 2, 1, 1) - self.input_checkbox_notification_action_critical = QtWidgets.QCheckBox(self.notification_actions_groupbox) - self.input_checkbox_notification_action_critical.setObjectName("input_checkbox_notification_action_critical") - self.gridLayout_11.addWidget(self.input_checkbox_notification_action_critical, 1, 0, 1, 1) - self.input_checkbox_notification_action_down = QtWidgets.QCheckBox(self.notification_actions_groupbox) - self.input_checkbox_notification_action_down.setObjectName("input_checkbox_notification_action_down") - self.gridLayout_11.addWidget(self.input_checkbox_notification_action_down, 2, 0, 1, 1) - self.input_checkbox_notification_action_ok = QtWidgets.QCheckBox(self.notification_actions_groupbox) - self.input_checkbox_notification_action_ok.setObjectName("input_checkbox_notification_action_ok") - self.gridLayout_11.addWidget(self.input_checkbox_notification_action_ok, 3, 0, 1, 1) - self.input_checkbox_notification_action_warning = QtWidgets.QCheckBox(self.notification_actions_groupbox) - self.input_checkbox_notification_action_warning.setObjectName("input_checkbox_notification_action_warning") - self.gridLayout_11.addWidget(self.input_checkbox_notification_action_warning, 0, 0, 1, 1) - self.input_lineedit_notification_action_down_string = QtWidgets.QLineEdit(self.notification_actions_groupbox) - self.input_lineedit_notification_action_down_string.setObjectName("input_lineedit_notification_action_down_string") - self.gridLayout_11.addWidget(self.input_lineedit_notification_action_down_string, 2, 2, 1, 1) - self.input_checkbox_notification_custom_action = QtWidgets.QCheckBox(self.notification_actions_groupbox) - self.input_checkbox_notification_custom_action.setObjectName("input_checkbox_notification_custom_action") - self.gridLayout_11.addWidget(self.input_checkbox_notification_custom_action, 4, 0, 1, 3) - self.notification_custom_action_groupbox = QtWidgets.QGroupBox(self.notification_actions_groupbox) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.notification_custom_action_groupbox.sizePolicy().hasHeightForWidth()) - self.notification_custom_action_groupbox.setSizePolicy(sizePolicy) - self.notification_custom_action_groupbox.setObjectName("notification_custom_action_groupbox") - self.gridLayout_7 = QtWidgets.QGridLayout(self.notification_custom_action_groupbox) - self.gridLayout_7.setContentsMargins(10, 10, 10, 10) - self.gridLayout_7.setSpacing(5) - self.gridLayout_7.setObjectName("gridLayout_7") - self.label_notification_custom_action_string = QtWidgets.QLabel(self.notification_custom_action_groupbox) - self.label_notification_custom_action_string.setObjectName("label_notification_custom_action_string") - self.gridLayout_7.addWidget(self.label_notification_custom_action_string, 1, 0, 1, 1) - self.label_notification_custom_action_separator = QtWidgets.QLabel(self.notification_custom_action_groupbox) - self.label_notification_custom_action_separator.setObjectName("label_notification_custom_action_separator") - self.gridLayout_7.addWidget(self.label_notification_custom_action_separator, 4, 0, 1, 1) - self.input_lineedit_notification_custom_action_separator = QtWidgets.QLineEdit(self.notification_custom_action_groupbox) - self.input_lineedit_notification_custom_action_separator.setObjectName("input_lineedit_notification_custom_action_separator") - self.gridLayout_7.addWidget(self.input_lineedit_notification_custom_action_separator, 4, 3, 1, 1) - self.input_checkbox_notification_custom_action_single = QtWidgets.QCheckBox(self.notification_custom_action_groupbox) - self.input_checkbox_notification_custom_action_single.setObjectName("input_checkbox_notification_custom_action_single") - self.gridLayout_7.addWidget(self.input_checkbox_notification_custom_action_single, 2, 3, 1, 1) - self.input_lineedit_notification_custom_action_string = QtWidgets.QLineEdit(self.notification_custom_action_groupbox) - self.input_lineedit_notification_custom_action_string.setObjectName("input_lineedit_notification_custom_action_string") - self.gridLayout_7.addWidget(self.input_lineedit_notification_custom_action_string, 1, 3, 1, 1) - self.gridLayout_11.addWidget(self.notification_custom_action_groupbox, 6, 0, 1, 3) - self.input_lineedit_notification_action_critical_string = QtWidgets.QLineEdit(self.notification_actions_groupbox) - self.input_lineedit_notification_action_critical_string.setObjectName("input_lineedit_notification_action_critical_string") - self.gridLayout_11.addWidget(self.input_lineedit_notification_action_critical_string, 1, 2, 1, 1) - self.input_lineedit_notification_action_warning_string = QtWidgets.QLineEdit(self.notification_actions_groupbox) - self.input_lineedit_notification_action_warning_string.setObjectName("input_lineedit_notification_action_warning_string") - self.gridLayout_11.addWidget(self.input_lineedit_notification_action_warning_string, 0, 2, 1, 1) - self.verticalLayout_7.addWidget(self.notification_actions_groupbox) - self.verticalLayout_6.addWidget(self.notification_groupbox) - spacerItem10 = QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.verticalLayout_6.addItem(spacerItem10) - self.tabs.addTab(self.tab_notifications, "") - self.tab_colors = QtWidgets.QWidget() - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.tab_colors.sizePolicy().hasHeightForWidth()) - self.tab_colors.setSizePolicy(sizePolicy) - self.tab_colors.setObjectName("tab_colors") - self.gridLayout_12 = QtWidgets.QGridLayout(self.tab_colors) - self.gridLayout_12.setContentsMargins(10, 10, 10, 10) - self.gridLayout_12.setSpacing(5) - self.gridLayout_12.setObjectName("gridLayout_12") - spacerItem11 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.gridLayout_12.addItem(spacerItem11, 12, 1, 1, 1) - self.label_colors = QtWidgets.QLabel(self.tab_colors) - self.label_colors.setObjectName("label_colors") - self.gridLayout_12.addWidget(self.label_colors, 0, 0, 1, 3) - self.states_groupbox = QtWidgets.QGroupBox(self.tab_colors) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.states_groupbox.sizePolicy().hasHeightForWidth()) - self.states_groupbox.setSizePolicy(sizePolicy) - self.states_groupbox.setObjectName("states_groupbox") - self.verticalLayout_11 = QtWidgets.QVBoxLayout(self.states_groupbox) - self.verticalLayout_11.setContentsMargins(10, 10, 10, 5) - self.verticalLayout_11.setSpacing(5) - self.verticalLayout_11.setObjectName("verticalLayout_11") - self.label_color_ok = QtWidgets.QLabel(self.states_groupbox) - self.label_color_ok.setObjectName("label_color_ok") - self.verticalLayout_11.addWidget(self.label_color_ok) - self.label_color_information = QtWidgets.QLabel(self.states_groupbox) - self.label_color_information.setObjectName("label_color_information") - self.verticalLayout_11.addWidget(self.label_color_information) - self.label_color_warning = QtWidgets.QLabel(self.states_groupbox) - self.label_color_warning.setObjectName("label_color_warning") - self.verticalLayout_11.addWidget(self.label_color_warning) - self.label_color_average = QtWidgets.QLabel(self.states_groupbox) - self.label_color_average.setObjectName("label_color_average") - self.verticalLayout_11.addWidget(self.label_color_average) - self.label_color_high = QtWidgets.QLabel(self.states_groupbox) - self.label_color_high.setObjectName("label_color_high") - self.verticalLayout_11.addWidget(self.label_color_high) - self.label_color_critical = QtWidgets.QLabel(self.states_groupbox) - self.label_color_critical.setObjectName("label_color_critical") - self.verticalLayout_11.addWidget(self.label_color_critical) - self.label_color_disaster = QtWidgets.QLabel(self.states_groupbox) - self.label_color_disaster.setObjectName("label_color_disaster") - self.verticalLayout_11.addWidget(self.label_color_disaster) - self.label_color_unknown = QtWidgets.QLabel(self.states_groupbox) - self.label_color_unknown.setObjectName("label_color_unknown") - self.verticalLayout_11.addWidget(self.label_color_unknown) - self.label_color_unreachable = QtWidgets.QLabel(self.states_groupbox) - self.label_color_unreachable.setObjectName("label_color_unreachable") - self.verticalLayout_11.addWidget(self.label_color_unreachable) - self.label_color_down = QtWidgets.QLabel(self.states_groupbox) - self.label_color_down.setObjectName("label_color_down") - self.verticalLayout_11.addWidget(self.label_color_down) - self.label_color_error = QtWidgets.QLabel(self.states_groupbox) - self.label_color_error.setObjectName("label_color_error") - self.verticalLayout_11.addWidget(self.label_color_error) - self.gridLayout_12.addWidget(self.states_groupbox, 1, 0, 8, 1) - self.text_groupbox = QtWidgets.QGroupBox(self.tab_colors) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.text_groupbox.sizePolicy().hasHeightForWidth()) - self.text_groupbox.setSizePolicy(sizePolicy) - self.text_groupbox.setObjectName("text_groupbox") - self.verticalLayout_8 = QtWidgets.QVBoxLayout(self.text_groupbox) - self.verticalLayout_8.setContentsMargins(10, 10, 10, 5) - self.verticalLayout_8.setSpacing(5) - self.verticalLayout_8.setObjectName("verticalLayout_8") - self.input_button_color_ok_text = QtWidgets.QPushButton(self.text_groupbox) - self.input_button_color_ok_text.setText("") - self.input_button_color_ok_text.setObjectName("input_button_color_ok_text") - self.verticalLayout_8.addWidget(self.input_button_color_ok_text) - self.input_button_color_information_text = QtWidgets.QPushButton(self.text_groupbox) - self.input_button_color_information_text.setText("") - self.input_button_color_information_text.setObjectName("input_button_color_information_text") - self.verticalLayout_8.addWidget(self.input_button_color_information_text) - self.input_button_color_warning_text = QtWidgets.QPushButton(self.text_groupbox) - self.input_button_color_warning_text.setText("") - self.input_button_color_warning_text.setObjectName("input_button_color_warning_text") - self.verticalLayout_8.addWidget(self.input_button_color_warning_text) - self.input_button_color_average_text = QtWidgets.QPushButton(self.text_groupbox) - self.input_button_color_average_text.setText("") - self.input_button_color_average_text.setObjectName("input_button_color_average_text") - self.verticalLayout_8.addWidget(self.input_button_color_average_text) - self.input_button_color_high_text = QtWidgets.QPushButton(self.text_groupbox) - self.input_button_color_high_text.setText("") - self.input_button_color_high_text.setObjectName("input_button_color_high_text") - self.verticalLayout_8.addWidget(self.input_button_color_high_text) - self.input_button_color_critical_text = QtWidgets.QPushButton(self.text_groupbox) - self.input_button_color_critical_text.setText("") - self.input_button_color_critical_text.setObjectName("input_button_color_critical_text") - self.verticalLayout_8.addWidget(self.input_button_color_critical_text) - self.input_button_color_disaster_text = QtWidgets.QPushButton(self.text_groupbox) - self.input_button_color_disaster_text.setText("") - self.input_button_color_disaster_text.setObjectName("input_button_color_disaster_text") - self.verticalLayout_8.addWidget(self.input_button_color_disaster_text) - self.input_button_color_unknown_text = QtWidgets.QPushButton(self.text_groupbox) - self.input_button_color_unknown_text.setText("") - self.input_button_color_unknown_text.setObjectName("input_button_color_unknown_text") - self.verticalLayout_8.addWidget(self.input_button_color_unknown_text) - self.input_button_color_unreachable_text = QtWidgets.QPushButton(self.text_groupbox) - self.input_button_color_unreachable_text.setText("") - self.input_button_color_unreachable_text.setObjectName("input_button_color_unreachable_text") - self.verticalLayout_8.addWidget(self.input_button_color_unreachable_text) - self.input_button_color_down_text = QtWidgets.QPushButton(self.text_groupbox) - self.input_button_color_down_text.setText("") - self.input_button_color_down_text.setObjectName("input_button_color_down_text") - self.verticalLayout_8.addWidget(self.input_button_color_down_text) - self.input_button_color_error_text = QtWidgets.QPushButton(self.text_groupbox) - self.input_button_color_error_text.setText("") - self.input_button_color_error_text.setObjectName("input_button_color_error_text") - self.verticalLayout_8.addWidget(self.input_button_color_error_text) - self.gridLayout_12.addWidget(self.text_groupbox, 1, 1, 8, 1) - self.background_groupbox = QtWidgets.QGroupBox(self.tab_colors) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.background_groupbox.sizePolicy().hasHeightForWidth()) - self.background_groupbox.setSizePolicy(sizePolicy) - self.background_groupbox.setObjectName("background_groupbox") - self.verticalLayout_12 = QtWidgets.QVBoxLayout(self.background_groupbox) - self.verticalLayout_12.setContentsMargins(10, 5, 10, 10) - self.verticalLayout_12.setSpacing(5) - self.verticalLayout_12.setObjectName("verticalLayout_12") - self.input_button_color_ok_background = QtWidgets.QPushButton(self.background_groupbox) - self.input_button_color_ok_background.setText("") - self.input_button_color_ok_background.setObjectName("input_button_color_ok_background") - self.verticalLayout_12.addWidget(self.input_button_color_ok_background) - self.input_button_color_information_background = QtWidgets.QPushButton(self.background_groupbox) - self.input_button_color_information_background.setText("") - self.input_button_color_information_background.setObjectName("input_button_color_information_background") - self.verticalLayout_12.addWidget(self.input_button_color_information_background) - self.input_button_color_warning_background = QtWidgets.QPushButton(self.background_groupbox) - self.input_button_color_warning_background.setText("") - self.input_button_color_warning_background.setObjectName("input_button_color_warning_background") - self.verticalLayout_12.addWidget(self.input_button_color_warning_background) - self.input_button_color_average_background = QtWidgets.QPushButton(self.background_groupbox) - self.input_button_color_average_background.setText("") - self.input_button_color_average_background.setObjectName("input_button_color_average_background") - self.verticalLayout_12.addWidget(self.input_button_color_average_background) - self.input_button_color_high_background = QtWidgets.QPushButton(self.background_groupbox) - self.input_button_color_high_background.setText("") - self.input_button_color_high_background.setObjectName("input_button_color_high_background") - self.verticalLayout_12.addWidget(self.input_button_color_high_background) - self.input_button_color_critical_background = QtWidgets.QPushButton(self.background_groupbox) - self.input_button_color_critical_background.setText("") - self.input_button_color_critical_background.setObjectName("input_button_color_critical_background") - self.verticalLayout_12.addWidget(self.input_button_color_critical_background) - self.input_button_color_disaster_background = QtWidgets.QPushButton(self.background_groupbox) - self.input_button_color_disaster_background.setText("") - self.input_button_color_disaster_background.setObjectName("input_button_color_disaster_background") - self.verticalLayout_12.addWidget(self.input_button_color_disaster_background) - self.input_button_color_unknown_background = QtWidgets.QPushButton(self.background_groupbox) - self.input_button_color_unknown_background.setText("") - self.input_button_color_unknown_background.setObjectName("input_button_color_unknown_background") - self.verticalLayout_12.addWidget(self.input_button_color_unknown_background) - self.input_button_color_unreachable_background = QtWidgets.QPushButton(self.background_groupbox) - self.input_button_color_unreachable_background.setText("") - self.input_button_color_unreachable_background.setObjectName("input_button_color_unreachable_background") - self.verticalLayout_12.addWidget(self.input_button_color_unreachable_background) - self.input_button_color_down_background = QtWidgets.QPushButton(self.background_groupbox) - self.input_button_color_down_background.setText("") - self.input_button_color_down_background.setObjectName("input_button_color_down_background") - self.verticalLayout_12.addWidget(self.input_button_color_down_background) - self.input_button_color_error_background = QtWidgets.QPushButton(self.background_groupbox) - self.input_button_color_error_background.setText("") - self.input_button_color_error_background.setObjectName("input_button_color_error_background") - self.verticalLayout_12.addWidget(self.input_button_color_error_background) - self.gridLayout_12.addWidget(self.background_groupbox, 1, 2, 8, 1) - self.horizontalLayout_12 = QtWidgets.QHBoxLayout() - self.horizontalLayout_12.setContentsMargins(5, 5, 5, 5) - self.horizontalLayout_12.setSpacing(5) - self.horizontalLayout_12.setObjectName("horizontalLayout_12") - self.button_colors_reset = QtWidgets.QPushButton(self.tab_colors) - self.button_colors_reset.setObjectName("button_colors_reset") - self.horizontalLayout_12.addWidget(self.button_colors_reset) - self.button_colors_defaults = QtWidgets.QPushButton(self.tab_colors) - self.button_colors_defaults.setObjectName("button_colors_defaults") - self.horizontalLayout_12.addWidget(self.button_colors_defaults) - self.gridLayout_12.addLayout(self.horizontalLayout_12, 10, 1, 1, 2) - self.groupBox_color_grid = QtWidgets.QGroupBox(self.tab_colors) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.groupBox_color_grid.sizePolicy().hasHeightForWidth()) - self.groupBox_color_grid.setSizePolicy(sizePolicy) - self.groupBox_color_grid.setObjectName("groupBox_color_grid") - self.gridLayout_18 = QtWidgets.QGridLayout(self.groupBox_color_grid) - self.gridLayout_18.setContentsMargins(10, 10, 10, 10) - self.gridLayout_18.setSpacing(5) - self.gridLayout_18.setObjectName("gridLayout_18") - self.input_checkbox_show_grid = QtWidgets.QCheckBox(self.groupBox_color_grid) - self.input_checkbox_show_grid.setObjectName("input_checkbox_show_grid") - self.gridLayout_18.addWidget(self.input_checkbox_show_grid, 0, 0, 1, 1) - self.input_checkbox_grid_use_custom_intensity = QtWidgets.QCheckBox(self.groupBox_color_grid) - self.input_checkbox_grid_use_custom_intensity.setEnabled(True) - self.input_checkbox_grid_use_custom_intensity.setObjectName("input_checkbox_grid_use_custom_intensity") - self.gridLayout_18.addWidget(self.input_checkbox_grid_use_custom_intensity, 1, 0, 1, 1) - self.input_slider_grid_alternation_intensity = QtWidgets.QSlider(self.groupBox_color_grid) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.input_slider_grid_alternation_intensity.sizePolicy().hasHeightForWidth()) - self.input_slider_grid_alternation_intensity.setSizePolicy(sizePolicy) - self.input_slider_grid_alternation_intensity.setMaximum(50) - self.input_slider_grid_alternation_intensity.setOrientation(QtCore.Qt.Horizontal) - self.input_slider_grid_alternation_intensity.setObjectName("input_slider_grid_alternation_intensity") - self.gridLayout_18.addWidget(self.input_slider_grid_alternation_intensity, 2, 0, 1, 1) - self.vbox_alternation_intensity_labels = QtWidgets.QVBoxLayout() - self.vbox_alternation_intensity_labels.setContentsMargins(5, 5, 5, 5) - self.vbox_alternation_intensity_labels.setSpacing(0) - self.vbox_alternation_intensity_labels.setObjectName("vbox_alternation_intensity_labels") - self.label_intensity_information_0 = QtWidgets.QLabel(self.groupBox_color_grid) - self.label_intensity_information_0.setAutoFillBackground(True) - self.label_intensity_information_0.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) - self.label_intensity_information_0.setObjectName("label_intensity_information_0") - self.vbox_alternation_intensity_labels.addWidget(self.label_intensity_information_0) - self.label_intensity_information_1 = QtWidgets.QLabel(self.groupBox_color_grid) - self.label_intensity_information_1.setAutoFillBackground(True) - self.label_intensity_information_1.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) - self.label_intensity_information_1.setObjectName("label_intensity_information_1") - self.vbox_alternation_intensity_labels.addWidget(self.label_intensity_information_1) - self.label_intensity_warning_0 = QtWidgets.QLabel(self.groupBox_color_grid) - self.label_intensity_warning_0.setAutoFillBackground(True) - self.label_intensity_warning_0.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) - self.label_intensity_warning_0.setObjectName("label_intensity_warning_0") - self.vbox_alternation_intensity_labels.addWidget(self.label_intensity_warning_0) - self.label_intensity_warning_1 = QtWidgets.QLabel(self.groupBox_color_grid) - self.label_intensity_warning_1.setAutoFillBackground(True) - self.label_intensity_warning_1.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) - self.label_intensity_warning_1.setObjectName("label_intensity_warning_1") - self.vbox_alternation_intensity_labels.addWidget(self.label_intensity_warning_1) - self.label_intensity_average_0 = QtWidgets.QLabel(self.groupBox_color_grid) - self.label_intensity_average_0.setAutoFillBackground(True) - self.label_intensity_average_0.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) - self.label_intensity_average_0.setObjectName("label_intensity_average_0") - self.vbox_alternation_intensity_labels.addWidget(self.label_intensity_average_0) - self.label_intensity_average_1 = QtWidgets.QLabel(self.groupBox_color_grid) - self.label_intensity_average_1.setAutoFillBackground(True) - self.label_intensity_average_1.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) - self.label_intensity_average_1.setObjectName("label_intensity_average_1") - self.vbox_alternation_intensity_labels.addWidget(self.label_intensity_average_1) - self.label_intensity_high_0 = QtWidgets.QLabel(self.groupBox_color_grid) - self.label_intensity_high_0.setAutoFillBackground(True) - self.label_intensity_high_0.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) - self.label_intensity_high_0.setObjectName("label_intensity_high_0") - self.vbox_alternation_intensity_labels.addWidget(self.label_intensity_high_0) - self.label_intensity_high_1 = QtWidgets.QLabel(self.groupBox_color_grid) - self.label_intensity_high_1.setAutoFillBackground(True) - self.label_intensity_high_1.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) - self.label_intensity_high_1.setObjectName("label_intensity_high_1") - self.vbox_alternation_intensity_labels.addWidget(self.label_intensity_high_1) - self.label_intensity_critical_0 = QtWidgets.QLabel(self.groupBox_color_grid) - self.label_intensity_critical_0.setAutoFillBackground(True) - self.label_intensity_critical_0.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) - self.label_intensity_critical_0.setObjectName("label_intensity_critical_0") - self.vbox_alternation_intensity_labels.addWidget(self.label_intensity_critical_0) - self.label_intensity_critical_1 = QtWidgets.QLabel(self.groupBox_color_grid) - self.label_intensity_critical_1.setAutoFillBackground(True) - self.label_intensity_critical_1.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) - self.label_intensity_critical_1.setObjectName("label_intensity_critical_1") - self.vbox_alternation_intensity_labels.addWidget(self.label_intensity_critical_1) - self.label_intensity_disaster_0 = QtWidgets.QLabel(self.groupBox_color_grid) - self.label_intensity_disaster_0.setAutoFillBackground(True) - self.label_intensity_disaster_0.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) - self.label_intensity_disaster_0.setObjectName("label_intensity_disaster_0") - self.vbox_alternation_intensity_labels.addWidget(self.label_intensity_disaster_0) - self.label_intensity_disaster_1 = QtWidgets.QLabel(self.groupBox_color_grid) - self.label_intensity_disaster_1.setAutoFillBackground(True) - self.label_intensity_disaster_1.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) - self.label_intensity_disaster_1.setObjectName("label_intensity_disaster_1") - self.vbox_alternation_intensity_labels.addWidget(self.label_intensity_disaster_1) - self.label_intensity_unknown_0 = QtWidgets.QLabel(self.groupBox_color_grid) - self.label_intensity_unknown_0.setAutoFillBackground(True) - self.label_intensity_unknown_0.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) - self.label_intensity_unknown_0.setObjectName("label_intensity_unknown_0") - self.vbox_alternation_intensity_labels.addWidget(self.label_intensity_unknown_0) - self.label_intensity_unknown_1 = QtWidgets.QLabel(self.groupBox_color_grid) - self.label_intensity_unknown_1.setAutoFillBackground(True) - self.label_intensity_unknown_1.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) - self.label_intensity_unknown_1.setObjectName("label_intensity_unknown_1") - self.vbox_alternation_intensity_labels.addWidget(self.label_intensity_unknown_1) - self.label_intensity_unreachable_0 = QtWidgets.QLabel(self.groupBox_color_grid) - self.label_intensity_unreachable_0.setAutoFillBackground(True) - self.label_intensity_unreachable_0.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) - self.label_intensity_unreachable_0.setObjectName("label_intensity_unreachable_0") - self.vbox_alternation_intensity_labels.addWidget(self.label_intensity_unreachable_0) - self.label_intensity_unreachable_1 = QtWidgets.QLabel(self.groupBox_color_grid) - self.label_intensity_unreachable_1.setAutoFillBackground(True) - self.label_intensity_unreachable_1.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) - self.label_intensity_unreachable_1.setObjectName("label_intensity_unreachable_1") - self.vbox_alternation_intensity_labels.addWidget(self.label_intensity_unreachable_1) - self.label_intensity_down_0 = QtWidgets.QLabel(self.groupBox_color_grid) - self.label_intensity_down_0.setAutoFillBackground(True) - self.label_intensity_down_0.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) - self.label_intensity_down_0.setObjectName("label_intensity_down_0") - self.vbox_alternation_intensity_labels.addWidget(self.label_intensity_down_0) - self.label_intensity_down_1 = QtWidgets.QLabel(self.groupBox_color_grid) - self.label_intensity_down_1.setAutoFillBackground(True) - self.label_intensity_down_1.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter) - self.label_intensity_down_1.setObjectName("label_intensity_down_1") - self.vbox_alternation_intensity_labels.addWidget(self.label_intensity_down_1) - self.gridLayout_18.addLayout(self.vbox_alternation_intensity_labels, 2, 1, 1, 1) - self.gridLayout_12.addWidget(self.groupBox_color_grid, 11, 0, 1, 3) - self.tabs.addTab(self.tab_colors, "") - self.tab_defaults = QtWidgets.QWidget() - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.tab_defaults.sizePolicy().hasHeightForWidth()) - self.tab_defaults.setSizePolicy(sizePolicy) - self.tab_defaults.setObjectName("tab_defaults") - self.verticalLayout_9 = QtWidgets.QVBoxLayout(self.tab_defaults) - self.verticalLayout_9.setContentsMargins(10, 10, 10, 10) - self.verticalLayout_9.setSpacing(5) - self.verticalLayout_9.setObjectName("verticalLayout_9") - self.label_defaults = QtWidgets.QLabel(self.tab_defaults) - self.label_defaults.setObjectName("label_defaults") - self.verticalLayout_9.addWidget(self.label_defaults) - self.label_defaults_acknowledge = QtWidgets.QGroupBox(self.tab_defaults) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_defaults_acknowledge.sizePolicy().hasHeightForWidth()) - self.label_defaults_acknowledge.setSizePolicy(sizePolicy) - self.label_defaults_acknowledge.setObjectName("label_defaults_acknowledge") - self.verticalLayout_10 = QtWidgets.QVBoxLayout(self.label_defaults_acknowledge) - self.verticalLayout_10.setContentsMargins(10, 10, 10, 10) - self.verticalLayout_10.setSpacing(5) - self.verticalLayout_10.setObjectName("verticalLayout_10") - self.input_checkbox_defaults_acknowledge_sticky = QtWidgets.QCheckBox(self.label_defaults_acknowledge) - self.input_checkbox_defaults_acknowledge_sticky.setObjectName("input_checkbox_defaults_acknowledge_sticky") - self.verticalLayout_10.addWidget(self.input_checkbox_defaults_acknowledge_sticky) - self.input_checkbox_defaults_acknowledge_send_notification = QtWidgets.QCheckBox(self.label_defaults_acknowledge) - self.input_checkbox_defaults_acknowledge_send_notification.setObjectName("input_checkbox_defaults_acknowledge_send_notification") - self.verticalLayout_10.addWidget(self.input_checkbox_defaults_acknowledge_send_notification) - self.input_checkbox_defaults_acknowledge_persistent_comment = QtWidgets.QCheckBox(self.label_defaults_acknowledge) - self.input_checkbox_defaults_acknowledge_persistent_comment.setObjectName("input_checkbox_defaults_acknowledge_persistent_comment") - self.verticalLayout_10.addWidget(self.input_checkbox_defaults_acknowledge_persistent_comment) - self.input_checkbox_defaults_acknowledge_all_services = QtWidgets.QCheckBox(self.label_defaults_acknowledge) - self.input_checkbox_defaults_acknowledge_all_services.setObjectName("input_checkbox_defaults_acknowledge_all_services") - self.verticalLayout_10.addWidget(self.input_checkbox_defaults_acknowledge_all_services) - self.horizontalLayout_13 = QtWidgets.QHBoxLayout() - self.horizontalLayout_13.setContentsMargins(-1, 5, 5, 5) - self.horizontalLayout_13.setSpacing(5) - self.horizontalLayout_13.setObjectName("horizontalLayout_13") - self.label_defaults_acknowledge_comment = QtWidgets.QLabel(self.label_defaults_acknowledge) - self.label_defaults_acknowledge_comment.setObjectName("label_defaults_acknowledge_comment") - self.horizontalLayout_13.addWidget(self.label_defaults_acknowledge_comment) - self.input_lineedit_defaults_acknowledge_comment = QtWidgets.QLineEdit(self.label_defaults_acknowledge) - self.input_lineedit_defaults_acknowledge_comment.setObjectName("input_lineedit_defaults_acknowledge_comment") - self.horizontalLayout_13.addWidget(self.input_lineedit_defaults_acknowledge_comment) - self.verticalLayout_10.addLayout(self.horizontalLayout_13) - self.input_checkbox_defaults_acknowledge_expire = QtWidgets.QCheckBox(self.label_defaults_acknowledge) - self.input_checkbox_defaults_acknowledge_expire.setEnabled(True) - self.input_checkbox_defaults_acknowledge_expire.setObjectName("input_checkbox_defaults_acknowledge_expire") - self.verticalLayout_10.addWidget(self.input_checkbox_defaults_acknowledge_expire) - self.horizontalLayout_7 = QtWidgets.QHBoxLayout() - self.horizontalLayout_7.setContentsMargins(0, 5, 5, 5) - self.horizontalLayout_7.setSpacing(5) - self.horizontalLayout_7.setObjectName("horizontalLayout_7") - self.label_expire_in = QtWidgets.QLabel(self.label_defaults_acknowledge) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_expire_in.sizePolicy().hasHeightForWidth()) - self.label_expire_in.setSizePolicy(sizePolicy) - self.label_expire_in.setObjectName("label_expire_in") - self.horizontalLayout_7.addWidget(self.label_expire_in) - self.input_spinbox_defaults_acknowledge_expire_duration_hours = QtWidgets.QSpinBox(self.label_defaults_acknowledge) - self.input_spinbox_defaults_acknowledge_expire_duration_hours.setObjectName("input_spinbox_defaults_acknowledge_expire_duration_hours") - self.horizontalLayout_7.addWidget(self.input_spinbox_defaults_acknowledge_expire_duration_hours) - self.label_expire_in_hours = QtWidgets.QLabel(self.label_defaults_acknowledge) - self.label_expire_in_hours.setObjectName("label_expire_in_hours") - self.horizontalLayout_7.addWidget(self.label_expire_in_hours) - self.input_spinbox_defaults_acknowledge_expire_duration_minutes = QtWidgets.QSpinBox(self.label_defaults_acknowledge) - self.input_spinbox_defaults_acknowledge_expire_duration_minutes.setObjectName("input_spinbox_defaults_acknowledge_expire_duration_minutes") - self.horizontalLayout_7.addWidget(self.input_spinbox_defaults_acknowledge_expire_duration_minutes) - self.label_expire_in_minutes = QtWidgets.QLabel(self.label_defaults_acknowledge) - self.label_expire_in_minutes.setObjectName("label_expire_in_minutes") - self.horizontalLayout_7.addWidget(self.label_expire_in_minutes) - spacerItem12 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.horizontalLayout_7.addItem(spacerItem12) - self.verticalLayout_10.addLayout(self.horizontalLayout_7) - self.verticalLayout_9.addWidget(self.label_defaults_acknowledge) - self.label_defaults_downtime = QtWidgets.QGroupBox(self.tab_defaults) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_defaults_downtime.sizePolicy().hasHeightForWidth()) - self.label_defaults_downtime.setSizePolicy(sizePolicy) - self.label_defaults_downtime.setObjectName("label_defaults_downtime") - self.gridLayout_10 = QtWidgets.QGridLayout(self.label_defaults_downtime) - self.gridLayout_10.setContentsMargins(10, 10, 10, 10) - self.gridLayout_10.setSpacing(5) - self.gridLayout_10.setObjectName("gridLayout_10") - self.label_defaults_downtime_comment = QtWidgets.QLabel(self.label_defaults_downtime) - self.label_defaults_downtime_comment.setObjectName("label_defaults_downtime_comment") - self.gridLayout_10.addWidget(self.label_defaults_downtime_comment, 3, 0, 1, 1) - self.label_defaults_duration_comment = QtWidgets.QLabel(self.label_defaults_downtime) - self.label_defaults_duration_comment.setObjectName("label_defaults_duration_comment") - self.gridLayout_10.addWidget(self.label_defaults_duration_comment, 1, 0, 1, 1) - self.input_lineedit_defaults_downtime_comment = QtWidgets.QLineEdit(self.label_defaults_downtime) - self.input_lineedit_defaults_downtime_comment.setObjectName("input_lineedit_defaults_downtime_comment") - self.gridLayout_10.addWidget(self.input_lineedit_defaults_downtime_comment, 3, 1, 1, 1) - self.gridLayout_9 = QtWidgets.QGridLayout() - self.gridLayout_9.setContentsMargins(-1, 5, 5, 5) - self.gridLayout_9.setSpacing(5) - self.gridLayout_9.setObjectName("gridLayout_9") - self.input_radiobutton_defaults_downtime_type_flexible = QtWidgets.QRadioButton(self.label_defaults_downtime) - self.input_radiobutton_defaults_downtime_type_flexible.setObjectName("input_radiobutton_defaults_downtime_type_flexible") - self.gridLayout_9.addWidget(self.input_radiobutton_defaults_downtime_type_flexible, 1, 1, 1, 1) - self.input_radiobutton_defaults_downtime_type_fixed = QtWidgets.QRadioButton(self.label_defaults_downtime) - self.input_radiobutton_defaults_downtime_type_fixed.setObjectName("input_radiobutton_defaults_downtime_type_fixed") - self.gridLayout_9.addWidget(self.input_radiobutton_defaults_downtime_type_fixed, 0, 1, 1, 1) - self.label_defaults_downtime_type = QtWidgets.QLabel(self.label_defaults_downtime) - self.label_defaults_downtime_type.setObjectName("label_defaults_downtime_type") - self.gridLayout_9.addWidget(self.label_defaults_downtime_type, 0, 0, 1, 1) - spacerItem13 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.gridLayout_9.addItem(spacerItem13, 0, 2, 1, 1) - self.gridLayout_10.addLayout(self.gridLayout_9, 0, 0, 1, 2) - self.horizontalLayout_8 = QtWidgets.QHBoxLayout() - self.horizontalLayout_8.setContentsMargins(-1, 5, 5, 5) - self.horizontalLayout_8.setSpacing(5) - self.horizontalLayout_8.setObjectName("horizontalLayout_8") - self.input_spinbox_defaults_downtime_duration_hours = QtWidgets.QSpinBox(self.label_defaults_downtime) - self.input_spinbox_defaults_downtime_duration_hours.setObjectName("input_spinbox_defaults_downtime_duration_hours") - self.horizontalLayout_8.addWidget(self.input_spinbox_defaults_downtime_duration_hours) - self.label_duration_hour = QtWidgets.QLabel(self.label_defaults_downtime) - self.label_duration_hour.setObjectName("label_duration_hour") - self.horizontalLayout_8.addWidget(self.label_duration_hour) - self.input_spinbox_defaults_downtime_duration_minutes = QtWidgets.QSpinBox(self.label_defaults_downtime) - self.input_spinbox_defaults_downtime_duration_minutes.setObjectName("input_spinbox_defaults_downtime_duration_minutes") - self.horizontalLayout_8.addWidget(self.input_spinbox_defaults_downtime_duration_minutes) - self.label_duration_minutes = QtWidgets.QLabel(self.label_defaults_downtime) - self.label_duration_minutes.setObjectName("label_duration_minutes") - self.horizontalLayout_8.addWidget(self.label_duration_minutes) - spacerItem14 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.horizontalLayout_8.addItem(spacerItem14) - self.gridLayout_10.addLayout(self.horizontalLayout_8, 1, 1, 1, 1) - self.verticalLayout_9.addWidget(self.label_defaults_downtime) - self.label_acknowledge_submit_result = QtWidgets.QGroupBox(self.tab_defaults) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_acknowledge_submit_result.sizePolicy().hasHeightForWidth()) - self.label_acknowledge_submit_result.setSizePolicy(sizePolicy) - self.label_acknowledge_submit_result.setObjectName("label_acknowledge_submit_result") - self.horizontalLayout_14 = QtWidgets.QHBoxLayout(self.label_acknowledge_submit_result) - self.horizontalLayout_14.setContentsMargins(10, 10, 10, 10) - self.horizontalLayout_14.setSpacing(5) - self.horizontalLayout_14.setObjectName("horizontalLayout_14") - self.label_defaults_submit_check_result_comment = QtWidgets.QLabel(self.label_acknowledge_submit_result) - self.label_defaults_submit_check_result_comment.setObjectName("label_defaults_submit_check_result_comment") - self.horizontalLayout_14.addWidget(self.label_defaults_submit_check_result_comment) - self.input_lineedit_defaults_submit_check_result_comment = QtWidgets.QLineEdit(self.label_acknowledge_submit_result) - self.input_lineedit_defaults_submit_check_result_comment.setObjectName("input_lineedit_defaults_submit_check_result_comment") - self.horizontalLayout_14.addWidget(self.input_lineedit_defaults_submit_check_result_comment) - self.verticalLayout_9.addWidget(self.label_acknowledge_submit_result) - spacerItem15 = QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.verticalLayout_9.addItem(spacerItem15) - self.tabs.addTab(self.tab_defaults, "") - self.verticalLayout.addWidget(self.tabs) - self.button_box = QtWidgets.QDialogButtonBox(settings_main) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.button_box.sizePolicy().hasHeightForWidth()) - self.button_box.setSizePolicy(sizePolicy) - self.button_box.setOrientation(QtCore.Qt.Horizontal) - self.button_box.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) - self.button_box.setObjectName("button_box") - self.verticalLayout.addWidget(self.button_box) - - self.retranslateUi(settings_main) - self.tabs.setCurrentIndex(6) - self.button_box.accepted.connect(settings_main.accept) - self.button_box.rejected.connect(settings_main.reject) - QtCore.QMetaObject.connectSlotsByName(settings_main) - settings_main.setTabOrder(self.list_servers, self.button_new_server) - settings_main.setTabOrder(self.button_new_server, self.button_edit_server) - settings_main.setTabOrder(self.button_edit_server, self.button_copy_server) - settings_main.setTabOrder(self.button_copy_server, self.button_delete_server) - settings_main.setTabOrder(self.button_delete_server, self.input_spinbox_update_interval_seconds) - settings_main.setTabOrder(self.input_spinbox_update_interval_seconds, self.input_checkbox_use_system_keyring) - settings_main.setTabOrder(self.input_checkbox_use_system_keyring, self.input_checkbox_debug_mode) - settings_main.setTabOrder(self.input_checkbox_debug_mode, self.input_checkbox_debug_to_file) - settings_main.setTabOrder(self.input_checkbox_debug_to_file, self.input_lineedit_debug_file) - settings_main.setTabOrder(self.input_lineedit_debug_file, self.input_checkbox_check_for_new_version) - settings_main.setTabOrder(self.input_checkbox_check_for_new_version, self.button_check_for_new_version_now) - settings_main.setTabOrder(self.button_check_for_new_version_now, self.input_radiobutton_long_display) - settings_main.setTabOrder(self.input_radiobutton_long_display, self.input_radiobutton_short_display) - settings_main.setTabOrder(self.input_radiobutton_short_display, self.input_radiobutton_statusbar_floating) - settings_main.setTabOrder(self.input_radiobutton_statusbar_floating, self.input_radiobutton_icon_in_systray) - settings_main.setTabOrder(self.input_radiobutton_icon_in_systray, self.input_radiobutton_fullscreen) - settings_main.setTabOrder(self.input_radiobutton_fullscreen, self.input_checkbox_systray_offset_use) - settings_main.setTabOrder(self.input_checkbox_systray_offset_use, self.input_spinbox_systray_offset) - settings_main.setTabOrder(self.input_spinbox_systray_offset, self.input_combobox_fullscreen_display) - settings_main.setTabOrder(self.input_combobox_fullscreen_display, self.button_fontchooser) - settings_main.setTabOrder(self.button_fontchooser, self.button_default_font) - settings_main.setTabOrder(self.button_default_font, self.input_radiobutton_popup_details_hover) - settings_main.setTabOrder(self.input_radiobutton_popup_details_hover, self.input_radiobutton_popup_details_clicking) - settings_main.setTabOrder(self.input_radiobutton_popup_details_clicking, self.input_radiobutton_close_details_hover) - settings_main.setTabOrder(self.input_radiobutton_close_details_hover, self.input_radiobutton_close_details_clicking) - settings_main.setTabOrder(self.input_radiobutton_close_details_clicking, self.input_radiobutton_close_details_clicking_somewhere) - settings_main.setTabOrder(self.input_radiobutton_close_details_clicking_somewhere, self.input_checkbox_highlight_new_events) - settings_main.setTabOrder(self.input_checkbox_highlight_new_events, self.input_checkbox_show_tooltips) - settings_main.setTabOrder(self.input_checkbox_show_tooltips, self.input_combobox_default_sort_field) - settings_main.setTabOrder(self.input_combobox_default_sort_field, self.input_combobox_default_sort_order) - settings_main.setTabOrder(self.input_combobox_default_sort_order, self.input_checkbox_filter_all_down_hosts) - settings_main.setTabOrder(self.input_checkbox_filter_all_down_hosts, self.input_checkbox_filter_all_unreachable_hosts) - settings_main.setTabOrder(self.input_checkbox_filter_all_unreachable_hosts, self.input_checkbox_filter_all_flapping_hosts) - settings_main.setTabOrder(self.input_checkbox_filter_all_flapping_hosts, self.input_checkbox_filter_all_critical_services) - settings_main.setTabOrder(self.input_checkbox_filter_all_critical_services, self.input_checkbox_filter_all_flapping_services) - settings_main.setTabOrder(self.input_checkbox_filter_all_flapping_services, self.input_checkbox_filter_all_unknown_services) - settings_main.setTabOrder(self.input_checkbox_filter_all_unknown_services, self.input_checkbox_filter_all_warning_services) - settings_main.setTabOrder(self.input_checkbox_filter_all_warning_services, self.input_checkbox_filter_all_information_services) - settings_main.setTabOrder(self.input_checkbox_filter_all_information_services, self.input_checkbox_filter_all_average_services) - settings_main.setTabOrder(self.input_checkbox_filter_all_average_services, self.input_checkbox_filter_all_disaster_services) - settings_main.setTabOrder(self.input_checkbox_filter_all_disaster_services, self.input_checkbox_filter_all_high_services) - settings_main.setTabOrder(self.input_checkbox_filter_all_high_services, self.input_checkbox_filter_acknowledged_hosts_services) - settings_main.setTabOrder(self.input_checkbox_filter_acknowledged_hosts_services, self.input_checkbox_filter_hosts_services_disabled_notifications) - settings_main.setTabOrder(self.input_checkbox_filter_hosts_services_disabled_notifications, self.input_checkbox_filter_hosts_services_disabled_checks) - settings_main.setTabOrder(self.input_checkbox_filter_hosts_services_disabled_checks, self.input_checkbox_filter_hosts_services_maintenance) - settings_main.setTabOrder(self.input_checkbox_filter_hosts_services_maintenance, self.input_checkbox_filter_services_on_acknowledged_hosts) - settings_main.setTabOrder(self.input_checkbox_filter_services_on_acknowledged_hosts, self.input_checkbox_filter_services_on_down_hosts) - settings_main.setTabOrder(self.input_checkbox_filter_services_on_down_hosts, self.input_checkbox_filter_services_on_hosts_in_maintenance) - settings_main.setTabOrder(self.input_checkbox_filter_services_on_hosts_in_maintenance, self.input_checkbox_filter_services_on_unreachable_hosts) - settings_main.setTabOrder(self.input_checkbox_filter_services_on_unreachable_hosts, self.input_checkbox_filter_hosts_in_soft_state) - settings_main.setTabOrder(self.input_checkbox_filter_hosts_in_soft_state, self.input_checkbox_filter_services_in_soft_state) - settings_main.setTabOrder(self.input_checkbox_filter_services_in_soft_state, self.input_checkbox_re_host_enabled) - settings_main.setTabOrder(self.input_checkbox_re_host_enabled, self.input_lineedit_re_host_pattern) - settings_main.setTabOrder(self.input_lineedit_re_host_pattern, self.input_checkbox_re_host_reverse) - settings_main.setTabOrder(self.input_checkbox_re_host_reverse, self.input_checkbox_re_service_enabled) - settings_main.setTabOrder(self.input_checkbox_re_service_enabled, self.input_lineedit_re_service_pattern) - settings_main.setTabOrder(self.input_lineedit_re_service_pattern, self.input_checkbox_re_service_reverse) - settings_main.setTabOrder(self.input_checkbox_re_service_reverse, self.input_checkbox_re_status_information_enabled) - settings_main.setTabOrder(self.input_checkbox_re_status_information_enabled, self.input_lineedit_re_status_information_pattern) - settings_main.setTabOrder(self.input_lineedit_re_status_information_pattern, self.input_checkbox_re_status_information_reverse) - settings_main.setTabOrder(self.input_checkbox_re_status_information_reverse, self.input_checkbox_re_duration_enabled) - settings_main.setTabOrder(self.input_checkbox_re_duration_enabled, self.input_lineedit_re_duration_pattern) - settings_main.setTabOrder(self.input_lineedit_re_duration_pattern, self.input_checkbox_re_duration_reverse) - settings_main.setTabOrder(self.input_checkbox_re_duration_reverse, self.input_checkbox_re_attempt_enabled) - settings_main.setTabOrder(self.input_checkbox_re_attempt_enabled, self.input_lineedit_re_attempt_pattern) - settings_main.setTabOrder(self.input_lineedit_re_attempt_pattern, self.input_checkbox_re_attempt_reverse) - settings_main.setTabOrder(self.input_checkbox_re_attempt_reverse, self.input_checkbox_re_groups_enabled) - settings_main.setTabOrder(self.input_checkbox_re_groups_enabled, self.input_lineedit_re_groups_pattern) - settings_main.setTabOrder(self.input_lineedit_re_groups_pattern, self.input_checkbox_re_groups_reverse) - settings_main.setTabOrder(self.input_checkbox_re_groups_reverse, self.list_actions) - settings_main.setTabOrder(self.list_actions, self.button_new_action) - settings_main.setTabOrder(self.button_new_action, self.button_edit_action) - settings_main.setTabOrder(self.button_edit_action, self.button_copy_action) - settings_main.setTabOrder(self.button_copy_action, self.button_delete_action) - settings_main.setTabOrder(self.button_delete_action, self.input_radiobutton_connect_by_host) - settings_main.setTabOrder(self.input_radiobutton_connect_by_host, self.input_radiobutton_connect_by_dns) - settings_main.setTabOrder(self.input_radiobutton_connect_by_dns, self.input_radiobutton_connect_by_ip) - settings_main.setTabOrder(self.input_radiobutton_connect_by_ip, self.input_radiobutton_use_default_browser) - settings_main.setTabOrder(self.input_radiobutton_use_default_browser, self.input_radiobutton_use_custom_browser) - settings_main.setTabOrder(self.input_radiobutton_use_custom_browser, self.input_lineedit_custom_browser) - settings_main.setTabOrder(self.input_lineedit_custom_browser, self.button_choose_browser) - settings_main.setTabOrder(self.button_choose_browser, self.input_checkbox_notification) - settings_main.setTabOrder(self.input_checkbox_notification, self.input_checkbox_notify_if_warning) - settings_main.setTabOrder(self.input_checkbox_notify_if_warning, self.input_checkbox_notify_if_critical) - settings_main.setTabOrder(self.input_checkbox_notify_if_critical, self.input_checkbox_notify_if_unknown) - settings_main.setTabOrder(self.input_checkbox_notify_if_unknown, self.input_checkbox_notify_if_unreachable) - settings_main.setTabOrder(self.input_checkbox_notify_if_unreachable, self.input_checkbox_notify_if_down) - settings_main.setTabOrder(self.input_checkbox_notify_if_down, self.input_checkbox_notify_if_information) - settings_main.setTabOrder(self.input_checkbox_notify_if_information, self.input_checkbox_notify_if_disaster) - settings_main.setTabOrder(self.input_checkbox_notify_if_disaster, self.input_checkbox_notify_if_average) - settings_main.setTabOrder(self.input_checkbox_notify_if_average, self.input_checkbox_notify_if_high) - settings_main.setTabOrder(self.input_checkbox_notify_if_high, self.input_checkbox_notification_flashing) - settings_main.setTabOrder(self.input_checkbox_notification_flashing, self.input_checkbox_notification_desktop) - settings_main.setTabOrder(self.input_checkbox_notification_desktop, self.input_checkbox_notification_sound) - settings_main.setTabOrder(self.input_checkbox_notification_sound, self.input_checkbox_notification_sound_repeat) - settings_main.setTabOrder(self.input_checkbox_notification_sound_repeat, self.input_radiobutton_notification_default_sound) - settings_main.setTabOrder(self.input_radiobutton_notification_default_sound, self.input_radiobutton_notification_custom_sound) - settings_main.setTabOrder(self.input_radiobutton_notification_custom_sound, self.input_lineedit_notification_custom_sound_warning) - settings_main.setTabOrder(self.input_lineedit_notification_custom_sound_warning, self.button_choose_warning) - settings_main.setTabOrder(self.button_choose_warning, self.button_play_warning) - settings_main.setTabOrder(self.button_play_warning, self.input_lineedit_notification_custom_sound_critical) - settings_main.setTabOrder(self.input_lineedit_notification_custom_sound_critical, self.button_choose_critical) - settings_main.setTabOrder(self.button_choose_critical, self.button_play_critical) - settings_main.setTabOrder(self.button_play_critical, self.input_lineedit_notification_custom_sound_down) - settings_main.setTabOrder(self.input_lineedit_notification_custom_sound_down, self.button_choose_down) - settings_main.setTabOrder(self.button_choose_down, self.button_play_down) - settings_main.setTabOrder(self.button_play_down, self.input_checkbox_notification_actions) - settings_main.setTabOrder(self.input_checkbox_notification_actions, self.input_checkbox_notification_action_warning) - settings_main.setTabOrder(self.input_checkbox_notification_action_warning, self.input_lineedit_notification_action_warning_string) - settings_main.setTabOrder(self.input_lineedit_notification_action_warning_string, self.input_checkbox_notification_action_critical) - settings_main.setTabOrder(self.input_checkbox_notification_action_critical, self.input_lineedit_notification_action_critical_string) - settings_main.setTabOrder(self.input_lineedit_notification_action_critical_string, self.input_checkbox_notification_action_down) - settings_main.setTabOrder(self.input_checkbox_notification_action_down, self.input_lineedit_notification_action_down_string) - settings_main.setTabOrder(self.input_lineedit_notification_action_down_string, self.input_checkbox_notification_action_ok) - settings_main.setTabOrder(self.input_checkbox_notification_action_ok, self.input_lineedit_notification_action_ok_string) - settings_main.setTabOrder(self.input_lineedit_notification_action_ok_string, self.input_checkbox_notification_custom_action) - settings_main.setTabOrder(self.input_checkbox_notification_custom_action, self.input_lineedit_notification_custom_action_string) - settings_main.setTabOrder(self.input_lineedit_notification_custom_action_string, self.input_button_color_ok_text) - settings_main.setTabOrder(self.input_button_color_ok_text, self.input_button_color_ok_background) - settings_main.setTabOrder(self.input_button_color_ok_background, self.input_button_color_information_text) - settings_main.setTabOrder(self.input_button_color_information_text, self.input_button_color_information_background) - settings_main.setTabOrder(self.input_button_color_information_background, self.input_button_color_warning_text) - settings_main.setTabOrder(self.input_button_color_warning_text, self.input_button_color_warning_background) - settings_main.setTabOrder(self.input_button_color_warning_background, self.input_button_color_average_text) - settings_main.setTabOrder(self.input_button_color_average_text, self.input_button_color_average_background) - settings_main.setTabOrder(self.input_button_color_average_background, self.input_button_color_high_text) - settings_main.setTabOrder(self.input_button_color_high_text, self.input_button_color_high_background) - settings_main.setTabOrder(self.input_button_color_high_background, self.input_button_color_critical_text) - settings_main.setTabOrder(self.input_button_color_critical_text, self.input_button_color_critical_background) - settings_main.setTabOrder(self.input_button_color_critical_background, self.input_button_color_disaster_text) - settings_main.setTabOrder(self.input_button_color_disaster_text, self.input_button_color_disaster_background) - settings_main.setTabOrder(self.input_button_color_disaster_background, self.input_button_color_unknown_text) - settings_main.setTabOrder(self.input_button_color_unknown_text, self.input_button_color_unknown_background) - settings_main.setTabOrder(self.input_button_color_unknown_background, self.input_button_color_unreachable_text) - settings_main.setTabOrder(self.input_button_color_unreachable_text, self.input_button_color_unreachable_background) - settings_main.setTabOrder(self.input_button_color_unreachable_background, self.input_button_color_down_text) - settings_main.setTabOrder(self.input_button_color_down_text, self.input_button_color_down_background) - settings_main.setTabOrder(self.input_button_color_down_background, self.input_button_color_error_text) - settings_main.setTabOrder(self.input_button_color_error_text, self.input_button_color_error_background) - settings_main.setTabOrder(self.input_button_color_error_background, self.button_colors_reset) - settings_main.setTabOrder(self.button_colors_reset, self.button_colors_defaults) - settings_main.setTabOrder(self.button_colors_defaults, self.input_checkbox_defaults_acknowledge_sticky) - settings_main.setTabOrder(self.input_checkbox_defaults_acknowledge_sticky, self.input_checkbox_defaults_acknowledge_send_notification) - settings_main.setTabOrder(self.input_checkbox_defaults_acknowledge_send_notification, self.input_checkbox_defaults_acknowledge_persistent_comment) - settings_main.setTabOrder(self.input_checkbox_defaults_acknowledge_persistent_comment, self.input_checkbox_defaults_acknowledge_all_services) - settings_main.setTabOrder(self.input_checkbox_defaults_acknowledge_all_services, self.input_lineedit_defaults_acknowledge_comment) - settings_main.setTabOrder(self.input_lineedit_defaults_acknowledge_comment, self.input_checkbox_defaults_acknowledge_expire) - settings_main.setTabOrder(self.input_checkbox_defaults_acknowledge_expire, self.input_spinbox_defaults_acknowledge_expire_duration_hours) - settings_main.setTabOrder(self.input_spinbox_defaults_acknowledge_expire_duration_hours, self.input_spinbox_defaults_acknowledge_expire_duration_minutes) - settings_main.setTabOrder(self.input_spinbox_defaults_acknowledge_expire_duration_minutes, self.input_radiobutton_defaults_downtime_type_fixed) - settings_main.setTabOrder(self.input_radiobutton_defaults_downtime_type_fixed, self.input_radiobutton_defaults_downtime_type_flexible) - settings_main.setTabOrder(self.input_radiobutton_defaults_downtime_type_flexible, self.input_spinbox_defaults_downtime_duration_hours) - settings_main.setTabOrder(self.input_spinbox_defaults_downtime_duration_hours, self.input_spinbox_defaults_downtime_duration_minutes) - settings_main.setTabOrder(self.input_spinbox_defaults_downtime_duration_minutes, self.input_lineedit_defaults_downtime_comment) - settings_main.setTabOrder(self.input_lineedit_defaults_downtime_comment, self.input_lineedit_defaults_submit_check_result_comment) - settings_main.setTabOrder(self.input_lineedit_defaults_submit_check_result_comment, self.tabs) - settings_main.setTabOrder(self.tabs, self.input_checkbox_show_grid) - settings_main.setTabOrder(self.input_checkbox_show_grid, self.input_checkbox_grid_use_custom_intensity) - settings_main.setTabOrder(self.input_checkbox_grid_use_custom_intensity, self.input_slider_grid_alternation_intensity) - settings_main.setTabOrder(self.input_slider_grid_alternation_intensity, self.input_radiobutton_windowed) - settings_main.setTabOrder(self.input_radiobutton_windowed, self.input_lineedit_notification_custom_action_separator) - settings_main.setTabOrder(self.input_lineedit_notification_custom_action_separator, self.input_checkbox_notification_custom_action_single) - - def retranslateUi(self, settings_main): - _translate = QtCore.QCoreApplication.translate - settings_main.setWindowTitle(_translate("settings_main", "Nagstamon 2.0 settings")) - self.input_checkbox_debug_mode.setText(_translate("settings_main", "Debug mode")) - self.input_checkbox_debug_to_file.setText(_translate("settings_main", "Debug to file:")) - self.button_new_server.setText(_translate("settings_main", "New server...")) - self.button_edit_server.setText(_translate("settings_main", "Edit server...")) - self.button_copy_server.setText(_translate("settings_main", "Copy server...")) - self.button_delete_server.setText(_translate("settings_main", "Delete server")) - self.input_checkbox_use_system_keyring.setText(_translate("settings_main", "Use system keyring for server credentials")) - self.label_update_interval_seconds.setText(_translate("settings_main", "Update interval:")) - self.label_interval_seconds.setText(_translate("settings_main", "seconds")) - self.input_checkbox_check_for_new_version.setText(_translate("settings_main", "Check for new version at startup")) - self.button_check_for_new_version_now.setText(_translate("settings_main", "Check for new version now")) - self.tabs.setTabText(self.tabs.indexOf(self.tab_servers), _translate("settings_main", "Servers")) - self.groupbox_display_size.setTitle(_translate("settings_main", "Statusbar display size:")) - self.input_radiobutton_short_display.setText(_translate("settings_main", "Short [ 1 | 4 ]")) - self.input_radiobutton_long_display.setText(_translate("settings_main", "Long [ 1 WARNING | 4 CRITICAL ]")) - self.groupbox_detailed_summary_popup.setTitle(_translate("settings_main", "Statusbar details popup:")) - self.input_radiobutton_close_details_clicking.setText(_translate("settings_main", "Close when clicking statusbar")) - self.input_radiobutton_popup_details_clicking.setText(_translate("settings_main", "Popup when clicking statusbar")) - self.input_radiobutton_popup_details_hover.setText(_translate("settings_main", "Popup when hovering over statusbar")) - self.input_radiobutton_close_details_hover.setText(_translate("settings_main", "Close when hovering out of popup")) - self.input_radiobutton_close_details_clicking_somewhere.setText(_translate("settings_main", "Close when clicking somewhere")) - self.input_checkbox_highlight_new_events.setText(_translate("settings_main", "Highlight new events in status details overview")) - self.input_checkbox_show_tooltips.setText(_translate("settings_main", "Show tooltips in status details overview")) - self.label_default_sort_field.setText(_translate("settings_main", "Default sort field:")) - self.label_default_sort_order.setText(_translate("settings_main", "Default sort order:")) - self.groupbox_appearance.setTitle(_translate("settings_main", "Appearance:")) - self.input_radiobutton_icon_in_systray.setText(_translate("settings_main", "Icon in systray")) - self.input_checkbox_systray_offset_use.setText(_translate("settings_main", "Use offset to correct position problems")) - self.label_fullscreen_display.setText(_translate("settings_main", "Display to use:")) - self.label_offset_statuswindow.setText(_translate("settings_main", "Offset for statuswindow:")) - self.input_radiobutton_fullscreen.setText(_translate("settings_main", "Fullscreen")) - self.input_radiobutton_statusbar_floating.setText(_translate("settings_main", "Floating statusbar")) - self.input_radiobutton_windowed.setText(_translate("settings_main", "Window")) - self.groupbox_font.setTitle(_translate("settings_main", "Font:")) - self.label_font.setText(_translate("settings_main", "The CRITICAL Fox jumped over the WARNING dog.")) - self.button_fontchooser.setText(_translate("settings_main", "Change Font...")) - self.button_default_font.setText(_translate("settings_main", "Revert to default font")) - self.tabs.setTabText(self.tabs.indexOf(self.tab_display), _translate("settings_main", "Display")) - self.input_checkbox_re_attempt_enabled.setText(_translate("settings_main", "Regular expression for attempt")) - self.input_checkbox_re_service_enabled.setText(_translate("settings_main", "Regular expression for services")) - self.input_checkbox_re_status_information_reverse.setText(_translate("settings_main", "reverse")) - self.input_checkbox_re_duration_reverse.setText(_translate("settings_main", "reverse")) - self.input_checkbox_re_status_information_enabled.setText(_translate("settings_main", "Regular expression for status informations")) - self.input_checkbox_re_service_reverse.setText(_translate("settings_main", "reverse")) - self.input_checkbox_re_groups_enabled.setText(_translate("settings_main", "Regular expression for groups")) - self.input_checkbox_re_attempt_reverse.setText(_translate("settings_main", "reverse")) - self.input_checkbox_re_duration_enabled.setText(_translate("settings_main", "Regular expression for duration")) - self.input_checkbox_re_groups_reverse.setText(_translate("settings_main", "reverse")) - self.label_python_re.setText(_translate("settings_main", "<a href=http://docs.python.org/howto/regex.html>See Python Regular Expressions HOWTO for filtering details.</a>")) - self.input_checkbox_re_host_enabled.setText(_translate("settings_main", "Regular expression for hosts")) - self.groupbox_filters.setTitle(_translate("settings_main", "Filter out the following:")) - self.input_checkbox_filter_all_down_hosts.setText(_translate("settings_main", "All down hosts")) - self.input_checkbox_filter_all_unreachable_hosts.setText(_translate("settings_main", "All unreachable hosts")) - self.input_checkbox_filter_all_unreachable_services.setText(_translate("settings_main", "All unreachable services")) - self.input_checkbox_filter_all_flapping_hosts.setText(_translate("settings_main", "All flapping hosts")) - self.input_checkbox_filter_all_critical_services.setText(_translate("settings_main", "All critical services")) - self.input_checkbox_filter_acknowledged_hosts_services.setText(_translate("settings_main", "Acknowledged hosts and services")) - self.input_checkbox_filter_hosts_services_disabled_notifications.setText(_translate("settings_main", "Hosts and services with disabled notifications")) - self.input_checkbox_filter_hosts_services_disabled_checks.setText(_translate("settings_main", "Hosts and services with disabled checks")) - self.input_checkbox_filter_hosts_services_maintenance.setText(_translate("settings_main", "Hosts and services down for maintenance")) - self.input_checkbox_filter_services_on_acknowledged_hosts.setText(_translate("settings_main", "Services on acknowledged hosts")) - self.input_checkbox_filter_services_on_down_hosts.setText(_translate("settings_main", "Services on down hosts")) - self.input_checkbox_filter_all_high_services.setText(_translate("settings_main", "All high services")) - self.input_checkbox_filter_all_disaster_services.setText(_translate("settings_main", "All disaster services")) - self.input_checkbox_filter_all_flapping_services.setText(_translate("settings_main", "All flapping services")) - self.input_checkbox_filter_all_unknown_services.setText(_translate("settings_main", "All unknown services")) - self.input_checkbox_filter_all_warning_services.setText(_translate("settings_main", "All warning services")) - self.input_checkbox_filter_all_information_services.setText(_translate("settings_main", "All information services")) - self.input_checkbox_filter_all_average_services.setText(_translate("settings_main", "All average services")) - self.input_checkbox_filter_services_on_hosts_in_maintenance.setText(_translate("settings_main", "Services on hosts in maintenance")) - self.input_checkbox_filter_services_on_unreachable_hosts.setText(_translate("settings_main", "Services on unreachable hosts")) - self.input_checkbox_filter_hosts_in_soft_state.setText(_translate("settings_main", "Hosts in soft state")) - self.input_checkbox_filter_services_in_soft_state.setText(_translate("settings_main", "Services in soft state")) - self.input_checkbox_re_host_reverse.setText(_translate("settings_main", "reverse")) - self.tabs.setTabText(self.tabs.indexOf(self.tab_filters), _translate("settings_main", "Filters")) - self.button_new_action.setText(_translate("settings_main", "New action...")) - self.button_edit_action.setText(_translate("settings_main", "Edit action...")) - self.button_copy_action.setText(_translate("settings_main", "Copy action...")) - self.button_delete_action.setText(_translate("settings_main", "Delete action")) - self.groupbox_connection_method.setTitle(_translate("settings_main", "Connection method:")) - self.input_radiobutton_connect_by_host.setText(_translate("settings_main", "Hostname provided by monitor")) - self.input_radiobutton_connect_by_dns.setText(_translate("settings_main", "DNS name by looking up IP address")) - self.input_radiobutton_connect_by_ip.setText(_translate("settings_main", "IP resolved by hostname")) - self.groupbox_browser.setTitle(_translate("settings_main", "Browser:")) - self.input_radiobutton_use_default_browser.setText(_translate("settings_main", "Use default browser")) - self.input_radiobutton_use_custom_browser.setText(_translate("settings_main", "Use alternative browser")) - self.groupbox_custom_browser.setTitle(_translate("settings_main", "Custom browser:")) - self.button_choose_browser.setText(_translate("settings_main", "Choose browser...")) - self.tabs.setTabText(self.tabs.indexOf(self.tab_actions), _translate("settings_main", "Actions")) - self.input_checkbox_notification.setText(_translate("settings_main", "Enable notification")) - self.notification_groupbox.setTitle(_translate("settings_main", "Notifications:")) - self.input_checkbox_notify_if_warning.setText(_translate("settings_main", "WARNING")) - self.input_checkbox_notify_if_critical.setText(_translate("settings_main", "CRITICAL")) - self.input_checkbox_notify_if_unknown.setText(_translate("settings_main", "UNKNOWN")) - self.input_checkbox_notify_if_unreachable.setText(_translate("settings_main", "UNREACHABLE")) - self.input_checkbox_notify_if_down.setText(_translate("settings_main", "DOWN")) - self.input_checkbox_notify_if_information.setText(_translate("settings_main", "INFORMATION")) - self.input_checkbox_notify_if_disaster.setText(_translate("settings_main", "DISASTER")) - self.input_checkbox_notify_if_average.setText(_translate("settings_main", "AVERAGE")) - self.input_checkbox_notify_if_high.setText(_translate("settings_main", "HIGH")) - self.input_checkbox_notification_flashing.setText(_translate("settings_main", "Flashing statusbar/appindicator/systray icon")) - self.input_checkbox_notification_desktop.setText(_translate("settings_main", "Desktop notification")) - self.input_checkbox_notification_sound.setText(_translate("settings_main", "Enable sound")) - self.notification_sounds_groupbox.setTitle(_translate("settings_main", "Sounds:")) - self.notification_custom_sounds_groupbox.setTitle(_translate("settings_main", "Custom sounds:")) - self.button_choose_critical.setText(_translate("settings_main", "Choose file...")) - self.button_play_warning.setText(_translate("settings_main", "Play")) - self.label_notification_sound_options_custom_sounds_warning.setText(_translate("settings_main", "WARNING:")) - self.label_notification_sound_options_custom_sounds_critical.setText(_translate("settings_main", "CRITICAL:")) - self.button_choose_down.setText(_translate("settings_main", "Choose file...")) - self.button_choose_warning.setText(_translate("settings_main", "Choose file...")) - self.button_play_critical.setText(_translate("settings_main", "Play")) - self.label_notification_sound_options_custom_sounds_down.setText(_translate("settings_main", "DOWN:")) - self.button_play_down.setText(_translate("settings_main", "Play")) - self.input_checkbox_notification_sound_repeat.setText(_translate("settings_main", "Repeat sound until notification has been noticed")) - self.input_radiobutton_notification_default_sound.setText(_translate("settings_main", "Use default Nagios sounds")) - self.input_radiobutton_notification_custom_sound.setText(_translate("settings_main", "Use custom sounds")) - self.input_checkbox_notification_actions.setText(_translate("settings_main", "Enable notification actions")) - self.notification_actions_groupbox.setTitle(_translate("settings_main", "Notification actions:")) - self.input_lineedit_notification_action_ok_string.setToolTip(_translate("settings_main", "Just one single command to be executed like starting some blinking light etc.")) - self.input_checkbox_notification_action_critical.setText(_translate("settings_main", "CRITICAL")) - self.input_checkbox_notification_action_down.setText(_translate("settings_main", "DOWN")) - self.input_checkbox_notification_action_ok.setText(_translate("settings_main", "OK")) - self.input_checkbox_notification_action_warning.setText(_translate("settings_main", "WARNING")) - self.input_lineedit_notification_action_down_string.setToolTip(_translate("settings_main", "Just one single command to be executed like starting some blinking light etc.")) - self.input_checkbox_notification_custom_action.setText(_translate("settings_main", "Use custom notification action")) - self.notification_custom_action_groupbox.setTitle(_translate("settings_main", "Custom notification action:")) - self.label_notification_custom_action_string.setText(_translate("settings_main", "Action string:")) - self.label_notification_custom_action_separator.setText(_translate("settings_main", "Event separator:")) - self.input_lineedit_notification_custom_action_separator.setToolTip(_translate("settings_main", "Arbitrarily choosen string or character to separate events in $EVENTS$.")) - self.input_checkbox_notification_custom_action_single.setText(_translate("settings_main", "Run one single action for every event")) - self.input_lineedit_notification_custom_action_string.setToolTip(_translate("settings_main", "<html><head/><body><p>Available variables for action strings are $EVENTS$ for all events concatenated by the event separator or $EVENT$ if single actions are choosen.</p><p>To get all information into executable the variable should be quoted like \'$EVENT$\'.</p></body></html>")) - self.input_lineedit_notification_action_critical_string.setToolTip(_translate("settings_main", "Just one single command to be executed like starting some blinking light etc.")) - self.input_lineedit_notification_action_warning_string.setToolTip(_translate("settings_main", "Just one single command to be executed like starting some blinking light etc.")) - self.tabs.setTabText(self.tabs.indexOf(self.tab_notifications), _translate("settings_main", "Notifications")) - self.label_colors.setText(_translate("settings_main", "Set colors for text and background of status information:")) - self.states_groupbox.setTitle(_translate("settings_main", "Status:")) - self.label_color_ok.setText(_translate("settings_main", " OK ")) - self.label_color_information.setText(_translate("settings_main", " INFORMATION")) - self.label_color_warning.setText(_translate("settings_main", " WARNING ")) - self.label_color_average.setText(_translate("settings_main", " AVERAGE ")) - self.label_color_high.setText(_translate("settings_main", " HIGH ")) - self.label_color_critical.setText(_translate("settings_main", " CRITICAL ")) - self.label_color_disaster.setText(_translate("settings_main", " DISASTER ")) - self.label_color_unknown.setText(_translate("settings_main", " UNKNOWN ")) - self.label_color_unreachable.setText(_translate("settings_main", " UNREACHABLE ")) - self.label_color_down.setText(_translate("settings_main", " DOWN ")) - self.label_color_error.setText(_translate("settings_main", " ERROR ")) - self.text_groupbox.setTitle(_translate("settings_main", "Text:")) - self.background_groupbox.setTitle(_translate("settings_main", "Background:")) - self.button_colors_reset.setText(_translate("settings_main", "Reset to previous colors")) - self.button_colors_defaults.setText(_translate("settings_main", "Revert to default colors")) - self.groupBox_color_grid.setTitle(_translate("settings_main", "Colored grid:")) - self.input_checkbox_show_grid.setText(_translate("settings_main", "Use alternating colors for better readability")) - self.input_checkbox_grid_use_custom_intensity.setText(_translate("settings_main", "Customize intensity of alternation")) - self.label_intensity_information_0.setText(_translate("settings_main", " INFORMATION")) - self.label_intensity_information_1.setText(_translate("settings_main", " INFORMATION")) - self.label_intensity_warning_0.setText(_translate("settings_main", " WARNING")) - self.label_intensity_warning_1.setText(_translate("settings_main", " WARNING")) - self.label_intensity_average_0.setText(_translate("settings_main", " AVERAGE")) - self.label_intensity_average_1.setText(_translate("settings_main", " AVERAGE")) - self.label_intensity_high_0.setText(_translate("settings_main", " HIGH")) - self.label_intensity_high_1.setText(_translate("settings_main", " HIGH")) - self.label_intensity_critical_0.setText(_translate("settings_main", " CRITICAL")) - self.label_intensity_critical_1.setText(_translate("settings_main", " CRITICAL")) - self.label_intensity_disaster_0.setText(_translate("settings_main", " DISASTER")) - self.label_intensity_disaster_1.setText(_translate("settings_main", " DISASTER")) - self.label_intensity_unknown_0.setText(_translate("settings_main", " UNKNOWN")) - self.label_intensity_unknown_1.setText(_translate("settings_main", " UNKNOWN")) - self.label_intensity_unreachable_0.setText(_translate("settings_main", " UNREACHABLE")) - self.label_intensity_unreachable_1.setText(_translate("settings_main", " UNREACHABLE")) - self.label_intensity_down_0.setText(_translate("settings_main", " DOWN")) - self.label_intensity_down_1.setText(_translate("settings_main", " DOWN")) - self.tabs.setTabText(self.tabs.indexOf(self.tab_colors), _translate("settings_main", "Colors")) - self.label_defaults.setText(_translate("settings_main", "Set some default settings for default actions:")) - self.label_defaults_acknowledge.setTitle(_translate("settings_main", "Acknowledgements:")) - self.input_checkbox_defaults_acknowledge_sticky.setText(_translate("settings_main", "Sticky acknowledgement")) - self.input_checkbox_defaults_acknowledge_send_notification.setText(_translate("settings_main", "Send notification")) - self.input_checkbox_defaults_acknowledge_persistent_comment.setText(_translate("settings_main", "Persistent comment")) - self.input_checkbox_defaults_acknowledge_all_services.setText(_translate("settings_main", "Acknowledge all services on host")) - self.label_defaults_acknowledge_comment.setText(_translate("settings_main", "Comment:")) - self.input_checkbox_defaults_acknowledge_expire.setText(_translate("settings_main", "Use expire acknowledgement")) - self.label_expire_in.setText(_translate("settings_main", "Expire in: ")) - self.label_expire_in_hours.setText(_translate("settings_main", "hours")) - self.label_expire_in_minutes.setText(_translate("settings_main", "minutes")) - self.label_defaults_downtime.setTitle(_translate("settings_main", "Downtime:")) - self.label_defaults_downtime_comment.setText(_translate("settings_main", "Comment:")) - self.label_defaults_duration_comment.setText(_translate("settings_main", "Duration:")) - self.input_radiobutton_defaults_downtime_type_flexible.setText(_translate("settings_main", "Flexible")) - self.input_radiobutton_defaults_downtime_type_fixed.setText(_translate("settings_main", "Fixed")) - self.label_defaults_downtime_type.setText(_translate("settings_main", "Type:")) - self.label_duration_hour.setText(_translate("settings_main", "hours")) - self.label_duration_minutes.setText(_translate("settings_main", "minutes")) - self.label_acknowledge_submit_result.setTitle(_translate("settings_main", "Submit check result:")) - self.label_defaults_submit_check_result_comment.setText(_translate("settings_main", "Comment:")) - self.tabs.setTabText(self.tabs.indexOf(self.tab_defaults), _translate("settings_main", "Defaults")) - - -if __name__ == "__main__": - import sys - app = QtWidgets.QApplication(sys.argv) - settings_main = QtWidgets.QDialog() - ui = Ui_settings_main() - ui.setupUi(settings_main) - settings_main.show() - sys.exit(app.exec()) diff --git a/Nagstamon/QUI/settings_server.py b/Nagstamon/QUI/settings_server.py deleted file mode 100644 index 26e206c73..000000000 --- a/Nagstamon/QUI/settings_server.py +++ /dev/null @@ -1,440 +0,0 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'Nagstamon/QUI/settings_server.ui' -# -# Created by: PyQt5 UI code generator 5.15.4 -# -# WARNING: Any manual changes made to this file will be lost when pyuic5 is -# run again. Do not edit this file unless you know what you are doing. - - -from PyQt5 import QtCore, QtGui, QtWidgets - - -class Ui_settings_server(object): - def setupUi(self, settings_server): - settings_server.setObjectName("settings_server") - settings_server.resize(659, 1231) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(settings_server.sizePolicy().hasHeightForWidth()) - settings_server.setSizePolicy(sizePolicy) - settings_server.setModal(True) - self.gridLayout = QtWidgets.QGridLayout(settings_server) - self.gridLayout.setContentsMargins(10, 10, 10, 10) - self.gridLayout.setHorizontalSpacing(5) - self.gridLayout.setVerticalSpacing(2) - self.gridLayout.setObjectName("gridLayout") - self.label_password = QtWidgets.QLabel(settings_server) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_password.sizePolicy().hasHeightForWidth()) - self.label_password.setSizePolicy(sizePolicy) - self.label_password.setObjectName("label_password") - self.gridLayout.addWidget(self.label_password, 8, 0, 1, 1) - self.input_lineedit_monitor_url = QtWidgets.QLineEdit(settings_server) - self.input_lineedit_monitor_url.setInputMask("") - self.input_lineedit_monitor_url.setObjectName("input_lineedit_monitor_url") - self.gridLayout.addWidget(self.input_lineedit_monitor_url, 4, 2, 1, 2) - self.label_name = QtWidgets.QLabel(settings_server) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_name.sizePolicy().hasHeightForWidth()) - self.label_name.setSizePolicy(sizePolicy) - self.label_name.setObjectName("label_name") - self.gridLayout.addWidget(self.label_name, 3, 0, 1, 1) - self.input_lineedit_username = QtWidgets.QLineEdit(settings_server) - self.input_lineedit_username.setObjectName("input_lineedit_username") - self.gridLayout.addWidget(self.input_lineedit_username, 6, 2, 1, 1) - self.input_lineedit_monitor_cgi_url = QtWidgets.QLineEdit(settings_server) - self.input_lineedit_monitor_cgi_url.setObjectName("input_lineedit_monitor_cgi_url") - self.gridLayout.addWidget(self.input_lineedit_monitor_cgi_url, 5, 2, 1, 2) - self.input_combobox_type = QtWidgets.QComboBox(settings_server) - self.input_combobox_type.setObjectName("input_combobox_type") - self.gridLayout.addWidget(self.input_combobox_type, 1, 2, 1, 1) - self.input_lineedit_password = QtWidgets.QLineEdit(settings_server) - self.input_lineedit_password.setEchoMode(QtWidgets.QLineEdit.Password) - self.input_lineedit_password.setObjectName("input_lineedit_password") - self.gridLayout.addWidget(self.input_lineedit_password, 8, 2, 1, 1) - self.input_checkbox_use_proxy = QtWidgets.QCheckBox(settings_server) - self.input_checkbox_use_proxy.setObjectName("input_checkbox_use_proxy") - self.gridLayout.addWidget(self.input_checkbox_use_proxy, 17, 0, 1, 1) - self.groupbox_proxy = QtWidgets.QGroupBox(settings_server) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.groupbox_proxy.sizePolicy().hasHeightForWidth()) - self.groupbox_proxy.setSizePolicy(sizePolicy) - self.groupbox_proxy.setObjectName("groupbox_proxy") - self.gridLayout_2 = QtWidgets.QGridLayout(self.groupbox_proxy) - self.gridLayout_2.setContentsMargins(10, 10, 10, 10) - self.gridLayout_2.setSpacing(5) - self.gridLayout_2.setObjectName("gridLayout_2") - self.label_proxy_password = QtWidgets.QLabel(self.groupbox_proxy) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_proxy_password.sizePolicy().hasHeightForWidth()) - self.label_proxy_password.setSizePolicy(sizePolicy) - self.label_proxy_password.setObjectName("label_proxy_password") - self.gridLayout_2.addWidget(self.label_proxy_password, 3, 0, 1, 1) - self.label_proxy_username = QtWidgets.QLabel(self.groupbox_proxy) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_proxy_username.sizePolicy().hasHeightForWidth()) - self.label_proxy_username.setSizePolicy(sizePolicy) - self.label_proxy_username.setObjectName("label_proxy_username") - self.gridLayout_2.addWidget(self.label_proxy_username, 2, 0, 1, 1) - self.input_lineedit_proxy_username = QtWidgets.QLineEdit(self.groupbox_proxy) - self.input_lineedit_proxy_username.setObjectName("input_lineedit_proxy_username") - self.gridLayout_2.addWidget(self.input_lineedit_proxy_username, 2, 1, 1, 1) - self.input_lineedit_proxy_address = QtWidgets.QLineEdit(self.groupbox_proxy) - self.input_lineedit_proxy_address.setObjectName("input_lineedit_proxy_address") - self.gridLayout_2.addWidget(self.input_lineedit_proxy_address, 1, 1, 1, 1) - self.label_proxy_address = QtWidgets.QLabel(self.groupbox_proxy) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_proxy_address.sizePolicy().hasHeightForWidth()) - self.label_proxy_address.setSizePolicy(sizePolicy) - self.label_proxy_address.setObjectName("label_proxy_address") - self.gridLayout_2.addWidget(self.label_proxy_address, 1, 0, 1, 1) - self.input_lineedit_proxy_password = QtWidgets.QLineEdit(self.groupbox_proxy) - self.input_lineedit_proxy_password.setEchoMode(QtWidgets.QLineEdit.Password) - self.input_lineedit_proxy_password.setObjectName("input_lineedit_proxy_password") - self.gridLayout_2.addWidget(self.input_lineedit_proxy_password, 3, 1, 1, 1) - self.input_checkbox_use_proxy_from_os = QtWidgets.QCheckBox(self.groupbox_proxy) - self.input_checkbox_use_proxy_from_os.setObjectName("input_checkbox_use_proxy_from_os") - self.gridLayout_2.addWidget(self.input_checkbox_use_proxy_from_os, 4, 0, 1, 2) - self.gridLayout.addWidget(self.groupbox_proxy, 18, 0, 1, 4) - self.button_box = QtWidgets.QDialogButtonBox(settings_server) - self.button_box.setOrientation(QtCore.Qt.Horizontal) - self.button_box.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) - self.button_box.setObjectName("button_box") - self.gridLayout.addWidget(self.button_box, 29, 3, 1, 1) - self.label_server_type = QtWidgets.QLabel(settings_server) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_server_type.sizePolicy().hasHeightForWidth()) - self.label_server_type.setSizePolicy(sizePolicy) - self.label_server_type.setObjectName("label_server_type") - self.gridLayout.addWidget(self.label_server_type, 1, 0, 1, 1) - self.label_username = QtWidgets.QLabel(settings_server) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_username.sizePolicy().hasHeightForWidth()) - self.label_username.setSizePolicy(sizePolicy) - self.label_username.setObjectName("label_username") - self.gridLayout.addWidget(self.label_username, 6, 0, 1, 1) - self.label_monitor_url = QtWidgets.QLabel(settings_server) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_monitor_url.sizePolicy().hasHeightForWidth()) - self.label_monitor_url.setSizePolicy(sizePolicy) - self.label_monitor_url.setObjectName("label_monitor_url") - self.gridLayout.addWidget(self.label_monitor_url, 4, 0, 1, 1) - self.input_lineedit_name = QtWidgets.QLineEdit(settings_server) - self.input_lineedit_name.setObjectName("input_lineedit_name") - self.gridLayout.addWidget(self.input_lineedit_name, 3, 2, 1, 2) - self.label_monitor_cgi_url = QtWidgets.QLabel(settings_server) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_monitor_cgi_url.sizePolicy().hasHeightForWidth()) - self.label_monitor_cgi_url.setSizePolicy(sizePolicy) - self.label_monitor_cgi_url.setObjectName("label_monitor_cgi_url") - self.gridLayout.addWidget(self.label_monitor_cgi_url, 5, 0, 1, 1) - self.input_checkbox_enabled = QtWidgets.QCheckBox(settings_server) - self.input_checkbox_enabled.setObjectName("input_checkbox_enabled") - self.gridLayout.addWidget(self.input_checkbox_enabled, 0, 0, 1, 4) - self.input_checkbox_save_password = QtWidgets.QCheckBox(settings_server) - self.input_checkbox_save_password.setObjectName("input_checkbox_save_password") - self.gridLayout.addWidget(self.input_checkbox_save_password, 8, 3, 1, 1) - spacerItem = QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) - self.gridLayout.addItem(spacerItem, 26, 0, 1, 4) - self.input_checkbox_show_options = QtWidgets.QCheckBox(settings_server) - self.input_checkbox_show_options.setObjectName("input_checkbox_show_options") - self.gridLayout.addWidget(self.input_checkbox_show_options, 27, 0, 1, 4) - self.groupbox_options = QtWidgets.QGroupBox(settings_server) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.groupbox_options.sizePolicy().hasHeightForWidth()) - self.groupbox_options.setSizePolicy(sizePolicy) - self.groupbox_options.setObjectName("groupbox_options") - self.gridLayout_3 = QtWidgets.QGridLayout(self.groupbox_options) - self.gridLayout_3.setContentsMargins(10, 10, 10, 10) - self.gridLayout_3.setHorizontalSpacing(10) - self.gridLayout_3.setVerticalSpacing(5) - self.gridLayout_3.setObjectName("gridLayout_3") - self.horizontalLayout = QtWidgets.QHBoxLayout() - self.horizontalLayout.setObjectName("horizontalLayout") - self.input_lineedit_custom_cert_ca_file = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_custom_cert_ca_file.setObjectName("input_lineedit_custom_cert_ca_file") - self.horizontalLayout.addWidget(self.input_lineedit_custom_cert_ca_file) - self.button_choose_custom_cert_ca_file = QtWidgets.QPushButton(self.groupbox_options) - self.button_choose_custom_cert_ca_file.setObjectName("button_choose_custom_cert_ca_file") - self.horizontalLayout.addWidget(self.button_choose_custom_cert_ca_file) - self.gridLayout_3.addLayout(self.horizontalLayout, 2, 2, 1, 2) - self.input_checkbox_use_autologin = QtWidgets.QCheckBox(self.groupbox_options) - self.input_checkbox_use_autologin.setObjectName("input_checkbox_use_autologin") - self.gridLayout_3.addWidget(self.input_checkbox_use_autologin, 7, 1, 1, 3) - self.input_lineedit_alertmanager_filter = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_alertmanager_filter.setObjectName("input_lineedit_alertmanager_filter") - self.gridLayout_3.addWidget(self.input_lineedit_alertmanager_filter, 12, 2, 1, 2) - self.label_map_to_hostname = QtWidgets.QLabel(self.groupbox_options) - self.label_map_to_hostname.setObjectName("label_map_to_hostname") - self.gridLayout_3.addWidget(self.label_map_to_hostname, 13, 1, 1, 1) - self.input_lineedit_map_to_hostname = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_map_to_hostname.setObjectName("input_lineedit_map_to_hostname") - self.gridLayout_3.addWidget(self.input_lineedit_map_to_hostname, 13, 2, 1, 2) - self.input_checkbox_use_display_name_service = QtWidgets.QCheckBox(self.groupbox_options) - self.input_checkbox_use_display_name_service.setObjectName("input_checkbox_use_display_name_service") - self.gridLayout_3.addWidget(self.input_checkbox_use_display_name_service, 22, 1, 1, 3) - self.input_lineedit_autologin_key = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_autologin_key.setText("") - self.input_lineedit_autologin_key.setObjectName("input_lineedit_autologin_key") - self.gridLayout_3.addWidget(self.input_lineedit_autologin_key, 9, 2, 1, 2) - self.input_lineedit_map_to_ok = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_map_to_ok.setObjectName("input_lineedit_map_to_ok") - self.gridLayout_3.addWidget(self.input_lineedit_map_to_ok, 16, 2, 1, 2) - self.label_idp_ecp_endpoint = QtWidgets.QLabel(self.groupbox_options) - self.label_idp_ecp_endpoint.setObjectName("label_idp_ecp_endpoint") - self.gridLayout_3.addWidget(self.label_idp_ecp_endpoint, 26, 1, 1, 1) - self.label_alertmanager_filter = QtWidgets.QLabel(self.groupbox_options) - self.label_alertmanager_filter.setObjectName("label_alertmanager_filter") - self.gridLayout_3.addWidget(self.label_alertmanager_filter, 12, 1, 1, 1) - self.label_map_to_servicename = QtWidgets.QLabel(self.groupbox_options) - self.label_map_to_servicename.setObjectName("label_map_to_servicename") - self.gridLayout_3.addWidget(self.label_map_to_servicename, 14, 1, 1, 1) - self.input_checkbox_custom_cert_use = QtWidgets.QCheckBox(self.groupbox_options) - self.input_checkbox_custom_cert_use.setObjectName("input_checkbox_custom_cert_use") - self.gridLayout_3.addWidget(self.input_checkbox_custom_cert_use, 1, 1, 1, 2) - spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.gridLayout_3.addItem(spacerItem1, 6, 3, 1, 1) - self.input_lineedit_service_filter = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_service_filter.setText("") - self.input_lineedit_service_filter.setObjectName("input_lineedit_service_filter") - self.gridLayout_3.addWidget(self.input_lineedit_service_filter, 11, 2, 1, 2) - self.label_timeout = QtWidgets.QLabel(self.groupbox_options) - self.label_timeout.setObjectName("label_timeout") - self.gridLayout_3.addWidget(self.label_timeout, 6, 1, 1, 1) - self.input_checkbox_force_authuser = QtWidgets.QCheckBox(self.groupbox_options) - self.input_checkbox_force_authuser.setObjectName("input_checkbox_force_authuser") - self.gridLayout_3.addWidget(self.input_checkbox_force_authuser, 24, 1, 1, 3) - self.input_lineedit_map_to_status_information = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_map_to_status_information.setObjectName("input_lineedit_map_to_status_information") - self.gridLayout_3.addWidget(self.input_lineedit_map_to_status_information, 15, 2, 1, 2) - self.input_checkbox_no_cookie_auth = QtWidgets.QCheckBox(self.groupbox_options) - self.input_checkbox_no_cookie_auth.setObjectName("input_checkbox_no_cookie_auth") - self.gridLayout_3.addWidget(self.input_checkbox_no_cookie_auth, 20, 1, 1, 3) - self.label_autologin_key = QtWidgets.QLabel(self.groupbox_options) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_autologin_key.sizePolicy().hasHeightForWidth()) - self.label_autologin_key.setSizePolicy(sizePolicy) - self.label_autologin_key.setObjectName("label_autologin_key") - self.gridLayout_3.addWidget(self.label_autologin_key, 9, 1, 1, 1) - spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.gridLayout_3.addItem(spacerItem2, 5, 3, 1, 1) - self.input_lineedit_idp_ecp_endpoint = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_idp_ecp_endpoint.setObjectName("input_lineedit_idp_ecp_endpoint") - self.gridLayout_3.addWidget(self.input_lineedit_idp_ecp_endpoint, 26, 2, 1, 2) - self.label_auth_type = QtWidgets.QLabel(self.groupbox_options) - self.label_auth_type.setObjectName("label_auth_type") - self.gridLayout_3.addWidget(self.label_auth_type, 5, 1, 1, 1) - self.label_service_filter = QtWidgets.QLabel(self.groupbox_options) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_service_filter.sizePolicy().hasHeightForWidth()) - self.label_service_filter.setSizePolicy(sizePolicy) - self.label_service_filter.setObjectName("label_service_filter") - self.gridLayout_3.addWidget(self.label_service_filter, 11, 1, 1, 1) - self.horizontalLayout_timeout_seconds = QtWidgets.QHBoxLayout() - self.horizontalLayout_timeout_seconds.setSpacing(5) - self.horizontalLayout_timeout_seconds.setObjectName("horizontalLayout_timeout_seconds") - self.input_spinbox_timeout = QtWidgets.QSpinBox(self.groupbox_options) - self.input_spinbox_timeout.setObjectName("input_spinbox_timeout") - self.horizontalLayout_timeout_seconds.addWidget(self.input_spinbox_timeout) - self.label_timeout_sec = QtWidgets.QLabel(self.groupbox_options) - self.label_timeout_sec.setObjectName("label_timeout_sec") - self.horizontalLayout_timeout_seconds.addWidget(self.label_timeout_sec) - self.gridLayout_3.addLayout(self.horizontalLayout_timeout_seconds, 6, 2, 1, 1) - self.label_monitor_site = QtWidgets.QLabel(self.groupbox_options) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_monitor_site.sizePolicy().hasHeightForWidth()) - self.label_monitor_site.setSizePolicy(sizePolicy) - self.label_monitor_site.setObjectName("label_monitor_site") - self.gridLayout_3.addWidget(self.label_monitor_site, 8, 1, 1, 1) - self.input_checkbox_use_description_name_service = QtWidgets.QCheckBox(self.groupbox_options) - self.input_checkbox_use_description_name_service.setObjectName("input_checkbox_use_description_name_service") - self.gridLayout_3.addWidget(self.input_checkbox_use_description_name_service, 23, 1, 1, 3) - self.input_combobox_authentication = QtWidgets.QComboBox(self.groupbox_options) - self.input_combobox_authentication.setObjectName("input_combobox_authentication") - self.gridLayout_3.addWidget(self.input_combobox_authentication, 5, 2, 1, 1) - self.groupbox_checkmk_views = QtWidgets.QGroupBox(self.groupbox_options) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.groupbox_checkmk_views.sizePolicy().hasHeightForWidth()) - self.groupbox_checkmk_views.setSizePolicy(sizePolicy) - self.groupbox_checkmk_views.setObjectName("groupbox_checkmk_views") - self.gridLayout_4 = QtWidgets.QGridLayout(self.groupbox_checkmk_views) - self.gridLayout_4.setObjectName("gridLayout_4") - self.label_checkmk_view_hosts = QtWidgets.QLabel(self.groupbox_checkmk_views) - self.label_checkmk_view_hosts.setObjectName("label_checkmk_view_hosts") - self.gridLayout_4.addWidget(self.label_checkmk_view_hosts, 0, 0, 1, 1) - self.label_checkmk_view_services = QtWidgets.QLabel(self.groupbox_checkmk_views) - self.label_checkmk_view_services.setObjectName("label_checkmk_view_services") - self.gridLayout_4.addWidget(self.label_checkmk_view_services, 1, 0, 1, 1) - self.input_lineedit_checkmk_view_services = QtWidgets.QLineEdit(self.groupbox_checkmk_views) - self.input_lineedit_checkmk_view_services.setObjectName("input_lineedit_checkmk_view_services") - self.gridLayout_4.addWidget(self.input_lineedit_checkmk_view_services, 1, 1, 1, 1) - self.input_lineedit_checkmk_view_hosts = QtWidgets.QLineEdit(self.groupbox_checkmk_views) - self.input_lineedit_checkmk_view_hosts.setObjectName("input_lineedit_checkmk_view_hosts") - self.gridLayout_4.addWidget(self.input_lineedit_checkmk_view_hosts, 0, 1, 1, 1) - self.button_checkmk_view_hosts_reset = QtWidgets.QPushButton(self.groupbox_checkmk_views) - self.button_checkmk_view_hosts_reset.setObjectName("button_checkmk_view_hosts_reset") - self.gridLayout_4.addWidget(self.button_checkmk_view_hosts_reset, 0, 2, 1, 1) - self.button_checkmk_view_services_reset = QtWidgets.QPushButton(self.groupbox_checkmk_views) - self.button_checkmk_view_services_reset.setObjectName("button_checkmk_view_services_reset") - self.gridLayout_4.addWidget(self.button_checkmk_view_services_reset, 1, 2, 1, 1) - self.gridLayout_3.addWidget(self.groupbox_checkmk_views, 25, 1, 1, 3) - self.label_host_filter = QtWidgets.QLabel(self.groupbox_options) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.label_host_filter.sizePolicy().hasHeightForWidth()) - self.label_host_filter.setSizePolicy(sizePolicy) - self.label_host_filter.setObjectName("label_host_filter") - self.gridLayout_3.addWidget(self.label_host_filter, 10, 1, 1, 1) - self.input_checkbox_ignore_cert = QtWidgets.QCheckBox(self.groupbox_options) - self.input_checkbox_ignore_cert.setObjectName("input_checkbox_ignore_cert") - self.gridLayout_3.addWidget(self.input_checkbox_ignore_cert, 0, 1, 1, 3) - self.input_lineedit_map_to_servicename = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_map_to_servicename.setObjectName("input_lineedit_map_to_servicename") - self.gridLayout_3.addWidget(self.input_lineedit_map_to_servicename, 14, 2, 1, 2) - self.input_lineedit_host_filter = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_host_filter.setText("") - self.input_lineedit_host_filter.setObjectName("input_lineedit_host_filter") - self.gridLayout_3.addWidget(self.input_lineedit_host_filter, 10, 2, 1, 2) - self.label_custom_ca_file = QtWidgets.QLabel(self.groupbox_options) - self.label_custom_ca_file.setObjectName("label_custom_ca_file") - self.gridLayout_3.addWidget(self.label_custom_ca_file, 2, 1, 1, 1) - self.label_map_to_status_information = QtWidgets.QLabel(self.groupbox_options) - self.label_map_to_status_information.setObjectName("label_map_to_status_information") - self.gridLayout_3.addWidget(self.label_map_to_status_information, 15, 1, 1, 1) - self.input_lineedit_monitor_site = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_monitor_site.setObjectName("input_lineedit_monitor_site") - self.gridLayout_3.addWidget(self.input_lineedit_monitor_site, 8, 2, 1, 1) - self.input_checkbox_use_display_name_host = QtWidgets.QCheckBox(self.groupbox_options) - self.input_checkbox_use_display_name_host.setObjectName("input_checkbox_use_display_name_host") - self.gridLayout_3.addWidget(self.input_checkbox_use_display_name_host, 21, 1, 1, 3) - self.input_lineedit_map_to_warning = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_map_to_warning.setObjectName("input_lineedit_map_to_warning") - self.gridLayout_3.addWidget(self.input_lineedit_map_to_warning, 17, 2, 1, 2) - self.input_lineedit_map_to_critical = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_map_to_critical.setObjectName("input_lineedit_map_to_critical") - self.gridLayout_3.addWidget(self.input_lineedit_map_to_critical, 18, 2, 1, 2) - self.input_lineedit_map_to_unknown = QtWidgets.QLineEdit(self.groupbox_options) - self.input_lineedit_map_to_unknown.setObjectName("input_lineedit_map_to_unknown") - self.gridLayout_3.addWidget(self.input_lineedit_map_to_unknown, 19, 2, 1, 2) - self.label_map_to_ok = QtWidgets.QLabel(self.groupbox_options) - self.label_map_to_ok.setObjectName("label_map_to_ok") - self.gridLayout_3.addWidget(self.label_map_to_ok, 16, 1, 1, 1) - self.label_map_to_warning = QtWidgets.QLabel(self.groupbox_options) - self.label_map_to_warning.setObjectName("label_map_to_warning") - self.gridLayout_3.addWidget(self.label_map_to_warning, 17, 1, 1, 1) - self.label_map_to_critical = QtWidgets.QLabel(self.groupbox_options) - self.label_map_to_critical.setObjectName("label_map_to_critical") - self.gridLayout_3.addWidget(self.label_map_to_critical, 18, 1, 1, 1) - self.label_map_to_unknown = QtWidgets.QLabel(self.groupbox_options) - self.label_map_to_unknown.setObjectName("label_map_to_unknown") - self.gridLayout_3.addWidget(self.label_map_to_unknown, 19, 1, 1, 1) - self.gridLayout.addWidget(self.groupbox_options, 28, 0, 1, 4) - - self.retranslateUi(settings_server) - QtCore.QMetaObject.connectSlotsByName(settings_server) - settings_server.setTabOrder(self.input_checkbox_enabled, self.input_combobox_type) - settings_server.setTabOrder(self.input_combobox_type, self.input_lineedit_name) - settings_server.setTabOrder(self.input_lineedit_name, self.input_lineedit_monitor_url) - settings_server.setTabOrder(self.input_lineedit_monitor_url, self.input_lineedit_monitor_cgi_url) - settings_server.setTabOrder(self.input_lineedit_monitor_cgi_url, self.input_lineedit_username) - settings_server.setTabOrder(self.input_lineedit_username, self.input_lineedit_password) - settings_server.setTabOrder(self.input_lineedit_password, self.input_checkbox_save_password) - settings_server.setTabOrder(self.input_checkbox_save_password, self.input_checkbox_use_proxy) - settings_server.setTabOrder(self.input_checkbox_use_proxy, self.input_lineedit_proxy_address) - settings_server.setTabOrder(self.input_lineedit_proxy_address, self.input_lineedit_proxy_username) - settings_server.setTabOrder(self.input_lineedit_proxy_username, self.input_lineedit_proxy_password) - settings_server.setTabOrder(self.input_lineedit_proxy_password, self.input_checkbox_use_proxy_from_os) - - def retranslateUi(self, settings_server): - _translate = QtCore.QCoreApplication.translate - settings_server.setWindowTitle(_translate("settings_server", "Dialog")) - self.label_password.setText(_translate("settings_server", "Password:")) - self.input_lineedit_monitor_url.setText(_translate("settings_server", "https://monitor-server")) - self.label_name.setText(_translate("settings_server", "Monitor name:")) - self.input_lineedit_username.setText(_translate("settings_server", "username")) - self.input_lineedit_monitor_cgi_url.setText(_translate("settings_server", "https://monitor-server/monitor/cgi-bin")) - self.input_lineedit_password.setText(_translate("settings_server", "1234567890")) - self.input_checkbox_use_proxy.setText(_translate("settings_server", "Use proxy")) - self.groupbox_proxy.setTitle(_translate("settings_server", "Proxy:")) - self.label_proxy_password.setText(_translate("settings_server", "Proxy password:")) - self.label_proxy_username.setText(_translate("settings_server", "Proxy username:")) - self.input_lineedit_proxy_username.setText(_translate("settings_server", "proxyusername")) - self.input_lineedit_proxy_address.setText(_translate("settings_server", "http://proxy:port/")) - self.label_proxy_address.setText(_translate("settings_server", "Proxy address:")) - self.input_lineedit_proxy_password.setText(_translate("settings_server", "1234567890")) - self.input_checkbox_use_proxy_from_os.setText(_translate("settings_server", "Use proxy from OS")) - self.label_server_type.setText(_translate("settings_server", "Monitor type:")) - self.label_username.setText(_translate("settings_server", "Username:")) - self.label_monitor_url.setText(_translate("settings_server", "Monitor URL:")) - self.input_lineedit_name.setText(_translate("settings_server", "Monitor server")) - self.label_monitor_cgi_url.setText(_translate("settings_server", "Monitor CGI URL:")) - self.input_checkbox_enabled.setText(_translate("settings_server", "Enabled")) - self.input_checkbox_save_password.setText(_translate("settings_server", "Save password")) - self.input_checkbox_show_options.setText(_translate("settings_server", "Show more options")) - self.groupbox_options.setTitle(_translate("settings_server", "Options:")) - self.button_choose_custom_cert_ca_file.setText(_translate("settings_server", "Choose file...")) - self.input_checkbox_use_autologin.setText(_translate("settings_server", "Use autologin")) - self.label_map_to_hostname.setText(_translate("settings_server", "Map to hostname:")) - self.input_checkbox_use_display_name_service.setText(_translate("settings_server", "Use display name as service name")) - self.label_idp_ecp_endpoint.setText(_translate("settings_server", "IdP ECP endpoint URL")) - self.label_alertmanager_filter.setText(_translate("settings_server", "Filter:")) - self.label_map_to_servicename.setText(_translate("settings_server", "Map to servicename:")) - self.input_checkbox_custom_cert_use.setText(_translate("settings_server", "Use custom CA file")) - self.label_timeout.setText(_translate("settings_server", "Timeout:")) - self.input_checkbox_force_authuser.setText(_translate("settings_server", "Only show permitted hosts for \"see all\" users (1.4.0i1 or newer)")) - self.input_checkbox_no_cookie_auth.setText(_translate("settings_server", "Do not use cookie authentication")) - self.label_autologin_key.setText(_translate("settings_server", "Autologin Key:")) - self.label_auth_type.setText(_translate("settings_server", "Authentication:")) - self.label_service_filter.setText(_translate("settings_server", "Service filter:")) - self.label_timeout_sec.setText(_translate("settings_server", "seconds")) - self.label_monitor_site.setText(_translate("settings_server", "Monitor Site:")) - self.input_checkbox_use_description_name_service.setText(_translate("settings_server", "Use description as service name")) - self.groupbox_checkmk_views.setTitle(_translate("settings_server", "Views:")) - self.label_checkmk_view_hosts.setText(_translate("settings_server", "Hosts:")) - self.label_checkmk_view_services.setText(_translate("settings_server", "Services:")) - self.button_checkmk_view_hosts_reset.setText(_translate("settings_server", "Reset")) - self.button_checkmk_view_services_reset.setText(_translate("settings_server", "Reset")) - self.label_host_filter.setText(_translate("settings_server", "Host filter:")) - self.input_checkbox_ignore_cert.setText(_translate("settings_server", "Ignore SSL/TLS certificate")) - self.label_custom_ca_file.setText(_translate("settings_server", "Custom CA file: ")) - self.label_map_to_status_information.setText(_translate("settings_server", "Map to status_information:")) - self.input_lineedit_monitor_site.setText(_translate("settings_server", "Site 1")) - self.input_checkbox_use_display_name_host.setText(_translate("settings_server", "Use display name as host name")) - self.label_map_to_ok.setText(_translate("settings_server", "Map to OK")) - self.label_map_to_warning.setText(_translate("settings_server", "Map to WARNING")) - self.label_map_to_critical.setText(_translate("settings_server", "Map to CRITICAL")) - self.label_map_to_unknown.setText(_translate("settings_server", "Map to UNKNOWN")) From d0a0a07e0a13d3cab1c1b8ba2ade1e31e01dc6dd Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 8 May 2022 15:45:40 +0200 Subject: [PATCH 240/884] screens going to be named --- Nagstamon/QUI/__init__.py | 63 ++++++++++++++------------------------- 1 file changed, 23 insertions(+), 40 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 1f5004a06..29065c1db 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -1225,8 +1225,8 @@ def set_mode(self): self.move(conf.position_x, conf.position_y) else: # get available desktop specs - available_x = desktop.availableGeometry(self).x() - available_y = desktop.availableGeometry(self).y() + available_x = self.screen().vailableGeometry().x() + available_y = self.screen().availableGeometry().y() self.move(available_x, available_y) # statusbar and detail window should be frameless and stay on top @@ -1252,8 +1252,8 @@ def set_mode(self): self.move(conf.position_x, conf.position_y) else: # get available desktop specs - available_x = desktop.availableGeometry(self).x() - available_y = desktop.availableGeometry(self).y() + available_x = self.screen().availableGeometry().x() + available_y = self.screen().availableGeometry().y() self.move(available_x, available_y) # need a close button @@ -1325,14 +1325,14 @@ def set_mode(self): self.servers_scrollarea.show() # keep window entry in taskbar and thus no Qt.Tool - self.setWindowFlags(Qt.Widget) + self.setWindowFlags(Qt.WindowType.Widget) # show statusbar actively self.setAttribute(Qt.WidgetAttribute.WA_ShowWithoutActivating, False) # some maybe sensible default self.setMinimumSize(700, 300) - self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) + self.setSizePolicy(QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.MinimumExpanding) # default maximum size self.setMaximumSize(16777215, 16777215) @@ -1732,11 +1732,7 @@ def calculate_size(self): """ get size of popup window """ - # screen number or widget object needed for desktop.availableGeometry - if conf.statusbar_floating or conf.windowed: - screen_or_widget = self - - elif conf.icon_in_systray: + if conf.icon_in_systray: # where is the pointer which clicked onto systray icon icon_x = systrayicon.geometry().x() icon_y = systrayicon.geometry().y() @@ -1766,12 +1762,12 @@ def calculate_size(self): # only consider offset if it is configured if conf.systray_offset_use and conf.icon_in_systray: - available_height = desktop.availableGeometry(screen_or_widget).height() - conf.systray_offset + available_height = self.screen().availableGeometry().height() - conf.systray_offset else: - available_height = desktop.availableGeometry(screen_or_widget).height() - available_width = desktop.availableGeometry(screen_or_widget).width() - available_x = desktop.availableGeometry(screen_or_widget).x() - available_y = desktop.availableGeometry(screen_or_widget).y() + available_height = self.screen().availableGeometry().height() + available_width = self.screen().availableGeometry().width() + available_x = self.screen().availableGeometry().x() + available_y = self.screen().availableGeometry().y() # Workaround for Cinnamon if OS not in OS_NON_LINUX and DESKTOP_CINNAMON: @@ -1787,7 +1783,7 @@ def calculate_size(self): # calculate top-ness only if window is closed if conf.statusbar_floating: # if self.is_shown is False: - if self.y() < desktop.screenGeometry(self).height() // 2 + available_y: + if self.y() < self.screen().screenGeometry().height() // 2 + available_y: self.top = True else: self.top = False @@ -1796,7 +1792,7 @@ def calculate_size(self): x = self.stored_x elif conf.icon_in_systray or conf.windowed: - if self.icon_y < desktop.screenGeometry(self).height() // 2 + available_y: + if self.icon_y < self.screen().screenGeometry().height() // 2 + available_y: self.top = True else: self.top = False @@ -1837,8 +1833,8 @@ def calculate_size(self): else: # when height is to large for current screen cut it if self.y() + self.height() - real_height < available_y: - height = desktop.screenGeometry().height() - available_y - ( - desktop.screenGeometry().height() - (self.y() + self.height())) + height = self.screen().screenGeometry().height() - available_y - ( + self.screen().screenGeometry().height() - (self.y() + self.height())) y = available_y else: height = real_height @@ -4996,7 +4992,7 @@ def initialize(self): self.ui.input_combobox_default_sort_order.setCurrentText(conf.default_sort_order.title()) # fill combobox with screens for fullscreen - for display in range(len(desktop)): + for display in APP.screens(): self.ui.input_combobox_fullscreen_display.addItem(str(display)) self.ui.input_combobox_fullscreen_display.setCurrentText(str(conf.fullscreen_display)) @@ -5134,7 +5130,7 @@ def ok(self): conf.font = self.font.toString() # update global font and icons font FONT = self.font - ICONS_FONT = QFont('Nagstamon', FONT.pointSize() + 2, QFont.Normal, False) + ICONS_FONT = QFont('Nagstamon', FONT.pointSize() + 2, QFont.Weight.Normal, False) # update brushes for treeview create_brushes() @@ -7104,20 +7100,12 @@ def get_screen(x, y): # integerify these values as they *might* be strings x = int(x) y = int(y) - - number_of_screens = desktop.screenCount() - for screen in range(number_of_screens + 1): - # if coordinates are inside screen just break and return screen - if (desktop.screenGeometry(screen).contains(x, y)): - break - - del (x, y) - - # when 'screen' reached number_of_screens no screen was found, thus return None - if screen == number_of_screens: - return None + screen = APP.screenAt(QPoint(x,y)) + del x, y + if screen: + return screen.name else: - return screen + return None def get_screen_geometry(screen_number): @@ -7198,11 +7186,6 @@ def is_modifier_pressed(): # check for updates check_version = CheckVersion() -# access to various desktop parameters -#desktop = APP.desktop() -# desktop = QScreen -desktop = APP.screens() - # access to clipboard clipboard = APP.clipboard() From 27779c142c338493f970a747c467fc40d81129b2 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 8 May 2022 22:58:43 +0200 Subject: [PATCH 241/884] floating statusbar --- Nagstamon/QUI/__init__.py | 40 ++++++++++++++++++++------------------- Nagstamon/QUI/qt.py | 2 +- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 29065c1db..c2ea9d6e3 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -813,23 +813,25 @@ def mousePressEvent(self, event): 2 - right button, popup menu """ - if event.button() == Qt.LeftButton: + if event.button() == Qt.MouseButton.LeftButton: self.mouse_pressed.emit() - if event.button() == Qt.RightButton: + if event.button() == Qt.MouseButton.RightButton: self.right_mouse_button_pressed = True # keep x and y relative to statusbar # if not set calculate relative position if not statuswindow.relative_x and \ not statuswindow.relative_y: - statuswindow.relative_x = event.globalX() - statuswindow.x() - statuswindow.relative_y = event.globalY() - statuswindow.y() + globby = event.globalPosition() + event_x, event_y = event.globalPosition() + statuswindow.relative_x = event_x - statuswindow.x() + statuswindow.relative_y = event_y - statuswindow.y() def mouseReleaseEvent(self, event): """ decide if moving or menu should be treated after mouse button was released """ - if event.button() == Qt.LeftButton: + if event.button() == Qt.MouseButton.LeftButton: # if popup window should be closed by clicking do it now if statuswindow.is_shown and \ (conf.close_details_clicking or @@ -845,7 +847,7 @@ def mouseReleaseEvent(self, event): statuswindow.relative_y = False statuswindow.moving = False - if event.button() == Qt.RightButton: + if event.button() == Qt.MouseButton.RightButton: self.right_mouse_button_pressed = False self.menu.show_at_cursor() @@ -914,7 +916,7 @@ def mouseReleaseEvent(self, event): """ left click and configured close-if-clicking-somewhere makes statuswindow close """ - if event.button() == Qt.LeftButton and conf.close_details_clicking_somewhere: + if event.button() == Qt.MouseButton.LeftButton and conf.close_details_clicking_somewhere: # if popup window should be closed by clicking do it now if statuswindow.is_shown and \ not conf.fullscreen and \ @@ -1221,7 +1223,7 @@ def set_mode(self): # show statusbar/statuswindow on last saved position # when coordinates are inside known screens - if get_screen(conf.position_x, conf.position_y) is not None: + if get_screen(conf.position_x, conf.position_y): self.move(conf.position_x, conf.position_y) else: # get available desktop specs @@ -1248,7 +1250,7 @@ def set_mode(self): # show statusbar/statuswindow on last saved position # when coordinates are inside known screens - if get_screen(conf.position_x, conf.position_y) is not None: + if get_screen(conf.position_x, conf.position_y): self.move(conf.position_x, conf.position_y) else: # get available desktop specs @@ -3379,7 +3381,7 @@ def mouseReleaseEvent_X(self, event): # special treatment if window should be closed when left-clicking somewhere # it is important to check if CTRL or SHIFT key is presses while clicking to select lines if conf.close_details_clicking_somewhere: - if event.button() == Qt.LeftButton: + if event.button() == Qt.MouseButton.LeftButton: modifiers = event.modifiers() # count selected rows - if more than 1 do not close popwin rows = [] @@ -3394,11 +3396,11 @@ def mouseReleaseEvent_X(self, event): else: statuswindow.hide_window() del modifiers, rows - elif event.button() == Qt.RightButton: + elif event.button() == Qt.MouseButton.RightButton: self.cell_clicked() return else: - if event.button() == Qt.RightButton or event.button() == Qt.LeftButton: + if event.button() == Qt.MouseButton.RightButton or event.button() == Qt.MouseButton.LeftButton: self.cell_clicked() return super(TreeView, self).mouseReleaseEvent(event) @@ -3409,7 +3411,7 @@ def mouseReleaseEvent(self, event): """ # special treatment if window should be closed when left-clicking somewhere # it is important to check if CTRL or SHIFT key is presses while clicking to select lines - if event.button() == Qt.LeftButton: + if event.button() == Qt.MouseButton.LeftButton: modifiers = event.modifiers() # count selected rows - if more than 1 do not close popwin rows = [] @@ -3429,7 +3431,7 @@ def mouseReleaseEvent(self, event): del modifiers, rows return else: - if event.button() == Qt.RightButton or event.button() == Qt.LeftButton: + if event.button() == Qt.MouseButton.RightButton or event.button() == Qt.MouseButton.LeftButton: self.cell_clicked() return @@ -7108,18 +7110,18 @@ def get_screen(x, y): return None -def get_screen_geometry(screen_number): +def get_screen_geometry(screen_name): """ set screen for fullscreen """ #number_of_screens = len(APP.screens()) #for screen in range(number_of_screens + 1): - for screen in APP.screns(): - if screen == screen_number: - return desktop.screenGeometry(screen) + for screen in APP.screens(): + if screen.name == screen_name: + return screen.screenGeometry() # if not enough displays available reset to display 0 - return desktop.screenGeometry(0) + return self.screen().screenGeometry() @Slot() diff --git a/Nagstamon/QUI/qt.py b/Nagstamon/QUI/qt.py index fc1d8b63a..ce5aa34aa 100644 --- a/Nagstamon/QUI/qt.py +++ b/Nagstamon/QUI/qt.py @@ -16,7 +16,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA # Select Qt version based on installation found -# Prefer in this order: Pyside6 - PyQt6 - PyQt5 +# Prefer in this order: PyQt6 - PyQt5 import sys From f0c20c5909a71e5e98d49395e07b3937f2897bb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric?= <github@octvcdrc.fr> Date: Tue, 10 May 2022 12:37:20 +0200 Subject: [PATCH 242/884] Remove line returns from a Host status information Replace line returns with a space from a host status information (services status information was already handled) --- Nagstamon/Servers/Centreon.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/Servers/Centreon.py b/Nagstamon/Servers/Centreon.py index d17d3801b..a15e0e353 100644 --- a/Nagstamon/Servers/Centreon.py +++ b/Nagstamon/Servers/Centreon.py @@ -646,7 +646,7 @@ def _get_status(self): self.new_hosts[str(l.hn.text)].status_type = self.HARD_SOFT[self.new_hosts[str(l.hn.text)].status_type] self.new_hosts[str(l.hn.text)].last_check = str(l.lc.text) self.new_hosts[str(l.hn.text)].duration = str(l.lsc.text) - self.new_hosts[str(l.hn.text)].status_information= str(l.ou.text) + self.new_hosts[str(l.hn.text)].status_information = str(l.ou.text).replace('\n', ' ').strip() if l.find('cih') != None: self.new_hosts[str(l.hn.text)].criticality = str(l.cih.text) else: From a158a38bff4cb583037ad2241c002e622852abf6 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 10 May 2022 23:16:21 +0200 Subject: [PATCH 243/884] floating statusbar works kind of --- Nagstamon/QUI/__init__.py | 43 ++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index c2ea9d6e3..e05cf13e1 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -823,9 +823,9 @@ def mousePressEvent(self, event): if not statuswindow.relative_x and \ not statuswindow.relative_y: globby = event.globalPosition() - event_x, event_y = event.globalPosition() - statuswindow.relative_x = event_x - statuswindow.x() - statuswindow.relative_y = event_y - statuswindow.y() + #event_x, event_y = event.globalPosition() + statuswindow.relative_x = event.globalPosition().x() - statuswindow.x() + statuswindow.relative_y = event.globalPosition().y() - statuswindow.y() def mouseReleaseEvent(self, event): """ @@ -865,11 +865,12 @@ def mouseMoveEvent(self, event): # lock window as moving # if not set calculate relative position if not statuswindow.relative_x and not statuswindow.relative_y: - statuswindow.relative_x = event.globalX() - statuswindow.x() - statuswindow.relative_y = event.globalY() - statuswindow.y() + statuswindow.relative_x = event.globalPosition().x() - statuswindow.x() + statuswindow.relative_y = event.globalPosition().y() - statuswindow.y() statuswindow.moving = True - statuswindow.move(event.globalX() - statuswindow.relative_x, event.globalY() - statuswindow.relative_y) + statuswindow.move(int(event.globalPosition().x() - statuswindow.relative_x), \ + int(event.globalPosition().y() - statuswindow.relative_y)) # needed for OSX - otherwise statusbar stays blank while moving statuswindow.update() @@ -1785,7 +1786,7 @@ def calculate_size(self): # calculate top-ness only if window is closed if conf.statusbar_floating: # if self.is_shown is False: - if self.y() < self.screen().screenGeometry().height() // 2 + available_y: + if self.y() < self.screen().geometry().height() // 2 + available_y: self.top = True else: self.top = False @@ -1794,7 +1795,7 @@ def calculate_size(self): x = self.stored_x elif conf.icon_in_systray or conf.windowed: - if self.icon_y < self.screen().screenGeometry().height() // 2 + available_y: + if self.icon_y < self.screen().geometry().height() // 2 + available_y: self.top = True else: self.top = False @@ -1835,8 +1836,8 @@ def calculate_size(self): else: # when height is to large for current screen cut it if self.y() + self.height() - real_height < available_y: - height = self.screen().screenGeometry().height() - available_y - ( - self.screen().screenGeometry().height() - (self.y() + self.height())) + height = self.screen().geometry().height() - available_y - ( + self.screen().geometry().height() - (self.y() + self.height())) y = available_y else: height = real_height @@ -3121,17 +3122,17 @@ def data(self, index, role): """ overridden method for data delivery for treeview """ - if role == Qt.DisplayRole: + if role == Qt.ItemDataRole.DisplayRole: return self.data_array[index.row()][index.column()] - elif role == Qt.ForegroundRole: + elif role == Qt.ItemDataRole.ForegroundRole: return self.data_array[index.row()][10] - elif role == Qt.BackgroundRole: + elif role == Qt.ItemDataRole.BackgroundRole: # return(self.data_array[index.row()][COLOR_INDEX['background'][index.column()]]) return self.data_array[index.row()][11] - elif role == Qt.FontRole: + elif role == Qt.ItemDataRole.FontRole: if index.column() == 1: return ICONS_FONT elif index.column() == 3: @@ -3139,11 +3140,11 @@ def data(self, index, role): else: return DUMMY_QVARIANT # provide icons via Qt.UserRole - elif role == Qt.UserRole: + elif role == Qt.ItemDataRole.UserRole: # depending on host or service column return host or service icon list return self.data_array[index.row()][7 + index.column()] - elif role == Qt.ToolTipRole: + elif role == Qt.ItemDataRole.ToolTipRole: # only if tooltips are wanted show them, combining host + service + status_info if conf.show_tooltips: return '''<div style=white-space:pre;margin:3px;><b>{0}: {1}</b></div> @@ -3418,9 +3419,9 @@ def mouseReleaseEvent(self, event): for index in self.selectedIndexes(): if index.row() not in rows: rows.append(index.row()) - if modifiers == Qt.ControlModifier or \ - modifiers == Qt.ShiftModifier or \ - modifiers == (Qt.ControlModifier | Qt.ShiftModifier) or \ + if modifiers == Qt.Modifier.CTRL or \ + modifiers == Qt.Modifier.SHIFT or \ + modifiers == (Qt.Modifier.CTRL | Qt.Modifier.SHIFT) or \ len(rows) > 1: super(TreeView, self).mouseReleaseEvent(event) else: @@ -7118,10 +7119,10 @@ def get_screen_geometry(screen_name): #for screen in range(number_of_screens + 1): for screen in APP.screens(): if screen.name == screen_name: - return screen.screenGeometry() + return screen.geometry() # if not enough displays available reset to display 0 - return self.screen().screenGeometry() + return self.screen().geometry() @Slot() From 28f0e2e403577da2deee94afcbdc6c62452b7800 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 10 May 2022 23:33:26 +0200 Subject: [PATCH 244/884] floating statusbar works kind of --- Nagstamon/QUI/__init__.py | 65 +++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 37 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index e05cf13e1..d02462ebb 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -231,15 +231,6 @@ # Flags for statusbar - experiment with Qt.ToolTip for Windows because # statusbar permanently seems to vanish at some users desktops # see https://github.com/HenriWahl/Nagstamon/issues/222 -# WINDOW_FLAGS = Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint | Qt.ToolTip -# ##if OS == 'Windows': -# ## # WINDOW_FLAGS = Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint | Qt.ToolTip -# ## WINDOW_FLAGS = Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint | Qt.Tool -# ## # WINDOW_FLAGS = Qt.FramelessWindowHint | Qt.Tool -# ## # WINDOW_FLAGS = Qt.FramelessWindowHint | Qt.ToolTip -# ## # WINDOW_FLAGS = Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool | Qt.BypassWindowManagerHint -# ##else: -# ## WINDOW_FLAGS = Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint | Qt.Tool WINDOW_FLAGS = Qt.WindowType.WindowStaysOnTopHint | Qt.WindowType.FramelessWindowHint | Qt.WindowType.Tool # icon for dialogs @@ -1297,7 +1288,7 @@ def set_mode(self): self.move(screen_geometry.x(), screen_geometry.y()) # keep window entry in taskbar and thus no Qt.Tool - self.setWindowFlags(Qt.Widget | Qt.FramelessWindowHint) + self.setWindowFlags(Qt.WindowType.Widget | Qt.WindowType.FramelessWindowHint) # show statusbar actively self.setAttribute(Qt.WidgetAttribute.WA_ShowWithoutActivating, False) @@ -3389,9 +3380,9 @@ def mouseReleaseEvent_X(self, event): for index in self.selectedIndexes(): if index.row() not in rows: rows.append(index.row()) - if modifiers == Qt.ControlModifier or \ - modifiers == Qt.ShiftModifier or \ - modifiers == (Qt.ControlModifier | Qt.ShiftModifier) or \ + if modifiers == Qt.Modifier.CTRL or \ + modifiers == Qt.Modifier.SHIFT or \ + modifiers == (Qt.Modifier.CTRL| Qt.Modifier.SHIFT) or \ len(rows) > 1: pass else: @@ -3446,7 +3437,7 @@ def keyPressEvent(self, event): """ Use to handle copy from keyboard """ - if event.matches(QKeySequence.Copy): + if event.matches(QKeySequence.StandardKey.Copy): self.action_clipboard_action_all() return super(TreeView, self).keyPressEvent(event) @@ -3644,9 +3635,9 @@ def action_menu_custom_response(self, action): list_rows.append(index.row()) for lrow in list_rows: - miserable_host = self.model().data(self.model().createIndex(lrow, 0), Qt.DisplayRole) - miserable_service = self.model().data(self.model().createIndex(lrow, 2), Qt.DisplayRole) - miserable_status_info = self.model().data(self.model().createIndex(lrow, 8), Qt.DisplayRole) + miserable_host = self.model().data(self.model().createIndex(lrow, 0), Qt.ItemDataRole.DisplayRole) + miserable_service = self.model().data(self.model().createIndex(lrow, 2), Qt.ItemDataRole.DisplayRole) + miserable_status_info = self.model().data(self.model().createIndex(lrow, 8), Qt.ItemDataRole.DisplayRole) # get data to send to action server = self.server.get_name() @@ -3716,8 +3707,8 @@ def action_monitor(self): indexes = self.selectedIndexes() if len(indexes) > 0: index = indexes[0] - miserable_host = self.model().data(self.model().createIndex(index.row(), 0), Qt.DisplayRole) - miserable_service = self.model().data(self.model().createIndex(index.row(), 2), Qt.DisplayRole) + miserable_host = self.model().data(self.model().createIndex(index.row(), 0), Qt.ItemDataRole.DisplayRole) + miserable_service = self.model().data(self.model().createIndex(index.row(), 2), Qt.ItemDataRole.DisplayRole) # open host/service monitor in browser self.server.open_monitor(miserable_host, miserable_service) @@ -3731,8 +3722,8 @@ def action_recheck(self): list_rows.append(index.row()) for lrow in list_rows: - miserable_host = self.model().data(self.model().createIndex(lrow, 0), Qt.DisplayRole) - miserable_service = self.model().data(self.model().createIndex(lrow, 2), Qt.DisplayRole) + miserable_host = self.model().data(self.model().createIndex(lrow, 0), Qt.ItemDataRole.DisplayRole) + miserable_service = self.model().data(self.model().createIndex(lrow, 2), Qt.ItemDataRole.DisplayRole) # send signal to worker recheck slot self.recheck.emit({'host': miserable_host, @@ -3750,8 +3741,8 @@ def action_acknowledge(self): list_rows.append(index.row()) for lrow in list_rows: - list_host.append(self.model().data(self.model().createIndex(lrow, 0), Qt.DisplayRole)) - list_service.append(self.model().data(self.model().createIndex(lrow, 2), Qt.DisplayRole)) + list_host.append(self.model().data(self.model().createIndex(lrow, 0), Qt.ItemDataRole.DisplayRole)) + list_service.append(self.model().data(self.model().createIndex(lrow, 2), Qt.ItemDataRole.DisplayRole)) # running worker method is left to OK button of dialog dialogs.acknowledge.initialize(server=self.server, @@ -3771,8 +3762,8 @@ def action_downtime(self): list_rows.append(index.row()) for lrow in list_rows: - list_host.append(self.model().data(self.model().createIndex(lrow, 0), Qt.DisplayRole)) - list_service.append(self.model().data(self.model().createIndex(lrow, 2), Qt.DisplayRole)) + list_host.append(self.model().data(self.model().createIndex(lrow, 0), Qt.ItemDataRole.DisplayRole)) + list_service.append(self.model().data(self.model().createIndex(lrow, 2), Qt.ItemDataRole.DisplayRole)) # running worker method is left to OK button of dialog dialogs.downtime.initialize(server=self.server, @@ -3803,9 +3794,9 @@ def action_archive_event(self): list_rows.append(index.row()) for lrow in list_rows: - list_host.append(self.model().data(self.model().createIndex(lrow, 0), Qt.DisplayRole)) - list_service.append(self.model().data(self.model().createIndex(lrow, 2), Qt.DisplayRole)) - list_status.append(self.model().data(self.model().createIndex(lrow, 8), Qt.DisplayRole)) + list_host.append(self.model().data(self.model().createIndex(lrow, 0), Qt.ItemDataRole.DisplayRole)) + list_service.append(self.model().data(self.model().createIndex(lrow, 2), Qt.ItemDataRole.DisplayRole)) + list_status.append(self.model().data(self.model().createIndex(lrow, 8), Qt.ItemDataRole.DisplayRole)) for line_number in range(len(list_host)): host = list_host[line_number] @@ -3837,8 +3828,8 @@ def action_submit(self): # only on 1 row indexes = self.selectedIndexes() index = indexes[0] - miserable_host = self.model().data(self.model().createIndex(index.row(), 0), Qt.DisplayRole) - miserable_service = self.model().data(self.model().createIndex(index.row(), 2), Qt.DisplayRole) + miserable_host = self.model().data(self.model().createIndex(index.row(), 0), Qt.ItemDataRole.DisplayRole) + miserable_service = self.model().data(self.model().createIndex(index.row(), 2), Qt.ItemDataRole.DisplayRole) # running worker method is left to OK button of dialog dialogs.submit.initialize(server=self.server, @@ -3862,7 +3853,7 @@ def action_clipboard_action_host(self): list_rows.append(index.row()) for lrow in list_rows: - list_host.append(self.model().data(self.model().createIndex(lrow, 0), Qt.DisplayRole)) + list_host.append(self.model().data(self.model().createIndex(lrow, 0), Qt.ItemDataRole.DisplayRole)) for line_number in range(len(list_host)): text = text + list_host[line_number] @@ -3887,7 +3878,7 @@ def action_clipboard_action_service(self): list_rows.append(index.row()) for lrow in list_rows: - list_service.append(self.model().data(self.model().createIndex(lrow, 2), Qt.DisplayRole)) + list_service.append(self.model().data(self.model().createIndex(lrow, 2), Qt.ItemDataRole.DisplayRole)) for line_number in range(len(list_service)): text = text + list_service[line_number] @@ -3911,7 +3902,7 @@ def action_clipboard_action_statusinformation(self): list_rows.append(index.row()) for lrow in list_rows: - list_status.append(self.model().data(self.model().createIndex(lrow, 8), Qt.DisplayRole)) + list_status.append(self.model().data(self.model().createIndex(lrow, 8), Qt.ItemDataRole.DisplayRole)) for line_number in range(len(list_status)): text = text + list_status[line_number] @@ -3937,8 +3928,8 @@ def action_clipboard_action_all(self): list_rows.append(index.row()) for lrow in list_rows: - list_host.append(self.model().data(self.model().createIndex(lrow, 0), Qt.DisplayRole)) - list_service.append(self.model().data(self.model().createIndex(lrow, 2), Qt.DisplayRole)) + list_host.append(self.model().data(self.model().createIndex(lrow, 0), Qt.ItemDataRole.DisplayRole)) + list_service.append(self.model().data(self.model().createIndex(lrow, 2), Qt.ItemDataRole.DisplayRole)) for line_number in range(len(list_host)): host = list_host[line_number] @@ -6918,9 +6909,9 @@ def show_message(self, message): message, QMessageBox.Ok, parent, - Qt.Dialog | Qt.MSWindowsFixedSizeDialogHint) + Qt.WindowType.Dialog | Qt.WindowType.MSWindowsFixedSizeDialogHint) messagebox.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose) - messagebox.setWindowModality(Qt.NonModal) + messagebox.setWindowModality(Qt.WindowModality.NonModal) messagebox.exec() class Worker(QObject): From 596033d81de57c55e4d21b7e05cdf700783541b3 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 14 May 2022 00:24:28 +0200 Subject: [PATCH 245/884] fedora 36 --- .github/workflows/build-release-latest.yml | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index d57b191c2..1781443ed 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -89,6 +89,19 @@ jobs: retention-days: 1 if-no-files-found: error + fedora-36: + runs-on: ubuntu-latest + needs: test + steps: + - uses: actions/checkout@v2 + - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + - uses: actions/upload-artifact@v2 + with: + path: build/*.rpm + retention-days: 1 + if-no-files-found: error + macos: runs-on: macos-10.15 needs: test @@ -154,7 +167,7 @@ jobs: repo-fedora: runs-on: ubuntu-latest - needs: [debian, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] + needs: [debian, fedora-33, fedora-34, fedora-35, fedora-36, macos, windows-32, windows-64] steps: # get binaries created by other jobs - uses: actions/download-artifact@v2 @@ -176,7 +189,7 @@ jobs: github-release: runs-on: ubuntu-latest - needs: [debian, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] + needs: [debian, fedora-33, fedora-34, fedora-35, fedora-36, macos, windows-32, windows-64] steps: - uses: actions/download-artifact@v2 - run: cd artifact && md5sum *agstamon* > md5sums.txt From 3014daa63fcf096053a9540be3e593755453a49f Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 14 May 2022 00:29:41 +0200 Subject: [PATCH 246/884] fedora 36 stable --- .github/workflows/build-release-stable.yml | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index 4f4a89e66..e536c22d5 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -84,6 +84,19 @@ jobs: path: build/*.rpm retention-days: 1 + fedora-36: + runs-on: ubuntu-latest + needs: test + steps: + - uses: actions/checkout@v2 + - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + - uses: actions/upload-artifact@v2 + with: + path: build/*.rpm + retention-days: 1 + if-no-files-found: error + macos: runs-on: macos-10.15 needs: test @@ -140,7 +153,7 @@ jobs: repo-fedora: runs-on: ubuntu-latest - needs: [debian, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] + needs: [debian, fedora-33, fedora-34, fedora-35, fedora-36, macos, windows-32, windows-64] steps: # get binaries created by other jobs - uses: actions/download-artifact@v2 @@ -159,7 +172,7 @@ jobs: upload-release: runs-on: ubuntu-latest - needs: [debian, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] + needs: [debian, fedora-33, fedora-34, fedora-35, fedora-36, macos, windows-32, windows-64] steps: - uses: actions/download-artifact@v2 - run: cd artifact && md5sum *agstamon* > md5sums.txt From b4fee8d1336d355861c77cc45695286b70f096f1 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 14 May 2022 01:17:36 +0200 Subject: [PATCH 247/884] FROM fedora:36 --- build/docker/Dockerfile-fedora-36 | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 build/docker/Dockerfile-fedora-36 diff --git a/build/docker/Dockerfile-fedora-36 b/build/docker/Dockerfile-fedora-36 new file mode 100644 index 000000000..353a3205b --- /dev/null +++ b/build/docker/Dockerfile-fedora-36 @@ -0,0 +1,25 @@ +FROM fedora:36 +LABEL maintainer=henri@nagstamon.de + +RUN dnf -y install desktop-file-utils \ + git \ + python3 \ + python3-beautifulsoup4 \ + python3-crypto \ + python3-cryptography \ + python3-dateutil \ + python3-devel \ + python3-keyring \ + python3-lxml \ + python3-psutil \ + python3-qt5 \ + python3-qt5-devel \ + python3-requests \ + python3-requests-kerberos \ + python3-SecretStorage \ + qt5-qtsvg \ + qt5-qtmultimedia \ + rpm-build + +CMD cd /nagstamon/build && \ + /usr/bin/python3 build.py From 24dc09eebd34a58d3fcbef8531aaecd841b52307 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 14 May 2022 11:56:58 +0200 Subject: [PATCH 248/884] FROM fedora:36 --- Nagstamon/resources/nagstamon.1.gz | Bin 788 -> 783 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Nagstamon/resources/nagstamon.1.gz b/Nagstamon/resources/nagstamon.1.gz index d5380cab085d11061caaa1136f9996d6d06ce2db..c5213f14bb224495e70854ae03c1e6b11c5040f0 100644 GIT binary patch literal 783 zcmV+q1MvJGiwFp6=YC=W|88Mtb97;CZ*DFz0HsyiYTG~%eb-kEq3uf}#SWxU6GDPx zY(sp(QYdNUhqW}a7E8NgFTVVJXV;fN;yi?6u&ueCb9QDGr57+_0vfhNSfW714CYWM zS%bpZD4Q9pFi&u6qJTch0LBmfIC&UN#?Xd~>sI?$*!mr|ZZ7B{YSH&a5XCNP)C<OJ zsST^8fHVYp;8Ft_jMYF!Zlr=u$+Hp+TcL)gVi^J#pm*9Rwgd80ZjO3oWaHWk1xlOi znu(B$9EAxhTw?_-=ghI~yfbX1G9#vzTm)(Qtqo}^UMZvYrg^YWamI7Cj{bdg=S<wE zne!kFJ~~_TGsgBg<Mlj<4m^hA$KE&zB08Vw9VciMc7h0IF*tVg?}I9zB785!ncRb- zKii))m)dj=FyVQ8#0j&WkKfn$(1tv+%D*V)r>7n6u?gIKwIUJtN`m!7X`17g@njkg z;~>&aAWl>L$|{3O(9Wb`CPl%Qb07ZLi@USoG#O6DKI3f<xCZv$n*dBhQAuXFSRSe@ zm;xgyqmZhGowP1lA>rdS0$WIHz?69GDGL{)jz|s=X!8Y{jZ~{25xik0W&$&#U4z<E z#iqhT<|RxTz2>;NgkeT#*d=sHKZQ~_*Ue~On5-pct9k_^m#?`YOd^M*RCaaj?#ZB9 zqtX%dwhbzRE)1(c+r~{G5I6YC77M<;guf`H*1pOUU*mxn$;(An*&Or8E1wYOOxCrF z%jlw$jp0(Ts@i#_NmN3jmezHGC0{!bPN#;M(($E~(;375h%McbR09fL;eI-1y?F9G z>-P5Jkru=BdudFgJ6BhyS)p}Fi>VKPcuoeB89^ho1k4gcfB%+rtP4Z|8^w*G-^C8P zOjS-iIv2RjO2#vGFUxvo>^34^cU}P7<o?>G_yG#V;SVb-xGkd%{oeCp6Xpsv4K{(e z^ANg|>C0@`A0!`9&|}3j6q*WJ+6LwI=ue-*>!Ty`>(@WME~sxVGi!L|OZhU611k5< N_y<k;=V4?90071|b|C-& literal 788 zcmV+v1MB=BiwFqrjVECO|88Mtb97;CZ*DFz0Hsx1YvMo@e$THsl-+$<lWL(0E2Xqp zX@z>hq%3XY!<d|y!DJ@PMeD!cGvlR8wGU+x)ST=0eRIwvO3z@(1T<`cut0%|8O)(j zvId2*Q8qJHVV>Zdi30j04=}pDkCWTscmy3dyJ&ZQh3(&A`|^w)qBeb>1ySsxhW%i~ z7TT~{3P?ks2QD>`!B`Ds<VGr3mpm)MuqA3}DwZK|0eYj2VjCbo<>siDMmDa!P@uHA zsF?`4$WfTE!WCA~cFr6-&O60MDl=qi!9|d!-#U<{;*~OLZ<+<$6lXkDYwzDicTdE9 znmG%?;Mm!ooiKJz882o*wBs=t-StOF5YhQWA2>myup2}$jlr>_e@Ci(itwWpCvta+ z{_K3xTx!$Z!Gx#L9w$uuK7L!{s112!m48vpPft7CViUOjYDFUOl?3aF(lp1<qwypj z#6hH+Kpdv}l~o3npq)v>Op1apW<LC`ANQt%NirCZe8&48a1HFgHvyQ2qLR#TvDj6a zGX+LcMj=%V8);p#Lc+&u1U8q}fGKg`Qx+~p?UC#t(B=y?YpIq$B6!D4%mijey9TwT zicN*P%odCrz2dlT!5||vtOY&NPoWgfbvfJ?CToe=vR=Z_<!f#TlgJ?{m0j+;dork2 zsB{GVw+0nK7lu`!ZQ~{oh#S1H`JBJC;01-$+E;nxE8OuSdAZ0cn`0h%<rCtZ$+~uN z8C`U;F<c5(RU5A~iAqS+!n#hd;425h>C`Y&I=+;0IAhozv86kbYCyp&+)l@|AIIaT zX|KPXkF*%1-%Dc}-90})3`@j|bsFXhHBA^TPg+cT^6gXdFrE@n!cH(P@%6WHNryZ~ z6tGs@82X)WpvP3@q@+`VtE^-^V>hy_H^#n2#PZt9VVm4v;uQaYLUH)R$_lQ^XidL2 zgyJ6%*B(M|Jb9iD?jMq46!ci}427nGmUcsVb?zsR;l<t&`DMI6ygaCHE;DO*<xBZ8 Sjsq(92Kfum2JPZ<1^@tv347oG From 21e6a4ac52395bce4644c59eec909874b64e72da Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 14 May 2022 12:08:02 +0200 Subject: [PATCH 249/884] .gitignore --- .gitignore | 19 ++++++++++++++----- Nagstamon/Config.py | 2 +- build/debian/changelog | 2 +- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 4638bd74b..bc74b9248 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,19 @@ -*.pyc -*~ -.idea/ __pycache__/ -*.swp -nagstamon.conf/ .directory +.idea/ .metadata/ .vscode/ +*.deb +*.dmg +*.exe +*.gz +*.pyc +*.rpm +*.swp +*.zip +*~ +build/bdist* dist/ +nagstamon +nagstamon.conf/ +nagstamon.egg-info/ \ No newline at end of file diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 459cc3ace..754d2f524 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20220422' + VERSION = '3.9-20220503' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index b762a17b3..220825891 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.9-20220422) unstable; urgency=low +nagstamon (3.9-20220503) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Wed, 08 Dec 2021 08:00:00 +0100 From 5df7c5debe2da7f8db36952f5da6f05c9ab7ca84 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 14 May 2022 12:08:59 +0200 Subject: [PATCH 250/884] .gitignore --- .gitignore | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 4638bd74b..bc74b9248 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,19 @@ -*.pyc -*~ -.idea/ __pycache__/ -*.swp -nagstamon.conf/ .directory +.idea/ .metadata/ .vscode/ +*.deb +*.dmg +*.exe +*.gz +*.pyc +*.rpm +*.swp +*.zip +*~ +build/bdist* dist/ +nagstamon +nagstamon.conf/ +nagstamon.egg-info/ \ No newline at end of file From 0b7386791103f8a9baf7de861d70024db46b433c Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 14 May 2022 12:14:25 +0200 Subject: [PATCH 251/884] fedora 36 again --- .github/workflows/build-release-latest.yml | 12 ++++++------ .github/workflows/build-release-stable.yml | 12 ++++++------ .../{Dockerfile-fedora-32 => Dockerfile-fedora-36} | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) rename build/docker/{Dockerfile-fedora-32 => Dockerfile-fedora-36} (98%) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 3b8f116b8..330c2664d 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -50,7 +50,7 @@ jobs: retention-days: 1 if-no-files-found: error - fedora-32: + fedora-33: runs-on: ubuntu-latest needs: test steps: @@ -63,7 +63,7 @@ jobs: retention-days: 1 if-no-files-found: error - fedora-33: + fedora-34: runs-on: ubuntu-latest needs: test steps: @@ -76,7 +76,7 @@ jobs: retention-days: 1 if-no-files-found: error - fedora-34: + fedora-35: runs-on: ubuntu-latest needs: test steps: @@ -89,7 +89,7 @@ jobs: retention-days: 1 if-no-files-found: error - fedora-35: + fedora-36: runs-on: ubuntu-latest needs: test steps: @@ -167,7 +167,7 @@ jobs: repo-fedora: runs-on: ubuntu-latest - needs: [debian, fedora-32, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] + needs: [debian, fedora-33, fedora-34, fedora-35, fedora-36, macos, windows-32, windows-64] steps: # get binaries created by other jobs - uses: actions/download-artifact@v2 @@ -189,7 +189,7 @@ jobs: github-release: runs-on: ubuntu-latest - needs: [debian, fedora-32, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] + needs: [debian, fedora-33, fedora-34, fedora-35, fedora-36, macos, windows-32, windows-64] steps: - uses: actions/download-artifact@v2 - run: cd artifact && md5sum *agstamon* > md5sums.txt diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index 842d5318e..c5cd1f47a 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -48,7 +48,7 @@ jobs: path: build/*.deb retention-days: 1 - fedora-32: + fedora-33: runs-on: ubuntu-latest needs: test steps: @@ -60,7 +60,7 @@ jobs: path: build/*.rpm retention-days: 1 - fedora-33: + fedora-34: runs-on: ubuntu-latest needs: test steps: @@ -72,7 +72,7 @@ jobs: path: build/*.rpm retention-days: 1 - fedora-34: + fedora-35: runs-on: ubuntu-latest needs: test steps: @@ -84,7 +84,7 @@ jobs: path: build/*.rpm retention-days: 1 - fedora-35: + fedora-36: runs-on: ubuntu-latest needs: test steps: @@ -152,7 +152,7 @@ jobs: repo-fedora: runs-on: ubuntu-latest - needs: [debian, fedora-32, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] + needs: [debian, fedora-33, fedora-34, fedora-35, fedora-36, macos, windows-32, windows-64] steps: # get binaries created by other jobs - uses: actions/download-artifact@v2 @@ -171,7 +171,7 @@ jobs: upload-release: runs-on: ubuntu-latest - needs: [debian, fedora-32, fedora-33, fedora-34, fedora-35, macos, windows-32, windows-64] + needs: [debian, fedora-33, fedora-34, fedora-35, fedora-36, macos, windows-32, windows-64] steps: - uses: actions/download-artifact@v2 - run: cd artifact && md5sum *agstamon* > md5sums.txt diff --git a/build/docker/Dockerfile-fedora-32 b/build/docker/Dockerfile-fedora-36 similarity index 98% rename from build/docker/Dockerfile-fedora-32 rename to build/docker/Dockerfile-fedora-36 index 9890b4dd2..353a3205b 100644 --- a/build/docker/Dockerfile-fedora-32 +++ b/build/docker/Dockerfile-fedora-36 @@ -1,4 +1,4 @@ -FROM fedora:32 +FROM fedora:36 LABEL maintainer=henri@nagstamon.de RUN dnf -y install desktop-file-utils \ From 0b7b44df2e2088dcff0f253192eb43de6a619b9d Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 14 May 2022 18:46:07 +0200 Subject: [PATCH 252/884] sound works --- Nagstamon/QUI/__init__.py | 70 +++++++++++++++++++++++++++------- build/requirements/macos.txt | 3 +- build/requirements/windows.txt | 2 +- 3 files changed, 59 insertions(+), 16 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index d02462ebb..73816cba3 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -34,8 +34,6 @@ # for details of imports look into qt.py from .qt import * -print(QT_VERSION_MAJOR) - from Nagstamon.Config import (Action, AppInfo, BOOLPOOL, @@ -6785,7 +6783,7 @@ def show(self): self.window.exec() -class MediaPlayer(QObject): +class MediaPlayerQt5(QObject): """ play media files for notification """ @@ -6796,18 +6794,15 @@ def __init__(self): QObject.__init__(self) self.player = QMediaPlayer(parent=self) - ##self.player.setVolume(100) - - # Qt6: https://doc-snapshots.qt.io/qt6-dev/qmediaplayer.html#setSource - - ##self.playlist = QMediaPlaylist() - ##self.player.setPlaylist(self.playlist) + self.player.setVolume(100) + self.playlist = QMediaPlaylist() + self.player.setPlaylist(self.playlist) # let statuswindow show message - ##self.send_message.connect(statuswindow.show_message) + self.send_message.connect(statuswindow.show_message) # connect with statuswindow notification worker - ##statuswindow.worker_notification.load_sound.connect(self.set_media) - ##statuswindow.worker_notification.play_sound.connect(self.play) + statuswindow.worker_notification.load_sound.connect(self.set_media) + statuswindow.worker_notification.play_sound.connect(self.play) @Slot(str) def set_media(self, media_file): @@ -6837,6 +6832,52 @@ def play(self): self.player.play() +class MediaPlayerQt6(QObject): + """ + play media files for notification + """ + # needed to show error in a thread-safe way + send_message = Signal(str, str) + + def __init__(self): + QObject.__init__(self) + self.audio_output = QAudioOutput() + self.audio_output.setVolume(100) + self.player = QMediaPlayer(parent=self) + self.player.setAudioOutput(self.audio_output) + # let statuswindow show message + self.send_message.connect(statuswindow.show_message) + # connect with statuswindow notification worker + statuswindow.worker_notification.load_sound.connect(self.set_media) + statuswindow.worker_notification.play_sound.connect(self.play) + + @Slot(str) + def set_media(self, media_file): + """ + Give media_file to player and if it is one of the default files check first if still exists + :param media_file: + :return: + """ + if media_file in RESOURCE_FILES: + # by using RESOURCE_FILES the file path will be checked on macOS and the file restored if necessary + media_file = RESOURCE_FILES[media_file] + # only existing file can be played + if os.path.exists(media_file): + url = QUrl.fromLocalFile(media_file) + self.player.setSource(url) + del url + return True + else: + # cry and tell no file was found + self.send_message.emit('warning', f'Sound file <b>\'{media_file}\'</b> not found for playback.') + return False + + @Slot() + def play(self): + # just play sound + self.player.play() + + class CheckVersion(QObject): """ checking for updates @@ -7211,4 +7252,7 @@ def is_modifier_pressed(): systrayicon.set_menu(menu) # versatile mediaplayer -mediaplayer = MediaPlayer() +if QT_VERSION_MAJOR < 6: + mediaplayer = MediaPlayerQt5() +else: + mediaplayer = MediaPlayerQt6() diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index a67d3af5e..79cab29c2 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -3,8 +3,7 @@ keyring lxml psutil pyinstaller -# PyQt5 5.15.4 is not working -pyqt5==5.15.3 +pyqt6 pysocks python-dateutil requests diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index b50ea6a21..1ffd4240a 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -6,7 +6,7 @@ lxml psutil pyinstaller pypiwin32 -pyqt5 +pyqt6 pysocks python-dateutil requests From 68d342784a89748b45bb1b5e4a684ba138da941f Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 14 May 2022 18:50:31 +0200 Subject: [PATCH 253/884] 20220514 --- .github/workflows/build-release-latest.yml | 2 +- .github/workflows/build-release-stable.yml | 2 +- Nagstamon/Config.py | 2 +- build/debian/changelog | 2 +- build/docker/Dockerfile-fedora-32 | 25 ---------------------- 5 files changed, 4 insertions(+), 29 deletions(-) delete mode 100644 build/docker/Dockerfile-fedora-32 diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 1781443ed..f72d11bd1 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -5,7 +5,7 @@ on: branches: '**' env: - python_win_version: 3.9.10 + python_win_version: 3.10.4 repo_dir: nagstamon-jekyll/docs/repo jobs: diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index e536c22d5..011e04392 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -4,7 +4,7 @@ on: tags: 'v*' env: - python_win_version: 3.9.4 + python_win_version: 3.10.4 repo_dir: nagstamon-jekyll/docs/repo jobs: diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 754d2f524..140e5ca86 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20220503' + VERSION = '3.9-20220514' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 220825891..447e4e089 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.9-20220503) unstable; urgency=low +nagstamon (3.9-20220514) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Wed, 08 Dec 2021 08:00:00 +0100 diff --git a/build/docker/Dockerfile-fedora-32 b/build/docker/Dockerfile-fedora-32 deleted file mode 100644 index 9890b4dd2..000000000 --- a/build/docker/Dockerfile-fedora-32 +++ /dev/null @@ -1,25 +0,0 @@ -FROM fedora:32 -LABEL maintainer=henri@nagstamon.de - -RUN dnf -y install desktop-file-utils \ - git \ - python3 \ - python3-beautifulsoup4 \ - python3-crypto \ - python3-cryptography \ - python3-dateutil \ - python3-devel \ - python3-keyring \ - python3-lxml \ - python3-psutil \ - python3-qt5 \ - python3-qt5-devel \ - python3-requests \ - python3-requests-kerberos \ - python3-SecretStorage \ - qt5-qtsvg \ - qt5-qtmultimedia \ - rpm-build - -CMD cd /nagstamon/build && \ - /usr/bin/python3 build.py From 4e482bc8fdf299f9ec3f98a92b30c6a9819137b2 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 14 May 2022 19:02:36 +0200 Subject: [PATCH 254/884] message dialog --- .github/workflows/build-release-latest.yml | 2 +- .github/workflows/build-release-stable.yml | 2 +- Nagstamon/QUI/__init__.py | 28 +++++++++++----------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index f72d11bd1..1f5acaf5b 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -5,7 +5,7 @@ on: branches: '**' env: - python_win_version: 3.10.4 + python_win_version: 3.9.12 repo_dir: nagstamon-jekyll/docs/repo jobs: diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index 011e04392..0859b0887 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -4,7 +4,7 @@ on: tags: 'v*' env: - python_win_version: 3.10.4 + python_win_version: 3.9.12 repo_dir: nagstamon-jekyll/docs/repo jobs: diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 73816cba3..08f31551c 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -2047,10 +2047,10 @@ def show_message(self, msg_type, message): """ title = " ".join((AppInfo.NAME, msg_type)) if msg_type == 'warning': - return (QMessageBox.warning(statuswindow, title, message)) + return (QMessageBox.Icon.Warning(statuswindow, title, message)) elif msg_type == 'information': - return (QMessageBox.information(statuswindow, title, message)) + return (QMessageBox.Icon.Information(statuswindow, title, message)) @Slot() def recheck_all(self): @@ -5218,9 +5218,9 @@ def delete_server(self): reply = QMessageBox.question(self.window, 'Nagstamon', 'Do you really want to delete monitor server <b>%s</b>?' % (server.name), - QMessageBox.Yes | QMessageBox.No, QMessageBox.No) + QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, QMessageBox.StandardButton.No) - if reply == QMessageBox.Yes: + if reply == QMessageBox.StandardButton.Yes: # in case server is enabled delete its vbox if server.enabled: for vbox in statuswindow.servers_vbox.children(): @@ -5302,9 +5302,9 @@ def delete_action(self): reply = QMessageBox.question(self.window, 'Nagstamon', 'Do you really want to delete action <b>%s</b>?' % (action.name), - QMessageBox.Yes | QMessageBox.No, QMessageBox.No) + QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, QMessageBox.StandardButton.No) - if reply == QMessageBox.Yes: + if reply == QMessageBox.StandardButton.Yes: # kick action out of config items conf.actions.pop(action.name) @@ -5947,10 +5947,10 @@ def ok(self): (self.mode in ['new', 'copy'] or self.mode == 'edit' and self.server_conf != conf.servers[self.ui.input_lineedit_name.text()]): # cry if duplicate name exists - QMessageBox.critical(self.window, 'Nagstamon', + QMessageBox.Icon.Critical(self.window, 'Nagstamon', 'The monitor server name <b>%s</b> is already used.' % (self.ui.input_lineedit_name.text()), - QMessageBox.Ok) + QMessageBox.StandardButton.Ok) else: # get configuration from UI for widget in self.ui.__dict__: @@ -6207,10 +6207,10 @@ def ok(self): (self.mode in ['new', 'copy'] or self.mode == 'edit' and self.action_conf != conf.actions[self.ui.input_lineedit_name.text()]): # cry if duplicate name exists - QMessageBox.critical(self.window, 'Nagstamon', + QMessageBox.Icon.Critical(self.window, 'Nagstamon', 'The action name <b>%s</b> is already used.' % (self.ui.input_lineedit_name.text()), - QMessageBox.Ok) + QMessageBox.StandardButton.Ok) else: # get configuration from UI for widget in self.ui.__dict__: @@ -6945,10 +6945,10 @@ def show_message(self, message): else: parent = self.parent - messagebox = QMessageBox(QMessageBox.Information, + messagebox = QMessageBox(QMessageBox.Icon.Information, 'Nagstamon version check', message, - QMessageBox.Ok, + QMessageBox.StandardButton.Ok, parent, Qt.WindowType.Dialog | Qt.WindowType.MSWindowsFixedSizeDialogHint) messagebox.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose) @@ -7153,8 +7153,8 @@ def get_screen_geometry(screen_name): if screen.name == screen_name: return screen.geometry() - # if not enough displays available reset to display 0 - return self.screen().geometry() + # if not enough displays available use primary screen + return APP.primaryScreen().geometry() @Slot() From ebccf3089527ec2811364f2de4f13f1d240197fc Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 14 May 2022 19:09:57 +0200 Subject: [PATCH 255/884] remove win32 support due to non-existen pyqt6 on pypi.org --- .github/workflows/build-release-latest.yml | 51 +++++++++++----------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 1f5acaf5b..ea00d5462 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -117,29 +117,30 @@ jobs: retention-days: 1 if-no-files-found: error - windows-32: - # better depend on stable build image - runs-on: windows-2019 - needs: test - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 - with: - python-version: ${{ env.python_win_version }} - architecture: x86 - - run: python -m pip install --no-warn-script-location -r build/requirements/windows.txt - # pretty hacky but no other idea to avoid gssapi being installed which breaks requests-kerberos - - run: python -m pip uninstall -y gssapi - - run: cd ${{ github.workspace }}/build; python build.py - env: - PYTHONPATH: ${{ github.workspace }} - - uses: actions/upload-artifact@v2 - with: - path: | - build/dist/*.zip - build/dist/*.exe - retention-days: 1 - if-no-files-found: error +# no PyQt6 for win32 available on Pypi.org +# windows-32: +# # better depend on stable build image +# runs-on: windows-2019 +# needs: test +# steps: +# - uses: actions/checkout@v2 +# - uses: actions/setup-python@v2 +# with: +# python-version: ${{ env.python_win_version }} +# architecture: x86 +# - run: python -m pip install --no-warn-script-location -r build/requirements/windows.txt +# # pretty hacky but no other idea to avoid gssapi being installed which breaks requests-kerberos +# - run: python -m pip uninstall -y gssapi +# - run: cd ${{ github.workspace }}/build; python build.py +# env: +# PYTHONPATH: ${{ github.workspace }} +# - uses: actions/upload-artifact@v2 +# with: +# path: | +# build/dist/*.zip +# build/dist/*.exe +# retention-days: 1 +# if-no-files-found: error windows-64: # better depend on stable build image @@ -167,7 +168,7 @@ jobs: repo-fedora: runs-on: ubuntu-latest - needs: [debian, fedora-33, fedora-34, fedora-35, fedora-36, macos, windows-32, windows-64] + needs: [debian, fedora-33, fedora-34, fedora-35, fedora-36, macos, windows-64] steps: # get binaries created by other jobs - uses: actions/download-artifact@v2 @@ -189,7 +190,7 @@ jobs: github-release: runs-on: ubuntu-latest - needs: [debian, fedora-33, fedora-34, fedora-35, fedora-36, macos, windows-32, windows-64] + needs: [debian, fedora-33, fedora-34, fedora-35, fedora-36, macos, windows-64] steps: - uses: actions/download-artifact@v2 - run: cd artifact && md5sum *agstamon* > md5sums.txt From defd94040a0f7039cb2edee0a3444001cd93a14b Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 15 May 2022 01:06:34 +0200 Subject: [PATCH 256/884] avoid Windows Kerberos Kfw trouble --- Nagstamon/QUI/__init__.py | 15 +++++++++------ Nagstamon/Servers/Generic.py | 25 ++++++++++++------------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 08f31551c..05545ddc2 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -92,12 +92,15 @@ print('No DBus for desktop notification available.') DBUS_AVAILABLE = False -# check ECP authentication support availability -try: - from requests_ecp import HTTPECPAuth - - ECP_AVAILABLE = True -except ImportError: +# same KfW trouble as in Servers/Generic.py +if OS != OS_WINDOWS: + # check ECP authentication support availability + try: + from requests_ecp import HTTPECPAuth + ECP_AVAILABLE = True + except ImportError: + ECP_AVAILABLE = False +else: ECP_AVAILABLE = False # since Qt6 HighDPI-awareness is default behaviour diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index 942d73d23..533fa1e31 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -52,21 +52,20 @@ OS_DARWIN, RESOURCES) - -# requests_gssapi is newer but not available everywhere -try: - # extra imports needed to get it compiled on macOS - import numbers - import gssapi.raw.cython_converters - from requests_gssapi import HTTPSPNEGOAuth as HTTPSKerberos -except ImportError: +if OS != OS_WINDOWS: + # requests_gssapi is newer but not available everywhere + try: + # extra imports needed to get it compiled on macOS + import numbers + import gssapi.raw.cython_converters + from requests_gssapi import HTTPSPNEGOAuth as HTTPSKerberos + except ImportError: + from requests_kerberos import HTTPKerberosAuth as HTTPSKerberos +else: + # requests_gssapi needs installation of KfW - Kerberos for Windows + # requests_kerberoes doesn't from requests_kerberos import HTTPKerberosAuth as HTTPSKerberos -try: - from requests_ecp import HTTPECPAuth -except: - pass - # disable annoying SubjectAltNameWarning warnings try: from requests.packages.urllib3.exceptions import SubjectAltNameWarning From b449d24f3752d2ea31928afbc66195a348639835 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 15 May 2022 01:10:59 +0200 Subject: [PATCH 257/884] OS_WINDOWS --- Nagstamon/Servers/Generic.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index 533fa1e31..f9050015c 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -50,6 +50,7 @@ debug_queue, OS, OS_DARWIN, + OS_WINDOWS, RESOURCES) if OS != OS_WINDOWS: From 0292f8ef609381a643ac69e1e3d9201034b38c28 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 15 May 2022 01:22:38 +0200 Subject: [PATCH 258/884] show displays for fullscreen --- Nagstamon/QUI/__init__.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 05545ddc2..529084c14 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -4987,8 +4987,8 @@ def initialize(self): self.ui.input_combobox_default_sort_order.setCurrentText(conf.default_sort_order.title()) # fill combobox with screens for fullscreen - for display in APP.screens(): - self.ui.input_combobox_fullscreen_display.addItem(str(display)) + for screen in APP.screens(): + self.ui.input_combobox_fullscreen_display.addItem(str(screen.name())) self.ui.input_combobox_fullscreen_display.setCurrentText(str(conf.fullscreen_display)) # fill servers listwidget with servers @@ -7150,13 +7150,11 @@ def get_screen_geometry(screen_name): """ set screen for fullscreen """ - #number_of_screens = len(APP.screens()) - #for screen in range(number_of_screens + 1): for screen in APP.screens(): - if screen.name == screen_name: + if screen.name() == screen_name: return screen.geometry() - # if not enough displays available use primary screen + # if screen_name didn't match available use primary screen return APP.primaryScreen().geometry() From 0ca638c73f37b964f275c666487794e863a8c355 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 15 May 2022 14:13:24 +0200 Subject: [PATCH 259/884] ui files in resources, 20220515 --- Nagstamon/Config.py | 2 +- Nagstamon/QUI/__init__.py | 1016 ++++++++--------- Nagstamon/resources/nagstamon.1.gz | Bin 788 -> 783 bytes .../files => resources/qui}/dialog_about.ui | 0 .../qui}/dialog_acknowledge.ui | 0 .../qui}/dialog_authentication.ui | 0 .../qui}/dialog_downtime.ui | 0 .../qui}/dialog_server_missing.ui | 0 .../files => resources/qui}/dialog_submit.ui | 0 .../qui}/settings_action.ui | 0 .../files => resources/qui}/settings_main.ui | 0 .../qui}/settings_server.ui | 0 Nagstamon/setup.py | 114 -- build/debian/changelog | 2 +- build/redhat/nagstamon.spec | 5 - setup.py | 8 +- 16 files changed, 513 insertions(+), 634 deletions(-) rename Nagstamon/{QUI/files => resources/qui}/dialog_about.ui (100%) rename Nagstamon/{QUI/files => resources/qui}/dialog_acknowledge.ui (100%) rename Nagstamon/{QUI/files => resources/qui}/dialog_authentication.ui (100%) rename Nagstamon/{QUI/files => resources/qui}/dialog_downtime.ui (100%) rename Nagstamon/{QUI/files => resources/qui}/dialog_server_missing.ui (100%) rename Nagstamon/{QUI/files => resources/qui}/dialog_submit.ui (100%) rename Nagstamon/{QUI/files => resources/qui}/settings_action.ui (100%) rename Nagstamon/{QUI/files => resources/qui}/settings_main.ui (100%) rename Nagstamon/{QUI/files => resources/qui}/settings_server.ui (100%) delete mode 100644 Nagstamon/setup.py diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 140e5ca86..141ad23b9 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20220514' + VERSION = '3.9-20220515' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 529084c14..c64c1ab3e 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -4521,10 +4521,10 @@ def __init__(self): self.downtime.initialize() # open defaults settings on button click - self.downtime.ui.button_change_defaults_downtime.clicked.connect(self.settings.show_defaults) - self.downtime.ui.button_change_defaults_downtime.clicked.connect(self.downtime.window.close) - self.acknowledge.ui.button_change_defaults_acknowledge.clicked.connect(self.settings.show_defaults) - self.acknowledge.ui.button_change_defaults_acknowledge.clicked.connect(self.acknowledge.window.close) + self.downtime.window.button_change_defaults_downtime.clicked.connect(self.settings.show_defaults) + self.downtime.window.button_change_defaults_downtime.clicked.connect(self.downtime.window.close) + self.acknowledge.window.button_change_defaults_acknowledge.clicked.connect(self.settings.show_defaults) + self.acknowledge.window.button_change_defaults_acknowledge.clicked.connect(self.acknowledge.window.close) # downtime dialog for miserable item context menu #self.submit = Dialog_Submit(Ui_dialog_submit) @@ -4541,8 +4541,8 @@ def __init__(self): self.server_missing = Dialog_Server_missing('dialog_server_missing') self.server_missing.initialize() # open server creation dialog - self.server_missing.ui.button_create_server.clicked.connect(self.settings.show_new_server) - self.server_missing.ui.button_enable_server.clicked.connect(self.settings.show) + self.server_missing.window.button_create_server.clicked.connect(self.settings.show_new_server) + self.server_missing.window.button_enable_server.clicked.connect(self.settings.show) # about dialog #self.about = Dialog_About(Ui_dialog_about) @@ -4577,11 +4577,9 @@ class Dialog(QObject): def __init__(self, dialog): QObject.__init__(self) - #self.window = QDialog() - self.ui = uic.loadUi(f'Nagstamon/QUI/files/{dialog}.ui') - self.window = self.ui - #self.ui.setupUi(self.window) + # load UI file from resources + self.window = uic.loadUi(f'{RESOURCES}/qui/{dialog}.ui') # explicitly set window flags to avoid '?' button on Windows self.window.setWindowFlags(Qt.WindowType.WindowCloseButtonHint) @@ -4593,9 +4591,9 @@ def __init__(self, dialog): self.window.setWindowIcon(ICON) # treat dialog content after pressing OK button - if 'button_box' in dir(self.ui): - self.ui.button_box.accepted.connect(self.ok) - self.ui.button_box.rejected.connect(self.cancel) + if 'button_box' in dir(self.window): + self.window.button_box.accepted.connect(self.ok) + self.window.button_box.rejected.connect(self.cancel) # QSignalMapper needed to connect all toggle-needing-checkboxes/radiobuttons to one .toggle()-method which # decides which sender to use as key in self.TOGGLE_DEPS @@ -4648,8 +4646,8 @@ def toggle(self, checkbox): change state of depending widgets, slot for signals from checkboxes in UI """ # Due to older Qt5 in Ubuntu 14.04 signalmapper has to use strings - self.toggle_visibility(self.ui.__dict__[checkbox], - self.TOGGLE_DEPS[self.ui.__dict__[checkbox]]) + self.toggle_visibility(self.window.__dict__[checkbox], + self.TOGGLE_DEPS[self.window.__dict__[checkbox]]) # adjust dialog window size after UI changes self.window.adjustSize() @@ -4712,260 +4710,260 @@ def __init__(self, dialog): # dictionary holds checkbox/radiobutton as key and relevant widgets in list self.TOGGLE_DEPS = { # debug mode - self.ui.input_checkbox_debug_mode: [self.ui.input_checkbox_debug_to_file, - self.ui.input_lineedit_debug_file], + self.window.input_checkbox_debug_mode: [self.window.input_checkbox_debug_to_file, + self.window.input_lineedit_debug_file], # regular expressions for filtering hosts - self.ui.input_checkbox_re_host_enabled: [self.ui.input_lineedit_re_host_pattern, - self.ui.input_checkbox_re_host_reverse], + self.window.input_checkbox_re_host_enabled: [self.window.input_lineedit_re_host_pattern, + self.window.input_checkbox_re_host_reverse], # regular expressions for filtering services - self.ui.input_checkbox_re_service_enabled: [self.ui.input_lineedit_re_service_pattern, - self.ui.input_checkbox_re_service_reverse], + self.window.input_checkbox_re_service_enabled: [self.window.input_lineedit_re_service_pattern, + self.window.input_checkbox_re_service_reverse], # regular expressions for filtering status information - self.ui.input_checkbox_re_status_information_enabled: [self.ui.input_lineedit_re_status_information_pattern, - self.ui.input_checkbox_re_status_information_reverse], + self.window.input_checkbox_re_status_information_enabled: [self.window.input_lineedit_re_status_information_pattern, + self.window.input_checkbox_re_status_information_reverse], # regular expressions for filtering duration - self.ui.input_checkbox_re_duration_enabled: [self.ui.input_lineedit_re_duration_pattern, - self.ui.input_checkbox_re_duration_reverse], + self.window.input_checkbox_re_duration_enabled: [self.window.input_lineedit_re_duration_pattern, + self.window.input_checkbox_re_duration_reverse], # regular expressions for filtering duration - self.ui.input_checkbox_re_attempt_enabled: [self.ui.input_lineedit_re_attempt_pattern, - self.ui.input_checkbox_re_attempt_reverse], + self.window.input_checkbox_re_attempt_enabled: [self.window.input_lineedit_re_attempt_pattern, + self.window.input_checkbox_re_attempt_reverse], # regular expressions for filtering groups - self.ui.input_checkbox_re_groups_enabled: [self.ui.input_lineedit_re_groups_pattern, - self.ui.input_checkbox_re_groups_reverse], + self.window.input_checkbox_re_groups_enabled: [self.window.input_lineedit_re_groups_pattern, + self.window.input_checkbox_re_groups_reverse], # offset for statuswindow when using systray - self.ui.input_radiobutton_icon_in_systray: [self.ui.input_checkbox_systray_offset_use], - self.ui.input_checkbox_systray_offset_use: [self.ui.input_spinbox_systray_offset, - self.ui.label_offset_statuswindow], + self.window.input_radiobutton_icon_in_systray: [self.window.input_checkbox_systray_offset_use], + self.window.input_checkbox_systray_offset_use: [self.window.input_spinbox_systray_offset, + self.window.label_offset_statuswindow], # display to use in fullscreen mode - self.ui.input_radiobutton_fullscreen: [self.ui.label_fullscreen_display, - self.ui.input_combobox_fullscreen_display], + self.window.input_radiobutton_fullscreen: [self.window.label_fullscreen_display, + self.window.input_combobox_fullscreen_display], # notifications in general - self.ui.input_checkbox_notification: [self.ui.notification_groupbox], + self.window.input_checkbox_notification: [self.window.notification_groupbox], # sound at all - self.ui.input_checkbox_notification_sound: [self.ui.notification_sounds_groupbox], + self.window.input_checkbox_notification_sound: [self.window.notification_sounds_groupbox], # custom sounds - self.ui.input_radiobutton_notification_custom_sound: [self.ui.notification_custom_sounds_groupbox], + self.window.input_radiobutton_notification_custom_sound: [self.window.notification_custom_sounds_groupbox], # notification actions - self.ui.input_checkbox_notification_actions: [self.ui.notification_actions_groupbox], + self.window.input_checkbox_notification_actions: [self.window.notification_actions_groupbox], # several notification actions depending on status - self.ui.input_checkbox_notification_action_warning: [ - self.ui.input_lineedit_notification_action_warning_string], - self.ui.input_checkbox_notification_action_critical: [ - self.ui.input_lineedit_notification_action_critical_string], - self.ui.input_checkbox_notification_action_down: [self.ui.input_lineedit_notification_action_down_string], - self.ui.input_checkbox_notification_action_ok: [self.ui.input_lineedit_notification_action_ok_string], + self.window.input_checkbox_notification_action_warning: [ + self.window.input_lineedit_notification_action_warning_string], + self.window.input_checkbox_notification_action_critical: [ + self.window.input_lineedit_notification_action_critical_string], + self.window.input_checkbox_notification_action_down: [self.window.input_lineedit_notification_action_down_string], + self.window.input_checkbox_notification_action_ok: [self.window.input_lineedit_notification_action_ok_string], # single custom notification action - self.ui.input_checkbox_notification_custom_action: [self.ui.notification_custom_action_groupbox], + self.window.input_checkbox_notification_custom_action: [self.window.notification_custom_action_groupbox], # use event separator or not - self.ui.input_checkbox_notification_custom_action_single: [ - self.ui.label_notification_custom_action_separator, - self.ui.input_lineedit_notification_custom_action_separator], + self.window.input_checkbox_notification_custom_action_single: [ + self.window.label_notification_custom_action_separator, + self.window.input_lineedit_notification_custom_action_separator], # customized color alternation - self.ui.input_checkbox_show_grid: [self.ui.input_checkbox_grid_use_custom_intensity], - self.ui.input_checkbox_grid_use_custom_intensity: [self.ui.input_slider_grid_alternation_intensity, - self.ui.label_intensity_information_0, - self.ui.label_intensity_information_1, - self.ui.label_intensity_warning_0, - self.ui.label_intensity_warning_1, - self.ui.label_intensity_average_0, - self.ui.label_intensity_average_1, - self.ui.label_intensity_high_0, - self.ui.label_intensity_high_1, - self.ui.label_intensity_critical_0, - self.ui.label_intensity_critical_1, - self.ui.label_intensity_disaster_0, - self.ui.label_intensity_disaster_1, - self.ui.label_intensity_down_0, - self.ui.label_intensity_down_1, - self.ui.label_intensity_unreachable_0, - self.ui.label_intensity_unreachable_1, - self.ui.label_intensity_unknown_0, - self.ui.label_intensity_unknown_1], - self.ui.input_radiobutton_use_custom_browser: [self.ui.groupbox_custom_browser, - self.ui.input_lineedit_custom_browser, - self.ui.button_choose_browser]} - - self.TOGGLE_DEPS_INVERTED = [self.ui.input_checkbox_notification_custom_action_single] + self.window.input_checkbox_show_grid: [self.window.input_checkbox_grid_use_custom_intensity], + self.window.input_checkbox_grid_use_custom_intensity: [self.window.input_slider_grid_alternation_intensity, + self.window.label_intensity_information_0, + self.window.label_intensity_information_1, + self.window.label_intensity_warning_0, + self.window.label_intensity_warning_1, + self.window.label_intensity_average_0, + self.window.label_intensity_average_1, + self.window.label_intensity_high_0, + self.window.label_intensity_high_1, + self.window.label_intensity_critical_0, + self.window.label_intensity_critical_1, + self.window.label_intensity_disaster_0, + self.window.label_intensity_disaster_1, + self.window.label_intensity_down_0, + self.window.label_intensity_down_1, + self.window.label_intensity_unreachable_0, + self.window.label_intensity_unreachable_1, + self.window.label_intensity_unknown_0, + self.window.label_intensity_unknown_1], + self.window.input_radiobutton_use_custom_browser: [self.window.groupbox_custom_browser, + self.window.input_lineedit_custom_browser, + self.window.button_choose_browser]} + + self.TOGGLE_DEPS_INVERTED = [self.window.input_checkbox_notification_custom_action_single] # set title to current version self.window.setWindowTitle(' '.join((AppInfo.NAME, AppInfo.VERSION))) # connect server buttons to server dialog - self.ui.button_new_server.clicked.connect(self.new_server) - self.ui.button_edit_server.clicked.connect(self.edit_server) - self.ui.button_copy_server.clicked.connect(self.copy_server) - self.ui.button_delete_server.clicked.connect(self.delete_server) + self.window.button_new_server.clicked.connect(self.new_server) + self.window.button_edit_server.clicked.connect(self.edit_server) + self.window.button_copy_server.clicked.connect(self.copy_server) + self.window.button_delete_server.clicked.connect(self.delete_server) # double click on server to edit - self.ui.list_servers.doubleClicked.connect(self.edit_server) + self.window.list_servers.doubleClicked.connect(self.edit_server) # connect check-for-updates button to update check - # self.ui.button_check_for_new_version_now.clicked.connect(check_version.check) - self.ui.button_check_for_new_version_now.clicked.connect(self.button_check_for_new_version_clicked) + # self.window.button_check_for_new_version_now.clicked.connect(check_version.check) + self.window.button_check_for_new_version_now.clicked.connect(self.button_check_for_new_version_clicked) self.check_for_new_version.connect(check_version.check) # avoid offset spinbox if offest is not enabled - self.ui.input_radiobutton_fullscreen.clicked.connect(self.toggle_systray_icon_offset) - self.ui.input_radiobutton_icon_in_systray.clicked.connect(self.toggle_systray_icon_offset) - self.ui.input_radiobutton_statusbar_floating.clicked.connect(self.toggle_systray_icon_offset) + self.window.input_radiobutton_fullscreen.clicked.connect(self.toggle_systray_icon_offset) + self.window.input_radiobutton_icon_in_systray.clicked.connect(self.toggle_systray_icon_offset) + self.window.input_radiobutton_statusbar_floating.clicked.connect(self.toggle_systray_icon_offset) # connect font chooser button to font choosing dialog - self.ui.button_fontchooser.clicked.connect(self.font_chooser) + self.window.button_fontchooser.clicked.connect(self.font_chooser) # connect revert-to-default-font button - self.ui.button_default_font.clicked.connect(self.font_default) + self.window.button_default_font.clicked.connect(self.font_default) # store font as default self.font = FONT # show current font in label_font - self.ui.label_font.setFont(FONT) + self.window.label_font.setFont(FONT) # connect action buttons to action dialog - self.ui.button_new_action.clicked.connect(self.new_action) - self.ui.button_edit_action.clicked.connect(self.edit_action) - self.ui.button_copy_action.clicked.connect(self.copy_action) - self.ui.button_delete_action.clicked.connect(self.delete_action) + self.window.button_new_action.clicked.connect(self.new_action) + self.window.button_edit_action.clicked.connect(self.edit_action) + self.window.button_copy_action.clicked.connect(self.copy_action) + self.window.button_delete_action.clicked.connect(self.delete_action) # double click on action to edit - self.ui.list_actions.doubleClicked.connect(self.edit_action) + self.window.list_actions.doubleClicked.connect(self.edit_action) # connect custom sound file buttons - self.ui.button_choose_warning.clicked.connect(self.choose_sound_file_warning) - self.ui.button_choose_critical.clicked.connect(self.choose_sound_file_critical) - self.ui.button_choose_down.clicked.connect(self.choose_sound_file_down) + self.window.button_choose_warning.clicked.connect(self.choose_sound_file_warning) + self.window.button_choose_critical.clicked.connect(self.choose_sound_file_critical) + self.window.button_choose_down.clicked.connect(self.choose_sound_file_down) # connect custom sound file buttons - self.ui.button_play_warning.clicked.connect(self.play_sound_file_warning) - self.ui.button_play_critical.clicked.connect(self.play_sound_file_critical) - self.ui.button_play_down.clicked.connect(self.play_sound_file_down) + self.window.button_play_warning.clicked.connect(self.play_sound_file_warning) + self.window.button_play_critical.clicked.connect(self.play_sound_file_critical) + self.window.button_play_down.clicked.connect(self.play_sound_file_down) # only show desktop notification on systems that support it if not dbus_connection.connected: - self.ui.input_checkbox_notification_desktop.hide() + self.window.input_checkbox_notification_desktop.hide() # set folder and play symbols to choose and play buttons - self.ui.button_choose_warning.setText('') - self.ui.button_choose_warning.setIcon(self.ui.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_DirIcon)) - self.ui.button_play_warning.setText('') - self.ui.button_play_warning.setIcon(self.ui.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_MediaPlay)) + self.window.button_choose_warning.setText('') + self.window.button_choose_warning.setIcon(self.window.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_DirIcon)) + self.window.button_play_warning.setText('') + self.window.button_play_warning.setIcon(self.window.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_MediaPlay)) - self.ui.button_choose_critical.setText('') - self.ui.button_choose_critical.setIcon(self.ui.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_DirIcon)) - self.ui.button_play_critical.setText('') - self.ui.button_play_critical.setIcon(self.ui.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_MediaPlay)) + self.window.button_choose_critical.setText('') + self.window.button_choose_critical.setIcon(self.window.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_DirIcon)) + self.window.button_play_critical.setText('') + self.window.button_play_critical.setIcon(self.window.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_MediaPlay)) - self.ui.button_choose_down.setText('') - self.ui.button_choose_down.setIcon(self.ui.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_DirIcon)) - self.ui.button_play_down.setText('') - self.ui.button_play_down.setIcon(self.ui.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_MediaPlay)) + self.window.button_choose_down.setText('') + self.window.button_choose_down.setIcon(self.window.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_DirIcon)) + self.window.button_play_down.setText('') + self.window.button_play_down.setIcon(self.window.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_MediaPlay)) # set browser file chooser icon and current custom browser path - self.ui.button_choose_browser.setText('') - self.ui.button_choose_browser.setIcon(self.ui.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_DirIcon)) - self.ui.input_lineedit_custom_browser.setText(conf.custom_browser) + self.window.button_choose_browser.setText('') + self.window.button_choose_browser.setIcon(self.window.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_DirIcon)) + self.window.input_lineedit_custom_browser.setText(conf.custom_browser) # connect choose browser button with file dialog - self.ui.button_choose_browser.clicked.connect(self.choose_browser_executable) + self.window.button_choose_browser.clicked.connect(self.choose_browser_executable) # QSignalMapper needed to connect all color buttons to color dialogs self.signalmapper_colors = QSignalMapper() # connect color buttons with color dialog - for widget in [x for x in self.ui.__dict__ if x.startswith('input_button_color_')]: - button = self.ui.__dict__[widget] + for widget in [x for x in self.window.__dict__ if x.startswith('input_button_color_')]: + button = self.window.__dict__[widget] item = widget.split('input_button_color_')[1] # multiplex slot for open color dialog by signal-mapping self.signalmapper_colors.setMapping(button, item) button.clicked.connect(self.signalmapper_colors.map) # connect reset and defaults buttons - self.ui.button_colors_reset.clicked.connect(self.paint_colors) - self.ui.button_colors_reset.clicked.connect(self.paint_color_alternation) - self.ui.button_colors_reset.clicked.connect(self.change_color_alternation_by_value) - self.ui.button_colors_defaults.clicked.connect(self.colors_defaults) - self.ui.button_colors_defaults.clicked.connect(self.paint_color_alternation) - self.ui.button_colors_defaults.clicked.connect(self.change_color_alternation_by_value) + self.window.button_colors_reset.clicked.connect(self.paint_colors) + self.window.button_colors_reset.clicked.connect(self.paint_color_alternation) + self.window.button_colors_reset.clicked.connect(self.change_color_alternation_by_value) + self.window.button_colors_defaults.clicked.connect(self.colors_defaults) + self.window.button_colors_defaults.clicked.connect(self.paint_color_alternation) + self.window.button_colors_defaults.clicked.connect(self.change_color_alternation_by_value) # paint alternating colors when example is wanted for customized intensity - self.ui.input_checkbox_grid_use_custom_intensity.clicked.connect(self.paint_color_alternation) - self.ui.input_checkbox_grid_use_custom_intensity.clicked.connect(self.change_color_alternation_by_value) - self.ui.input_checkbox_grid_use_custom_intensity.clicked.connect(self.toggle_zabbix_widgets) + self.window.input_checkbox_grid_use_custom_intensity.clicked.connect(self.paint_color_alternation) + self.window.input_checkbox_grid_use_custom_intensity.clicked.connect(self.change_color_alternation_by_value) + self.window.input_checkbox_grid_use_custom_intensity.clicked.connect(self.toggle_zabbix_widgets) # finally map signals with .sender() - [<type>] is important! self.signalmapper_colors.mappedString[str].connect(self.color_chooser) # connect slider to alternating colors - self.ui.input_slider_grid_alternation_intensity.valueChanged.connect(self.change_color_alternation) + self.window.input_slider_grid_alternation_intensity.valueChanged.connect(self.change_color_alternation) # apply toggle-dependencies between checkboxes and certain widgets self.toggle_toggles() # workaround to avoid gigantic settings dialog # list of Zabbix-related widgets, only to be shown if there is a Zabbix monitor server configured - self.ZABBIX_WIDGETS = [self.ui.input_checkbox_filter_all_average_services, - self.ui.input_checkbox_filter_all_disaster_services, - self.ui.input_checkbox_filter_all_high_services, - self.ui.input_checkbox_filter_all_information_services, - self.ui.input_checkbox_notify_if_average, - self.ui.input_checkbox_notify_if_disaster, - self.ui.input_checkbox_notify_if_high, - self.ui.input_checkbox_notify_if_information, - self.ui.input_button_color_average_text, - self.ui.input_button_color_average_background, - self.ui.input_button_color_disaster_text, - self.ui.input_button_color_disaster_background, - self.ui.input_button_color_high_text, - self.ui.input_button_color_high_background, - self.ui.input_button_color_information_text, - self.ui.input_button_color_information_background, - self.ui.label_color_average, - self.ui.label_color_disaster, - self.ui.label_color_high, - self.ui.label_color_information] + self.ZABBIX_WIDGETS = [self.window.input_checkbox_filter_all_average_services, + self.window.input_checkbox_filter_all_disaster_services, + self.window.input_checkbox_filter_all_high_services, + self.window.input_checkbox_filter_all_information_services, + self.window.input_checkbox_notify_if_average, + self.window.input_checkbox_notify_if_disaster, + self.window.input_checkbox_notify_if_high, + self.window.input_checkbox_notify_if_information, + self.window.input_button_color_average_text, + self.window.input_button_color_average_background, + self.window.input_button_color_disaster_text, + self.window.input_button_color_disaster_background, + self.window.input_button_color_high_text, + self.window.input_button_color_high_background, + self.window.input_button_color_information_text, + self.window.input_button_color_information_background, + self.window.label_color_average, + self.window.label_color_disaster, + self.window.label_color_high, + self.window.label_color_information] # Labes for customized color intensity - self.ZABBIX_COLOR_INTENSITY_LABELS = [self.ui.label_intensity_average_0, - self.ui.label_intensity_average_1, - self.ui.label_intensity_disaster_0, - self.ui.label_intensity_disaster_1, - self.ui.label_intensity_high_0, - self.ui.label_intensity_high_1, - self.ui.label_intensity_information_0, - self.ui.label_intensity_information_1] + self.ZABBIX_COLOR_INTENSITY_LABELS = [self.window.label_intensity_average_0, + self.window.label_intensity_average_1, + self.window.label_intensity_disaster_0, + self.window.label_intensity_disaster_1, + self.window.label_intensity_high_0, + self.window.label_intensity_high_1, + self.window.label_intensity_information_0, + self.window.label_intensity_information_1] # the next workaround... - self.OP5MONITOR_WIDGETS = [self.ui.input_checkbox_re_groups_enabled, - self.ui.input_lineedit_re_groups_pattern, - self.ui.input_checkbox_re_groups_reverse] + self.OP5MONITOR_WIDGETS = [self.window.input_checkbox_re_groups_enabled, + self.window.input_lineedit_re_groups_pattern, + self.window.input_checkbox_re_groups_reverse] # ...and another... - self.EXPIRE_TIME_WIDGETS = [self.ui.input_checkbox_defaults_acknowledge_expire, - self.ui.label_expire_in, - self.ui.label_expire_in_hours, - self.ui.label_expire_in_minutes, - self.ui.input_spinbox_defaults_acknowledge_expire_duration_hours, - self.ui.input_spinbox_defaults_acknowledge_expire_duration_minutes] + self.EXPIRE_TIME_WIDGETS = [self.window.input_checkbox_defaults_acknowledge_expire, + self.window.label_expire_in, + self.window.label_expire_in_hours, + self.window.label_expire_in_minutes, + self.window.input_spinbox_defaults_acknowledge_expire_duration_hours, + self.window.input_spinbox_defaults_acknowledge_expire_duration_minutes] def initialize(self): # apply configuration values # start with servers tab - self.ui.tabs.setCurrentIndex(0) - for widget in dir(self.ui): + self.window.tabs.setCurrentIndex(0) + for widget in dir(self.window): if widget.startswith('input_'): if widget.startswith('input_checkbox_'): if conf.__dict__[widget.split('input_checkbox_')[1]] is True: - self.ui.__dict__[widget].toggle() + self.window.__dict__[widget].toggle() elif widget.startswith('input_radiobutton_'): if conf.__dict__[widget.split('input_radiobutton_')[1]] is True: - self.ui.__dict__[widget].toggle() + self.window.__dict__[widget].toggle() elif widget.startswith('input_lineedit_'): # older versions of Nagstamon have a bool value for custom_action_separator # which leads to a crash here - thus str() to solve this - self.ui.__dict__[widget].setText(str(conf.__dict__[widget.split('input_lineedit_')[1]])) + self.window.__dict__[widget].setText(str(conf.__dict__[widget.split('input_lineedit_')[1]])) elif widget.startswith('input_spinbox_'): - self.ui.__dict__[widget].setValue(int(conf.__dict__[widget.split('input_spinbox_')[1]])) + self.window.__dict__[widget].setValue(int(conf.__dict__[widget.split('input_spinbox_')[1]])) elif widget.startswith('input_slider_'): - self.ui.__dict__[widget].setValue(int(conf.__dict__[widget.split('input_slider_')[1]])) + self.window.__dict__[widget].setValue(int(conf.__dict__[widget.split('input_slider_')[1]])) # bruteforce size smallification, lazy try/except variant try: - self.ui.__dict__[widget].adjustSize() + self.window.__dict__[widget].adjustSize() except: pass # fill default order fields combobox with s names @@ -4974,34 +4972,34 @@ def initialize(self): while '' in sort_fields: sort_fields.remove('') - self.ui.input_combobox_default_sort_field.addItems(sort_fields) + self.window.input_combobox_default_sort_field.addItems(sort_fields) # catch exception which will occur when older settings are used which have real header names as values try: - self.ui.input_combobox_default_sort_field.setCurrentText(HEADERS_KEYS_HEADERS[conf.default_sort_field]) + self.window.input_combobox_default_sort_field.setCurrentText(HEADERS_KEYS_HEADERS[conf.default_sort_field]) except Exception: - self.ui.input_combobox_default_sort_field.setCurrentText(conf.default_sort_field) + self.window.input_combobox_default_sort_field.setCurrentText(conf.default_sort_field) # fill default sort order combobox - self.ui.input_combobox_default_sort_order.addItems(['Ascending', 'Descending']) + self.window.input_combobox_default_sort_order.addItems(['Ascending', 'Descending']) # .title() to get upper first letter - self.ui.input_combobox_default_sort_order.setCurrentText(conf.default_sort_order.title()) + self.window.input_combobox_default_sort_order.setCurrentText(conf.default_sort_order.title()) # fill combobox with screens for fullscreen for screen in APP.screens(): - self.ui.input_combobox_fullscreen_display.addItem(str(screen.name())) - self.ui.input_combobox_fullscreen_display.setCurrentText(str(conf.fullscreen_display)) + self.window.input_combobox_fullscreen_display.addItem(str(screen.name())) + self.window.input_combobox_fullscreen_display.setCurrentText(str(conf.fullscreen_display)) # fill servers listwidget with servers - self.fill_list(self.ui.list_servers, conf.servers) + self.fill_list(self.window.list_servers, conf.servers) # select first item - self.ui.list_servers.setCurrentRow(0) + self.window.list_servers.setCurrentRow(0) # fill actions listwidget with actions - self.fill_list(self.ui.list_actions, conf.actions) + self.fill_list(self.window.list_actions, conf.actions) # select first item - self.ui.list_actions.setCurrentRow(0) + self.window.list_actions.setCurrentRow(0) # paint colors onto color selection buttons and alternation example self.paint_colors() @@ -5010,9 +5008,9 @@ def initialize(self): # hide keyring setting if keyring is not available if KEYRING: - self.ui.input_checkbox_use_system_keyring.show() + self.window.input_checkbox_use_system_keyring.show() else: - self.ui.input_checkbox_use_system_keyring.hide() + self.window.input_checkbox_use_system_keyring.hide() # important final size adjustment self.window.adjustSize() @@ -5033,7 +5031,7 @@ def show(self, tab=0): self.show_dialog.emit() # jump to requested tab in settings dialog - self.ui.tabs.setCurrentIndex(tab) + self.window.tabs.setCurrentIndex(tab) # reset window if only needs smaller screen estate self.window.adjustSize() @@ -5045,7 +5043,7 @@ def show_new_server(self): opens settings and new server dialogs - used by dialogs.server_missing """ # emulate button click - self.ui.button_new_server.clicked.emit() + self.window.button_new_server.clicked.emit() @Slot() def show_filters(self): @@ -5080,7 +5078,7 @@ def ok(self): # do all stuff necessary after OK button was clicked # put widget values into conf - for widget in self.ui.__dict__.values(): + for widget in self.window.__dict__.values(): if widget.objectName().startswith('input_checkbox_'): conf.__dict__[widget.objectName().split('input_checkbox_')[1]] = widget.isChecked() elif widget.objectName().startswith('input_radiobutton_'): @@ -5095,7 +5093,7 @@ def ok(self): conf.__dict__[widget.objectName().split('input_combobox_')[1]] = widget.currentText() elif widget.objectName().startswith('input_button_color_'): # get color value from color button stylesheet - color = self.ui.__dict__[widget.objectName()].styleSheet() + color = self.window.__dict__[widget.objectName()].styleSheet() color = color.split(':')[1].strip().split(';')[0] conf.__dict__[widget.objectName().split('input_button_')[1]] = color @@ -5217,7 +5215,7 @@ def delete_server(self): delete server, stop its thread, remove from config and list """ # server to delete from current row in servers list - server = conf.servers[self.ui.list_servers.currentItem().text()] + server = conf.servers[self.window.list_servers.currentItem().text()] reply = QMessageBox.question(self.window, 'Nagstamon', 'Do you really want to delete monitor server <b>%s</b>?' % (server.name), @@ -5240,9 +5238,9 @@ def delete_server(self): # refresh list # row index 0 to x - row = self.ui.list_servers.currentRow() + row = self.window.list_servers.currentRow() # count real number, 1 to x - count = self.ui.list_servers.count() + count = self.window.list_servers.count() # if deleted row was the last line the new current row has to be the new last line, accidently the same as count if row == count - 1: @@ -5253,9 +5251,9 @@ def delete_server(self): row = row + 1 # refresh list and mark new current row - self.refresh_list(list_widget=self.ui.list_servers, + self.refresh_list(list_widget=self.window.list_servers, list_conf=conf.servers, - current=self.ui.list_servers.item(row).text()) + current=self.window.list_servers.item(row).text()) del (row, count) # delete server config file from disk @@ -5301,7 +5299,7 @@ def delete_action(self): delete action remove from config and list """ # action to delete from current row in actions list - action = conf.actions[self.ui.list_actions.currentItem().text()] + action = conf.actions[self.window.list_actions.currentItem().text()] reply = QMessageBox.question(self.window, 'Nagstamon', 'Do you really want to delete action <b>%s</b>?' % (action.name), @@ -5313,9 +5311,9 @@ def delete_action(self): # refresh list # row index 0 to x - row = self.ui.list_actions.currentRow() + row = self.window.list_actions.currentRow() # count real number, 1 to x - count = self.ui.list_actions.count() + count = self.window.list_actions.count() # if deleted row was the last line the new current row has to be the new last line, accidently the same as count if row == count - 1: @@ -5326,8 +5324,8 @@ def delete_action(self): row = row + 1 # refresh list and mark new current row - self.refresh_list(list_widget=self.ui.list_actions, list_conf=conf.actions, - current=self.ui.list_actions.item(row).text()) + self.refresh_list(list_widget=self.window.list_actions, list_conf=conf.actions, + current=self.window.list_actions.item(row).text()) del (row, count) @@ -5344,7 +5342,7 @@ def decoration_function(self): # execute decorated function method(self) # shortcut for widget to fill and revaluate - widget = self.ui.__dict__['input_lineedit_notification_custom_sound_%s' % self.sound_file_type] + widget = self.window.__dict__['input_lineedit_notification_custom_sound_%s' % self.sound_file_type] # use 2 filters, sound files and all files file = dialogs.file_chooser.getOpenFileName(self.window, @@ -5382,7 +5380,7 @@ def decoration_function(self): # execute decorated function method(self) # shortcut for widget to fill and revaluate - widget = self.ui.__dict__['input_lineedit_notification_custom_sound_%s' % self.sound_file_type] + widget = self.window.__dict__['input_lineedit_notification_custom_sound_%s' % self.sound_file_type] # get file path from widget file = widget.text() @@ -5414,15 +5412,15 @@ def paint_colors(self): """ # color buttons for color in [x for x in conf.__dict__ if x.startswith('color_')]: - self.ui.__dict__['input_button_%s' % (color)].setStyleSheet('''background-color: %s; + self.window.__dict__['input_button_%s' % (color)].setStyleSheet('''background-color: %s; border-width: 1px; border-color: black; border-style: solid;''' % conf.__dict__[color]) # example color labels - for label in [x for x in self.ui.__dict__ if x.startswith('label_color_')]: + for label in [x for x in self.window.__dict__ if x.startswith('label_color_')]: status = label.split('label_color_')[1] - self.ui.__dict__[label].setStyleSheet('color: %s; background: %s' % + self.window.__dict__[label].setStyleSheet('color: %s; background: %s' % (conf.__dict__['color_%s_text' % (status)], (conf.__dict__['color_%s_background' % (status)]))) @@ -5435,23 +5433,23 @@ def colors_defaults(self): for default_color in [x for x in conf.__dict__ if x.startswith('default_color_')]: # cut 'default_' off to get color color = default_color.split('default_')[1] - self.ui.__dict__['input_button_%s' % (color)].setStyleSheet('''background-color: %s; + self.window.__dict__['input_button_%s' % (color)].setStyleSheet('''background-color: %s; border-width: 1px; border-color: black; border-style: solid;''' % conf.__dict__[default_color]) # example color labels - for label in [x for x in self.ui.__dict__ if x.startswith('label_color_')]: + for label in [x for x in self.window.__dict__ if x.startswith('label_color_')]: status = label.split('label_color_')[1] # get color values from color button stylesheets - color_text = self.ui.__dict__['input_button_color_' + status + '_text'].styleSheet() + color_text = self.window.__dict__['input_button_color_' + status + '_text'].styleSheet() color_text = color_text.split(':')[1].strip().split(';')[0] - color_background = self.ui.__dict__['input_button_color_' + status + '_background'].styleSheet() + color_background = self.window.__dict__['input_button_color_' + status + '_background'].styleSheet() color_background = color_background.split(':')[1].strip().split(';')[0] # apply color values from stylesheet to label - self.ui.__dict__[label].setStyleSheet('color: %s; background: %s' % + self.window.__dict__[label].setStyleSheet('color: %s; background: %s' % (color_text, color_background)) @Slot(str) @@ -5464,24 +5462,24 @@ def color_chooser(self, item): new_color = QColorDialog.getColor(QColor(color), parent=self.window) # if canceled the color is invalid if new_color.isValid(): - self.ui.__dict__['input_button_color_%s' % (item)].setStyleSheet('''background-color: %s; + self.window.__dict__['input_button_color_%s' % (item)].setStyleSheet('''background-color: %s; border-width: 1px; border-color: black; border-style: solid;''' % new_color.name()) status = item.split('_')[0] # get color value from stylesheet to paint example - text = self.ui.__dict__['input_button_color_%s_text' % (status)].styleSheet() + text = self.window.__dict__['input_button_color_%s_text' % (status)].styleSheet() text = text.split(':')[1].strip().split(';')[0] - background = self.ui.__dict__['input_button_color_%s_background' % (status)].styleSheet() + background = self.window.__dict__['input_button_color_%s_background' % (status)].styleSheet() background = background.split(':')[1].strip().split(';')[0] # set example color - self.ui.__dict__['label_color_%s' % (status)].setStyleSheet('''color: {0}; + self.window.__dict__['label_color_%s' % (status)].setStyleSheet('''color: {0}; background: {1} '''.format(text, background)) # update alternation colors self.paint_color_alternation() - self.change_color_alternation(self.ui.input_slider_grid_alternation_intensity.value()) + self.change_color_alternation(self.window.input_slider_grid_alternation_intensity.value()) def paint_color_alternation(self): """ @@ -5491,17 +5489,17 @@ def paint_color_alternation(self): """ for state in COLORS: # get text color from button CSS - text = self.ui.__dict__['input_button_color_{0}_text' + text = self.window.__dict__['input_button_color_{0}_text' .format(state.lower())] \ .styleSheet() \ .split(';\n')[0].split(': ')[1] # get background color from button CSS - background = self.ui.__dict__['input_button_color_{0}_background' + background = self.window.__dict__['input_button_color_{0}_background' .format(state.lower())] \ .styleSheet() \ .split(';\n')[0].split(': ')[1] # set CSS - self.ui.__dict__['label_intensity_{0}_0'.format(state.lower())] \ + self.window.__dict__['label_intensity_{0}_0'.format(state.lower())] \ .setStyleSheet('''color: {0}; background-color: {1}; padding-top: 3px; @@ -5516,16 +5514,16 @@ def change_color_alternation(self, value): """ for state in COLORS: # only evaluate colors if there is any stylesheet - if len(self.ui.__dict__['input_button_color_{0}_text' + if len(self.window.__dict__['input_button_color_{0}_text' .format(state.lower())] \ .styleSheet()) > 0: # access both labels - label_0 = self.ui.__dict__['label_intensity_{0}_0'.format(state.lower())] - label_1 = self.ui.__dict__['label_intensity_{0}_1'.format(state.lower())] + label_0 = self.window.__dict__['label_intensity_{0}_0'.format(state.lower())] + label_1 = self.window.__dict__['label_intensity_{0}_1'.format(state.lower())] # get text color from text color chooser button - text = self.ui.__dict__['input_button_color_{0}_text' + text = self.window.__dict__['input_button_color_{0}_text' .format(state.lower())] \ .styleSheet() \ .split(';\n')[0].split(': ')[1] @@ -5561,7 +5559,7 @@ def change_color_alternation_by_value(self): """ to be fired up when colors are reset """ - self.change_color_alternation(self.ui.input_slider_grid_alternation_intensity.value()) + self.change_color_alternation(self.window.input_slider_grid_alternation_intensity.value()) @Slot() def font_chooser(self): @@ -5569,14 +5567,14 @@ def font_chooser(self): use font dialog to choose a font """ self.font = QFontDialog.getFont(self.font, parent=self.window)[0] - self.ui.label_font.setFont(self.font) + self.window.label_font.setFont(self.font) @Slot() def font_default(self): """ reset font to default font which was valid when Nagstamon was launched """ - self.ui.label_font.setFont(DEFAULT_FONT) + self.window.label_font.setFont(DEFAULT_FONT) self.font = DEFAULT_FONT @Slot() @@ -5608,7 +5606,7 @@ def choose_browser_executable(self): # only take filename if QFileDialog gave something useful back if file != '': - self.ui.input_lineedit_custom_browser.setText(file) + self.window.input_lineedit_custom_browser.setText(file) @Slot() def toggle_zabbix_widgets(self): @@ -5629,7 +5627,7 @@ def toggle_zabbix_widgets(self): for widget in self.ZABBIX_WIDGETS: widget.hide() # remove custom color intensity labels - if use_zabbix and self.ui.input_checkbox_grid_use_custom_intensity.isChecked(): + if use_zabbix and self.window.input_checkbox_grid_use_custom_intensity.isChecked(): for widget in self.ZABBIX_COLOR_INTENSITY_LABELS: widget.show() else: @@ -5677,16 +5675,16 @@ def toggle_systray_icon_offset(self): """ Only show offset spinbox when offset is enabled """ - if self.ui.input_checkbox_systray_offset_use.isVisible(): - if self.ui.input_checkbox_systray_offset_use.isChecked(): - self.ui.input_spinbox_systray_offset.show() - self.ui.label_offset_statuswindow.show() + if self.window.input_checkbox_systray_offset_use.isVisible(): + if self.window.input_checkbox_systray_offset_use.isChecked(): + self.window.input_spinbox_systray_offset.show() + self.window.label_offset_statuswindow.show() else: - self.ui.input_spinbox_systray_offset.hide() - self.ui.label_offset_statuswindow.hide() + self.window.input_spinbox_systray_offset.hide() + self.window.label_offset_statuswindow.hide() else: - self.ui.input_spinbox_systray_offset.hide() - self.ui.label_offset_statuswindow.hide() + self.window.input_spinbox_systray_offset.hide() + self.window.label_offset_statuswindow.hide() class Dialog_Server(Dialog): @@ -5703,99 +5701,99 @@ def __init__(self, dialog): # which widgets have to be hidden because of irrelevance # dictionary holds checkbox/radiobutton as key and relevant widgets in list self.TOGGLE_DEPS = { - self.ui.input_checkbox_use_autologin: [self.ui.label_autologin_key, - self.ui.input_lineedit_autologin_key], - self.ui.input_checkbox_use_proxy: [self.ui.groupbox_proxy], - - self.ui.input_checkbox_use_proxy_from_os: [self.ui.label_proxy_address, - self.ui.input_lineedit_proxy_address, - self.ui.label_proxy_username, - self.ui.input_lineedit_proxy_username, - self.ui.label_proxy_password, - self.ui.input_lineedit_proxy_password], - self.ui.input_checkbox_show_options: [self.ui.groupbox_options], - self.ui.input_checkbox_custom_cert_use: [self.ui.label_custom_ca_file, - self.ui.input_lineedit_custom_cert_ca_file, - self.ui.button_choose_custom_cert_ca_file]} - - self.TOGGLE_DEPS_INVERTED = [self.ui.input_checkbox_use_proxy_from_os] + self.window.input_checkbox_use_autologin: [self.window.label_autologin_key, + self.window.input_lineedit_autologin_key], + self.window.input_checkbox_use_proxy: [self.window.groupbox_proxy], + + self.window.input_checkbox_use_proxy_from_os: [self.window.label_proxy_address, + self.window.input_lineedit_proxy_address, + self.window.label_proxy_username, + self.window.input_lineedit_proxy_username, + self.window.label_proxy_password, + self.window.input_lineedit_proxy_password], + self.window.input_checkbox_show_options: [self.window.groupbox_options], + self.window.input_checkbox_custom_cert_use: [self.window.label_custom_ca_file, + self.window.input_lineedit_custom_cert_ca_file, + self.window.button_choose_custom_cert_ca_file]} + + self.TOGGLE_DEPS_INVERTED = [self.window.input_checkbox_use_proxy_from_os] # these widgets are shown or hidden depending on server type properties # the servers listed at each widget do need them self.VOLATILE_WIDGETS = { - self.ui.label_monitor_cgi_url: ['Nagios', 'Icinga', 'Thruk', 'Sensu', 'SensuGo'], - self.ui.input_lineedit_monitor_cgi_url: ['Nagios', 'Icinga', 'Thruk', 'Sensu', 'SensuGo'], - self.ui.input_checkbox_use_autologin: ['Centreon', 'monitos4x', 'Thruk'], - self.ui.input_lineedit_autologin_key: ['Centreon', 'monitos4x', 'Thruk'], - self.ui.label_autologin_key: ['Centreon', 'monitos4x', 'Thruk'], - self.ui.input_checkbox_no_cookie_auth: ['IcingaWeb2', 'Sensu'], - self.ui.input_checkbox_use_display_name_host: ['Icinga', 'IcingaWeb2'], - self.ui.input_checkbox_use_display_name_service: ['Icinga', 'IcingaWeb2', 'Thruk'], - self.ui.input_checkbox_use_description_name_service: ['Zabbix'], - self.ui.input_checkbox_force_authuser: ['Checkmk Multisite'], - self.ui.groupbox_checkmk_views: ['Checkmk Multisite'], - self.ui.input_lineedit_host_filter: ['op5Monitor'], - self.ui.input_lineedit_service_filter: ['op5Monitor'], - self.ui.label_service_filter: ['op5Monitor'], - self.ui.label_host_filter: ['op5Monitor'], - self.ui.label_monitor_site: ['Sensu'], - self.ui.input_lineedit_monitor_site: ['Sensu'], - self.ui.label_map_to_hostname: ['Prometheus', 'Alertmanager'], - self.ui.input_lineedit_map_to_hostname: ['Prometheus', 'Alertmanager'], - self.ui.label_map_to_servicename: ['Prometheus', 'Alertmanager'], - self.ui.input_lineedit_map_to_servicename: ['Prometheus', 'Alertmanager'], - self.ui.label_map_to_status_information: ['Prometheus', 'Alertmanager'], - self.ui.input_lineedit_map_to_status_information: ['Prometheus', 'Alertmanager'], - self.ui.label_alertmanager_filter: ['Alertmanager'], - self.ui.input_lineedit_alertmanager_filter: ['Alertmanager'], - self.ui.label_map_to_ok: ['Alertmanager'], - self.ui.input_lineedit_map_to_ok: ['Alertmanager'], - self.ui.label_map_to_unknown: ['Alertmanager'], - self.ui.input_lineedit_map_to_unknown: ['Alertmanager'], - self.ui.label_map_to_warning: ['Alertmanager'], - self.ui.input_lineedit_map_to_warning: ['Alertmanager'], - self.ui.label_map_to_critical: ['Alertmanager'], - self.ui.input_lineedit_map_to_critical: ['Alertmanager'] + self.window.label_monitor_cgi_url: ['Nagios', 'Icinga', 'Thruk', 'Sensu', 'SensuGo'], + self.window.input_lineedit_monitor_cgi_url: ['Nagios', 'Icinga', 'Thruk', 'Sensu', 'SensuGo'], + self.window.input_checkbox_use_autologin: ['Centreon', 'monitos4x', 'Thruk'], + self.window.input_lineedit_autologin_key: ['Centreon', 'monitos4x', 'Thruk'], + self.window.label_autologin_key: ['Centreon', 'monitos4x', 'Thruk'], + self.window.input_checkbox_no_cookie_auth: ['IcingaWeb2', 'Sensu'], + self.window.input_checkbox_use_display_name_host: ['Icinga', 'IcingaWeb2'], + self.window.input_checkbox_use_display_name_service: ['Icinga', 'IcingaWeb2', 'Thruk'], + self.window.input_checkbox_use_description_name_service: ['Zabbix'], + self.window.input_checkbox_force_authuser: ['Checkmk Multisite'], + self.window.groupbox_checkmk_views: ['Checkmk Multisite'], + self.window.input_lineedit_host_filter: ['op5Monitor'], + self.window.input_lineedit_service_filter: ['op5Monitor'], + self.window.label_service_filter: ['op5Monitor'], + self.window.label_host_filter: ['op5Monitor'], + self.window.label_monitor_site: ['Sensu'], + self.window.input_lineedit_monitor_site: ['Sensu'], + self.window.label_map_to_hostname: ['Prometheus', 'Alertmanager'], + self.window.input_lineedit_map_to_hostname: ['Prometheus', 'Alertmanager'], + self.window.label_map_to_servicename: ['Prometheus', 'Alertmanager'], + self.window.input_lineedit_map_to_servicename: ['Prometheus', 'Alertmanager'], + self.window.label_map_to_status_information: ['Prometheus', 'Alertmanager'], + self.window.input_lineedit_map_to_status_information: ['Prometheus', 'Alertmanager'], + self.window.label_alertmanager_filter: ['Alertmanager'], + self.window.input_lineedit_alertmanager_filter: ['Alertmanager'], + self.window.label_map_to_ok: ['Alertmanager'], + self.window.input_lineedit_map_to_ok: ['Alertmanager'], + self.window.label_map_to_unknown: ['Alertmanager'], + self.window.input_lineedit_map_to_unknown: ['Alertmanager'], + self.window.label_map_to_warning: ['Alertmanager'], + self.window.input_lineedit_map_to_warning: ['Alertmanager'], + self.window.label_map_to_critical: ['Alertmanager'], + self.window.input_lineedit_map_to_critical: ['Alertmanager'] } # to be used when selecting authentication method Kerberos self.AUTHENTICATION_WIDGETS = [ - self.ui.label_username, - self.ui.input_lineedit_username, - self.ui.label_password, - self.ui.input_lineedit_password, - self.ui.input_checkbox_save_password] + self.window.label_username, + self.window.input_lineedit_username, + self.window.label_password, + self.window.input_lineedit_password, + self.window.input_checkbox_save_password] self.AUTHENTICATION_ECP_WIDGETS = [ - self.ui.label_idp_ecp_endpoint, - self.ui.input_lineedit_idp_ecp_endpoint] + self.window.label_idp_ecp_endpoint, + self.window.input_lineedit_idp_ecp_endpoint] # fill default order fields combobox with monitor server types - self.ui.input_combobox_type.addItems(sorted(SERVER_TYPES.keys(), key=str.lower)) + self.window.input_combobox_type.addItems(sorted(SERVER_TYPES.keys(), key=str.lower)) # default to Nagios as it is the mostly used monitor server - self.ui.input_combobox_type.setCurrentText('Nagios') + self.window.input_combobox_type.setCurrentText('Nagios') # set folder and play symbols to choose and play buttons - self.ui.button_choose_custom_cert_ca_file.setText('') - self.ui.button_choose_custom_cert_ca_file.setIcon( - self.ui.button_choose_custom_cert_ca_file.style().standardIcon(QStyle.StandardPixmap.SP_DirIcon)) + self.window.button_choose_custom_cert_ca_file.setText('') + self.window.button_choose_custom_cert_ca_file.setIcon( + self.window.button_choose_custom_cert_ca_file.style().standardIcon(QStyle.StandardPixmap.SP_DirIcon)) # connect choose custom cert CA file button with file dialog - self.ui.button_choose_custom_cert_ca_file.clicked.connect(self.choose_custom_cert_ca_file) + self.window.button_choose_custom_cert_ca_file.clicked.connect(self.choose_custom_cert_ca_file) # fill authentication combobox - self.ui.input_combobox_authentication.addItems(['Basic', 'Digest', 'Kerberos']) + self.window.input_combobox_authentication.addItems(['Basic', 'Digest', 'Kerberos']) if ECP_AVAILABLE is True: - self.ui.input_combobox_authentication.addItems(['ECP']) + self.window.input_combobox_authentication.addItems(['ECP']) # detect change of server type which leads to certain options shown or hidden - self.ui.input_combobox_type.activated.connect(self.toggle_type) + self.window.input_combobox_type.activated.connect(self.toggle_type) # when authentication is changed to Kerberos then disable username/password as the are now useless - self.ui.input_combobox_authentication.activated.connect(self.toggle_authentication) + self.window.input_combobox_authentication.activated.connect(self.toggle_authentication) # reset Checkmk views - self.ui.button_checkmk_view_hosts_reset.clicked.connect(self.checkmk_view_hosts_reset) - self.ui.button_checkmk_view_services_reset.clicked.connect(self.checkmk_view_services_reset) + self.window.button_checkmk_view_hosts_reset.clicked.connect(self.checkmk_view_hosts_reset) + self.window.button_checkmk_view_services_reset.clicked.connect(self.checkmk_view_services_reset) # mode needed for evaluate dialog after ok button pressed - defaults to 'new' self.mode = 'new' @@ -5805,7 +5803,7 @@ def toggle_type(self, server_type_index=0): # server_type_index is not needed - we get the server type from .currentText() # check if server type is listed in volatile widgets to decide if it has to be shown or hidden for widget, server_types in self.VOLATILE_WIDGETS.items(): - if self.ui.input_combobox_type.currentText() in server_types: + if self.window.input_combobox_type.currentText() in server_types: widget.show() else: widget.hide() @@ -5815,14 +5813,14 @@ def toggle_authentication(self): """ when authentication is changed to Kerberos then disable username/password as the are now useless """ - if self.ui.input_combobox_authentication.currentText() == 'Kerberos': + if self.window.input_combobox_authentication.currentText() == 'Kerberos': for widget in self.AUTHENTICATION_WIDGETS: widget.hide() else: for widget in self.AUTHENTICATION_WIDGETS: widget.show() - if self.ui.input_combobox_authentication.currentText() == 'ECP': + if self.window.input_combobox_authentication.currentText() == 'ECP': for widget in self.AUTHENTICATION_ECP_WIDGETS: widget.show() else: @@ -5850,26 +5848,26 @@ def decoration_function(self, **kwargs): method(self, **kwargs) # run through all input widgets and and apply defaults from config - for widget in self.ui.__dict__: + for widget in self.window.__dict__: if widget.startswith('input_'): if widget.startswith('input_checkbox_'): setting = widget.split('input_checkbox_')[1] - self.ui.__dict__[widget].setChecked(self.server_conf.__dict__[setting]) + self.window.__dict__[widget].setChecked(self.server_conf.__dict__[setting]) elif widget.startswith('input_radiobutton_'): setting = widget.split('input_radiobutton_')[1] - self.ui.__dict__[widget].setChecked(self.server_conf.__dict__[setting]) + self.window.__dict__[widget].setChecked(self.server_conf.__dict__[setting]) elif widget.startswith('input_combobox_'): setting = widget.split('input_combobox_')[1] - self.ui.__dict__[widget].setCurrentText(self.server_conf.__dict__[setting]) + self.window.__dict__[widget].setCurrentText(self.server_conf.__dict__[setting]) elif widget.startswith('input_lineedit_'): setting = widget.split('input_lineedit_')[1] - self.ui.__dict__[widget].setText(self.server_conf.__dict__[setting]) + self.window.__dict__[widget].setText(self.server_conf.__dict__[setting]) elif widget.startswith('input_spinbox_'): setting = widget.split('input_spinbox_')[1] - self.ui.__dict__[widget].setValue(self.server_conf.__dict__[setting]) + self.window.__dict__[widget].setValue(self.server_conf.__dict__[setting]) # set current authentication type by using capitalized first letter via .title() - self.ui.input_combobox_authentication.setCurrentText(self.server_conf.authentication.title()) + self.window.input_combobox_authentication.setCurrentText(self.server_conf.authentication.title()) # initially hide not needed widgets self.toggle_type() @@ -5883,7 +5881,7 @@ def decoration_function(self, **kwargs): # open extra options if wanted e.g. by button_fix_tls_error if 'show_options' in self.__dict__: if self.show_options: - self.ui.input_checkbox_show_options.setChecked(True) + self.window.input_checkbox_show_options.setChecked(True) # important final size adjustment self.window.adjustSize() @@ -5915,7 +5913,7 @@ def edit(self, server_name=None, show_options=False): self.mode = 'edit' # shorter server conf if server_name is None: - self.server_conf = conf.servers[dialogs.settings.ui.list_servers.currentItem().text()] + self.server_conf = conf.servers[dialogs.settings.window.list_servers.currentItem().text()] else: self.server_conf = conf.servers[server_name] # store monitor name in case it will be changed @@ -5932,7 +5930,7 @@ def copy(self): """ self.mode = 'copy' # shorter server conf - self.server_conf = deepcopy(conf.servers[dialogs.settings.ui.list_servers.currentItem().text()]) + self.server_conf = deepcopy(conf.servers[dialogs.settings.window.list_servers.currentItem().text()]) # set window title before name change to reflect copy self.window.setWindowTitle('Copy %s' % (self.server_conf.name)) # indicate copy of other server @@ -5946,33 +5944,33 @@ def ok(self): global servers # check that no duplicate name exists - if self.ui.input_lineedit_name.text() in conf.servers and \ + if self.window.input_lineedit_name.text() in conf.servers and \ (self.mode in ['new', 'copy'] or - self.mode == 'edit' and self.server_conf != conf.servers[self.ui.input_lineedit_name.text()]): + self.mode == 'edit' and self.server_conf != conf.servers[self.window.input_lineedit_name.text()]): # cry if duplicate name exists QMessageBox.Icon.Critical(self.window, 'Nagstamon', 'The monitor server name <b>%s</b> is already used.' % - (self.ui.input_lineedit_name.text()), + (self.window.input_lineedit_name.text()), QMessageBox.StandardButton.Ok) else: # get configuration from UI - for widget in self.ui.__dict__: + for widget in self.window.__dict__: if widget.startswith('input_'): if widget.startswith('input_checkbox_'): setting = widget.split('input_checkbox_')[1] - self.server_conf.__dict__[setting] = self.ui.__dict__[widget].isChecked() + self.server_conf.__dict__[setting] = self.window.__dict__[widget].isChecked() elif widget.startswith('input_radiobutton_'): setting = widget.split('input_radiobutton_')[1] - self.server_conf.__dict__[setting] = self.ui.__dict__[widget].isChecked() + self.server_conf.__dict__[setting] = self.window.__dict__[widget].isChecked() elif widget.startswith('input_combobox_'): setting = widget.split('input_combobox_')[1] - self.server_conf.__dict__[setting] = self.ui.__dict__[widget].currentText() + self.server_conf.__dict__[setting] = self.window.__dict__[widget].currentText() elif widget.startswith('input_lineedit_'): setting = widget.split('input_lineedit_')[1] - self.server_conf.__dict__[setting] = self.ui.__dict__[widget].text() + self.server_conf.__dict__[setting] = self.window.__dict__[widget].text() elif widget.startswith('input_spinbox_'): setting = widget.split('input_spinbox_')[1] - self.server_conf.__dict__[setting] = self.ui.__dict__[widget].value() + self.server_conf.__dict__[setting] = self.window.__dict__[widget].value() # URLs should not end with / - clean it self.server_conf.monitor_url = self.server_conf.monitor_url.rstrip('/') @@ -6012,7 +6010,7 @@ def ok(self): servers.pop(self.previous_server_conf.name) # some monitor servers do not need cgi-url - reuse self.VOLATILE_WIDGETS to find out which one - if self.server_conf.type not in self.VOLATILE_WIDGETS[self.ui.input_lineedit_monitor_cgi_url]: + if self.server_conf.type not in self.VOLATILE_WIDGETS[self.window.input_lineedit_monitor_cgi_url]: self.server_conf.monitor_cgi_url = self.server_conf.monitor_url # add new server configuration in every case @@ -6034,7 +6032,7 @@ def ok(self): del (servers_freshly_sorted) # refresh list of servers, give call the current server name to highlight it - dialogs.settings.refresh_list(list_widget=dialogs.settings.ui.list_servers, + dialogs.settings.refresh_list(list_widget=dialogs.settings.window.list_servers, list_conf=conf.servers, current=self.server_conf.name) @@ -6063,15 +6061,15 @@ def choose_custom_cert_ca_file(self): # only take filename if QFileDialog gave something useful back if file != '': - self.ui.input_lineedit_custom_cert_ca_file.setText(file) + self.window.input_lineedit_custom_cert_ca_file.setText(file) @Slot() def checkmk_view_hosts_reset(self): - self.ui.input_lineedit_checkmk_view_hosts.setText('nagstamon_hosts') + self.window.input_lineedit_checkmk_view_hosts.setText('nagstamon_hosts') @Slot() def checkmk_view_services_reset(self): - self.ui.input_lineedit_checkmk_view_services.setText('nagstamon_svc') + self.window.input_lineedit_checkmk_view_services.setText('nagstamon_svc') class Dialog_Action(Dialog): @@ -6091,27 +6089,27 @@ def __init__(self, dialog): # which widgets have to be hidden because of irrelevance # dictionary holds checkbox/radiobutton as key and relevant widgets in list self.TOGGLE_DEPS = { - self.ui.input_checkbox_re_host_enabled: [self.ui.input_lineedit_re_host_pattern, - self.ui.input_checkbox_re_host_reverse], - self.ui.input_checkbox_re_service_enabled: [self.ui.input_lineedit_re_service_pattern, - self.ui.input_checkbox_re_service_reverse], - self.ui.input_checkbox_re_status_information_enabled: [self.ui.input_lineedit_re_status_information_pattern, - self.ui.input_checkbox_re_status_information_reverse], - self.ui.input_checkbox_re_duration_enabled: [self.ui.input_lineedit_re_duration_pattern, - self.ui.input_checkbox_re_duration_reverse], - self.ui.input_checkbox_re_attempt_enabled: [self.ui.input_lineedit_re_attempt_pattern, - self.ui.input_checkbox_re_attempt_reverse], - self.ui.input_checkbox_re_groups_enabled: [self.ui.input_lineedit_re_groups_pattern, - self.ui.input_checkbox_re_groups_reverse]} + self.window.input_checkbox_re_host_enabled: [self.window.input_lineedit_re_host_pattern, + self.window.input_checkbox_re_host_reverse], + self.window.input_checkbox_re_service_enabled: [self.window.input_lineedit_re_service_pattern, + self.window.input_checkbox_re_service_reverse], + self.window.input_checkbox_re_status_information_enabled: [self.window.input_lineedit_re_status_information_pattern, + self.window.input_checkbox_re_status_information_reverse], + self.window.input_checkbox_re_duration_enabled: [self.window.input_lineedit_re_duration_pattern, + self.window.input_checkbox_re_duration_reverse], + self.window.input_checkbox_re_attempt_enabled: [self.window.input_lineedit_re_attempt_pattern, + self.window.input_checkbox_re_attempt_reverse], + self.window.input_checkbox_re_groups_enabled: [self.window.input_lineedit_re_groups_pattern, + self.window.input_checkbox_re_groups_reverse]} # fill action types into combobox - self.ui.input_combobox_type.addItems(sorted(self.ACTION_TYPES.values())) + self.window.input_combobox_type.addItems(sorted(self.ACTION_TYPES.values())) # fill default order fields combobox with monitor server types - self.ui.input_combobox_monitor_type.addItem("All monitor servers") - self.ui.input_combobox_monitor_type.addItems(sorted(SERVER_TYPES.keys(), key=str.lower)) + self.window.input_combobox_monitor_type.addItem("All monitor servers") + self.window.input_combobox_monitor_type.addItems(sorted(SERVER_TYPES.keys(), key=str.lower)) # default to Nagios as it is the mostly used monitor server - self.ui.input_combobox_monitor_type.setCurrentIndex(0) + self.window.input_combobox_monitor_type.setCurrentIndex(0) def dialog_decoration(method): """ @@ -6132,24 +6130,24 @@ def decoration_function(self): method(self) # run through all input widgets and and apply defaults from config - for widget in self.ui.__dict__: + for widget in self.window.__dict__: if widget.startswith('input_'): if widget.startswith('input_checkbox_'): setting = widget.split('input_checkbox_')[1] - self.ui.__dict__[widget].setChecked(self.action_conf.__dict__[setting]) + self.window.__dict__[widget].setChecked(self.action_conf.__dict__[setting]) elif widget.startswith('input_radiobutton_'): setting = widget.split('input_radiobutton_')[1] - self.ui.__dict__[widget].setChecked(self.action_conf.__dict__[setting]) + self.window.__dict__[widget].setChecked(self.action_conf.__dict__[setting]) elif widget.startswith('input_lineedit_'): setting = widget.split('input_lineedit_')[1] - self.ui.__dict__[widget].setText(self.action_conf.__dict__[setting]) + self.window.__dict__[widget].setText(self.action_conf.__dict__[setting]) elif widget.startswith('input_textedit_'): setting = widget.split('input_textedit_')[1] - self.ui.__dict__[widget].setText(self.action_conf.__dict__[setting]) + self.window.__dict__[widget].setText(self.action_conf.__dict__[setting]) # set comboboxes - self.ui.input_combobox_type.setCurrentText(self.ACTION_TYPES[self.action_conf.type.lower()]) - self.ui.input_combobox_monitor_type.setCurrentText(self.action_conf.monitor_type) + self.window.input_combobox_type.setCurrentText(self.ACTION_TYPES[self.action_conf.type.lower()]) + self.window.input_combobox_monitor_type.setCurrentText(self.action_conf.monitor_type) # apply toggle-dependencies between checkboxes and certain widgets self.toggle_toggles() @@ -6182,7 +6180,7 @@ def edit(self): """ self.mode = 'edit' # shorter action conf - self.action_conf = conf.actions[dialogs.settings.ui.list_actions.currentItem().text()] + self.action_conf = conf.actions[dialogs.settings.window.list_actions.currentItem().text()] # store action name in case it will be changed self.previous_action_conf = deepcopy(self.action_conf) # set window title @@ -6195,7 +6193,7 @@ def copy(self): """ self.mode = 'copy' # shorter action conf - self.action_conf = deepcopy(conf.actions[dialogs.settings.ui.list_actions.currentItem().text()]) + self.action_conf = deepcopy(conf.actions[dialogs.settings.window.list_actions.currentItem().text()]) # set window title before name change to reflect copy self.window.setWindowTitle('Copy %s' % (self.action_conf.name)) # indicate copy of other action @@ -6206,33 +6204,33 @@ def ok(self): evaluate state of widgets to get new configuration """ # check that no duplicate name exists - if self.ui.input_lineedit_name.text() in conf.actions and \ + if self.window.input_lineedit_name.text() in conf.actions and \ (self.mode in ['new', 'copy'] or - self.mode == 'edit' and self.action_conf != conf.actions[self.ui.input_lineedit_name.text()]): + self.mode == 'edit' and self.action_conf != conf.actions[self.window.input_lineedit_name.text()]): # cry if duplicate name exists QMessageBox.Icon.Critical(self.window, 'Nagstamon', 'The action name <b>%s</b> is already used.' % - (self.ui.input_lineedit_name.text()), + (self.window.input_lineedit_name.text()), QMessageBox.StandardButton.Ok) else: # get configuration from UI - for widget in self.ui.__dict__: + for widget in self.window.__dict__: if widget.startswith('input_'): if widget.startswith('input_checkbox_'): setting = widget.split('input_checkbox_')[1] - self.action_conf.__dict__[setting] = self.ui.__dict__[widget].isChecked() + self.action_conf.__dict__[setting] = self.window.__dict__[widget].isChecked() if widget.startswith('input_radiobutton_'): setting = widget.split('input_radiobutton_')[1] - self.action_conf.__dict__[setting] = self.ui.__dict__[widget].isChecked() + self.action_conf.__dict__[setting] = self.window.__dict__[widget].isChecked() elif widget.startswith('input_combobox_'): setting = widget.split('input_combobox_')[1] - self.action_conf.__dict__[setting] = self.ui.__dict__[widget].currentText() + self.action_conf.__dict__[setting] = self.window.__dict__[widget].currentText() elif widget.startswith('input_lineedit_'): setting = widget.split('input_lineedit_')[1] - self.action_conf.__dict__[setting] = self.ui.__dict__[widget].text() + self.action_conf.__dict__[setting] = self.window.__dict__[widget].text() elif widget.startswith('input_textedit_'): setting = widget.split('input_textedit_')[1] - self.action_conf.__dict__[setting] = self.ui.__dict__[widget].toPlainText() + self.action_conf.__dict__[setting] = self.window.__dict__[widget].toPlainText() # edited action will be deleted and recreated with new configuration if self.mode == 'edit': @@ -6250,7 +6248,7 @@ def ok(self): conf.actions[self.action_conf.name] = self.action_conf # refresh list of actions, give call the current action name to highlight it - dialogs.settings.refresh_list(list_widget=dialogs.settings.ui.list_actions, + dialogs.settings.refresh_list(list_widget=dialogs.settings.window.list_actions, list_conf=conf.actions, current=self.action_conf.name) @@ -6279,7 +6277,7 @@ def __init__(self, dialog): Dialog.__init__(self, dialog) self.TOGGLE_DEPS = { - self.ui.input_checkbox_use_expire_time: [self.ui.input_datetime_expire_time] + self.window.input_checkbox_use_expire_time: [self.window.input_datetime_expire_time] } # still clumsy but better than negating the other server types @@ -6289,12 +6287,12 @@ def __init__(self, dialog): x.TYPE not in PROMETHEUS_OR_ALERTMANAGER] self.VOLATILE_WIDGETS = { - self.ui.input_checkbox_use_expire_time: ['IcingaWeb2'], - self.ui.input_datetime_expire_time: ['IcingaWeb2', 'Alertmanager'], - self.ui.input_checkbox_sticky_acknowledgement: NOT_PROMETHEUS_OR_ALERTMANAGER, - self.ui.input_checkbox_send_notification: NOT_PROMETHEUS_OR_ALERTMANAGER, - self.ui.input_checkbox_persistent_comment: NOT_PROMETHEUS_OR_ALERTMANAGER, - self.ui.input_checkbox_acknowledge_all_services: NOT_PROMETHEUS_OR_ALERTMANAGER + self.window.input_checkbox_use_expire_time: ['IcingaWeb2'], + self.window.input_datetime_expire_time: ['IcingaWeb2', 'Alertmanager'], + self.window.input_checkbox_sticky_acknowledgement: NOT_PROMETHEUS_OR_ALERTMANAGER, + self.window.input_checkbox_send_notification: NOT_PROMETHEUS_OR_ALERTMANAGER, + self.window.input_checkbox_persistent_comment: NOT_PROMETHEUS_OR_ALERTMANAGER, + self.window.input_checkbox_acknowledge_all_services: NOT_PROMETHEUS_OR_ALERTMANAGER } self.FORCE_DATETIME_EXPIRE_TIME = ['Alertmanager'] @@ -6314,29 +6312,29 @@ def initialize(self, server=None, host=[], service=[]): else: str = str + 'Service <b>%s</b> on host <b>%s</b><br>' % (self.service_list[i], self.host_list[i]) - self.ui.input_label_description.setText(str) + self.window.input_label_description.setText(str) # default flags of monitor acknowledgement - self.ui.input_checkbox_sticky_acknowledgement.setChecked(conf.defaults_acknowledge_sticky) - self.ui.input_checkbox_send_notification.setChecked(conf.defaults_acknowledge_send_notification) - self.ui.input_checkbox_persistent_comment.setChecked(conf.defaults_acknowledge_persistent_comment) - self.ui.input_checkbox_use_expire_time.setChecked(conf.defaults_acknowledge_expire) + self.window.input_checkbox_sticky_acknowledgement.setChecked(conf.defaults_acknowledge_sticky) + self.window.input_checkbox_send_notification.setChecked(conf.defaults_acknowledge_send_notification) + self.window.input_checkbox_persistent_comment.setChecked(conf.defaults_acknowledge_persistent_comment) + self.window.input_checkbox_use_expire_time.setChecked(conf.defaults_acknowledge_expire) if len(self.host_list) == 1: - self.ui.input_checkbox_acknowledge_all_services.setChecked(conf.defaults_acknowledge_all_services) - self.ui.input_checkbox_acknowledge_all_services.show() + self.window.input_checkbox_acknowledge_all_services.setChecked(conf.defaults_acknowledge_all_services) + self.window.input_checkbox_acknowledge_all_services.show() else: - self.ui.input_checkbox_acknowledge_all_services.setChecked(False) - self.ui.input_checkbox_acknowledge_all_services.hide() + self.window.input_checkbox_acknowledge_all_services.setChecked(False) + self.window.input_checkbox_acknowledge_all_services.hide() # default author + comment - self.ui.input_lineedit_comment.setText(conf.defaults_acknowledge_comment) - self.ui.input_lineedit_comment.setFocus() + self.window.input_lineedit_comment.setText(conf.defaults_acknowledge_comment) + self.window.input_lineedit_comment.setFocus() # set default and minimum value for expire time qdatetime = QDateTime.currentDateTime() - self.ui.input_datetime_expire_time.setMinimumDateTime(qdatetime) + self.window.input_datetime_expire_time.setMinimumDateTime(qdatetime) # set default expire time from configuration - self.ui.input_datetime_expire_time.setDateTime(qdatetime.addSecs( + self.window.input_datetime_expire_time.setDateTime(qdatetime.addSecs( conf.defaults_acknowledge_expire_duration_hours * 60 * 60 + conf.defaults_acknowledge_expire_duration_minutes * 60 )) @@ -6349,11 +6347,11 @@ def initialize(self, server=None, host=[], service=[]): else: widget.hide() if self.server.TYPE in self.FORCE_DATETIME_EXPIRE_TIME: - self.ui.input_datetime_expire_time.show() + self.window.input_datetime_expire_time.show() # Adjust to current size if items are hidden in menu # Otherwise it will get confused and chop off text - self.ui.options_groupbox.adjustSize() + self.window.options_groupbox.adjustSize() self.window.adjustSize() def ok(self): @@ -6362,7 +6360,7 @@ def ok(self): """ # create a list of all service of selected host to acknowledge them all all_services = list() - acknowledge_all_services = self.ui.input_checkbox_acknowledge_all_services.isChecked() + acknowledge_all_services = self.window.input_checkbox_acknowledge_all_services.isChecked() if acknowledge_all_services is True: for i in self.server.nagitems_filtered["services"].values(): @@ -6370,10 +6368,10 @@ def ok(self): if s.host in self.host_list: all_services.append(s.name) - if self.ui.input_checkbox_use_expire_time.isChecked() or self.server.TYPE in self.FORCE_DATETIME_EXPIRE_TIME: + if self.window.input_checkbox_use_expire_time.isChecked() or self.server.TYPE in self.FORCE_DATETIME_EXPIRE_TIME: # Format used in UI # 2019-11-01T18:17:39 - expire_datetime = self.ui.input_datetime_expire_time.dateTime().toString("yyyy-MM-ddTHH:mm:ss") + expire_datetime = self.window.input_datetime_expire_time.dateTime().toString("yyyy-MM-ddTHH:mm:ss") else: expire_datetime = None @@ -6386,10 +6384,10 @@ def ok(self): 'host': host, 'service': service, 'author': self.server.username, - 'comment': self.ui.input_lineedit_comment.text(), - 'sticky': self.ui.input_checkbox_sticky_acknowledgement.isChecked(), - 'notify': self.ui.input_checkbox_send_notification.isChecked(), - 'persistent': self.ui.input_checkbox_persistent_comment.isChecked(), + 'comment': self.window.input_lineedit_comment.text(), + 'sticky': self.window.input_checkbox_sticky_acknowledgement.isChecked(), + 'notify': self.window.input_checkbox_send_notification.isChecked(), + 'persistent': self.window.input_checkbox_persistent_comment.isChecked(), 'acknowledge_all_services': acknowledge_all_services, 'all_services': all_services, 'expire_time': expire_datetime}) @@ -6428,31 +6426,31 @@ def initialize(self, server=None, host=[], service=[]): else: str = str + 'Service <b>%s</b> on host <b>%s</b><br>' % (self.service_list[i], self.host_list[i]) - self.ui.input_label_description.setText(str) + self.window.input_label_description.setText(str) # default flags of monitor acknowledgement - self.ui.input_spinbox_duration_hours.setValue(int(conf.defaults_downtime_duration_hours)) - self.ui.input_spinbox_duration_minutes.setValue(int(conf.defaults_downtime_duration_minutes)) - self.ui.input_radiobutton_type_fixed.setChecked(conf.defaults_downtime_type_fixed) - self.ui.input_radiobutton_type_flexible.setChecked(conf.defaults_downtime_type_flexible) + self.window.input_spinbox_duration_hours.setValue(int(conf.defaults_downtime_duration_hours)) + self.window.input_spinbox_duration_minutes.setValue(int(conf.defaults_downtime_duration_minutes)) + self.window.input_radiobutton_type_fixed.setChecked(conf.defaults_downtime_type_fixed) + self.window.input_radiobutton_type_flexible.setChecked(conf.defaults_downtime_type_flexible) # hide/show downtime settings according to typw - self.ui.input_radiobutton_type_fixed.clicked.connect(self.set_type_fixed) - self.ui.input_radiobutton_type_flexible.clicked.connect(self.set_type_flexible) + self.window.input_radiobutton_type_fixed.clicked.connect(self.set_type_fixed) + self.window.input_radiobutton_type_flexible.clicked.connect(self.set_type_flexible) # show or hide widgets for time settings - if self.ui.input_radiobutton_type_fixed.isChecked(): + if self.window.input_radiobutton_type_fixed.isChecked(): self.set_type_fixed() else: self.set_type_flexible() # empty times at start, will be filled by set_start_end - self.ui.input_lineedit_start_time.setText('n/a') - self.ui.input_lineedit_end_time.setText('n/a') + self.window.input_lineedit_start_time.setText('n/a') + self.window.input_lineedit_end_time.setText('n/a') # default author + comment - self.ui.input_lineedit_comment.setText(conf.defaults_downtime_comment) - self.ui.input_lineedit_comment.setFocus() + self.window.input_lineedit_comment.setText(conf.defaults_downtime_comment) + self.window.input_lineedit_comment.setFocus() if self.server is not None: # at first initialization server is still None @@ -6463,7 +6461,7 @@ def ok(self): schedule downtime for miserable host/service """ # type of downtime - fixed or flexible - if self.ui.input_radiobutton_type_fixed.isChecked() is True: + if self.window.input_radiobutton_type_fixed.isChecked() is True: fixed = 1 else: fixed = 0 @@ -6476,12 +6474,12 @@ def ok(self): 'host': host, 'service': service, 'author': self.server.username, - 'comment': self.ui.input_lineedit_comment.text(), + 'comment': self.window.input_lineedit_comment.text(), 'fixed': fixed, - 'start_time': self.ui.input_lineedit_start_time.text(), - 'end_time': self.ui.input_lineedit_end_time.text(), - 'hours': int(self.ui.input_spinbox_duration_hours.value()), - 'minutes': int(self.ui.input_spinbox_duration_minutes.value())}) + 'start_time': self.window.input_lineedit_start_time.text(), + 'end_time': self.window.input_lineedit_end_time.text(), + 'hours': int(self.window.input_spinbox_duration_hours.value()), + 'minutes': int(self.window.input_spinbox_duration_minutes.value())}) Slot(str, str) @@ -6489,8 +6487,8 @@ def set_start_end(self, start, end): """ put values sent by worker into start and end fields """ - self.ui.input_lineedit_start_time.setText(start) - self.ui.input_lineedit_end_time.setText(end) + self.window.input_lineedit_start_time.setText(start) + self.window.input_lineedit_end_time.setText(end) Slot() @@ -6498,11 +6496,11 @@ def set_type_fixed(self): """ enable/disable appropriate widgets if type is "Fixed" """ - self.ui.label_duration.hide() - self.ui.label_duration_hours.hide() - self.ui.label_duration_minutes.hide() - self.ui.input_spinbox_duration_hours.hide() - self.ui.input_spinbox_duration_minutes.hide() + self.window.label_duration.hide() + self.window.label_duration_hours.hide() + self.window.label_duration_minutes.hide() + self.window.input_spinbox_duration_hours.hide() + self.window.input_spinbox_duration_minutes.hide() Slot() @@ -6510,11 +6508,11 @@ def set_type_flexible(self): """ enable/disable appropriate widgets if type is "Flexible" """ - self.ui.label_duration.show() - self.ui.label_duration_hours.show() - self.ui.label_duration_minutes.show() - self.ui.input_spinbox_duration_hours.show() - self.ui.input_spinbox_duration_minutes.show() + self.window.label_duration.show() + self.window.label_duration_hours.show() + self.window.label_duration_minutes.show() + self.window.input_spinbox_duration_hours.show() + self.window.input_spinbox_duration_minutes.show() class Dialog_Submit(Dialog): @@ -6540,37 +6538,37 @@ def initialize(self, server=None, host='', service=''): if service == "": # set label for acknowledging a host self.window.setWindowTitle('Submit check result for host') - self.ui.input_label_description.setText('Host <b>%s</b>' % (host)) + self.window.input_label_description.setText('Host <b>%s</b>' % (host)) # services do not need all states - self.ui.input_radiobutton_result_up.show() - self.ui.input_radiobutton_result_ok.hide() - self.ui.input_radiobutton_result_warning.hide() - self.ui.input_radiobutton_result_critical.hide() - self.ui.input_radiobutton_result_unknown.show() - self.ui.input_radiobutton_result_unreachable.show() - self.ui.input_radiobutton_result_down.show() + self.window.input_radiobutton_result_up.show() + self.window.input_radiobutton_result_ok.hide() + self.window.input_radiobutton_result_warning.hide() + self.window.input_radiobutton_result_critical.hide() + self.window.input_radiobutton_result_unknown.show() + self.window.input_radiobutton_result_unreachable.show() + self.window.input_radiobutton_result_down.show() # activate first radiobutton - self.ui.input_radiobutton_result_up.setChecked(True) + self.window.input_radiobutton_result_up.setChecked(True) else: # set label for acknowledging a service on host self.window.setWindowTitle('Submit check result for service') - self.ui.input_label_description.setText('Service <b>%s</b> on host <b>%s</b>' % (service, host)) + self.window.input_label_description.setText('Service <b>%s</b> on host <b>%s</b>' % (service, host)) # hosts do not need all states - self.ui.input_radiobutton_result_up.hide() - self.ui.input_radiobutton_result_ok.show() - self.ui.input_radiobutton_result_warning.show() - self.ui.input_radiobutton_result_critical.show() - self.ui.input_radiobutton_result_unknown.show() - self.ui.input_radiobutton_result_unreachable.hide() - self.ui.input_radiobutton_result_down.hide() + self.window.input_radiobutton_result_up.hide() + self.window.input_radiobutton_result_ok.show() + self.window.input_radiobutton_result_warning.show() + self.window.input_radiobutton_result_critical.show() + self.window.input_radiobutton_result_unknown.show() + self.window.input_radiobutton_result_unreachable.hide() + self.window.input_radiobutton_result_down.hide() # activate first radiobutton - self.ui.input_radiobutton_result_ok.setChecked(True) + self.window.input_radiobutton_result_ok.setChecked(True) # clear text fields - self.ui.input_lineedit_check_output.setText('') - self.ui.input_lineedit_performance_data.setText('') - self.ui.input_lineedit_comment.setText(conf.defaults_submit_check_result_comment) - self.ui.input_lineedit_check_output.setFocus() + self.window.input_lineedit_check_output.setText('') + self.window.input_lineedit_performance_data.setText('') + self.window.input_lineedit_comment.setText(conf.defaults_submit_check_result_comment) + self.window.input_lineedit_check_output.setFocus() def ok(self): """ @@ -6580,7 +6578,7 @@ def ok(self): state = "ok" for button in ["ok", "up", "warning", "critical", "unreachable", "unknown", "down"]: - if self.ui.__dict__['input_radiobutton_result_' + button].isChecked(): + if self.window.__dict__['input_radiobutton_result_' + button].isChecked(): state = button break @@ -6589,9 +6587,9 @@ def ok(self): 'host': self.host, 'service': self.service, 'state': state, - 'comment': self.ui.input_lineedit_comment.text(), - 'check_output': self.ui.input_lineedit_check_output.text(), - 'performance_data': self.ui.input_lineedit_performance_data.text()}) + 'comment': self.window.input_lineedit_comment.text(), + 'check_output': self.window.input_lineedit_check_output.text(), + 'performance_data': self.window.input_lineedit_performance_data.text()}) class Dialog_Authentication(Dialog): @@ -6615,25 +6613,25 @@ def initialize(self): self.window.setWindowTitle('Authenticate {0}'.format(self.server.name)) if self.server.type in ['Centreon', 'Thruk']: - self.ui.input_checkbox_use_autologin.show() - self.ui.input_lineedit_autologin_key.show() - self.ui.input_lineedit_autologin_key.show() - self.ui.label_autologin_key.show() + self.window.input_checkbox_use_autologin.show() + self.window.input_lineedit_autologin_key.show() + self.window.input_lineedit_autologin_key.show() + self.window.label_autologin_key.show() # enable switching autologin key and password - self.ui.input_checkbox_use_autologin.clicked.connect(self.toggle_autologin) - self.ui.input_checkbox_use_autologin.setChecked(self.server.use_autologin) - self.ui.input_lineedit_autologin_key.setText(self.server.autologin_key) + self.window.input_checkbox_use_autologin.clicked.connect(self.toggle_autologin) + self.window.input_checkbox_use_autologin.setChecked(self.server.use_autologin) + self.window.input_lineedit_autologin_key.setText(self.server.autologin_key) # initialize autologin self.toggle_autologin() else: - self.ui.input_checkbox_use_autologin.hide() - self.ui.input_lineedit_autologin_key.hide() - self.ui.label_autologin_key.hide() + self.window.input_checkbox_use_autologin.hide() + self.window.input_lineedit_autologin_key.hide() + self.window.label_autologin_key.hide() # set existing values - self.ui.input_lineedit_username.setText(self.server.username) - self.ui.input_lineedit_password.setText(self.server.password) - self.ui.input_checkbox_save_password.setChecked(conf.servers[self.server.name].save_password) + self.window.input_lineedit_username.setText(self.server.username) + self.window.input_lineedit_password.setText(self.server.password) + self.window.input_checkbox_save_password.setChecked(conf.servers[self.server.name].save_password) @Slot(str) def show_auth_dialog(self, server): @@ -6656,23 +6654,23 @@ def ok(self): # close window fist to avoid lagging UI self.window.close() - self.server.username = self.ui.input_lineedit_username.text() - self.server.password = self.ui.input_lineedit_password.text() + self.server.username = self.window.input_lineedit_username.text() + self.server.password = self.window.input_lineedit_password.text() self.server.refresh_authentication = False # store password if it should be saved - if self.ui.input_checkbox_save_password.isChecked(): + if self.window.input_checkbox_save_password.isChecked(): conf.servers[self.server.name].username = self.server.username conf.servers[self.server.name].password = self.server.password - conf.servers[self.server.name].save_password = self.ui.input_checkbox_save_password.isChecked() + conf.servers[self.server.name].save_password = self.window.input_checkbox_save_password.isChecked() # store server settings conf.SaveMultipleConfig('servers', 'server') # Centreon if self.server.type in ['Centreon', 'Thruk']: - if self.ui.input_checkbox_use_autologin: - conf.servers[self.server.name].use_autologin = self.ui.input_checkbox_use_autologin.isChecked() - conf.servers[self.server.name].autologin_key = self.ui.input_lineedit_autologin_key.text() + if self.window.input_checkbox_use_autologin: + conf.servers[self.server.name].use_autologin = self.window.input_checkbox_use_autologin.isChecked() + conf.servers[self.server.name].autologin_key = self.window.input_lineedit_autologin_key.text() # store server settings conf.SaveMultipleConfig('servers', 'server') @@ -6690,24 +6688,24 @@ def toggle_autologin(self): """ toolge autologin option for Centreon """ - if self.ui.input_checkbox_use_autologin.isChecked(): - self.ui.label_username.hide() - self.ui.label_password.hide() - self.ui.input_lineedit_username.hide() - self.ui.input_lineedit_password.hide() - self.ui.input_checkbox_save_password.hide() + if self.window.input_checkbox_use_autologin.isChecked(): + self.window.label_username.hide() + self.window.label_password.hide() + self.window.input_lineedit_username.hide() + self.window.input_lineedit_password.hide() + self.window.input_checkbox_save_password.hide() - self.ui.label_autologin_key.show() - self.ui.input_lineedit_autologin_key.show() + self.window.label_autologin_key.show() + self.window.input_lineedit_autologin_key.show() else: - self.ui.label_username.show() - self.ui.label_password.show() - self.ui.input_lineedit_username.show() - self.ui.input_lineedit_password.show() - self.ui.input_checkbox_save_password.show() + self.window.label_username.show() + self.window.label_password.show() + self.window.input_lineedit_username.show() + self.window.input_lineedit_password.show() + self.window.input_checkbox_save_password.show() - self.ui.label_autologin_key.hide() - self.ui.input_lineedit_autologin_key.hide() + self.window.label_autologin_key.hide() + self.window.input_lineedit_autologin_key.hide() # adjust dialog window size after UI changes self.window.adjustSize() @@ -6722,28 +6720,28 @@ def __init__(self, dialog): Dialog.__init__(self, dialog) # hide dialog when server is to be created or enabled - self.ui.button_create_server.clicked.connect(self.window.hide) - self.ui.button_enable_server.clicked.connect(self.window.hide) + self.window.button_create_server.clicked.connect(self.window.hide) + self.window.button_enable_server.clicked.connect(self.window.hide) # simply hide window if ignore button chosen - self.ui.button_ignore.clicked.connect(self.window.hide) + self.window.button_ignore.clicked.connect(self.window.hide) # byebye if exit button was pressed - self.ui.button_exit.clicked.connect(self.window.hide) - self.ui.button_exit.clicked.connect(exit) + self.window.button_exit.clicked.connect(self.window.hide) + self.window.button_exit.clicked.connect(exit) def initialize(self, mode='no_server'): """ use dialog for missing and not enabled servers, depending on mode """ if mode == 'no_server': - self.ui.label_no_server_configured.show() - self.ui.label_no_server_enabled.hide() - self.ui.button_enable_server.hide() - self.ui.button_create_server.show() + self.window.label_no_server_configured.show() + self.window.label_no_server_enabled.hide() + self.window.button_enable_server.hide() + self.window.button_create_server.show() else: - self.ui.label_no_server_configured.hide() - self.ui.label_no_server_enabled.show() - self.ui.button_enable_server.show() - self.ui.button_create_server.hide() + self.window.label_no_server_configured.hide() + self.window.label_no_server_enabled.show() + self.window.button_enable_server.show() + self.window.button_create_server.hide() class Dialog_About(Dialog): @@ -6756,31 +6754,31 @@ def __init__(self, dialog): # first add the logo on top - no idea how to achive in Qt Designer logo = QSvgWidget('{0}{1}nagstamon.svg'.format(RESOURCES, os.sep)) logo.setFixedSize(100, 100) - self.ui.vbox_about.insertWidget(1, logo, 0, Qt.AlignmentFlag.AlignHCenter) + self.window.vbox_about.insertWidget(1, logo, 0, Qt.AlignmentFlag.AlignHCenter) # update version information - self.ui.label_nagstamon.setText('<h1>{0} {1}</h1>'.format(AppInfo.NAME, AppInfo.VERSION)) - self.ui.label_nagstamon_long.setText('<h2>Nagios¹ status monitor for your desktop</2>') - self.ui.label_copyright.setText(AppInfo.COPYRIGHT) - self.ui.label_website.setText('<a href={0}>{0}</a>'.format(AppInfo.WEBSITE)) - self.ui.label_website.setOpenExternalLinks(True) - self.ui.label_footnote.setText('<small>¹ plus Checkmk, Op5, Icinga, Centreon and more</small>') + self.window.label_nagstamon.setText('<h1>{0} {1}</h1>'.format(AppInfo.NAME, AppInfo.VERSION)) + self.window.label_nagstamon_long.setText('<h2>Nagios¹ status monitor for your desktop</2>') + self.window.label_copyright.setText(AppInfo.COPYRIGHT) + self.window.label_website.setText('<a href={0}>{0}</a>'.format(AppInfo.WEBSITE)) + self.window.label_website.setOpenExternalLinks(True) + self.window.label_footnote.setText('<small>¹ plus Checkmk, Op5, Icinga, Centreon and more</small>') # fill in license information license_file = open('{0}{1}LICENSE'.format(RESOURCES, os.sep)) license = license_file.read() license_file.close() - self.ui.textedit_license.setPlainText(license) - self.ui.textedit_license.setReadOnly(True) + self.window.textedit_license.setPlainText(license) + self.window.textedit_license.setReadOnly(True) # fill in credits information credits_file = open('{0}{1}CREDITS'.format(RESOURCES, os.sep), encoding='utf-8') credits = credits_file.read() credits_file.close() - self.ui.textedit_credits.setText(credits) - self.ui.textedit_credits.setOpenExternalLinks(True) - self.ui.textedit_credits.setReadOnly(True) + self.window.textedit_credits.setText(credits) + self.window.textedit_credits.setOpenExternalLinks(True) + self.window.textedit_credits.setReadOnly(True) - self.ui.tabs.setCurrentIndex(0) + self.window.tabs.setCurrentIndex(0) def show(self): self.window.exec() diff --git a/Nagstamon/resources/nagstamon.1.gz b/Nagstamon/resources/nagstamon.1.gz index d5380cab085d11061caaa1136f9996d6d06ce2db..c720f2fa9bc07c1a10b4b6b043b8d88f7a734d57 100644 GIT binary patch literal 783 zcmV+q1MvJGiwFqe?SNte|88Mtb97;CZ*DFz0HsyiYTG~%eb-kEq3uf}#SWxU6GDPx zY(sp(QYdNUhqW}a7E8NgFTVVJXV;fN;yi?6u&ueCb9QDGr57+_0vfhNSfW714CYWM zS%bpZD4Q9pFi&u6qJTch0LBmfIC&UN#?Xd~>sI?$*!mr|ZZ7B{YSH&a5XCNP)C<OJ zsST^8fHVYp;8Ft_jMYF!Zlr=u$+Hp+TcL)gVi^J#pm*9Rwgd80ZjO3oWaHWk1xlOi znu(B$9EAxhTw?_-=ghI~yfbX1G9#vzTm)(Qtqo}^UMZvYrg^YWamI7Cj{bdg=S<wE zne!kFJ~~_TGsgBg<Mlj<4m^hA$KE&zB08Vw9VciMc7h0IF*tVg?}I9zB785!ncRb- zKii))m)dj=FyVQ8#0j&WkKfn$(1tv+%D*V)r>7n6u?gIKwIUJtN`m!7X`17g@njkg z;~>&aAWl>L$|{3O(9Wb`CPl%Qb07ZLi@USoG#O6DKI3f<xCZv$n*dBhQAuXFSRSe@ zm;xgyqmZhGowP1lA>rdS0$WIHz?69GDGL{)jz|s=X!8Y{jZ~{25xik0W&$&#U4z<E z#iqhT<|RxTz2>;NgkeT#*d=sHKZQ~_*Ue~On5-pct9k_^m#?`YOd^M*RCaaj?#ZB9 zqtX%dwhbzRE)1(c+r~{G5I6YC77M<;guf`H*1pOUU*mxn$;(An*&Or8E1wYOOxCrF z%jlw$jp0(Ts@i#_NmN3jmezHGC0{!bPN#;M(($E~(;375h%McbR09fL;eI-1y?F9G z>-P5Jkru=BdudFgJ6BhyS)p}Fi>VKPcuoeB89^ho1k4gcfB%+rtP4Z|8^w*G-^C8P zOjS-iIv2RjO2#vGFUxvo>^34^cU}P7<o?>G_yG#V;SVb-xGkd%{oeCp6Xpsv4K{(e z^ANg|>C0@`A0!`9&|}3j6q*WJ+6LwI=ue-*>!Ty`>(@WME~sxVGi!L|OZhU611k5< N_y<k;=V4?9002n8cAx+N literal 788 zcmV+v1MB=BiwFqrjVECO|88Mtb97;CZ*DFz0Hsx1YvMo@e$THsl-+$<lWL(0E2Xqp zX@z>hq%3XY!<d|y!DJ@PMeD!cGvlR8wGU+x)ST=0eRIwvO3z@(1T<`cut0%|8O)(j zvId2*Q8qJHVV>Zdi30j04=}pDkCWTscmy3dyJ&ZQh3(&A`|^w)qBeb>1ySsxhW%i~ z7TT~{3P?ks2QD>`!B`Ds<VGr3mpm)MuqA3}DwZK|0eYj2VjCbo<>siDMmDa!P@uHA zsF?`4$WfTE!WCA~cFr6-&O60MDl=qi!9|d!-#U<{;*~OLZ<+<$6lXkDYwzDicTdE9 znmG%?;Mm!ooiKJz882o*wBs=t-StOF5YhQWA2>myup2}$jlr>_e@Ci(itwWpCvta+ z{_K3xTx!$Z!Gx#L9w$uuK7L!{s112!m48vpPft7CViUOjYDFUOl?3aF(lp1<qwypj z#6hH+Kpdv}l~o3npq)v>Op1apW<LC`ANQt%NirCZe8&48a1HFgHvyQ2qLR#TvDj6a zGX+LcMj=%V8);p#Lc+&u1U8q}fGKg`Qx+~p?UC#t(B=y?YpIq$B6!D4%mijey9TwT zicN*P%odCrz2dlT!5||vtOY&NPoWgfbvfJ?CToe=vR=Z_<!f#TlgJ?{m0j+;dork2 zsB{GVw+0nK7lu`!ZQ~{oh#S1H`JBJC;01-$+E;nxE8OuSdAZ0cn`0h%<rCtZ$+~uN z8C`U;F<c5(RU5A~iAqS+!n#hd;425h>C`Y&I=+;0IAhozv86kbYCyp&+)l@|AIIaT zX|KPXkF*%1-%Dc}-90})3`@j|bsFXhHBA^TPg+cT^6gXdFrE@n!cH(P@%6WHNryZ~ z6tGs@82X)WpvP3@q@+`VtE^-^V>hy_H^#n2#PZt9VVm4v;uQaYLUH)R$_lQ^XidL2 zgyJ6%*B(M|Jb9iD?jMq46!ci}427nGmUcsVb?zsR;l<t&`DMI6ygaCHE;DO*<xBZ8 Sjsq(92Kfum2JPZ<1^@tv347oG diff --git a/Nagstamon/QUI/files/dialog_about.ui b/Nagstamon/resources/qui/dialog_about.ui similarity index 100% rename from Nagstamon/QUI/files/dialog_about.ui rename to Nagstamon/resources/qui/dialog_about.ui diff --git a/Nagstamon/QUI/files/dialog_acknowledge.ui b/Nagstamon/resources/qui/dialog_acknowledge.ui similarity index 100% rename from Nagstamon/QUI/files/dialog_acknowledge.ui rename to Nagstamon/resources/qui/dialog_acknowledge.ui diff --git a/Nagstamon/QUI/files/dialog_authentication.ui b/Nagstamon/resources/qui/dialog_authentication.ui similarity index 100% rename from Nagstamon/QUI/files/dialog_authentication.ui rename to Nagstamon/resources/qui/dialog_authentication.ui diff --git a/Nagstamon/QUI/files/dialog_downtime.ui b/Nagstamon/resources/qui/dialog_downtime.ui similarity index 100% rename from Nagstamon/QUI/files/dialog_downtime.ui rename to Nagstamon/resources/qui/dialog_downtime.ui diff --git a/Nagstamon/QUI/files/dialog_server_missing.ui b/Nagstamon/resources/qui/dialog_server_missing.ui similarity index 100% rename from Nagstamon/QUI/files/dialog_server_missing.ui rename to Nagstamon/resources/qui/dialog_server_missing.ui diff --git a/Nagstamon/QUI/files/dialog_submit.ui b/Nagstamon/resources/qui/dialog_submit.ui similarity index 100% rename from Nagstamon/QUI/files/dialog_submit.ui rename to Nagstamon/resources/qui/dialog_submit.ui diff --git a/Nagstamon/QUI/files/settings_action.ui b/Nagstamon/resources/qui/settings_action.ui similarity index 100% rename from Nagstamon/QUI/files/settings_action.ui rename to Nagstamon/resources/qui/settings_action.ui diff --git a/Nagstamon/QUI/files/settings_main.ui b/Nagstamon/resources/qui/settings_main.ui similarity index 100% rename from Nagstamon/QUI/files/settings_main.ui rename to Nagstamon/resources/qui/settings_main.ui diff --git a/Nagstamon/QUI/files/settings_server.ui b/Nagstamon/resources/qui/settings_server.ui similarity index 100% rename from Nagstamon/QUI/files/settings_server.ui rename to Nagstamon/resources/qui/settings_server.ui diff --git a/Nagstamon/setup.py b/Nagstamon/setup.py deleted file mode 100644 index 2e393e4cd..000000000 --- a/Nagstamon/setup.py +++ /dev/null @@ -1,114 +0,0 @@ -# encoding: utf-8 - -# Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - -# from distutils.core import setup -import sys -import platform - -from Nagstamon.Config import AppInfo - -NAME = AppInfo.NAME -VERSION = AppInfo.VERSION - - -# workaround to get directory of Qt5 plugins to add missing 'mediaservice' folder needed for audio on OSX and Windows -import os.path -from PyQt5 import QtCore -if platform.system() == 'Windows': - QTPLUGINS = os.path.join(os.path.dirname(QtCore.__file__), 'plugins') -elif platform.system() == 'Darwin': - # works of course only with Fink-based Qt5-installation - QTPLUGINS = '/sw/lib/qt5-mac/plugins' - -if platform.system() in ('Windows', 'Darwin'): - from cx_Freeze import setup, Executable - os_dependent_include_files = ['Nagstamon/resources/qt.conf', - 'Nagstamon/resources', - '{0}/mediaservice'.format(QTPLUGINS)] -else: - from distutils.core import setup - os_dependent_include_files = ['Nagstamon/resources'] - -if platform.system() == 'Windows': - base = 'Win32GUI' if sys.platform == 'win32' else None - -CLASSIFIERS = [ - 'Intended Audience :: System Administrators', - 'Development Status :: 5 - Production/Stable', - 'Environment :: Win32 (MS Windows)', - 'Environment :: X11 Applications', - 'Environment :: MacOS X', - 'License :: OSI Approved :: GNU General Public License (GPL)', - 'Operating System :: Microsoft :: Windows', - 'Operating System :: POSIX :: Linux', - 'Operating System :: POSIX', - 'Natural Language :: English', - 'Programming Language :: Python', - 'Topic :: System :: Monitoring', - 'Topic :: System :: Networking :: Monitoring' -] - -# Dependencies are automatically detected, but it might need -# fine tuning. -build_exe_options = dict(packages=['PyQt5.QtNetwork', - 'keyring.backends.file', - 'keyring.backends.Gnome', - 'keyring.backends.Google', - 'keyring.backends.kwallet', - 'keyring.backends.multi', - 'keyring.backends.OS_X', - 'keyring.backends.pyfs', - 'keyring.backends.SecretService', - 'keyring.backends.Windows'], - include_files=os_dependent_include_files, - include_msvcr=True, - excludes=[]) - -bdist_mac_options = dict(iconfile='Nagstamon/resources/nagstamon.icns', - custom_info_plist='Nagstamon/resources/Info.plist') - -bdist_dmg_options = dict(volume_label='{0} {1}'.format(NAME, VERSION), - applications_shortcut=False) - -executables = [ - Executable('nagstamon-qt.py', - base=base, - icon='Nagstamon/resources/nagstamon.ico') -] - -setup(name=NAME, - version=VERSION, - license='GNU GPL v2', - description='Nagios status monitor for desktop', - long_description='Nagstamon is a Nagios status monitor which takes place in systray or on desktop (GNOME, KDE, Windows) as floating statusbar to inform you in realtime about the status of your Nagios and derivatives monitored network. It allows to connect to multiple Nagios, Icinga, Opsview, Op5Monitor, Checkmk/Multisite, Centreon and Thruk servers.', - classifiers=CLASSIFIERS, - author='Henri Wahl', - author_email='henri@nagstamon.de', - url='https://nagstamon.de', - download_url='https://nagstamon.de/download', - scripts=['nagstamon.py'], - packages=['Nagstamon', 'Nagstamon.Server', 'Nagstamon.thirdparty'], - package_dir={'Nagstamon': 'Nagstamon'}, - package_data={'Nagstamon': ['resources/*']}, - data_files=[('%s/share/man/man1' % sys.prefix, ['Nagstamon/resources/nagstamon.1'])], - options=dict(build_exe=build_exe_options, - bdist_mac=bdist_mac_options, - bdist_dmg=bdist_dmg_options), - executables=executables - ) diff --git a/build/debian/changelog b/build/debian/changelog index 447e4e089..7f3406025 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.9-20220514) unstable; urgency=low +nagstamon (3.9-20220515) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Wed, 08 Dec 2021 08:00:00 +0100 diff --git a/build/redhat/nagstamon.spec b/build/redhat/nagstamon.spec index 5c8a7163a..398d8bc6f 100644 --- a/build/redhat/nagstamon.spec +++ b/build/redhat/nagstamon.spec @@ -47,11 +47,6 @@ Opsview, Op5Monitor, Checkmk/Multisite, Centreon and Thruk servers. %install %{__python3} setup.py install --single-version-externally-managed -O1 --root=%{buildroot} -#Fix 'non-executable-script' error -chmod +x %{buildroot}%{python3_sitelib}/Nagstamon/Servers/Multisite.py -chmod +x %{buildroot}%{python3_sitelib}/Nagstamon/thirdparty/keyring/cli.py -chmod +x %{buildroot}%{python3_sitelib}/Nagstamon/Config.py - #Provide directory to install icon for desktop file mkdir -p %{buildroot}%{_datadir}/pixmaps diff --git a/setup.py b/setup.py index 6575075ae..a77a9a443 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,8 @@ import platform import os.path -from Nagstamon.Config import AppInfo +from Nagstamon.Config import AppInfo, \ + OS from Nagstamon.Helpers import get_distro # dummy debug queue for compiling @@ -31,7 +32,6 @@ NAME = AppInfo.NAME -OS = platform.system() # make name lowercase for Linux/Unix if OS not in ['Windows', 'Darwin']: if OS == 'Linux': @@ -71,7 +71,7 @@ # Dependencies are automatically detected, but it might need # fine tuning. -build_exe_options = dict(packages=['PyQt5.QtNetwork', +build_exe_options = dict(packages=['PyQt6.QtNetwork', 'keyring.backends.kwallet', 'keyring.backends.OS_X', 'keyring.backends.SecretService', @@ -125,7 +125,7 @@ 'Nagstamon.thirdparty.Xlib.support', 'Nagstamon.thirdparty.Xlib.xobject'], package_dir={'Nagstamon': 'Nagstamon'}, - package_data={'Nagstamon': ['resources/*']}, + package_data={'Nagstamon': ['resources/*', 'resources/qui/*']}, data_files=[('%s/share/man/man1' % sys.prefix, ['Nagstamon/resources/nagstamon.1.gz']), ('%s/share/pixmaps' % sys.prefix, ['Nagstamon/resources/nagstamon.svg']), ('%s/share/applications' % sys.prefix, ['Nagstamon/resources/nagstamon.desktop'])], From 19e5f76eb5a7cf985c27d162d069135a730db9ca Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 15 May 2022 14:32:33 +0200 Subject: [PATCH 260/884] fix resources recursion --- Nagstamon/resources/nagstamon.1.gz | Bin 783 -> 783 bytes setup.py | 8 +++----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Nagstamon/resources/nagstamon.1.gz b/Nagstamon/resources/nagstamon.1.gz index c720f2fa9bc07c1a10b4b6b043b8d88f7a734d57..78a4b7e479be6dad18c5838055f4000df69f0186 100644 GIT binary patch delta 15 WcmeBY>t|z=@8;l0`m~XalNkUZ69fVP delta 15 WcmeBY>t|z=@8;lm_;w>3Co=#e^aOkW diff --git a/setup.py b/setup.py index a77a9a443..4f3053e2e 100644 --- a/setup.py +++ b/setup.py @@ -51,7 +51,6 @@ from setuptools import setup os_dependent_include_files = ['Nagstamon/resources'] -executables = [] if os.path.exists('nagstamon'): NAGSTAMON_SCRIPT = 'nagstamon' @@ -71,7 +70,7 @@ # Dependencies are automatically detected, but it might need # fine tuning. -build_exe_options = dict(packages=['PyQt6.QtNetwork', +build_exe_options = dict(packages=['PyQt5.QtNetwork', 'keyring.backends.kwallet', 'keyring.backends.OS_X', 'keyring.backends.SecretService', @@ -125,13 +124,12 @@ 'Nagstamon.thirdparty.Xlib.support', 'Nagstamon.thirdparty.Xlib.xobject'], package_dir={'Nagstamon': 'Nagstamon'}, - package_data={'Nagstamon': ['resources/*', 'resources/qui/*']}, + package_data={'Nagstamon': ['resources/**/*']}, data_files=[('%s/share/man/man1' % sys.prefix, ['Nagstamon/resources/nagstamon.1.gz']), ('%s/share/pixmaps' % sys.prefix, ['Nagstamon/resources/nagstamon.svg']), ('%s/share/applications' % sys.prefix, ['Nagstamon/resources/nagstamon.desktop'])], options=dict(build_exe=build_exe_options, bdist_mac=bdist_mac_options, bdist_dmg=bdist_dmg_options, - bdist_rpm=bdist_rpm_options), - executables=executables + bdist_rpm=bdist_rpm_options) ) From bbb13b03aca01e03f6bba3d0917a33c808f255d0 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 15 May 2022 14:59:48 +0200 Subject: [PATCH 261/884] make Qt5 work on Fedora without pyqt6 --- Nagstamon/QUI/qt.py | 50 +++++++++++++++-------------- Nagstamon/resources/nagstamon.1.gz | Bin 783 -> 783 bytes 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/Nagstamon/QUI/qt.py b/Nagstamon/QUI/qt.py index ce5aa34aa..392a14696 100644 --- a/Nagstamon/QUI/qt.py +++ b/Nagstamon/QUI/qt.py @@ -31,9 +31,10 @@ except ImportError: sys.exit('Qt is missing') -if 'PyQt6' in sys.modules: - # PySide/PyQt compatibility - from PyQt6.QtCore import pyqtSignal as Signal, \ +# because 'PyQt6' is in sys.modules even if the import some line befoe failed +# the backup PyQt5 should be loaded earlier if it exists due to exception treatment +if 'PyQt5' in sys.modules: + from PyQt5.QtCore import pyqtSignal as Signal, \ pyqtSlot as Slot, \ PYQT_VERSION_STR as QT_VERSION_STR, \ QAbstractTableModel, \ @@ -48,8 +49,7 @@ QTimer, \ QUrl, \ QXmlStreamReader - from PyQt6.QtGui import QAction, \ - QBrush, \ + from PyQt5.QtGui import QBrush, \ QColor, \ QCursor, \ QFont, \ @@ -59,11 +59,13 @@ QPainter, \ QPalette, \ QPixmap - from PyQt6.QtMultimedia import QAudioOutput, \ - QMediaPlayer - from PyQt6.QtSvg import QSvgRenderer - from PyQt6.QtSvgWidgets import QSvgWidget - from PyQt6.QtWidgets import QAbstractItemView, \ + from PyQt5.QtMultimedia import QMediaContent, \ + QMediaPlayer, \ + QMediaPlaylist + from PyQt5.QtSvg import QSvgRenderer, \ + QSvgWidget + from PyQt5.QtWidgets import QAbstractItemView, \ + QAction, \ QApplication, \ QColorDialog, \ QComboBox, \ @@ -87,12 +89,13 @@ QSystemTrayIcon, \ QVBoxLayout, \ QWidget - from PyQt6 import uic + from PyQt5 import uic # for later decision which differences have to be considered - QT_FLAVOR = 'PyQt6' + QT_FLAVOR = 'PyQt5' -elif 'PyQt5' in sys.modules: - from PyQt5.QtCore import pyqtSignal as Signal, \ +elif 'PyQt6' in sys.modules: + # PySide/PyQt compatibility + from PyQt6.QtCore import pyqtSignal as Signal, \ pyqtSlot as Slot, \ PYQT_VERSION_STR as QT_VERSION_STR, \ QAbstractTableModel, \ @@ -107,7 +110,8 @@ QTimer, \ QUrl, \ QXmlStreamReader - from PyQt5.QtGui import QBrush, \ + from PyQt6.QtGui import QAction, \ + QBrush, \ QColor, \ QCursor, \ QFont, \ @@ -117,13 +121,11 @@ QPainter, \ QPalette, \ QPixmap - from PyQt5.QtMultimedia import QMediaContent, \ - QMediaPlayer, \ - QMediaPlaylist - from PyQt5.QtSvg import QSvgRenderer, \ - QSvgWidget - from PyQt5.QtWidgets import QAbstractItemView, \ - QAction, \ + from PyQt6.QtMultimedia import QAudioOutput, \ + QMediaPlayer + from PyQt6.QtSvg import QSvgRenderer + from PyQt6.QtSvgWidgets import QSvgWidget + from PyQt6.QtWidgets import QAbstractItemView, \ QApplication, \ QColorDialog, \ QComboBox, \ @@ -147,9 +149,9 @@ QSystemTrayIcon, \ QVBoxLayout, \ QWidget - from PyQt5 import uic + from PyQt6 import uic # for later decision which differences have to be considered - QT_FLAVOR = 'PyQt5' + QT_FLAVOR = 'PyQt6' # elif 'PySide6' in sys.modules: # from PySide6.QtCore import Signal, \ diff --git a/Nagstamon/resources/nagstamon.1.gz b/Nagstamon/resources/nagstamon.1.gz index 78a4b7e479be6dad18c5838055f4000df69f0186..14a71816f7cfa65993b123dc593c282d63d26e41 100644 GIT binary patch delta 15 WcmeBY>t|z=@8;kT{<)EjlNkUXTm!lQ delta 15 WcmeBY>t|z=@8;l0`m~XalNkUZ69fVP From 79429691195a9c3550e9cb26da4eaa54794adc23 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 15 May 2022 15:01:54 +0200 Subject: [PATCH 262/884] ... --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 4f3053e2e..031cb167f 100644 --- a/setup.py +++ b/setup.py @@ -70,7 +70,7 @@ # Dependencies are automatically detected, but it might need # fine tuning. -build_exe_options = dict(packages=['PyQt5.QtNetwork', +build_exe_options = dict(packages=['PyQt6.QtNetwork', 'keyring.backends.kwallet', 'keyring.backends.OS_X', 'keyring.backends.SecretService', @@ -85,6 +85,7 @@ bdist_dmg_options = dict(volume_label='{0} {1}'.format(NAME, VERSION), applications_shortcut=False) +# Fedora seems to have no complete pyqt5 yet bdist_rpm_options = dict(requires='python3 ' 'python3-beautifulsoup4 ' 'python3-crypto ' From 7d0c198d2bc0a67f84fd3590db77fabbe5c4413a Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 15 May 2022 23:11:36 +0200 Subject: [PATCH 263/884] experimenting with AppImage --- Nagstamon/QUI/__init__.py | 2 +- build/docker/Dockerfile-appimage | 17 +++++++++++++++++ build/requirements/linux.txt | 2 +- 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 build/docker/Dockerfile-appimage diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index c64c1ab3e..59af3c980 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -1220,7 +1220,7 @@ def set_mode(self): self.move(conf.position_x, conf.position_y) else: # get available desktop specs - available_x = self.screen().vailableGeometry().x() + available_x = self.screen().availableGeometry().x() available_y = self.screen().availableGeometry().y() self.move(available_x, available_y) diff --git a/build/docker/Dockerfile-appimage b/build/docker/Dockerfile-appimage new file mode 100644 index 000000000..e582eff38 --- /dev/null +++ b/build/docker/Dockerfile-appimage @@ -0,0 +1,17 @@ +FROM debian:11 + +RUN apt update && \ + apt -y upgrade &&\ + apt -y install apt-utils + +RUN apt -y install python3 \ + python3-pip \ + python3-setuptools + +RUN pip3 install appimage-builder + +RUN apt -y install strace patchelf libkrb5-dev wget fuse file + +RUN wget https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage -O /usr/local/bin/appimagetool +RUN chmod +x /usr/local/bin/appimagetool + diff --git a/build/requirements/linux.txt b/build/requirements/linux.txt index c573aec97..94d331b56 100644 --- a/build/requirements/linux.txt +++ b/build/requirements/linux.txt @@ -2,7 +2,7 @@ beautifulsoup4 keyring lxml psutil -pyqt5 +pyqt6 pysocks python-dateutil requests From 2fc853a67df9dc58e6149dc34cf56c1cf4ffc9ff Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 16 May 2022 22:36:54 +0200 Subject: [PATCH 264/884] fix debian build --- Nagstamon/Config.py | 2 +- Nagstamon/QUI/__init__.py | 6 ++---- build/debian/changelog | 2 +- build/debian/rules | 11 ----------- setup.py | 2 +- 5 files changed, 5 insertions(+), 18 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 141ad23b9..910c26ceb 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20220515' + VERSION = '3.9-20220516' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 59af3c980..6ae24c67b 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -814,8 +814,6 @@ def mousePressEvent(self, event): # if not set calculate relative position if not statuswindow.relative_x and \ not statuswindow.relative_y: - globby = event.globalPosition() - #event_x, event_y = event.globalPosition() statuswindow.relative_x = event.globalPosition().x() - statuswindow.x() statuswindow.relative_y = event.globalPosition().y() - statuswindow.y() @@ -2050,10 +2048,10 @@ def show_message(self, msg_type, message): """ title = " ".join((AppInfo.NAME, msg_type)) if msg_type == 'warning': - return (QMessageBox.Icon.Warning(statuswindow, title, message)) + return QMessageBox.Icon.Warning(statuswindow, title, message) elif msg_type == 'information': - return (QMessageBox.Icon.Information(statuswindow, title, message)) + return QMessageBox.Icon.Information(statuswindow, title, message) @Slot() def recheck_all(self): diff --git a/build/debian/changelog b/build/debian/changelog index 7f3406025..5a42e9ef1 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.9-20220515) unstable; urgency=low +nagstamon (3.9-20220516) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Wed, 08 Dec 2021 08:00:00 +0100 diff --git a/build/debian/rules b/build/debian/rules index 72e7acce4..641186e52 100755 --- a/build/debian/rules +++ b/build/debian/rules @@ -1,15 +1,4 @@ #!/usr/bin/make -f -# otherwise LICENSE file is missing for about dialog -#export DH_ALWAYS_EXCLUDE=LICENSE - %: dh $@ --with python3 --buildsystem=pybuild - -#override_dh_link: -# dh_link usr/share/nagstamon/nagstamon.py usr/bin/nagstamon - -#override_dh_auto_install: -# dh_auto_install -- --install-lib=/usr/share/nagstamon \ -# --install-scripts=/usr/share/nagstamon \ - diff --git a/setup.py b/setup.py index 031cb167f..e628a921a 100644 --- a/setup.py +++ b/setup.py @@ -125,7 +125,7 @@ 'Nagstamon.thirdparty.Xlib.support', 'Nagstamon.thirdparty.Xlib.xobject'], package_dir={'Nagstamon': 'Nagstamon'}, - package_data={'Nagstamon': ['resources/**/*']}, + package_data={'Nagstamon': ['resources/*.*', 'resources/qui/*']}, data_files=[('%s/share/man/man1' % sys.prefix, ['Nagstamon/resources/nagstamon.1.gz']), ('%s/share/pixmaps' % sys.prefix, ['Nagstamon/resources/nagstamon.svg']), ('%s/share/applications' % sys.prefix, ['Nagstamon/resources/nagstamon.desktop'])], From 9faa644f1fed4eba513b40d8783f6fb83474db08 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 16 May 2022 22:47:37 +0200 Subject: [PATCH 265/884] fix debian build II --- .github/workflows/build-release-latest.yml | 12 ++++++------ build/debian/rules | 11 +++++++++++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index ea00d5462..1f53fc102 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -37,7 +37,7 @@ jobs: run: | python -m unittest tests/test_*.py - debian: + appimage: runs-on: ubuntu-latest needs: test steps: @@ -46,11 +46,11 @@ jobs: - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon - uses: actions/upload-artifact@v2 with: - path: build/*.deb + path: build/*.AppImage retention-days: 1 if-no-files-found: error - fedora-33: + debian: runs-on: ubuntu-latest needs: test steps: @@ -59,7 +59,7 @@ jobs: - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon - uses: actions/upload-artifact@v2 with: - path: build/*.rpm + path: build/*.deb retention-days: 1 if-no-files-found: error @@ -168,7 +168,7 @@ jobs: repo-fedora: runs-on: ubuntu-latest - needs: [debian, fedora-33, fedora-34, fedora-35, fedora-36, macos, windows-64] + needs: [appimage, debian, fedora-34, fedora-35, fedora-36, macos, windows-64] steps: # get binaries created by other jobs - uses: actions/download-artifact@v2 @@ -190,7 +190,7 @@ jobs: github-release: runs-on: ubuntu-latest - needs: [debian, fedora-33, fedora-34, fedora-35, fedora-36, macos, windows-64] + needs: [appimage, debian, fedora-34, fedora-35, fedora-36, macos, windows-64] steps: - uses: actions/download-artifact@v2 - run: cd artifact && md5sum *agstamon* > md5sums.txt diff --git a/build/debian/rules b/build/debian/rules index 641186e52..72e7acce4 100755 --- a/build/debian/rules +++ b/build/debian/rules @@ -1,4 +1,15 @@ #!/usr/bin/make -f +# otherwise LICENSE file is missing for about dialog +#export DH_ALWAYS_EXCLUDE=LICENSE + %: dh $@ --with python3 --buildsystem=pybuild + +#override_dh_link: +# dh_link usr/share/nagstamon/nagstamon.py usr/bin/nagstamon + +#override_dh_auto_install: +# dh_auto_install -- --install-lib=/usr/share/nagstamon \ +# --install-scripts=/usr/share/nagstamon \ + From 6ed5a8ecbe687b292a2fadb258270dbe6a2a6979 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 16 May 2022 22:48:07 +0200 Subject: [PATCH 266/884] fix debian build II --- .github/workflows/build-release-latest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 1f53fc102..779966b8b 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -168,7 +168,7 @@ jobs: repo-fedora: runs-on: ubuntu-latest - needs: [appimage, debian, fedora-34, fedora-35, fedora-36, macos, windows-64] + needs: [debian, fedora-34, fedora-35, fedora-36, macos, windows-64] steps: # get binaries created by other jobs - uses: actions/download-artifact@v2 @@ -190,7 +190,7 @@ jobs: github-release: runs-on: ubuntu-latest - needs: [appimage, debian, fedora-34, fedora-35, fedora-36, macos, windows-64] + needs: [debian, fedora-34, fedora-35, fedora-36, macos, windows-64] steps: - uses: actions/download-artifact@v2 - run: cd artifact && md5sum *agstamon* > md5sums.txt From 15969baa7b3e090400f7c58c9acaf6f093e701ac Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 16 May 2022 22:54:54 +0200 Subject: [PATCH 267/884] appimage --- .github/workflows/build-release-latest.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 779966b8b..9ac07a8a4 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -39,11 +39,12 @@ jobs: appimage: runs-on: ubuntu-latest - needs: test + #needs: test steps: - uses: actions/checkout@v2 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + # higher privileges needed for creation of AppImage + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon --cap-add SYS_ADMIN --cap-add MKNOD --device /dev/fuse:mrw build-nagstamon - uses: actions/upload-artifact@v2 with: path: build/*.AppImage From 7b98c0f2155f21c5cd92c9d69a11be1321c30c9f Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 16 May 2022 23:06:06 +0200 Subject: [PATCH 268/884] appimage II --- build/appimage/AppImageBuilder.yml | 39 ++++++++++++++++++++++++++++++ build/debian/rules | 11 --------- build/docker/Dockerfile-appimage | 5 ++++ 3 files changed, 44 insertions(+), 11 deletions(-) create mode 100644 build/appimage/AppImageBuilder.yml diff --git a/build/appimage/AppImageBuilder.yml b/build/appimage/AppImageBuilder.yml new file mode 100644 index 000000000..f98db584e --- /dev/null +++ b/build/appimage/AppImageBuilder.yml @@ -0,0 +1,39 @@ +# https://appimage-builder.readthedocs.io/en/latest/examples/pyqt.html +version: 1 +script: + - rm -rf AppDir | true + - mkdir -p AppDir/app + - mkdir -p AppDir/usr/local/share/icons + - mkdir -p AppDir/usr/local/share/applications + - cp -r ../../Nagstamon AppDir/app/Nagstamon + - cp ../../nagstamon.py AppDir/app/nagstamon.py + - cp ../../Nagstamon/resources/nagstamon.svg AppDir/usr/local/share/icons + - cp ../../Nagstamon/resources/nagstamon.desktop AppDir + - pip3 install --prefix=/usr --system --ignore-installed --root=AppDir --requirement ../requirements/linux.txt +AppDir: + path: ./AppDir + app_info: + id: nagstamon + name: Nagstamon + icon: nagstamon + version: '3.9' + exec: usr/bin/python3 + exec_args: "${APPDIR}/app/nagstamon.py $@" + apt: + arch: amd64 + allow_unauthenticated: true + sources: + - sourceline: deb http://deb.debian.org/debian bullseye main + - sourceline: deb http://security.debian.org/debian-security bullseye-security + main + - sourceline: deb http://deb.debian.org/debian bullseye-updates main + include: + - coreutils + - python3 + runtime: + env: + PYTHONHOME: '${APPDIR}/usr' + PYTHONPATH: '${APPDIR}/usr/lib/python3.9/site-packages' +AppImage: + arch: x86_64 + update-information: guess diff --git a/build/debian/rules b/build/debian/rules index 72e7acce4..641186e52 100755 --- a/build/debian/rules +++ b/build/debian/rules @@ -1,15 +1,4 @@ #!/usr/bin/make -f -# otherwise LICENSE file is missing for about dialog -#export DH_ALWAYS_EXCLUDE=LICENSE - %: dh $@ --with python3 --buildsystem=pybuild - -#override_dh_link: -# dh_link usr/share/nagstamon/nagstamon.py usr/bin/nagstamon - -#override_dh_auto_install: -# dh_auto_install -- --install-lib=/usr/share/nagstamon \ -# --install-scripts=/usr/share/nagstamon \ - diff --git a/build/docker/Dockerfile-appimage b/build/docker/Dockerfile-appimage index e582eff38..4a60880bb 100644 --- a/build/docker/Dockerfile-appimage +++ b/build/docker/Dockerfile-appimage @@ -15,3 +15,8 @@ RUN apt -y install strace patchelf libkrb5-dev wget fuse file RUN wget https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage -O /usr/local/bin/appimagetool RUN chmod +x /usr/local/bin/appimagetool +RUN which appimage-builder + + +CMD cd /nagstamon/build/appimage && \ + /usr/local/bin/apimage-builder From 1782330b31bb5568c994c936f2acdc40f528fa58 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 16 May 2022 23:08:36 +0200 Subject: [PATCH 269/884] debian fix --- build/docker/Dockerfile-appimage | 1 - setup.py | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/build/docker/Dockerfile-appimage b/build/docker/Dockerfile-appimage index 4a60880bb..0897e16a8 100644 --- a/build/docker/Dockerfile-appimage +++ b/build/docker/Dockerfile-appimage @@ -17,6 +17,5 @@ RUN chmod +x /usr/local/bin/appimagetool RUN which appimage-builder - CMD cd /nagstamon/build/appimage && \ /usr/local/bin/apimage-builder diff --git a/setup.py b/setup.py index e628a921a..7c0c30abc 100644 --- a/setup.py +++ b/setup.py @@ -125,7 +125,9 @@ 'Nagstamon.thirdparty.Xlib.support', 'Nagstamon.thirdparty.Xlib.xobject'], package_dir={'Nagstamon': 'Nagstamon'}, - package_data={'Nagstamon': ['resources/*.*', 'resources/qui/*']}, + package_data={'Nagstamon': ['resources/*.*', + 'resources/qui/*', + '../LICENSE']}, data_files=[('%s/share/man/man1' % sys.prefix, ['Nagstamon/resources/nagstamon.1.gz']), ('%s/share/pixmaps' % sys.prefix, ['Nagstamon/resources/nagstamon.svg']), ('%s/share/applications' % sys.prefix, ['Nagstamon/resources/nagstamon.desktop'])], From 158358ace93bbff58c2bb52bb7b489bacf5c3825 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 16 May 2022 23:10:50 +0200 Subject: [PATCH 270/884] debian fix --- build/docker/Dockerfile-appimage | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/docker/Dockerfile-appimage b/build/docker/Dockerfile-appimage index 0897e16a8..8d7c7d214 100644 --- a/build/docker/Dockerfile-appimage +++ b/build/docker/Dockerfile-appimage @@ -18,4 +18,4 @@ RUN chmod +x /usr/local/bin/appimagetool RUN which appimage-builder CMD cd /nagstamon/build/appimage && \ - /usr/local/bin/apimage-builder + /usr/local/bin/appimage-builder From 6b0cdbe400c53b95433798dea4c0a2bce00c0029 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 16 May 2022 23:30:27 +0200 Subject: [PATCH 271/884] debian fix LICENSE --- build/docker/Dockerfile-appimage | 2 -- setup.py | 47 ++++++++++++++++---------------- 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/build/docker/Dockerfile-appimage b/build/docker/Dockerfile-appimage index 8d7c7d214..c5a4491da 100644 --- a/build/docker/Dockerfile-appimage +++ b/build/docker/Dockerfile-appimage @@ -15,7 +15,5 @@ RUN apt -y install strace patchelf libkrb5-dev wget fuse file RUN wget https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage -O /usr/local/bin/appimagetool RUN chmod +x /usr/local/bin/appimagetool -RUN which appimage-builder - CMD cd /nagstamon/build/appimage && \ /usr/local/bin/appimage-builder diff --git a/setup.py b/setup.py index 7c0c30abc..97e2b66ad 100644 --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ import os.path from Nagstamon.Config import AppInfo, \ - OS + OS from Nagstamon.Helpers import get_distro # dummy debug queue for compiling @@ -50,6 +50,7 @@ NAGSTAMON_SCRIPT = 'nagstamon.py' from setuptools import setup + os_dependent_include_files = ['Nagstamon/resources'] if os.path.exists('nagstamon'): NAGSTAMON_SCRIPT = 'nagstamon' @@ -87,21 +88,21 @@ # Fedora seems to have no complete pyqt5 yet bdist_rpm_options = dict(requires='python3 ' - 'python3-beautifulsoup4 ' - 'python3-crypto ' - 'python3-cryptography ' - 'python3-dateutil ' - 'python3-keyring ' - 'python3-lxml ' - 'python3-psutil ' - 'python3-pysocks ' - 'python3-qt5 ' - 'python3-requests ' - 'python3-requests-kerberos ' - 'python3-SecretStorage ' - 'qt5-qtmultimedia ' - 'qt5-qtsvg ', -dist_dir='./build') + 'python3-beautifulsoup4 ' + 'python3-crypto ' + 'python3-cryptography ' + 'python3-dateutil ' + 'python3-keyring ' + 'python3-lxml ' + 'python3-psutil ' + 'python3-pysocks ' + 'python3-qt5 ' + 'python3-requests ' + 'python3-requests-kerberos ' + 'python3-SecretStorage ' + 'qt5-qtmultimedia ' + 'qt5-qtsvg ', + dist_dir='./build') setup(name=NAME, version=VERSION, @@ -126,13 +127,13 @@ 'Nagstamon.thirdparty.Xlib.xobject'], package_dir={'Nagstamon': 'Nagstamon'}, package_data={'Nagstamon': ['resources/*.*', - 'resources/qui/*', - '../LICENSE']}, + 'resources/qui/*'], + 'Nagstamon/resources/LICENSE': ['../LICENSE']}, data_files=[('%s/share/man/man1' % sys.prefix, ['Nagstamon/resources/nagstamon.1.gz']), - ('%s/share/pixmaps' % sys.prefix, ['Nagstamon/resources/nagstamon.svg']), - ('%s/share/applications' % sys.prefix, ['Nagstamon/resources/nagstamon.desktop'])], + ('%s/share/pixmaps' % sys.prefix, ['Nagstamon/resources/nagstamon.svg']), + ('%s/share/applications' % sys.prefix, ['Nagstamon/resources/nagstamon.desktop'])], options=dict(build_exe=build_exe_options, - bdist_mac=bdist_mac_options, - bdist_dmg=bdist_dmg_options, - bdist_rpm=bdist_rpm_options) + bdist_mac=bdist_mac_options, + bdist_dmg=bdist_dmg_options, + bdist_rpm=bdist_rpm_options) ) From 728110b8c3838ee7d9fa608df6ae278baef0edc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric?= <github@octvcdrc.fr> Date: Tue, 10 May 2022 12:37:20 +0200 Subject: [PATCH 272/884] Remove line returns from a Host status information Replace line returns with a space from a host status information (services status information was already handled) --- Nagstamon/Servers/Centreon.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/Servers/Centreon.py b/Nagstamon/Servers/Centreon.py index 3fb224500..d4edc6fcb 100644 --- a/Nagstamon/Servers/Centreon.py +++ b/Nagstamon/Servers/Centreon.py @@ -646,7 +646,7 @@ def _get_status(self): self.new_hosts[str(l.hn.text)].status_type = self.HARD_SOFT[self.new_hosts[str(l.hn.text)].status_type] self.new_hosts[str(l.hn.text)].last_check = str(l.lc.text) self.new_hosts[str(l.hn.text)].duration = str(l.lsc.text) - self.new_hosts[str(l.hn.text)].status_information= str(l.ou.text) + self.new_hosts[str(l.hn.text)].status_information = str(l.ou.text).replace('\n', ' ').strip() if l.find('cih') != None: self.new_hosts[str(l.hn.text)].criticality = str(l.cih.text) else: From 3200a3698872d1561bee76daec156e938817020e Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Tue, 17 May 2022 14:46:36 +0200 Subject: [PATCH 273/884] global_position --- Nagstamon/QUI/__init__.py | 22 +++++++++++++++------- Nagstamon/QUI/qt.py | 1 + 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 6ae24c67b..e2107ddba 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -814,8 +814,12 @@ def mousePressEvent(self, event): # if not set calculate relative position if not statuswindow.relative_x and \ not statuswindow.relative_y: - statuswindow.relative_x = event.globalPosition().x() - statuswindow.x() - statuswindow.relative_y = event.globalPosition().y() - statuswindow.y() + if QT_VERSION_MAJOR == 6: + global_position = event.globalPosition() + elif QT_VERSION_MAJOR == 5: + global_position = event.globalPos() + statuswindow.relative_x = global_position.x() - statuswindow.x() + statuswindow.relative_y = global_position.y() - statuswindow.y() def mouseReleaseEvent(self, event): """ @@ -852,15 +856,19 @@ def mouseMoveEvent(self, event): statuswindow.is_shown and statuswindow.is_shown_timestamp + 0.5 < time.time()): if not conf.fullscreen and not conf.windowed and not self.right_mouse_button_pressed: + # Qt5 & Qt6 have different methods for getting the global position + if QT_VERSION_MAJOR == 6: + global_position = event.globalPosition() + elif QT_VERSION_MAJOR == 5: + global_position = event.globalPos() # lock window as moving # if not set calculate relative position if not statuswindow.relative_x and not statuswindow.relative_y: - statuswindow.relative_x = event.globalPosition().x() - statuswindow.x() - statuswindow.relative_y = event.globalPosition().y() - statuswindow.y() - + statuswindow.relative_x = global_position.x() - statuswindow.x() + statuswindow.relative_y = global_position.y() - statuswindow.y() statuswindow.moving = True - statuswindow.move(int(event.globalPosition().x() - statuswindow.relative_x), \ - int(event.globalPosition().y() - statuswindow.relative_y)) + statuswindow.move(int(global_position.x() - statuswindow.relative_x), + int(global_position.y() - statuswindow.relative_y)) # needed for OSX - otherwise statusbar stays blank while moving statuswindow.update() diff --git a/Nagstamon/QUI/qt.py b/Nagstamon/QUI/qt.py index 392a14696..c06e268bd 100644 --- a/Nagstamon/QUI/qt.py +++ b/Nagstamon/QUI/qt.py @@ -212,3 +212,4 @@ # get int-ed version parts QT_VERSION_MAJOR, QT_VERSION_MINOR, QT_VERSION_BUGFIX = [int(x) for x in QT_VERSION_STR.split('.')] + From ccb1631629adce21e8b222f917cc9e26d0eb41ce Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 17 May 2022 18:54:05 +0200 Subject: [PATCH 274/884] fix LICENSE + CREDITS in setup.py --- setup.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 97e2b66ad..31807381b 100644 --- a/setup.py +++ b/setup.py @@ -127,8 +127,9 @@ 'Nagstamon.thirdparty.Xlib.xobject'], package_dir={'Nagstamon': 'Nagstamon'}, package_data={'Nagstamon': ['resources/*.*', - 'resources/qui/*'], - 'Nagstamon/resources/LICENSE': ['../LICENSE']}, + 'resources/qui/*', + 'resources/LICENSE', + 'resources/CREDITS']}, data_files=[('%s/share/man/man1' % sys.prefix, ['Nagstamon/resources/nagstamon.1.gz']), ('%s/share/pixmaps' % sys.prefix, ['Nagstamon/resources/nagstamon.svg']), ('%s/share/applications' % sys.prefix, ['Nagstamon/resources/nagstamon.desktop'])], From 0ddc8d138ed279d2b39b83c5a0f1f7c26bee9b6f Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 17 May 2022 22:29:48 +0200 Subject: [PATCH 275/884] get_global_position(event) --- Nagstamon/QUI/__init__.py | 13 ++++--------- Nagstamon/QUI/qt.py | 13 +++++++++++++ build/docker/Dockerfile-fedora-33 | 25 ------------------------- 3 files changed, 17 insertions(+), 34 deletions(-) delete mode 100644 build/docker/Dockerfile-fedora-33 diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index e2107ddba..6e2ac373d 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -814,10 +814,8 @@ def mousePressEvent(self, event): # if not set calculate relative position if not statuswindow.relative_x and \ not statuswindow.relative_y: - if QT_VERSION_MAJOR == 6: - global_position = event.globalPosition() - elif QT_VERSION_MAJOR == 5: - global_position = event.globalPos() + # Qt5 & Qt6 have different methods for getting the global position so take it from qt.py + global_position = get_global_position(event) statuswindow.relative_x = global_position.x() - statuswindow.x() statuswindow.relative_y = global_position.y() - statuswindow.y() @@ -856,11 +854,8 @@ def mouseMoveEvent(self, event): statuswindow.is_shown and statuswindow.is_shown_timestamp + 0.5 < time.time()): if not conf.fullscreen and not conf.windowed and not self.right_mouse_button_pressed: - # Qt5 & Qt6 have different methods for getting the global position - if QT_VERSION_MAJOR == 6: - global_position = event.globalPosition() - elif QT_VERSION_MAJOR == 5: - global_position = event.globalPos() + # Qt5 & Qt6 have different methods for getting the global position so take it from qt.py + global_position = get_global_position(event) # lock window as moving # if not set calculate relative position if not statuswindow.relative_x and not statuswindow.relative_y: diff --git a/Nagstamon/QUI/qt.py b/Nagstamon/QUI/qt.py index c06e268bd..103867cab 100644 --- a/Nagstamon/QUI/qt.py +++ b/Nagstamon/QUI/qt.py @@ -93,6 +93,13 @@ # for later decision which differences have to be considered QT_FLAVOR = 'PyQt5' + def get_global_position(event): + ''' + Qt5 uses other method than Qt6 + ''' + return event.globalPos() + + elif 'PyQt6' in sys.modules: # PySide/PyQt compatibility from PyQt6.QtCore import pyqtSignal as Signal, \ @@ -153,6 +160,12 @@ # for later decision which differences have to be considered QT_FLAVOR = 'PyQt6' + def get_global_position(event): + ''' + Qt5 uses other method than Qt6 + ''' + return event.globalPosition() + # elif 'PySide6' in sys.modules: # from PySide6.QtCore import Signal, \ # Slot, \ diff --git a/build/docker/Dockerfile-fedora-33 b/build/docker/Dockerfile-fedora-33 deleted file mode 100644 index d45e40f40..000000000 --- a/build/docker/Dockerfile-fedora-33 +++ /dev/null @@ -1,25 +0,0 @@ -FROM fedora:33 -LABEL maintainer=henri@nagstamon.de - -RUN dnf -y install desktop-file-utils \ - git \ - python3 \ - python3-beautifulsoup4 \ - python3-crypto \ - python3-cryptography \ - python3-dateutil \ - python3-devel \ - python3-keyring \ - python3-lxml \ - python3-psutil \ - python3-qt5 \ - python3-qt5-devel \ - python3-requests \ - python3-requests-kerberos \ - python3-SecretStorage \ - qt5-qtsvg \ - qt5-qtmultimedia \ - rpm-build - -CMD cd /nagstamon/build && \ - /usr/bin/python3 build.py From 6c6c2b2e28e285afefdbb6b1593d074272a5d6fc Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 17 May 2022 22:40:47 +0200 Subject: [PATCH 276/884] MediaPlayer --- Nagstamon/QUI/__init__.py | 100 +------------------------------------- Nagstamon/QUI/qt.py | 94 +++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 99 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 6e2ac373d..2b54175d6 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -6785,101 +6785,6 @@ def show(self): self.window.exec() -class MediaPlayerQt5(QObject): - """ - play media files for notification - """ - # needed to show error in a thread-safe way - send_message = Signal(str, str) - - def __init__(self): - QObject.__init__(self) - self.player = QMediaPlayer(parent=self) - - self.player.setVolume(100) - self.playlist = QMediaPlaylist() - self.player.setPlaylist(self.playlist) - - # let statuswindow show message - self.send_message.connect(statuswindow.show_message) - # connect with statuswindow notification worker - statuswindow.worker_notification.load_sound.connect(self.set_media) - statuswindow.worker_notification.play_sound.connect(self.play) - - @Slot(str) - def set_media(self, media_file): - """ - Give media_file to player and if it is one of the default files check first if still exists - :param media_file: - :return: - """ - if media_file in RESOURCE_FILES: - # by using RESOURCE_FILES the file path will be checked on macOS and the file restored if necessary - media_file = RESOURCE_FILES[media_file] - # only existing file can be played - if os.path.exists(media_file): - url = QUrl.fromLocalFile(media_file) - mediacontent = QMediaContent(url) - self.player.setMedia(mediacontent) - del url, mediacontent - return True - else: - # cry and tell no file was found - self.send_message.emit('warning', 'Sound file <b>\'{0}\'</b> not found for playback.'.format(media_file)) - return False - - @Slot() - def play(self): - # just play sound - self.player.play() - - -class MediaPlayerQt6(QObject): - """ - play media files for notification - """ - # needed to show error in a thread-safe way - send_message = Signal(str, str) - - def __init__(self): - QObject.__init__(self) - self.audio_output = QAudioOutput() - self.audio_output.setVolume(100) - self.player = QMediaPlayer(parent=self) - self.player.setAudioOutput(self.audio_output) - # let statuswindow show message - self.send_message.connect(statuswindow.show_message) - # connect with statuswindow notification worker - statuswindow.worker_notification.load_sound.connect(self.set_media) - statuswindow.worker_notification.play_sound.connect(self.play) - - @Slot(str) - def set_media(self, media_file): - """ - Give media_file to player and if it is one of the default files check first if still exists - :param media_file: - :return: - """ - if media_file in RESOURCE_FILES: - # by using RESOURCE_FILES the file path will be checked on macOS and the file restored if necessary - media_file = RESOURCE_FILES[media_file] - # only existing file can be played - if os.path.exists(media_file): - url = QUrl.fromLocalFile(media_file) - self.player.setSource(url) - del url - return True - else: - # cry and tell no file was found - self.send_message.emit('warning', f'Sound file <b>\'{media_file}\'</b> not found for playback.') - return False - - @Slot() - def play(self): - # just play sound - self.player.play() - - class CheckVersion(QObject): """ checking for updates @@ -7252,7 +7157,4 @@ def is_modifier_pressed(): systrayicon.set_menu(menu) # versatile mediaplayer -if QT_VERSION_MAJOR < 6: - mediaplayer = MediaPlayerQt5() -else: - mediaplayer = MediaPlayerQt6() +mediaplayer = MediaPlayer(statuswindow, RESOURCE_FILES) diff --git a/Nagstamon/QUI/qt.py b/Nagstamon/QUI/qt.py index 103867cab..3629ab676 100644 --- a/Nagstamon/QUI/qt.py +++ b/Nagstamon/QUI/qt.py @@ -18,6 +18,7 @@ # Select Qt version based on installation found # Prefer in this order: PyQt6 - PyQt5 +from pathlib import Path import sys # Enough to handle with differences between PyQt5 + PyQt6, so PySide6 will be @@ -99,6 +100,53 @@ def get_global_position(event): ''' return event.globalPos() + class MediaPlayer(QObject): + """ + play media files for notification + """ + # needed to show error in a thread-safe way + send_message = Signal(str, str) + + def __init__(self, statuswindow, resource_files): + QObject.__init__(self) + self.player = QMediaPlayer(parent=self) + + self.player.setVolume(100) + self.playlist = QMediaPlaylist() + self.player.setPlaylist(self.playlist) + self.resource_files = resource_files + # let statuswindow show message + self.send_message.connect(statuswindow.show_message) + # connect with statuswindow notification worker + statuswindow.worker_notification.load_sound.connect(self.set_media) + statuswindow.worker_notification.play_sound.connect(self.play) + + @Slot(str) + def set_media(self, media_file): + """ + Give media_file to player and if it is one of the default files check first if still exists + :param media_file: + :return: + """ + if media_file in self.resource_files: + # by using RESOURCE_FILES the file path will be checked on macOS and the file restored if necessary + media_file = self.resource_files[media_file] + # only existing file can be played + if Path(media_file).exists: + url = QUrl.fromLocalFile(media_file) + mediacontent = QMediaContent(url) + self.player.setMedia(mediacontent) + del url, mediacontent + return True + else: + # cry and tell no file was found + self.send_message.emit('warning', 'Sound file <b>\'{0}\'</b> not found for playback.'.format(media_file)) + return False + + @Slot() + def play(self): + # just play sound + self.player.play() elif 'PyQt6' in sys.modules: # PySide/PyQt compatibility @@ -166,6 +214,52 @@ def get_global_position(event): ''' return event.globalPosition() + class MediaPlayer(QObject): + """ + play media files for notification + """ + # needed to show error in a thread-safe way + send_message = Signal(str, str) + + def __init__(self, statuswindow, resource_files): + QObject.__init__(self) + self.audio_output = QAudioOutput() + self.audio_output.setVolume(100) + self.player = QMediaPlayer(parent=self) + self.player.setAudioOutput(self.audio_output) + self.resource_files = resource_files + # let statuswindow show message + self.send_message.connect(statuswindow.show_message) + # connect with statuswindow notification worker + statuswindow.worker_notification.load_sound.connect(self.set_media) + statuswindow.worker_notification.play_sound.connect(self.play) + + @Slot(str) + def set_media(self, media_file): + """ + Give media_file to player and if it is one of the default files check first if still exists + :param media_file: + :return: + """ + if media_file in self.resource_files: + # by using RESOURCE_FILES the file path will be checked on macOS and the file restored if necessary + media_file = self.resource_files[media_file] + # only existing file can be played + if Path(media_file).exists(): + url = QUrl.fromLocalFile(media_file) + self.player.setSource(url) + del url + return True + else: + # cry and tell no file was found + self.send_message.emit('warning', f'Sound file <b>\'{media_file}\'</b> not found for playback.') + return False + + @Slot() + def play(self): + # just play sound + self.player.play() + # elif 'PySide6' in sys.modules: # from PySide6.QtCore import Signal, \ # Slot, \ From e97c3dd6dcbf61b2dd19f1922dad8f7668a713ca Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 17 May 2022 22:53:02 +0200 Subject: [PATCH 277/884] better Qt flavor detection --- Nagstamon/QUI/qt.py | 20 +++++++++++--------- nagstamon.py | 14 ++++++++++---- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/Nagstamon/QUI/qt.py b/Nagstamon/QUI/qt.py index 3629ab676..729f5f987 100644 --- a/Nagstamon/QUI/qt.py +++ b/Nagstamon/QUI/qt.py @@ -26,15 +26,23 @@ # by the little import the appropriate PyQt version will be loaded try: from PyQt6.QtCore import PYQT_VERSION_STR as QT_VERSION_STR + # get int-ed version parts + QT_VERSION_MAJOR, QT_VERSION_MINOR, QT_VERSION_BUGFIX = [int(x) for x in QT_VERSION_STR.split('.')] + # for later decision which differences have to be considered + QT_FLAVOR = 'PyQt6' except ImportError: try: from PyQt5.QtCore import PYQT_VERSION_STR as QT_VERSION_STR + # get int-ed version parts + QT_VERSION_MAJOR, QT_VERSION_MINOR, QT_VERSION_BUGFIX = [int(x) for x in QT_VERSION_STR.split('.')] + # for later decision which differences have to be considered + QT_FLAVOR = 'PyQt5' except ImportError: sys.exit('Qt is missing') -# because 'PyQt6' is in sys.modules even if the import some line befoe failed +# because 'PyQt6' is in sys.modules even if the import some line before failed # the backup PyQt5 should be loaded earlier if it exists due to exception treatment -if 'PyQt5' in sys.modules: +if QT_FLAVOR == 'PyQt5': from PyQt5.QtCore import pyqtSignal as Signal, \ pyqtSlot as Slot, \ PYQT_VERSION_STR as QT_VERSION_STR, \ @@ -91,8 +99,6 @@ QVBoxLayout, \ QWidget from PyQt5 import uic - # for later decision which differences have to be considered - QT_FLAVOR = 'PyQt5' def get_global_position(event): ''' @@ -148,7 +154,7 @@ def play(self): # just play sound self.player.play() -elif 'PyQt6' in sys.modules: +elif QT_FLAVOR == 'PyQt6': # PySide/PyQt compatibility from PyQt6.QtCore import pyqtSignal as Signal, \ pyqtSlot as Slot, \ @@ -316,7 +322,3 @@ def play(self): # QWidget # # for later decision which differences have to be considered # QT_FLAVOR = 'PySide6' - -# get int-ed version parts -QT_VERSION_MAJOR, QT_VERSION_MINOR, QT_VERSION_BUGFIX = [int(x) for x in QT_VERSION_STR.split('.')] - diff --git a/nagstamon.py b/nagstamon.py index f5130fbc0..9d78ebb41 100755 --- a/nagstamon.py +++ b/nagstamon.py @@ -33,6 +33,7 @@ # if there are more args, than the config folder, nagstaCLI is been executed if len(sys.argv) > 2: import nagstacli # fix crash if more than 2 args are passed - mprenditore + nagstacli.executeCli() sys.exit(1) @@ -43,9 +44,11 @@ # get GUI from Nagstamon.QUI import (APP, - statuswindow, - check_version, - check_servers) + statuswindow, + check_version, + check_servers, + QT_FLAVOR, + QT_VERSION_STR) # ask for help if no servers are configured check_servers() @@ -58,10 +61,13 @@ if conf.check_for_new_version is True: check_version.check(start_mode=True, parent=statuswindow) + # debug + print(f'Using {QT_FLAVOR} {QT_VERSION_STR}') + APP.exec() - del(APP) sys.exit(0) except Exception as err: import traceback + traceback.print_exc(file=sys.stdout) From eca97a576006677e1240019eb2419de732916307 Mon Sep 17 00:00:00 2001 From: Benoit Poulet <benoit.poulet@businessdecision.com> Date: Wed, 25 May 2022 14:24:28 +0200 Subject: [PATCH 278/884] hosts + services display OK --- Nagstamon/Servers/Centreon.py | 724 ++++++++++++++++++++++------------ 1 file changed, 464 insertions(+), 260 deletions(-) diff --git a/Nagstamon/Servers/Centreon.py b/Nagstamon/Servers/Centreon.py index d4edc6fcb..54c271f16 100644 --- a/Nagstamon/Servers/Centreon.py +++ b/Nagstamon/Servers/Centreon.py @@ -24,6 +24,10 @@ import sys import re import copy +# API V2 +import pprint +import json +import requests from datetime import datetime, timedelta @@ -59,12 +63,17 @@ class CentreonServer(GenericServer): centreon_version = None # Token that centreon use to protect the system centreon_token = None + # URLs of the Centreon pages + urls_centreon = None # To only detect broker once first_login = True # limit number of services retrived limit_services_number = 9999 # default value, applies to version 2.2 and others XML_PATH = 'xml' + # use the RestAPI + use_restapi = True + restapi_version = "latest" def init_config(self): ''' @@ -72,8 +81,10 @@ def init_config(self): ''' # set URLs here already self.init_HTTP() - if not self.tls_error and self.centreon_version is not None: - self._define_url() + # Changed this because define_url was called 2 times + #if not self.tls_error and self.centreon_version is not None: + if not self.tls_error and self.urls_centreon is None: + self.define_url() def init_HTTP(self): """ @@ -81,6 +92,9 @@ def init_HTTP(self): """ if self.session is None: GenericServer.init_HTTP(self) + if self.use_restapi == True: + # prepare for JSON + self.session.headers.update({'Content-Type': 'application/json'}) if self.centreon_version is None: result_versioncheck = self.FetchURL(self.monitor_cgi_url + '/index.php', giveback='raw') @@ -89,7 +103,7 @@ def init_HTTP(self): if re.search('2\.2\.[0-9]', raw_versioncheck): self.centreon_version = 2.2 if conf.debug_mode is True: - self.Debug(server=self.get_name(), debug='Centreon version detected : 2.2') + self.Debug(server='[' + self.get_name() + ']', debug='Centreon version detected : 2.2') # URLs for browser shortlinks/buttons on popup window self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?p=1', 'hosts': '$MONITOR$/main.php?p=20103&o=hpb', @@ -98,7 +112,7 @@ def init_HTTP(self): elif re.search('2\.[3-6]\.[0-5]', raw_versioncheck): self.centreon_version = 2.3456 if conf.debug_mode is True: - self.Debug(server=self.get_name(), debug='Centreon version detected : 2.3 <=> 2.6.5') + self.Debug(server='[' + self.get_name() + ']', debug='Centreon version detected : 2.3 <=> 2.6.5') # URLs for browser shortlinks/buttons on popup window self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?p=1', 'hosts': '$MONITOR$/main.php?p=20103&o=hpb', @@ -107,7 +121,7 @@ def init_HTTP(self): elif re.search('2\.6\.[6-9]', raw_versioncheck): self.centreon_version = 2.66 if conf.debug_mode is True: - self.Debug(server=self.get_name(), debug='Centreon version detected : 2.6.6') + self.Debug(server='[' + self.get_name() + ']', debug='Centreon version detected : 2.6.6') # URLs for browser shortlinks/buttons on popup window self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?p=1', 'hosts': '$MONITOR$/main.php?p=20103&o=hpb', @@ -117,7 +131,7 @@ def init_HTTP(self): # Centreon 2.7 only support C. Broker self.centreon_version = 2.7 if conf.debug_mode is True: - self.Debug(server=self.get_name(), debug='Centreon version detected : 2.7') + self.Debug(server='[' + self.get_name() + ']', debug='Centreon version detected : 2.7') # URLs for browser shortlinks/buttons on popup window self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?', 'hosts': '$MONITOR$/main.php?p=20202&o=hpb', @@ -127,7 +141,7 @@ def init_HTTP(self): # Centreon 2.8 only support C. Broker self.centreon_version = 2.8 if conf.debug_mode is True: - self.Debug(server=self.get_name(), debug='Centreon version detected : 2.8') + self.Debug(server='[' + self.get_name() + ']', debug='Centreon version detected : 2.8') # URLs for browser shortlinks/buttons on popup window self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?', 'hosts': '$MONITOR$/main.php?p=20202', @@ -136,7 +150,7 @@ def init_HTTP(self): elif re.search('18\.10\.[0-9]', raw_versioncheck): self.centreon_version = 18.10 if conf.debug_mode is True: - self.Debug(server=self.get_name(), debug='Centreon version detected : 18.10') + self.Debug(server='[' + self.get_name() + ']', debug='Centreon version detected : 18.10') # URLs for browser shortlinks/buttons on popup window self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?', 'hosts': '$MONITOR$/main.php?p=20202', @@ -145,7 +159,7 @@ def init_HTTP(self): elif re.search('19\.(04|10)\.[0-9]', raw_versioncheck) or re.search('20\.(04|10)\.[0-9]', raw_versioncheck): self.centreon_version = 19.04 if conf.debug_mode is True: - self.Debug(server=self.get_name(), debug='Centreon version detected : 19.04 <=> 20.10') + self.Debug(server='[' + self.get_name() + ']', debug='Centreon version detected : 19.04 <=> 21.04') # URLs for browser shortlinks/buttons on popup window self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?', 'hosts': '$MONITOR$/main.php?p=20202', @@ -155,7 +169,7 @@ def init_HTTP(self): # unsupported version or unable do determine self.centreon_version = 19.04 if conf.debug_mode is True: - self.Debug(server=self.get_name(), debug='Centreon version unknown : supposed to be >= 19.04') + self.Debug(server='[' + self.get_name() + ']', debug='Centreon version unknown : supposed to be >= 19.04') # URLs for browser shortlinks/buttons on popup window self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?', 'hosts': '$MONITOR$/main.php?p=20202&o=hpb', @@ -163,10 +177,10 @@ def init_HTTP(self): 'history': '$MONITOR$/main.php?p=203'} else: if conf.debug_mode is True: - self.Debug(server=self.get_name(), debug='Error getting the home page : ' + error_versioncheck) + self.Debug(server='[' + self.get_name() + ']', debug='Error getting the home page : ' + error_versioncheck) if self.first_login: - self.SID = self._get_sid().result + self.SID = self.get_sid().result self.first_login = False del result_versioncheck, raw_versioncheck, error_versioncheck @@ -176,7 +190,7 @@ def reset_HTTP(self): Centreon needs deletion of SID ''' self.SID = None - self.SID = self._get_sid().result + self.SID = self.get_sid().result def open_monitor(self, host, service=''): if self.use_autologin is True: @@ -223,8 +237,7 @@ def open_monitor(self, host, service=''): else: webbrowser_open(self.urls_centreon['main_with_frames'] + '?' + urllib.parse.urlencode({'p':20201,'o':'svcd', 'host_name':host, 'service_description':service}) + auth ) - - def _get_sid(self): + def get_sid(self): ''' gets a shiny new SID for XML HTTP requests to Centreon cutting it out via .partition() from raw HTML additionally get php session cookie @@ -239,12 +252,48 @@ def _get_sid(self): 'history': self.BROWSER_URLS['history'] + auth} raw = self.FetchURL(self.monitor_cgi_url + '/index.php?p=101&autologin=1&useralias=' + self.username + '&token=' + self.autologin_key, giveback='raw') if conf.debug_mode == True: - self.Debug(server=self.get_name(), debug='Autologin : ' + self.username + ' : ' + self.autologin_key) + self.Debug(server='[' + self.get_name() + ']', debug='Autologin : ' + self.username + ' : ' + self.autologin_key) # Gathering of the token who will be used to interact with Centreon (start with 2.66) if self.centreon_version >= 2.66 and self.centreon_version < 19.04: page = self.FetchURL(self.monitor_cgi_url + '/main.get.php') self.centreon_token = page.result.find('input', {'name': "centreon_token"})['value'] + elif self.use_restapi == True: + cgi_data = { + "security": { + "credentials": { + "login": self.username, + "password": self.password + } + } + } + + # Post json + json_string = json.dumps(cgi_data) + result = self.FetchURL(self.monitor_cgi_url + '/api/' + self.restapi_version + '/login', cgi_data=json_string, giveback='raw') + + data = json.loads(result.result) + error = result.error + status_code = result.status_code + + if conf.debug_mode: + self.Debug(server=self.get_name(), + debug="Fetched JSON: " + pprint.pformat(data)) + + + # check if any error occured + errors_occured = self.check_for_error(data, error, status_code) + if errors_occured is not False: + return(errors_occured) + + sid = data["security"]["token"] + + if conf.debug_mode == True: + self.Debug(server='[' + self.get_name() + ']', debug='API login : ' + self.username + ' / ' + self.password + ' > Token : ' + sid) + + self.session.headers.update({'X-Auth-Token': sid}) + return Result(result=sid) + # Password auth else: login = self.FetchURL(self.monitor_cgi_url + '/index.php') @@ -265,16 +314,16 @@ def _get_sid(self): login_data = {"useralias" : self.username, "password" : self.password, "submit" : "Login"} raw = self.FetchURL(self.monitor_cgi_url + "/index.php",cgi_data=login_data, giveback="raw") if conf.debug_mode == True: - self.Debug(server=self.get_name(), debug='Password login : ' + self.username + ' : ' + self.password) + self.Debug(server='[' + self.get_name() + ']', debug='Password login : ' + self.username + ' : ' + self.password) sid = self.session.cookies['PHPSESSID'] if conf.debug_mode == True: - self.Debug(server=self.get_name(), debug='SID : ' + sid) + self.Debug(server='[' + self.get_name() + ']', debug='SID : ' + sid) if self.centreon_version >= 2.66 and self.centreon_version < 19.04: - self.Debug(server=self.get_name(), debug='Centreon Token : ' + self.centreon_token) + self.Debug(server='[' + self.get_name() + ']', debug='Centreon Token : ' + self.centreon_token) # those broker urls would not be changing too often so this check migth be done here if self.first_login: - self._get_xml_path(sid) + self.get_xml_path(sid) self.first_login = False return Result(result=sid) @@ -284,7 +333,6 @@ def _get_sid(self): result, error = self.Error(sys.exc_info()) return Result(result=result, error=error) - def get_start_end(self, host): ''' get start and end time for downtime from Centreon server @@ -325,7 +373,6 @@ def get_start_end(self, host): self.Error(sys.exc_info()) return 'n/a', 'n/a' - def GetHost(self, host): ''' Centreonified way to get host ip - attribute 'a' in down hosts xml is of no use for up @@ -365,7 +412,7 @@ def GetHost(self, host): address = socket.gethostbyaddr(ip)[0] except: if conf.debug_mode == True: - self.Debug(server=self.get_name(), debug='Unable to do a reverse DNS lookup on IP: ' + ip) + self.Debug(server='[' + self.get_name() + ']', debug='Unable to do a reverse DNS lookup on IP: ' + ip) address = ip else: address = ip @@ -381,13 +428,12 @@ def GetHost(self, host): # print IP in debug mode if conf.debug_mode == True: - self.Debug(server=self.get_name(), debug='IP of %s:' % (host) + ' ' + address) + self.Debug(server='[' + self.get_name() + ']', debug='IP of %s:' % (host) + ' ' + address) # give back host or ip return Result(result=address) - - def _get_xml_path(self, sid): + def get_xml_path(self, sid): ''' Find out where this instance of Centreon is publishing the status XMLs Centreon 2.6 + ndo/c.broker - /include/monitoring/status/Hosts/xml/{ndo,broker}/hostXML.php according to configuration @@ -404,26 +450,25 @@ def _get_xml_path(self, sid): if re.search('var _addrXML.*xml\/ndo\/host', raw): self.XML_PATH = 'xml/ndo' if conf.debug_mode == True: - self.Debug(server=self.get_name(), debug='Detected broker : NDO') + self.Debug(server='[' + self.get_name() + ']', debug='Detected broker : NDO') elif re.search('var _addrXML.*xml\/broker\/host', raw): self.XML_PATH = 'xml/broker' if conf.debug_mode == True: - self.Debug(server=self.get_name(), debug='Detected broker : C. Broker') + self.Debug(server='[' + self.get_name() + ']', debug='Detected broker : C. Broker') else: if conf.debug_mode == True: - self.Debug(server=self.get_name(), debug='Could not detect the broker for Centeron 2.[3-6]. Using Centreon Broker') + self.Debug(server='[' + self.get_name() + ']', debug='Could not detect the broker for Centeron 2.[3-6]. Using Centreon Broker') self.XML_PATH = 'xml/broker' del raw else: if conf.debug_mode == True: - self.Debug(server=self.get_name(), debug='Unable to fetch the main page to detect the broker : ' + error) + self.Debug(server='[' + self.get_name() + ']', debug='Unable to fetch the main page to detect the broker : ' + error) del result, error else: if conf.debug_mode == True: - self.Debug(server=self.get_name(), debug='Only Centreon Broker is supported in Centeon >= 2.7 -> XML_PATH='+ self.XML_PATH) + self.Debug(server='[' + self.get_name() + ']', debug='Only Centreon Broker is supported in Centeon >= 2.7 -> XML_PATH='+ self.XML_PATH) - - def _define_url(self): + def define_url(self): urls_centreon_2_2 = { 'main': self.monitor_cgi_url + '/main.php', 'index': self.monitor_cgi_url + '/index.php', @@ -474,6 +519,12 @@ def _define_url(self): 'keepAlive': self.monitor_cgi_url + '/api/internal.php?object=centreon_keepalive&action=keepAlive' } + urls_centreon_api_v2 = { + 'login': self.monitor_cgi_url + '/api/' + self.restapi_version + '/login', + 'services': self.monitor_cgi_url + '/api/' + self.restapi_version + '/monitoring/resources', + 'hosts': self.monitor_cgi_url + '/api/' + self.restapi_version + '/monitoring/resources' + } + if self.centreon_version < 2.7: self.urls_centreon = urls_centreon_2_2 elif self.centreon_version == 2.7: @@ -481,13 +532,14 @@ def _define_url(self): elif self.centreon_version == 2.8: self.urls_centreon = urls_centreon_2_8 # 18.10 and beyond - elif self.centreon_version >= 18.10: + elif self.centreon_version >= 18.10 and self.use_restapi == False: self.urls_centreon = urls_centreon_18_10 + elif self.centreon_version >= 18.10 and self.use_restapi == True: + self.urls_centreon = urls_centreon_api_v2 if conf.debug_mode == True: - self.Debug(server=self.get_name(), debug='URLs defined for Centreon %s' % (self.centreon_version)) - + self.Debug(server='[' + self.get_name() + ']', debug='URLs defined for Centreon %s' % (self.centreon_version)) - def _get_host_id(self, host): + def get_host_id(self, host): ''' get host_id via parsing raw html ''' @@ -506,7 +558,7 @@ def _get_host_id(self, host): del raw else: if conf.debug_mode == True: - self.Debug(server=self.get_name(), debug='Host ID could not be retrieved.') + self.Debug(server='[' + self.get_name() + ']', debug='Host ID could not be retrieved.') # some cleanup del result, error @@ -515,7 +567,7 @@ def _get_host_id(self, host): try: if int(host_id): if conf.debug_mode == True: - self.Debug(server=self.get_name(), host=host, debug='Host ID is ' + host_id) + self.Debug(server='[' + self.get_name() + ']', host=host, debug='Host ID is ' + host_id) return host_id else: return '' @@ -523,7 +575,7 @@ def _get_host_id(self, host): return '' - def _get_host_and_service_id(self, host, service): + def get_host_and_service_id(self, host, service): ''' parse a ton of html to get a host and a service id... ''' @@ -541,10 +593,10 @@ def _get_host_and_service_id(self, host, service): svc_id = raw.partition("var svc_id = '")[2].partition("'")[0] del raw if conf.debug_mode == True: - self.Debug(server=self.get_name(), host=host, service=service, debug='- Get host/svc ID : ' + host_id + '/' + svc_id) + self.Debug(server='[' + self.get_name() + ']', host=host, service=service, debug='- Get host/svc ID : ' + host_id + '/' + svc_id) else: if conf.debug_mode == True: - self.Debug(server=self.get_name(), host=host, service=service, debug='- IDs could not be retrieved.') + self.Debug(server='[' + self.get_name() + ']', host=host, service=service, debug='- IDs could not be retrieved.') # some cleanup del result, error @@ -553,7 +605,7 @@ def _get_host_and_service_id(self, host, service): try: if int(host_id) and int(svc_id): if conf.debug_mode == True: - self.Debug(server=self.get_name(), host=host, service=service, debug='- Host & Service ID are valid (int)') + self.Debug(server='[' + self.get_name() + ']', host=host, service=service, debug='- Host & Service ID are valid (int)') return host_id,svc_id else: return '','' @@ -566,7 +618,7 @@ def _get_status(self): Get status from Centreon Server ''' # Be sure that the session is still active - result = self._check_session() + result = self.check_session() if result is not None: if result.result == 'ERROR': if 'urls_centreon' in result.error: @@ -576,8 +628,11 @@ def _get_status(self): # services (unknown, warning or critical?) if self.centreon_version < 2.7: nagcgiurl_services = self.urls_centreon['xml_services'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'svcpb', 'sort_type':'status', 'sid':self.SID}) - else: + elif self.centreon_version >= 2.7 and self.use_restapi == False: nagcgiurl_services = self.urls_centreon['xml_services'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'svcpb', 'p':20201, 'nc':0, 'criticality':0, 'statusService':'svcpb', 'sSetOrderInMemory':1, 'sid':self.SID}) + elif self.centreon_version >= 2.7 and self.use_restapi == True: + # https://demo.centreon.com/centreon/api/latest/monitoring/resources?page=1&limit=30&sort_by={"status_severity_code":"asc","last_status_change":"desc"}&types=["service"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"] + url_services = self.urls_centreon['services'] + '?types=["service"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"]&limit=' + str(self.limit_services_number) # hosts (up or down or unreachable) # define hosts xml URL, because of inconsistant url @@ -585,89 +640,136 @@ def _get_status(self): nagcgiurl_hosts = self.urls_centreon['xml_hosts'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'hpb', 'sort_type':'status', 'sid':self.SID}) elif self.centreon_version >= 2.7 and self.centreon_version < 19.04: nagcgiurl_hosts = self.urls_centreon['xml_hosts'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'hpb', 'p':20202, 'criticality':0, 'statusHost':'hpb', 'sSetOrderInMemory':1, 'sid':self.SID}) - else: + elif self.centreon_version >= 19.04 and self.use_restapi == False: nagcgiurl_hosts = self.urls_centreon['xml_hosts'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'hpb', 'p':20202, 'criticality':0, 'statusHost':'hpb', 'sSetOrderInMemory':1}) + elif self.centreon_version >= 19.04 and self.use_restapi == True: + # https://demo.centreon.com/centreon/api/latest/monitoring/resources?page=1&limit=30&sort_by={"status_severity_code":"asc","last_status_change":"desc"}&types=["host"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"] + url_hosts = self.urls_centreon['hosts'] + '?types=["host"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"]&limit=' + str(self.limit_services_number) + # hosts - mostly the down ones # unfortunately the hosts status page has a different structure so # hosts must be analyzed separately try: - result = self.FetchURL(nagcgiurl_hosts, giveback='xml') - xmlobj, error, status_code = result.result, result.error, result.status_code - - # check if any error occured - errors_occured = self.check_for_error(xmlobj, error, status_code) - - # if there are errors return them - if errors_occured != False: - return(errors_occured) - - # Check if the result is not empty - if len(xmlobj) == 0: - if conf.debug_mode == True: - self.Debug(server=self.get_name(), debug='Empty host XML result') - return Result(result=None, error="Empty host XML result") - - # in case there are no children session ID is expired - if xmlobj.text.lower() == 'bad session id': - del xmlobj - if conf.debug_mode == True: - self.Debug(server=self.get_name(), debug='Bad session ID, retrieving new one...') - - # try again... - self.SID = self._get_sid().result + if self.centreon_version <= 19.04 and self.use_restapi == False: result = self.FetchURL(nagcgiurl_hosts, giveback='xml') xmlobj, error, status_code = result.result, result.error, result.status_code + + # check if any error occured errors_occured = self.check_for_error(xmlobj, error, status_code) + # if there are errors return them if errors_occured != False: return(errors_occured) - # a second time a bad session id should raise an error + # Check if the result is not empty + if len(xmlobj) == 0: + if conf.debug_mode == True: + self.Debug(server='[' + self.get_name() + ']', debug='Empty host XML result') + return Result(result=None, error="Empty host XML result") + + # in case there are no children session ID is expired if xmlobj.text.lower() == 'bad session id': + del xmlobj if conf.debug_mode == True: - self.Debug(server=self.get_name(), debug='Even after renewing session ID, unable to get the XML') - return Result(result='ERROR', - error='Bad session ID', - status_code=status_code) - - for l in xmlobj.findAll('l'): - try: - # host objects contain service objects - if not l.hn.text in self.new_hosts: - self.new_hosts[str(l.hn.text)] = GenericHost() - self.new_hosts[str(l.hn.text)].name = str(l.hn.text) - self.new_hosts[str(l.hn.text)].server = self.name - self.new_hosts[str(l.hn.text)].status = str(l.cs.text) - # disgusting workaround for https://github.com/HenriWahl/Nagstamon/issues/91 - if self.new_hosts[str(l.hn.text)].status in self.TRANSLATIONS: - self.new_hosts[str(l.hn.text)].status = self.TRANSLATIONS[self.new_hosts[str(l.hn.text)].status] - self.new_hosts[str(l.hn.text)].attempt, self.new_hosts[str(l.hn.text)].status_type = str(l.tr.text).split(' ') - self.new_hosts[str(l.hn.text)].status_type = self.HARD_SOFT[self.new_hosts[str(l.hn.text)].status_type] - self.new_hosts[str(l.hn.text)].last_check = str(l.lc.text) - self.new_hosts[str(l.hn.text)].duration = str(l.lsc.text) - self.new_hosts[str(l.hn.text)].status_information = str(l.ou.text).replace('\n', ' ').strip() - if l.find('cih') != None: - self.new_hosts[str(l.hn.text)].criticality = str(l.cih.text) - else: - self.new_hosts[str(l.hn.text)].criticality = '' - self.new_hosts[str(l.hn.text)].acknowledged = bool(int(str(l.ha.text))) - self.new_hosts[str(l.hn.text)].scheduled_downtime = bool(int(str(l.hdtm.text))) - if l.find('is') != None: - self.new_hosts[str(l.hn.text)].flapping = bool(int(str(l.find('is').text))) - else: - self.new_hosts[str(l.hn.text)].flapping = False - self.new_hosts[str(l.hn.text)].notifications_disabled = not bool(int(str(l.ne.text))) - self.new_hosts[str(l.hn.text)].passiveonly = not bool(int(str(l.ace.text))) - except: - import traceback - traceback.print_exc(file=sys.stdout) - # set checking flag back to False - self.isChecking = False - result, error = self.Error(sys.exc_info()) - return Result(result=result, error=error) - - del xmlobj + self.Debug(server='[' + self.get_name() + ']', debug='Bad session ID, retrieving new one...') + + # try again... + self.SID = self.get_sid().result + result = self.FetchURL(nagcgiurl_hosts, giveback='xml') + xmlobj, error, status_code = result.result, result.error, result.status_code + errors_occured = self.check_for_error(xmlobj, error, status_code) + # if there are errors return them + if errors_occured != False: + return(errors_occured) + + # a second time a bad session id should raise an error + if xmlobj.text.lower() == 'bad session id': + if conf.debug_mode == True: + self.Debug(server='[' + self.get_name() + ']', debug='Even after renewing session ID, unable to get the XML') + return Result(result='ERROR', + error='Bad session ID', + status_code=status_code) + + for l in xmlobj.findAll('l'): + try: + # host objects contain service objects + if not l.hn.text in self.new_hosts: + self.new_hosts[str(l.hn.text)] = GenericHost() + self.new_hosts[str(l.hn.text)].name = str(l.hn.text) + self.new_hosts[str(l.hn.text)].server = self.name + self.new_hosts[str(l.hn.text)].status = str(l.cs.text) + # disgusting workaround for https://github.com/HenriWahl/Nagstamon/issues/91 + if self.new_hosts[str(l.hn.text)].status in self.TRANSLATIONS: + self.new_hosts[str(l.hn.text)].status = self.TRANSLATIONS[self.new_hosts[str(l.hn.text)].status] + self.new_hosts[str(l.hn.text)].attempt, self.new_hosts[str(l.hn.text)].status_type = str(l.tr.text).split(' ') + self.new_hosts[str(l.hn.text)].status_type = self.HARD_SOFT[self.new_hosts[str(l.hn.text)].status_type] + self.new_hosts[str(l.hn.text)].last_check = str(l.lc.text) + self.new_hosts[str(l.hn.text)].duration = str(l.lsc.text) + self.new_hosts[str(l.hn.text)].status_information = str(l.ou.text).replace('\n', ' ').strip() + if l.find('cih') != None: + self.new_hosts[str(l.hn.text)].criticality = str(l.cih.text) + else: + self.new_hosts[str(l.hn.text)].criticality = '' + self.new_hosts[str(l.hn.text)].acknowledged = bool(int(str(l.ha.text))) + self.new_hosts[str(l.hn.text)].scheduled_downtime = bool(int(str(l.hdtm.text))) + if l.find('is') != None: + self.new_hosts[str(l.hn.text)].flapping = bool(int(str(l.find('is').text))) + else: + self.new_hosts[str(l.hn.text)].flapping = False + self.new_hosts[str(l.hn.text)].notifications_disabled = not bool(int(str(l.ne.text))) + self.new_hosts[str(l.hn.text)].passiveonly = not bool(int(str(l.ace.text))) + + except: + import traceback + traceback.print_exc(file=sys.stdout) + # set checking flag back to False + self.isChecking = False + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) + + del xmlobj + elif self.centreon_version >= 19.04 and self.use_restapi == True: + # Get json + result = self.FetchURL(url_hosts, giveback='raw') + + data = json.loads(result.result) + error = result.error + status_code = result.status_code + + if conf.debug_mode: + self.Debug(server=self.get_name(), + debug="Get Hosts status Fetched JSON: " + pprint.pformat(data)) + + # check if any error occured + errors_occured = self.check_for_error(data, error, status_code) + if errors_occured is not False: + return(errors_occured) + + for alerts in data["result"]: + # Attributs à remplir + new_host = alerts["name"] + self.new_hosts[new_host] = GenericHost() + self.new_hosts[new_host].name = alerts["name"] + self.new_hosts[new_host].server = self.name + self.new_hosts[new_host].criticality = alerts["severity_level"] + self.new_hosts[new_host].status = alerts["status"]["name"] + self.new_hosts[new_host].last_check = alerts["last_check"] + # last_state_change = datetime.strptime(alerts["last_status_change"], '%Y-%m-%dT%H:%M:%S%z').replace(tzinfo=None) + self.new_hosts[new_host].duration = alerts["duration"] + self.new_hosts[new_host].attempt = alerts["tries"] + self.new_hosts[new_host].status_information = alerts["information"] + self.new_hosts[new_host].passiveonly = alerts["passive_checks"] + self.new_hosts[new_host].notifications_disabled = not alerts["notification_enabled"] + self.new_hosts[new_host].flapping = alerts["flapping"] + self.new_hosts[new_host].acknowledged = alerts["acknowledged"] + self.new_hosts[new_host].scheduled_downtime = alerts["in_downtime"] + if "(S)" in alerts["tries"]: + self.new_hosts[new_host].status_type = self.HARD_SOFT['(S)'] + else: + self.new_hosts[new_host].status_type = self.HARD_SOFT['(H)'] + self.Debug(server='[' + self.get_name() + ']', debug='Host indexed : ' + self.new_hosts[new_host].name) + except: import traceback @@ -679,153 +781,206 @@ def _get_status(self): # services try: - result = self.FetchURL(nagcgiurl_services, giveback='xml') - xmlobj, error, status_code = result.result, result.error, result.status_code - - # check if any error occured - errors_occured = self.check_for_error(xmlobj, error, status_code) - # if there are errors return them - if errors_occured != False: - return(errors_occured) - - # Check if the result is not empty - if len(xmlobj) == 0: - if conf.debug_mode == True: - self.Debug(server=self.get_name(), debug='Empty service XML result') - return Result(result=None, error="Empty service XML result") - - # in case there are no children session id is invalid - if xmlobj.text.lower() == 'bad session id': - # debug - if conf.debug_mode == True: - self.Debug(server=self.get_name(), debug='Bad session ID, retrieving new one...') - # try again... - self.SID = self._get_sid().result + if self.centreon_version <= 19.04 and self.use_restapi == False: result = self.FetchURL(nagcgiurl_services, giveback='xml') xmlobj, error, status_code = result.result, result.error, result.status_code + + # check if any error occured errors_occured = self.check_for_error(xmlobj, error, status_code) # if there are errors return them if errors_occured != False: return(errors_occured) - # a second time a bad session id should raise an error + # Check if the result is not empty + if len(xmlobj) == 0: + if conf.debug_mode == True: + self.Debug(server='[' + self.get_name() + ']', debug='Empty service XML result') + return Result(result=None, error="Empty service XML result") + + # in case there are no children session id is invalid if xmlobj.text.lower() == 'bad session id': - return Result(result='ERROR', - error='Bad session ID', - status_code=status_code) - - # In Centreon 2.8, Meta are merged with regular services - if self.centreon_version < 2.8: - # define meta-services xml URL - if self.centreon_version == 2.7: - nagcgiurl_meta_services = self.urls_centreon['xml_meta'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'meta', 'sort_type':'status', 'sid':self.SID}) - else: - nagcgiurl_meta_services = self.urls_centreon['xml_meta'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'meta', 'sort_type':'status', 'sid':self.SID}) + # debug + if conf.debug_mode == True: + self.Debug(server='[' + self.get_name() + ']', debug='Bad session ID, retrieving new one...') + # try again... + self.SID = self.get_sid().result + result = self.FetchURL(nagcgiurl_services, giveback='xml') + xmlobj, error, status_code = result.result, result.error, result.status_code + errors_occured = self.check_for_error(xmlobj, error, status_code) + # if there are errors return them + if errors_occured != False: + return(errors_occured) + + # a second time a bad session id should raise an error + if xmlobj.text.lower() == 'bad session id': + return Result(result='ERROR', + error='Bad session ID', + status_code=status_code) + + # In Centreon 2.8, Meta are merged with regular services + if self.centreon_version < 2.8: + # define meta-services xml URL + if self.centreon_version == 2.7: + nagcgiurl_meta_services = self.urls_centreon['xml_meta'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'meta', 'sort_type':'status', 'sid':self.SID}) + else: + nagcgiurl_meta_services = self.urls_centreon['xml_meta'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'meta', 'sort_type':'status', 'sid':self.SID}) - # retrive meta-services xml STATUS - result_meta = self.FetchURL(nagcgiurl_meta_services, giveback='xml') - xmlobj_meta, error_meta, status_code_meta = result_meta.result, result_meta.error, result_meta.status_code + # retrive meta-services xml STATUS + result_meta = self.FetchURL(nagcgiurl_meta_services, giveback='xml') + xmlobj_meta, error_meta, status_code_meta = result_meta.result, result_meta.error, result_meta.status_code - # check if any error occured - errors_occured = self.check_for_error(xmlobj_meta, error_meta, status_code_meta) + # check if any error occured + errors_occured = self.check_for_error(xmlobj_meta, error_meta, status_code_meta) - # if there are errors return them - if errors_occured != False: - return(errors_occured) + # if there are errors return them + if errors_occured != False: + return(errors_occured) - # a second time a bad session id should raise an error - if xmlobj_meta.text.lower() == 'bad session id': - if conf.debug_mode == True: - self.Debug(server=self.get_name(), debug='Even after renewing session ID, unable to get the XML') + # a second time a bad session id should raise an error + if xmlobj_meta.text.lower() == 'bad session id': + if conf.debug_mode == True: + self.Debug(server='[' + self.get_name() + ']', debug='Even after renewing session ID, unable to get the XML') - return Result(result='ERROR', - error='Bad session ID', - status_code=status_code_meta) + return Result(result='ERROR', + error='Bad session ID', + status_code=status_code_meta) - # INSERT META-services xml at the end of the services xml - try: - xmlobj.append(xmlobj_meta.reponse) - except: + # INSERT META-services xml at the end of the services xml + try: + xmlobj.append(xmlobj_meta.reponse) + except: + import traceback + traceback.print_exc(file=sys.stdout) + # set checking flag back to False + self.isChecking = False + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) + # do some cleanup + del xmlobj_meta + + for l in xmlobj.findAll('l'): + try: + # host objects contain service objects + ###if not self.new_hosts.has_key(str(l.hn.text)): + if not l.hn.text in self.new_hosts: + self.new_hosts[str(l.hn.text)] = GenericHost() + self.new_hosts[str(l.hn.text)].name = str(l.hn.text) + self.new_hosts[str(l.hn.text)].status = 'UP' + # if a service does not exist create its object + if not l.sd.text in self.new_hosts[str(l.hn.text)].services: + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)] = GenericService() + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].host = str(l.hn.text) + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].name = str(l.sd.text) + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].server = self.name + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status = str(l.cs.text) + + if self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].host == '_Module_Meta': + # ajusting service name for Meta services + if self.centreon_version < 2.8: + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].name = '{} ({})'.format(str(l.sd.text), l.rsd.text) + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].attempt = str(l.ca.text) + else: + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].name = '{} ({})'.format(str(l.sdn.text), l.sdl.text) + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].attempt, \ + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_type = str(l.ca.text).split(' ') + else: + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].attempt, \ + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_type = str(l.ca.text).split(' ') + + # disgusting workaround for https://github.com/HenriWahl/Nagstamon/issues/91 + # Still needed in Centreon 2.8 at least : https://github.com/HenriWahl/Nagstamon/issues/344 + # Need enhancement, we can do service state matching with this field <sc>service_unknown</sc> + #if self.centreon_version < 2.66: + if self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status in self.TRANSLATIONS: + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status = self.TRANSLATIONS[\ + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status] + + if not (self.centreon_version < 2.8 and self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].host == '_Module_Meta'): + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_type =\ + self.HARD_SOFT[self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_type] + + if conf.debug_mode == True: + self.Debug(server='[' + self.get_name() + ']', debug='Parsing service XML (Host/Service/Status_type) ' + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].host + '/' + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].name + '/' + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_type) + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].last_check = str(l.lc.text) + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].duration = str(l.d.text) + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_information = str(l.po.text).replace('\n', ' ').strip() + + if l.find('cih') != None: + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].criticality = str(l.cih.text) + else: + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].criticality = '' + + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].acknowledged = bool(int(str(l.pa.text))) + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].notifications_disabled = not bool(int(str(l.ne.text))) + + # for features not available in centreon < 2.8 and meta services + if not (self.centreon_version < 2.8 and self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].host == '_Module_Meta'): + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].scheduled_downtime = bool(int(str(l.dtm.text))) + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].flapping = bool(int(str(l.find('is').text))) + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].passiveonly = not bool(int(str(l.ac.text))) + + except: import traceback traceback.print_exc(file=sys.stdout) # set checking flag back to False self.isChecking = False result, error = self.Error(sys.exc_info()) return Result(result=result, error=error) + # do some cleanup - del xmlobj_meta - - for l in xmlobj.findAll('l'): - try: - # host objects contain service objects - ###if not self.new_hosts.has_key(str(l.hn.text)): - if not l.hn.text in self.new_hosts: - self.new_hosts[str(l.hn.text)] = GenericHost() - self.new_hosts[str(l.hn.text)].name = str(l.hn.text) - self.new_hosts[str(l.hn.text)].status = 'UP' - # if a service does not exist create its object - if not l.sd.text in self.new_hosts[str(l.hn.text)].services: - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)] = GenericService() - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].host = str(l.hn.text) - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].name = str(l.sd.text) - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].server = self.name - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status = str(l.cs.text) - - if self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].host == '_Module_Meta': - # ajusting service name for Meta services - if self.centreon_version < 2.8: - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].name = '{} ({})'.format(str(l.sd.text), l.rsd.text) - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].attempt = str(l.ca.text) - else: - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].name = '{} ({})'.format(str(l.sdn.text), l.sdl.text) - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].attempt, \ - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_type = str(l.ca.text).split(' ') - else: - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].attempt, \ - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_type = str(l.ca.text).split(' ') - - # disgusting workaround for https://github.com/HenriWahl/Nagstamon/issues/91 - # Still needed in Centreon 2.8 at least : https://github.com/HenriWahl/Nagstamon/issues/344 - # Need enhancement, we can do service state matching with this field <sc>service_unknown</sc> - #if self.centreon_version < 2.66: - if self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status in self.TRANSLATIONS: - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status = self.TRANSLATIONS[\ - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status] - - if not (self.centreon_version < 2.8 and self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].host == '_Module_Meta'): - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_type =\ - self.HARD_SOFT[self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_type] + del xmlobj - if conf.debug_mode == True: - self.Debug(server=self.get_name(), debug='Parsing service XML (Host/Service/Status_type) ' + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].host + '/' + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].name + '/' + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_type) - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].last_check = str(l.lc.text) - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].duration = str(l.d.text) - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_information = str(l.po.text).replace('\n', ' ').strip() - - if l.find('cih') != None: - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].criticality = str(l.cih.text) - else: - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].criticality = '' - - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].acknowledged = bool(int(str(l.pa.text))) - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].notifications_disabled = not bool(int(str(l.ne.text))) - - # for features not available in centreon < 2.8 and meta services - if not (self.centreon_version < 2.8 and self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].host == '_Module_Meta'): - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].scheduled_downtime = bool(int(str(l.dtm.text))) - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].flapping = bool(int(str(l.find('is').text))) - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].passiveonly = not bool(int(str(l.ac.text))) - - except: - import traceback - traceback.print_exc(file=sys.stdout) - # set checking flag back to False - self.isChecking = False - result, error = self.Error(sys.exc_info()) - return Result(result=result, error=error) - - # do some cleanup - del xmlobj + elif self.centreon_version >= 19.04 and self.use_restapi == True: + # Get json + result = self.FetchURL(url_services, giveback='raw') + + data = json.loads(result.result) + error = result.error + status_code = result.status_code + + if conf.debug_mode: + self.Debug(server=self.get_name(), + debug="Get Services status Fetched JSON: " + pprint.pformat(data)) + + # check if any error occured + errors_occured = self.check_for_error(data, error, status_code) + if errors_occured is not False: + return(errors_occured) + + for alerts in data["result"]: + new_host = alerts["parent"]["name"] + new_service = alerts["name"] + # Needed if non-ok services are on a UP host + if not new_host in self.new_hosts: + self.new_hosts[new_host] = GenericHost() + self.new_hosts[new_host].name = new_host + self.new_hosts[new_host].status = 'UP' + self.new_hosts[new_host].services[new_service] = GenericService() + # Attributs à remplir + self.Debug(server='[' + self.get_name() + ']', debug='Service indexed : ' + alerts["parent"]["name"]) + self.Debug(server='[' + self.get_name() + ']', debug='Service status : ' + alerts["status"]["name"]) + + self.new_hosts[new_host].services[new_service].server = self.name + self.new_hosts[new_host].services[new_service].host = alerts["parent"]["name"] + self.new_hosts[new_host].services[new_service].name = alerts["name"] + self.new_hosts[new_host].services[new_service].status = alerts["status"]["name"] + self.new_hosts[new_host].services[new_service].last_check = alerts["last_check"] + # last_state_change = datetime.strptime(alerts["last_state_change"], '%Y-%m-%dT%H:%M:%S%z').replace(tzinfo=None) + # self.new_hosts[new_host].services[new_service].duration = datetime.now() - last_state_change + self.new_hosts[new_host].services[new_service].duration = alerts["duration"] + self.new_hosts[new_host].services[new_service].attempt = alerts["tries"] + self.new_hosts[new_host].services[new_service].status_information = alerts["information"] + # Impossible de trouver les 3 suivants dans le json + self.new_hosts[new_host].services[new_service].passiveonly = alerts["passive_checks"] + self.new_hosts[new_host].services[new_service].notifications_disabled = not alerts["notification_enabled"] + self.new_hosts[new_host].services[new_service].flapping = alerts["flapping"] + self.new_hosts[new_host].services[new_service].acknowledged = alerts["acknowledged"] + self.new_hosts[new_host].services[new_service].scheduled_downtime = alerts["in_downtime"] + if "(S)" in alerts["tries"]: + self.new_hosts[new_host].services[new_service].status_type = self.HARD_SOFT['(S)'] + else: + self.new_hosts[new_host].services[new_service].status_type = self.HARD_SOFT['(H)'] + self.new_hosts[new_host].services[new_service].criticality = alerts["severity_level"] except: import traceback @@ -926,7 +1081,7 @@ def _set_recheck(self, host, service): # Meta if host == '_Module_Meta': if conf.debug_mode == True: - self.Debug(server=self.get_name(), debug='Recheck on a Meta service, more work to be done') + self.Debug(server='[' + self.get_name() + ']', debug='Recheck on a Meta service, more work to be done') m = re.search(r'^.+ \((?P<rsd>.+)\)$', service) if m: rsd = m.group('rsd') @@ -937,7 +1092,7 @@ def _set_recheck(self, host, service): elif service == '': # ... it can only be a host, so check all his services and there is a command for that - host_id = self._get_host_id(host) + host_id = self.get_host_id(host) if self.centreon_version < 2.7: url = self.urls_centreon['xml_hostSendCommand'] + '?' + urllib.parse.urlencode({'cmd':'host_schedule_check', 'actiontype':1,'host_id':host_id,'sid':self.SID}) @@ -947,7 +1102,7 @@ def _set_recheck(self, host, service): else: # service @ host - host_id, service_id = self._get_host_and_service_id(host, service) + host_id, service_id = self.get_host_and_service_id(host, service) # Starting from 19.04 this must be in POST if self.centreon_version < 19.04: @@ -1071,34 +1226,83 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t self.Error(sys.exc_info()) - def _check_session(self): + def check_session(self): if conf.debug_mode == True: - self.Debug(server=self.get_name(), debug='Checking session status') + self.Debug(server='[' + self.get_name() + ']', debug='Checking session status') if 'url_centreon' not in self.__dict__: self.init_config() try: - if self.centreon_version >= 18.10: - result = self.FetchURL(self.urls_centreon['keepAlive'], giveback='raw') - self.raw, self.error, self.status_code = result.result, result.error, result.status_code - # Return 200 & null a session is open + if self.use_restapi == True: if conf.debug_mode == True: - self.Debug(server=self.get_name(), debug='Session status : ' + self.raw + ', http code : ' + str(self.status_code)) - # 401 if no valid session is present - if self.status_code == 401: - self.SID = self._get_sid().result + self.Debug(server='[' + self.get_name() + ']', debug='The token will be deleted if it has not been used for more than one hour. Current Token = ' + self.SID ) + + + cgi_data = {'limit':'0'} + self.session = requests.Session() + self.session.headers['Content-Type'] = 'application/json' + self.session.headers['X-Auth-Token'] = self.SID + # This request must be done in a GET, so just encode the parameters and fetch + result = self.FetchURL(self.urls_centreon['services'] + '?' + urllib.parse.urlencode(cgi_data), giveback="raw") + + # Get json + # ~ result = self.FetchURL(self.urls_centreon['services'] + '?' + urllib.parse.urlencode({ 'limit':0 }), giveback='raw') + + + # ~ self.session = requests.Session() + # ~ self.session.headers['Content-Type'] = 'application/json' + # ~ self.session.headers['X-Auth-Token'] = self.SID + # ~ url = 'https://demo.centreon.com/centreon/api/latest/monitoring/services?limit=0' + # ~ response = self.session.get(url, timeout=5) + # ~ data = json.loads(response.text) + # ~ error = response.reason + # ~ status_code = response.status_code + + + print(self.session.headers) + + data = json.loads(result.result) + error = result.error + status_code = result.status_code + + if conf.debug_mode: + self.Debug(server=self.get_name(), + debug="Check-session Fetched JSON: " + pprint.pformat(data)) + self.Debug(server=self.get_name(), + debug="Error : " + error + " Status code : " + str(status_code)) + + # check if any error occured + errors_occured = self.check_for_error(data, error, status_code) + if errors_occured is not False: + return(errors_occured) + + if not data["result"]: + self.SID = self.get_sid().result if conf.debug_mode == True: - self.Debug(server=self.get_name(), debug='Session renewed') + self.Debug(server='[' + self.get_name() + ']', debug='Session renewed') else: - result = self.FetchURL(self.urls_centreon['autologoutXMLresponse'], giveback='xml') - xmlobj, error, status_code = result.result, result.error, result.status_code - self.session_state = xmlobj.find("state").text.lower() - if conf.debug_mode == True: - self.Debug(server=self.get_name(), debug='Session status : ' + self.session_state) - if self.session_state == "nok": - self.SID = self._get_sid().result + if self.centreon_version >= 18.10: + result = self.FetchURL(self.urls_centreon['keepAlive'], giveback='raw') + self.raw, self.error, self.status_code = result.result, result.error, result.status_code + # Return 200 & null a session is open if conf.debug_mode == True: - self.Debug(server=self.get_name(), debug='Session renewed') + self.Debug(server='[' + self.get_name() + ']', debug='Session status : ' + self.raw + ', http code : ' + str(self.status_code)) + # 401 if no valid session is present + if self.status_code == 401: + self.SID = self.get_sid().result + if conf.debug_mode == True: + self.Debug(server='[' + self.get_name() + ']', debug='Session renewed') + + else: + result = self.FetchURL(self.urls_centreon['autologoutXMLresponse'], giveback='xml') + xmlobj, error, status_code = result.result, result.error, result.status_code + self.session_state = xmlobj.find("state").text.lower() + if conf.debug_mode == True: + self.Debug(server='[' + self.get_name() + ']', debug='Session status : ' + self.session_state) + if self.session_state == "nok": + self.SID = self.get_sid().result + if conf.debug_mode == True: + self.Debug(server='[' + self.get_name() + ']', debug='Session renewed') except: import traceback From c12c6f609af21edf1ad38f1a082ba9c9567d59be Mon Sep 17 00:00:00 2001 From: Benoit Poulet <benoit.poulet@businessdecision.com> Date: Fri, 27 May 2022 16:48:12 +0200 Subject: [PATCH 279/884] Centreon class API only (list OK, Ack Ok) --- Nagstamon/Servers/Centreon.py | 1021 ++++++++------------------------- 1 file changed, 247 insertions(+), 774 deletions(-) diff --git a/Nagstamon/Servers/Centreon.py b/Nagstamon/Servers/Centreon.py index 54c271f16..dd277f7cc 100644 --- a/Nagstamon/Servers/Centreon.py +++ b/Nagstamon/Servers/Centreon.py @@ -59,8 +59,6 @@ class CentreonServer(GenericServer): # Centreon works better or at all with html.parser for BeautifulSoup PARSER = 'html.parser' - # Needed to detect each Centreon's version - centreon_version = None # Token that centreon use to protect the system centreon_token = None # URLs of the Centreon pages @@ -71,119 +69,52 @@ class CentreonServer(GenericServer): limit_services_number = 9999 # default value, applies to version 2.2 and others XML_PATH = 'xml' - # use the RestAPI - use_restapi = True - restapi_version = "latest" def init_config(self): ''' dummy init_config, called at thread start, not really needed here, just omit extra properties ''' + + # FIX but be provided by user + self.user_provided_centreon_version = "20.04" + + if re.search('2(0|1|2)\.(04|10)', self.user_provided_centreon_version): + self.centreon_version = 20.04 + if conf.debug_mode is True: + self.Debug(server='[' + self.get_name() + ']', debug='Centreon version selected : 20.04 <=> 22.04') + # URLs for browser shortlinks/buttons on popup window + self.BROWSER_URLS = {'monitor': '$MONITOR$/monitoring/resources', + 'hosts': '$MONITOR$/monitoring/resources', + 'services': '$MONITOR$/monitoring/resources', + 'history': '$MONITOR$/main.php?p=20301'} + # RestAPI version + self.restapi_version = "latest" + + else: + if conf.debug_mode is True: + self.Debug(server='[' + self.get_name() + ']', debug='No Centreon version provided') + # set URLs here already - self.init_HTTP() + # self.init_HTTP() + # Changed this because define_url was called 2 times #if not self.tls_error and self.centreon_version is not None: if not self.tls_error and self.urls_centreon is None: self.define_url() + def init_HTTP(self): """ initialize HTTP connection """ if self.session is None: GenericServer.init_HTTP(self) - if self.use_restapi == True: - # prepare for JSON - self.session.headers.update({'Content-Type': 'application/json'}) - - if self.centreon_version is None: - result_versioncheck = self.FetchURL(self.monitor_cgi_url + '/index.php', giveback='raw') - raw_versioncheck, error_versioncheck = result_versioncheck.result, result_versioncheck.error - if error_versioncheck == '': - if re.search('2\.2\.[0-9]', raw_versioncheck): - self.centreon_version = 2.2 - if conf.debug_mode is True: - self.Debug(server='[' + self.get_name() + ']', debug='Centreon version detected : 2.2') - # URLs for browser shortlinks/buttons on popup window - self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?p=1', - 'hosts': '$MONITOR$/main.php?p=20103&o=hpb', - 'services': '$MONITOR$/main.php?p=20202&o=svcpb', - 'history': '$MONITOR$/main.php?p=203'} - elif re.search('2\.[3-6]\.[0-5]', raw_versioncheck): - self.centreon_version = 2.3456 - if conf.debug_mode is True: - self.Debug(server='[' + self.get_name() + ']', debug='Centreon version detected : 2.3 <=> 2.6.5') - # URLs for browser shortlinks/buttons on popup window - self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?p=1', - 'hosts': '$MONITOR$/main.php?p=20103&o=hpb', - 'services': '$MONITOR$/main.php?p=20202&o=svcpb', - 'history': '$MONITOR$/main.php?p=203'} - elif re.search('2\.6\.[6-9]', raw_versioncheck): - self.centreon_version = 2.66 - if conf.debug_mode is True: - self.Debug(server='[' + self.get_name() + ']', debug='Centreon version detected : 2.6.6') - # URLs for browser shortlinks/buttons on popup window - self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?p=1', - 'hosts': '$MONITOR$/main.php?p=20103&o=hpb', - 'services': '$MONITOR$/main.php?p=20202&o=svcpb', - 'history': '$MONITOR$/main.php?p=203'} - elif re.search('2\.7\.[0-9]', raw_versioncheck): - # Centreon 2.7 only support C. Broker - self.centreon_version = 2.7 - if conf.debug_mode is True: - self.Debug(server='[' + self.get_name() + ']', debug='Centreon version detected : 2.7') - # URLs for browser shortlinks/buttons on popup window - self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?', - 'hosts': '$MONITOR$/main.php?p=20202&o=hpb', - 'services': '$MONITOR$/main.php?p=20201&o=svcpb', - 'history': '$MONITOR$/main.php?p=203'} - elif re.search('2\.8\.[0-9]', raw_versioncheck): - # Centreon 2.8 only support C. Broker - self.centreon_version = 2.8 - if conf.debug_mode is True: - self.Debug(server='[' + self.get_name() + ']', debug='Centreon version detected : 2.8') - # URLs for browser shortlinks/buttons on popup window - self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?', - 'hosts': '$MONITOR$/main.php?p=20202', - 'services': '$MONITOR$/main.php?p=20201', - 'history': '$MONITOR$/main.php?p=203'} - elif re.search('18\.10\.[0-9]', raw_versioncheck): - self.centreon_version = 18.10 - if conf.debug_mode is True: - self.Debug(server='[' + self.get_name() + ']', debug='Centreon version detected : 18.10') - # URLs for browser shortlinks/buttons on popup window - self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?', - 'hosts': '$MONITOR$/main.php?p=20202', - 'services': '$MONITOR$/main.php?p=20201', - 'history': '$MONITOR$/main.php?p=203'} - elif re.search('19\.(04|10)\.[0-9]', raw_versioncheck) or re.search('20\.(04|10)\.[0-9]', raw_versioncheck): - self.centreon_version = 19.04 - if conf.debug_mode is True: - self.Debug(server='[' + self.get_name() + ']', debug='Centreon version detected : 19.04 <=> 21.04') - # URLs for browser shortlinks/buttons on popup window - self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?', - 'hosts': '$MONITOR$/main.php?p=20202', - 'services': '$MONITOR$/main.php?p=20201', - 'history': '$MONITOR$/main.php?p=203'} - else: - # unsupported version or unable do determine - self.centreon_version = 19.04 - if conf.debug_mode is True: - self.Debug(server='[' + self.get_name() + ']', debug='Centreon version unknown : supposed to be >= 19.04') - # URLs for browser shortlinks/buttons on popup window - self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?', - 'hosts': '$MONITOR$/main.php?p=20202&o=hpb', - 'services': '$MONITOR$/main.php?p=20201&o=svcpb', - 'history': '$MONITOR$/main.php?p=203'} - else: - if conf.debug_mode is True: - self.Debug(server='[' + self.get_name() + ']', debug='Error getting the home page : ' + error_versioncheck) + self.session.headers.update({'Content-Type': 'application/json'}) if self.first_login: self.SID = self.get_sid().result self.first_login = False - del result_versioncheck, raw_versioncheck, error_versioncheck def reset_HTTP(self): ''' @@ -192,139 +123,65 @@ def reset_HTTP(self): self.SID = None self.SID = self.get_sid().result + + def define_url(self): + urls_centreon_api_v2 = { + 'main': self.monitor_cgi_url + '/monitoring/resources', + 'login': self.monitor_cgi_url + '/api/' + self.restapi_version + '/login', + 'services': self.monitor_cgi_url + '/api/' + self.restapi_version + '/monitoring/resources', + 'hosts': self.monitor_cgi_url + '/api/' + self.restapi_version + '/monitoring/resources' + } + + if self.centreon_version == 20.04: + self.urls_centreon = urls_centreon_api_v2 + if conf.debug_mode == True: + self.Debug(server='[' + self.get_name() + ']', debug='URLs defined for Centreon %s' % (self.centreon_version)) + + def open_monitor(self, host, service=''): if self.use_autologin is True: auth = '&autologin=1&useralias=' + self.username + '&token=' + self.autologin_key else: auth = '' - # Meta - if host == '_Module_Meta': - # Centreon < 2.7 - if self.centreon_version < 2.7: - webbrowser_open(self.urls_centreon['index'] + '?' + urllib.parse.urlencode({'p': 20206, 'o': 'meta'}) + auth ) - # Centreon 2.7 - elif self.centreon_version == 2.7: - webbrowser_open(self.urls_centreon['main'] + '?' + urllib.parse.urlencode({'p':20206, 'o':'meta'}) + auth ) - # Centreon 2.8 - elif self.centreon_version == 2.8: - m = re.search(r'^.+ \((?P<rsd>.+)\)$', service) - if m: - service = m.group('rsd') - webbrowser_open(self.urls_centreon['main'] + '?' + urllib.parse.urlencode({'p':20201,'o':'svcd','host_name':'_Module_Meta','service_description':service}) + auth ) - # Starting from Centreon 18.10 - else: - m = re.search(r'^.+ \((?P<rsd>.+)\)$', service) - if m: - service = m.group('rsd') - webbrowser_open(self.urls_centreon['main_with_frames'] + '?' + urllib.parse.urlencode({'p':20201,'o':'svcd','host_name':'_Module_Meta','service_description':service}) + auth ) - - # must be a host if service is empty - elif service == '': - if self.centreon_version < 2.7: - webbrowser_open(self.urls_centreon['main'] + '?' + urllib.parse.urlencode({'p':201,'o':'hd', 'host_name':host}) + auth ) - elif self.centreon_version in [2.7, 2.8]: - webbrowser_open(self.urls_centreon['main'] + '?' + urllib.parse.urlencode({'p':20202,'o':'hd', 'host_name':host}) + auth ) - else: - webbrowser_open(self.urls_centreon['main_with_frames'] + '?' + urllib.parse.urlencode({'p':20202,'o':'hd', 'host_name':host}) + auth ) + webbrowser_open(self.urls_centreon['main'] + auth ) - # so it's a service - else: - if self.centreon_version < 2.7: - webbrowser_open(self.urls_centreon['main'] + '?' + urllib.parse.urlencode({'p':202, 'o':'svcd', 'host_name':host, 'service_description':service}) + auth ) - elif self.centreon_version in [2.7, 2.8]: - webbrowser_open(self.urls_centreon['main'] + '?' + urllib.parse.urlencode({'p':20201,'o':'svcd', 'host_name':host, 'service_description':service}) + auth ) - else: - webbrowser_open(self.urls_centreon['main_with_frames'] + '?' + urllib.parse.urlencode({'p':20201,'o':'svcd', 'host_name':host, 'service_description':service}) + auth ) def get_sid(self): - ''' - gets a shiny new SID for XML HTTP requests to Centreon cutting it out via .partition() from raw HTML - additionally get php session cookie - ''' try: - # Aulogin with key, BROWSER_URLS needs the key - if self.use_autologin == True: - auth = '&autologin=1&useralias=' + self.username + '&token=' + self.autologin_key - self.BROWSER_URLS= { 'monitor': self.BROWSER_URLS['monitor'] + auth,\ - 'hosts': self.BROWSER_URLS['hosts'] + auth,\ - 'services': self.BROWSER_URLS['services'] + auth,\ - 'history': self.BROWSER_URLS['history'] + auth} - raw = self.FetchURL(self.monitor_cgi_url + '/index.php?p=101&autologin=1&useralias=' + self.username + '&token=' + self.autologin_key, giveback='raw') - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Autologin : ' + self.username + ' : ' + self.autologin_key) - # Gathering of the token who will be used to interact with Centreon (start with 2.66) - if self.centreon_version >= 2.66 and self.centreon_version < 19.04: - page = self.FetchURL(self.monitor_cgi_url + '/main.get.php') - self.centreon_token = page.result.find('input', {'name': "centreon_token"})['value'] - - elif self.use_restapi == True: - cgi_data = { - "security": { - "credentials": { - "login": self.username, - "password": self.password - } + cgi_data = { + "security": { + "credentials": { + "login": self.username, + "password": self.password } } + } - # Post json - json_string = json.dumps(cgi_data) - result = self.FetchURL(self.monitor_cgi_url + '/api/' + self.restapi_version + '/login', cgi_data=json_string, giveback='raw') - - data = json.loads(result.result) - error = result.error - status_code = result.status_code - - if conf.debug_mode: - self.Debug(server=self.get_name(), - debug="Fetched JSON: " + pprint.pformat(data)) - + # Post json + json_string = json.dumps(cgi_data) + result = self.FetchURL(self.monitor_cgi_url + '/api/' + self.restapi_version + '/login', cgi_data=json_string, giveback='raw') - # check if any error occured - errors_occured = self.check_for_error(data, error, status_code) - if errors_occured is not False: - return(errors_occured) + data = json.loads(result.result) + error = result.error + status_code = result.status_code - sid = data["security"]["token"] + if conf.debug_mode: + self.Debug(server=self.get_name(), + debug="Fetched JSON: " + pprint.pformat(data)) - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='API login : ' + self.username + ' / ' + self.password + ' > Token : ' + sid) - self.session.headers.update({'X-Auth-Token': sid}) - return Result(result=sid) + # check if any error occured + errors_occured = self.check_for_error(data, error, status_code) + if errors_occured is not False: + return(errors_occured) - # Password auth - else: - login = self.FetchURL(self.monitor_cgi_url + '/index.php') - if login.error == '' and login.status_code == 200: - # Centreon >= 2.6.6 implement a token - if self.centreon_version >= 2.66 and self.centreon_version <= 19.04: - form = login.result.find('form') - form_inputs = {} - # Need to catch the centreon_token for login to work - for form_input in ('centreon_token', 'submitLogin'): - form_inputs[form_input] = form.find('input', {'name': form_input})['value'] - self.centreon_token = form_inputs['centreon_token'] - form_inputs['useralias'] = self.username - form_inputs['password'] = self.password - # fire up login button with all needed data - raw = self.FetchURL(self.monitor_cgi_url + '/index.php', cgi_data=form_inputs) - else: - login_data = {"useralias" : self.username, "password" : self.password, "submit" : "Login"} - raw = self.FetchURL(self.monitor_cgi_url + "/index.php",cgi_data=login_data, giveback="raw") - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Password login : ' + self.username + ' : ' + self.password) + sid = data["security"]["token"] - sid = self.session.cookies['PHPSESSID'] if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='SID : ' + sid) - if self.centreon_version >= 2.66 and self.centreon_version < 19.04: - self.Debug(server='[' + self.get_name() + ']', debug='Centreon Token : ' + self.centreon_token) - # those broker urls would not be changing too often so this check migth be done here - if self.first_login: - self.get_xml_path(sid) - self.first_login = False + self.Debug(server='[' + self.get_name() + ']', debug='API login : ' + self.username + ' / ' + self.password + ' > Token : ' + sid) + + self.session.headers.update({'X-Auth-Token': sid}) return Result(result=sid) except: @@ -333,6 +190,7 @@ def get_sid(self): result, error = self.Error(sys.exc_info()) return Result(result=result, error=error) + def get_start_end(self, host): ''' get start and end time for downtime from Centreon server @@ -373,6 +231,7 @@ def get_start_end(self, host): self.Error(sys.exc_info()) return 'n/a', 'n/a' + def GetHost(self, host): ''' Centreonified way to get host ip - attribute 'a' in down hosts xml is of no use for up @@ -433,184 +292,74 @@ def GetHost(self, host): # give back host or ip return Result(result=address) - def get_xml_path(self, sid): - ''' - Find out where this instance of Centreon is publishing the status XMLs - Centreon 2.6 + ndo/c.broker - /include/monitoring/status/Hosts/xml/{ndo,broker}/hostXML.php according to configuration - Centreon 2.7 + c.broker - /include/monitoring/status/Hosts/xml/hostXML.php - Centreon 2.8 + c.broker - /include/monitoring/status/Hosts/xml/hostXML.php - regexping HTML for Javascript - ''' - if self.centreon_version <= 2.66: - # 2.6 support NDO and C. Broker, we must check which one is used - cgi_data = {'p':201, 'sid':sid} - result = self.FetchURL(self.monitor_cgi_url + '/main.php', cgi_data=cgi_data, giveback='raw') - raw, error = result.result, result.error - if error == '': - if re.search('var _addrXML.*xml\/ndo\/host', raw): - self.XML_PATH = 'xml/ndo' - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Detected broker : NDO') - elif re.search('var _addrXML.*xml\/broker\/host', raw): - self.XML_PATH = 'xml/broker' - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Detected broker : C. Broker') - else: - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Could not detect the broker for Centeron 2.[3-6]. Using Centreon Broker') - self.XML_PATH = 'xml/broker' - del raw - else: - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Unable to fetch the main page to detect the broker : ' + error) - del result, error - else: - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Only Centreon Broker is supported in Centeon >= 2.7 -> XML_PATH='+ self.XML_PATH) - - def define_url(self): - urls_centreon_2_2 = { - 'main': self.monitor_cgi_url + '/main.php', - 'index': self.monitor_cgi_url + '/index.php', - 'xml_services': self.monitor_cgi_url + '/include/monitoring/status/Services/' + self.XML_PATH + '/serviceXML.php', - 'xml_hosts': self.monitor_cgi_url + '/include/monitoring/status/Hosts/' + self.XML_PATH + '/hostXML.php', - 'xml_meta': self.monitor_cgi_url + '/include/monitoring/status/Meta/' + self.XML_PATH + '/metaServiceXML.php', - 'xml_hostSendCommand': self.monitor_cgi_url + '/include/monitoring/objectDetails/xml/hostSendCommand.php', - 'xml_serviceSendCommand': self.monitor_cgi_url + '/include/monitoring/objectDetails/xml/serviceSendCommand.php', - 'external_cmd_cmdPopup': self.monitor_cgi_url + '/include/monitoring/external_cmd/cmdPopup.php', - # no idea if this really exist in centreon < 2.7 - 'autologoutXMLresponse': self.monitor_cgi_url + '/include/common/javascript/autologoutXMLresponse.php' - } - # inconsistant url in Centreon 2.7 - urls_centreon_2_7 = { - 'main': self.monitor_cgi_url + '/main.php', - 'index': self.monitor_cgi_url + '/index.php', - 'xml_services': self.monitor_cgi_url + '/include/monitoring/status/Services/' + self.XML_PATH + '/serviceXML.php', - 'xml_hosts': self.monitor_cgi_url + '/include/monitoring/status/Hosts/' + self.XML_PATH + '/broker/hostXML.php', - 'xml_meta': self.monitor_cgi_url + '/include/monitoring/status/Meta/' + self.XML_PATH + '/broker/metaServiceXML.php', - 'xml_hostSendCommand': self.monitor_cgi_url + '/include/monitoring/objectDetails/xml/hostSendCommand.php', - 'xml_serviceSendCommand': self.monitor_cgi_url + '/include/monitoring/objectDetails/xml/serviceSendCommand.php', - 'external_cmd_cmdPopup': self.monitor_cgi_url + '/include/monitoring/external_cmd/cmdPopup.php', - 'autologoutXMLresponse': self.monitor_cgi_url + '/include/common/javascript/autologoutXMLresponse.php' - } - - urls_centreon_2_8 = { - 'main': self.monitor_cgi_url + '/main.php', - 'index': self.monitor_cgi_url + '/index.php', - 'xml_services': self.monitor_cgi_url + '/include/monitoring/status/Services/' + self.XML_PATH + '/serviceXML.php', - 'xml_hosts': self.monitor_cgi_url + '/include/monitoring/status/Hosts/' + self.XML_PATH + '/hostXML.php', - 'xml_hostSendCommand': self.monitor_cgi_url + '/include/monitoring/objectDetails/xml/hostSendCommand.php', - 'xml_serviceSendCommand': self.monitor_cgi_url + '/include/monitoring/objectDetails/xml/serviceSendCommand.php', - 'external_cmd_cmdPopup': self.monitor_cgi_url + '/include/monitoring/external_cmd/cmdPopup.php', - 'autologoutXMLresponse': self.monitor_cgi_url + '/include/core/autologout/autologoutXMLresponse.php' - } + def get_host_and_service_id(self, host, service=''): + if service == "": + # Hosts only + if self.centreon_version == 20.04: + # https://demo.centreon.com/centreon/api/latest/monitoring/resources?page=1&limit=30&sort_by={"status_severity_code":"asc","last_status_change":"desc"}&types=["host"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"] + url_hosts = self.urls_centreon['hosts'] + '?types=["host"]&search={"h.name":"' + host + '"}' - urls_centreon_18_10 = { - 'main': self.monitor_cgi_url + '/main.get.php', - # needed to get the frames around the page when opening the monitoring on a host/service - 'main_with_frames': self.monitor_cgi_url + '/main.php', - 'index': self.monitor_cgi_url + '/index.php', - 'xml_services': self.monitor_cgi_url + '/include/monitoring/status/Services/' + self.XML_PATH + '/serviceXML.php', - 'xml_hosts': self.monitor_cgi_url + '/include/monitoring/status/Hosts/' + self.XML_PATH + '/hostXML.php', - 'xml_hostSendCommand': self.monitor_cgi_url + '/include/monitoring/objectDetails/xml/hostSendCommand.php', - 'xml_serviceSendCommand': self.monitor_cgi_url + '/include/monitoring/objectDetails/xml/serviceSendCommand.php', - 'external_cmd_cmdPopup': self.monitor_cgi_url + '/include/monitoring/external_cmd/cmdPopup.php', - 'keepAlive': self.monitor_cgi_url + '/api/internal.php?object=centreon_keepalive&action=keepAlive' - } - - urls_centreon_api_v2 = { - 'login': self.monitor_cgi_url + '/api/' + self.restapi_version + '/login', - 'services': self.monitor_cgi_url + '/api/' + self.restapi_version + '/monitoring/resources', - 'hosts': self.monitor_cgi_url + '/api/' + self.restapi_version + '/monitoring/resources' - } + try: + if self.centreon_version == 20.04: + # Get json + result = self.FetchURL(url_hosts, giveback='raw') - if self.centreon_version < 2.7: - self.urls_centreon = urls_centreon_2_2 - elif self.centreon_version == 2.7: - self.urls_centreon = urls_centreon_2_7 - elif self.centreon_version == 2.8: - self.urls_centreon = urls_centreon_2_8 - # 18.10 and beyond - elif self.centreon_version >= 18.10 and self.use_restapi == False: - self.urls_centreon = urls_centreon_18_10 - elif self.centreon_version >= 18.10 and self.use_restapi == True: - self.urls_centreon = urls_centreon_api_v2 - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='URLs defined for Centreon %s' % (self.centreon_version)) + data = json.loads(result.result) + error = result.error + status_code = result.status_code - def get_host_id(self, host): - ''' - get host_id via parsing raw html - ''' - if self.centreon_version < 2.7: - cgi_data = {'p': 20102, 'o': 'hd', 'host_name': host, 'sid': self.SID} - else: - cgi_data = {'p': 20202, 'o': 'hd', 'host_name': host, 'sid': self.SID} + # check if any error occured + errors_occured = self.check_for_error(data, error, status_code) + if errors_occured is not False: + return(errors_occured) - url = self.urls_centreon['main'] + '?' + urllib.parse.urlencode(cgi_data) + host_id = str(data["result"][0]["id"]) - result = self.FetchURL(url, giveback='raw') - raw, error = result.result, result.error + if conf.debug_mode == True: + self.Debug(server='[' + self.get_name() + ']', debug='Get Host ID : ' + host + " / " + host_id) + return host_id - if error == '': - host_id = raw.partition("var host_id = '")[2].partition("'")[0] - del raw + except: + import traceback + traceback.print_exc(file=sys.stdout) + # set checking flag back to False + self.isChecking = False + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) else: - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Host ID could not be retrieved.') + # Host + Service + if self.centreon_version == 20.04: + url_service = self.urls_centreon['services'] + '?types=["service"]&search={"$and":[{"h.name":{"$eq":"'+host+'"}}, {"s.description":{"$eq":"'+service+'"}}]}' - # some cleanup - del result, error + try: + if self.centreon_version == 20.04: + # Get json + result = self.FetchURL(url_service, giveback='raw') - # only if host_id is an usable integer return it - try: - if int(host_id): - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', host=host, debug='Host ID is ' + host_id) - return host_id - else: - return '' - except: - return '' + data = json.loads(result.result) + error = result.error + status_code = result.status_code + # check if any error occured + errors_occured = self.check_for_error(data, error, status_code) + if errors_occured is not False: + return(errors_occured) - def get_host_and_service_id(self, host, service): - ''' - parse a ton of html to get a host and a service id... - ''' - cgi_data = {'p':'20201',\ - 'host_name':host,\ - 'service_description':service,\ - 'o':'svcd'} - - # This request must be done in a GET, so just encode the parameters and fetch - result = self.FetchURL(self.urls_centreon['main'] + '?' + urllib.parse.urlencode(cgi_data), giveback="raw") - raw, error = result.result, result.error - - if error == '': - host_id = raw.partition("var host_id = '")[2].partition("'")[0] - svc_id = raw.partition("var svc_id = '")[2].partition("'")[0] - del raw - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', host=host, service=service, debug='- Get host/svc ID : ' + host_id + '/' + svc_id) - else: - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', host=host, service=service, debug='- IDs could not be retrieved.') + host_id = str(data["result"][0]["parent"]["id"]) + service_id = str(data["result"][0]["id"]) - # some cleanup - del result, error + if conf.debug_mode == True: + self.Debug(server='[' + self.get_name() + ']', debug='Get Host / Service ID : ' + host_id + " / " + service_id) + return host_id,service_id - # only if host_id is an usable integer return it - try: - if int(host_id) and int(svc_id): - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', host=host, service=service, debug='- Host & Service ID are valid (int)') - return host_id,svc_id - else: - return '','' - except: - return '','' + except: + import traceback + traceback.print_exc(file=sys.stdout) + # set checking flag back to False + self.isChecking = False + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) def _get_status(self): @@ -625,111 +374,19 @@ def _get_status(self): result.error = 'Connection error' return result - # services (unknown, warning or critical?) - if self.centreon_version < 2.7: - nagcgiurl_services = self.urls_centreon['xml_services'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'svcpb', 'sort_type':'status', 'sid':self.SID}) - elif self.centreon_version >= 2.7 and self.use_restapi == False: - nagcgiurl_services = self.urls_centreon['xml_services'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'svcpb', 'p':20201, 'nc':0, 'criticality':0, 'statusService':'svcpb', 'sSetOrderInMemory':1, 'sid':self.SID}) - elif self.centreon_version >= 2.7 and self.use_restapi == True: + # Services URL + if self.centreon_version == 20.04 : # https://demo.centreon.com/centreon/api/latest/monitoring/resources?page=1&limit=30&sort_by={"status_severity_code":"asc","last_status_change":"desc"}&types=["service"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"] - url_services = self.urls_centreon['services'] + '?types=["service"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"]&limit=' + str(self.limit_services_number) - - # hosts (up or down or unreachable) - # define hosts xml URL, because of inconsistant url - if self.centreon_version < 2.7: - nagcgiurl_hosts = self.urls_centreon['xml_hosts'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'hpb', 'sort_type':'status', 'sid':self.SID}) - elif self.centreon_version >= 2.7 and self.centreon_version < 19.04: - nagcgiurl_hosts = self.urls_centreon['xml_hosts'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'hpb', 'p':20202, 'criticality':0, 'statusHost':'hpb', 'sSetOrderInMemory':1, 'sid':self.SID}) - elif self.centreon_version >= 19.04 and self.use_restapi == False: - nagcgiurl_hosts = self.urls_centreon['xml_hosts'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'hpb', 'p':20202, 'criticality':0, 'statusHost':'hpb', 'sSetOrderInMemory':1}) - elif self.centreon_version >= 19.04 and self.use_restapi == True: + url_services = self.urls_centreon['services'] + '?types=["metaservice","service"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"]&limit=' + str(self.limit_services_number) + + # Hosts URL + if self.centreon_version == 20.04: # https://demo.centreon.com/centreon/api/latest/monitoring/resources?page=1&limit=30&sort_by={"status_severity_code":"asc","last_status_change":"desc"}&types=["host"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"] url_hosts = self.urls_centreon['hosts'] + '?types=["host"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"]&limit=' + str(self.limit_services_number) - - # hosts - mostly the down ones - # unfortunately the hosts status page has a different structure so - # hosts must be analyzed separately + # Hosts try: - if self.centreon_version <= 19.04 and self.use_restapi == False: - result = self.FetchURL(nagcgiurl_hosts, giveback='xml') - xmlobj, error, status_code = result.result, result.error, result.status_code - - # check if any error occured - errors_occured = self.check_for_error(xmlobj, error, status_code) - - # if there are errors return them - if errors_occured != False: - return(errors_occured) - - # Check if the result is not empty - if len(xmlobj) == 0: - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Empty host XML result') - return Result(result=None, error="Empty host XML result") - - # in case there are no children session ID is expired - if xmlobj.text.lower() == 'bad session id': - del xmlobj - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Bad session ID, retrieving new one...') - - # try again... - self.SID = self.get_sid().result - result = self.FetchURL(nagcgiurl_hosts, giveback='xml') - xmlobj, error, status_code = result.result, result.error, result.status_code - errors_occured = self.check_for_error(xmlobj, error, status_code) - # if there are errors return them - if errors_occured != False: - return(errors_occured) - - # a second time a bad session id should raise an error - if xmlobj.text.lower() == 'bad session id': - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Even after renewing session ID, unable to get the XML') - return Result(result='ERROR', - error='Bad session ID', - status_code=status_code) - - for l in xmlobj.findAll('l'): - try: - # host objects contain service objects - if not l.hn.text in self.new_hosts: - self.new_hosts[str(l.hn.text)] = GenericHost() - self.new_hosts[str(l.hn.text)].name = str(l.hn.text) - self.new_hosts[str(l.hn.text)].server = self.name - self.new_hosts[str(l.hn.text)].status = str(l.cs.text) - # disgusting workaround for https://github.com/HenriWahl/Nagstamon/issues/91 - if self.new_hosts[str(l.hn.text)].status in self.TRANSLATIONS: - self.new_hosts[str(l.hn.text)].status = self.TRANSLATIONS[self.new_hosts[str(l.hn.text)].status] - self.new_hosts[str(l.hn.text)].attempt, self.new_hosts[str(l.hn.text)].status_type = str(l.tr.text).split(' ') - self.new_hosts[str(l.hn.text)].status_type = self.HARD_SOFT[self.new_hosts[str(l.hn.text)].status_type] - self.new_hosts[str(l.hn.text)].last_check = str(l.lc.text) - self.new_hosts[str(l.hn.text)].duration = str(l.lsc.text) - self.new_hosts[str(l.hn.text)].status_information = str(l.ou.text).replace('\n', ' ').strip() - if l.find('cih') != None: - self.new_hosts[str(l.hn.text)].criticality = str(l.cih.text) - else: - self.new_hosts[str(l.hn.text)].criticality = '' - self.new_hosts[str(l.hn.text)].acknowledged = bool(int(str(l.ha.text))) - self.new_hosts[str(l.hn.text)].scheduled_downtime = bool(int(str(l.hdtm.text))) - if l.find('is') != None: - self.new_hosts[str(l.hn.text)].flapping = bool(int(str(l.find('is').text))) - else: - self.new_hosts[str(l.hn.text)].flapping = False - self.new_hosts[str(l.hn.text)].notifications_disabled = not bool(int(str(l.ne.text))) - self.new_hosts[str(l.hn.text)].passiveonly = not bool(int(str(l.ace.text))) - - except: - import traceback - traceback.print_exc(file=sys.stdout) - # set checking flag back to False - self.isChecking = False - result, error = self.Error(sys.exc_info()) - return Result(result=result, error=error) - - del xmlobj - elif self.centreon_version >= 19.04 and self.use_restapi == True: + if self.centreon_version >= 20.04: # Get json result = self.FetchURL(url_hosts, giveback='raw') @@ -737,9 +394,9 @@ def _get_status(self): error = result.error status_code = result.status_code - if conf.debug_mode: - self.Debug(server=self.get_name(), - debug="Get Hosts status Fetched JSON: " + pprint.pformat(data)) + # if conf.debug_mode: + # self.Debug(server=self.get_name(), + # debug="Get Hosts status Fetched JSON: " + pprint.pformat(data)) # check if any error occured errors_occured = self.check_for_error(data, error, status_code) @@ -747,7 +404,6 @@ def _get_status(self): return(errors_occured) for alerts in data["result"]: - # Attributs à remplir new_host = alerts["name"] self.new_hosts[new_host] = GenericHost() self.new_hosts[new_host].name = alerts["name"] @@ -768,8 +424,7 @@ def _get_status(self): self.new_hosts[new_host].status_type = self.HARD_SOFT['(S)'] else: self.new_hosts[new_host].status_type = self.HARD_SOFT['(H)'] - self.Debug(server='[' + self.get_name() + ']', debug='Host indexed : ' + self.new_hosts[new_host].name) - + self.Debug(server='[' + self.get_name() + ']', debug='Host indexed : ' + new_host) except: import traceback @@ -779,158 +434,9 @@ def _get_status(self): result, error = self.Error(sys.exc_info()) return Result(result=result, error=error) - # services + # Services try: - if self.centreon_version <= 19.04 and self.use_restapi == False: - result = self.FetchURL(nagcgiurl_services, giveback='xml') - xmlobj, error, status_code = result.result, result.error, result.status_code - - # check if any error occured - errors_occured = self.check_for_error(xmlobj, error, status_code) - # if there are errors return them - if errors_occured != False: - return(errors_occured) - - # Check if the result is not empty - if len(xmlobj) == 0: - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Empty service XML result') - return Result(result=None, error="Empty service XML result") - - # in case there are no children session id is invalid - if xmlobj.text.lower() == 'bad session id': - # debug - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Bad session ID, retrieving new one...') - # try again... - self.SID = self.get_sid().result - result = self.FetchURL(nagcgiurl_services, giveback='xml') - xmlobj, error, status_code = result.result, result.error, result.status_code - errors_occured = self.check_for_error(xmlobj, error, status_code) - # if there are errors return them - if errors_occured != False: - return(errors_occured) - - # a second time a bad session id should raise an error - if xmlobj.text.lower() == 'bad session id': - return Result(result='ERROR', - error='Bad session ID', - status_code=status_code) - - # In Centreon 2.8, Meta are merged with regular services - if self.centreon_version < 2.8: - # define meta-services xml URL - if self.centreon_version == 2.7: - nagcgiurl_meta_services = self.urls_centreon['xml_meta'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'meta', 'sort_type':'status', 'sid':self.SID}) - else: - nagcgiurl_meta_services = self.urls_centreon['xml_meta'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'meta', 'sort_type':'status', 'sid':self.SID}) - - # retrive meta-services xml STATUS - result_meta = self.FetchURL(nagcgiurl_meta_services, giveback='xml') - xmlobj_meta, error_meta, status_code_meta = result_meta.result, result_meta.error, result_meta.status_code - - # check if any error occured - errors_occured = self.check_for_error(xmlobj_meta, error_meta, status_code_meta) - - # if there are errors return them - if errors_occured != False: - return(errors_occured) - - # a second time a bad session id should raise an error - if xmlobj_meta.text.lower() == 'bad session id': - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Even after renewing session ID, unable to get the XML') - - return Result(result='ERROR', - error='Bad session ID', - status_code=status_code_meta) - - # INSERT META-services xml at the end of the services xml - try: - xmlobj.append(xmlobj_meta.reponse) - except: - import traceback - traceback.print_exc(file=sys.stdout) - # set checking flag back to False - self.isChecking = False - result, error = self.Error(sys.exc_info()) - return Result(result=result, error=error) - # do some cleanup - del xmlobj_meta - - for l in xmlobj.findAll('l'): - try: - # host objects contain service objects - ###if not self.new_hosts.has_key(str(l.hn.text)): - if not l.hn.text in self.new_hosts: - self.new_hosts[str(l.hn.text)] = GenericHost() - self.new_hosts[str(l.hn.text)].name = str(l.hn.text) - self.new_hosts[str(l.hn.text)].status = 'UP' - # if a service does not exist create its object - if not l.sd.text in self.new_hosts[str(l.hn.text)].services: - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)] = GenericService() - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].host = str(l.hn.text) - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].name = str(l.sd.text) - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].server = self.name - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status = str(l.cs.text) - - if self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].host == '_Module_Meta': - # ajusting service name for Meta services - if self.centreon_version < 2.8: - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].name = '{} ({})'.format(str(l.sd.text), l.rsd.text) - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].attempt = str(l.ca.text) - else: - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].name = '{} ({})'.format(str(l.sdn.text), l.sdl.text) - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].attempt, \ - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_type = str(l.ca.text).split(' ') - else: - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].attempt, \ - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_type = str(l.ca.text).split(' ') - - # disgusting workaround for https://github.com/HenriWahl/Nagstamon/issues/91 - # Still needed in Centreon 2.8 at least : https://github.com/HenriWahl/Nagstamon/issues/344 - # Need enhancement, we can do service state matching with this field <sc>service_unknown</sc> - #if self.centreon_version < 2.66: - if self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status in self.TRANSLATIONS: - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status = self.TRANSLATIONS[\ - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status] - - if not (self.centreon_version < 2.8 and self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].host == '_Module_Meta'): - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_type =\ - self.HARD_SOFT[self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_type] - - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Parsing service XML (Host/Service/Status_type) ' + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].host + '/' + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].name + '/' + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_type) - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].last_check = str(l.lc.text) - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].duration = str(l.d.text) - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_information = str(l.po.text).replace('\n', ' ').strip() - - if l.find('cih') != None: - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].criticality = str(l.cih.text) - else: - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].criticality = '' - - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].acknowledged = bool(int(str(l.pa.text))) - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].notifications_disabled = not bool(int(str(l.ne.text))) - - # for features not available in centreon < 2.8 and meta services - if not (self.centreon_version < 2.8 and self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].host == '_Module_Meta'): - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].scheduled_downtime = bool(int(str(l.dtm.text))) - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].flapping = bool(int(str(l.find('is').text))) - self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].passiveonly = not bool(int(str(l.ac.text))) - - except: - import traceback - traceback.print_exc(file=sys.stdout) - # set checking flag back to False - self.isChecking = False - result, error = self.Error(sys.exc_info()) - return Result(result=result, error=error) - - # do some cleanup - del xmlobj - - elif self.centreon_version >= 19.04 and self.use_restapi == True: + if self.centreon_version >= 20.04: # Get json result = self.FetchURL(url_services, giveback='raw') @@ -938,9 +444,9 @@ def _get_status(self): error = result.error status_code = result.status_code - if conf.debug_mode: - self.Debug(server=self.get_name(), - debug="Get Services status Fetched JSON: " + pprint.pformat(data)) + # if conf.debug_mode: + # self.Debug(server=self.get_name(), + # debug="Get Services status Fetched JSON: " + pprint.pformat(data)) # check if any error occured errors_occured = self.check_for_error(data, error, status_code) @@ -948,7 +454,10 @@ def _get_status(self): return(errors_occured) for alerts in data["result"]: - new_host = alerts["parent"]["name"] + if alerts["type"] == "metaservice": + new_host = "Meta_Services" + else: + new_host = alerts["parent"]["name"] new_service = alerts["name"] # Needed if non-ok services are on a UP host if not new_host in self.new_hosts: @@ -957,12 +466,11 @@ def _get_status(self): self.new_hosts[new_host].status = 'UP' self.new_hosts[new_host].services[new_service] = GenericService() # Attributs à remplir - self.Debug(server='[' + self.get_name() + ']', debug='Service indexed : ' + alerts["parent"]["name"]) - self.Debug(server='[' + self.get_name() + ']', debug='Service status : ' + alerts["status"]["name"]) + self.Debug(server='[' + self.get_name() + ']', debug='Service indexed : ' + new_host + ' / ' + new_service) self.new_hosts[new_host].services[new_service].server = self.name - self.new_hosts[new_host].services[new_service].host = alerts["parent"]["name"] - self.new_hosts[new_host].services[new_service].name = alerts["name"] + self.new_hosts[new_host].services[new_service].host = new_host + self.new_hosts[new_host].services[new_service].name = new_service self.new_hosts[new_host].services[new_service].status = alerts["status"]["name"] self.new_hosts[new_host].services[new_service].last_check = alerts["last_check"] # last_state_change = datetime.strptime(alerts["last_state_change"], '%Y-%m-%dT%H:%M:%S%z').replace(tzinfo=None) @@ -970,7 +478,6 @@ def _get_status(self): self.new_hosts[new_host].services[new_service].duration = alerts["duration"] self.new_hosts[new_host].services[new_service].attempt = alerts["tries"] self.new_hosts[new_host].services[new_service].status_information = alerts["information"] - # Impossible de trouver les 3 suivants dans le json self.new_hosts[new_host].services[new_service].passiveonly = alerts["passive_checks"] self.new_hosts[new_host].services[new_service].notifications_disabled = not alerts["notification_enabled"] self.new_hosts[new_host].services[new_service].flapping = alerts["flapping"] @@ -995,81 +502,72 @@ def _get_status(self): def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=[]): - # decision about host or service - they have different URLs try: + # host if service == '': - # host - cgi_data = {'cmd': '14', - 'host_name': host, - 'author': author, - 'comment': comment, - 'submit': 'Add', - 'notify': int(notify), - 'persistent': int(persistent), - 'sticky': int(sticky), - 'ackhostservice': '0', - 'en': '1'} - if self.centreon_version < 2.7: - cgi_data['p'] = '20105' - cgi_data['o'] = 'hpb' - else: - cgi_data['p'] = '20202' - cgi_data['o'] = 'hpb' - cgi_data['centreon_token'] = self.centreon_token + cgi_data = { + "comment": comment, + "is_notify_contacts": notify, + "is_persistent_comment": persistent, + "is_sticky": sticky, + "with_services": True + } - # Post - raw = self.FetchURL(self.urls_centreon['main'], cgi_data=cgi_data, giveback='raw') - del raw + host_id = self.get_host_and_service_id(host) - # if host is acknowledged and all services should be to or if a service is acknowledged - # (and all other on this host too) + # Post json + json_string = json.dumps(cgi_data) + # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/hosts/{host_id}/acknowledgements + result = self.FetchURL(self.monitor_cgi_url + '/api/' + self.restapi_version + '/monitoring/hosts/' + host_id + '/acknowledgements', cgi_data=json_string, giveback='raw') + + error = result.error + status_code = result.status_code + + if conf.debug_mode: + self.Debug(server='[' + self.get_name() + ']', + debug="Set Ack on Host, status code : " + str(status_code)) + + + # Service if service != '' or len(all_services) > 0: - # service(s) @ host - # if all_services is empty only one service has to be checked - the one clicked - # otherwise if there all services should be acknowledged - if len(all_services) == 0: all_services = [service] + if len(all_services) == 0: + all_services = [service] + + acknowledgements_list=[] - # acknowledge all services on a host for s in all_services: - cgi_data = {'cmd': '15', - 'host_name': host, - 'author': author, - 'comment': comment, - 'submit': 'Add', - 'notify': int(notify), - 'service_description': s, - 'force_check': '1', - 'persistent': int(persistent), - # following not needed in 18.10, required in wich version ? - 'persistant': int(persistent), - 'sticky': int(sticky), - 'o': 'svcd', - 'en': '1'} - if self.centreon_version < 2.7: - cgi_data['p'] = '20215' - else: - cgi_data['p'] = '20201' - cgi_data['centreon_token'] = self.centreon_token - - # in case of a meta-service, extract the 'rsd' field from the service name : - if host == '_Module_Meta': - m = re.search(r'^.+ \((?P<rsd>.+)\)$', s) - if m: - rsd = m.group('rsd') - if self.centreon_version < 2.8: - cgi_data = {'p': '20206', - 'o': 'meta', - 'cmd': '70', - 'select[' + host + ';' + rsd + ']': '1', - 'limit': '0'} - elif self.centreon_version in [2.8, 18.10]: - cgi_data['service_description'] = rsd - - # POST, for some strange reason only working if giveback is 'raw' - raw = self.FetchURL(self.urls_centreon['main'], cgi_data=cgi_data, giveback='raw') - del raw + host_id, service_id = self.get_host_and_service_id(host, service) + + ack = { + "comment": comment, + "is_notify_contacts": notify, + "is_persistent_comment": persistent, + "is_sticky": sticky, + "resource_id": service_id, + "parent_resource_id": host_id + } + + acknowledgements_list.append(ack) + + # Post json + json_string = json.dumps(acknowledgements_list) + # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/services/acknowledgements + result = self.FetchURL(self.monitor_cgi_url + '/api/' + self.restapi_version + '/monitoring/services/acknowledgements', cgi_data=json_string, giveback='raw') + + error = result.error + status_code = result.status_code + + if conf.debug_mode: + self.Debug(server='[' + self.get_name() + ']', + debug="Set Ack on Host ("+host+") / Service ("+service+"), status code : " + str(status_code)) + except: - self.Error(sys.exc_info()) + import traceback + traceback.print_exc(file=sys.stdout) + # set checking flag back to False + self.isChecking = False + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) def _set_recheck(self, host, service): @@ -1092,7 +590,7 @@ def _set_recheck(self, host, service): elif service == '': # ... it can only be a host, so check all his services and there is a command for that - host_id = self.get_host_id(host) + host_id = self.get_host_and_service_id(host) if self.centreon_version < 2.7: url = self.urls_centreon['xml_hostSendCommand'] + '?' + urllib.parse.urlencode({'cmd':'host_schedule_check', 'actiontype':1,'host_id':host_id,'sid':self.SID}) @@ -1232,77 +730,52 @@ def check_session(self): if 'url_centreon' not in self.__dict__: self.init_config() try: - if self.use_restapi == True: + if conf.debug_mode == True: + self.Debug(server='[' + self.get_name() + ']', debug='The token will be deleted if it has not been used for more than one hour. Current Token = ' + self.SID ) + + cgi_data = {'limit':'0'} + self.session = requests.Session() + self.session.headers['Content-Type'] = 'application/json' + self.session.headers['X-Auth-Token'] = self.SID + + # Get en empty service list, to check the status of the current token + # This request must be done in a GET, so just encode the parameters and fetch + result = self.FetchURL(self.urls_centreon['services'] + '?' + urllib.parse.urlencode(cgi_data), giveback="raw") + + # Get json + # ~ result = self.FetchURL(self.urls_centreon['services'] + '?' + urllib.parse.urlencode({ 'limit':0 }), giveback='raw') + + # ~ self.session = requests.Session() + # ~ self.session.headers['Content-Type'] = 'application/json' + # ~ self.session.headers['X-Auth-Token'] = self.SID + # ~ url = 'https://demo.centreon.com/centreon/api/latest/monitoring/services?limit=0' + # ~ response = self.session.get(url, timeout=5) + # ~ data = json.loads(response.text) + # ~ error = response.reason + # ~ status_code = response.status_code + + print(self.session.headers) + + data = json.loads(result.result) + error = result.error + status_code = result.status_code + + if conf.debug_mode: + self.Debug(server=self.get_name(), + debug="Check-session, Fetched JSON: " + pprint.pformat(data)) + self.Debug(server=self.get_name(), + debug="Check-session, Error : " + error + " Status code : " + str(status_code)) + + # check if any error occured + errors_occured = self.check_for_error(data, error, status_code) + if errors_occured is not False: + return(errors_occured) + + # If we got nothing, the token expired and must be renewed + if not data["result"]: + self.SID = self.get_sid().result if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='The token will be deleted if it has not been used for more than one hour. Current Token = ' + self.SID ) - - - cgi_data = {'limit':'0'} - self.session = requests.Session() - self.session.headers['Content-Type'] = 'application/json' - self.session.headers['X-Auth-Token'] = self.SID - # This request must be done in a GET, so just encode the parameters and fetch - result = self.FetchURL(self.urls_centreon['services'] + '?' + urllib.parse.urlencode(cgi_data), giveback="raw") - - # Get json - # ~ result = self.FetchURL(self.urls_centreon['services'] + '?' + urllib.parse.urlencode({ 'limit':0 }), giveback='raw') - - - # ~ self.session = requests.Session() - # ~ self.session.headers['Content-Type'] = 'application/json' - # ~ self.session.headers['X-Auth-Token'] = self.SID - # ~ url = 'https://demo.centreon.com/centreon/api/latest/monitoring/services?limit=0' - # ~ response = self.session.get(url, timeout=5) - # ~ data = json.loads(response.text) - # ~ error = response.reason - # ~ status_code = response.status_code - - - print(self.session.headers) - - data = json.loads(result.result) - error = result.error - status_code = result.status_code - - if conf.debug_mode: - self.Debug(server=self.get_name(), - debug="Check-session Fetched JSON: " + pprint.pformat(data)) - self.Debug(server=self.get_name(), - debug="Error : " + error + " Status code : " + str(status_code)) - - # check if any error occured - errors_occured = self.check_for_error(data, error, status_code) - if errors_occured is not False: - return(errors_occured) - - if not data["result"]: - self.SID = self.get_sid().result - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Session renewed') - - else: - if self.centreon_version >= 18.10: - result = self.FetchURL(self.urls_centreon['keepAlive'], giveback='raw') - self.raw, self.error, self.status_code = result.result, result.error, result.status_code - # Return 200 & null a session is open - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Session status : ' + self.raw + ', http code : ' + str(self.status_code)) - # 401 if no valid session is present - if self.status_code == 401: - self.SID = self.get_sid().result - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Session renewed') - - else: - result = self.FetchURL(self.urls_centreon['autologoutXMLresponse'], giveback='xml') - xmlobj, error, status_code = result.result, result.error, result.status_code - self.session_state = xmlobj.find("state").text.lower() - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Session status : ' + self.session_state) - if self.session_state == "nok": - self.SID = self.get_sid().result - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Session renewed') + self.Debug(server='[' + self.get_name() + ']', debug='Session renewed') except: import traceback From 385e3b02f6586404293550a9f9352c73cebbf94f Mon Sep 17 00:00:00 2001 From: Benoit Poulet <benoit.poulet@businessdecision.com> Date: Fri, 27 May 2022 17:43:47 +0200 Subject: [PATCH 280/884] Recheck OK --- Nagstamon/Servers/Centreon.py | 84 ++++++++++++++++------------------- 1 file changed, 38 insertions(+), 46 deletions(-) diff --git a/Nagstamon/Servers/Centreon.py b/Nagstamon/Servers/Centreon.py index dd277f7cc..88561c4bc 100644 --- a/Nagstamon/Servers/Centreon.py +++ b/Nagstamon/Servers/Centreon.py @@ -571,63 +571,55 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi def _set_recheck(self, host, service): - ''' - host and service ids are needed to tell Centreon what whe want - ''' try: - # decision about host or service - they have different URLs - # Meta - if host == '_Module_Meta': - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Recheck on a Meta service, more work to be done') - m = re.search(r'^.+ \((?P<rsd>.+)\)$', service) - if m: - rsd = m.group('rsd') - if self.centreon_version < 2.8: - url = self.urls_centreon['main'] + '?' + urllib.parse.urlencode({'p': '20206','o': 'meta','cmd': '3','select[' + host + ';' + rsd + ']': '1','limit':'0'}) - else: - url = self.urls_centreon['main'] + '?' + urllib.parse.urlencode({'p': '202','o': 'svc','cmd': '3','select[' + host + ';' + rsd + ']': '1','limit':'1','centreon_token':self.centreon_token}) + # Host + if service == '': + cgi_data = { + "is_forced": True + } - elif service == '': - # ... it can only be a host, so check all his services and there is a command for that host_id = self.get_host_and_service_id(host) - if self.centreon_version < 2.7: - url = self.urls_centreon['xml_hostSendCommand'] + '?' + urllib.parse.urlencode({'cmd':'host_schedule_check', 'actiontype':1,'host_id':host_id,'sid':self.SID}) - else: - url = self.urls_centreon['xml_hostSendCommand'] + '?' + urllib.parse.urlencode({'cmd':'host_schedule_check', 'actiontype':1,'host_id':host_id}) - del host_id + # Post json + json_string = json.dumps(cgi_data) + # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/hosts/{host_id}/check + result = self.FetchURL(self.monitor_cgi_url + '/api/' + self.restapi_version + '/monitoring/hosts/' + host_id + '/check', cgi_data=json_string, giveback='raw') + error = result.error + status_code = result.status_code + + if conf.debug_mode: + self.Debug(server='[' + self.get_name() + ']', + debug="Recheck on Host : "+host+", status code : " + str(status_code)) + + # Service else: - # service @ host + cgi_data = { + "is_forced": True + } + host_id, service_id = self.get_host_and_service_id(host, service) - # Starting from 19.04 this must be in POST - if self.centreon_version < 19.04: - # fill and encode URL data - cgi_data = urllib.parse.urlencode({'cmd':'service_schedule_check', 'actiontype':1,\ - 'host_id':host_id, 'service_id':service_id, 'sid':self.SID}) + # Post json + json_string = json.dumps(cgi_data) + # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/hosts/{host_id}/services/{service_id}/check + result = self.FetchURL(self.monitor_cgi_url + '/api/' + self.restapi_version + '/monitoring/hosts/' + host_id + '/services/' + service_id + '/check', cgi_data=json_string, giveback='raw') + + error = result.error + status_code = result.status_code + + if conf.debug_mode: + self.Debug(server='[' + self.get_name() + ']', + debug="Recheck on Host ("+host+") / Service ("+service+"), status code : " + str(status_code)) - url = self.urls_centreon['xml_serviceSendCommand'] + '?' + cgi_data - del host_id, service_id - else: - cgi_data = {'cmd': 'service_schedule_check', - 'host_id': host_id, - 'service_id': service_id, - 'actiontype': '0'} - del host_id, service_id - if self.centreon_version < 19.04: - # execute GET request - raw = self.FetchURL(url, giveback='raw') - del raw - else: - # running remote cgi command with POST method, for some strange reason only working if - # giveback is 'raw' - raw = self.FetchURL(self.urls_centreon['xml_serviceSendCommand'], cgi_data=cgi_data, giveback='raw') - del raw except: - self.Error(sys.exc_info()) + import traceback + traceback.print_exc(file=sys.stdout) + # set checking flag back to False + self.isChecking = False + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) def _set_downtime(self, host, service, author, comment, fixed, start_time, end_time, hours, minutes): From da806542fd919d77688771b26fceeb6a6b36786b Mon Sep 17 00:00:00 2001 From: Benoit Poulet <benoit.poulet@businessdecision.com> Date: Mon, 30 May 2022 10:34:22 +0200 Subject: [PATCH 281/884] Downtimes OK --- Nagstamon/Servers/Centreon.py | 163 ++++++++++++++++------------------ 1 file changed, 76 insertions(+), 87 deletions(-) diff --git a/Nagstamon/Servers/Centreon.py b/Nagstamon/Servers/Centreon.py index 88561c4bc..5c21d49e4 100644 --- a/Nagstamon/Servers/Centreon.py +++ b/Nagstamon/Servers/Centreon.py @@ -177,10 +177,13 @@ def get_sid(self): return(errors_occured) sid = data["security"]["token"] + # ID of the user is needed by some requests + user_id = data["contact"]["id"] if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='API login : ' + self.username + ' / ' + self.password + ' > Token : ' + sid) + self.Debug(server='[' + self.get_name() + ']', debug='API login : ' + self.username + ' / ' + self.password + ' > Token : ' + sid + ' > User ID : ' + str(user_id)) + self.user_id = user_id self.session.headers.update({'X-Auth-Token': sid}) return Result(result=sid) @@ -623,97 +626,83 @@ def _set_recheck(self, host, service): def _set_downtime(self, host, service, author, comment, fixed, start_time, end_time, hours, minutes): - ''' - gets actual host and service ids and apply them to downtime cgi - ''' - try: - # duration unit is minute - duration = (hours * 60) + minutes - # need cmdPopup.php needs boolean - if fixed == 1: - fixed = 'true' - else: - fixed = 'false' + obj_start_time = datetime.strptime(start_time, '%m/%d/%Y %H:%M') + obj_end_time = datetime.strptime(end_time, '%m/%d/%Y %H:%M') - # Host downtime + # Nagstamon don’t provide the TZ, we need to get it from the OS + obj_start_time = obj_start_time.replace(tzinfo=datetime.now().astimezone().tzinfo) + obj_end_time = obj_end_time.replace(tzinfo=datetime.now().astimezone().tzinfo) + + # duration unit is second + duration = (hours * 3600) + (minutes * 60) + + # API require boolean + if fixed == 1: + fixed = True + else: + fixed = False + + try: if service == '': - if self.centreon_version < 19.04: - cgi_data = {'cmd':75,\ - 'duration':duration,\ - 'duration_scale':'m',\ - 'start':start_time,\ - 'end':end_time,\ - 'comment':comment,\ - 'fixed':fixed,\ - 'downtimehostservice':'true',\ - 'author':author,\ - 'sid':self.SID,\ - 'select['+host+']':1 - } - # Params has changed starting from 19.04 - else: - cgi_data = {'cmd':75, - 'duration':duration, - 'duration_scale':'m', - 'comment':comment, - 'start':start_time, - 'end':end_time, - 'host_or_centreon_time':0, - 'fixed':fixed, - 'downtimehostservice':'true', - 'author':author, - 'resources':'["'+host+'"]' - } - - # Service downtime - else: - # Centreon 2.8 only, in case of a meta-service, extract the 'rsd' field from the service name : - if host == '_Module_Meta' and self.centreon_version in [2.8, 18.10]: - m = re.search(r'^.+ \((?P<rsd>.+)\)$', service) - if m: - rsd = m.group('rsd') - service = rsd - if self.centreon_version < 19.04: - cgi_data = {'cmd':74,\ - 'duration':duration,\ - 'duration_scale':'m',\ - 'start':start_time,\ - 'end':end_time,\ - 'comment':comment,\ - 'fixed':fixed,\ - 'downtimehostservice':0,\ - 'author':author,\ - 'sid':self.SID,\ - 'select['+host+';'+service+']':1 - } - - # Params has changed starting from 19.04 - else: - cgi_data = {'cmd':74, - 'duration':duration, - 'duration_scale':'m', - 'comment':comment, - 'start':start_time, - 'end':end_time, - 'host_or_centreon_time':0, - 'fixed':fixed, - 'downtimehostservice':0, - 'author':author, - 'resources':'["'+host+'%3B'+service+'"]' - } - - if self.centreon_version < 19.04: - # This request must be done in a GET, so just encode the parameters and fetch - raw = self.FetchURL(self.urls_centreon['external_cmd_cmdPopup'] + '?' + urllib.parse.urlencode(cgi_data), giveback="raw") - del raw - # Starting from 19.04, must be POST + # Host + cgi_data = { + "start_time": obj_start_time.strftime('%Y-%m-%dT%H:%M:%S%z'), + "end_time": obj_end_time.strftime('%Y-%m-%dT%H:%M:%S%z'), + "is_fixed": fixed, + "duration": duration, + "author_id": self.user_id, + "comment": comment, + "with_services": True + } + + host_id = self.get_host_and_service_id(host) + + # Post json + json_string = json.dumps(cgi_data) + # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/hosts/{host_id}/downtimes + result = self.FetchURL(self.monitor_cgi_url + '/api/' + self.restapi_version + '/monitoring/hosts/' + host_id + '/downtimes', cgi_data=json_string, giveback='raw') + + error = result.error + status_code = result.status_code + + if conf.debug_mode: + self.Debug(server='[' + self.get_name() + ']', + debug="Downtime on Host : "+host+", status code : " + str(status_code)) + + # Service else: - # Do it in POST - raw = self.FetchURL(self.urls_centreon['external_cmd_cmdPopup'], cgi_data=cgi_data, giveback='raw') - del raw + cgi_data = { + "start_time": obj_start_time.strftime('%Y-%m-%dT%H:%M:%S%z'), + "end_time": obj_end_time.strftime('%Y-%m-%dT%H:%M:%S%z'), + "is_fixed": fixed, + "duration": duration, + "author_id": self.user_id, + "comment": comment + } + + host_id, service_id = self.get_host_and_service_id(host, service) + + # Post json + json_string = json.dumps(cgi_data) + # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/hosts/{host_id}/services/{service_id}/downtimes + result = self.FetchURL(self.monitor_cgi_url + '/api/' + self.restapi_version + '/monitoring/hosts/' + host_id + '/services/' + service_id + '/downtimes', cgi_data=json_string, giveback='raw') + + + error = result.error + status_code = result.status_code + + if conf.debug_mode: + self.Debug(server='[' + self.get_name() + ']', + debug="Downtime on Host ("+host+") / Service ("+service+"), status code : " + str(status_code)) + except: - self.Error(sys.exc_info()) + import traceback + traceback.print_exc(file=sys.stdout) + # set checking flag back to False + self.isChecking = False + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) def check_session(self): From 5b70024ed08af688749dca4f688a4f134eea27e1 Mon Sep 17 00:00:00 2001 From: Benoit Poulet <benoit.poulet@businessdecision.com> Date: Mon, 30 May 2022 12:31:54 +0200 Subject: [PATCH 282/884] GetHost OK + cleanup --- Nagstamon/Servers/Centreon.py | 203 ++++++++-------------------------- 1 file changed, 45 insertions(+), 158 deletions(-) diff --git a/Nagstamon/Servers/Centreon.py b/Nagstamon/Servers/Centreon.py index 5c21d49e4..666ffda98 100644 --- a/Nagstamon/Servers/Centreon.py +++ b/Nagstamon/Servers/Centreon.py @@ -40,35 +40,20 @@ class CentreonServer(GenericServer): TYPE = 'Centreon' - # centreon generic web interface uses a sid which is needed to ask for news - SID = None + # Centreon API uses a token + token = None # HARD/SOFT state mapping HARD_SOFT = {'(H)': 'hard', '(S)': 'soft'} - # apparently necessesary because of non-english states as in https://github.com/HenriWahl/Nagstamon/issues/91 (Centeron 2.5) - TRANSLATIONS = {'INDISPONIBLE': 'DOWN', - 'INJOIGNABLE': 'UNREACHABLE', - 'CRITIQUE': 'CRITICAL', - 'INCONNU': 'UNKNOWN', - 'ALERTE': 'WARNING'} - # Entries for monitor default actions in context menu MENU_ACTIONS = ['Monitor', 'Recheck', 'Acknowledge', 'Downtime'] - # Centreon works better or at all with html.parser for BeautifulSoup - PARSER = 'html.parser' - - # Token that centreon use to protect the system - centreon_token = None # URLs of the Centreon pages urls_centreon = None - # To only detect broker once - first_login = True + # limit number of services retrived limit_services_number = 9999 - # default value, applies to version 2.2 and others - XML_PATH = 'xml' def init_config(self): ''' @@ -104,24 +89,10 @@ def init_config(self): def init_HTTP(self): - """ - initialize HTTP connection - """ if self.session is None: GenericServer.init_HTTP(self) self.session.headers.update({'Content-Type': 'application/json'}) - - if self.first_login: - self.SID = self.get_sid().result - self.first_login = False - - - def reset_HTTP(self): - ''' - Centreon needs deletion of SID - ''' - self.SID = None - self.SID = self.get_sid().result + self.token = self.get_token().result def define_url(self): @@ -147,7 +118,7 @@ def open_monitor(self, host, service=''): webbrowser_open(self.urls_centreon['main'] + auth ) - def get_sid(self): + def get_token(self): try: cgi_data = { "security": { @@ -176,16 +147,16 @@ def get_sid(self): if errors_occured is not False: return(errors_occured) - sid = data["security"]["token"] + token = data["security"]["token"] # ID of the user is needed by some requests user_id = data["contact"]["id"] if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='API login : ' + self.username + ' / ' + self.password + ' > Token : ' + sid + ' > User ID : ' + str(user_id)) + self.Debug(server='[' + self.get_name() + ']', debug='API login : ' + self.username + ' / ' + self.password + ' > Token : ' + token + ' > User ID : ' + str(user_id)) self.user_id = user_id - self.session.headers.update({'X-Auth-Token': sid}) - return Result(result=sid) + self.session.headers.update({'X-Auth-Token': token}) + return Result(result=token) except: import traceback @@ -194,106 +165,40 @@ def get_sid(self): return Result(result=result, error=error) - def get_start_end(self, host): - ''' - get start and end time for downtime from Centreon server - ''' - try: - # It's not possible since 18.10 to get date from the webinterface - # because it's set in javascript - if self.centreon_version < 18.10: - cgi_data = {'o':'ah',\ - 'host_name':host} - if self.centreon_version < 2.7: - cgi_data['p'] = '20106' - elif self.centreon_version == 2.7: - cgi_data['p'] = '210' - elif self.centreon_version == 2.8: - cgi_data['o'] = 'a' - cgi_data['p'] = '210' - result = self.FetchURL(self.urls_centreon['main'], cgi_data = cgi_data, giveback='obj') - - html, error = result.result, result.error - if error == '': - start_date = html.find(attrs={'name':'start'}).attrs['value'] - start_hour = html.find(attrs={'name':'start_time'}).attrs['value'] - start_time = start_date + ' ' + start_hour - - end_date = html.find(attrs={'name':'end'}).attrs['value'] - end_hour = html.find(attrs={'name':'end_time'}).attrs['value'] - end_time = end_date + ' ' + end_hour - return start_time, end_time - - else: - start_time = datetime.now().strftime("%m/%d/%Y %H:%M") - end_time = datetime.now() + timedelta(hours=2) - end_time = end_time.strftime("%m/%d/%Y %H:%M") - return start_time, end_time + def GetHost(self, host): + if self.centreon_version == 20.04: + # https://demo.centreon.com/centreon/api/latest/monitoring/resources?page=1&limit=30&sort_by={"status_severity_code":"asc","last_status_change":"desc"}&types=["host"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"] + url_hosts = self.urls_centreon['hosts'] + '?types=["host"]&search={"h.name":"' + host + '"}' - except: - self.Error(sys.exc_info()) - return 'n/a', 'n/a' + try: + if self.centreon_version == 20.04: + # Get json + result = self.FetchURL(url_hosts, giveback='raw') + data = json.loads(result.result) + error = result.error + status_code = result.status_code - def GetHost(self, host): - ''' - Centreonified way to get host ip - attribute 'a' in down hosts xml is of no use for up - hosts so we need to get ip anyway from web page - ''' - # the fastest method is taking hostname as used in monitor - if conf.connect_by_host == True or host == '': - return Result(result=host) - - # do a web interface search limited to only one result - the hostname - cgi_data = {'sid': self.SID, - 'search': host, - 'num': 0, - 'limit': 1, - 'sort_type':'hostname', - 'order': 'ASC', - 'date_time_format_status': 'd/m/Y H:i:s', - 'o': 'h', - 'p': 20102, - 'time': 0} - - centreon_hosts = self.urls_centreon['xml_hosts'] + '?' + urllib.parse.urlencode(cgi_data) - - result = self.FetchURL(centreon_hosts, giveback='xml') - xmlobj, error, status_code = result.result, result.error, result.status_code - - # initialize ip string - ip = '' - - if len(xmlobj) != 0: - ip = str(xmlobj.l.a.text) - # when connection by DNS is not configured do it by IP - try: - if conf.connect_by_dns == True: - # try to get DNS name for ip (reverse DNS), if not available use ip - try: - address = socket.gethostbyaddr(ip)[0] - except: - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Unable to do a reverse DNS lookup on IP: ' + ip) - address = ip - else: - address = ip - except: - result, error = self.Error(sys.exc_info()) - return Result(result=result, error=error) + # check if any error occured + errors_occured = self.check_for_error(data, error, status_code) + if errors_occured is not False: + return(errors_occured) - else: - result, error = self.Error(sys.exc_info()) - return Result(error=error) + fqdn = str(data["result"][0]["fqdn"]) - del xmlobj + if conf.debug_mode == True: + self.Debug(server='[' + self.get_name() + ']', debug='Get Host FQDN or address : ' + host + " / " + fqdn) - # print IP in debug mode - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='IP of %s:' % (host) + ' ' + address) + # Give back host or ip + return Result(result=fqdn) - # give back host or ip - return Result(result=address) + except: + import traceback + traceback.print_exc(file=sys.stdout) + # set checking flag back to False + self.isChecking = False + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) def get_host_and_service_id(self, host, service=''): @@ -708,35 +613,22 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t def check_session(self): if conf.debug_mode == True: self.Debug(server='[' + self.get_name() + ']', debug='Checking session status') - if 'url_centreon' not in self.__dict__: - self.init_config() + # Not needed anymore as URLs are set at start + # if 'url_centreon' not in self.__dict__: + # self.init_config() try: if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='The token will be deleted if it has not been used for more than one hour. Current Token = ' + self.SID ) + self.Debug(server='[' + self.get_name() + ']', debug='Check-session, the token will be deleted if it has not been used for more than one hour. Current Token = ' + self.token ) cgi_data = {'limit':'0'} self.session = requests.Session() self.session.headers['Content-Type'] = 'application/json' - self.session.headers['X-Auth-Token'] = self.SID + self.session.headers['X-Auth-Token'] = self.token # Get en empty service list, to check the status of the current token # This request must be done in a GET, so just encode the parameters and fetch result = self.FetchURL(self.urls_centreon['services'] + '?' + urllib.parse.urlencode(cgi_data), giveback="raw") - # Get json - # ~ result = self.FetchURL(self.urls_centreon['services'] + '?' + urllib.parse.urlencode({ 'limit':0 }), giveback='raw') - - # ~ self.session = requests.Session() - # ~ self.session.headers['Content-Type'] = 'application/json' - # ~ self.session.headers['X-Auth-Token'] = self.SID - # ~ url = 'https://demo.centreon.com/centreon/api/latest/monitoring/services?limit=0' - # ~ response = self.session.get(url, timeout=5) - # ~ data = json.loads(response.text) - # ~ error = response.reason - # ~ status_code = response.status_code - - print(self.session.headers) - data = json.loads(result.result) error = result.error status_code = result.status_code @@ -747,16 +639,11 @@ def check_session(self): self.Debug(server=self.get_name(), debug="Check-session, Error : " + error + " Status code : " + str(status_code)) - # check if any error occured - errors_occured = self.check_for_error(data, error, status_code) - if errors_occured is not False: - return(errors_occured) - - # If we got nothing, the token expired and must be renewed - if not data["result"]: - self.SID = self.get_sid().result + # If we got an 401, the token expired and must be renewed + if status_code == 401: + self.token = self.get_token().result if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Session renewed') + self.Debug(server='[' + self.get_name() + ']', debug='Check-session, session renewed') except: import traceback From 7b2792965ec2916a0c78e1bb94bed95227af104d Mon Sep 17 00:00:00 2001 From: Benoit Poulet <benoit.poulet@businessdecision.com> Date: Tue, 31 May 2022 11:27:07 +0200 Subject: [PATCH 283/884] rework to use "ressources" endpoint --- Nagstamon/Servers/Centreon.py | 520 ++++++++++++++++++++-------------- 1 file changed, 301 insertions(+), 219 deletions(-) diff --git a/Nagstamon/Servers/Centreon.py b/Nagstamon/Servers/Centreon.py index 666ffda98..14bf2031b 100644 --- a/Nagstamon/Servers/Centreon.py +++ b/Nagstamon/Servers/Centreon.py @@ -36,6 +36,11 @@ from Nagstamon.Config import conf from Nagstamon.Helpers import webbrowser_open +# This class support Centreon V2 API + +# Things to do : +# - change the way host/services status is gathered, must change it +# to use 'ressources' class CentreonServer(GenericServer): TYPE = 'Centreon' @@ -57,13 +62,14 @@ class CentreonServer(GenericServer): def init_config(self): ''' - dummy init_config, called at thread start, not really needed here, just omit extra properties + init_config, called at thread start, not really needed here, just omit extra properties ''' - # FIX but be provided by user + # FIX but be provided by user as their is no way to detect it self.user_provided_centreon_version = "20.04" if re.search('2(0|1|2)\.(04|10)', self.user_provided_centreon_version): + # from 20.04 to 22.04 the API to use is «latest» self.centreon_version = 20.04 if conf.debug_mode is True: self.Debug(server='[' + self.get_name() + ']', debug='Centreon version selected : 20.04 <=> 22.04') @@ -79,11 +85,7 @@ def init_config(self): if conf.debug_mode is True: self.Debug(server='[' + self.get_name() + ']', debug='No Centreon version provided') - # set URLs here already - # self.init_HTTP() - # Changed this because define_url was called 2 times - #if not self.tls_error and self.centreon_version is not None: if not self.tls_error and self.urls_centreon is None: self.define_url() @@ -97,14 +99,13 @@ def init_HTTP(self): def define_url(self): urls_centreon_api_v2 = { - 'main': self.monitor_cgi_url + '/monitoring/resources', + 'resources': self.monitor_cgi_url + '/api/' + self.restapi_version + '/monitoring/resources', 'login': self.monitor_cgi_url + '/api/' + self.restapi_version + '/login', 'services': self.monitor_cgi_url + '/api/' + self.restapi_version + '/monitoring/resources', 'hosts': self.monitor_cgi_url + '/api/' + self.restapi_version + '/monitoring/resources' } - if self.centreon_version == 20.04: - self.urls_centreon = urls_centreon_api_v2 + self.urls_centreon = urls_centreon_api_v2 if conf.debug_mode == True: self.Debug(server='[' + self.get_name() + ']', debug='URLs defined for Centreon %s' % (self.centreon_version)) @@ -115,7 +116,7 @@ def open_monitor(self, host, service=''): else: auth = '' - webbrowser_open(self.urls_centreon['main'] + auth ) + webbrowser_open(self.urls_centreon['resources'] + auth ) def get_token(self): @@ -131,7 +132,7 @@ def get_token(self): # Post json json_string = json.dumps(cgi_data) - result = self.FetchURL(self.monitor_cgi_url + '/api/' + self.restapi_version + '/login', cgi_data=json_string, giveback='raw') + result = self.FetchURL(self.urls_centreon['login'], cgi_data=json_string, giveback='raw') data = json.loads(result.result) error = result.error @@ -166,31 +167,29 @@ def get_token(self): def GetHost(self, host): - if self.centreon_version == 20.04: - # https://demo.centreon.com/centreon/api/latest/monitoring/resources?page=1&limit=30&sort_by={"status_severity_code":"asc","last_status_change":"desc"}&types=["host"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"] - url_hosts = self.urls_centreon['hosts'] + '?types=["host"]&search={"h.name":"' + host + '"}' + # https://demo.centreon.com/centreon/api/latest/monitoring/resources?page=1&limit=30&sort_by={"status_severity_code":"asc","last_status_change":"desc"}&types=["host"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"] + url_hosts = self.urls_centreon['hosts'] + '?types=["host"]&search={"h.name":"' + host + '"}' try: - if self.centreon_version == 20.04: - # Get json - result = self.FetchURL(url_hosts, giveback='raw') + # Get json + result = self.FetchURL(url_hosts, giveback='raw') - data = json.loads(result.result) - error = result.error - status_code = result.status_code + data = json.loads(result.result) + error = result.error + status_code = result.status_code - # check if any error occured - errors_occured = self.check_for_error(data, error, status_code) - if errors_occured is not False: - return(errors_occured) + # check if any error occured + errors_occured = self.check_for_error(data, error, status_code) + if errors_occured is not False: + return(errors_occured) - fqdn = str(data["result"][0]["fqdn"]) + fqdn = str(data["result"][0]["fqdn"]) - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Get Host FQDN or address : ' + host + " / " + fqdn) + if conf.debug_mode == True: + self.Debug(server='[' + self.get_name() + ']', debug='Get Host FQDN or address : ' + host + " / " + fqdn) - # Give back host or ip - return Result(result=fqdn) + # Give back host or ip + return Result(result=fqdn) except: import traceback @@ -204,32 +203,36 @@ def GetHost(self, host): def get_host_and_service_id(self, host, service=''): if service == "": # Hosts only - if self.centreon_version == 20.04: - # https://demo.centreon.com/centreon/api/latest/monitoring/resources?page=1&limit=30&sort_by={"status_severity_code":"asc","last_status_change":"desc"}&types=["host"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"] - url_hosts = self.urls_centreon['hosts'] + '?types=["host"]&search={"h.name":"' + host + '"}' + # https://demo.centreon.com/centreon/api/latest/monitoring/resources?page=1&limit=30&sort_by={"status_severity_code":"asc","last_status_change":"desc"}&types=["host"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"] + url_hosts = self.urls_centreon['hosts'] + '?types=["host"]&search={"h.name":"' + host + '"}' try: - if self.centreon_version == 20.04: - # Get json - result = self.FetchURL(url_hosts, giveback='raw') + # Get json + result = self.FetchURL(url_hosts, giveback='raw') - data = json.loads(result.result) - error = result.error - status_code = result.status_code + data = json.loads(result.result) + error = result.error + status_code = result.status_code - # check if any error occured - errors_occured = self.check_for_error(data, error, status_code) - if errors_occured is not False: - return(errors_occured) + # check if any error occured + errors_occured = self.check_for_error(data, error, status_code) + if errors_occured is not False: + return(errors_occured) - host_id = str(data["result"][0]["id"]) + host_id = data["result"][0]["id"] - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Get Host ID : ' + host + " / " + host_id) - return host_id + if conf.debug_mode == True: + self.Debug(server='[' + self.get_name() + ']', debug='Get Host ID : ' + host + " / " + str(host_id)) + return host_id except: import traceback + + + + + + traceback.print_exc(file=sys.stdout) # set checking flag back to False self.isChecking = False @@ -237,29 +240,33 @@ def get_host_and_service_id(self, host, service=''): return Result(result=result, error=error) else: # Host + Service - if self.centreon_version == 20.04: + if host == "Meta_Services": + url_service = self.urls_centreon['services'] + '?types=["metaservice"]&search={"s.name":"'+service+'"}' + else: url_service = self.urls_centreon['services'] + '?types=["service"]&search={"$and":[{"h.name":{"$eq":"'+host+'"}}, {"s.description":{"$eq":"'+service+'"}}]}' try: - if self.centreon_version == 20.04: - # Get json - result = self.FetchURL(url_service, giveback='raw') + # Get json + result = self.FetchURL(url_service, giveback='raw') - data = json.loads(result.result) - error = result.error - status_code = result.status_code + data = json.loads(result.result) + error = result.error + status_code = result.status_code - # check if any error occured - errors_occured = self.check_for_error(data, error, status_code) - if errors_occured is not False: - return(errors_occured) + # check if any error occured + errors_occured = self.check_for_error(data, error, status_code) + if errors_occured is not False: + return(errors_occured) - host_id = str(data["result"][0]["parent"]["id"]) - service_id = str(data["result"][0]["id"]) + if host == "Meta_Services": + host_id = 0 + else: + host_id = data["result"][0]["parent"]["id"] + service_id = data["result"][0]["id"] - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Get Host / Service ID : ' + host_id + " / " + service_id) - return host_id,service_id + if conf.debug_mode == True: + self.Debug(server='[' + self.get_name() + ']', debug='Get Host / Service ID : ' + str(host_id) + " / " + str(service_id)) + return host_id,service_id except: import traceback @@ -270,6 +277,17 @@ def get_host_and_service_id(self, host, service=''): return Result(result=result, error=error) + def get_start_end(self, host): + # I don’t know how to get this info... + # self.defaults_downtime_duration_hours = 2 + # self.defaults_downtime_duration_minutes = 0 + start = datetime.now() + end = datetime.now() + timedelta(hours=2) + + return (str(start.strftime('%Y-%m-%d %H:%M')), + str(end.strftime('%Y-%m-%d %H:%M'))) + + def _get_status(self): ''' Get status from Centreon Server @@ -283,56 +301,53 @@ def _get_status(self): return result # Services URL - if self.centreon_version == 20.04 : - # https://demo.centreon.com/centreon/api/latest/monitoring/resources?page=1&limit=30&sort_by={"status_severity_code":"asc","last_status_change":"desc"}&types=["service"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"] - url_services = self.urls_centreon['services'] + '?types=["metaservice","service"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"]&limit=' + str(self.limit_services_number) + # https://demo.centreon.com/centreon/api/latest/monitoring/resources?page=1&limit=30&sort_by={"status_severity_code":"asc","last_status_change":"desc"}&types=["service"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"] + url_services = self.urls_centreon['services'] + '?types=["metaservice","service"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"]&limit=' + str(self.limit_services_number) # Hosts URL - if self.centreon_version == 20.04: - # https://demo.centreon.com/centreon/api/latest/monitoring/resources?page=1&limit=30&sort_by={"status_severity_code":"asc","last_status_change":"desc"}&types=["host"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"] - url_hosts = self.urls_centreon['hosts'] + '?types=["host"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"]&limit=' + str(self.limit_services_number) + # https://demo.centreon.com/centreon/api/latest/monitoring/resources?page=1&limit=30&sort_by={"status_severity_code":"asc","last_status_change":"desc"}&types=["host"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"] + url_hosts = self.urls_centreon['hosts'] + '?types=["host"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"]&limit=' + str(self.limit_services_number) # Hosts try: - if self.centreon_version >= 20.04: - # Get json - result = self.FetchURL(url_hosts, giveback='raw') + # Get json + result = self.FetchURL(url_hosts, giveback='raw') - data = json.loads(result.result) - error = result.error - status_code = result.status_code + data = json.loads(result.result) + error = result.error + status_code = result.status_code - # if conf.debug_mode: - # self.Debug(server=self.get_name(), - # debug="Get Hosts status Fetched JSON: " + pprint.pformat(data)) + # if conf.debug_mode: + # self.Debug(server=self.get_name(), + # debug="Get Hosts status Fetched JSON: " + pprint.pformat(data)) - # check if any error occured - errors_occured = self.check_for_error(data, error, status_code) - if errors_occured is not False: - return(errors_occured) + # check if any error occured + errors_occured = self.check_for_error(data, error, status_code) + if errors_occured is not False: + return(errors_occured) - for alerts in data["result"]: - new_host = alerts["name"] - self.new_hosts[new_host] = GenericHost() - self.new_hosts[new_host].name = alerts["name"] - self.new_hosts[new_host].server = self.name - self.new_hosts[new_host].criticality = alerts["severity_level"] - self.new_hosts[new_host].status = alerts["status"]["name"] - self.new_hosts[new_host].last_check = alerts["last_check"] - # last_state_change = datetime.strptime(alerts["last_status_change"], '%Y-%m-%dT%H:%M:%S%z').replace(tzinfo=None) - self.new_hosts[new_host].duration = alerts["duration"] - self.new_hosts[new_host].attempt = alerts["tries"] - self.new_hosts[new_host].status_information = alerts["information"] - self.new_hosts[new_host].passiveonly = alerts["passive_checks"] - self.new_hosts[new_host].notifications_disabled = not alerts["notification_enabled"] - self.new_hosts[new_host].flapping = alerts["flapping"] - self.new_hosts[new_host].acknowledged = alerts["acknowledged"] - self.new_hosts[new_host].scheduled_downtime = alerts["in_downtime"] - if "(S)" in alerts["tries"]: - self.new_hosts[new_host].status_type = self.HARD_SOFT['(S)'] - else: - self.new_hosts[new_host].status_type = self.HARD_SOFT['(H)'] - self.Debug(server='[' + self.get_name() + ']', debug='Host indexed : ' + new_host) + for alerts in data["result"]: + new_host = alerts["name"] + self.new_hosts[new_host] = GenericHost() + self.new_hosts[new_host].name = alerts["name"] + self.new_hosts[new_host].server = self.name + self.new_hosts[new_host].criticality = alerts["severity_level"] + self.new_hosts[new_host].status = alerts["status"]["name"] + self.new_hosts[new_host].last_check = alerts["last_check"] + # last_state_change = datetime.strptime(alerts["last_status_change"], '%Y-%m-%dT%H:%M:%S%z').replace(tzinfo=None) + self.new_hosts[new_host].duration = alerts["duration"] + self.new_hosts[new_host].attempt = alerts["tries"] + self.new_hosts[new_host].status_information = alerts["information"] + self.new_hosts[new_host].passiveonly = alerts["passive_checks"] + self.new_hosts[new_host].notifications_disabled = not alerts["notification_enabled"] + self.new_hosts[new_host].flapping = alerts["flapping"] + self.new_hosts[new_host].acknowledged = alerts["acknowledged"] + self.new_hosts[new_host].scheduled_downtime = alerts["in_downtime"] + if "(S)" in alerts["tries"]: + self.new_hosts[new_host].status_type = self.HARD_SOFT['(S)'] + else: + self.new_hosts[new_host].status_type = self.HARD_SOFT['(H)'] + self.Debug(server='[' + self.get_name() + ']', debug='Host indexed : ' + new_host) except: import traceback @@ -344,58 +359,57 @@ def _get_status(self): # Services try: - if self.centreon_version >= 20.04: - # Get json - result = self.FetchURL(url_services, giveback='raw') + # Get json + result = self.FetchURL(url_services, giveback='raw') - data = json.loads(result.result) - error = result.error - status_code = result.status_code + data = json.loads(result.result) + error = result.error + status_code = result.status_code - # if conf.debug_mode: - # self.Debug(server=self.get_name(), - # debug="Get Services status Fetched JSON: " + pprint.pformat(data)) + # if conf.debug_mode: + # self.Debug(server=self.get_name(), + # debug="Get Services status Fetched JSON: " + pprint.pformat(data)) - # check if any error occured - errors_occured = self.check_for_error(data, error, status_code) - if errors_occured is not False: - return(errors_occured) + # check if any error occured + errors_occured = self.check_for_error(data, error, status_code) + if errors_occured is not False: + return(errors_occured) - for alerts in data["result"]: - if alerts["type"] == "metaservice": - new_host = "Meta_Services" - else: - new_host = alerts["parent"]["name"] - new_service = alerts["name"] - # Needed if non-ok services are on a UP host - if not new_host in self.new_hosts: - self.new_hosts[new_host] = GenericHost() - self.new_hosts[new_host].name = new_host - self.new_hosts[new_host].status = 'UP' - self.new_hosts[new_host].services[new_service] = GenericService() - # Attributs à remplir - self.Debug(server='[' + self.get_name() + ']', debug='Service indexed : ' + new_host + ' / ' + new_service) - - self.new_hosts[new_host].services[new_service].server = self.name - self.new_hosts[new_host].services[new_service].host = new_host - self.new_hosts[new_host].services[new_service].name = new_service - self.new_hosts[new_host].services[new_service].status = alerts["status"]["name"] - self.new_hosts[new_host].services[new_service].last_check = alerts["last_check"] - # last_state_change = datetime.strptime(alerts["last_state_change"], '%Y-%m-%dT%H:%M:%S%z').replace(tzinfo=None) - # self.new_hosts[new_host].services[new_service].duration = datetime.now() - last_state_change - self.new_hosts[new_host].services[new_service].duration = alerts["duration"] - self.new_hosts[new_host].services[new_service].attempt = alerts["tries"] - self.new_hosts[new_host].services[new_service].status_information = alerts["information"] - self.new_hosts[new_host].services[new_service].passiveonly = alerts["passive_checks"] - self.new_hosts[new_host].services[new_service].notifications_disabled = not alerts["notification_enabled"] - self.new_hosts[new_host].services[new_service].flapping = alerts["flapping"] - self.new_hosts[new_host].services[new_service].acknowledged = alerts["acknowledged"] - self.new_hosts[new_host].services[new_service].scheduled_downtime = alerts["in_downtime"] - if "(S)" in alerts["tries"]: - self.new_hosts[new_host].services[new_service].status_type = self.HARD_SOFT['(S)'] - else: - self.new_hosts[new_host].services[new_service].status_type = self.HARD_SOFT['(H)'] - self.new_hosts[new_host].services[new_service].criticality = alerts["severity_level"] + for alerts in data["result"]: + if alerts["type"] == "metaservice": + new_host = "Meta_Services" + else: + new_host = alerts["parent"]["name"] + new_service = alerts["name"] + # Needed if non-ok services are on a UP host + if not new_host in self.new_hosts: + self.new_hosts[new_host] = GenericHost() + self.new_hosts[new_host].name = new_host + self.new_hosts[new_host].status = 'UP' + self.new_hosts[new_host].services[new_service] = GenericService() + # Attributs à remplir + self.Debug(server='[' + self.get_name() + ']', debug='Service indexed : ' + new_host + ' / ' + new_service) + + self.new_hosts[new_host].services[new_service].server = self.name + self.new_hosts[new_host].services[new_service].host = new_host + self.new_hosts[new_host].services[new_service].name = new_service + self.new_hosts[new_host].services[new_service].status = alerts["status"]["name"] + self.new_hosts[new_host].services[new_service].last_check = alerts["last_check"] + # last_state_change = datetime.strptime(alerts["last_state_change"], '%Y-%m-%dT%H:%M:%S%z').replace(tzinfo=None) + # self.new_hosts[new_host].services[new_service].duration = datetime.now() - last_state_change + self.new_hosts[new_host].services[new_service].duration = alerts["duration"] + self.new_hosts[new_host].services[new_service].attempt = alerts["tries"] + self.new_hosts[new_host].services[new_service].status_information = alerts["information"] + self.new_hosts[new_host].services[new_service].passiveonly = alerts["passive_checks"] + self.new_hosts[new_host].services[new_service].notifications_disabled = not alerts["notification_enabled"] + self.new_hosts[new_host].services[new_service].flapping = alerts["flapping"] + self.new_hosts[new_host].services[new_service].acknowledged = alerts["acknowledged"] + self.new_hosts[new_host].services[new_service].scheduled_downtime = alerts["in_downtime"] + if "(S)" in alerts["tries"]: + self.new_hosts[new_host].services[new_service].status_type = self.HARD_SOFT['(S)'] + else: + self.new_hosts[new_host].services[new_service].status_type = self.HARD_SOFT['(H)'] + self.new_hosts[new_host].services[new_service].criticality = alerts["severity_level"] except: import traceback @@ -411,22 +425,37 @@ def _get_status(self): def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=[]): try: + + acknowledgements = { + "acknowledgement": { + "comment": comment, + "with_services": True, + "is_notify_contacts": notify, + "is_persistent_comment": persistent, + "is_sticky": sticky + }, + "resources": [ + ] + } + # host if service == '': - cgi_data = { - "comment": comment, - "is_notify_contacts": notify, - "is_persistent_comment": persistent, - "is_sticky": sticky, - "with_services": True + host_id = self.get_host_and_service_id(host) + + new_resource = { + "type": "host", + "id": host_id, + "parent": { + "id": None + } } - host_id = self.get_host_and_service_id(host) + acknowledgements["resources"].append(new_resource) # Post json - json_string = json.dumps(cgi_data) + json_string = json.dumps(acknowledgements) # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/hosts/{host_id}/acknowledgements - result = self.FetchURL(self.monitor_cgi_url + '/api/' + self.restapi_version + '/monitoring/hosts/' + host_id + '/acknowledgements', cgi_data=json_string, giveback='raw') + result = self.FetchURL(self.urls_centreon['resources'] + '/acknowledge', cgi_data=json_string, giveback='raw') error = result.error status_code = result.status_code @@ -441,33 +470,43 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi if len(all_services) == 0: all_services = [service] - acknowledgements_list=[] - for s in all_services: host_id, service_id = self.get_host_and_service_id(host, service) - ack = { - "comment": comment, - "is_notify_contacts": notify, - "is_persistent_comment": persistent, - "is_sticky": sticky, - "resource_id": service_id, - "parent_resource_id": host_id - } + if host == "Meta_Services": + new_resource = { + "type": "metaservice", + "id": service_id, + "parent": { + "id": None + } + } - acknowledgements_list.append(ack) + else: + new_resource = { + "type": "service", + "id": service_id, + "parent": { + "id": host_id + } + } + + acknowledgements["resources"].append(new_resource) + if conf.debug_mode: + self.Debug(server='[' + self.get_name() + ']', + debug="Stack ack for Host ("+host+") / Service ("+service+")") # Post json - json_string = json.dumps(acknowledgements_list) + json_string = json.dumps(acknowledgements) # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/services/acknowledgements - result = self.FetchURL(self.monitor_cgi_url + '/api/' + self.restapi_version + '/monitoring/services/acknowledgements', cgi_data=json_string, giveback='raw') + result = self.FetchURL(self.urls_centreon['resources'] + '/acknowledge', cgi_data=json_string, giveback='raw') error = result.error status_code = result.status_code if conf.debug_mode: self.Debug(server='[' + self.get_name() + ']', - debug="Set Ack on Host ("+host+") / Service ("+service+"), status code : " + str(status_code)) + debug="Set Acks, status code : " + str(status_code)) except: import traceback @@ -479,19 +518,28 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi def _set_recheck(self, host, service): + rechecks = { + "resources": [ + ] + } + try: # Host if service == '': - cgi_data = { - "is_forced": True + host_id = self.get_host_and_service_id(host) + + new_resource = { + "type": "host", + "id": host_id, + "parent": None } - host_id = self.get_host_and_service_id(host) + rechecks["resources"].append(new_resource) # Post json - json_string = json.dumps(cgi_data) - # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/hosts/{host_id}/check - result = self.FetchURL(self.monitor_cgi_url + '/api/' + self.restapi_version + '/monitoring/hosts/' + host_id + '/check', cgi_data=json_string, giveback='raw') + json_string = json.dumps(rechecks) + # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/resources/check + result = self.FetchURL(self.urls_centreon['resources'] + '/check', cgi_data=json_string, giveback='raw') error = result.error status_code = result.status_code @@ -500,26 +548,39 @@ def _set_recheck(self, host, service): self.Debug(server='[' + self.get_name() + ']', debug="Recheck on Host : "+host+", status code : " + str(status_code)) - # Service + # Service else: - cgi_data = { - "is_forced": True - } - host_id, service_id = self.get_host_and_service_id(host, service) + if host == "Meta_Services": + new_resource = { + "type": "metaservice", + "id": service_id, + "parent": None + } + + else: + new_resource = { + "type": "service", + "id": service_id, + "parent": { + "id": host_id + } + } + + rechecks["resources"].append(new_resource) + # Post json - json_string = json.dumps(cgi_data) - # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/hosts/{host_id}/services/{service_id}/check - result = self.FetchURL(self.monitor_cgi_url + '/api/' + self.restapi_version + '/monitoring/hosts/' + host_id + '/services/' + service_id + '/check', cgi_data=json_string, giveback='raw') + json_string = json.dumps(rechecks) + # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/resources/check + result = self.FetchURL(self.urls_centreon['resources'] + '/check', cgi_data=json_string, giveback='raw') error = result.error status_code = result.status_code if conf.debug_mode: self.Debug(server='[' + self.get_name() + ']', - debug="Recheck on Host ("+host+") / Service ("+service+"), status code : " + str(status_code)) - + debug="Reckeck on Host ("+host+") / Service ("+service+"), status code : " + str(status_code)) except: import traceback @@ -531,8 +592,8 @@ def _set_recheck(self, host, service): def _set_downtime(self, host, service, author, comment, fixed, start_time, end_time, hours, minutes): - obj_start_time = datetime.strptime(start_time, '%m/%d/%Y %H:%M') - obj_end_time = datetime.strptime(end_time, '%m/%d/%Y %H:%M') + obj_start_time = datetime.strptime(start_time, '%Y-%m-%d %H:%M') + obj_end_time = datetime.strptime(end_time, '%Y-%m-%d %H:%M') # Nagstamon don’t provide the TZ, we need to get it from the OS obj_start_time = obj_start_time.replace(tzinfo=datetime.now().astimezone().tzinfo) @@ -547,25 +608,36 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t else: fixed = False + downtimes = { + "downtime": { + "comment": comment, + "with_services": True, + "is_fixed": fixed, + "duration": duration, + "start_time": obj_start_time.strftime('%Y-%m-%dT%H:%M:%S%z'), + "end_time": obj_end_time.strftime('%Y-%m-%dT%H:%M:%S%z') + }, + "resources": [ + ] + } + try: if service == '': - # Host - cgi_data = { - "start_time": obj_start_time.strftime('%Y-%m-%dT%H:%M:%S%z'), - "end_time": obj_end_time.strftime('%Y-%m-%dT%H:%M:%S%z'), - "is_fixed": fixed, - "duration": duration, - "author_id": self.user_id, - "comment": comment, - "with_services": True + # Host + host_id = self.get_host_and_service_id(host) + + new_resource = { + "type": "host", + "id": host_id, + "parent": None } - host_id = self.get_host_and_service_id(host) + downtimes["resources"].append(new_resource) # Post json - json_string = json.dumps(cgi_data) - # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/hosts/{host_id}/downtimes - result = self.FetchURL(self.monitor_cgi_url + '/api/' + self.restapi_version + '/monitoring/hosts/' + host_id + '/downtimes', cgi_data=json_string, giveback='raw') + json_string = json.dumps(downtimes) + # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/resources/downtime + result = self.FetchURL(self.urls_centreon['resources'] + '/downtime', cgi_data=json_string, giveback='raw') error = result.error status_code = result.status_code @@ -576,22 +648,32 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t # Service else: - cgi_data = { - "start_time": obj_start_time.strftime('%Y-%m-%dT%H:%M:%S%z'), - "end_time": obj_end_time.strftime('%Y-%m-%dT%H:%M:%S%z'), - "is_fixed": fixed, - "duration": duration, - "author_id": self.user_id, - "comment": comment - } - host_id, service_id = self.get_host_and_service_id(host, service) - # Post json - json_string = json.dumps(cgi_data) - # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/hosts/{host_id}/services/{service_id}/downtimes - result = self.FetchURL(self.monitor_cgi_url + '/api/' + self.restapi_version + '/monitoring/hosts/' + host_id + '/services/' + service_id + '/downtimes', cgi_data=json_string, giveback='raw') + if host == "Meta_Services": + new_resource = { + "type": "metaservice", + "id": service_id, + "parent": { + "id": None + } + } + else: + new_resource = { + "type": "service", + "id": service_id, + "parent": { + "id": host_id + } + } + + downtimes["resources"].append(new_resource) + + # Post json + json_string = json.dumps(downtimes) + # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/resources/downtime + result = self.FetchURL(self.urls_centreon['resources'] + '/downtime', cgi_data=json_string, giveback='raw') error = result.error status_code = result.status_code @@ -627,7 +709,7 @@ def check_session(self): # Get en empty service list, to check the status of the current token # This request must be done in a GET, so just encode the parameters and fetch - result = self.FetchURL(self.urls_centreon['services'] + '?' + urllib.parse.urlencode(cgi_data), giveback="raw") + result = self.FetchURL(self.urls_centreon['resources'] + '?' + urllib.parse.urlencode(cgi_data), giveback="raw") data = json.loads(result.result) error = result.error From b420f2449d8d7e02a7cb5640a264a9f372e960d1 Mon Sep 17 00:00:00 2001 From: Benoit Poulet <benoit.poulet@businessdecision.com> Date: Tue, 31 May 2022 12:39:18 +0200 Subject: [PATCH 284/884] remove autologin as seems deprecated --- Nagstamon/Servers/Centreon.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Nagstamon/Servers/Centreon.py b/Nagstamon/Servers/Centreon.py index 14bf2031b..01b805038 100644 --- a/Nagstamon/Servers/Centreon.py +++ b/Nagstamon/Servers/Centreon.py @@ -111,12 +111,15 @@ def define_url(self): def open_monitor(self, host, service=''): - if self.use_autologin is True: - auth = '&autologin=1&useralias=' + self.username + '&token=' + self.autologin_key - else: - auth = '' - - webbrowser_open(self.urls_centreon['resources'] + auth ) + # Autologin seems deprecated as admin must enable it globaly and use the old pages + # Ex : http://10.66.113.52/centreon/main.php?autologin=1&useralias=admin&token=xxxxxx + # if self.use_autologin is True: + # auth = '&autologin=1&useralias=' + self.username + '&token=' + self.autologin_key + # else: + # auth = '' + # webbrowser_open(self.urls_centreon['resources'] + auth ) + + webbrowser_open(self.urls_centreon['resources'] ) def get_token(self): From cfa0c93dafcf9f57d83e7c03cc139d9cbc6be738 Mon Sep 17 00:00:00 2001 From: Benoit Poulet <benoit.poulet@businessdecision.com> Date: Fri, 3 Jun 2022 11:04:43 +0200 Subject: [PATCH 285/884] Update Centreon.py --- Nagstamon/Servers/Centreon.py | 1452 +++++++++++++++++++++------------ 1 file changed, 911 insertions(+), 541 deletions(-) diff --git a/Nagstamon/Servers/Centreon.py b/Nagstamon/Servers/Centreon.py index 01b805038..d4edc6fcb 100644 --- a/Nagstamon/Servers/Centreon.py +++ b/Nagstamon/Servers/Centreon.py @@ -24,10 +24,6 @@ import sys import re import copy -# API V2 -import pprint -import json -import requests from datetime import datetime, timedelta @@ -36,131 +32,251 @@ from Nagstamon.Config import conf from Nagstamon.Helpers import webbrowser_open -# This class support Centreon V2 API - -# Things to do : -# - change the way host/services status is gathered, must change it -# to use 'ressources' class CentreonServer(GenericServer): TYPE = 'Centreon' - # Centreon API uses a token - token = None + # centreon generic web interface uses a sid which is needed to ask for news + SID = None # HARD/SOFT state mapping HARD_SOFT = {'(H)': 'hard', '(S)': 'soft'} + # apparently necessesary because of non-english states as in https://github.com/HenriWahl/Nagstamon/issues/91 (Centeron 2.5) + TRANSLATIONS = {'INDISPONIBLE': 'DOWN', + 'INJOIGNABLE': 'UNREACHABLE', + 'CRITIQUE': 'CRITICAL', + 'INCONNU': 'UNKNOWN', + 'ALERTE': 'WARNING'} + # Entries for monitor default actions in context menu MENU_ACTIONS = ['Monitor', 'Recheck', 'Acknowledge', 'Downtime'] - # URLs of the Centreon pages - urls_centreon = None + # Centreon works better or at all with html.parser for BeautifulSoup + PARSER = 'html.parser' + # Needed to detect each Centreon's version + centreon_version = None + # Token that centreon use to protect the system + centreon_token = None + # To only detect broker once + first_login = True # limit number of services retrived limit_services_number = 9999 + # default value, applies to version 2.2 and others + XML_PATH = 'xml' def init_config(self): ''' - init_config, called at thread start, not really needed here, just omit extra properties + dummy init_config, called at thread start, not really needed here, just omit extra properties ''' - - # FIX but be provided by user as their is no way to detect it - self.user_provided_centreon_version = "20.04" - - if re.search('2(0|1|2)\.(04|10)', self.user_provided_centreon_version): - # from 20.04 to 22.04 the API to use is «latest» - self.centreon_version = 20.04 - if conf.debug_mode is True: - self.Debug(server='[' + self.get_name() + ']', debug='Centreon version selected : 20.04 <=> 22.04') - # URLs for browser shortlinks/buttons on popup window - self.BROWSER_URLS = {'monitor': '$MONITOR$/monitoring/resources', - 'hosts': '$MONITOR$/monitoring/resources', - 'services': '$MONITOR$/monitoring/resources', - 'history': '$MONITOR$/main.php?p=20301'} - # RestAPI version - self.restapi_version = "latest" - - else: - if conf.debug_mode is True: - self.Debug(server='[' + self.get_name() + ']', debug='No Centreon version provided') - - # Changed this because define_url was called 2 times - if not self.tls_error and self.urls_centreon is None: - self.define_url() - + # set URLs here already + self.init_HTTP() + if not self.tls_error and self.centreon_version is not None: + self._define_url() def init_HTTP(self): + """ + initialize HTTP connection + """ if self.session is None: GenericServer.init_HTTP(self) - self.session.headers.update({'Content-Type': 'application/json'}) - self.token = self.get_token().result + if self.centreon_version is None: + result_versioncheck = self.FetchURL(self.monitor_cgi_url + '/index.php', giveback='raw') + raw_versioncheck, error_versioncheck = result_versioncheck.result, result_versioncheck.error + if error_versioncheck == '': + if re.search('2\.2\.[0-9]', raw_versioncheck): + self.centreon_version = 2.2 + if conf.debug_mode is True: + self.Debug(server=self.get_name(), debug='Centreon version detected : 2.2') + # URLs for browser shortlinks/buttons on popup window + self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?p=1', + 'hosts': '$MONITOR$/main.php?p=20103&o=hpb', + 'services': '$MONITOR$/main.php?p=20202&o=svcpb', + 'history': '$MONITOR$/main.php?p=203'} + elif re.search('2\.[3-6]\.[0-5]', raw_versioncheck): + self.centreon_version = 2.3456 + if conf.debug_mode is True: + self.Debug(server=self.get_name(), debug='Centreon version detected : 2.3 <=> 2.6.5') + # URLs for browser shortlinks/buttons on popup window + self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?p=1', + 'hosts': '$MONITOR$/main.php?p=20103&o=hpb', + 'services': '$MONITOR$/main.php?p=20202&o=svcpb', + 'history': '$MONITOR$/main.php?p=203'} + elif re.search('2\.6\.[6-9]', raw_versioncheck): + self.centreon_version = 2.66 + if conf.debug_mode is True: + self.Debug(server=self.get_name(), debug='Centreon version detected : 2.6.6') + # URLs for browser shortlinks/buttons on popup window + self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?p=1', + 'hosts': '$MONITOR$/main.php?p=20103&o=hpb', + 'services': '$MONITOR$/main.php?p=20202&o=svcpb', + 'history': '$MONITOR$/main.php?p=203'} + elif re.search('2\.7\.[0-9]', raw_versioncheck): + # Centreon 2.7 only support C. Broker + self.centreon_version = 2.7 + if conf.debug_mode is True: + self.Debug(server=self.get_name(), debug='Centreon version detected : 2.7') + # URLs for browser shortlinks/buttons on popup window + self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?', + 'hosts': '$MONITOR$/main.php?p=20202&o=hpb', + 'services': '$MONITOR$/main.php?p=20201&o=svcpb', + 'history': '$MONITOR$/main.php?p=203'} + elif re.search('2\.8\.[0-9]', raw_versioncheck): + # Centreon 2.8 only support C. Broker + self.centreon_version = 2.8 + if conf.debug_mode is True: + self.Debug(server=self.get_name(), debug='Centreon version detected : 2.8') + # URLs for browser shortlinks/buttons on popup window + self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?', + 'hosts': '$MONITOR$/main.php?p=20202', + 'services': '$MONITOR$/main.php?p=20201', + 'history': '$MONITOR$/main.php?p=203'} + elif re.search('18\.10\.[0-9]', raw_versioncheck): + self.centreon_version = 18.10 + if conf.debug_mode is True: + self.Debug(server=self.get_name(), debug='Centreon version detected : 18.10') + # URLs for browser shortlinks/buttons on popup window + self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?', + 'hosts': '$MONITOR$/main.php?p=20202', + 'services': '$MONITOR$/main.php?p=20201', + 'history': '$MONITOR$/main.php?p=203'} + elif re.search('19\.(04|10)\.[0-9]', raw_versioncheck) or re.search('20\.(04|10)\.[0-9]', raw_versioncheck): + self.centreon_version = 19.04 + if conf.debug_mode is True: + self.Debug(server=self.get_name(), debug='Centreon version detected : 19.04 <=> 20.10') + # URLs for browser shortlinks/buttons on popup window + self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?', + 'hosts': '$MONITOR$/main.php?p=20202', + 'services': '$MONITOR$/main.php?p=20201', + 'history': '$MONITOR$/main.php?p=203'} + else: + # unsupported version or unable do determine + self.centreon_version = 19.04 + if conf.debug_mode is True: + self.Debug(server=self.get_name(), debug='Centreon version unknown : supposed to be >= 19.04') + # URLs for browser shortlinks/buttons on popup window + self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?', + 'hosts': '$MONITOR$/main.php?p=20202&o=hpb', + 'services': '$MONITOR$/main.php?p=20201&o=svcpb', + 'history': '$MONITOR$/main.php?p=203'} + else: + if conf.debug_mode is True: + self.Debug(server=self.get_name(), debug='Error getting the home page : ' + error_versioncheck) - def define_url(self): - urls_centreon_api_v2 = { - 'resources': self.monitor_cgi_url + '/api/' + self.restapi_version + '/monitoring/resources', - 'login': self.monitor_cgi_url + '/api/' + self.restapi_version + '/login', - 'services': self.monitor_cgi_url + '/api/' + self.restapi_version + '/monitoring/resources', - 'hosts': self.monitor_cgi_url + '/api/' + self.restapi_version + '/monitoring/resources' - } + if self.first_login: + self.SID = self._get_sid().result + self.first_login = False - self.urls_centreon = urls_centreon_api_v2 - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='URLs defined for Centreon %s' % (self.centreon_version)) + del result_versioncheck, raw_versioncheck, error_versioncheck + def reset_HTTP(self): + ''' + Centreon needs deletion of SID + ''' + self.SID = None + self.SID = self._get_sid().result def open_monitor(self, host, service=''): - # Autologin seems deprecated as admin must enable it globaly and use the old pages - # Ex : http://10.66.113.52/centreon/main.php?autologin=1&useralias=admin&token=xxxxxx - # if self.use_autologin is True: - # auth = '&autologin=1&useralias=' + self.username + '&token=' + self.autologin_key - # else: - # auth = '' - # webbrowser_open(self.urls_centreon['resources'] + auth ) + if self.use_autologin is True: + auth = '&autologin=1&useralias=' + self.username + '&token=' + self.autologin_key + else: + auth = '' + + # Meta + if host == '_Module_Meta': + # Centreon < 2.7 + if self.centreon_version < 2.7: + webbrowser_open(self.urls_centreon['index'] + '?' + urllib.parse.urlencode({'p': 20206, 'o': 'meta'}) + auth ) + # Centreon 2.7 + elif self.centreon_version == 2.7: + webbrowser_open(self.urls_centreon['main'] + '?' + urllib.parse.urlencode({'p':20206, 'o':'meta'}) + auth ) + # Centreon 2.8 + elif self.centreon_version == 2.8: + m = re.search(r'^.+ \((?P<rsd>.+)\)$', service) + if m: + service = m.group('rsd') + webbrowser_open(self.urls_centreon['main'] + '?' + urllib.parse.urlencode({'p':20201,'o':'svcd','host_name':'_Module_Meta','service_description':service}) + auth ) + # Starting from Centreon 18.10 + else: + m = re.search(r'^.+ \((?P<rsd>.+)\)$', service) + if m: + service = m.group('rsd') + webbrowser_open(self.urls_centreon['main_with_frames'] + '?' + urllib.parse.urlencode({'p':20201,'o':'svcd','host_name':'_Module_Meta','service_description':service}) + auth ) + + # must be a host if service is empty + elif service == '': + if self.centreon_version < 2.7: + webbrowser_open(self.urls_centreon['main'] + '?' + urllib.parse.urlencode({'p':201,'o':'hd', 'host_name':host}) + auth ) + elif self.centreon_version in [2.7, 2.8]: + webbrowser_open(self.urls_centreon['main'] + '?' + urllib.parse.urlencode({'p':20202,'o':'hd', 'host_name':host}) + auth ) + else: + webbrowser_open(self.urls_centreon['main_with_frames'] + '?' + urllib.parse.urlencode({'p':20202,'o':'hd', 'host_name':host}) + auth ) - webbrowser_open(self.urls_centreon['resources'] ) + # so it's a service + else: + if self.centreon_version < 2.7: + webbrowser_open(self.urls_centreon['main'] + '?' + urllib.parse.urlencode({'p':202, 'o':'svcd', 'host_name':host, 'service_description':service}) + auth ) + elif self.centreon_version in [2.7, 2.8]: + webbrowser_open(self.urls_centreon['main'] + '?' + urllib.parse.urlencode({'p':20201,'o':'svcd', 'host_name':host, 'service_description':service}) + auth ) + else: + webbrowser_open(self.urls_centreon['main_with_frames'] + '?' + urllib.parse.urlencode({'p':20201,'o':'svcd', 'host_name':host, 'service_description':service}) + auth ) - def get_token(self): + def _get_sid(self): + ''' + gets a shiny new SID for XML HTTP requests to Centreon cutting it out via .partition() from raw HTML + additionally get php session cookie + ''' try: - cgi_data = { - "security": { - "credentials": { - "login": self.username, - "password": self.password - } - } - } - - # Post json - json_string = json.dumps(cgi_data) - result = self.FetchURL(self.urls_centreon['login'], cgi_data=json_string, giveback='raw') - - data = json.loads(result.result) - error = result.error - status_code = result.status_code - - if conf.debug_mode: - self.Debug(server=self.get_name(), - debug="Fetched JSON: " + pprint.pformat(data)) - - - # check if any error occured - errors_occured = self.check_for_error(data, error, status_code) - if errors_occured is not False: - return(errors_occured) + # Aulogin with key, BROWSER_URLS needs the key + if self.use_autologin == True: + auth = '&autologin=1&useralias=' + self.username + '&token=' + self.autologin_key + self.BROWSER_URLS= { 'monitor': self.BROWSER_URLS['monitor'] + auth,\ + 'hosts': self.BROWSER_URLS['hosts'] + auth,\ + 'services': self.BROWSER_URLS['services'] + auth,\ + 'history': self.BROWSER_URLS['history'] + auth} + raw = self.FetchURL(self.monitor_cgi_url + '/index.php?p=101&autologin=1&useralias=' + self.username + '&token=' + self.autologin_key, giveback='raw') + if conf.debug_mode == True: + self.Debug(server=self.get_name(), debug='Autologin : ' + self.username + ' : ' + self.autologin_key) + # Gathering of the token who will be used to interact with Centreon (start with 2.66) + if self.centreon_version >= 2.66 and self.centreon_version < 19.04: + page = self.FetchURL(self.monitor_cgi_url + '/main.get.php') + self.centreon_token = page.result.find('input', {'name': "centreon_token"})['value'] - token = data["security"]["token"] - # ID of the user is needed by some requests - user_id = data["contact"]["id"] + # Password auth + else: + login = self.FetchURL(self.monitor_cgi_url + '/index.php') + if login.error == '' and login.status_code == 200: + # Centreon >= 2.6.6 implement a token + if self.centreon_version >= 2.66 and self.centreon_version <= 19.04: + form = login.result.find('form') + form_inputs = {} + # Need to catch the centreon_token for login to work + for form_input in ('centreon_token', 'submitLogin'): + form_inputs[form_input] = form.find('input', {'name': form_input})['value'] + self.centreon_token = form_inputs['centreon_token'] + form_inputs['useralias'] = self.username + form_inputs['password'] = self.password + # fire up login button with all needed data + raw = self.FetchURL(self.monitor_cgi_url + '/index.php', cgi_data=form_inputs) + else: + login_data = {"useralias" : self.username, "password" : self.password, "submit" : "Login"} + raw = self.FetchURL(self.monitor_cgi_url + "/index.php",cgi_data=login_data, giveback="raw") + if conf.debug_mode == True: + self.Debug(server=self.get_name(), debug='Password login : ' + self.username + ' : ' + self.password) + sid = self.session.cookies['PHPSESSID'] if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='API login : ' + self.username + ' / ' + self.password + ' > Token : ' + token + ' > User ID : ' + str(user_id)) - - self.user_id = user_id - self.session.headers.update({'X-Auth-Token': token}) - return Result(result=token) + self.Debug(server=self.get_name(), debug='SID : ' + sid) + if self.centreon_version >= 2.66 and self.centreon_version < 19.04: + self.Debug(server=self.get_name(), debug='Centreon Token : ' + self.centreon_token) + # those broker urls would not be changing too often so this check migth be done here + if self.first_login: + self._get_xml_path(sid) + self.first_login = False + return Result(result=sid) except: import traceback @@ -169,126 +285,280 @@ def get_token(self): return Result(result=result, error=error) - def GetHost(self, host): - # https://demo.centreon.com/centreon/api/latest/monitoring/resources?page=1&limit=30&sort_by={"status_severity_code":"asc","last_status_change":"desc"}&types=["host"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"] - url_hosts = self.urls_centreon['hosts'] + '?types=["host"]&search={"h.name":"' + host + '"}' - + def get_start_end(self, host): + ''' + get start and end time for downtime from Centreon server + ''' try: - # Get json - result = self.FetchURL(url_hosts, giveback='raw') - - data = json.loads(result.result) - error = result.error - status_code = result.status_code + # It's not possible since 18.10 to get date from the webinterface + # because it's set in javascript + if self.centreon_version < 18.10: + cgi_data = {'o':'ah',\ + 'host_name':host} + if self.centreon_version < 2.7: + cgi_data['p'] = '20106' + elif self.centreon_version == 2.7: + cgi_data['p'] = '210' + elif self.centreon_version == 2.8: + cgi_data['o'] = 'a' + cgi_data['p'] = '210' + result = self.FetchURL(self.urls_centreon['main'], cgi_data = cgi_data, giveback='obj') + + html, error = result.result, result.error + if error == '': + start_date = html.find(attrs={'name':'start'}).attrs['value'] + start_hour = html.find(attrs={'name':'start_time'}).attrs['value'] + start_time = start_date + ' ' + start_hour + + end_date = html.find(attrs={'name':'end'}).attrs['value'] + end_hour = html.find(attrs={'name':'end_time'}).attrs['value'] + end_time = end_date + ' ' + end_hour + return start_time, end_time - # check if any error occured - errors_occured = self.check_for_error(data, error, status_code) - if errors_occured is not False: - return(errors_occured) + else: + start_time = datetime.now().strftime("%m/%d/%Y %H:%M") + end_time = datetime.now() + timedelta(hours=2) + end_time = end_time.strftime("%m/%d/%Y %H:%M") + return start_time, end_time - fqdn = str(data["result"][0]["fqdn"]) + except: + self.Error(sys.exc_info()) + return 'n/a', 'n/a' - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Get Host FQDN or address : ' + host + " / " + fqdn) - # Give back host or ip - return Result(result=fqdn) + def GetHost(self, host): + ''' + Centreonified way to get host ip - attribute 'a' in down hosts xml is of no use for up + hosts so we need to get ip anyway from web page + ''' + # the fastest method is taking hostname as used in monitor + if conf.connect_by_host == True or host == '': + return Result(result=host) + + # do a web interface search limited to only one result - the hostname + cgi_data = {'sid': self.SID, + 'search': host, + 'num': 0, + 'limit': 1, + 'sort_type':'hostname', + 'order': 'ASC', + 'date_time_format_status': 'd/m/Y H:i:s', + 'o': 'h', + 'p': 20102, + 'time': 0} + + centreon_hosts = self.urls_centreon['xml_hosts'] + '?' + urllib.parse.urlencode(cgi_data) + + result = self.FetchURL(centreon_hosts, giveback='xml') + xmlobj, error, status_code = result.result, result.error, result.status_code + + # initialize ip string + ip = '' + + if len(xmlobj) != 0: + ip = str(xmlobj.l.a.text) + # when connection by DNS is not configured do it by IP + try: + if conf.connect_by_dns == True: + # try to get DNS name for ip (reverse DNS), if not available use ip + try: + address = socket.gethostbyaddr(ip)[0] + except: + if conf.debug_mode == True: + self.Debug(server=self.get_name(), debug='Unable to do a reverse DNS lookup on IP: ' + ip) + address = ip + else: + address = ip + except: + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) - except: - import traceback - traceback.print_exc(file=sys.stdout) - # set checking flag back to False - self.isChecking = False + else: result, error = self.Error(sys.exc_info()) - return Result(result=result, error=error) - + return Result(error=error) - def get_host_and_service_id(self, host, service=''): - if service == "": - # Hosts only - # https://demo.centreon.com/centreon/api/latest/monitoring/resources?page=1&limit=30&sort_by={"status_severity_code":"asc","last_status_change":"desc"}&types=["host"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"] - url_hosts = self.urls_centreon['hosts'] + '?types=["host"]&search={"h.name":"' + host + '"}' + del xmlobj - try: - # Get json - result = self.FetchURL(url_hosts, giveback='raw') - - data = json.loads(result.result) - error = result.error - status_code = result.status_code + # print IP in debug mode + if conf.debug_mode == True: + self.Debug(server=self.get_name(), debug='IP of %s:' % (host) + ' ' + address) - # check if any error occured - errors_occured = self.check_for_error(data, error, status_code) - if errors_occured is not False: - return(errors_occured) + # give back host or ip + return Result(result=address) - host_id = data["result"][0]["id"] + def _get_xml_path(self, sid): + ''' + Find out where this instance of Centreon is publishing the status XMLs + Centreon 2.6 + ndo/c.broker - /include/monitoring/status/Hosts/xml/{ndo,broker}/hostXML.php according to configuration + Centreon 2.7 + c.broker - /include/monitoring/status/Hosts/xml/hostXML.php + Centreon 2.8 + c.broker - /include/monitoring/status/Hosts/xml/hostXML.php + regexping HTML for Javascript + ''' + if self.centreon_version <= 2.66: + # 2.6 support NDO and C. Broker, we must check which one is used + cgi_data = {'p':201, 'sid':sid} + result = self.FetchURL(self.monitor_cgi_url + '/main.php', cgi_data=cgi_data, giveback='raw') + raw, error = result.result, result.error + if error == '': + if re.search('var _addrXML.*xml\/ndo\/host', raw): + self.XML_PATH = 'xml/ndo' + if conf.debug_mode == True: + self.Debug(server=self.get_name(), debug='Detected broker : NDO') + elif re.search('var _addrXML.*xml\/broker\/host', raw): + self.XML_PATH = 'xml/broker' + if conf.debug_mode == True: + self.Debug(server=self.get_name(), debug='Detected broker : C. Broker') + else: + if conf.debug_mode == True: + self.Debug(server=self.get_name(), debug='Could not detect the broker for Centeron 2.[3-6]. Using Centreon Broker') + self.XML_PATH = 'xml/broker' + del raw + else: if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Get Host ID : ' + host + " / " + str(host_id)) - return host_id - - except: - import traceback + self.Debug(server=self.get_name(), debug='Unable to fetch the main page to detect the broker : ' + error) + del result, error + else: + if conf.debug_mode == True: + self.Debug(server=self.get_name(), debug='Only Centreon Broker is supported in Centeon >= 2.7 -> XML_PATH='+ self.XML_PATH) + + + def _define_url(self): + urls_centreon_2_2 = { + 'main': self.monitor_cgi_url + '/main.php', + 'index': self.monitor_cgi_url + '/index.php', + 'xml_services': self.monitor_cgi_url + '/include/monitoring/status/Services/' + self.XML_PATH + '/serviceXML.php', + 'xml_hosts': self.monitor_cgi_url + '/include/monitoring/status/Hosts/' + self.XML_PATH + '/hostXML.php', + 'xml_meta': self.monitor_cgi_url + '/include/monitoring/status/Meta/' + self.XML_PATH + '/metaServiceXML.php', + 'xml_hostSendCommand': self.monitor_cgi_url + '/include/monitoring/objectDetails/xml/hostSendCommand.php', + 'xml_serviceSendCommand': self.monitor_cgi_url + '/include/monitoring/objectDetails/xml/serviceSendCommand.php', + 'external_cmd_cmdPopup': self.monitor_cgi_url + '/include/monitoring/external_cmd/cmdPopup.php', + # no idea if this really exist in centreon < 2.7 + 'autologoutXMLresponse': self.monitor_cgi_url + '/include/common/javascript/autologoutXMLresponse.php' + } + # inconsistant url in Centreon 2.7 + urls_centreon_2_7 = { + 'main': self.monitor_cgi_url + '/main.php', + 'index': self.monitor_cgi_url + '/index.php', + 'xml_services': self.monitor_cgi_url + '/include/monitoring/status/Services/' + self.XML_PATH + '/serviceXML.php', + 'xml_hosts': self.monitor_cgi_url + '/include/monitoring/status/Hosts/' + self.XML_PATH + '/broker/hostXML.php', + 'xml_meta': self.monitor_cgi_url + '/include/monitoring/status/Meta/' + self.XML_PATH + '/broker/metaServiceXML.php', + 'xml_hostSendCommand': self.monitor_cgi_url + '/include/monitoring/objectDetails/xml/hostSendCommand.php', + 'xml_serviceSendCommand': self.monitor_cgi_url + '/include/monitoring/objectDetails/xml/serviceSendCommand.php', + 'external_cmd_cmdPopup': self.monitor_cgi_url + '/include/monitoring/external_cmd/cmdPopup.php', + 'autologoutXMLresponse': self.monitor_cgi_url + '/include/common/javascript/autologoutXMLresponse.php' + } + urls_centreon_2_8 = { + 'main': self.monitor_cgi_url + '/main.php', + 'index': self.monitor_cgi_url + '/index.php', + 'xml_services': self.monitor_cgi_url + '/include/monitoring/status/Services/' + self.XML_PATH + '/serviceXML.php', + 'xml_hosts': self.monitor_cgi_url + '/include/monitoring/status/Hosts/' + self.XML_PATH + '/hostXML.php', + 'xml_hostSendCommand': self.monitor_cgi_url + '/include/monitoring/objectDetails/xml/hostSendCommand.php', + 'xml_serviceSendCommand': self.monitor_cgi_url + '/include/monitoring/objectDetails/xml/serviceSendCommand.php', + 'external_cmd_cmdPopup': self.monitor_cgi_url + '/include/monitoring/external_cmd/cmdPopup.php', + 'autologoutXMLresponse': self.monitor_cgi_url + '/include/core/autologout/autologoutXMLresponse.php' + } + urls_centreon_18_10 = { + 'main': self.monitor_cgi_url + '/main.get.php', + # needed to get the frames around the page when opening the monitoring on a host/service + 'main_with_frames': self.monitor_cgi_url + '/main.php', + 'index': self.monitor_cgi_url + '/index.php', + 'xml_services': self.monitor_cgi_url + '/include/monitoring/status/Services/' + self.XML_PATH + '/serviceXML.php', + 'xml_hosts': self.monitor_cgi_url + '/include/monitoring/status/Hosts/' + self.XML_PATH + '/hostXML.php', + 'xml_hostSendCommand': self.monitor_cgi_url + '/include/monitoring/objectDetails/xml/hostSendCommand.php', + 'xml_serviceSendCommand': self.monitor_cgi_url + '/include/monitoring/objectDetails/xml/serviceSendCommand.php', + 'external_cmd_cmdPopup': self.monitor_cgi_url + '/include/monitoring/external_cmd/cmdPopup.php', + 'keepAlive': self.monitor_cgi_url + '/api/internal.php?object=centreon_keepalive&action=keepAlive' + } + if self.centreon_version < 2.7: + self.urls_centreon = urls_centreon_2_2 + elif self.centreon_version == 2.7: + self.urls_centreon = urls_centreon_2_7 + elif self.centreon_version == 2.8: + self.urls_centreon = urls_centreon_2_8 + # 18.10 and beyond + elif self.centreon_version >= 18.10: + self.urls_centreon = urls_centreon_18_10 + if conf.debug_mode == True: + self.Debug(server=self.get_name(), debug='URLs defined for Centreon %s' % (self.centreon_version)) - traceback.print_exc(file=sys.stdout) - # set checking flag back to False - self.isChecking = False - result, error = self.Error(sys.exc_info()) - return Result(result=result, error=error) + def _get_host_id(self, host): + ''' + get host_id via parsing raw html + ''' + if self.centreon_version < 2.7: + cgi_data = {'p': 20102, 'o': 'hd', 'host_name': host, 'sid': self.SID} else: - # Host + Service - if host == "Meta_Services": - url_service = self.urls_centreon['services'] + '?types=["metaservice"]&search={"s.name":"'+service+'"}' - else: - url_service = self.urls_centreon['services'] + '?types=["service"]&search={"$and":[{"h.name":{"$eq":"'+host+'"}}, {"s.description":{"$eq":"'+service+'"}}]}' + cgi_data = {'p': 20202, 'o': 'hd', 'host_name': host, 'sid': self.SID} - try: - # Get json - result = self.FetchURL(url_service, giveback='raw') + url = self.urls_centreon['main'] + '?' + urllib.parse.urlencode(cgi_data) - data = json.loads(result.result) - error = result.error - status_code = result.status_code + result = self.FetchURL(url, giveback='raw') + raw, error = result.result, result.error - # check if any error occured - errors_occured = self.check_for_error(data, error, status_code) - if errors_occured is not False: - return(errors_occured) + if error == '': + host_id = raw.partition("var host_id = '")[2].partition("'")[0] + del raw + else: + if conf.debug_mode == True: + self.Debug(server=self.get_name(), debug='Host ID could not be retrieved.') - if host == "Meta_Services": - host_id = 0 - else: - host_id = data["result"][0]["parent"]["id"] - service_id = data["result"][0]["id"] + # some cleanup + del result, error + # only if host_id is an usable integer return it + try: + if int(host_id): if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Get Host / Service ID : ' + str(host_id) + " / " + str(service_id)) - return host_id,service_id + self.Debug(server=self.get_name(), host=host, debug='Host ID is ' + host_id) + return host_id + else: + return '' + except: + return '' - except: - import traceback - traceback.print_exc(file=sys.stdout) - # set checking flag back to False - self.isChecking = False - result, error = self.Error(sys.exc_info()) - return Result(result=result, error=error) + def _get_host_and_service_id(self, host, service): + ''' + parse a ton of html to get a host and a service id... + ''' + cgi_data = {'p':'20201',\ + 'host_name':host,\ + 'service_description':service,\ + 'o':'svcd'} + + # This request must be done in a GET, so just encode the parameters and fetch + result = self.FetchURL(self.urls_centreon['main'] + '?' + urllib.parse.urlencode(cgi_data), giveback="raw") + raw, error = result.result, result.error + + if error == '': + host_id = raw.partition("var host_id = '")[2].partition("'")[0] + svc_id = raw.partition("var svc_id = '")[2].partition("'")[0] + del raw + if conf.debug_mode == True: + self.Debug(server=self.get_name(), host=host, service=service, debug='- Get host/svc ID : ' + host_id + '/' + svc_id) + else: + if conf.debug_mode == True: + self.Debug(server=self.get_name(), host=host, service=service, debug='- IDs could not be retrieved.') - def get_start_end(self, host): - # I don’t know how to get this info... - # self.defaults_downtime_duration_hours = 2 - # self.defaults_downtime_duration_minutes = 0 - start = datetime.now() - end = datetime.now() + timedelta(hours=2) + # some cleanup + del result, error - return (str(start.strftime('%Y-%m-%d %H:%M')), - str(end.strftime('%Y-%m-%d %H:%M'))) + # only if host_id is an usable integer return it + try: + if int(host_id) and int(svc_id): + if conf.debug_mode == True: + self.Debug(server=self.get_name(), host=host, service=service, debug='- Host & Service ID are valid (int)') + return host_id,svc_id + else: + return '','' + except: + return '','' def _get_status(self): @@ -296,61 +566,108 @@ def _get_status(self): Get status from Centreon Server ''' # Be sure that the session is still active - result = self.check_session() + result = self._check_session() if result is not None: if result.result == 'ERROR': if 'urls_centreon' in result.error: result.error = 'Connection error' return result - # Services URL - # https://demo.centreon.com/centreon/api/latest/monitoring/resources?page=1&limit=30&sort_by={"status_severity_code":"asc","last_status_change":"desc"}&types=["service"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"] - url_services = self.urls_centreon['services'] + '?types=["metaservice","service"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"]&limit=' + str(self.limit_services_number) - - # Hosts URL - # https://demo.centreon.com/centreon/api/latest/monitoring/resources?page=1&limit=30&sort_by={"status_severity_code":"asc","last_status_change":"desc"}&types=["host"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"] - url_hosts = self.urls_centreon['hosts'] + '?types=["host"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"]&limit=' + str(self.limit_services_number) + # services (unknown, warning or critical?) + if self.centreon_version < 2.7: + nagcgiurl_services = self.urls_centreon['xml_services'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'svcpb', 'sort_type':'status', 'sid':self.SID}) + else: + nagcgiurl_services = self.urls_centreon['xml_services'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'svcpb', 'p':20201, 'nc':0, 'criticality':0, 'statusService':'svcpb', 'sSetOrderInMemory':1, 'sid':self.SID}) + + # hosts (up or down or unreachable) + # define hosts xml URL, because of inconsistant url + if self.centreon_version < 2.7: + nagcgiurl_hosts = self.urls_centreon['xml_hosts'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'hpb', 'sort_type':'status', 'sid':self.SID}) + elif self.centreon_version >= 2.7 and self.centreon_version < 19.04: + nagcgiurl_hosts = self.urls_centreon['xml_hosts'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'hpb', 'p':20202, 'criticality':0, 'statusHost':'hpb', 'sSetOrderInMemory':1, 'sid':self.SID}) + else: + nagcgiurl_hosts = self.urls_centreon['xml_hosts'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'hpb', 'p':20202, 'criticality':0, 'statusHost':'hpb', 'sSetOrderInMemory':1}) - # Hosts + # hosts - mostly the down ones + # unfortunately the hosts status page has a different structure so + # hosts must be analyzed separately try: - # Get json - result = self.FetchURL(url_hosts, giveback='raw') - - data = json.loads(result.result) - error = result.error - status_code = result.status_code - - # if conf.debug_mode: - # self.Debug(server=self.get_name(), - # debug="Get Hosts status Fetched JSON: " + pprint.pformat(data)) + result = self.FetchURL(nagcgiurl_hosts, giveback='xml') + xmlobj, error, status_code = result.result, result.error, result.status_code # check if any error occured - errors_occured = self.check_for_error(data, error, status_code) - if errors_occured is not False: + errors_occured = self.check_for_error(xmlobj, error, status_code) + + # if there are errors return them + if errors_occured != False: return(errors_occured) - for alerts in data["result"]: - new_host = alerts["name"] - self.new_hosts[new_host] = GenericHost() - self.new_hosts[new_host].name = alerts["name"] - self.new_hosts[new_host].server = self.name - self.new_hosts[new_host].criticality = alerts["severity_level"] - self.new_hosts[new_host].status = alerts["status"]["name"] - self.new_hosts[new_host].last_check = alerts["last_check"] - # last_state_change = datetime.strptime(alerts["last_status_change"], '%Y-%m-%dT%H:%M:%S%z').replace(tzinfo=None) - self.new_hosts[new_host].duration = alerts["duration"] - self.new_hosts[new_host].attempt = alerts["tries"] - self.new_hosts[new_host].status_information = alerts["information"] - self.new_hosts[new_host].passiveonly = alerts["passive_checks"] - self.new_hosts[new_host].notifications_disabled = not alerts["notification_enabled"] - self.new_hosts[new_host].flapping = alerts["flapping"] - self.new_hosts[new_host].acknowledged = alerts["acknowledged"] - self.new_hosts[new_host].scheduled_downtime = alerts["in_downtime"] - if "(S)" in alerts["tries"]: - self.new_hosts[new_host].status_type = self.HARD_SOFT['(S)'] - else: - self.new_hosts[new_host].status_type = self.HARD_SOFT['(H)'] - self.Debug(server='[' + self.get_name() + ']', debug='Host indexed : ' + new_host) + # Check if the result is not empty + if len(xmlobj) == 0: + if conf.debug_mode == True: + self.Debug(server=self.get_name(), debug='Empty host XML result') + return Result(result=None, error="Empty host XML result") + + # in case there are no children session ID is expired + if xmlobj.text.lower() == 'bad session id': + del xmlobj + if conf.debug_mode == True: + self.Debug(server=self.get_name(), debug='Bad session ID, retrieving new one...') + + # try again... + self.SID = self._get_sid().result + result = self.FetchURL(nagcgiurl_hosts, giveback='xml') + xmlobj, error, status_code = result.result, result.error, result.status_code + errors_occured = self.check_for_error(xmlobj, error, status_code) + # if there are errors return them + if errors_occured != False: + return(errors_occured) + + # a second time a bad session id should raise an error + if xmlobj.text.lower() == 'bad session id': + if conf.debug_mode == True: + self.Debug(server=self.get_name(), debug='Even after renewing session ID, unable to get the XML') + return Result(result='ERROR', + error='Bad session ID', + status_code=status_code) + + for l in xmlobj.findAll('l'): + try: + # host objects contain service objects + if not l.hn.text in self.new_hosts: + self.new_hosts[str(l.hn.text)] = GenericHost() + self.new_hosts[str(l.hn.text)].name = str(l.hn.text) + self.new_hosts[str(l.hn.text)].server = self.name + self.new_hosts[str(l.hn.text)].status = str(l.cs.text) + # disgusting workaround for https://github.com/HenriWahl/Nagstamon/issues/91 + if self.new_hosts[str(l.hn.text)].status in self.TRANSLATIONS: + self.new_hosts[str(l.hn.text)].status = self.TRANSLATIONS[self.new_hosts[str(l.hn.text)].status] + self.new_hosts[str(l.hn.text)].attempt, self.new_hosts[str(l.hn.text)].status_type = str(l.tr.text).split(' ') + self.new_hosts[str(l.hn.text)].status_type = self.HARD_SOFT[self.new_hosts[str(l.hn.text)].status_type] + self.new_hosts[str(l.hn.text)].last_check = str(l.lc.text) + self.new_hosts[str(l.hn.text)].duration = str(l.lsc.text) + self.new_hosts[str(l.hn.text)].status_information = str(l.ou.text).replace('\n', ' ').strip() + if l.find('cih') != None: + self.new_hosts[str(l.hn.text)].criticality = str(l.cih.text) + else: + self.new_hosts[str(l.hn.text)].criticality = '' + self.new_hosts[str(l.hn.text)].acknowledged = bool(int(str(l.ha.text))) + self.new_hosts[str(l.hn.text)].scheduled_downtime = bool(int(str(l.hdtm.text))) + if l.find('is') != None: + self.new_hosts[str(l.hn.text)].flapping = bool(int(str(l.find('is').text))) + else: + self.new_hosts[str(l.hn.text)].flapping = False + self.new_hosts[str(l.hn.text)].notifications_disabled = not bool(int(str(l.ne.text))) + self.new_hosts[str(l.hn.text)].passiveonly = not bool(int(str(l.ace.text))) + except: + import traceback + traceback.print_exc(file=sys.stdout) + # set checking flag back to False + self.isChecking = False + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) + + del xmlobj except: import traceback @@ -360,59 +677,155 @@ def _get_status(self): result, error = self.Error(sys.exc_info()) return Result(result=result, error=error) - # Services + # services try: - # Get json - result = self.FetchURL(url_services, giveback='raw') - - data = json.loads(result.result) - error = result.error - status_code = result.status_code - - # if conf.debug_mode: - # self.Debug(server=self.get_name(), - # debug="Get Services status Fetched JSON: " + pprint.pformat(data)) + result = self.FetchURL(nagcgiurl_services, giveback='xml') + xmlobj, error, status_code = result.result, result.error, result.status_code # check if any error occured - errors_occured = self.check_for_error(data, error, status_code) - if errors_occured is not False: + errors_occured = self.check_for_error(xmlobj, error, status_code) + # if there are errors return them + if errors_occured != False: return(errors_occured) - for alerts in data["result"]: - if alerts["type"] == "metaservice": - new_host = "Meta_Services" - else: - new_host = alerts["parent"]["name"] - new_service = alerts["name"] - # Needed if non-ok services are on a UP host - if not new_host in self.new_hosts: - self.new_hosts[new_host] = GenericHost() - self.new_hosts[new_host].name = new_host - self.new_hosts[new_host].status = 'UP' - self.new_hosts[new_host].services[new_service] = GenericService() - # Attributs à remplir - self.Debug(server='[' + self.get_name() + ']', debug='Service indexed : ' + new_host + ' / ' + new_service) - - self.new_hosts[new_host].services[new_service].server = self.name - self.new_hosts[new_host].services[new_service].host = new_host - self.new_hosts[new_host].services[new_service].name = new_service - self.new_hosts[new_host].services[new_service].status = alerts["status"]["name"] - self.new_hosts[new_host].services[new_service].last_check = alerts["last_check"] - # last_state_change = datetime.strptime(alerts["last_state_change"], '%Y-%m-%dT%H:%M:%S%z').replace(tzinfo=None) - # self.new_hosts[new_host].services[new_service].duration = datetime.now() - last_state_change - self.new_hosts[new_host].services[new_service].duration = alerts["duration"] - self.new_hosts[new_host].services[new_service].attempt = alerts["tries"] - self.new_hosts[new_host].services[new_service].status_information = alerts["information"] - self.new_hosts[new_host].services[new_service].passiveonly = alerts["passive_checks"] - self.new_hosts[new_host].services[new_service].notifications_disabled = not alerts["notification_enabled"] - self.new_hosts[new_host].services[new_service].flapping = alerts["flapping"] - self.new_hosts[new_host].services[new_service].acknowledged = alerts["acknowledged"] - self.new_hosts[new_host].services[new_service].scheduled_downtime = alerts["in_downtime"] - if "(S)" in alerts["tries"]: - self.new_hosts[new_host].services[new_service].status_type = self.HARD_SOFT['(S)'] + # Check if the result is not empty + if len(xmlobj) == 0: + if conf.debug_mode == True: + self.Debug(server=self.get_name(), debug='Empty service XML result') + return Result(result=None, error="Empty service XML result") + + # in case there are no children session id is invalid + if xmlobj.text.lower() == 'bad session id': + # debug + if conf.debug_mode == True: + self.Debug(server=self.get_name(), debug='Bad session ID, retrieving new one...') + # try again... + self.SID = self._get_sid().result + result = self.FetchURL(nagcgiurl_services, giveback='xml') + xmlobj, error, status_code = result.result, result.error, result.status_code + errors_occured = self.check_for_error(xmlobj, error, status_code) + # if there are errors return them + if errors_occured != False: + return(errors_occured) + + # a second time a bad session id should raise an error + if xmlobj.text.lower() == 'bad session id': + return Result(result='ERROR', + error='Bad session ID', + status_code=status_code) + + # In Centreon 2.8, Meta are merged with regular services + if self.centreon_version < 2.8: + # define meta-services xml URL + if self.centreon_version == 2.7: + nagcgiurl_meta_services = self.urls_centreon['xml_meta'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'meta', 'sort_type':'status', 'sid':self.SID}) else: - self.new_hosts[new_host].services[new_service].status_type = self.HARD_SOFT['(H)'] - self.new_hosts[new_host].services[new_service].criticality = alerts["severity_level"] + nagcgiurl_meta_services = self.urls_centreon['xml_meta'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'meta', 'sort_type':'status', 'sid':self.SID}) + + # retrive meta-services xml STATUS + result_meta = self.FetchURL(nagcgiurl_meta_services, giveback='xml') + xmlobj_meta, error_meta, status_code_meta = result_meta.result, result_meta.error, result_meta.status_code + + # check if any error occured + errors_occured = self.check_for_error(xmlobj_meta, error_meta, status_code_meta) + + # if there are errors return them + if errors_occured != False: + return(errors_occured) + + # a second time a bad session id should raise an error + if xmlobj_meta.text.lower() == 'bad session id': + if conf.debug_mode == True: + self.Debug(server=self.get_name(), debug='Even after renewing session ID, unable to get the XML') + + return Result(result='ERROR', + error='Bad session ID', + status_code=status_code_meta) + + # INSERT META-services xml at the end of the services xml + try: + xmlobj.append(xmlobj_meta.reponse) + except: + import traceback + traceback.print_exc(file=sys.stdout) + # set checking flag back to False + self.isChecking = False + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) + # do some cleanup + del xmlobj_meta + + for l in xmlobj.findAll('l'): + try: + # host objects contain service objects + ###if not self.new_hosts.has_key(str(l.hn.text)): + if not l.hn.text in self.new_hosts: + self.new_hosts[str(l.hn.text)] = GenericHost() + self.new_hosts[str(l.hn.text)].name = str(l.hn.text) + self.new_hosts[str(l.hn.text)].status = 'UP' + # if a service does not exist create its object + if not l.sd.text in self.new_hosts[str(l.hn.text)].services: + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)] = GenericService() + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].host = str(l.hn.text) + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].name = str(l.sd.text) + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].server = self.name + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status = str(l.cs.text) + + if self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].host == '_Module_Meta': + # ajusting service name for Meta services + if self.centreon_version < 2.8: + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].name = '{} ({})'.format(str(l.sd.text), l.rsd.text) + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].attempt = str(l.ca.text) + else: + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].name = '{} ({})'.format(str(l.sdn.text), l.sdl.text) + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].attempt, \ + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_type = str(l.ca.text).split(' ') + else: + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].attempt, \ + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_type = str(l.ca.text).split(' ') + + # disgusting workaround for https://github.com/HenriWahl/Nagstamon/issues/91 + # Still needed in Centreon 2.8 at least : https://github.com/HenriWahl/Nagstamon/issues/344 + # Need enhancement, we can do service state matching with this field <sc>service_unknown</sc> + #if self.centreon_version < 2.66: + if self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status in self.TRANSLATIONS: + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status = self.TRANSLATIONS[\ + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status] + + if not (self.centreon_version < 2.8 and self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].host == '_Module_Meta'): + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_type =\ + self.HARD_SOFT[self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_type] + + if conf.debug_mode == True: + self.Debug(server=self.get_name(), debug='Parsing service XML (Host/Service/Status_type) ' + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].host + '/' + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].name + '/' + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_type) + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].last_check = str(l.lc.text) + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].duration = str(l.d.text) + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_information = str(l.po.text).replace('\n', ' ').strip() + + if l.find('cih') != None: + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].criticality = str(l.cih.text) + else: + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].criticality = '' + + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].acknowledged = bool(int(str(l.pa.text))) + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].notifications_disabled = not bool(int(str(l.ne.text))) + + # for features not available in centreon < 2.8 and meta services + if not (self.centreon_version < 2.8 and self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].host == '_Module_Meta'): + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].scheduled_downtime = bool(int(str(l.dtm.text))) + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].flapping = bool(int(str(l.find('is').text))) + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].passiveonly = not bool(int(str(l.ac.text))) + + except: + import traceback + traceback.print_exc(file=sys.stdout) + # set checking flag back to False + self.isChecking = False + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) + + # do some cleanup + del xmlobj except: import traceback @@ -427,308 +840,265 @@ def _get_status(self): def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=[]): + # decision about host or service - they have different URLs try: - - acknowledgements = { - "acknowledgement": { - "comment": comment, - "with_services": True, - "is_notify_contacts": notify, - "is_persistent_comment": persistent, - "is_sticky": sticky - }, - "resources": [ - ] - } - - # host if service == '': - host_id = self.get_host_and_service_id(host) - - new_resource = { - "type": "host", - "id": host_id, - "parent": { - "id": None - } - } - - acknowledgements["resources"].append(new_resource) - - # Post json - json_string = json.dumps(acknowledgements) - # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/hosts/{host_id}/acknowledgements - result = self.FetchURL(self.urls_centreon['resources'] + '/acknowledge', cgi_data=json_string, giveback='raw') - - error = result.error - status_code = result.status_code - - if conf.debug_mode: - self.Debug(server='[' + self.get_name() + ']', - debug="Set Ack on Host, status code : " + str(status_code)) + # host + cgi_data = {'cmd': '14', + 'host_name': host, + 'author': author, + 'comment': comment, + 'submit': 'Add', + 'notify': int(notify), + 'persistent': int(persistent), + 'sticky': int(sticky), + 'ackhostservice': '0', + 'en': '1'} + if self.centreon_version < 2.7: + cgi_data['p'] = '20105' + cgi_data['o'] = 'hpb' + else: + cgi_data['p'] = '20202' + cgi_data['o'] = 'hpb' + cgi_data['centreon_token'] = self.centreon_token + # Post + raw = self.FetchURL(self.urls_centreon['main'], cgi_data=cgi_data, giveback='raw') + del raw - # Service + # if host is acknowledged and all services should be to or if a service is acknowledged + # (and all other on this host too) if service != '' or len(all_services) > 0: - if len(all_services) == 0: - all_services = [service] + # service(s) @ host + # if all_services is empty only one service has to be checked - the one clicked + # otherwise if there all services should be acknowledged + if len(all_services) == 0: all_services = [service] + # acknowledge all services on a host for s in all_services: - host_id, service_id = self.get_host_and_service_id(host, service) - - if host == "Meta_Services": - new_resource = { - "type": "metaservice", - "id": service_id, - "parent": { - "id": None - } - } - + cgi_data = {'cmd': '15', + 'host_name': host, + 'author': author, + 'comment': comment, + 'submit': 'Add', + 'notify': int(notify), + 'service_description': s, + 'force_check': '1', + 'persistent': int(persistent), + # following not needed in 18.10, required in wich version ? + 'persistant': int(persistent), + 'sticky': int(sticky), + 'o': 'svcd', + 'en': '1'} + if self.centreon_version < 2.7: + cgi_data['p'] = '20215' else: - new_resource = { - "type": "service", - "id": service_id, - "parent": { - "id": host_id - } - } - - acknowledgements["resources"].append(new_resource) - if conf.debug_mode: - self.Debug(server='[' + self.get_name() + ']', - debug="Stack ack for Host ("+host+") / Service ("+service+")") - - # Post json - json_string = json.dumps(acknowledgements) - # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/services/acknowledgements - result = self.FetchURL(self.urls_centreon['resources'] + '/acknowledge', cgi_data=json_string, giveback='raw') - - error = result.error - status_code = result.status_code - - if conf.debug_mode: - self.Debug(server='[' + self.get_name() + ']', - debug="Set Acks, status code : " + str(status_code)) - + cgi_data['p'] = '20201' + cgi_data['centreon_token'] = self.centreon_token + + # in case of a meta-service, extract the 'rsd' field from the service name : + if host == '_Module_Meta': + m = re.search(r'^.+ \((?P<rsd>.+)\)$', s) + if m: + rsd = m.group('rsd') + if self.centreon_version < 2.8: + cgi_data = {'p': '20206', + 'o': 'meta', + 'cmd': '70', + 'select[' + host + ';' + rsd + ']': '1', + 'limit': '0'} + elif self.centreon_version in [2.8, 18.10]: + cgi_data['service_description'] = rsd + + # POST, for some strange reason only working if giveback is 'raw' + raw = self.FetchURL(self.urls_centreon['main'], cgi_data=cgi_data, giveback='raw') + del raw except: - import traceback - traceback.print_exc(file=sys.stdout) - # set checking flag back to False - self.isChecking = False - result, error = self.Error(sys.exc_info()) - return Result(result=result, error=error) + self.Error(sys.exc_info()) def _set_recheck(self, host, service): - rechecks = { - "resources": [ - ] - } - + ''' + host and service ids are needed to tell Centreon what whe want + ''' try: - # Host - if service == '': - host_id = self.get_host_and_service_id(host) - - new_resource = { - "type": "host", - "id": host_id, - "parent": None - } - - rechecks["resources"].append(new_resource) - - # Post json - json_string = json.dumps(rechecks) - # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/resources/check - result = self.FetchURL(self.urls_centreon['resources'] + '/check', cgi_data=json_string, giveback='raw') + # decision about host or service - they have different URLs + # Meta + if host == '_Module_Meta': + if conf.debug_mode == True: + self.Debug(server=self.get_name(), debug='Recheck on a Meta service, more work to be done') + m = re.search(r'^.+ \((?P<rsd>.+)\)$', service) + if m: + rsd = m.group('rsd') + if self.centreon_version < 2.8: + url = self.urls_centreon['main'] + '?' + urllib.parse.urlencode({'p': '20206','o': 'meta','cmd': '3','select[' + host + ';' + rsd + ']': '1','limit':'0'}) + else: + url = self.urls_centreon['main'] + '?' + urllib.parse.urlencode({'p': '202','o': 'svc','cmd': '3','select[' + host + ';' + rsd + ']': '1','limit':'1','centreon_token':self.centreon_token}) - error = result.error - status_code = result.status_code + elif service == '': + # ... it can only be a host, so check all his services and there is a command for that + host_id = self._get_host_id(host) - if conf.debug_mode: - self.Debug(server='[' + self.get_name() + ']', - debug="Recheck on Host : "+host+", status code : " + str(status_code)) + if self.centreon_version < 2.7: + url = self.urls_centreon['xml_hostSendCommand'] + '?' + urllib.parse.urlencode({'cmd':'host_schedule_check', 'actiontype':1,'host_id':host_id,'sid':self.SID}) + else: + url = self.urls_centreon['xml_hostSendCommand'] + '?' + urllib.parse.urlencode({'cmd':'host_schedule_check', 'actiontype':1,'host_id':host_id}) + del host_id - # Service else: - host_id, service_id = self.get_host_and_service_id(host, service) + # service @ host + host_id, service_id = self._get_host_and_service_id(host, service) - if host == "Meta_Services": - new_resource = { - "type": "metaservice", - "id": service_id, - "parent": None - } + # Starting from 19.04 this must be in POST + if self.centreon_version < 19.04: + # fill and encode URL data + cgi_data = urllib.parse.urlencode({'cmd':'service_schedule_check', 'actiontype':1,\ + 'host_id':host_id, 'service_id':service_id, 'sid':self.SID}) + url = self.urls_centreon['xml_serviceSendCommand'] + '?' + cgi_data + del host_id, service_id else: - new_resource = { - "type": "service", - "id": service_id, - "parent": { - "id": host_id - } - } - - rechecks["resources"].append(new_resource) - - # Post json - json_string = json.dumps(rechecks) - # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/resources/check - result = self.FetchURL(self.urls_centreon['resources'] + '/check', cgi_data=json_string, giveback='raw') - - error = result.error - status_code = result.status_code - - if conf.debug_mode: - self.Debug(server='[' + self.get_name() + ']', - debug="Reckeck on Host ("+host+") / Service ("+service+"), status code : " + str(status_code)) - + cgi_data = {'cmd': 'service_schedule_check', + 'host_id': host_id, + 'service_id': service_id, + 'actiontype': '0'} + del host_id, service_id + + if self.centreon_version < 19.04: + # execute GET request + raw = self.FetchURL(url, giveback='raw') + del raw + else: + # running remote cgi command with POST method, for some strange reason only working if + # giveback is 'raw' + raw = self.FetchURL(self.urls_centreon['xml_serviceSendCommand'], cgi_data=cgi_data, giveback='raw') + del raw except: - import traceback - traceback.print_exc(file=sys.stdout) - # set checking flag back to False - self.isChecking = False - result, error = self.Error(sys.exc_info()) - return Result(result=result, error=error) + self.Error(sys.exc_info()) def _set_downtime(self, host, service, author, comment, fixed, start_time, end_time, hours, minutes): - obj_start_time = datetime.strptime(start_time, '%Y-%m-%d %H:%M') - obj_end_time = datetime.strptime(end_time, '%Y-%m-%d %H:%M') - - # Nagstamon don’t provide the TZ, we need to get it from the OS - obj_start_time = obj_start_time.replace(tzinfo=datetime.now().astimezone().tzinfo) - obj_end_time = obj_end_time.replace(tzinfo=datetime.now().astimezone().tzinfo) - - # duration unit is second - duration = (hours * 3600) + (minutes * 60) - - # API require boolean - if fixed == 1: - fixed = True - else: - fixed = False - - downtimes = { - "downtime": { - "comment": comment, - "with_services": True, - "is_fixed": fixed, - "duration": duration, - "start_time": obj_start_time.strftime('%Y-%m-%dT%H:%M:%S%z'), - "end_time": obj_end_time.strftime('%Y-%m-%dT%H:%M:%S%z') - }, - "resources": [ - ] - } - + ''' + gets actual host and service ids and apply them to downtime cgi + ''' try: - if service == '': - # Host - host_id = self.get_host_and_service_id(host) - - new_resource = { - "type": "host", - "id": host_id, - "parent": None - } - - downtimes["resources"].append(new_resource) - - # Post json - json_string = json.dumps(downtimes) - # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/resources/downtime - result = self.FetchURL(self.urls_centreon['resources'] + '/downtime', cgi_data=json_string, giveback='raw') - - error = result.error - status_code = result.status_code - - if conf.debug_mode: - self.Debug(server='[' + self.get_name() + ']', - debug="Downtime on Host : "+host+", status code : " + str(status_code)) - - # Service + # duration unit is minute + duration = (hours * 60) + minutes + # need cmdPopup.php needs boolean + if fixed == 1: + fixed = 'true' else: - host_id, service_id = self.get_host_and_service_id(host, service) - - if host == "Meta_Services": - new_resource = { - "type": "metaservice", - "id": service_id, - "parent": { - "id": None - } - } + fixed = 'false' + # Host downtime + if service == '': + if self.centreon_version < 19.04: + cgi_data = {'cmd':75,\ + 'duration':duration,\ + 'duration_scale':'m',\ + 'start':start_time,\ + 'end':end_time,\ + 'comment':comment,\ + 'fixed':fixed,\ + 'downtimehostservice':'true',\ + 'author':author,\ + 'sid':self.SID,\ + 'select['+host+']':1 + } + # Params has changed starting from 19.04 else: - new_resource = { - "type": "service", - "id": service_id, - "parent": { - "id": host_id - } - } - - downtimes["resources"].append(new_resource) - - # Post json - json_string = json.dumps(downtimes) - # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/resources/downtime - result = self.FetchURL(self.urls_centreon['resources'] + '/downtime', cgi_data=json_string, giveback='raw') - - error = result.error - status_code = result.status_code - - if conf.debug_mode: - self.Debug(server='[' + self.get_name() + ']', - debug="Downtime on Host ("+host+") / Service ("+service+"), status code : " + str(status_code)) - + cgi_data = {'cmd':75, + 'duration':duration, + 'duration_scale':'m', + 'comment':comment, + 'start':start_time, + 'end':end_time, + 'host_or_centreon_time':0, + 'fixed':fixed, + 'downtimehostservice':'true', + 'author':author, + 'resources':'["'+host+'"]' + } + + # Service downtime + else: + # Centreon 2.8 only, in case of a meta-service, extract the 'rsd' field from the service name : + if host == '_Module_Meta' and self.centreon_version in [2.8, 18.10]: + m = re.search(r'^.+ \((?P<rsd>.+)\)$', service) + if m: + rsd = m.group('rsd') + service = rsd + if self.centreon_version < 19.04: + cgi_data = {'cmd':74,\ + 'duration':duration,\ + 'duration_scale':'m',\ + 'start':start_time,\ + 'end':end_time,\ + 'comment':comment,\ + 'fixed':fixed,\ + 'downtimehostservice':0,\ + 'author':author,\ + 'sid':self.SID,\ + 'select['+host+';'+service+']':1 + } + + # Params has changed starting from 19.04 + else: + cgi_data = {'cmd':74, + 'duration':duration, + 'duration_scale':'m', + 'comment':comment, + 'start':start_time, + 'end':end_time, + 'host_or_centreon_time':0, + 'fixed':fixed, + 'downtimehostservice':0, + 'author':author, + 'resources':'["'+host+'%3B'+service+'"]' + } + + if self.centreon_version < 19.04: + # This request must be done in a GET, so just encode the parameters and fetch + raw = self.FetchURL(self.urls_centreon['external_cmd_cmdPopup'] + '?' + urllib.parse.urlencode(cgi_data), giveback="raw") + del raw + # Starting from 19.04, must be POST + else: + # Do it in POST + raw = self.FetchURL(self.urls_centreon['external_cmd_cmdPopup'], cgi_data=cgi_data, giveback='raw') + del raw except: - import traceback - traceback.print_exc(file=sys.stdout) - # set checking flag back to False - self.isChecking = False - result, error = self.Error(sys.exc_info()) - return Result(result=result, error=error) + self.Error(sys.exc_info()) - def check_session(self): + def _check_session(self): if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Checking session status') - # Not needed anymore as URLs are set at start - # if 'url_centreon' not in self.__dict__: - # self.init_config() + self.Debug(server=self.get_name(), debug='Checking session status') + if 'url_centreon' not in self.__dict__: + self.init_config() try: - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Check-session, the token will be deleted if it has not been used for more than one hour. Current Token = ' + self.token ) - - cgi_data = {'limit':'0'} - self.session = requests.Session() - self.session.headers['Content-Type'] = 'application/json' - self.session.headers['X-Auth-Token'] = self.token - - # Get en empty service list, to check the status of the current token - # This request must be done in a GET, so just encode the parameters and fetch - result = self.FetchURL(self.urls_centreon['resources'] + '?' + urllib.parse.urlencode(cgi_data), giveback="raw") - - data = json.loads(result.result) - error = result.error - status_code = result.status_code - - if conf.debug_mode: - self.Debug(server=self.get_name(), - debug="Check-session, Fetched JSON: " + pprint.pformat(data)) - self.Debug(server=self.get_name(), - debug="Check-session, Error : " + error + " Status code : " + str(status_code)) - - # If we got an 401, the token expired and must be renewed - if status_code == 401: - self.token = self.get_token().result + if self.centreon_version >= 18.10: + result = self.FetchURL(self.urls_centreon['keepAlive'], giveback='raw') + self.raw, self.error, self.status_code = result.result, result.error, result.status_code + # Return 200 & null a session is open + if conf.debug_mode == True: + self.Debug(server=self.get_name(), debug='Session status : ' + self.raw + ', http code : ' + str(self.status_code)) + # 401 if no valid session is present + if self.status_code == 401: + self.SID = self._get_sid().result + if conf.debug_mode == True: + self.Debug(server=self.get_name(), debug='Session renewed') + + else: + result = self.FetchURL(self.urls_centreon['autologoutXMLresponse'], giveback='xml') + xmlobj, error, status_code = result.result, result.error, result.status_code + self.session_state = xmlobj.find("state").text.lower() if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Check-session, session renewed') + self.Debug(server=self.get_name(), debug='Session status : ' + self.session_state) + if self.session_state == "nok": + self.SID = self._get_sid().result + if conf.debug_mode == True: + self.Debug(server=self.get_name(), debug='Session renewed') except: import traceback From a0f0abfa3a83fb9637dd43f7577b59bf50b9206f Mon Sep 17 00:00:00 2001 From: Benoit Poulet <benoit.poulet@businessdecision.com> Date: Fri, 3 Jun 2022 11:45:47 +0200 Subject: [PATCH 286/884] Create CentreonAPI.py --- Nagstamon/Servers/CentreonAPI.py | 737 +++++++++++++++++++++++++++++++ 1 file changed, 737 insertions(+) create mode 100644 Nagstamon/Servers/CentreonAPI.py diff --git a/Nagstamon/Servers/CentreonAPI.py b/Nagstamon/Servers/CentreonAPI.py new file mode 100644 index 000000000..d7989ece9 --- /dev/null +++ b/Nagstamon/Servers/CentreonAPI.py @@ -0,0 +1,737 @@ +# encoding: utf-8 + +# Nagstamon - Nagios status monitor for your desktop +# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +import urllib.request +import urllib.parse +import urllib.error +import socket +import sys +import re +import copy +# API V2 +import pprint +import json +import requests + +from datetime import datetime, timedelta + +from Nagstamon.Objects import * +from Nagstamon.Servers.Generic import GenericServer +from Nagstamon.Config import conf +from Nagstamon.Helpers import webbrowser_open + +# This class support Centreon V2 API + +# Things to do : +# - change the way host/services status is gathered, must change it +# to use 'ressources'. + +class CentreonServer(GenericServer): + TYPE = 'Centreon API' + + # Centreon API uses a token + token = None + + # HARD/SOFT state mapping + HARD_SOFT = {'(H)': 'hard', '(S)': 'soft'} + + # Entries for monitor default actions in context menu + MENU_ACTIONS = ['Monitor', 'Recheck', 'Acknowledge', 'Downtime'] + + # URLs of the Centreon pages + urls_centreon = None + + # limit number of services retrived + limit_services_number = 9999 + + def init_config(self): + ''' + init_config, called at thread start, not really needed here, just omit extra properties + ''' + + # FIX but be provided by user as their is no way to detect it + self.user_provided_centreon_version = "20.04" + + if re.search('2(0|1|2)\.(04|10)', self.user_provided_centreon_version): + # from 20.04 to 22.04 the API to use is «latest» + self.centreon_version = 20.04 + if conf.debug_mode is True: + self.Debug(server='[' + self.get_name() + ']', debug='Centreon version selected : 20.04 <=> 22.04') + # URLs for browser shortlinks/buttons on popup window + self.BROWSER_URLS = {'monitor': '$MONITOR$/monitoring/resources', + 'hosts': '$MONITOR$/monitoring/resources', + 'services': '$MONITOR$/monitoring/resources', + 'history': '$MONITOR$/main.php?p=20301'} + # RestAPI version + self.restapi_version = "latest" + + else: + if conf.debug_mode is True: + self.Debug(server='[' + self.get_name() + ']', debug='No Centreon version provided') + + # Changed this because define_url was called 2 times + if not self.tls_error and self.urls_centreon is None: + self.define_url() + + + def init_HTTP(self): + if self.session is None: + GenericServer.init_HTTP(self) + self.session.headers.update({'Content-Type': 'application/json'}) + self.token = self.get_token().result + + + def define_url(self): + urls_centreon_api_v2 = { + 'resources': self.monitor_cgi_url + '/api/' + self.restapi_version + '/monitoring/resources', + 'login': self.monitor_cgi_url + '/api/' + self.restapi_version + '/login', + 'services': self.monitor_cgi_url + '/api/' + self.restapi_version + '/monitoring/resources', + 'hosts': self.monitor_cgi_url + '/api/' + self.restapi_version + '/monitoring/resources' + } + + self.urls_centreon = urls_centreon_api_v2 + if conf.debug_mode == True: + self.Debug(server='[' + self.get_name() + ']', debug='URLs defined for Centreon %s' % (self.centreon_version)) + + + def open_monitor(self, host, service=''): + # Autologin seems deprecated as admin must enable it globaly and use the old pages + # Ex : http://10.66.113.52/centreon/main.php?autologin=1&useralias=admin&token=xxxxxx + # if self.use_autologin is True: + # auth = '&autologin=1&useralias=' + self.username + '&token=' + self.autologin_key + # else: + # auth = '' + # webbrowser_open(self.urls_centreon['resources'] + auth ) + + webbrowser_open(self.urls_centreon['resources'] ) + + + def get_token(self): + try: + cgi_data = { + "security": { + "credentials": { + "login": self.username, + "password": self.password + } + } + } + + # Post json + json_string = json.dumps(cgi_data) + result = self.FetchURL(self.urls_centreon['login'], cgi_data=json_string, giveback='raw') + + data = json.loads(result.result) + error = result.error + status_code = result.status_code + + if conf.debug_mode: + self.Debug(server=self.get_name(), + debug="Fetched JSON: " + pprint.pformat(data)) + + + # check if any error occured + errors_occured = self.check_for_error(data, error, status_code) + if errors_occured is not False: + return(errors_occured) + + token = data["security"]["token"] + # ID of the user is needed by some requests + user_id = data["contact"]["id"] + + if conf.debug_mode == True: + self.Debug(server='[' + self.get_name() + ']', debug='API login : ' + self.username + ' / ' + self.password + ' > Token : ' + token + ' > User ID : ' + str(user_id)) + + self.user_id = user_id + self.session.headers.update({'X-Auth-Token': token}) + return Result(result=token) + + except: + import traceback + traceback.print_exc(file=sys.stdout) + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) + + + def GetHost(self, host): + # https://demo.centreon.com/centreon/api/latest/monitoring/resources?page=1&limit=30&sort_by={"status_severity_code":"asc","last_status_change":"desc"}&types=["host"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"] + url_hosts = self.urls_centreon['hosts'] + '?types=["host"]&search={"h.name":"' + host + '"}' + + try: + # Get json + result = self.FetchURL(url_hosts, giveback='raw') + + data = json.loads(result.result) + error = result.error + status_code = result.status_code + + # check if any error occured + errors_occured = self.check_for_error(data, error, status_code) + if errors_occured is not False: + return(errors_occured) + + fqdn = str(data["result"][0]["fqdn"]) + + if conf.debug_mode == True: + self.Debug(server='[' + self.get_name() + ']', debug='Get Host FQDN or address : ' + host + " / " + fqdn) + + # Give back host or ip + return Result(result=fqdn) + + except: + import traceback + traceback.print_exc(file=sys.stdout) + # set checking flag back to False + self.isChecking = False + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) + + + def get_host_and_service_id(self, host, service=''): + if service == "": + # Hosts only + # https://demo.centreon.com/centreon/api/latest/monitoring/resources?page=1&limit=30&sort_by={"status_severity_code":"asc","last_status_change":"desc"}&types=["host"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"] + url_hosts = self.urls_centreon['hosts'] + '?types=["host"]&search={"h.name":"' + host + '"}' + + try: + # Get json + result = self.FetchURL(url_hosts, giveback='raw') + + data = json.loads(result.result) + error = result.error + status_code = result.status_code + + # check if any error occured + errors_occured = self.check_for_error(data, error, status_code) + if errors_occured is not False: + return(errors_occured) + + host_id = data["result"][0]["id"] + + if conf.debug_mode == True: + self.Debug(server='[' + self.get_name() + ']', debug='Get Host ID : ' + host + " / " + str(host_id)) + return host_id + + except: + import traceback + + + + + + + traceback.print_exc(file=sys.stdout) + # set checking flag back to False + self.isChecking = False + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) + else: + # Host + Service + if host == "Meta_Services": + url_service = self.urls_centreon['services'] + '?types=["metaservice"]&search={"s.name":"'+service+'"}' + else: + url_service = self.urls_centreon['services'] + '?types=["service"]&search={"$and":[{"h.name":{"$eq":"'+host+'"}}, {"s.description":{"$eq":"'+service+'"}}]}' + + try: + # Get json + result = self.FetchURL(url_service, giveback='raw') + + data = json.loads(result.result) + error = result.error + status_code = result.status_code + + # check if any error occured + errors_occured = self.check_for_error(data, error, status_code) + if errors_occured is not False: + return(errors_occured) + + if host == "Meta_Services": + host_id = 0 + else: + host_id = data["result"][0]["parent"]["id"] + service_id = data["result"][0]["id"] + + if conf.debug_mode == True: + self.Debug(server='[' + self.get_name() + ']', debug='Get Host / Service ID : ' + str(host_id) + " / " + str(service_id)) + return host_id,service_id + + except: + import traceback + traceback.print_exc(file=sys.stdout) + # set checking flag back to False + self.isChecking = False + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) + + + def get_start_end(self, host): + # I don’t know how to get this info... + # self.defaults_downtime_duration_hours = 2 + # self.defaults_downtime_duration_minutes = 0 + start = datetime.now() + end = datetime.now() + timedelta(hours=2) + + return (str(start.strftime('%Y-%m-%d %H:%M')), + str(end.strftime('%Y-%m-%d %H:%M'))) + + + def _get_status(self): + ''' + Get status from Centreon Server + ''' + # Be sure that the session is still active + result = self.check_session() + if result is not None: + if result.result == 'ERROR': + if 'urls_centreon' in result.error: + result.error = 'Connection error' + return result + + # Services URL + # https://demo.centreon.com/centreon/api/latest/monitoring/resources?page=1&limit=30&sort_by={"status_severity_code":"asc","last_status_change":"desc"}&types=["service"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"] + url_services = self.urls_centreon['services'] + '?types=["metaservice","service"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"]&limit=' + str(self.limit_services_number) + + # Hosts URL + # https://demo.centreon.com/centreon/api/latest/monitoring/resources?page=1&limit=30&sort_by={"status_severity_code":"asc","last_status_change":"desc"}&types=["host"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"] + url_hosts = self.urls_centreon['hosts'] + '?types=["host"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"]&limit=' + str(self.limit_services_number) + + # Hosts + try: + # Get json + result = self.FetchURL(url_hosts, giveback='raw') + + data = json.loads(result.result) + error = result.error + status_code = result.status_code + + # if conf.debug_mode: + # self.Debug(server=self.get_name(), + # debug="Get Hosts status Fetched JSON: " + pprint.pformat(data)) + + # check if any error occured + errors_occured = self.check_for_error(data, error, status_code) + if errors_occured is not False: + return(errors_occured) + + for alerts in data["result"]: + new_host = alerts["name"] + self.new_hosts[new_host] = GenericHost() + self.new_hosts[new_host].name = alerts["name"] + self.new_hosts[new_host].server = self.name + self.new_hosts[new_host].criticality = alerts["severity_level"] + self.new_hosts[new_host].status = alerts["status"]["name"] + self.new_hosts[new_host].last_check = alerts["last_check"] + # last_state_change = datetime.strptime(alerts["last_status_change"], '%Y-%m-%dT%H:%M:%S%z').replace(tzinfo=None) + self.new_hosts[new_host].duration = alerts["duration"] + self.new_hosts[new_host].attempt = alerts["tries"] + self.new_hosts[new_host].status_information = alerts["information"] + self.new_hosts[new_host].passiveonly = alerts["passive_checks"] + self.new_hosts[new_host].notifications_disabled = not alerts["notification_enabled"] + self.new_hosts[new_host].flapping = alerts["flapping"] + self.new_hosts[new_host].acknowledged = alerts["acknowledged"] + self.new_hosts[new_host].scheduled_downtime = alerts["in_downtime"] + if "(S)" in alerts["tries"]: + self.new_hosts[new_host].status_type = self.HARD_SOFT['(S)'] + else: + self.new_hosts[new_host].status_type = self.HARD_SOFT['(H)'] + self.Debug(server='[' + self.get_name() + ']', debug='Host indexed : ' + new_host) + + except: + import traceback + traceback.print_exc(file=sys.stdout) + # set checking flag back to False + self.isChecking = False + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) + + # Services + try: + # Get json + result = self.FetchURL(url_services, giveback='raw') + + data = json.loads(result.result) + error = result.error + status_code = result.status_code + + # if conf.debug_mode: + # self.Debug(server=self.get_name(), + # debug="Get Services status Fetched JSON: " + pprint.pformat(data)) + + # check if any error occured + errors_occured = self.check_for_error(data, error, status_code) + if errors_occured is not False: + return(errors_occured) + + for alerts in data["result"]: + if alerts["type"] == "metaservice": + new_host = "Meta_Services" + else: + new_host = alerts["parent"]["name"] + new_service = alerts["name"] + # Needed if non-ok services are on a UP host + if not new_host in self.new_hosts: + self.new_hosts[new_host] = GenericHost() + self.new_hosts[new_host].name = new_host + self.new_hosts[new_host].status = 'UP' + self.new_hosts[new_host].services[new_service] = GenericService() + # Attributs à remplir + self.Debug(server='[' + self.get_name() + ']', debug='Service indexed : ' + new_host + ' / ' + new_service) + + self.new_hosts[new_host].services[new_service].server = self.name + self.new_hosts[new_host].services[new_service].host = new_host + self.new_hosts[new_host].services[new_service].name = new_service + self.new_hosts[new_host].services[new_service].status = alerts["status"]["name"] + self.new_hosts[new_host].services[new_service].last_check = alerts["last_check"] + # last_state_change = datetime.strptime(alerts["last_state_change"], '%Y-%m-%dT%H:%M:%S%z').replace(tzinfo=None) + # self.new_hosts[new_host].services[new_service].duration = datetime.now() - last_state_change + self.new_hosts[new_host].services[new_service].duration = alerts["duration"] + self.new_hosts[new_host].services[new_service].attempt = alerts["tries"] + self.new_hosts[new_host].services[new_service].status_information = alerts["information"] + self.new_hosts[new_host].services[new_service].passiveonly = alerts["passive_checks"] + self.new_hosts[new_host].services[new_service].notifications_disabled = not alerts["notification_enabled"] + self.new_hosts[new_host].services[new_service].flapping = alerts["flapping"] + self.new_hosts[new_host].services[new_service].acknowledged = alerts["acknowledged"] + self.new_hosts[new_host].services[new_service].scheduled_downtime = alerts["in_downtime"] + if "(S)" in alerts["tries"]: + self.new_hosts[new_host].services[new_service].status_type = self.HARD_SOFT['(S)'] + else: + self.new_hosts[new_host].services[new_service].status_type = self.HARD_SOFT['(H)'] + self.new_hosts[new_host].services[new_service].criticality = alerts["severity_level"] + + except: + import traceback + traceback.print_exc(file=sys.stdout) + # set checking flag back to False + self.isChecking = False + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) + + # return True if all worked well + return Result() + + + def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=[]): + try: + + acknowledgements = { + "acknowledgement": { + "comment": comment, + "with_services": True, + "is_notify_contacts": notify, + "is_persistent_comment": persistent, + "is_sticky": sticky + }, + "resources": [ + ] + } + + # host + if service == '': + host_id = self.get_host_and_service_id(host) + + new_resource = { + "type": "host", + "id": host_id, + "parent": { + "id": None + } + } + + acknowledgements["resources"].append(new_resource) + + # Post json + json_string = json.dumps(acknowledgements) + # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/hosts/{host_id}/acknowledgements + result = self.FetchURL(self.urls_centreon['resources'] + '/acknowledge', cgi_data=json_string, giveback='raw') + + error = result.error + status_code = result.status_code + + if conf.debug_mode: + self.Debug(server='[' + self.get_name() + ']', + debug="Set Ack on Host, status code : " + str(status_code)) + + + # Service + if service != '' or len(all_services) > 0: + if len(all_services) == 0: + all_services = [service] + + for s in all_services: + host_id, service_id = self.get_host_and_service_id(host, service) + + if host == "Meta_Services": + new_resource = { + "type": "metaservice", + "id": service_id, + "parent": { + "id": None + } + } + + else: + new_resource = { + "type": "service", + "id": service_id, + "parent": { + "id": host_id + } + } + + acknowledgements["resources"].append(new_resource) + if conf.debug_mode: + self.Debug(server='[' + self.get_name() + ']', + debug="Stack ack for Host ("+host+") / Service ("+service+")") + + # Post json + json_string = json.dumps(acknowledgements) + # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/services/acknowledgements + result = self.FetchURL(self.urls_centreon['resources'] + '/acknowledge', cgi_data=json_string, giveback='raw') + + error = result.error + status_code = result.status_code + + if conf.debug_mode: + self.Debug(server='[' + self.get_name() + ']', + debug="Set Acks, status code : " + str(status_code)) + + except: + import traceback + traceback.print_exc(file=sys.stdout) + # set checking flag back to False + self.isChecking = False + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) + + + def _set_recheck(self, host, service): + rechecks = { + "resources": [ + ] + } + + try: + # Host + if service == '': + host_id = self.get_host_and_service_id(host) + + new_resource = { + "type": "host", + "id": host_id, + "parent": None + } + + rechecks["resources"].append(new_resource) + + # Post json + json_string = json.dumps(rechecks) + # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/resources/check + result = self.FetchURL(self.urls_centreon['resources'] + '/check', cgi_data=json_string, giveback='raw') + + error = result.error + status_code = result.status_code + + if conf.debug_mode: + self.Debug(server='[' + self.get_name() + ']', + debug="Recheck on Host : "+host+", status code : " + str(status_code)) + + # Service + else: + host_id, service_id = self.get_host_and_service_id(host, service) + + if host == "Meta_Services": + new_resource = { + "type": "metaservice", + "id": service_id, + "parent": None + } + + else: + new_resource = { + "type": "service", + "id": service_id, + "parent": { + "id": host_id + } + } + + rechecks["resources"].append(new_resource) + + # Post json + json_string = json.dumps(rechecks) + # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/resources/check + result = self.FetchURL(self.urls_centreon['resources'] + '/check', cgi_data=json_string, giveback='raw') + + error = result.error + status_code = result.status_code + + if conf.debug_mode: + self.Debug(server='[' + self.get_name() + ']', + debug="Reckeck on Host ("+host+") / Service ("+service+"), status code : " + str(status_code)) + + except: + import traceback + traceback.print_exc(file=sys.stdout) + # set checking flag back to False + self.isChecking = False + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) + + + def _set_downtime(self, host, service, author, comment, fixed, start_time, end_time, hours, minutes): + obj_start_time = datetime.strptime(start_time, '%Y-%m-%d %H:%M') + obj_end_time = datetime.strptime(end_time, '%Y-%m-%d %H:%M') + + # Nagstamon don’t provide the TZ, we need to get it from the OS + obj_start_time = obj_start_time.replace(tzinfo=datetime.now().astimezone().tzinfo) + obj_end_time = obj_end_time.replace(tzinfo=datetime.now().astimezone().tzinfo) + + # duration unit is second + duration = (hours * 3600) + (minutes * 60) + + # API require boolean + if fixed == 1: + fixed = True + else: + fixed = False + + downtimes = { + "downtime": { + "comment": comment, + "with_services": True, + "is_fixed": fixed, + "duration": duration, + "start_time": obj_start_time.strftime('%Y-%m-%dT%H:%M:%S%z'), + "end_time": obj_end_time.strftime('%Y-%m-%dT%H:%M:%S%z') + }, + "resources": [ + ] + } + + try: + if service == '': + # Host + host_id = self.get_host_and_service_id(host) + + new_resource = { + "type": "host", + "id": host_id, + "parent": None + } + + downtimes["resources"].append(new_resource) + + # Post json + json_string = json.dumps(downtimes) + # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/resources/downtime + result = self.FetchURL(self.urls_centreon['resources'] + '/downtime', cgi_data=json_string, giveback='raw') + + error = result.error + status_code = result.status_code + + if conf.debug_mode: + self.Debug(server='[' + self.get_name() + ']', + debug="Downtime on Host : "+host+", status code : " + str(status_code)) + + # Service + else: + host_id, service_id = self.get_host_and_service_id(host, service) + + if host == "Meta_Services": + new_resource = { + "type": "metaservice", + "id": service_id, + "parent": { + "id": None + } + } + + else: + new_resource = { + "type": "service", + "id": service_id, + "parent": { + "id": host_id + } + } + + downtimes["resources"].append(new_resource) + + # Post json + json_string = json.dumps(downtimes) + # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/resources/downtime + result = self.FetchURL(self.urls_centreon['resources'] + '/downtime', cgi_data=json_string, giveback='raw') + + error = result.error + status_code = result.status_code + + if conf.debug_mode: + self.Debug(server='[' + self.get_name() + ']', + debug="Downtime on Host ("+host+") / Service ("+service+"), status code : " + str(status_code)) + + + except: + import traceback + traceback.print_exc(file=sys.stdout) + # set checking flag back to False + self.isChecking = False + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) + + + def check_session(self): + if conf.debug_mode == True: + self.Debug(server='[' + self.get_name() + ']', debug='Checking session status') + # Not needed anymore as URLs are set at start + # if 'url_centreon' not in self.__dict__: + # self.init_config() + try: + if conf.debug_mode == True: + self.Debug(server='[' + self.get_name() + ']', debug='Check-session, the token will be deleted if it has not been used for more than one hour. Current Token = ' + self.token ) + + cgi_data = {'limit':'0'} + self.session = requests.Session() + self.session.headers['Content-Type'] = 'application/json' + self.session.headers['X-Auth-Token'] = self.token + + # Get en empty service list, to check the status of the current token + # This request must be done in a GET, so just encode the parameters and fetch + result = self.FetchURL(self.urls_centreon['resources'] + '?' + urllib.parse.urlencode(cgi_data), giveback="raw") + + data = json.loads(result.result) + error = result.error + status_code = result.status_code + + if conf.debug_mode: + self.Debug(server=self.get_name(), + debug="Check-session, Fetched JSON: " + pprint.pformat(data)) + self.Debug(server=self.get_name(), + debug="Check-session, Error : " + error + " Status code : " + str(status_code)) + + # If we got an 401, the token expired and must be renewed + if status_code == 401: + self.token = self.get_token().result + if conf.debug_mode == True: + self.Debug(server='[' + self.get_name() + ']', debug='Check-session, session renewed') + + except: + import traceback + traceback.print_exc(file=sys.stdout) + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) From e0529f1f54c8388185c156ddacfa85cd520dbef1 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 4 Jun 2022 21:46:24 +0200 Subject: [PATCH 287/884] stopped crash at exit --- Nagstamon/Config.py | 2 +- Nagstamon/QUI/__init__.py | 96 ++++++++++++++++++++++++++------------- build/debian/changelog | 4 +- nagstamon.py | 4 +- 4 files changed, 68 insertions(+), 38 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 910c26ceb..50fb2279c 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20220516' + VERSION = '3.9-20220604' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 2b54175d6..ac05dfb81 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -111,6 +111,7 @@ except AttributeError: pass QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True) + # global application instance APP = QApplication(sys.argv) @@ -1087,9 +1088,12 @@ def __init__(self): check_version.version_info_retrieved.connect(self.hide_window) # worker and thread duo needed for notifications - self.worker_notification_thread = QThread(self) + self.worker_notification_thread = QThread(parent=self) self.worker_notification = self.Worker_Notification() + # clean shutdown of thread + self.worker_notification.finish.connect(self.finish_worker_notification_thread) + # flashing statusbar self.worker_notification.start_flash.connect(self.statusbar.flash) self.worker_notification.stop_flash.connect(self.statusbar.reset) @@ -1148,7 +1152,7 @@ def __init__(self): # a thread + worker is necessary to do actions thread-safe in background # like debugging - self.worker_thread = QThread(self) + self.worker_thread = QThread(parent=self) self.worker = self.Worker() self.worker.moveToThread(self.worker_thread) # start thread and debugging loop if debugging is enabled @@ -1159,6 +1163,9 @@ def __init__(self): # start with low priority self.worker_thread.start(QThread.Priority.LowestPriority) + # clean shutdown of thread + self.worker.finish.connect(self.finish_worker_thread) + # part of the stupid workaround for Qt-5.10-Windows-QSystemTrayIcon-madness self.connect_systrayicon() @@ -1174,7 +1181,7 @@ def connect_systrayicon(self): ''' global menu # show status popup when systray icon was clicked - systrayicon.show_popwin.connect(self.show_window_systrayicon) + systrayicon.show_popwin.connect(self.show_window_systrayicon) systrayicon.hide_popwin.connect(self.hide_window) # flashing statusicon self.worker_notification.start_flash.connect(systrayicon.flash) @@ -2051,10 +2058,10 @@ def show_message(self, msg_type, message): """ title = " ".join((AppInfo.NAME, msg_type)) if msg_type == 'warning': - return QMessageBox.Icon.Warning(statuswindow, title, message) + return QMessageBox(QMessageBox.Icon.Warning, title, message, parent=statuswindow).show() elif msg_type == 'information': - return QMessageBox.Icon.Information(statuswindow, title, message) + return QMessageBox(QMessageBox.Icon.Information, title, message, parent=statuswindow).show() @Slot() def recheck_all(self): @@ -2148,11 +2155,33 @@ def kill(self): self.servers_scrollarea_widget.deleteLater() return self.deleteLater() - class Worker(QObject): + @Slot() + def finish_worker_thread(self): + """ + attempt to shutdown thread cleanly + """ + # tell thread to quit + self.worker_thread.quit() + # wait until thread is really stopped + self.worker_thread.wait() + + @Slot() + def finish_worker_notification_thread(self): + """ + attempt to shutdown thread cleanly + """ + # tell thread to quit + self.worker_notification_thread.quit() + # wait until thread is really stopped + self.worker_notification_thread.wait() + + class Worker(QObject): """ run a thread for example for debugging """ + # send signal if ready to stop + finish = Signal() def __init__(self): QObject.__init__(self) @@ -2232,6 +2261,9 @@ class Worker_Notification(QObject): # desktop notification needs to store count of states status_count = dict() + # send signal if ready to stop + finish = Signal() + def __init__(self): QObject.__init__(self) @@ -3256,7 +3288,7 @@ def __init__(self, columncount, rowcount, sort_column, sort_order, server, paren # a thread + worker is necessary to get new monitor server data in the background and # to refresh the table cell by cell after new data is available - self.worker_thread = QThread(self) + self.worker_thread = QThread(parent=self) self.worker = self.Worker(server=server, sort_column=self.sort_column, sort_order=self.sort_order) self.worker.moveToThread(self.worker_thread) @@ -4000,11 +4032,7 @@ def finish_worker_thread(self): attempt to shutdown thread cleanly """ # tell thread to quit - if OS == OS_WINDOWS: - self.worker_thread.quit() - else: - # tell thread to quit with force - self.worker_thread.terminate() + self.worker_thread.quit() # wait until thread is really stopped self.worker_thread.wait() @@ -4156,7 +4184,7 @@ def get_status(self): self.server.thread_counter += 1 # if running flag is still set call myself after 1 second - if self.running is True: + if self.running: self.timer.singleShot(1000, self.get_status) else: # tell treeview to finish worker_thread @@ -5155,15 +5183,18 @@ def ok(self): server_vbox.table.worker.finish.emit() # wait until all threads are stopped for server_vbox in statuswindow.servers_vbox.children(): - server_vbox.table.worker_thread.terminate() + #server_vbox.table.worker_thread.terminate() + server_vbox.table.worker_thread.quit() server_vbox.table.worker_thread.wait() # wait until statuswindow worker has finished - statuswindow.worker_thread.terminate() + #statuswindow.worker_thread.terminate() + statuswindow.worker_thread.quit() statuswindow.worker_thread.wait() # wait until statuswindow notification worker has finished - statuswindow.worker_notification_thread.terminate() + #statuswindow.worker_notification_thread.terminate() + statuswindow.worker_notification_thread.quit() statuswindow.worker_notification_thread.wait() # kick out ol' statuswindow @@ -6815,7 +6846,7 @@ def check(self, start_mode=False, parent=None): self.parent = parent # thread for worker to avoid - self.worker_thread = QThread(self) + self.worker_thread = QThread(parent=self) self.worker = self.Worker() # if update check is ready it sends the message to GUI thread @@ -7070,30 +7101,31 @@ def exit(): # store position of statuswindow/statusbar statuswindow.store_position_to_conf() + # save configuration + conf.SaveConfig() + # hide statuswindow first ro avoid lag when waiting for finished threads statuswindow.hide() - # stop statuswindow worker - statuswindow.worker.running = False - - # save configuration - conf.SaveConfig() + # stop statuswindow workers + statuswindow.worker.finish.emit() + statuswindow.worker_notification.finish.emit() # tell all treeview threads to stop for server_vbox in statuswindow.servers_vbox.children(): server_vbox.table.worker.finish.emit() - # delete all windows - for dialog in dialogs.__dict__.values(): - try: - dialog.window().destroy() - except Exception: - dialog.window.destroy() - statuswindow.destroy() - + APP.exit() + # # delete all windows + # for dialog in dialogs.__dict__.values(): + # try: + # dialog.window().destroy() + # except: + # dialog.window.destroy() + # statuswindow.destroy() + # # bye bye - APP.instance().quit() - + #APP.instance().quit() def check_servers(): """ diff --git a/build/debian/changelog b/build/debian/changelog index 5a42e9ef1..04a9ece69 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.9-20220516) unstable; urgency=low +nagstamon (3.9-20220604) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Wed, 08 Dec 2021 08:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Sat, 04 Jun 2022 08:00:00 +0100 nagstamon (3.8.0) stable; urgency=low * New upstream diff --git a/nagstamon.py b/nagstamon.py index 9d78ebb41..1191180de 100755 --- a/nagstamon.py +++ b/nagstamon.py @@ -64,10 +64,8 @@ # debug print(f'Using {QT_FLAVOR} {QT_VERSION_STR}') - APP.exec() - sys.exit(0) + sys.exit(APP.exec()) except Exception as err: import traceback - traceback.print_exc(file=sys.stdout) From 60df769867418313bea98d24c231ed64710ad6f9 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 4 Jun 2022 22:03:42 +0200 Subject: [PATCH 288/884] disable nonworking appimage --- .github/workflows/build-release-latest.yml | 28 ++++++++++----------- Nagstamon/QUI/__init__.py | 29 +++++++++++++--------- 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 9ac07a8a4..8cde355ee 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -5,7 +5,7 @@ on: branches: '**' env: - python_win_version: 3.9.12 + python_win_version: 3.10.4 repo_dir: nagstamon-jekyll/docs/repo jobs: @@ -37,19 +37,19 @@ jobs: run: | python -m unittest tests/test_*.py - appimage: - runs-on: ubuntu-latest - #needs: test - steps: - - uses: actions/checkout@v2 - - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - # higher privileges needed for creation of AppImage - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon --cap-add SYS_ADMIN --cap-add MKNOD --device /dev/fuse:mrw build-nagstamon - - uses: actions/upload-artifact@v2 - with: - path: build/*.AppImage - retention-days: 1 - if-no-files-found: error +# appimage: +# runs-on: ubuntu-latest +# #needs: test +# steps: +# - uses: actions/checkout@v2 +# - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . +# # higher privileges needed for creation of AppImage +# - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon --cap-add SYS_ADMIN --cap-add MKNOD --device /dev/fuse:mrw build-nagstamon +# - uses: actions/upload-artifact@v2 +# with: +# path: build/*.AppImage +# retention-days: 1 +# if-no-files-found: error debian: runs-on: ubuntu-latest diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index ac05dfb81..3ab874a53 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -5171,7 +5171,7 @@ def ok(self): # increase number of display changes for silly Windows-hides-statusbar-after-display-mode-change problem NUMBER_OF_DISPLAY_CHANGES += 1 - # stop statuswindow worker + # stop statuswindow workers statuswindow.worker.running = False statuswindow.worker_notification.running = False @@ -5181,21 +5181,26 @@ def ok(self): # tell all treeview threads to stop for server_vbox in statuswindow.servers_vbox.children(): server_vbox.table.worker.finish.emit() - # wait until all threads are stopped - for server_vbox in statuswindow.servers_vbox.children(): - #server_vbox.table.worker_thread.terminate() - server_vbox.table.worker_thread.quit() - server_vbox.table.worker_thread.wait() - # wait until statuswindow worker has finished - #statuswindow.worker_thread.terminate() - statuswindow.worker_thread.quit() - statuswindow.worker_thread.wait() + # stop statuswindow workers + statuswindow.worker.finish.emit() + statuswindow.worker_notification.finish.emit() + + # # wait until all threads are stopped + # for server_vbox in statuswindow.servers_vbox.children(): + # #server_vbox.table.worker_thread.terminate() + # server_vbox.table.worker_thread.quit() + # server_vbox.table.worker_thread.wait() + # + # # wait until statuswindow worker has finished + # #statuswindow.worker_thread.terminate() + # statuswindow.worker_thread.quit() + # statuswindow.worker_thread.wait() # wait until statuswindow notification worker has finished #statuswindow.worker_notification_thread.terminate() - statuswindow.worker_notification_thread.quit() - statuswindow.worker_notification_thread.wait() + # statuswindow.worker_notification_thread.quit() + # statuswindow.worker_notification_thread.wait() # kick out ol' statuswindow statuswindow.kill() From 253488a36ec941c09136ab442696c3eda0c111f4 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 5 Jun 2022 00:40:36 +0200 Subject: [PATCH 289/884] modifiers unified --- Nagstamon/QUI/__init__.py | 262 ++++++++++++++++------------------ Nagstamon/QUI/qt.py | 44 ++++-- Nagstamon/Servers/Centreon.py | 2 +- 3 files changed, 153 insertions(+), 155 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 3ab874a53..35e4d3a6f 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -111,7 +111,7 @@ except AttributeError: pass QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True) - + # global application instance APP = QApplication(sys.argv) @@ -970,7 +970,7 @@ def __init__(self): Status window combined from status bar and popup window """ # attempt with desktop as parent for window Qt.Tool - #QWidget.__init__(self, parent=APP.desktop()) + # QWidget.__init__(self, parent=APP.desktop()) QWidget.__init__(self) # immediately hide to avoid flicker on Windows and OSX @@ -1181,7 +1181,7 @@ def connect_systrayicon(self): ''' global menu # show status popup when systray icon was clicked - systrayicon.show_popwin.connect(self.show_window_systrayicon) + systrayicon.show_popwin.connect(self.show_window_systrayicon) systrayicon.hide_popwin.connect(self.hide_window) # flashing statusicon self.worker_notification.start_flash.connect(systrayicon.flash) @@ -2175,7 +2175,6 @@ def finish_worker_notification_thread(self): # wait until thread is really stopped self.worker_notification_thread.wait() - class Worker(QObject): """ run a thread for example for debugging @@ -3400,37 +3399,6 @@ def adjust_table(self): # after setting table whole window can be repainted self.ready_to_resize.emit() - def mouseReleaseEvent_X(self, event): - """ - forward clicked cell info from event - """ - # special treatment if window should be closed when left-clicking somewhere - # it is important to check if CTRL or SHIFT key is presses while clicking to select lines - if conf.close_details_clicking_somewhere: - if event.button() == Qt.MouseButton.LeftButton: - modifiers = event.modifiers() - # count selected rows - if more than 1 do not close popwin - rows = [] - for index in self.selectedIndexes(): - if index.row() not in rows: - rows.append(index.row()) - if modifiers == Qt.Modifier.CTRL or \ - modifiers == Qt.Modifier.SHIFT or \ - modifiers == (Qt.Modifier.CTRL| Qt.Modifier.SHIFT) or \ - len(rows) > 1: - pass - else: - statuswindow.hide_window() - del modifiers, rows - elif event.button() == Qt.MouseButton.RightButton: - self.cell_clicked() - return - else: - if event.button() == Qt.MouseButton.RightButton or event.button() == Qt.MouseButton.LeftButton: - self.cell_clicked() - return - super(TreeView, self).mouseReleaseEvent(event) - def mouseReleaseEvent(self, event): """ forward clicked cell info from event @@ -3444,9 +3412,8 @@ def mouseReleaseEvent(self, event): for index in self.selectedIndexes(): if index.row() not in rows: rows.append(index.row()) - if modifiers == Qt.Modifier.CTRL or \ - modifiers == Qt.Modifier.SHIFT or \ - modifiers == (Qt.Modifier.CTRL | Qt.Modifier.SHIFT) or \ + # Qt5 vs Qt6 + if is_modifier_pressed(modifiers) or \ len(rows) > 1: super(TreeView, self).mouseReleaseEvent(event) else: @@ -3456,10 +3423,9 @@ def mouseReleaseEvent(self, event): self.cell_clicked() del modifiers, rows return - else: - if event.button() == Qt.MouseButton.RightButton or event.button() == Qt.MouseButton.LeftButton: - self.cell_clicked() - return + elif event.button() == Qt.MouseButton.RightButton or event.button() == Qt.MouseButton.LeftButton: + self.cell_clicked() + return def wheelEvent(self, event): """ @@ -3657,6 +3623,7 @@ def cell_clicked(self): else: self.action_menu.available = True + @Slot(str) def action_menu_custom_response(self, action): # avoid blocked context menu @@ -3708,6 +3675,7 @@ def action_menu_custom_response(self, action): # clean up del list_rows + @Slot() def action_response_decorator(method): """ @@ -3727,6 +3695,7 @@ def decoration_function(self): return (decoration_function) + @action_response_decorator def action_edit_actions(self): # buttons in toparee @@ -3735,6 +3704,7 @@ def action_edit_actions(self): # open actions tab (#3) of settings dialog dialogs.settings.show(tab=3) + @action_response_decorator def action_monitor(self): # only on 1 row @@ -3747,6 +3717,7 @@ def action_monitor(self): # open host/service monitor in browser self.server.open_monitor(miserable_host, miserable_service) + @action_response_decorator def action_recheck(self): # How many rows we have @@ -3763,6 +3734,7 @@ def action_recheck(self): self.recheck.emit({'host': miserable_host, 'service': miserable_service}) + @action_response_decorator def action_acknowledge(self): list_host = [] @@ -3784,6 +3756,7 @@ def action_acknowledge(self): service=list_service) dialogs.acknowledge.show() + @action_response_decorator def action_downtime(self): list_host = [] @@ -3805,6 +3778,7 @@ def action_downtime(self): service=list_service) dialogs.downtime.show() + @action_response_decorator def action_archive_event(self): """ @@ -3857,6 +3831,7 @@ def action_archive_event(self): # clean up del index, indexes, list_rows, list_host, list_service, list_status + @action_response_decorator def action_submit(self): # only on 1 row @@ -3871,6 +3846,7 @@ def action_submit(self): service=miserable_service) dialogs.submit.show() + @Slot() def action_clipboard_action_host(self): """ @@ -3896,6 +3872,7 @@ def action_clipboard_action_host(self): clipboard.setText(text) + @Slot() def action_clipboard_action_service(self): """ @@ -3921,6 +3898,7 @@ def action_clipboard_action_service(self): clipboard.setText(text) + @Slot() def action_clipboard_action_statusinformation(self): """ @@ -3945,6 +3923,7 @@ def action_clipboard_action_statusinformation(self): clipboard.setText(text) + @Slot() def action_clipboard_action_all(self): """ @@ -3989,6 +3968,7 @@ def action_clipboard_action_all(self): # copy text to clipboard clipboard.setText(text) + @Slot() def refresh(self): """ @@ -4017,6 +3997,7 @@ def refresh(self): self.status_changed.emit(self.server.name, self.server.worst_status_diff, self.server.worst_status_current) + @Slot(int, Qt.SortOrder) def sort_columns(self, sort_column, sort_order): """ @@ -4026,6 +4007,7 @@ def sort_columns(self, sort_column, sort_order): # intransmissible self.sort_data_array_for_columns.emit(int(sort_column), int(sort_order), True) + @Slot() def finish_worker_thread(self): """ @@ -4036,6 +4018,7 @@ def finish_worker_thread(self): # wait until thread is really stopped self.worker_thread.wait() + class Worker(QObject): """ attempt to run a server status update thread - only needed by table so it is defined here inside table @@ -4109,8 +4092,7 @@ def get_status(self): # if counter is at least update interval get status if self.server.thread_counter >= conf.update_interval_seconds: # only if no multiple selection is done at the moment and no context action menu is open - # if not is_modifier_pressed() and not self.action_menu_shown: - if not is_modifier_pressed() and APP.activePopupWidget() is None: + if not is_modifier_pressed(APP.keyboardModifiers()) and APP.activePopupWidget() is None: # reflect status retrieval attempt on server vbox label self.change_label_status.emit('Refreshing...', '') @@ -4513,10 +4495,6 @@ def unfresh_event_history(self): for event in self.server.events_history.keys(): self.server.events_history[event] = False - # @Slot(bool) - # def track_action_menu(self, action_menu_shown): - # self.action_menu_shown = action_menu_shown - class Dialogs(object): """ @@ -4525,7 +4503,7 @@ class for accessing all dialogs def __init__(self): # settings main dialog - #self.settings = Dialog_Settings(Ui_settings_main) + # self.settings = Dialog_Settings(Ui_settings_main) self.settings = Dialog_Settings('settings_main') self.settings.initialize() @@ -4556,17 +4534,17 @@ def __init__(self): self.acknowledge.window.button_change_defaults_acknowledge.clicked.connect(self.acknowledge.window.close) # downtime dialog for miserable item context menu - #self.submit = Dialog_Submit(Ui_dialog_submit) + # self.submit = Dialog_Submit(Ui_dialog_submit) self.submit = Dialog_Submit('dialog_submit') self.submit.initialize() # authentication dialog for username/password - #self.authentication = Dialog_Authentication(Ui_dialog_authentication) + # self.authentication = Dialog_Authentication(Ui_dialog_authentication) self.authentication = Dialog_Authentication('dialog_authentication') self.authentication.initialize() # dialog for asking about disabled or not configured servers - #self.server_missing = Dialog_Server_missing(Ui_dialog_server_missing) + # self.server_missing = Dialog_Server_missing(Ui_dialog_server_missing) self.server_missing = Dialog_Server_missing('dialog_server_missing') self.server_missing.initialize() # open server creation dialog @@ -4574,7 +4552,7 @@ def __init__(self): self.server_missing.window.button_enable_server.clicked.connect(self.settings.show) # about dialog - #self.about = Dialog_About(Ui_dialog_about) + # self.about = Dialog_About(Ui_dialog_about) self.about = Dialog_About('dialog_about') # file chooser Dialog @@ -4740,32 +4718,33 @@ def __init__(self, dialog): self.TOGGLE_DEPS = { # debug mode self.window.input_checkbox_debug_mode: [self.window.input_checkbox_debug_to_file, - self.window.input_lineedit_debug_file], + self.window.input_lineedit_debug_file], # regular expressions for filtering hosts self.window.input_checkbox_re_host_enabled: [self.window.input_lineedit_re_host_pattern, - self.window.input_checkbox_re_host_reverse], + self.window.input_checkbox_re_host_reverse], # regular expressions for filtering services self.window.input_checkbox_re_service_enabled: [self.window.input_lineedit_re_service_pattern, - self.window.input_checkbox_re_service_reverse], + self.window.input_checkbox_re_service_reverse], # regular expressions for filtering status information - self.window.input_checkbox_re_status_information_enabled: [self.window.input_lineedit_re_status_information_pattern, - self.window.input_checkbox_re_status_information_reverse], + self.window.input_checkbox_re_status_information_enabled: [ + self.window.input_lineedit_re_status_information_pattern, + self.window.input_checkbox_re_status_information_reverse], # regular expressions for filtering duration self.window.input_checkbox_re_duration_enabled: [self.window.input_lineedit_re_duration_pattern, - self.window.input_checkbox_re_duration_reverse], + self.window.input_checkbox_re_duration_reverse], # regular expressions for filtering duration self.window.input_checkbox_re_attempt_enabled: [self.window.input_lineedit_re_attempt_pattern, - self.window.input_checkbox_re_attempt_reverse], + self.window.input_checkbox_re_attempt_reverse], # regular expressions for filtering groups self.window.input_checkbox_re_groups_enabled: [self.window.input_lineedit_re_groups_pattern, - self.window.input_checkbox_re_groups_reverse], + self.window.input_checkbox_re_groups_reverse], # offset for statuswindow when using systray self.window.input_radiobutton_icon_in_systray: [self.window.input_checkbox_systray_offset_use], self.window.input_checkbox_systray_offset_use: [self.window.input_spinbox_systray_offset, - self.window.label_offset_statuswindow], + self.window.label_offset_statuswindow], # display to use in fullscreen mode self.window.input_radiobutton_fullscreen: [self.window.label_fullscreen_display, - self.window.input_combobox_fullscreen_display], + self.window.input_combobox_fullscreen_display], # notifications in general self.window.input_checkbox_notification: [self.window.notification_groupbox], # sound at all @@ -4779,8 +4758,10 @@ def __init__(self, dialog): self.window.input_lineedit_notification_action_warning_string], self.window.input_checkbox_notification_action_critical: [ self.window.input_lineedit_notification_action_critical_string], - self.window.input_checkbox_notification_action_down: [self.window.input_lineedit_notification_action_down_string], - self.window.input_checkbox_notification_action_ok: [self.window.input_lineedit_notification_action_ok_string], + self.window.input_checkbox_notification_action_down: [ + self.window.input_lineedit_notification_action_down_string], + self.window.input_checkbox_notification_action_ok: [ + self.window.input_lineedit_notification_action_ok_string], # single custom notification action self.window.input_checkbox_notification_custom_action: [self.window.notification_custom_action_groupbox], # use event separator or not @@ -4790,27 +4771,27 @@ def __init__(self, dialog): # customized color alternation self.window.input_checkbox_show_grid: [self.window.input_checkbox_grid_use_custom_intensity], self.window.input_checkbox_grid_use_custom_intensity: [self.window.input_slider_grid_alternation_intensity, - self.window.label_intensity_information_0, - self.window.label_intensity_information_1, - self.window.label_intensity_warning_0, - self.window.label_intensity_warning_1, - self.window.label_intensity_average_0, - self.window.label_intensity_average_1, - self.window.label_intensity_high_0, - self.window.label_intensity_high_1, - self.window.label_intensity_critical_0, - self.window.label_intensity_critical_1, - self.window.label_intensity_disaster_0, - self.window.label_intensity_disaster_1, - self.window.label_intensity_down_0, - self.window.label_intensity_down_1, - self.window.label_intensity_unreachable_0, - self.window.label_intensity_unreachable_1, - self.window.label_intensity_unknown_0, - self.window.label_intensity_unknown_1], + self.window.label_intensity_information_0, + self.window.label_intensity_information_1, + self.window.label_intensity_warning_0, + self.window.label_intensity_warning_1, + self.window.label_intensity_average_0, + self.window.label_intensity_average_1, + self.window.label_intensity_high_0, + self.window.label_intensity_high_1, + self.window.label_intensity_critical_0, + self.window.label_intensity_critical_1, + self.window.label_intensity_disaster_0, + self.window.label_intensity_disaster_1, + self.window.label_intensity_down_0, + self.window.label_intensity_down_1, + self.window.label_intensity_unreachable_0, + self.window.label_intensity_unreachable_1, + self.window.label_intensity_unknown_0, + self.window.label_intensity_unknown_1], self.window.input_radiobutton_use_custom_browser: [self.window.groupbox_custom_browser, - self.window.input_lineedit_custom_browser, - self.window.button_choose_browser]} + self.window.input_lineedit_custom_browser, + self.window.button_choose_browser]} self.TOGGLE_DEPS_INVERTED = [self.window.input_checkbox_notification_custom_action_single] @@ -4870,23 +4851,30 @@ def __init__(self, dialog): # set folder and play symbols to choose and play buttons self.window.button_choose_warning.setText('') - self.window.button_choose_warning.setIcon(self.window.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_DirIcon)) + self.window.button_choose_warning.setIcon( + self.window.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_DirIcon)) self.window.button_play_warning.setText('') - self.window.button_play_warning.setIcon(self.window.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_MediaPlay)) + self.window.button_play_warning.setIcon( + self.window.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_MediaPlay)) self.window.button_choose_critical.setText('') - self.window.button_choose_critical.setIcon(self.window.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_DirIcon)) + self.window.button_choose_critical.setIcon( + self.window.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_DirIcon)) self.window.button_play_critical.setText('') - self.window.button_play_critical.setIcon(self.window.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_MediaPlay)) + self.window.button_play_critical.setIcon( + self.window.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_MediaPlay)) self.window.button_choose_down.setText('') - self.window.button_choose_down.setIcon(self.window.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_DirIcon)) + self.window.button_choose_down.setIcon( + self.window.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_DirIcon)) self.window.button_play_down.setText('') - self.window.button_play_down.setIcon(self.window.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_MediaPlay)) + self.window.button_play_down.setIcon( + self.window.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_MediaPlay)) # set browser file chooser icon and current custom browser path self.window.button_choose_browser.setText('') - self.window.button_choose_browser.setIcon(self.window.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_DirIcon)) + self.window.button_choose_browser.setIcon( + self.window.button_play_warning.style().standardIcon(QStyle.StandardPixmap.SP_DirIcon)) self.window.input_lineedit_custom_browser.setText(conf.custom_browser) # connect choose browser button with file dialog self.window.button_choose_browser.clicked.connect(self.choose_browser_executable) @@ -5198,7 +5186,7 @@ def ok(self): # statuswindow.worker_thread.wait() # wait until statuswindow notification worker has finished - #statuswindow.worker_notification_thread.terminate() + # statuswindow.worker_notification_thread.terminate() # statuswindow.worker_notification_thread.quit() # statuswindow.worker_notification_thread.wait() @@ -5256,7 +5244,8 @@ def delete_server(self): reply = QMessageBox.question(self.window, 'Nagstamon', 'Do you really want to delete monitor server <b>%s</b>?' % (server.name), - QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, QMessageBox.StandardButton.No) + QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, + QMessageBox.StandardButton.No) if reply == QMessageBox.StandardButton.Yes: # in case server is enabled delete its vbox @@ -5307,7 +5296,7 @@ def refresh_list(self, list_widget, list_conf, current=''): self.fill_list(list_widget, list_conf) # select current edited item # activate currently created/edited server monitor item by first searching it in the list - list_widget.setCurrentItem(list_widget.findItems(current, Qt.MatchExactly)[0]) + list_widget.setCurrentItem(list_widget.findItems(current, Qt.MatchFlag.MatchExactly)[0]) @Slot() def new_action(self): @@ -5340,7 +5329,8 @@ def delete_action(self): reply = QMessageBox.question(self.window, 'Nagstamon', 'Do you really want to delete action <b>%s</b>?' % (action.name), - QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, QMessageBox.StandardButton.No) + QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, + QMessageBox.StandardButton.No) if reply == QMessageBox.StandardButton.Yes: # kick action out of config items @@ -5453,13 +5443,13 @@ def paint_colors(self): border-width: 1px; border-color: black; border-style: solid;''' - % conf.__dict__[color]) + % conf.__dict__[color]) # example color labels for label in [x for x in self.window.__dict__ if x.startswith('label_color_')]: status = label.split('label_color_')[1] self.window.__dict__[label].setStyleSheet('color: %s; background: %s' % - (conf.__dict__['color_%s_text' % (status)], - (conf.__dict__['color_%s_background' % (status)]))) + (conf.__dict__['color_%s_text' % (status)], + (conf.__dict__['color_%s_background' % (status)]))) @Slot() def colors_defaults(self): @@ -5474,7 +5464,7 @@ def colors_defaults(self): border-width: 1px; border-color: black; border-style: solid;''' - % conf.__dict__[default_color]) + % conf.__dict__[default_color]) # example color labels for label in [x for x in self.window.__dict__ if x.startswith('label_color_')]: status = label.split('label_color_')[1] @@ -5487,7 +5477,7 @@ def colors_defaults(self): # apply color values from stylesheet to label self.window.__dict__[label].setStyleSheet('color: %s; background: %s' % - (color_text, color_background)) + (color_text, color_background)) @Slot(str) def color_chooser(self, item): @@ -5503,7 +5493,7 @@ def color_chooser(self, item): border-width: 1px; border-color: black; border-style: solid;''' - % new_color.name()) + % new_color.name()) status = item.split('_')[0] # get color value from stylesheet to paint example text = self.window.__dict__['input_button_color_%s_text' % (status)].styleSheet() @@ -5527,12 +5517,12 @@ def paint_color_alternation(self): for state in COLORS: # get text color from button CSS text = self.window.__dict__['input_button_color_{0}_text' - .format(state.lower())] \ + .format(state.lower())] \ .styleSheet() \ .split(';\n')[0].split(': ')[1] # get background color from button CSS background = self.window.__dict__['input_button_color_{0}_background' - .format(state.lower())] \ + .format(state.lower())] \ .styleSheet() \ .split(';\n')[0].split(': ')[1] # set CSS @@ -5561,7 +5551,7 @@ def change_color_alternation(self, value): # get text color from text color chooser button text = self.window.__dict__['input_button_color_{0}_text' - .format(state.lower())] \ + .format(state.lower())] \ .styleSheet() \ .split(';\n')[0].split(': ')[1] @@ -5739,19 +5729,19 @@ def __init__(self, dialog): # dictionary holds checkbox/radiobutton as key and relevant widgets in list self.TOGGLE_DEPS = { self.window.input_checkbox_use_autologin: [self.window.label_autologin_key, - self.window.input_lineedit_autologin_key], + self.window.input_lineedit_autologin_key], self.window.input_checkbox_use_proxy: [self.window.groupbox_proxy], self.window.input_checkbox_use_proxy_from_os: [self.window.label_proxy_address, - self.window.input_lineedit_proxy_address, - self.window.label_proxy_username, - self.window.input_lineedit_proxy_username, - self.window.label_proxy_password, - self.window.input_lineedit_proxy_password], + self.window.input_lineedit_proxy_address, + self.window.label_proxy_username, + self.window.input_lineedit_proxy_username, + self.window.label_proxy_password, + self.window.input_lineedit_proxy_password], self.window.input_checkbox_show_options: [self.window.groupbox_options], self.window.input_checkbox_custom_cert_use: [self.window.label_custom_ca_file, - self.window.input_lineedit_custom_cert_ca_file, - self.window.button_choose_custom_cert_ca_file]} + self.window.input_lineedit_custom_cert_ca_file, + self.window.button_choose_custom_cert_ca_file]} self.TOGGLE_DEPS_INVERTED = [self.window.input_checkbox_use_proxy_from_os] @@ -5986,9 +5976,9 @@ def ok(self): self.mode == 'edit' and self.server_conf != conf.servers[self.window.input_lineedit_name.text()]): # cry if duplicate name exists QMessageBox.Icon.Critical(self.window, 'Nagstamon', - 'The monitor server name <b>%s</b> is already used.' % - (self.window.input_lineedit_name.text()), - QMessageBox.StandardButton.Ok) + 'The monitor server name <b>%s</b> is already used.' % + (self.window.input_lineedit_name.text()), + QMessageBox.StandardButton.Ok) else: # get configuration from UI for widget in self.window.__dict__: @@ -6127,17 +6117,18 @@ def __init__(self, dialog): # dictionary holds checkbox/radiobutton as key and relevant widgets in list self.TOGGLE_DEPS = { self.window.input_checkbox_re_host_enabled: [self.window.input_lineedit_re_host_pattern, - self.window.input_checkbox_re_host_reverse], + self.window.input_checkbox_re_host_reverse], self.window.input_checkbox_re_service_enabled: [self.window.input_lineedit_re_service_pattern, - self.window.input_checkbox_re_service_reverse], - self.window.input_checkbox_re_status_information_enabled: [self.window.input_lineedit_re_status_information_pattern, - self.window.input_checkbox_re_status_information_reverse], + self.window.input_checkbox_re_service_reverse], + self.window.input_checkbox_re_status_information_enabled: [ + self.window.input_lineedit_re_status_information_pattern, + self.window.input_checkbox_re_status_information_reverse], self.window.input_checkbox_re_duration_enabled: [self.window.input_lineedit_re_duration_pattern, - self.window.input_checkbox_re_duration_reverse], + self.window.input_checkbox_re_duration_reverse], self.window.input_checkbox_re_attempt_enabled: [self.window.input_lineedit_re_attempt_pattern, - self.window.input_checkbox_re_attempt_reverse], + self.window.input_checkbox_re_attempt_reverse], self.window.input_checkbox_re_groups_enabled: [self.window.input_lineedit_re_groups_pattern, - self.window.input_checkbox_re_groups_reverse]} + self.window.input_checkbox_re_groups_reverse]} # fill action types into combobox self.window.input_combobox_type.addItems(sorted(self.ACTION_TYPES.values())) @@ -6246,9 +6237,9 @@ def ok(self): self.mode == 'edit' and self.action_conf != conf.actions[self.window.input_lineedit_name.text()]): # cry if duplicate name exists QMessageBox.Icon.Critical(self.window, 'Nagstamon', - 'The action name <b>%s</b> is already used.' % - (self.window.input_lineedit_name.text()), - QMessageBox.StandardButton.Ok) + 'The action name <b>%s</b> is already used.' % + (self.window.input_lineedit_name.text()), + QMessageBox.StandardButton.Ok) else: # get configuration from UI for widget in self.window.__dict__: @@ -7078,7 +7069,7 @@ def get_screen(x, y): # integerify these values as they *might* be strings x = int(x) y = int(y) - screen = APP.screenAt(QPoint(x,y)) + screen = APP.screenAt(QPoint(x, y)) del x, y if screen: return screen.name @@ -7130,7 +7121,8 @@ def exit(): # statuswindow.destroy() # # bye bye - #APP.instance().quit() + # APP.instance().quit() + def check_servers(): """ @@ -7146,20 +7138,6 @@ def check_servers(): dialogs.server_missing.initialize('no_server_enabled') -def is_modifier_pressed(): - """ - check if (left) CTRL or Shift keys are pressed - """ - modifiers = APP.keyboardModifiers() - if modifiers == Qt.Modifier.CTRL or \ - modifiers == Qt.Modifier.SHIFT or \ - modifiers == (Qt.Modifier.CTRL | Qt.Modifier.SHIFT): - del modifiers - return True - del modifiers - return False - - # check for updates check_version = CheckVersion() diff --git a/Nagstamon/QUI/qt.py b/Nagstamon/QUI/qt.py index 729f5f987..47254962a 100644 --- a/Nagstamon/QUI/qt.py +++ b/Nagstamon/QUI/qt.py @@ -100,12 +100,6 @@ QWidget from PyQt5 import uic - def get_global_position(event): - ''' - Qt5 uses other method than Qt6 - ''' - return event.globalPos() - class MediaPlayer(QObject): """ play media files for notification @@ -154,6 +148,23 @@ def play(self): # just play sound self.player.play() + def get_global_position(event): + ''' + Qt5 uses other method than Qt6 + ''' + return event.globalPos() + + def is_modifier_pressed(modifiers): + """ + check if (left) CTRL or Shift keys are pressed + """ + if modifiers == Qt.Modifier.CTRL or \ + modifiers == Qt.Modifier.SHIFT or \ + modifiers == (Qt.Modifier.CTRL | Qt.Modifier.SHIFT): + return True + return False + + elif QT_FLAVOR == 'PyQt6': # PySide/PyQt compatibility from PyQt6.QtCore import pyqtSignal as Signal, \ @@ -214,12 +225,6 @@ def play(self): # for later decision which differences have to be considered QT_FLAVOR = 'PyQt6' - def get_global_position(event): - ''' - Qt5 uses other method than Qt6 - ''' - return event.globalPosition() - class MediaPlayer(QObject): """ play media files for notification @@ -266,6 +271,21 @@ def play(self): # just play sound self.player.play() + def get_global_position(event): + ''' + Qt5 uses other method than Qt6 + ''' + return event.globalPosition() + + def is_modifier_pressed(modifiers): + """ + check if (left) CTRL or Shift keys are pressed + """ + if modifiers.name in ['ControlModifier', 'ShiftModifier']: + return True + return False + + # elif 'PySide6' in sys.modules: # from PySide6.QtCore import Signal, \ # Slot, \ diff --git a/Nagstamon/Servers/Centreon.py b/Nagstamon/Servers/Centreon.py index d4edc6fcb..5fcdff749 100644 --- a/Nagstamon/Servers/Centreon.py +++ b/Nagstamon/Servers/Centreon.py @@ -267,7 +267,7 @@ def _get_sid(self): if conf.debug_mode == True: self.Debug(server=self.get_name(), debug='Password login : ' + self.username + ' : ' + self.password) - sid = self.session.cookies['PHPSESSID'] + sid = self.session.cookies.get('PHPSESSID', '') if conf.debug_mode == True: self.Debug(server=self.get_name(), debug='SID : ' + sid) if self.centreon_version >= 2.66 and self.centreon_version < 19.04: From 43014f9546e48fc6777c7deaf4c7fdeadb5f8c82 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 5 Jun 2022 01:02:36 +0200 Subject: [PATCH 290/884] fixed global pos --- Nagstamon/QUI/qt.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Nagstamon/QUI/qt.py b/Nagstamon/QUI/qt.py index 47254962a..093ff30b6 100644 --- a/Nagstamon/QUI/qt.py +++ b/Nagstamon/QUI/qt.py @@ -271,11 +271,11 @@ def play(self): # just play sound self.player.play() - def get_global_position(event): - ''' - Qt5 uses other method than Qt6 - ''' - return event.globalPosition() + def get_global_position(event): + ''' + Qt5 uses other method than Qt6 + ''' + return event.globalPosition() def is_modifier_pressed(modifiers): """ From cecf8489afcc92d63917906916c20decf381916d Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 5 Jun 2022 09:51:41 +0200 Subject: [PATCH 291/884] yet another 'fix' for gssapi in windows --- .github/workflows/build-release-latest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 8cde355ee..29a3bac1b 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -131,7 +131,7 @@ jobs: # architecture: x86 # - run: python -m pip install --no-warn-script-location -r build/requirements/windows.txt # # pretty hacky but no other idea to avoid gssapi being installed which breaks requests-kerberos -# - run: python -m pip uninstall -y gssapi +# - run: python -m pip uninstall -y gssapi requests-gssapi # - run: cd ${{ github.workspace }}/build; python build.py # env: # PYTHONPATH: ${{ github.workspace }} @@ -155,7 +155,7 @@ jobs: architecture: x64 - run: python -m pip install --no-warn-script-location -r build/requirements/windows.txt # pretty hacky but no other idea to avoid gssapi being installed which breaks requests-kerberos - - run: python -m pip uninstall -y gssapi + - run: python -m pip uninstall -y gssapi requests-gssapi - run: cd ${{ github.workspace }}/build; python build.py env: PYTHONPATH: ${{ github.workspace }} From 0ff619faf7da104f25df802ebca8cf3e87f25abf Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 5 Jun 2022 09:52:19 +0200 Subject: [PATCH 292/884] 20220605 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 50fb2279c..0cb56f3f8 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20220604' + VERSION = '3.9-20220605' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 04a9ece69..1bdc52916 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.9-20220604) unstable; urgency=low +nagstamon (3.9-20220605) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Sat, 04 Jun 2022 08:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Sun, 05 Jun 2022 08:00:00 +0100 nagstamon (3.8.0) stable; urgency=low * New upstream From 49f02dcdaa68b5bae5b2586907cabb6140c96ee8 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 5 Jun 2022 13:41:44 +0200 Subject: [PATCH 293/884] simplified keyboard modifier detection --- Nagstamon/QUI/__init__.py | 51 +++++++++++++++++++++++---------------- Nagstamon/QUI/qt.py | 29 +++++++--------------- 2 files changed, 39 insertions(+), 41 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 35e4d3a6f..bea867e9a 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -3399,33 +3399,42 @@ def adjust_table(self): # after setting table whole window can be repainted self.ready_to_resize.emit() + def count_selected_rows(self): + """ + find out if rows are selected and return their number + """ + rows = [] + for index in self.selectedIndexes(): + if index.row() not in rows: + rows.append(index.row()) + return len(rows) + def mouseReleaseEvent(self, event): """ forward clicked cell info from event """ # special treatment if window should be closed when left-clicking somewhere # it is important to check if CTRL or SHIFT key is presses while clicking to select lines - if event.button() == Qt.MouseButton.LeftButton: - modifiers = event.modifiers() - # count selected rows - if more than 1 do not close popwin - rows = [] - for index in self.selectedIndexes(): - if index.row() not in rows: - rows.append(index.row()) - # Qt5 vs Qt6 - if is_modifier_pressed(modifiers) or \ - len(rows) > 1: - super(TreeView, self).mouseReleaseEvent(event) - else: - if conf.close_details_clicking_somewhere: - statuswindow.hide_window() + if conf.close_details_clicking_somewhere: + if event.button() == Qt.MouseButton.LeftButton: + modifiers = event.modifiers() + # count selected rows - if more than 1 do not close popwin + if modifiers or self.count_selected_rows() > 1: + super(TreeView, self).mouseReleaseEvent(event) else: - self.cell_clicked() - del modifiers, rows - return - elif event.button() == Qt.MouseButton.RightButton or event.button() == Qt.MouseButton.LeftButton: - self.cell_clicked() - return + if conf.close_details_clicking_somewhere: + statuswindow.hide_window() + else: + self.cell_clicked() + return + elif event.button() == Qt.MouseButton.RightButton: + self.cell_clicked() + return + else: + if event.button() in [Qt.MouseButton.LeftButton, Qt.MouseButton.RightButton]: + self.cell_clicked() + return + super(TreeView, self).mouseReleaseEvent(event) def wheelEvent(self, event): """ @@ -4092,7 +4101,7 @@ def get_status(self): # if counter is at least update interval get status if self.server.thread_counter >= conf.update_interval_seconds: # only if no multiple selection is done at the moment and no context action menu is open - if not is_modifier_pressed(APP.keyboardModifiers()) and APP.activePopupWidget() is None: + if not APP.keyboardModifiers() and APP.activePopupWidget() is None: # reflect status retrieval attempt on server vbox label self.change_label_status.emit('Refreshing...', '') diff --git a/Nagstamon/QUI/qt.py b/Nagstamon/QUI/qt.py index 093ff30b6..0ea0a262a 100644 --- a/Nagstamon/QUI/qt.py +++ b/Nagstamon/QUI/qt.py @@ -26,6 +26,7 @@ # by the little import the appropriate PyQt version will be loaded try: from PyQt6.QtCore import PYQT_VERSION_STR as QT_VERSION_STR + # get int-ed version parts QT_VERSION_MAJOR, QT_VERSION_MINOR, QT_VERSION_BUGFIX = [int(x) for x in QT_VERSION_STR.split('.')] # for later decision which differences have to be considered @@ -33,6 +34,7 @@ except ImportError: try: from PyQt5.QtCore import PYQT_VERSION_STR as QT_VERSION_STR + # get int-ed version parts QT_VERSION_MAJOR, QT_VERSION_MINOR, QT_VERSION_BUGFIX = [int(x) for x in QT_VERSION_STR.split('.')] # for later decision which differences have to be considered @@ -100,6 +102,7 @@ QWidget from PyQt5 import uic + class MediaPlayer(QObject): """ play media files for notification @@ -140,7 +143,8 @@ def set_media(self, media_file): return True else: # cry and tell no file was found - self.send_message.emit('warning', 'Sound file <b>\'{0}\'</b> not found for playback.'.format(media_file)) + self.send_message.emit('warning', + 'Sound file <b>\'{0}\'</b> not found for playback.'.format(media_file)) return False @Slot() @@ -148,22 +152,13 @@ def play(self): # just play sound self.player.play() + def get_global_position(event): ''' Qt5 uses other method than Qt6 ''' return event.globalPos() - def is_modifier_pressed(modifiers): - """ - check if (left) CTRL or Shift keys are pressed - """ - if modifiers == Qt.Modifier.CTRL or \ - modifiers == Qt.Modifier.SHIFT or \ - modifiers == (Qt.Modifier.CTRL | Qt.Modifier.SHIFT): - return True - return False - elif QT_FLAVOR == 'PyQt6': # PySide/PyQt compatibility @@ -222,9 +217,11 @@ def is_modifier_pressed(modifiers): QVBoxLayout, \ QWidget from PyQt6 import uic + # for later decision which differences have to be considered QT_FLAVOR = 'PyQt6' + class MediaPlayer(QObject): """ play media files for notification @@ -271,21 +268,13 @@ def play(self): # just play sound self.player.play() + def get_global_position(event): ''' Qt5 uses other method than Qt6 ''' return event.globalPosition() - def is_modifier_pressed(modifiers): - """ - check if (left) CTRL or Shift keys are pressed - """ - if modifiers.name in ['ControlModifier', 'ShiftModifier']: - return True - return False - - # elif 'PySide6' in sys.modules: # from PySide6.QtCore import Signal, \ # Slot, \ From 575602f50416f5fc9874e32993bcef3a9877f417 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 5 Jun 2022 16:31:34 +0200 Subject: [PATCH 294/884] Win3 --- .github/workflows/build-release-latest.yml | 53 ++++++++++++---------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 29a3bac1b..fd7269c29 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -1,3 +1,4 @@ + name: build-release-latest on: push: @@ -118,30 +119,32 @@ jobs: retention-days: 1 if-no-files-found: error -# no PyQt6 for win32 available on Pypi.org -# windows-32: -# # better depend on stable build image -# runs-on: windows-2019 -# needs: test -# steps: -# - uses: actions/checkout@v2 -# - uses: actions/setup-python@v2 -# with: -# python-version: ${{ env.python_win_version }} -# architecture: x86 -# - run: python -m pip install --no-warn-script-location -r build/requirements/windows.txt -# # pretty hacky but no other idea to avoid gssapi being installed which breaks requests-kerberos -# - run: python -m pip uninstall -y gssapi requests-gssapi -# - run: cd ${{ github.workspace }}/build; python build.py -# env: -# PYTHONPATH: ${{ github.workspace }} -# - uses: actions/upload-artifact@v2 -# with: -# path: | -# build/dist/*.zip -# build/dist/*.exe -# retention-days: 1 -# if-no-files-found: error + + windows-32: + # better depend on stable build image + runs-on: windows-2019 + needs: test + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: ${{ env.python_win_version }} + architecture: x86 + # pyqt6 is not available for 32 bit Windows + - run: ((Get-Content -path build/requirements/windows.txt -Raw) -replace 'pyqt6','pyqt5') | Set-Content -Path build/requirements/windows.txt + - run: python -m pip install --no-warn-script-location -r build/requirements/windows.txt + # pretty hacky but no other idea to avoid gssapi being installed which breaks requests-kerberos + - run: python -m pip uninstall -y gssapi requests-gssapi + - run: cd ${{ github.workspace }}/build; python build.py + env: + PYTHONPATH: ${{ github.workspace }} + - uses: actions/upload-artifact@v2 + with: + path: | + build/dist/*.zip + build/dist/*.exe + retention-days: 1 + if-no-files-found: error windows-64: # better depend on stable build image @@ -202,4 +205,4 @@ jobs: automatic_release_tag: "latest" prerelease: true files: | - artifact/* + artifact/* \ No newline at end of file From 1e34e0481114da91d64753fb5867b93f7ac551e7 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 5 Jun 2022 18:28:57 +0200 Subject: [PATCH 295/884] fix win32 --- .github/workflows/build-release-latest.yml | 49 +++++++++++----------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 29a3bac1b..e14e08e4e 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -118,30 +118,31 @@ jobs: retention-days: 1 if-no-files-found: error -# no PyQt6 for win32 available on Pypi.org -# windows-32: -# # better depend on stable build image -# runs-on: windows-2019 -# needs: test -# steps: -# - uses: actions/checkout@v2 -# - uses: actions/setup-python@v2 -# with: -# python-version: ${{ env.python_win_version }} -# architecture: x86 -# - run: python -m pip install --no-warn-script-location -r build/requirements/windows.txt -# # pretty hacky but no other idea to avoid gssapi being installed which breaks requests-kerberos -# - run: python -m pip uninstall -y gssapi requests-gssapi -# - run: cd ${{ github.workspace }}/build; python build.py -# env: -# PYTHONPATH: ${{ github.workspace }} -# - uses: actions/upload-artifact@v2 -# with: -# path: | -# build/dist/*.zip -# build/dist/*.exe -# retention-days: 1 -# if-no-files-found: error + windows-32: + # better depend on stable build image + runs-on: windows-2019 + needs: test + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: ${{ env.python_win_version }} + architecture: x86 + # no PyQt6 for win32 available on Pypi.org + - run: ((Get-Content -path build/requirements/windows.txt -Raw) -replace 'pyqt6','pyqt5') | Set-Content -Path build/requirements/windows.txt + - run: python -m pip install --no-warn-script-location -r build/requirements/windows.txt + # pretty hacky but no other idea to avoid gssapi being installed which breaks requests-kerberos + - run: python -m pip uninstall -y gssapi requests-gssapi + - run: cd ${{ github.workspace }}/build; python build.py + env: + PYTHONPATH: ${{ github.workspace }} + - uses: actions/upload-artifact@v2 + with: + path: | + build/dist/*.zip + build/dist/*.exe + retention-days: 1 + if-no-files-found: error windows-64: # better depend on stable build image From de54e969802034f8f19bdbeef279761134193ac9 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 5 Jun 2022 18:32:51 +0200 Subject: [PATCH 296/884] less needs in github actions --- .github/workflows/build-release-latest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index e14e08e4e..cdf2f678e 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -170,7 +170,7 @@ jobs: repo-fedora: runs-on: ubuntu-latest - needs: [debian, fedora-34, fedora-35, fedora-36, macos, windows-64] + needs: [fedora-34, fedora-35, fedora-36] steps: # get binaries created by other jobs - uses: actions/download-artifact@v2 @@ -192,7 +192,7 @@ jobs: github-release: runs-on: ubuntu-latest - needs: [debian, fedora-34, fedora-35, fedora-36, macos, windows-64] + needs: [debian, fedora-34, fedora-35, fedora-36, macos, windows-32, windows-64] steps: - uses: actions/download-artifact@v2 - run: cd artifact && md5sum *agstamon* > md5sums.txt From 44206039e0c9c1f7f21c16aad867f708443d87fe Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 6 Jun 2022 21:21:41 +0200 Subject: [PATCH 297/884] 20220606 --- Nagstamon/Config.py | 2 +- Nagstamon/QUI/__init__.py | 8 +++++--- build/debian/changelog | 2 +- build/debian/control | 2 +- build/redhat/nagstamon.spec | 1 + build/requirements/linux.txt | 1 + 6 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 0cb56f3f8..e8c5e41a7 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20220605' + VERSION = '3.9-20220606' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index bea867e9a..05b0e3308 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -83,12 +83,14 @@ try: from dbus import (Interface, SessionBus) - from dbus.mainloop.glib import DBusQtMainLoop + # no DBusQtMainLoop available for Qt6 + from dbus.mainloop.glib import DBusGMainLoop as DBusMainLoop # flag to check later if DBus is available DBUS_AVAILABLE = True - except ImportError: + except ImportError as ie: + print(ie) print('No DBus for desktop notification available.') DBUS_AVAILABLE = False @@ -6997,7 +6999,7 @@ def __init__(self): # see https://github.com/HenriWahl/Nagstamon/issues/320 try: # import dbus # never used - dbus_mainloop = DBusQtMainLoop(set_as_default=True) + dbus_mainloop = DBusMainLoop(set_as_default=True) dbus_sessionbus = SessionBus(dbus_mainloop) dbus_object = dbus_sessionbus.get_object('org.freedesktop.Notifications', '/org/freedesktop/Notifications') diff --git a/build/debian/changelog b/build/debian/changelog index 1bdc52916..6030cd433 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.9-20220605) unstable; urgency=low +nagstamon (3.9-20220606) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Sun, 05 Jun 2022 08:00:00 +0100 diff --git a/build/debian/control b/build/debian/control index 491871f04..07dbc6de4 100644 --- a/build/debian/control +++ b/build/debian/control @@ -13,7 +13,7 @@ Vcs-Browser: https://codeload.github.com/HenriWahl/Nagstamon/zip/master Package: nagstamon Architecture: all -Depends: ${python3:Depends}, ${misc:Depends}, python3-dateutil, python3-pkg-resources, python3-bs4, python3-lxml, python3-pyqt5, python3-pyqt5.qtsvg, python3-pyqt5.qtmultimedia, libqt5multimedia5-plugins, python3-requests, python3-requests-kerberos, python3-psutil, python3-dbus.mainloop.pyqt5, python3-keyring, python3-socks +Depends: ${python3:Depends}, ${misc:Depends}, python3-dateutil, python3-pkg-resources, python3-bs4, python3-lxml, python3-pyqt5, python3-pyqt5.qtsvg, python3-pyqt5.qtmultimedia, libqt5multimedia5-plugins, python3-requests, python3-requests-kerberos, python3-psutil, python3-dbus, python3-keyring, python3-socks Description: Nagios status monitor which takes place in systray or on desktop Nagstamon is a Nagios status monitor which takes place in systray or on desktop (GNOME, KDE) as floating statusbar to inform you in diff --git a/build/redhat/nagstamon.spec b/build/redhat/nagstamon.spec index 398d8bc6f..dbc380f02 100644 --- a/build/redhat/nagstamon.spec +++ b/build/redhat/nagstamon.spec @@ -20,6 +20,7 @@ Requires: python3-beautifulsoup4 Requires: python3-crypto Requires: python3-cryptography Requires: python3-dateutil +Requires: python3-dbus Requires: python3-keyring Requires: python3-lxml Requires: python3-psutil diff --git a/build/requirements/linux.txt b/build/requirements/linux.txt index 94d331b56..1cb63d211 100644 --- a/build/requirements/linux.txt +++ b/build/requirements/linux.txt @@ -1,4 +1,5 @@ beautifulsoup4 +dbus-python keyring lxml psutil From 4389c73e35b48e2fe6debb797017ada41f3fca16 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 6 Jun 2022 22:38:08 +0200 Subject: [PATCH 298/884] libdbus-1-dev --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index cdf2f678e..699114e95 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -23,7 +23,7 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | - sudo apt-get install libkrb5-dev + sudo apt-get install libdbus-1-dev libkrb5-dev python -m pip install --upgrade pip pip install pytest pylint #flake8 if [ -f build/requirements/linux.txt ]; then pip install -r build/requirements/linux.txt; fi From 1802a66ffacdf3b8d3e23d65011c9c1b90f6aaf6 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 6 Jun 2022 22:40:21 +0200 Subject: [PATCH 299/884] wheel --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 699114e95..6004f9677 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -25,7 +25,7 @@ jobs: run: | sudo apt-get install libdbus-1-dev libkrb5-dev python -m pip install --upgrade pip - pip install pytest pylint #flake8 + pip install pytest pylint wheel #flake8 if [ -f build/requirements/linux.txt ]; then pip install -r build/requirements/linux.txt; fi # - name: Lint with flake8 # run: | From ca78aa20d675f758681d3e13694747d4aa906804 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 6 Jun 2022 23:15:36 +0200 Subject: [PATCH 300/884] qt version about dialog --- Nagstamon/QUI/__init__.py | 1 + Nagstamon/QUI/qt.py | 1 - Nagstamon/resources/qui/dialog_about.ui | 12 +++++++++++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 05b0e3308..e72a84967 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -6800,6 +6800,7 @@ def __init__(self, dialog): self.window.label_copyright.setText(AppInfo.COPYRIGHT) self.window.label_website.setText('<a href={0}>{0}</a>'.format(AppInfo.WEBSITE)) self.window.label_website.setOpenExternalLinks(True) + self.window.label_versions.setText(f'Python: {platform.python_version()}, Qt: {QT_VERSION_STR}') self.window.label_footnote.setText('<small>¹ plus Checkmk, Op5, Icinga, Centreon and more</small>') # fill in license information diff --git a/Nagstamon/QUI/qt.py b/Nagstamon/QUI/qt.py index 0ea0a262a..05b473777 100644 --- a/Nagstamon/QUI/qt.py +++ b/Nagstamon/QUI/qt.py @@ -34,7 +34,6 @@ except ImportError: try: from PyQt5.QtCore import PYQT_VERSION_STR as QT_VERSION_STR - # get int-ed version parts QT_VERSION_MAJOR, QT_VERSION_MINOR, QT_VERSION_BUGFIX = [int(x) for x in QT_VERSION_STR.split('.')] # for later decision which differences have to be considered diff --git a/Nagstamon/resources/qui/dialog_about.ui b/Nagstamon/resources/qui/dialog_about.ui index 35aefe52b..d429be96a 100644 --- a/Nagstamon/resources/qui/dialog_about.ui +++ b/Nagstamon/resources/qui/dialog_about.ui @@ -7,7 +7,7 @@ <x>0</x> <y>0</y> <width>500</width> - <height>300</height> + <height>322</height> </rect> </property> <property name="sizePolicy"> @@ -92,6 +92,16 @@ </property> </widget> </item> + <item> + <widget class="QLabel" name="label_versions"> + <property name="text"> + <string>Python Qt versions</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> <item> <spacer name="verticalSpacer"> <property name="orientation"> From c610a99458f92a14087be5dec39b89323be856aa Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 6 Jun 2022 23:28:12 +0200 Subject: [PATCH 301/884] media play --- Nagstamon/QUI/__init__.py | 7 +++---- Nagstamon/QUI/qt.py | 5 +---- nagstamon.py | 3 --- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index e72a84967..8164f85ee 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -2303,12 +2303,11 @@ def start(self, server_name, worst_status_diff, worst_status_current): sound_file = conf.__dict__[ 'notification_custom_sound_{0}'.format(worst_status_diff.lower())] - # once loaded file will be played by every server, even if it is - # not the self.notifying_server that loaded it - self.load_sound.emit(sound_file) - # only one enabled server should access the mediaplayer if self.notifying_server == server_name: + # once loaded file will be played by every server, even if it is + # not the self.notifying_server that loaded it + self.load_sound.emit(sound_file) self.play_sound.emit() # Notification actions diff --git a/Nagstamon/QUI/qt.py b/Nagstamon/QUI/qt.py index 05b473777..d206cdfee 100644 --- a/Nagstamon/QUI/qt.py +++ b/Nagstamon/QUI/qt.py @@ -231,7 +231,6 @@ class MediaPlayer(QObject): def __init__(self, statuswindow, resource_files): QObject.__init__(self) self.audio_output = QAudioOutput() - self.audio_output.setVolume(100) self.player = QMediaPlayer(parent=self) self.player.setAudioOutput(self.audio_output) self.resource_files = resource_files @@ -253,9 +252,7 @@ def set_media(self, media_file): media_file = self.resource_files[media_file] # only existing file can be played if Path(media_file).exists(): - url = QUrl.fromLocalFile(media_file) - self.player.setSource(url) - del url + self.player.setSource(QUrl.fromLocalFile(media_file)) return True else: # cry and tell no file was found diff --git a/nagstamon.py b/nagstamon.py index 1191180de..e2f308917 100755 --- a/nagstamon.py +++ b/nagstamon.py @@ -61,9 +61,6 @@ if conf.check_for_new_version is True: check_version.check(start_mode=True, parent=statuswindow) - # debug - print(f'Using {QT_FLAVOR} {QT_VERSION_STR}') - sys.exit(APP.exec()) except Exception as err: From 4136cf0d7d9b5063fea0a78cee24f9297459042a Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Fri, 10 Jun 2022 23:49:18 +0200 Subject: [PATCH 302/884] arrow --- build/requirements/linux.txt | 1 + build/requirements/macos.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/build/requirements/linux.txt b/build/requirements/linux.txt index 1cb63d211..176a735bd 100644 --- a/build/requirements/linux.txt +++ b/build/requirements/linux.txt @@ -1,3 +1,4 @@ +arrow beautifulsoup4 dbus-python keyring diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index 79cab29c2..68a3418cb 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -1,3 +1,4 @@ +arrow beautifulsoup4 keyring lxml From 340fbe280b5b59d58244bb59acb4d569a410cdc2 Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Sat, 11 Jun 2022 12:58:37 +0200 Subject: [PATCH 303/884] fixed action menu popup mess --- Nagstamon/Config.py | 2 +- Nagstamon/QUI/__init__.py | 376 +++++++++++++++++--------------------- build/debian/changelog | 4 +- 3 files changed, 172 insertions(+), 210 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index e8c5e41a7..050a8ceb1 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20220606' + VERSION = '3.9-20220611' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 8164f85ee..87da931f2 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -99,6 +99,7 @@ # check ECP authentication support availability try: from requests_ecp import HTTPECPAuth + ECP_AVAILABLE = True except ImportError: ECP_AVAILABLE = False @@ -496,7 +497,7 @@ class MenuAtCursor(QMenu): open menu at position of mouse pointer - normal .exec() shows menu at (0, 0) """ # flag to avoid too fast popping up menus - available = True + #available = True is_shown = Signal(bool) @@ -2134,12 +2135,6 @@ def raise_window_on_all_desktops(self): not conf.windowed and \ APP.activePopupWidget() == None: try: - # find out if no context menu is shown and thus would be - # overlapped by statuswindow - for vbox in self.servers_vbox.children(): - # jump out here if any action_menu is shown - if not vbox.table.action_menu.available: - return self.raise_() except Exception as err: # apparently a race condition could occur on set_mode() - grab it here and continue @@ -3254,8 +3249,6 @@ def __init__(self, columncount, rowcount, sort_column, sort_order, server, paren # action context menu self.action_menu = MenuAtCursor(parent=self) - # flag to avoid popping up menus when clicking somehwere - self.action_menu.available = True # signalmapper for getting triggered actions self.signalmapper_action_menu = QSignalMapper() # connect menu to responder @@ -3416,26 +3409,24 @@ def mouseReleaseEvent(self, event): """ # special treatment if window should be closed when left-clicking somewhere # it is important to check if CTRL or SHIFT key is presses while clicking to select lines + modifiers = event.modifiers() if conf.close_details_clicking_somewhere: if event.button() == Qt.MouseButton.LeftButton: - modifiers = event.modifiers() # count selected rows - if more than 1 do not close popwin if modifiers or self.count_selected_rows() > 1: super(TreeView, self).mouseReleaseEvent(event) else: - if conf.close_details_clicking_somewhere: - statuswindow.hide_window() - else: - self.cell_clicked() + statuswindow.hide_window() return elif event.button() == Qt.MouseButton.RightButton: self.cell_clicked() return + elif not modifiers or \ + event.button() == Qt.MouseButton.RightButton: + self.cell_clicked() + return else: - if event.button() in [Qt.MouseButton.LeftButton, Qt.MouseButton.RightButton]: - self.cell_clicked() - return - super(TreeView, self).mouseReleaseEvent(event) + super(TreeView, self).mouseReleaseEvent(event) def wheelEvent(self, event): """ @@ -3458,187 +3449,176 @@ def cell_clicked(self): Windows reacts differently to clicks into table cells than Linux and MacOSX Therefore the .available flag is necessary """ + # empty the menu + self.action_menu.clear() - if self.action_menu.available or OS != OS_WINDOWS: - # set flag for Windows - self.action_menu.available = False - - # empty the menu - self.action_menu.clear() - - # clear signal mappings - self.signalmapper_action_menu.removeMappings(self.signalmapper_action_menu) - - # add custom actions - actions_list = list(conf.actions) - actions_list.sort(key=str.lower) - - # How many rows do we have - list_rows = [] - for index in self.selectedIndexes(): - if index.row() not in list_rows: - list_rows.append(index.row()) - - # dummy definition to avoid crash if no actions are enabled - asked for some lines later - miserable_service = None - - # Add custom actions if all selected rows want them, one per one - for a in actions_list: - # shortcut for next lines - action = conf.actions[a] - - # check if current monitor server type is in action - # second check for server type is legacy-compatible with older settings - if action.enabled is True and (action.monitor_type in ['', self.server.TYPE] or - action.monitor_type not in SERVER_TYPES): - - # menu item visibility flag - item_visible = None - - for lrow in list_rows: - # temporary menu item visibility flag to collect all visibility info - item_visible_temporary = False - # take data from model data_array - miserable_host = self.model().data_array[lrow][0] - miserable_service = self.model().data_array[lrow][2] - - # check if clicked line is a service or host - # if it is check if the action is targeted on hosts or services - if miserable_service: - if action.filter_target_service is True: - # only check if there is some to check - if action.re_host_enabled is True: - if is_found_by_re(miserable_host, - action.re_host_pattern, - action.re_host_reverse): - item_visible_temporary = True - # dito - if action.re_service_enabled is True: - if is_found_by_re(miserable_service, - action.re_service_pattern, - action.re_service_reverse): - item_visible_temporary = True - # dito - if action.re_status_information_enabled is True: - if is_found_by_re(miserable_service, - action.re_status_information_pattern, - action.re_status_information_reverse): - item_visible_temporary = True - # dito - if action.re_duration_enabled is True: - if is_found_by_re(miserable_service, - action.re_duration_pattern, - action.re_duration_reverse): - item_visible_temporary = True - - # dito - if action.re_attempt_enabled is True: - if is_found_by_re(miserable_service, - action.re_attempt_pattern, - action.re_attempt_reverse): - item_visible_temporary = True - - # dito - if action.re_groups_enabled is True: - if is_found_by_re(miserable_service, - action.re_groups_pattern, - action.re_groups_reverse): - item_visible_temporary = True - - # fallback if no regexp is selected - if action.re_host_enabled == action.re_service_enabled == \ - action.re_status_information_enabled == action.re_duration_enabled == \ - action.re_attempt_enabled == action.re_groups_enabled is False: + # clear signal mappings + self.signalmapper_action_menu.removeMappings(self.signalmapper_action_menu) + + # add custom actions + actions_list = list(conf.actions) + actions_list.sort(key=str.lower) + + # How many rows do we have + list_rows = [] + for index in self.selectedIndexes(): + if index.row() not in list_rows: + list_rows.append(index.row()) + + # dummy definition to avoid crash if no actions are enabled - asked for some lines later + miserable_service = None + + # Add custom actions if all selected rows want them, one per one + for a in actions_list: + # shortcut for next lines + action = conf.actions[a] + + # check if current monitor server type is in action + # second check for server type is legacy-compatible with older settings + if action.enabled is True and (action.monitor_type in ['', self.server.TYPE] or + action.monitor_type not in SERVER_TYPES): + + # menu item visibility flag + item_visible = None + + for lrow in list_rows: + # temporary menu item visibility flag to collect all visibility info + item_visible_temporary = False + # take data from model data_array + miserable_host = self.model().data_array[lrow][0] + miserable_service = self.model().data_array[lrow][2] + + # check if clicked line is a service or host + # if it is check if the action is targeted on hosts or services + if miserable_service: + if action.filter_target_service is True: + # only check if there is some to check + if action.re_host_enabled is True: + if is_found_by_re(miserable_host, + action.re_host_pattern, + action.re_host_reverse): + item_visible_temporary = True + # dito + if action.re_service_enabled is True: + if is_found_by_re(miserable_service, + action.re_service_pattern, + action.re_service_reverse): + item_visible_temporary = True + # dito + if action.re_status_information_enabled is True: + if is_found_by_re(miserable_service, + action.re_status_information_pattern, + action.re_status_information_reverse): + item_visible_temporary = True + # dito + if action.re_duration_enabled is True: + if is_found_by_re(miserable_service, + action.re_duration_pattern, + action.re_duration_reverse): item_visible_temporary = True - else: - # hosts should only care about host specific actions, no services - if action.filter_target_host is True: - if action.re_host_enabled is True: - if is_found_by_re(miserable_host, - action.re_host_pattern, - action.re_host_reverse): - item_visible_temporary = True - else: - # a non specific action will be displayed per default + # dito + if action.re_attempt_enabled is True: + if is_found_by_re(miserable_service, + action.re_attempt_pattern, + action.re_attempt_reverse): item_visible_temporary = True - # when item_visible never has been set it shall be false - # also if at least one row leads to not-showing the item it will be false - if item_visible_temporary and item_visible is None: - item_visible = True - if not item_visible_temporary: - item_visible = False + # dito + if action.re_groups_enabled is True: + if is_found_by_re(miserable_service, + action.re_groups_pattern, + action.re_groups_reverse): + item_visible_temporary = True - else: - item_visible = False - - # populate context menu with service actions - if item_visible is True: - # create action - action_menuentry = QAction(a, self) - # add action - self.action_menu.addAction(action_menuentry) - # action to signalmapper - self.signalmapper_action_menu.setMapping(action_menuentry, a) - action_menuentry.triggered.connect(self.signalmapper_action_menu.map) - - del action, item_visible - - # create and add default actions - action_edit_actions = QAction('Edit actions...', self) - action_edit_actions.triggered.connect(self.action_edit_actions) - self.action_menu.addAction(action_edit_actions) - # put actions into menu after separator - - self.action_menu.addSeparator() - if 'Monitor' in self.server.MENU_ACTIONS and len(list_rows) == 1: - action_monitor = QAction('Monitor', self) - action_monitor.triggered.connect(self.action_monitor) - self.action_menu.addAction(action_monitor) - - if 'Recheck' in self.server.MENU_ACTIONS: - action_recheck = QAction('Recheck', self) - action_recheck.triggered.connect(self.action_recheck) - self.action_menu.addAction(action_recheck) - - if 'Acknowledge' in self.server.MENU_ACTIONS: - action_acknowledge = QAction('Acknowledge', self) - action_acknowledge.triggered.connect(self.action_acknowledge) - self.action_menu.addAction(action_acknowledge) - - if 'Downtime' in self.server.MENU_ACTIONS: - action_downtime = QAction('Downtime', self) - action_downtime.triggered.connect(self.action_downtime) - self.action_menu.addAction(action_downtime) - - # special menu entry for Checkmk Multisite for archiving events - if self.server.type == 'Checkmk Multisite' and len(list_rows) == 1: - if miserable_service == 'Events': - action_archive_event = QAction('Archive event', self) - action_archive_event.triggered.connect(self.action_archive_event) - self.action_menu.addAction(action_archive_event) - - # not all servers allow to submit fake check results - if 'Submit check result' in self.server.MENU_ACTIONS and len(list_rows) == 1: - action_submit = QAction('Submit check result', self) - action_submit.triggered.connect(self.action_submit) - self.action_menu.addAction(action_submit) - - # experimental clipboard submenu - self.action_menu.addMenu(self.clipboard_menu) - - # show menu - self.action_menu.show_at_cursor() - else: - self.action_menu.available = True + # fallback if no regexp is selected + if action.re_host_enabled == action.re_service_enabled == \ + action.re_status_information_enabled == action.re_duration_enabled == \ + action.re_attempt_enabled == action.re_groups_enabled is False: + item_visible_temporary = True + + else: + # hosts should only care about host specific actions, no services + if action.filter_target_host is True: + if action.re_host_enabled is True: + if is_found_by_re(miserable_host, + action.re_host_pattern, + action.re_host_reverse): + item_visible_temporary = True + else: + # a non specific action will be displayed per default + item_visible_temporary = True + + # when item_visible never has been set it shall be false + # also if at least one row leads to not-showing the item it will be false + if item_visible_temporary and item_visible is None: + item_visible = True + if not item_visible_temporary: + item_visible = False + else: + item_visible = False + + # populate context menu with service actions + if item_visible is True: + # create action + action_menuentry = QAction(a, self) + # add action + self.action_menu.addAction(action_menuentry) + # action to signalmapper + self.signalmapper_action_menu.setMapping(action_menuentry, a) + action_menuentry.triggered.connect(self.signalmapper_action_menu.map) + + del action, item_visible + + # create and add default actions + action_edit_actions = QAction('Edit actions...', self) + action_edit_actions.triggered.connect(self.action_edit_actions) + self.action_menu.addAction(action_edit_actions) + # put actions into menu after separator + + self.action_menu.addSeparator() + if 'Monitor' in self.server.MENU_ACTIONS and len(list_rows) == 1: + action_monitor = QAction('Monitor', self) + action_monitor.triggered.connect(self.action_monitor) + self.action_menu.addAction(action_monitor) + + if 'Recheck' in self.server.MENU_ACTIONS: + action_recheck = QAction('Recheck', self) + action_recheck.triggered.connect(self.action_recheck) + self.action_menu.addAction(action_recheck) + + if 'Acknowledge' in self.server.MENU_ACTIONS: + action_acknowledge = QAction('Acknowledge', self) + action_acknowledge.triggered.connect(self.action_acknowledge) + self.action_menu.addAction(action_acknowledge) + + if 'Downtime' in self.server.MENU_ACTIONS: + action_downtime = QAction('Downtime', self) + action_downtime.triggered.connect(self.action_downtime) + self.action_menu.addAction(action_downtime) + + # special menu entry for Checkmk Multisite for archiving events + if self.server.type == 'Checkmk Multisite' and len(list_rows) == 1: + if miserable_service == 'Events': + action_archive_event = QAction('Archive event', self) + action_archive_event.triggered.connect(self.action_archive_event) + self.action_menu.addAction(action_archive_event) + + # not all servers allow to submit fake check results + if 'Submit check result' in self.server.MENU_ACTIONS and len(list_rows) == 1: + action_submit = QAction('Submit check result', self) + action_submit.triggered.connect(self.action_submit) + self.action_menu.addAction(action_submit) + + # experimental clipboard submenu + self.action_menu.addMenu(self.clipboard_menu) + + # show menu + self.action_menu.show_at_cursor() @Slot(str) def action_menu_custom_response(self, action): - # avoid blocked context menu - self.action_menu.available = True - # How many rows do we have list_rows = [] for index in self.selectedIndexes(): @@ -3685,7 +3665,6 @@ def action_menu_custom_response(self, action): # clean up del list_rows - @Slot() def action_response_decorator(method): """ @@ -3693,8 +3672,6 @@ def action_response_decorator(method): """ def decoration_function(self): - # avoid blocked context menu - self.action_menu.available = True # run decorated method method(self) # default actions need closed statuswindow to display own dialogs @@ -3705,7 +3682,6 @@ def decoration_function(self): return (decoration_function) - @action_response_decorator def action_edit_actions(self): # buttons in toparee @@ -3714,7 +3690,6 @@ def action_edit_actions(self): # open actions tab (#3) of settings dialog dialogs.settings.show(tab=3) - @action_response_decorator def action_monitor(self): # only on 1 row @@ -3727,7 +3702,6 @@ def action_monitor(self): # open host/service monitor in browser self.server.open_monitor(miserable_host, miserable_service) - @action_response_decorator def action_recheck(self): # How many rows we have @@ -3744,7 +3718,6 @@ def action_recheck(self): self.recheck.emit({'host': miserable_host, 'service': miserable_service}) - @action_response_decorator def action_acknowledge(self): list_host = [] @@ -3766,7 +3739,6 @@ def action_acknowledge(self): service=list_service) dialogs.acknowledge.show() - @action_response_decorator def action_downtime(self): list_host = [] @@ -3788,7 +3760,6 @@ def action_downtime(self): service=list_service) dialogs.downtime.show() - @action_response_decorator def action_archive_event(self): """ @@ -3841,7 +3812,6 @@ def action_archive_event(self): # clean up del index, indexes, list_rows, list_host, list_service, list_status - @action_response_decorator def action_submit(self): # only on 1 row @@ -3856,7 +3826,6 @@ def action_submit(self): service=miserable_service) dialogs.submit.show() - @Slot() def action_clipboard_action_host(self): """ @@ -3882,7 +3851,6 @@ def action_clipboard_action_host(self): clipboard.setText(text) - @Slot() def action_clipboard_action_service(self): """ @@ -3908,7 +3876,6 @@ def action_clipboard_action_service(self): clipboard.setText(text) - @Slot() def action_clipboard_action_statusinformation(self): """ @@ -3933,7 +3900,6 @@ def action_clipboard_action_statusinformation(self): clipboard.setText(text) - @Slot() def action_clipboard_action_all(self): """ @@ -3978,7 +3944,6 @@ def action_clipboard_action_all(self): # copy text to clipboard clipboard.setText(text) - @Slot() def refresh(self): """ @@ -4007,7 +3972,6 @@ def refresh(self): self.status_changed.emit(self.server.name, self.server.worst_status_diff, self.server.worst_status_current) - @Slot(int, Qt.SortOrder) def sort_columns(self, sort_column, sort_order): """ @@ -4017,7 +3981,6 @@ def sort_columns(self, sort_column, sort_order): # intransmissible self.sort_data_array_for_columns.emit(int(sort_column), int(sort_order), True) - @Slot() def finish_worker_thread(self): """ @@ -4028,7 +3991,6 @@ def finish_worker_thread(self): # wait until thread is really stopped self.worker_thread.wait() - class Worker(QObject): """ attempt to run a server status update thread - only needed by table so it is defined here inside table diff --git a/build/debian/changelog b/build/debian/changelog index 6030cd433..eb27bb1c6 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.9-20220606) unstable; urgency=low +nagstamon (3.9-20220611) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Sun, 05 Jun 2022 08:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Sat, 11 Jun 2022 08:00:00 +0100 nagstamon (3.8.0) stable; urgency=low * New upstream From 203e74ac0a70028e83e615c2927b0a07ba818a18 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 19 Jun 2022 12:10:59 +0200 Subject: [PATCH 304/884] 20220619 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 050a8ceb1..f10e10801 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20220611' + VERSION = '3.9-20220619' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index eb27bb1c6..f455c1816 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.9-20220611) unstable; urgency=low +nagstamon (3.9-20220619) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Sat, 11 Jun 2022 08:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Sun, 19 Jun 2022 08:00:00 +0100 nagstamon (3.8.0) stable; urgency=low * New upstream From fde9314c7e9068ef50f4844e841dfc9fb7862fc0 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 19 Jun 2022 12:59:33 +0200 Subject: [PATCH 305/884] virtualsize --- Nagstamon/QUI/__init__.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 87da931f2..798fc2006 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -1227,7 +1227,7 @@ def set_mode(self): # show statusbar/statuswindow on last saved position # when coordinates are inside known screens - if get_screen(conf.position_x, conf.position_y): + if get_screen_name(conf.position_x, conf.position_y): self.move(conf.position_x, conf.position_y) else: # get available desktop specs @@ -1254,7 +1254,7 @@ def set_mode(self): # show statusbar/statuswindow on last saved position # when coordinates are inside known screens - if get_screen(conf.position_x, conf.position_y): + if get_screen_name(conf.position_x, conf.position_y): self.move(conf.position_x, conf.position_y) else: # get available desktop specs @@ -1504,11 +1504,17 @@ def show_window_systrayicon(self): if icon_y == 0: icon_y = QCursor.pos().y() + screen = APP.screenAt(QPoint(icon_x, icon_y)) + # get available desktop specs - available_width = self.screen().availableGeometry().width() - available_height = self.screen().availableGeometry().height() - available_x = self.screen().availableGeometry().x() - available_y = self.screen().availableGeometry().y() + # available_width = self.screen().availableGeometry().width() + # available_height = self.screen().availableGeometry().height() + # available_x = self.screen().availableGeometry().x() + # available_y = self.screen().availableGeometry().y() + available_width = screen.availableVirtualGeometry().width() + available_height = screen.availableVirtualGeometry().height() + available_x = screen.availableVirtualGeometry().x() + available_y = screen.availableVirtualGeometry().y() y = 0 @@ -1764,7 +1770,7 @@ def calculate_size(self): elif icon_y != 0: self.icon_y = icon_y - screen_or_widget = get_screen(self.icon_x, self.icon_y) + screen_or_widget = get_screen_name(self.icon_x, self.icon_y) # only consider offset if it is configured if conf.systray_offset_use and conf.icon_in_systray: @@ -7034,7 +7040,7 @@ def create_brushes(): QBRUSHES[1][COLORS[state] + role] = QBRUSHES[0][COLORS[state] + role] -def get_screen(x, y): +def get_screen_name(x, y): """ find out which screen the given coordinates belong to gives back 'None' if coordinates are out of any known screen From e4d77879d1ed94f30ad4447e1694199f32e216b1 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 19 Jun 2022 14:56:12 +0200 Subject: [PATCH 306/884] virtualsize III --- Nagstamon/QUI/__init__.py | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 798fc2006..19abeb8dc 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -1277,6 +1277,7 @@ def set_mode(self): if OS == OS_WINDOWS: systrayicon = SystemTrayIcon() self.connect_systrayicon() + systrayicon.show_popwin.emit() systrayicon.show() # need a close button @@ -1284,11 +1285,6 @@ def set_mode(self): elif conf.fullscreen: # no need for systray - # if OS == OS_WINDOWS: - # # workaround for PyQt behavior since Qt 5.10 - # systrayicon = QSystemTrayIcon() - # else: - # systrayicon.hide() systrayicon.hide() # needed permanently @@ -1507,15 +1503,16 @@ def show_window_systrayicon(self): screen = APP.screenAt(QPoint(icon_x, icon_y)) # get available desktop specs - # available_width = self.screen().availableGeometry().width() - # available_height = self.screen().availableGeometry().height() - # available_x = self.screen().availableGeometry().x() - # available_y = self.screen().availableGeometry().y() - available_width = screen.availableVirtualGeometry().width() - available_height = screen.availableVirtualGeometry().height() - available_x = screen.availableVirtualGeometry().x() - available_y = screen.availableVirtualGeometry().y() - + if OS != OS_NON_LINUX: + available_width = screen.availableVirtualGeometry().width() + available_height = screen.availableVirtualGeometry().height() + available_x = screen.availableVirtualGeometry().x() + available_y = screen.availableVirtualGeometry().y() + else: + available_width = screen.availableGeometry().width() + available_height = screen.availableGeometry().height() + available_x = screen.availableGeometry().x() + available_y = screen.availableGeometry().y() y = 0 if icon_x > (available_width + available_x) // 2: @@ -1534,7 +1531,6 @@ def show_window_systrayicon(self): # already show here because was closed before in hide_window() self.show() - self.show_window() else: self.hide_window() From bdf6826c714c3cd71b1446103d2c84e677165878 Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Mon, 20 Jun 2022 07:31:31 +0200 Subject: [PATCH 307/884] no extra treatment of systray for windows --- Nagstamon/QUI/__init__.py | 60 ++++++++++++++------------------------- 1 file changed, 21 insertions(+), 39 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 19abeb8dc..03a163979 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -1110,6 +1110,27 @@ def __init__(self): # stop notification if window gets shown or hidden self.hiding.connect(self.worker_notification.stop) + # systray connections + # show status popup when systray icon was clicked + systrayicon.show_popwin.connect(self.show_window_systrayicon) + systrayicon.hide_popwin.connect(self.hide_window) + # flashing statusicon + self.worker_notification.start_flash.connect(systrayicon.flash) + self.worker_notification.stop_flash.connect(systrayicon.reset) + # connect status window server vboxes to systray + for server_vbox in self.servers_vbox.children(): + if 'server' in server_vbox.__dict__.keys(): + # tell systray after table was refreshed + server_vbox.table.worker.new_status.connect(systrayicon.show_state) + # show error icon in systray + server_vbox.table.worker.show_error.connect(systrayicon.set_error) + server_vbox.table.worker.hide_error.connect(systrayicon.reset_error) + + # context menu, checking for existence necessary at startup + global menu + if not menu == None: + systrayicon.set_menu(menu) + self.worker_notification.moveToThread(self.worker_notification_thread) # start with low priority self.worker_notification_thread.start(QThread.Priority.LowestPriority) @@ -1169,39 +1190,9 @@ def __init__(self): # clean shutdown of thread self.worker.finish.connect(self.finish_worker_thread) - # part of the stupid workaround for Qt-5.10-Windows-QSystemTrayIcon-madness - self.connect_systrayicon() - # finally show up self.set_mode() - def connect_systrayicon(self): - ''' - stupid workaround for QSystemTrayIcon-problem under Windows since Qt 5.10: - - systray icon cannot be hidden anymore and so if it should be hidden it has to be reinitialized without icon - - to react to signals these have to be reconnected when changing mode via set_mode() - :return: - ''' - global menu - # show status popup when systray icon was clicked - systrayicon.show_popwin.connect(self.show_window_systrayicon) - systrayicon.hide_popwin.connect(self.hide_window) - # flashing statusicon - self.worker_notification.start_flash.connect(systrayicon.flash) - self.worker_notification.stop_flash.connect(systrayicon.reset) - # connect status window server vboxes to systray - for server_vbox in self.servers_vbox.children(): - if 'server' in server_vbox.__dict__.keys(): - # tell systray after table was refreshed - server_vbox.table.worker.new_status.connect(systrayicon.show_state) - # show error icon in systray - server_vbox.table.worker.show_error.connect(systrayicon.set_error) - server_vbox.table.worker.hide_error.connect(systrayicon.reset_error) - - # context menu, checking for existence necessary at startup - if not menu == None: - systrayicon.set_menu(menu) - def set_mode(self): """ apply presentation mode @@ -1217,11 +1208,6 @@ def set_mode(self): if conf.statusbar_floating: # no need for systray - # if OS == OS_WINDOWS: - # # workaround for PyQt behavior since Qt 5.10 - # systrayicon = QSystemTrayIcon() - # else: - # systrayicon.hide() systrayicon.hide() self.statusbar.show() @@ -1274,10 +1260,6 @@ def set_mode(self): self.setAttribute(Qt.WidgetAttribute.WA_ShowWithoutActivating) # yeah! systray! - if OS == OS_WINDOWS: - systrayicon = SystemTrayIcon() - self.connect_systrayicon() - systrayicon.show_popwin.emit() systrayicon.show() # need a close button From 6a5249ee2d81cb115a5dfd010e8c07027f8b70bc Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 26 Jun 2022 13:56:28 +0200 Subject: [PATCH 308/884] 20220626 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index f10e10801..e83ef552c 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20220619' + VERSION = '3.9-20220626' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index f455c1816..35f0ec542 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.9-20220619) unstable; urgency=low +nagstamon (3.9-20220626) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Sun, 19 Jun 2022 08:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Sun, 26 Jun 2022 08:00:00 +0100 nagstamon (3.8.0) stable; urgency=low * New upstream From 678a00f0f70a32bd01fb42e7f912a6a57770dbe7 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 26 Jun 2022 15:13:55 +0200 Subject: [PATCH 309/884] proposal for switching Centreon versions --- .../Servers/{ => Centreon}/CentreonAPI.py | 26 ++++---- .../CentreonLegacy.py} | 64 ++++++++++--------- Nagstamon/Servers/Centreon/__init__.py | 40 ++++++++++++ 3 files changed, 87 insertions(+), 43 deletions(-) rename Nagstamon/Servers/{ => Centreon}/CentreonAPI.py (98%) rename Nagstamon/Servers/{Centreon.py => Centreon/CentreonLegacy.py} (97%) create mode 100644 Nagstamon/Servers/Centreon/__init__.py diff --git a/Nagstamon/Servers/CentreonAPI.py b/Nagstamon/Servers/Centreon/CentreonAPI.py similarity index 98% rename from Nagstamon/Servers/CentreonAPI.py rename to Nagstamon/Servers/Centreon/CentreonAPI.py index d7989ece9..cca5ba317 100644 --- a/Nagstamon/Servers/CentreonAPI.py +++ b/Nagstamon/Servers/Centreon/CentreonAPI.py @@ -43,29 +43,31 @@ # to use 'ressources'. class CentreonServer(GenericServer): - TYPE = 'Centreon API' - # Centreon API uses a token - token = None + def __init__(self, **kwds): + self.TYPE = 'Centreon' - # HARD/SOFT state mapping - HARD_SOFT = {'(H)': 'hard', '(S)': 'soft'} + # Centreon API uses a token + self.token = None - # Entries for monitor default actions in context menu - MENU_ACTIONS = ['Monitor', 'Recheck', 'Acknowledge', 'Downtime'] + # HARD/SOFT state mapping + self.HARD_SOFT = {'(H)': 'hard', '(S)': 'soft'} - # URLs of the Centreon pages - urls_centreon = None + # Entries for monitor default actions in context menu + self.MENU_ACTIONS = ['Monitor', 'Recheck', 'Acknowledge', 'Downtime'] - # limit number of services retrived - limit_services_number = 9999 + # URLs of the Centreon pages + self.urls_centreon = None + + # limit number of services retrived + self.limit_services_number = 9999 def init_config(self): ''' init_config, called at thread start, not really needed here, just omit extra properties ''' - # FIX but be provided by user as their is no way to detect it + # FIX but be provided by user as their is no way to detect it self.user_provided_centreon_version = "20.04" if re.search('2(0|1|2)\.(04|10)', self.user_provided_centreon_version): diff --git a/Nagstamon/Servers/Centreon.py b/Nagstamon/Servers/Centreon/CentreonLegacy.py similarity index 97% rename from Nagstamon/Servers/Centreon.py rename to Nagstamon/Servers/Centreon/CentreonLegacy.py index 5fcdff749..fbd5ff56d 100644 --- a/Nagstamon/Servers/Centreon.py +++ b/Nagstamon/Servers/Centreon/CentreonLegacy.py @@ -34,37 +34,39 @@ class CentreonServer(GenericServer): - TYPE = 'Centreon' - - # centreon generic web interface uses a sid which is needed to ask for news - SID = None - - # HARD/SOFT state mapping - HARD_SOFT = {'(H)': 'hard', '(S)': 'soft'} - - # apparently necessesary because of non-english states as in https://github.com/HenriWahl/Nagstamon/issues/91 (Centeron 2.5) - TRANSLATIONS = {'INDISPONIBLE': 'DOWN', - 'INJOIGNABLE': 'UNREACHABLE', - 'CRITIQUE': 'CRITICAL', - 'INCONNU': 'UNKNOWN', - 'ALERTE': 'WARNING'} - - # Entries for monitor default actions in context menu - MENU_ACTIONS = ['Monitor', 'Recheck', 'Acknowledge', 'Downtime'] - - # Centreon works better or at all with html.parser for BeautifulSoup - PARSER = 'html.parser' - - # Needed to detect each Centreon's version - centreon_version = None - # Token that centreon use to protect the system - centreon_token = None - # To only detect broker once - first_login = True - # limit number of services retrived - limit_services_number = 9999 - # default value, applies to version 2.2 and others - XML_PATH = 'xml' + + def __init__(self, **kwds): + self.TYPE = 'Centreon' + + # centreon generic web interface uses a sid which is needed to ask for news + self.SID = None + + # HARD/SOFT state mapping + self.HARD_SOFT = {'(H)': 'hard', '(S)': 'soft'} + + # apparently necessesary because of non-english states as in https://github.com/HenriWahl/Nagstamon/issues/91 (Centeron 2.5) + self.TRANSLATIONS = {'INDISPONIBLE': 'DOWN', + 'INJOIGNABLE': 'UNREACHABLE', + 'CRITIQUE': 'CRITICAL', + 'INCONNU': 'UNKNOWN', + 'ALERTE': 'WARNING'} + + # Entries for monitor default actions in context menu + self.MENU_ACTIONS = ['Monitor', 'Recheck', 'Acknowledge', 'Downtime'] + + # Centreon works better or at all with html.parser for BeautifulSoup + self.PARSER = 'html.parser' + + # Needed to detect each Centreon's version + self.centreon_version = None + # Token that centreon use to protect the system + self.centreon_token = None + # To only detect broker once + self.first_login = True + # limit number of services retrived + self.limit_services_number = 9999 + # default value, applies to version 2.2 and others + self.XML_PATH = 'xml' def init_config(self): ''' diff --git a/Nagstamon/Servers/Centreon/__init__.py b/Nagstamon/Servers/Centreon/__init__.py new file mode 100644 index 000000000..cf97f6561 --- /dev/null +++ b/Nagstamon/Servers/Centreon/__init__.py @@ -0,0 +1,40 @@ +# Nagstamon - Nagios status monitor for your desktop +# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + +from ..Generic import GenericServer +from Nagstamon.Config import conf + + +class CentreonServer(GenericServer): + """ + Use this class as switch + """ + TYPE = 'Centreon' + def __init__(self, **kwds): + # like all servers we need to initialize Generic + GenericServer.__init__(self, **kwds) + # due to not being initialized right now we need to access config directly to get this instance's config + server_conf = conf.servers.get(kwds.get('name')) + if server_conf: + # This URL exists on Centreon 22x - if not accessiblie it must be legacy + versions_raw = self.FetchURL(f'{server_conf.monitor_cgi_url}/api/latest/platform/versions', no_auth=True) + if versions_raw.status_code == 200: + from .CentreonAPI import CentreonServer as CentreonServerReal + else: + from .CentreonLegacy import CentreonServer as CentreonServerReal + CentreonServerReal.__init__(self, name=kwds.get('name')) From 2a399c89bcfd29c7f8d7d265e27e6b6985102305 Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Sun, 26 Jun 2022 20:01:32 +0200 Subject: [PATCH 310/884] no extra treatment of systray for windows part II --- .github/workflows/build-release-latest.yml | 2 +- Nagstamon/QUI/__init__.py | 27 +++++----------------- 2 files changed, 7 insertions(+), 22 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 6004f9677..378b8acc2 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -5,7 +5,7 @@ on: branches: '**' env: - python_win_version: 3.10.4 + python_win_version: 3.10.5 repo_dir: nagstamon-jekyll/docs/repo jobs: diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 03a163979..7ec24926b 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -94,16 +94,11 @@ print('No DBus for desktop notification available.') DBUS_AVAILABLE = False -# same KfW trouble as in Servers/Generic.py -if OS != OS_WINDOWS: - # check ECP authentication support availability - try: - from requests_ecp import HTTPECPAuth - - ECP_AVAILABLE = True - except ImportError: - ECP_AVAILABLE = False -else: +# check ECP authentication support availability +try: + from requests_ecp import HTTPECPAuth + ECP_AVAILABLE = True +except ImportError: ECP_AVAILABLE = False # since Qt6 HighDPI-awareness is default behaviour @@ -321,7 +316,7 @@ def __init__(self): # little workaround to match statuswindow.worker_notification.worst_notification_status self.icons['UP'] = self.icons['OK'] # default icon is OK - if OS != OS_WINDOWS or conf.icon_in_systray: + if conf.icon_in_systray: self.setIcon(self.icons['OK']) if conf.debug_mode: debug_queue.append('DEBUG: SystemTrayIcon initial icon: {}'.format(self.currentIconName())) @@ -413,11 +408,6 @@ def icon_clicked(self, reason): """ evaluate mouse click """ - # # some obscure Windows problem again - # if reason == QSystemTrayIcon.Context and OS == 'Windows': - # self.show_menu.emit() - # only react on left mouse click on OSX - # elif reason == (QSystemTrayIcon.Trigger or QSystemTrayIcon.DoubleClick): if reason in (QSystemTrayIcon.ActivationReason.Trigger, QSystemTrayIcon.ActivationReason.DoubleClick, QSystemTrayIcon.ActivationReason.MiddleClick): @@ -1296,11 +1286,6 @@ def set_mode(self): self.toparea.button_close.hide() elif conf.windowed: - # if OS == OS_WINDOWS: - # # workaround for PyQt behavior since Qt 5.10 - # #systrayicon = QSystemTrayIcon() - # else: - # systrayicon.hide() systrayicon.hide() # no need for close button From 376eee856cf5dcde7078d2ee207321e7f61bf149 Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Sun, 26 Jun 2022 20:45:44 +0200 Subject: [PATCH 311/884] 20220626 for qt6 --- Nagstamon/Config.py | 2 +- Nagstamon/QUI/__init__.py | 3 ++- Nagstamon/QUI/qt.py | 12 ++++++++++++ build/debian/changelog | 4 ++-- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index f10e10801..e83ef552c 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20220619' + VERSION = '3.9-20220626' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 7ec24926b..2fb14305f 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -3948,7 +3948,8 @@ def sort_columns(self, sort_column, sort_order): """ # better int() the Qt.* values because they partly seem to be # intransmissible - self.sort_data_array_for_columns.emit(int(sort_column), int(sort_order), True) + # get_sort_order_value() cures the differences between Qt5 and Qt6 + self.sort_data_array_for_columns.emit(int(sort_column), int(get_sort_order_value(sort_order)), True) @Slot() def finish_worker_thread(self): diff --git a/Nagstamon/QUI/qt.py b/Nagstamon/QUI/qt.py index d206cdfee..621631557 100644 --- a/Nagstamon/QUI/qt.py +++ b/Nagstamon/QUI/qt.py @@ -158,6 +158,12 @@ def get_global_position(event): ''' return event.globalPos() + def get_sort_order_value(sort_order): + ''' + Qt5 has int for Qt.SortOrder but Qt6 has Qt.SortOrder.[Ascending|Descending]Order + ''' + return sort_order + elif QT_FLAVOR == 'PyQt6': # PySide/PyQt compatibility @@ -271,6 +277,12 @@ def get_global_position(event): ''' return event.globalPosition() + def get_sort_order_value(sort_order): + ''' + Qt5 has int for Qt.SortOrder but Qt6 has Qt.SortOrder.[Ascending|Descending]Order + ''' + return sort_order.value + # elif 'PySide6' in sys.modules: # from PySide6.QtCore import Signal, \ # Slot, \ diff --git a/build/debian/changelog b/build/debian/changelog index f455c1816..35f0ec542 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.9-20220619) unstable; urgency=low +nagstamon (3.9-20220626) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Sun, 19 Jun 2022 08:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Sun, 26 Jun 2022 08:00:00 +0100 nagstamon (3.8.0) stable; urgency=low * New upstream From 25d350fcae372574dab81df39ae7c39bff4b273f Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 28 Jun 2022 23:15:52 +0200 Subject: [PATCH 312/884] solution for Centreon switch --- Nagstamon/Servers/Centreon/CentreonAPI.py | 3 +++ Nagstamon/Servers/Centreon/CentreonLegacy.py | 3 +++ Nagstamon/Servers/Centreon/__init__.py | 24 ++++++++++++++++---- Nagstamon/Servers/__init__.py | 3 +++ 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/Nagstamon/Servers/Centreon/CentreonAPI.py b/Nagstamon/Servers/Centreon/CentreonAPI.py index cca5ba317..59a21f8be 100644 --- a/Nagstamon/Servers/Centreon/CentreonAPI.py +++ b/Nagstamon/Servers/Centreon/CentreonAPI.py @@ -45,6 +45,9 @@ class CentreonServer(GenericServer): def __init__(self, **kwds): + + GenericServer.__init__(self, **kwds) + self.TYPE = 'Centreon' # Centreon API uses a token diff --git a/Nagstamon/Servers/Centreon/CentreonLegacy.py b/Nagstamon/Servers/Centreon/CentreonLegacy.py index fbd5ff56d..522446706 100644 --- a/Nagstamon/Servers/Centreon/CentreonLegacy.py +++ b/Nagstamon/Servers/Centreon/CentreonLegacy.py @@ -36,6 +36,9 @@ class CentreonServer(GenericServer): def __init__(self, **kwds): + + GenericServer.__init__(self, **kwds) + self.TYPE = 'Centreon' # centreon generic web interface uses a sid which is needed to ask for news diff --git a/Nagstamon/Servers/Centreon/__init__.py b/Nagstamon/Servers/Centreon/__init__.py index cf97f6561..60fb1d165 100644 --- a/Nagstamon/Servers/Centreon/__init__.py +++ b/Nagstamon/Servers/Centreon/__init__.py @@ -15,7 +15,9 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +import json +from . import CentreonAPI from ..Generic import GenericServer from Nagstamon.Config import conf @@ -30,11 +32,23 @@ def __init__(self, **kwds): GenericServer.__init__(self, **kwds) # due to not being initialized right now we need to access config directly to get this instance's config server_conf = conf.servers.get(kwds.get('name')) - if server_conf: - # This URL exists on Centreon 22x - if not accessiblie it must be legacy - versions_raw = self.FetchURL(f'{server_conf.monitor_cgi_url}/api/latest/platform/versions', no_auth=True) + if server_conf and server_conf.enabled: + # This URL exists on Centreon 22.x - if not accessible it must be legacy + versions_raw = self.FetchURL(f'{server_conf.monitor_cgi_url}/api/latest/platform/versions', no_auth=True, giveback='raw') + self.Debug(server='[' + self.get_name() + ']', debug='Status code %s' % (str(versions_raw.status_code))) if versions_raw.status_code == 200: - from .CentreonAPI import CentreonServer as CentreonServerReal + # API V2 is usable only after 20.10 + data = json.loads(versions_raw.result) + ver_major = int(data["web"]["major"]) + ver_minor = int(data["web"]["minor"]) + if (ver_major > 20) or (ver_major == 20 and ver_minor == 10): + self.Debug(server='[' + self.get_name() + ']', debug='>>>>>>>>>>>>>>>> API ' + str(ver_major) + ' ' + str(ver_minor)) + from .CentreonAPI import CentreonServer as CentreonServerReal + else: + self.Debug(server='[' + self.get_name() + ']', debug='>>>>>>>>>>>>>>>> LEGACY') + from .CentreonLegacy import CentreonServer as CentreonServerReal else: from .CentreonLegacy import CentreonServer as CentreonServerReal - CentreonServerReal.__init__(self, name=kwds.get('name')) + self.Debug(server='[' + self.get_name() + ']', debug='>>>>>>>>>>>>>>>> LEGACY') + # kind of mad but helps the Servers/__init__.py to detect if there is any other class to be used + self.ClassServerReal = CentreonServerReal diff --git a/Nagstamon/Servers/__init__.py b/Nagstamon/Servers/__init__.py index 6d0f0bc3e..bcf9189dd 100644 --- a/Nagstamon/Servers/__init__.py +++ b/Nagstamon/Servers/__init__.py @@ -147,6 +147,9 @@ def create_server(server=None): return # give argument servername so CentreonServer could use it for initializing MD5 cache new_server = SERVER_TYPES[server.type](name=server.name) + # apparently somewhat hacky but at the end works - might be used for others than Centreon as well + if hasattr(new_server, 'ClassServerReal'): + new_server = new_server.ClassServerReal(name=server.name) new_server.type = server.type new_server.enabled = server.enabled new_server.monitor_url = server.monitor_url From 8f725b25f7dc35d754b634234138af40966f1556 Mon Sep 17 00:00:00 2001 From: Benoit Poulet <benoit.poulet@businessdecision.com> Date: Wed, 29 Jun 2022 12:09:03 +0200 Subject: [PATCH 313/884] 20.10 API v2 is buggy, starting from 21.04 --- Nagstamon/Servers/Centreon/__init__.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Nagstamon/Servers/Centreon/__init__.py b/Nagstamon/Servers/Centreon/__init__.py index 60fb1d165..7220e6ded 100644 --- a/Nagstamon/Servers/Centreon/__init__.py +++ b/Nagstamon/Servers/Centreon/__init__.py @@ -21,7 +21,6 @@ from ..Generic import GenericServer from Nagstamon.Config import conf - class CentreonServer(GenericServer): """ Use this class as switch @@ -37,18 +36,18 @@ def __init__(self, **kwds): versions_raw = self.FetchURL(f'{server_conf.monitor_cgi_url}/api/latest/platform/versions', no_auth=True, giveback='raw') self.Debug(server='[' + self.get_name() + ']', debug='Status code %s' % (str(versions_raw.status_code))) if versions_raw.status_code == 200: - # API V2 is usable only after 20.10 data = json.loads(versions_raw.result) ver_major = int(data["web"]["major"]) ver_minor = int(data["web"]["minor"]) - if (ver_major > 20) or (ver_major == 20 and ver_minor == 10): - self.Debug(server='[' + self.get_name() + ']', debug='>>>>>>>>>>>>>>>> API ' + str(ver_major) + ' ' + str(ver_minor)) + # API V2 is usable only after 21.04 (not tested), ressources endpoit is buggy in 20.10 + if ver_major >= 21: + self.Debug(server='[' + self.get_name() + ']', debug='Loading class API, Centreon version : ' + str(ver_major) + '.' + str(ver_minor)) from .CentreonAPI import CentreonServer as CentreonServerReal else: - self.Debug(server='[' + self.get_name() + ']', debug='>>>>>>>>>>>>>>>> LEGACY') + self.Debug(server='[' + self.get_name() + ']', debug='Loading class LEGACY, Centreon version : ' + str(ver_major) + '.' + str(ver_minor)) from .CentreonLegacy import CentreonServer as CentreonServerReal else: from .CentreonLegacy import CentreonServer as CentreonServerReal - self.Debug(server='[' + self.get_name() + ']', debug='>>>>>>>>>>>>>>>> LEGACY') + self.Debug(server='[' + self.get_name() + ']', debug='Loading class LEGACY, Centreon version will be checked later') # kind of mad but helps the Servers/__init__.py to detect if there is any other class to be used self.ClassServerReal = CentreonServerReal From cc0e075b96cb50b0f64b88d59a95afe2484f8043 Mon Sep 17 00:00:00 2001 From: Benoit Poulet <benoit.poulet@businessdecision.com> Date: Wed, 29 Jun 2022 12:09:11 +0200 Subject: [PATCH 314/884] rewrite version detection --- Nagstamon/Servers/Centreon/CentreonAPI.py | 33 ++++++++++++++--------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/Nagstamon/Servers/Centreon/CentreonAPI.py b/Nagstamon/Servers/Centreon/CentreonAPI.py index 59a21f8be..2d3481d0a 100644 --- a/Nagstamon/Servers/Centreon/CentreonAPI.py +++ b/Nagstamon/Servers/Centreon/CentreonAPI.py @@ -37,10 +37,8 @@ from Nagstamon.Helpers import webbrowser_open # This class support Centreon V2 API - # Things to do : -# - change the way host/services status is gathered, must change it -# to use 'ressources'. +# - BROWSER_URLS -> move into define_url() to be consistent class CentreonServer(GenericServer): @@ -67,17 +65,26 @@ def __init__(self, **kwds): def init_config(self): ''' - init_config, called at thread start, not really needed here, just omit extra properties + init_config, called at thread start ''' + # Version check + result = self.FetchURL(f'{self.monitor_cgi_url}/api/latest/platform/versions', no_auth=True, giveback='raw') + + data = json.loads(result.result) + error = result.error + status_code = result.status_code + + # check if any error occured + errors_occured = self.check_for_error(data, error, status_code) + if errors_occured is not False: + return(errors_occured) - # FIX but be provided by user as their is no way to detect it - self.user_provided_centreon_version = "20.04" + self.centreon_version_major = int(data["web"]["major"]) - if re.search('2(0|1|2)\.(04|10)', self.user_provided_centreon_version): - # from 20.04 to 22.04 the API to use is «latest» - self.centreon_version = 20.04 + if self.centreon_version_major >= 21: + # starting from 21.xx the API to use is «latest», until something change if conf.debug_mode is True: - self.Debug(server='[' + self.get_name() + ']', debug='Centreon version selected : 20.04 <=> 22.04') + self.Debug(server='[' + self.get_name() + ']', debug='Centreon code version selected : >= 21') # URLs for browser shortlinks/buttons on popup window self.BROWSER_URLS = {'monitor': '$MONITOR$/monitoring/resources', 'hosts': '$MONITOR$/monitoring/resources', @@ -88,7 +95,7 @@ def init_config(self): else: if conf.debug_mode is True: - self.Debug(server='[' + self.get_name() + ']', debug='No Centreon version provided') + self.Debug(server='[' + self.get_name() + ']', debug='Unsupported Centreon version') # Changed this because define_url was called 2 times if not self.tls_error and self.urls_centreon is None: @@ -112,7 +119,7 @@ def define_url(self): self.urls_centreon = urls_centreon_api_v2 if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='URLs defined for Centreon %s' % (self.centreon_version)) + self.Debug(server='[' + self.get_name() + ']', debug='URLs defined for Centreon vers. : %s' % (self.centreon_version_major)) def open_monitor(self, host, service=''): @@ -727,7 +734,7 @@ def check_session(self): self.Debug(server=self.get_name(), debug="Check-session, Fetched JSON: " + pprint.pformat(data)) self.Debug(server=self.get_name(), - debug="Check-session, Error : " + error + " Status code : " + str(status_code)) + debug="Check-session, Error : " + error + ", Status code : " + str(status_code)) # If we got an 401, the token expired and must be renewed if status_code == 401: From 41f1d4696013c11922e15d1fdf435e068fde4c3b Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Mon, 4 Jul 2022 12:21:15 +0200 Subject: [PATCH 315/884] 20220704 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index e83ef552c..1e8398489 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20220626' + VERSION = '3.9-20220704' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 35f0ec542..142295776 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.9-20220626) unstable; urgency=low +nagstamon (3.9-20220704) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Sun, 26 Jun 2022 08:00:00 +0100 From 7c277f983ec5d7777e566680a09603d5dd4329c3 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 7 Jul 2022 10:05:28 +0200 Subject: [PATCH 316/884] 20220707 --- build/debian/changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/debian/changelog b/build/debian/changelog index 142295776..8e08c6fac 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.9-20220704) unstable; urgency=low +nagstamon (3.9-20220707) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Sun, 26 Jun 2022 08:00:00 +0100 From daeac85bc0b3e1a01092c4efe218c711d384c6de Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 7 Jul 2022 10:06:01 +0200 Subject: [PATCH 317/884] 20220707 --- Nagstamon/Config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 1e8398489..571455497 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20220704' + VERSION = '3.9-20220707' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' From dc2b85672851f151eddf0451290f31988392a34c Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 7 Jul 2022 10:09:39 +0200 Subject: [PATCH 318/884] 20220707 --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 31807381b..09567772f 100644 --- a/setup.py +++ b/setup.py @@ -119,6 +119,7 @@ 'Nagstamon.QUI', 'Nagstamon.Servers', 'Nagstamon.Servers.Alertmanager', + 'Nagstamon.Servers.Centreon', 'Nagstamon.thirdparty', 'Nagstamon.thirdparty.Xlib', 'Nagstamon.thirdparty.Xlib.ext', From 01ef645f2efb733cd0662221d2aafd2eacf35bd3 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 9 Jul 2022 11:28:44 +0200 Subject: [PATCH 319/884] 3.9-20220709 fixed systray icon color --- Nagstamon/Config.py | 2 +- Nagstamon/QUI/__init__.py | 21 +++++++++++---------- build/debian/changelog | 2 +- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 571455497..70ae0aae6 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20220707' + VERSION = '3.9-20220709' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 2fb14305f..634f06460 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -411,7 +411,7 @@ def icon_clicked(self, reason): if reason in (QSystemTrayIcon.ActivationReason.Trigger, QSystemTrayIcon.ActivationReason.DoubleClick, QSystemTrayIcon.ActivationReason.MiddleClick): - # when green icon is displayed and no popwin is about to po up show at least menu + # when green icon is displayed and no popwin is about to pop up show at least menu if get_worst_status() == 'UP' and OS == OS_DARWIN: self.menu.show_at_cursor() else: @@ -1107,14 +1107,6 @@ def __init__(self): # flashing statusicon self.worker_notification.start_flash.connect(systrayicon.flash) self.worker_notification.stop_flash.connect(systrayicon.reset) - # connect status window server vboxes to systray - for server_vbox in self.servers_vbox.children(): - if 'server' in server_vbox.__dict__.keys(): - # tell systray after table was refreshed - server_vbox.table.worker.new_status.connect(systrayicon.show_state) - # show error icon in systray - server_vbox.table.worker.show_error.connect(systrayicon.set_error) - server_vbox.table.worker.hide_error.connect(systrayicon.reset_error) # context menu, checking for existence necessary at startup global menu @@ -1127,6 +1119,15 @@ def __init__(self): self.create_ServerVBoxes() + # connect status window server vboxes to systray + for server_vbox in self.servers_vbox.children(): + if 'server' in server_vbox.__dict__.keys(): + # tell systray after table was refreshed + server_vbox.table.worker.new_status.connect(systrayicon.show_state) + # show error icon in systray + server_vbox.table.worker.show_error.connect(systrayicon.set_error) + server_vbox.table.worker.hide_error.connect(systrayicon.reset_error) + self.servers_scrollarea_widget.setLayout(self.servers_vbox) self.servers_scrollarea.setWidget(self.servers_scrollarea_widget) self.servers_scrollarea.setWidgetResizable(True) @@ -2563,7 +2564,7 @@ def adjust_size(self): # adjust logo size to fit to label size self.logo.adjust_size(height, height) - # avoid flickerung/artefact by updating immediately + # avoid flickering/artefact by updating immediately self.summarize_states() @Slot(str) diff --git a/build/debian/changelog b/build/debian/changelog index 8e08c6fac..20897222a 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.9-20220707) unstable; urgency=low +nagstamon (3.9-20220709) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Sun, 26 Jun 2022 08:00:00 +0100 From 8984f622ffe1a2af1adddb90bd4db1cf042f5558 Mon Sep 17 00:00:00 2001 From: HenriWahl <nutshell-debatable-pelt-dole-luminance-twisted-mating-pebble-plausibly> Date: Sun, 10 Jul 2022 09:30:53 +0200 Subject: [PATCH 320/884] attempt to fix multi-display trouble with qt6 --- Nagstamon/QUI/__init__.py | 36 +--------------------------------- Nagstamon/Servers/Generic.py | 2 +- build/requirements/windows.txt | 2 +- 3 files changed, 3 insertions(+), 37 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 634f06460..df25e6c18 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -1458,46 +1458,12 @@ def show_window_systrayicon(self): handle clicks onto systray icon """ if not self.is_shown: - - # where is the pointer which clicked onto systray icon - icon_x = systrayicon.geometry().x() - icon_y = systrayicon.geometry().y() - # strangely enough on KDE the systray icon geometry gives back 0, 0 as coordinates - if icon_x == 0: - icon_x = QCursor.pos().x() - if icon_y == 0: - icon_y = QCursor.pos().y() - - screen = APP.screenAt(QPoint(icon_x, icon_y)) - - # get available desktop specs - if OS != OS_NON_LINUX: - available_width = screen.availableVirtualGeometry().width() - available_height = screen.availableVirtualGeometry().height() - available_x = screen.availableVirtualGeometry().x() - available_y = screen.availableVirtualGeometry().y() - else: - available_width = screen.availableGeometry().width() - available_height = screen.availableGeometry().height() - available_x = screen.availableGeometry().x() - available_y = screen.availableGeometry().y() - y = 0 - - if icon_x > (available_width + available_x) // 2: - x = available_x + available_width - self.statusbar.width() - else: - x = available_x + self.statusbar.width() - - if icon_y > (available_height - available_y) // 2: - y = available_height - available_y - - self.move(x, y) - # under unfortunate circumstances statusbar might have the the moving flag true # fix it here because it makes no sense but might cause non-appearing statuswindow self.moving = False # already show here because was closed before in hide_window() + # best results achieved when doing .show() before .show_window() self.show() self.show_window() else: diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index f9050015c..cbb23e05b 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -53,7 +53,7 @@ OS_WINDOWS, RESOURCES) -if OS != OS_WINDOWS: +if OS == OS_DARWIN: # requests_gssapi is newer but not available everywhere try: # extra imports needed to get it compiled on macOS diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index 1ffd4240a..0372bdb92 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -10,7 +10,7 @@ pyqt6 pysocks python-dateutil requests -requests-kerberos requests-ecp +requests-kerberos setuptools wheel From 648d02bb1c452fd082503d6ac253f60ca3383365 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 10 Jul 2022 09:36:32 +0200 Subject: [PATCH 321/884] 3.9-20220710 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 70ae0aae6..c29a9bd73 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20220709' + VERSION = '3.9-20220710' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 20897222a..8276e3ce1 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.9-20220709) unstable; urgency=low +nagstamon (3.9-20220710) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Sun, 26 Jun 2022 08:00:00 +0100 From f31cfa1d9273630c42bc88173e1a88d2f8fe0407 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 10 Jul 2022 14:16:02 +0200 Subject: [PATCH 322/884] smallish typos --- Nagstamon/QUI/__init__.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index df25e6c18..56a3293a7 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -68,7 +68,7 @@ # only on X11/Linux thirdparty path should be added because it contains the Xlib module # needed to tell window manager via EWMH to keep Nagstamon window on all virtual desktops # TODO: test if X11 or Wayland is used -if not OS in OS_NON_LINUX: +if OS not in OS_NON_LINUX: # extract thirdparty path from resources path - make submodules accessible by thirdparty modules THIRDPARTY = os.sep.join(RESOURCES.split(os.sep)[0:-1] + ['thirdparty']) sys.path.insert(0, THIRDPARTY) @@ -1700,8 +1700,6 @@ def calculate_size(self): elif icon_y != 0: self.icon_y = icon_y - screen_or_widget = get_screen_name(self.icon_x, self.icon_y) - # only consider offset if it is configured if conf.systray_offset_use and conf.icon_in_systray: available_height = self.screen().availableGeometry().height() - conf.systray_offset @@ -2054,15 +2052,15 @@ def raise_window_on_all_desktops(self): if conf.windowed: return # X11/Linux needs some special treatment to get the statusbar floating on all virtual desktops - if not OS in OS_NON_LINUX: + if OS not in OS_NON_LINUX: # get all windows... winid = self.winId().__int__() self.ewmh.setWmDesktop(winid, 0xffffffff) self.ewmh.display.flush() - # apparently sometime the floating statusbsr vanishes in the background + # apparently sometime the floating statusbar vanishes in the background # lets try here to keep it on top - only if not fullscreen - if not conf.fullscreen and not conf.windowed and not platform.system == OS_WINDOWS: + if not conf.fullscreen and not conf.windowed and not OS == OS_WINDOWS: self.setWindowFlags(WINDOW_FLAGS) # again and again try to keep that statuswindow on top! From 1f14a2572427d5b4e17c1e8481febafa3e9cec68 Mon Sep 17 00:00:00 2001 From: hunsbea <aahunsberger@gmail.com> Date: Wed, 3 Aug 2022 14:49:05 +0800 Subject: [PATCH 323/884] Fix zabbix bugs regarding timeout, ack/closing, and duplicate services --- Nagstamon/Servers/Generic.py | 41 ---------------- Nagstamon/Servers/Zabbix.py | 92 +++++++++++------------------------- 2 files changed, 28 insertions(+), 105 deletions(-) diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index cbb23e05b..cfc4e50ac 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -1245,47 +1245,6 @@ def GetStatus(self, output=None): self.nagitems_filtered['services']['UNKNOWN'].append(service) self.unknown += 1 - # zabbix support - if service.status == "INFORMATION": - if conf.filter_all_unknown_services is True: - if conf.debug_mode: - self.Debug(server=self.get_name(), - debug="Filter: INFORMATION " + str(host.name) + ";" + str(service.name)) - service.visible = False - else: - self.nagitems_filtered["services"]["INFORMATION"].append(service) - self.information += 1 - - if service.status == "AVERAGE": - if conf.filter_all_unknown_services is True: - if conf.debug_mode: - self.Debug(server=self.get_name(), - debug="Filter: AVERAGE " + str(host.name) + ";" + str(service.name)) - service.visible = False - else: - self.nagitems_filtered["services"]["AVERAGE"].append(service) - self.average += 1 - - if service.status == "HIGH": - if conf.filter_all_unknown_services is True: - if conf.debug_mode: - self.Debug(server=self.get_name(), - debug="Filter: HIGH " + str(host.name) + ";" + str(service.name)) - service.visible = False - else: - self.nagitems_filtered["services"]["HIGH"].append(service) - self.high += 1 - - if service.status == "DISASTER": - if conf.filter_all_unknown_services is True: - if conf.debug_mode: - self.Debug(server=self.get_name(), - debug="Filter: DISASTER " + str(host.name) + ";" + str(service.name)) - service.visible = False - else: - self.nagitems_filtered["services"]["DISASTER"].append(service) - self.disaster += 1 - # Add service flags for status icons in treeview if service.acknowledged: service.service_flags += 'A' diff --git a/Nagstamon/Servers/Zabbix.py b/Nagstamon/Servers/Zabbix.py index c9b2a1644..ae8bc19e1 100644 --- a/Nagstamon/Servers/Zabbix.py +++ b/Nagstamon/Servers/Zabbix.py @@ -74,6 +74,7 @@ def __init__(self, **kwds): self.username = conf.servers[self.get_name()].username self.password = conf.servers[self.get_name()].password + self.timeout = conf.servers[self.get_name()].timeout self.ignore_cert = conf.servers[self.get_name()].ignore_cert self.use_description_name_service = conf.servers[self.get_name()].use_description_name_service if self.ignore_cert is True: @@ -86,7 +87,7 @@ def _login(self): # create ZabbixAPI if not yet created if self.zapi is None: self.zapi = ZabbixAPI(server=self.monitor_url, path="", log_level=self.log_level, - validate_certs=self.validate_certs) + validate_certs=self.validate_certs, timeout=self.timeout) # login if not yet logged in, or if login was refused previously if not self.zapi.logged_in(): self.zapi.login(self.username, self.password) @@ -150,7 +151,7 @@ def _get_status(self): 'skipDependent': True, 'monitored': True, 'active': True, - 'output': ['triggerid', 'description', 'lastchange'], + 'output': ['triggerid', 'description', 'lastchange', 'manual_close'], # 'expandDescription': True, # 'expandComment': True, 'selectLastEvent': ['eventid', 'name', 'ns', 'clock', 'acknowledged', @@ -351,8 +352,9 @@ def _get_status(self): for item in service['items']: status_information = item['name'] + ": " + item['lastvalue'] + ", " + status_information n = { - 'host': '', - 'hostname': '', + 'host': service['hosts'][0]['host'], + 'hostid': service['hosts'][0]['hostid'], + 'hostname': service['hosts'][0]['name'], 'service': service['lastEvent']['name'], 'server': self.name, 'status': status, @@ -372,12 +374,9 @@ def _get_status(self): # Zabbix data 'triggerid': service['triggerid'], 'eventid': service['lastEvent']['eventid'], + 'allow_manual_close': 'manual_close' not in service or str(service['manual_close']) == '1', } - n['hostid'] = service['hosts'][0]['hostid'] - n['host'] = service['hosts'][0]['host'] - n['hostname'] = service['hosts'][0]['name'] - key = n["hostname"] if len(n['hostname']) != 0 else n["host"] # key = n["hostid"]; @@ -419,6 +418,7 @@ def _get_status(self): self.new_hosts[key].services[new_service].hostid = n["hostid"] self.new_hosts[key].services[new_service].triggerid = n["triggerid"] self.new_hosts[key].services[new_service].eventid = n["eventid"] + self.new_hosts[key].services[new_service].allow_manual_close = n["allow_manual_close"] if conf.debug_mode is True: self.Debug(server=self.get_name(), debug="Adding new service[" + new_service + "] **" + n['service'] + "**") @@ -469,40 +469,20 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi self.Debug(server=self.get_name(), debug="Set Acknowledge Host: " + host + " Service: " + service + " Sticky: " + str( sticky) + " persistent:" + str(persistent) + " All services: " + str(all_services)) - # print("Set Acknowledge Host: " + host + " Service: " + service + " Sticky: " + str( - # sticky) + " persistent:" + str(persistent) + " All services: " + str(all_services)) - # Service column is storing current trigger id - services = [] - services.append(service) - - # acknowledge all problems (column services) on a host when told to do so - for s in all_services: - services.append(s) - self._login() - eventids=[] + eventids = set() + unclosable_events = set() + all_services.append(service) get_host = self.hosts[host] # Through all Services - for service in services: + for s in all_services: # find Trigger ID for host_service in get_host.services: host_service = get_host.services[host_service] - if host_service.name == service: - eventids.append(host_service.eventid) - break - - #for e in self.zapi.event.get({'triggerids': [triggerid], - # # from zabbix 2.2 should be used "objectids" instead of "triggerids" - # 'objectids': [triggerid], - # # 'acknowledged': False, - # 'sortfield': 'clock', - # 'sortorder': 'DESC'}): - # # Get only current event status, but retrieving first row ordered by clock DESC - # # If event status is not "OK" (Still is an active problem), mark event to acknowledge/close - # if e['value'] != '0': - # events.append(e['eventid']) - # # Only take care of newest event, discard all next - # break + if host_service.name == s: + eventids.add(host_service.eventid) + if not host_service.allow_manual_close: + unclosable_events.add(host_service.eventid) # If events pending of acknowledge, execute ack if len(eventids) > 0: @@ -513,25 +493,22 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi # 8 - change severity # 16 - unacknowledge event actions = 2 - # If sticky is set then close only current event - # if triggerid == service and sticky: - # # do not send the "Close" flag if this event does not allow manual closing - # triggers = self.zapi.trigger.get({ - # 'output': ['triggerid', 'manual_close'], - # 'filter': {'triggerid': triggerid}}) - # if not triggers or 'manual_close' not in triggers[0] or str(triggers[0]['manual_close']) == '1': - # actions |= 1 - # The current Nagstamon menu items don't match up too well with the Zabbix actions, - # but perhaps "Persistent comment" is the closest thing to acknowledgement - # if persistent: - # actions |= 2 if comment: actions |= 4 - if conf.debug_mode is True: + if conf.debug_mode: self.Debug(server=self.get_name(), debug="Events to acknowledge: " + str(eventids) + " Close: " + str(actions)) - # print("Events to acknowledge: " + str(eventids) + " Close: " + str(actions)) - self.zapi.event.acknowledge({'eventids': eventids, 'message': comment, 'action': actions}) + # If some events are not closable, we need to make 2 requests, 1 for the closable and one for the not closable + if sticky and unclosable_events: + closable_actions = actions | 1 + closable_events = set(e for e in eventids if e not in unclosable_events) + self.zapi.event.acknowledge({'eventids': list(closable_events), 'message': comment, 'action': closable_actions}) + self.zapi.event.acknowledge({'eventids': list(unclosable_events), 'message': comment, 'action': actions}) + else: + if sticky: + actions |= 1 + # print("Events to acknowledge: " + str(eventids) + " Close: " + str(actions)) + self.zapi.event.acknowledge({'eventids': list(eventids), 'message': comment, 'action': actions}) def _set_downtime(self, hostname, service, author, comment, fixed, start_time, end_time, hours, minutes): # Check if there is an associated Application tag with this trigger/item @@ -540,19 +517,6 @@ def _set_downtime(self, hostname, service, author, comment, fixed, start_time, e if self.hosts[hostname].services[host_service].name == service: triggerid = self.hosts[hostname].services[host_service].triggerid break - # triggers = self.zapi.trigger.get({ - # 'selectItems': ['itemid'], - # 'output': ['triggerid'], - # 'filter': {'triggerid': service}}) - # if triggers and triggers[0]['items']: - # items = self.zapi.item.get({ - # 'itemids': [triggers[0]['items'][0]['itemid']], - # 'output': ['itemid'], - # 'selectTags': 'extend'}) - # if items and items[0]['tags']: - # for tag in items[0]['tags']: - # if tag['tag'] == 'Application': - # app = tag['value'] hostids = [self.hosts[hostname].hostid] From e23e5f06f03d4c309f90694c2f527ff6e95afa87 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 7 Aug 2022 09:16:41 +0200 Subject: [PATCH 324/884] 20220807 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index c29a9bd73..a32b2bac3 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20220710' + VERSION = '3.9-20220807' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 8276e3ce1..202760b83 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.9-20220710) unstable; urgency=low +nagstamon (3.9-20220807) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Sun, 26 Jun 2022 08:00:00 +0100 From 0848dde21d901dab95f109a0e0378d01d12f784f Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Sun, 7 Aug 2022 14:21:56 +0200 Subject: [PATCH 325/884] popwin calculation improved --- Nagstamon/QUI/__init__.py | 50 +++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 56a3293a7..8eb7beeca 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -1678,7 +1678,6 @@ def calculate_size(self): # where is the pointer which clicked onto systray icon icon_x = systrayicon.geometry().x() icon_y = systrayicon.geometry().y() - if OS in OS_NON_LINUX: if self.icon_x == 0: self.icon_x = QCursor.pos().x() @@ -1694,6 +1693,7 @@ def calculate_size(self): if icon_y == 0 and self.icon_y == 0: self.icon_y = QCursor.pos().y() + if OS in OS_NON_LINUX: if self.icon_y == 0: self.icon_y = QCursor.pos().y() @@ -1705,6 +1705,7 @@ def calculate_size(self): available_height = self.screen().availableGeometry().height() - conf.systray_offset else: available_height = self.screen().availableGeometry().height() + available_width = self.screen().availableGeometry().width() available_x = self.screen().availableGeometry().x() available_y = self.screen().availableGeometry().y() @@ -1716,8 +1717,6 @@ def calculate_size(self): if available_y == 0: available_y = available_height - # del (screen_or_widget) - # take whole screen height into account when deciding about upper/lower-ness # add available_y because it might vary on differently setup screens # calculate top-ness only if window is closed @@ -1771,7 +1770,7 @@ def calculate_size(self): # when statusbar hangs around in lowermost part of current screen extend from bottom to top else: - # when height is to large for current screen cut it + # when height is too large for current screen cut it if self.y() + self.height() - real_height < available_y: height = self.screen().geometry().height() - available_y - ( self.screen().geometry().height() - (self.y() + self.height())) @@ -1792,19 +1791,26 @@ def calculate_size(self): height = available_height - available_y # when statusbar hangs around in lowermost part of current screen extend from bottom to top else: - # when height is to large for current screen cut it - if self.y() + self.height() - real_height < available_y: - # simply take the available max height if there is no more screen real estate - # possible because systrayicon resides aside from available space, in fact cutting it + ## when height is too large for current screen cut it + # if self.y() + self.height() - real_height < available_y: + # # simply take the available max height if there is no more screen real estate + # # possible because systrayicon resides aside from available space, in fact cutting it + # height = available_height + # y = available_height - height + # else: + # if available_height < real_height: + # y = available_y + # height = available_height + # else: + # y = available_height - real_height + # height = real_height + + if available_height < real_height: + y = available_y height = available_height - y = available_height - height else: - if available_height < real_height: - y = available_y - height = available_height - else: - y = available_height - real_height - height = real_height + y = available_height - real_height + height = real_height return width, height, x, y @@ -1915,11 +1921,21 @@ def leaveEvent(self, event): check if popup has to be hidden depending on mouse position """ + print('leave' * 10) + # depending on display mode the leave time offset shall be different because + # it may be too short in systray mode and lead to flickering window + if conf.statusbar_floating: + leave_time_offset = 0.25 + elif conf.icon_in_systray: + leave_time_offset = 1 + else: + leave_time_offset = 0 + # check first if popup has to be shown by hovering or clicking if conf.close_details_hover and \ not conf.fullscreen and \ not conf.windowed and \ - self.is_shown_timestamp + 0.25 < time.time(): + self.is_shown_timestamp + leave_time_offset < time.time(): # only hide window if cursor is outside of it mouse_x = QCursor.pos().x() mouse_y = QCursor.pos().y() @@ -1928,8 +1944,6 @@ def leaveEvent(self, event): mouse_y <= self.y() or mouse_y >= self.y() + self.height(): self.hide_window() - del (mouse_x, mouse_y) - def closeEvent(self, event): """ window close From d03400b4a04ba8af9f3bf271edae02c48ffd35d5 Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Sun, 7 Aug 2022 14:27:31 +0200 Subject: [PATCH 326/884] popwin calculation improved minus debug --- Nagstamon/QUI/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 8eb7beeca..a802c2c54 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -1920,8 +1920,6 @@ def leaveEvent(self, event): """ check if popup has to be hidden depending on mouse position """ - - print('leave' * 10) # depending on display mode the leave time offset shall be different because # it may be too short in systray mode and lead to flickering window if conf.statusbar_floating: From 291c1dd20b0e74549f0b7b8fa4fa4eb0b0daa8a7 Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Sun, 7 Aug 2022 14:44:45 +0200 Subject: [PATCH 327/884] improved leave_timeout_offset --- Nagstamon/QUI/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index a802c2c54..e7bcfc92e 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -1925,7 +1925,8 @@ def leaveEvent(self, event): if conf.statusbar_floating: leave_time_offset = 0.25 elif conf.icon_in_systray: - leave_time_offset = 1 + # offset is max 1 and smaller if window is smaller too + leave_time_offset = self.height() / self.screen().availableGeometry().height() else: leave_time_offset = 0 From 1b52b2a8815524656c5b4dc2f14d4eee8f5fc679 Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Mon, 8 Aug 2022 22:44:12 +0200 Subject: [PATCH 328/884] avoid artefact with Qt6 when systray is OK --- Nagstamon/QUI/__init__.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index e7bcfc92e..ee81c238b 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -411,9 +411,11 @@ def icon_clicked(self, reason): if reason in (QSystemTrayIcon.ActivationReason.Trigger, QSystemTrayIcon.ActivationReason.DoubleClick, QSystemTrayIcon.ActivationReason.MiddleClick): - # when green icon is displayed and no popwin is about to pop up show at least menu - if get_worst_status() == 'UP' and OS == OS_DARWIN: - self.menu.show_at_cursor() + # when green icon is displayed and no popwin is about to pop up... + if get_worst_status() == 'UP': + # ...nothing to do except on macOS where menu should be shown + if OS == OS_DARWIN: + self.menu.show_at_cursor() else: # show status window if there is something to tell if statuswindow.is_shown: From d35e3c65fba2aa1e3245f9e490c8577a36e7808b Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Mon, 8 Aug 2022 22:44:40 +0200 Subject: [PATCH 329/884] 3.9-20220808 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index a32b2bac3..0bf02d231 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20220807' + VERSION = '3.9-20220808' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 202760b83..93d34e03e 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.9-20220807) unstable; urgency=low +nagstamon (3.9-20220808) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Sun, 26 Jun 2022 08:00:00 +0100 From ba741049c6c8fdec888f05c183075dc940be7407 Mon Sep 17 00:00:00 2001 From: hunsbea <aahunsberger@gmail.com> Date: Thu, 11 Aug 2022 16:43:14 +0800 Subject: [PATCH 330/884] Zabbix allow filtering downtimed services without filtering the whole host --- Nagstamon/Servers/Zabbix.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Servers/Zabbix.py b/Nagstamon/Servers/Zabbix.py index ae8bc19e1..479c18db1 100644 --- a/Nagstamon/Servers/Zabbix.py +++ b/Nagstamon/Servers/Zabbix.py @@ -132,6 +132,8 @@ def _get_status(self): # Service # ========================================= services = [] + services_in_maintenance = set() + try: api_version = int(''.join(self.zapi.api_version().split('.')[:-1])) # Make API Version smaller except ZabbixAPIException: @@ -143,6 +145,23 @@ def _get_status(self): print(sys.exc_info()) return Result(result=result, error=error) + try: + now_ts = int(datetime.datetime.utcnow().timestamp()) + # only the maintenance object knows about services "in downtime" + maintenances = self.zapi.maintenance.get({ + "output": ["active_since", "active_till", "tags"], + "selectTags": "extend" + }) + for m in maintenances: + if int(m["active_since"]) > now_ts or int(m["active_till"]) < now_ts: + continue + for tag in m["tags"]: + if tag["tag"] == "triggerid": + services_in_maintenance.add(tag["value"]) + # Don't really care if this fails, just means we won't exclude any downtimed services + except Exception: + print(sys.exc_info()) + try: try: # Get a list of all issues (AKA tripped triggers) @@ -367,10 +386,10 @@ def _get_status(self): 'command': 'zabbix', # status flags 'passiveonly': False, - 'notifications_disabled': False, + 'notifications_disabled': service['triggerid'] in services_in_maintenance, 'flapping': False, 'acknowledged': bool(int(service['lastEvent']['acknowledged'])), - 'scheduled_downtime': False, + 'scheduled_downtime': service['triggerid'] in services_in_maintenance, # Zabbix data 'triggerid': service['triggerid'], 'eventid': service['lastEvent']['eventid'], From c0e2ad5bea84b5967451d6a79314cba305f8fab3 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 14 Aug 2022 18:34:46 +0200 Subject: [PATCH 331/884] fix ssv verify for CentreonAPI.py --- Nagstamon/Config.py | 2 +- Nagstamon/QUI/__init__.py | 16 ++++--------- Nagstamon/Servers/Centreon/CentreonAPI.py | 28 ++++------------------- Nagstamon/Servers/Centreon/__init__.py | 2 +- Nagstamon/Servers/Generic.py | 4 ---- build/debian/changelog | 4 ++-- 6 files changed, 13 insertions(+), 43 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index a32b2bac3..f6eb1cf9d 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20220807' + VERSION = '3.9-20220814' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 56a3293a7..5fac27480 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -2091,6 +2091,8 @@ def finish_worker_thread(self): """ attempt to shutdown thread cleanly """ + # stop debugging + self.worker.debug_loop_looping = False # tell thread to quit self.worker_thread.quit() # wait until thread is really stopped @@ -2157,7 +2159,7 @@ def debug_loop(self): time.sleep(1) # unset looping - self.debug_mode_looping = False + self.debug_loop_looping = False # close file if any if self.debug_file is not None: self.close_debug_file() @@ -7008,7 +7010,7 @@ def exit(): # save configuration conf.SaveConfig() - # hide statuswindow first ro avoid lag when waiting for finished threads + # hide statuswindow first to avoid lag when waiting for finished threads statuswindow.hide() # stop statuswindow workers @@ -7020,16 +7022,6 @@ def exit(): server_vbox.table.worker.finish.emit() APP.exit() - # # delete all windows - # for dialog in dialogs.__dict__.values(): - # try: - # dialog.window().destroy() - # except: - # dialog.window.destroy() - # statuswindow.destroy() - # - # bye bye - # APP.instance().quit() def check_servers(): diff --git a/Nagstamon/Servers/Centreon/CentreonAPI.py b/Nagstamon/Servers/Centreon/CentreonAPI.py index 2d3481d0a..9c963ad0c 100644 --- a/Nagstamon/Servers/Centreon/CentreonAPI.py +++ b/Nagstamon/Servers/Centreon/CentreonAPI.py @@ -17,13 +17,11 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +import traceback import urllib.request import urllib.parse import urllib.error -import socket import sys -import re -import copy # API V2 import pprint import json @@ -103,12 +101,11 @@ def init_config(self): def init_HTTP(self): + GenericServer.init_HTTP(self) if self.session is None: - GenericServer.init_HTTP(self) self.session.headers.update({'Content-Type': 'application/json'}) self.token = self.get_token().result - def define_url(self): urls_centreon_api_v2 = { 'resources': self.monitor_cgi_url + '/api/' + self.restapi_version + '/monitoring/resources', @@ -175,7 +172,6 @@ def get_token(self): return Result(result=token) except: - import traceback traceback.print_exc(file=sys.stdout) result, error = self.Error(sys.exc_info()) return Result(result=result, error=error) @@ -207,7 +203,6 @@ def GetHost(self, host): return Result(result=fqdn) except: - import traceback traceback.print_exc(file=sys.stdout) # set checking flag back to False self.isChecking = False @@ -241,13 +236,6 @@ def get_host_and_service_id(self, host, service=''): return host_id except: - import traceback - - - - - - traceback.print_exc(file=sys.stdout) # set checking flag back to False self.isChecking = False @@ -284,7 +272,6 @@ def get_host_and_service_id(self, host, service=''): return host_id,service_id except: - import traceback traceback.print_exc(file=sys.stdout) # set checking flag back to False self.isChecking = False @@ -365,7 +352,6 @@ def _get_status(self): self.Debug(server='[' + self.get_name() + ']', debug='Host indexed : ' + new_host) except: - import traceback traceback.print_exc(file=sys.stdout) # set checking flag back to False self.isChecking = False @@ -427,7 +413,6 @@ def _get_status(self): self.new_hosts[new_host].services[new_service].criticality = alerts["severity_level"] except: - import traceback traceback.print_exc(file=sys.stdout) # set checking flag back to False self.isChecking = False @@ -524,7 +509,6 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi debug="Set Acks, status code : " + str(status_code)) except: - import traceback traceback.print_exc(file=sys.stdout) # set checking flag back to False self.isChecking = False @@ -598,7 +582,6 @@ def _set_recheck(self, host, service): debug="Reckeck on Host ("+host+") / Service ("+service+"), status code : " + str(status_code)) except: - import traceback traceback.print_exc(file=sys.stdout) # set checking flag back to False self.isChecking = False @@ -699,7 +682,6 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t except: - import traceback traceback.print_exc(file=sys.stdout) # set checking flag back to False self.isChecking = False @@ -715,7 +697,7 @@ def check_session(self): # self.init_config() try: if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Check-session, the token will be deleted if it has not been used for more than one hour. Current Token = ' + self.token ) + self.Debug(server='[' + self.get_name() + ']', debug='Check-session, the token will be deleted if it has not been used for more than one hour. Current Token = ' + str(self.token) ) cgi_data = {'limit':'0'} self.session = requests.Session() @@ -725,7 +707,8 @@ def check_session(self): # Get en empty service list, to check the status of the current token # This request must be done in a GET, so just encode the parameters and fetch result = self.FetchURL(self.urls_centreon['resources'] + '?' + urllib.parse.urlencode(cgi_data), giveback="raw") - + if result.status_code == 403: + self.get_token() data = json.loads(result.result) error = result.error status_code = result.status_code @@ -743,7 +726,6 @@ def check_session(self): self.Debug(server='[' + self.get_name() + ']', debug='Check-session, session renewed') except: - import traceback traceback.print_exc(file=sys.stdout) result, error = self.Error(sys.exc_info()) return Result(result=result, error=error) diff --git a/Nagstamon/Servers/Centreon/__init__.py b/Nagstamon/Servers/Centreon/__init__.py index 7220e6ded..0f25dfa4b 100644 --- a/Nagstamon/Servers/Centreon/__init__.py +++ b/Nagstamon/Servers/Centreon/__init__.py @@ -39,7 +39,7 @@ def __init__(self, **kwds): data = json.loads(versions_raw.result) ver_major = int(data["web"]["major"]) ver_minor = int(data["web"]["minor"]) - # API V2 is usable only after 21.04 (not tested), ressources endpoit is buggy in 20.10 + # API V2 is usable only after 21.04 (not tested), ressources endpoint is buggy in 20.10 if ver_major >= 21: self.Debug(server='[' + self.get_name() + ']', debug='Loading class API, Centreon version : ' + str(ver_major) + '.' + str(ver_minor)) from .CentreonAPI import CentreonServer as CentreonServerReal diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index cfc4e50ac..211b1710f 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -1484,8 +1484,6 @@ def FetchURL(self, url, giveback='obj', cgi_data=None, no_auth=False, multipart= except Exception: if conf.debug_mode: - #traceback.print_exc(file=sys.stdout) - #self.Debug(server=self.get_name(), debug=' '.join(sys.exc_info())) self.Error(sys.exc_info()) result, error = self.Error(sys.exc_info()) if error.startswith('requests.exceptions.SSLError:'): @@ -1516,9 +1514,7 @@ def FetchURL(self, url, giveback='obj', cgi_data=None, no_auth=False, multipart= status_code=response.status_code) except Exception: - #traceback.print_exc(file=sys.stdout) self.Error(sys.exc_info()) - result, error = self.Error(sys.exc_info()) return Result(result=result, error=error, status_code=response.status_code) diff --git a/build/debian/changelog b/build/debian/changelog index 202760b83..d2df34619 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.9-20220807) unstable; urgency=low +nagstamon (3.9-20220814) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Sun, 26 Jun 2022 08:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Sun, 14 Aug 2022 08:00:00 +0100 nagstamon (3.8.0) stable; urgency=low * New upstream From b2e5d53bcf94517cc2b42abf5d03af2e57288861 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 14 Aug 2022 20:16:56 +0200 Subject: [PATCH 332/884] try to fix non-existing TLS problem --- Nagstamon/Servers/Generic.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index 211b1710f..589d52da0 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -1399,6 +1399,9 @@ def FetchURL(self, url, giveback='obj', cgi_data=None, no_auth=False, multipart= NEW: gives back a list containing result and, if necessary, a more clear error description ''' + # asume TLS is OK when connecting + self.tls_error = False + # run this method which checks itself if there is some action to take for initializing connection # if no_auth is true do not use Auth headers, used by check for new version try: From 701e4086b2e9f1a73339115cb0e0aaf9a1c780f8 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 15 Aug 2022 00:39:01 +0200 Subject: [PATCH 333/884] strip servername --- Nagstamon/Config.py | 7 +++--- Nagstamon/QUI/__init__.py | 42 ++++++++++++++++++++---------------- Nagstamon/Servers/Generic.py | 2 +- build/debian/changelog | 2 +- 4 files changed, 28 insertions(+), 25 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index f6eb1cf9d..2d65d34d8 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20220814' + VERSION = '3.9-20220815' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' @@ -519,12 +519,11 @@ def LoadMultipleConfig(self, settingsdir, setting, configobj): config.read(self.configdir + os.sep + settingsdir + os.sep + settingsfile) # create object for every setting - name = config.sections()[0].replace(setting + '_', '', 1) - + name = config.get(config.sections()[0], 'name') settings[name] = globals()[configobj]() # go through all items of the server - for i in config.items(setting + '_' + name): + for i in config.items(config.sections()[0]): # create a key of every config item with its appropriate value if i[1] in BOOLPOOL: value = BOOLPOOL[i[1]] diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 16f7a66a3..487853360 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -97,6 +97,7 @@ # check ECP authentication support availability try: from requests_ecp import HTTPECPAuth + ECP_AVAILABLE = True except ImportError: ECP_AVAILABLE = False @@ -489,7 +490,7 @@ class MenuAtCursor(QMenu): open menu at position of mouse pointer - normal .exec() shows menu at (0, 0) """ # flag to avoid too fast popping up menus - #available = True + # available = True is_shown = Signal(bool) @@ -2010,10 +2011,10 @@ def show_message(self, msg_type, message): """ title = " ".join((AppInfo.NAME, msg_type)) if msg_type == 'warning': - return QMessageBox(QMessageBox.Icon.Warning, title, message, parent=statuswindow).show() + return QMessageBox.warning(statuswindow, title, message) elif msg_type == 'information': - return QMessageBox(QMessageBox.Icon.Information, title, message, parent=statuswindow).show() + return QMessageBox.information(statuswindow,title, message) @Slot() def recheck_all(self): @@ -5167,7 +5168,7 @@ def delete_server(self): server = conf.servers[self.window.list_servers.currentItem().text()] reply = QMessageBox.question(self.window, 'Nagstamon', - 'Do you really want to delete monitor server <b>%s</b>?' % (server.name), + f'Do you really want to delete monitor server <b>{server.name}</b>?', QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, QMessageBox.StandardButton.No) @@ -5252,7 +5253,7 @@ def delete_action(self): action = conf.actions[self.window.list_actions.currentItem().text()] reply = QMessageBox.question(self.window, 'Nagstamon', - 'Do you really want to delete action <b>%s</b>?' % (action.name), + 'Do you really want to delete action <b>{action.name}</b>?', QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, QMessageBox.StandardButton.No) @@ -5894,15 +5895,18 @@ def ok(self): # global statement necessary because of reordering of servers OrderedDict global servers + # strip name to avoid whitespace + server_name = self.window.input_lineedit_name.text().strip() + # check that no duplicate name exists - if self.window.input_lineedit_name.text() in conf.servers and \ + if server_name in conf.servers and \ (self.mode in ['new', 'copy'] or - self.mode == 'edit' and self.server_conf != conf.servers[self.window.input_lineedit_name.text()]): + self.mode == 'edit' and self.server_conf != conf.servers[server_name]): # cry if duplicate name exists - QMessageBox.Icon.Critical(self.window, 'Nagstamon', - 'The monitor server name <b>%s</b> is already used.' % - (self.window.input_lineedit_name.text()), - QMessageBox.StandardButton.Ok) + QMessageBox.critical(self.window, + 'Nagstamon', + f'The monitor server name <b>{server_name}</b> is already used.', + QMessageBox.StandardButton.Ok) else: # get configuration from UI for widget in self.window.__dict__: @@ -5964,15 +5968,16 @@ def ok(self): if self.server_conf.type not in self.VOLATILE_WIDGETS[self.window.input_lineedit_monitor_cgi_url]: self.server_conf.monitor_cgi_url = self.server_conf.monitor_url - # add new server configuration in every case - conf.servers[self.server_conf.name] = self.server_conf + # add new server configuration in every case and use stripped name to avoid spaces + self.server_conf.name = server_name + conf.servers[server_name] = self.server_conf # add new server instance to global servers dict - servers[self.server_conf.name] = create_server(self.server_conf) + servers[server_name] = create_server(self.server_conf) if self.server_conf.enabled is True: - servers[self.server_conf.name].enabled = True + servers[server_name].enabled = True # create vbox - statuswindow.servers_vbox.addLayout(statuswindow.create_ServerVBox(servers[self.server_conf.name])) + statuswindow.servers_vbox.addLayout(statuswindow.create_ServerVBox(servers[server_name])) # renew list of server vboxes in status window statuswindow.sort_ServerVBoxes() @@ -6160,9 +6165,8 @@ def ok(self): (self.mode in ['new', 'copy'] or self.mode == 'edit' and self.action_conf != conf.actions[self.window.input_lineedit_name.text()]): # cry if duplicate name exists - QMessageBox.Icon.Critical(self.window, 'Nagstamon', - 'The action name <b>%s</b> is already used.' % - (self.window.input_lineedit_name.text()), + QMessageBox.critical(self.window, 'Nagstamon', + f'The action name <b>{self.window.input_lineedit_name.text()}</b> is already used.', QMessageBox.StandardButton.Ok) else: # get configuration from UI diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index 589d52da0..946cf7b65 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -1399,7 +1399,7 @@ def FetchURL(self, url, giveback='obj', cgi_data=None, no_auth=False, multipart= NEW: gives back a list containing result and, if necessary, a more clear error description ''' - # asume TLS is OK when connecting + # assume TLS is OK when connecting self.tls_error = False # run this method which checks itself if there is some action to take for initializing connection diff --git a/build/debian/changelog b/build/debian/changelog index d2df34619..e6f97518d 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.9-20220814) unstable; urgency=low +nagstamon (3.9-20220815) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Sun, 14 Aug 2022 08:00:00 +0100 From db0b30204d92ac2d177ce67534fe2f6482f58373 Mon Sep 17 00:00:00 2001 From: hunsbea <aahunsberger@gmail.com> Date: Wed, 17 Aug 2022 17:57:06 +0800 Subject: [PATCH 334/884] github.com/HenriWahl/Nagstamon/issues/826 --- Nagstamon/Servers/Zabbix.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Servers/Zabbix.py b/Nagstamon/Servers/Zabbix.py index 479c18db1..aa4c861ed 100644 --- a/Nagstamon/Servers/Zabbix.py +++ b/Nagstamon/Servers/Zabbix.py @@ -182,6 +182,19 @@ def _get_status(self): 'filter': {'value': 1}, }) + # https://github.com/HenriWahl/Nagstamon/issues/826 Zabbix 5.0 may have an empty list for + # the 'lastEvent' key if the trigger has no associated events + for service in services: + if service['lastEvent'] == []: + service['lastEvent'] = { + 'eventid': -1, + 'acknowledged': '0', + 'name': service['description'], + 'severity': 'UNKN' + } + elif isinstance(service['lastEvent'], list): + service['lastEvent'] = service['lastEvent'][0] + except ZabbixAPIException: # FIXME Is there a cleaner way to handle this? I just borrowed # this code from 80 lines ahead. -- AGV @@ -499,9 +512,13 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi for host_service in get_host.services: host_service = get_host.services[host_service] if host_service.name == s: - eventids.add(host_service.eventid) + eventid = host_service.eventid + # https://github.com/HenriWahl/Nagstamon/issues/826 we may have set eventid = -1 earlier if there was no associated event + if eventid == -1: + continue + eventids.add(eventid) if not host_service.allow_manual_close: - unclosable_events.add(host_service.eventid) + unclosable_events.add(eventid) # If events pending of acknowledge, execute ack if len(eventids) > 0: From e504fc4635aadd9b5d2dfe4260b6b4f1625f0d47 Mon Sep 17 00:00:00 2001 From: Alyx LG <107560367+Alyx-LeGuen@users.noreply.github.com> Date: Wed, 17 Aug 2022 14:41:49 +0200 Subject: [PATCH 335/884] Fix centreonAPI JSONDecodeError after getting a token Replay the request after getting a new token, otherwise result.result will be empty and it will throw an error "Error : json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)" --- Nagstamon/Servers/Centreon/CentreonAPI.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Nagstamon/Servers/Centreon/CentreonAPI.py b/Nagstamon/Servers/Centreon/CentreonAPI.py index 9c963ad0c..7c67247d3 100644 --- a/Nagstamon/Servers/Centreon/CentreonAPI.py +++ b/Nagstamon/Servers/Centreon/CentreonAPI.py @@ -708,6 +708,7 @@ def check_session(self): # This request must be done in a GET, so just encode the parameters and fetch result = self.FetchURL(self.urls_centreon['resources'] + '?' + urllib.parse.urlencode(cgi_data), giveback="raw") if result.status_code == 403: + result = self.FetchURL(self.urls_centreon['resources'] + '?' + urllib.parse.urlencode(cgi_data), giveback="raw") self.get_token() data = json.loads(result.result) error = result.error From a26074533b153c0822d0e088256b86c4d07d7eed Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Wed, 17 Aug 2022 14:46:13 +0200 Subject: [PATCH 336/884] merges for Zabbix and Centreon --- Nagstamon/Config.py | 2 +- Nagstamon/resources/nagstamon.1.gz | Bin 783 -> 783 bytes build/debian/changelog | 4 ++-- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index f6eb1cf9d..c53b8e114 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20220814' + VERSION = '3.9-20220817' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/resources/nagstamon.1.gz b/Nagstamon/resources/nagstamon.1.gz index c5213f14bb224495e70854ae03c1e6b11c5040f0..437d01d93a4d19710dca54e5efe2c1b54e1577aa 100644 GIT binary patch delta 16 XcmeBY>t|z^@8;mRw&>SJb`E9$Dvbpp delta 16 XcmeBY>t|z^@8;mhdS17Yor4(wCd&kV diff --git a/build/debian/changelog b/build/debian/changelog index d2df34619..40d06c26e 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.9-20220814) unstable; urgency=low +nagstamon (3.9-20220817) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Sun, 14 Aug 2022 08:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Wed, 17 Aug 2022 08:00:00 +0100 nagstamon (3.8.0) stable; urgency=low * New upstream From e80b8c23db5237611ab9b7a2594269a4985fc728 Mon Sep 17 00:00:00 2001 From: Alyx LG <107560367+Alyx-LeGuen@users.noreply.github.com> Date: Wed, 17 Aug 2022 14:49:05 +0200 Subject: [PATCH 337/884] Fix centreonAPI JSONDecodeError after getting a token Replay the request after getting a new token, otherwise result.result will be empty and it will throw an error "Error : json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)" --- Nagstamon/Servers/Centreon/CentreonAPI.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/Servers/Centreon/CentreonAPI.py b/Nagstamon/Servers/Centreon/CentreonAPI.py index 7c67247d3..51e655f15 100644 --- a/Nagstamon/Servers/Centreon/CentreonAPI.py +++ b/Nagstamon/Servers/Centreon/CentreonAPI.py @@ -708,8 +708,8 @@ def check_session(self): # This request must be done in a GET, so just encode the parameters and fetch result = self.FetchURL(self.urls_centreon['resources'] + '?' + urllib.parse.urlencode(cgi_data), giveback="raw") if result.status_code == 403: - result = self.FetchURL(self.urls_centreon['resources'] + '?' + urllib.parse.urlencode(cgi_data), giveback="raw") self.get_token() + result = self.FetchURL(self.urls_centreon['resources'] + '?' + urllib.parse.urlencode(cgi_data), giveback="raw") data = json.loads(result.result) error = result.error status_code = result.status_code From 2805b326f8d8535e65f372bd374a3e3685dcb423 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 18 Aug 2022 01:18:06 +0200 Subject: [PATCH 338/884] fiddle with Centreons version detection --- Nagstamon/Config.py | 2 +- Nagstamon/Servers/Centreon/__init__.py | 3 +++ Nagstamon/Servers/Generic.py | 17 ++++++++++++----- build/debian/changelog | 2 +- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index c53b8e114..06783d4bd 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20220817' + VERSION = '3.9-20220818' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/Servers/Centreon/__init__.py b/Nagstamon/Servers/Centreon/__init__.py index 0f25dfa4b..a88697534 100644 --- a/Nagstamon/Servers/Centreon/__init__.py +++ b/Nagstamon/Servers/Centreon/__init__.py @@ -32,6 +32,9 @@ def __init__(self, **kwds): # due to not being initialized right now we need to access config directly to get this instance's config server_conf = conf.servers.get(kwds.get('name')) if server_conf and server_conf.enabled: + # because auf being very early in init process the property ignore_cert is not known yet + # add it here to be able to fetch URL and ignore certs if activated + self.ignore_cert = server_conf.ignore_cert # This URL exists on Centreon 22.x - if not accessible it must be legacy versions_raw = self.FetchURL(f'{server_conf.monitor_cgi_url}/api/latest/platform/versions', no_auth=True, giveback='raw') self.Debug(server='[' + self.get_name() + ']', debug='Status code %s' % (str(versions_raw.status_code))) diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index 589d52da0..50be6cdf0 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -187,7 +187,7 @@ def __init__(self, **kwds): # flag which tells GUI if there is an TLS problem self.tls_error = False - # counter for login attempts - have to be threaten differently by every monitoring server type + # counter for login attempts - have to be treatened differently by every monitoring server type self.login_count = 0 # to handle Icinga versions this information is necessary, might be of future use for others too @@ -1447,9 +1447,9 @@ def FetchURL(self, url, giveback='obj', cgi_data=None, no_auth=False, multipart= # most requests come without multipart/form-data if multipart is False: if cgi_data is None: - response = self.session.get(url, timeout=self.timeout) + response = self.session.get(url, timeout=self.timeout, verify=not self.ignore_cert) else: - response = self.session.post(url, data=cgi_data, timeout=self.timeout) + response = self.session.post(url, data=cgi_data, timeout=self.timeout, verify=not self.ignore_cert) else: # Checkmk and Opsview need multipart/form-data encoding # http://stackoverflow.com/questions/23120974/python-requests-post-multipart-form-data-without-filename-in-http-request#23131823 @@ -1463,6 +1463,13 @@ def FetchURL(self, url, giveback='obj', cgi_data=None, no_auth=False, multipart= # send request without authentication data temporary_session = requests.Session() temporary_session.headers['User-Agent'] = self.USER_AGENT + # default to check TLS validity + if self.ignore_cert: + temporary_session.verify = False + elif self.custom_cert_use: + temporary_session.verify = self.custom_cert_ca_file + else: + temporary_session.verify = True # add proxy information if necessary self.proxify(temporary_session) @@ -1470,9 +1477,9 @@ def FetchURL(self, url, giveback='obj', cgi_data=None, no_auth=False, multipart= # most requests come without multipart/form-data if multipart is False: if cgi_data is None: - response = temporary_session.get(url, timeout=self.timeout) + response = temporary_session.get(url, timeout=self.timeout, verify=not self.ignore_cert) else: - response = temporary_session.post(url, data=cgi_data, timeout=self.timeout) + response = temporary_session.post(url, data=cgi_data, timeout=self.timeout, verify=not self.ignore_cert) else: # Checkmk and Opsview need multipart/form-data encoding # http://stackoverflow.com/questions/23120974/python-requests-post-multipart-form-data-without-filename-in-http-request#23131823 diff --git a/build/debian/changelog b/build/debian/changelog index 40d06c26e..02b16fcc2 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.9-20220817) unstable; urgency=low +nagstamon (3.9-20220818) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Wed, 17 Aug 2022 08:00:00 +0100 From 060432a12db4d666c558f88de7d8149410e75895 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 20 Aug 2022 08:19:06 +0200 Subject: [PATCH 339/884] catch unidentified Centreon version --- Nagstamon/Servers/Centreon/CentreonLegacy.py | 43 +++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/Nagstamon/Servers/Centreon/CentreonLegacy.py b/Nagstamon/Servers/Centreon/CentreonLegacy.py index 522446706..651f41e1b 100644 --- a/Nagstamon/Servers/Centreon/CentreonLegacy.py +++ b/Nagstamon/Servers/Centreon/CentreonLegacy.py @@ -1082,29 +1082,32 @@ def _check_session(self): if 'url_centreon' not in self.__dict__: self.init_config() try: - if self.centreon_version >= 18.10: - result = self.FetchURL(self.urls_centreon['keepAlive'], giveback='raw') - self.raw, self.error, self.status_code = result.result, result.error, result.status_code - # Return 200 & null a session is open - if conf.debug_mode == True: - self.Debug(server=self.get_name(), debug='Session status : ' + self.raw + ', http code : ' + str(self.status_code)) - # 401 if no valid session is present - if self.status_code == 401: - self.SID = self._get_sid().result + if self.centreon_version: + if self.centreon_version >= 18.10: + result = self.FetchURL(self.urls_centreon['keepAlive'], giveback='raw') + self.raw, self.error, self.status_code = result.result, result.error, result.status_code + # Return 200 & null a session is open if conf.debug_mode == True: - self.Debug(server=self.get_name(), debug='Session renewed') + self.Debug(server=self.get_name(), debug='Session status : ' + self.raw + ', http code : ' + str(self.status_code)) + # 401 if no valid session is present + if self.status_code == 401: + self.SID = self._get_sid().result + if conf.debug_mode == True: + self.Debug(server=self.get_name(), debug='Session renewed') - else: - result = self.FetchURL(self.urls_centreon['autologoutXMLresponse'], giveback='xml') - xmlobj, error, status_code = result.result, result.error, result.status_code - self.session_state = xmlobj.find("state").text.lower() - if conf.debug_mode == True: - self.Debug(server=self.get_name(), debug='Session status : ' + self.session_state) - if self.session_state == "nok": - self.SID = self._get_sid().result + else: + result = self.FetchURL(self.urls_centreon['autologoutXMLresponse'], giveback='xml') + xmlobj, error, status_code = result.result, result.error, result.status_code + self.session_state = xmlobj.find("state").text.lower() if conf.debug_mode == True: - self.Debug(server=self.get_name(), debug='Session renewed') - + self.Debug(server=self.get_name(), debug='Session status : ' + self.session_state) + if self.session_state == "nok": + self.SID = self._get_sid().result + if conf.debug_mode == True: + self.Debug(server=self.get_name(), debug='Session renewed') + else: + return Result(result='ERROR', + error='Cannot detect Centreon version') except: import traceback traceback.print_exc(file=sys.stdout) From c093f03f82132166dcbf9bf47f8877d4e565d109 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 20 Aug 2022 09:09:32 +0200 Subject: [PATCH 340/884] catch connection errors in Centreon 22 --- Nagstamon/Servers/Centreon/CentreonAPI.py | 242 ++++++++++--------- Nagstamon/Servers/Centreon/CentreonLegacy.py | 20 +- 2 files changed, 139 insertions(+), 123 deletions(-) diff --git a/Nagstamon/Servers/Centreon/CentreonAPI.py b/Nagstamon/Servers/Centreon/CentreonAPI.py index 51e655f15..bc55a11af 100644 --- a/Nagstamon/Servers/Centreon/CentreonAPI.py +++ b/Nagstamon/Servers/Centreon/CentreonAPI.py @@ -34,6 +34,7 @@ from Nagstamon.Config import conf from Nagstamon.Helpers import webbrowser_open + # This class support Centreon V2 API # Things to do : # - BROWSER_URLS -> move into define_url() to be consistent @@ -75,7 +76,7 @@ def init_config(self): # check if any error occured errors_occured = self.check_for_error(data, error, status_code) if errors_occured is not False: - return(errors_occured) + return (errors_occured) self.centreon_version_major = int(data["web"]["major"]) @@ -85,9 +86,9 @@ def init_config(self): self.Debug(server='[' + self.get_name() + ']', debug='Centreon code version selected : >= 21') # URLs for browser shortlinks/buttons on popup window self.BROWSER_URLS = {'monitor': '$MONITOR$/monitoring/resources', - 'hosts': '$MONITOR$/monitoring/resources', - 'services': '$MONITOR$/monitoring/resources', - 'history': '$MONITOR$/main.php?p=20301'} + 'hosts': '$MONITOR$/monitoring/resources', + 'services': '$MONITOR$/monitoring/resources', + 'history': '$MONITOR$/main.php?p=20301'} # RestAPI version self.restapi_version = "latest" @@ -99,7 +100,6 @@ def init_config(self): if not self.tls_error and self.urls_centreon is None: self.define_url() - def init_HTTP(self): GenericServer.init_HTTP(self) if self.session is None: @@ -116,8 +116,8 @@ def define_url(self): self.urls_centreon = urls_centreon_api_v2 if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='URLs defined for Centreon vers. : %s' % (self.centreon_version_major)) - + self.Debug(server='[' + self.get_name() + ']', + debug='URLs defined for Centreon vers. : %s' % (self.centreon_version_major)) def open_monitor(self, host, service=''): # Autologin seems deprecated as admin must enable it globaly and use the old pages @@ -128,8 +128,7 @@ def open_monitor(self, host, service=''): # auth = '' # webbrowser_open(self.urls_centreon['resources'] + auth ) - webbrowser_open(self.urls_centreon['resources'] ) - + webbrowser_open(self.urls_centreon['resources']) def get_token(self): try: @@ -154,18 +153,19 @@ def get_token(self): self.Debug(server=self.get_name(), debug="Fetched JSON: " + pprint.pformat(data)) - # check if any error occured errors_occured = self.check_for_error(data, error, status_code) if errors_occured is not False: - return(errors_occured) + return (errors_occured) token = data["security"]["token"] # ID of the user is needed by some requests user_id = data["contact"]["id"] if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='API login : ' + self.username + ' / ' + self.password + ' > Token : ' + token + ' > User ID : ' + str(user_id)) + self.Debug(server='[' + self.get_name() + ']', + debug='API login : ' + self.username + ' / ' + self.password + ' > Token : ' + token + ' > User ID : ' + str( + user_id)) self.user_id = user_id self.session.headers.update({'X-Auth-Token': token}) @@ -176,7 +176,6 @@ def get_token(self): result, error = self.Error(sys.exc_info()) return Result(result=result, error=error) - def GetHost(self, host): # https://demo.centreon.com/centreon/api/latest/monitoring/resources?page=1&limit=30&sort_by={"status_severity_code":"asc","last_status_change":"desc"}&types=["host"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"] url_hosts = self.urls_centreon['hosts'] + '?types=["host"]&search={"h.name":"' + host + '"}' @@ -192,12 +191,13 @@ def GetHost(self, host): # check if any error occured errors_occured = self.check_for_error(data, error, status_code) if errors_occured is not False: - return(errors_occured) + return (errors_occured) fqdn = str(data["result"][0]["fqdn"]) if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Get Host FQDN or address : ' + host + " / " + fqdn) + self.Debug(server='[' + self.get_name() + ']', + debug='Get Host FQDN or address : ' + host + " / " + fqdn) # Give back host or ip return Result(result=fqdn) @@ -209,7 +209,6 @@ def GetHost(self, host): result, error = self.Error(sys.exc_info()) return Result(result=result, error=error) - def get_host_and_service_id(self, host, service=''): if service == "": # Hosts only @@ -227,7 +226,7 @@ def get_host_and_service_id(self, host, service=''): # check if any error occured errors_occured = self.check_for_error(data, error, status_code) if errors_occured is not False: - return(errors_occured) + return (errors_occured) host_id = data["result"][0]["id"] @@ -244,9 +243,11 @@ def get_host_and_service_id(self, host, service=''): else: # Host + Service if host == "Meta_Services": - url_service = self.urls_centreon['services'] + '?types=["metaservice"]&search={"s.name":"'+service+'"}' + url_service = self.urls_centreon[ + 'services'] + '?types=["metaservice"]&search={"s.name":"' + service + '"}' else: - url_service = self.urls_centreon['services'] + '?types=["service"]&search={"$and":[{"h.name":{"$eq":"'+host+'"}}, {"s.description":{"$eq":"'+service+'"}}]}' + url_service = self.urls_centreon[ + 'services'] + '?types=["service"]&search={"$and":[{"h.name":{"$eq":"' + host + '"}}, {"s.description":{"$eq":"' + service + '"}}]}' try: # Get json @@ -259,7 +260,7 @@ def get_host_and_service_id(self, host, service=''): # check if any error occured errors_occured = self.check_for_error(data, error, status_code) if errors_occured is not False: - return(errors_occured) + return (errors_occured) if host == "Meta_Services": host_id = 0 @@ -268,8 +269,9 @@ def get_host_and_service_id(self, host, service=''): service_id = data["result"][0]["id"] if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Get Host / Service ID : ' + str(host_id) + " / " + str(service_id)) - return host_id,service_id + self.Debug(server='[' + self.get_name() + ']', + debug='Get Host / Service ID : ' + str(host_id) + " / " + str(service_id)) + return host_id, service_id except: traceback.print_exc(file=sys.stdout) @@ -278,7 +280,6 @@ def get_host_and_service_id(self, host, service=''): result, error = self.Error(sys.exc_info()) return Result(result=result, error=error) - def get_start_end(self, host): # I don’t know how to get this info... # self.defaults_downtime_duration_hours = 2 @@ -289,7 +290,6 @@ def get_start_end(self, host): return (str(start.strftime('%Y-%m-%d %H:%M')), str(end.strftime('%Y-%m-%d %H:%M'))) - def _get_status(self): ''' Get status from Centreon Server @@ -304,11 +304,15 @@ def _get_status(self): # Services URL # https://demo.centreon.com/centreon/api/latest/monitoring/resources?page=1&limit=30&sort_by={"status_severity_code":"asc","last_status_change":"desc"}&types=["service"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"] - url_services = self.urls_centreon['services'] + '?types=["metaservice","service"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"]&limit=' + str(self.limit_services_number) + url_services = self.urls_centreon[ + 'services'] + '?types=["metaservice","service"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"]&limit=' + str( + self.limit_services_number) # Hosts URL # https://demo.centreon.com/centreon/api/latest/monitoring/resources?page=1&limit=30&sort_by={"status_severity_code":"asc","last_status_change":"desc"}&types=["host"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"] - url_hosts = self.urls_centreon['hosts'] + '?types=["host"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"]&limit=' + str(self.limit_services_number) + url_hosts = self.urls_centreon[ + 'hosts'] + '?types=["host"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"]&limit=' + str( + self.limit_services_number) # Hosts try: @@ -326,7 +330,7 @@ def _get_status(self): # check if any error occured errors_occured = self.check_for_error(data, error, status_code) if errors_occured is not False: - return(errors_occured) + return (errors_occured) for alerts in data["result"]: new_host = alerts["name"] @@ -374,7 +378,7 @@ def _get_status(self): # check if any error occured errors_occured = self.check_for_error(data, error, status_code) if errors_occured is not False: - return(errors_occured) + return (errors_occured) for alerts in data["result"]: if alerts["type"] == "metaservice": @@ -389,7 +393,8 @@ def _get_status(self): self.new_hosts[new_host].status = 'UP' self.new_hosts[new_host].services[new_service] = GenericService() # Attributs à remplir - self.Debug(server='[' + self.get_name() + ']', debug='Service indexed : ' + new_host + ' / ' + new_service) + self.Debug(server='[' + self.get_name() + ']', + debug='Service indexed : ' + new_host + ' / ' + new_service) self.new_hosts[new_host].services[new_service].server = self.name self.new_hosts[new_host].services[new_service].host = new_host @@ -402,7 +407,8 @@ def _get_status(self): self.new_hosts[new_host].services[new_service].attempt = alerts["tries"] self.new_hosts[new_host].services[new_service].status_information = alerts["information"] self.new_hosts[new_host].services[new_service].passiveonly = alerts["passive_checks"] - self.new_hosts[new_host].services[new_service].notifications_disabled = not alerts["notification_enabled"] + self.new_hosts[new_host].services[new_service].notifications_disabled = not alerts[ + "notification_enabled"] self.new_hosts[new_host].services[new_service].flapping = alerts["flapping"] self.new_hosts[new_host].services[new_service].acknowledged = alerts["acknowledged"] self.new_hosts[new_host].services[new_service].scheduled_downtime = alerts["in_downtime"] @@ -422,20 +428,19 @@ def _get_status(self): # return True if all worked well return Result() - def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=[]): try: acknowledgements = { - "acknowledgement": { - "comment": comment, - "with_services": True, - "is_notify_contacts": notify, - "is_persistent_comment": persistent, - "is_sticky": sticky - }, - "resources": [ - ] + "acknowledgement": { + "comment": comment, + "with_services": True, + "is_notify_contacts": notify, + "is_persistent_comment": persistent, + "is_sticky": sticky + }, + "resources": [ + ] } # host @@ -443,11 +448,11 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi host_id = self.get_host_and_service_id(host) new_resource = { - "type": "host", - "id": host_id, - "parent": { - "id": None - } + "type": "host", + "id": host_id, + "parent": { + "id": None + } } acknowledgements["resources"].append(new_resource) @@ -455,7 +460,8 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi # Post json json_string = json.dumps(acknowledgements) # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/hosts/{host_id}/acknowledgements - result = self.FetchURL(self.urls_centreon['resources'] + '/acknowledge', cgi_data=json_string, giveback='raw') + result = self.FetchURL(self.urls_centreon['resources'] + '/acknowledge', cgi_data=json_string, + giveback='raw') error = result.error status_code = result.status_code @@ -464,7 +470,6 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi self.Debug(server='[' + self.get_name() + ']', debug="Set Ack on Host, status code : " + str(status_code)) - # Service if service != '' or len(all_services) > 0: if len(all_services) == 0: @@ -475,31 +480,32 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi if host == "Meta_Services": new_resource = { - "type": "metaservice", - "id": service_id, - "parent": { - "id": None - } + "type": "metaservice", + "id": service_id, + "parent": { + "id": None + } } else: new_resource = { - "type": "service", - "id": service_id, - "parent": { - "id": host_id - } + "type": "service", + "id": service_id, + "parent": { + "id": host_id + } } acknowledgements["resources"].append(new_resource) if conf.debug_mode: self.Debug(server='[' + self.get_name() + ']', - debug="Stack ack for Host ("+host+") / Service ("+service+")") + debug="Stack ack for Host (" + host + ") / Service (" + service + ")") # Post json json_string = json.dumps(acknowledgements) # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/services/acknowledgements - result = self.FetchURL(self.urls_centreon['resources'] + '/acknowledge', cgi_data=json_string, giveback='raw') + result = self.FetchURL(self.urls_centreon['resources'] + '/acknowledge', cgi_data=json_string, + giveback='raw') error = result.error status_code = result.status_code @@ -515,11 +521,10 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi result, error = self.Error(sys.exc_info()) return Result(result=result, error=error) - def _set_recheck(self, host, service): rechecks = { - "resources": [ - ] + "resources": [ + ] } try: @@ -528,9 +533,9 @@ def _set_recheck(self, host, service): host_id = self.get_host_and_service_id(host) new_resource = { - "type": "host", - "id": host_id, - "parent": None + "type": "host", + "id": host_id, + "parent": None } rechecks["resources"].append(new_resource) @@ -545,26 +550,26 @@ def _set_recheck(self, host, service): if conf.debug_mode: self.Debug(server='[' + self.get_name() + ']', - debug="Recheck on Host : "+host+", status code : " + str(status_code)) + debug="Recheck on Host : " + host + ", status code : " + str(status_code)) - # Service + # Service else: host_id, service_id = self.get_host_and_service_id(host, service) if host == "Meta_Services": new_resource = { - "type": "metaservice", - "id": service_id, - "parent": None + "type": "metaservice", + "id": service_id, + "parent": None } else: new_resource = { - "type": "service", - "id": service_id, - "parent": { - "id": host_id - } + "type": "service", + "id": service_id, + "parent": { + "id": host_id + } } rechecks["resources"].append(new_resource) @@ -579,7 +584,8 @@ def _set_recheck(self, host, service): if conf.debug_mode: self.Debug(server='[' + self.get_name() + ']', - debug="Reckeck on Host ("+host+") / Service ("+service+"), status code : " + str(status_code)) + debug="Reckeck on Host (" + host + ") / Service (" + service + "), status code : " + str( + status_code)) except: traceback.print_exc(file=sys.stdout) @@ -588,7 +594,6 @@ def _set_recheck(self, host, service): result, error = self.Error(sys.exc_info()) return Result(result=result, error=error) - def _set_downtime(self, host, service, author, comment, fixed, start_time, end_time, hours, minutes): obj_start_time = datetime.strptime(start_time, '%Y-%m-%d %H:%M') obj_end_time = datetime.strptime(end_time, '%Y-%m-%d %H:%M') @@ -600,23 +605,23 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t # duration unit is second duration = (hours * 3600) + (minutes * 60) - # API require boolean + # API require boolean if fixed == 1: fixed = True else: fixed = False downtimes = { - "downtime": { - "comment": comment, - "with_services": True, - "is_fixed": fixed, - "duration": duration, - "start_time": obj_start_time.strftime('%Y-%m-%dT%H:%M:%S%z'), - "end_time": obj_end_time.strftime('%Y-%m-%dT%H:%M:%S%z') - }, - "resources": [ - ] + "downtime": { + "comment": comment, + "with_services": True, + "is_fixed": fixed, + "duration": duration, + "start_time": obj_start_time.strftime('%Y-%m-%dT%H:%M:%S%z'), + "end_time": obj_end_time.strftime('%Y-%m-%dT%H:%M:%S%z') + }, + "resources": [ + ] } try: @@ -625,9 +630,9 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t host_id = self.get_host_and_service_id(host) new_resource = { - "type": "host", - "id": host_id, - "parent": None + "type": "host", + "id": host_id, + "parent": None } downtimes["resources"].append(new_resource) @@ -635,14 +640,15 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t # Post json json_string = json.dumps(downtimes) # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/resources/downtime - result = self.FetchURL(self.urls_centreon['resources'] + '/downtime', cgi_data=json_string, giveback='raw') + result = self.FetchURL(self.urls_centreon['resources'] + '/downtime', cgi_data=json_string, + giveback='raw') error = result.error status_code = result.status_code if conf.debug_mode: self.Debug(server='[' + self.get_name() + ']', - debug="Downtime on Host : "+host+", status code : " + str(status_code)) + debug="Downtime on Host : " + host + ", status code : " + str(status_code)) # Service else: @@ -650,20 +656,20 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t if host == "Meta_Services": new_resource = { - "type": "metaservice", - "id": service_id, - "parent": { - "id": None - } + "type": "metaservice", + "id": service_id, + "parent": { + "id": None + } } else: new_resource = { - "type": "service", - "id": service_id, - "parent": { - "id": host_id - } + "type": "service", + "id": service_id, + "parent": { + "id": host_id + } } downtimes["resources"].append(new_resource) @@ -671,14 +677,16 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t # Post json json_string = json.dumps(downtimes) # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/resources/downtime - result = self.FetchURL(self.urls_centreon['resources'] + '/downtime', cgi_data=json_string, giveback='raw') + result = self.FetchURL(self.urls_centreon['resources'] + '/downtime', cgi_data=json_string, + giveback='raw') error = result.error status_code = result.status_code if conf.debug_mode: self.Debug(server='[' + self.get_name() + ']', - debug="Downtime on Host ("+host+") / Service ("+service+"), status code : " + str(status_code)) + debug="Downtime on Host (" + host + ") / Service (" + service + "), status code : " + str( + status_code)) except: @@ -688,7 +696,6 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t result, error = self.Error(sys.exc_info()) return Result(result=result, error=error) - def check_session(self): if conf.debug_mode == True: self.Debug(server='[' + self.get_name() + ']', debug='Checking session status') @@ -697,22 +704,31 @@ def check_session(self): # self.init_config() try: if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Check-session, the token will be deleted if it has not been used for more than one hour. Current Token = ' + str(self.token) ) + self.Debug(server='[' + self.get_name() + ']', + debug='Check-session, the token will be deleted if it has not been used for more than one hour. Current Token = ' + str( + self.token)) - cgi_data = {'limit':'0'} + cgi_data = {'limit': '0'} self.session = requests.Session() self.session.headers['Content-Type'] = 'application/json' self.session.headers['X-Auth-Token'] = self.token # Get en empty service list, to check the status of the current token # This request must be done in a GET, so just encode the parameters and fetch - result = self.FetchURL(self.urls_centreon['resources'] + '?' + urllib.parse.urlencode(cgi_data), giveback="raw") + result = self.FetchURL(self.urls_centreon['resources'] + '?' + urllib.parse.urlencode(cgi_data), + giveback="raw") if result.status_code == 403: self.get_token() - result = self.FetchURL(self.urls_centreon['resources'] + '?' + urllib.parse.urlencode(cgi_data), giveback="raw") - data = json.loads(result.result) - error = result.error - status_code = result.status_code + result = self.FetchURL(self.urls_centreon['resources'] + '?' + urllib.parse.urlencode(cgi_data), + giveback="raw") + if not 'ConnectTimeoutError' in result.error and \ + not 'NewConnectionError' in result.error: + data = json.loads(result.result) + error = result.error + status_code = result.status_code + else: + return Result(result='ERROR', + error='Connection error') if conf.debug_mode: self.Debug(server=self.get_name(), diff --git a/Nagstamon/Servers/Centreon/CentreonLegacy.py b/Nagstamon/Servers/Centreon/CentreonLegacy.py index 651f41e1b..30be37dd1 100644 --- a/Nagstamon/Servers/Centreon/CentreonLegacy.py +++ b/Nagstamon/Servers/Centreon/CentreonLegacy.py @@ -1081,8 +1081,8 @@ def _check_session(self): self.Debug(server=self.get_name(), debug='Checking session status') if 'url_centreon' not in self.__dict__: self.init_config() - try: - if self.centreon_version: + if self.centreon_version: + try: if self.centreon_version >= 18.10: result = self.FetchURL(self.urls_centreon['keepAlive'], giveback='raw') self.raw, self.error, self.status_code = result.result, result.error, result.status_code @@ -1105,11 +1105,11 @@ def _check_session(self): self.SID = self._get_sid().result if conf.debug_mode == True: self.Debug(server=self.get_name(), debug='Session renewed') - else: - return Result(result='ERROR', - error='Cannot detect Centreon version') - except: - import traceback - traceback.print_exc(file=sys.stdout) - result, error = self.Error(sys.exc_info()) - return Result(result=result, error=error) + except: + import traceback + traceback.print_exc(file=sys.stdout) + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) + else: + return Result(result='ERROR', + error='Cannot detect Centreon version') \ No newline at end of file From ae8f00e49578a883afc1045fb42cf47e015caaf3 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 20 Aug 2022 09:10:23 +0200 Subject: [PATCH 341/884] 3.9-20220820 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 06783d4bd..a462d79f5 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20220818' + VERSION = '3.9-20220820' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 02b16fcc2..269c8fbb9 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.9-20220818) unstable; urgency=low +nagstamon (3.9-20220820) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Wed, 17 Aug 2022 08:00:00 +0100 From 2e5028d135394e35c88c0764e7916a7a8fdb2323 Mon Sep 17 00:00:00 2001 From: HenriWahl <henri@nagstamon.de> Date: Sat, 20 Aug 2022 15:32:18 -0700 Subject: [PATCH 342/884] try to fix unwanted size changes in macOS --- Nagstamon/QUI/__init__.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 16f7a66a3..2edd87fe7 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -1461,7 +1461,7 @@ def show_window_systrayicon(self): """ if not self.is_shown: # under unfortunate circumstances statusbar might have the the moving flag true - # fix it here because it makes no sense but might cause non-appearing statuswindow + # fix it here because it makes no sense but might cause non-appearing statuswindow‚ self.moving = False # already show here because was closed before in hide_window() @@ -1478,6 +1478,10 @@ def show_window(self, event=None): """ # do not show up when being dragged around if not self.moving: + + print("show", self.windowFlags()) + print('show', self.maximumSize()) + # check if really all is OK for vbox in self.servers_vbox.children(): if vbox.server.all_ok and \ @@ -1507,7 +1511,7 @@ def show_window(self, event=None): if not vbox.server.all_ok: vbox.show_all() # show at least server vbox header to notify about connection or other errors - elif vbox.server.status != '' or vbox.server.refresh_authentication or vbox.server.tls_error: + if vbox.server.status != '' or vbox.server.refresh_authentication or vbox.server.tls_error: vbox.show_only_header() elif vbox.server.all_ok and vbox.server.status == '': vbox.hide_all() @@ -1627,8 +1631,10 @@ def hide_window(self): self.statusbar.show() self.toparea.hide() self.servers_scrollarea.hide() - self.setMinimumSize(1, 1) self.adjustSize() + # macOS needs this since Qt6 to avoid statuswindow size changeability + self.setMinimumSize(self.stored_width, 16) + self.setMaximumSize(self.stored_width, 16) if conf.icon_in_systray: self.close() From c3feb5f6c1c56199cb22d75c74c24772f4ef9c6c Mon Sep 17 00:00:00 2001 From: HenriWahl <henri@nagstamon.de> Date: Sat, 20 Aug 2022 15:36:32 -0700 Subject: [PATCH 343/884] remove print statements --- Nagstamon/QUI/__init__.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 2edd87fe7..d943393f7 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -1478,10 +1478,6 @@ def show_window(self, event=None): """ # do not show up when being dragged around if not self.moving: - - print("show", self.windowFlags()) - print('show', self.maximumSize()) - # check if really all is OK for vbox in self.servers_vbox.children(): if vbox.server.all_ok and \ From 0ba708249bc63961a70671d99f064882121771b9 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 21 Aug 2022 08:51:32 +0200 Subject: [PATCH 344/884] fix statuswindow height --- Nagstamon/QUI/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index d943393f7..2ecbbd920 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -1629,8 +1629,8 @@ def hide_window(self): self.servers_scrollarea.hide() self.adjustSize() # macOS needs this since Qt6 to avoid statuswindow size changeability - self.setMinimumSize(self.stored_width, 16) - self.setMaximumSize(self.stored_width, 16) + self.setMinimumSize(self.stored_width, self.stored_height) + self.setMaximumSize(self.stored_width, self.stored_height) if conf.icon_in_systray: self.close() @@ -1827,6 +1827,7 @@ def resize_window(self, width, height, x, y): self.stored_x = self.x() self.stored_y = self.y() self.stored_width = self.width() + self.stored_height = self.height() if OS == OS_WINDOWS: # absolutely strange, but no other solution available @@ -1919,6 +1920,7 @@ def store_position(self): self.stored_x = self.x() self.stored_y = self.y() self.stored_width = self.width() + self.stored_height = self.height() def leaveEvent(self, event): """ From de9e26a843a71b92f3201f19ee5f19a825be5b61 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 21 Aug 2022 09:42:22 +0200 Subject: [PATCH 345/884] fix statuswindow height macOS --- Nagstamon/QUI/__init__.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 2ecbbd920..0d707d19f 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -1627,10 +1627,13 @@ def hide_window(self): self.statusbar.show() self.toparea.hide() self.servers_scrollarea.hide() - self.adjustSize() # macOS needs this since Qt6 to avoid statuswindow size changeability - self.setMinimumSize(self.stored_width, self.stored_height) - self.setMaximumSize(self.stored_width, self.stored_height) + if OS == OS_DARWIN: + self.setMinimumSize(self.stored_width, self.stored_height) + self.setMaximumSize(self.stored_width, self.stored_height) + else: + self.setMinimumSize(1, 1) + self.adjustSize() if conf.icon_in_systray: self.close() From e6771cd60cf9e0fc4f2afbc16594754238cbaf80 Mon Sep 17 00:00:00 2001 From: Benoit Poulet <benoit.poulet@businessdecision.com> Date: Mon, 22 Aug 2022 17:55:15 +0200 Subject: [PATCH 346/884] Disable Monitor in the context menu As i don't know how to do it. --- Nagstamon/Servers/Centreon/CentreonAPI.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Servers/Centreon/CentreonAPI.py b/Nagstamon/Servers/Centreon/CentreonAPI.py index bc55a11af..bb71fe958 100644 --- a/Nagstamon/Servers/Centreon/CentreonAPI.py +++ b/Nagstamon/Servers/Centreon/CentreonAPI.py @@ -54,7 +54,10 @@ def __init__(self, **kwds): self.HARD_SOFT = {'(H)': 'hard', '(S)': 'soft'} # Entries for monitor default actions in context menu - self.MENU_ACTIONS = ['Monitor', 'Recheck', 'Acknowledge', 'Downtime'] + # Removed Monitor, as I don’t know a way to show directly the service + # or host details page, so i show the main ressource page + # self.MENU_ACTIONS = ['Monitor', 'Recheck', 'Acknowledge', 'Submit check result', 'Downtime'] + self.MENU_ACTIONS = ['Recheck', 'Acknowledge', 'Downtime'] # URLs of the Centreon pages self.urls_centreon = None @@ -120,6 +123,7 @@ def define_url(self): debug='URLs defined for Centreon vers. : %s' % (self.centreon_version_major)) def open_monitor(self, host, service=''): + # Used for self.MENU_ACTIONS = ['Monitor'] # Autologin seems deprecated as admin must enable it globaly and use the old pages # Ex : http://10.66.113.52/centreon/main.php?autologin=1&useralias=admin&token=xxxxxx # if self.use_autologin is True: @@ -127,8 +131,7 @@ def open_monitor(self, host, service=''): # else: # auth = '' # webbrowser_open(self.urls_centreon['resources'] + auth ) - - webbrowser_open(self.urls_centreon['resources']) + webbrowser_open(self.monitor_cgi_url) def get_token(self): try: From 6c089faee5da8271b8443835ea986d9328b59ac0 Mon Sep 17 00:00:00 2001 From: Benoit Poulet <benoit.poulet@businessdecision.com> Date: Tue, 23 Aug 2022 16:02:34 +0200 Subject: [PATCH 347/884] Fix error HTTPS on 22.04 with valid certificate AttributeError: 'CentreonServer' object has no attribute 'custom_cert_use' --- Nagstamon/Servers/Centreon/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Nagstamon/Servers/Centreon/__init__.py b/Nagstamon/Servers/Centreon/__init__.py index a88697534..917db9b82 100644 --- a/Nagstamon/Servers/Centreon/__init__.py +++ b/Nagstamon/Servers/Centreon/__init__.py @@ -35,6 +35,7 @@ def __init__(self, **kwds): # because auf being very early in init process the property ignore_cert is not known yet # add it here to be able to fetch URL and ignore certs if activated self.ignore_cert = server_conf.ignore_cert + self.custom_cert_use = server_conf.custom_cert_use # This URL exists on Centreon 22.x - if not accessible it must be legacy versions_raw = self.FetchURL(f'{server_conf.monitor_cgi_url}/api/latest/platform/versions', no_auth=True, giveback='raw') self.Debug(server='[' + self.get_name() + ']', debug='Status code %s' % (str(versions_raw.status_code))) From f0df9cbba4c51a550fce0334c855529c75b5df4f Mon Sep 17 00:00:00 2001 From: nautics889 <cyberukr@gmail.com> Date: Fri, 26 Aug 2022 15:02:41 +0300 Subject: [PATCH 348/884] Fixed mutual default argument in methods. Got rid from a mutual default argument `all_services` in components for server interaction. Mutual default argument can cause unpredictable behavoiur. --- Nagstamon/Servers/Alertmanager/alertmanagerserver.py | 2 +- Nagstamon/Servers/Centreon/CentreonAPI.py | 6 +++--- Nagstamon/Servers/Centreon/CentreonLegacy.py | 7 ++++--- Nagstamon/Servers/Generic.py | 4 ++-- Nagstamon/Servers/Icinga.py | 4 ++-- Nagstamon/Servers/Icinga2API.py | 2 +- Nagstamon/Servers/IcingaWeb2.py | 2 +- Nagstamon/Servers/Monitos4x.py | 6 +++--- Nagstamon/Servers/Multisite.py | 7 ++++--- Nagstamon/Servers/Opsview.py | 2 +- Nagstamon/Servers/SnagView3.py | 6 +++--- Nagstamon/Servers/Thruk.py | 4 ++-- Nagstamon/Servers/Zabbix.py | 4 +++- Nagstamon/Servers/op5Monitor.py | 2 +- 14 files changed, 31 insertions(+), 27 deletions(-) diff --git a/Nagstamon/Servers/Alertmanager/alertmanagerserver.py b/Nagstamon/Servers/Alertmanager/alertmanagerserver.py index 175dc9a83..6ead148ed 100644 --- a/Nagstamon/Servers/Alertmanager/alertmanagerserver.py +++ b/Nagstamon/Servers/Alertmanager/alertmanagerserver.py @@ -328,7 +328,7 @@ def set_acknowledge(self, info_dict): def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, - all_services=[], expire_time=None): + all_services=None, expire_time=None): alert = self.hosts[host].services[service] ends_at = convert_timestring_to_utc(expire_time) diff --git a/Nagstamon/Servers/Centreon/CentreonAPI.py b/Nagstamon/Servers/Centreon/CentreonAPI.py index bb71fe958..b68989342 100644 --- a/Nagstamon/Servers/Centreon/CentreonAPI.py +++ b/Nagstamon/Servers/Centreon/CentreonAPI.py @@ -431,7 +431,7 @@ def _get_status(self): # return True if all worked well return Result() - def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=[]): + def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=None): try: acknowledgements = { @@ -474,8 +474,8 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi debug="Set Ack on Host, status code : " + str(status_code)) # Service - if service != '' or len(all_services) > 0: - if len(all_services) == 0: + if service != '' or all_services: + if not all_services: all_services = [service] for s in all_services: diff --git a/Nagstamon/Servers/Centreon/CentreonLegacy.py b/Nagstamon/Servers/Centreon/CentreonLegacy.py index 30be37dd1..f17cfb39a 100644 --- a/Nagstamon/Servers/Centreon/CentreonLegacy.py +++ b/Nagstamon/Servers/Centreon/CentreonLegacy.py @@ -844,7 +844,7 @@ def _get_status(self): return Result() - def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=[]): + def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=None): # decision about host or service - they have different URLs try: if service == '': @@ -873,11 +873,12 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi # if host is acknowledged and all services should be to or if a service is acknowledged # (and all other on this host too) - if service != '' or len(all_services) > 0: + if service != '' or all_services: # service(s) @ host # if all_services is empty only one service has to be checked - the one clicked # otherwise if there all services should be acknowledged - if len(all_services) == 0: all_services = [service] + if not all_services: + all_services = [service] # acknowledge all services on a host for s in all_services: diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index 50be6cdf0..4090e96ec 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -412,7 +412,7 @@ def set_acknowledge(self, info_dict): all_services) - def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=[]): + def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=None): ''' send acknowledge to monitor server - might be different on every monitor type ''' @@ -452,7 +452,7 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi self.FetchURL(url, giveback='raw', cgi_data=cgi_data) # acknowledge all services on a host - if len(all_services) > 0: + if all_services: for s in all_services: cgi_data['cmd_typ'] = '34' cgi_data['service'] = s diff --git a/Nagstamon/Servers/Icinga.py b/Nagstamon/Servers/Icinga.py index 7d75a0b52..0f3431fd2 100644 --- a/Nagstamon/Servers/Icinga.py +++ b/Nagstamon/Servers/Icinga.py @@ -634,7 +634,7 @@ def _set_recheck(self, host, service): self.FetchURL(self.monitor_cgi_url + '/cmd.cgi', giveback='raw', cgi_data=cgi_data) - def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=[]): + def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=None): ''' send acknowledge to monitor server extra _method necessary due to https://github.com/HenriWahl/Nagstamon/issues/192 @@ -680,7 +680,7 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi self.FetchURL(url, giveback='raw', cgi_data=cgi_data) # acknowledge all services on a host - if len(all_services) > 0: + if all_services: for s in all_services: cgi_data['cmd_typ'] = '34' cgi_data['service'] = s diff --git a/Nagstamon/Servers/Icinga2API.py b/Nagstamon/Servers/Icinga2API.py index 207e3f9cb..dad173f0a 100644 --- a/Nagstamon/Servers/Icinga2API.py +++ b/Nagstamon/Servers/Icinga2API.py @@ -209,7 +209,7 @@ def _set_recheck(self, host, service): pass def _set_acknowledge(self, host, service, author, comment, sticky, - notify, persistent, all_services=[]): + notify, persistent, all_services=None): ''' Send acknowledge to monitor server ''' diff --git a/Nagstamon/Servers/IcingaWeb2.py b/Nagstamon/Servers/IcingaWeb2.py index c9a14565a..7008564c1 100644 --- a/Nagstamon/Servers/IcingaWeb2.py +++ b/Nagstamon/Servers/IcingaWeb2.py @@ -406,7 +406,7 @@ def set_acknowledge(self, info_dict): info_dict['expire_time']) - def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=[], expire_time=None): + def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=None, expire_time=None): # First retrieve the info page for this host/service if service == '': # url = self.monitor_cgi_url + '/monitoring/host/acknowledge-problem?host=' + host diff --git a/Nagstamon/Servers/Monitos4x.py b/Nagstamon/Servers/Monitos4x.py index db6bd7d2f..adf582f11 100644 --- a/Nagstamon/Servers/Monitos4x.py +++ b/Nagstamon/Servers/Monitos4x.py @@ -456,7 +456,7 @@ def _set_recheck(self, host, service): else: self.session.post('{0}/api/{1}/{2}/reschedule'.format(self.monitor_url, type ,uuid)) - def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=[]): + def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=None): """ Do a POST-Request to set an acknowledgement for a host, service or host with all services in monitos 4 @@ -467,13 +467,13 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi :param sticky: Bool - Sticky Acknowledgement :param notify: Bool - Send Notifications :param persistent: Bool - Persistent comment - :param all_services: Array - List of all services (filled only if 'Acknowledge all services on host' is set) + :param all_services: Optional[Array] - List of all services (filled only if 'Acknowledge all services on host' is set) """ form_data = dict() type = 'host' - if len(all_services) > 0: # Host & all Services + if all_services: # Host & all Services uuid = self.hosts[host].uuid form_data = json.dumps( {'comment': comment, 'notify': int(notify), 'persistent': int(persistent), diff --git a/Nagstamon/Servers/Multisite.py b/Nagstamon/Servers/Multisite.py index 2cb4ec9fe..0d20786b6 100644 --- a/Nagstamon/Servers/Multisite.py +++ b/Nagstamon/Servers/Multisite.py @@ -516,7 +516,7 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t debug='Invalid start/end date/time given') - def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=[]): + def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=None): p = { '_acknowledge': 'Acknowledge', '_ack_sticky': sticky == 1 and 'on' or '', @@ -527,8 +527,9 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi self._action(self.hosts[host].site, host, service, p) # acknowledge all services on a host when told to do so - for s in all_services: - self._action(self.hosts[host].site, host, s, p) + if all_services: + for s in all_services: + self._action(self.hosts[host].site, host, s, p) def _set_recheck(self, host, service): diff --git a/Nagstamon/Servers/Opsview.py b/Nagstamon/Servers/Opsview.py index 267430150..c7d59f564 100644 --- a/Nagstamon/Servers/Opsview.py +++ b/Nagstamon/Servers/Opsview.py @@ -158,7 +158,7 @@ def _set_submit_check_result(self, host, service, state, comment, check_output, self.FetchURL(url + cgi_data, giveback="raw", cgi_data=({ })) - def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=[]): + def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=None): """ Sumit acknowledgement for host or service """ diff --git a/Nagstamon/Servers/SnagView3.py b/Nagstamon/Servers/SnagView3.py index 46f4ce947..dc9f03e8a 100644 --- a/Nagstamon/Servers/SnagView3.py +++ b/Nagstamon/Servers/SnagView3.py @@ -366,7 +366,7 @@ def _set_recheck(self, host, service): self.session.post( '{0}/rest/private/nagios/command/execute'.format(self.monitor_url), data=form_data) - def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=[]): + def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=None): """ Do a POST-Request to set an acknowledgement for a host, service or host with all services in SNAG-View 3 @@ -377,11 +377,11 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi :param sticky: Bool - Sticky Acknowledgement :param notify: Bool - Send Notifications :param persistent: Bool - Persistent comment - :param all_services: Array - List of all services (filled only if 'Acknowledge all services on host' is set) + :param all_services: Optional[Array] - List of all services (filled only if 'Acknowledge all services on host' is set) """ form_data = dict() - if len(all_services) > 0: # Host & all Services + if all_services: # Host & all Services form_data['commandType'] = 'sv_host' form_data['commandName'] = 'acknowledge-host-service-problems' form_data['params'] = json.dumps( diff --git a/Nagstamon/Servers/Thruk.py b/Nagstamon/Servers/Thruk.py index b4faf108a..a86cc2e1a 100644 --- a/Nagstamon/Servers/Thruk.py +++ b/Nagstamon/Servers/Thruk.py @@ -151,7 +151,7 @@ def open_monitor(self, host, service=''): debug='Open host/service monitor web page {0}'.format(url)) webbrowser_open(url) - def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=[]): + def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=None): ''' send acknowledge to monitor server - might be different on every monitor type ''' @@ -191,7 +191,7 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi self.FetchURL(url, giveback='raw', cgi_data=cgi_data) # acknowledge all services on a host - if len(all_services) > 0: + if all_services: for s in all_services: cgi_data['cmd_typ'] = '34' cgi_data['service'] = self.hosts[host].services[ s ].real_name diff --git a/Nagstamon/Servers/Zabbix.py b/Nagstamon/Servers/Zabbix.py index aa4c861ed..1a948472f 100644 --- a/Nagstamon/Servers/Zabbix.py +++ b/Nagstamon/Servers/Zabbix.py @@ -496,7 +496,7 @@ def open_monitor(self, host, service=""): def set_recheck(self, info_dict): pass - def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=[]): + def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=None): if conf.debug_mode is True: self.Debug(server=self.get_name(), debug="Set Acknowledge Host: " + host + " Service: " + service + " Sticky: " + str( @@ -504,6 +504,8 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi self._login() eventids = set() unclosable_events = set() + if all_services is None: + all_services = [] all_services.append(service) get_host = self.hosts[host] # Through all Services diff --git a/Nagstamon/Servers/op5Monitor.py b/Nagstamon/Servers/op5Monitor.py index 13f61144c..06cd89ece 100644 --- a/Nagstamon/Servers/op5Monitor.py +++ b/Nagstamon/Servers/op5Monitor.py @@ -315,7 +315,7 @@ def _set_recheck(self, host, service): self.send_command(command, params) - def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services): + def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=None): params = {'host_name': host, 'sticky': int(sticky), 'notify': int(notify), 'persistent': int(persistent), 'comment': comment} From 1aa53d91671b6361a2daa90b6f8dead286b8240d Mon Sep 17 00:00:00 2001 From: MarcusCaepio <7324088+MarcusCaepio@users.noreply.github.com> Date: Wed, 31 Aug 2022 07:33:52 +0200 Subject: [PATCH 349/884] Create IcingaDBWeb.py Initial class for IcingaDBWeb. The health check still has to be done. --- Nagstamon/Servers/IcingaDBWeb.py | 639 +++++++++++++++++++++++++++++++ 1 file changed, 639 insertions(+) create mode 100644 Nagstamon/Servers/IcingaDBWeb.py diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py new file mode 100644 index 000000000..84c9b5b9c --- /dev/null +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -0,0 +1,639 @@ +# encoding: utf-8 + +# Nagstamon - Nagios status monitor for your desktop +# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +# Initial implementation by Marcus Mönnig +# +# This Server class connects against IcingaWeb2. The monitor URL in the setup should be +# something like http://icinga2/icingaweb2 +# +# Status/TODOs: +# +# * The IcingaWeb2 API is not implemented yet, so currently this implementation uses +# two HTTP requests per action. The first fetches the HTML, then the form data is extracted and +# then a second HTTP POST request is made which actually executed the action. +# Once IcingaWeb2 has an API, it's probably the better choice. + + +from Nagstamon.Servers.Generic import GenericServer +import urllib.parse +import sys +import copy +import json +import datetime +import socket + +from bs4 import BeautifulSoup +from Nagstamon.Objects import (GenericHost, + GenericService, + Result) +from Nagstamon.Config import (conf, + AppInfo) +from Nagstamon.Helpers import webbrowser_open + + +def strfdelta(tdelta, fmt): + d = {'days': tdelta.days} + d['hours'], rem = divmod(tdelta.seconds, 3600) + d['minutes'], d['seconds'] = divmod(rem, 60) + return fmt.format(**d) + + +class IcingaDBWebServer(GenericServer): + """ + object of Icinga server + """ + TYPE = 'IcingaDBWeb' + MENU_ACTIONS = ['Monitor', 'Recheck', 'Acknowledge', 'Submit check result', 'Downtime'] + STATES_MAPPING = {'hosts' : {0 : 'UP', 1 : 'DOWN', 2 : 'UNREACHABLE'}, \ + 'services' : {0 : 'OK', 1 : 'WARNING', 2 : 'CRITICAL', 3 : 'UNKNOWN'}} + STATES_MAPPING_REV = {'hosts' : { 'UP': 0, 'DOWN': 1, 'UNREACHABLE': 2}, \ + 'services' : {'OK': 0, 'WARNING': 1, 'CRITICAL': 2, 'UNKNOWN': 3}} + BROWSER_URLS = { 'monitor': '$MONITOR-CGI$/dashboard', \ + 'hosts': '$MONITOR-CGI$/icingadb/hosts', \ + 'services': '$MONITOR-CGI$/icingadb/services', \ + 'history': '$MONITOR-CGI$/icingadb/history'} + + + def init_config(self): + """ + set URLs for CGI - they are static and there is no need to set them with every cycle + """ + # dummy default empty cgi urls - get filled later when server version is known + self.cgiurl_services = None + self.cgiurl_hosts = None + self.cgiurl_monitoring_health = None + + # https://github.com/HenriWahl/Nagstamon/issues/400 + # The displayed name for host and service is the Icinga2 "internal" name and not the display_name from host/service configuration + # This name is stored in host/service dict under key 'name' but is also used as dict key for dict containing all hosts/services + # The "internal" name must still be used to query IcingaWeb2 and is in dict under key 'real_name' since https://github.com/HenriWahl/Nagstamon/issues/192 + self.use_display_name_host = True + self.use_display_name_service = True + + def init_HTTP(self): + """ + initializing of session object + """ + GenericServer.init_HTTP(self) + + if self.session and not 'Referer' in self.session.headers: + self.session.headers['Referer'] = self.monitor_cgi_url + '/icingaweb2/icingadb' + + # normally cookie auth will be used + if not self.no_cookie_auth: + if 'cookies' not in dir(self.session) or len(self.session.cookies) == 0: + # get login page, thus automatically a cookie + login = self.FetchURL('{0}/authentication/login'.format(self.monitor_url)) + if login.error == '' and login.status_code == 200: + form = login.result.find('form') + form_inputs = {} + for form_input in ('redirect', 'formUID', 'CSRFToken', 'btn_submit'): + if not form.find('input', {'name': form_input}) is None: + form_inputs[form_input] = form.find('input', {'name': form_input})['value'] + else: + form_inputs[form_input] = '' + form_inputs['username'] = self.username + form_inputs['password'] = self.password + + # fire up login button with all needed data + self.FetchURL('{0}/authentication/login'.format(self.monitor_url), cgi_data=form_inputs) + + + def _get_status(self): + """ + Get status from Icinga Server - only JSON + """ + # define CGI URLs for hosts and services + if self.cgiurl_hosts == self.cgiurl_services == self.cgiurl_monitoring_health == None: + # services (unknown, warning or critical?) + self.cgiurl_services = {'hard': self.monitor_cgi_url + '/icingadb/services?service.state.is_problem=y&service.state.in_downtime=n&service.state.state_type=hard&columns=service.state.last_update,service.state.is_reachable&format=json', \ + 'soft': self.monitor_cgi_url + '/icingadb/services?service.state.is_problem=y&service.state.in_downtime=n&service.state.state_type=soft&columns=service.state.last_update,service.state.is_reachable&format=json'} + # hosts (up or down or unreachable) + self.cgiurl_hosts = {'hard': self.monitor_cgi_url + '/icingadb/hosts?host.state.is_problem=y&host.state.state_type=hard&columns=host.state.last_update&format=json', \ + 'soft': self.monitor_cgi_url + '/icingadb/hosts?host.state.is_problem=y&host.state.state_type=soft&columns=host.state.last_update&format=json'} + # monitoring health + self.cgiurl_monitoring_health = self.monitor_cgi_url + '/icingaweb2/health?format=json' + + # new_hosts dictionary + self.new_hosts = dict() + + # hosts - mostly the down ones + # now using JSON output from Icinga + try: + for status_type in 'hard', 'soft': + # first attempt + result = self.FetchURL(self.cgiurl_hosts[status_type], giveback='raw') + # authentication errors get a status code 200 too back because its + # HTML works fine :-( + if result.status_code < 400 and\ + result.result.startswith('<'): + # in case of auth error reset HTTP session and try again + self.reset_HTTP() + result = self.FetchURL(self.cgiurl_hosts[status_type], giveback='raw') + # if it does not work again tell GUI there is a problem + if result.status_code < 400 and\ + result.result.startswith('<'): + self.refresh_authentication = True + return Result(result=result.result, + error='Authentication error', + status_code=result.status_code) + + # purify JSON result of unnecessary control sequence \n + jsonraw, error, status_code = copy.deepcopy(result.result.replace('\n', '')),\ + copy.deepcopy(result.error),\ + result.status_code + + if error != '' or status_code >= 400: + return Result(result=jsonraw, + error=error, + status_code=status_code) + + # check if any error occured + self.check_for_error(jsonraw, error, status_code) + + # Check if the backend is running + # If it isn't running the last values stored in the database are returned/shown + # Unfortunately we need to make a extra request for this and only, if monitoring health is possible + if self.cgiurl_monitoring_health: + pass + # TODO: Health checks for IcingaDB and icinga-redis + # try: + # result = self.FetchURL(self.cgiurl_monitoring_health, giveback='raw') + # monitoring_health = json.loads(result.result)[0] + # if (monitoring_health['is_currently_running'] == '0'): + # return Result(result=monitoring_health, + # error='Icinga2 backend not running') + # except json.decoder.JSONDecodeError: + # # https://github.com/HenriWahl/Nagstamon/issues/619 + # # Icinga2 monitoring health status query does not seem to work (on older version?) + # self.cgiurl_monitoring_health = None + + hosts = json.loads(jsonraw) + + for host in hosts: + # make dict of tuples for better reading + h = dict(host.items()) + + # host + if self.use_display_name_host == False: + # according to http://sourceforge.net/p/nagstamon/bugs/83/ it might + # better be host_name instead of host_display_name + # legacy Icinga adjustments + if 'host_name' in h: host_name = h['host_name'] + elif 'host' in h: host_name = h['host'] + else: + # https://github.com/HenriWahl/Nagstamon/issues/46 on the other hand has + # problems with that so here we go with extra display_name option + host_name = h['host_display_name'] + + # host objects contain service objects + if not host_name in self.new_hosts: + self.new_hosts[host_name] = GenericHost() + self.new_hosts[host_name].name = host_name + self.new_hosts[host_name].server = self.name + self.new_hosts[host_name].status = self.STATES_MAPPING['hosts'][int(h['host_state'])] + self.new_hosts[host_name].last_check = datetime.datetime.fromtimestamp(int(h['host_last_check'])) + self.new_hosts[host_name].attempt = h['host_attempt'] + self.new_hosts[host_name].status_information = BeautifulSoup(h['host_output'].replace('\n', ' ').strip(), 'html.parser').text + self.new_hosts[host_name].passiveonly = not(int(h['host_active_checks_enabled'])) + self.new_hosts[host_name].notifications_disabled = not(int(h['host_notifications_enabled'])) + self.new_hosts[host_name].flapping = bool(int(h['host_is_flapping'])) + self.new_hosts[host_name].acknowledged = bool(int(h['host_acknowledged'])) + self.new_hosts[host_name].scheduled_downtime = bool(int(h['host_in_downtime'])) + self.new_hosts[host_name].status_type = status_type + + # extra Icinga properties to solve https://github.com/HenriWahl/Nagstamon/issues/192 + # acknowledge needs host_description and no display name + self.new_hosts[host_name].real_name = h['host_name'] + + # Icinga only updates the attempts for soft states. When hard state is reached, a flag is set and + # attemt is set to 1/x. + if (status_type == 'hard'): + try: + maxAttempts = h['host_attempt'].split('/')[1] + self.new_hosts[host_name].attempt = "{0}/{0}".format(maxAttempts) + except Exception: + self.new_hosts[host_name].attempt = "HARD" + + # extra duration needed for calculation + if h['host_last_state_change'] is not None: + last_change = h['host_last_state_change'] if h['host_last_state_change'] is not None else 0 + duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(last_change)) + self.new_hosts[host_name].duration = strfdelta(duration,'{days}d {hours}h {minutes}m {seconds}s') + else: + self.new_hosts[host_name].duration = 'n/a' + del h, host_name + except: + import traceback + traceback.print_exc(file=sys.stdout) + + # set checking flag back to False + self.isChecking = False + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) + + # services + try: + for status_type in 'hard', 'soft': + result = self.FetchURL(self.cgiurl_services[status_type], giveback='raw') + # purify JSON result of unnecessary control sequence \n + jsonraw, error, status_code = copy.deepcopy(result.result.replace('\n', '')),\ + copy.deepcopy(result.error),\ + result.status_code + + if error != '' or status_code >= 400: + return Result(result=jsonraw, + error=error, + status_code=status_code) + + # check if any error occured + self.check_for_error(jsonraw, error, status_code) + + services = copy.deepcopy(json.loads(jsonraw)) + + for service in services: + # make dict of tuples for better reading + s = dict(service.items()) + + if self.use_display_name_host == False: + # according to http://sourceforge.net/p/nagstamon/bugs/83/ it might + # better be host_name instead of host_display_name + # legacy Icinga adjustments + if 'host_name' in s: host_name = s['host_name'] + elif 'host' in s: host_name = s['host'] + else: + # https://github.com/HenriWahl/Nagstamon/issues/46 on the other hand has + # problems with that so here we go with extra display_name option + host_name = s['host_display_name'] + + # host objects contain service objects + if not host_name in self.new_hosts: + self.new_hosts[host_name] = GenericHost() + self.new_hosts[host_name].name = host_name + self.new_hosts[host_name].status = 'UP' + # extra Icinga properties to solve https://github.com/HenriWahl/Nagstamon/issues/192 + # acknowledge needs host_description and no display name + self.new_hosts[host_name].real_name = s['host_name'] + + service_name = s['service_display_name'] + + # if a service does not exist create its object + if not service_name in self.new_hosts[host_name].services: + self.new_hosts[host_name].services[service_name] = GenericService() + self.new_hosts[host_name].services[service_name].host = host_name + self.new_hosts[host_name].services[service_name].name = service_name + self.new_hosts[host_name].services[service_name].server = self.name + self.new_hosts[host_name].services[service_name].status = self.STATES_MAPPING['services'][int(s['service_state'])] + self.new_hosts[host_name].services[service_name].last_check = datetime.datetime.fromtimestamp(int(s['service_last_check'])) + self.new_hosts[host_name].services[service_name].attempt = s['service_attempt'] + self.new_hosts[host_name].services[service_name].status_information = BeautifulSoup(s['service_output'].replace('\n', ' ').strip(), 'html.parser').text + self.new_hosts[host_name].services[service_name].passiveonly = not(int(s['service_active_checks_enabled'])) + self.new_hosts[host_name].services[service_name].notifications_disabled = not(int(s['service_notifications_enabled'])) + self.new_hosts[host_name].services[service_name].flapping = bool(int(s['service_is_flapping'])) + self.new_hosts[host_name].services[service_name].acknowledged = bool(int(s['service_acknowledged'])) + self.new_hosts[host_name].services[service_name].scheduled_downtime = bool(int(s['service_in_downtime'])) + self.new_hosts[host_name].services[service_name].status_type = status_type + self.new_hosts[host_name].services[service_name].unreachable = s['service_is_reachable'] == '0' + + if self.new_hosts[host_name].services[service_name].unreachable: + self.new_hosts[host_name].services[service_name].status_information += " (SERVICE UNREACHABLE)" + + # extra Icinga properties to solve https://github.com/HenriWahl/Nagstamon/issues/192 + # acknowledge needs service_description and no display name + self.new_hosts[host_name].services[service_name].real_name = s['service_description'] + + # Icinga only updates the attempts for soft states. When hard state is reached, a flag is set and + # attemt is set to 1/x. + if (status_type == 'hard'): + try: + maxAttempts = s['service_attempt'].split('/')[1] + self.new_hosts[host_name].services[service_name].attempt = "{0}/{0}".format(maxAttempts) + except Exception: + self.new_hosts[host_name].services[service_name].attempt = "HARD" + + # extra duration needed for calculation + if s['service_last_state_change'] is not None: + last_change = s['service_last_state_change'] if s['service_last_state_change'] is not None else 0 + duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(last_change)) + self.new_hosts[host_name].services[service_name].duration = strfdelta(duration, '{days}d {hours}h {minutes}m {seconds}s') + else: + self.new_hosts[host_name].services[service_name].duration = 'n/a' + + del s, host_name, service_name + except: + + import traceback + traceback.print_exc(file=sys.stdout) + + # set checking flag back to False + self.isChecking = False + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) + + # some cleanup + del jsonraw, error, hosts, services + + # dummy return in case all is OK + return Result() + + + def _set_recheck(self, host, service): + # First retrieve the info page for this host/service + if service == '': + url = self.monitor_cgi_url + '/icingadb/host?name=' + self.hosts[host].real_name + else: + url = self.monitor_cgi_url + '/icingadb/service?name=' + self.hosts[host].services[service].real_name + '&host.name=' + self.hosts[host].real_name + result = self.FetchURL(url, giveback='raw') + + if result.error != '': + return result + else: + pageraw = result.result + + pagesoup = BeautifulSoup(pageraw, 'html.parser') + + # Extract the relevant form element values + + formtag = pagesoup.find('form', {'name':'IcingaModuleMonitoringFormsCommandObjectCheckNowCommandForm'}) + CSRFToken = formtag.findNext('input', {'name':'CSRFToken'})['value'] + formUID = formtag.findNext('input', {'name':'formUID'})['value'] + btn_submit = formtag.findNext('button', {'name':'btn_submit'})['value'] + + # Pass these values to the same URL as cgi_data + cgi_data = {} + cgi_data['CSRFToken'] = CSRFToken + cgi_data['formUID'] = formUID + cgi_data['btn_submit'] = btn_submit + result = self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + + + # Overwrite function from generic server to add expire_time value + def set_acknowledge(self, info_dict): + ''' + different monitors might have different implementations of _set_acknowledge + ''' + if info_dict['acknowledge_all_services'] is True: + all_services = info_dict['all_services'] + else: + all_services = [] + + # Make sure expire_time is set + #if not info_dict['expire_time']: + # info_dict['expire_time'] = None + + self._set_acknowledge(info_dict['host'], + info_dict['service'], + info_dict['author'], + info_dict['comment'], + info_dict['sticky'], + info_dict['notify'], + info_dict['persistent'], + all_services, + info_dict['expire_time']) + + + def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=[], expire_time=None): + # First retrieve the info page for this host/service + if service == '': + # url = self.monitor_cgi_url + '/monitoring/host/acknowledge-problem?host=' + host + url = '{0}/icingadb/host/acknowledge?name={1}'.format(self.monitor_cgi_url, + self.hosts[host].real_name) + else: + # url = self.monitor_cgi_url + '/monitoring/service/acknowledge-problem?host=' + host + '&service=' + service + url = '{0}/icingadb/service/acknowledge?host.name={1}&name={2}'.format(self.monitor_cgi_url, + self.hosts[host].real_name, + self.hosts[host].services[service].real_name) + result = self.FetchURL(url, giveback='raw') + + if result.error != '': + return result + else: + pageraw = result.result + + pagesoup = BeautifulSoup(pageraw, 'html.parser') + + # Extract the relevant form element values + formtag = pagesoup.find('form', {'name':'IcingaModuleMonitoringFormsCommandObjectAcknowledgeProblemCommandForm'}) + + CSRFToken = formtag.findNext('input', {'name':'CSRFToken'})['value'] + formUID = formtag.findNext('input', {'name':'formUID'})['value'] + btn_submit = formtag.findNext('input', {'name':'btn_submit'})['value'] + + # Pass these values to the same URL as cgi_data + cgi_data = {} + cgi_data['CSRFToken'] = CSRFToken + cgi_data['formUID'] = formUID + cgi_data['btn_submit'] = btn_submit +# + cgi_data['comment'] = comment + cgi_data['persistent'] = int(persistent) + cgi_data['sticky'] = int(sticky) + cgi_data['notify'] = int(notify) + cgi_data['comment'] = comment + if expire_time: + cgi_data['expire'] = 1 + cgi_data['expire_time'] = expire_time + + self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + + if len(all_services) > 0: + for s in all_services: + # cheap, recursive solution... + self._set_acknowledge(host, s, author, comment, sticky, notify, persistent, [], expire_time) + + + def _set_submit_check_result(self, host, service, state, comment, check_output, performance_data): + # First retrieve the info page for this host/service + if service == '': + url = self.monitor_cgi_url + '/icingadb/host/process-checkresult?name=' + self.hosts[host].real_name + status = self.STATES_MAPPING_REV['hosts'][state.upper()] + else: + url = self.monitor_cgi_url + '/icingadb/service/process-checkresult?host.name=' + self.hosts[host].real_name + '&name=' + self.hosts[host].services[service].real_name + status = self.STATES_MAPPING_REV['services'][state.upper()] + + result = self.FetchURL(url, giveback='raw') + + if result.error != '': + return result + else: + pageraw = result.result + + pagesoup = BeautifulSoup(pageraw, 'html.parser') + + # Extract the relevant form element values + + formtag = pagesoup.find('form', {'name':'IcingaModuleMonitoringFormsCommandObjectProcessCheckResultCommandForm'}) + CSRFToken = formtag.findNext('input', {'name':'CSRFToken'})['value'] + formUID = formtag.findNext('input', {'name':'formUID'})['value'] + btn_submit = formtag.findNext('input', {'name':'btn_submit'})['value'] + + # Pass these values to the same URL as cgi_data + cgi_data = {} + cgi_data['CSRFToken'] = CSRFToken + cgi_data['formUID'] = formUID + cgi_data['btn_submit'] = btn_submit + + cgi_data['status'] = status + cgi_data['output'] = check_output + cgi_data['perfdata'] = performance_data + + self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + + + def _set_downtime(self, host, service, author, comment, fixed, start_time, end_time, hours, minutes): + # First retrieve the info page for this host/service + if service == '': + url = self.monitor_cgi_url + '/icingadb/host/schedule-downtime?name=' + self.hosts[host].real_name + else: + url = self.monitor_cgi_url + '/icingadb/service/schedule-downtime?host.name=' + self.hosts[host].real_name + '&name=' + self.hosts[host].services[service].real_name + + result = self.FetchURL(url, giveback='raw') + + if result.error != '': + return result + else: + pageraw = result.result + + pagesoup = BeautifulSoup(pageraw, 'html.parser') + + # Extract the relevant form element values + if service == '': + formtag = pagesoup.find('form', {'name':'IcingaModuleMonitoringFormsCommandObjectScheduleHostDowntimeCommandForm'}) + else: + formtag = pagesoup.find('form', {'name':'IcingaModuleMonitoringFormsCommandObjectScheduleServiceDowntimeCommandForm'}) + + CSRFToken = formtag.findNext('input', {'name':'CSRFToken'})['value'] + formUID = formtag.findNext('input', {'name':'formUID'})['value'] + btn_submit = formtag.findNext('input', {'name':'btn_submit'})['value'] + + # Pass these values to the same URL as cgi_data + cgi_data = {} + cgi_data['CSRFToken'] = CSRFToken + cgi_data['formUID'] = formUID + cgi_data['btn_submit'] = btn_submit + cgi_data['comment'] = comment + if fixed: + cgi_data['type'] = 'fixed' + else: + cgi_data['type'] = 'flexible' + cgi_data['hours'] = hours + cgi_data['minutes'] = minutes + if start_time == '' or start_time == 'n/a': + start = datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%S') + else: + start = start_time + if end_time == '' or end_time == 'n/a': + end = (datetime.datetime.now() + datetime.timedelta(hours=hours, minutes=minutes)).strftime('%Y-%m-%dT%H:%M:%S') + else: + end = end_time + + cgi_data['start'] = start + cgi_data['end'] = end + + self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + + + def get_start_end(self, host): + ''' + for GUI to get actual downtime start and end from server - they may vary so it's better to get + directly from web interface + ''' + try: + downtime = self.FetchURL(self.monitor_cgi_url + '/icingadb/host/schedule-downtime?name=' + self.hosts[host].real_name) + start = downtime.result.find('input', {'name': 'start'})['value'] + end = downtime.result.find('input', {'name': 'end'})['value'] + # give values back as tuple + return start, end + except: + self.Error(sys.exc_info()) + return 'n/a', 'n/a' + + + def open_monitor(self, host, service=''): + ''' + open monitor from tablewidget context menu + ''' + # only type is important so do not care of service '' in case of host monitor + if service == '': + url = '{0}/icingadb/hosts?host.state.is_problem=y&sort=host.state.severity#!{1}/icingadb/hosts/{2}'.format(self.monitor_url, + (urllib.parse.urlparse(self.monitor_url).path), + urllib.parse.urlencode( + {'host': self.hosts[host].real_name}).replace('+', ' ')) + else: + url = '{0}/icingadb/services?service.state.is_problem=y&sort=service.state.severity&dir=desc#!{1}/icingadb/services/{2}'.format(self.monitor_url, + (urllib.parse.urlparse(self.monitor_url).path), + urllib.parse.urlencode( + {'host': self.hosts[host].real_name, + 'service': self.hosts[host].services[service].real_name}).replace('+', ' ')) + if conf.debug_mode: + self.Debug(server=self.get_name(), host=host, service=service, + debug='Open host/service monitor web page {0}'.format(url)) + webbrowser_open(url) + + def GetHost(self, host): + ''' + find out ip or hostname of given host to access hosts/devices which do not appear in DNS but + have their ip saved in Icinga + ''' + # Host is the display name as in the GUI + # but we need the FQDN not the display name + host = self.hosts[host].real_name + + # the fasted method is taking hostname as used in monitor + if conf.connect_by_host is True or host == '': + return Result(result=host) + + # initialize ip string + ip = '' + address = '' + + # glue nagios cgi url and hostinfo + cgiurl_host = self.monitor_cgi_url + '/icingadb/hosts?name={0}&columns=host.address&format=json'.format(host) + + # get host info + hostobj = self.FetchURL(cgiurl_host, giveback='raw') + jsonhost = hostobj.result + + try: + # take ip from json output + result = json.loads(jsonhost)[0] + ip = result["host_address"] + + # print IP in debug mode + if conf.debug_mode is True: + self.Debug(server=self.get_name(), host=host, debug='IP of %s:' % (host) + ' ' + ip) + + # when connection by DNS is not configured do it by IP + if conf.connect_by_dns is True: + # try to get DNS name for ip, if not available use ip + try: + address = socket.gethostbyaddr(ip)[0] + except socket.error: + address = ip + else: + address = ip + except Exception: + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) + + # do some cleanup + del hostobj + + # give back host or ip + return Result(result=address) From aaded5c1da90077a1d30d21b28353af483aba0b3 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Wed, 28 Sep 2022 12:21:41 +0200 Subject: [PATCH 350/884] fix unused custom ca --- Nagstamon/Config.py | 2 +- Nagstamon/QUI/__init__.py | 2 -- Nagstamon/Servers/Generic.py | 32 +++++++++++++++++++++++--------- build/debian/changelog | 2 +- 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index ca00e7ea5..dd28dae51 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20220820' + VERSION = '3.9-20220928' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 5e832a1c2..7e09655d1 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -97,7 +97,6 @@ # check ECP authentication support availability try: from requests_ecp import HTTPECPAuth - ECP_AVAILABLE = True except ImportError: ECP_AVAILABLE = False @@ -2964,7 +2963,6 @@ def hide_all(self): self.label_stretcher.hide() self.button_authenticate.hide() self.button_fix_tls_error.hide() - # special table treatment self.table.hide() self.table.is_shown = False diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index f799586e2..6b4deace8 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -30,6 +30,13 @@ from bs4 import BeautifulSoup import requests +# check ECP authentication support availability +try: + from requests_ecp import HTTPECPAuth + ECP_AVAILABLE = True +except ImportError: + ECP_AVAILABLE = False + from Nagstamon.Helpers import (host_is_filtered_out_by_re, ServiceIsFilteredOutByRE, StatusInformationIsFilteredOutByRE, @@ -50,7 +57,6 @@ debug_queue, OS, OS_DARWIN, - OS_WINDOWS, RESOURCES) if OS == OS_DARWIN: @@ -71,11 +77,18 @@ try: from requests.packages.urllib3.exceptions import SubjectAltNameWarning requests.packages.urllib3.disable_warnings(SubjectAltNameWarning) -except: +except ImportError: # older requests version might not have the packages submodule # for example the one in Ubuntu 14.04 pass +# avoid flooding logs with InsecureRequestWarnings for connections with 'no_auth' set +# interestingly this is not available in requests.packages.urllib3.exceptions +try: + import urllib3 + urllib3.disable_warnings() +except ImportError: + pass class GenericServer(object): @@ -280,7 +293,7 @@ def create_session(self): session.auth = requests.auth.HTTPBasicAuth(self.username, self.password) elif self.authentication == 'digest': session.auth = requests.auth.HTTPDigestAuth(self.username, self.password) - elif self.authentication == 'ecp': + elif self.authentication == 'ecp' and ECP_AVAILABLE: session.auth = HTTPECPAuth(self.idp_ecp_endpoint, username=self.username, password=self.password) elif self.authentication == 'kerberos': session.auth = HTTPSKerberos() @@ -1439,7 +1452,6 @@ def FetchURL(self, url, giveback='obj', cgi_data=None, no_auth=False, multipart= # use session only for connections to monitor servers, other requests like looking for updates # should go out without credentials if no_auth is False and not self.refresh_authentication: - # if no_auth is False: # check if there is really a session if not self.session: self.reset_HTTP() @@ -1447,9 +1459,9 @@ def FetchURL(self, url, giveback='obj', cgi_data=None, no_auth=False, multipart= # most requests come without multipart/form-data if multipart is False: if cgi_data is None: - response = self.session.get(url, timeout=self.timeout, verify=not self.ignore_cert) + response = self.session.get(url, timeout=self.timeout) else: - response = self.session.post(url, data=cgi_data, timeout=self.timeout, verify=not self.ignore_cert) + response = self.session.post(url, data=cgi_data, timeout=self.timeout) else: # Checkmk and Opsview need multipart/form-data encoding # http://stackoverflow.com/questions/23120974/python-requests-post-multipart-form-data-without-filename-in-http-request#23131823 @@ -1477,9 +1489,11 @@ def FetchURL(self, url, giveback='obj', cgi_data=None, no_auth=False, multipart= # most requests come without multipart/form-data if multipart is False: if cgi_data is None: - response = temporary_session.get(url, timeout=self.timeout, verify=not self.ignore_cert) + #response = temporary_session.get(url, timeout=self.timeout, verify=not self.ignore_cert) + response = temporary_session.get(url, timeout=self.timeout, verify=False) else: - response = temporary_session.post(url, data=cgi_data, timeout=self.timeout, verify=not self.ignore_cert) + #response = temporary_session.post(url, data=cgi_data, timeout=self.timeout, verify=not self.ignore_cert) + response = temporary_session.post(url, data=cgi_data, timeout=self.timeout, verify=False) else: # Checkmk and Opsview need multipart/form-data encoding # http://stackoverflow.com/questions/23120974/python-requests-post-multipart-form-data-without-filename-in-http-request#23131823 @@ -1487,7 +1501,7 @@ def FetchURL(self, url, giveback='obj', cgi_data=None, no_auth=False, multipart= for key in cgi_data: form_data[key] = (None, cgi_data[key]) # get response with cgi_data encodes as files - response = temporary_session.post(url, files=form_data, timeout=self.timeout) + response = temporary_session.post(url, files=form_data, timeout=self.timeout, verify=False) # cleanup del temporary_session diff --git a/build/debian/changelog b/build/debian/changelog index 269c8fbb9..3e682011b 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.9-20220820) unstable; urgency=low +nagstamon (3.9-20220928) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Wed, 17 Aug 2022 08:00:00 +0100 From e7da178b34453e16d2aaa4784cb1fe569434f228 Mon Sep 17 00:00:00 2001 From: Yannick Charton <tontonitch-pro@yahoo.fr> Date: Wed, 28 Sep 2022 15:41:29 +0200 Subject: [PATCH 351/884] Adding IcingaDBWeb to the server types Adding IcingaDBWeb to the server types. Should be as simple as that I suppose ;-) --- Nagstamon/Servers/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Nagstamon/Servers/__init__.py b/Nagstamon/Servers/__init__.py index bcf9189dd..355a3f6db 100644 --- a/Nagstamon/Servers/__init__.py +++ b/Nagstamon/Servers/__init__.py @@ -37,6 +37,7 @@ from Nagstamon.Servers.Centreon import CentreonServer from Nagstamon.Servers.Icinga import IcingaServer from Nagstamon.Servers.IcingaWeb2 import IcingaWeb2Server +from Nagstamon.Servers.IcingaDBWebServer import IcingaDBWebServer from Nagstamon.Servers.Multisite import MultisiteServer from Nagstamon.Servers.op5Monitor import Op5MonitorServer from Nagstamon.Servers.Opsview import OpsviewServer From ac0e95b8a124de18e6c1b2990e944f16fc3f4677 Mon Sep 17 00:00:00 2001 From: HenriWahl <henri@nagstamon.de> Date: Wed, 28 Sep 2022 06:55:27 -0700 Subject: [PATCH 352/884] fix freaking out statusbar --- Nagstamon/QUI/__init__.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 7e09655d1..c29124bb7 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -1628,9 +1628,10 @@ def hide_window(self): self.toparea.hide() self.servers_scrollarea.hide() # macOS needs this since Qt6 to avoid statuswindow size changeability + # looks silly but works to force using the own hint as hint if OS == OS_DARWIN: - self.setMinimumSize(self.stored_width, self.stored_height) - self.setMaximumSize(self.stored_width, self.stored_height) + self.setMinimumSize(self.sizeHint()) + self.setMaximumSize(self.sizeHint()) else: self.setMinimumSize(1, 1) self.adjustSize() @@ -1881,10 +1882,10 @@ def adjust_size(self): y = self.y() self.setMaximumSize(hint) self.setMinimumSize(hint) - del (hint) + del hint self.resize_window(width, height, x, y) - del (width, height, x, y) + del width, height, x, y else: self.adjust_dummy_columns() @@ -2833,15 +2834,23 @@ def __init__(self, server, parent=None): self.button_history = PushButton_BrowserURL(text='History', parent=parent, server=self.server, url_type='history') self.button_edit = Button('Edit', parent=parent) + ###self.button_edit.setStyleSheet('background: green;') + # use label instead of spacer to be clickable self.label_stretcher = ClosingLabel('', parent=parent) self.label_stretcher.setSizePolicy(QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.Expanding) + ###self.label_stretcher.setStyleSheet('background: green;') self.label_status = ServerStatusLabel(parent=parent) self.label_status.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Expanding) + ###self.label_status.setStyleSheet('background: green;') + self.button_authenticate = QPushButton('Authenticate', parent=parent) + ###self.button_authenticate.setStyleSheet('background: green;') + + ###self.button_authenticate.setStyleSheet('background: green;') self.button_fix_tls_error = QPushButton('Fix error', parent=parent) From 77ac5bc24a0ab9d258ff5682b6a130f95f99b376 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Wed, 28 Sep 2022 16:08:42 +0200 Subject: [PATCH 353/884] Nagstamon.Servers.IcingaDBWeb --- Nagstamon/Servers/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/Servers/__init__.py b/Nagstamon/Servers/__init__.py index 355a3f6db..c56bbe3a4 100644 --- a/Nagstamon/Servers/__init__.py +++ b/Nagstamon/Servers/__init__.py @@ -37,7 +37,7 @@ from Nagstamon.Servers.Centreon import CentreonServer from Nagstamon.Servers.Icinga import IcingaServer from Nagstamon.Servers.IcingaWeb2 import IcingaWeb2Server -from Nagstamon.Servers.IcingaDBWebServer import IcingaDBWebServer +from Nagstamon.Servers.IcingaDBWeb import IcingaDBWebServer from Nagstamon.Servers.Multisite import MultisiteServer from Nagstamon.Servers.op5Monitor import Op5MonitorServer from Nagstamon.Servers.Opsview import OpsviewServer From c133da64d5a30a0f549a13521cc6701591bf88db Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Wed, 28 Sep 2022 16:28:21 +0200 Subject: [PATCH 354/884] fix IcingaWebDB --- Nagstamon/Servers/__init__.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/Nagstamon/Servers/__init__.py b/Nagstamon/Servers/__init__.py index c56bbe3a4..f157bc6c0 100644 --- a/Nagstamon/Servers/__init__.py +++ b/Nagstamon/Servers/__init__.py @@ -229,10 +229,26 @@ def create_server(server=None): # moved registration process here because of circular dependencies -servers_list = [CentreonServer, IcingaServer, IcingaWeb2Server, MultisiteServer, NagiosServer, - Op5MonitorServer, OpsviewServer, ThrukServer, ZabbixServer, SensuServer, - SensuGoServer, LivestatusServer, ZenossServer, Monitos3Server, Monitos4xServer, - SnagViewServer, PrometheusServer, AlertmanagerServer, ZabbixProblemBasedServer] +servers_list = [AlertmanagerServer, + CentreonServer, + IcingaServer, + IcingaDBWebServer, + IcingaWeb2Server, + LivestatusServer, + Monitos3Server, + Monitos4xServer, + MultisiteServer, + NagiosServer, + Op5MonitorServer, + OpsviewServer, + PrometheusServer, + SensuGoServer, + SensuServer, + SnagViewServer, + ThrukServer, + ZabbixProblemBasedServer, + ZabbixServer, + ZenossServer] # we use these servers conditionally if modules are available only if icinga2api_is_available is True: servers_list.append(Icinga2APIServer) From 2e288a5fb13ef2c098be84256d16f0c2eeea7566 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Wed, 28 Sep 2022 16:56:27 +0200 Subject: [PATCH 355/884] QVariant instead of 'QVariant' --- Nagstamon/QUI/__init__.py | 7 ++----- Nagstamon/QUI/qt.py | 3 +++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index c29124bb7..b672fb403 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -145,9 +145,6 @@ # filled by create_brushes() QBRUSHES = {0: {}, 1: {}} -# dummy QVariant as empty return value for model data() -DUMMY_QVARIANT = 'QVariant' - # headers for tablewidgets HEADERS = OrderedDict([('host', {'header': 'Host', 'column': 0}), @@ -3124,7 +3121,7 @@ def data(self, index, role): elif index.column() == 3: return ICONS_FONT else: - return DUMMY_QVARIANT + return QVariant # provide icons via Qt.UserRole elif role == Qt.ItemDataRole.UserRole: # depending on host or service column return host or service icon list @@ -3138,7 +3135,7 @@ def data(self, index, role): self.data_array[index.row()][2], self.data_array[index.row()][8]) else: - return DUMMY_QVARIANT + return QVariant class TreeView(QTreeView): diff --git a/Nagstamon/QUI/qt.py b/Nagstamon/QUI/qt.py index 621631557..ac486b084 100644 --- a/Nagstamon/QUI/qt.py +++ b/Nagstamon/QUI/qt.py @@ -58,6 +58,7 @@ QThread, \ QTimer, \ QUrl, \ + QVariant, \ QXmlStreamReader from PyQt5.QtGui import QBrush, \ QColor, \ @@ -181,6 +182,7 @@ def get_sort_order_value(sort_order): QThread, \ QTimer, \ QUrl, \ + QVariant, \ QXmlStreamReader from PyQt6.QtGui import QAction, \ QBrush, \ @@ -297,6 +299,7 @@ def get_sort_order_value(sort_order): # QThread, \ # QTimer, \ # QUrl, \ +# QVariant, \ # QXmlStreamReader # from PySide6.QtGui import QAction, \ # QBrush, \ From 27e766ed90e6d88533f24a3f8d31302aeb5f5523 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 29 Sep 2022 08:49:19 +0200 Subject: [PATCH 356/884] 3.9-20220929 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index dd28dae51..228a5424f 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20220928' + VERSION = '3.9-20220929' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 3e682011b..a40bdb890 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.9-20220928) unstable; urgency=low +nagstamon (3.9-20220929) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Wed, 17 Aug 2022 08:00:00 +0100 From fa521fc865b1c38414cd40e804d7ac0156eeb3cc Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 29 Sep 2022 21:19:49 +0200 Subject: [PATCH 357/884] hide disabled servers --- Nagstamon/QUI/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index b672fb403..1f45e0c52 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -559,7 +559,7 @@ def initialize(self): self.action_servers = dict() # connect every server to its monitoring webpage - for server in servers: + for server in sorted([x.name for x in conf.servers.values() if x.enabled], key=str.lower): self.action_servers[server] = QAction(server, self) self.action_servers[server].triggered.connect(servers[server].open_monitor_webpage) self.addAction(self.action_servers[server]) @@ -743,7 +743,7 @@ def fill(self): """ self.clear() self.addItem('Go to monitor...') - self.addItems(sorted(conf.servers.keys(), key=str.lower)) + self.addItems(sorted([x.name for x in conf.servers.values() if x.enabled], key=str.lower)) @Slot() def response(self): From 67f4380e274e833611861c7e82406422d9511eb6 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 29 Sep 2022 21:26:08 +0200 Subject: [PATCH 358/884] fedora 37 --- .github/workflows/build-release-latest.yml | 17 +++++++++++++-- .github/workflows/build-release-stable.yml | 11 +++++----- build/docker/Dockerfile-fedora-37 | 25 ++++++++++++++++++++++ 3 files changed, 46 insertions(+), 7 deletions(-) create mode 100644 build/docker/Dockerfile-fedora-37 diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 378b8acc2..0af7647a8 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -103,6 +103,19 @@ jobs: retention-days: 1 if-no-files-found: error + fedora-37: + runs-on: ubuntu-latest + needs: test + steps: + - uses: actions/checkout@v2 + - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + - uses: actions/upload-artifact@v2 + with: + path: build/*.rpm + retention-days: 1 + if-no-files-found: error + macos: runs-on: macos-10.15 needs: test @@ -170,7 +183,7 @@ jobs: repo-fedora: runs-on: ubuntu-latest - needs: [fedora-34, fedora-35, fedora-36] + needs: [fedora-34, fedora-35, fedora-36, fedora-37] steps: # get binaries created by other jobs - uses: actions/download-artifact@v2 @@ -192,7 +205,7 @@ jobs: github-release: runs-on: ubuntu-latest - needs: [debian, fedora-34, fedora-35, fedora-36, macos, windows-32, windows-64] + needs: [debian, fedora-34, fedora-35, fedora-36, fedora-37, macos, windows-32, windows-64] steps: - uses: actions/download-artifact@v2 - run: cd artifact && md5sum *agstamon* > md5sums.txt diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index 0859b0887..33a5653e2 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -48,7 +48,7 @@ jobs: path: build/*.deb retention-days: 1 - fedora-33: + fedora-34: runs-on: ubuntu-latest needs: test steps: @@ -60,7 +60,7 @@ jobs: path: build/*.rpm retention-days: 1 - fedora-34: + fedora-35: runs-on: ubuntu-latest needs: test steps: @@ -72,7 +72,7 @@ jobs: path: build/*.rpm retention-days: 1 - fedora-35: + fedora-36: runs-on: ubuntu-latest needs: test steps: @@ -83,8 +83,9 @@ jobs: with: path: build/*.rpm retention-days: 1 + if-no-files-found: error - fedora-36: + fedora-37: runs-on: ubuntu-latest needs: test steps: @@ -153,7 +154,7 @@ jobs: repo-fedora: runs-on: ubuntu-latest - needs: [debian, fedora-33, fedora-34, fedora-35, fedora-36, macos, windows-32, windows-64] + needs: [fedora-34, fedora-35, fedora-36, fedora-37] steps: # get binaries created by other jobs - uses: actions/download-artifact@v2 diff --git a/build/docker/Dockerfile-fedora-37 b/build/docker/Dockerfile-fedora-37 new file mode 100644 index 000000000..fd595a69a --- /dev/null +++ b/build/docker/Dockerfile-fedora-37 @@ -0,0 +1,25 @@ +FROM fedora:37 +LABEL maintainer=henri@nagstamon.de + +RUN dnf -y install desktop-file-utils \ + git \ + python3 \ + python3-beautifulsoup4 \ + python3-crypto \ + python3-cryptography \ + python3-dateutil \ + python3-devel \ + python3-keyring \ + python3-lxml \ + python3-psutil \ + python3-qt5 \ + python3-qt5-devel \ + python3-requests \ + python3-requests-kerberos \ + python3-SecretStorage \ + qt5-qtsvg \ + qt5-qtmultimedia \ + rpm-build + +CMD cd /nagstamon/build && \ + /usr/bin/python3 build.py From 9ed79572cc73c8072d15b7598e0868cee119ad3f Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 29 Sep 2022 21:54:52 +0200 Subject: [PATCH 359/884] removed nagstacli --- Nagstamon/Config.py | 33 ++--------- nagstacli.py | 136 -------------------------------------------- nagstamon.py | 7 --- 3 files changed, 5 insertions(+), 171 deletions(-) delete mode 100755 nagstacli.py diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 228a5424f..9f2282c43 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -320,36 +320,13 @@ def __init__(self): # would not find a config file self.unconfigured = True - # adding cli args variable - self.cli_args = {} - - # Parse the command line - parser = argparse.ArgumentParser(description='Nagstamon for your CLI') - - # separate NagstaCLI from - if len(sys.argv) > 2 or (len(sys.argv) > 1 and sys.argv[1] in ['--help', '-h']): - parser.add_argument('--servername', type=str, help="name of the (Nagios)server. Look in nagstamon config") - parser.add_argument('--hostname', type=str) - parser.add_argument('--comment', type=str, default="") - parser.add_argument('--service', type=str, default="", - help="specify service, if needed. Mostly the whole host goes to downstate") - parser.add_argument('--fixed', type=str, choices=['y', 'n'], default="y", - help="fixed=n means wait for service/host to go down, then start the downtime") - parser.add_argument('--start_time', type=str, help="start time for downtime") - parser.add_argument('--hours', type=int, help="amount of hours for downtime") - parser.add_argument('--minutes', type=int, help="amount of minutes for downtime") - parser.add_argument('--config', type=str, help="Path for configuration folder") - parser.add_argument('--output', type=str, choices=['y', 'n'], default="y", - help="lists given parameter (for debugging)") - else: - parser.add_argument('config', nargs='?', help='Path for configuration folder') - - self.cli_args, unknown = parser.parse_known_args() + # when more tha a config directory was given something is wrong + if len(sys.argv) > 2: + sys.exit('Currently Nagstamon supports only 1 config directory.') # try to use a given config file - there must be one given - if len(sys.argv) < 3 and self.cli_args.config: - # allow to give a config file - self.configdir = self.cli_args.config + elif len(sys.argv) == 2: + self.configdir = sys.argv[1] # otherwise if there exits a configdir in current working directory it should be used elif os.path.exists(os.getcwd() + os.sep + 'nagstamon.config'): diff --git a/nagstacli.py b/nagstacli.py deleted file mode 100755 index e98013fcd..000000000 --- a/nagstacli.py +++ /dev/null @@ -1,136 +0,0 @@ -#!/usr/bin/env python3 -# encoding: utf-8 - -# Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. Maik Lüdeke <m.luedeke@ifw-dresden.de> -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - -import sys -import socket -# import datetime # never used -import re - -# fix/patch for https://bugs.launchpad.net/ubuntu/+source/nagstamon/+bug/732544 -socket.setdefaulttimeout(30) -# checks if there is a current value for the variable, if not the default value will be returned - - -def checkDefaultValue(value, default): - if value is None: - return default - else: - return value - - -# extracts the time from the start_time and adds hours and minutes -def createEndTime(info_dict): - - # extracts the time from arg 'start_time' - # then adds the hours/minutes to the start_date and reassambles the string - regex = re.compile('(.*)\s(\d{1,2})([\:\.])(\d{1,2})(.*)') - - time_string = re.search(regex, info_dict['start_time']) - - start_hour = int(time_string.group(2)) - separator = (time_string.group(3)) - start_minute = int(time_string.group(4)) - # if the time has a format like "12:12:53.122" the seconds and milliseconds were - # extracted and then attached as string again - if (len(time_string.group()) == 6): - attached_timestring = int(time_string.group(5)) - else: - attached_timestring = "" - # calculate the hour/minutes for downtime. convert 120 minutes to 2 h or 90 minutes to 1 h 30 min - hour, minute = divmod(info_dict['minutes'] + int(start_minute) + - (60 * (start_hour + info_dict['hours'])), 60) - - if hour > 23: - print("it is (at the moment) not possible to end the timer after 0:00 o`clock") - sys.exit(0) - - info_dict['end_time'] = (time_string.group(1) + " " + - str(hour) + separator + '{0:02d}'.format(minute) + attached_timestring) - - return info_dict - - -def executeCli(): - # from Nagstamon.Config import (conf, - # debug_queue) - from Nagstamon.Config import conf - - from Nagstamon.Servers import (create_server) - - # Initialize global configuration - - from Nagstamon.Objects import (GenericHost) - - # creates new server object from given servername - server = create_server(conf.servers[conf.cli_args.servername]) - - # gets the current default start/endtime from the server (default current time + 2h) - start_end_time = server.get_start_end(conf.cli_args.hostname) - default_start_time = start_end_time[0] - default_end_time = start_end_time[1] - # gets the default downtime duration from the nagstamon config - # default_downtime = conf.defaults_downtime_duration_minutes # never used - - server.GetStatus() - - server.hosts[conf.cli_args.hostname] = GenericHost() - server.hosts[conf.cli_args.hostname].name = conf.cli_args.hostname - server.hosts[conf.cli_args.hostname].server = server.name - server.hosts[conf.cli_args.hostname].site = "monitor" - - fixedType = {"y": True, "n": False} - - info_dict = dict() - info_dict['host'] = conf.cli_args.hostname - info_dict['service'] = conf.cli_args.service - info_dict['author'] = server.username - info_dict['comment'] = conf.cli_args.comment - info_dict['fixed'] = fixedType[conf.cli_args.fixed] - - info_dict['start_time'] = checkDefaultValue(conf.cli_args.start_time, default_start_time) - info_dict['end_time'] = default_end_time - - info_dict['hours'] = checkDefaultValue(conf.cli_args.hours, 0) - info_dict['minutes'] = checkDefaultValue(conf.cli_args.minutes, conf.defaults_downtime_duration_minutes) - info_dict['view_name'] = "host" - - info_dict = createEndTime(info_dict) - # creates output,v which parameter were processed - if conf.cli_args.output == 'y': - print('trying to downtime host "' + info_dict['host'] + '", with the following parameters:') - if info_dict['service'] != "": - print('service: ', info_dict['service']) - if info_dict['comment'] is not None: - print('comment: ', info_dict['comment']) - print('fixed: ', info_dict['fixed']) - print('start time: ', info_dict['start_time']) - print('end time: ', info_dict['end_time']) - - server.set_downtime(info_dict) - -try: - if __name__ == '__main__': - # ##debug_queue = list() - executeCli() - - -except Exception as err: - import traceback - traceback.print_exc(file=sys.stdout) diff --git a/nagstamon.py b/nagstamon.py index e2f308917..6d6061aa7 100755 --- a/nagstamon.py +++ b/nagstamon.py @@ -30,13 +30,6 @@ from Nagstamon.Helpers import lock_config_folder - # if there are more args, than the config folder, nagstaCLI is been executed - if len(sys.argv) > 2: - import nagstacli # fix crash if more than 2 args are passed - mprenditore - - nagstacli.executeCli() - sys.exit(1) - # Acquire the lock if not lock_config_folder(conf.configdir): print('An instance is already running this config ({})'.format(conf.configdir)) From d82ad39001540611e02244b480d69c5a507ef6f9 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri@nagstamon.de> Date: Thu, 29 Sep 2022 22:47:57 +0200 Subject: [PATCH 360/884] remove unused hide/show_items --- Nagstamon/QUI/__init__.py | 17 +---------------- README.md | 2 +- 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 1f45e0c52..92813be2b 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -257,22 +257,6 @@ def __init__(self, spacing=None, parent=None): self.setSpacing(spacing) self.setContentsMargins(0, 0, 0, 0) # no margin - def hide_items(self): - """ - cruise through all child widgets and hide them - self,count()-1 is needed because the last item is None - """ - for item in range(self.count() - 1): - self.itemAt(item).widget().hide() - - def show_items(self): - """ - cruise through all child widgets and show them - self,count()-1 is needed because the last item is None - """ - for item in range(self.count() - 1): - self.itemAt(item).widget().show() - class QIconWithFilename(QIcon): """ @@ -2916,6 +2900,7 @@ def get_real_height(self): height += self.button_monitor.sizeHint().height() + 2 return height + @Slot() def show_all(self): """ diff --git a/README.md b/README.md index d130038a3..57af22e09 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ It is inspired by Nagios Checker for Firefox – just without an open Firefox wi Nagstamon is released under the GPLv2 and free to use and modify. -Nagstamon is written in Python 3 and uses the Qt 5 GUI toolkit which makes it very portable. It has been tested successfully on latest Ubuntu, Debian, Windows, NetBSD, OpenBSD, FreeBSD and MacOS X. +Nagstamon is written in Python 3 and uses the Qt 5/6 GUI toolkit which makes it very portable. It has been tested successfully on latest Ubuntu, Debian, Windows, NetBSD, OpenBSD, FreeBSD and MacOS X. It works with GNOME, KDE, Windows and macOS desktops. Successfully tested monitors include: From ab6a49c61140e2c242a8a439caca1ef1f504d7bf Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 29 Sep 2022 23:14:48 +0200 Subject: [PATCH 361/884] avoid exit at build --- Nagstamon/Config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 9f2282c43..6f10443b6 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -322,7 +322,7 @@ def __init__(self): # when more tha a config directory was given something is wrong if len(sys.argv) > 2: - sys.exit('Currently Nagstamon supports only 1 config directory.') + print('Currently Nagstamon supports only 1 config directory.') # try to use a given config file - there must be one given elif len(sys.argv) == 2: From 1ae3d136d39813e1fc4a648d6f04ed30b5d25bc6 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri@nagstamon.de> Date: Thu, 29 Sep 2022 23:50:12 +0200 Subject: [PATCH 362/884] finally remove spaces --- Nagstamon/QUI/__init__.py | 63 ++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 92813be2b..812f131e7 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -1399,7 +1399,11 @@ def sort_ServerVBoxes(self): self.servers_scrollarea_widget.setLayout(self.servers_vbox) self.servers_scrollarea.setWidget(self.servers_scrollarea_widget) - del (vboxes_dict) + self.servers_scrollarea.contentsMargins().setTop(0) + self.servers_scrollarea.contentsMargins().setBottom(0) + + + del vboxes_dict def create_ServerVBoxes(self): # create vbox for each enabled server @@ -2815,26 +2819,30 @@ def __init__(self, server, parent=None): self.button_history = PushButton_BrowserURL(text='History', parent=parent, server=self.server, url_type='history') self.button_edit = Button('Edit', parent=parent) - ###self.button_edit.setStyleSheet('background: green;') + # .setAttribute(Qt.WidgetAttribute.WA_LayoutUsesWidgetRect) # use label instead of spacer to be clickable self.label_stretcher = ClosingLabel('', parent=parent) self.label_stretcher.setSizePolicy(QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.Expanding) - ###self.label_stretcher.setStyleSheet('background: green;') self.label_status = ServerStatusLabel(parent=parent) self.label_status.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Expanding) - ###self.label_status.setStyleSheet('background: green;') - self.button_authenticate = QPushButton('Authenticate', parent=parent) - ###self.button_authenticate.setStyleSheet('background: green;') - - ###self.button_authenticate.setStyleSheet('background: green;') self.button_fix_tls_error = QPushButton('Fix error', parent=parent) + # avoid useless spaces in macOS when server has nothing to show + # see https://bugreports.qt.io/browse/QTBUG-2699 + self.button_monitor.setAttribute(Qt.WidgetAttribute.WA_LayoutUsesWidgetRect) + self.button_history.setAttribute(Qt.WidgetAttribute.WA_LayoutUsesWidgetRect) + self.button_services.setAttribute(Qt.WidgetAttribute.WA_LayoutUsesWidgetRect) + self.button_hosts.setAttribute(Qt.WidgetAttribute.WA_LayoutUsesWidgetRect) + self.button_edit.setAttribute(Qt.WidgetAttribute.WA_LayoutUsesWidgetRect) + self.button_authenticate.setAttribute(Qt.WidgetAttribute.WA_LayoutUsesWidgetRect) + self.button_fix_tls_error.setAttribute(Qt.WidgetAttribute.WA_LayoutUsesWidgetRect) + self.button_monitor.clicked.connect(self.button_monitor.open_url) self.button_hosts.clicked.connect(self.button_hosts.open_url) self.button_services.clicked.connect(self.button_services.open_url) @@ -2859,7 +2867,7 @@ def __init__(self, server, parent=None): # when stored as simple lowercase keys sort_column = HEADERS_KEYS_COLUMNS[conf.default_sort_field] except Exception: - # when as legacy stored as presetation string + # when as legacy stored as presentation string sort_column = HEADERS_HEADERS_COLUMNS[conf.default_sort_field] # convert sort order to number as used in Qt.SortOrder @@ -2900,22 +2908,21 @@ def get_real_height(self): height += self.button_monitor.sizeHint().height() + 2 return height - @Slot() def show_all(self): """ show all items in server vbox except the table - not needed if empty """ - self.label.show() - self.button_monitor.show() + self.button_authenticate.hide() + self.button_edit.show() + self.button_fix_tls_error.hide() + self.button_history.show() self.button_hosts.show() + self.button_monitor.show() self.button_services.show() - self.button_history.show() - self.button_edit.show() + self.label.show() self.label_status.show() self.label_stretcher.show() - self.button_authenticate.hide() - self.button_fix_tls_error.hide() # special table treatment self.table.show() self.table.is_shown = True @@ -2925,16 +2932,16 @@ def show_only_header(self): """ show all items in server vbox except the table - not needed if empty or major connection problem """ - self.label.show() - self.button_monitor.show() + self.button_authenticate.hide() + self.button_edit.show() + self.button_history.show() self.button_hosts.show() + self.button_fix_tls_error.hide() + self.button_monitor.show() self.button_services.show() - self.button_history.show() - self.button_edit.show() + self.label.show() self.label_status.show() self.label_stretcher.show() - self.button_authenticate.hide() - self.button_fix_tls_error.hide() # special table treatment self.table.hide() self.table.is_shown = False @@ -2944,16 +2951,16 @@ def hide_all(self): """ hide all items in server vbox """ - self.label.hide() - self.button_monitor.hide() + self.button_authenticate.hide() + self.button_edit.hide() + self.button_fix_tls_error.hide() + self.button_history.hide() self.button_hosts.hide() + self.button_monitor.hide() self.button_services.hide() - self.button_history.hide() - self.button_edit.hide() + self.label.hide() self.label_status.hide() self.label_stretcher.hide() - self.button_authenticate.hide() - self.button_fix_tls_error.hide() # special table treatment self.table.hide() self.table.is_shown = False From 5a64adb2f82b7ccbdbed3c086d3f081cf897e8e0 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Fri, 30 Sep 2022 00:14:50 +0200 Subject: [PATCH 363/884] fix build --- Nagstamon/Config.py | 3 ++- build/debian/changelog | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 6f10443b6..3d6e7a355 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20220929' + VERSION = '3.9-20220930' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' @@ -323,6 +323,7 @@ def __init__(self): # when more tha a config directory was given something is wrong if len(sys.argv) > 2: print('Currently Nagstamon supports only 1 config directory.') + self.configdir = sys.argv[1] # try to use a given config file - there must be one given elif len(sys.argv) == 2: diff --git a/build/debian/changelog b/build/debian/changelog index a40bdb890..4216adbd8 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.9-20220929) unstable; urgency=low +nagstamon (3.9-20220930) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Wed, 17 Aug 2022 08:00:00 +0100 From 706b943b2d52d167e8dffdac0fbf61a84dc6b53d Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Fri, 30 Sep 2022 10:21:12 +0200 Subject: [PATCH 364/884] fix macos label_status size --- Nagstamon/QUI/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 812f131e7..f1cc0efb4 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -2827,7 +2827,7 @@ def __init__(self, server, parent=None): self.label_stretcher.setSizePolicy(QSizePolicy.Policy.MinimumExpanding, QSizePolicy.Policy.Expanding) self.label_status = ServerStatusLabel(parent=parent) - self.label_status.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Expanding) + self.label_status.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed) self.button_authenticate = QPushButton('Authenticate', parent=parent) From 5f08df9f02238dbeb04ae6fc2014cf2b93352774 Mon Sep 17 00:00:00 2001 From: Yannick Charton <tontonitch-pro@yahoo.fr> Date: Sat, 1 Oct 2022 11:37:00 +0200 Subject: [PATCH 365/884] Icingadbweb support url fix and host check adaptations --- Nagstamon/Servers/IcingaDBWeb.py | 67 ++++++++++++++++---------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index 84c9b5b9c..4b7de1386 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -78,7 +78,7 @@ def init_config(self): self.cgiurl_services = None self.cgiurl_hosts = None self.cgiurl_monitoring_health = None - + # https://github.com/HenriWahl/Nagstamon/issues/400 # The displayed name for host and service is the Icinga2 "internal" name and not the display_name from host/service configuration # This name is stored in host/service dict under key 'name' but is also used as dict key for dict containing all hosts/services @@ -110,7 +110,7 @@ def init_HTTP(self): form_inputs[form_input] = '' form_inputs['username'] = self.username form_inputs['password'] = self.password - + # fire up login button with all needed data self.FetchURL('{0}/authentication/login'.format(self.monitor_url), cgi_data=form_inputs) @@ -128,7 +128,7 @@ def _get_status(self): self.cgiurl_hosts = {'hard': self.monitor_cgi_url + '/icingadb/hosts?host.state.is_problem=y&host.state.state_type=hard&columns=host.state.last_update&format=json', \ 'soft': self.monitor_cgi_url + '/icingadb/hosts?host.state.is_problem=y&host.state.state_type=soft&columns=host.state.last_update&format=json'} # monitoring health - self.cgiurl_monitoring_health = self.monitor_cgi_url + '/icingaweb2/health?format=json' + self.cgiurl_monitoring_health = self.monitor_cgi_url + '/health?format=json' # new_hosts dictionary self.new_hosts = dict() @@ -136,16 +136,16 @@ def _get_status(self): # hosts - mostly the down ones # now using JSON output from Icinga try: - for status_type in 'hard', 'soft': + for status_type in 'hard', 'soft': # first attempt - result = self.FetchURL(self.cgiurl_hosts[status_type], giveback='raw') + result = self.FetchURL(self.cgiurl_hosts[status_type], giveback='raw') # authentication errors get a status code 200 too back because its # HTML works fine :-( if result.status_code < 400 and\ result.result.startswith('<'): # in case of auth error reset HTTP session and try again self.reset_HTTP() - result = self.FetchURL(self.cgiurl_hosts[status_type], giveback='raw') + result = self.FetchURL(self.cgiurl_hosts[status_type], giveback='raw') # if it does not work again tell GUI there is a problem if result.status_code < 400 and\ result.result.startswith('<'): @@ -153,7 +153,7 @@ def _get_status(self): return Result(result=result.result, error='Authentication error', status_code=result.status_code) - + # purify JSON result of unnecessary control sequence \n jsonraw, error, status_code = copy.deepcopy(result.result.replace('\n', '')),\ copy.deepcopy(result.error),\ @@ -193,47 +193,48 @@ def _get_status(self): # host if self.use_display_name_host == False: # according to http://sourceforge.net/p/nagstamon/bugs/83/ it might - # better be host_name instead of host_display_name - # legacy Icinga adjustments - if 'host_name' in h: host_name = h['host_name'] - elif 'host' in h: host_name = h['host'] + # better be name instead of display_name + host_name = h['name'] else: # https://github.com/HenriWahl/Nagstamon/issues/46 on the other hand has # problems with that so here we go with extra display_name option - host_name = h['host_display_name'] + host_name = h['display_name'] # host objects contain service objects if not host_name in self.new_hosts: self.new_hosts[host_name] = GenericHost() self.new_hosts[host_name].name = host_name self.new_hosts[host_name].server = self.name - self.new_hosts[host_name].status = self.STATES_MAPPING['hosts'][int(h['host_state'])] - self.new_hosts[host_name].last_check = datetime.datetime.fromtimestamp(int(h['host_last_check'])) - self.new_hosts[host_name].attempt = h['host_attempt'] - self.new_hosts[host_name].status_information = BeautifulSoup(h['host_output'].replace('\n', ' ').strip(), 'html.parser').text - self.new_hosts[host_name].passiveonly = not(int(h['host_active_checks_enabled'])) - self.new_hosts[host_name].notifications_disabled = not(int(h['host_notifications_enabled'])) - self.new_hosts[host_name].flapping = bool(int(h['host_is_flapping'])) - self.new_hosts[host_name].acknowledged = bool(int(h['host_acknowledged'])) - self.new_hosts[host_name].scheduled_downtime = bool(int(h['host_in_downtime'])) self.new_hosts[host_name].status_type = status_type - + if (status_type == 'hard'): + self.new_hosts[host_name].status = self.STATES_MAPPING['hosts'][int(h['state']['hard_state'])] + else: + self.new_hosts[host_name].status = self.STATES_MAPPING['hosts'][int(h['state']['soft_state'])] + + self.new_hosts[host_name].last_check = datetime.datetime.fromtimestamp(int(h['state']['last_update'])) + self.new_hosts[host_name].attempt = "{}/{}".format(h['state']['check_attempt'],h['state']['max_check_attempts']) + self.new_hosts[host_name].status_information = BeautifulSoup(h['state']['output'].replace('\n', ' ').strip(), 'html.parser').text + self.new_hosts[host_name].passiveonly = not(int(h['active_checks_enabled'])) + self.new_hosts[host_name].notifications_disabled = not(int(h['notifications_enabled'])) + self.new_hosts[host_name].flapping = bool(int(h['state']['is_flapping'] or 0)) + self.new_hosts[host_name].acknowledged = bool(int(h['state']['is_acknowledged'] or 0)) + self.new_hosts[host_name].scheduled_downtime = bool(int(h['state']['in_downtime'] or 0)) + # extra Icinga properties to solve https://github.com/HenriWahl/Nagstamon/issues/192 # acknowledge needs host_description and no display name - self.new_hosts[host_name].real_name = h['host_name'] + self.new_hosts[host_name].real_name = h['name'] # Icinga only updates the attempts for soft states. When hard state is reached, a flag is set and # attemt is set to 1/x. if (status_type == 'hard'): try: - maxAttempts = h['host_attempt'].split('/')[1] - self.new_hosts[host_name].attempt = "{0}/{0}".format(maxAttempts) + self.new_hosts[host_name].attempt = "{0}/{0}".format(h['max_check_attempts']) except Exception: self.new_hosts[host_name].attempt = "HARD" - + # extra duration needed for calculation - if h['host_last_state_change'] is not None: - last_change = h['host_last_state_change'] if h['host_last_state_change'] is not None else 0 + if h['state']['last_state_change'] is not None: + last_change = h['state']['last_state_change'] if h['state']['last_state_change'] is not None else 0 duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(last_change)) self.new_hosts[host_name].duration = strfdelta(duration,'{days}d {hours}h {minutes}m {seconds}s') else: @@ -261,7 +262,7 @@ def _get_status(self): return Result(result=jsonraw, error=error, status_code=status_code) - + # check if any error occured self.check_for_error(jsonraw, error, status_code) @@ -300,7 +301,7 @@ def _get_status(self): self.new_hosts[host_name].services[service_name].name = service_name self.new_hosts[host_name].services[service_name].server = self.name self.new_hosts[host_name].services[service_name].status = self.STATES_MAPPING['services'][int(s['service_state'])] - self.new_hosts[host_name].services[service_name].last_check = datetime.datetime.fromtimestamp(int(s['service_last_check'])) + self.new_hosts[host_name].services[service_name].last_check = datetime.datetime.fromtimestamp(int(s['service_last_check'])) self.new_hosts[host_name].services[service_name].attempt = s['service_attempt'] self.new_hosts[host_name].services[service_name].status_information = BeautifulSoup(s['service_output'].replace('\n', ' ').strip(), 'html.parser').text self.new_hosts[host_name].services[service_name].passiveonly = not(int(s['service_active_checks_enabled'])) @@ -313,7 +314,7 @@ def _get_status(self): if self.new_hosts[host_name].services[service_name].unreachable: self.new_hosts[host_name].services[service_name].status_information += " (SERVICE UNREACHABLE)" - + # extra Icinga properties to solve https://github.com/HenriWahl/Nagstamon/issues/192 # acknowledge needs service_description and no display name self.new_hosts[host_name].services[service_name].real_name = s['service_description'] @@ -326,7 +327,7 @@ def _get_status(self): self.new_hosts[host_name].services[service_name].attempt = "{0}/{0}".format(maxAttempts) except Exception: self.new_hosts[host_name].services[service_name].attempt = "HARD" - + # extra duration needed for calculation if s['service_last_state_change'] is not None: last_change = s['service_last_state_change'] if s['service_last_state_change'] is not None else 0 @@ -570,7 +571,7 @@ def open_monitor(self, host, service=''): open monitor from tablewidget context menu ''' # only type is important so do not care of service '' in case of host monitor - if service == '': + if service == '': url = '{0}/icingadb/hosts?host.state.is_problem=y&sort=host.state.severity#!{1}/icingadb/hosts/{2}'.format(self.monitor_url, (urllib.parse.urlparse(self.monitor_url).path), urllib.parse.urlencode( From 1e44551dadb11eea95140c8c474cf4daf36b57c6 Mon Sep 17 00:00:00 2001 From: Yannick Charton <tontonitch-pro@yahoo.fr> Date: Sat, 1 Oct 2022 12:30:30 +0200 Subject: [PATCH 366/884] IcingaDBWeb support - service check adaptation and some fixes --- Nagstamon/Servers/IcingaDBWeb.py | 47 ++++++++++++++++---------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index 4b7de1386..706dcb21a 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -212,7 +212,7 @@ def _get_status(self): self.new_hosts[host_name].status = self.STATES_MAPPING['hosts'][int(h['state']['soft_state'])] self.new_hosts[host_name].last_check = datetime.datetime.fromtimestamp(int(h['state']['last_update'])) - self.new_hosts[host_name].attempt = "{}/{}".format(h['state']['check_attempt'],h['state']['max_check_attempts']) + self.new_hosts[host_name].attempt = "{}/{}".format(h['state']['check_attempt'],h['max_check_attempts']) self.new_hosts[host_name].status_information = BeautifulSoup(h['state']['output'].replace('\n', ' ').strip(), 'html.parser').text self.new_hosts[host_name].passiveonly = not(int(h['active_checks_enabled'])) self.new_hosts[host_name].notifications_disabled = not(int(h['notifications_enabled'])) @@ -274,14 +274,12 @@ def _get_status(self): if self.use_display_name_host == False: # according to http://sourceforge.net/p/nagstamon/bugs/83/ it might - # better be host_name instead of host_display_name - # legacy Icinga adjustments - if 'host_name' in s: host_name = s['host_name'] - elif 'host' in s: host_name = s['host'] + # better be name instead of display_name + host_name = s['host']['name'] else: # https://github.com/HenriWahl/Nagstamon/issues/46 on the other hand has # problems with that so here we go with extra display_name option - host_name = s['host_display_name'] + host_name = s['host']['display_name'] # host objects contain service objects if not host_name in self.new_hosts: @@ -290,9 +288,9 @@ def _get_status(self): self.new_hosts[host_name].status = 'UP' # extra Icinga properties to solve https://github.com/HenriWahl/Nagstamon/issues/192 # acknowledge needs host_description and no display name - self.new_hosts[host_name].real_name = s['host_name'] + self.new_hosts[host_name].real_name = s['host']['name'] - service_name = s['service_display_name'] + service_name = s['display_name'] # if a service does not exist create its object if not service_name in self.new_hosts[host_name].services: @@ -300,37 +298,40 @@ def _get_status(self): self.new_hosts[host_name].services[service_name].host = host_name self.new_hosts[host_name].services[service_name].name = service_name self.new_hosts[host_name].services[service_name].server = self.name - self.new_hosts[host_name].services[service_name].status = self.STATES_MAPPING['services'][int(s['service_state'])] - self.new_hosts[host_name].services[service_name].last_check = datetime.datetime.fromtimestamp(int(s['service_last_check'])) - self.new_hosts[host_name].services[service_name].attempt = s['service_attempt'] - self.new_hosts[host_name].services[service_name].status_information = BeautifulSoup(s['service_output'].replace('\n', ' ').strip(), 'html.parser').text - self.new_hosts[host_name].services[service_name].passiveonly = not(int(s['service_active_checks_enabled'])) - self.new_hosts[host_name].services[service_name].notifications_disabled = not(int(s['service_notifications_enabled'])) - self.new_hosts[host_name].services[service_name].flapping = bool(int(s['service_is_flapping'])) - self.new_hosts[host_name].services[service_name].acknowledged = bool(int(s['service_acknowledged'])) - self.new_hosts[host_name].services[service_name].scheduled_downtime = bool(int(s['service_in_downtime'])) self.new_hosts[host_name].services[service_name].status_type = status_type - self.new_hosts[host_name].services[service_name].unreachable = s['service_is_reachable'] == '0' + if (status_type == 'hard'): + self.new_hosts[host_name].services[service_name].status = self.STATES_MAPPING['services'][int(s['state']['hard_state'])] + else: + self.new_hosts[host_name].services[service_name].status = self.STATES_MAPPING['services'][int(s['state']['soft_state'])] + + self.new_hosts[host_name].services[service_name].last_check = datetime.datetime.fromtimestamp(int(s['state']['last_update'])) + self.new_hosts[host_name].services[service_name].attempt = "{}/{}".format(s['state']['check_attempt'],s['max_check_attempts']) + self.new_hosts[host_name].services[service_name].status_information = BeautifulSoup(s['state']['output'].replace('\n', ' ').strip(), 'html.parser').text + self.new_hosts[host_name].services[service_name].passiveonly = not(int(s['active_checks_enabled'])) + self.new_hosts[host_name].services[service_name].notifications_disabled = not(int(s['notifications_enabled'])) + self.new_hosts[host_name].services[service_name].flapping = bool(int(s['state']['is_flapping'] or 0)) + self.new_hosts[host_name].services[service_name].acknowledged = bool(int(s['state']['is_acknowledged'] or 0)) + self.new_hosts[host_name].services[service_name].scheduled_downtime = bool(int(s['state']['in_downtime'] or 0)) + self.new_hosts[host_name].services[service_name].unreachable = bool(int(s['state']['is_reachable'] or 0)) if self.new_hosts[host_name].services[service_name].unreachable: self.new_hosts[host_name].services[service_name].status_information += " (SERVICE UNREACHABLE)" # extra Icinga properties to solve https://github.com/HenriWahl/Nagstamon/issues/192 # acknowledge needs service_description and no display name - self.new_hosts[host_name].services[service_name].real_name = s['service_description'] + self.new_hosts[host_name].services[service_name].real_name = s['name'] # Icinga only updates the attempts for soft states. When hard state is reached, a flag is set and # attemt is set to 1/x. if (status_type == 'hard'): try: - maxAttempts = s['service_attempt'].split('/')[1] - self.new_hosts[host_name].services[service_name].attempt = "{0}/{0}".format(maxAttempts) + self.new_hosts[host_name].services[service_name].attempt = "{0}/{0}".format(s['max_check_attempts']) except Exception: self.new_hosts[host_name].services[service_name].attempt = "HARD" # extra duration needed for calculation - if s['service_last_state_change'] is not None: - last_change = s['service_last_state_change'] if s['service_last_state_change'] is not None else 0 + if s['state']['last_state_change'] is not None: + last_change = s['state']['last_state_change'] if s['state']['last_state_change'] is not None else 0 duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(last_change)) self.new_hosts[host_name].services[service_name].duration = strfdelta(duration, '{days}d {hours}h {minutes}m {seconds}s') else: From 4f10f0ce02855a6cfb8e3d155b44d15418c256d6 Mon Sep 17 00:00:00 2001 From: Yannick Charton <tontonitch-pro@yahoo.fr> Date: Sat, 1 Oct 2022 12:35:27 +0200 Subject: [PATCH 367/884] workflow updates for testing only --- .github/workflows/build-release-latest.yml | 22 --- .github/workflows/build-release-stable.yml | 187 --------------------- 2 files changed, 209 deletions(-) delete mode 100644 .github/workflows/build-release-stable.yml diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 0af7647a8..70ce9ae36 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -181,28 +181,6 @@ jobs: retention-days: 1 if-no-files-found: error - repo-fedora: - runs-on: ubuntu-latest - needs: [fedora-34, fedora-35, fedora-36, fedora-37] - steps: - # get binaries created by other jobs - - uses: actions/download-artifact@v2 - # organize SSH deploy key for nagstamon-repo - - run: mkdir ~/.ssh - - run: echo "${{ secrets.NAGSTAMON_REPO_KEY_WEB }}" > ~/.ssh/id_ed25519 - - run: chmod -R go-rwx ~/.ssh - # get and prepare nagstamon-jekyll - - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - - run: rm -rf ${{ env.repo_dir }}/fedora/latest - - run: mkdir -p ${{ env.repo_dir }}/fedora/latest - # copy *.rpm files into nagstamon-jekyll - - run: cp -r artifact/*.rpm ${{ env.repo_dir }}/fedora/latest - # create rpm repo via Fedora container - - run: docker run --rm -v $PWD/${{ env.repo_dir }}/fedora/latest:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo" - # commit and push new binaries to nagstamon-repo - - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - - run: cd ${{ env.repo_dir }} && git add . && git commit -am "new latest repo" && git push - github-release: runs-on: ubuntu-latest needs: [debian, fedora-34, fedora-35, fedora-36, fedora-37, macos, windows-32, windows-64] diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml deleted file mode 100644 index 33a5653e2..000000000 --- a/.github/workflows/build-release-stable.yml +++ /dev/null @@ -1,187 +0,0 @@ -name: build-release-stable -on: - push: - tags: 'v*' - -env: - python_win_version: 3.9.12 - repo_dir: nagstamon-jekyll/docs/repo - -jobs: - test: - runs-on: ubuntu-latest - strategy: - matrix: - python-version: [3.7, 3.9] - - steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - sudo apt-get install libkrb5-dev - python -m pip install --upgrade pip - pip install pytest pylint #flake8 - if [ -f build/requirements/linux.txt ]; then pip install -r build/requirements/linux.txt; fi - # - name: Lint with flake8 - # run: | - # # stop the build if there are Python syntax errors or undefined names - # flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - # # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - # flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - - name: Test with unittest - run: | - python -m unittest tests/test_*.py - - debian: - runs-on: ubuntu-latest - needs: test - steps: - - uses: actions/checkout@v2 - - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon - - uses: actions/upload-artifact@v2 - with: - path: build/*.deb - retention-days: 1 - - fedora-34: - runs-on: ubuntu-latest - needs: test - steps: - - uses: actions/checkout@v2 - - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon - - uses: actions/upload-artifact@v2 - with: - path: build/*.rpm - retention-days: 1 - - fedora-35: - runs-on: ubuntu-latest - needs: test - steps: - - uses: actions/checkout@v2 - - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon - - uses: actions/upload-artifact@v2 - with: - path: build/*.rpm - retention-days: 1 - - fedora-36: - runs-on: ubuntu-latest - needs: test - steps: - - uses: actions/checkout@v2 - - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon - - uses: actions/upload-artifact@v2 - with: - path: build/*.rpm - retention-days: 1 - if-no-files-found: error - - fedora-37: - runs-on: ubuntu-latest - needs: test - steps: - - uses: actions/checkout@v2 - - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon - - uses: actions/upload-artifact@v2 - with: - path: build/*.rpm - retention-days: 1 - if-no-files-found: error - - macos: - runs-on: macos-10.15 - needs: test - steps: - - uses: actions/checkout@v2 - - run: pip3 install --no-warn-script-location -r build/requirements/macos.txt - - run: cd ${{ github.workspace }}/build; python3 build.py - env: - PYTHONPATH: ${{ github.workspace }} - - uses: actions/upload-artifact@v2 - with: - path: build/*.dmg - retention-days: 1 - - windows-32: - runs-on: windows-latest - needs: test - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 - with: - python-version: ${{ env.python_win_version }} - architecture: x86 - - run: python -m pip install --no-warn-script-location -r build/requirements/windows.txt - - run: cd ${{ github.workspace }}/build; python build.py - env: - PYTHONPATH: ${{ github.workspace }} - - uses: actions/upload-artifact@v2 - with: - path: | - build/dist/*.zip - build/dist/*.exe - retention-days: 1 - - windows-64: - runs-on: windows-latest - needs: test - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 - with: - python-version: ${{ env.python_win_version }} - architecture: x64 - - run: python -m pip install --no-warn-script-location -r build/requirements/windows.txt - - run: cd ${{ github.workspace }}/build; python build.py - env: - PYTHONPATH: ${{ github.workspace }} - - uses: actions/upload-artifact@v2 - with: - path: | - build/dist/*.zip - build/dist/*.exe - retention-days: 1 - - repo-fedora: - runs-on: ubuntu-latest - needs: [fedora-34, fedora-35, fedora-36, fedora-37] - steps: - # get binaries created by other jobs - - uses: actions/download-artifact@v2 - # organize SSH deploy key for nagstamon-repo - - run: mkdir ~/.ssh - - run: echo "${{ secrets.NAGSTAMON_REPO_KEY_WEB }}" > ~/.ssh/id_ed25519 - - run: chmod -R go-rwx ~/.ssh - # get and prepare nagstamon-repo - - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - - run: rm -rf ${{ env.repo_dir }}/fedora/?? - # copy *.rpm files into nagstamon-jekyll and create rpm repo via Fedora container - - run: for noarch_rpm in artifact/*.noarch.rpm; do version=$(echo $noarch_rpm | python3 -c "file=input(); print(file.split('fedora')[1].split('-')[0])"); mkdir -p mkdir -p ${{ env.repo_dir }}/fedora/$version; cp -r artifact/*.fedora$version-*.rpm ${{ env.repo_dir }}/fedora/$version; docker run --rm -v $PWD/${{ env.repo_dir }}/fedora/$version:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo"; done - # commit and push new binaries to nagstamon-repo - - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - - run: cd ${{ env.repo_dir }} && git add . && git commit -am "new stable repo" && git push - - upload-release: - runs-on: ubuntu-latest - needs: [debian, fedora-33, fedora-34, fedora-35, fedora-36, macos, windows-32, windows-64] - steps: - - uses: actions/download-artifact@v2 - - run: cd artifact && md5sum *agstamon* > md5sums.txt - - run: cd artifact && sha256sum *agstamon* > sha256sums.txt - - uses: marvinpinto/action-automatic-releases@latest - with: - repo_token: "${{ secrets.GITHUB_TOKEN }}" - prerelease: false - draft: true - files: | - artifact/* From 165910212bf23a9391135ac1e1d2a3b5ecbd7cbb Mon Sep 17 00:00:00 2001 From: Yannick Charton <tontonitch-pro@yahoo.fr> Date: Sat, 1 Oct 2022 12:56:44 +0200 Subject: [PATCH 368/884] IcingaDBWeb timestamp float convert first --- Nagstamon/Servers/IcingaDBWeb.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index 706dcb21a..72f6b5b7c 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -211,7 +211,7 @@ def _get_status(self): else: self.new_hosts[host_name].status = self.STATES_MAPPING['hosts'][int(h['state']['soft_state'])] - self.new_hosts[host_name].last_check = datetime.datetime.fromtimestamp(int(h['state']['last_update'])) + self.new_hosts[host_name].last_check = datetime.datetime.fromtimestamp(int(float(h['state']['last_update']))) self.new_hosts[host_name].attempt = "{}/{}".format(h['state']['check_attempt'],h['max_check_attempts']) self.new_hosts[host_name].status_information = BeautifulSoup(h['state']['output'].replace('\n', ' ').strip(), 'html.parser').text self.new_hosts[host_name].passiveonly = not(int(h['active_checks_enabled'])) @@ -235,7 +235,7 @@ def _get_status(self): # extra duration needed for calculation if h['state']['last_state_change'] is not None: last_change = h['state']['last_state_change'] if h['state']['last_state_change'] is not None else 0 - duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(last_change)) + duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(float(last_change))) self.new_hosts[host_name].duration = strfdelta(duration,'{days}d {hours}h {minutes}m {seconds}s') else: self.new_hosts[host_name].duration = 'n/a' @@ -304,7 +304,7 @@ def _get_status(self): else: self.new_hosts[host_name].services[service_name].status = self.STATES_MAPPING['services'][int(s['state']['soft_state'])] - self.new_hosts[host_name].services[service_name].last_check = datetime.datetime.fromtimestamp(int(s['state']['last_update'])) + self.new_hosts[host_name].services[service_name].last_check = datetime.datetime.fromtimestamp(int(float(s['state']['last_update']))) self.new_hosts[host_name].services[service_name].attempt = "{}/{}".format(s['state']['check_attempt'],s['max_check_attempts']) self.new_hosts[host_name].services[service_name].status_information = BeautifulSoup(s['state']['output'].replace('\n', ' ').strip(), 'html.parser').text self.new_hosts[host_name].services[service_name].passiveonly = not(int(s['active_checks_enabled'])) @@ -332,7 +332,7 @@ def _get_status(self): # extra duration needed for calculation if s['state']['last_state_change'] is not None: last_change = s['state']['last_state_change'] if s['state']['last_state_change'] is not None else 0 - duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(last_change)) + duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(float(last_change))) self.new_hosts[host_name].services[service_name].duration = strfdelta(duration, '{days}d {hours}h {minutes}m {seconds}s') else: self.new_hosts[host_name].services[service_name].duration = 'n/a' From dfa859f24b9f690cf2b38f975d642afd58e40fea Mon Sep 17 00:00:00 2001 From: Yannick Charton <tontonitch-pro@yahoo.fr> Date: Sat, 1 Oct 2022 13:33:03 +0200 Subject: [PATCH 369/884] IcingaDBWeb - fix inverted SERVICE REACHABLE check --- Nagstamon/Servers/IcingaDBWeb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index 72f6b5b7c..b429be576 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -312,7 +312,7 @@ def _get_status(self): self.new_hosts[host_name].services[service_name].flapping = bool(int(s['state']['is_flapping'] or 0)) self.new_hosts[host_name].services[service_name].acknowledged = bool(int(s['state']['is_acknowledged'] or 0)) self.new_hosts[host_name].services[service_name].scheduled_downtime = bool(int(s['state']['in_downtime'] or 0)) - self.new_hosts[host_name].services[service_name].unreachable = bool(int(s['state']['is_reachable'] or 0)) + self.new_hosts[host_name].services[service_name].unreachable = not bool(int(s['state']['is_reachable'] or 0)) if self.new_hosts[host_name].services[service_name].unreachable: self.new_hosts[host_name].services[service_name].status_information += " (SERVICE UNREACHABLE)" From f54cacb4babcc2a5298a50bc98ec71accc4df4e3 Mon Sep 17 00:00:00 2001 From: Yannick Charton <tontonitch-pro@yahoo.fr> Date: Sun, 2 Oct 2022 19:29:52 +0200 Subject: [PATCH 370/884] Fixing generated open_monitor generated url for IcingaDBWeb Fixing generated open_monitor generated url for IcingaDBWeb --- Nagstamon/Servers/IcingaDBWeb.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index b429be576..aafa744ed 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -573,16 +573,16 @@ def open_monitor(self, host, service=''): ''' # only type is important so do not care of service '' in case of host monitor if service == '': - url = '{0}/icingadb/hosts?host.state.is_problem=y&sort=host.state.severity#!{1}/icingadb/hosts/{2}'.format(self.monitor_url, + url = '{0}/icingadb/hosts?host.state.is_problem=y&sort=host.state.severity#!{1}/icingadb/host?{2}'.format(self.monitor_url, (urllib.parse.urlparse(self.monitor_url).path), urllib.parse.urlencode( - {'host': self.hosts[host].real_name}).replace('+', ' ')) + {'name': self.hosts[host].real_name}).replace('+', ' ')) else: - url = '{0}/icingadb/services?service.state.is_problem=y&sort=service.state.severity&dir=desc#!{1}/icingadb/services/{2}'.format(self.monitor_url, + url = '{0}/icingadb/services?service.state.is_problem=y&sort=service.state.severity%20desc#!{1}/icingadb/service?{2}'.format(self.monitor_url, (urllib.parse.urlparse(self.monitor_url).path), urllib.parse.urlencode( - {'host': self.hosts[host].real_name, - 'service': self.hosts[host].services[service].real_name}).replace('+', ' ')) + {'name': self.hosts[host].services[service].real_name, + 'host.name': self.hosts[host].real_name}).replace('+', ' ')) if conf.debug_mode: self.Debug(server=self.get_name(), host=host, service=service, debug='Open host/service monitor web page {0}'.format(url)) From 711b251490b99a037efa39c67241b77aff89465e Mon Sep 17 00:00:00 2001 From: Yannick Charton <tontonitch-pro@yahoo.fr> Date: Sun, 2 Oct 2022 20:33:04 +0200 Subject: [PATCH 371/884] IcingaDBWeb ack adapt first try --- Nagstamon/Servers/IcingaDBWeb.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index aafa744ed..16c0cfb9b 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -414,13 +414,13 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi # First retrieve the info page for this host/service if service == '': # url = self.monitor_cgi_url + '/monitoring/host/acknowledge-problem?host=' + host - url = '{0}/icingadb/host/acknowledge?name={1}'.format(self.monitor_cgi_url, + url = '{0}/icingadb/host/acknowledge?name={1}&showCompact=1'.format(self.monitor_cgi_url, self.hosts[host].real_name) else: - # url = self.monitor_cgi_url + '/monitoring/service/acknowledge-problem?host=' + host + '&service=' + service - url = '{0}/icingadb/service/acknowledge?host.name={1}&name={2}'.format(self.monitor_cgi_url, - self.hosts[host].real_name, - self.hosts[host].services[service].real_name) + url = '{0}/icingadb/service/acknowledge?name={1}&host.name={2}&showCompact=1'.format(self.monitor_cgi_url, + self.hosts[host].services[service].real_name, + self.hosts[host].real_name + ) result = self.FetchURL(url, giveback='raw') if result.error != '': @@ -434,23 +434,23 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi formtag = pagesoup.find('form', {'name':'IcingaModuleMonitoringFormsCommandObjectAcknowledgeProblemCommandForm'}) CSRFToken = formtag.findNext('input', {'name':'CSRFToken'})['value'] - formUID = formtag.findNext('input', {'name':'formUID'})['value'] + #formUID = formtag.findNext('input', {'name':'formUID'})['value'] btn_submit = formtag.findNext('input', {'name':'btn_submit'})['value'] # Pass these values to the same URL as cgi_data cgi_data = {} cgi_data['CSRFToken'] = CSRFToken - cgi_data['formUID'] = formUID + #cgi_data['formUID'] = formUID cgi_data['btn_submit'] = btn_submit -# - cgi_data['comment'] = comment - cgi_data['persistent'] = int(persistent) - cgi_data['sticky'] = int(sticky) - cgi_data['notify'] = int(notify) cgi_data['comment'] = comment + cgi_data['persistent'] = int(persistent).map(dict(0=n, 1=y)) + cgi_data['sticky'] = int(sticky).map(dict(0=n, 1=y)) + cgi_data['notify'] = int(notify).map(dict(0=n, 1=y)) if expire_time: - cgi_data['expire'] = 1 + cgi_data['expire'] = 'y' cgi_data['expire_time'] = expire_time + else: + cgi_data['expire'] = 'n' self.FetchURL(url, giveback='raw', cgi_data=cgi_data) From 90a8a09b5f7e2e45c2d4ff74147aef678b7825a3 Mon Sep 17 00:00:00 2001 From: Yannick Charton <tontonitch-pro@yahoo.fr> Date: Sun, 2 Oct 2022 20:53:19 +0200 Subject: [PATCH 372/884] convert fix --- Nagstamon/Servers/IcingaDBWeb.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index 16c0cfb9b..3376419b1 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -443,9 +443,9 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi #cgi_data['formUID'] = formUID cgi_data['btn_submit'] = btn_submit cgi_data['comment'] = comment - cgi_data['persistent'] = int(persistent).map(dict(0=n, 1=y)) - cgi_data['sticky'] = int(sticky).map(dict(0=n, 1=y)) - cgi_data['notify'] = int(notify).map(dict(0=n, 1=y)) + cgi_data['persistent'] = int(persistent).replace((1, 0), ('y', 'n'), inplace=True) + cgi_data['sticky'] = int(sticky).replace((1, 0), ('y', 'n'), inplace=True) + cgi_data['notify'] = int(notify).replace((1, 0), ('y', 'n'), inplace=True) if expire_time: cgi_data['expire'] = 'y' cgi_data['expire_time'] = expire_time From 6a1710895705e1a5700d43d532fb2f77c9128750 Mon Sep 17 00:00:00 2001 From: Yannick Charton <tontonitch-pro@yahoo.fr> Date: Sun, 2 Oct 2022 21:42:04 +0200 Subject: [PATCH 373/884] IcngaDBWeb - handle is_acknowledged value 'sticky' --- Nagstamon/Servers/IcingaDBWeb.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index 3376419b1..cc41012d8 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -217,7 +217,8 @@ def _get_status(self): self.new_hosts[host_name].passiveonly = not(int(h['active_checks_enabled'])) self.new_hosts[host_name].notifications_disabled = not(int(h['notifications_enabled'])) self.new_hosts[host_name].flapping = bool(int(h['state']['is_flapping'] or 0)) - self.new_hosts[host_name].acknowledged = bool(int(h['state']['is_acknowledged'] or 0)) + #s['state']['is_acknowledged'] can be null, 0, 1, or 'sticky' + self.new_hosts[host_name].acknowledged = bool(int(h['state']['is_acknowledged'].replace(('sticky', ''), (1, 0), inplace=True) or 0)) self.new_hosts[host_name].scheduled_downtime = bool(int(h['state']['in_downtime'] or 0)) # extra Icinga properties to solve https://github.com/HenriWahl/Nagstamon/issues/192 @@ -310,7 +311,8 @@ def _get_status(self): self.new_hosts[host_name].services[service_name].passiveonly = not(int(s['active_checks_enabled'])) self.new_hosts[host_name].services[service_name].notifications_disabled = not(int(s['notifications_enabled'])) self.new_hosts[host_name].services[service_name].flapping = bool(int(s['state']['is_flapping'] or 0)) - self.new_hosts[host_name].services[service_name].acknowledged = bool(int(s['state']['is_acknowledged'] or 0)) + #s['state']['is_acknowledged'] can be null, 0, 1, or 'sticky' + self.new_hosts[host_name].services[service_name].acknowledged = bool(int(s['state']['is_acknowledged'].replace(('sticky', ''), (1, 0), inplace=True) or 0)) self.new_hosts[host_name].services[service_name].scheduled_downtime = bool(int(s['state']['in_downtime'] or 0)) self.new_hosts[host_name].services[service_name].unreachable = not bool(int(s['state']['is_reachable'] or 0)) From 3b1028f31406a80a2dfbe152913622310ac6c2b3 Mon Sep 17 00:00:00 2001 From: Yannick Charton <tontonitch-pro@yahoo.fr> Date: Sun, 2 Oct 2022 21:59:09 +0200 Subject: [PATCH 374/884] fix last commit --- Nagstamon/Servers/IcingaDBWeb.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index cc41012d8..066e7ccee 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -218,7 +218,7 @@ def _get_status(self): self.new_hosts[host_name].notifications_disabled = not(int(h['notifications_enabled'])) self.new_hosts[host_name].flapping = bool(int(h['state']['is_flapping'] or 0)) #s['state']['is_acknowledged'] can be null, 0, 1, or 'sticky' - self.new_hosts[host_name].acknowledged = bool(int(h['state']['is_acknowledged'].replace(('sticky', ''), (1, 0), inplace=True) or 0)) + self.new_hosts[host_name].acknowledged = bool(int(h['state']['is_acknowledged'].replace('sticky', 1) or 0)) self.new_hosts[host_name].scheduled_downtime = bool(int(h['state']['in_downtime'] or 0)) # extra Icinga properties to solve https://github.com/HenriWahl/Nagstamon/issues/192 @@ -312,7 +312,7 @@ def _get_status(self): self.new_hosts[host_name].services[service_name].notifications_disabled = not(int(s['notifications_enabled'])) self.new_hosts[host_name].services[service_name].flapping = bool(int(s['state']['is_flapping'] or 0)) #s['state']['is_acknowledged'] can be null, 0, 1, or 'sticky' - self.new_hosts[host_name].services[service_name].acknowledged = bool(int(s['state']['is_acknowledged'].replace(('sticky', ''), (1, 0), inplace=True) or 0)) + self.new_hosts[host_name].services[service_name].acknowledged = bool(int(s['state']['is_acknowledged'].replace('sticky', 1) or 0)) self.new_hosts[host_name].services[service_name].scheduled_downtime = bool(int(s['state']['in_downtime'] or 0)) self.new_hosts[host_name].services[service_name].unreachable = not bool(int(s['state']['is_reachable'] or 0)) From 17fbd1070a3e8eeea2cdd8fdd62f5960aaa7e718 Mon Sep 17 00:00:00 2001 From: Yannick Charton <tontonitch-pro@yahoo.fr> Date: Sun, 2 Oct 2022 22:17:27 +0200 Subject: [PATCH 375/884] IcingaDBWeb fixes --- Nagstamon/Servers/IcingaDBWeb.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index 066e7ccee..473e1d1e9 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -218,7 +218,7 @@ def _get_status(self): self.new_hosts[host_name].notifications_disabled = not(int(h['notifications_enabled'])) self.new_hosts[host_name].flapping = bool(int(h['state']['is_flapping'] or 0)) #s['state']['is_acknowledged'] can be null, 0, 1, or 'sticky' - self.new_hosts[host_name].acknowledged = bool(int(h['state']['is_acknowledged'].replace('sticky', 1) or 0)) + self.new_hosts[host_name].acknowledged = bool(int(h['state']['is_acknowledged'].replace('sticky', '1') or 0)) self.new_hosts[host_name].scheduled_downtime = bool(int(h['state']['in_downtime'] or 0)) # extra Icinga properties to solve https://github.com/HenriWahl/Nagstamon/issues/192 @@ -234,7 +234,7 @@ def _get_status(self): self.new_hosts[host_name].attempt = "HARD" # extra duration needed for calculation - if h['state']['last_state_change'] is not None: + if h['state']['last_state_change'] is not None or h['state']['last_state_change'] == 0: last_change = h['state']['last_state_change'] if h['state']['last_state_change'] is not None else 0 duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(float(last_change))) self.new_hosts[host_name].duration = strfdelta(duration,'{days}d {hours}h {minutes}m {seconds}s') @@ -312,7 +312,7 @@ def _get_status(self): self.new_hosts[host_name].services[service_name].notifications_disabled = not(int(s['notifications_enabled'])) self.new_hosts[host_name].services[service_name].flapping = bool(int(s['state']['is_flapping'] or 0)) #s['state']['is_acknowledged'] can be null, 0, 1, or 'sticky' - self.new_hosts[host_name].services[service_name].acknowledged = bool(int(s['state']['is_acknowledged'].replace('sticky', 1) or 0)) + self.new_hosts[host_name].services[service_name].acknowledged = bool(int(s['state']['is_acknowledged'].replace('sticky', '1') or 0)) self.new_hosts[host_name].services[service_name].scheduled_downtime = bool(int(s['state']['in_downtime'] or 0)) self.new_hosts[host_name].services[service_name].unreachable = not bool(int(s['state']['is_reachable'] or 0)) @@ -332,7 +332,7 @@ def _get_status(self): self.new_hosts[host_name].services[service_name].attempt = "HARD" # extra duration needed for calculation - if s['state']['last_state_change'] is not None: + if s['state']['last_state_change'] is not None or s['state']['last_state_change'] == 0: last_change = s['state']['last_state_change'] if s['state']['last_state_change'] is not None else 0 duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(float(last_change))) self.new_hosts[host_name].services[service_name].duration = strfdelta(duration, '{days}d {hours}h {minutes}m {seconds}s') From e94c20fc42f970a3571c5d2b38f8ab52495b08e6 Mon Sep 17 00:00:00 2001 From: Yannick Charton <tontonitch-pro@yahoo.fr> Date: Sun, 2 Oct 2022 22:29:45 +0200 Subject: [PATCH 376/884] logic check small fix --- Nagstamon/Servers/IcingaDBWeb.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index 473e1d1e9..51b75e82c 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -234,7 +234,7 @@ def _get_status(self): self.new_hosts[host_name].attempt = "HARD" # extra duration needed for calculation - if h['state']['last_state_change'] is not None or h['state']['last_state_change'] == 0: + if h['state']['last_state_change'] is not None and h['state']['last_state_change'] != 0: last_change = h['state']['last_state_change'] if h['state']['last_state_change'] is not None else 0 duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(float(last_change))) self.new_hosts[host_name].duration = strfdelta(duration,'{days}d {hours}h {minutes}m {seconds}s') @@ -332,7 +332,7 @@ def _get_status(self): self.new_hosts[host_name].services[service_name].attempt = "HARD" # extra duration needed for calculation - if s['state']['last_state_change'] is not None or s['state']['last_state_change'] == 0: + if s['state']['last_state_change'] is not None and s['state']['last_state_change'] != 0: last_change = s['state']['last_state_change'] if s['state']['last_state_change'] is not None else 0 duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(float(last_change))) self.new_hosts[host_name].services[service_name].duration = strfdelta(duration, '{days}d {hours}h {minutes}m {seconds}s') From d5304203033d3e5579ee11d53da79776802a5ee3 Mon Sep 17 00:00:00 2001 From: Yannick Charton <tontonitch-pro@yahoo.fr> Date: Sun, 2 Oct 2022 22:50:11 +0200 Subject: [PATCH 377/884] IcingaDBWeb - cleaning old logic --- Nagstamon/Servers/IcingaDBWeb.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index 51b75e82c..64769b845 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -235,8 +235,7 @@ def _get_status(self): # extra duration needed for calculation if h['state']['last_state_change'] is not None and h['state']['last_state_change'] != 0: - last_change = h['state']['last_state_change'] if h['state']['last_state_change'] is not None else 0 - duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(float(last_change))) + duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(float(h['state']['last_state_change']))) self.new_hosts[host_name].duration = strfdelta(duration,'{days}d {hours}h {minutes}m {seconds}s') else: self.new_hosts[host_name].duration = 'n/a' @@ -333,8 +332,7 @@ def _get_status(self): # extra duration needed for calculation if s['state']['last_state_change'] is not None and s['state']['last_state_change'] != 0: - last_change = s['state']['last_state_change'] if s['state']['last_state_change'] is not None else 0 - duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(float(last_change))) + duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(float(s['state']['last_state_change']))) self.new_hosts[host_name].services[service_name].duration = strfdelta(duration, '{days}d {hours}h {minutes}m {seconds}s') else: self.new_hosts[host_name].services[service_name].duration = 'n/a' From 0c99d8542ee3cf25538337e9968f01d0d3a90b1d Mon Sep 17 00:00:00 2001 From: Yannick Charton <tontonitch-pro@yahoo.fr> Date: Sun, 2 Oct 2022 23:19:05 +0200 Subject: [PATCH 378/884] Propagate a recent fix on other server types to IcingaDBWeb --- Nagstamon/Servers/IcingaDBWeb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index 64769b845..4e0a2a9fb 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -410,7 +410,7 @@ def set_acknowledge(self, info_dict): info_dict['expire_time']) - def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=[], expire_time=None): + def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=None, expire_time=None): # First retrieve the info page for this host/service if service == '': # url = self.monitor_cgi_url + '/monitoring/host/acknowledge-problem?host=' + host From ca42385f4270544dfd73c53467f54de66c8fe3d0 Mon Sep 17 00:00:00 2001 From: Benoit Poulet <benoit.poulet@businessdecision.com> Date: Mon, 3 Oct 2022 14:39:13 +0200 Subject: [PATCH 379/884] fix bad token management token was always renewed at each refresh --- Nagstamon/Servers/Centreon/CentreonAPI.py | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/Nagstamon/Servers/Centreon/CentreonAPI.py b/Nagstamon/Servers/Centreon/CentreonAPI.py index bb71fe958..fb5169d2c 100644 --- a/Nagstamon/Servers/Centreon/CentreonAPI.py +++ b/Nagstamon/Servers/Centreon/CentreonAPI.py @@ -708,20 +708,20 @@ def check_session(self): try: if conf.debug_mode == True: self.Debug(server='[' + self.get_name() + ']', - debug='Check-session, the token will be deleted if it has not been used for more than one hour. Current Token = ' + str( + debug='Check-session, the token expire if not been used for more than one hour. Current Token = ' + str( self.token)) cgi_data = {'limit': '0'} - self.session = requests.Session() - self.session.headers['Content-Type'] = 'application/json' - self.session.headers['X-Auth-Token'] = self.token - # Get en empty service list, to check the status of the current token # This request must be done in a GET, so just encode the parameters and fetch result = self.FetchURL(self.urls_centreon['resources'] + '?' + urllib.parse.urlencode(cgi_data), giveback="raw") - if result.status_code == 403: - self.get_token() + + # If we got an 403 or 401, the token expired and must be renewed + if result.status_code == 403 or result.status_code == 401: + self.token = self.get_token().result + if conf.debug_mode == True: + self.Debug(server='[' + self.get_name() + ']', debug='Check-session, session renewed') result = self.FetchURL(self.urls_centreon['resources'] + '?' + urllib.parse.urlencode(cgi_data), giveback="raw") if not 'ConnectTimeoutError' in result.error and \ @@ -739,12 +739,6 @@ def check_session(self): self.Debug(server=self.get_name(), debug="Check-session, Error : " + error + ", Status code : " + str(status_code)) - # If we got an 401, the token expired and must be renewed - if status_code == 401: - self.token = self.get_token().result - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', debug='Check-session, session renewed') - except: traceback.print_exc(file=sys.stdout) result, error = self.Error(sys.exc_info()) From 1523fad405d014fdc289412e3849cc791ba18149 Mon Sep 17 00:00:00 2001 From: Benoit Poulet <benoit.poulet@businessdecision.com> Date: Mon, 3 Oct 2022 14:48:56 +0200 Subject: [PATCH 380/884] small cleaning --- Nagstamon/Servers/Centreon/CentreonAPI.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Nagstamon/Servers/Centreon/CentreonAPI.py b/Nagstamon/Servers/Centreon/CentreonAPI.py index 36910b7a9..c7d06d409 100644 --- a/Nagstamon/Servers/Centreon/CentreonAPI.py +++ b/Nagstamon/Servers/Centreon/CentreonAPI.py @@ -120,7 +120,7 @@ def define_url(self): self.urls_centreon = urls_centreon_api_v2 if conf.debug_mode == True: self.Debug(server='[' + self.get_name() + ']', - debug='URLs defined for Centreon vers. : %s' % (self.centreon_version_major)) + debug='URLs defined for Centreon version : %s' % (self.centreon_version_major)) def open_monitor(self, host, service=''): # Used for self.MENU_ACTIONS = ['Monitor'] @@ -702,9 +702,7 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t def check_session(self): if conf.debug_mode == True: self.Debug(server='[' + self.get_name() + ']', debug='Checking session status') - # Not needed anymore as URLs are set at start - # if 'url_centreon' not in self.__dict__: - # self.init_config() + try: if conf.debug_mode == True: self.Debug(server='[' + self.get_name() + ']', From f0d34c7c65fccd2e9d28989ed96bc548f07e9ed5 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 3 Oct 2022 21:15:38 +0200 Subject: [PATCH 381/884] 3.9-20221003 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 2 +- build/windows/nagstamon.iss | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 3d6e7a355..a1f2d8290 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20220930' + VERSION = '3.9-20221003' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 4216adbd8..4edb77cc4 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.9-20220930) unstable; urgency=low +nagstamon (3.9-20221003) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Wed, 17 Aug 2022 08:00:00 +0100 diff --git a/build/windows/nagstamon.iss b/build/windows/nagstamon.iss index 02c1c97b5..7fe1ae044 100644 --- a/build/windows/nagstamon.iss +++ b/build/windows/nagstamon.iss @@ -1,7 +1,7 @@ [Setup] AppName=Nagstamon AppVerName=Nagstamon {#version} -DefaultDirName={pf}\Nagstamon +DefaultDirName={commonpf}\Nagstamon DefaultGroupName=Nagstamon AlwaysUsePersonalGroup=false ShowLanguageDialog=no From 192d795444fc5f0e105de3f6a969b615593dfbb9 Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Mon, 3 Oct 2022 23:34:15 +0200 Subject: [PATCH 382/884] windows pyqt6 6.3.1 --- build/requirements/windows.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index 0372bdb92..88aea36a0 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -6,7 +6,7 @@ lxml psutil pyinstaller pypiwin32 -pyqt6 +pyqt6==6.3.1 pysocks python-dateutil requests From 7afeeb8c2cea30073c642ee1172fdbdecf846dde Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Mon, 3 Oct 2022 23:47:31 +0200 Subject: [PATCH 383/884] -replace 'pyqt6==6.3.1','pyqt5' --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 0af7647a8..fa0354d17 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -142,7 +142,7 @@ jobs: python-version: ${{ env.python_win_version }} architecture: x86 # no PyQt6 for win32 available on Pypi.org - - run: ((Get-Content -path build/requirements/windows.txt -Raw) -replace 'pyqt6','pyqt5') | Set-Content -Path build/requirements/windows.txt + - run: ((Get-Content -path build/requirements/windows.txt -Raw) -replace 'pyqt6==6.3.1','pyqt5') | Set-Content -Path build/requirements/windows.txt - run: python -m pip install --no-warn-script-location -r build/requirements/windows.txt # pretty hacky but no other idea to avoid gssapi being installed which breaks requests-kerberos - run: python -m pip uninstall -y gssapi requests-gssapi From 1940de65b2ee06c9e3cfd0ca0533a69060aace0d Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Mon, 3 Oct 2022 23:52:12 +0200 Subject: [PATCH 384/884] -replace 'pyqt6.*','pyqt5' --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index fa0354d17..16191399c 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -142,7 +142,7 @@ jobs: python-version: ${{ env.python_win_version }} architecture: x86 # no PyQt6 for win32 available on Pypi.org - - run: ((Get-Content -path build/requirements/windows.txt -Raw) -replace 'pyqt6==6.3.1','pyqt5') | Set-Content -Path build/requirements/windows.txt + - run: ((Get-Content -path build/requirements/windows.txt -Raw) -replace 'pyqt6.*','pyqt5') | Set-Content -Path build/requirements/windows.txt - run: python -m pip install --no-warn-script-location -r build/requirements/windows.txt # pretty hacky but no other idea to avoid gssapi being installed which breaks requests-kerberos - run: python -m pip uninstall -y gssapi requests-gssapi From ad033ab5ce4166aa87d90d079330c3da175560ab Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Tue, 4 Oct 2022 07:19:02 +0200 Subject: [PATCH 385/884] pyqt6-qt6==6.3.1 --- build/requirements/windows.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index 88aea36a0..8580cc3d1 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -7,6 +7,7 @@ psutil pyinstaller pypiwin32 pyqt6==6.3.1 +pyqt6-qt6==6.3.1 pysocks python-dateutil requests From b39afef9f38ed003faa241e110bdd5616614b10d Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 4 Oct 2022 07:24:10 +0200 Subject: [PATCH 386/884] Create codeql-analysis.yml --- .github/workflows/codeql-analysis.yml | 74 +++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 .github/workflows/codeql-analysis.yml diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 000000000..4fb532440 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,74 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ "master" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + schedule: + - cron: '20 3 * * 2' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'python' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" From 1f2672dbe157aea5aa2f09e9fe005f0ef0506136 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 4 Oct 2022 09:14:43 +0200 Subject: [PATCH 387/884] Update macos.txt --- build/requirements/macos.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index 68a3418cb..f9f5354bf 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -4,7 +4,8 @@ keyring lxml psutil pyinstaller -pyqt6 +pyqt6==6.3.1 +pyqt6-qt6==6.3.1 pysocks python-dateutil requests From 592d3b890495d3df92cf18526fcfca436a23c6a1 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 4 Oct 2022 09:14:55 +0200 Subject: [PATCH 388/884] Update linux.txt --- build/requirements/linux.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/requirements/linux.txt b/build/requirements/linux.txt index 176a735bd..6295f165e 100644 --- a/build/requirements/linux.txt +++ b/build/requirements/linux.txt @@ -4,7 +4,8 @@ dbus-python keyring lxml psutil -pyqt6 +pyqt6==6.3.1 +pyqt6-qt6==6.3.1 pysocks python-dateutil requests From 9f498af26cf47c8437cb78782b12c764b9309e67 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 4 Oct 2022 09:15:38 +0200 Subject: [PATCH 389/884] Delete codeql-analysis.yml --- .github/workflows/codeql-analysis.yml | 74 --------------------------- 1 file changed, 74 deletions(-) delete mode 100644 .github/workflows/codeql-analysis.yml diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml deleted file mode 100644 index 4fb532440..000000000 --- a/.github/workflows/codeql-analysis.yml +++ /dev/null @@ -1,74 +0,0 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# -name: "CodeQL" - -on: - push: - branches: [ "master" ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ "master" ] - schedule: - - cron: '20 3 * * 2' - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - - strategy: - fail-fast: false - matrix: - language: [ 'python' ] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] - # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - - # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs - # queries: security-extended,security-and-quality - - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v2 - - # ℹ️ Command-line programs to run using the OS shell. - # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun - - # If the Autobuild fails above, remove it and uncomment the following three lines. - # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. - - # - run: | - # echo "Run, Build Application using script" - # ./location_of_script_within_repo/buildscript.sh - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 - with: - category: "/language:${{matrix.language}}" From 6a0af192e4da49773599776e7459901cb3f921a3 Mon Sep 17 00:00:00 2001 From: Yannick Charton <tontonitch-pro@yahoo.fr> Date: Thu, 6 Oct 2022 16:29:50 +0200 Subject: [PATCH 390/884] IcingaDBWeb - acknowledgement support IcingaDBWeb - acknowledgement support --- Nagstamon/Servers/IcingaDBWeb.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index 4e0a2a9fb..e41842efc 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -93,7 +93,7 @@ def init_HTTP(self): GenericServer.init_HTTP(self) if self.session and not 'Referer' in self.session.headers: - self.session.headers['Referer'] = self.monitor_cgi_url + '/icingaweb2/icingadb' + self.session.headers['Referer'] = self.monitor_cgi_url # normally cookie auth will be used if not self.no_cookie_auth: @@ -431,7 +431,7 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi pagesoup = BeautifulSoup(pageraw, 'html.parser') # Extract the relevant form element values - formtag = pagesoup.find('form', {'name':'IcingaModuleMonitoringFormsCommandObjectAcknowledgeProblemCommandForm'}) + formtag = pagesoup.find('form', {'class':'icinga-form icinga-controls'}) CSRFToken = formtag.findNext('input', {'name':'CSRFToken'})['value'] #formUID = formtag.findNext('input', {'name':'formUID'})['value'] @@ -443,15 +443,22 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi #cgi_data['formUID'] = formUID cgi_data['btn_submit'] = btn_submit cgi_data['comment'] = comment - cgi_data['persistent'] = int(persistent).replace((1, 0), ('y', 'n'), inplace=True) - cgi_data['sticky'] = int(sticky).replace((1, 0), ('y', 'n'), inplace=True) - cgi_data['notify'] = int(notify).replace((1, 0), ('y', 'n'), inplace=True) + cgi_data['persistent'] = str(persistent).replace('True', 'y').replace('False', 'n') + cgi_data['sticky'] = str(sticky).replace('True', 'y').replace('False', 'n') + cgi_data['notify'] = str(notify).replace('True', 'y').replace('False', 'n') if expire_time: cgi_data['expire'] = 'y' cgi_data['expire_time'] = expire_time else: cgi_data['expire'] = 'n' + # X-Icinga-WindowId seems to be the one triggering icinga2/icingaweb2 + self.session.headers['X-Icinga-WindowId'] = 'jbpqmtviznre_ivmury' + self.session.headers['X-Requested-With'] = 'XMLHttpRequest' + self.session.headers['X-Icinga-Accept'] = 'text/html' + self.session.headers['X-Icinga-Container'] = 'modal-content' + self.session.headers['Origin'] = self.monitor_cgi_url + self.session.headers.update({'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}) self.FetchURL(url, giveback='raw', cgi_data=cgi_data) if len(all_services) > 0: From 649b8221f37e0f2013a8dcd880640b7f6dffa322 Mon Sep 17 00:00:00 2001 From: Yannick Charton <tontonitch-pro@yahoo.fr> Date: Fri, 7 Oct 2022 20:59:36 +0200 Subject: [PATCH 391/884] IcingaDBWeb - more robust implementation better headers ans scope, added debug and cleaned code --- Nagstamon/Servers/IcingaDBWeb.py | 52 ++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index e41842efc..9860a2219 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -421,6 +421,11 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi self.hosts[host].services[service].real_name, self.hosts[host].real_name ) + + # Set correct headers + self.session.headers['X-Requested-With'] = 'XMLHttpRequest' + self.session.headers.update({'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}) + result = self.FetchURL(url, giveback='raw') if result.error != '': @@ -430,17 +435,19 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi pagesoup = BeautifulSoup(pageraw, 'html.parser') + ## Super debug + #if conf.debug_mode: + # self.Debug(server=self.get_name(), host=host, service=service, + # debug='Retrieve html from {0}: {1}'.format(url,pagesoup.prettify())) + # Extract the relevant form element values formtag = pagesoup.find('form', {'class':'icinga-form icinga-controls'}) - - CSRFToken = formtag.findNext('input', {'name':'CSRFToken'})['value'] - #formUID = formtag.findNext('input', {'name':'formUID'})['value'] btn_submit = formtag.findNext('input', {'name':'btn_submit'})['value'] + CSRFToken = formtag.findNext('input', {'name':'CSRFToken'})['value'] # Pass these values to the same URL as cgi_data cgi_data = {} cgi_data['CSRFToken'] = CSRFToken - #cgi_data['formUID'] = formUID cgi_data['btn_submit'] = btn_submit cgi_data['comment'] = comment cgi_data['persistent'] = str(persistent).replace('True', 'y').replace('False', 'n') @@ -452,15 +459,34 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi else: cgi_data['expire'] = 'n' - # X-Icinga-WindowId seems to be the one triggering icinga2/icingaweb2 - self.session.headers['X-Icinga-WindowId'] = 'jbpqmtviznre_ivmury' - self.session.headers['X-Requested-With'] = 'XMLHttpRequest' - self.session.headers['X-Icinga-Accept'] = 'text/html' - self.session.headers['X-Icinga-Container'] = 'modal-content' - self.session.headers['Origin'] = self.monitor_cgi_url - self.session.headers.update({'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}) - self.FetchURL(url, giveback='raw', cgi_data=cgi_data) - + response = self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + + # Some debug data + data = response.result + error = response.error + status_code = response.status_code + if conf.debug_mode: + self.Debug(server=self.get_name(), host=host, service=service, + debug='Achnowledgement response') + self.Debug(server=self.get_name(), host=host, service=service, + debug="- response: {0}".format(response)) + self.Debug(server=self.get_name(), host=host, service=service, + debug="- data: {0}".format(data)) + self.Debug(server=self.get_name(), host=host, service=service, + debug="- error: {0}".format(error)) + self.Debug(server=self.get_name(), host=host, service=service, + debug="- status_code: {0}".format(status_code)) + + # Test the result + if data != "Invalid CSRF token provided": + if conf.debug_mode: + self.Debug(server=self.get_name(), host=host, service=service, + debug="CSRF valid") + else: + if conf.debug_mode: + self.Debug(server=self.get_name(), host=host, service=service, + debug="ERROR: CSRF invalid") + if len(all_services) > 0: for s in all_services: # cheap, recursive solution... From 9d50008ddc0dfd43b64fa62cd06e9a75c306dd4a Mon Sep 17 00:00:00 2001 From: Yannick Charton <tontonitch-pro@yahoo.fr> Date: Sat, 8 Oct 2022 09:48:26 +0200 Subject: [PATCH 392/884] Update build-release-latest.yml runs-on macos 12 as 10.15 is deprecated --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index f23149793..4d4ee7f91 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -117,7 +117,7 @@ jobs: if-no-files-found: error macos: - runs-on: macos-10.15 + runs-on: macos-12 needs: test steps: - uses: actions/checkout@v2 From aa50ad66fe111379f7494ba661299197096b3247 Mon Sep 17 00:00:00 2001 From: Yannick Charton <tontonitch-pro@yahoo.fr> Date: Sat, 8 Oct 2022 14:29:16 +0200 Subject: [PATCH 393/884] IcingaDBWeb - better implem. + actions better forms detection, better acknowledge action handling, implementation of actions recheck, submit_check_result, set_downtime, and get_start_end for set downtime, stronger url writing, some cleaning --- Nagstamon/Servers/IcingaDBWeb.py | 239 ++++++++++++++++++++++++------- 1 file changed, 185 insertions(+), 54 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index 9860a2219..570fba953 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -356,11 +356,18 @@ def _get_status(self): def _set_recheck(self, host, service): + # Set correct headers + self.session.headers['X-Requested-With'] = 'XMLHttpRequest' + self.session.headers.update({'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}) + # First retrieve the info page for this host/service if service == '': - url = self.monitor_cgi_url + '/icingadb/host?name=' + self.hosts[host].real_name + url = '{0}/icingadb/host?name={1}'.format(self.monitor_cgi_url, self.hosts[host].real_name) else: - url = self.monitor_cgi_url + '/icingadb/service?name=' + self.hosts[host].services[service].real_name + '&host.name=' + self.hosts[host].real_name + url = '{0}/icingadb/service?name={1}&host.name={2}'.format(self.monitor_cgi_url, + self.hosts[host].services[service].real_name, + self.hosts[host].real_name + ) result = self.FetchURL(url, giveback='raw') if result.error != '': @@ -369,20 +376,57 @@ def _set_recheck(self, host, service): pageraw = result.result pagesoup = BeautifulSoup(pageraw, 'html.parser') + print(pagesoup.prettify()) + + ## Super debug + #if conf.debug_mode: + # self.Debug(server=self.get_name(), host=host, service=service, + # debug='[Recheck] Retrieve html from {0}: \n{1}'.format(url,pagesoup.prettify())) # Extract the relevant form element values + + formtag = pagesoup.select_one('form[action*="check-now"]') + #print('-----------------') + #print(formtag.prettify()) + #print('-----------------') + + if conf.debug_mode: + self.Debug(server=self.get_name(), host=host, service=service, + debug='[Recheck] Retrieve html form from {0}: \n{1}'.format(url,formtag.prettify())) - formtag = pagesoup.find('form', {'name':'IcingaModuleMonitoringFormsCommandObjectCheckNowCommandForm'}) - CSRFToken = formtag.findNext('input', {'name':'CSRFToken'})['value'] - formUID = formtag.findNext('input', {'name':'formUID'})['value'] btn_submit = formtag.findNext('button', {'name':'btn_submit'})['value'] + CSRFToken = formtag.findNext('input', {'name':'CSRFToken'})['value'] - # Pass these values to the same URL as cgi_data + # Pass these values to the check-now URL as cgi_data cgi_data = {} cgi_data['CSRFToken'] = CSRFToken - cgi_data['formUID'] = formUID cgi_data['btn_submit'] = btn_submit - result = self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + + if service == '': + url = '{0}/icingadb/host/check-now?name={1}'.format(self.monitor_cgi_url, self.hosts[host].real_name) + else: + url = '{0}/icingadb/service/check-now?name={1}&host.name={2}'.format(self.monitor_cgi_url, + self.hosts[host].services[service].real_name, + self.hosts[host].real_name + ) + + response = self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + + # Some debug data + data = response.result + error = response.error + status_code = response.status_code + if conf.debug_mode: + self.Debug(server=self.get_name(), host=host, service=service, + debug='Recheck response') + self.Debug(server=self.get_name(), host=host, service=service, + debug="- response: {0}".format(response)) + self.Debug(server=self.get_name(), host=host, service=service, + debug="- data: {0}".format(data)) + self.Debug(server=self.get_name(), host=host, service=service, + debug="- error: {0}".format(error)) + self.Debug(server=self.get_name(), host=host, service=service, + debug="- status_code: {0}".format(status_code)) # Overwrite function from generic server to add expire_time value @@ -411,9 +455,12 @@ def set_acknowledge(self, info_dict): def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=None, expire_time=None): + # Set correct headers + self.session.headers['X-Requested-With'] = 'XMLHttpRequest' + self.session.headers.update({'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}) + # First retrieve the info page for this host/service if service == '': - # url = self.monitor_cgi_url + '/monitoring/host/acknowledge-problem?host=' + host url = '{0}/icingadb/host/acknowledge?name={1}&showCompact=1'.format(self.monitor_cgi_url, self.hosts[host].real_name) else: @@ -422,10 +469,6 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi self.hosts[host].real_name ) - # Set correct headers - self.session.headers['X-Requested-With'] = 'XMLHttpRequest' - self.session.headers.update({'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}) - result = self.FetchURL(url, giveback='raw') if result.error != '': @@ -435,13 +478,22 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi pagesoup = BeautifulSoup(pageraw, 'html.parser') - ## Super debug + ## Super debug #if conf.debug_mode: # self.Debug(server=self.get_name(), host=host, service=service, - # debug='Retrieve html from {0}: {1}'.format(url,pagesoup.prettify())) + # debug='[Acknowledge] Retrieve html from {0}: {1}'.format(url,pagesoup.prettify())) # Extract the relevant form element values - formtag = pagesoup.find('form', {'class':'icinga-form icinga-controls'}) + + formtag = pagesoup.select_one('form[action*="acknowledge"]') + #print('-----------------') + #print(formtag.prettify()) + #print('-----------------') + + if conf.debug_mode: + self.Debug(server=self.get_name(), host=host, service=service, + debug='[Acknowledge] Retrieve html form from {0}: \n{1}'.format(url,formtag.prettify())) + btn_submit = formtag.findNext('input', {'name':'btn_submit'})['value'] CSRFToken = formtag.findNext('input', {'name':'CSRFToken'})['value'] @@ -460,7 +512,7 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi cgi_data['expire'] = 'n' response = self.FetchURL(url, giveback='raw', cgi_data=cgi_data) - + # Some debug data data = response.result error = response.error @@ -476,17 +528,7 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi debug="- error: {0}".format(error)) self.Debug(server=self.get_name(), host=host, service=service, debug="- status_code: {0}".format(status_code)) - - # Test the result - if data != "Invalid CSRF token provided": - if conf.debug_mode: - self.Debug(server=self.get_name(), host=host, service=service, - debug="CSRF valid") - else: - if conf.debug_mode: - self.Debug(server=self.get_name(), host=host, service=service, - debug="ERROR: CSRF invalid") - + if len(all_services) > 0: for s in all_services: # cheap, recursive solution... @@ -494,12 +536,20 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi def _set_submit_check_result(self, host, service, state, comment, check_output, performance_data): + # Set correct headers + self.session.headers['X-Requested-With'] = 'XMLHttpRequest' + self.session.headers.update({'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}) + # First retrieve the info page for this host/service if service == '': - url = self.monitor_cgi_url + '/icingadb/host/process-checkresult?name=' + self.hosts[host].real_name + url = '{0}/icingadb/host/process-checkresult?name='.format(self.monitor_cgi_url, + self.hosts[host].real_name) status = self.STATES_MAPPING_REV['hosts'][state.upper()] else: - url = self.monitor_cgi_url + '/icingadb/service/process-checkresult?host.name=' + self.hosts[host].real_name + '&name=' + self.hosts[host].services[service].real_name + url = '{0}/icingadb/service/process-checkresult?name={1}&host.name={2}'.format(self.monitor_cgi_url, + self.hosts[host].services[service].real_name, + self.hosts[host].real_name + ) status = self.STATES_MAPPING_REV['services'][state.upper()] result = self.FetchURL(url, giveback='raw') @@ -511,33 +561,68 @@ def _set_submit_check_result(self, host, service, state, comment, check_output, pagesoup = BeautifulSoup(pageraw, 'html.parser') + ## Super debug + #if conf.debug_mode: + # self.Debug(server=self.get_name(), host=host, service=service, + # debug='[Submit check result] Retrieve html from {0}: \n{1}'.format(url,pagesoup.prettify())) + # Extract the relevant form element values - formtag = pagesoup.find('form', {'name':'IcingaModuleMonitoringFormsCommandObjectProcessCheckResultCommandForm'}) - CSRFToken = formtag.findNext('input', {'name':'CSRFToken'})['value'] - formUID = formtag.findNext('input', {'name':'formUID'})['value'] - btn_submit = formtag.findNext('input', {'name':'btn_submit'})['value'] + formtag = pagesoup.select_one('form[action*="process-checkresult"]') + #print('-----------------') + #print(formtag.prettify()) + #print('-----------------') + + if conf.debug_mode: + self.Debug(server=self.get_name(), host=host, service=service, + debug='[Submit check result] Retrieve html form from {0}: \n{1}'.format(url,formtag.prettify())) + btn_submit = formtag.findNext('input', {'name':'btn_submit'})['value'] + CSRFToken = formtag.findNext('input', {'name':'CSRFToken'})['value'] + # Pass these values to the same URL as cgi_data cgi_data = {} cgi_data['CSRFToken'] = CSRFToken - cgi_data['formUID'] = formUID cgi_data['btn_submit'] = btn_submit cgi_data['status'] = status cgi_data['output'] = check_output cgi_data['perfdata'] = performance_data - self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + response = self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + + # Some debug data + data = response.result + error = response.error + status_code = response.status_code + if conf.debug_mode: + self.Debug(server=self.get_name(), host=host, service=service, + debug='Achnowledgement response') + self.Debug(server=self.get_name(), host=host, service=service, + debug="- response: {0}".format(response)) + self.Debug(server=self.get_name(), host=host, service=service, + debug="- data: {0}".format(data)) + self.Debug(server=self.get_name(), host=host, service=service, + debug="- error: {0}".format(error)) + self.Debug(server=self.get_name(), host=host, service=service, + debug="- status_code: {0}".format(status_code)) def _set_downtime(self, host, service, author, comment, fixed, start_time, end_time, hours, minutes): + # Set correct headers + self.session.headers['X-Requested-With'] = 'XMLHttpRequest' + self.session.headers.update({'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}) + # First retrieve the info page for this host/service if service == '': - url = self.monitor_cgi_url + '/icingadb/host/schedule-downtime?name=' + self.hosts[host].real_name + url = '{0}/icingadb/host/schedule-downtime?name={1}'.format(self.monitor_cgi_url, + self.hosts[host].real_name) else: - url = self.monitor_cgi_url + '/icingadb/service/schedule-downtime?host.name=' + self.hosts[host].real_name + '&name=' + self.hosts[host].services[service].real_name - + url = '{0}/icingadb/service/schedule-downtime?name={1}&host.name={2}&showCompact=1'.format(self.monitor_cgi_url, + self.hosts[host].services[service].real_name, + self.hosts[host].real_name + ) + result = self.FetchURL(url, giveback='raw') if result.error != '': @@ -547,26 +632,34 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t pagesoup = BeautifulSoup(pageraw, 'html.parser') - # Extract the relevant form element values - if service == '': - formtag = pagesoup.find('form', {'name':'IcingaModuleMonitoringFormsCommandObjectScheduleHostDowntimeCommandForm'}) - else: - formtag = pagesoup.find('form', {'name':'IcingaModuleMonitoringFormsCommandObjectScheduleServiceDowntimeCommandForm'}) + ## Super debug + #if conf.debug_mode: + # self.Debug(server=self.get_name(), host=host, service=service, + # debug='[Set downtime] Retrieve html from {0}: \n{1}'.format(url,pagesoup.prettify())) - CSRFToken = formtag.findNext('input', {'name':'CSRFToken'})['value'] - formUID = formtag.findNext('input', {'name':'formUID'})['value'] + # Extract the relevant form element values + + formtag = pagesoup.select_one('form[action*="schedule-downtime"]') + #print('-----------------') + #print(formtag.prettify()) + #print('-----------------') + + if conf.debug_mode: + self.Debug(server=self.get_name(), host=host, service=service, + debug='[Set downtime] Retrieve html form from {0}: \n{1}'.format(url,formtag.prettify())) + btn_submit = formtag.findNext('input', {'name':'btn_submit'})['value'] + CSRFToken = formtag.findNext('input', {'name':'CSRFToken'})['value'] # Pass these values to the same URL as cgi_data cgi_data = {} cgi_data['CSRFToken'] = CSRFToken - cgi_data['formUID'] = formUID cgi_data['btn_submit'] = btn_submit cgi_data['comment'] = comment if fixed: - cgi_data['type'] = 'fixed' + cgi_data['flexible'] = 'n' else: - cgi_data['type'] = 'flexible' + cgi_data['flexible'] = 'y' cgi_data['hours'] = hours cgi_data['minutes'] = minutes if start_time == '' or start_time == 'n/a': @@ -581,7 +674,23 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t cgi_data['start'] = start cgi_data['end'] = end - self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + response = self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + + # Some debug data + data = response.result + error = response.error + status_code = response.status_code + if conf.debug_mode: + self.Debug(server=self.get_name(), host=host, service=service, + debug='Achnowledgement response') + self.Debug(server=self.get_name(), host=host, service=service, + debug="- response: {0}".format(response)) + self.Debug(server=self.get_name(), host=host, service=service, + debug="- data: {0}".format(data)) + self.Debug(server=self.get_name(), host=host, service=service, + debug="- error: {0}".format(error)) + self.Debug(server=self.get_name(), host=host, service=service, + debug="- status_code: {0}".format(status_code)) def get_start_end(self, host): @@ -589,10 +698,32 @@ def get_start_end(self, host): for GUI to get actual downtime start and end from server - they may vary so it's better to get directly from web interface ''' + # Set correct headers + self.session.headers['X-Requested-With'] = 'XMLHttpRequest' + self.session.headers.update({'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}) + try: - downtime = self.FetchURL(self.monitor_cgi_url + '/icingadb/host/schedule-downtime?name=' + self.hosts[host].real_name) - start = downtime.result.find('input', {'name': 'start'})['value'] - end = downtime.result.find('input', {'name': 'end'})['value'] + url = '{0}/icingadb/host/schedule-downtime?name={1}'.format(self.monitor_cgi_url, self.hosts[host].real_name) + downtime = self.FetchURL(url, giveback='raw') + + if downtime.error != '': + return 'n/a', 'n/a' + else: + pageraw = downtime.result + + pagesoup = BeautifulSoup(pageraw, 'html.parser') + + #print('-----------------') + #print(pagesoup.prettify()) + #print('-----------------') + + # Super debug + if conf.debug_mode: + self.Debug(server=self.get_name(), host=host, service='', + debug='[Get downtime start/end] Retrieve html from {0}: {1}'.format(url,pagesoup.prettify())) + + start = pagesoup.find('input', {'name': 'start'})['value'] + end = pagesoup.find('input', {'name': 'end'})['value'] # give values back as tuple return start, end except: @@ -618,7 +749,7 @@ def open_monitor(self, host, service=''): 'host.name': self.hosts[host].real_name}).replace('+', ' ')) if conf.debug_mode: self.Debug(server=self.get_name(), host=host, service=service, - debug='Open host/service monitor web page {0}'.format(url)) + debug='[Open monitor] Open host/service monitor web page {0}'.format(url)) webbrowser_open(url) def GetHost(self, host): From 2fc54ab112d122c728b0bfe6a6317bc5d83f7864 Mon Sep 17 00:00:00 2001 From: Yannick Charton <tontonitch-pro@yahoo.fr> Date: Sat, 8 Oct 2022 15:36:47 +0200 Subject: [PATCH 394/884] Align with origin --- .github/workflows/build-release-latest.yml | 24 ++- .github/workflows/build-release-stable.yml | 187 +++++++++++++++++++++ 2 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/build-release-stable.yml diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 4d4ee7f91..16191399c 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -117,7 +117,7 @@ jobs: if-no-files-found: error macos: - runs-on: macos-12 + runs-on: macos-10.15 needs: test steps: - uses: actions/checkout@v2 @@ -181,6 +181,28 @@ jobs: retention-days: 1 if-no-files-found: error + repo-fedora: + runs-on: ubuntu-latest + needs: [fedora-34, fedora-35, fedora-36, fedora-37] + steps: + # get binaries created by other jobs + - uses: actions/download-artifact@v2 + # organize SSH deploy key for nagstamon-repo + - run: mkdir ~/.ssh + - run: echo "${{ secrets.NAGSTAMON_REPO_KEY_WEB }}" > ~/.ssh/id_ed25519 + - run: chmod -R go-rwx ~/.ssh + # get and prepare nagstamon-jekyll + - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git + - run: rm -rf ${{ env.repo_dir }}/fedora/latest + - run: mkdir -p ${{ env.repo_dir }}/fedora/latest + # copy *.rpm files into nagstamon-jekyll + - run: cp -r artifact/*.rpm ${{ env.repo_dir }}/fedora/latest + # create rpm repo via Fedora container + - run: docker run --rm -v $PWD/${{ env.repo_dir }}/fedora/latest:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo" + # commit and push new binaries to nagstamon-repo + - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" + - run: cd ${{ env.repo_dir }} && git add . && git commit -am "new latest repo" && git push + github-release: runs-on: ubuntu-latest needs: [debian, fedora-34, fedora-35, fedora-36, fedora-37, macos, windows-32, windows-64] diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml new file mode 100644 index 000000000..33a5653e2 --- /dev/null +++ b/.github/workflows/build-release-stable.yml @@ -0,0 +1,187 @@ +name: build-release-stable +on: + push: + tags: 'v*' + +env: + python_win_version: 3.9.12 + repo_dir: nagstamon-jekyll/docs/repo + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.7, 3.9] + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + sudo apt-get install libkrb5-dev + python -m pip install --upgrade pip + pip install pytest pylint #flake8 + if [ -f build/requirements/linux.txt ]; then pip install -r build/requirements/linux.txt; fi + # - name: Lint with flake8 + # run: | + # # stop the build if there are Python syntax errors or undefined names + # flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + # flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with unittest + run: | + python -m unittest tests/test_*.py + + debian: + runs-on: ubuntu-latest + needs: test + steps: + - uses: actions/checkout@v2 + - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + - uses: actions/upload-artifact@v2 + with: + path: build/*.deb + retention-days: 1 + + fedora-34: + runs-on: ubuntu-latest + needs: test + steps: + - uses: actions/checkout@v2 + - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + - uses: actions/upload-artifact@v2 + with: + path: build/*.rpm + retention-days: 1 + + fedora-35: + runs-on: ubuntu-latest + needs: test + steps: + - uses: actions/checkout@v2 + - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + - uses: actions/upload-artifact@v2 + with: + path: build/*.rpm + retention-days: 1 + + fedora-36: + runs-on: ubuntu-latest + needs: test + steps: + - uses: actions/checkout@v2 + - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + - uses: actions/upload-artifact@v2 + with: + path: build/*.rpm + retention-days: 1 + if-no-files-found: error + + fedora-37: + runs-on: ubuntu-latest + needs: test + steps: + - uses: actions/checkout@v2 + - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + - uses: actions/upload-artifact@v2 + with: + path: build/*.rpm + retention-days: 1 + if-no-files-found: error + + macos: + runs-on: macos-10.15 + needs: test + steps: + - uses: actions/checkout@v2 + - run: pip3 install --no-warn-script-location -r build/requirements/macos.txt + - run: cd ${{ github.workspace }}/build; python3 build.py + env: + PYTHONPATH: ${{ github.workspace }} + - uses: actions/upload-artifact@v2 + with: + path: build/*.dmg + retention-days: 1 + + windows-32: + runs-on: windows-latest + needs: test + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: ${{ env.python_win_version }} + architecture: x86 + - run: python -m pip install --no-warn-script-location -r build/requirements/windows.txt + - run: cd ${{ github.workspace }}/build; python build.py + env: + PYTHONPATH: ${{ github.workspace }} + - uses: actions/upload-artifact@v2 + with: + path: | + build/dist/*.zip + build/dist/*.exe + retention-days: 1 + + windows-64: + runs-on: windows-latest + needs: test + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + with: + python-version: ${{ env.python_win_version }} + architecture: x64 + - run: python -m pip install --no-warn-script-location -r build/requirements/windows.txt + - run: cd ${{ github.workspace }}/build; python build.py + env: + PYTHONPATH: ${{ github.workspace }} + - uses: actions/upload-artifact@v2 + with: + path: | + build/dist/*.zip + build/dist/*.exe + retention-days: 1 + + repo-fedora: + runs-on: ubuntu-latest + needs: [fedora-34, fedora-35, fedora-36, fedora-37] + steps: + # get binaries created by other jobs + - uses: actions/download-artifact@v2 + # organize SSH deploy key for nagstamon-repo + - run: mkdir ~/.ssh + - run: echo "${{ secrets.NAGSTAMON_REPO_KEY_WEB }}" > ~/.ssh/id_ed25519 + - run: chmod -R go-rwx ~/.ssh + # get and prepare nagstamon-repo + - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git + - run: rm -rf ${{ env.repo_dir }}/fedora/?? + # copy *.rpm files into nagstamon-jekyll and create rpm repo via Fedora container + - run: for noarch_rpm in artifact/*.noarch.rpm; do version=$(echo $noarch_rpm | python3 -c "file=input(); print(file.split('fedora')[1].split('-')[0])"); mkdir -p mkdir -p ${{ env.repo_dir }}/fedora/$version; cp -r artifact/*.fedora$version-*.rpm ${{ env.repo_dir }}/fedora/$version; docker run --rm -v $PWD/${{ env.repo_dir }}/fedora/$version:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo"; done + # commit and push new binaries to nagstamon-repo + - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" + - run: cd ${{ env.repo_dir }} && git add . && git commit -am "new stable repo" && git push + + upload-release: + runs-on: ubuntu-latest + needs: [debian, fedora-33, fedora-34, fedora-35, fedora-36, macos, windows-32, windows-64] + steps: + - uses: actions/download-artifact@v2 + - run: cd artifact && md5sum *agstamon* > md5sums.txt + - run: cd artifact && sha256sum *agstamon* > sha256sums.txt + - uses: marvinpinto/action-automatic-releases@latest + with: + repo_token: "${{ secrets.GITHUB_TOKEN }}" + prerelease: false + draft: true + files: | + artifact/* From 535a5d4729651dea0b245495fafcced3bbfb9882 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 10 Oct 2022 10:52:51 +0200 Subject: [PATCH 395/884] 3.9-20221010 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index a1f2d8290..b7bba3741 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20221003' + VERSION = '3.9-20221010' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 4edb77cc4..8e86caf0b 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.9-20221003) unstable; urgency=low +nagstamon (3.9-20221010) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Wed, 17 Aug 2022 08:00:00 +0100 From 849e2edd867d463473a8d1c7e38085b6ad1126e9 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 11 Oct 2022 13:06:45 +0200 Subject: [PATCH 396/884] add automatically /check_mk to URL if not existent --- Nagstamon/Servers/Multisite.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Nagstamon/Servers/Multisite.py b/Nagstamon/Servers/Multisite.py index 0d20786b6..9cfe3058e 100644 --- a/Nagstamon/Servers/Multisite.py +++ b/Nagstamon/Servers/Multisite.py @@ -74,6 +74,10 @@ def init_HTTP(self): if self.monitor_url.endswith('/'): self.monitor_url.rstrip('/') + # Add /check_mk if not already existent - makes setting URL simpler + if not self.monitor_url.endswith('/check_mk'): + self.monitor_url += '/check_mk' + # Prepare all urls needed by nagstamon if not yet done if len(self.urls) == len(self.statemap): self.urls = { From 6f99fc810f79967108838482bb13ec92e8d672c1 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 11 Oct 2022 13:49:28 +0200 Subject: [PATCH 397/884] fixed connection error window size --- Nagstamon/Helpers.py | 4 ++-- Nagstamon/QUI/__init__.py | 13 ++++++------- Nagstamon/Servers/Multisite.py | 4 ++++ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Nagstamon/Helpers.py b/Nagstamon/Helpers.py index f2dc20ec0..55fff1aa7 100644 --- a/Nagstamon/Helpers.py +++ b/Nagstamon/Helpers.py @@ -394,8 +394,8 @@ def lock_config_folder(folder): pidFile.seek(0) pidFile.truncate() pidFile.write('{}@{}@{}'.format(curPid, curBootTime, curUserName)) - except Exception as err: - print(err) + except Exception as error: + print(error) return True diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index f1cc0efb4..9bbc166b7 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -89,8 +89,8 @@ # flag to check later if DBus is available DBUS_AVAILABLE = True - except ImportError as ie: - print(ie) + except ImportError as error: + print(error) print('No DBus for desktop notification available.') DBUS_AVAILABLE = False @@ -2078,9 +2078,9 @@ def raise_window_on_all_desktops(self): APP.activePopupWidget() == None: try: self.raise_() - except Exception as err: + except Exception as error: # apparently a race condition could occur on set_mode() - grab it here and continue - print(err) + print(error) def kill(self): """ @@ -3318,7 +3318,8 @@ def get_real_height(self): height = 0 # only count if there is anything to display - there is no use of the headers only - if self.model().rowCount(self) > 0: + if self.is_shown and \ + self.model().rowCount(self) > 0: # height summary starts with headers' height # apparently height works better/without scrollbar if some pixels are added height = self.header().sizeHint().height() + 2 @@ -3919,11 +3920,9 @@ def refresh(self): # check if status changed and notification is necessary # send signal because there are unseen events # status has changed if there are unseen events in the list OR (current status is up AND has been changed since last time) - bla = self.server.get_events_history_count() if (self.server.get_events_history_count() > 0) or \ ((self.server.worst_status_current == 'UP') and ( self.server.worst_status_current != self.server.worst_status_last)): - pass self.status_changed.emit(self.server.name, self.server.worst_status_diff, self.server.worst_status_current) diff --git a/Nagstamon/Servers/Multisite.py b/Nagstamon/Servers/Multisite.py index 0d20786b6..df34589a6 100644 --- a/Nagstamon/Servers/Multisite.py +++ b/Nagstamon/Servers/Multisite.py @@ -74,6 +74,10 @@ def init_HTTP(self): if self.monitor_url.endswith('/'): self.monitor_url.rstrip('/') + # Add /check_mk if not already existent - makes setting URL simpler + if not self.monitor_url.endswith('/check_mk'): + self.monitor_url += '/check_mk' + # Prepare all urls needed by nagstamon if not yet done if len(self.urls) == len(self.statemap): self.urls = { From cbd95bc69a78542cb24a1f945be8767795fa5eb2 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 11 Oct 2022 18:06:56 +0200 Subject: [PATCH 398/884] unfixed connection error window size --- Nagstamon/QUI/__init__.py | 81 ++++++++------------------------------- 1 file changed, 17 insertions(+), 64 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 9bbc166b7..e371c7793 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -1784,20 +1784,6 @@ def calculate_size(self): height = available_height - available_y # when statusbar hangs around in lowermost part of current screen extend from bottom to top else: - ## when height is too large for current screen cut it - # if self.y() + self.height() - real_height < available_y: - # # simply take the available max height if there is no more screen real estate - # # possible because systrayicon resides aside from available space, in fact cutting it - # height = available_height - # y = available_height - height - # else: - # if available_height < real_height: - # y = available_y - # height = available_height - # else: - # y = available_height - real_height - # height = real_height - if available_height < real_height: y = available_y height = available_height @@ -3034,9 +3020,6 @@ class Model(QAbstractTableModel): row_count = 0 column_count = len(HEADERS_HEADERS) - # dummy QModelIndex for dataChanged signal - dummy_qmodelindex = QModelIndex() - # tell treeview if flags columns should be hidden or not hosts_flags_column_needed = Signal(bool) services_flags_column_needed = Signal(bool) @@ -3049,21 +3032,20 @@ def rowCount(self, parent): """ overridden method to get number of rows """ - # return(len(self.data_array)) - return (self.row_count) + return self.row_count def columnCount(self, parent): """ overridden method to get number of columns """ - return (self.column_count) + return self.column_count def headerData(self, column, orientation, role): """ overridden method to get headers of columns """ if role == Qt.ItemDataRole.DisplayRole: - return (HEADERS_HEADERS[column]) + return HEADERS_HEADERS[column] @Slot(list, dict) # @Slot(list) @@ -3076,7 +3058,7 @@ def fill_data_array(self, data_array, info): self.beginResetModel() # first empty the data storage - del (self.data_array[:]) + del self.data_array[:] # use delivered data array self.data_array = data_array @@ -3088,15 +3070,18 @@ def fill_data_array(self, data_array, info): self.hosts_flags_column_needed.emit(info['hosts_flags_column_needed']) self.services_flags_column_needed.emit(info['services_flags_column_needed']) - self.model_data_array_filled.emit() - # new model applied self.endResetModel() + self.model_data_array_filled.emit() + def data(self, index, role): """ overridden method for data delivery for treeview """ + + print(index, role) + if role == Qt.ItemDataRole.DisplayRole: return self.data_array[index.row()][index.column()] @@ -3104,7 +3089,6 @@ def data(self, index, role): return self.data_array[index.row()][10] elif role == Qt.ItemDataRole.BackgroundRole: - # return(self.data_array[index.row()][COLOR_INDEX['background'][index.column()]]) return self.data_array[index.row()][11] elif role == Qt.ItemDataRole.FontRole: @@ -3228,8 +3212,7 @@ def __init__(self, columncount, rowcount, sort_column, sort_order, server, paren self.clipboard_action_all.triggered.connect(self.action_clipboard_action_all) self.clipboard_menu.addAction(self.clipboard_action_all) - self.treeview_model = Model(server=self.server, parent=self) - self.setModel(self.treeview_model) + self.setModel(Model(server=self.server, parent=self)) self.model().model_data_array_filled.connect(self.adjust_table) self.model().hosts_flags_column_needed.connect(self.show_hosts_flags_column) self.model().services_flags_column_needed.connect(self.show_services_flags_column) @@ -3256,9 +3239,6 @@ def __init__(self, columncount, rowcount, sort_column, sort_order, server, paren # quit thread if worker has finished self.worker.finish.connect(self.finish_worker_thread) - # receive information if action menu is shown - # self.action_menu.is_shown.connect(self.worker.track_action_menu) - # get status if started self.worker_thread.started.connect(self.worker.get_status) # start with priority 0 = lowest @@ -3286,8 +3266,6 @@ def __init__(self, columncount, rowcount, sort_column, sort_order, server, paren # display mode - all or only header to display error self.is_shown = False - # connect signal for handling copy from keyboard - @Slot() def set_font(self): """ @@ -3317,9 +3295,12 @@ def get_real_height(self): """ height = 0 + mddl = self.model() + + rwcnt = mddl.rowCount(self) + # only count if there is anything to display - there is no use of the headers only - if self.is_shown and \ - self.model().rowCount(self) > 0: + if self.model().rowCount(self) > 0: # height summary starts with headers' height # apparently height works better/without scrollbar if some pixels are added height = self.header().sizeHint().height() + 2 @@ -3327,7 +3308,7 @@ def get_real_height(self): # maybe simply take nagitems_filtered_count? height += self.indexRowSizeHint(self.model().index(0, 0)) * self.model().rowCount(self) - return (height) + return height def get_real_width(self): width = 0 @@ -3345,7 +3326,6 @@ def adjust_table(self): self.setMinimumHeight(self.get_real_height()) self.setMaximumHeight(self.get_real_height()) self.setSizePolicy(QSizePolicy.Policy.Ignored, QSizePolicy.Policy.Maximum) - # after setting table whole window can be repainted self.ready_to_resize.emit() @@ -4194,8 +4174,6 @@ def sort_data_array(self, sort_column, sort_order, header_clicked=False): if header_clicked: self.data_array_sorted.emit(self.data_array, self.info) - del (first_sort) - # store last sorting column for next sorting only if header was clicked if header_clicked: # last sorting column needs to be cached to avoid losing it @@ -4430,27 +4408,22 @@ class for accessing all dialogs def __init__(self): # settings main dialog - # self.settings = Dialog_Settings(Ui_settings_main) self.settings = Dialog_Settings('settings_main') self.settings.initialize() # server settings dialog - # self.server = Dialog_Server(Ui_settings_server) self.server = Dialog_Server('settings_server') self.server.initialize() # action settings dialog - # self.action = Dialog_Action(Ui_settings_action) self.action = Dialog_Action('settings_action') self.action.initialize() # acknowledge dialog for miserable item context menu - # self.acknowledge = Dialog_Acknowledge(Ui_dialog_acknowledge) self.acknowledge = Dialog_Acknowledge('dialog_acknowledge') self.acknowledge.initialize() # downtime dialog for miserable item context menu - # self.downtime = Dialog_Downtime(Ui_dialog_downtime) self.downtime = Dialog_Downtime('dialog_downtime') self.downtime.initialize() @@ -4461,17 +4434,14 @@ def __init__(self): self.acknowledge.window.button_change_defaults_acknowledge.clicked.connect(self.acknowledge.window.close) # downtime dialog for miserable item context menu - # self.submit = Dialog_Submit(Ui_dialog_submit) self.submit = Dialog_Submit('dialog_submit') self.submit.initialize() # authentication dialog for username/password - # self.authentication = Dialog_Authentication(Ui_dialog_authentication) self.authentication = Dialog_Authentication('dialog_authentication') self.authentication.initialize() # dialog for asking about disabled or not configured servers - # self.server_missing = Dialog_Server_missing(Ui_dialog_server_missing) self.server_missing = Dialog_Server_missing('dialog_server_missing') self.server_missing.initialize() # open server creation dialog @@ -4479,7 +4449,6 @@ def __init__(self): self.server_missing.window.button_enable_server.clicked.connect(self.settings.show) # about dialog - # self.about = Dialog_About(Ui_dialog_about) self.about = Dialog_About('dialog_about') # file chooser Dialog @@ -4739,7 +4708,7 @@ def __init__(self, dialog): self.window.button_check_for_new_version_now.clicked.connect(self.button_check_for_new_version_clicked) self.check_for_new_version.connect(check_version.check) - # avoid offset spinbox if offest is not enabled + # avoid offset spinbox if offset is not enabled self.window.input_radiobutton_fullscreen.clicked.connect(self.toggle_systray_icon_offset) self.window.input_radiobutton_icon_in_systray.clicked.connect(self.toggle_systray_icon_offset) self.window.input_radiobutton_statusbar_floating.clicked.connect(self.toggle_systray_icon_offset) @@ -5101,22 +5070,6 @@ def ok(self): statuswindow.worker.finish.emit() statuswindow.worker_notification.finish.emit() - # # wait until all threads are stopped - # for server_vbox in statuswindow.servers_vbox.children(): - # #server_vbox.table.worker_thread.terminate() - # server_vbox.table.worker_thread.quit() - # server_vbox.table.worker_thread.wait() - # - # # wait until statuswindow worker has finished - # #statuswindow.worker_thread.terminate() - # statuswindow.worker_thread.quit() - # statuswindow.worker_thread.wait() - - # wait until statuswindow notification worker has finished - # statuswindow.worker_notification_thread.terminate() - # statuswindow.worker_notification_thread.quit() - # statuswindow.worker_notification_thread.wait() - # kick out ol' statuswindow statuswindow.kill() From 281f4640efe7c3dd901283669af33175ff123dc7 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 11 Oct 2022 19:11:24 +0200 Subject: [PATCH 399/884] fixed server status label CSS --- Nagstamon/QUI/__init__.py | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index e371c7793..8fb870fec 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -945,8 +945,6 @@ def __init__(self): """ Status window combined from status bar and popup window """ - # attempt with desktop as parent for window Qt.Tool - # QWidget.__init__(self, parent=APP.desktop()) QWidget.__init__(self) # immediately hide to avoid flicker on Windows and OSX @@ -1714,7 +1712,6 @@ def calculate_size(self): # add available_y because it might vary on differently setup screens # calculate top-ness only if window is closed if conf.statusbar_floating: - # if self.is_shown is False: if self.y() < self.screen().geometry().height() // 2 + available_y: self.top = True else: @@ -2736,8 +2733,10 @@ def change(self, text, style=''): else: self.setStyleSheet('''background: {0}; margin-top: 8px; + padding-top: 3px; margin-bottom: 8px; - border-radius: 6px; + padding-bottom: 3px; + border-radius: 4px; '''.format(COLOR_STATUS_LABEL[style])) elif style == '': self.setStyleSheet('') @@ -2911,7 +2910,7 @@ def show_all(self): self.label_stretcher.show() # special table treatment self.table.show() - self.table.is_shown = True + #self.table.is_shown = True @Slot() def show_only_header(self): @@ -2930,7 +2929,7 @@ def show_only_header(self): self.label_stretcher.show() # special table treatment self.table.hide() - self.table.is_shown = False + #self.table.is_shown = False @Slot() def hide_all(self): @@ -2949,7 +2948,7 @@ def hide_all(self): self.label_stretcher.hide() # special table treatment self.table.hide() - self.table.is_shown = False + #self.table.is_shown = False @Slot() def delete(self): @@ -3263,8 +3262,8 @@ def __init__(self, columncount, rowcount, sort_column, sort_order, server, paren # execute action by worker self.request_action.connect(self.worker.execute_action) - # display mode - all or only header to display error - self.is_shown = False + ## display mode - all or only header to display error + #self.is_shown = False @Slot() def set_font(self): @@ -3889,11 +3888,11 @@ def refresh(self): if statuswindow is not None: # do nothing if window is moving to avoid lagging movement if not statuswindow.moving: - # get_status table cells with new data by thread - if len(self.model().data_array) > 0: - self.is_shown = True - else: - self.is_shown = False + ## get_status table cells with new data by thread + #if len(self.model().data_array) > 0: + # self.is_shown = True + #else: + # self.is_shown = False # tell statusbar it should update self.refreshed.emit() From ac23a759f11b06710f368412e45d7eb917c2edb9 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Wed, 12 Oct 2022 12:15:52 +0200 Subject: [PATCH 400/884] 3.9-20221011 --- Nagstamon/Config.py | 2 +- Nagstamon/QUI/__init__.py | 7 ++----- build/debian/changelog | 2 +- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index b7bba3741..684257937 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20221010' + VERSION = '3.9-20221011' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 8fb870fec..667d93bd6 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -1490,7 +1490,7 @@ def show_window(self, event=None): if not vbox.server.all_ok: vbox.show_all() # show at least server vbox header to notify about connection or other errors - if vbox.server.status != '' or vbox.server.refresh_authentication or vbox.server.tls_error: + elif vbox.server.status != '' or vbox.server.refresh_authentication or vbox.server.tls_error: vbox.show_only_header() elif vbox.server.all_ok and vbox.server.status == '': vbox.hide_all() @@ -2896,7 +2896,7 @@ def get_real_height(self): @Slot() def show_all(self): """ - show all items in server vbox except the table - not needed if empty + show all items in server vbox """ self.button_authenticate.hide() self.button_edit.show() @@ -3078,9 +3078,6 @@ def data(self, index, role): """ overridden method for data delivery for treeview """ - - print(index, role) - if role == Qt.ItemDataRole.DisplayRole: return self.data_array[index.row()][index.column()] diff --git a/build/debian/changelog b/build/debian/changelog index 8e86caf0b..a38105685 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.9-20221010) unstable; urgency=low +nagstamon (3.9-20221011) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Wed, 17 Aug 2022 08:00:00 +0100 From d0a766cf63586150306231625db7d93338ddef0f Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Wed, 12 Oct 2022 12:33:51 +0200 Subject: [PATCH 401/884] actions/*@v3 --- .github/workflows/build-release-latest.yml | 48 +++++++++++----------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 16191399c..cdb21d4d4 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -16,9 +16,9 @@ jobs: python-version: [3.7, 3.9] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} - name: Install dependencies @@ -41,11 +41,11 @@ jobs: # runs-on: ubuntu-latest # #needs: test # steps: -# - uses: actions/checkout@v2 +# - uses: actions/checkout@v3 # - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . # # higher privileges needed for creation of AppImage # - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon --cap-add SYS_ADMIN --cap-add MKNOD --device /dev/fuse:mrw build-nagstamon -# - uses: actions/upload-artifact@v2 +# - uses: actions/upload-artifact@v3 # with: # path: build/*.AppImage # retention-days: 1 @@ -55,10 +55,10 @@ jobs: runs-on: ubuntu-latest needs: test steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: build/*.deb retention-days: 1 @@ -68,10 +68,10 @@ jobs: runs-on: ubuntu-latest needs: test steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: build/*.rpm retention-days: 1 @@ -81,10 +81,10 @@ jobs: runs-on: ubuntu-latest needs: test steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: build/*.rpm retention-days: 1 @@ -94,10 +94,10 @@ jobs: runs-on: ubuntu-latest needs: test steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: build/*.rpm retention-days: 1 @@ -107,10 +107,10 @@ jobs: runs-on: ubuntu-latest needs: test steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: build/*.rpm retention-days: 1 @@ -120,12 +120,12 @@ jobs: runs-on: macos-10.15 needs: test steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - run: pip3 install --no-warn-script-location -r build/requirements/macos.txt - run: cd ${{ github.workspace }}/build; python3 build.py env: PYTHONPATH: ${{ github.workspace }} - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: build/*.dmg retention-days: 1 @@ -136,8 +136,8 @@ jobs: runs-on: windows-2019 needs: test steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 with: python-version: ${{ env.python_win_version }} architecture: x86 @@ -149,7 +149,7 @@ jobs: - run: cd ${{ github.workspace }}/build; python build.py env: PYTHONPATH: ${{ github.workspace }} - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: | build/dist/*.zip @@ -162,8 +162,8 @@ jobs: runs-on: windows-2019 needs: test steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 with: python-version: ${{ env.python_win_version }} architecture: x64 @@ -173,7 +173,7 @@ jobs: - run: cd ${{ github.workspace }}/build; python build.py env: PYTHONPATH: ${{ github.workspace }} - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: | build/dist/*.zip @@ -186,7 +186,7 @@ jobs: needs: [fedora-34, fedora-35, fedora-36, fedora-37] steps: # get binaries created by other jobs - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 # organize SSH deploy key for nagstamon-repo - run: mkdir ~/.ssh - run: echo "${{ secrets.NAGSTAMON_REPO_KEY_WEB }}" > ~/.ssh/id_ed25519 @@ -207,7 +207,7 @@ jobs: runs-on: ubuntu-latest needs: [debian, fedora-34, fedora-35, fedora-36, fedora-37, macos, windows-32, windows-64] steps: - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 - run: cd artifact && md5sum *agstamon* > md5sums.txt - run: cd artifact && sha256sum *agstamon* > sha256sums.txt - uses: marvinpinto/action-automatic-releases@latest From 3b440b08f8259f43082d0fd48d9ffeeeda04302f Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Wed, 12 Oct 2022 12:41:17 +0200 Subject: [PATCH 402/884] Create SECURITY.md --- SECURITY.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..910cbdbed --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,15 @@ +# Security Policy + +## Supported Versions + +Versions currently being supported with security updates: + +| Version | Supported | +| ------- | ------------------ | +| 3.8.x | :white_check_mark: | +| < 3.8.x | :x: | +| 3.9.x | :white_check_mark: | + +## Reporting a Vulnerability + +If you found a vulnerability send it to security@nagstamon.de. From 88c2b2c337fe3130bfba5f8656ee6504442eb66b Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Wed, 12 Oct 2022 12:42:37 +0200 Subject: [PATCH 403/884] windows python 3.10.8 --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index cdb21d4d4..92f4e6cb5 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -5,7 +5,7 @@ on: branches: '**' env: - python_win_version: 3.10.5 + python_win_version: 3.10.8 repo_dir: nagstamon-jekyll/docs/repo jobs: From 31ebb9b11178b11165292f88bbf4d5966b327dd2 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Wed, 12 Oct 2022 12:44:48 +0200 Subject: [PATCH 404/884] fix https://github.com/HenriWahl/Nagstamon/security/code-scanning/1 --- Nagstamon/thirdparty/zabbix_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/thirdparty/zabbix_api.py b/Nagstamon/thirdparty/zabbix_api.py index c776b5417..afbc451ff 100644 --- a/Nagstamon/thirdparty/zabbix_api.py +++ b/Nagstamon/thirdparty/zabbix_api.py @@ -201,7 +201,7 @@ def login(self, user='', password='', save=True): raise ZabbixAPIException("No authentication information available.") # don't print the raw password. - hashed_pw_string = "md5(" + hashlib.md5(l_password.encode('utf-8')).hexdigest() + ")" + hashed_pw_string = "sha256(" + hashlib.sha256(l_password.encode('utf-8')).hexdigest() + ")" self.debug(logging.DEBUG, "Trying to login with %s:%s" % (repr(l_user), repr(hashed_pw_string))) obj = self.json_obj('user.login', {'user': l_user, 'password': l_password}, auth=False) From 49dd7cdaef9f047a69b98808fd0742ab35d21e50 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Wed, 12 Oct 2022 12:52:07 +0200 Subject: [PATCH 405/884] Create codeql.yml --- .github/workflows/codeql.yml | 74 ++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 000000000..49484a561 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,74 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ "master" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + schedule: + - cron: '20 23 * * 5' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'python' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" From 083fdb88aec1edf2dc8ea478396fa997e36d09b3 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Wed, 12 Oct 2022 12:59:40 +0200 Subject: [PATCH 406/884] python 3.10.7 --- .github/workflows/build-release-latest.yml | 2 +- .github/workflows/build-release-stable.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 92f4e6cb5..3106293da 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -5,7 +5,7 @@ on: branches: '**' env: - python_win_version: 3.10.8 + python_win_version: 3.10.7 repo_dir: nagstamon-jekyll/docs/repo jobs: diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index 33a5653e2..18d5c2f68 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -4,7 +4,7 @@ on: tags: 'v*' env: - python_win_version: 3.9.12 + python_win_version: 3.10.7 repo_dir: nagstamon-jekyll/docs/repo jobs: From e4c985582d5b1b9b73a436bca13843e57a25fcc1 Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Wed, 12 Oct 2022 16:33:17 +0200 Subject: [PATCH 407/884] avoid glitchy logo in statusbar with custom font size --- Nagstamon/QUI/__init__.py | 11 ++++++----- nagstamon.py | 3 +++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 667d93bd6..561d72ef7 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -2503,20 +2503,21 @@ def adjust_size(self): are shown at the same time - which is very likely the case """ # take height for logo - height = 0 + #height = 0 # run through labels to set font and get height for logo for label in self.color_labels.values(): label.setFont(FONT) - if label.fontMetrics().height() > height: - height = label.fontMetrics().height() + # if label.fontMetrics().height() > height: + # height = label.fontMetrics().height() self.label_message.setFont(FONT) + height = self.label_message.sizeHint().height() # absolutely silly but no other cure in sight # strange miscalculation of nagstamon logo on MacOSX - if OS == OS_DARWIN and 18 <= height <= 24: - height += 1 + #if OS == OS_DARWIN and 18 <= height <= 24: + # height += 1 # adjust logo size to fit to label size self.logo.adjust_size(height, height) diff --git a/nagstamon.py b/nagstamon.py index 6d6061aa7..62b6b8623 100755 --- a/nagstamon.py +++ b/nagstamon.py @@ -18,6 +18,9 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# according to https://gitlab.com/alelec/pip-system-certs/-/issues/7#note_1066992053 +#import pip_system_certs.wrapt_requests + import sys import socket From 6edfe4796742fcac9c0884c43e94d6521250c2ce Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Wed, 12 Oct 2022 16:53:33 +0200 Subject: [PATCH 408/884] avoid glitchy logo in statusbar with custom font size cleanup --- Nagstamon/QUI/__init__.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 561d72ef7..f919da99f 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -2514,12 +2514,7 @@ def adjust_size(self): self.label_message.setFont(FONT) height = self.label_message.sizeHint().height() - # absolutely silly but no other cure in sight - # strange miscalculation of nagstamon logo on MacOSX - #if OS == OS_DARWIN and 18 <= height <= 24: - # height += 1 - - # adjust logo size to fit to label size + # adjust logo size to fit to label size - due to being a square height and width are the same self.logo.adjust_size(height, height) # avoid flickering/artefact by updating immediately From e97d63ccdb4f468e20ce83798eb50796f174e0bc Mon Sep 17 00:00:00 2001 From: Benoit Poulet <benoit.poulet@businessdecision.com> Date: Mon, 17 Oct 2022 12:52:51 +0200 Subject: [PATCH 409/884] Fix for Centreon 22.10, API fixed to 22.04 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit They broke their own rules of API stability. So we need to bring back ugly «if version» --- Nagstamon/Servers/Centreon/CentreonAPI.py | 149 ++++++++++++---------- 1 file changed, 82 insertions(+), 67 deletions(-) diff --git a/Nagstamon/Servers/Centreon/CentreonAPI.py b/Nagstamon/Servers/Centreon/CentreonAPI.py index c7d06d409..52b7045e3 100644 --- a/Nagstamon/Servers/Centreon/CentreonAPI.py +++ b/Nagstamon/Servers/Centreon/CentreonAPI.py @@ -82,22 +82,27 @@ def init_config(self): return (errors_occured) self.centreon_version_major = int(data["web"]["major"]) + self.centreon_version_minor = int(data["web"]["minor"]) + if conf.debug_mode is True: + self.Debug(server='[' + self.get_name() + ']', debug='Centreon version detected : ' + str(self.centreon_version_major) + '.' + str(self.centreon_version_minor)) if self.centreon_version_major >= 21: - # starting from 21.xx the API to use is «latest», until something change - if conf.debug_mode is True: - self.Debug(server='[' + self.get_name() + ']', debug='Centreon code version selected : >= 21') # URLs for browser shortlinks/buttons on popup window self.BROWSER_URLS = {'monitor': '$MONITOR$/monitoring/resources', 'hosts': '$MONITOR$/monitoring/resources', 'services': '$MONITOR$/monitoring/resources', 'history': '$MONITOR$/main.php?p=20301'} # RestAPI version - self.restapi_version = "latest" + if self.centreon_version_major == 21: + self.restapi_version = "latest" + else: + self.restapi_version = "v22.04" + if conf.debug_mode is True: + self.Debug(server='[' + self.get_name() + ']', debug='Centreon API version used : ' + self.restapi_version) else: if conf.debug_mode is True: - self.Debug(server='[' + self.get_name() + ']', debug='Unsupported Centreon version') + self.Debug(server='[' + self.get_name() + ']', debug='Unsupported Centreon version, must be >= 21') # Changed this because define_url was called 2 times if not self.tls_error and self.urls_centreon is None: @@ -118,9 +123,6 @@ def define_url(self): } self.urls_centreon = urls_centreon_api_v2 - if conf.debug_mode == True: - self.Debug(server='[' + self.get_name() + ']', - debug='URLs defined for Centreon version : %s' % (self.centreon_version_major)) def open_monitor(self, host, service=''): # Used for self.MENU_ACTIONS = ['Monitor'] @@ -335,28 +337,35 @@ def _get_status(self): if errors_occured is not False: return (errors_occured) - for alerts in data["result"]: - new_host = alerts["name"] - self.new_hosts[new_host] = GenericHost() - self.new_hosts[new_host].name = alerts["name"] - self.new_hosts[new_host].server = self.name - self.new_hosts[new_host].criticality = alerts["severity_level"] - self.new_hosts[new_host].status = alerts["status"]["name"] - self.new_hosts[new_host].last_check = alerts["last_check"] - # last_state_change = datetime.strptime(alerts["last_status_change"], '%Y-%m-%dT%H:%M:%S%z').replace(tzinfo=None) - self.new_hosts[new_host].duration = alerts["duration"] - self.new_hosts[new_host].attempt = alerts["tries"] - self.new_hosts[new_host].status_information = alerts["information"] - self.new_hosts[new_host].passiveonly = alerts["passive_checks"] - self.new_hosts[new_host].notifications_disabled = not alerts["notification_enabled"] - self.new_hosts[new_host].flapping = alerts["flapping"] - self.new_hosts[new_host].acknowledged = alerts["acknowledged"] - self.new_hosts[new_host].scheduled_downtime = alerts["in_downtime"] - if "(S)" in alerts["tries"]: - self.new_hosts[new_host].status_type = self.HARD_SOFT['(S)'] - else: - self.new_hosts[new_host].status_type = self.HARD_SOFT['(H)'] - self.Debug(server='[' + self.get_name() + ']', debug='Host indexed : ' + new_host) + if data["meta"]["total"] == 0: + self.Debug(server='[' + self.get_name() + ']', debug='No host down') + else: + for alerts in data["result"]: + new_host = alerts["name"] + self.new_hosts[new_host] = GenericHost() + self.new_hosts[new_host].name = alerts["name"] + self.new_hosts[new_host].server = self.name + # API inconsistency, even by fixing exact version number + if self.centreon_version_major == 21 or (self.centreon_version_major == 22 and self.centreon_version_minor == 4): + self.new_hosts[new_host].criticality = alerts["severity_level"] + else: + self.new_hosts[new_host].criticality = alerts["severity"] + self.new_hosts[new_host].status = alerts["status"]["name"] + self.new_hosts[new_host].last_check = alerts["last_check"] + # last_state_change = datetime.strptime(alerts["last_status_change"], '%Y-%m-%dT%H:%M:%S%z').replace(tzinfo=None) + self.new_hosts[new_host].duration = alerts["duration"] + self.new_hosts[new_host].attempt = alerts["tries"] + self.new_hosts[new_host].status_information = alerts["information"] + self.new_hosts[new_host].passiveonly = alerts["passive_checks"] + self.new_hosts[new_host].notifications_disabled = not alerts["notification_enabled"] + self.new_hosts[new_host].flapping = alerts["flapping"] + self.new_hosts[new_host].acknowledged = alerts["acknowledged"] + self.new_hosts[new_host].scheduled_downtime = alerts["in_downtime"] + if "(S)" in alerts["tries"]: + self.new_hosts[new_host].status_type = self.HARD_SOFT['(S)'] + else: + self.new_hosts[new_host].status_type = self.HARD_SOFT['(H)'] + self.Debug(server='[' + self.get_name() + ']', debug='Host indexed : ' + new_host) except: traceback.print_exc(file=sys.stdout) @@ -383,43 +392,49 @@ def _get_status(self): if errors_occured is not False: return (errors_occured) - for alerts in data["result"]: - if alerts["type"] == "metaservice": - new_host = "Meta_Services" - else: - new_host = alerts["parent"]["name"] - new_service = alerts["name"] - # Needed if non-ok services are on a UP host - if not new_host in self.new_hosts: - self.new_hosts[new_host] = GenericHost() - self.new_hosts[new_host].name = new_host - self.new_hosts[new_host].status = 'UP' - self.new_hosts[new_host].services[new_service] = GenericService() - # Attributs à remplir - self.Debug(server='[' + self.get_name() + ']', - debug='Service indexed : ' + new_host + ' / ' + new_service) - - self.new_hosts[new_host].services[new_service].server = self.name - self.new_hosts[new_host].services[new_service].host = new_host - self.new_hosts[new_host].services[new_service].name = new_service - self.new_hosts[new_host].services[new_service].status = alerts["status"]["name"] - self.new_hosts[new_host].services[new_service].last_check = alerts["last_check"] - # last_state_change = datetime.strptime(alerts["last_state_change"], '%Y-%m-%dT%H:%M:%S%z').replace(tzinfo=None) - # self.new_hosts[new_host].services[new_service].duration = datetime.now() - last_state_change - self.new_hosts[new_host].services[new_service].duration = alerts["duration"] - self.new_hosts[new_host].services[new_service].attempt = alerts["tries"] - self.new_hosts[new_host].services[new_service].status_information = alerts["information"] - self.new_hosts[new_host].services[new_service].passiveonly = alerts["passive_checks"] - self.new_hosts[new_host].services[new_service].notifications_disabled = not alerts[ - "notification_enabled"] - self.new_hosts[new_host].services[new_service].flapping = alerts["flapping"] - self.new_hosts[new_host].services[new_service].acknowledged = alerts["acknowledged"] - self.new_hosts[new_host].services[new_service].scheduled_downtime = alerts["in_downtime"] - if "(S)" in alerts["tries"]: - self.new_hosts[new_host].services[new_service].status_type = self.HARD_SOFT['(S)'] - else: - self.new_hosts[new_host].services[new_service].status_type = self.HARD_SOFT['(H)'] - self.new_hosts[new_host].services[new_service].criticality = alerts["severity_level"] + if data["meta"]["total"] == 0: + self.Debug(server='[' + self.get_name() + ']', debug='No service down') + else: + for alerts in data["result"]: + if alerts["type"] == "metaservice": + new_host = "Meta_Services" + else: + new_host = alerts["parent"]["name"] + new_service = alerts["name"] + # Needed if non-ok services are on a UP host + if not new_host in self.new_hosts: + self.new_hosts[new_host] = GenericHost() + self.new_hosts[new_host].name = new_host + self.new_hosts[new_host].status = 'UP' + self.new_hosts[new_host].services[new_service] = GenericService() + # Attributs à remplir + self.Debug(server='[' + self.get_name() + ']', + debug='Service indexed : ' + new_host + ' / ' + new_service) + + self.new_hosts[new_host].services[new_service].server = self.name + self.new_hosts[new_host].services[new_service].host = new_host + self.new_hosts[new_host].services[new_service].name = new_service + self.new_hosts[new_host].services[new_service].status = alerts["status"]["name"] + self.new_hosts[new_host].services[new_service].last_check = alerts["last_check"] + # last_state_change = datetime.strptime(alerts["last_state_change"], '%Y-%m-%dT%H:%M:%S%z').replace(tzinfo=None) + # self.new_hosts[new_host].services[new_service].duration = datetime.now() - last_state_change + self.new_hosts[new_host].services[new_service].duration = alerts["duration"] + self.new_hosts[new_host].services[new_service].attempt = alerts["tries"] + self.new_hosts[new_host].services[new_service].status_information = alerts["information"] + self.new_hosts[new_host].services[new_service].passiveonly = alerts["passive_checks"] + self.new_hosts[new_host].services[new_service].notifications_disabled = not alerts["notification_enabled"] + self.new_hosts[new_host].services[new_service].flapping = alerts["flapping"] + self.new_hosts[new_host].services[new_service].acknowledged = alerts["acknowledged"] + self.new_hosts[new_host].services[new_service].scheduled_downtime = alerts["in_downtime"] + if "(S)" in alerts["tries"]: + self.new_hosts[new_host].services[new_service].status_type = self.HARD_SOFT['(S)'] + else: + self.new_hosts[new_host].services[new_service].status_type = self.HARD_SOFT['(H)'] + # API inconsistency, even by fixing exact version number + if self.centreon_version_major == 21 or (self.centreon_version_major == 22 and self.centreon_version_minor == 4): + self.new_hosts[new_host].services[new_service].criticality = alerts["severity_level"] + else: + self.new_hosts[new_host].services[new_service].criticality = alerts["severity"] except: traceback.print_exc(file=sys.stdout) From 11667292ec913e6aeb539e20daed30fcbcffc179 Mon Sep 17 00:00:00 2001 From: iamurer <simon.murer@stud.hslu.ch> Date: Mon, 17 Oct 2022 14:27:47 +0200 Subject: [PATCH 410/884] add support for token based authentication --- Nagstamon/QUI/__init__.py | 16 +++++++++++++++- Nagstamon/Servers/Generic.py | 10 ++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index f919da99f..1aed34ccc 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -5663,6 +5663,10 @@ def __init__(self, dialog): self.window.input_lineedit_password, self.window.input_checkbox_save_password] + self.AUTHENTICATION_BEARER_WIDGETS = [ + self.window.label_username, + self.window.input_lineedit_username] + self.AUTHENTICATION_ECP_WIDGETS = [ self.window.label_idp_ecp_endpoint, self.window.input_lineedit_idp_ecp_endpoint] @@ -5680,7 +5684,7 @@ def __init__(self, dialog): self.window.button_choose_custom_cert_ca_file.clicked.connect(self.choose_custom_cert_ca_file) # fill authentication combobox - self.window.input_combobox_authentication.addItems(['Basic', 'Digest', 'Kerberos']) + self.window.input_combobox_authentication.addItems(['Basic', 'Digest', 'Kerberos', 'Bearer']) if ECP_AVAILABLE is True: self.window.input_combobox_authentication.addItems(['ECP']) @@ -5726,6 +5730,16 @@ def toggle_authentication(self): for widget in self.AUTHENTICATION_ECP_WIDGETS: widget.hide() + # change credential input for bearer auth + if self.window.input_combobox_authentication.currentText() == 'Bearer': + for widget in self.AUTHENTICATION_BEARER_WIDGETS: + widget.hide() + self.window.label_password.setText('Token') + else: + for widget in self.AUTHENTICATION_BEARER_WIDGETS: + widget.show() + self.window.label_password.setText('Password') + # after hiding authentication widgets dialog might shrink self.window.adjustSize() diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index 130b2cd39..5663e0744 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -90,6 +90,14 @@ except ImportError: pass +# add possibility for bearer auth to requests +class BearerAuth(requests.auth.AuthBase): + def __init__(self, token): + self.token = token + def __call__(self, r): + r.headers["Authorization"] = "Bearer " + self.token + return r + class GenericServer(object): ''' @@ -297,6 +305,8 @@ def create_session(self): session.auth = HTTPECPAuth(self.idp_ecp_endpoint, username=self.username, password=self.password) elif self.authentication == 'kerberos': session.auth = HTTPSKerberos() + elif self.authentication == 'bearer': + session.auth = BearerAuth(self.password) # default to check TLS validity if self.ignore_cert: From 82fde82e1985cb517761b297b4bbad90aa63b494 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 18 Oct 2022 00:23:50 +0200 Subject: [PATCH 411/884] 3.9-20221018 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 684257937..8fc62f185 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20221011' + VERSION = '3.9-20221018' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index a38105685..a4f70a6f2 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.9-20221011) unstable; urgency=low +nagstamon (3.9-20221018) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Wed, 17 Aug 2022 08:00:00 +0100 From 0e0b8ae3517503ee78f90129affd38b17cbe9b02 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 18 Oct 2022 20:09:54 +0200 Subject: [PATCH 412/884] new build release yml --- .github/workflows/build-release-stable.yml | 72 +++++++++++++--------- 1 file changed, 43 insertions(+), 29 deletions(-) diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index 18d5c2f68..88bf5fb87 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -15,16 +15,16 @@ jobs: python-version: [3.7, 3.9] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | - sudo apt-get install libkrb5-dev + sudo apt-get install libdbus-1-dev libkrb5-dev python -m pip install --upgrade pip - pip install pytest pylint #flake8 + pip install pytest pylint wheel #flake8 if [ -f build/requirements/linux.txt ]; then pip install -r build/requirements/linux.txt; fi # - name: Lint with flake8 # run: | @@ -40,46 +40,49 @@ jobs: runs-on: ubuntu-latest needs: test steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: build/*.deb retention-days: 1 + if-no-files-found: error fedora-34: runs-on: ubuntu-latest needs: test steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: build/*.rpm retention-days: 1 + if-no-files-found: error fedora-35: runs-on: ubuntu-latest needs: test steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: build/*.rpm retention-days: 1 + if-no-files-found: error fedora-36: runs-on: ubuntu-latest needs: test steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: build/*.rpm retention-days: 1 @@ -89,10 +92,10 @@ jobs: runs-on: ubuntu-latest needs: test steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: build/*.rpm retention-days: 1 @@ -102,67 +105,78 @@ jobs: runs-on: macos-10.15 needs: test steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - run: pip3 install --no-warn-script-location -r build/requirements/macos.txt - run: cd ${{ github.workspace }}/build; python3 build.py env: PYTHONPATH: ${{ github.workspace }} - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: build/*.dmg retention-days: 1 + if-no-files-found: error windows-32: - runs-on: windows-latest + # better depend on stable build image + runs-on: windows-2019 needs: test steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 with: python-version: ${{ env.python_win_version }} architecture: x86 + # no PyQt6 for win32 available on Pypi.org + - run: ((Get-Content -path build/requirements/windows.txt -Raw) -replace 'pyqt6.*','pyqt5') | Set-Content -Path build/requirements/windows.txt - run: python -m pip install --no-warn-script-location -r build/requirements/windows.txt + # pretty hacky but no other idea to avoid gssapi being installed which breaks requests-kerberos + - run: python -m pip uninstall -y gssapi requests-gssapi - run: cd ${{ github.workspace }}/build; python build.py env: PYTHONPATH: ${{ github.workspace }} - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: | build/dist/*.zip build/dist/*.exe retention-days: 1 + if-no-files-found: error windows-64: - runs-on: windows-latest + # better depend on stable build image + runs-on: windows-2019 needs: test steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 with: python-version: ${{ env.python_win_version }} architecture: x64 - run: python -m pip install --no-warn-script-location -r build/requirements/windows.txt + # pretty hacky but no other idea to avoid gssapi being installed which breaks requests-kerberos + - run: python -m pip uninstall -y gssapi requests-gssapi - run: cd ${{ github.workspace }}/build; python build.py env: PYTHONPATH: ${{ github.workspace }} - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: | build/dist/*.zip build/dist/*.exe retention-days: 1 + if-no-files-found: error repo-fedora: runs-on: ubuntu-latest needs: [fedora-34, fedora-35, fedora-36, fedora-37] steps: # get binaries created by other jobs - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 # organize SSH deploy key for nagstamon-repo - run: mkdir ~/.ssh - run: echo "${{ secrets.NAGSTAMON_REPO_KEY_WEB }}" > ~/.ssh/id_ed25519 - run: chmod -R go-rwx ~/.ssh - # get and prepare nagstamon-repo + # get and prepare nagstamon-jekyll - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - run: rm -rf ${{ env.repo_dir }}/fedora/?? # copy *.rpm files into nagstamon-jekyll and create rpm repo via Fedora container @@ -171,11 +185,11 @@ jobs: - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - run: cd ${{ env.repo_dir }} && git add . && git commit -am "new stable repo" && git push - upload-release: + github-release: runs-on: ubuntu-latest - needs: [debian, fedora-33, fedora-34, fedora-35, fedora-36, macos, windows-32, windows-64] + needs: [debian, fedora-34, fedora-35, fedora-36, fedora-37, macos, windows-32, windows-64] steps: - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 - run: cd artifact && md5sum *agstamon* > md5sums.txt - run: cd artifact && sha256sum *agstamon* > sha256sums.txt - uses: marvinpinto/action-automatic-releases@latest From bdf33af0e20c74e9b05f8548e9fd9c4adc38e9ec Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 18 Oct 2022 20:32:46 +0200 Subject: [PATCH 413/884] prepare stable release --- build/debian/changelog | 11 +++++++++-- build/docker/Dockerfile-fedora-34 | 4 ++++ build/docker/Dockerfile-fedora-35 | 4 ++++ build/docker/Dockerfile-fedora-36 | 8 ++++---- build/docker/Dockerfile-fedora-37 | 8 ++++---- build/redhat/nagstamon.spec | 10 +++++----- 6 files changed, 30 insertions(+), 15 deletions(-) diff --git a/build/debian/changelog b/build/debian/changelog index a4f70a6f2..794123e88 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,14 @@ nagstamon (3.9-20221018) unstable; urgency=low * New upstream - - -- Henri Wahl <henri@nagstamon.de> Wed, 17 Aug 2022 08:00:00 +0100 + - upgrade to Qt6 GUI framework + - updates for latest Centreon + - added bearer authentication + - added IcingaDB support + - extended Zabbix support + - fixes for GUI + - fixes for TLS + - fixes for Checkmk + -- Henri Wahl <henri@nagstamon.de> Wed, 19 Oct 2022 08:00:00 +0100 nagstamon (3.8.0) stable; urgency=low * New upstream diff --git a/build/docker/Dockerfile-fedora-34 b/build/docker/Dockerfile-fedora-34 index 5b28a9647..8ce6e630c 100644 --- a/build/docker/Dockerfile-fedora-34 +++ b/build/docker/Dockerfile-fedora-34 @@ -21,5 +21,9 @@ RUN dnf -y install desktop-file-utils \ qt5-qtmultimedia \ rpm-build +# simple workaround for still building legacy Fedora 35 RPM with Qt5 support because Qt66 is not available +WORKDIR /nagstamon/build +RUN sed -i s/qt6/qt5/g redhat/nagstamon.spec + CMD cd /nagstamon/build && \ /usr/bin/python3 build.py diff --git a/build/docker/Dockerfile-fedora-35 b/build/docker/Dockerfile-fedora-35 index 01457f023..21156f675 100644 --- a/build/docker/Dockerfile-fedora-35 +++ b/build/docker/Dockerfile-fedora-35 @@ -21,5 +21,9 @@ RUN dnf -y install desktop-file-utils \ qt5-qtmultimedia \ rpm-build +# simple workaround for still building legacy Fedora 35 RPM with Qt5 support because Qt66 is not available +WORKDIR /nagstamon/build +RUN sed -i s/qt6/qt5/g redhat/nagstamon.spec + CMD cd /nagstamon/build && \ /usr/bin/python3 build.py diff --git a/build/docker/Dockerfile-fedora-36 b/build/docker/Dockerfile-fedora-36 index 353a3205b..d958eaba4 100644 --- a/build/docker/Dockerfile-fedora-36 +++ b/build/docker/Dockerfile-fedora-36 @@ -12,13 +12,13 @@ RUN dnf -y install desktop-file-utils \ python3-keyring \ python3-lxml \ python3-psutil \ - python3-qt5 \ - python3-qt5-devel \ + python3-qt6 \ + python3-qt6-devel \ python3-requests \ python3-requests-kerberos \ python3-SecretStorage \ - qt5-qtsvg \ - qt5-qtmultimedia \ + qt6-qtsvg \ + qt6-qtmultimedia \ rpm-build CMD cd /nagstamon/build && \ diff --git a/build/docker/Dockerfile-fedora-37 b/build/docker/Dockerfile-fedora-37 index fd595a69a..fa03b59ff 100644 --- a/build/docker/Dockerfile-fedora-37 +++ b/build/docker/Dockerfile-fedora-37 @@ -12,13 +12,13 @@ RUN dnf -y install desktop-file-utils \ python3-keyring \ python3-lxml \ python3-psutil \ - python3-qt5 \ - python3-qt5-devel \ + python3-qt6 \ + python3-qt6-devel \ python3-requests \ python3-requests-kerberos \ python3-SecretStorage \ - qt5-qtsvg \ - qt5-qtmultimedia \ + qt6-qtsvg \ + qt6-qtmultimedia \ rpm-build CMD cd /nagstamon/build && \ diff --git a/build/redhat/nagstamon.spec b/build/redhat/nagstamon.spec index dbc380f02..dfc45c080 100644 --- a/build/redhat/nagstamon.spec +++ b/build/redhat/nagstamon.spec @@ -3,7 +3,7 @@ %global shortcommit %(c=%{commit}; echo ${c:0:7}) Name: nagstamon -Version: 3.1.99 +Version: 3.10.0 Release: 0.1.%{gitdate}git%{shortcommit}%{?dist} Summary: Nagios status monitor for desktop @@ -13,7 +13,7 @@ Source0: https://github.com/HenriWahl/Nagstamon/archive/%{commit}/nagstamon-%{c BuildArch: noarch BuildRequires: python3-devel -BuildRequires: python3-qt5-devel +BuildRequires: python3-qt6-devel BuildRequires: desktop-file-utils Requires: python3 Requires: python3-beautifulsoup4 @@ -25,12 +25,12 @@ Requires: python3-keyring Requires: python3-lxml Requires: python3-psutil Requires: python3-pysocks -Requires: python3-qt5 +Requires: python3-qt6 Requires: python3-requests Requires: python3-requests-kerberos Requires: python3-SecretStorage -Requires: qt5-qtsvg -Requires: qt5-qtmultimedia +Requires: qt6-qtsvg +Requires: qt6-qtmultimedia %description Nagstamon is a Nagios status monitor which takes place in system tray From 6ccefcc84d407b7ef6e36680c2d1669fcd712951 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 18 Oct 2022 20:58:49 +0200 Subject: [PATCH 414/884] only qt5 for fedora --- build/docker/Dockerfile-fedora-34 | 4 ---- build/docker/Dockerfile-fedora-35 | 4 ---- build/docker/Dockerfile-fedora-36 | 8 ++++---- build/docker/Dockerfile-fedora-37 | 8 ++++---- build/redhat/nagstamon.spec | 8 ++++---- 5 files changed, 12 insertions(+), 20 deletions(-) diff --git a/build/docker/Dockerfile-fedora-34 b/build/docker/Dockerfile-fedora-34 index 8ce6e630c..5b28a9647 100644 --- a/build/docker/Dockerfile-fedora-34 +++ b/build/docker/Dockerfile-fedora-34 @@ -21,9 +21,5 @@ RUN dnf -y install desktop-file-utils \ qt5-qtmultimedia \ rpm-build -# simple workaround for still building legacy Fedora 35 RPM with Qt5 support because Qt66 is not available -WORKDIR /nagstamon/build -RUN sed -i s/qt6/qt5/g redhat/nagstamon.spec - CMD cd /nagstamon/build && \ /usr/bin/python3 build.py diff --git a/build/docker/Dockerfile-fedora-35 b/build/docker/Dockerfile-fedora-35 index 21156f675..01457f023 100644 --- a/build/docker/Dockerfile-fedora-35 +++ b/build/docker/Dockerfile-fedora-35 @@ -21,9 +21,5 @@ RUN dnf -y install desktop-file-utils \ qt5-qtmultimedia \ rpm-build -# simple workaround for still building legacy Fedora 35 RPM with Qt5 support because Qt66 is not available -WORKDIR /nagstamon/build -RUN sed -i s/qt6/qt5/g redhat/nagstamon.spec - CMD cd /nagstamon/build && \ /usr/bin/python3 build.py diff --git a/build/docker/Dockerfile-fedora-36 b/build/docker/Dockerfile-fedora-36 index d958eaba4..353a3205b 100644 --- a/build/docker/Dockerfile-fedora-36 +++ b/build/docker/Dockerfile-fedora-36 @@ -12,13 +12,13 @@ RUN dnf -y install desktop-file-utils \ python3-keyring \ python3-lxml \ python3-psutil \ - python3-qt6 \ - python3-qt6-devel \ + python3-qt5 \ + python3-qt5-devel \ python3-requests \ python3-requests-kerberos \ python3-SecretStorage \ - qt6-qtsvg \ - qt6-qtmultimedia \ + qt5-qtsvg \ + qt5-qtmultimedia \ rpm-build CMD cd /nagstamon/build && \ diff --git a/build/docker/Dockerfile-fedora-37 b/build/docker/Dockerfile-fedora-37 index fa03b59ff..fd595a69a 100644 --- a/build/docker/Dockerfile-fedora-37 +++ b/build/docker/Dockerfile-fedora-37 @@ -12,13 +12,13 @@ RUN dnf -y install desktop-file-utils \ python3-keyring \ python3-lxml \ python3-psutil \ - python3-qt6 \ - python3-qt6-devel \ + python3-qt5 \ + python3-qt5-devel \ python3-requests \ python3-requests-kerberos \ python3-SecretStorage \ - qt6-qtsvg \ - qt6-qtmultimedia \ + qt5-qtsvg \ + qt5-qtmultimedia \ rpm-build CMD cd /nagstamon/build && \ diff --git a/build/redhat/nagstamon.spec b/build/redhat/nagstamon.spec index dfc45c080..cd3967001 100644 --- a/build/redhat/nagstamon.spec +++ b/build/redhat/nagstamon.spec @@ -13,7 +13,7 @@ Source0: https://github.com/HenriWahl/Nagstamon/archive/%{commit}/nagstamon-%{c BuildArch: noarch BuildRequires: python3-devel -BuildRequires: python3-qt6-devel +BuildRequires: python3-qt5-devel BuildRequires: desktop-file-utils Requires: python3 Requires: python3-beautifulsoup4 @@ -25,12 +25,12 @@ Requires: python3-keyring Requires: python3-lxml Requires: python3-psutil Requires: python3-pysocks -Requires: python3-qt6 +Requires: python3-qt5 Requires: python3-requests Requires: python3-requests-kerberos Requires: python3-SecretStorage -Requires: qt6-qtsvg -Requires: qt6-qtmultimedia +Requires: qt5-qtsvg +Requires: qt5-qtmultimedia %description Nagstamon is a Nagios status monitor which takes place in system tray From 53dcc4f47edfc4efbd978e4c55f822eb3e0f7e6e Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 18 Oct 2022 21:07:43 +0200 Subject: [PATCH 415/884] pyqt6 instead of qt6 for fedora --- build/docker/Dockerfile-fedora-34 | 2 ++ build/docker/Dockerfile-fedora-35 | 2 ++ build/docker/Dockerfile-fedora-36 | 8 ++++---- build/docker/Dockerfile-fedora-37 | 8 ++++---- build/redhat/nagstamon.spec | 8 ++++---- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/build/docker/Dockerfile-fedora-34 b/build/docker/Dockerfile-fedora-34 index 5b28a9647..ad1e4141f 100644 --- a/build/docker/Dockerfile-fedora-34 +++ b/build/docker/Dockerfile-fedora-34 @@ -22,4 +22,6 @@ RUN dnf -y install desktop-file-utils \ rpm-build CMD cd /nagstamon/build && \ + sed -i s/pyqt6/pyqt5/g redhat/nagstamon.spec && \ + sed -i s/qt6/qt5/g redhat/nagstamon.spec && \ /usr/bin/python3 build.py diff --git a/build/docker/Dockerfile-fedora-35 b/build/docker/Dockerfile-fedora-35 index 01457f023..d6d7929d8 100644 --- a/build/docker/Dockerfile-fedora-35 +++ b/build/docker/Dockerfile-fedora-35 @@ -22,4 +22,6 @@ RUN dnf -y install desktop-file-utils \ rpm-build CMD cd /nagstamon/build && \ + sed -i s/pyqt6/pyqt5/g redhat/nagstamon.spec && \ + sed -i s/qt6/qt5/g redhat/nagstamon.spec && \ /usr/bin/python3 build.py diff --git a/build/docker/Dockerfile-fedora-36 b/build/docker/Dockerfile-fedora-36 index 353a3205b..11b08bf0d 100644 --- a/build/docker/Dockerfile-fedora-36 +++ b/build/docker/Dockerfile-fedora-36 @@ -12,13 +12,13 @@ RUN dnf -y install desktop-file-utils \ python3-keyring \ python3-lxml \ python3-psutil \ - python3-qt5 \ - python3-qt5-devel \ + python3-pyqt6 \ + python3-pyqt6-devel \ python3-requests \ python3-requests-kerberos \ python3-SecretStorage \ - qt5-qtsvg \ - qt5-qtmultimedia \ + qt6-qtsvg \ + qt6-qtmultimedia \ rpm-build CMD cd /nagstamon/build && \ diff --git a/build/docker/Dockerfile-fedora-37 b/build/docker/Dockerfile-fedora-37 index fd595a69a..7bf37c740 100644 --- a/build/docker/Dockerfile-fedora-37 +++ b/build/docker/Dockerfile-fedora-37 @@ -12,13 +12,13 @@ RUN dnf -y install desktop-file-utils \ python3-keyring \ python3-lxml \ python3-psutil \ - python3-qt5 \ - python3-qt5-devel \ + python3-pyqt6 \ + python3-pyqt6-devel \ python3-requests \ python3-requests-kerberos \ python3-SecretStorage \ - qt5-qtsvg \ - qt5-qtmultimedia \ + qt6-qtsvg \ + qt6-qtmultimedia \ rpm-build CMD cd /nagstamon/build && \ diff --git a/build/redhat/nagstamon.spec b/build/redhat/nagstamon.spec index cd3967001..cfea8acfb 100644 --- a/build/redhat/nagstamon.spec +++ b/build/redhat/nagstamon.spec @@ -13,7 +13,7 @@ Source0: https://github.com/HenriWahl/Nagstamon/archive/%{commit}/nagstamon-%{c BuildArch: noarch BuildRequires: python3-devel -BuildRequires: python3-qt5-devel +BuildRequires: python3-pyqt6-devel BuildRequires: desktop-file-utils Requires: python3 Requires: python3-beautifulsoup4 @@ -25,12 +25,12 @@ Requires: python3-keyring Requires: python3-lxml Requires: python3-psutil Requires: python3-pysocks -Requires: python3-qt5 +Requires: python3-pyqt6 Requires: python3-requests Requires: python3-requests-kerberos Requires: python3-SecretStorage -Requires: qt5-qtsvg -Requires: qt5-qtmultimedia +Requires: qt6-qtsvg +Requires: qt6-qtmultimedia %description Nagstamon is a Nagios status monitor which takes place in system tray From 72c7ad875e1819f5d03d4f8413246f17dbb86a47 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 18 Oct 2022 21:30:46 +0200 Subject: [PATCH 416/884] repo-fedora needs all artifacts --- .github/workflows/build-release-latest.yml | 3 ++- .github/workflows/build-release-stable.yml | 3 ++- build/build.py | 2 +- build/docker/Dockerfile-fedora-34 | 1 + build/docker/Dockerfile-fedora-35 | 1 + 5 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 3106293da..b718c2a49 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -183,7 +183,8 @@ jobs: repo-fedora: runs-on: ubuntu-latest - needs: [fedora-34, fedora-35, fedora-36, fedora-37] + # if not all are ready there might be trouble when downloading artifacts + needs: [debian, fedora-34, fedora-35, fedora-36, fedora-37, macos, windows-32, windows-64] steps: # get binaries created by other jobs - uses: actions/download-artifact@v3 diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index 88bf5fb87..e7b352dd1 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -168,7 +168,8 @@ jobs: repo-fedora: runs-on: ubuntu-latest - needs: [fedora-34, fedora-35, fedora-36, fedora-37] + # if not all are ready there might be trouble when downloading artifacts + needs: [debian, fedora-34, fedora-35, fedora-36, fedora-37, macos, windows-32, windows-64] steps: # get binaries created by other jobs - uses: actions/download-artifact@v3 diff --git a/build/build.py b/build/build.py index 6c748064f..4a2b686ac 100644 --- a/build/build.py +++ b/build/build.py @@ -87,7 +87,7 @@ def winmain(): '--icon=..\\Nagstamon\\resources\\nagstamon.ico', '--windowed', '--name=Nagstamon', - '--hidden-import=PyQt5.uic.plugins', + '--hidden-import=PyQt6.uic.plugins', '--hidden-import=win32timezone', '..\\nagstamon.py'], shell=True) diff --git a/build/docker/Dockerfile-fedora-34 b/build/docker/Dockerfile-fedora-34 index ad1e4141f..14b8ad971 100644 --- a/build/docker/Dockerfile-fedora-34 +++ b/build/docker/Dockerfile-fedora-34 @@ -21,6 +21,7 @@ RUN dnf -y install desktop-file-utils \ qt5-qtmultimedia \ rpm-build +# ugly workaround for legacy Qt5 on Fedora < 36 CMD cd /nagstamon/build && \ sed -i s/pyqt6/pyqt5/g redhat/nagstamon.spec && \ sed -i s/qt6/qt5/g redhat/nagstamon.spec && \ diff --git a/build/docker/Dockerfile-fedora-35 b/build/docker/Dockerfile-fedora-35 index d6d7929d8..e9d5f2ed2 100644 --- a/build/docker/Dockerfile-fedora-35 +++ b/build/docker/Dockerfile-fedora-35 @@ -21,6 +21,7 @@ RUN dnf -y install desktop-file-utils \ qt5-qtmultimedia \ rpm-build +# ugly workaround for legacy Qt5 on Fedora < 36 CMD cd /nagstamon/build && \ sed -i s/pyqt6/pyqt5/g redhat/nagstamon.spec && \ sed -i s/qt6/qt5/g redhat/nagstamon.spec && \ From 2321ec768d99af3cc279c20b418cbd651abe29ee Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 20 Oct 2022 17:59:23 +0200 Subject: [PATCH 417/884] small fix for IcingaDBWeb --- Nagstamon/Config.py | 2 +- Nagstamon/Servers/IcingaDBWeb.py | 10 ++++++---- build/debian/changelog | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 8fc62f185..793c1cff5 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20221018' + VERSION = '3.9-20221019' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index 570fba953..1fde5b422 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -214,8 +214,8 @@ def _get_status(self): self.new_hosts[host_name].last_check = datetime.datetime.fromtimestamp(int(float(h['state']['last_update']))) self.new_hosts[host_name].attempt = "{}/{}".format(h['state']['check_attempt'],h['max_check_attempts']) self.new_hosts[host_name].status_information = BeautifulSoup(h['state']['output'].replace('\n', ' ').strip(), 'html.parser').text - self.new_hosts[host_name].passiveonly = not(int(h['active_checks_enabled'])) - self.new_hosts[host_name].notifications_disabled = not(int(h['notifications_enabled'])) + self.new_hosts[host_name].passiveonly = not int(h.get('active_checks_enabled') or '0') + self.new_hosts[host_name].notifications_disabled = not int(h.get('notifications_enabled') or '0') self.new_hosts[host_name].flapping = bool(int(h['state']['is_flapping'] or 0)) #s['state']['is_acknowledged'] can be null, 0, 1, or 'sticky' self.new_hosts[host_name].acknowledged = bool(int(h['state']['is_acknowledged'].replace('sticky', '1') or 0)) @@ -304,11 +304,13 @@ def _get_status(self): else: self.new_hosts[host_name].services[service_name].status = self.STATES_MAPPING['services'][int(s['state']['soft_state'])] + print(s) + self.new_hosts[host_name].services[service_name].last_check = datetime.datetime.fromtimestamp(int(float(s['state']['last_update']))) self.new_hosts[host_name].services[service_name].attempt = "{}/{}".format(s['state']['check_attempt'],s['max_check_attempts']) self.new_hosts[host_name].services[service_name].status_information = BeautifulSoup(s['state']['output'].replace('\n', ' ').strip(), 'html.parser').text - self.new_hosts[host_name].services[service_name].passiveonly = not(int(s['active_checks_enabled'])) - self.new_hosts[host_name].services[service_name].notifications_disabled = not(int(s['notifications_enabled'])) + self.new_hosts[host_name].services[service_name].passiveonly = not int(s.get('active_checks_enabled') or '0') + self.new_hosts[host_name].services[service_name].notifications_disabled = not int(s.get('notifications_enabled') or '0') self.new_hosts[host_name].services[service_name].flapping = bool(int(s['state']['is_flapping'] or 0)) #s['state']['is_acknowledged'] can be null, 0, 1, or 'sticky' self.new_hosts[host_name].services[service_name].acknowledged = bool(int(s['state']['is_acknowledged'].replace('sticky', '1') or 0)) diff --git a/build/debian/changelog b/build/debian/changelog index 794123e88..b67448833 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.9-20221018) unstable; urgency=low +nagstamon (3.9-20221019) unstable; urgency=low * New upstream - upgrade to Qt6 GUI framework - updates for latest Centreon From 27dcba8b352d5f1be04dfcf4557cae0ba90f0610 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 20 Oct 2022 18:00:52 +0200 Subject: [PATCH 418/884] small fix for IcingaDBWeb minus debug --- Nagstamon/Servers/IcingaDBWeb.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index 1fde5b422..c1d753470 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -303,9 +303,6 @@ def _get_status(self): self.new_hosts[host_name].services[service_name].status = self.STATES_MAPPING['services'][int(s['state']['hard_state'])] else: self.new_hosts[host_name].services[service_name].status = self.STATES_MAPPING['services'][int(s['state']['soft_state'])] - - print(s) - self.new_hosts[host_name].services[service_name].last_check = datetime.datetime.fromtimestamp(int(float(s['state']['last_update']))) self.new_hosts[host_name].services[service_name].attempt = "{}/{}".format(s['state']['check_attempt'],s['max_check_attempts']) self.new_hosts[host_name].services[service_name].status_information = BeautifulSoup(s['state']['output'].replace('\n', ' ').strip(), 'html.parser').text From 4cbcd4273f4423faf9d2e5e4a2c823cffc39feb1 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 25 Oct 2022 19:56:24 +0200 Subject: [PATCH 419/884] prepare 3.10 --- ChangeLog | 17 +++++++++++++++-- build/debian/changelog | 5 +++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index 04c81dd74..a310a2738 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,17 @@ -nagstamon (3.8.0) unstable; urgency=low +nagstamon (3.10.0) stable; urgency=low + * New upstream + - upgrade to Qt6 GUI framework + - updates for latest Centreon + - added bearer authentication + - added IcingaDB support + - extended Zabbix support + - fixes for GUI + - fixes for TLS + - fixes for Checkmk + + -- Henri Wahl <henri@nagstamon.de> Mon, 25 Oct 2022 08:00:00 +0100 + +nagstamon (3.8.0) stable; urgency=low * New upstream - added alertmanager acknownledgment - added ECP authentication @@ -10,7 +23,7 @@ nagstamon (3.8.0) unstable; urgency=low -- Henri Wahl <henri@nagstamon.de> Mon, 15 Nov 2021 19:00:00 +0100 -nagstamon (3.6.0) unstable; urgency=low +nagstamon (3.6.0) stable; urgency=low * New upstream - added Prometheus support - added SensuGo support diff --git a/build/debian/changelog b/build/debian/changelog index b67448833..1986e20e5 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.9-20221019) unstable; urgency=low +nagstamon (3.10.0) stable; urgency=low * New upstream - upgrade to Qt6 GUI framework - updates for latest Centreon @@ -8,7 +8,8 @@ nagstamon (3.9-20221019) unstable; urgency=low - fixes for GUI - fixes for TLS - fixes for Checkmk - -- Henri Wahl <henri@nagstamon.de> Wed, 19 Oct 2022 08:00:00 +0100 + + -- Henri Wahl <henri@nagstamon.de> Mon, 25 Oct 2022 08:00:00 +0100 nagstamon (3.8.0) stable; urgency=low * New upstream From 3b1b133a0605d2cc476104ecda49c4ba62301d25 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 25 Oct 2022 20:00:53 +0200 Subject: [PATCH 420/884] prepare 3.10 --- .github/workflows/build-release-latest.yml | 14 -------- build/appimage/AppImageBuilder.yml | 39 ---------------------- build/docker/Dockerfile-appimage | 19 ----------- 3 files changed, 72 deletions(-) delete mode 100644 build/appimage/AppImageBuilder.yml delete mode 100644 build/docker/Dockerfile-appimage diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index b718c2a49..1b91ab749 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -37,20 +37,6 @@ jobs: run: | python -m unittest tests/test_*.py -# appimage: -# runs-on: ubuntu-latest -# #needs: test -# steps: -# - uses: actions/checkout@v3 -# - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . -# # higher privileges needed for creation of AppImage -# - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon --cap-add SYS_ADMIN --cap-add MKNOD --device /dev/fuse:mrw build-nagstamon -# - uses: actions/upload-artifact@v3 -# with: -# path: build/*.AppImage -# retention-days: 1 -# if-no-files-found: error - debian: runs-on: ubuntu-latest needs: test diff --git a/build/appimage/AppImageBuilder.yml b/build/appimage/AppImageBuilder.yml deleted file mode 100644 index f98db584e..000000000 --- a/build/appimage/AppImageBuilder.yml +++ /dev/null @@ -1,39 +0,0 @@ -# https://appimage-builder.readthedocs.io/en/latest/examples/pyqt.html -version: 1 -script: - - rm -rf AppDir | true - - mkdir -p AppDir/app - - mkdir -p AppDir/usr/local/share/icons - - mkdir -p AppDir/usr/local/share/applications - - cp -r ../../Nagstamon AppDir/app/Nagstamon - - cp ../../nagstamon.py AppDir/app/nagstamon.py - - cp ../../Nagstamon/resources/nagstamon.svg AppDir/usr/local/share/icons - - cp ../../Nagstamon/resources/nagstamon.desktop AppDir - - pip3 install --prefix=/usr --system --ignore-installed --root=AppDir --requirement ../requirements/linux.txt -AppDir: - path: ./AppDir - app_info: - id: nagstamon - name: Nagstamon - icon: nagstamon - version: '3.9' - exec: usr/bin/python3 - exec_args: "${APPDIR}/app/nagstamon.py $@" - apt: - arch: amd64 - allow_unauthenticated: true - sources: - - sourceline: deb http://deb.debian.org/debian bullseye main - - sourceline: deb http://security.debian.org/debian-security bullseye-security - main - - sourceline: deb http://deb.debian.org/debian bullseye-updates main - include: - - coreutils - - python3 - runtime: - env: - PYTHONHOME: '${APPDIR}/usr' - PYTHONPATH: '${APPDIR}/usr/lib/python3.9/site-packages' -AppImage: - arch: x86_64 - update-information: guess diff --git a/build/docker/Dockerfile-appimage b/build/docker/Dockerfile-appimage deleted file mode 100644 index c5a4491da..000000000 --- a/build/docker/Dockerfile-appimage +++ /dev/null @@ -1,19 +0,0 @@ -FROM debian:11 - -RUN apt update && \ - apt -y upgrade &&\ - apt -y install apt-utils - -RUN apt -y install python3 \ - python3-pip \ - python3-setuptools - -RUN pip3 install appimage-builder - -RUN apt -y install strace patchelf libkrb5-dev wget fuse file - -RUN wget https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage -O /usr/local/bin/appimagetool -RUN chmod +x /usr/local/bin/appimagetool - -CMD cd /nagstamon/build/appimage && \ - /usr/local/bin/appimage-builder From a90e07400506294bf0425aee04c3523e3dc23cef Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 25 Oct 2022 20:03:33 +0200 Subject: [PATCH 421/884] prepare 3.10 --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 1b91ab749..daa20a93a 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -2,7 +2,7 @@ name: build-release-latest on: push: tags-ignore: 'v*' - branches: '**' + branches: '!master' env: python_win_version: 3.10.7 From 13a209e5470db71d26f19d58c1363f7ef9cd1dea Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 25 Oct 2022 21:53:51 +0200 Subject: [PATCH 422/884] prepare 3.10 fix build --- .github/workflows/build-release-latest.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index daa20a93a..1c3bb7fc2 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -2,7 +2,6 @@ name: build-release-latest on: push: tags-ignore: 'v*' - branches: '!master' env: python_win_version: 3.10.7 From 2d8b253cf48db4b237768338e053f74b3504e7a0 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 25 Oct 2022 22:06:53 +0200 Subject: [PATCH 423/884] branches: '**' --- .github/workflows/build-release-latest.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 1c3bb7fc2..1b91ab749 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -2,6 +2,7 @@ name: build-release-latest on: push: tags-ignore: 'v*' + branches: '**' env: python_win_version: 3.10.7 From 8d880da9695bdbaa245c223f8177f562ab56ecca Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 27 Oct 2022 23:37:15 +0200 Subject: [PATCH 424/884] prepare 3.10, win python 3.10.8? --- .github/workflows/build-release-latest.yml | 4 ++-- .github/workflows/build-release-stable.yml | 2 +- ChangeLog | 2 +- build/debian/changelog | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 1b91ab749..112fefa51 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -5,7 +5,7 @@ on: branches: '**' env: - python_win_version: 3.10.7 + python_win_version: 3.10.8 repo_dir: nagstamon-jekyll/docs/repo jobs: @@ -127,7 +127,7 @@ jobs: with: python-version: ${{ env.python_win_version }} architecture: x86 - # no PyQt6 for win32 available on Pypi.org + # no PyQt6 for win32 available on pypi.org - run: ((Get-Content -path build/requirements/windows.txt -Raw) -replace 'pyqt6.*','pyqt5') | Set-Content -Path build/requirements/windows.txt - run: python -m pip install --no-warn-script-location -r build/requirements/windows.txt # pretty hacky but no other idea to avoid gssapi being installed which breaks requests-kerberos diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index e7b352dd1..33a9b5f22 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -126,7 +126,7 @@ jobs: with: python-version: ${{ env.python_win_version }} architecture: x86 - # no PyQt6 for win32 available on Pypi.org + # no PyQt6 for win32 available on pypi.org - run: ((Get-Content -path build/requirements/windows.txt -Raw) -replace 'pyqt6.*','pyqt5') | Set-Content -Path build/requirements/windows.txt - run: python -m pip install --no-warn-script-location -r build/requirements/windows.txt # pretty hacky but no other idea to avoid gssapi being installed which breaks requests-kerberos diff --git a/ChangeLog b/ChangeLog index a310a2738..f56cfd3ab 100644 --- a/ChangeLog +++ b/ChangeLog @@ -9,7 +9,7 @@ nagstamon (3.10.0) stable; urgency=low - fixes for TLS - fixes for Checkmk - -- Henri Wahl <henri@nagstamon.de> Mon, 25 Oct 2022 08:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Thu, 27 Oct 2022 08:00:00 +0100 nagstamon (3.8.0) stable; urgency=low * New upstream diff --git a/build/debian/changelog b/build/debian/changelog index 1986e20e5..1be4476e3 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -9,7 +9,7 @@ nagstamon (3.10.0) stable; urgency=low - fixes for TLS - fixes for Checkmk - -- Henri Wahl <henri@nagstamon.de> Mon, 25 Oct 2022 08:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Thu, 27 Oct 2022 08:00:00 +0100 nagstamon (3.8.0) stable; urgency=low * New upstream From ce4257dfb39ed133341d98b04452aecd41cc5cb4 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 27 Oct 2022 23:48:53 +0200 Subject: [PATCH 425/884] v3.10.0 --- Nagstamon/Config.py | 2 +- README.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 793c1cff5..9f77d125e 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.9-20221019' + VERSION = '3.10.0' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/README.md b/README.md index 57af22e09..258b2fa06 100644 --- a/README.md +++ b/README.md @@ -20,12 +20,12 @@ Successfully tested monitors include: - Checkmk/Multisite 1.1.10+ - Thruk 1.5.0+ - monitos 4.4+ + - Prometheus + - Alertmanager + - Zabbix 2.2+ - Livestatus – experimental - - Zabbix 2.2+ – experimental - Zenoss – experimental - monitos 3 - experimental - SNAG-View3 - experimental - - Prometheus - experimental - - Alertmanager - experimental See https://nagstamon.de for further information. From 503f1629996e87948eab0e95ea50b465d40096ed Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Fri, 28 Oct 2022 00:49:50 +0200 Subject: [PATCH 426/884] - build on master --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 112fefa51..1f5a78871 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -2,7 +2,7 @@ name: build-release-latest on: push: tags-ignore: 'v*' - branches: '**' + branches: '!master' env: python_win_version: 3.10.8 From c0d07103d2a1ea0076104eb00990a28abb44ee32 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Wed, 2 Nov 2022 00:07:19 +0100 Subject: [PATCH 427/884] map mappedString onto mapped --- Nagstamon/QUI/qt.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Nagstamon/QUI/qt.py b/Nagstamon/QUI/qt.py index ac486b084..add133648 100644 --- a/Nagstamon/QUI/qt.py +++ b/Nagstamon/QUI/qt.py @@ -103,6 +103,17 @@ from PyQt5 import uic + class QSignalMapper(QSignalMapper): + """ + QSignalMapper has method mappedString since Qt 5.15 which is not available in Ubuntu 20.04 + See https://github.com/HenriWahl/Nagstamon/issues/865 for details + """ + def __init__(self): + super().__init__() + # map mappedString onto mapped + self.mappedString = self.mapped + + class MediaPlayer(QObject): """ play media files for notification From 922c6afc625b3136bad80ddc71579b48678b88bc Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Wed, 2 Nov 2022 07:38:36 +0100 Subject: [PATCH 428/884] 3.11-20221102 --- Nagstamon/Config.py | 2 +- Nagstamon/QUI/__init__.py | 53 ++++++++++++++++++++++++++------------- build/debian/changelog | 5 ++++ 3 files changed, 41 insertions(+), 19 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 9f77d125e..8b055c286 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.10.0' + VERSION = '3.11-20221102' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 1aed34ccc..e9fb314c4 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -979,8 +979,8 @@ def __init__(self): self.servers_scrollarea = QScrollArea(self) # scrollable area for server vboxes # avoid horizontal scrollbars self.servers_scrollarea.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) - self.servers_scrollarea_widget = QWidget( - self.servers_scrollarea) # necessary widget to contain vbox for servers + # necessary widget to contain vbox for servers + self.servers_scrollarea_widget = QWidget(self.servers_scrollarea) self.servers_scrollarea.hide() self.vbox.addWidget(self.statusbar) @@ -1165,6 +1165,22 @@ def __init__(self): # finally show up self.set_mode() + def get_screen(self): + """ + very hackish fix for https://github.com/HenriWahl/Nagstamon/issues/865 + should actually fit into qt.py but due to the reference to APP it could only + be solved here + """ + # Qt6 has .screen() as replacement for QDesktopWidget... + if QT_VERSION_MAJOR > 5: + return self.screen() + # ...and .screen() exists since Qt5 5.15... + elif QT_VERSION_MINOR < 15: + return APP.desktop() + # ...so newer ones can use .screen() again + else: + return self.screen() + def set_mode(self): """ apply presentation mode @@ -1189,8 +1205,8 @@ def set_mode(self): self.move(conf.position_x, conf.position_y) else: # get available desktop specs - available_x = self.screen().availableGeometry().x() - available_y = self.screen().availableGeometry().y() + available_x = self.get_screen().availableGeometry().x() + available_y = self.get_screen().availableGeometry().y() self.move(available_x, available_y) # statusbar and detail window should be frameless and stay on top @@ -1216,8 +1232,8 @@ def set_mode(self): self.move(conf.position_x, conf.position_y) else: # get available desktop specs - available_x = self.screen().availableGeometry().x() - available_y = self.screen().availableGeometry().y() + available_x = self.get_screen().availableGeometry().x() + available_y = self.get_screen().availableGeometry().y() self.move(available_x, available_y) # need a close button @@ -1388,12 +1404,13 @@ def sort_ServerVBoxes(self): servers_vbox_new.addLayout(vboxes_dict[vbox]) # add expanding stretching item at the end for fullscreen beauty - servers_vbox_new.addSpacerItem(QSpacerItem(0, self.screen().availableGeometry().height(), + servers_vbox_new.addSpacerItem(QSpacerItem(0, self.get_screen().availableGeometry().height(), QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Expanding)) # switch to new servers_vbox self.servers_vbox = servers_vbox_new - self.servers_scrollarea_widget = QWidget() # necessary widget to contain vbox for servers + # necessary widget to contain vbox for servers + self.servers_scrollarea_widget = QWidget() self.servers_scrollarea_widget.setLayout(self.servers_vbox) self.servers_scrollarea.setWidget(self.servers_scrollarea_widget) @@ -1693,13 +1710,13 @@ def calculate_size(self): # only consider offset if it is configured if conf.systray_offset_use and conf.icon_in_systray: - available_height = self.screen().availableGeometry().height() - conf.systray_offset + available_height = self.get_screen().availableGeometry().height() - conf.systray_offset else: - available_height = self.screen().availableGeometry().height() + available_height = self.get_screen().availableGeometry().height() - available_width = self.screen().availableGeometry().width() - available_x = self.screen().availableGeometry().x() - available_y = self.screen().availableGeometry().y() + available_width = self.get_screen().availableGeometry().width() + available_x = self.get_screen().availableGeometry().x() + available_y = self.get_screen().availableGeometry().y() # Workaround for Cinnamon if OS not in OS_NON_LINUX and DESKTOP_CINNAMON: @@ -1712,7 +1729,7 @@ def calculate_size(self): # add available_y because it might vary on differently setup screens # calculate top-ness only if window is closed if conf.statusbar_floating: - if self.y() < self.screen().geometry().height() // 2 + available_y: + if self.y() < self.get_screen().geometry().height() // 2 + available_y: self.top = True else: self.top = False @@ -1721,7 +1738,7 @@ def calculate_size(self): x = self.stored_x elif conf.icon_in_systray or conf.windowed: - if self.icon_y < self.screen().geometry().height() // 2 + available_y: + if self.icon_y < self.get_screen().geometry().height() // 2 + available_y: self.top = True else: self.top = False @@ -1762,8 +1779,8 @@ def calculate_size(self): else: # when height is too large for current screen cut it if self.y() + self.height() - real_height < available_y: - height = self.screen().geometry().height() - available_y - ( - self.screen().geometry().height() - (self.y() + self.height())) + height = self.get_screen().geometry().height() - available_y - ( + self.get_screen().geometry().height() - (self.y() + self.height())) y = available_y else: height = real_height @@ -1904,7 +1921,7 @@ def leaveEvent(self, event): leave_time_offset = 0.25 elif conf.icon_in_systray: # offset is max 1 and smaller if window is smaller too - leave_time_offset = self.height() / self.screen().availableGeometry().height() + leave_time_offset = self.height() / self.get_screen().availableGeometry().height() else: leave_time_offset = 0 diff --git a/build/debian/changelog b/build/debian/changelog index 1be4476e3..8b42c4675 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,3 +1,8 @@ +nagstamon (3.11-20221102) unstable; urgency=low + * New upstream + + -- Henri Wahl <henri@nagstamon.de> Wed, 02 Nov 2022 08:00:00 +0100 + nagstamon (3.10.0) stable; urgency=low * New upstream - upgrade to Qt6 GUI framework From 17809297fd103184f2d7e15cc20c59ec4773cf75 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Wed, 2 Nov 2022 07:42:09 +0100 Subject: [PATCH 429/884] build-release-latest.yml --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 1f5a78871..6092e28e0 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -2,7 +2,7 @@ name: build-release-latest on: push: tags-ignore: 'v*' - branches: '!master' + #branches: '!master' env: python_win_version: 3.10.8 From f510ea23410055a2f35315e4fdc230cb7f6051ed Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Wed, 2 Nov 2022 07:45:24 +0100 Subject: [PATCH 430/884] build-release-latest.yml --- .github/workflows/build-release-latest.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 6092e28e0..b20fb37e4 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -2,7 +2,9 @@ name: build-release-latest on: push: tags-ignore: 'v*' - #branches: '!master' + branches: + - '**' + - '!master' env: python_win_version: 3.10.8 From 918bb4a9e5f4f39e36ebddae50bed5e0655b71f2 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Wed, 2 Nov 2022 07:48:54 +0100 Subject: [PATCH 431/884] apt update --- .github/workflows/build-release-latest.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index b20fb37e4..b1ed5efa3 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -25,7 +25,8 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | - sudo apt-get install libdbus-1-dev libkrb5-dev + sudo apt update + sudo apt -y install libdbus-1-dev libkrb5-dev python -m pip install --upgrade pip pip install pytest pylint wheel #flake8 if [ -f build/requirements/linux.txt ]; then pip install -r build/requirements/linux.txt; fi From 558468aba64bf985e46cbd14b5ea18011b00b1d1 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 3 Nov 2022 22:38:30 +0100 Subject: [PATCH 432/884] try to fix Centreon flapping problem --- Nagstamon/Config.py | 2 +- Nagstamon/Servers/Centreon/CentreonAPI.py | 8 ++++++-- build/debian/changelog | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 8b055c286..7f0bd23f3 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.11-20221102' + VERSION = '3.11-20221103' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/Servers/Centreon/CentreonAPI.py b/Nagstamon/Servers/Centreon/CentreonAPI.py index 52b7045e3..93bcdf2bb 100644 --- a/Nagstamon/Servers/Centreon/CentreonAPI.py +++ b/Nagstamon/Servers/Centreon/CentreonAPI.py @@ -358,7 +358,9 @@ def _get_status(self): self.new_hosts[new_host].status_information = alerts["information"] self.new_hosts[new_host].passiveonly = alerts["passive_checks"] self.new_hosts[new_host].notifications_disabled = not alerts["notification_enabled"] - self.new_hosts[new_host].flapping = alerts["flapping"] + # avoid crash if flapping is not configured in Centreon + # according to https://github.com/HenriWahl/Nagstamon/issues/866#issuecomment-1302257034 + self.new_hosts[new_host].flapping = alerts.get("flapping", False) self.new_hosts[new_host].acknowledged = alerts["acknowledged"] self.new_hosts[new_host].scheduled_downtime = alerts["in_downtime"] if "(S)" in alerts["tries"]: @@ -423,7 +425,9 @@ def _get_status(self): self.new_hosts[new_host].services[new_service].status_information = alerts["information"] self.new_hosts[new_host].services[new_service].passiveonly = alerts["passive_checks"] self.new_hosts[new_host].services[new_service].notifications_disabled = not alerts["notification_enabled"] - self.new_hosts[new_host].services[new_service].flapping = alerts["flapping"] + # avoid crash if flapping is not configured in Centreon + # according to https://github.com/HenriWahl/Nagstamon/issues/866#issuecomment-1302257034 + self.new_hosts[new_host].services[new_service].flapping = alerts.get("flapping", False) self.new_hosts[new_host].services[new_service].acknowledged = alerts["acknowledged"] self.new_hosts[new_host].services[new_service].scheduled_downtime = alerts["in_downtime"] if "(S)" in alerts["tries"]: diff --git a/build/debian/changelog b/build/debian/changelog index 8b42c4675..6185aa75e 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.11-20221102) unstable; urgency=low +nagstamon (3.11-20221103) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Wed, 02 Nov 2022 08:00:00 +0100 From 99b1965c93f31e3ce40a12c8260b5eb2d6fca054 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Fri, 4 Nov 2022 06:54:54 +0100 Subject: [PATCH 433/884] prepare 3.10.1 --- ChangeLog | 7 +++++++ build/debian/changelog | 6 ++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index f56cfd3ab..dbece0a16 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +nagstamon (3.10.1) stable; urgency=low + * New upstream + - fixes Centreon flapping bug + - fixes Kubuntu 20.04 not starting bug + + -- Henri Wahl <henri@nagstamon.de> Fri, 04 Nov 2022 08:00:00 +0100 + nagstamon (3.10.0) stable; urgency=low * New upstream - upgrade to Qt6 GUI framework diff --git a/build/debian/changelog b/build/debian/changelog index 6185aa75e..4bc0ee25e 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,9 @@ -nagstamon (3.11-20221103) unstable; urgency=low +nagstamon (3.10.1) stable; urgency=low * New upstream + - fixes Centreon flapping bug + - fixes Kubuntu 20.04 not starting bug - -- Henri Wahl <henri@nagstamon.de> Wed, 02 Nov 2022 08:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Fri, 04 Nov 2022 08:00:00 +0100 nagstamon (3.10.0) stable; urgency=low * New upstream From d1ab247c83ccf573f2725ac4c857bc0767bb1e2a Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Fri, 4 Nov 2022 07:04:14 +0100 Subject: [PATCH 434/884] 3.10.1 --- Nagstamon/Config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 7f0bd23f3..81a266284 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.11-20221103' + VERSION = '3.10.1' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' From 29d001476cf549122f931434b74510a76140d360 Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Sat, 5 Nov 2022 00:30:00 +0100 Subject: [PATCH 435/884] build debug --- .github/workflows/build-release-latest.yml | 28 ++++++++++++++++++++-- build/build.py | 16 +++++++++---- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index b1ed5efa3..cd16c0b5a 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -170,10 +170,34 @@ jobs: retention-days: 1 if-no-files-found: error + windows-64-debug: + # better depend on stable build image + runs-on: windows-2019 + needs: test + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 + with: + python-version: ${{ env.python_win_version }} + architecture: x64 + - run: python -m pip install --no-warn-script-location -r build/requirements/windows.txt + # pretty hacky but no other idea to avoid gssapi being installed which breaks requests-kerberos + - run: python -m pip uninstall -y gssapi requests-gssapi + - run: cd ${{ github.workspace }}/build; python build.py debug + env: + PYTHONPATH: ${{ github.workspace }} + - uses: actions/upload-artifact@v3 + with: + path: | + build/dist/*.zip + build/dist/*.exe + retention-days: 1 + if-no-files-found: error + repo-fedora: runs-on: ubuntu-latest # if not all are ready there might be trouble when downloading artifacts - needs: [debian, fedora-34, fedora-35, fedora-36, fedora-37, macos, windows-32, windows-64] + needs: [debian, fedora-34, fedora-35, fedora-36, fedora-37, macos, windows-32, windows-64, windows-64-debug] steps: # get binaries created by other jobs - uses: actions/download-artifact@v3 @@ -195,7 +219,7 @@ jobs: github-release: runs-on: ubuntu-latest - needs: [debian, fedora-34, fedora-35, fedora-36, fedora-37, macos, windows-32, windows-64] + needs: [debian, fedora-34, fedora-35, fedora-36, fedora-37, macos, windows-32, windows-64, windows-64-debug] steps: - uses: actions/download-artifact@v3 - run: cd artifact && md5sum *agstamon* > md5sums.txt diff --git a/build/build.py b/build/build.py index 4a2b686ac..6928e6b81 100644 --- a/build/build.py +++ b/build/build.py @@ -25,7 +25,7 @@ import subprocess import zipfile import glob -import time + CURRENT_DIR = os.getcwd() NAGSTAMON_DIR = os.path.normpath('{0}{1}..{1}'.format(CURRENT_DIR, os.sep)) @@ -44,6 +44,13 @@ PYTHON_VERSION = '{0}.{1}'.format(sys.version_info[0], sys.version_info[1]) +# depending of debug build or not a console window will be shown or not +if len(sys.argv) > 1 and sys.argv[1] == 'debug': + GUI_MODE = '--console' + FILE_SUFFIX = '_debug' +else: + GUI_MODE = '--windowed' + FILE_SUFFIX = '' def winmain(): """ @@ -69,7 +76,7 @@ def winmain(): ISCC = r'{0}{1}Inno Setup 6{1}iscc.exe'.format(os.environ['PROGRAMFILES{0}'.format(ARCH_OPTS[ARCH][2])], os.sep) DIR_BUILD_EXE = '{0}{1}dist{1}Nagstamon'.format(CURRENT_DIR, os.sep, ARCH_OPTS[ARCH][0], PYTHON_VERSION) - DIR_BUILD_NAGSTAMON = '{0}{1}dist{1}Nagstamon-{2}-win{3}'.format(CURRENT_DIR, os.sep, VERSION, ARCH) + DIR_BUILD_NAGSTAMON = f'{CURRENT_DIR}{os.sep}dist{os.sep}Nagstamon-{VERSION}-win{ARCH}{FILE_SUFFIX}' FILE_ZIP = '{0}.zip'.format(DIR_BUILD_NAGSTAMON) # clean older binaries @@ -85,11 +92,12 @@ def winmain(): '--noconfirm', '--add-data=..\\Nagstamon/resources;resources', '--icon=..\\Nagstamon\\resources\\nagstamon.ico', - '--windowed', '--name=Nagstamon', '--hidden-import=PyQt6.uic.plugins', '--hidden-import=win32timezone', - '..\\nagstamon.py'], shell=True) + GUI_MODE, + '..\\nagstamon.py'], + shell=True) # rename output os.rename(DIR_BUILD_EXE, DIR_BUILD_NAGSTAMON) From 0a5696a8e0065bae9a582297d5a54ad5bc86365a Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Sat, 5 Nov 2022 00:47:43 +0100 Subject: [PATCH 436/884] build debug old fstrings --- build/build.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/build/build.py b/build/build.py index 6928e6b81..7e86962d0 100644 --- a/build/build.py +++ b/build/build.py @@ -44,7 +44,7 @@ PYTHON_VERSION = '{0}.{1}'.format(sys.version_info[0], sys.version_info[1]) -# depending of debug build or not a console window will be shown or not +# depending on debug build or not a console window will be shown or not if len(sys.argv) > 1 and sys.argv[1] == 'debug': GUI_MODE = '--console' FILE_SUFFIX = '_debug' @@ -52,6 +52,7 @@ GUI_MODE = '--windowed' FILE_SUFFIX = '' + def winmain(): """ execute steps necessary for compilation of Windows binaries and setup.exe @@ -74,9 +75,10 @@ def winmain(): print('VERSION_IS:', VERSION_IS) + # old-school formatstrings needed for old Debian build base distro jessie and its old python ISCC = r'{0}{1}Inno Setup 6{1}iscc.exe'.format(os.environ['PROGRAMFILES{0}'.format(ARCH_OPTS[ARCH][2])], os.sep) DIR_BUILD_EXE = '{0}{1}dist{1}Nagstamon'.format(CURRENT_DIR, os.sep, ARCH_OPTS[ARCH][0], PYTHON_VERSION) - DIR_BUILD_NAGSTAMON = f'{CURRENT_DIR}{os.sep}dist{os.sep}Nagstamon-{VERSION}-win{ARCH}{FILE_SUFFIX}' + DIR_BUILD_NAGSTAMON = '{0}{1}dist{1}Nagstamon-{2}-win{3}{4}'.format(CURRENT_DIR, os.sep, VERSION, ARCH, FILE_SUFFIX) FILE_ZIP = '{0}.zip'.format(DIR_BUILD_NAGSTAMON) # clean older binaries From d2a8cbd5ef7b1d9ad852d1cb14086b2a70d7862a Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Sat, 5 Nov 2022 01:36:35 +0100 Subject: [PATCH 437/884] think about batch file --- build/build.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/build/build.py b/build/build.py index 7e86962d0..7d068ea76 100644 --- a/build/build.py +++ b/build/build.py @@ -46,13 +46,16 @@ # depending on debug build or not a console window will be shown or not if len(sys.argv) > 1 and sys.argv[1] == 'debug': + DEBUG = True GUI_MODE = '--console' FILE_SUFFIX = '_debug' else: + DEBUG = False GUI_MODE = '--windowed' FILE_SUFFIX = '' + def winmain(): """ execute steps necessary for compilation of Windows binaries and setup.exe @@ -116,16 +119,17 @@ def winmain(): for file in files: zip_archive.write('{0}{1}{2}'.format(root, os.sep, file)) - # execute InnoSetup with many variables set by ISCC.EXE outside .iss file - subprocess.call([ISCC, - r'/Dsource={0}'.format(DIR_BUILD_NAGSTAMON), - r'/Dversion_is={0}'.format(VERSION_IS), - r'/Dversion={0}'.format(VERSION), - r'/Darch={0}'.format(ARCH), - r'/Darchs_allowed={0}'.format(ARCH_OPTS[ARCH][3]), - r'/Dresources={0}{1}resources'.format(DIR_BUILD_NAGSTAMON, os.sep), - r'/O{0}{1}dist'.format(CURRENT_DIR, os.sep), - r'{0}{1}windows{1}nagstamon.iss'.format(CURRENT_DIR, os.sep)], shell=True) + if not DEBUG: + # execute InnoSetup with many variables set by ISCC.EXE outside .iss file + subprocess.call([ISCC, + r'/Dsource={0}'.format(DIR_BUILD_NAGSTAMON), + r'/Dversion_is={0}'.format(VERSION_IS), + r'/Dversion={0}'.format(VERSION), + r'/Darch={0}'.format(ARCH), + r'/Darchs_allowed={0}'.format(ARCH_OPTS[ARCH][3]), + r'/Dresources={0}{1}resources'.format(DIR_BUILD_NAGSTAMON, os.sep), + r'/O{0}{1}dist'.format(CURRENT_DIR, os.sep), + r'{0}{1}windows{1}nagstamon.iss'.format(CURRENT_DIR, os.sep)], shell=True) def macmain(): From a8a2132da84ebe2ec829b6412af93c404c1901b4 Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Sat, 5 Nov 2022 01:46:26 +0100 Subject: [PATCH 438/884] added debug batch file --- .github/workflows/build-release-latest.yml | 1 - Nagstamon/Config.py | 2 +- build/build.py | 6 ++++++ build/debian/changelog | 5 +++++ 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index cd16c0b5a..1659ff204 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -190,7 +190,6 @@ jobs: with: path: | build/dist/*.zip - build/dist/*.exe retention-days: 1 if-no-files-found: error diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 81a266284..c8edc4381 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.10.1' + VERSION = '3.11-22021104' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/build.py b/build/build.py index 7d068ea76..8b6085f43 100644 --- a/build/build.py +++ b/build/build.py @@ -18,6 +18,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +from pathlib import Path import platform import os, os.path import sys @@ -113,6 +114,11 @@ def winmain(): # create .zip file if os.path.exists('{0}{1}dist'.format(CURRENT_DIR, os.sep)): os.chdir('{0}{1}dist'.format(CURRENT_DIR, os.sep)) + # create simple batch file for debugging + if DEBUG: + batch_file = Path('nagstamon-debug.bat') + # cmd /k keeps the console window open to get some debug output + batch_file.write_text('cmd /k nagstamon.exe') zip_archive = zipfile.ZipFile(FILE_ZIP, mode='w', compression=zipfile.ZIP_DEFLATED) zip_archive.write(os.path.basename(DIR_BUILD_NAGSTAMON)) for root, dirs, files in os.walk(os.path.basename(DIR_BUILD_NAGSTAMON)): diff --git a/build/debian/changelog b/build/debian/changelog index 4bc0ee25e..bc405e33a 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,3 +1,8 @@ +nagstamon (3.11-20221104) stable; urgency=low + * New upstream + + -- Henri Wahl <henri@nagstamon.de> Fri, 04 Nov 2022 08:00:00 +0100 + nagstamon (3.10.1) stable; urgency=low * New upstream - fixes Centreon flapping bug From 041dc147b4ca106d6fcff099b0a003f2065ca317 Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Sat, 5 Nov 2022 02:02:06 +0100 Subject: [PATCH 439/884] fixed wrong directory --- build/build.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/build/build.py b/build/build.py index 8b6085f43..48a60a1a5 100644 --- a/build/build.py +++ b/build/build.py @@ -48,13 +48,16 @@ # depending on debug build or not a console window will be shown or not if len(sys.argv) > 1 and sys.argv[1] == 'debug': DEBUG = True + # create console window with pyinstaller to get some output GUI_MODE = '--console' - FILE_SUFFIX = '_debug' + # add '_debug' to name of zip file + FILENAME_SUFFIX = '_debug' else: DEBUG = False + # no console window via pyinstaller GUI_MODE = '--windowed' - FILE_SUFFIX = '' - + # also no need for filename suffix + FILENAME_SUFFIX = '' def winmain(): @@ -82,7 +85,7 @@ def winmain(): # old-school formatstrings needed for old Debian build base distro jessie and its old python ISCC = r'{0}{1}Inno Setup 6{1}iscc.exe'.format(os.environ['PROGRAMFILES{0}'.format(ARCH_OPTS[ARCH][2])], os.sep) DIR_BUILD_EXE = '{0}{1}dist{1}Nagstamon'.format(CURRENT_DIR, os.sep, ARCH_OPTS[ARCH][0], PYTHON_VERSION) - DIR_BUILD_NAGSTAMON = '{0}{1}dist{1}Nagstamon-{2}-win{3}{4}'.format(CURRENT_DIR, os.sep, VERSION, ARCH, FILE_SUFFIX) + DIR_BUILD_NAGSTAMON = '{0}{1}dist{1}Nagstamon-{2}-win{3}{4}'.format(CURRENT_DIR, os.sep, VERSION, ARCH, FILENAME_SUFFIX) FILE_ZIP = '{0}.zip'.format(DIR_BUILD_NAGSTAMON) # clean older binaries @@ -108,17 +111,20 @@ def winmain(): # rename output os.rename(DIR_BUILD_EXE, DIR_BUILD_NAGSTAMON) + # create simple batch file for debugging + if DEBUG: + # got to Nagstamon build directory with Nagstamon.exe + os.chdir(DIR_BUILD_NAGSTAMON) + batch_file = Path('nagstamon-debug.bat') + # cmd /k keeps the console window open to get some debug output + batch_file.write_text('cmd /k nagstamon.exe') + # after cleaning start zipping and setup.exe-building - go back to original directory os.chdir(CURRENT_DIR) # create .zip file if os.path.exists('{0}{1}dist'.format(CURRENT_DIR, os.sep)): os.chdir('{0}{1}dist'.format(CURRENT_DIR, os.sep)) - # create simple batch file for debugging - if DEBUG: - batch_file = Path('nagstamon-debug.bat') - # cmd /k keeps the console window open to get some debug output - batch_file.write_text('cmd /k nagstamon.exe') zip_archive = zipfile.ZipFile(FILE_ZIP, mode='w', compression=zipfile.ZIP_DEFLATED) zip_archive.write(os.path.basename(DIR_BUILD_NAGSTAMON)) for root, dirs, files in os.walk(os.path.basename(DIR_BUILD_NAGSTAMON)): From 42d18ca0eb44656b67a5fb258420f87a0a571fbf Mon Sep 17 00:00:00 2001 From: Kaleb Luedtke <trenlymc@gmail.com> Date: Fri, 4 Nov 2022 20:31:30 -0500 Subject: [PATCH 440/884] Add Values to ARP --- build/windows/nagstamon.iss | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build/windows/nagstamon.iss b/build/windows/nagstamon.iss index 7fe1ae044..c9db9538a 100644 --- a/build/windows/nagstamon.iss +++ b/build/windows/nagstamon.iss @@ -1,6 +1,8 @@ [Setup] AppName=Nagstamon AppVerName=Nagstamon {#version} +AppVersion={#version} +AppPublisher=Henri Wahl DefaultDirName={commonpf}\Nagstamon DefaultGroupName=Nagstamon AlwaysUsePersonalGroup=false @@ -38,4 +40,4 @@ var ReturnCode: Integer; begin Exec(ExpandConstant('taskkill.exe'), '/f /t /im nagstamon.exe', '', SW_HIDE, ewWaitUntilTerminated, ReturnCode); -end; \ No newline at end of file +end; From bcb29f737edd81dc1f8fab1a9fd2547efb7321fc Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 5 Nov 2022 13:57:12 +0100 Subject: [PATCH 441/884] 3.11-20221105 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index c8edc4381..087922d78 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.11-22021104' + VERSION = '3.11-22021105' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index bc405e33a..9673e672c 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.11-20221104) stable; urgency=low +nagstamon (3.11-20221105) stable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Fri, 04 Nov 2022 08:00:00 +0100 From 7ccfcb961a27d756ae33452e6b7835df4eac0f6e Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 5 Nov 2022 14:02:12 +0100 Subject: [PATCH 442/884] temporarily disable test due to failing dependencies --- .github/workflows/build-release-latest.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 1659ff204..38f07ca77 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -42,7 +42,7 @@ jobs: debian: runs-on: ubuntu-latest - needs: test + #needs: test steps: - uses: actions/checkout@v3 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . @@ -55,7 +55,7 @@ jobs: fedora-34: runs-on: ubuntu-latest - needs: test + #needs: test steps: - uses: actions/checkout@v3 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . @@ -68,7 +68,7 @@ jobs: fedora-35: runs-on: ubuntu-latest - needs: test + #needs: test steps: - uses: actions/checkout@v3 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . @@ -94,7 +94,7 @@ jobs: fedora-37: runs-on: ubuntu-latest - needs: test + #needs: test steps: - uses: actions/checkout@v3 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . @@ -107,7 +107,7 @@ jobs: macos: runs-on: macos-10.15 - needs: test + #needs: test steps: - uses: actions/checkout@v3 - run: pip3 install --no-warn-script-location -r build/requirements/macos.txt @@ -149,7 +149,7 @@ jobs: windows-64: # better depend on stable build image runs-on: windows-2019 - needs: test + #needs: test steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v3 @@ -173,7 +173,7 @@ jobs: windows-64-debug: # better depend on stable build image runs-on: windows-2019 - needs: test + #needs: test steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v3 From 68f148f86de2989e1a89e3b7e72f67633f03a6c3 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 5 Nov 2022 14:03:02 +0100 Subject: [PATCH 443/884] temporarily disable test due to failing dependencies part II --- .github/workflows/build-release-latest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 38f07ca77..669db0b56 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -81,7 +81,7 @@ jobs: fedora-36: runs-on: ubuntu-latest - needs: test + #needs: test steps: - uses: actions/checkout@v3 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . @@ -123,7 +123,7 @@ jobs: windows-32: # better depend on stable build image runs-on: windows-2019 - needs: test + #needs: test steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v3 From 5cc4e8210bb8c8438410a093a7d7336294eda1a6 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Wed, 9 Nov 2022 11:00:25 +0100 Subject: [PATCH 444/884] 3.11-20221105 --- Nagstamon/Config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 087922d78..a5789c711 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.11-22021105' + VERSION = '3.11-20221105' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' From a668eb7f874af4a52ca52f950ce9831daffba568 Mon Sep 17 00:00:00 2001 From: "Gierschner, Christian" <christian.gierschner@ukdd.de> Date: Mon, 14 Nov 2022 16:53:03 +0100 Subject: [PATCH 445/884] closes #873 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- build/macos/nagstamon.spec | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index a5789c711..1e44376c3 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.11-20221105' + VERSION = '3.11-20221114' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 9673e672c..cf62ecd54 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.11-20221105) stable; urgency=low +nagstamon (3.11-20221114) stable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Fri, 04 Nov 2022 08:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Fri, 14 Nov 2022 08:00:00 +0100 nagstamon (3.10.1) stable; urgency=low * New upstream diff --git a/build/macos/nagstamon.spec b/build/macos/nagstamon.spec index eaedf06e8..0ed0b7dfd 100644 --- a/build/macos/nagstamon.spec +++ b/build/macos/nagstamon.spec @@ -46,5 +46,6 @@ app = BUNDLE(exe, version=os.environ['NAGSTAMON_VERSION'], info_plist={ 'NSRequiresAquaSystemAppearance': False, - 'LSBackgroundOnly': False + 'LSBackgroundOnly': False, + 'LSUIElement': True }) From fc921af93ccd99acd6c56c1604eebc9441af53f6 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 14 Nov 2022 17:03:39 +0100 Subject: [PATCH 446/884] Update build-release-latest.yml --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 669db0b56..ffa407f84 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -4,7 +4,7 @@ on: tags-ignore: 'v*' branches: - '**' - - '!master' + #- '!master' env: python_win_version: 3.10.8 From e29fdeabe3fa171df9e2da63d66ecf8b889e22e6 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 14 Nov 2022 22:56:57 +0100 Subject: [PATCH 447/884] comment for LSUIElement --- .github/workflows/build-release-latest.yml | 2 +- build/macos/nagstamon.spec | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index ffa407f84..669db0b56 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -4,7 +4,7 @@ on: tags-ignore: 'v*' branches: - '**' - #- '!master' + - '!master' env: python_win_version: 3.10.8 diff --git a/build/macos/nagstamon.spec b/build/macos/nagstamon.spec index 0ed0b7dfd..ac8d7ec59 100644 --- a/build/macos/nagstamon.spec +++ b/build/macos/nagstamon.spec @@ -39,6 +39,7 @@ exe = EXE(pyz, entitlements_file=None, icon='../../Nagstamon/resources/nagstamon.icns') +# LSUIElement in info_plist hides the icon in dock app = BUNDLE(exe, name='Nagstamon.app', icon='../../Nagstamon/resources/nagstamon.icns', From 03959a4adf1003d11d1defe0a4efaa9115350191 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 14 Nov 2022 23:31:11 +0100 Subject: [PATCH 448/884] fix changelog --- build/debian/changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/debian/changelog b/build/debian/changelog index cf62ecd54..e381e3fe3 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.11-20221114) stable; urgency=low +nagstamon (3.11-20221114) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Fri, 14 Nov 2022 08:00:00 +0100 From 0a77569d1a3e01d2fb09b8653c26260d6497d3f6 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 14 Nov 2022 23:34:27 +0100 Subject: [PATCH 449/884] fix version --- build/macos/nagstamon.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/macos/nagstamon.spec b/build/macos/nagstamon.spec index ac8d7ec59..5c02b1c39 100644 --- a/build/macos/nagstamon.spec +++ b/build/macos/nagstamon.spec @@ -43,7 +43,7 @@ exe = EXE(pyz, app = BUNDLE(exe, name='Nagstamon.app', icon='../../Nagstamon/resources/nagstamon.icns', - bundle_identifier='de.ifw-dresden.nagstamon', + bundle_identifier='de.nagstamon', version=os.environ['NAGSTAMON_VERSION'], info_plist={ 'NSRequiresAquaSystemAppearance': False, From d5892faf6aa8335bb6fc5627bbac9f4c3605516a Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 14 Nov 2022 23:38:56 +0100 Subject: [PATCH 450/884] avoid unwanted builds of stable branch --- .github/workflows/build-release-latest.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 669db0b56..c9be656f5 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -5,6 +5,7 @@ on: branches: - '**' - '!master' + - '!v*' env: python_win_version: 3.10.8 From 4d87250cf335e8bb88a71a0dbda2db6fbab6060c Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 14 Nov 2022 23:45:46 +0100 Subject: [PATCH 451/884] avoid unwanted builds of stable branch part II --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index c9be656f5..cd68a3b60 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -5,7 +5,7 @@ on: branches: - '**' - '!master' - - '!v*' + - '!*.*.*' env: python_win_version: 3.10.8 From 2cb4189dd13cd0b593a1ddb019cccbe358f61b24 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 14 Nov 2022 23:59:45 +0100 Subject: [PATCH 452/884] needs test again --- .github/workflows/build-release-latest.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index cd68a3b60..c1c40af5f 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -43,7 +43,7 @@ jobs: debian: runs-on: ubuntu-latest - #needs: test + needs: test steps: - uses: actions/checkout@v3 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . @@ -56,7 +56,7 @@ jobs: fedora-34: runs-on: ubuntu-latest - #needs: test + needs: test steps: - uses: actions/checkout@v3 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . @@ -69,7 +69,7 @@ jobs: fedora-35: runs-on: ubuntu-latest - #needs: test + needs: test steps: - uses: actions/checkout@v3 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . @@ -82,7 +82,7 @@ jobs: fedora-36: runs-on: ubuntu-latest - #needs: test + needs: test steps: - uses: actions/checkout@v3 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . @@ -95,7 +95,7 @@ jobs: fedora-37: runs-on: ubuntu-latest - #needs: test + needs: test steps: - uses: actions/checkout@v3 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . @@ -108,7 +108,7 @@ jobs: macos: runs-on: macos-10.15 - #needs: test + needs: test steps: - uses: actions/checkout@v3 - run: pip3 install --no-warn-script-location -r build/requirements/macos.txt @@ -124,7 +124,7 @@ jobs: windows-32: # better depend on stable build image runs-on: windows-2019 - #needs: test + needs: test steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v3 @@ -150,7 +150,7 @@ jobs: windows-64: # better depend on stable build image runs-on: windows-2019 - #needs: test + needs: test steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v3 @@ -174,7 +174,7 @@ jobs: windows-64-debug: # better depend on stable build image runs-on: windows-2019 - #needs: test + needs: test steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v3 From 53927230596749d813213d0853a61b6a95fee92c Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 19 Nov 2022 12:53:48 +0100 Subject: [PATCH 453/884] show icon in dock on macOS if in window mode --- Nagstamon/Config.py | 2 +- Nagstamon/QUI/__init__.py | 29 +++++++++++++++++++++++++++++ build/debian/changelog | 4 ++-- build/requirements/macos.txt | 1 + 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 1e44376c3..85f14e91f 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.11-20221114' + VERSION = '3.11-20221119' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index e9fb314c4..dc3757c45 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -94,6 +94,12 @@ print('No DBus for desktop notification available.') DBUS_AVAILABLE = False +# make icon status in macOS dock accessible via NSApp, used by set_macos_dock_icon_visible() +if OS == OS_DARWIN: + from AppKit import (NSApp, + NSApplicationPresentationDefault, + NSApplicationPresentationHideDock) + # check ECP authentication support availability try: from requests_ecp import HTTPECPAuth @@ -1239,6 +1245,10 @@ def set_mode(self): # need a close button self.toparea.button_close.show() + # no need for icon in dock if floating + if OS == OS_DARWIN: + set_macos_dock_icon_visibility(False) + elif conf.icon_in_systray: # statusbar and detail window should be frameless and stay on top # tool flag helps to be invisible in taskbar @@ -1253,6 +1263,10 @@ def set_mode(self): # need a close button self.toparea.button_close.show() + # no need for icon in dock if in systray + if OS == OS_DARWIN: + set_macos_dock_icon_visibility(False) + elif conf.fullscreen: # no need for systray systrayicon.hide() @@ -1276,6 +1290,8 @@ def set_mode(self): # fullscreen mode is rather buggy on everything other than OSX so just use a maximized window if OS == OS_DARWIN: self.showFullScreen() + # no need for icon in dock if fullscreen + set_macos_dock_icon_visibility(False) else: self.show() self.showMaximized() @@ -1315,6 +1331,10 @@ def set_mode(self): # make sure window comes up self.raise_() + # show icon in dock if window is set + if OS == OS_DARWIN: + set_macos_dock_icon_visibility(True) + # store position for showing/hiding statuswindow self.stored_x = self.x() self.stored_y = self.y() @@ -7034,6 +7054,15 @@ def check_servers(): dialogs.server_missing.show() dialogs.server_missing.initialize('no_server_enabled') +def set_macos_dock_icon_visibility(visible=False): + """ + small helper to make dock icon visible or not in macOS + inspired by https://stackoverflow.com/questions/6796028/start-a-gui-process-in-mac-os-x-without-dock-icon + """ + if visible: + NSApp.setActivationPolicy_(NSApplicationPresentationDefault) + else: + NSApp.setActivationPolicy_(NSApplicationPresentationHideDock) # check for updates check_version = CheckVersion() diff --git a/build/debian/changelog b/build/debian/changelog index e381e3fe3..03725f4ad 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.11-20221114) unstable; urgency=low +nagstamon (3.11-20221119) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Fri, 14 Nov 2022 08:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Sat, 19 Nov 2022 08:00:00 +0100 nagstamon (3.10.1) stable; urgency=low * New upstream diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index f9f5354bf..a7ffe0283 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -4,6 +4,7 @@ keyring lxml psutil pyinstaller +pyobjc-framework-ApplicationServices pyqt6==6.3.1 pyqt6-qt6==6.3.1 pysocks From 918cca9b74da24a7f1459a0eb82cae3d9cd57cb6 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 19 Nov 2022 13:57:23 +0100 Subject: [PATCH 454/884] show icon in dock on macOS if in window mode part II --- Nagstamon/QUI/__init__.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index dc3757c45..4579a3eee 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -1201,6 +1201,10 @@ def set_mode(self): self.servers_scrollarea.hide() if conf.statusbar_floating: + # no need for icon in dock if floating - apply first to avoid window in background + if OS == OS_DARWIN: + set_macos_dock_icon_visibility(False) + # no need for systray systrayicon.hide() self.statusbar.show() @@ -1245,11 +1249,11 @@ def set_mode(self): # need a close button self.toparea.button_close.show() - # no need for icon in dock if floating + elif conf.icon_in_systray: + # no need for icon in dock if in systray if OS == OS_DARWIN: set_macos_dock_icon_visibility(False) - elif conf.icon_in_systray: # statusbar and detail window should be frameless and stay on top # tool flag helps to be invisible in taskbar self.setWindowFlags(WINDOW_FLAGS) @@ -1263,10 +1267,6 @@ def set_mode(self): # need a close button self.toparea.button_close.show() - # no need for icon in dock if in systray - if OS == OS_DARWIN: - set_macos_dock_icon_visibility(False) - elif conf.fullscreen: # no need for systray systrayicon.hide() @@ -1300,6 +1300,10 @@ def set_mode(self): self.toparea.button_close.hide() elif conf.windowed: + # show icon in dock if window is set + if OS == OS_DARWIN: + set_macos_dock_icon_visibility(True) + systrayicon.hide() # no need for close button @@ -1331,10 +1335,6 @@ def set_mode(self): # make sure window comes up self.raise_() - # show icon in dock if window is set - if OS == OS_DARWIN: - set_macos_dock_icon_visibility(True) - # store position for showing/hiding statuswindow self.stored_x = self.x() self.stored_y = self.y() From beacbffea600c68097f8afec1e425823d10ea61a Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 20 Nov 2022 20:06:18 +0100 Subject: [PATCH 455/884] fedora qt6 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 2 +- setup.py | 52 ++++++++++++++++++++++++++++-------------- 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 1e44376c3..85f14e91f 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.11-20221114' + VERSION = '3.11-20221119' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index e381e3fe3..e1c80fa01 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.11-20221114) unstable; urgency=low +nagstamon (3.11-20221119) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Fri, 14 Nov 2022 08:00:00 +0100 diff --git a/setup.py b/setup.py index 09567772f..8535cfc39 100644 --- a/setup.py +++ b/setup.py @@ -86,23 +86,41 @@ bdist_dmg_options = dict(volume_label='{0} {1}'.format(NAME, VERSION), applications_shortcut=False) -# Fedora seems to have no complete pyqt5 yet -bdist_rpm_options = dict(requires='python3 ' - 'python3-beautifulsoup4 ' - 'python3-crypto ' - 'python3-cryptography ' - 'python3-dateutil ' - 'python3-keyring ' - 'python3-lxml ' - 'python3-psutil ' - 'python3-pysocks ' - 'python3-qt5 ' - 'python3-requests ' - 'python3-requests-kerberos ' - 'python3-SecretStorage ' - 'qt5-qtmultimedia ' - 'qt5-qtsvg ', - dist_dir='./build') +# older Fedora needs Qt5 +if DIST.lower() == 'fedora' and int(DIST_VERSION) < 36: + bdist_rpm_options = dict(requires='python3 ' + 'python3-beautifulsoup4 ' + 'python3-crypto ' + 'python3-cryptography ' + 'python3-dateutil ' + 'python3-keyring ' + 'python3-lxml ' + 'python3-psutil ' + 'python3-pysocks ' + 'python3-qt5 ' + 'python3-requests ' + 'python3-requests-kerberos ' + 'python3-SecretStorage ' + 'qt5-qtmultimedia ' + 'qt5-qtsvg ', + dist_dir='./build') +else: + bdist_rpm_options = dict(requires='python3 ' + 'python3-beautifulsoup4 ' + 'python3-crypto ' + 'python3-cryptography ' + 'python3-dateutil ' + 'python3-keyring ' + 'python3-lxml ' + 'python3-psutil ' + 'python3-pysocks ' + 'python3-pyqt6 ' + 'python3-requests ' + 'python3-requests-kerberos ' + 'python3-SecretStorage ' + 'qt6-qtmultimedia ' + 'qt6-qtsvg ', + dist_dir='./build') setup(name=NAME, version=VERSION, From 327f555397388b373605c3db2b66b0d0c5118ad5 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 20 Nov 2022 20:14:22 +0100 Subject: [PATCH 456/884] Debian 9 --- build/docker/Dockerfile-debian | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/docker/Dockerfile-debian b/build/docker/Dockerfile-debian index 7566d9eda..662d48dca 100644 --- a/build/docker/Dockerfile-debian +++ b/build/docker/Dockerfile-debian @@ -1,4 +1,4 @@ -FROM debian:8 +FROM debian:9 LABEL maintainer=henri@nagstamon.de RUN apt -y update From 0d11d5c7519a1c7289445dd8dd0af6ad1e30e9fe Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 20 Nov 2022 20:19:24 +0100 Subject: [PATCH 457/884] Debian 9 pysocks --- build/docker/Dockerfile-debian | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/docker/Dockerfile-debian b/build/docker/Dockerfile-debian index 662d48dca..d8ccb9786 100644 --- a/build/docker/Dockerfile-debian +++ b/build/docker/Dockerfile-debian @@ -23,7 +23,7 @@ RUN apt -y install debhelper \ python3-requests \ python3-requests-kerberos \ python3-setuptools \ - python3-pysocks + python3-socks CMD cd /nagstamon/build && \ /usr/bin/python3 build.py \ No newline at end of file From 90967f50d66a3f49c10edbf48e55b37680d96278 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 20 Nov 2022 20:39:05 +0100 Subject: [PATCH 458/884] Debian 10 --- build/docker/Dockerfile-debian | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/docker/Dockerfile-debian b/build/docker/Dockerfile-debian index d8ccb9786..1c884a0cb 100644 --- a/build/docker/Dockerfile-debian +++ b/build/docker/Dockerfile-debian @@ -1,4 +1,4 @@ -FROM debian:9 +FROM debian:10 LABEL maintainer=henri@nagstamon.de RUN apt -y update From 9001161f368f3d378f7ea35311ab6b9ec42bd6b9 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Mon, 21 Nov 2022 15:51:43 +0100 Subject: [PATCH 459/884] updated Debian build --- build/debian/compat | 1 - build/debian/control | 14 ++++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) delete mode 100644 build/debian/compat diff --git a/build/debian/compat b/build/debian/compat deleted file mode 100644 index ec635144f..000000000 --- a/build/debian/compat +++ /dev/null @@ -1 +0,0 @@ -9 diff --git a/build/debian/control b/build/debian/control index 07dbc6de4..da1ac2d43 100644 --- a/build/debian/control +++ b/build/debian/control @@ -1,20 +1,22 @@ Source: nagstamon Section: utils -X-Python3-Version: >= 3.4 Priority: optional Maintainer: Python Applications Packaging Team <python-apps-team@lists.alioth.debian.org> Uploaders: Carl Chenet <chaica@ohmytux.com> -Build-Depends: debhelper (>= 9), python3 (>= 3.4), quilt (>= 0.63) -Build-Depends-Indep: python-support -Standards-Version: 3.9.2 +Build-Depends: dbus, debhelper-compat (= 13), dh-python, dh-sequence-python3, quilt (>= 0.63) +Build-Depends-Indep: python3-all (>= 3.4), python3-keyring, python3-setuptools, python3-psutil, +Standards-Version: 4.6.0 Homepage: https://nagstamon.de Vcs-Git: git://github.com/HenriWahl/Nagstamon.git Vcs-Browser: https://codeload.github.com/HenriWahl/Nagstamon/zip/master Package: nagstamon Architecture: all -Depends: ${python3:Depends}, ${misc:Depends}, python3-dateutil, python3-pkg-resources, python3-bs4, python3-lxml, python3-pyqt5, python3-pyqt5.qtsvg, python3-pyqt5.qtmultimedia, libqt5multimedia5-plugins, python3-requests, python3-requests-kerberos, python3-psutil, python3-dbus, python3-keyring, python3-socks -Description: Nagios status monitor which takes place in systray or on desktop +Depends: ${python3:Depends}, ${misc:Depends}, python3-pkg-resources, python3-bs4, python3-lxml, + python3-pyqt5, python3-pyqt5.qtsvg, python3-pyqt5.qtmultimedia, libqt5multimedia5-plugins, + python3-requests, python3-requests-kerberos, python3-psutil, python3-dbus.mainloop.pyqt5, + python3-keyring, python3-ewmh +Recommends: python3-secretstorageDescription: Nagios status monitor which takes place in systray or on desktop Nagstamon is a Nagios status monitor which takes place in systray or on desktop (GNOME, KDE) as floating statusbar to inform you in realtime about the status of your Nagios and some of its derivatives monitored network. From 7ff9f15920fdd1380cf84b3a241623e2d69be8fc Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Mon, 21 Nov 2022 16:36:31 +0100 Subject: [PATCH 460/884] updated Debian build added pkgs --- build/debian/control | 3 ++- build/docker/Dockerfile-debian | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/build/debian/control b/build/debian/control index da1ac2d43..f00ef62ee 100644 --- a/build/debian/control +++ b/build/debian/control @@ -16,7 +16,8 @@ Depends: ${python3:Depends}, ${misc:Depends}, python3-pkg-resources, python3-bs4 python3-pyqt5, python3-pyqt5.qtsvg, python3-pyqt5.qtmultimedia, libqt5multimedia5-plugins, python3-requests, python3-requests-kerberos, python3-psutil, python3-dbus.mainloop.pyqt5, python3-keyring, python3-ewmh -Recommends: python3-secretstorageDescription: Nagios status monitor which takes place in systray or on desktop +Recommends: python3-secretstorage +Description: Nagios status monitor which takes place in systray or on desktop Nagstamon is a Nagios status monitor which takes place in systray or on desktop (GNOME, KDE) as floating statusbar to inform you in realtime about the status of your Nagios and some of its derivatives monitored network. diff --git a/build/docker/Dockerfile-debian b/build/docker/Dockerfile-debian index 1c884a0cb..db39417bf 100644 --- a/build/docker/Dockerfile-debian +++ b/build/docker/Dockerfile-debian @@ -6,6 +6,7 @@ RUN apt -y install apt-utils # python3-pysocks in debian:8 becomes python3-socks in later versions RUN apt -y install debhelper \ + dh-python \ fakeroot \ git \ make \ From d610a8f08414ec001fce3efa5255631f16eaf860 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Mon, 21 Nov 2022 16:39:14 +0100 Subject: [PATCH 461/884] updated Debian build added pkgs nocheck --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index c1c40af5f..cc42ed270 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -47,7 +47,7 @@ jobs: steps: - uses: actions/checkout@v3 - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon -e DEB_BUILD_OPTIONS=nocheck build-nagstamon - uses: actions/upload-artifact@v3 with: path: build/*.deb From 80064633a5dcf46d8ab4fee5b55dc476d8722709 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Mon, 21 Nov 2022 17:02:08 +0100 Subject: [PATCH 462/884] updated XLib for EWMH --- Nagstamon/Config.py | 2 +- Nagstamon/thirdparty/Xlib/X.py | 27 +- Nagstamon/thirdparty/Xlib/XK.py | 25 +- Nagstamon/thirdparty/Xlib/Xatom.py | 25 +- Nagstamon/thirdparty/Xlib/Xcursorfont.py | 25 +- Nagstamon/thirdparty/Xlib/Xutil.py | 25 +- Nagstamon/thirdparty/Xlib/__init__.py | 27 +- Nagstamon/thirdparty/Xlib/display.py | 150 +- Nagstamon/thirdparty/Xlib/error.py | 35 +- Nagstamon/thirdparty/Xlib/ext/__init__.py | 38 +- Nagstamon/thirdparty/Xlib/ext/composite.py | 76 +- Nagstamon/thirdparty/Xlib/ext/damage.py | 182 + Nagstamon/thirdparty/Xlib/ext/dpms.py | 233 + Nagstamon/thirdparty/Xlib/ext/ge.py | 112 + Nagstamon/thirdparty/Xlib/ext/nvcontrol.py | 5393 +++++++++++++++++ Nagstamon/thirdparty/Xlib/ext/randr.py | 192 +- Nagstamon/thirdparty/Xlib/ext/record.py | 31 +- Nagstamon/thirdparty/Xlib/ext/res.py | 288 + Nagstamon/thirdparty/Xlib/ext/screensaver.py | 198 + Nagstamon/thirdparty/Xlib/ext/security.py | 139 + Nagstamon/thirdparty/Xlib/ext/shape.py | 545 +- Nagstamon/thirdparty/Xlib/ext/xfixes.py | 201 + Nagstamon/thirdparty/Xlib/ext/xinerama.py | 28 +- Nagstamon/thirdparty/Xlib/ext/xinput.py | 777 +++ Nagstamon/thirdparty/Xlib/ext/xtest.py | 25 +- .../thirdparty/Xlib/keysymdef/__init__.py | 25 +- Nagstamon/thirdparty/Xlib/keysymdef/xf86.py | 379 +- .../thirdparty/Xlib/protocol/__init__.py | 25 +- Nagstamon/thirdparty/Xlib/protocol/display.py | 256 +- Nagstamon/thirdparty/Xlib/protocol/event.py | 31 +- Nagstamon/thirdparty/Xlib/protocol/request.py | 39 +- Nagstamon/thirdparty/Xlib/protocol/rq.py | 641 +- Nagstamon/thirdparty/Xlib/protocol/structs.py | 29 +- Nagstamon/thirdparty/Xlib/rdb.py | 52 +- Nagstamon/thirdparty/Xlib/support/__init__.py | 25 +- Nagstamon/thirdparty/Xlib/support/connect.py | 67 +- Nagstamon/thirdparty/Xlib/support/lock.py | 29 +- .../thirdparty/Xlib/support/unix_connect.py | 168 +- .../thirdparty/Xlib/support/vms_connect.py | 33 +- Nagstamon/thirdparty/Xlib/threaded.py | 34 +- Nagstamon/thirdparty/Xlib/xauth.py | 46 +- Nagstamon/thirdparty/Xlib/xobject/__init__.py | 25 +- Nagstamon/thirdparty/Xlib/xobject/colormap.py | 32 +- Nagstamon/thirdparty/Xlib/xobject/cursor.py | 35 +- Nagstamon/thirdparty/Xlib/xobject/drawable.py | 145 +- Nagstamon/thirdparty/Xlib/xobject/fontable.py | 35 +- Nagstamon/thirdparty/Xlib/xobject/icccm.py | 25 +- Nagstamon/thirdparty/Xlib/xobject/resource.py | 37 +- build/debian/changelog | 2 +- 49 files changed, 9438 insertions(+), 1576 deletions(-) create mode 100644 Nagstamon/thirdparty/Xlib/ext/damage.py create mode 100644 Nagstamon/thirdparty/Xlib/ext/dpms.py create mode 100644 Nagstamon/thirdparty/Xlib/ext/ge.py create mode 100644 Nagstamon/thirdparty/Xlib/ext/nvcontrol.py create mode 100644 Nagstamon/thirdparty/Xlib/ext/res.py create mode 100644 Nagstamon/thirdparty/Xlib/ext/screensaver.py create mode 100644 Nagstamon/thirdparty/Xlib/ext/security.py create mode 100644 Nagstamon/thirdparty/Xlib/ext/xfixes.py create mode 100644 Nagstamon/thirdparty/Xlib/ext/xinput.py diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 85f14e91f..cd91a92e9 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.11-20221119' + VERSION = '3.11-20221121' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/thirdparty/Xlib/X.py b/Nagstamon/thirdparty/Xlib/X.py index 80eeafdaa..1a09e3922 100644 --- a/Nagstamon/thirdparty/Xlib/X.py +++ b/Nagstamon/thirdparty/Xlib/X.py @@ -2,19 +2,22 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA # Avoid overwriting None if doing "from Xlib.X import *" NONE = 0 @@ -194,6 +197,8 @@ FamilyInternet = 0 FamilyDECnet = 1 FamilyChaos = 2 +FamilyServerInterpreted = 5 +FamilyInternetV6 = 6 PropertyNewValue = 0 PropertyDelete = 1 ColormapUninstalled = 0 diff --git a/Nagstamon/thirdparty/Xlib/XK.py b/Nagstamon/thirdparty/Xlib/XK.py index 76b20f09d..7603ccd03 100644 --- a/Nagstamon/thirdparty/Xlib/XK.py +++ b/Nagstamon/thirdparty/Xlib/XK.py @@ -2,19 +2,22 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA # # This module defines some functions for working with X keysyms as well # as a modular keysym definition and loading mechanism. See the keysym diff --git a/Nagstamon/thirdparty/Xlib/Xatom.py b/Nagstamon/thirdparty/Xlib/Xatom.py index 94bcebba7..b0137779f 100644 --- a/Nagstamon/thirdparty/Xlib/Xatom.py +++ b/Nagstamon/thirdparty/Xlib/Xatom.py @@ -2,19 +2,22 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA PRIMARY = 1 SECONDARY = 2 diff --git a/Nagstamon/thirdparty/Xlib/Xcursorfont.py b/Nagstamon/thirdparty/Xlib/Xcursorfont.py index 589044a4c..19919438f 100644 --- a/Nagstamon/thirdparty/Xlib/Xcursorfont.py +++ b/Nagstamon/thirdparty/Xlib/Xcursorfont.py @@ -2,19 +2,22 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA num_glyphs = 154 X_cursor = 0 diff --git a/Nagstamon/thirdparty/Xlib/Xutil.py b/Nagstamon/thirdparty/Xlib/Xutil.py index ba3141f0e..768c5e2fd 100644 --- a/Nagstamon/thirdparty/Xlib/Xutil.py +++ b/Nagstamon/thirdparty/Xlib/Xutil.py @@ -2,19 +2,22 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA NoValue = 0x0000 diff --git a/Nagstamon/thirdparty/Xlib/__init__.py b/Nagstamon/thirdparty/Xlib/__init__.py index 7e0e26547..9db8beec1 100644 --- a/Nagstamon/thirdparty/Xlib/__init__.py +++ b/Nagstamon/thirdparty/Xlib/__init__.py @@ -2,21 +2,24 @@ # # Copyright (C) 2000-2002 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA -__version__ = (0, 15) +__version__ = (0, 32) __version_extra__ = '' diff --git a/Nagstamon/thirdparty/Xlib/display.py b/Nagstamon/thirdparty/Xlib/display.py index 12387c80d..e0f7b5c8c 100644 --- a/Nagstamon/thirdparty/Xlib/display.py +++ b/Nagstamon/thirdparty/Xlib/display.py @@ -2,46 +2,55 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA # Python modules import types +# Python 2/3 compatibility. +from six import create_unbound_method + # Xlib modules -from . import error, ext, X +from . import error +from . import ext +from . import X # Xlib.protocol modules -from Xlib.protocol import display, request, event, rq +from .protocol import display as protocol_display +from .protocol import request, event, rq # Xlib.xobjects modules -import Xlib.xobject.resource -import Xlib.xobject.drawable -import Xlib.xobject.fontable -import Xlib.xobject.colormap -import Xlib.xobject.cursor +from .xobject import resource +from .xobject import drawable +from .xobject import fontable +from .xobject import colormap +from .xobject import cursor _resource_baseclasses = { - 'resource': Xlib.xobject.resource.Resource, - 'drawable': Xlib.xobject.drawable.Drawable, - 'window': Xlib.xobject.drawable.Window, - 'pixmap': Xlib.xobject.drawable.Pixmap, - 'fontable': Xlib.xobject.fontable.Fontable, - 'font': Xlib.xobject.fontable.Font, - 'gc': Xlib.xobject.fontable.GC, - 'colormap': Xlib.xobject.colormap.Colormap, - 'cursor': Xlib.xobject.cursor.Cursor, + 'resource': resource.Resource, + 'drawable': drawable.Drawable, + 'window': drawable.Window, + 'pixmap': drawable.Pixmap, + 'fontable': fontable.Fontable, + 'font': fontable.Font, + 'gc': fontable.GC, + 'colormap': colormap.Colormap, + 'cursor': cursor.Cursor, } _resource_hierarchy = { @@ -52,14 +61,14 @@ 'fontable': ('font', 'gc') } -class _BaseDisplay(display.Display): - resource_classes = _resource_baseclasses.copy() +class _BaseDisplay(protocol_display.Display): # Implement a cache of atom names, used by Window objects when # dealing with some ICCCM properties not defined in Xlib.Xatom def __init__(self, *args, **keys): - display.Display.__init__(*(self, ) + args, **keys) + self.resource_classes = _resource_baseclasses.copy() + protocol_display.Display.__init__(self, *args, **keys) self._atom_cache = {} def get_atom(self, atomname, only_if_exists=0): @@ -75,7 +84,7 @@ def get_atom(self, atomname, only_if_exists=0): return r.atom -class Display: +class Display(object): def __init__(self, display = None): self.display = _BaseDisplay(display) @@ -94,6 +103,11 @@ def __init__(self, display = None): self.class_extension_dicts = {} self.display_extension_methods = {} + # a dict that maps the event name to the code + # or, when it's an event with a subcode, to a tuple of (event,subcode) + # note this wraps the dict so you address it as + # extension_event.EXTENSION_EVENT_NAME rather than + # extension_event["EXTENSION_EVENT_NAME"] self.extension_event = rq.DictWrapper({}) exts = self.list_extensions() @@ -103,7 +117,7 @@ def __init__(self, display = None): if extname in exts: # Import the module and fetch it - __import__('ext.' + modname,globals(),level=1) + __import__('Xlib.ext.' + modname) mod = getattr(ext, modname) info = self.query_extension(extname) @@ -116,11 +130,11 @@ def __init__(self, display = None): # Finalize extensions by creating new classes - for type_, dict in self.class_extension_dicts.items(): - origcls = self.display.resource_classes[type_] - self.display.resource_classes[type_] = type(origcls.__name__, - (origcls, object), - dict) + for class_name, dictionary in self.class_extension_dicts.items(): + origcls = self.display.resource_classes[class_name] + self.display.resource_classes[class_name] = type(origcls.__name__, + (origcls,), + dictionary) # Problem: we have already created some objects without the # extensions: the screen roots and default colormaps. @@ -263,17 +277,19 @@ def extension_add_method(self, object, name, function): self.display_extension_methods[name] = function else: - types = (object, ) + _resource_hierarchy.get(object, ()) - for type in types: - cls = _resource_baseclasses[type] + class_list = (object, ) + _resource_hierarchy.get(object, ()) + for class_name in class_list: + cls = _resource_baseclasses[class_name] if hasattr(cls, name): - raise AssertionError('attempting to replace %s method: %s' % (type, name)) + raise AssertionError('attempting to replace %s method: %s' % (class_name, name)) + + method = create_unbound_method(function, cls) # Maybe should check extension overrides too try: - self.class_extension_dicts[type][name] = function + self.class_extension_dicts[class_name][name] = method except KeyError: - self.class_extension_dicts[type] = { name: function } + self.class_extension_dicts[class_name] = { name: method } def extension_add_event(self, code, evt, name = None): """extension_add_event(code, evt, [name]) @@ -287,8 +303,8 @@ def extension_add_event(self, code, evt, name = None): extension_event. """ - newevt = type('{0}.SUB{1}'.format(evt.__name__, code), - evt.__bases__, evt.__dict__.copy()) + newevt = type(evt.__name__, evt.__bases__, + evt.__dict__.copy()) newevt._code = code self.display.add_extension_event(code, newevt) @@ -298,9 +314,34 @@ def extension_add_event(self, code, evt, name = None): setattr(self.extension_event, name, code) + def extension_add_subevent(self, code, subcode, evt, name = None): + """extension_add_subevent(code, evt, [name]) + + Add an extension subevent. CODE is the numeric code, subcode + is the sub-ID of this event that shares the code ID with other + sub-events and EVT is the event class. EVT will be cloned, and + the attribute _code of the new event class will be set to CODE. + + If NAME is omitted, it will be set to the name of EVT. This + name is used to insert an entry in the DictWrapper + extension_event. + """ + + newevt = type(evt.__name__, evt.__bases__, + evt.__dict__.copy()) + newevt._code = code + + self.display.add_extension_event(code, newevt, subcode) + + if name is None: + name = evt.__name__ + + # store subcodes as a tuple of (event code, subcode) in the + # extension dict maintained in the display object + setattr(self.extension_event, name, (code,subcode)) - def add_extension_error(self, code, err): - """add_extension_error(code, err) + def extension_add_error(self, code, err): + """extension_add_error(code, err) Add an extension error. CODE is the numeric code, and ERR is the error class. @@ -348,7 +389,7 @@ def keysym_to_keycodes(self, keysym): lowest index and secondarily on the lowest keycode.""" try: # Copy the map list, reversing the arguments - return [(x[1], x[0]) for x in self._keymap_syms[keysym]] + return map(lambda x: (x[1], x[0]), self._keymap_syms[keysym]) except KeyError: return [] @@ -476,7 +517,7 @@ def send_event(self, destination, event, event_mask = 0, propagate = 0, event = event) def ungrab_pointer(self, time, onerror = None): - """elease a grabbed pointer and any queued events. See + """Release a grabbed pointer and any queued events. See XUngrabPointer(3X11).""" request.UngrabPointer(display = self.display, onerror = onerror, @@ -590,7 +631,7 @@ def open_font(self, name): self.display.free_resource_id(fid) return None else: - cls = self.display.get_resource_class('font', Xlib.xobject.fontable.Font) + cls = self.display.get_resource_class('font', fontable.Font) return cls(self.display, fid, owner = 1) def list_fonts(self, pattern, max_names): @@ -620,7 +661,7 @@ def list_fonts_with_info(self, pattern, max_names): font_ascent font_descent replies_hint - See the descripton of XFontStruct in XGetFontProperty(3X11) + See the description of XFontStruct in XGetFontProperty(3X11) for details on these values. properties A list of properties. Each entry has two attributes: @@ -773,7 +814,7 @@ def change_pointer_control(self, accel = None, threshold = None, onerror = None) request.ChangePointerControl(display = self.display, onerror = onerror, do_accel = do_accel, - do_thres = do_threshold, + do_thresh = do_threshold, accel_num = accel_num, accel_denum = accel_denum, threshold = threshold) @@ -808,7 +849,8 @@ def get_screen_saver(self): def change_hosts(self, mode, host_family, host, onerror = None): """mode is either X.HostInsert or X.HostDelete. host_family is - one of X.FamilyInternet, X.FamilyDECnet or X.FamilyChaos. + one of X.FamilyInternet, X.FamilyDECnet, X.FamilyChaos, + X.FamilyServerInterpreted or X.FamilyInternetV6. host is a list of bytes. For the Internet family, it should be the four bytes of an IPv4 address.""" @@ -827,7 +869,7 @@ def list_hosts(self): The hosts on the access list. Each entry has the following attributes: family - X.FamilyInternet, X.FamilyDECnet, or X.FamilyChaos. + X.FamilyInternet, X.FamilyDECnet, X.FamilyChaos, X.FamilyServerInterpreted or X.FamilyInternetV6. name A list of byte values, the coding depends on family. For the Internet family, it is the 4 bytes of an IPv4 address. diff --git a/Nagstamon/thirdparty/Xlib/error.py b/Nagstamon/thirdparty/Xlib/error.py index 5f5af5dfa..cb6d0d07a 100644 --- a/Nagstamon/thirdparty/Xlib/error.py +++ b/Nagstamon/thirdparty/Xlib/error.py @@ -2,25 +2,28 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA # Xlib modules -from Xlib import X +from . import X # Xlib.protocol modules -from Xlib.protocol import rq +from .protocol import rq class DisplayError(Exception): @@ -73,9 +76,9 @@ def __str__(self): s = [] for f in ('code', 'resource_id', 'sequence_number', 'major_opcode', 'minor_opcode'): - s.append('%s = %s' % (f, self._data[f])) + s.append('{0} = {1}'.format(f, self._data[f])) - return '%s: %s' % (self.__class__, ', '.join(s)) + return '{0}: {1}'.format(self.__class__, ', '.join(s)) class XResourceError(XError): _fields = rq.Struct( rq.Card8('type'), # Always 0 @@ -126,7 +129,7 @@ class BadImplementation(XError): pass } -class CatchError: +class CatchError(object): def __init__(self, *errors): self.error_types = errors self.error = None diff --git a/Nagstamon/thirdparty/Xlib/ext/__init__.py b/Nagstamon/thirdparty/Xlib/ext/__init__.py index 176599b5c..37229bacf 100644 --- a/Nagstamon/thirdparty/Xlib/ext/__init__.py +++ b/Nagstamon/thirdparty/Xlib/ext/__init__.py @@ -2,31 +2,45 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA # __extensions__ is a list of tuples: (extname, extmod) # extname is the name of the extension according to the X # protocol. extmod is the name of the module in this package. __extensions__ = [ + # We load this first so other extensions can register generic event data + # structures. + ('Generic Event Extension', 'ge'), ('XTEST', 'xtest'), ('SHAPE', 'shape'), ('XINERAMA', 'xinerama'), ('RECORD', 'record'), ('Composite', 'composite'), ('RANDR', 'randr'), + ('XFIXES', 'xfixes'), + ('SECURITY', 'security'), + ('XInputExtension', 'xinput'), + ('NV-CONTROL', 'nvcontrol'), + ('DAMAGE', 'damage'), + ('DPMS', 'dpms'), + ('X-Resource', 'res'), + ('MIT-SCREEN-SAVER', 'screensaver'), ] -__all__ = [x[1] for x in __extensions__] +__all__ = map(lambda x: x[1], __extensions__) diff --git a/Nagstamon/thirdparty/Xlib/ext/composite.py b/Nagstamon/thirdparty/Xlib/ext/composite.py index 347f67120..0e10b6350 100644 --- a/Nagstamon/thirdparty/Xlib/ext/composite.py +++ b/Nagstamon/thirdparty/Xlib/ext/composite.py @@ -4,19 +4,22 @@ # # Copyright (C) 2007 Peter Liljenberg <peter.liljenberg@gmail.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA """Composite extension, allowing windows to be rendered to off-screen storage. @@ -62,6 +65,8 @@ def query_version(self): return QueryVersion( display = self.display, opcode = self.display.get_extension_major(extname), + major_version=0, + minor_version=4 ) @@ -75,11 +80,12 @@ class RedirectWindow(rq.Request): rq.Pad(3), ) -def redirect_window(self, update): +def redirect_window(self, update, onerror = None): """Redirect the hierarchy starting at this window to off-screen storage. """ RedirectWindow(display = self.display, + onerror = onerror, opcode = self.display.get_extension_major(extname), window = self, update = update, @@ -96,11 +102,12 @@ class RedirectSubwindows(rq.Request): rq.Pad(3), ) -def redirect_subwindows(self, update): +def redirect_subwindows(self, update, onerror = None): """Redirect the hierarchies starting at all current and future children to this window to off-screen storage. """ RedirectSubwindows(display = self.display, + onerror = onerror, opcode = self.display.get_extension_major(extname), window = self, update = update, @@ -117,10 +124,11 @@ class UnredirectWindow(rq.Request): rq.Pad(3), ) -def unredirect_window(self, update): +def unredirect_window(self, update, onerror = None): """Stop redirecting this window hierarchy. """ UnredirectWindow(display = self.display, + onerror = onerror, opcode = self.display.get_extension_major(extname), window = self, update = update, @@ -137,10 +145,11 @@ class UnredirectSubindows(rq.Request): rq.Pad(3), ) -def unredirect_subwindows(self, update): +def unredirect_subwindows(self, update, onerror = None): """Stop redirecting the hierarchies of children to this window. """ RedirectWindow(display = self.display, + onerror = onerror, opcode = self.display.get_extension_major(extname), window = self, update = update, @@ -156,14 +165,15 @@ class CreateRegionFromBorderClip(rq.Request): rq.Window('window'), ) -def create_region_from_border_clip(self): +def create_region_from_border_clip(self, onerror = None): """Create a region of the border clip of the window, i.e. the area that is not clipped by the parent and any sibling windows. """ - + rid = self.display.allocate_resource_id() CreateRegionFromBorderClip( display = self.display, + onerror = onerror, opcode = self.display.get_extension_major(extname), region = rid, window = self, @@ -182,7 +192,7 @@ class NameWindowPixmap(rq.Request): rq.Pixmap('pixmap'), ) -def name_window_pixmap(self): +def name_window_pixmap(self, onerror = None): """Create a new pixmap that refers to the off-screen storage of the window, including its border. @@ -195,6 +205,7 @@ def name_window_pixmap(self): pid = self.display.allocate_resource_id() NameWindowPixmap(display = self.display, + onerror = onerror, opcode = self.display.get_extension_major(extname), window = self, pixmap = pid, @@ -202,7 +213,30 @@ def name_window_pixmap(self): cls = self.display.get_resource_class('pixmap', drawable.Pixmap) return cls(self.display, pid, owner = 1) - + +class GetOverlayWindow(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(7), + rq.RequestLength(), + rq.Window('window') + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Window('overlay_window'), + rq.Pad(20), + ) + +def get_overlay_window(self): + """Return the overlay window of the root window. + """ + + return GetOverlayWindow(display = self.display, + opcode = self.display.get_extension_major(extname), + window = self) def init(disp, info): disp.extension_add_method('display', @@ -232,3 +266,7 @@ def init(disp, info): disp.extension_add_method('window', 'composite_name_window_pixmap', name_window_pixmap) + + disp.extension_add_method('window', + 'composite_get_overlay_window', + get_overlay_window) diff --git a/Nagstamon/thirdparty/Xlib/ext/damage.py b/Nagstamon/thirdparty/Xlib/ext/damage.py new file mode 100644 index 000000000..60c56606e --- /dev/null +++ b/Nagstamon/thirdparty/Xlib/ext/damage.py @@ -0,0 +1,182 @@ +# Xlib.ext.damage -- DAMAGE extension module +# +# Copyright (C) 2018 Joseph Kogut <joseph.kogut@gmail.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + + +from Xlib import X +from Xlib.protocol import rq, structs +from Xlib.xobject import resource +from Xlib.error import XError + +extname = 'DAMAGE' + +# Event codes # +DamageNotifyCode = 0 + +# Error codes # +BadDamageCode = 0 + +class BadDamageError(XError): + pass + +# DamageReportLevel options +DamageReportRawRectangles = 0 +DamageReportDeltaRectangles = 1 +DamageReportBoundingBox = 2 +DamageReportNonEmpty = 3 + +DamageReportLevel = ( + DamageReportRawRectangles, + DamageReportDeltaRectangles, + DamageReportBoundingBox, + DamageReportNonEmpty, +) + +DAMAGE = rq.Card32 + +# Methods + +class QueryVersion(rq.ReplyRequest): + _request = rq.Struct(rq.Card8('opcode'), + rq.Opcode(0), + rq.RequestLength(), + rq.Card32('major_version'), + rq.Card32('minor_version'), + ) + + _reply = rq.Struct(rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('major_version'), + rq.Card32('minor_version'), + rq.Pad(16), + ) + +def query_version(self): + return QueryVersion(display=self.display, + opcode=self.display.get_extension_major(extname), + major_version=1, + minor_version=1) + +class DamageCreate(rq.Request): + _request = rq.Struct(rq.Card8('opcode'), + rq.Opcode(1), + rq.RequestLength(), + DAMAGE('damage'), + rq.Drawable('drawable'), + rq.Set('level', 1, DamageReportLevel), + rq.Pad(3), + ) + +def damage_create(self, level): + did = self.display.allocate_resource_id() + DamageCreate(display=self.display, + opcode=self.display.get_extension_major(extname), + damage=did, + drawable=self.id, + level=level, + ) + return did + +class DamageDestroy(rq.Request): + _request = rq.Struct(rq.Card8('opcode'), + rq.Opcode(2), + rq.RequestLength(), + DAMAGE('damage') + ) + +def damage_destroy(self, damage): + DamageDestroy(display=self.display, + opcode=self.display.get_extension_major(extname), + damage=damage, + ) + + self.display.free_resource_id(damage) + +class DamageSubtract(rq.Request): + _request = rq.Struct(rq.Card8('opcode'), + rq.Opcode(3), + rq.RequestLength(), + DAMAGE('damage'), + rq.Card32('repair'), + rq.Card32('parts') + ) + +def damage_subtract(self, damage, repair=X.NONE, parts=X.NONE): + DamageSubtract(display=self.display, + opcode=self.display.get_extension_major(extname), + damage=damage, + repair=repair, + parts=parts) + +class DamageAdd(rq.Request): + _request = rq.Struct(rq.Card8('opcode'), + rq.Opcode(4), + rq.RequestLength(), + rq.Card32('repair'), + rq.Card32('parts'), + ) + +def damage_add(self, repair, parts): + DamageAdd(display=self.display, + opcode=self.display.get_extension_major(extname), + repair=repair, + parts=parts) + +# Events # + +class DamageNotify(rq.Event): + _code = None + _fields = rq.Struct( + rq.Card8('type'), + rq.Card8('level'), + rq.Card16('sequence_number'), + rq.Drawable('drawable'), + DAMAGE('damage'), + rq.Card32('timestamp'), + rq.Object('area', structs.Rectangle), + rq.Object('drawable_geometry', structs.Rectangle) + ) + +def init(disp, info): + disp.extension_add_method('display', + 'damage_query_version', + query_version) + + disp.extension_add_method('drawable', + 'damage_create', + damage_create) + + disp.extension_add_method('display', + 'damage_destroy', + damage_destroy) + + disp.extension_add_method('display', + 'damage_subtract', + damage_subtract) + + disp.extension_add_method('drawable', + 'damage_add', + damage_add) + + disp.extension_add_event(info.first_event + DamageNotifyCode, DamageNotify) + + disp.extension_add_error(code=BadDamageCode, err=BadDamageError) diff --git a/Nagstamon/thirdparty/Xlib/ext/dpms.py b/Nagstamon/thirdparty/Xlib/ext/dpms.py new file mode 100644 index 000000000..3ff9a246d --- /dev/null +++ b/Nagstamon/thirdparty/Xlib/ext/dpms.py @@ -0,0 +1,233 @@ +# Xlib.ext.dpms -- X Display Power Management Signaling +# +# Copyright (C) 2020 Thiago Kenji Okada <thiagokokada@gmail.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +''' +This extension provides X Protocol control over the VESA Display +Power Management Signaling (DPMS) characteristics of video boards +under control of the X Window System. + +Documentation: https://www.x.org/releases/X11R7.7/doc/xextproto/dpms.html +''' + +from Xlib import X +from Xlib.protocol import rq + +extname = 'DPMS' + + +# DPMS Extension Power Levels +# 0 DPMSModeOn In use +# 1 DPMSModeStandby Blanked, low power +# 2 DPMSModeSuspend Blanked, lower power +# 3 DPMSModeOff Shut off, awaiting activity +DPMSModeOn = 0 +DPMSModeStandby = 1 +DPMSModeSuspend = 2 +DPMSModeOff = 3 + +DPMSPowerLevel = ( + DPMSModeOn, + DPMSModeStandby, + DPMSModeSuspend, + DPMSModeOff, +) + + +class DPMSGetVersion(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(0), + rq.RequestLength(), + rq.Card16('major_version'), + rq.Card16('minor_version'), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card16('major_version'), + rq.Card16('minor_version'), + rq.Pad(20), + ) + + +def get_version(self): + return DPMSGetVersion(display=self.display, + opcode=self.display.get_extension_major(extname), + major_version=1, + minor_version=1) + + +class DPMSCapable(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(1), + rq.RequestLength(), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Bool('capable'), + rq.Pad(23), + ) + + +def capable(self): + return DPMSCapable(display=self.display, + opcode=self.display.get_extension_major(extname), + major_version=1, + minor_version=1) + + +class DPMSGetTimeouts(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(2), + rq.RequestLength(), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card16('standby_timeout'), + rq.Card16('suspend_timeout'), + rq.Card16('off_timeout'), + rq.Pad(18), + ) + + +def get_timeouts(self): + return DPMSGetTimeouts(display=self.display, + opcode=self.display.get_extension_major(extname), + major_version=1, + minor_version=1) + + +class DPMSSetTimeouts(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(3), + rq.RequestLength(), + rq.Card16('standby_timeout'), + rq.Card16('suspend_timeout'), + rq.Card16('off_timeout'), + rq.Pad(2) + ) + + +def set_timeouts(self, standby_timeout, suspend_timeout, off_timeout): + return DPMSSetTimeouts(display=self.display, + opcode=self.display.get_extension_major(extname), + major_version=1, + minor_version=1, + standby_timeout=standby_timeout, + suspend_timeout=suspend_timeout, + off_timeout=off_timeout) + + +class DPMSEnable(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(4), + rq.RequestLength(), + ) + + +def enable(self): + return DPMSEnable(display=self.display, + opcode=self.display.get_extension_major(extname), + major_version=1, + minor_version=1) + + +class DPMSDisable(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(5), + rq.RequestLength(), + ) + + +def disable(self): + return DPMSDisable(display=self.display, + opcode=self.display.get_extension_major(extname), + major_version=1, + minor_version=1) + + +class DPMSForceLevel(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(6), + rq.RequestLength(), + rq.Resource('power_level', DPMSPowerLevel), + ) + + +def force_level(self, power_level): + return DPMSForceLevel(display=self.display, + opcode=self.display.get_extension_major(extname), + major_version=1, + minor_version=1, + power_level=power_level) + + +class DPMSInfo(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(7), + rq.RequestLength(), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card16('power_level'), + rq.Bool('state'), + rq.Pad(21), + ) + + +def info(self): + return DPMSInfo(display=self.display, + opcode=self.display.get_extension_major(extname), + major_version=1, + minor_version=1) + + +def init(disp, _info): + disp.extension_add_method('display', 'dpms_get_version', get_version) + disp.extension_add_method('display', 'dpms_capable', capable) + disp.extension_add_method('display', 'dpms_get_timeouts', get_timeouts) + disp.extension_add_method('display', 'dpms_set_timeouts', set_timeouts) + disp.extension_add_method('display', 'dpms_enable', enable) + disp.extension_add_method('display', 'dpms_disable', disable) + disp.extension_add_method('display', 'dpms_force_level', force_level) + disp.extension_add_method('display', 'dpms_info', info) diff --git a/Nagstamon/thirdparty/Xlib/ext/ge.py b/Nagstamon/thirdparty/Xlib/ext/ge.py new file mode 100644 index 000000000..291ffa9a4 --- /dev/null +++ b/Nagstamon/thirdparty/Xlib/ext/ge.py @@ -0,0 +1,112 @@ +# Xlib.ext.ge -- Generic Event extension module +# +# Copyright (C) 2012 Outpost Embedded, LLC +# Forest Bond <forest.bond@rapidrollout.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +''' +ge - Generic Event Extension +''' + +from Xlib.protocol import rq + +extname = 'Generic Event Extension' + + +GenericEventCode = 35 + + +class GEQueryVersion(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(0), + rq.RequestLength(), + rq.Card32('major_version'), + rq.Card32('minor_version'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('major_version'), + rq.Card32('minor_version'), + rq.Pad(16), + ) + + +def query_version(self): + return GEQueryVersion( + display=self.display, + opcode=self.display.get_extension_major(extname), + major_version=1, + minor_version=0, + ) + + +class GenericEvent(rq.Event): + _code = GenericEventCode + _fields = rq.Struct( + rq.Card8('type'), + rq.Card8('extension'), + rq.Card16('sequence_number'), + rq.Card32('length'), + rq.Card16('evtype'), + # Some generic events make use of this space, but with + # others the data is simply discarded. In any case we + # don't need to explicitly pad this out as we are + # always given at least 32 bytes and we save + # everything after the first ten as the "data" field. + #rq.Pad(22), + ) + + def __init__(self, binarydata = None, display = None, **keys): + if binarydata: + data = binarydata[10:] + binarydata = binarydata[:10] + else: + data = '' + + rq.Event.__init__( + self, + binarydata=binarydata, + display=display, + **keys + ) + + if display: + ge_event_data = getattr(display, 'ge_event_data', None) + if ge_event_data: + estruct = ge_event_data.get((self.extension, self.evtype), None) + if estruct: + data, _ = estruct.parse_binary(data, display) + + self._data['data'] = data + + +def add_event_data(self, extension, evtype, estruct): + if not hasattr(self.display, 'ge_event_data'): + self.display.ge_event_data = {} + self.display.ge_event_data[(extension, evtype)] = estruct + + +def init(disp, info): + disp.extension_add_method('display', 'ge_query_version', query_version) + disp.extension_add_method('display', 'ge_add_event_data', add_event_data) + disp.extension_add_event(GenericEventCode, GenericEvent) diff --git a/Nagstamon/thirdparty/Xlib/ext/nvcontrol.py b/Nagstamon/thirdparty/Xlib/ext/nvcontrol.py new file mode 100644 index 000000000..7a21826c9 --- /dev/null +++ b/Nagstamon/thirdparty/Xlib/ext/nvcontrol.py @@ -0,0 +1,5393 @@ +# Xlib.ext.nvcontrol -- NV-CONTROL extension module +# +# Copyright (C) 2019 Roberto Leinardi <roberto@leinardi.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + + +"""NV-CONTROL - provide access to the NV-CONTROL extension information.""" + +from Xlib.protocol import rq + +extname = 'NV-CONTROL' + + +def query_target_count(self, target): + """Return the target count""" + reply = NVCtrlQueryTargetCountReplyRequest(display=self.display, + opcode=self.display.get_extension_major(extname), + target_type=target.type()) + return int(reply._data.get('count')) + + +def query_int_attribute(self, target, display_mask, attr): + """Return the value of an integer attribute""" + reply = NVCtrlQueryAttributeReplyRequest(display=self.display, + opcode=self.display.get_extension_major(extname), + target_id=target.id(), + target_type=target.type(), + display_mask=display_mask, + attr=attr) + if not reply._data.get('flags'): + return None + return int(reply._data.get('value')) + + +def set_int_attribute(self, target, display_mask, attr, value): + """Set the value of an integer attribute""" + reply = NVCtrlSetAttributeAndGetStatusReplyRequest(display=self.display, + opcode=self.display.get_extension_major(extname), + target_id=target.id(), + target_type=target.type(), + display_mask=display_mask, + attr=attr, + value=value) + return reply._data.get('flags') != 0 + + +def query_string_attribute(self, target, display_mask, attr): + """Return the value of a string attribute""" + reply = NVCtrlQueryStringAttributeReplyRequest(display=self.display, + opcode=self.display.get_extension_major(extname), + target_id=target.id(), + target_type=target.type(), + display_mask=display_mask, + attr=attr) + if not reply._data.get('flags'): + return None + return str(reply._data.get('string')).strip('\0') + + +def query_valid_attr_values(self, target, display_mask, attr): + """Return the value of an integer attribute""" + reply = NVCtrlQueryValidAttributeValuesReplyRequest(display=self.display, + opcode=self.display.get_extension_major(extname), + target_id=target.id(), + target_type=target.type(), + display_mask=display_mask, + attr=attr) + if not reply._data.get('flags'): + return None + return int(reply._data.get('min')), int(reply._data.get('max')) + + +def query_binary_data(self, target, display_mask, attr): + """Return binary data""" + reply = NVCtrlQueryBinaryDataReplyRequest(display=self.display, + opcode=self.display.get_extension_major(extname), + target_id=target.id(), + target_type=target.type(), + display_mask=display_mask, + attr=attr) + if not reply._data.get('flags'): + return None + return reply._data.get('data') + + +def get_coolers_used_by_gpu(self, target): + reply = NVCtrlQueryListCard32ReplyRequest(display=self.display, + opcode=self.display.get_extension_major(extname), + target_id=target.id(), + target_type=target.type(), + display_mask=0, + attr=NV_CTRL_BINARY_DATA_COOLERS_USED_BY_GPU) + if not reply._data.get('flags'): + return None + fans = reply._data.get('list') + if len(fans) > 1: + return fans[1:] + else: + return None + + +def get_gpu_count(self): + """Return the number of GPU's present in the system.""" + return int(query_target_count(self, Gpu())) + + +def get_name(self, target): + """Return the GPU product name on which the specified X screen is running""" + return query_string_attribute(self, target, 0, NV_CTRL_STRING_PRODUCT_NAME) + + +def get_driver_version(self, target): + """Return the NVIDIA (kernel level) driver version for the specified screen or GPU""" + return query_string_attribute(self, target, 0, NV_CTRL_STRING_NVIDIA_DRIVER_VERSION) + + +def get_vbios_version(self, target): + """Return the version of the VBIOS for the specified screen or GPU""" + return query_string_attribute(self, target, 0, NV_CTRL_STRING_VBIOS_VERSION) + + +def get_gpu_uuid(self, target): + return query_string_attribute(self, target, 0, NV_CTRL_STRING_GPU_UUID) + + +def get_utilization_rates(self, target): + string = query_string_attribute(self, target, 0, NV_CTRL_STRING_GPU_UTILIZATION) + result = {} + if string is not None and string != '': + for line in string.split(','): + [key, value] = line.split('=')[:2] + result[key.strip()] = int(value) if value.isdigit() else value + return result + + +def get_performance_modes(self, target): + string = query_string_attribute(self, target, 0, NV_CTRL_STRING_PERFORMANCE_MODES) + result = [] + if string is not None and string != '': + for perf in string.split(';'): + perf_dict = {} + for line in perf.split(','): + [key, value] = line.split('=')[:2] + perf_dict[key.strip()] = int(value) if value.isdigit() else value + result.append(perf_dict) + return result + + +def get_clock_info(self, target): + string = query_string_attribute(self, target, 0, NV_CTRL_STRING_GPU_CURRENT_CLOCK_FREQS) + result = {} + if string is not None and string != '': + for line in string.split(','): + [key, value] = line.split('=')[:2] + result[key.strip()] = int(value) if value.isdigit() else value + return result + + +def get_vram(self, target): + return query_int_attribute(self, target, 0, NV_CTRL_VIDEO_RAM) + + +def get_irq(self, target): + """Return the interrupt request line used by the GPU driving the screen""" + return query_int_attribute(self, target, 0, NV_CTRL_IRQ) + + +def supports_framelock(self, target): + """Return whether the underlying GPU supports Frame Lock. + + All of the other frame lock attributes are only applicable if this returns True. + """ + return query_int_attribute(self, target, 0, NV_CTRL_FRAMELOCK) == 1 + + +def gvo_supported(self, screen): + """Return whether this X screen supports GVO + + If this screen does not support GVO output, then all other GVO attributes are unavailable. + """ + return query_int_attribute(self, screen, [], NV_CTRL_GVO_SUPPORTED) + + +def get_core_temp(self, target): + """Return the current core temperature of the GPU driving the X screen.""" + return query_int_attribute(self, target, 0, NV_CTRL_GPU_CORE_TEMPERATURE) + + +def get_core_threshold(self, target): + """Return the current GPU core slowdown threshold temperature. + + It reflects the temperature at which the GPU is throttled to prevent overheating. + """ + return query_int_attribute(self, target, 0, NV_CTRL_GPU_CORE_THRESHOLD) + + +def get_default_core_threshold(self, target): + """Return the default core threshold temperature.""" + return query_int_attribute(self, target, 0, NV_CTRL_GPU_DEFAULT_CORE_THRESHOLD) + + +def get_max_core_threshold(self, target): + """Return the maximum core threshold temperature.""" + return query_int_attribute(self, target, 0, NV_CTRL_GPU_MAX_CORE_THRESHOLD) + + +def get_ambient_temp(self, target): + """Return the current temperature in the immediate neighbourhood of the GPU driving the X screen.""" + return query_int_attribute(self, target, 0, NV_CTRL_AMBIENT_TEMPERATURE) + + +def get_cuda_cores(self, target): + return query_int_attribute(self, target, 0, NV_CTRL_GPU_CORES) + + +def get_memory_bus_width(self, target): + return query_int_attribute(self, target, 0, NV_CTRL_GPU_MEMORY_BUS_WIDTH) + + +def get_total_dedicated_gpu_memory(self, target): + return query_int_attribute(self, target, 0, NV_CTRL_TOTAL_DEDICATED_GPU_MEMORY) + + +def get_used_dedicated_gpu_memory(self, target): + return query_int_attribute(self, target, 0, NV_CTRL_USED_DEDICATED_GPU_MEMORY) + + +def get_curr_pcie_link_width(self, target): + return query_int_attribute(self, target, 0, NV_CTRL_GPU_PCIE_CURRENT_LINK_WIDTH) + + +def get_max_pcie_link_width(self, target): + return query_int_attribute(self, target, 0, NV_CTRL_GPU_PCIE_MAX_LINK_WIDTH) + + +def get_curr_pcie_link_generation(self, target): + return query_int_attribute(self, target, 0, NV_CTRL_GPU_PCIE_GENERATION) + + +def get_encoder_utilization(self, target): + return query_int_attribute(self, target, 0, NV_CTRL_VIDEO_ENCODER_UTILIZATION) + + +def get_decoder_utilization(self, target): + return query_int_attribute(self, target, 0, NV_CTRL_VIDEO_DECODER_UTILIZATION) + + +def get_current_performance_level(self, target): + return query_int_attribute(self, target, 0, NV_CTRL_GPU_CURRENT_PERFORMANCE_LEVEL) + + +def get_gpu_nvclock_offset(self, target, perf_level): + return query_int_attribute(self, target, perf_level, NV_CTRL_GPU_NVCLOCK_OFFSET) + + +def set_gpu_nvclock_offset(self, target, perf_level, offset): + return set_int_attribute(self, target, perf_level, NV_CTRL_GPU_NVCLOCK_OFFSET, offset) + + +def set_gpu_nvclock_offset_all_levels(self, target, offset): + return set_int_attribute(self, target, 0, NV_CTRL_GPU_NVCLOCK_OFFSET_ALL_PERFORMANCE_LEVELS, offset) + + +def get_gpu_nvclock_offset_range(self, target, perf_level): + return query_valid_attr_values(self, target, perf_level, NV_CTRL_GPU_NVCLOCK_OFFSET) + + +def get_mem_transfer_rate_offset(self, target, perf_level): + return query_int_attribute(self, target, perf_level, NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET) + + +def set_mem_transfer_rate_offset(self, target, perf_level, offset): + return set_int_attribute(self, target, perf_level, NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET, offset) + + +def set_mem_transfer_rate_offset_all_levels(self, target, offset): + return set_int_attribute(self, target, 0, NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET_ALL_PERFORMANCE_LEVELS, offset) + + +def get_mem_transfer_rate_offset_range(self, target, perf_level): + return query_valid_attr_values(self, target, perf_level, NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET) + + +def get_cooler_manual_control_enabled(self, target): + return query_int_attribute(self, target, 0, NV_CTRL_GPU_COOLER_MANUAL_CONTROL) + + +def set_cooler_manual_control_enabled(self, target, enabled): + return set_int_attribute(self, target, 0, NV_CTRL_GPU_COOLER_MANUAL_CONTROL, 1 if enabled else 0) == 1 + + +def get_fan_duty(self, target): + return query_int_attribute(self, target, 0, NV_CTRL_THERMAL_COOLER_CURRENT_LEVEL) + + +def set_fan_duty(self, cooler, speed): + return set_int_attribute(self, cooler, 0, NV_CTRL_THERMAL_COOLER_LEVEL, speed) + + +def get_fan_rpm(self, target): + return query_int_attribute(self, target, 0, NV_CTRL_THERMAL_COOLER_SPEED) + + +def get_max_displays(self, target): + """Return the maximum number of display devices that can be driven simultaneously on a GPU. + + Note that this does not indicate the maximum number of bits that can be set in + NV_CTRL_CONNECTED_DISPLAYS, because more display devices can be connected than are actively + in use. + """ + return query_int_attribute(self, target, 0, NV_CTRL_MAX_DISPLAYS) + + +def _displaystr2num(st): + """Return a display number from a string""" + num = None + for s, n in [('DFP-', 16), ('TV-', 8), ('CRT-', 0)]: + if st.startswith(s): + try: + curnum = int(st[len(s):]) + if 0 <= curnum <= 7: + num = n + curnum + break + except Exception: + pass + if num is not None: + return num + else: + raise ValueError('Unrecognised display name: ' + st) + + +def _displays2mask(displays): + """Return a display mask from an array of display numbers.""" + mask = 0 + for d in displays: + mask += (1 << _displaystr2num(d)) + return mask + + +def init(disp, info): + disp.extension_add_method('display', 'nvcontrol_query_target_count', query_target_count) + disp.extension_add_method('display', 'nvcontrol_query_int_attribute', query_int_attribute) + disp.extension_add_method('display', 'nvcontrol_query_string_attribute', query_string_attribute) + disp.extension_add_method('display', 'nvcontrol_query_valid_attr_values', query_valid_attr_values) + disp.extension_add_method('display', 'nvcontrol_query_binary_data', query_binary_data) + disp.extension_add_method('display', 'nvcontrol_get_gpu_count', get_gpu_count) + disp.extension_add_method('display', 'nvcontrol_get_vram', get_vram) + disp.extension_add_method('display', 'nvcontrol_get_irq', get_irq) + disp.extension_add_method('display', 'nvcontrol_supports_framelock', supports_framelock) + disp.extension_add_method('display', 'nvcontrol_get_core_temp', get_core_temp) + disp.extension_add_method('display', 'nvcontrol_get_core_threshold', get_core_threshold) + disp.extension_add_method('display', 'nvcontrol_get_default_core_threshold', get_default_core_threshold) + disp.extension_add_method('display', 'nvcontrol_get_max_core_threshold', get_max_core_threshold) + disp.extension_add_method('display', 'nvcontrol_get_ambient_temp', get_ambient_temp) + disp.extension_add_method('display', 'nvcontrol_get_cuda_cores', get_cuda_cores) + disp.extension_add_method('display', 'nvcontrol_get_memory_bus_width', get_memory_bus_width) + disp.extension_add_method('display', 'nvcontrol_get_total_dedicated_gpu_memory', get_total_dedicated_gpu_memory) + disp.extension_add_method('display', 'nvcontrol_get_used_dedicated_gpu_memory', get_used_dedicated_gpu_memory) + disp.extension_add_method('display', 'nvcontrol_get_curr_pcie_link_width', get_curr_pcie_link_width) + disp.extension_add_method('display', 'nvcontrol_get_max_pcie_link_width', get_max_pcie_link_width) + disp.extension_add_method('display', 'nvcontrol_get_curr_pcie_link_generation', get_curr_pcie_link_generation) + disp.extension_add_method('display', 'nvcontrol_get_encoder_utilization', get_encoder_utilization) + disp.extension_add_method('display', 'nvcontrol_get_decoder_utilization', get_decoder_utilization) + disp.extension_add_method('display', 'nvcontrol_get_current_performance_level', get_current_performance_level) + disp.extension_add_method('display', 'nvcontrol_get_gpu_nvclock_offset', get_gpu_nvclock_offset) + disp.extension_add_method('display', 'nvcontrol_set_gpu_nvclock_offset', set_gpu_nvclock_offset) + disp.extension_add_method('display', 'nvcontrol_set_gpu_nvclock_offset_all_levels', set_gpu_nvclock_offset_all_levels) + disp.extension_add_method('display', 'nvcontrol_get_mem_transfer_rate_offset', get_mem_transfer_rate_offset) + disp.extension_add_method('display', 'nvcontrol_set_mem_transfer_rate_offset', set_mem_transfer_rate_offset) + disp.extension_add_method('display', 'nvcontrol_set_mem_transfer_rate_offset_all_levels', set_mem_transfer_rate_offset_all_levels) + disp.extension_add_method('display', 'nvcontrol_get_cooler_manual_control_enabled', + get_cooler_manual_control_enabled) + disp.extension_add_method('display', 'nvcontrol_get_fan_duty', get_fan_duty) + disp.extension_add_method('display', 'nvcontrol_set_fan_duty', set_fan_duty) + disp.extension_add_method('display', 'nvcontrol_get_fan_rpm', get_fan_rpm) + disp.extension_add_method('display', 'nvcontrol_get_coolers_used_by_gpu', get_coolers_used_by_gpu) + disp.extension_add_method('display', 'nvcontrol_get_max_displays', get_max_displays) + disp.extension_add_method('display', 'nvcontrol_get_name', get_name) + disp.extension_add_method('display', 'nvcontrol_get_driver_version', get_driver_version) + disp.extension_add_method('display', 'nvcontrol_get_vbios_version', get_vbios_version) + disp.extension_add_method('display', 'nvcontrol_get_gpu_uuid', get_gpu_uuid) + disp.extension_add_method('display', 'nvcontrol_get_utilization_rates', get_utilization_rates) + disp.extension_add_method('display', 'nvcontrol_get_performance_modes', get_performance_modes) + disp.extension_add_method('display', 'nvcontrol_get_clock_info', get_clock_info) + disp.extension_add_method('display', 'nvcontrol_set_cooler_manual_control_enabled', + set_cooler_manual_control_enabled) + disp.extension_add_method('display', 'nvcontrol_get_gpu_nvclock_offset_range', + get_gpu_nvclock_offset_range) + disp.extension_add_method('display', 'nvcontrol_get_mem_transfer_rate_offset_range', + get_mem_transfer_rate_offset_range) + + +############################################################################ +# +# Attributes +# +# Some attributes may only be read; some may require a display_mask +# argument and others may be valid only for specific target types. +# This information is encoded in the "permission" comment after each +# attribute #define, and can be queried at run time with +# XNVCTRLQueryValidAttributeValues() and/or +# XNVCTRLQueryValidTargetAttributeValues() +# +# Key to Integer Attribute "Permissions": +# +# R: The attribute is readable (in general, all attributes will be +# readable) +# +# W: The attribute is writable (attributes may not be writable for +# various reasons: they represent static system information, they +# can only be changed by changing an XF86Config option, etc). +# +# D: The attribute requires the display mask argument. The +# attributes NV_CTRL_CONNECTED_DISPLAYS and NV_CTRL_ENABLED_DISPLAYS +# will be a bitmask of what display devices are connected and what +# display devices are enabled for use in X, respectively. Each bit +# in the bitmask represents a display device; it is these bits which +# should be used as the display_mask when dealing with attributes +# designated with "D" below. For attributes that do not require the +# display mask, the argument is ignored. +# +# Alternatively, NV-CONTROL versions 1.27 and greater allow these +# attributes to be accessed via display target types, in which case +# the display_mask is ignored. +# +# G: The attribute may be queried using an NV_CTRL_TARGET_TYPE_GPU +# target type via XNVCTRLQueryTargetAttribute(). +# +# F: The attribute may be queried using an NV_CTRL_TARGET_TYPE_FRAMELOCK +# target type via XNVCTRLQueryTargetAttribute(). +# +# X: When Xinerama is enabled, this attribute is kept consistent across +# all Physical X Screens; assignment of this attribute will be +# broadcast by the NVIDIA X Driver to all X Screens. +# +# V: The attribute may be queried using an NV_CTRL_TARGET_TYPE_VCSC +# target type via XNVCTRLQueryTargetAttribute(). +# +# I: The attribute may be queried using an NV_CTRL_TARGET_TYPE_GVI target type +# via XNVCTRLQueryTargetAttribute(). +# +# Q: The attribute is a 64-bit integer attribute; use the 64-bit versions +# of the appropriate query interfaces. +# +# C: The attribute may be queried using an NV_CTRL_TARGET_TYPE_COOLER target +# type via XNVCTRLQueryTargetAttribute(). +# +# S: The attribute may be queried using an NV_CTRL_TARGET_TYPE_THERMAL_SENSOR +# target type via XNVCTRLQueryTargetAttribute(). +# +# T: The attribute may be queried using an +# NV_CTRL_TARGET_TYPE_3D_VISION_PRO_TRANSCEIVER target type +# via XNVCTRLQueryTargetAttribute(). +# +# NOTE: Unless mentioned otherwise, all attributes may be queried using +# an NV_CTRL_TARGET_TYPE_X_SCREEN target type via +# XNVCTRLQueryTargetAttribute(). +# + + +############################################################################ + +# +# Integer attributes: +# +# Integer attributes can be queried through the XNVCTRLQueryAttribute() and +# XNVCTRLQueryTargetAttribute() function calls. +# +# Integer attributes can be set through the XNVCTRLSetAttribute() and +# XNVCTRLSetTargetAttribute() function calls. +# +# Unless otherwise noted, all integer attributes can be queried/set +# using an NV_CTRL_TARGET_TYPE_X_SCREEN target. Attributes that cannot +# take an NV_CTRL_TARGET_TYPE_X_SCREEN also cannot be queried/set through +# XNVCTRLQueryAttribute()/XNVCTRLSetAttribute() (Since these assume +# an X Screen target). +# + + +# +# NV_CTRL_FLATPANEL_SCALING - not supported +# + +NV_CTRL_FLATPANEL_SCALING = 2 # not supported +NV_CTRL_FLATPANEL_SCALING_DEFAULT = 0 # not supported +NV_CTRL_FLATPANEL_SCALING_NATIVE = 1 # not supported +NV_CTRL_FLATPANEL_SCALING_SCALED = 2 # not supported +NV_CTRL_FLATPANEL_SCALING_CENTERED = 3 # not supported +NV_CTRL_FLATPANEL_SCALING_ASPECT_SCALED = 4 # not supported + +# +# NV_CTRL_FLATPANEL_DITHERING - not supported +# +# NV_CTRL_DITHERING should be used instead. +# + +NV_CTRL_FLATPANEL_DITHERING = 3 # not supported +NV_CTRL_FLATPANEL_DITHERING_DEFAULT = 0 # not supported +NV_CTRL_FLATPANEL_DITHERING_ENABLED = 1 # not supported +NV_CTRL_FLATPANEL_DITHERING_DISABLED = 2 # not supported + +# +# NV_CTRL_DITHERING - the requested dithering configuration; +# possible values are: +# +# 0: auto (the driver will decide when to dither) +# 1: enabled (the driver will always dither when possible) +# 2: disabled (the driver will never dither) +# + +NV_CTRL_DITHERING = 3 # RWDG +NV_CTRL_DITHERING_AUTO = 0 +NV_CTRL_DITHERING_ENABLED = 1 +NV_CTRL_DITHERING_DISABLED = 2 + +# +# NV_CTRL_DIGITAL_VIBRANCE - sets the digital vibrance level for the +# specified display device. +# + +NV_CTRL_DIGITAL_VIBRANCE = 4 # RWDG + +# +# NV_CTRL_BUS_TYPE - returns the bus type through which the specified device +# is connected to the computer. +# When this attribute is queried on an X screen target, the bus type of the +# GPU driving the X screen is returned. +# + +NV_CTRL_BUS_TYPE = 5 # R--GI +NV_CTRL_BUS_TYPE_AGP = 0 +NV_CTRL_BUS_TYPE_PCI = 1 +NV_CTRL_BUS_TYPE_PCI_EXPRESS = 2 +NV_CTRL_BUS_TYPE_INTEGRATED = 3 + +# +# NV_CTRL_TOTAL_GPU_MEMORY - returns the total amount of memory available +# to the specified GPU (or the GPU driving the specified X +# screen). Note: if the GPU supports TurboCache(TM), the value +# reported may exceed the amount of video memory installed on the +# GPU. The value reported for integrated GPUs may likewise exceed +# the amount of dedicated system memory set aside by the system +# BIOS for use by the integrated GPU. +# + +NV_CTRL_TOTAL_GPU_MEMORY = 6 # R--G +NV_CTRL_VIDEO_RAM = NV_CTRL_TOTAL_GPU_MEMORY + +# +# NV_CTRL_IRQ - returns the interrupt request line used by the specified +# device. +# When this attribute is queried on an X screen target, the IRQ of the GPU +# driving the X screen is returned. +# + +NV_CTRL_IRQ = 7 # R--GI + +# +# NV_CTRL_OPERATING_SYSTEM - returns the operating system on which +# the X server is running. +# + +NV_CTRL_OPERATING_SYSTEM = 8 # R--G +NV_CTRL_OPERATING_SYSTEM_LINUX = 0 +NV_CTRL_OPERATING_SYSTEM_FREEBSD = 1 +NV_CTRL_OPERATING_SYSTEM_SUNOS = 2 + +# +# NV_CTRL_SYNC_TO_VBLANK - enables sync to vblank for OpenGL clients. +# This setting is only applied to OpenGL clients that are started +# after this setting is applied. +# + +NV_CTRL_SYNC_TO_VBLANK = 9 # RW-X +NV_CTRL_SYNC_TO_VBLANK_OFF = 0 +NV_CTRL_SYNC_TO_VBLANK_ON = 1 + +# +# NV_CTRL_LOG_ANISO - enables anisotropic filtering for OpenGL +# clients; on some NVIDIA hardware, this can only be enabled or +# disabled; on other hardware different levels of anisotropic +# filtering can be specified. This setting is only applied to OpenGL +# clients that are started after this setting is applied. +# + +NV_CTRL_LOG_ANISO = 10 # RW-X + +# +# NV_CTRL_FSAA_MODE - the FSAA setting for OpenGL clients; possible +# FSAA modes: +# +# NV_CTRL_FSAA_MODE_2x "2x Bilinear Multisampling" +# NV_CTRL_FSAA_MODE_2x_5t "2x Quincunx Multisampling" +# NV_CTRL_FSAA_MODE_15x15 "1.5 x 1.5 Supersampling" +# NV_CTRL_FSAA_MODE_2x2 "2 x 2 Supersampling" +# NV_CTRL_FSAA_MODE_4x "4x Bilinear Multisampling" +# NV_CTRL_FSAA_MODE_4x_9t "4x Gaussian Multisampling" +# NV_CTRL_FSAA_MODE_8x "2x Bilinear Multisampling by 4x Supersampling" +# NV_CTRL_FSAA_MODE_16x "4x Bilinear Multisampling by 4x Supersampling" +# NV_CTRL_FSAA_MODE_8xS "4x Multisampling by 2x Supersampling" +# +# This setting is only applied to OpenGL clients that are started +# after this setting is applied. +# + +NV_CTRL_FSAA_MODE = 11 # RW-X +NV_CTRL_FSAA_MODE_NONE = 0 +NV_CTRL_FSAA_MODE_2x = 1 +NV_CTRL_FSAA_MODE_2x_5t = 2 +NV_CTRL_FSAA_MODE_15x15 = 3 +NV_CTRL_FSAA_MODE_2x2 = 4 +NV_CTRL_FSAA_MODE_4x = 5 +NV_CTRL_FSAA_MODE_4x_9t = 6 +NV_CTRL_FSAA_MODE_8x = 7 +NV_CTRL_FSAA_MODE_16x = 8 +NV_CTRL_FSAA_MODE_8xS = 9 +NV_CTRL_FSAA_MODE_8xQ = 10 +NV_CTRL_FSAA_MODE_16xS = 11 +NV_CTRL_FSAA_MODE_16xQ = 12 +NV_CTRL_FSAA_MODE_32xS = 13 +NV_CTRL_FSAA_MODE_32x = 14 +NV_CTRL_FSAA_MODE_64xS = 15 +NV_CTRL_FSAA_MODE_MAX = NV_CTRL_FSAA_MODE_64xS + +# +# NV_CTRL_UBB - returns whether UBB is enabled for the specified X +# screen. +# + +NV_CTRL_UBB = 13 # R-- +NV_CTRL_UBB_OFF = 0 +NV_CTRL_UBB_ON = 1 + +# +# NV_CTRL_OVERLAY - returns whether the RGB overlay is enabled for +# the specified X screen. +# + +NV_CTRL_OVERLAY = 14 # R-- +NV_CTRL_OVERLAY_OFF = 0 +NV_CTRL_OVERLAY_ON = 1 + +# +# NV_CTRL_STEREO - returns whether stereo (and what type) is enabled +# for the specified X screen. +# + +NV_CTRL_STEREO = 16 # R-- +NV_CTRL_STEREO_OFF = 0 +NV_CTRL_STEREO_DDC = 1 +NV_CTRL_STEREO_BLUELINE = 2 +NV_CTRL_STEREO_DIN = 3 +NV_CTRL_STEREO_PASSIVE_EYE_PER_DPY = 4 +NV_CTRL_STEREO_VERTICAL_INTERLACED = 5 +NV_CTRL_STEREO_COLOR_INTERLACED = 6 +NV_CTRL_STEREO_HORIZONTAL_INTERLACED = 7 +NV_CTRL_STEREO_CHECKERBOARD_PATTERN = 8 +NV_CTRL_STEREO_INVERSE_CHECKERBOARD_PATTERN = 9 +NV_CTRL_STEREO_3D_VISION = 10 +NV_CTRL_STEREO_3D_VISION_PRO = 11 +NV_CTRL_STEREO_HDMI_3D = 12 +NV_CTRL_STEREO_TRIDELITY_SL = 13 +NV_CTRL_STEREO_INBAND_STEREO_SIGNALING = 14 +NV_CTRL_STEREO_MAX = NV_CTRL_STEREO_INBAND_STEREO_SIGNALING + +# +# NV_CTRL_EMULATE - not supported +# + +NV_CTRL_EMULATE = 17 # not supported +NV_CTRL_EMULATE_NONE = 0 # not supported + +# +# NV_CTRL_TWINVIEW - returns whether TwinView is enabled for the +# specified X screen. +# + +NV_CTRL_TWINVIEW = 18 # R-- +NV_CTRL_TWINVIEW_NOT_ENABLED = 0 +NV_CTRL_TWINVIEW_ENABLED = 1 + +# +# NV_CTRL_CONNECTED_DISPLAYS - deprecated +# +# NV_CTRL_BINARY_DATA_DISPLAYS_CONNECTED_TO_GPU and +# NV_CTRL_BINARY_DATA_DISPLAYS_ASSIGNED_TO_XSCREEN should be used instead. +# + +NV_CTRL_CONNECTED_DISPLAYS = 19 # deprecated + +# +# NV_CTRL_ENABLED_DISPLAYS - Event that notifies when one or more display +# devices are enabled or disabled on a GPU and/or X screen. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. +# +# Note: Querying this value has been deprecated. +# NV_CTRL_BINARY_DATA_DISPLAYS_CONNECTED_TO_GPU, +# NV_CTRL_DISPLAY_ENABLED, and +# NV_CTRL_BINARY_DATA_DISPLAYS_ENABLED_ON_XSCREEN should be used +# instead to obtain the list of enabled displays. +# + +NV_CTRL_ENABLED_DISPLAYS = 20 # ---G + +############################################################################ +# +# Integer attributes specific to configuring Frame Lock on boards that +# support it. +# + + +# +# NV_CTRL_FRAMELOCK - returns whether the underlying GPU supports +# Frame Lock. All of the other frame lock attributes are only +# applicable if NV_CTRL_FRAMELOCK is _SUPPORTED. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. +# + +NV_CTRL_FRAMELOCK = 21 # R--G +NV_CTRL_FRAMELOCK_NOT_SUPPORTED = 0 +NV_CTRL_FRAMELOCK_SUPPORTED = 1 + +# +# NV_CTRL_FRAMELOCK_MASTER - deprecated +# +# NV_CTRL_FRAMELOCK_DISPLAY_CONFIG should be used instead. +# + +NV_CTRL_FRAMELOCK_MASTER = 22 # deprecated +NV_CTRL_FRAMELOCK_MASTER_FALSE = 0 # deprecated +NV_CTRL_FRAMELOCK_MASTER_TRUE = 1 # deprecated + +# +# NV_CTRL_FRAMELOCK_POLARITY - sync either to the rising edge of the +# frame lock pulse, the falling edge of the frame lock pulse or both. +# +# On Quadro Sync II, this attribute is ignored when +# NV_CTRL_USE_HOUSE_SYNC is OUTPUT. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN +# target. +# + +NV_CTRL_FRAMELOCK_POLARITY = 23 # RW-F +NV_CTRL_FRAMELOCK_POLARITY_RISING_EDGE = 0x1 +NV_CTRL_FRAMELOCK_POLARITY_FALLING_EDGE = 0x2 +NV_CTRL_FRAMELOCK_POLARITY_BOTH_EDGES = 0x3 + +# +# NV_CTRL_FRAMELOCK_SYNC_DELAY - delay between the frame lock pulse +# and the GPU sync. This value must be multiplied by +# NV_CTRL_FRAMELOCK_SYNC_DELAY_RESOLUTION to determine the sync delay in +# nanoseconds. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN +# target. +# +# USAGE NOTE: NV_CTRL_FRAMELOCK_SYNC_DELAY_MAX and +# NV_CTRL_FRAMELOCK_SYNC_DELAY_FACTOR are deprecated. +# The Sync Delay _MAX and _FACTOR are different for different +# Quadro Sync products and so, to be correct, the valid values for +# NV_CTRL_FRAMELOCK_SYNC_DELAY must be queried to get the range +# of acceptable sync delay values, and +# NV_CTRL_FRAMELOCK_SYNC_DELAY_RESOLUTION must be queried to +# obtain the correct factor. +# + +NV_CTRL_FRAMELOCK_SYNC_DELAY = 24 # RW-F +NV_CTRL_FRAMELOCK_SYNC_DELAY_MAX = 2047 # deprecated +NV_CTRL_FRAMELOCK_SYNC_DELAY_FACTOR = 7.81 # deprecated + +# +# NV_CTRL_FRAMELOCK_SYNC_INTERVAL - how many house sync pulses +# between the frame lock sync generation (0 == sync every house sync); +# this only applies to the master when receiving house sync. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN +# target. +# + +NV_CTRL_FRAMELOCK_SYNC_INTERVAL = 25 # RW-F + +# +# NV_CTRL_FRAMELOCK_PORT0_STATUS - status of the rj45 port0. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN +# target. +# + +NV_CTRL_FRAMELOCK_PORT0_STATUS = 26 # R--F +NV_CTRL_FRAMELOCK_PORT0_STATUS_INPUT = 0 +NV_CTRL_FRAMELOCK_PORT0_STATUS_OUTPUT = 1 + +# +# NV_CTRL_FRAMELOCK_PORT1_STATUS - status of the rj45 port1. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN +# target. +# + +NV_CTRL_FRAMELOCK_PORT1_STATUS = 27 # R--F +NV_CTRL_FRAMELOCK_PORT1_STATUS_INPUT = 0 +NV_CTRL_FRAMELOCK_PORT1_STATUS_OUTPUT = 1 + +# +# NV_CTRL_FRAMELOCK_HOUSE_STATUS - returns whether or not the house +# sync input signal was detected on the BNC connector of the frame lock +# board. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN +# target. +# + +NV_CTRL_FRAMELOCK_HOUSE_STATUS = 28 # R--F +NV_CTRL_FRAMELOCK_HOUSE_STATUS_NOT_DETECTED = 0 +NV_CTRL_FRAMELOCK_HOUSE_STATUS_DETECTED = 1 + +# +# NV_CTRL_FRAMELOCK_SYNC - enable/disable the syncing of display +# devices to the frame lock pulse as specified by previous calls to +# NV_CTRL_FRAMELOCK_DISPLAY_CONFIG. +# +# This attribute can only be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_GPU target. This attribute cannot be +# queried using a NV_CTRL_TARGET_TYPE_X_SCREEN. +# + +NV_CTRL_FRAMELOCK_SYNC = 29 # RW-G +NV_CTRL_FRAMELOCK_SYNC_DISABLE = 0 +NV_CTRL_FRAMELOCK_SYNC_ENABLE = 1 + +# +# NV_CTRL_FRAMELOCK_SYNC_READY - reports whether a frame lock +# board is receiving sync (regardless of whether or not any display +# devices are using the sync). +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN +# target. +# + +NV_CTRL_FRAMELOCK_SYNC_READY = 30 # R--F +NV_CTRL_FRAMELOCK_SYNC_READY_FALSE = 0 +NV_CTRL_FRAMELOCK_SYNC_READY_TRUE = 1 + +# +# NV_CTRL_FRAMELOCK_STEREO_SYNC - this indicates that the GPU stereo +# signal is in sync with the frame lock stereo signal. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN +# target. +# + +NV_CTRL_FRAMELOCK_STEREO_SYNC = 31 # R--G +NV_CTRL_FRAMELOCK_STEREO_SYNC_FALSE = 0 +NV_CTRL_FRAMELOCK_STEREO_SYNC_TRUE = 1 + +# +# NV_CTRL_FRAMELOCK_TEST_SIGNAL - to test the connections in the sync +# group, tell the master to enable a test signal, then query port[01] +# status and sync_ready on all slaves. When done, tell the master to +# disable the test signal. Test signal should only be manipulated +# while NV_CTRL_FRAMELOCK_SYNC is enabled. +# +# The TEST_SIGNAL is also used to reset the Universal Frame Count (as +# returned by the glXQueryFrameCountNV() function in the +# GLX_NV_swap_group extension). Note: for best accuracy of the +# Universal Frame Count, it is recommended to toggle the TEST_SIGNAL +# on and off after enabling frame lock. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. +# + +NV_CTRL_FRAMELOCK_TEST_SIGNAL = 32 # RW-G +NV_CTRL_FRAMELOCK_TEST_SIGNAL_DISABLE = 0 +NV_CTRL_FRAMELOCK_TEST_SIGNAL_ENABLE = 1 + +# +# NV_CTRL_FRAMELOCK_ETHERNET_DETECTED - The frame lock boards are +# cabled together using regular cat5 cable, connecting to rj45 ports +# on the backplane of the card. There is some concern that users may +# think these are ethernet ports and connect them to a +# router/hub/etc. The hardware can detect this and will shut off to +# prevent damage (either to itself or to the router). +# NV_CTRL_FRAMELOCK_ETHERNET_DETECTED may be called to find out if +# ethernet is connected to one of the rj45 ports. An appropriate +# error message should then be displayed. The _PORT0 and _PORT1 +# values may be or'ed together. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN +# target. +# + +NV_CTRL_FRAMELOCK_ETHERNET_DETECTED = 33 # R--F +NV_CTRL_FRAMELOCK_ETHERNET_DETECTED_NONE = 0 +NV_CTRL_FRAMELOCK_ETHERNET_DETECTED_PORT0 = 0x1 +NV_CTRL_FRAMELOCK_ETHERNET_DETECTED_PORT1 = 0x2 + +# +# NV_CTRL_FRAMELOCK_VIDEO_MODE - get/set what video mode is used +# to interperate the house sync signal. This should only be set +# on the master. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN +# target. +# + +NV_CTRL_FRAMELOCK_VIDEO_MODE = 34 # RW-F +NV_CTRL_FRAMELOCK_VIDEO_MODE_NONE = 0 +NV_CTRL_FRAMELOCK_VIDEO_MODE_TTL = 1 +NV_CTRL_FRAMELOCK_VIDEO_MODE_NTSCPALSECAM = 2 +NV_CTRL_FRAMELOCK_VIDEO_MODE_HDTV = 3 + +# +# During FRAMELOCK bring-up, the above values were redefined to +# these: +# + +NV_CTRL_FRAMELOCK_VIDEO_MODE_COMPOSITE_AUTO = 0 +NV_CTRL_FRAMELOCK_VIDEO_MODE_COMPOSITE_BI_LEVEL = 2 +NV_CTRL_FRAMELOCK_VIDEO_MODE_COMPOSITE_TRI_LEVEL = 3 + +# +# NV_CTRL_FRAMELOCK_SYNC_RATE - this is the refresh rate that the +# frame lock board is sending to the GPU, in milliHz. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN +# target. +# + +NV_CTRL_FRAMELOCK_SYNC_RATE = 35 # R--F + +############################################################################ + +# +# NV_CTRL_FORCE_GENERIC_CPU - not supported +# + +NV_CTRL_FORCE_GENERIC_CPU = 37 # not supported +NV_CTRL_FORCE_GENERIC_CPU_DISABLE = 0 # not supported +NV_CTRL_FORCE_GENERIC_CPU_ENABLE = 1 # not supported + +# +# NV_CTRL_OPENGL_AA_LINE_GAMMA - for OpenGL clients, allow +# Gamma-corrected antialiased lines to consider variances in the +# color display capabilities of output devices when rendering smooth +# lines. Only available on recent Quadro GPUs. This setting is only +# applied to OpenGL clients that are started after this setting is +# applied. +# + +NV_CTRL_OPENGL_AA_LINE_GAMMA = 38 # RW-X +NV_CTRL_OPENGL_AA_LINE_GAMMA_DISABLE = 0 +NV_CTRL_OPENGL_AA_LINE_GAMMA_ENABLE = 1 + +# +# NV_CTRL_FRAMELOCK_TIMING - this is TRUE when the gpu is both receiving +# and locked to an input timing signal. Timing information may come from +# the following places: Another frame lock device that is set to master, +# the house sync signal, or the GPU's internal timing from a display +# device. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. +# + +NV_CTRL_FRAMELOCK_TIMING = 39 # R--G +NV_CTRL_FRAMELOCK_TIMING_FALSE = 0 +NV_CTRL_FRAMELOCK_TIMING_TRUE = 1 + +# +# NV_CTRL_FLIPPING_ALLOWED - when TRUE, OpenGL will swap by flipping +# when possible; when FALSE, OpenGL will always swap by blitting. +# + +NV_CTRL_FLIPPING_ALLOWED = 40 # RW-X +NV_CTRL_FLIPPING_ALLOWED_FALSE = 0 +NV_CTRL_FLIPPING_ALLOWED_TRUE = 1 + +# +# NV_CTRL_ARCHITECTURE - returns the architecture on which the X server is +# running. +# + +NV_CTRL_ARCHITECTURE = 41 # R-- +NV_CTRL_ARCHITECTURE_X86 = 0 +NV_CTRL_ARCHITECTURE_X86_64 = 1 +NV_CTRL_ARCHITECTURE_IA64 = 2 +NV_CTRL_ARCHITECTURE_ARM = 3 +NV_CTRL_ARCHITECTURE_AARCH64 = 4 +NV_CTRL_ARCHITECTURE_PPC64LE = 5 + +# +# NV_CTRL_TEXTURE_CLAMPING - texture clamping mode in OpenGL. By +# default, _SPEC is used, which forces OpenGL texture clamping to +# conform with the OpenGL specification. _EDGE forces NVIDIA's +# OpenGL implementation to remap GL_CLAMP to GL_CLAMP_TO_EDGE, +# which is not strictly conformant, but some applications rely on +# the non-conformant behavior. +# + +NV_CTRL_TEXTURE_CLAMPING = 42 # RW-X +NV_CTRL_TEXTURE_CLAMPING_EDGE = 0 +NV_CTRL_TEXTURE_CLAMPING_SPEC = 1 + +# +# The NV_CTRL_CURSOR_SHADOW - not supported +# +# use an ARGB cursor instead. +# + +NV_CTRL_CURSOR_SHADOW = 43 # not supported +NV_CTRL_CURSOR_SHADOW_DISABLE = 0 # not supported +NV_CTRL_CURSOR_SHADOW_ENABLE = 1 # not supported + +NV_CTRL_CURSOR_SHADOW_ALPHA = 44 # not supported +NV_CTRL_CURSOR_SHADOW_RED = 45 # not supported +NV_CTRL_CURSOR_SHADOW_GREEN = 46 # not supported +NV_CTRL_CURSOR_SHADOW_BLUE = 47 # not supported + +NV_CTRL_CURSOR_SHADOW_X_OFFSET = 48 # not supported +NV_CTRL_CURSOR_SHADOW_Y_OFFSET = 49 # not supported + +# +# When Application Control for FSAA is enabled, then what the +# application requests is used, and NV_CTRL_FSAA_MODE is ignored. If +# this is disabled, then any application setting is overridden with +# NV_CTRL_FSAA_MODE +# + +NV_CTRL_FSAA_APPLICATION_CONTROLLED = 50 # RW-X +NV_CTRL_FSAA_APPLICATION_CONTROLLED_ENABLED = 1 +NV_CTRL_FSAA_APPLICATION_CONTROLLED_DISABLED = 0 + +# +# When Application Control for LogAniso is enabled, then what the +# application requests is used, and NV_CTRL_LOG_ANISO is ignored. If +# this is disabled, then any application setting is overridden with +# NV_CTRL_LOG_ANISO +# + +NV_CTRL_LOG_ANISO_APPLICATION_CONTROLLED = 51 # RW-X +NV_CTRL_LOG_ANISO_APPLICATION_CONTROLLED_ENABLED = 1 +NV_CTRL_LOG_ANISO_APPLICATION_CONTROLLED_DISABLED = 0 + +# +# IMAGE_SHARPENING adjusts the sharpness of the display's image +# quality by amplifying high frequency content. Valid values will +# normally be in the range [0,32). Only available on GeForceFX or +# newer. +# + +NV_CTRL_IMAGE_SHARPENING = 52 # RWDG + +# +# NV_CTRL_TV_OVERSCAN - not supported +# + +NV_CTRL_TV_OVERSCAN = 53 # not supported + +# +# NV_CTRL_TV_FLICKER_FILTER - not supported +# + +NV_CTRL_TV_FLICKER_FILTER = 54 # not supported + +# +# NV_CTRL_TV_BRIGHTNESS - not supported +# + +NV_CTRL_TV_BRIGHTNESS = 55 # not supported + +# +# NV_CTRL_TV_HUE - not supported +# + +NV_CTRL_TV_HUE = 56 # not supported + +# +# NV_CTRL_TV_CONTRAST - not suppoerted +# + +NV_CTRL_TV_CONTRAST = 57 # not supported + +# +# NV_CTRL_TV_SATURATION - not supported +# + +NV_CTRL_TV_SATURATION = 58 # not supported + +# +# NV_CTRL_TV_RESET_SETTINGS - not supported +# + +NV_CTRL_TV_RESET_SETTINGS = 59 # not supported + +# +# NV_CTRL_GPU_CORE_TEMPERATURE reports the current core temperature +# of the GPU driving the X screen. +# + +NV_CTRL_GPU_CORE_TEMPERATURE = 60 # R--G + +# +# NV_CTRL_GPU_CORE_THRESHOLD reports the current GPU core slowdown +# threshold temperature, NV_CTRL_GPU_DEFAULT_CORE_THRESHOLD and +# NV_CTRL_GPU_MAX_CORE_THRESHOLD report the default and MAX core +# slowdown threshold temperatures. +# +# NV_CTRL_GPU_CORE_THRESHOLD reflects the temperature at which the +# GPU is throttled to prevent overheating. +# + +NV_CTRL_GPU_CORE_THRESHOLD = 61 # R--G +NV_CTRL_GPU_DEFAULT_CORE_THRESHOLD = 62 # R--G +NV_CTRL_GPU_MAX_CORE_THRESHOLD = 63 # R--G + +# +# NV_CTRL_AMBIENT_TEMPERATURE reports the current temperature in the +# immediate neighbourhood of the GPU driving the X screen. +# + +NV_CTRL_AMBIENT_TEMPERATURE = 64 # R--G + +# +# NV_CTRL_PBUFFER_SCANOUT_SUPPORTED - returns whether this X screen +# supports scanout of FP pbuffers; +# +# if this screen does not support PBUFFER_SCANOUT, then all other +# PBUFFER_SCANOUT attributes are unavailable. +# +# PBUFFER_SCANOUT is supported if and only if: +# - Twinview is configured with clone mode. The secondary screen is used to +# scanout the pbuffer. +# - The desktop is running in with 16 bits per pixel. +# +NV_CTRL_PBUFFER_SCANOUT_SUPPORTED = 65 # not supported +NV_CTRL_PBUFFER_SCANOUT_FALSE = 0 +NV_CTRL_PBUFFER_SCANOUT_TRUE = 1 + +# +# NV_CTRL_PBUFFER_SCANOUT_XID indicates the XID of the pbuffer used for +# scanout. +# +NV_CTRL_PBUFFER_SCANOUT_XID = 66 # not supported + +############################################################################ +# +# The NV_CTRL_GVO_* integer attributes are used to configure GVO +# (Graphics to Video Out). This functionality is available, for +# example, on the Quadro SDI Output card. +# +# The following is a typical usage pattern for the GVO attributes: +# +# - query NV_CTRL_GVO_SUPPORTED to determine if the X screen supports GV0. +# +# - specify NV_CTRL_GVO_SYNC_MODE (one of FREE_RUNNING, GENLOCK, or +# FRAMELOCK); if you specify GENLOCK or FRAMELOCK, you should also +# specify NV_CTRL_GVO_SYNC_SOURCE. +# +# - Use NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECTED and +# NV_CTRL_GVO_SDI_SYNC_INPUT_DETECTED to detect what input syncs are +# present. +# +# (If no analog sync is detected but it is known that a valid +# bi-level or tri-level sync is connected set +# NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECT_MODE appropriately and +# retest with NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECTED). +# +# - if syncing to input sync, query the +# NV_CTRL_GVIO_DETECTED_VIDEO_FORMAT attribute; note that Input video +# format can only be queried after SYNC_SOURCE is specified. +# +# - specify the NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT +# +# - specify the NV_CTRL_GVO_DATA_FORMAT +# +# - specify any custom Color Space Conversion (CSC) matrix, offset, +# and scale with XNVCTRLSetGvoColorConversion(). +# +# - if using the GLX_NV_video_out extension to display one or more +# pbuffers, call glXGetVideoDeviceNV() to lock the GVO output for use +# by the GLX client; then bind the pbuffer(s) to the GVO output with +# glXBindVideoImageNV() and send pbuffers to the GVO output with +# glXSendPbufferToVideoNV(); see the GLX_NV_video_out spec for more +# details. +# +# - if using the GLX_NV_present_video extension, call +# glXBindVideoDeviceNV() to bind the GVO video device to current +# OpenGL context. +# +# Note that setting most GVO attributes only causes the value to be +# cached in the X server. The values will be flushed to the hardware +# either when the next MetaMode is set that uses the GVO display +# device, or when a GLX pbuffer is bound to the GVO output (with +# glXBindVideoImageNV()). +# +# Note that GLX_NV_video_out/GLX_NV_present_video and X screen use +# are mutually exclusive. If a MetaMode is currently using the GVO +# device, then glXGetVideoDeviceNV and glXBindVideoImageNV() will +# fail. Similarly, if a GLX client has locked the GVO output (via +# glXGetVideoDeviceNV or glXBindVideoImageNV), then setting a +# MetaMode that uses the GVO device will fail. The +# NV_CTRL_GVO_GLX_LOCKED event will be sent when a GLX client locks +# the GVO output. +# +# + + +# +# NV_CTRL_GVO_SUPPORTED - returns whether this X screen supports GVO; +# if this screen does not support GVO output, then all other GVO +# attributes are unavailable. +# + +NV_CTRL_GVO_SUPPORTED = 67 # R-- +NV_CTRL_GVO_SUPPORTED_FALSE = 0 +NV_CTRL_GVO_SUPPORTED_TRUE = 1 + +# +# NV_CTRL_GVO_SYNC_MODE - selects the GVO sync mode; possible values +# are: +# +# FREE_RUNNING - GVO does not sync to any external signal +# +# GENLOCK - the GVO output is genlocked to an incoming sync signal; +# genlocking locks at hsync. This requires that the output video +# format exactly match the incoming sync video format. +# +# FRAMELOCK - the GVO output is frame locked to an incoming sync +# signal; frame locking locks at vsync. This requires that the output +# video format have the same refresh rate as the incoming sync video +# format. +# + +NV_CTRL_GVO_SYNC_MODE = 68 # RW- +NV_CTRL_GVO_SYNC_MODE_FREE_RUNNING = 0 +NV_CTRL_GVO_SYNC_MODE_GENLOCK = 1 +NV_CTRL_GVO_SYNC_MODE_FRAMELOCK = 2 + +# +# NV_CTRL_GVO_SYNC_SOURCE - if NV_CTRL_GVO_SYNC_MODE is set to either +# GENLOCK or FRAMELOCK, this controls which sync source is used as +# the incoming sync signal (either Composite or SDI). If +# NV_CTRL_GVO_SYNC_MODE is FREE_RUNNING, this attribute has no +# effect. +# + +NV_CTRL_GVO_SYNC_SOURCE = 69 # RW- +NV_CTRL_GVO_SYNC_SOURCE_COMPOSITE = 0 +NV_CTRL_GVO_SYNC_SOURCE_SDI = 1 + +# +# NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT - specifies the desired output video +# format for GVO devices or the desired input video format for GVI devices. +# +# Note that for GVO, the valid video formats may vary depending on +# the NV_CTRL_GVO_SYNC_MODE and the incoming sync video format. See +# the definition of NV_CTRL_GVO_SYNC_MODE. +# +# Note that when querying the ValidValues for this data type, the +# values are reported as bits within a bitmask +# (ATTRIBUTE_TYPE_INT_BITS); unfortunately, there are more valid +# value bits than will fit in a single 32-bit value. To solve this, +# query the ValidValues for NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT to +# check which of the first 31 VIDEO_FORMATS are valid, query the +# ValidValues for NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT2 to check which +# of the 32-63 VIDEO_FORMATS are valid, and query the ValidValues of +# NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT3 to check which of the 64-95 +# VIDEO_FORMATS are valid. +# +# Note: Setting this attribute on a GVI device may also result in the +# following NV-CONTROL attributes being reset on that device (to +# ensure the configuration remains valid): +# NV_CTRL_GVI_REQUESTED_STREAM_BITS_PER_COMPONENT +# NV_CTRL_GVI_REQUESTED_STREAM_COMPONENT_SAMPLING +# + +NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT = 70 # RW--I + +NV_CTRL_GVIO_VIDEO_FORMAT_NONE = 0 +NV_CTRL_GVIO_VIDEO_FORMAT_487I_59_94_SMPTE259_NTSC = 1 +NV_CTRL_GVIO_VIDEO_FORMAT_576I_50_00_SMPTE259_PAL = 2 +NV_CTRL_GVIO_VIDEO_FORMAT_720P_59_94_SMPTE296 = 3 +NV_CTRL_GVIO_VIDEO_FORMAT_720P_60_00_SMPTE296 = 4 +NV_CTRL_GVIO_VIDEO_FORMAT_1035I_59_94_SMPTE260 = 5 +NV_CTRL_GVIO_VIDEO_FORMAT_1035I_60_00_SMPTE260 = 6 +NV_CTRL_GVIO_VIDEO_FORMAT_1080I_50_00_SMPTE295 = 7 +NV_CTRL_GVIO_VIDEO_FORMAT_1080I_50_00_SMPTE274 = 8 +NV_CTRL_GVIO_VIDEO_FORMAT_1080I_59_94_SMPTE274 = 9 +NV_CTRL_GVIO_VIDEO_FORMAT_1080I_60_00_SMPTE274 = 10 +NV_CTRL_GVIO_VIDEO_FORMAT_1080P_23_976_SMPTE274 = 11 +NV_CTRL_GVIO_VIDEO_FORMAT_1080P_24_00_SMPTE274 = 12 +NV_CTRL_GVIO_VIDEO_FORMAT_1080P_25_00_SMPTE274 = 13 +NV_CTRL_GVIO_VIDEO_FORMAT_1080P_29_97_SMPTE274 = 14 +NV_CTRL_GVIO_VIDEO_FORMAT_1080P_30_00_SMPTE274 = 15 +NV_CTRL_GVIO_VIDEO_FORMAT_720P_50_00_SMPTE296 = 16 +NV_CTRL_GVIO_VIDEO_FORMAT_1080I_48_00_SMPTE274 = 17 +NV_CTRL_GVIO_VIDEO_FORMAT_1080I_47_96_SMPTE274 = 18 +NV_CTRL_GVIO_VIDEO_FORMAT_720P_30_00_SMPTE296 = 19 +NV_CTRL_GVIO_VIDEO_FORMAT_720P_29_97_SMPTE296 = 20 +NV_CTRL_GVIO_VIDEO_FORMAT_720P_25_00_SMPTE296 = 21 +NV_CTRL_GVIO_VIDEO_FORMAT_720P_24_00_SMPTE296 = 22 +NV_CTRL_GVIO_VIDEO_FORMAT_720P_23_98_SMPTE296 = 23 +NV_CTRL_GVIO_VIDEO_FORMAT_1080PSF_25_00_SMPTE274 = 24 +NV_CTRL_GVIO_VIDEO_FORMAT_1080PSF_29_97_SMPTE274 = 25 +NV_CTRL_GVIO_VIDEO_FORMAT_1080PSF_30_00_SMPTE274 = 26 +NV_CTRL_GVIO_VIDEO_FORMAT_1080PSF_24_00_SMPTE274 = 27 +NV_CTRL_GVIO_VIDEO_FORMAT_1080PSF_23_98_SMPTE274 = 28 +NV_CTRL_GVIO_VIDEO_FORMAT_2048P_30_00_SMPTE372 = 29 +NV_CTRL_GVIO_VIDEO_FORMAT_2048P_29_97_SMPTE372 = 30 +NV_CTRL_GVIO_VIDEO_FORMAT_2048I_60_00_SMPTE372 = 31 +NV_CTRL_GVIO_VIDEO_FORMAT_2048I_59_94_SMPTE372 = 32 +NV_CTRL_GVIO_VIDEO_FORMAT_2048P_25_00_SMPTE372 = 33 +NV_CTRL_GVIO_VIDEO_FORMAT_2048I_50_00_SMPTE372 = 34 +NV_CTRL_GVIO_VIDEO_FORMAT_2048P_24_00_SMPTE372 = 35 +NV_CTRL_GVIO_VIDEO_FORMAT_2048P_23_98_SMPTE372 = 36 +NV_CTRL_GVIO_VIDEO_FORMAT_2048I_48_00_SMPTE372 = 37 +NV_CTRL_GVIO_VIDEO_FORMAT_2048I_47_96_SMPTE372 = 38 +NV_CTRL_GVIO_VIDEO_FORMAT_1080P_50_00_3G_LEVEL_A_SMPTE274 = 39 +NV_CTRL_GVIO_VIDEO_FORMAT_1080P_59_94_3G_LEVEL_A_SMPTE274 = 40 +NV_CTRL_GVIO_VIDEO_FORMAT_1080P_60_00_3G_LEVEL_A_SMPTE274 = 41 +NV_CTRL_GVIO_VIDEO_FORMAT_1080P_60_00_3G_LEVEL_B_SMPTE274 = 42 +NV_CTRL_GVIO_VIDEO_FORMAT_1080I_60_00_3G_LEVEL_B_SMPTE274 = 43 +NV_CTRL_GVIO_VIDEO_FORMAT_2048I_60_00_3G_LEVEL_B_SMPTE372 = 44 +NV_CTRL_GVIO_VIDEO_FORMAT_1080P_50_00_3G_LEVEL_B_SMPTE274 = 45 +NV_CTRL_GVIO_VIDEO_FORMAT_1080I_50_00_3G_LEVEL_B_SMPTE274 = 46 +NV_CTRL_GVIO_VIDEO_FORMAT_2048I_50_00_3G_LEVEL_B_SMPTE372 = 47 +NV_CTRL_GVIO_VIDEO_FORMAT_1080P_30_00_3G_LEVEL_B_SMPTE274 = 48 +NV_CTRL_GVIO_VIDEO_FORMAT_2048P_30_00_3G_LEVEL_B_SMPTE372 = 49 +NV_CTRL_GVIO_VIDEO_FORMAT_1080P_25_00_3G_LEVEL_B_SMPTE274 = 50 +NV_CTRL_GVIO_VIDEO_FORMAT_2048P_25_00_3G_LEVEL_B_SMPTE372 = 51 +NV_CTRL_GVIO_VIDEO_FORMAT_1080P_24_00_3G_LEVEL_B_SMPTE274 = 52 +NV_CTRL_GVIO_VIDEO_FORMAT_2048P_24_00_3G_LEVEL_B_SMPTE372 = 53 +NV_CTRL_GVIO_VIDEO_FORMAT_1080I_48_00_3G_LEVEL_B_SMPTE274 = 54 +NV_CTRL_GVIO_VIDEO_FORMAT_2048I_48_00_3G_LEVEL_B_SMPTE372 = 55 +NV_CTRL_GVIO_VIDEO_FORMAT_1080P_59_94_3G_LEVEL_B_SMPTE274 = 56 +NV_CTRL_GVIO_VIDEO_FORMAT_1080I_59_94_3G_LEVEL_B_SMPTE274 = 57 +NV_CTRL_GVIO_VIDEO_FORMAT_2048I_59_94_3G_LEVEL_B_SMPTE372 = 58 +NV_CTRL_GVIO_VIDEO_FORMAT_1080P_29_97_3G_LEVEL_B_SMPTE274 = 59 +NV_CTRL_GVIO_VIDEO_FORMAT_2048P_29_97_3G_LEVEL_B_SMPTE372 = 60 +NV_CTRL_GVIO_VIDEO_FORMAT_1080P_23_98_3G_LEVEL_B_SMPTE274 = 61 +NV_CTRL_GVIO_VIDEO_FORMAT_2048P_23_98_3G_LEVEL_B_SMPTE372 = 62 +NV_CTRL_GVIO_VIDEO_FORMAT_1080I_47_96_3G_LEVEL_B_SMPTE274 = 63 +NV_CTRL_GVIO_VIDEO_FORMAT_2048I_47_96_3G_LEVEL_B_SMPTE372 = 64 + +# +# The following have been renamed; NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT and the +# corresponding NV_CTRL_GVIO_* formats should be used instead. +# +NV_CTRL_GVO_OUTPUT_VIDEO_FORMAT = 70 # renamed + +NV_CTRL_GVO_VIDEO_FORMAT_NONE = 0 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_487I_59_94_SMPTE259_NTSC = 1 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_576I_50_00_SMPTE259_PAL = 2 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_720P_59_94_SMPTE296 = 3 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_720P_60_00_SMPTE296 = 4 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1035I_59_94_SMPTE260 = 5 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1035I_60_00_SMPTE260 = 6 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1080I_50_00_SMPTE295 = 7 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1080I_50_00_SMPTE274 = 8 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1080I_59_94_SMPTE274 = 9 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1080I_60_00_SMPTE274 = 10 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1080P_23_976_SMPTE274 = 11 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1080P_24_00_SMPTE274 = 12 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1080P_25_00_SMPTE274 = 13 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1080P_29_97_SMPTE274 = 14 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1080P_30_00_SMPTE274 = 15 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_720P_50_00_SMPTE296 = 16 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1080I_48_00_SMPTE274 = 17 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1080I_47_96_SMPTE274 = 18 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_720P_30_00_SMPTE296 = 19 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_720P_29_97_SMPTE296 = 20 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_720P_25_00_SMPTE296 = 21 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_720P_24_00_SMPTE296 = 22 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_720P_23_98_SMPTE296 = 23 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1080PSF_25_00_SMPTE274 = 24 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1080PSF_29_97_SMPTE274 = 25 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1080PSF_30_00_SMPTE274 = 26 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1080PSF_24_00_SMPTE274 = 27 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_1080PSF_23_98_SMPTE274 = 28 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_2048P_30_00_SMPTE372 = 29 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_2048P_29_97_SMPTE372 = 30 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_2048I_60_00_SMPTE372 = 31 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_2048I_59_94_SMPTE372 = 32 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_2048P_25_00_SMPTE372 = 33 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_2048I_50_00_SMPTE372 = 34 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_2048P_24_00_SMPTE372 = 35 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_2048P_23_98_SMPTE372 = 36 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_2048I_48_00_SMPTE372 = 37 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_2048I_47_96_SMPTE372 = 38 # renamed + +# +# NV_CTRL_GVIO_DETECTED_VIDEO_FORMAT - indicates the input video format +# detected for GVO or GVI devices; the possible values are the +# NV_CTRL_GVIO_VIDEO_FORMAT constants. +# +# For GVI devices, the jack number should be specified in the lower +# 16 bits of the "display_mask" parameter, while the channel number should be +# specified in the upper 16 bits. +# + +NV_CTRL_GVIO_DETECTED_VIDEO_FORMAT = 71 # R--I + +# +# NV_CTRL_GVO_INPUT_VIDEO_FORMAT - renamed +# +# NV_CTRL_GVIO_DETECTED_VIDEO_FORMAT should be used instead. +# + +NV_CTRL_GVO_INPUT_VIDEO_FORMAT = 71 # renamed + +# +# NV_CTRL_GVO_DATA_FORMAT - This controls how the data in the source +# (either the X screen or the GLX pbuffer) is interpretted and +# displayed. +# +# Note: some of the below DATA_FORMATS have been renamed. For +# example, R8G8B8_TO_RGB444 has been renamed to X8X8X8_444_PASSTHRU. +# This is to more accurately reflect DATA_FORMATS where the +# per-channel data could be either RGB or YCrCb -- the point is that +# the driver and GVO hardware do not perform any implicit color space +# conversion on the data; it is passed through to the SDI out. +# + +NV_CTRL_GVO_DATA_FORMAT = 72 # RW- +NV_CTRL_GVO_DATA_FORMAT_R8G8B8_TO_YCRCB444 = 0 +NV_CTRL_GVO_DATA_FORMAT_R8G8B8A8_TO_YCRCBA4444 = 1 +NV_CTRL_GVO_DATA_FORMAT_R8G8B8Z10_TO_YCRCBZ4444 = 2 +NV_CTRL_GVO_DATA_FORMAT_R8G8B8_TO_YCRCB422 = 3 +NV_CTRL_GVO_DATA_FORMAT_R8G8B8A8_TO_YCRCBA4224 = 4 +NV_CTRL_GVO_DATA_FORMAT_R8G8B8Z10_TO_YCRCBZ4224 = 5 +NV_CTRL_GVO_DATA_FORMAT_R8G8B8_TO_RGB444 = 6 # renamed +NV_CTRL_GVO_DATA_FORMAT_X8X8X8_444_PASSTHRU = 6 +NV_CTRL_GVO_DATA_FORMAT_R8G8B8A8_TO_RGBA4444 = 7 # renamed +NV_CTRL_GVO_DATA_FORMAT_X8X8X8A8_4444_PASSTHRU = 7 +NV_CTRL_GVO_DATA_FORMAT_R8G8B8Z10_TO_RGBZ4444 = 8 # renamed +NV_CTRL_GVO_DATA_FORMAT_X8X8X8Z8_4444_PASSTHRU = 8 +NV_CTRL_GVO_DATA_FORMAT_Y10CR10CB10_TO_YCRCB444 = 9 # renamed +NV_CTRL_GVO_DATA_FORMAT_X10X10X10_444_PASSTHRU = 9 +NV_CTRL_GVO_DATA_FORMAT_Y10CR8CB8_TO_YCRCB444 = 10 # renamed +NV_CTRL_GVO_DATA_FORMAT_X10X8X8_444_PASSTHRU = 10 +NV_CTRL_GVO_DATA_FORMAT_Y10CR8CB8A10_TO_YCRCBA4444 = 11 # renamed +NV_CTRL_GVO_DATA_FORMAT_X10X8X8A10_4444_PASSTHRU = 11 +NV_CTRL_GVO_DATA_FORMAT_Y10CR8CB8Z10_TO_YCRCBZ4444 = 12 # renamed +NV_CTRL_GVO_DATA_FORMAT_X10X8X8Z10_4444_PASSTHRU = 12 +NV_CTRL_GVO_DATA_FORMAT_DUAL_R8G8B8_TO_DUAL_YCRCB422 = 13 +NV_CTRL_GVO_DATA_FORMAT_DUAL_Y8CR8CB8_TO_DUAL_YCRCB422 = 14 # renamed +NV_CTRL_GVO_DATA_FORMAT_DUAL_X8X8X8_TO_DUAL_422_PASSTHRU = 14 +NV_CTRL_GVO_DATA_FORMAT_R10G10B10_TO_YCRCB422 = 15 +NV_CTRL_GVO_DATA_FORMAT_R10G10B10_TO_YCRCB444 = 16 +NV_CTRL_GVO_DATA_FORMAT_Y12CR12CB12_TO_YCRCB444 = 17 # renamed +NV_CTRL_GVO_DATA_FORMAT_X12X12X12_444_PASSTHRU = 17 +NV_CTRL_GVO_DATA_FORMAT_R12G12B12_TO_YCRCB444 = 18 +NV_CTRL_GVO_DATA_FORMAT_X8X8X8_422_PASSTHRU = 19 +NV_CTRL_GVO_DATA_FORMAT_X8X8X8A8_4224_PASSTHRU = 20 +NV_CTRL_GVO_DATA_FORMAT_X8X8X8Z8_4224_PASSTHRU = 21 +NV_CTRL_GVO_DATA_FORMAT_X10X10X10_422_PASSTHRU = 22 +NV_CTRL_GVO_DATA_FORMAT_X10X8X8_422_PASSTHRU = 23 +NV_CTRL_GVO_DATA_FORMAT_X10X8X8A10_4224_PASSTHRU = 24 +NV_CTRL_GVO_DATA_FORMAT_X10X8X8Z10_4224_PASSTHRU = 25 +NV_CTRL_GVO_DATA_FORMAT_X12X12X12_422_PASSTHRU = 26 +NV_CTRL_GVO_DATA_FORMAT_R12G12B12_TO_YCRCB422 = 27 + +# +# NV_CTRL_GVO_DISPLAY_X_SCREEN - not supported +# + +NV_CTRL_GVO_DISPLAY_X_SCREEN = 73 # not supported +NV_CTRL_GVO_DISPLAY_X_SCREEN_ENABLE = 1 # not supported +NV_CTRL_GVO_DISPLAY_X_SCREEN_DISABLE = 0 # not supported + +# +# NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECTED - indicates whether +# Composite Sync input is detected. +# + +NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECTED = 74 # R-- +NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECTED_FALSE = 0 +NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECTED_TRUE = 1 + +# +# NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECT_MODE - get/set the +# Composite Sync input detect mode. +# + +NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECT_MODE = 75 # RW- +NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECT_MODE_AUTO = 0 +NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECT_MODE_BI_LEVEL = 1 +NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECT_MODE_TRI_LEVEL = 2 + +# +# NV_CTRL_GVO_SYNC_INPUT_DETECTED - indicates whether SDI Sync input +# is detected, and what type. +# + +NV_CTRL_GVO_SDI_SYNC_INPUT_DETECTED = 76 # R-- +NV_CTRL_GVO_SDI_SYNC_INPUT_DETECTED_NONE = 0 +NV_CTRL_GVO_SDI_SYNC_INPUT_DETECTED_HD = 1 +NV_CTRL_GVO_SDI_SYNC_INPUT_DETECTED_SD = 2 + +# +# NV_CTRL_GVO_VIDEO_OUTPUTS - indicates which GVO video output +# connectors are currently outputing data. +# + +NV_CTRL_GVO_VIDEO_OUTPUTS = 77 # R-- +NV_CTRL_GVO_VIDEO_OUTPUTS_NONE = 0 +NV_CTRL_GVO_VIDEO_OUTPUTS_VIDEO1 = 1 +NV_CTRL_GVO_VIDEO_OUTPUTS_VIDEO2 = 2 +NV_CTRL_GVO_VIDEO_OUTPUTS_VIDEO_BOTH = 3 + +# +# NV_CTRL_GVO_FIRMWARE_VERSION - deprecated +# +# NV_CTRL_STRING_GVIO_FIRMWARE_VERSION should be used instead. +# + +NV_CTRL_GVO_FIRMWARE_VERSION = 78 # deprecated + +# +# NV_CTRL_GVO_SYNC_DELAY_PIXELS - controls the delay between the +# input sync and the output sync in numbers of pixels from hsync; +# this is a 12 bit value. +# +# If the NV_CTRL_GVO_CAPABILITIES_ADVANCE_SYNC_SKEW bit is set, +# then setting this value will set an advance instead of a delay. +# + +NV_CTRL_GVO_SYNC_DELAY_PIXELS = 79 # RW- + +# +# NV_CTRL_GVO_SYNC_DELAY_LINES - controls the delay between the input +# sync and the output sync in numbers of lines from vsync; this is a +# 12 bit value. +# +# If the NV_CTRL_GVO_CAPABILITIES_ADVANCE_SYNC_SKEW bit is set, +# then setting this value will set an advance instead of a delay. +# + +NV_CTRL_GVO_SYNC_DELAY_LINES = 80 # RW- + +# +# NV_CTRL_GVO_INPUT_VIDEO_FORMAT_REACQUIRE - must be set for a period +# of about 2 seconds for the new InputVideoFormat to be properly +# locked to. In nvidia-settings, we do a reacquire whenever genlock +# or frame lock mode is entered into, when the user clicks the +# "detect" button. This value can be written, but always reads back +# _FALSE. +# + +NV_CTRL_GVO_INPUT_VIDEO_FORMAT_REACQUIRE = 81 # -W- +NV_CTRL_GVO_INPUT_VIDEO_FORMAT_REACQUIRE_FALSE = 0 +NV_CTRL_GVO_INPUT_VIDEO_FORMAT_REACQUIRE_TRUE = 1 + +# +# NV_CTRL_GVO_GLX_LOCKED - deprecated +# +# NV_CTRL_GVO_LOCK_OWNER should be used instead. +# + +NV_CTRL_GVO_GLX_LOCKED = 82 # deprecated +NV_CTRL_GVO_GLX_LOCKED_FALSE = 0 # deprecated +NV_CTRL_GVO_GLX_LOCKED_TRUE = 1 # deprecated + +# +# NV_CTRL_GVIO_VIDEO_FORMAT_{WIDTH,HEIGHT,REFRESH_RATE} - query the +# width, height, and refresh rate for the specified +# NV_CTRL_GVIO_VIDEO_FORMAT_*. So that this can be queried with +# existing interfaces, XNVCTRLQueryAttribute() should be used, and +# the video format specified in the display_mask field; eg: +# +# XNVCTRLQueryAttribute (dpy, +# screen, +# NV_CTRL_GVIO_VIDEO_FORMAT_487I_59_94_SMPTE259_NTSC, +# NV_CTRL_GVIO_VIDEO_FORMAT_WIDTH, +# &value); +# +# Note that Refresh Rate is in milliHertz values +# + +NV_CTRL_GVIO_VIDEO_FORMAT_WIDTH = 83 # R--I +NV_CTRL_GVIO_VIDEO_FORMAT_HEIGHT = 84 # R--I +NV_CTRL_GVIO_VIDEO_FORMAT_REFRESH_RATE = 85 # R--I + +# The following have been renamed; use the NV_CTRL_GVIO_* versions, instead +NV_CTRL_GVO_VIDEO_FORMAT_WIDTH = 83 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_HEIGHT = 84 # renamed +NV_CTRL_GVO_VIDEO_FORMAT_REFRESH_RATE = 85 # renamed + +# +# NV_CTRL_GVO_X_SCREEN_PAN_[XY] - not supported +# + +NV_CTRL_GVO_X_SCREEN_PAN_X = 86 # not supported +NV_CTRL_GVO_X_SCREEN_PAN_Y = 87 # not supported + +# +# NV_CTRL_GPU_OVERCLOCKING_STATE - not supported +# + +NV_CTRL_GPU_OVERCLOCKING_STATE = 88 # not supported +NV_CTRL_GPU_OVERCLOCKING_STATE_NONE = 0 # not supported +NV_CTRL_GPU_OVERCLOCKING_STATE_MANUAL = 1 # not supported + +# +# NV_CTRL_GPU_{2,3}D_CLOCK_FREQS - not supported +# + +NV_CTRL_GPU_2D_CLOCK_FREQS = 89 # not supported +NV_CTRL_GPU_3D_CLOCK_FREQS = 90 # not supported + +# +# NV_CTRL_GPU_DEFAULT_{2,3}D_CLOCK_FREQS - not supported +# + +NV_CTRL_GPU_DEFAULT_2D_CLOCK_FREQS = 91 # not supported +NV_CTRL_GPU_DEFAULT_3D_CLOCK_FREQS = 92 # not supported + +# +# NV_CTRL_GPU_CURRENT_CLOCK_FREQS - query the current GPU and memory +# clocks of the graphics device driving the X screen. +# +# NV_CTRL_GPU_CURRENT_CLOCK_FREQS is a "packed" integer attribute; +# the GPU clock is stored in the upper 16 bits of the integer, and +# the memory clock is stored in the lower 16 bits of the integer. +# All clock values are in MHz. All clock values are in MHz. +# + +NV_CTRL_GPU_CURRENT_CLOCK_FREQS = 93 # R--G + +# +# NV_CTRL_GPU_OPTIMAL_CLOCK_FREQS - not supported +# + +NV_CTRL_GPU_OPTIMAL_CLOCK_FREQS = 94 # not supported +NV_CTRL_GPU_OPTIMAL_CLOCK_FREQS_INVALID = 0 # not supported + +# +# NV_CTRL_GPU_OPTIMAL_CLOCK_FREQS_DETECTION - not supported +# + +NV_CTRL_GPU_OPTIMAL_CLOCK_FREQS_DETECTION = 95 # not supported +NV_CTRL_GPU_OPTIMAL_CLOCK_FREQS_DETECTION_START = 0 # not supported +NV_CTRL_GPU_OPTIMAL_CLOCK_FREQS_DETECTION_CANCEL = 1 # not supported + +# +# NV_CTRL_GPU_OPTIMAL_CLOCK_FREQS_DETECTION_STATE - not supported +# + +NV_CTRL_GPU_OPTIMAL_CLOCK_FREQS_DETECTION_STATE = 96 # not supported +NV_CTRL_GPU_OPTIMAL_CLOCK_FREQS_DETECTION_STATE_IDLE = 0 # not supported +NV_CTRL_GPU_OPTIMAL_CLOCK_FREQS_DETECTION_STATE_BUSY = 1 # not supported + +# +# NV_CTRL_FLATPANEL_CHIP_LOCATION - for the specified display device, +# report whether the flat panel is driven by the on-chip controller, +# or a separate controller chip elsewhere on the graphics board. +# This attribute is only available for flat panels. +# + +NV_CTRL_FLATPANEL_CHIP_LOCATION = 215 # R-DG +NV_CTRL_FLATPANEL_CHIP_LOCATION_INTERNAL = 0 +NV_CTRL_FLATPANEL_CHIP_LOCATION_EXTERNAL = 1 + +# +# NV_CTRL_FLATPANEL_LINK - report the number of links for a DVI connection, or +# the main link's active lane count for DisplayPort. +# This attribute is only available for flat panels. +# + +NV_CTRL_FLATPANEL_LINK = 216 # R-DG +NV_CTRL_FLATPANEL_LINK_SINGLE = 0 +NV_CTRL_FLATPANEL_LINK_DUAL = 1 +NV_CTRL_FLATPANEL_LINK_QUAD = 3 + +# +# NV_CTRL_FLATPANEL_SIGNAL - for the specified display device, report +# whether the flat panel is driven by an LVDS, TMDS, or DisplayPort signal. +# This attribute is only available for flat panels. +# + +NV_CTRL_FLATPANEL_SIGNAL = 217 # R-DG +NV_CTRL_FLATPANEL_SIGNAL_LVDS = 0 +NV_CTRL_FLATPANEL_SIGNAL_TMDS = 1 +NV_CTRL_FLATPANEL_SIGNAL_DISPLAYPORT = 2 + +# +# NV_CTRL_USE_HOUSE_SYNC - when INPUT, the server (master) frame lock +# device will propagate the incoming house sync signal as the outgoing +# frame lock sync signal. If the frame lock device cannot detect a +# frame lock sync signal, it will default to using the internal timings +# from the GPU connected to the primary connector. +# +# When set to OUTPUT, the server (master) frame lock device will +# generate a house sync signal from its internal timing and output +# this signal over the BNC connector on the frame lock device. This +# is only allowed on a Quadro Sync II device. If an incoming house +# sync signal is present on the BNC connector, this setting will +# have no effect. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN +# target. +# + +NV_CTRL_USE_HOUSE_SYNC = 218 # RW-F +NV_CTRL_USE_HOUSE_SYNC_DISABLED = 0 # aliases with FALSE +NV_CTRL_USE_HOUSE_SYNC_INPUT = 1 # aliases with TRUE +NV_CTRL_USE_HOUSE_SYNC_OUTPUT = 2 +NV_CTRL_USE_HOUSE_SYNC_FALSE = 0 +NV_CTRL_USE_HOUSE_SYNC_TRUE = 1 + +# +# NV_CTRL_EDID_AVAILABLE - report if an EDID is available for the +# specified display device. +# +# This attribute may also be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN +# target. +# + +NV_CTRL_EDID_AVAILABLE = 219 # R-DG +NV_CTRL_EDID_AVAILABLE_FALSE = 0 +NV_CTRL_EDID_AVAILABLE_TRUE = 1 + +# +# NV_CTRL_FORCE_STEREO - when TRUE, OpenGL will force stereo flipping +# even when no stereo drawables are visible (if the device is configured +# to support it, see the "Stereo" X config option). +# When false, fall back to the default behavior of only flipping when a +# stereo drawable is visible. +# + +NV_CTRL_FORCE_STEREO = 220 # RW- +NV_CTRL_FORCE_STEREO_FALSE = 0 +NV_CTRL_FORCE_STEREO_TRUE = 1 + +# +# NV_CTRL_IMAGE_SETTINGS - the image quality setting for OpenGL clients. +# +# This setting is only applied to OpenGL clients that are started +# after this setting is applied. +# + +NV_CTRL_IMAGE_SETTINGS = 221 # RW-X +NV_CTRL_IMAGE_SETTINGS_HIGH_QUALITY = 0 +NV_CTRL_IMAGE_SETTINGS_QUALITY = 1 +NV_CTRL_IMAGE_SETTINGS_PERFORMANCE = 2 +NV_CTRL_IMAGE_SETTINGS_HIGH_PERFORMANCE = 3 + +# +# NV_CTRL_XINERAMA - return whether xinerama is enabled +# + +NV_CTRL_XINERAMA = 222 # R--G +NV_CTRL_XINERAMA_OFF = 0 +NV_CTRL_XINERAMA_ON = 1 + +# +# NV_CTRL_XINERAMA_STEREO - when TRUE, OpenGL will allow stereo flipping +# on multiple X screens configured with Xinerama. +# When FALSE, flipping is allowed only on one X screen at a time. +# + +NV_CTRL_XINERAMA_STEREO = 223 # RW- +NV_CTRL_XINERAMA_STEREO_FALSE = 0 +NV_CTRL_XINERAMA_STEREO_TRUE = 1 + +# +# NV_CTRL_BUS_RATE - if the bus type of the specified device is AGP, then +# NV_CTRL_BUS_RATE returns the configured AGP transfer rate. If the bus type +# is PCI Express, then this attribute returns the maximum link width. +# When this attribute is queried on an X screen target, the bus rate of the +# GPU driving the X screen is returned. +# + +NV_CTRL_BUS_RATE = 224 # R--GI + +# +# NV_CTRL_GPU_PCIE_MAX_LINK_WIDTH - returns the maximum +# PCIe link width, in number of lanes. +# +NV_CTRL_GPU_PCIE_MAX_LINK_WIDTH = NV_CTRL_BUS_RATE +# +# NV_CTRL_SHOW_SLI_VISUAL_INDICATOR - when TRUE, OpenGL will draw information +# about the current SLI mode. +# + +NV_CTRL_SHOW_SLI_VISUAL_INDICATOR = 225 # RW-X +NV_CTRL_SHOW_SLI_VISUAL_INDICATOR_FALSE = 0 +NV_CTRL_SHOW_SLI_VISUAL_INDICATOR_TRUE = 1 + +# +# NV_CTRL_SHOW_SLI_HUD - when TRUE, OpenGL will draw information about the +# current SLI mode. +# Renamed this attribute to NV_CTRL_SHOW_SLI_VISUAL_INDICATOR +# + +NV_CTRL_SHOW_SLI_HUD = NV_CTRL_SHOW_SLI_VISUAL_INDICATOR +NV_CTRL_SHOW_SLI_HUD_FALSE = NV_CTRL_SHOW_SLI_VISUAL_INDICATOR_FALSE +NV_CTRL_SHOW_SLI_HUD_TRUE = NV_CTRL_SHOW_SLI_VISUAL_INDICATOR_TRUE + +# +# NV_CTRL_XV_SYNC_TO_DISPLAY - deprecated +# +# NV_CTRL_XV_SYNC_TO_DISPLAY_ID should be used instead. +# + +NV_CTRL_XV_SYNC_TO_DISPLAY = 226 # deprecated + +# +# NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT2 - this attribute is only +# intended to be used to query the ValidValues for +# NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT for VIDEO_FORMAT values between +# 31 and 63. See NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT for details. +# + +NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT2 = 227 # ---GI + +# +# NV_CTRL_GVO_OUTPUT_VIDEO_FORMAT2 - renamed +# +# NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT2 should be used instead. +# +NV_CTRL_GVO_OUTPUT_VIDEO_FORMAT2 = 227 # renamed + +# +# NV_CTRL_GVO_OVERRIDE_HW_CSC - Override the SDI hardware's Color Space +# Conversion with the values controlled through +# XNVCTRLSetGvoColorConversion() and XNVCTRLGetGvoColorConversion(). If +# this attribute is FALSE, then the values specified through +# XNVCTRLSetGvoColorConversion() are ignored. +# + +NV_CTRL_GVO_OVERRIDE_HW_CSC = 228 # RW- +NV_CTRL_GVO_OVERRIDE_HW_CSC_FALSE = 0 +NV_CTRL_GVO_OVERRIDE_HW_CSC_TRUE = 1 + +# +# NV_CTRL_GVO_CAPABILITIES - this read-only attribute describes GVO +# capabilities that differ between NVIDIA SDI products. This value +# is a bitmask where each bit indicates whether that capability is +# available. +# +# APPLY_CSC_IMMEDIATELY - whether the CSC matrix, offset, and scale +# specified through XNVCTRLSetGvoColorConversion() will take affect +# immediately, or only after SDI output is disabled and enabled +# again. +# +# APPLY_CSC_TO_X_SCREEN - whether the CSC matrix, offset, and scale +# specified through XNVCTRLSetGvoColorConversion() will also apply +# to GVO output of an X screen, or only to OpenGL GVO output, as +# enabled through the GLX_NV_video_out extension. +# +# COMPOSITE_TERMINATION - whether the 75 ohm termination of the +# SDI composite input signal can be programmed through the +# NV_CTRL_GVO_COMPOSITE_TERMINATION attribute. +# +# SHARED_SYNC_BNC - whether the SDI device has a single BNC +# connector used for both (SDI & Composite) incoming signals. +# +# MULTIRATE_SYNC - whether the SDI device supports synchronization +# of input and output video modes that match in being odd or even +# modes (ie, AA.00 Hz modes can be synched to other BB.00 Hz modes and +# AA.XX Hz can match to BB.YY Hz where .XX and .YY are not .00) +# + +NV_CTRL_GVO_CAPABILITIES = 229 # R-- +NV_CTRL_GVO_CAPABILITIES_APPLY_CSC_IMMEDIATELY = 0x00000001 +NV_CTRL_GVO_CAPABILITIES_APPLY_CSC_TO_X_SCREEN = 0x00000002 +NV_CTRL_GVO_CAPABILITIES_COMPOSITE_TERMINATION = 0x00000004 +NV_CTRL_GVO_CAPABILITIES_SHARED_SYNC_BNC = 0x00000008 +NV_CTRL_GVO_CAPABILITIES_MULTIRATE_SYNC = 0x00000010 +NV_CTRL_GVO_CAPABILITIES_ADVANCE_SYNC_SKEW = 0x00000020 + +# +# NV_CTRL_GVO_COMPOSITE_TERMINATION - enable or disable 75 ohm +# termination of the SDI composite input signal. +# + +NV_CTRL_GVO_COMPOSITE_TERMINATION = 230 # RW- +NV_CTRL_GVO_COMPOSITE_TERMINATION_ENABLE = 1 +NV_CTRL_GVO_COMPOSITE_TERMINATION_DISABLE = 0 + +# +# NV_CTRL_ASSOCIATED_DISPLAY_DEVICES - deprecated +# +# NV_CTRL_BINARY_DATA_DISPLAYS_ASSIGNED_TO_XSCREEN should be used instead. +# + +NV_CTRL_ASSOCIATED_DISPLAY_DEVICES = 231 # deprecated + +# +# NV_CTRL_FRAMELOCK_SLAVES - deprecated +# +# NV_CTRL_FRAMELOCK_DISPLAY_CONFIG should be used instead. +# + +NV_CTRL_FRAMELOCK_SLAVES = 232 # deprecated + +# +# NV_CTRL_FRAMELOCK_MASTERABLE - deprecated +# +# NV_CTRL_FRAMELOCK_DISPLAY_CONFIG should be used instead. +# + +NV_CTRL_FRAMELOCK_MASTERABLE = 233 # deprecated + +# +# NV_CTRL_PROBE_DISPLAYS - re-probes the hardware to detect what +# display devices are connected to the GPU or GPU driving the +# specified X screen. The return value is deprecated and should not be used. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. +# + +NV_CTRL_PROBE_DISPLAYS = 234 # R--G + +# +# NV_CTRL_REFRESH_RATE - Returns the refresh rate of the specified +# display device in 100# Hz (ie. to get the refresh rate in Hz, divide +# the returned value by 100.) +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. +# + +NV_CTRL_REFRESH_RATE = 235 # R-DG + +# +# NV_CTRL_GVO_FLIP_QUEUE_SIZE - The Graphics to Video Out interface +# exposed through NV-CONTROL and the GLX_NV_video_out extension uses +# an internal flip queue when pbuffers are sent to the video device +# (via glXSendPbufferToVideoNV()). The NV_CTRL_GVO_FLIP_QUEUE_SIZE +# can be used to query and assign the flip queue size. This +# attribute is applied to GLX when glXGetVideoDeviceNV() is called by +# the application. +# + +NV_CTRL_GVO_FLIP_QUEUE_SIZE = 236 # RW- + +# +# NV_CTRL_CURRENT_SCANLINE - query the current scanline for the +# specified display device. +# + +NV_CTRL_CURRENT_SCANLINE = 237 # R-DG + +# +# NV_CTRL_INITIAL_PIXMAP_PLACEMENT - Controls where X pixmaps are initially +# created. +# +# NV_CTRL_INITIAL_PIXMAP_PLACEMENT_FORCE_SYSMEM causes pixmaps to stay in +# system memory. These pixmaps can't be accelerated by the NVIDIA driver; this +# will cause blank windows if used with an OpenGL compositing manager. +# NV_CTRL_INITIAL_PIXMAP_PLACEMENT_SYSMEM creates pixmaps in system memory +# initially, but allows them to migrate to video memory. +# NV_CTRL_INITIAL_PIXMAP_PLACEMENT_VIDMEM creates pixmaps in video memory +# when enough resources are available. +# NV_CTRL_INITIAL_PIXMAP_PLACEMENT_RESERVED is currently reserved for future +# use. Behavior is undefined. +# NV_CTRL_INITIAL_PIXMAP_PLACEMENT_GPU_SYSMEM creates pixmaps in GPU accessible +# system memory when enough resources are available. +# + +NV_CTRL_INITIAL_PIXMAP_PLACEMENT = 238 # RW- +NV_CTRL_INITIAL_PIXMAP_PLACEMENT_FORCE_SYSMEM = 0 +NV_CTRL_INITIAL_PIXMAP_PLACEMENT_SYSMEM = 1 +NV_CTRL_INITIAL_PIXMAP_PLACEMENT_VIDMEM = 2 +NV_CTRL_INITIAL_PIXMAP_PLACEMENT_RESERVED = 3 +NV_CTRL_INITIAL_PIXMAP_PLACEMENT_GPU_SYSMEM = 4 + +# +# NV_CTRL_PCI_BUS - Returns the PCI bus number the specified device is using. +# + +NV_CTRL_PCI_BUS = 239 # R--GI + +# +# NV_CTRL_PCI_DEVICE - Returns the PCI device number the specified device is +# using. +# + +NV_CTRL_PCI_DEVICE = 240 # R--GI + +# +# NV_CTRL_PCI_FUNCTION - Returns the PCI function number the specified device +# is using. +# + +NV_CTRL_PCI_FUNCTION = 241 # R--GI + +# +# NV_CTRL_FRAMELOCK_FPGA_REVISION - Queries the FPGA revision of the +# Frame Lock device. +# +# This attribute must be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_FRAMELOCK target. +# + +NV_CTRL_FRAMELOCK_FPGA_REVISION = 242 # R--F + +# +# NV_CTRL_MAX_SCREEN_{WIDTH,HEIGHT} - the maximum allowable size, in +# pixels, of either the specified X screen (if the target_type of the +# query is an X screen), or any X screen on the specified GPU (if the +# target_type of the query is a GPU). +# + +NV_CTRL_MAX_SCREEN_WIDTH = 243 # R--G +NV_CTRL_MAX_SCREEN_HEIGHT = 244 # R--G + +# +# NV_CTRL_MAX_DISPLAYS - The maximum number of display devices that +# can be driven simultaneously on a GPU (e.g., that can be used in a +# MetaMode at once). Note that this does not indicate the maximum +# number of displays that are listed in NV_CTRL_BINARY_DATA_DISPLAYS_ON_GPU +# and NV_CTRL_BINARY_DATA_DISPLAYS_CONNECTED_TO_GPU because more display +# devices can be connected than are actively in use. +# + +NV_CTRL_MAX_DISPLAYS = 245 # R--G + +# +# NV_CTRL_DYNAMIC_TWINVIEW - Returns whether or not the screen +# supports dynamic twinview. +# + +NV_CTRL_DYNAMIC_TWINVIEW = 246 # R-- + +# +# NV_CTRL_MULTIGPU_DISPLAY_OWNER - Returns the (NV-CONTROL) GPU ID of +# the GPU that has the display device(s) used for showing the X Screen. +# + +NV_CTRL_MULTIGPU_DISPLAY_OWNER = 247 # R-- + +# +# NV_CTRL_GPU_SCALING - not supported +# + +NV_CTRL_GPU_SCALING = 248 # not supported + +NV_CTRL_GPU_SCALING_TARGET_INVALID = 0 # not supported +NV_CTRL_GPU_SCALING_TARGET_FLATPANEL_BEST_FIT = 1 # not supported +NV_CTRL_GPU_SCALING_TARGET_FLATPANEL_NATIVE = 2 # not supported + +NV_CTRL_GPU_SCALING_METHOD_INVALID = 0 # not supported +NV_CTRL_GPU_SCALING_METHOD_STRETCHED = 1 # not supported +NV_CTRL_GPU_SCALING_METHOD_CENTERED = 2 # not supported +NV_CTRL_GPU_SCALING_METHOD_ASPECT_SCALED = 3 # not supported + +# +# NV_CTRL_FRONTEND_RESOLUTION - not supported +# + +NV_CTRL_FRONTEND_RESOLUTION = 249 # not supported + +# +# NV_CTRL_BACKEND_RESOLUTION - not supported +# + +NV_CTRL_BACKEND_RESOLUTION = 250 # not supported + +# +# NV_CTRL_FLATPANEL_NATIVE_RESOLUTION - not supported +# + +NV_CTRL_FLATPANEL_NATIVE_RESOLUTION = 251 # not supported + +# +# NV_CTRL_FLATPANEL_BEST_FIT_RESOLUTION - not supported +# + +NV_CTRL_FLATPANEL_BEST_FIT_RESOLUTION = 252 # not supported + +# +# NV_CTRL_GPU_SCALING_ACTIVE - not supported +# + +NV_CTRL_GPU_SCALING_ACTIVE = 253 # not supported + +# +# NV_CTRL_DFP_SCALING_ACTIVE - not supported +# + +NV_CTRL_DFP_SCALING_ACTIVE = 254 # not supported + +# +# NV_CTRL_FSAA_APPLICATION_ENHANCED - Controls how the NV_CTRL_FSAA_MODE +# is applied when NV_CTRL_FSAA_APPLICATION_CONTROLLED is set to +# NV_CTRL_APPLICATION_CONTROLLED_DISABLED. When +# NV_CTRL_FSAA_APPLICATION_ENHANCED is _DISABLED, OpenGL applications will +# be forced to use the FSAA mode specified by NV_CTRL_FSAA_MODE. when set +# to _ENABLED, only those applications that have selected a multisample +# FBConfig will be made to use the NV_CTRL_FSAA_MODE specified. +# +# This attribute is ignored when NV_CTRL_FSAA_APPLICATION_CONTROLLED is +# set to NV_CTRL_FSAA_APPLICATION_CONTROLLED_ENABLED. +# + +NV_CTRL_FSAA_APPLICATION_ENHANCED = 255 # RW-X +NV_CTRL_FSAA_APPLICATION_ENHANCED_ENABLED = 1 +NV_CTRL_FSAA_APPLICATION_ENHANCED_DISABLED = 0 + +# +# NV_CTRL_FRAMELOCK_SYNC_RATE_4 - This is the refresh rate that the +# frame lock board is sending to the GPU with 4 digits of precision. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_FRAMELOCK. +# + +NV_CTRL_FRAMELOCK_SYNC_RATE_4 = 256 # R--F + +# +# NV_CTRL_GVO_LOCK_OWNER - indicates that the GVO device is available +# or in use (by GLX or an X screen). +# +# The GVO device is locked by GLX when either glXGetVideoDeviceNV +# (part of GLX_NV_video_out) or glXBindVideoDeviceNV (part of +# GLX_NV_present_video) is called. All GVO output resources are +# locked until released by the GLX_NV_video_out/GLX_NV_present_video +# client. +# +# The GVO device is locked/unlocked by an X screen, when the GVO device is +# used in a MetaMode on an X screen. +# +# When the GVO device is locked, setting of the following GVO NV-CONTROL +# attributes will not happen immediately and will instead be cached. The +# GVO resource will need to be disabled/released and re-enabled/claimed for +# the values to be flushed. These attributes are: +# +# NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT +# NV_CTRL_GVO_DATA_FORMAT +# NV_CTRL_GVO_FLIP_QUEUE_SIZE +# + +NV_CTRL_GVO_LOCK_OWNER = 257 # R-- +NV_CTRL_GVO_LOCK_OWNER_NONE = 0 +NV_CTRL_GVO_LOCK_OWNER_GLX = 1 +NV_CTRL_GVO_LOCK_OWNER_CLONE = 2 # not supported +NV_CTRL_GVO_LOCK_OWNER_X_SCREEN = 3 + +# +# NV_CTRL_HWOVERLAY - when a workstation overlay is in use, reports +# whether the hardware overlay is used, or if the overlay is emulated. +# + +NV_CTRL_HWOVERLAY = 258 # R-- +NV_CTRL_HWOVERLAY_FALSE = 0 +NV_CTRL_HWOVERLAY_TRUE = 1 + +# +# NV_CTRL_NUM_GPU_ERRORS_RECOVERED - Returns the number of GPU errors +# occured. This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_X_SCREEN target. +# + +NV_CTRL_NUM_GPU_ERRORS_RECOVERED = 259 # R--- + +# +# NV_CTRL_REFRESH_RATE_3 - Returns the refresh rate of the specified +# display device in 1000# Hz (ie. to get the refresh rate in Hz, divide +# the returned value by 1000.) +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. +# + +NV_CTRL_REFRESH_RATE_3 = 260 # R-DG + +# +# NV_CTRL_ONDEMAND_VBLANK_INTERRUPTS - not supported +# + +NV_CTRL_ONDEMAND_VBLANK_INTERRUPTS = 261 # not supported +NV_CTRL_ONDEMAND_VBLANK_INTERRUPTS_OFF = 0 # not supported +NV_CTRL_ONDEMAND_VBLANK_INTERRUPTS_ON = 1 # not supported + +# +# NV_CTRL_GPU_POWER_SOURCE reports the type of power source +# of the GPU driving the X screen. +# + +NV_CTRL_GPU_POWER_SOURCE = 262 # R--G +NV_CTRL_GPU_POWER_SOURCE_AC = 0 +NV_CTRL_GPU_POWER_SOURCE_BATTERY = 1 + +# +# NV_CTRL_GPU_CURRENT_PERFORMANCE_MODE - not supported +# + +NV_CTRL_GPU_CURRENT_PERFORMANCE_MODE = 263 # not supported +NV_CTRL_GPU_CURRENT_PERFORMANCE_MODE_DESKTOP = 0 # not supported +NV_CTRL_GPU_CURRENT_PERFORMANCE_MODE_MAXPERF = 1 # not supported + +# NV_CTRL_GLYPH_CACHE - Enables RENDER Glyph Caching to VRAM + +NV_CTRL_GLYPH_CACHE = 264 # RW- +NV_CTRL_GLYPH_CACHE_DISABLED = 0 +NV_CTRL_GLYPH_CACHE_ENABLED = 1 + +# +# NV_CTRL_GPU_CURRENT_PERFORMANCE_LEVEL reports the current +# Performance level of the GPU driving the X screen. Each +# Performance level has associated NVClock and Mem Clock values. +# + +NV_CTRL_GPU_CURRENT_PERFORMANCE_LEVEL = 265 # R--G + +# +# NV_CTRL_GPU_ADAPTIVE_CLOCK_STATE reports if Adaptive Clocking +# is Enabled on the GPU driving the X screen. +# + +NV_CTRL_GPU_ADAPTIVE_CLOCK_STATE = 266 # R--G +NV_CTRL_GPU_ADAPTIVE_CLOCK_STATE_DISABLED = 0 +NV_CTRL_GPU_ADAPTIVE_CLOCK_STATE_ENABLED = 1 + +# +# NV_CTRL_GVO_OUTPUT_VIDEO_LOCKED - Returns whether or not the GVO output +# video is locked to the GPU. +# + +NV_CTRL_GVO_OUTPUT_VIDEO_LOCKED = 267 # R--- +NV_CTRL_GVO_OUTPUT_VIDEO_LOCKED_FALSE = 0 +NV_CTRL_GVO_OUTPUT_VIDEO_LOCKED_TRUE = 1 + +# +# NV_CTRL_GVO_SYNC_LOCK_STATUS - Returns whether or not the GVO device +# is locked to the input ref signal. If the sync mode is set to +# NV_CTRL_GVO_SYNC_MODE_GENLOCK, then this returns the genlock +# sync status, and if the sync mode is set to NV_CTRL_GVO_SYNC_MODE_FRAMELOCK, +# then this reports the frame lock status. +# + +NV_CTRL_GVO_SYNC_LOCK_STATUS = 268 # R--- +NV_CTRL_GVO_SYNC_LOCK_STATUS_UNLOCKED = 0 +NV_CTRL_GVO_SYNC_LOCK_STATUS_LOCKED = 1 + +# +# NV_CTRL_GVO_ANC_TIME_CODE_GENERATION - Allows SDI device to generate +# time codes in the ANC region of the SDI video output stream. +# + +NV_CTRL_GVO_ANC_TIME_CODE_GENERATION = 269 # RW-- +NV_CTRL_GVO_ANC_TIME_CODE_GENERATION_DISABLE = 0 +NV_CTRL_GVO_ANC_TIME_CODE_GENERATION_ENABLE = 1 + +# +# NV_CTRL_GVO_COMPOSITE - Enables/Disables SDI compositing. This attribute +# is only available when an SDI input source is detected and is in genlock +# mode. +# + +NV_CTRL_GVO_COMPOSITE = 270 # RW-- +NV_CTRL_GVO_COMPOSITE_DISABLE = 0 +NV_CTRL_GVO_COMPOSITE_ENABLE = 1 + +# +# NV_CTRL_GVO_COMPOSITE_ALPHA_KEY - When compositing is enabled, this +# enables/disables alpha blending. +# + +NV_CTRL_GVO_COMPOSITE_ALPHA_KEY = 271 # RW-- +NV_CTRL_GVO_COMPOSITE_ALPHA_KEY_DISABLE = 0 +NV_CTRL_GVO_COMPOSITE_ALPHA_KEY_ENABLE = 1 + +# +# NV_CTRL_GVO_COMPOSITE_LUMA_KEY_RANGE - Set the values of a luma +# channel range. This is a packed int that has the following format +# (in order of high-bits to low bits): +# +# Range # (11 bits), (Enabled 1 bit), min value (10 bits), max value (10 bits) +# +# To query the current values, pass the range # throught the display_mask +# variable. +# + +NV_CTRL_GVO_COMPOSITE_LUMA_KEY_RANGE = 272 # RW-- + +# +# NV_CTRL_GVO_COMPOSITE_CR_KEY_RANGE - Set the values of a CR +# channel range. This is a packed int that has the following format +# (in order of high-bits to low bits): +# +# Range # (11 bits), (Enabled 1 bit), min value (10 bits), max value (10 bits) +# +# To query the current values, pass the range # throught he display_mask +# variable. +# + +NV_CTRL_GVO_COMPOSITE_CR_KEY_RANGE = 273 # RW-- + +# +# NV_CTRL_GVO_COMPOSITE_CB_KEY_RANGE - Set the values of a CB +# channel range. This is a packed int that has the following format +# (in order of high-bits to low bits): +# +# Range # (11 bits), (Enabled 1 bit), min value (10 bits), max value (10 bits) +# +# To query the current values, pass the range # throught he display_mask +# variable. +# + +NV_CTRL_GVO_COMPOSITE_CB_KEY_RANGE = 274 # RW-- + +# +# NV_CTRL_GVO_COMPOSITE_NUM_KEY_RANGES - Returns the number of ranges +# available for each channel (Y/Luma, Cr, and Cb.) +# + +NV_CTRL_GVO_COMPOSITE_NUM_KEY_RANGES = 275 # R--- + +# +# NV_CTRL_SWITCH_TO_DISPLAYS - not supported +# + +NV_CTRL_SWITCH_TO_DISPLAYS = 276 # not supported + +# +# NV_CTRL_NOTEBOOK_DISPLAY_CHANGE_LID_EVENT - not supported +# + +NV_CTRL_NOTEBOOK_DISPLAY_CHANGE_LID_EVENT = 277 # not supported + +# +# NV_CTRL_NOTEBOOK_INTERNAL_LCD - deprecated +# + +NV_CTRL_NOTEBOOK_INTERNAL_LCD = 278 # deprecated + +# +# NV_CTRL_DEPTH_30_ALLOWED - returns whether the NVIDIA X driver supports +# depth 30 on the specified X screen or GPU. +# + +NV_CTRL_DEPTH_30_ALLOWED = 279 # R--G + +# +# NV_CTRL_MODE_SET_EVENT This attribute is sent as an event +# when hotkey, ctrl-alt-+/- or randr event occurs. Note that +# This attribute cannot be set or queried and is meant to +# be received by clients that wish to be notified of when +# mode set events occur. +# + +NV_CTRL_MODE_SET_EVENT = 280 # --- + +# +# NV_CTRL_OPENGL_AA_LINE_GAMMA_VALUE - the gamma value used by +# OpenGL when NV_CTRL_OPENGL_AA_LINE_GAMMA is enabled +# + +NV_CTRL_OPENGL_AA_LINE_GAMMA_VALUE = 281 # RW-X + +# +# NV_CTRL_VCSC_HIGH_PERF_MODE - deprecated +# +# Is used to both query High Performance Mode status on the Visual Computing +# System, and also to enable or disable High Performance Mode. +# + +NV_CTRL_VCSC_HIGH_PERF_MODE = 282 # RW-V +NV_CTRL_VCSC_HIGH_PERF_MODE_DISABLE = 0 +NV_CTRL_VCSC_HIGH_PERF_MODE_ENABLE = 1 + +# +# NV_CTRL_DISPLAYPORT_LINK_RATE - returns the negotiated lane bandwidth of the +# DisplayPort main link. The numerical value of this attribute is the link +# rate in bps divided by 27000000. +# This attribute is only available for DisplayPort flat panels. +# + +NV_CTRL_DISPLAYPORT_LINK_RATE = 291 # R-DG +NV_CTRL_DISPLAYPORT_LINK_RATE_DISABLED = 0x0 +NV_CTRL_DISPLAYPORT_LINK_RATE_1_62GBPS = 0x6 # deprecated +NV_CTRL_DISPLAYPORT_LINK_RATE_2_70GBPS = 0xA # deprecated + +# +# NV_CTRL_STEREO_EYES_EXCHANGE - Controls whether or not the left and right +# eyes of a stereo image are flipped. +# + +NV_CTRL_STEREO_EYES_EXCHANGE = 292 # RW-X +NV_CTRL_STEREO_EYES_EXCHANGE_OFF = 0 +NV_CTRL_STEREO_EYES_EXCHANGE_ON = 1 + +# +# NV_CTRL_NO_SCANOUT - returns whether the special "NoScanout" mode is +# enabled on the specified X screen or GPU; for details on this mode, +# see the description of the "none" value for the "UseDisplayDevice" +# X configuration option in the NVIDIA driver README. +# + +NV_CTRL_NO_SCANOUT = 293 # R--G +NV_CTRL_NO_SCANOUT_DISABLED = 0 +NV_CTRL_NO_SCANOUT_ENABLED = 1 + +# +# NV_CTRL_GVO_CSC_CHANGED_EVENT This attribute is sent as an event +# when the color space conversion matrix has been altered by another +# client. +# + +NV_CTRL_GVO_CSC_CHANGED_EVENT = 294 # --- + +# +# NV_CTRL_FRAMELOCK_SLAVEABLE - deprecated +# +# NV_CTRL_FRAMELOCK_DISPLAY_CONFIG should be used instead. +# + +NV_CTRL_FRAMELOCK_SLAVEABLE = 295 # deprecated + +# +# NV_CTRL_GVO_SYNC_TO_DISPLAY This attribute controls whether or not +# the non-SDI display device will be sync'ed to the SDI display device +# (when configured in TwinView, Clone Mode or when using the SDI device +# with OpenGL). +# + +NV_CTRL_GVO_SYNC_TO_DISPLAY = 296 # --- +NV_CTRL_GVO_SYNC_TO_DISPLAY_DISABLE = 0 +NV_CTRL_GVO_SYNC_TO_DISPLAY_ENABLE = 1 + +# +# NV_CTRL_X_SERVER_UNIQUE_ID - returns a pseudo-unique identifier for this +# X server. Intended for use in cases where an NV-CONTROL client communicates +# with multiple X servers, and wants some level of confidence that two +# X Display connections correspond to the same or different X servers. +# + +NV_CTRL_X_SERVER_UNIQUE_ID = 297 # R--- + +# +# NV_CTRL_PIXMAP_CACHE - This attribute controls whether the driver attempts to +# store video memory pixmaps in a cache. The cache speeds up allocation and +# deallocation of pixmaps, but could use more memory than when the cache is +# disabled. +# + +NV_CTRL_PIXMAP_CACHE = 298 # RW-X +NV_CTRL_PIXMAP_CACHE_DISABLE = 0 +NV_CTRL_PIXMAP_CACHE_ENABLE = 1 + +# +# NV_CTRL_PIXMAP_CACHE_ROUNDING_SIZE_KB - When the pixmap cache is enabled and +# there is not enough free space in the cache to fit a new pixmap, the driver +# will round up to the next multiple of this number of kilobytes when +# allocating more memory for the cache. +# + +NV_CTRL_PIXMAP_CACHE_ROUNDING_SIZE_KB = 299 # RW-X + +# +# NV_CTRL_IS_GVO_DISPLAY - returns whether or not a given display is an +# SDI device. +# + +NV_CTRL_IS_GVO_DISPLAY = 300 # R-D +NV_CTRL_IS_GVO_DISPLAY_FALSE = 0 +NV_CTRL_IS_GVO_DISPLAY_TRUE = 1 + +# +# NV_CTRL_PCI_ID - Returns the PCI vendor and device ID of the specified +# device. +# +# NV_CTRL_PCI_ID is a "packed" integer attribute; the PCI vendor ID is stored +# in the upper 16 bits of the integer, and the PCI device ID is stored in the +# lower 16 bits of the integer. +# + +NV_CTRL_PCI_ID = 301 # R--GI + +# +# NV_CTRL_GVO_FULL_RANGE_COLOR - Allow full range color data [4-1019] +# without clamping to [64-940]. +# + +NV_CTRL_GVO_FULL_RANGE_COLOR = 302 # RW- +NV_CTRL_GVO_FULL_RANGE_COLOR_DISABLED = 0 +NV_CTRL_GVO_FULL_RANGE_COLOR_ENABLED = 1 + +# +# NV_CTRL_SLI_MOSAIC_MODE_AVAILABLE - Returns whether or not +# SLI Mosaic Mode supported. +# + +NV_CTRL_SLI_MOSAIC_MODE_AVAILABLE = 303 # R-- +NV_CTRL_SLI_MOSAIC_MODE_AVAILABLE_FALSE = 0 +NV_CTRL_SLI_MOSAIC_MODE_AVAILABLE_TRUE = 1 + +# +# NV_CTRL_GVO_ENABLE_RGB_DATA - Allows clients to specify when +# the GVO board should process colors as RGB when the output data +# format is one of the NV_CTRL_GVO_DATA_FORMAT_???_PASSTRHU modes. +# + +NV_CTRL_GVO_ENABLE_RGB_DATA = 304 # RW- +NV_CTRL_GVO_ENABLE_RGB_DATA_DISABLE = 0 +NV_CTRL_GVO_ENABLE_RGB_DATA_ENABLE = 1 + +# +# NV_CTRL_IMAGE_SHARPENING_DEFAULT - Returns default value of +# Image Sharpening. +# + +NV_CTRL_IMAGE_SHARPENING_DEFAULT = 305 # R-- + +# +# NV_CTRL_PCI_DOMAIN - Returns the PCI domain number the specified device is +# using. +# + +NV_CTRL_PCI_DOMAIN = 306 # R--GI + +# +# NV_CTRL_GVI_NUM_JACKS - Returns the number of input BNC jacks available +# on a GVI device. +# + +NV_CTRL_GVI_NUM_JACKS = 307 # R--I + +# +# NV_CTRL_GVI_MAX_LINKS_PER_STREAM - Returns the maximum supported number of +# links that can be tied to one stream. +# + +NV_CTRL_GVI_MAX_LINKS_PER_STREAM = 308 # R--I + +# +# NV_CTRL_GVI_DETECTED_CHANNEL_BITS_PER_COMPONENT - Returns the detected +# number of bits per component (BPC) of data on the given input jack+ +# channel. +# +# The jack number should be specified in the lower 16 bits of the +# "display_mask" parameter, while the channel number should be specified in +# the upper 16 bits. +# + +NV_CTRL_GVI_DETECTED_CHANNEL_BITS_PER_COMPONENT = 309 # R--I +NV_CTRL_GVI_BITS_PER_COMPONENT_UNKNOWN = 0 +NV_CTRL_GVI_BITS_PER_COMPONENT_8 = 1 +NV_CTRL_GVI_BITS_PER_COMPONENT_10 = 2 +NV_CTRL_GVI_BITS_PER_COMPONENT_12 = 3 + +# +# NV_CTRL_GVI_REQUESTED_STREAM_BITS_PER_COMPONENT - Specify the number of +# bits per component (BPC) of data for the captured stream. +# The stream number should be specified in the "display_mask" parameter. +# +# Note: Setting this attribute may also result in the following +# NV-CONTROL attributes being reset on the GVI device (to ensure +# the configuration remains valid): +# NV_CTRL_GVI_REQUESTED_STREAM_COMPONENT_SAMPLING +# + +NV_CTRL_GVI_REQUESTED_STREAM_BITS_PER_COMPONENT = 310 # RW-I + +# +# NV_CTRL_GVI_DETECTED_CHANNEL_COMPONENT_SAMPLING - Returns the detected +# sampling format for the input jack+channel. +# +# The jack number should be specified in the lower 16 bits of the +# "display_mask" parameter, while the channel number should be specified in +# the upper 16 bits. +# + +NV_CTRL_GVI_DETECTED_CHANNEL_COMPONENT_SAMPLING = 311 # R--I +NV_CTRL_GVI_COMPONENT_SAMPLING_UNKNOWN = 0 +NV_CTRL_GVI_COMPONENT_SAMPLING_4444 = 1 +NV_CTRL_GVI_COMPONENT_SAMPLING_4224 = 2 +NV_CTRL_GVI_COMPONENT_SAMPLING_444 = 3 +NV_CTRL_GVI_COMPONENT_SAMPLING_422 = 4 +NV_CTRL_GVI_COMPONENT_SAMPLING_420 = 5 + +# +# NV_CTRL_GVI_REQUESTED_COMPONENT_SAMPLING - Specify the sampling format for +# the captured stream. +# The possible values are the NV_CTRL_GVI_DETECTED_COMPONENT_SAMPLING +# constants. +# The stream number should be specified in the "display_mask" parameter. +# + +NV_CTRL_GVI_REQUESTED_STREAM_COMPONENT_SAMPLING = 312 # RW-I + +# +# NV_CTRL_GVI_CHROMA_EXPAND - Enable or disable 4:2:2 -> 4:4:4 chroma +# expansion for the captured stream. This value is ignored when a +# COMPONENT_SAMPLING format is selected that does not use chroma subsampling, +# or if a BITS_PER_COMPONENT value is selected that is not supported. +# The stream number should be specified in the "display_mask" parameter. +# + +NV_CTRL_GVI_REQUESTED_STREAM_CHROMA_EXPAND = 313 # RW-I +NV_CTRL_GVI_CHROMA_EXPAND_FALSE = 0 +NV_CTRL_GVI_CHROMA_EXPAND_TRUE = 1 + +# +# NV_CTRL_GVI_DETECTED_CHANNEL_COLOR_SPACE - Returns the detected color space +# of the input jack+channel. +# +# The jack number should be specified in the lower 16 bits of the +# "display_mask" parameter, while the channel number should be specified in +# the upper 16 bits. +# + +NV_CTRL_GVI_DETECTED_CHANNEL_COLOR_SPACE = 314 # R--I +NV_CTRL_GVI_COLOR_SPACE_UNKNOWN = 0 +NV_CTRL_GVI_COLOR_SPACE_GBR = 1 +NV_CTRL_GVI_COLOR_SPACE_GBRA = 2 +NV_CTRL_GVI_COLOR_SPACE_GBRD = 3 +NV_CTRL_GVI_COLOR_SPACE_YCBCR = 4 +NV_CTRL_GVI_COLOR_SPACE_YCBCRA = 5 +NV_CTRL_GVI_COLOR_SPACE_YCBCRD = 6 + +# +# NV_CTRL_GVI_DETECTED_CHANNEL_LINK_ID - Returns the detected link identifier +# for the given input jack+channel. +# +# The jack number should be specified in the lower 16 bits of the +# "display_mask" parameter, while the channel number should be specified in +# the upper 16 bits. +# + +NV_CTRL_GVI_DETECTED_CHANNEL_LINK_ID = 315 # R--I +NV_CTRL_GVI_LINK_ID_UNKNOWN = 0xFFFF + +# +# NV_CTRL_GVI_DETECTED_CHANNEL_SMPTE352_IDENTIFIER - Returns the 4-byte +# SMPTE 352 identifier from the given input jack+channel. +# +# The jack number should be specified in the lower 16 bits of the +# "display_mask" parameter, while the channel number should be specified in +# the upper 16 bits. +# + +NV_CTRL_GVI_DETECTED_CHANNEL_SMPTE352_IDENTIFIER = 316 # R--I + +# +# NV_CTRL_GVI_GLOBAL_IDENTIFIER - Returns a global identifier for the +# GVI device. This identifier can be used to relate GVI devices named +# in NV-CONTROL with those enumerated in OpenGL. +# + +NV_CTRL_GVI_GLOBAL_IDENTIFIER = 317 # R--I + +# +# NV_CTRL_FRAMELOCK_SYNC_DELAY_RESOLUTION - Returns the number of nanoseconds +# that one unit of NV_CTRL_FRAMELOCK_SYNC_DELAY corresponds to. +# +NV_CTRL_FRAMELOCK_SYNC_DELAY_RESOLUTION = 318 # R-- + +# +# NV_CTRL_GPU_COOLER_MANUAL_CONTROL - Query the current or set a new +# cooler control state; the value of this attribute controls the +# availability of additional cooler control attributes (see below). +# +# Note: this attribute is unavailable unless cooler control support +# has been enabled in the X server (by the user). +# + +NV_CTRL_GPU_COOLER_MANUAL_CONTROL = 319 # RW-G +NV_CTRL_GPU_COOLER_MANUAL_CONTROL_FALSE = 0 +NV_CTRL_GPU_COOLER_MANUAL_CONTROL_TRUE = 1 + +# +# NV_CTRL_THERMAL_COOLER_LEVEL - The cooler's target level. +# Normally, the driver dynamically adjusts the cooler based on +# the needs of the GPU. But when NV_CTRL_GPU_COOLER_MANUAL_CONTROL=TRUE, +# the driver will attempt to make the cooler achieve the setting in +# NV_CTRL_THERMAL_COOLER_LEVEL. The actual current level of the cooler +# is reported in NV_CTRL_THERMAL_COOLER_CURRENT_LEVEL. +# + +NV_CTRL_THERMAL_COOLER_LEVEL = 320 # RW-C + +# NV_CTRL_THERMAL_COOLER_LEVEL_SET_DEFAULT - Sets default values of +# cooler. +# + +NV_CTRL_THERMAL_COOLER_LEVEL_SET_DEFAULT = 321 # -W-C + +# +# NV_CTRL_THERMAL_COOLER_CONTROL_TYPE - +# Returns a cooler's control signal characteristics. +# The possible types are restricted, Variable and Toggle. +# + +NV_CTRL_THERMAL_COOLER_CONTROL_TYPE = 322 # R--C +NV_CTRL_THERMAL_COOLER_CONTROL_TYPE_NONE = 0 +NV_CTRL_THERMAL_COOLER_CONTROL_TYPE_TOGGLE = 1 +NV_CTRL_THERMAL_COOLER_CONTROL_TYPE_VARIABLE = 2 + +# +# NV_CTRL_THERMAL_COOLER_TARGET - Returns objects that cooler cools. +# Targets may be GPU, Memory, Power Supply or All of these. +# GPU_RELATED = GPU | MEMORY | POWER_SUPPLY +# +# + +NV_CTRL_THERMAL_COOLER_TARGET = 323 # R--C +NV_CTRL_THERMAL_COOLER_TARGET_NONE = 0 +NV_CTRL_THERMAL_COOLER_TARGET_GPU = 1 +NV_CTRL_THERMAL_COOLER_TARGET_MEMORY = 2 +NV_CTRL_THERMAL_COOLER_TARGET_POWER_SUPPLY = 4 +NV_CTRL_THERMAL_COOLER_TARGET_GPU_RELATED = NV_CTRL_THERMAL_COOLER_TARGET_GPU | NV_CTRL_THERMAL_COOLER_TARGET_MEMORY | NV_CTRL_THERMAL_COOLER_TARGET_POWER_SUPPLY + +# +# NV_CTRL_GPU_ECC_SUPPORTED - Reports whether ECC is supported by the +# targeted GPU. +# +NV_CTRL_GPU_ECC_SUPPORTED = 324 # R--G +NV_CTRL_GPU_ECC_SUPPORTED_FALSE = 0 +NV_CTRL_GPU_ECC_SUPPORTED_TRUE = 1 + +# +# NV_CTRL_GPU_ECC_STATUS - Returns the current hardware ECC setting +# for the targeted GPU. +# +NV_CTRL_GPU_ECC_STATUS = 325 # R--G +NV_CTRL_GPU_ECC_STATUS_DISABLED = 0 +NV_CTRL_GPU_ECC_STATUS_ENABLED = 1 + +# +# NV_CTRL_GPU_ECC_CONFIGURATION - Reports whether ECC can be configured +# dynamically for the GPU in question. +# +NV_CTRL_GPU_ECC_CONFIGURATION_SUPPORTED = 326 # R--G +NV_CTRL_GPU_ECC_CONFIGURATION_SUPPORTED_FALSE = 0 +NV_CTRL_GPU_ECC_CONFIGURATION_SUPPORTED_TRUE = 1 + +# +# NV_CTRL_GPU_ECC_CONFIGURATION_SETTING - Returns the current ECC +# configuration setting or specifies new settings. New settings do not +# take effect until the next POST. +# +NV_CTRL_GPU_ECC_CONFIGURATION = 327 # RW-G +NV_CTRL_GPU_ECC_CONFIGURATION_DISABLED = 0 +NV_CTRL_GPU_ECC_CONFIGURATION_ENABLED = 1 + +# +# NV_CTRL_GPU_ECC_DEFAULT_CONFIGURATION_SETTING - Returns the default +# ECC configuration setting. +# +NV_CTRL_GPU_ECC_DEFAULT_CONFIGURATION = 328 # R--G +NV_CTRL_GPU_ECC_DEFAULT_CONFIGURATION_DISABLED = 0 +NV_CTRL_GPU_ECC_DEFAULT_CONFIGURATION_ENABLED = 1 + +# +# NV_CTRL_GPU_ECC_SINGLE_BIT_ERRORS - Returns the number of single-bit +# ECC errors detected by the targeted GPU since the last POST. +# Note: this attribute is a 64-bit integer attribute. +# +NV_CTRL_GPU_ECC_SINGLE_BIT_ERRORS = 329 # R--GQ + +# +# NV_CTRL_GPU_ECC_DOUBLE_BIT_ERRORS - Returns the number of double-bit +# ECC errors detected by the targeted GPU since the last POST. +# Note: this attribute is a 64-bit integer attribute. +# +NV_CTRL_GPU_ECC_DOUBLE_BIT_ERRORS = 330 # R--GQ + +# +# NV_CTRL_GPU_ECC_AGGREGATE_SINGLE_BIT_ERRORS - Returns the number of +# single-bit ECC errors detected by the targeted GPU since the +# last counter reset. +# Note: this attribute is a 64-bit integer attribute. +# +NV_CTRL_GPU_ECC_AGGREGATE_SINGLE_BIT_ERRORS = 331 # R--GQ + +# +# NV_CTRL_GPU_ECC_AGGREGATE_DOUBLE_BIT_ERRORS - Returns the number of +# double-bit ECC errors detected by the targeted GPU since the +# last counter reset. +# Note: this attribute is a 64-bit integer attribute. +# +NV_CTRL_GPU_ECC_AGGREGATE_DOUBLE_BIT_ERRORS = 332 # R--GQ + +# +# NV_CTRL_GPU_ECC_RESET_ERROR_STATUS - Resets the volatile/aggregate +# single-bit and double-bit error counters. This attribute is a +# bitmask attribute. +# +NV_CTRL_GPU_ECC_RESET_ERROR_STATUS = 333 # -W-G +NV_CTRL_GPU_ECC_RESET_ERROR_STATUS_VOLATILE = 0x00000001 +NV_CTRL_GPU_ECC_RESET_ERROR_STATUS_AGGREGATE = 0x00000002 + +# +# NV_CTRL_GPU_POWER_MIZER_MODE - Provides a hint to the driver +# as to how to manage the performance of the GPU. +# +# ADAPTIVE - adjust GPU clocks based on GPU +# utilization +# PREFER_MAXIMUM_PERFORMANCE - raise GPU clocks to favor +# maximum performance, to the extent +# that thermal and other constraints +# allow +# AUTO - let the driver choose the performance +# policy +# PREFER_CONSISTENT_PERFORMANCE - lock to GPU base clocks +# +NV_CTRL_GPU_POWER_MIZER_MODE = 334 # RW-G +NV_CTRL_GPU_POWER_MIZER_MODE_ADAPTIVE = 0 +NV_CTRL_GPU_POWER_MIZER_MODE_PREFER_MAXIMUM_PERFORMANCE = 1 +NV_CTRL_GPU_POWER_MIZER_MODE_AUTO = 2 +NV_CTRL_GPU_POWER_MIZER_MODE_PREFER_CONSISTENT_PERFORMANCE = 3 + +# +# NV_CTRL_GVI_SYNC_OUTPUT_FORMAT - Returns the output sync signal +# from the GVI device. +# + +NV_CTRL_GVI_SYNC_OUTPUT_FORMAT = 335 # R--I + +# +# NV_CTRL_GVI_MAX_CHANNELS_PER_JACK - Returns the maximum +# supported number of (logical) channels within a single physical jack of +# a GVI device. For most SDI video formats, there is only one channel +# (channel 0). But for 3G video formats (as specified in SMPTE 425), +# as an example, there are two channels (channel 0 and channel 1) per +# physical jack. +# + +NV_CTRL_GVI_MAX_CHANNELS_PER_JACK = 336 # R--I + +# +# NV_CTRL_GVI_MAX_STREAMS - Returns the maximum number of streams +# that can be configured on the GVI device. +# + +NV_CTRL_GVI_MAX_STREAMS = 337 # R--I + +# +# NV_CTRL_GVI_NUM_CAPTURE_SURFACES - The GVI interface exposed through +# NV-CONTROL and the GLX_NV_video_input extension uses internal capture +# surfaces when frames are read from the GVI device. The +# NV_CTRL_GVI_NUM_CAPTURE_SURFACES can be used to query and assign the +# number of capture surfaces. This attribute is applied when +# glXBindVideoCaptureDeviceNV() is called by the application. +# +# A lower number of capture surfaces will mean less video memory is used, +# but can result in frames being dropped if the application cannot keep up +# with the capture device. A higher number will prevent frames from being +# dropped, making capture more reliable but will consume move video memory. +# +NV_CTRL_GVI_NUM_CAPTURE_SURFACES = 338 # RW-I + +# +# NV_CTRL_OVERSCAN_COMPENSATION - not supported +# +NV_CTRL_OVERSCAN_COMPENSATION = 339 # not supported + +# +# NV_CTRL_GPU_PCIE_GENERATION - Reports the current PCIe generation. +# +NV_CTRL_GPU_PCIE_GENERATION = 341 # R--GI +NV_CTRL_GPU_PCIE_GENERATION1 = 0x00000001 +NV_CTRL_GPU_PCIE_GENERATION2 = 0x00000002 +NV_CTRL_GPU_PCIE_GENERATION3 = 0x00000003 + +# +# NV_CTRL_GVI_BOUND_GPU - Returns the NV_CTRL_TARGET_TYPE_GPU target_id of +# the GPU currently bound to the GVI device. Returns -1 if no GPU is +# currently bound to the GVI device. +# +NV_CTRL_GVI_BOUND_GPU = 342 # R--I + +# +# NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT3 - this attribute is only +# intended to be used to query the ValidValues for +# NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT for VIDEO_FORMAT values between +# 64 and 95. See NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT for details. +# + +NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT3 = 343 # ---GI + +# +# NV_CTRL_ACCELERATE_TRAPEZOIDS - Toggles RENDER Trapezoid acceleration +# + +NV_CTRL_ACCELERATE_TRAPEZOIDS = 344 # RW- +NV_CTRL_ACCELERATE_TRAPEZOIDS_DISABLE = 0 +NV_CTRL_ACCELERATE_TRAPEZOIDS_ENABLE = 1 + +# +# NV_CTRL_GPU_CORES - Returns number of GPU cores supported by the graphics +# pipeline. +# + +NV_CTRL_GPU_CORES = 345 # R--G + +# +# NV_CTRL_GPU_MEMORY_BUS_WIDTH - Returns memory bus bandwidth on the associated +# subdevice. +# + +NV_CTRL_GPU_MEMORY_BUS_WIDTH = 346 # R--G + +# +# NV_CTRL_GVI_TEST_MODE - This attribute controls the GVI test mode. When +# enabled, the GVI device will generate fake data as quickly as possible. All +# GVI settings are still valid when this is enabled (e.g., the requested video +# format is honored and sets the video size). +# This may be used to test the pipeline. +# + +NV_CTRL_GVI_TEST_MODE = 347 # R--I +NV_CTRL_GVI_TEST_MODE_DISABLE = 0 +NV_CTRL_GVI_TEST_MODE_ENABLE = 1 + +# +# NV_CTRL_COLOR_SPACE - This option controls the preferred color space of the +# video signal. This may not match the current color space depending on the +# current mode on this display. +# +# NV_CTRL_CURRENT_COLOR_SPACE will reflect the actual color space in use. +# +NV_CTRL_COLOR_SPACE = 348 # RWDG +NV_CTRL_COLOR_SPACE_RGB = 0 +NV_CTRL_COLOR_SPACE_YCbCr422 = 1 +NV_CTRL_COLOR_SPACE_YCbCr444 = 2 + +# +# NV_CTRL_COLOR_RANGE - This option controls the preferred color range of the +# video signal. +# +# If the current color space requires it, the actual color range will be +# limited. +# +# NV_CTRL_CURRENT_COLOR_RANGE will reflect the actual color range in use. +# +NV_CTRL_COLOR_RANGE = 349 # RWDG +NV_CTRL_COLOR_RANGE_FULL = 0 +NV_CTRL_COLOR_RANGE_LIMITED = 1 + +# +# NV_CTRL_GPU_SCALING_DEFAULT_TARGET - not supported +# + +NV_CTRL_GPU_SCALING_DEFAULT_TARGET = 350 # not supported + +# +# NV_CTRL_GPU_SCALING_DEFAULT_METHOD - not supported +# + +NV_CTRL_GPU_SCALING_DEFAULT_METHOD = 351 # not supported + +# +# NV_CTRL_DITHERING_MODE - Controls the dithering mode, when +# NV_CTRL_CURRENT_DITHERING is Enabled. +# +# AUTO: allow the driver to choose the dithering mode automatically. +# +# DYNAMIC_2X2: use a 2x2 matrix to dither from the GPU's pixel +# pipeline to the bit depth of the flat panel. The matrix values +# are changed from frame to frame. +# +# STATIC_2X2: use a 2x2 matrix to dither from the GPU's pixel +# pipeline to the bit depth of the flat panel. The matrix values +# do not change from frame to frame. +# +# TEMPORAL: use a pseudorandom value from a uniform distribution calculated at +# every pixel to achieve stochastic dithering. This method produces a better +# visual result than 2x2 matrix approaches. +# +NV_CTRL_DITHERING_MODE = 352 # RWDG +NV_CTRL_DITHERING_MODE_AUTO = 0 +NV_CTRL_DITHERING_MODE_DYNAMIC_2X2 = 1 +NV_CTRL_DITHERING_MODE_STATIC_2X2 = 2 +NV_CTRL_DITHERING_MODE_TEMPORAL = 3 + +# +# NV_CTRL_CURRENT_DITHERING - Returns the current dithering state. +# +NV_CTRL_CURRENT_DITHERING = 353 # R-DG +NV_CTRL_CURRENT_DITHERING_DISABLED = 0 +NV_CTRL_CURRENT_DITHERING_ENABLED = 1 + +# +# NV_CTRL_CURRENT_DITHERING_MODE - Returns the current dithering +# mode. +# +NV_CTRL_CURRENT_DITHERING_MODE = 354 # R-DG +NV_CTRL_CURRENT_DITHERING_MODE_NONE = 0 +NV_CTRL_CURRENT_DITHERING_MODE_DYNAMIC_2X2 = 1 +NV_CTRL_CURRENT_DITHERING_MODE_STATIC_2X2 = 2 +NV_CTRL_CURRENT_DITHERING_MODE_TEMPORAL = 3 + +# +# NV_CTRL_THERMAL_SENSOR_READING - Returns the thermal sensor's current +# reading. +# +NV_CTRL_THERMAL_SENSOR_READING = 355 # R--S + +# +# NV_CTRL_THERMAL_SENSOR_PROVIDER - Returns the hardware device that +# provides the thermal sensor. +# +NV_CTRL_THERMAL_SENSOR_PROVIDER = 356 # R--S +NV_CTRL_THERMAL_SENSOR_PROVIDER_NONE = 0 +NV_CTRL_THERMAL_SENSOR_PROVIDER_GPU_INTERNAL = 1 +NV_CTRL_THERMAL_SENSOR_PROVIDER_ADM1032 = 2 +NV_CTRL_THERMAL_SENSOR_PROVIDER_ADT7461 = 3 +NV_CTRL_THERMAL_SENSOR_PROVIDER_MAX6649 = 4 +NV_CTRL_THERMAL_SENSOR_PROVIDER_MAX1617 = 5 +NV_CTRL_THERMAL_SENSOR_PROVIDER_LM99 = 6 +NV_CTRL_THERMAL_SENSOR_PROVIDER_LM89 = 7 +NV_CTRL_THERMAL_SENSOR_PROVIDER_LM64 = 8 +NV_CTRL_THERMAL_SENSOR_PROVIDER_G781 = 9 +NV_CTRL_THERMAL_SENSOR_PROVIDER_ADT7473 = 10 +NV_CTRL_THERMAL_SENSOR_PROVIDER_SBMAX6649 = 11 +NV_CTRL_THERMAL_SENSOR_PROVIDER_VBIOSEVT = 12 +NV_CTRL_THERMAL_SENSOR_PROVIDER_OS = 13 +NV_CTRL_THERMAL_SENSOR_PROVIDER_UNKNOWN = 0xFFFFFFFF + +# +# NV_CTRL_THERMAL_SENSOR_TARGET - Returns what hardware component +# the thermal sensor is measuring. +# +NV_CTRL_THERMAL_SENSOR_TARGET = 357 # R--S +NV_CTRL_THERMAL_SENSOR_TARGET_NONE = 0 +NV_CTRL_THERMAL_SENSOR_TARGET_GPU = 1 +NV_CTRL_THERMAL_SENSOR_TARGET_MEMORY = 2 +NV_CTRL_THERMAL_SENSOR_TARGET_POWER_SUPPLY = 4 +NV_CTRL_THERMAL_SENSOR_TARGET_BOARD = 8 +NV_CTRL_THERMAL_SENSOR_TARGET_UNKNOWN = 0xFFFFFFFF + +# +# NV_CTRL_SHOW_MULTIGPU_VISUAL_INDICATOR - when TRUE, OpenGL will +# draw information about the current MULTIGPU mode. +# +NV_CTRL_SHOW_MULTIGPU_VISUAL_INDICATOR = 358 # RW-X +NV_CTRL_SHOW_MULTIGPU_VISUAL_INDICATOR_FALSE = 0 +NV_CTRL_SHOW_MULTIGPU_VISUAL_INDICATOR_TRUE = 1 + +# +# NV_CTRL_GPU_CURRENT_PROCESSOR_CLOCK_FREQS - Returns GPU's processor +# clock freqs. +# +NV_CTRL_GPU_CURRENT_PROCESSOR_CLOCK_FREQS = 359 # RW-G + +# +# NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS - query the flags (various information +# for the specified NV_CTRL_GVIO_VIDEO_FORMAT_*. So that this can be +# queried with existing interfaces, the video format should be specified +# in the display_mask field; eg: +# +# XNVCTRLQueryTargetAttribute(dpy, +# NV_CTRL_TARGET_TYPE_GVI, +# gvi, +# NV_CTRL_GVIO_VIDEO_FORMAT_720P_60_00_SMPTE296, +# NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS, +# &flags); +# +# Note: The NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS_3G_1080P_NO_12BPC flag is set +# for those 1080P 3G modes (level A and B) that do not support +# 12 bits per component (when configuring a GVI stream.) +# + +NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS = 360 # R--I +NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS_NONE = 0x00000000 +NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS_INTERLACED = 0x00000001 +NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS_PROGRESSIVE = 0x00000002 +NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS_PSF = 0x00000004 +NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS_3G_LEVEL_A = 0x00000008 +NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS_3G_LEVEL_B = 0x00000010 +NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS_3G = NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS_3G_LEVEL_A | NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS_3G_LEVEL_B +NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS_3G_1080P_NO_12BPC = 0x00000020 + +# +# NV_CTRL_GPU_PCIE_MAX_LINK_SPEED - returns maximum PCIe link speed, +# in gigatransfers per second (GT/s). +# + +NV_CTRL_GPU_PCIE_MAX_LINK_SPEED = 361 # R--GI + +# +# NV_CTRL_3D_VISION_PRO_RESET_TRANSCEIVER_TO_FACTORY_SETTINGS - Resets the +# 3D Vision Pro transceiver to its factory settings. +# +NV_CTRL_3D_VISION_PRO_RESET_TRANSCEIVER_TO_FACTORY_SETTINGS = 363 # -W-T + +# +# NV_CTRL_3D_VISION_PRO_TRANSCEIVER_CHANNEL - Controls the channel that is +# currently used by the 3D Vision Pro transceiver. +# +NV_CTRL_3D_VISION_PRO_TRANSCEIVER_CHANNEL = 364 # RW-T + +# +# NV_CTRL_3D_VISION_PRO_TRANSCEIVER_MODE - Controls the mode in which the +# 3D Vision Pro transceiver operates. +# NV_CTRL_3D_VISION_PRO_TM_LOW_RANGE is bidirectional +# NV_CTRL_3D_VISION_PRO_TM_MEDIUM_RANGE is bidirectional +# NV_CTRL_3D_VISION_PRO_TM_HIGH_RANGE may be bidirectional just up to a +# given range, and unidirectional beyond it +# NV_CTRL_3D_VISION_PRO_TM_COUNT is the total number of +# 3D Vision Pro transceiver modes +# +NV_CTRL_3D_VISION_PRO_TRANSCEIVER_MODE = 365 # RW-T +NV_CTRL_3D_VISION_PRO_TRANSCEIVER_MODE_INVALID = 0 +NV_CTRL_3D_VISION_PRO_TRANSCEIVER_MODE_LOW_RANGE = 1 +NV_CTRL_3D_VISION_PRO_TRANSCEIVER_MODE_MEDIUM_RANGE = 2 +NV_CTRL_3D_VISION_PRO_TRANSCEIVER_MODE_HIGH_RANGE = 3 +NV_CTRL_3D_VISION_PRO_TRANSCEIVER_MODE_COUNT = 4 + +# +# NV_CTRL_SYNCHRONOUS_PALETTE_UPDATES - controls whether updates to the color +# lookup table (LUT) are synchronous with respect to X rendering. For example, +# if an X client sends XStoreColors followed by XFillRectangle, the driver will +# guarantee that the FillRectangle request is not processed until after the +# updated LUT colors are actually visible on the screen if +# NV_CTRL_SYNCHRONOUS_PALETTE_UPDATES is enabled. Otherwise, the rendering may +# occur first. +# +# This makes a difference for applications that use the LUT to animate, such as +# XPilot. If you experience flickering in applications that use LUT +# animations, try enabling this attribute. +# +# When synchronous updates are enabled, XStoreColors requests will be processed +# at your screen's refresh rate. +# + +NV_CTRL_SYNCHRONOUS_PALETTE_UPDATES = 367 # RWDG +NV_CTRL_SYNCHRONOUS_PALETTE_UPDATES_DISABLE = 0 +NV_CTRL_SYNCHRONOUS_PALETTE_UPDATES_ENABLE = 1 + +# +# NV_CTRL_DITHERING_DEPTH - Controls the dithering depth when +# NV_CTRL_CURRENT_DITHERING is ENABLED. Some displays connected +# to the GPU via the DVI or LVDS interfaces cannot display the +# full color range of ten bits per channel, so the GPU will +# dither to either 6 or 8 bits per channel. +# +NV_CTRL_DITHERING_DEPTH = 368 # RWDG +NV_CTRL_DITHERING_DEPTH_AUTO = 0 +NV_CTRL_DITHERING_DEPTH_6_BITS = 1 +NV_CTRL_DITHERING_DEPTH_8_BITS = 2 + +# +# NV_CTRL_CURRENT_DITHERING_DEPTH - Returns the current dithering +# depth value. +# +NV_CTRL_CURRENT_DITHERING_DEPTH = 369 # R-DG +NV_CTRL_CURRENT_DITHERING_DEPTH_NONE = 0 +NV_CTRL_CURRENT_DITHERING_DEPTH_6_BITS = 1 +NV_CTRL_CURRENT_DITHERING_DEPTH_8_BITS = 2 + +# +# NV_CTRL_3D_VISION_PRO_TRANSCEIVER_CHANNEL_FREQUENCY - Returns the +# frequency of the channel(in kHz) of the 3D Vision Pro transceiver. +# Use the display_mask parameter to specify the channel number. +# +NV_CTRL_3D_VISION_PRO_TRANSCEIVER_CHANNEL_FREQUENCY = 370 # R--T + +# +# NV_CTRL_3D_VISION_PRO_TRANSCEIVER_CHANNEL_QUALITY - Returns the +# quality of the channel(in percentage) of the 3D Vision Pro transceiver. +# Use the display_mask parameter to specify the channel number. +# +NV_CTRL_3D_VISION_PRO_TRANSCEIVER_CHANNEL_QUALITY = 371 # R--T + +# +# NV_CTRL_3D_VISION_PRO_TRANSCEIVER_CHANNEL_COUNT - Returns the number of +# channels on the 3D Vision Pro transceiver. +# +NV_CTRL_3D_VISION_PRO_TRANSCEIVER_CHANNEL_COUNT = 372 # R--T + +# +# NV_CTRL_3D_VISION_PRO_PAIR_GLASSES - Puts the 3D Vision Pro +# transceiver into pairing mode to gather additional glasses. +# NV_CTRL_3D_VISION_PRO_PAIR_GLASSES_STOP - stops any pairing +# NV_CTRL_3D_VISION_PRO_PAIR_GLASSES_BEACON - starts continuous +# pairing via beacon mode +# Any other value, N - Puts the 3D Vision Pro transceiver into +# authenticated pairing mode for N seconds. +# +NV_CTRL_3D_VISION_PRO_PAIR_GLASSES = 373 # -W-T +NV_CTRL_3D_VISION_PRO_PAIR_GLASSES_STOP = 0 +NV_CTRL_3D_VISION_PRO_PAIR_GLASSES_BEACON = 0xFFFFFFFF + +# +# NV_CTRL_3D_VISION_PRO_UNPAIR_GLASSES - Tells a specific pair +# of glasses to unpair. The glasses will "forget" the address +# of the 3D Vision Pro transceiver to which they have been paired. +# To unpair all the currently paired glasses, specify +# the glasses id as 0. +# +NV_CTRL_3D_VISION_PRO_UNPAIR_GLASSES = 374 # -W-T + +# +# NV_CTRL_3D_VISION_PRO_DISCOVER_GLASSES - Tells the 3D Vision Pro +# transceiver about the glasses that have been paired using +# NV_CTRL_3D_VISION_PRO_PAIR_GLASSES_BEACON. Unless this is done, +# the 3D Vision Pro transceiver will not know about glasses paired in +# beacon mode. +# +NV_CTRL_3D_VISION_PRO_DISCOVER_GLASSES = 375 # -W-T + +# +# NV_CTRL_3D_VISION_PRO_IDENTIFY_GLASSES - Causes glasses LEDs to +# flash for a short period of time. +# +NV_CTRL_3D_VISION_PRO_IDENTIFY_GLASSES = 376 # -W-T + +# +# NV_CTRL_3D_VISION_PRO_GLASSES_SYNC_CYCLE - Controls the +# sync cycle duration(in milliseconds) of the glasses. +# Use the display_mask parameter to specify the glasses id. +# +NV_CTRL_3D_VISION_PRO_GLASSES_SYNC_CYCLE = 378 # RW-T + +# +# NV_CTRL_3D_VISION_PRO_GLASSES_MISSED_SYNC_CYCLES - Returns the +# number of state sync cycles recently missed by the glasses. +# Use the display_mask parameter to specify the glasses id. +# +NV_CTRL_3D_VISION_PRO_GLASSES_MISSED_SYNC_CYCLES = 379 # R--T + +# +# NV_CTRL_3D_VISION_PRO_GLASSES_BATTERY_LEVEL - Returns the +# battery level(in percentage) of the glasses. +# Use the display_mask parameter to specify the glasses id. +# +NV_CTRL_3D_VISION_PRO_GLASSES_BATTERY_LEVEL = 380 # R--T + +# +# NV_CTRL_GVO_ANC_PARITY_COMPUTATION - Controls the SDI device's computation +# of the parity bit (bit 8) for ANC data words. +# + +NV_CTRL_GVO_ANC_PARITY_COMPUTATION = 381 # RW--- +NV_CTRL_GVO_ANC_PARITY_COMPUTATION_AUTO = 0 +NV_CTRL_GVO_ANC_PARITY_COMPUTATION_ON = 1 +NV_CTRL_GVO_ANC_PARITY_COMPUTATION_OFF = 2 + +# +# NV_CTRL_3D_VISION_PRO_GLASSES_PAIR_EVENT - This attribute is sent +# as an event when glasses get paired in response to pair command +# from any of the clients. +# +NV_CTRL_3D_VISION_PRO_GLASSES_PAIR_EVENT = 382 # ---T + +# +# NV_CTRL_3D_VISION_PRO_GLASSES_UNPAIR_EVENT - This attribute is sent +# as an event when glasses get unpaired in response to unpair command +# from any of the clients. +# +NV_CTRL_3D_VISION_PRO_GLASSES_UNPAIR_EVENT = 383 # ---T + +# +# NV_CTRL_GPU_PCIE_CURRENT_LINK_WIDTH - returns the current +# PCIe link width, in number of lanes. +# +NV_CTRL_GPU_PCIE_CURRENT_LINK_WIDTH = 384 # R--GI + +# +# NV_CTRL_GPU_PCIE_CURRENT_LINK_SPEED - returns the current +# PCIe link speed, in megatransfers per second (GT/s). +# +NV_CTRL_GPU_PCIE_CURRENT_LINK_SPEED = 385 # R--GI + +# +# NV_CTRL_GVO_AUDIO_BLANKING - specifies whether the GVO device should delete +# audio ancillary data packets when frames are repeated. +# +# When a new frame is not ready in time, the current frame, including all +# ancillary data packets, is repeated. When this data includes audio packets, +# this can result in stutters or clicks. When this option is enabled, the GVO +# device will detect when frames are repeated, identify audio ancillary data +# packets, and mark them for deletion. +# +# This option is applied when the GVO device is bound. +# +NV_CTRL_GVO_AUDIO_BLANKING = 386 # RW- +NV_CTRL_GVO_AUDIO_BLANKING_DISABLE = 0 +NV_CTRL_GVO_AUDIO_BLANKING_ENABLE = 1 + +# +# NV_CTRL_CURRENT_METAMODE_ID - switch modes to the MetaMode with +# the specified ID. +# +NV_CTRL_CURRENT_METAMODE_ID = 387 # RW- + +# +# NV_CTRL_DISPLAY_ENABLED - Returns whether or not the display device +# is currently enabled. +# +NV_CTRL_DISPLAY_ENABLED = 388 # R-D +NV_CTRL_DISPLAY_ENABLED_TRUE = 1 +NV_CTRL_DISPLAY_ENABLED_FALSE = 0 + +# +# NV_CTRL_FRAMELOCK_INCOMING_HOUSE_SYNC_RATE: this is the rate +# of an incomming house sync signal to the frame lock board, in milliHz. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN +# target. +# +NV_CTRL_FRAMELOCK_INCOMING_HOUSE_SYNC_RATE = 389 # R--F + +# +# NV_CTRL_FXAA - enables FXAA. A pixel shader based anti- +# aliasing method. +# +NV_CTRL_FXAA = 390 # RW-X +NV_CTRL_FXAA_DISABLE = 0 +NV_CTRL_FXAA_ENABLE = 1 + +# +# NV_CTRL_DISPLAY_RANDR_OUTPUT_ID - the RandR Output ID (type RROutput) +# that corresponds to the specified Display Device target. If a new +# enough version of RandR is not available in the X server, +# DISPLAY_RANDR_OUTPUT_ID will be 0. +# +NV_CTRL_DISPLAY_RANDR_OUTPUT_ID = 391 # R-D- + +# +# NV_CTRL_FRAMELOCK_DISPLAY_CONFIG - Configures whether the display device +# should listen, ignore or drive the framelock sync signal. +# +# Note that whether or not a display device may be set as a client/server +# depends on the current configuration. For example, only one server may be +# set per Quadro Sync device, and displays can only be configured as a client +# if their refresh rate sufficiently matches the refresh rate of the server +# device. +# +# Note that when querying the ValidValues for this data type, the values are +# reported as bits within a bitmask (ATTRIBUTE_TYPE_INT_BITS); +# +NV_CTRL_FRAMELOCK_DISPLAY_CONFIG = 392 # RWD +NV_CTRL_FRAMELOCK_DISPLAY_CONFIG_DISABLED = 0 +NV_CTRL_FRAMELOCK_DISPLAY_CONFIG_CLIENT = 1 +NV_CTRL_FRAMELOCK_DISPLAY_CONFIG_SERVER = 2 + +# +# NV_CTRL_TOTAL_DEDICATED_GPU_MEMORY - Returns the total amount of dedicated +# GPU video memory, in MB, on the specified GPU. This excludes any TurboCache +# padding included in the value returned by NV_CTRL_TOTAL_GPU_MEMORY. +# +NV_CTRL_TOTAL_DEDICATED_GPU_MEMORY = 393 # R--G + +# +# NV_CTRL_USED_DEDICATED_GPU_MEMORY- Returns the amount of video memory +# currently used on the graphics card in MB. +# +NV_CTRL_USED_DEDICATED_GPU_MEMORY = 394 # R--G + +# +# NV_CTRL_GPU_DOUBLE_PRECISION_BOOST_IMMEDIATE +# Some GPUs can make a tradeoff between double-precision floating-point +# performance and clock speed. Enabling double-precision floating point +# performance may benefit CUDA or OpenGL applications that require high +# bandwidth double-precision performance. Disabling this feature may benefit +# graphics applications that require higher clock speeds. +# +# This attribute is only available when toggling double precision boost +# can be done immediately (without need for a rebooot). +# +NV_CTRL_GPU_DOUBLE_PRECISION_BOOST_IMMEDIATE = 395 # RW-G +NV_CTRL_GPU_DOUBLE_PRECISION_BOOST_IMMEDIATE_DISABLED = 0 +NV_CTRL_GPU_DOUBLE_PRECISION_BOOST_IMMEDIATE_ENABLED = 1 + +# +# NV_CTRL_GPU_DOUBLE_PRECISION_BOOST_REBOOT +# Some GPUs can make a tradeoff between double-precision floating-point +# performance and clock speed. Enabling double-precision floating point +# performance may benefit CUDA or OpenGL applications that require high +# bandwidth double-precision performance. Disabling this feature may benefit +# graphics applications that require higher clock speeds. +# +# This attribute is only available when toggling double precision boost +# requires a reboot. +# + +NV_CTRL_GPU_DOUBLE_PRECISION_BOOST_REBOOT = 396 # RW-G +NV_CTRL_GPU_DOUBLE_PRECISION_BOOST_REBOOT_DISABLED = 0 +NV_CTRL_GPU_DOUBLE_PRECISION_BOOST_REBOOT_ENALED = 1 + +# +# NV_CTRL_DPY_HDMI_3D - Returns whether the specified display device is +# currently using HDMI 3D Frame Packed Stereo mode. Clients may use this +# to help interpret the refresh rate returned by NV_CTRL_REFRESH_RATE or +# NV_CTRL_REFRESH_RATE_3, which will be doubled when using HDMI 3D mode. +# +# This attribute may be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_GPU target. +# + +NV_CTRL_DPY_HDMI_3D = 397 # R-DG +NV_CTRL_DPY_HDMI_3D_DISABLED = 0 +NV_CTRL_DPY_HDMI_3D_ENABLED = 1 + +# +# NV_CTRL_BASE_MOSAIC - Returns whether Base Mosaic is currently enabled on the +# given GPU. Querying the valid values of this attribute returns capabilities. +# + +NV_CTRL_BASE_MOSAIC = 398 # R--G +NV_CTRL_BASE_MOSAIC_DISABLED = 0 +NV_CTRL_BASE_MOSAIC_FULL = 1 +NV_CTRL_BASE_MOSAIC_LIMITED = 2 + +# +# NV_CTRL_MULTIGPU_MASTER_POSSIBLE - Returns whether the GPU can be configured +# as the master GPU in a Multi GPU configuration (SLI, SLI Mosaic, +# Base Mosaic). +# + +NV_CTRL_MULTIGPU_MASTER_POSSIBLE = 399 # R--G +NV_CTRL_MULTIGPU_MASTER_POSSIBLE_FALSE = 0 +NV_CTRL_MULTIGPU_MASTER_POSSIBLE_TRUE = 1 + +# +# NV_CTRL_GPU_POWER_MIZER_DEFAULT_MODE - Returns the default PowerMizer mode +# for the given GPU. +# +NV_CTRL_GPU_POWER_MIZER_DEFAULT_MODE = 400 # R--G + +# +# NV_CTRL_XV_SYNC_TO_DISPLAY_ID - When XVideo Sync To VBlank is enabled, this +# controls which display device will be synched to if the display is enabled. +# Returns NV_CTRL_XV_SYNC_TO_DISPLAY_ID_AUTO if no display has been +# selected. +# +NV_CTRL_XV_SYNC_TO_DISPLAY_ID = 401 # RW- +NV_CTRL_XV_SYNC_TO_DISPLAY_ID_AUTO = 0xFFFFFFFF + +# +# NV_CTRL_BACKLIGHT_BRIGHTNESS - The backlight brightness of an internal panel. +# +NV_CTRL_BACKLIGHT_BRIGHTNESS = 402 # RWD- + +# +# NV_CTRL_GPU_LOGO_BRIGHTNESS - Controls brightness +# of the logo on the GPU, if any. The value is variable from 0% - 100%. +# +NV_CTRL_GPU_LOGO_BRIGHTNESS = 403 # RW-G + +# +# NV_CTRL_GPU_SLI_LOGO_BRIGHTNESS - Controls brightness of the logo +# on the SLI bridge, if any. The value is variable from 0% - 100%. +# +NV_CTRL_GPU_SLI_LOGO_BRIGHTNESS = 404 # RW-G + +# +# NV_CTRL_THERMAL_COOLER_SPEED - Returns cooler's current operating speed in +# rotations per minute (RPM). +# + +NV_CTRL_THERMAL_COOLER_SPEED = 405 # R--C + +# +# NV_CTRL_PALETTE_UPDATE_EVENT - The Color Palette has been changed and the +# color correction info needs to be updated. +# + +NV_CTRL_PALETTE_UPDATE_EVENT = 406 # --- + +# +# NV_CTRL_VIDEO_ENCODER_UTILIZATION - Returns the video encoder engine +# utilization as a percentage. +# +NV_CTRL_VIDEO_ENCODER_UTILIZATION = 407 # R--G + +# +# NV_CTRL_GSYNC_ALLOWED - when TRUE, OpenGL will enable G-SYNC when possible; +# when FALSE, OpenGL will always use a fixed monitor refresh rate. +# + +NV_CTRL_GSYNC_ALLOWED = 408 # RW-X +NV_CTRL_GSYNC_ALLOWED_FALSE = 0 +NV_CTRL_GSYNC_ALLOWED_TRUE = 1 + +# +# NV_CTRL_GPU_NVCLOCK_OFFSET - This attribute controls the GPU clock offsets +# (in MHz) used for overclocking per performance level. +# Use the display_mask parameter to specify the performance level. +# +# Note: To enable overclocking support, set the X configuration +# option "Coolbits" to value "8". +# +# This offset can have any integer value between +# NVCTRLAttributeValidValues.u.range.min and +# NVCTRLAttributeValidValues.u.range.max (inclusive). +# +# This attribute is available on GeForce GTX 400 series and later +# Geforce GPUs. +# +NV_CTRL_GPU_NVCLOCK_OFFSET = 409 # RW-G + +# +# NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET - This attribute controls +# the memory transfer rate offsets (in MHz) used for overclocking +# per performance level. +# Use the display_mask parameter to specify the performance level. +# +# Note: To enable overclocking support, set the X configuration +# option "Coolbits" to value "8". +# +# This offset can have any integer value between +# NVCTRLAttributeValidValues.u.range.min and +# NVCTRLAttributeValidValues.u.range.max (inclusive). +# +# This attribute is available on GeForce GTX 400 series and later +# Geforce GPUs. +# +NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET = 410 # RW-G + +# +# NV_CTRL_VIDEO_DECODER_UTILIZATION - Returns the video decoder engine +# utilization as a percentage. +# +NV_CTRL_VIDEO_DECODER_UTILIZATION = 411 # R--G + +# +# NV_CTRL_GPU_OVER_VOLTAGE_OFFSET - This attribute controls +# the overvoltage offset in microvolts (uV). +# +# Note: To enable overvoltage support, set the X configuration +# option "Coolbits" to value "16". +# +# This offset can have any integer value between +# NVCTRLAttributeValidValues.u.range.min and +# NVCTRLAttributeValidValues.u.range.max (inclusive). +# +# This attribute is available on GeForce GTX 400 series and later +# Geforce GPUs. +# + +NV_CTRL_GPU_OVER_VOLTAGE_OFFSET = 412 # RW-G + +# +# NV_CTRL_GPU_CURRENT_CORE_VOLTAGE - This attribute returns the +# GPU's current operating voltage in microvolts (uV). +# +# This attribute is available on GPUs that support +# NV_CTRL_GPU_OVER_VOLTAGE_OFFSET. +# +NV_CTRL_GPU_CURRENT_CORE_VOLTAGE = 413 # R--G + +# +# NV_CTRL_CURRENT_COLOR_SPACE - Returns the current color space of the video +# signal. +# +# This will match NV_CTRL_COLOR_SPACE unless the current mode on this display +# device is an HDMI 2.0 4K@60Hz mode and the display device or GPU does not +# support driving this mode in RGB, in which case YCbCr420 will be returned. +# +NV_CTRL_CURRENT_COLOR_SPACE = 414 # R-DG +NV_CTRL_CURRENT_COLOR_SPACE_RGB = 0 +NV_CTRL_CURRENT_COLOR_SPACE_YCbCr422 = 1 +NV_CTRL_CURRENT_COLOR_SPACE_YCbCr444 = 2 +NV_CTRL_CURRENT_COLOR_SPACE_YCbCr420 = 3 + +# +# NV_CTRL_CURRENT_COLOR_RANGE - Returns the current color range of the video +# signal. +# +NV_CTRL_CURRENT_COLOR_RANGE = 415 # R-DG +NV_CTRL_CURRENT_COLOR_RANGE_FULL = 0 +NV_CTRL_CURRENT_COLOR_RANGE_LIMITED = 1 + +# +# NV_CTRL_SHOW_GSYNC_VISUAL_INDICATOR - when TRUE, OpenGL will indicate when +# G-SYNC is in use for full-screen applications. +# + +NV_CTRL_SHOW_GSYNC_VISUAL_INDICATOR = 416 # RW-X +NV_CTRL_SHOW_GSYNC_VISUAL_INDICATOR_FALSE = 0 +NV_CTRL_SHOW_GSYNC_VISUAL_INDICATOR_TRUE = 1 + +# +# NV_CTRL_THERMAL_COOLER_CURRENT_LEVEL - Returns cooler's current +# operating level. This may fluctuate dynamically. When +# NV_CTRL_GPU_COOLER_MANUAL_CONTROL=TRUE, the driver attempts +# to make this match NV_CTRL_THERMAL_COOLER_LEVEL. When +# NV_CTRL_GPU_COOLER_MANUAL_CONTROL=FALSE, the driver adjusts the +# current level based on the needs of the GPU. +# + +NV_CTRL_THERMAL_COOLER_CURRENT_LEVEL = 417 # R--C + +# +# NV_CTRL_STEREO_SWAP_MODE - This attribute controls the swap mode when +# Quad-Buffered stereo is used. +# NV_CTRL_STEREO_SWAP_MODE_APPLICATION_CONTROL : Stereo swap mode is derived +# from the value of swap interval. +# If it's odd, the per eye swap mode is used. +# If it's even, the per eye pair swap mode is used. +# NV_CTRL_STEREO_SWAP_MODE_PER_EYE : The driver swaps each eye as it is ready. +# NV_CTRL_STEREO_SWAP_MODE_PER_EYE_PAIR : The driver waits for both eyes to +# complete rendering before swapping. +# + +NV_CTRL_STEREO_SWAP_MODE = 418 # RW-X +NV_CTRL_STEREO_SWAP_MODE_APPLICATION_CONTROL = 0 +NV_CTRL_STEREO_SWAP_MODE_PER_EYE = 1 +NV_CTRL_STEREO_SWAP_MODE_PER_EYE_PAIR = 2 + +# +# NV_CTRL_CURRENT_XV_SYNC_TO_DISPLAY_ID - When XVideo Sync To VBlank is +# enabled, this returns the display id of the device currently synched to. +# Returns NV_CTRL_XV_SYNC_TO_DISPLAY_ID_AUTO if no display is currently +# set. +# + +NV_CTRL_CURRENT_XV_SYNC_TO_DISPLAY_ID = 419 # R-- + +# +# NV_CTRL_GPU_FRAMELOCK_FIRMWARE_UNSUPPORTED - Returns true if the +# Quadro Sync card connected to this GPU has a firmware version incompatible +# with this GPU. +# + +NV_CTRL_GPU_FRAMELOCK_FIRMWARE_UNSUPPORTED = 420 # R--G +NV_CTRL_GPU_FRAMELOCK_FIRMWARE_UNSUPPORTED_FALSE = 0 +NV_CTRL_GPU_FRAMELOCK_FIRMWARE_UNSUPPORTED_TRUE = 1 + +# +# NV_CTRL_DISPLAYPORT_CONNECTOR_TYPE - Returns the connector type used by +# a DisplayPort display. +# + +NV_CTRL_DISPLAYPORT_CONNECTOR_TYPE = 421 # R-DG +NV_CTRL_DISPLAYPORT_CONNECTOR_TYPE_UNKNOWN = 0 +NV_CTRL_DISPLAYPORT_CONNECTOR_TYPE_DISPLAYPORT = 1 +NV_CTRL_DISPLAYPORT_CONNECTOR_TYPE_HDMI = 2 +NV_CTRL_DISPLAYPORT_CONNECTOR_TYPE_DVI = 3 +NV_CTRL_DISPLAYPORT_CONNECTOR_TYPE_VGA = 4 + +# +# NV_CTRL_DISPLAYPORT_IS_MULTISTREAM - Returns multi-stream support for +# DisplayPort displays. +# +NV_CTRL_DISPLAYPORT_IS_MULTISTREAM = 422 # R-DG + +# +# NV_CTRL_DISPLAYPORT_SINK_IS_AUDIO_CAPABLE - Returns whether a DisplayPort +# device supports audio. +# +NV_CTRL_DISPLAYPORT_SINK_IS_AUDIO_CAPABLE = 423 # R-DG + +# +# NV_CTRL_GPU_NVCLOCK_OFFSET_ALL_PERFORMANCE_LEVELS - This attribute +# controls the GPU clock offsets (in MHz) used for overclocking. +# The offset is applied to all performance levels. +# +# Note: To enable overclocking support, set the X configuration +# option "Coolbits" to value "8". +# +# This offset can have any integer value between +# NVCTRLAttributeValidValues.u.range.min and +# NVCTRLAttributeValidValues.u.range.max (inclusive). +# +# This attribute is available on GeForce GTX 1000 series and later +# Geforce GPUs. +# +NV_CTRL_GPU_NVCLOCK_OFFSET_ALL_PERFORMANCE_LEVELS = 424 # RW-G + +# +# NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET_ALL_PERFORMANCE_LEVELS - This +# attribute controls the memory transfer rate offsets (in MHz) used +# for overclocking. The offset is applied to all performance levels. +# +# Note: To enable overclocking support, set the X configuration +# option "Coolbits" to value "8". +# +# This offset can have any integer value between +# NVCTRLAttributeValidValues.u.range.min and +# NVCTRLAttributeValidValues.u.range.max (inclusive). +# +# This attribute is available on GeForce GTX 1000 series and later +# Geforce GPUs. +# +NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET_ALL_PERFORMANCE_LEVELS = 425 # RW-G + +# +# NV_CTRL_FRAMELOCK_FIRMWARE_VERSION - Queries the firmware major version of +# the Frame Lock device. +# +# This attribute must be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_FRAMELOCK target. +# + +NV_CTRL_FRAMELOCK_FIRMWARE_VERSION = 426 # R--F + +# +# NV_CTRL_FRAMELOCK_FIRMWARE_MINOR_VERSION - Queries the firmware minor +# version of the Frame Lock device. +# +# This attribute must be queried through XNVCTRLQueryTargetAttribute() +# using a NV_CTRL_TARGET_TYPE_FRAMELOCK target. +# + +NV_CTRL_FRAMELOCK_FIRMWARE_MINOR_VERSION = 427 # R--F + +# +# NV_CTRL_SHOW_GRAPHICS_VISUAL_INDICATOR - when TRUE, graphics APIs will +# indicate various runtime information such as flip/blit, vsync status, API +# in use. +# + +NV_CTRL_SHOW_GRAPHICS_VISUAL_INDICATOR = 428 # RW-X +NV_CTRL_SHOW_GRAPHICS_VISUAL_INDICATOR_FALSE = 0 +NV_CTRL_SHOW_GRAPHICS_VISUAL_INDICATOR_TRUE = 1 + +NV_CTRL_LAST_ATTRIBUTE = NV_CTRL_SHOW_GRAPHICS_VISUAL_INDICATOR + +############################################################################ + +# +# String Attributes: +# +# String attributes can be queryied through the XNVCTRLQueryStringAttribute() +# and XNVCTRLQueryTargetStringAttribute() function calls. +# +# String attributes can be set through the XNVCTRLSetStringAttribute() +# function call. (There are currently no string attributes that can be +# set on non-X Screen targets.) +# +# Unless otherwise noted, all string attributes can be queried/set using an +# NV_CTRL_TARGET_TYPE_X_SCREEN target. Attributes that cannot take an +# NV_CTRL_TARGET_TYPE_X_SCREEN target also cannot be queried/set through +# XNVCTRLQueryStringAttribute()/XNVCTRLSetStringAttribute() (Since +# these assume an X Screen target). +# + + +# +# NV_CTRL_STRING_PRODUCT_NAME - the product name on which the +# specified X screen is running, or the product name of the specified +# Frame Lock device. +# +# This attribute may be queried through XNVCTRLQueryTargetStringAttribute() +# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target to +# return the product name of the GPU, or a NV_CTRL_TARGET_TYPE_FRAMELOCK to +# return the product name of the Frame Lock device. +# + +NV_CTRL_STRING_PRODUCT_NAME = 0 # R--GF + +# +# NV_CTRL_STRING_VBIOS_VERSION - the video bios version on the GPU on +# which the specified X screen is running. +# + +NV_CTRL_STRING_VBIOS_VERSION = 1 # R--G + +# +# NV_CTRL_STRING_NVIDIA_DRIVER_VERSION - string representation of the +# NVIDIA driver version number for the NVIDIA X driver in use. +# + +NV_CTRL_STRING_NVIDIA_DRIVER_VERSION = 3 # R--G + +# +# NV_CTRL_STRING_DISPLAY_DEVICE_NAME - name of the display device +# specified in the display_mask argument. +# +# This attribute may be queried through XNVCTRLQueryTargetStringAttribute() +# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. +# + +NV_CTRL_STRING_DISPLAY_DEVICE_NAME = 4 # R-DG + +# +# NV_CTRL_STRING_TV_ENCODER_NAME - not supported +# + +NV_CTRL_STRING_TV_ENCODER_NAME = 5 # not supported + +# +# NV_CTRL_STRING_GVIO_FIRMWARE_VERSION - indicates the version of the +# Firmware on the GVIO device. +# + +NV_CTRL_STRING_GVIO_FIRMWARE_VERSION = 8 # R--I + +# +# NV_CTRL_STRING_GVO_FIRMWARE_VERSION - renamed +# +# NV_CTRL_STRING_GVIO_FIRMWARE_VERSION should be used instead. +# +NV_CTRL_STRING_GVO_FIRMWARE_VERSION = 8 # renamed + +# +# NV_CTRL_STRING_CURRENT_MODELINE - Return the ModeLine currently +# being used by the specified display device. +# +# This attribute may be queried through XNVCTRLQueryTargetStringAttribute() +# using an NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. +# +# The ModeLine string may be prepended with a comma-separated list of +# "token=value" pairs, separated from the ModeLine string by "::". +# This "token=value" syntax is the same as that used in +# NV_CTRL_BINARY_DATA_MODELINES +# + +NV_CTRL_STRING_CURRENT_MODELINE = 9 # R-DG + +# +# NV_CTRL_STRING_ADD_MODELINE - Adds a ModeLine to the specified +# display device. The ModeLine is not added if validation fails. +# +# The ModeLine string should have the same syntax as a ModeLine in +# the X configuration file; e.g., +# +# "1600x1200" 229.5 1600 1664 1856 2160 1200 1201 1204 1250 +HSync +VSync +# + +NV_CTRL_STRING_ADD_MODELINE = 10 # -WDG + +# +# NV_CTRL_STRING_DELETE_MODELINE - Deletes an existing ModeLine +# from the specified display device. The currently selected +# ModeLine cannot be deleted. (This also means you cannot delete +# the last ModeLine.) +# +# The ModeLine string should have the same syntax as a ModeLine in +# the X configuration file; e.g., +# +# "1600x1200" 229.5 1600 1664 1856 2160 1200 1201 1204 1250 +HSync +VSync +# + +NV_CTRL_STRING_DELETE_MODELINE = 11 # -WDG + +# +# NV_CTRL_STRING_CURRENT_METAMODE - Returns the metamode currently +# being used by the specified X screen. The MetaMode string has the +# same syntax as the MetaMode X configuration option, as documented +# in the NVIDIA driver README. +# +# The returned string may be prepended with a comma-separated list of +# "token=value" pairs, separated from the MetaMode string by "::". +# This "token=value" syntax is the same as that used in +# NV_CTRL_BINARY_DATA_METAMODES. +# + +NV_CTRL_STRING_CURRENT_METAMODE = 12 # RW-- +NV_CTRL_STRING_CURRENT_METAMODE_VERSION_1 = NV_CTRL_STRING_CURRENT_METAMODE + +# +# NV_CTRL_STRING_ADD_METAMODE - Adds a MetaMode to the specified +# X Screen. +# +# It is recommended to not use this attribute, but instead use +# NV_CTRL_STRING_OPERATION_ADD_METAMODE. +# + +NV_CTRL_STRING_ADD_METAMODE = 13 # -W-- + +# +# NV_CTRL_STRING_DELETE_METAMODE - Deletes an existing MetaMode from +# the specified X Screen. The currently selected MetaMode cannot be +# deleted. (This also means you cannot delete the last MetaMode). +# The MetaMode string should have the same syntax as the MetaMode X +# configuration option, as documented in the NVIDIA driver README. +# + +NV_CTRL_STRING_DELETE_METAMODE = 14 # -WD-- + +# +# NV_CTRL_STRING_VCSC_PRODUCT_NAME - deprecated +# +# Queries the product name of the VCSC device. +# +# This attribute must be queried through XNVCTRLQueryTargetStringAttribute() +# using a NV_CTRL_TARGET_TYPE_VCSC target. +# + +NV_CTRL_STRING_VCSC_PRODUCT_NAME = 15 # R---V + +# +# NV_CTRL_STRING_VCSC_PRODUCT_ID - deprecated +# +# Queries the product ID of the VCSC device. +# +# This attribute must be queried through XNVCTRLQueryTargetStringAttribute() +# using a NV_CTRL_TARGET_TYPE_VCSC target. +# + +NV_CTRL_STRING_VCSC_PRODUCT_ID = 16 # R---V + +# +# NV_CTRL_STRING_VCSC_SERIAL_NUMBER - deprecated +# +# Queries the unique serial number of the VCS device. +# +# This attribute must be queried through XNVCTRLQueryTargetStringAttribute() +# using a NV_CTRL_TARGET_TYPE_VCSC target. +# + +NV_CTRL_STRING_VCSC_SERIAL_NUMBER = 17 # R---V + +# +# NV_CTRL_STRING_VCSC_BUILD_DATE - deprecated +# +# Queries the date of the VCS device. the returned string is in the following +# format: "Week.Year" +# +# This attribute must be queried through XNVCTRLQueryTargetStringAttribute() +# using a NV_CTRL_TARGET_TYPE_VCSC target. +# + +NV_CTRL_STRING_VCSC_BUILD_DATE = 18 # R---V + +# +# NV_CTRL_STRING_VCSC_FIRMWARE_VERSION - deprecated +# +# Queries the firmware version of the VCS device. +# +# This attribute must be queried through XNVCTRLQueryTargetStringAttribute() +# using a NV_CTRL_TARGET_TYPE_VCSC target. +# + +NV_CTRL_STRING_VCSC_FIRMWARE_VERSION = 19 # R---V + +# +# NV_CTRL_STRING_VCSC_FIRMWARE_REVISION - deprecated +# +# Queries the firmware revision of the VCS device. +# +# This attribute must be queried through XNVCTRLQueryTargetStringAttribute() +# using a NV_CTRL_TARGET_TYPE_VCS target. +# + +NV_CTRL_STRING_VCSC_FIRMWARE_REVISION = 20 # R---V + +# +# NV_CTRL_STRING_VCSC_HARDWARE_VERSION - deprecated +# +# Queries the hardware version of the VCS device. +# +# This attribute must be queried through XNVCTRLQueryTargetStringAttribute() +# using a NV_CTRL_TARGET_TYPE_VCSC target. +# + +NV_CTRL_STRING_VCSC_HARDWARE_VERSION = 21 # R---V + +# +# NV_CTRL_STRING_VCSC_HARDWARE_REVISION - deprecated +# +# Queries the hardware revision of the VCS device. +# +# This attribute must be queried through XNVCTRLQueryTargetStringAttribute() +# using a NV_CTRL_TARGET_TYPE_VCSC target. +# + +NV_CTRL_STRING_VCSC_HARDWARE_REVISION = 22 # R---V + +# +# NV_CTRL_STRING_MOVE_METAMODE - Moves a MetaMode to the specified +# index location. The MetaMode must already exist in the X Screen's +# list of MetaModes (as returned by the NV_CTRL_BINARY_DATA_METAMODES +# attribute). If the index is larger than the number of MetaModes in +# the list, the MetaMode is moved to the end of the list. The +# MetaMode string should have the same syntax as the MetaMode X +# configuration option, as documented in the NVIDIA driver README. + +# The MetaMode string must be prepended with a comma-separated list +# of "token=value" pairs, separated from the MetaMode string by "::". +# Currently, the only valid token is "index", which indicates where +# in the MetaMode list the MetaMode should be moved to. +# +# Other tokens may be added in the future. +# +# E.g., +# "index=5 :: CRT-0: 1024x768 @1024x768 +0+0" +# + +NV_CTRL_STRING_MOVE_METAMODE = 23 # -W-- + +# +# NV_CTRL_STRING_VALID_HORIZ_SYNC_RANGES - returns the valid +# horizontal sync ranges used to perform mode validation for the +# specified display device. The ranges are in the same format as the +# "HorizSync" X config option: +# +# "horizsync-range may be a comma separated list of either discrete +# values or ranges of values. A range of values is two values +# separated by a dash." +# +# The values are in kHz. +# +# Additionally, the string may be prepended with a comma-separated +# list of "token=value" pairs, separated from the HorizSync string by +# "::". Valid tokens: +# +# Token Value +# "source" "edid" - HorizSync is from the display device's EDID +# "xconfig" - HorizSync is from the "HorizSync" entry in +# the Monitor section of the X config file +# "option" - HorizSync is from the "HorizSync" NVIDIA X +# config option +# "builtin" - HorizSync is from NVIDIA X driver builtin +# default values +# +# Additional tokens and/or values may be added in the future. +# +# Example: "source=edid :: 30.000-62.000" +# + +NV_CTRL_STRING_VALID_HORIZ_SYNC_RANGES = 24 # R-DG + +# +# NV_CTRL_STRING_VALID_VERT_REFRESH_RANGES - returns the valid +# vertical refresh ranges used to perform mode validation for the +# specified display device. The ranges are in the same format as the +# "VertRefresh" X config option: +# +# "vertrefresh-range may be a comma separated list of either discrete +# values or ranges of values. A range of values is two values +# separated by a dash." +# +# The values are in Hz. +# +# Additionally, the string may be prepended with a comma-separated +# list of "token=value" pairs, separated from the VertRefresh string by +# "::". Valid tokens: +# +# Token Value +# "source" "edid" - VertRefresh is from the display device's EDID +# "xconfig" - VertRefresh is from the "VertRefresh" entry in +# the Monitor section of the X config file +# "option" - VertRefresh is from the "VertRefresh" NVIDIA X +# config option +# "builtin" - VertRefresh is from NVIDIA X driver builtin +# default values +# +# Additional tokens and/or values may be added in the future. +# +# Example: "source=edid :: 50.000-75.000" +# + +NV_CTRL_STRING_VALID_VERT_REFRESH_RANGES = 25 # R-DG + +# +# NV_CTRL_STRING_SCREEN_RECTANGLE - returns the physical X Screen's +# initial position and size (in absolute coordinates) within the +# desktop as the "token=value" string: "x=#, y=#, width=#, height=#" +# +# Querying this attribute returns success only when Xinerama is enabled +# or the X server ABI is greater than equal to 12. +# + +NV_CTRL_STRING_SCREEN_RECTANGLE = 26 # R--- + +# +# NV_CTRL_STRING_XINERAMA_SCREEN_INFO - renamed +# +# NV_CTRL_STRING_SCREEN_RECTANGLE should be used instead. +# + +NV_CTRL_STRING_XINERAMA_SCREEN_INFO = 26 # renamed + +# +# NV_CTRL_STRING_TWINVIEW_XINERAMA_INFO_ORDER - used to specify the +# order that display devices will be returned via Xinerama when +# nvidiaXineramaInfo is enabled. Follows the same syntax as the +# nvidiaXineramaInfoOrder X config option. +# + +NV_CTRL_STRING_NVIDIA_XINERAMA_INFO_ORDER = 27 # RW-- + +NV_CTRL_STRING_TWINVIEW_XINERAMA_INFO_ORDER = NV_CTRL_STRING_NVIDIA_XINERAMA_INFO_ORDER # for backwards compatibility: + +# +# NV_CTRL_STRING_SLI_MODE - returns a string describing the current +# SLI mode, if any, or FALSE if SLI is not currently enabled. +# +# This string should be used for informational purposes only, and +# should not be used to distinguish between SLI modes, other than to +# recognize when SLI is disabled (FALSE is returned) or +# enabled (the returned string is non-NULL and describes the current +# SLI configuration). +# + +NV_CTRL_STRING_SLI_MODE = 28 # R---*/ + +# +# NV_CTRL_STRING_PERFORMANCE_MODES - returns a string with all the +# performance modes defined for this GPU along with their associated +# NV Clock and Memory Clock values. +# Not all tokens will be reported on all GPUs, and additional tokens +# may be added in the future. +# For backwards compatibility we still provide nvclock, memclock, and +# processorclock those are the same as nvclockmin, memclockmin and +# processorclockmin. +# +# Note: These clock values take into account the offset +# set by clients through NV_CTRL_GPU_NVCLOCK_OFFSET and +# NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET. +# +# Each performance modes are returned as a comma-separated list of +# "token=value" pairs. Each set of performance mode tokens are separated +# by a ";". Valid tokens: +# +# Token Value +# "perf" integer - the Performance level +# "nvclock" integer - the GPU clocks (in MHz) for the perf level +# "nvclockmin" integer - the GPU clocks min (in MHz) for the perf level +# "nvclockmax" integer - the GPU clocks max (in MHz) for the perf level +# "nvclockeditable" integer - if the GPU clock domain is editable +# for the perf level +# "memclock" integer - the memory clocks (in MHz) for the perf level +# "memclockmin" integer - the memory clocks min (in MHz) for the perf level +# "memclockmax" integer - the memory clocks max (in MHz) for the perf level +# "memclockeditable" integer - if the memory clock domain is editable +# for the perf level +# "memtransferrate" integer - the memory transfer rate (in MHz) +# for the perf level +# "memtransferratemin" integer - the memory transfer rate min (in MHz) +# for the perf level +# "memtransferratemax" integer - the memory transfer rate max (in MHz) +# for the perf level +# "memtransferrateeditable" integer - if the memory transfer rate is editable +# for the perf level +# "processorclock" integer - the processor clocks (in MHz) +# for the perf level +# "processorclockmin" integer - the processor clocks min (in MHz) +# for the perf level +# "processorclockmax" integer - the processor clocks max (in MHz) +# for the perf level +# "processorclockeditable" integer - if the processor clock domain is editable +# for the perf level +# +# Example: +# +# perf=0, nvclock=324, nvclockmin=324, nvclockmax=324, nvclockeditable=0, +# memclock=324, memclockmin=324, memclockmax=324, memclockeditable=0, +# memtransferrate=648, memtransferratemin=648, memtransferratemax=648, +# memtransferrateeditable=0 ; +# perf=1, nvclock=324, nvclockmin=324, nvclockmax=640, nvclockeditable=0, +# memclock=810, memclockmin=810, memclockmax=810, memclockeditable=0, +# memtransferrate=1620, memtransferrate=1620, memtransferrate=1620, +# memtransferrateeditable=0 ; +# +# This attribute may be queried through XNVCTRLQueryTargetStringAttribute() +# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. +# + +NV_CTRL_STRING_PERFORMANCE_MODES = 29 # R--G + +# +# NV_CTRL_STRING_VCSC_FAN_STATUS - deprecated +# +# Returns a string with status of all the fans in the Visual Computing System, +# if such a query is supported. Fan information is reported along with its +# tachometer reading (in RPM) and a flag indicating whether the fan has failed +# or not. +# +# Valid tokens: +# +# Token Value +# "fan" integer - the Fan index +# "speed" integer - the tachometer reading of the fan in rpm +# "fail" integer - flag to indicate whether the fan has failed +# +# Example: +# +# fan=0, speed=694, fail=0 ; fan=1, speed=693, fail=0 +# +# This attribute must be queried through XNVCTRLQueryTargetStringAttribute() +# using a NV_CTRL_TARGET_TYPE_VCSC target. +# +# + +NV_CTRL_STRING_VCSC_FAN_STATUS = 30 # R---V + +# +# NV_CTRL_STRING_VCSC_TEMPERATURES - Deprecated +# +# Returns a string with all Temperature readings in the Visual Computing +# System, if such a query is supported. Intake, Exhaust and Board Temperature +# values are reported in Celcius. +# +# Valid tokens: +# +# Token Value +# "intake" integer - the intake temperature for the VCS +# "exhaust" integer - the exhaust temperature for the VCS +# "board" integer - the board temperature of the VCS +# +# Example: +# +# intake=29, exhaust=46, board=41 +# +# This attribute must be queried through XNVCTRLQueryTargetStringAttribute() +# using a NV_CTRL_TARGET_TYPE_VCSC target. +# +# + +NV_CTRL_STRING_VCSC_TEMPERATURES = 31 # R---V + +# +# NV_CTRL_STRING_VCSC_PSU_INFO - Deprecated +# +# Returns a string with all Power Supply Unit related readings in the Visual +# Computing System, if such a query is supported. Current in amperes, Power +# in watts, Voltage in volts and PSU state may be reported. Not all PSU types +# support all of these values, and therefore some readings may be unknown. +# +# Valid tokens: +# +# Token Value +# "current" integer - the current drawn in amperes by the VCS +# "power" integer - the power drawn in watts by the VCS +# "voltage" integer - the voltage reading of the VCS +# "state" integer - flag to indicate whether PSU is operating normally +# +# Example: +# +# current=10, power=15, voltage=unknown, state=normal +# +# This attribute must be queried through XNVCTRLQueryTargetStringAttribute() +# using a NV_CTRL_TARGET_TYPE_VCSC target. +# +# + + +NV_CTRL_STRING_VCSC_PSU_INFO = 32 # R---V + +# +# NV_CTRL_STRING_GVIO_VIDEO_FORMAT_NAME - query the name for the specified +# NV_CTRL_GVIO_VIDEO_FORMAT_*. So that this can be queried with existing +# interfaces, XNVCTRLQueryStringAttribute() should be used, and the video +# format specified in the display_mask field; eg: +# +# XNVCTRLQueryStringAttribute(dpy, +# screen, +# NV_CTRL_GVIO_VIDEO_FORMAT_720P_60_00_SMPTE296, +# NV_CTRL_GVIO_VIDEO_FORMAT_NAME, +# &name); +# + +NV_CTRL_STRING_GVIO_VIDEO_FORMAT_NAME = 33 # R--GI + +# +# NV_CTRL_STRING_GVO_VIDEO_FORMAT_NAME - renamed +# +# NV_CTRL_STRING_GVIO_VIDEO_FORMAT_NAME should be used instead. +# +NV_CTRL_STRING_GVO_VIDEO_FORMAT_NAME = 33 # renamed + +# +# NV_CTRL_STRING_GPU_CURRENT_CLOCK_FREQS - returns a string with the +# associated NV Clock, Memory Clock and Processor Clock values. +# +# Current valid tokens are "nvclock", "nvclockmin", "nvclockmax", +# "memclock", "memclockmin", "memclockmax", "processorclock", +# "processorclockmin" and "processorclockmax". +# Not all tokens will be reported on all GPUs, and additional tokens +# may be added in the future. +# +# Note: These clock values take into account the offset +# set by clients through NV_CTRL_GPU_NVCLOCK_OFFSET and +# NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET. +# +# Clock values are returned as a comma-separated list of +# "token=value" pairs. +# Valid tokens: +# +# Token Value +# "nvclock" integer - the GPU clocks (in MHz) for the perf level +# "nvclockmin" integer - the GPU clocks min (in MHz) for the perf level +# "nvclockmax" integer - the GPU clocks max (in MHz) for the perf level +# "nvclockeditable" integer - if the GPU clock domain is editable +# for the perf level +# "memclock" integer - the memory clocks (in MHz) for the perf level +# "memclockmin" integer - the memory clocks min (in MHz) for the perf level +# "memclockmax" integer - the memory clocks (max in MHz) for the perf level +# "memclockeditable" integer - if the memory clock domain is editable +# for the perf level +# "memtransferrate" integer - the memory transfer rate (in MHz) +# for the perf level +# "memtransferratemin" integer - the memory transfer rate min (in MHz) +# for the perf level +# "memtransferratemax" integer - the memory transfer rate max (in MHz) +# for the perf level +# "memtransferrateeditable" integer - if the memory transfer rate is editable +# for the perf level +# "processorclock" integer - the processor clocks (in MHz) +# for the perf level +# "processorclockmin" integer - the processor clocks min (in MHz) +# for the perf level +# "processorclockmax" integer - the processor clocks max (in MHz) +# for the perf level +# "processorclockeditable" integer - if the processor clock domain is editable +# for the perf level +# +# Example: +# +# nvclock=324, nvclockmin=324, nvclockmax=324, nvclockeditable=0 +# memclock=324, memclockmin=324, memclockmax=324, memclockeditable=0 +# memtrasferrate=628 +# +# This attribute may be queried through XNVCTRLQueryTargetStringAttribute() +# using an NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. +# + +NV_CTRL_STRING_GPU_CURRENT_CLOCK_FREQS = 34 # RW-G + +# +# NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_HARDWARE_REVISION - Returns the +# hardware revision of the 3D Vision Pro transceiver. +# +NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_HARDWARE_REVISION = 35 # R--T + +# +# NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_FIRMWARE_VERSION_A - Returns the +# firmware version of chip A of the 3D Vision Pro transceiver. +# +NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_FIRMWARE_VERSION_A = 36 # R--T + +# +# NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_FIRMWARE_DATE_A - Returns the +# date of the firmware of chip A of the 3D Vision Pro transceiver. +# +NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_FIRMWARE_DATE_A = 37 # R--T + +# +# NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_FIRMWARE_VERSION_B - Returns the +# firmware version of chip B of the 3D Vision Pro transceiver. +# +NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_FIRMWARE_VERSION_B = 38 # R--T + +# +# NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_FIRMWARE_DATE_B - Returns the +# date of the firmware of chip B of the 3D Vision Pro transceiver. +# +NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_FIRMWARE_DATE_B = 39 # R--T + +# +# NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_ADDRESS - Returns the RF address +# of the 3D Vision Pro transceiver. +# +NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_ADDRESS = 40 # R--T + +# +# NV_CTRL_STRING_3D_VISION_PRO_GLASSES_FIRMWARE_VERSION_A - Returns the +# firmware version of chip A of the glasses. +# Use the display_mask parameter to specify the glasses id. +# +NV_CTRL_STRING_3D_VISION_PRO_GLASSES_FIRMWARE_VERSION_A = 41 # R--T + +# +# NV_CTRL_STRING_3D_VISION_PRO_GLASSES_FIRMWARE_DATE_A - Returns the +# date of the firmware of chip A of the glasses. +# Use the display_mask parameter to specify the glasses id. +# +NV_CTRL_STRING_3D_VISION_PRO_GLASSES_FIRMWARE_DATE_A = 42 # R--T + +# +# NV_CTRL_STRING_3D_VISION_PRO_GLASSES_ADDRESS - Returns the RF address +# of the glasses. +# Use the display_mask parameter to specify the glasses id. +# +NV_CTRL_STRING_3D_VISION_PRO_GLASSES_ADDRESS = 43 # R--T + +# +# NV_CTRL_STRING_3D_VISION_PRO_GLASSES_NAME - Controls the name the +# glasses should use. +# Use the display_mask parameter to specify the glasses id. +# Glasses' name should start and end with an alpha-numeric character. +# +NV_CTRL_STRING_3D_VISION_PRO_GLASSES_NAME = 44 # RW-T + +# +# NV_CTRL_STRING_CURRENT_METAMODE_VERSION_2 - Returns the metamode currently +# being used by the specified X screen. The MetaMode string has the same +# syntax as the MetaMode X configuration option, as documented in the NVIDIA +# driver README. Also, see NV_CTRL_BINARY_DATA_METAMODES_VERSION_2 for more +# details on the base syntax. +# +# The returned string may also be prepended with a comma-separated list of +# "token=value" pairs, separated from the MetaMode string by "::". +# +NV_CTRL_STRING_CURRENT_METAMODE_VERSION_2 = 45 # RW-- + +# +# NV_CTRL_STRING_DISPLAY_NAME_TYPE_BASENAME - Returns a type name for the +# display device ("CRT", "DFP", or "TV"). However, note that the determination +# of the name is based on the protocol through which the X driver communicates +# to the display device. E.g., if the driver communicates using VGA ,then the +# basename is "CRT"; if the driver communicates using TMDS, LVDS, or DP, then +# the name is "DFP". +# +NV_CTRL_STRING_DISPLAY_NAME_TYPE_BASENAME = 46 # R-D- + +# +# NV_CTRL_STRING_DISPLAY_NAME_TYPE_ID - Returns the type-based name + ID for +# the display device, e.g. "CRT-0", "DFP-1", "TV-2". If this device is a +# DisplayPort multistream device, then this name will also be prepended with the +# device's port address like so: "DFP-1.0.1.2.3". See +# NV_CTRL_STRING_DISPLAY_NAME_TYPE_BASENAME for more information about the +# construction of type-based names. +# +NV_CTRL_STRING_DISPLAY_NAME_TYPE_ID = 47 # R-D- + +# +# NV_CTRL_STRING_DISPLAY_NAME_DP_GUID - Returns the GUID of the DisplayPort +# display device. e.g. "DP-GUID-f16a5bde-79f3-11e1-b2ae-8b5a8969ba9c" +# +# The display device must be a DisplayPort 1.2 device. +# +NV_CTRL_STRING_DISPLAY_NAME_DP_GUID = 48 # R-D- + +# +# NV_CTRL_STRING_DISPLAY_NAME_EDID_HASH - Returns the SHA-1 hash of the +# display device's EDID in 8-4-4-4-12 UID format. e.g. +# "DPY-EDID-f16a5bde-79f3-11e1-b2ae-8b5a8969ba9c" +# +# The display device must have a valid EDID. +# +NV_CTRL_STRING_DISPLAY_NAME_EDID_HASH = 49 # R-D- + +# +# NV_CTRL_STRING_DISPLAY_NAME_TARGET_INDEX - Returns the current NV-CONTROL +# target ID (name) of the display device. e.g. "DPY-1", "DPY-4" +# +# This name for the display device is not guarenteed to be the same between +# different runs of the X server. +# +NV_CTRL_STRING_DISPLAY_NAME_TARGET_INDEX = 50 # R-D- + +# +# NV_CTRL_STRING_DISPLAY_NAME_RANDR - Returns the RandR output name for the +# display device. e.g. "VGA-1", "DVI-I-0", "DVI-D-3", "LVDS-1", "DP-2", +# "HDMI-3", "eDP-6". This name should match If this device is a DisplayPort +# 1.2 device, then this name will also be prepended with the device's port +# address like so: "DVI-I-3.0.1.2.3" +# +NV_CTRL_STRING_DISPLAY_NAME_RANDR = 51 # R-D- + +# +# NV_CTRL_STRING_GPU_UUID - Returns the UUID of the given GPU. +# +NV_CTRL_STRING_GPU_UUID = 52 # R--G + +# +# NV_CTRL_STRING_GPU_UTILIZATION - Returns the current percentage usage +# of the various components of the GPU. +# +# Current valid tokens are "graphics", "memory", "video" and "PCIe". +# Not all tokens will be reported on all GPUs, and additional tokens +# may be added in the future. +# +# Utilization values are returned as a comma-separated list of +# "token=value" pairs. +# Valid tokens: +# +# Token Value +# "graphics" integer - the percentage usage of graphics engine. +# "memory" integer - the percentage usage of FB. +# "video" integer - the percentage usage of video engine. +# "PCIe" integer - the percentage usage of PCIe bandwidth. +# +# +# Example: +# +# graphics=45, memory=6, video=0, PCIe=0 +# +# This attribute may be queried through XNVCTRLQueryTargetStringAttribute() +# using an NV_CTRL_TARGET_TYPE_GPU. +# +NV_CTRL_STRING_GPU_UTILIZATION = 53 # R--G + +# +# NV_CTRL_STRING_MULTIGPU_MODE - returns a string describing the current +# MULTIGPU mode, if any, or FALSE if MULTIGPU is not currently enabled. +# +NV_CTRL_STRING_MULTIGPU_MODE = 54 # R--- + +# +# NV_CTRL_STRING_PRIME_OUTPUTS_DATA - returns a semicolon delimited list of +# strings that describe all PRIME configured displays. +# +# ex. "xpos=1920, ypos=0, width=1280, height=1024, screen=0;xpos=3200, +# ypos=0, width=800, height=600, screen=0;" +# +NV_CTRL_STRING_PRIME_OUTPUTS_DATA = 55 # R--- + +NV_CTRL_STRING_LAST_ATTRIBUTE = NV_CTRL_STRING_PRIME_OUTPUTS_DATA + +############################################################################ + +# +# Binary Data Attributes: +# +# Binary data attributes can be queryied through the XNVCTRLQueryBinaryData() +# and XNVCTRLQueryTargetBinaryData() function calls. +# +# There are currently no binary data attributes that can be set. +# +# Unless otherwise noted, all Binary data attributes can be queried +# using an NV_CTRL_TARGET_TYPE_X_SCREEN target. Attributes that cannot take +# an NV_CTRL_TARGET_TYPE_X_SCREEN target also cannot be queried through +# XNVCTRLQueryBinaryData() (Since an X Screen target is assumed). +# + + +# +# NV_CTRL_BINARY_DATA_EDID - Returns a display device's EDID information +# data. +# +# This attribute may be queried through XNVCTRLQueryTargetBinaryData() +# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. +# + +NV_CTRL_BINARY_DATA_EDID = 0 # R-DG + +# +# NV_CTRL_BINARY_DATA_MODELINES - Returns a display device's supported +# ModeLines. ModeLines are returned in a buffer, separated by a single +# '\0' and terminated by two consecutive '\0' s like so: +# +# "ModeLine 1\0ModeLine 2\0ModeLine 3\0Last ModeLine\0\0" +# +# This attribute may be queried through XNVCTRLQueryTargetBinaryData() +# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. +# +# Each ModeLine string may be prepended with a comma-separated list +# of "token=value" pairs, separated from the ModeLine string with a +# "::". Valid tokens: +# +# Token Value +# "source" "xserver" - the ModeLine is from the core X server +# "xconfig" - the ModeLine was specified in the X config file +# "builtin" - the NVIDIA driver provided this builtin ModeLine +# "vesa" - this is a VESA standard ModeLine +# "edid" - the ModeLine was in the display device's EDID +# "nv-control" - the ModeLine was specified via NV-CONTROL +# +# "xconfig-name" - for ModeLines that were specified in the X config +# file, this is the name the X config file +# gave for the ModeLine. +# +# Note that a ModeLine can have several sources; the "source" token +# can appear multiple times in the "token=value" pairs list. +# Additional source values may be specified in the future. +# +# Additional tokens may be added in the future, so it is recommended +# that any token parser processing the returned string from +# NV_CTRL_BINARY_DATA_MODELINES be implemented to gracefully ignore +# unrecognized tokens. +# +# E.g., +# +# "source=xserver, source=vesa, source=edid :: "1024x768_70" 75.0 1024 1048 1184 1328 768 771 777 806 -HSync -VSync" +# "source=xconfig, xconfig-name=1600x1200_60.00 :: "1600x1200_60_0" 161.0 1600 1704 1880 2160 1200 1201 1204 1242 -HSync +VSync" +# + +NV_CTRL_BINARY_DATA_MODELINES = 1 # R-DG + +# +# NV_CTRL_BINARY_DATA_METAMODES - Returns an X Screen's supported +# MetaModes. MetaModes are returned in a buffer separated by a +# single '\0' and terminated by two consecutive '\0' s like so: +# +# "MetaMode 1\0MetaMode 2\0MetaMode 3\0Last MetaMode\0\0" +# +# The MetaMode string should have the same syntax as the MetaMode X +# configuration option, as documented in the NVIDIA driver README. + +# Each MetaMode string may be prepended with a comma-separated list +# of "token=value" pairs, separated from the MetaMode string with +# "::". Currently, valid tokens are: +# +# Token Value +# "id" <number> - the id of this MetaMode; this is stored in +# the Vertical Refresh field, as viewed +# by the XRandR and XF86VidMode X# +# extensions. +# +# "switchable" "yes"/"no" - whether this MetaMode may be switched to via +# ctrl-alt-+/-; Implicit MetaModes (see +# the "IncludeImplicitMetaModes" X +# config option), for example, are not +# normally made available through +# ctrl-alt-+/-. +# +# "source" "xconfig" - the MetaMode was specified in the X +# config file. +# "implicit" - the MetaMode was implicitly added; see the +# "IncludeImplicitMetaModes" X config option +# for details. +# "nv-control" - the MetaMode was added via the NV-CONTROL X +# extension to the currently running X server. +# "RandR" - the MetaMode was modified in response to an +# RandR RRSetCrtcConfig request. +# +# Additional tokens may be added in the future, so it is recommended +# that any token parser processing the returned string from +# NV_CTRL_BINARY_DATA_METAMODES be implemented to gracefully ignore +# unrecognized tokens. +# +# E.g., +# +# "id=50, switchable=yes, source=xconfig :: CRT-0: 1024x768 @1024x768 +0+0" +# + +NV_CTRL_BINARY_DATA_METAMODES = 2 # R-D- +NV_CTRL_BINARY_DATA_METAMODES_VERSION_1 = NV_CTRL_BINARY_DATA_METAMODES + +# +# NV_CTRL_BINARY_DATA_XSCREENS_USING_GPU - Returns the list of X +# screens currently driven by the given GPU. +# +# The format of the returned data is: +# +# 4 CARD32 number of screens +# 4# n CARD32 screen indices +# +# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() +# using a NV_CTRL_TARGET_TYPE_GPU target. This attribute cannot be +# queried using a NV_CTRL_TARGET_TYPE_X_SCREEN. +# + +NV_CTRL_BINARY_DATA_XSCREENS_USING_GPU = 3 # R-DG + +# +# NV_CTRL_BINARY_DATA_GPUS_USED_BY_XSCREEN - Returns the list of GPUs +# currently in use by the given X screen. +# +# The format of the returned data is: +# +# 4 CARD32 number of GPUs +# 4# n CARD32 GPU indices +# + +NV_CTRL_BINARY_DATA_GPUS_USED_BY_XSCREEN = 4 # R--- + +# +# NV_CTRL_BINARY_DATA_GPUS_USING_FRAMELOCK - Returns the list of +# GPUs currently connected to the given frame lock board. +# +# The format of the returned data is: +# +# 4 CARD32 number of GPUs +# 4# n CARD32 GPU indices +# +# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() +# using a NV_CTRL_TARGET_TYPE_FRAMELOCK target. This attribute cannot be +# queried using a NV_CTRL_TARGET_TYPE_X_SCREEN. +# + +NV_CTRL_BINARY_DATA_GPUS_USING_FRAMELOCK = 5 # R-DF + +# +# NV_CTRL_BINARY_DATA_DISPLAY_VIEWPORT - Returns the Display Device's +# viewport box into the given X Screen (in X Screen coordinates.) +# +# The format of the returned data is: +# +# 4 CARD32 Offset X +# 4 CARD32 Offset Y +# 4 CARD32 Width +# 4 CARD32 Height +# + +NV_CTRL_BINARY_DATA_DISPLAY_VIEWPORT = 6 # R-DG + +# +# NV_CTRL_BINARY_DATA_FRAMELOCKS_USED_BY_GPU - Returns the list of +# Framelock devices currently connected to the given GPU. +# +# The format of the returned data is: +# +# 4 CARD32 number of Framelocks +# 4# n CARD32 Framelock indices +# +# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() +# using a NV_CTRL_TARGET_TYPE_GPU target. This attribute cannot be +# queried using a NV_CTRL_TARGET_TYPE_X_SCREEN. +# + +NV_CTRL_BINARY_DATA_FRAMELOCKS_USED_BY_GPU = 7 # R-DG + +# +# NV_CTRL_BINARY_DATA_GPUS_USING_VCSC - Deprecated +# +# Returns the list of GPU devices connected to the given VCS. +# +# The format of the returned data is: +# +# 4 CARD32 number of GPUs +# 4# n CARD32 GPU indices +# +# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() +# using a NV_CTRL_TARGET_TYPE_VCSC target. This attribute cannot be +# queried using a NV_CTRL_TARGET_TYPE_X_SCREEN and cannot be queried using +# a NV_CTRL_TARGET_TYPE_X_GPU +# + +NV_CTRL_BINARY_DATA_GPUS_USING_VCSC = 8 # R-DV + +# +# NV_CTRL_BINARY_DATA_VCSCS_USED_BY_GPU - Deprecated +# +# Returns the VCSC device that is controlling the given GPU. +# +# The format of the returned data is: +# +# 4 CARD32 number of VCS (always 1) +# 4# n CARD32 VCS indices +# +# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() +# using a NV_CTRL_TARGET_TYPE_GPU target. This attribute cannot be +# queried using a NV_CTRL_TARGET_TYPE_X_SCREEN +# + +NV_CTRL_BINARY_DATA_VCSCS_USED_BY_GPU = 9 # R-DG + +# +# NV_CTRL_BINARY_DATA_COOLERS_USED_BY_GPU - Returns the coolers that +# are cooling the given GPU. +# +# The format of the returned data is: +# +# 4 CARD32 number of COOLER +# 4# n CARD32 COOLER indices +# +# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() +# using a NV_CTRL_TARGET_TYPE_GPU target. This attribute cannot be +# queried using a NV_CTRL_TARGET_TYPE_X_SCREEN +# + +NV_CTRL_BINARY_DATA_COOLERS_USED_BY_GPU = 10 # R-DG + +# +# NV_CTRL_BINARY_DATA_GPUS_USED_BY_LOGICAL_XSCREEN - Returns the list of +# GPUs currently driving the given X screen. If Xinerama is enabled, this +# will return all GPUs that are driving any X screen. +# +# The format of the returned data is: +# +# 4 CARD32 number of GPUs +# 4# n CARD32 GPU indices +# + +NV_CTRL_BINARY_DATA_GPUS_USED_BY_LOGICAL_XSCREEN = 11 # R--- + +# +# NV_CTRL_BINARY_DATA_THERMAL_SENSORS_USED_BY_GPU - Returns the sensors that +# are attached to the given GPU. +# +# The format of the returned data is: +# +# 4 CARD32 number of SENSOR +# 4# n CARD32 SENSOR indices +# +# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() +# using a NV_CTRL_TARGET_TYPE_GPU target. This attribute cannot be +# queried using a NV_CTRL_TARGET_TYPE_X_SCREEN +# + +NV_CTRL_BINARY_DATA_THERMAL_SENSORS_USED_BY_GPU = 12 # R--G + +# +# NV_CTRL_BINARY_DATA_GLASSES_PAIRED_TO_3D_VISION_PRO_TRANSCEIVER - Returns +# the id of the glasses that are currently paired to the given +# 3D Vision Pro transceiver. +# +# The format of the returned data is: +# +# 4 CARD32 number of glasses +# 4# n CARD32 id of glasses +# +# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() +# using a NV_CTRL_TARGET_TYPE_3D_VISION_PRO_TRANSCEIVER target. +# +NV_CTRL_BINARY_DATA_GLASSES_PAIRED_TO_3D_VISION_PRO_TRANSCEIVER = 13 # R--T + +# +# NV_CTRL_BINARY_DATA_DISPLAY_TARGETS - Returns all the display devices +# currently connected to any GPU on the X server. +# +# The format of the returned data is: +# +# 4 CARD32 number of display devices +# 4# n CARD32 display device indices +# +# This attribute can only be queried through XNVCTRLQueryTargetBinaryData(). +# + +NV_CTRL_BINARY_DATA_DISPLAY_TARGETS = 14 # R--- + +# +# NV_CTRL_BINARY_DATA_DISPLAYS_CONNECTED_TO_GPU - Returns the list of +# display devices that are connected to the GPU target. +# +# The format of the returned data is: +# +# 4 CARD32 number of display devices +# 4# n CARD32 display device indices +# +# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() +# using a NV_CTRL_TARGET_TYPE_GPU target. +# + +NV_CTRL_BINARY_DATA_DISPLAYS_CONNECTED_TO_GPU = 15 # R--G + +# +# NV_CTRL_BINARY_DATA_METAMODES_VERSION_2 - Returns values similar to +# NV_CTRL_BINARY_DATA_METAMODES(_VERSION_1) but also returns extended syntax +# information to indicate a specific display device, as well as other per- +# display deviceflags as "token=value" pairs. For example: +# +# "DPY-1: 1280x1024 {Stereo=PassiveLeft}, +# DPY-2: 1280x1024 {Stereo=PassiveRight}," +# +# The display device names have the form "DPY-%d", where the integer +# part of the name is the NV-CONTROL target ID for that display device +# for this instance of the X server. Note that display device NV-CONTROL +# target IDs are not guaranteed to be the same from one run of the X +# server to the next. +# + +NV_CTRL_BINARY_DATA_METAMODES_VERSION_2 = 16 # R-D- + +# +# NV_CTRL_BINARY_DATA_DISPLAYS_ENABLED_ON_XSCREEN - Returns the list of +# display devices that are currently scanning out the X screen target. +# +# The format of the returned data is: +# +# 4 CARD32 number of display devices +# 4# n CARD32 display device indices +# +# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() +# using a NV_CTRL_TARGET_TYPE_X_SCREEN target. +# + +NV_CTRL_BINARY_DATA_DISPLAYS_ENABLED_ON_XSCREEN = 17 # R--- + +# +# NV_CTRL_BINARY_DATA_DISPLAYS_ASSIGNED_TO_XSCREEN - Returns the list of +# display devices that are currently assigned the X screen target. +# +# The format of the returned data is: +# +# 4 CARD32 number of display devices +# 4# n CARD32 display device indices +# +# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() +# using a NV_CTRL_TARGET_TYPE_X_SCREEN target. +# + +NV_CTRL_BINARY_DATA_DISPLAYS_ASSIGNED_TO_XSCREEN = 18 # R--- + +# +# NV_CTRL_BINARY_DATA_GPU_FLAGS - Returns a list of flags for the +# given GPU. A flag can, for instance, be a capability which enables +# or disables some features according to the GPU state. +# +# The format of the returned data is: +# +# 4 CARD32 number of GPU flags +# 4# n CARD32 GPU flag +# +# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() +# using a NV_CTRL_TARGET_TYPE_GPU target. +# +NV_CTRL_BINARY_DATA_GPU_FLAGS = 19 # R--- + +# Stereo and display composition transformations are mutually exclusive. +NV_CTRL_BINARY_DATA_GPU_FLAGS_STEREO_DISPLAY_TRANSFORM_EXCLUSIVE = 0 +# Overlay and display composition transformations are mutually exclusive. +NV_CTRL_BINARY_DATA_GPU_FLAGS_OVERLAY_DISPLAY_TRANSFORM_EXCLUSIVE = 1 +# Depth 8 and display composition transformations are mutually exclusive. +NV_CTRL_BINARY_DATA_GPU_FLAGS_DEPTH_8_DISPLAY_TRANSFORM_EXCLUSIVE = 2 + +# +# NV_CTRL_BINARY_DATA_DISPLAYS_ON_GPU - Returns the list of valid +# display devices that can be connected to the GPU target. +# +# The format of the returned data is: +# +# 4 CARD32 number of display devices +# 4# n CARD32 display device indices +# +# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() +# using a NV_CTRL_TARGET_TYPE_GPU target. +# + +NV_CTRL_BINARY_DATA_DISPLAYS_ON_GPU = 20 # R--G + +NV_CTRL_BINARY_DATA_LAST_ATTRIBUTE = NV_CTRL_BINARY_DATA_DISPLAYS_ON_GPU + +############################################################################ + +# +# String Operation Attributes: +# +# These attributes are used with the XNVCTRLStringOperation() +# function; a string is specified as input, and a string is returned +# as output. +# +# Unless otherwise noted, all attributes can be operated upon using +# an NV_CTRL_TARGET_TYPE_X_SCREEN target. +# + + +# +# NV_CTRL_STRING_OPERATION_ADD_METAMODE - provide a MetaMode string +# as input, and returns a string containing comma-separated list of +# "token=value" pairs as output. Currently, the only output token is +# "id", which indicates the id that was assigned to the MetaMode. +# +# All ModeLines referenced in the MetaMode must already exist for +# each display device (as returned by the +# NV_CTRL_BINARY_DATA_MODELINES attribute). +# +# The MetaMode string should have the same syntax as the MetaMode X +# configuration option, as documented in the NVIDIA driver README. +# +# The input string can optionally be prepended with a string of +# comma-separated "token=value" pairs, separated from the MetaMode +# string by "::". Currently, the only valid token is "index" which +# indicates the insertion index for the MetaMode. +# +# E.g., +# +# Input: "index=5 :: 1600x1200+0+0, 1600x1200+1600+0" +# Output: "id=58" +# +# which causes the MetaMode to be inserted at position 5 in the +# MetaMode list (all entries after 5 will be shifted down one slot in +# the list), and the X server's containing mode stores 58 as the +# VRefresh, so that the MetaMode can be uniquely identifed through +# XRandR and XF86VidMode. +# + +NV_CTRL_STRING_OPERATION_ADD_METAMODE = 0 # ---- + +# +# NV_CTRL_STRING_OPERATION_GTF_MODELINE - provide as input a string +# of comma-separated "token=value" pairs, and returns a ModeLine +# string, computed using the GTF formula using the parameters from +# the input string. Valid tokens for the input string are "width", +# "height", and "refreshrate". +# +# E.g., +# +# Input: "width=1600, height=1200, refreshrate=60" +# Output: "160.96 1600 1704 1880 2160 1200 1201 1204 1242 -HSync +VSync" +# +# This operation does not have any impact on any display device's +# modePool, and the ModeLine is not validated; it is simply intended +# for generating ModeLines. +# + +NV_CTRL_STRING_OPERATION_GTF_MODELINE = 1 # --- + +# +# NV_CTRL_STRING_OPERATION_CVT_MODELINE - provide as input a string +# of comma-separated "token=value" pairs, and returns a ModeLine +# string, computed using the CVT formula using the parameters from +# the input string. Valid tokens for the input string are "width", +# "height", "refreshrate", and "reduced-blanking". The +# "reduced-blanking" argument can be "0" or "1", to enable or disable +# use of reduced blanking for the CVT formula. +# +# E.g., +# +# Input: "width=1600, height=1200, refreshrate=60, reduced-blanking=1" +# Output: "130.25 1600 1648 1680 1760 1200 1203 1207 1235 +HSync -VSync" +# +# This operation does not have any impact on any display device's +# modePool, and the ModeLine is not validated; it is simply intended +# for generating ModeLines. +# + +NV_CTRL_STRING_OPERATION_CVT_MODELINE = 2 # --- + +# +# NV_CTRL_STRING_OPERATION_BUILD_MODEPOOL - build a ModePool for the +# specified display device on the specified target (either an X +# screen or a GPU). This is typically used to generate a ModePool +# for a display device on a GPU on which no X screens are present. +# +# Currently, a display device's ModePool is static for the life of +# the X server, so XNVCTRLStringOperation will return FALSE if +# requested to build a ModePool on a display device that already has +# a ModePool. +# +# The string input to BUILD_MODEPOOL may be NULL. If it is not NULL, +# then it is interpreted as a double-colon ("::") separated list +# of "option=value" pairs, where the options and the syntax of their +# values are the X configuration options that impact the behavior of +# modePool construction; namely: +# +# "ModeValidation" +# "HorizSync" +# "VertRefresh" +# "FlatPanelProperties" +# "ExactModeTimingsDVI" +# "UseEdidFreqs" +# +# An example input string might look like: +# +# "ModeValidation=NoVesaModes :: HorizSync=50-110 :: VertRefresh=50-150" +# +# This request currently does not return a string. +# + +NV_CTRL_STRING_OPERATION_BUILD_MODEPOOL = 3 # DG + +# +# NV_CTRL_STRING_OPERATION_GVI_CONFIGURE_STREAMS - Configure the streams- +# to-jack+channel topology for a GVI (Graphics capture board). +# +# The string input to GVI_CONFIGURE_STREAMS may be NULL. If this is the +# case, then the current topology is returned. +# +# If the input string to GVI_CONFIGURE_STREAMS is not NULL, the string +# is interpreted as a semicolon (";") separated list of comma-separated +# lists of "option=value" pairs that define a stream's composition. The +# available options and their values are: +# +# "stream": Defines which stream this comma-separated list describes. +# Valid values are the integers between 0 and +# NV_CTRL_GVI_NUM_STREAMS-1 (inclusive). +# +# "linkN": Defines a jack+channel pair to use for the given link N. +# Valid options are the string "linkN", where N is an integer +# between 0 and NV_CTRL_GVI_MAX_LINKS_PER_STREAM-1 (inclusive). +# Valid values for these options are strings of the form +# "jackX" and/or "jackX.Y", where X is an integer between 0 and +# NV_CTRL_GVI_NUM_JACKS-1 (inclusive), and Y (optional) is an +# integer between 0 and NV_CTRL_GVI_MAX_CHANNELS_PER_JACK-1 +# (inclusive). +# +# An example input string might look like: +# +# "stream=0, link0=jack0, link1=jack1; stream=1, link0=jack2.1" +# +# This example specifies two streams, stream 0 and stream 1. Stream 0 +# is defined to capture link0 data from the first channel (channel 0) of +# BNC jack 0 and link1 data from the first channel of BNC jack 1. The +# second stream (Stream 1) is defined to capture link0 data from channel 1 +# (second channel) of BNC jack 2. +# +# This example shows a possible configuration for capturing 3G input: +# +# "stream=0, link0=jack0.0, link1=jack0.1" +# +# Applications should query the following attributes to determine +# possible combinations: +# +# NV_CTRL_GVI_MAX_STREAMS +# NV_CTRL_GVI_MAX_LINKS_PER_STREAM +# NV_CTRL_GVI_NUM_JACKS +# NV_CTRL_GVI_MAX_CHANNELS_PER_JACK +# +# Note: A jack+channel pair can only be tied to one link/stream. +# +# Upon successful configuration or querying of this attribute, a string +# representing the current topology for all known streams on the device +# will be returned. On failure, NULL is returned. +# +# Note: Setting this attribute may also result in the following +# NV-CONTROL attributes being reset on the GVI device (to ensure +# the configuration remains valid): +# NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT +# NV_CTRL_GVI_REQUESTED_STREAM_BITS_PER_COMPONENT +# NV_CTRL_GVI_REQUESTED_STREAM_COMPONENT_SAMPLING +# + +NV_CTRL_STRING_OPERATION_GVI_CONFIGURE_STREAMS = 4 # RW-I + +# +# NV_CTRL_STRING_OPERATION_PARSE_METAMODE - Parses the given MetaMode string +# and returns the validated MetaMode string - possibly re-calculating various +# values such as ViewPortIn. If the MetaMode matches an existing MetaMode, +# the details of the existing MetaMode are returned. If the MetaMode fails to +# be parsed, NULL is returned. +# +NV_CTRL_STRING_OPERATION_PARSE_METAMODE = 5 # R--- + +NV_CTRL_STRING_OPERATION_LAST_ATTRIBUTE = NV_CTRL_STRING_OPERATION_PARSE_METAMODE + +############################################################################### +# NV-CONTROL major op numbers. these constants identify the request type +# +X_nvCtrlQueryExtension = 0 +X_nvCtrlQueryAttribute = 2 +X_nvCtrlQueryStringAttribute = 4 +X_nvCtrlQueryValidAttributeValues = 5 +X_nvCtrlSetStringAttribute = 9 +X_nvCtrlSetAttributeAndGetStatus = 19 +X_nvCtrlQueryBinaryData = 20 +X_nvCtrlQueryTargetCount = 24 +X_nvCtrlStringOperation = 25 + +############################################################################### +# various lists that go with attrs, but are handled more compactly +# this way. these lists are indexed by the possible values of their attrs +# and are explained in NVCtrl.h +# + +ATTRIBUTE_TYPE_UNKNOWN = 0 +ATTRIBUTE_TYPE_INTEGER = 1 +ATTRIBUTE_TYPE_BITMASK = 2 +ATTRIBUTE_TYPE_BOOL = 3 +ATTRIBUTE_TYPE_RANGE = 4 +ATTRIBUTE_TYPE_INT_BITS = 5 + +ATTRIBUTE_TYPE_READ = 0x01 +ATTRIBUTE_TYPE_WRITE = 0x02 +ATTRIBUTE_TYPE_DISPLAY = 0x04 +ATTRIBUTE_TYPE_GPU = 0x08 +ATTRIBUTE_TYPE_FRAMELOCK = 0x10 +ATTRIBUTE_TYPE_X_SCREEN = 0x20 +ATTRIBUTE_TYPE_XINERAMA = 0x40 +ATTRIBUTE_TYPE_VCSC = 0x80 + +############################################################################ + +# +# Attribute Targets +# +# Targets define attribute groups. For example, some attributes are only +# valid to set on a GPU, others are only valid when talking about an +# X Screen. Target types are then what is used to identify the target +# group of the attribute you wish to set/query. +# +# Here are the supported target types: +# + +NV_CTRL_TARGET_TYPE_X_SCREEN = 0 +NV_CTRL_TARGET_TYPE_GPU = 1 +NV_CTRL_TARGET_TYPE_FRAMELOCK = 2 +# Visual Computing System - deprecated. To be removed along with all +# VCS-specific attributes in a later release. +NV_CTRL_TARGET_TYPE_VCSC = 3 +NV_CTRL_TARGET_TYPE_GVI = 4 +NV_CTRL_TARGET_TYPE_COOLER = 5 # e.g., fan +NV_CTRL_TARGET_TYPE_THERMAL_SENSOR = 6 +NV_CTRL_TARGET_TYPE_3D_VISION_PRO_TRANSCEIVER = 7 +NV_CTRL_TARGET_TYPE_DISPLAY = 8 + + +############################################################################### +# Targets, to indicate where a command should be executed. +# +class Target(object): + def __init__(self): + self._id = -1 + self._type = -1 + self._name = '' + + def id(self): + return self._id + + def type(self): + return self._type + + def __str__(self): + return '<nVidia {} #{}>'.format(self._name, self.id()) + + +class Gpu(Target): + def __init__(self, ngpu=0): + """Target a GPU""" + super(self.__class__, self).__init__() + self._id = ngpu + self._type = NV_CTRL_TARGET_TYPE_GPU + self._name = 'GPU' + + +class Screen(Target): + def __init__(self, nscr=0): + """Target an X screen""" + super(self.__class__, self).__init__() + self._id = nscr + self._type = NV_CTRL_TARGET_TYPE_X_SCREEN + self._name = 'X screen' + + +class Cooler(Target): + def __init__(self, nfan=0): + """Target a fann""" + super(self.__class__, self).__init__() + self._id = nfan + self._type = NV_CTRL_TARGET_TYPE_COOLER + self._name = 'Cooler' + + +class NVCtrlQueryTargetCountReplyRequest(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(X_nvCtrlQueryTargetCount), + rq.RequestLength(), + rq.Card32('target_type'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('padb1'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('count'), + rq.Card32('pad4'), + rq.Card32('pad5'), + rq.Card32('pad6'), + rq.Card32('pad7'), + rq.Card32('pad8'), + ) + + +class NVCtrlQueryAttributeReplyRequest(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(X_nvCtrlQueryAttribute), + rq.RequestLength(), + rq.Card16('target_id'), + rq.Card16('target_type'), + rq.Card32('display_mask'), + rq.Card32('attr'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('pad0'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('flags'), + rq.Int32('value'), + rq.Card32('pad4'), + rq.Card32('pad5'), + rq.Card32('pad6'), + rq.Card32('pad7'), + ) + + +class NVCtrlSetAttributeAndGetStatusReplyRequest(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(X_nvCtrlSetAttributeAndGetStatus), + rq.RequestLength(), + rq.Card16('target_id'), + rq.Card16('target_type'), + rq.Card32('display_mask'), + rq.Card32('attr'), + rq.Int32('value') + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('pad0'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('flags'), + rq.Card32('pad3'), + rq.Card32('pad4'), + rq.Card32('pad5'), + rq.Card32('pad6'), + rq.Card32('pad7'), + ) + + +class NVCtrlQueryStringAttributeReplyRequest(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(X_nvCtrlQueryStringAttribute), + rq.RequestLength(), + rq.Card16('target_id'), + rq.Card16('target_type'), + rq.Card32('display_mask'), + rq.Card32('attr'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('pad0'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('flags'), + rq.Card32('string', 4), + rq.Card32('pad4'), + rq.Card32('pad5'), + rq.Card32('pad6'), + rq.Card32('pad7'), + rq.String8('string'), + ) + + +class NVCtrlQueryValidAttributeValuesReplyRequest(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(X_nvCtrlQueryValidAttributeValues), + rq.RequestLength(), + rq.Card16('target_id'), + rq.Card16('target_type'), + rq.Card32('display_mask'), + rq.Card32('attr'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('pad0'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('flags'), + rq.Int32('attr_type'), + rq.Int32('min'), + rq.Int32('max'), + rq.Card32('bits'), + rq.Card32('perms'), + ) + + +class NVCtrlQueryBinaryDataReplyRequest(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(X_nvCtrlQueryBinaryData), + rq.RequestLength(), + rq.Card16('target_id'), + rq.Card16('target_type'), + rq.Card32('display_mask'), + rq.Card32('attr'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('pad0'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('flags'), + rq.Card32('data', 4), + rq.Card32('pad4'), + rq.Card32('pad5'), + rq.Card32('pad6'), + rq.Card32('pad7'), + rq.Binary('data'), + ) + + +class NVCtrlQueryListCard32ReplyRequest(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(X_nvCtrlQueryBinaryData), + rq.RequestLength(), + rq.Card16('target_id'), + rq.Card16('target_type'), + rq.Card32('display_mask'), + rq.Card32('attr'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('pad0'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('flags'), + rq.Card32('list', 4), + rq.Card32('pad4'), + rq.Card32('pad5'), + rq.Card32('pad6'), + rq.Card32('pad7'), + rq.List('list', rq.Card32), + ) diff --git a/Nagstamon/thirdparty/Xlib/ext/randr.py b/Nagstamon/thirdparty/Xlib/ext/randr.py index c06e06036..256e3deee 100644 --- a/Nagstamon/thirdparty/Xlib/ext/randr.py +++ b/Nagstamon/thirdparty/Xlib/ext/randr.py @@ -2,33 +2,39 @@ # # Copyright (C) 2006 Mike Meyer <mwm@mired.org> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA """RandR - provide access to the RandR extension information. -This implementation is based off version 1.3 of the XRandR protocol, and may +This implementation is based off version 1.5 of the XRandR protocol, and may not be compatible with other versions. -Version 1.2 of the protocol is documented at: +Version 1.5 of the protocol is documented at: http://cgit.freedesktop.org/xorg/proto/randrproto/tree/randrproto.txt +Version 1.3.1 here: +http://www.x.org/releases/X11R7.5/doc/randrproto/randrproto.txt + """ +from tkinter import W from Xlib import X from Xlib.protocol import rq, structs @@ -117,6 +123,12 @@ BadRRCrtc = 1 BadRRMode = 2 +# Error classes # +class BadRROutputError(Exception): pass + +class BadRRCrtcError(Exception): pass + +class BadRRModeError(Exception): pass # Data Structures # @@ -163,6 +175,19 @@ rq.Card32('matrix33'), ) +MonitorInfo = rq.Struct( + rq.Card32('name'), + rq.Bool('primary'), + rq.Bool('automatic'), + rq.LengthOf('crtcs', 2), + rq.Int16('x'), + rq.Int16('y'), + rq.Card16('width_in_pixels'), + rq.Card16('height_in_pixels'), + rq.Card32('width_in_millimeters'), + rq.Card32('height_in_millimeters'), + rq.List('crtcs', rq.Card32Obj) +) # Requests # @@ -192,7 +217,7 @@ def query_version(self): display=self.display, opcode=self.display.get_extension_major(extname), major_version=1, - minor_version=3, + minor_version=5, ) @@ -444,12 +469,11 @@ class GetOutputInfo(rq.ReplyRequest): rq.Card8('subpixel_order'), rq.LengthOf('crtcs', 2), rq.LengthOf('modes', 2), - rq.LengthOf('preferred', 2), + rq.Card16('num_preferred'), rq.LengthOf('clones', 2), rq.LengthOf('name', 2), rq.List('crtcs', rq.Card32Obj), rq.List('modes', rq.Card32Obj), - rq.List('preferred', rq.Card32Obj), rq.List('clones', rq.Card32Obj), rq.String8('name'), ) @@ -551,19 +575,18 @@ class ChangeOutputProperty(rq.Request): rq.Card8('mode'), rq.Pad(2), rq.LengthOf('value', 4), - rq.List('value', rq.Card8Obj), + rq.PropertyData('value'), ) -def change_output_property(self, output, property, type, format, mode, nUnits): +def change_output_property(self, output, property, type, mode, value): return ChangeOutputProperty( display=self.display, opcode=self.display.get_extension_major(extname), output=output, property=property, type=type, - format=format, mode=mode, - nUnits=nUnits, + value=value, ) @@ -611,15 +634,17 @@ class GetOutputProperty(rq.ReplyRequest): rq.List('value', rq.Card8Obj), ) -def get_output_property(self, output, property, type, longOffset, longLength): +def get_output_property(self, output, property, type, long_offset, long_length, delete=False, pending=False): return GetOutputProperty( display=self.display, opcode=self.display.get_extension_major(extname), output=output, property=property, type=type, - longOffset=longOffset, - longLength=longLength, + long_offset=long_offset, + long_length=long_length, + delete=delete, + pending=pending, ) @@ -641,11 +666,13 @@ class CreateMode(rq.ReplyRequest): rq.Pad(20), ) -def create_mode(self): +def create_mode(self, mode, name): return CreateMode ( display=self.display, opcode=self.display.get_extension_major(extname), window=self, + mode=mode, + name=name, ) @@ -674,7 +701,7 @@ class AddOutputMode(rq.Request): rq.Card32('mode'), ) -def add_output_mode(self): +def add_output_mode(self, output, mode): return AddOutputMode( display=self.display, opcode=self.display.get_extension_major(extname), @@ -692,7 +719,7 @@ class DeleteOutputMode(rq.Request): rq.Card32('mode'), ) -def delete_output_mode(self): +def delete_output_mode(self, output, mode): return DeleteOutputMode( display=self.display, opcode=self.display.get_extension_major(extname), @@ -715,6 +742,8 @@ class GetCrtcInfo(rq.ReplyRequest): rq.Card16('sequence_number'), rq.ReplyLength(), rq.Card32('timestamp'), + rq.Int16('x'), + rq.Int16('y'), rq.Card16('width'), rq.Card16('height'), rq.Card32('mode'), @@ -759,14 +788,17 @@ class SetCrtcConfig(rq.ReplyRequest): rq.Pad(20), ) -def set_crtc_config(self, crtc, config_timestamp, mode, rotation, timestamp=X.CurrentTime): +def set_crtc_config(self, crtc, config_timestamp, x, y, mode, rotation, outputs, timestamp=X.CurrentTime): return SetCrtcConfig ( display=self.display, opcode=self.display.get_extension_major(extname), crtc=crtc, config_timestamp=config_timestamp, + x=x, + y=y, mode=mode, rotation=rotation, + outputs=outputs, timestamp=timestamp, ) @@ -807,7 +839,7 @@ class GetCrtcGamma(rq.ReplyRequest): rq.Card8('status'), rq.Card16('sequence_number'), rq.ReplyLength(), - rq.Card16('size'), + rq.LengthOf(('red', 'green', 'blue'), 2), rq.Pad(22), rq.List('red', rq.Card16Obj), rq.List('green', rq.Card16Obj), @@ -835,12 +867,15 @@ class SetCrtcGamma(rq.Request): rq.List('blue', rq.Card16Obj), ) -def set_crtc_gamma(self, crtc, size): +def set_crtc_gamma(self, crtc, size, red, green, blue): return SetCrtcGamma( display=self.display, opcode=self.display.get_extension_major(extname), crtc=crtc, size=size, + red=red, + green=green, + blue=blue, ) @@ -1063,6 +1098,76 @@ def get_output_primary(self): ) +# Version 1.5 methods + +class GetMonitors(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(42), + rq.RequestLength(), + rq.Window('window'), + rq.Bool('is_active'), + rq.Pad(3) + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('timestamp'), + rq.LengthOf('monitors', 4), + rq.Card32('outputs'), + rq.Pad(12), + rq.List('monitors', MonitorInfo) + ) + + +def get_monitors(self, is_active=True): + return GetMonitors( + display=self.display, + opcode=self.display.get_extension_major(extname), + window=self, + is_active=is_active + ) + +class SetMonitor(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(43), + rq.RequestLength(), + rq.Window('window'), + rq.Object('monitor_info', MonitorInfo) + ) + + +def set_monitor(self, monitor_info): + return SetMonitor( + display=self.display, + opcode=self.display.get_extension_major(extname), + window=self, + monitor_info=monitor_info + ) + + +class DeleteMonitor(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(44), + rq.RequestLength(), + rq.Window('window'), + rq.Card32('name') + ) + + +def delete_monitor(self, name): + return DeleteMonitor( + display=self.display, + opcode=self.display.get_extension_major(extname), + window=self, + name=name + ) + # Events # class ScreenChangeNotify(rq.Event): @@ -1134,8 +1239,6 @@ class OutputPropertyNotify(rq.Event): rq.Card8('state'), rq.Pad(11), ) - - # Initialization # def init(disp, info): @@ -1171,11 +1274,20 @@ def init(disp, info): disp.extension_add_method('display', 'xrandr_get_panning', get_panning) disp.extension_add_method('display', 'xrandr_set_panning', set_panning) - disp.extension_add_event(info.first_event, ScreenChangeNotify) - disp.extension_add_event(info.first_event + 1, CrtcChangeNotify) - disp.extension_add_event(info.first_event + 2, OutputChangeNotify) - disp.extension_add_event(info.first_event + 3, OutputPropertyNotify) - - #disp.extension_add_error(BadRROutput, BadRROutputError) - #disp.extension_add_error(BadRRCrtc, BadRRCrtcError) - #disp.extension_add_error(BadRRMode, BadRRModeError) + # If the server is running RANDR 1.5+, enable 1.5 compatible methods and events + version = query_version(disp) + if version.major_version == 1 and version.minor_version >= 5: + # version 1.5 compatible + disp.extension_add_method('window', 'xrandr_get_monitors', get_monitors) + disp.extension_add_method('window', 'xrandr_set_monitor', set_monitor) + disp.extension_add_method('window', 'xrandr_delete_monitor', delete_monitor) + + disp.extension_add_event(info.first_event + RRScreenChangeNotify, ScreenChangeNotify) + # add RRNotify events (1 event code with 3 subcodes) + disp.extension_add_subevent(info.first_event + RRNotify, RRNotify_CrtcChange, CrtcChangeNotify) + disp.extension_add_subevent(info.first_event + RRNotify, RRNotify_OutputChange, OutputChangeNotify) + disp.extension_add_subevent(info.first_event + RRNotify, RRNotify_OutputProperty, OutputPropertyNotify) + + disp.extension_add_error(BadRROutput, BadRROutputError) + disp.extension_add_error(BadRRCrtc, BadRRCrtcError) + disp.extension_add_error(BadRRMode, BadRRModeError) diff --git a/Nagstamon/thirdparty/Xlib/ext/record.py b/Nagstamon/thirdparty/Xlib/ext/record.py index 3c3f164d1..bb53ec191 100644 --- a/Nagstamon/thirdparty/Xlib/ext/record.py +++ b/Nagstamon/thirdparty/Xlib/ext/record.py @@ -2,19 +2,22 @@ # # Copyright (C) 2006 Alex Badea <vamposdecampos@gmail.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA from Xlib import X from Xlib.protocol import rq @@ -43,8 +46,10 @@ rq.Card16('first'), rq.Card16('last')) Record_ExtRange = rq.Struct( - rq.Object('major_range', Record_Range8), - rq.Object('minor_range', Record_Range16)) + rq.Card8('major_range_first'), + rq.Card8('major_range_last'), + rq.Card16('minor_range_first'), + rq.Card16('minor_range_last')) Record_Range = rq.Struct( rq.Object('core_requests', Record_Range8), rq.Object('core_replies', Record_Range8), diff --git a/Nagstamon/thirdparty/Xlib/ext/res.py b/Nagstamon/thirdparty/Xlib/ext/res.py new file mode 100644 index 000000000..f2c4e9feb --- /dev/null +++ b/Nagstamon/thirdparty/Xlib/ext/res.py @@ -0,0 +1,288 @@ +# Xlib.ext.res -- X-Resource extension module +# +# Copyright (C) 2021 Aleksei Bavshin <alebastr89@gmail.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 51 Franklin Street, +# Fifth Floor, +# Boston, MA 02110-1301 USA + +"""X-Resource extension allows a client to query the X server about its usage +of various resources. + +For detailed description see any of the following documents. +Protocol specification: + https://www.x.org/releases/current/doc/resourceproto/resproto.txt +XCB Protocol specification: + https://cgit.freedesktop.org/xcb/proto/tree/src/res.xml +""" +from Xlib.protocol import rq + +RES_MAJOR_VERSION = 1 +RES_MINOR_VERSION = 2 + +extname = "X-Resource" + +# v1.0 +ResQueryVersion = 0 +ResQueryClients = 1 +ResQueryClientResources = 2 +ResQueryClientPixmapBytes = 3 +# v1.2 +ResQueryClientIds = 4 +ResQueryResourceBytes = 5 + + +class QueryVersion(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8("opcode"), + rq.Opcode(ResQueryVersion), + rq.RequestLength(), + rq.Card8("client_major"), + rq.Card8("client_minor"), + rq.Pad(2)) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16("sequence_number"), + rq.ReplyLength(), + rq.Card16("server_major"), + rq.Card16("server_minor"), + rq.Pad(20)) + + +def query_version(self, client_major=RES_MAJOR_VERSION, + client_minor=RES_MINOR_VERSION): + """ Query the protocol version supported by the X server. + + The client sends the highest supported version to the server and the + server sends the highest version it supports, but no higher than the + requested version.""" + return QueryVersion( + display=self.display, + opcode=self.display.get_extension_major(extname), + client_major=client_major, + client_minor=client_minor) + + +Client = rq.Struct( + rq.Card32("resource_base"), + rq.Card32("resource_mask")) + + +class QueryClients(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8("opcode"), + rq.Opcode(ResQueryClients), + rq.RequestLength()) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16("sequence_number"), + rq.ReplyLength(), + rq.LengthOf("clients", 4), + rq.Pad(20), + rq.List("clients", Client)) + + +def query_clients(self): + """Request the list of all currently connected clients.""" + return QueryClients( + display=self.display, + opcode=self.display.get_extension_major(extname)) + + +Type = rq.Struct( + rq.Card32("resource_type"), + rq.Card32("count")) + + +class QueryClientResources(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8("opcode"), + rq.Opcode(ResQueryClientResources), + rq.RequestLength(), + rq.Card32("client")) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16("sequence_number"), + rq.ReplyLength(), + rq.LengthOf("types", 4), + rq.Pad(20), + rq.List("types", Type)) + + +def query_client_resources(self, client): + """Request the number of resources owned by a client. + + The server will return the counts of each type of resource. + """ + return QueryClientResources( + display=self.display, + opcode=self.display.get_extension_major(extname), + client=client) + + +class QueryClientPixmapBytes(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8("opcode"), + rq.Opcode(ResQueryClientPixmapBytes), + rq.RequestLength(), + rq.Card32("client")) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16("sequence_number"), + rq.ReplyLength(), + rq.Card32("bytes"), + rq.Card32("bytes_overflow"), + rq.Pad(16)) + + +def query_client_pixmap_bytes(self, client): + """Query the pixmap usage of some client. + + The returned number is a sum of memory usage of each pixmap that can be + attributed to the given client. + """ + return QueryClientPixmapBytes( + display=self.display, + opcode=self.display.get_extension_major(extname), + client=client) + + +class SizeOf(rq.LengthOf): + """A SizeOf stores the size in bytes of some other Field whose size + may vary, e.g. List + """ + def __init__(self, name, size, item_size): + rq.LengthOf.__init__(self, name, size) + self.item_size = item_size + + def parse_value(self, length, display): + return length // self.item_size + + +ClientXIDMask = 1 << 0 +LocalClientPIDMask = 1 << 1 + + +ClientIdSpec = rq.Struct( + rq.Card32("client"), + rq.Card32("mask")) + + +ClientIdValue = rq.Struct( + rq.Object("spec", ClientIdSpec), + SizeOf("value", 4, 4), + rq.List("value", rq.Card32Obj)) + + +class QueryClientIds(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8("opcode"), + rq.Opcode(ResQueryClientIds), + rq.RequestLength(), + rq.LengthOf("specs", 4), + rq.List("specs", ClientIdSpec)) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16("sequence_number"), + rq.ReplyLength(), + rq.LengthOf("ids", 4), + rq.Pad(20), + rq.List("ids", ClientIdValue)) + + +def query_client_ids(self, specs): + """Request to identify a given set of clients with some identification method. + + The request sends a list of specifiers that select clients and + identification methods to server. The server then tries to identify the + chosen clients using the identification methods specified for each client. + The server returns IDs for those clients that were successfully identified. + """ + return QueryClientIds( + display=self.display, + opcode=self.display.get_extension_major(extname), + specs=specs) + + +ResourceIdSpec = rq.Struct( + rq.Card32("resource"), + rq.Card32("type")) + + +ResourceSizeSpec = rq.Struct( + # inline struct ResourceIdSpec to work around + # a parser bug with nested objects + rq.Card32("resource"), + rq.Card32("type"), + rq.Card32("bytes"), + rq.Card32("ref_count"), + rq.Card32("use_count")) + + +ResourceSizeValue = rq.Struct( + rq.Object("size", ResourceSizeSpec), + rq.LengthOf("cross_references", 4), + rq.List("cross_references", ResourceSizeSpec)) + + +class QueryResourceBytes(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8("opcode"), + rq.Opcode(ResQueryResourceBytes), + rq.RequestLength(), + rq.Card32("client"), + rq.LengthOf("specs", 4), + rq.List("specs", ResourceIdSpec)) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16("sequence_number"), + rq.ReplyLength(), + rq.LengthOf("sizes", 4), + rq.Pad(20), + rq.List("sizes", ResourceSizeValue)) + + +def query_resource_bytes(self, client, specs): + """Query the sizes of resources from X server. + + The request sends a list of specifiers that selects resources for size + calculation. The server tries to calculate the sizes of chosen resources + and returns an estimate for a resource only if the size could be determined + """ + return QueryResourceBytes( + display=self.display, + opcode=self.display.get_extension_major(extname), + client=client, + specs=specs) + + +def init(disp, info): + disp.extension_add_method("display", "res_query_version", query_version) + disp.extension_add_method("display", "res_query_clients", query_clients) + disp.extension_add_method("display", "res_query_client_resources", + query_client_resources) + disp.extension_add_method("display", "res_query_client_pixmap_bytes", + query_client_pixmap_bytes) + disp.extension_add_method("display", "res_query_client_ids", + query_client_ids) + disp.extension_add_method("display", "res_query_resource_bytes", + query_resource_bytes) diff --git a/Nagstamon/thirdparty/Xlib/ext/screensaver.py b/Nagstamon/thirdparty/Xlib/ext/screensaver.py new file mode 100644 index 000000000..12f4325c4 --- /dev/null +++ b/Nagstamon/thirdparty/Xlib/ext/screensaver.py @@ -0,0 +1,198 @@ +# Xlib.ext.screensaver -- X ScreenSaver extension module +# +# Copyright (C) 2022 Vladimir Panteleev <git@cy.md> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 51 Franklin Street, +# Fifth Floor, +# Boston, MA 02110-1301 USA + +"""This extension allows registering the client as an X screensaver, +or query information about the current screensaver. + +For detailed description see any of the following documents. +Protocol specification: + https://www.x.org/releases/X11R7.7/doc/scrnsaverproto/saver.html +XCB Protocol specification: + https://cgit.freedesktop.org/xcb/proto/tree/src/screensaver.xml + +""" + +from Xlib import X +from Xlib.protocol import rq, structs + +extname = 'MIT-SCREEN-SAVER' + +# Event members +NotifyMask = 1 +CycleMask = 2 + +# Notify state +StateOff = 0 +StateOn = 1 +StateCycle = 2 + +# Notify kind +KindBlanked = 0 +KindInternal = 1 +KindExternal = 2 + +class QueryVersion(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(0), + rq.RequestLength(), + rq.Card8('major_version'), + rq.Card8('minor_version'), + rq.Pad(2), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card16('major_version'), + rq.Card16('minor_version'), + rq.Pad(20), + ) + +def query_version(self): + return QueryVersion(display=self.display, + opcode=self.display.get_extension_major(extname), + major_version=1, + minor_version=0) + + +class QueryInfo(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(1), + rq.RequestLength(), + rq.Drawable('drawable'), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Card8('state'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Window('saver_window'), + rq.Card32('til_or_since'), + rq.Card32('idle'), + rq.Card32('event_mask'), # rq.Set('event_mask', 4, (NotifyMask, CycleMask)), + rq.Card8('kind'), + rq.Pad(7), + ) + +def query_info(self): + return QueryInfo(display=self.display, + opcode=self.display.get_extension_major(extname), + drawable=self, + ) + + +class SelectInput(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(2), + rq.RequestLength(), + rq.Drawable('drawable'), + rq.Card32('event_mask'), # rq.Set('event_mask', 4, (NotifyMask, CycleMask)), + ) + +def select_input(self, mask): + return SelectInput(display=self.display, + opcode=self.display.get_extension_major(extname), + drawable=self, + event_mask=mask, + ) + + +class SetAttributes(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(3), + rq.RequestLength(), + rq.Drawable('drawable'), + rq.Int16('x'), + rq.Int16('y'), + rq.Card16('width'), + rq.Card16('height'), + rq.Card16('border_width'), + rq.Set('window_class', 1, (X.CopyFromParent, X.InputOutput, X.InputOnly)), + rq.Card8('depth'), + rq.Card32('visual'), + structs.WindowValues('attrs'), + ) + +def set_attributes(self, x, y, width, height, border_width, + window_class = X.CopyFromParent, + depth = X.CopyFromParent, + visual = X.CopyFromParent, + onerror = None, + **keys): + return SetAttributes(display=self.display, + onerror = onerror, + opcode=self.display.get_extension_major(extname), + drawable=self, + x = x, + y = y, + width = width, + height = height, + border_width = border_width, + window_class = window_class, + depth = depth, + visual = visual, + attrs = keys) + + +class UnsetAttributes(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(4), + rq.RequestLength(), + rq.Drawable('drawable'), + ) + +def unset_attributes(self, onerror = None): + return UnsetAttributes(display=self.display, + onerror = onerror, + opcode=self.display.get_extension_major(extname), + drawable=self) + + +class Notify(rq.Event): + _code = None + _fields = rq.Struct( + rq.Card8('type'), + rq.Set('state', 1, (StateOff, StateOn, StateCycle)), + rq.Card16('sequence_number'), + rq.Card32('timestamp'), + rq.Window('root'), + rq.Window('window'), + rq.Set('kind', 1, (KindBlanked, KindInternal, KindExternal)), + rq.Bool('forced'), + rq.Pad(14), + ) + +def init(disp, info): + disp.extension_add_method('display', 'screensaver_query_version', query_version) + disp.extension_add_method('drawable', 'screensaver_query_info', query_info) + disp.extension_add_method('drawable', 'screensaver_select_input', select_input) + disp.extension_add_method('drawable', 'screensaver_set_attributes', set_attributes) + disp.extension_add_method('drawable', 'screensaver_unset_attributes', unset_attributes) + + disp.extension_add_event(info.first_event + 0, Notify) diff --git a/Nagstamon/thirdparty/Xlib/ext/security.py b/Nagstamon/thirdparty/Xlib/ext/security.py new file mode 100644 index 000000000..a82101710 --- /dev/null +++ b/Nagstamon/thirdparty/Xlib/ext/security.py @@ -0,0 +1,139 @@ +# Xlib.ext.security -- SECURITY extension module +# +# Copyright (C) 2010-2013 Outpost Embedded, LLC +# Forest Bond <forest.bond@rapidrollout.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +''' +A partial implementation of the SECURITY extension. Support for the +SecurityAuthorizationRevoked event is not implemented. +''' + +from Xlib.protocol import rq + + +extname = 'SECURITY' + + +SecurityClientTrusted = 0 +SecurityClientUntrusted = 1 + +SecurityAuthorizationRevokedMask = 1 + + +AUTHID = rq.Card32 + + +class QueryVersion(rq.ReplyRequest): + _request = rq.Struct(rq.Card8('opcode'), + rq.Opcode(0), + rq.RequestLength(), + rq.Card16('major_version'), + rq.Card16('minor_version') + ) + _reply = rq.Struct(rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card16('major_version'), + rq.Card16('minor_version'), + rq.Pad(20) + ) + + +def query_version(self): + return QueryVersion(display=self.display, + opcode=self.display.get_extension_major(extname), + major_version=1, + minor_version=0) + + +class SecurityGenerateAuthorization(rq.ReplyRequest): + # The order of fields here does not match the specifications I've seen + # online, but it *does* match with the X.org implementation. I guess the + # spec is out-of-date. + _request = rq.Struct(rq.Card8('opcode'), + rq.Opcode(1), + rq.RequestLength(), + rq.LengthOf('auth_proto', 2), + rq.LengthOf('auth_data', 2), + rq.Card32('value_mask'), + rq.String8('auth_proto'), + rq.Binary('auth_data'), + rq.List('values', rq.Card32Obj) + ) + _reply = rq.Struct(rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + AUTHID('authid'), + rq.LengthOf('auth_data_return', 2), + rq.Pad(18), + rq.Binary('auth_data_return') + ) + + +def generate_authorization(self, auth_proto, auth_data=b'', timeout=None, + trust_level=None, group=None, event_mask=None): + value_mask = 0 + values = [] + if timeout is not None: + value_mask |= 1 + values.append(timeout) + if trust_level is not None: + value_mask |= 2 + values.append(trust_level) + if group is not None: + value_mask |= 4 + values.append(group) + if event_mask is not None: + value_mask |= 8 + values.append(event_mask) + return SecurityGenerateAuthorization(display=self.display, + opcode=self.display.get_extension_major(extname), + value_mask=value_mask, + auth_proto=auth_proto, + auth_data=auth_data, + values=values) + + +class SecurityRevokeAuthorization(rq.Request): + _request = rq.Struct(rq.Card8('opcode'), + rq.Opcode(2), + rq.RequestLength(), + AUTHID('authid') + ) + + +def revoke_authorization(self, authid): + return SecurityRevokeAuthorization(display=self.display, + opcode=self.display.get_extension_major(extname), + authid=authid) + + +def init(disp, info): + disp.extension_add_method('display', + 'security_query_version', + query_version) + disp.extension_add_method('display', + 'security_generate_authorization', + generate_authorization) + disp.extension_add_method('display', + 'security_revoke_authorization', + revoke_authorization) diff --git a/Nagstamon/thirdparty/Xlib/ext/shape.py b/Nagstamon/thirdparty/Xlib/ext/shape.py index 7394e676d..05a517acc 100644 --- a/Nagstamon/thirdparty/Xlib/ext/shape.py +++ b/Nagstamon/thirdparty/Xlib/ext/shape.py @@ -1,334 +1,297 @@ -# Xlib.ext.shape -- SHAPE extension module -# -# Copyright (C) 2002 Jeffrey Boser <verin@lvcm.com> -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - - -# Constants to use -# -# Regions of a window -ShapeBounding = 0 # the 'edge' of a shaped window -ShapeClip = 1 # the clipping region -# Shape Operations -ShapeSet = 0 # Set the region unmodified (dest=src) -ShapeUnion = 1 # Add the new region to the old (dest=src|dest) -ShapeIntersect = 2 # Use the intersection (dest=src&dest) -ShapeSubtract = 3 # remove region (dest = dest - intersect) -ShapeInvert = 4 # opposite of subtract (dest = src - intersect) -# Events -ShapeNotifyMask = (1<<0) #a keypress mask? -ShapeNotify = 0 #still unsure of these values - -# How to Use -# The basic functions that change the shapes of things are: -# shape_rectangles (uses a set of rectangles as the source) -# operation, region, ordering, rects -# shape_mask (uses a bitmap as the source) -# operation, region, x_offset, y_offset, bitmap -# shape_combine (uses a window as the source) -# operation, src_region, dest_region, x_offset, y_offset, src_window -# shape_offset (moves the region) -# region, x_offset, y_offset -# The functions to find stuff out (these three return mappings of field/values): -# shape_query_version (shape extension version) -# major_version, minor_version -# shape_query_extents (rectangle boundaries of a window's regions) -# clip_shaped, clip_x, clip_y, clip_width, clip_height, -# bounding_shaped, bounding_x, bounding_y, bounding_width, bounding_height -# shape_input_selected (if the window products shapenotify events) -# enabled -# shape_get_rectangles (the rectangles set by shape_rectangles) -# ordering, rects -# And to turn on shape notify events: -# shape_select_input -# enable - - - -from Xlib import X +# Automatically generated file; DO NOT EDIT. +# Generated from: /usr/share/xcb/shape.xml + from Xlib.protocol import rq, structs + extname = 'SHAPE' -class QueryVersion(rq.ReplyRequest): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(0), - rq.RequestLength(), - ) - _reply = rq.Struct( - rq.ReplyCode(), - rq.Pad(1), - rq.Card16('sequence_number'), - rq.ReplyLength(), - rq.Card16('major_version'), - rq.Card16('minor_version'), - rq.Pad(20), - ) - -def query_version(self): - return QueryVersion( - display = self.display, - opcode = self.display.get_extension_major(extname), - ) +OP = rq.Card8 +class SO: + Set = 0 + Union = 1 + Intersect = 2 + Subtract = 3 + Invert = 4 +class SK: + Bounding = 0 + Clip = 1 + Input = 2 -class Rectangles(rq.Request): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(1), - rq.RequestLength(), - rq.Card8('operation'), - rq.Set('region', 1, (ShapeBounding, ShapeClip)), - rq.Card8('ordering'), - rq.Pad(1), - rq.Window('window'), - rq.Int16('x'), - rq.Int16('y'), - rq.List('rectangles', structs.Rectangle), - ) - -def rectangles(self, region, operation, ordering, x, y, rectangles): - Rectangles( - display = self.display, - opcode = self.display.get_extension_major(extname), - operation = operation, - region = region, - ordering = ordering, - window = self.id, - x = x, - y = y, - rectangles = rectangles, - ) +class KIND(rq.Set): + def __init__(self, name): + super(KIND, self).__init__(name, 1, + values=(SK.Bounding, + SK.Clip, + SK.Input)) +class NotifyEventData(rq.Event): + _code = None + _fields = rq.Struct( + rq.Card8('type'), + KIND('shape_kind'), + rq.Card16('sequence_number'), + rq.Window('affected_window'), + rq.Int16('extents_x'), + rq.Int16('extents_y'), + rq.Card16('extents_width'), + rq.Card16('extents_height'), + rq.Card32('server_time'), + rq.Card8('shaped'), + rq.Pad(11), + ) + +class QueryVersion(rq.ReplyRequest): -class Mask(rq.Request): _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(2), - rq.RequestLength(), - rq.Card8('operation'), - rq.Set('region', 1, (ShapeBounding, ShapeClip)), - rq.Pad(2), - rq.Window('window'), - rq.Int16('x'), - rq.Int16('y'), - rq.Pixmap('source', (X.NONE, )), - ) - -def mask(self, operation, region, x, y, source): - Mask(display = self.display, - opcode = self.display.get_extension_major(extname), - window = self.id, - operation = operation, - region = region, - x = x, - y = y, - source = source, - ) + rq.Card8('opcode'), + rq.Opcode(0), + rq.RequestLength(), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card16('major_version'), + rq.Card16('minor_version'), + ) +class Rectangles(rq.Request): -class Combine(rq.Request): _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(3), - rq.RequestLength(), - rq.Card8('operation'), - rq.Set('dest_region', 1, (ShapeBounding, ShapeClip)), - rq.Set('source_region', 1, (ShapeBounding, ShapeClip)), - rq.Pad(1), - rq.Window('dest'), - rq.Int16('x'), - rq.Int16('y'), - rq.Window('source'), - ) - -def combine(self, operation, region, source, source_region, x, y): - Combine( - display = self.display, - opcode = self.display.get_extension_major(extname), - operation = operation, - dest_region = region, - source_region = source_region, - dest = self.id, - x = x, - y = y, - source = source, - ) + rq.Card8('opcode'), + rq.Opcode(1), + rq.RequestLength(), + OP('operation'), + KIND('destination_kind'), + rq.Card8('ordering'), + rq.Pad(1), + rq.Window('destination_window'), + rq.Int16('x_offset'), + rq.Int16('y_offset'), + rq.List('rectangles', structs.Rectangle, pad=0), + ) +class Mask(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(2), + rq.RequestLength(), + OP('operation'), + KIND('destination_kind'), + rq.Pad(2), + rq.Window('destination_window'), + rq.Int16('x_offset'), + rq.Int16('y_offset'), + rq.Pixmap('source_bitmap'), + ) + +class Combine(rq.Request): -class Offset(rq.Request): _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(4), - rq.RequestLength(), - rq.Set('region', 1, (ShapeBounding, ShapeClip)), - rq.Pad(3), - rq.Window('window'), - rq.Int16('x'), - rq.Int16('y'), - ) - -def offset(self, region, x, y): - Offset( - display = self.display, - opcode = self.display.get_extension_major(extname), - region = region, - window = self.id, - x = x, - y = y, - ) + rq.Card8('opcode'), + rq.Opcode(3), + rq.RequestLength(), + OP('operation'), + KIND('destination_kind'), + KIND('source_kind'), + rq.Pad(1), + rq.Window('destination_window'), + rq.Int16('x_offset'), + rq.Int16('y_offset'), + rq.Window('source_window'), + ) +class Offset(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(4), + rq.RequestLength(), + KIND('destination_kind'), + rq.Pad(3), + rq.Window('destination_window'), + rq.Int16('x_offset'), + rq.Int16('y_offset'), + ) class QueryExtents(rq.ReplyRequest): + _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(5), - rq.RequestLength(), - rq.Window('window'), - ) + rq.Card8('opcode'), + rq.Opcode(5), + rq.RequestLength(), + rq.Window('destination_window'), + ) _reply = rq.Struct( - rq.ReplyCode(), - rq.Pad(1), - rq.Card16('sequence_number'), - rq.ReplyLength(), - rq.Bool('bounding_shaped'), - rq.Bool('clip_shaped'), - rq.Pad(2), - rq.Int16('bounding_x'), - rq.Int16('bounding_y'), - rq.Card16('bounding_width'), - rq.Card16('bounding_height'), - rq.Int16('clip_x'), - rq.Int16('clip_y'), - rq.Card16('clip_width'), - rq.Card16('clip_height'), - rq.Pad(4), - ) - -def query_extents(self): - return QueryExtents( - display = self.display, - opcode = self.display.get_extension_major(extname), - window = self.id, - ) - + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card8('bounding_shaped'), + rq.Card8('clip_shaped'), + rq.Pad(2), + rq.Int16('bounding_shape_extents_x'), + rq.Int16('bounding_shape_extents_y'), + rq.Card16('bounding_shape_extents_width'), + rq.Card16('bounding_shape_extents_height'), + rq.Int16('clip_shape_extents_x'), + rq.Int16('clip_shape_extents_y'), + rq.Card16('clip_shape_extents_width'), + rq.Card16('clip_shape_extents_height'), + ) class SelectInput(rq.Request): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(6), - rq.RequestLength(), - rq.Window('window'), - rq.Bool('enable'), - rq.Pad(3), - ) - -def select_input(self, enable = 1): - SelectInput( - display = self.display, - opcode = self.display.get_extension_major(extname), - window = self.id, - enable = enable, - ) + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(6), + rq.RequestLength(), + rq.Window('destination_window'), + rq.Card8('enable'), + rq.Pad(3), + ) class InputSelected(rq.ReplyRequest): + _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(7), - rq.RequestLength(), - rq.Window('window'), - ) + rq.Card8('opcode'), + rq.Opcode(7), + rq.RequestLength(), + rq.Window('destination_window'), + ) _reply = rq.Struct( - rq.ReplyCode(), - rq.Bool('enabled'), - rq.Card16('sequence_number'), - rq.ReplyLength(), - rq.Pad(24), - ) - -def input_selected(self): - reply = InputSelected( - display = self.display, - opcode = self.display.get_extension_major(extname), - window = self.id, - ) - return reply.enabled - + rq.ReplyCode(), + rq.Card8('enabled'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + ) class GetRectangles(rq.ReplyRequest): + _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(8), - rq.RequestLength(), - rq.Window('window'), - rq.Set('region', 1, (ShapeBounding, ShapeClip)), - rq.Pad(3), - ) + rq.Card8('opcode'), + rq.Opcode(8), + rq.RequestLength(), + rq.Window('window'), + KIND('source_kind'), + rq.Pad(3), + ) _reply = rq.Struct( - rq.ReplyCode(), - rq.Card8('ordering'), - rq.Card16('sequence_number'), - rq.ReplyLength(), - rq.LengthOf('rectangles', 4), - rq.Pad(20), - rq.List('rectangles', structs.Rectangle), - ) - -def get_rectangles(self, region): + rq.ReplyCode(), + rq.Card8('ordering'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.LengthOf('rectangles', 4), + rq.Pad(20), + rq.List('rectangles', structs.Rectangle, pad=0), + ) + +class Event: + # Sub events. + Notify = 0 + +def combine(self, operation, destination_kind, source_kind, x_offset, y_offset): + Combine( + display=self.display, + opcode=self.display.get_extension_major(extname), + source_window=self, + operation=operation, + destination_kind=destination_kind, + source_kind=source_kind, + x_offset=x_offset, + y_offset=y_offset, + ) + +def get_rectangles(self, source_kind): return GetRectangles( - display = self.display, - opcode = self.display.get_extension_major(extname), - window = self.id, - region = region, - ) + display=self.display, + opcode=self.display.get_extension_major(extname), + window=self, + source_kind=source_kind, + ) + +def input_selected(self, ): + return InputSelected( + display=self.display, + opcode=self.display.get_extension_major(extname), + destination_window=self, + ) + +def mask(self, operation, destination_kind, x_offset, y_offset, source_bitmap): + Mask( + display=self.display, + opcode=self.display.get_extension_major(extname), + destination_window=self, + operation=operation, + destination_kind=destination_kind, + x_offset=x_offset, + y_offset=y_offset, + source_bitmap=source_bitmap, + ) + +def offset(self, destination_kind, x_offset, y_offset): + Offset( + display=self.display, + opcode=self.display.get_extension_major(extname), + destination_window=self, + destination_kind=destination_kind, + x_offset=x_offset, + y_offset=y_offset, + ) + +def query_extents(self, ): + return QueryExtents( + display=self.display, + opcode=self.display.get_extension_major(extname), + destination_window=self, + ) +def query_version(self, ): + return QueryVersion( + display=self.display, + opcode=self.display.get_extension_major(extname), + ) -class ShapeNotify(rq.Event): - _code = None - _fields = rq.Struct( rq.Card8('type'), - rq.Set('region', 1, (ShapeBounding, ShapeClip)), - rq.Card16('sequence_number'), - rq.Window('window'), - rq.Int16('x'), - rq.Int16('y'), - rq.Card16('width'), - rq.Card16('height'), - rq.Card32('time'), - rq.Bool('shaped'), - rq.Pad(11), - ) +def rectangles(self, operation, destination_kind, ordering, x_offset, y_offset, rectangles): + Rectangles( + display=self.display, + opcode=self.display.get_extension_major(extname), + destination_window=self, + operation=operation, + destination_kind=destination_kind, + ordering=ordering, + x_offset=x_offset, + y_offset=y_offset, + rectangles=rectangles, + ) + +def select_input(self, enable): + SelectInput( + display=self.display, + opcode=self.display.get_extension_major(extname), + destination_window=self, + enable=enable, + ) def init(disp, info): - disp.extension_add_method('display', 'shape_query_version', query_version ) - disp.extension_add_method('window', 'shape_rectangles', rectangles ) - disp.extension_add_method('window', 'shape_mask', mask ) - disp.extension_add_method('window', 'shape_combine', combine ) - disp.extension_add_method('window', 'shape_offset', offset ) - disp.extension_add_method('window', 'shape_query_extents', query_extents ) - disp.extension_add_method('window', 'shape_select_input', select_input ) - disp.extension_add_method('window', 'shape_input_selected', input_selected ) - disp.extension_add_method('window', 'shape_get_rectangles', get_rectangles ) - - disp.extension_add_event(info.first_event, ShapeNotify) + disp.extension_add_method('window', 'shape_combine', combine) + disp.extension_add_method('window', 'shape_get_rectangles', get_rectangles) + disp.extension_add_method('window', 'shape_input_selected', input_selected) + disp.extension_add_method('window', 'shape_mask', mask) + disp.extension_add_method('window', 'shape_offset', offset) + disp.extension_add_method('window', 'shape_query_extents', query_extents) + disp.extension_add_method('display', 'shape_query_version', query_version) + disp.extension_add_method('window', 'shape_rectangles', rectangles) + disp.extension_add_method('window', 'shape_select_input', select_input) + disp.extension_add_event(info.first_event + Event.Notify, NotifyEventData, 'ShapeNotify') + diff --git a/Nagstamon/thirdparty/Xlib/ext/xfixes.py b/Nagstamon/thirdparty/Xlib/ext/xfixes.py new file mode 100644 index 000000000..59f9277b4 --- /dev/null +++ b/Nagstamon/thirdparty/Xlib/ext/xfixes.py @@ -0,0 +1,201 @@ +# Xlib.ext.xfixes -- XFIXES extension module +# +# Copyright (C) 2010-2011 Outpost Embedded, LLC +# Forest Bond <forest.bond@rapidrollout.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +''' +A partial implementation of the XFIXES extension. Only the HideCursor and +ShowCursor requests and SelectionNotify events are provided. +''' + +from Xlib import X +from Xlib.protocol import rq, structs + +extname = 'XFIXES' + +XFixesSelectionNotify = 0 +XFixesCursorNotify = 1 + +XFixesSetSelectionOwnerNotifyMask = (1 << 0) +XFixesSelectionWindowDestroyNotifyMask = (1 << 1) +XFixesSelectionClientCloseNotifyMask = (1 << 2) +XFixesDisplayCursorNotifyMask = (1 << 0) + +XFixesSetSelectionOwnerNotify = 0 +XFixesSelectionWindowDestroyNotify = 1 +XFixesSelectionClientCloseNotify = 2 +XFixesDisplayCursorNotify = 0 + +class QueryVersion(rq.ReplyRequest): + _request = rq.Struct(rq.Card8('opcode'), + rq.Opcode(0), + rq.RequestLength(), + rq.Card32('major_version'), + rq.Card32('minor_version') + ) + _reply = rq.Struct(rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('major_version'), + rq.Card32('minor_version'), + rq.Pad(16) + ) + + +def query_version(self): + return QueryVersion(display=self.display, + opcode=self.display.get_extension_major(extname), + major_version=4, + minor_version=0) + + +class HideCursor(rq.Request): + _request = rq.Struct(rq.Card8('opcode'), + rq.Opcode(29), + rq.RequestLength(), + rq.Window('window') + ) + +def hide_cursor(self): + HideCursor(display=self.display, + opcode=self.display.get_extension_major(extname), + window=self) + + +class ShowCursor(rq.Request): + _request = rq.Struct(rq.Card8('opcode'), + rq.Opcode(30), + rq.RequestLength(), + rq.Window('window') + ) + + +def show_cursor(self): + ShowCursor(display=self.display, + opcode=self.display.get_extension_major(extname), + window=self) + +class SelectSelectionInput(rq.Request): + _request = rq.Struct(rq.Card8('opcode'), + rq.Opcode(2), + rq.RequestLength(), + rq.Window('window'), + rq.Card32('selection'), + rq.Card32('mask') + ) + +def select_selection_input(self, window, selection, mask): + return SelectSelectionInput(opcode=self.display.get_extension_major(extname), + display=self.display, + window=window, + selection=selection, + mask=mask) + + +class SelectionNotify(rq.Event): + _code = None + _fields = rq.Struct(rq.Card8('type'), + rq.Card8('sub_code'), + rq.Card16('sequence_number'), + rq.Window('window'), + rq.Window('owner'), + rq.Card32('selection'), + rq.Card32('timestamp'), + rq.Card32('selection_timestamp'), + rq.Pad(8)) + + +class SetSelectionOwnerNotify(SelectionNotify): + pass + + +class SelectionWindowDestroyNotify(SelectionNotify): + pass + + +class SelectionClientCloseNotify(SelectionNotify): + pass + + +class SelectCursorInput(rq.Request): + _request = rq.Struct(rq.Card8('opcode'), + rq.Opcode(3), + rq.RequestLength(), + rq.Window('window'), + rq.Card32('mask') + ) + +def select_cursor_input(self, window, mask): + return SelectCursorInput(opcode=self.display.get_extension_major(extname), + display=self.display, + window=window, + cursor_serial=0, + mask=mask) + + +class GetCursorImage(rq.ReplyRequest): + _request = rq.Struct(rq.Card8('opcode'), + rq.Opcode(4), + rq.RequestLength() + ) + _reply = rq.Struct(rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Int16('x'), + rq.Int16('y'), + rq.Card16('width'), + rq.Card16('height'), + rq.Card16('xhot'), + rq.Card16('yhot'), + rq.Card32('cursor_serial'), + rq.Pad(8), + rq.List('cursor_image', rq.Card32) + ) + +def get_cursor_image(self, window): + return GetCursorImage(opcode=self.display.get_extension_major(extname), + display=self.display, + ) + + +class DisplayCursorNotify(rq.Event): + _code = None + _fields = rq.Struct(rq.Card8('type'), + rq.Card8('sub_code'), + rq.Card16('sequence_number'), + rq.Window('window'), + rq.Card32('cursor_serial'), + rq.Card32('timestamp')) + + +def init(disp, info): + disp.extension_add_method('display', 'xfixes_select_selection_input', select_selection_input) + disp.extension_add_method('display', 'xfixes_query_version', query_version) + disp.extension_add_method('window', 'xfixes_hide_cursor', hide_cursor) + disp.extension_add_method('window', 'xfixes_show_cursor', show_cursor) + disp.extension_add_method('display', 'xfixes_select_cursor_input', select_cursor_input) + disp.extension_add_method('display', 'xfixes_get_cursor_image', get_cursor_image) + + disp.extension_add_subevent(info.first_event + XFixesSelectionNotify, XFixesSetSelectionOwnerNotify, SetSelectionOwnerNotify) + disp.extension_add_subevent(info.first_event + XFixesSelectionNotify, XFixesSelectionWindowDestroyNotify, SelectionWindowDestroyNotify) + disp.extension_add_subevent(info.first_event + XFixesSelectionNotify, XFixesSelectionClientCloseNotify, SelectionClientCloseNotify) + disp.extension_add_subevent(info.first_event + XFixesCursorNotify, XFixesDisplayCursorNotify, DisplayCursorNotify) diff --git a/Nagstamon/thirdparty/Xlib/ext/xinerama.py b/Nagstamon/thirdparty/Xlib/ext/xinerama.py index 2f64e8112..f0546707b 100644 --- a/Nagstamon/thirdparty/Xlib/ext/xinerama.py +++ b/Nagstamon/thirdparty/Xlib/ext/xinerama.py @@ -2,20 +2,22 @@ # # Copyright (C) 2006 Mike Meyer <mwm@mired.org> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA """Xinerama - provide access to the Xinerama extension information. @@ -28,7 +30,7 @@ this is untested because I don't have a server that implements it. The functions loosely follow the libXineram functions. Mostly, they -return an rq.Struct in lieue of passing in pointers that get data from +return an rq.Struct in lieu of passing in pointers that get data from the rq.Struct crammed into them. The exception is isActive, which returns the state information - because that's what libXinerama does.""" diff --git a/Nagstamon/thirdparty/Xlib/ext/xinput.py b/Nagstamon/thirdparty/Xlib/ext/xinput.py new file mode 100644 index 000000000..f92180647 --- /dev/null +++ b/Nagstamon/thirdparty/Xlib/ext/xinput.py @@ -0,0 +1,777 @@ +# Xlib.ext.xinput -- XInput extension module +# +# Copyright (C) 2012 Outpost Embedded, LLC +# Forest Bond <forest.bond@rapidrollout.com> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +''' +A very incomplete implementation of the XInput extension. +''' + +import sys +import array +import struct + +# Python 2/3 compatibility. +from six import integer_types + +from Xlib.protocol import rq +from Xlib import X + + +extname = 'XInputExtension' + +PropertyDeleted = 0 +PropertyCreated = 1 +PropertyModified = 2 + +NotifyNormal = 0 +NotifyGrab = 1 +NotifyUngrab = 2 +NotifyWhileGrabbed = 3 +NotifyPassiveGrab = 4 +NotifyPassiveUngrab = 5 + +NotifyAncestor = 0 +NotifyVirtual = 1 +NotifyInferior = 2 +NotifyNonlinear = 3 +NotifyNonlinearVirtual = 4 +NotifyPointer = 5 +NotifyPointerRoot = 6 +NotifyDetailNone = 7 + +GrabtypeButton = 0 +GrabtypeKeycode = 1 +GrabtypeEnter = 2 +GrabtypeFocusIn = 3 +GrabtypeTouchBegin = 4 + +AnyModifier = (1 << 31) +AnyButton = 0 +AnyKeycode = 0 + +AsyncDevice = 0 +SyncDevice = 1 +ReplayDevice = 2 +AsyncPairedDevice = 3 +AsyncPair = 4 +SyncPair = 5 + +SlaveSwitch = 1 +DeviceChange = 2 + +MasterAdded = (1 << 0) +MasterRemoved = (1 << 1) +SlaveAdded = (1 << 2) +SlaveRemoved = (1 << 3) +SlaveAttached = (1 << 4) +SlaveDetached = (1 << 5) +DeviceEnabled = (1 << 6) +DeviceDisabled = (1 << 7) + +AddMaster = 1 +RemoveMaster = 2 +AttachSlave = 3 +DetachSlave = 4 + +AttachToMaster = 1 +Floating = 2 + +ModeRelative = 0 +ModeAbsolute = 1 + +MasterPointer = 1 +MasterKeyboard = 2 +SlavePointer = 3 +SlaveKeyboard = 4 +FloatingSlave = 5 + +KeyClass = 0 +ButtonClass = 1 +ValuatorClass = 2 +ScrollClass = 3 +TouchClass = 8 + +KeyRepeat = (1 << 16) + +AllDevices = 0 +AllMasterDevices = 1 + +DeviceChanged = 1 +KeyPress = 2 +KeyRelease = 3 +ButtonPress = 4 +ButtonRelease = 5 +Motion = 6 +Enter = 7 +Leave = 8 +FocusIn = 9 +FocusOut = 10 +HierarchyChanged = 11 +PropertyEvent = 12 +RawKeyPress = 13 +RawKeyRelease = 14 +RawButtonPress = 15 +RawButtonRelease = 16 +RawMotion = 17 + +DeviceChangedMask = (1 << DeviceChanged) +KeyPressMask = (1 << KeyPress) +KeyReleaseMask = (1 << KeyRelease) +ButtonPressMask = (1 << ButtonPress) +ButtonReleaseMask = (1 << ButtonRelease) +MotionMask = (1 << Motion) +EnterMask = (1 << Enter) +LeaveMask = (1 << Leave) +FocusInMask = (1 << FocusIn) +FocusOutMask = (1 << FocusOut) +HierarchyChangedMask = (1 << HierarchyChanged) +PropertyEventMask = (1 << PropertyEvent) +RawKeyPressMask = (1 << RawKeyPress) +RawKeyReleaseMask = (1 << RawKeyRelease) +RawButtonPressMask = (1 << RawButtonPress) +RawButtonReleaseMask = (1 << RawButtonRelease) +RawMotionMask = (1 << RawMotion) + +GrabModeSync = 0 +GrabModeAsync = 1 +GrabModeTouch = 2 + +DEVICEID = rq.Card16 +DEVICE = rq.Card16 +DEVICEUSE = rq.Card8 + +PROPERTY_TYPE_FLOAT = 'FLOAT' + +class FP1616(rq.Int32): + + def check_value(self, value): + return int(value * 65536.0) + + def parse_value(self, value, display): + return float(value) / float(1 << 16) + +class FP3232(rq.ValueField): + structcode = 'lL' + structvalues = 2 + + def check_value(self, value): + return value + + def parse_value(self, value, display): + integral, frac = value + ret = float(integral) + # optimised math.ldexp(float(frac), -32) + ret += float(frac) * (1.0 / (1 << 32)) + return ret + +class XIQueryVersion(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(47), + rq.RequestLength(), + rq.Card16('major_version'), + rq.Card16('minor_version'), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card16('major_version'), + rq.Card16('minor_version'), + rq.Pad(20), + ) + + +def query_version(self): + return XIQueryVersion( + display=self.display, + opcode=self.display.get_extension_major(extname), + major_version=2, + minor_version=0, + ) + +class Mask(rq.List): + + def __init__(self, name): + rq.List.__init__(self, name, rq.Card32, pad=0) + + def pack_value(self, val): + + mask_seq = array.array(rq.struct_to_array_codes['L']) + + if isinstance(val, integer_types): + # We need to build a "binary mask" that (as far as I can tell) is + # encoded in native byte order from end to end. The simple case is + # with a single unsigned 32-bit value, for which we construct an + # array with just one item. For values too big to fit inside 4 + # bytes we build a longer array, being careful to maintain native + # byte order across the entire set of values. + if sys.byteorder == 'little': + def fun(val): + mask_seq.insert(0, val) + elif sys.byteorder == 'big': + fun = mask_seq.append + else: + raise AssertionError(sys.byteorder) + while val: + fun(val & 0xFFFFFFFF) + val = val >> 32 + else: + mask_seq.extend(val) + + return rq.encode_array(mask_seq), len(mask_seq), None + +EventMask = rq.Struct( + DEVICE('deviceid'), + rq.LengthOf('mask', 2), + Mask('mask'), +) + + +class XISelectEvents(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(46), + rq.RequestLength(), + rq.Window('window'), + rq.LengthOf('masks', 2), + rq.Pad(2), + rq.List('masks', EventMask), + ) + +def select_events(self, event_masks): + ''' + select_events(event_masks) + + event_masks: + Sequence of (deviceid, mask) pairs, where deviceid is a numerical device + ID, or AllDevices or AllMasterDevices, and mask is either an unsigned + integer or sequence of 32 bits unsigned values + ''' + return XISelectEvents( + display=self.display, + opcode=self.display.get_extension_major(extname), + window=self, + masks=event_masks, + ) + +AnyInfo = rq.Struct( + rq.Card16('type'), + rq.Card16('length'), + rq.Card16('sourceid'), + rq.Pad(2), +) + +class ButtonMask(object): + + def __init__(self, value, length): + self._value = value + self._length = length + + def __len__(self): + return self._length + + def __getitem__(self, key): + return self._value & (1 << key) + + def __str__(self): + return repr(self) + + def __repr__(self): + return '0b{value:0{width}b}'.format(value=self._value, + width=self._length) + +class ButtonState(rq.ValueField): + + structcode = None + + def __init__(self, name): + rq.ValueField.__init__(self, name) + + def parse_binary_value(self, data, display, length, fmt): + # Mask: bitfield of <length> button states. + mask_len = 4 * ((((length + 7) >> 3) + 3) >> 2) + mask_data = data[:mask_len] + mask_value = 0 + for byte in reversed(struct.unpack('={0:d}B'.format(mask_len), mask_data)): + mask_value <<= 8 + mask_value |= byte + data = data[mask_len:] + assert (mask_value & 1) == 0 + return ButtonMask(mask_value >> 1, length), data + +ButtonInfo = rq.Struct( + rq.Card16('type'), + rq.Card16('length'), + rq.Card16('sourceid'), + rq.LengthOf(('state', 'labels'), 2), + ButtonState('state'), + rq.List('labels', rq.Card32), +) + +KeyInfo = rq.Struct( + rq.Card16('type'), + rq.Card16('length'), + rq.Card16('sourceid'), + rq.LengthOf('keycodes', 2), + rq.List('keycodes', rq.Card32), +) + +ValuatorInfo = rq.Struct( + rq.Card16('type'), + rq.Card16('length'), + rq.Card16('sourceid'), + rq.Card16('number'), + rq.Card32('label'), + FP3232('min'), + FP3232('max'), + FP3232('value'), + rq.Card32('resolution'), + rq.Card8('mode'), + rq.Pad(3), +) + +ScrollInfo = rq.Struct( + rq.Card16('type'), + rq.Card16('length'), + rq.Card16('sourceid'), + rq.Card16('number'), + rq.Card16('scroll_type'), + rq.Pad(2), + rq.Card32('flags'), + FP3232('increment'), +) + +TouchInfo = rq.Struct( + rq.Card16('type'), + rq.Card16('length'), + rq.Card16('sourceid'), + rq.Card8('mode'), + rq.Card8('num_touches'), +) + +INFO_CLASSES = { + KeyClass: KeyInfo, + ButtonClass: ButtonInfo, + ValuatorClass: ValuatorInfo, + ScrollClass: ScrollInfo, + TouchClass: TouchInfo, +} + +class ClassInfoClass(object): + + structcode = None + + def parse_binary(self, data, display): + class_type, length = struct.unpack('=HH', data[:4]) + class_struct = INFO_CLASSES.get(class_type, AnyInfo) + class_data, _ = class_struct.parse_binary(data, display) + data = data[length * 4:] + return class_data, data + +ClassInfo = ClassInfoClass() + +DeviceInfo = rq.Struct( + DEVICEID('deviceid'), + rq.Card16('use'), + rq.Card16('attachment'), + rq.LengthOf('classes', 2), + rq.LengthOf('name', 2), + rq.Bool('enabled'), + rq.Pad(1), + rq.String8('name', 4), + rq.List('classes', ClassInfo), +) + +class XIQueryDevice(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(48), + rq.RequestLength(), + DEVICEID('deviceid'), + rq.Pad(2), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.LengthOf('devices', 2), + rq.Pad(22), + rq.List('devices', DeviceInfo), + ) + +def query_device(self, deviceid): + return XIQueryDevice( + display=self.display, + opcode=self.display.get_extension_major(extname), + deviceid=deviceid, + ) + +class XIListProperties(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(56), + rq.RequestLength(), + DEVICEID('deviceid'), + rq.Pad(2), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.LengthOf('atoms', 2), + rq.Pad(22), + rq.List('atoms', rq.Card32Obj), + ) + +def list_device_properties(self, deviceid): + return XIListProperties( + display=self.display, + opcode=self.display.get_extension_major(extname), + deviceid=deviceid, + ) + +class XIGetProperty(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(59), + rq.RequestLength(), + DEVICEID('deviceid'), + rq.Card8('delete'), + rq.Pad(1), + rq.Card32('property'), + rq.Card32('type'), + rq.Card32('offset'), + rq.Card32('length'), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card32('type'), + rq.Card32('bytes_after'), + rq.LengthOf('value', 4), + rq.Format('value', 1), + rq.Pad(11), + rq.PropertyData('value') + ) + +def get_device_property(self, deviceid, property, type, offset, length, delete=False): + return XIGetProperty( + display=self.display, + opcode=self.display.get_extension_major(extname), + deviceid=deviceid, + property=property, + type=type, + offset=offset, + length=length, + delete=delete, + ) + +class XIChangeProperty(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(57), + rq.RequestLength(), + DEVICEID('deviceid'), + rq.Card8('mode'), + rq.Format('value', 1), + rq.Card32('property'), + rq.Card32('type'), + rq.LengthOf('value', 4), + rq.PropertyData('value'), + ) + +def change_device_property(self, deviceid, property, type, mode, value): + return XIChangeProperty( + display=self.display, + opcode=self.display.get_extension_major(extname), + deviceid=deviceid, + property=property, + type=type, + mode=mode, + value=value, + ) + +class XIDeleteProperty(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(58), + rq.RequestLength(), + DEVICEID('deviceid'), + rq.Pad(2), + rq.Card32('property'), + ) + +def delete_device_property(self, deviceid, property): + return XIDeleteProperty( + display=self.display, + opcode=self.display.get_extension_major(extname), + deviceid=deviceid, + property=property, + ) + +class XIGrabDevice(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(51), + rq.RequestLength(), + rq.Window('grab_window'), + rq.Card32('time'), + rq.Cursor('cursor', (X.NONE, )), + DEVICEID('deviceid'), + rq.Set('grab_mode', 1, (GrabModeSync, GrabModeAsync)), + rq.Set('paired_device_mode', 1, (GrabModeSync, GrabModeAsync)), + rq.Bool('owner_events'), + rq.Pad(1), + rq.LengthOf('mask', 2), + Mask('mask'), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card8('status'), + rq.Pad(23), + ) + +def grab_device(self, deviceid, time, grab_mode, paired_device_mode, owner_events, event_mask): + return XIGrabDevice( + display=self.display, + opcode=self.display.get_extension_major(extname), + deviceid=deviceid, + grab_window=self, + time=time, + cursor=X.NONE, + grab_mode=grab_mode, + paired_device_mode=paired_device_mode, + owner_events=owner_events, + mask=event_mask, + ) + +class XIUngrabDevice(rq.Request): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(52), + rq.RequestLength(), + rq.Card32('time'), + DEVICEID('deviceid'), + rq.Pad(2), + ) + +def ungrab_device(self, deviceid, time): + return XIUngrabDevice( + display=self.display, + opcode=self.display.get_extension_major(extname), + time=time, + deviceid=deviceid, + ) + +class XIPassiveGrabDevice(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(54), + rq.RequestLength(), + rq.Card32('time'), + rq.Window('grab_window'), + rq.Cursor('cursor', (X.NONE, )), + rq.Card32('detail'), + DEVICEID('deviceid'), + rq.LengthOf('modifiers', 2), + rq.LengthOf('mask', 2), + rq.Set('grab_type', 1, (GrabtypeButton, GrabtypeKeycode, GrabtypeEnter, + GrabtypeFocusIn, GrabtypeTouchBegin)), + rq.Set('grab_mode', 1, (GrabModeSync, GrabModeAsync)), + rq.Set('paired_device_mode', 1, (GrabModeSync, GrabModeAsync)), + rq.Bool('owner_events'), + rq.Pad(2), + Mask('mask'), + rq.List('modifiers', rq.Card32), + ) + + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.LengthOf('modifiers', 2), + rq.Pad(22), + rq.List('modifiers', rq.Card32), + ) + +def passive_grab_device(self, deviceid, time, detail, + grab_type, grab_mode, paired_device_mode, + owner_events, event_mask, modifiers): + return XIPassiveGrabDevice( + display=self.display, + opcode=self.display.get_extension_major(extname), + deviceid=deviceid, + grab_window=self, + time=time, + cursor=X.NONE, + detail=detail, + grab_type=grab_type, + grab_mode=grab_mode, + paired_device_mode=paired_device_mode, + owner_events=owner_events, + mask=event_mask, + modifiers=modifiers, + ) + +def grab_keycode(self, deviceid, time, keycode, + grab_mode, paired_device_mode, + owner_events, event_mask, modifiers): + return passive_grab_device(self, deviceid, time, keycode, + GrabtypeKeycode, + grab_mode, paired_device_mode, + owner_events, event_mask, modifiers) + +class XIPassiveUngrabDevice(rq.Request): + + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(55), + rq.RequestLength(), + rq.Window('grab_window'), + rq.Card32('detail'), + DEVICEID('deviceid'), + rq.LengthOf('modifiers', 2), + rq.Set('grab_type', 1, (GrabtypeButton, GrabtypeKeycode, + GrabtypeEnter, GrabtypeFocusIn, + GrabtypeTouchBegin)), + rq.Pad(3), + rq.List('modifiers', rq.Card32), + ) + +def passive_ungrab_device(self, deviceid, detail, grab_type, modifiers): + return XIPassiveUngrabDevice( + display=self.display, + opcode=self.display.get_extension_major(extname), + deviceid=deviceid, + grab_window=self, + detail=detail, + grab_type=grab_type, + modifiers=modifiers, + ) + +def ungrab_keycode(self, deviceid, keycode, modifiers): + return passive_ungrab_device(self, deviceid, keycode, + GrabtypeKeycode, modifiers) + +HierarchyInfo = rq.Struct( + DEVICEID('deviceid'), + DEVICEID('attachment'), + DEVICEUSE('type'), + rq.Bool('enabled'), + rq.Pad(2), + rq.Card32('flags'), +) + + +HierarchyEventData = rq.Struct( + DEVICEID('deviceid'), + rq.Card32('time'), + rq.Card32('flags'), + rq.LengthOf('info', 2), + rq.Pad(10), + rq.List('info', HierarchyInfo), +) + +ModifierInfo = rq.Struct( + rq.Card32('base_mods'), + rq.Card32('latched_mods'), + rq.Card32('locked_mods'), + rq.Card32('effective_mods'), +) + +GroupInfo = rq.Struct( + rq.Card8('base_group'), + rq.Card8('latched_group'), + rq.Card8('locked_group'), + rq.Card8('effective_group'), +) + +DeviceEventData = rq.Struct( + DEVICEID('deviceid'), + rq.Card32('time'), + rq.Card32('detail'), + rq.Window('root'), + rq.Window('event'), + rq.Window('child'), + FP1616('root_x'), + FP1616('root_y'), + FP1616('event_x'), + FP1616('event_y'), + rq.LengthOf('buttons', 2), + rq.Card16('valulators_len'), + DEVICEID('sourceid'), + rq.Pad(2), + rq.Card32('flags'), + rq.Object('mods', ModifierInfo), + rq.Object('groups', GroupInfo), + ButtonState('buttons'), +) + +DeviceChangedEventData = rq.Struct( + DEVICEID('deviceid'), + rq.Card32('time'), + rq.LengthOf('classes', 2), + DEVICEID('sourceid'), + rq.Card8('reason'), + rq.Pad(11), + rq.List('classes', ClassInfo), +) + +PropertyEventData = rq.Struct( + DEVICEID('deviceid'), + rq.Card32('time'), + rq.Card32('property'), + rq.Card8('what'), + rq.Pad(11), +) + +def init(disp, info): + disp.extension_add_method('display', 'xinput_query_version', query_version) + disp.extension_add_method('window', 'xinput_select_events', select_events) + disp.extension_add_method('display', 'xinput_query_device', query_device) + disp.extension_add_method('window', 'xinput_grab_device', grab_device) + disp.extension_add_method('display', 'xinput_ungrab_device', ungrab_device) + disp.extension_add_method('window', 'xinput_grab_keycode', grab_keycode) + disp.extension_add_method('window', 'xinput_ungrab_keycode', ungrab_keycode) + disp.extension_add_method('display', 'xinput_get_device_property', get_device_property) + disp.extension_add_method('display', 'xinput_list_device_properties', list_device_properties) + disp.extension_add_method('display', 'xinput_change_device_property', change_device_property) + disp.extension_add_method('display', 'xinput_delete_device_property', delete_device_property) + if hasattr(disp,"ge_add_event_data"): + for device_event in (ButtonPress, ButtonRelease, KeyPress, KeyRelease, Motion): + disp.ge_add_event_data(info.major_opcode, device_event, DeviceEventData) + disp.ge_add_event_data(info.major_opcode, DeviceChanged, DeviceEventData) + disp.ge_add_event_data(info.major_opcode, HierarchyChanged, HierarchyEventData) + disp.ge_add_event_data(info.major_opcode, PropertyEvent, PropertyEventData) diff --git a/Nagstamon/thirdparty/Xlib/ext/xtest.py b/Nagstamon/thirdparty/Xlib/ext/xtest.py index 6146e68ab..602df2ae8 100644 --- a/Nagstamon/thirdparty/Xlib/ext/xtest.py +++ b/Nagstamon/thirdparty/Xlib/ext/xtest.py @@ -2,19 +2,22 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA from Xlib import X from Xlib.protocol import rq diff --git a/Nagstamon/thirdparty/Xlib/keysymdef/__init__.py b/Nagstamon/thirdparty/Xlib/keysymdef/__init__.py index 75fe2bc67..4ff14416e 100644 --- a/Nagstamon/thirdparty/Xlib/keysymdef/__init__.py +++ b/Nagstamon/thirdparty/Xlib/keysymdef/__init__.py @@ -2,19 +2,22 @@ # # Copyright (C) 2001 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA __all__ = [ 'apl', diff --git a/Nagstamon/thirdparty/Xlib/keysymdef/xf86.py b/Nagstamon/thirdparty/Xlib/keysymdef/xf86.py index a0d79aef4..4ccf12a71 100644 --- a/Nagstamon/thirdparty/Xlib/keysymdef/xf86.py +++ b/Nagstamon/thirdparty/Xlib/keysymdef/xf86.py @@ -1,179 +1,202 @@ -XK_XF86_MonBrightnessUp = 0x1008FF02 -XK_XF86_MonBrightnessDown = 0x1008FF03 -XK_XF86_KbdLightOnOff = 0x1008FF04 -XK_XF86_KbdBrightnessUp = 0x1008FF05 -XK_XF86_KbdBrightnessDown = 0x1008FF06 - -XK_XF86_Standby = 0x1008FF10 -XK_XF86_AudioLowerVolume = 0x1008FF11 -XK_XF86_AudioMute = 0x1008FF12 -XK_XF86_AudioRaiseVolume = 0x1008FF13 -XK_XF86_AudioPlay = 0x1008FF14 -XK_XF86_AudioStop = 0x1008FF15 -XK_XF86_AudioPrev = 0x1008FF16 -XK_XF86_AudioNext = 0x1008FF17 -XK_XF86_HomePage = 0x1008FF18 -XK_XF86_Mail = 0x1008FF19 -XK_XF86_Start = 0x1008FF1A -XK_XF86_Search = 0x1008FF1B -XK_XF86_AudioRecord = 0x1008FF1C - -XK_XF86_Calculator = 0x1008FF1D -XK_XF86_Memo = 0x1008FF1E -XK_XF86_ToDoList = 0x1008FF1F -XK_XF86_Calendar = 0x1008FF20 -XK_XF86_PowerDown = 0x1008FF21 -XK_XF86_ContrastAdjust = 0x1008FF22 -XK_XF86_RockerUp = 0x1008FF23 -XK_XF86_RockerDown = 0x1008FF24 -XK_XF86_RockerEnter = 0x1008FF25 - -XK_XF86_Back = 0x1008FF26 -XK_XF86_Forward = 0x1008FF27 -XK_XF86_Stop = 0x1008FF28 -XK_XF86_Refresh = 0x1008FF29 -XK_XF86_PowerOff = 0x1008FF2A -XK_XF86_WakeUp = 0x1008FF2B -XK_XF86_Eject = 0x1008FF2C -XK_XF86_ScreenSaver = 0x1008FF2D -XK_XF86_WWW = 0x1008FF2E -XK_XF86_Sleep = 0x1008FF2F -XK_XF86_Favorites = 0x1008FF30 -XK_XF86_AudioPause = 0x1008FF31 -XK_XF86_AudioMedia = 0x1008FF32 -XK_XF86_MyComputer = 0x1008FF33 -XK_XF86_VendorHome = 0x1008FF34 -XK_XF86_LightBulb = 0x1008FF35 -XK_XF86_Shop = 0x1008FF36 -XK_XF86_History = 0x1008FF37 -XK_XF86_OpenURL = 0x1008FF38 -XK_XF86_AddFavorite = 0x1008FF39 -XK_XF86_HotLinks = 0x1008FF3A -XK_XF86_BrightnessAdjust = 0x1008FF3B -XK_XF86_Finance = 0x1008FF3C -XK_XF86_Community = 0x1008FF3D -XK_XF86_AudioRewind = 0x1008FF3E -XK_XF86_XF86BackForward = 0x1008FF3F -XK_XF86_Launch0 = 0x1008FF40 -XK_XF86_Launch1 = 0x1008FF41 -XK_XF86_Launch2 = 0x1008FF42 -XK_XF86_Launch3 = 0x1008FF43 -XK_XF86_Launch4 = 0x1008FF44 -XK_XF86_Launch5 = 0x1008FF45 -XK_XF86_Launch6 = 0x1008FF46 -XK_XF86_Launch7 = 0x1008FF47 -XK_XF86_Launch8 = 0x1008FF48 -XK_XF86_Launch9 = 0x1008FF49 -XK_XF86_LaunchA = 0x1008FF4A -XK_XF86_LaunchB = 0x1008FF4B -XK_XF86_LaunchC = 0x1008FF4C -XK_XF86_LaunchD = 0x1008FF4D -XK_XF86_LaunchE = 0x1008FF4E -XK_XF86_LaunchF = 0x1008FF4F - -XK_XF86_ApplicationLeft = 0x1008FF50 -XK_XF86_ApplicationRight = 0x1008FF51 -XK_XF86_Book = 0x1008FF52 -XK_XF86_CD = 0x1008FF53 -XK_XF86_Calculater = 0x1008FF54 -XK_XF86_Clear = 0x1008FF55 -XK_XF86_Close = 0x1008FF56 -XK_XF86_Copy = 0x1008FF57 -XK_XF86_Cut = 0x1008FF58 -XK_XF86_Display = 0x1008FF59 -XK_XF86_DOS = 0x1008FF5A -XK_XF86_Documents = 0x1008FF5B -XK_XF86_Excel = 0x1008FF5C -XK_XF86_Explorer = 0x1008FF5D -XK_XF86_Game = 0x1008FF5E -XK_XF86_Go = 0x1008FF5F -XK_XF86_iTouch = 0x1008FF60 -XK_XF86_LogOff = 0x1008FF61 -XK_XF86_Market = 0x1008FF62 -XK_XF86_Meeting = 0x1008FF63 -XK_XF86_MenuKB = 0x1008FF65 -XK_XF86_MenuPB = 0x1008FF66 -XK_XF86_MySites = 0x1008FF67 -XK_XF86_New = 0x1008FF68 -XK_XF86_News = 0x1008FF69 -XK_XF86_OfficeHome = 0x1008FF6A -XK_XF86_Open = 0x1008FF6B -XK_XF86_Option = 0x1008FF6C -XK_XF86_Paste = 0x1008FF6D -XK_XF86_Phone = 0x1008FF6E -XK_XF86_Q = 0x1008FF70 -XK_XF86_Reply = 0x1008FF72 -XK_XF86_Reload = 0x1008FF73 -XK_XF86_RotateWindows = 0x1008FF74 -XK_XF86_RotationPB = 0x1008FF75 -XK_XF86_RotationKB = 0x1008FF76 -XK_XF86_Save = 0x1008FF77 -XK_XF86_ScrollUp = 0x1008FF78 -XK_XF86_ScrollDown = 0x1008FF79 -XK_XF86_ScrollClick = 0x1008FF7A -XK_XF86_Send = 0x1008FF7B -XK_XF86_Spell = 0x1008FF7C -XK_XF86_SplitScreen = 0x1008FF7D -XK_XF86_Support = 0x1008FF7E -XK_XF86_TaskPane = 0x1008FF7F -XK_XF86_Terminal = 0x1008FF80 -XK_XF86_Tools = 0x1008FF81 -XK_XF86_Travel = 0x1008FF82 -XK_XF86_UserPB = 0x1008FF84 -XK_XF86_User1KB = 0x1008FF85 -XK_XF86_User2KB = 0x1008FF86 -XK_XF86_Video = 0x1008FF87 -XK_XF86_WheelButton = 0x1008FF88 -XK_XF86_Word = 0x1008FF89 -XK_XF86_Xfer = 0x1008FF8A -XK_XF86_ZoomIn = 0x1008FF8B -XK_XF86_ZoomOut = 0x1008FF8C - -XK_XF86_Away = 0x1008FF8D -XK_XF86_Messenger = 0x1008FF8E -XK_XF86_WebCam = 0x1008FF8F -XK_XF86_MailForward = 0x1008FF90 -XK_XF86_Pictures = 0x1008FF91 -XK_XF86_Music = 0x1008FF92 - -XK_XF86_Battery = 0x1008FF93 -XK_XF86_Bluetooth = 0x1008FF94 -XK_XF86_WLAN = 0x1008FF95 -XK_XF86_UWB = 0x1008FF96 - -XK_XF86_AudioForward = 0x1008FF97 -XK_XF86_AudioRepeat = 0x1008FF98 -XK_XF86_AudioRandomPlay = 0x1008FF99 -XK_XF86_Subtitle = 0x1008FF9A -XK_XF86_AudioCycleTrack = 0x1008FF9B -XK_XF86_CycleAngle = 0x1008FF9C -XK_XF86_FrameBack = 0x1008FF9D -XK_XF86_FrameForward = 0x1008FF9E -XK_XF86_Time = 0x1008FF9F -XK_XF86_Select = 0x1008FFA0 -XK_XF86_View = 0x1008FFA1 -XK_XF86_TopMenu = 0x1008FFA2 - -XK_XF86_Red = 0x1008FFA3 -XK_XF86_Green = 0x1008FFA4 -XK_XF86_Yellow = 0x1008FFA5 -XK_XF86_Blue = 0x1008FFA6 - -XK_XF86_Switch_VT_1 = 0x1008FE01 -XK_XF86_Switch_VT_2 = 0x1008FE02 -XK_XF86_Switch_VT_3 = 0x1008FE03 -XK_XF86_Switch_VT_4 = 0x1008FE04 -XK_XF86_Switch_VT_5 = 0x1008FE05 -XK_XF86_Switch_VT_6 = 0x1008FE06 -XK_XF86_Switch_VT_7 = 0x1008FE07 -XK_XF86_Switch_VT_8 = 0x1008FE08 -XK_XF86_Switch_VT_9 = 0x1008FE09 -XK_XF86_Switch_VT_10 = 0x1008FE0A -XK_XF86_Switch_VT_11 = 0x1008FE0B -XK_XF86_Switch_VT_12 = 0x1008FE0C - -XK_XF86_Ungrab = 0x1008FE20 -XK_XF86_ClearGrab = 0x1008FE21 -XK_XF86_Next_VMode = 0x1008FE22 -XK_XF86_Prev_VMode = 0x1008FE23 +XK_XF86_ModeLock = 0x1008FF01 +XK_XF86_MonBrightnessUp = 0x1008FF02 +XK_XF86_MonBrightnessDown = 0x1008FF03 +XK_XF86_KbdLightOnOff = 0x1008FF04 +XK_XF86_KbdBrightnessUp = 0x1008FF05 +XK_XF86_KbdBrightnessDown = 0x1008FF06 +XK_XF86_MonBrightnessCycle = 0x1008FF07 + +XK_XF86_Standby = 0x1008FF10 +XK_XF86_AudioLowerVolume = 0x1008FF11 +XK_XF86_AudioMute = 0x1008FF12 +XK_XF86_AudioRaiseVolume = 0x1008FF13 +XK_XF86_AudioPlay = 0x1008FF14 +XK_XF86_AudioStop = 0x1008FF15 +XK_XF86_AudioPrev = 0x1008FF16 +XK_XF86_AudioNext = 0x1008FF17 +XK_XF86_HomePage = 0x1008FF18 +XK_XF86_Mail = 0x1008FF19 +XK_XF86_Start = 0x1008FF1A +XK_XF86_Search = 0x1008FF1B +XK_XF86_AudioRecord = 0x1008FF1C + +XK_XF86_Calculator = 0x1008FF1D +XK_XF86_Memo = 0x1008FF1E +XK_XF86_ToDoList = 0x1008FF1F +XK_XF86_Calendar = 0x1008FF20 +XK_XF86_PowerDown = 0x1008FF21 +XK_XF86_ContrastAdjust = 0x1008FF22 +XK_XF86_RockerUp = 0x1008FF23 +XK_XF86_RockerDown = 0x1008FF24 +XK_XF86_RockerEnter = 0x1008FF25 + +XK_XF86_Back = 0x1008FF26 +XK_XF86_Forward = 0x1008FF27 +XK_XF86_Stop = 0x1008FF28 +XK_XF86_Refresh = 0x1008FF29 +XK_XF86_PowerOff = 0x1008FF2A +XK_XF86_WakeUp = 0x1008FF2B +XK_XF86_Eject = 0x1008FF2C +XK_XF86_ScreenSaver = 0x1008FF2D +XK_XF86_WWW = 0x1008FF2E +XK_XF86_Sleep = 0x1008FF2F +XK_XF86_Favorites = 0x1008FF30 +XK_XF86_AudioPause = 0x1008FF31 +XK_XF86_AudioMedia = 0x1008FF32 +XK_XF86_MyComputer = 0x1008FF33 +XK_XF86_VendorHome = 0x1008FF34 +XK_XF86_LightBulb = 0x1008FF35 +XK_XF86_Shop = 0x1008FF36 +XK_XF86_History = 0x1008FF37 +XK_XF86_OpenURL = 0x1008FF38 +XK_XF86_AddFavorite = 0x1008FF39 +XK_XF86_HotLinks = 0x1008FF3A +XK_XF86_BrightnessAdjust = 0x1008FF3B +XK_XF86_Finance = 0x1008FF3C +XK_XF86_Community = 0x1008FF3D +XK_XF86_AudioRewind = 0x1008FF3E +XK_XF86_XF86BackForward = 0x1008FF3F +XK_XF86_Launch0 = 0x1008FF40 +XK_XF86_Launch1 = 0x1008FF41 +XK_XF86_Launch2 = 0x1008FF42 +XK_XF86_Launch3 = 0x1008FF43 +XK_XF86_Launch4 = 0x1008FF44 +XK_XF86_Launch5 = 0x1008FF45 +XK_XF86_Launch6 = 0x1008FF46 +XK_XF86_Launch7 = 0x1008FF47 +XK_XF86_Launch8 = 0x1008FF48 +XK_XF86_Launch9 = 0x1008FF49 +XK_XF86_LaunchA = 0x1008FF4A +XK_XF86_LaunchB = 0x1008FF4B +XK_XF86_LaunchC = 0x1008FF4C +XK_XF86_LaunchD = 0x1008FF4D +XK_XF86_LaunchE = 0x1008FF4E +XK_XF86_LaunchF = 0x1008FF4F + +XK_XF86_ApplicationLeft = 0x1008FF50 +XK_XF86_ApplicationRight = 0x1008FF51 +XK_XF86_Book = 0x1008FF52 +XK_XF86_CD = 0x1008FF53 +XK_XF86_Calculater = 0x1008FF54 +XK_XF86_Clear = 0x1008FF55 +XK_XF86_Close = 0x1008FF56 +XK_XF86_Copy = 0x1008FF57 +XK_XF86_Cut = 0x1008FF58 +XK_XF86_Display = 0x1008FF59 +XK_XF86_DOS = 0x1008FF5A +XK_XF86_Documents = 0x1008FF5B +XK_XF86_Excel = 0x1008FF5C +XK_XF86_Explorer = 0x1008FF5D +XK_XF86_Game = 0x1008FF5E +XK_XF86_Go = 0x1008FF5F +XK_XF86_iTouch = 0x1008FF60 +XK_XF86_LogOff = 0x1008FF61 +XK_XF86_Market = 0x1008FF62 +XK_XF86_Meeting = 0x1008FF63 +XK_XF86_MenuKB = 0x1008FF65 +XK_XF86_MenuPB = 0x1008FF66 +XK_XF86_MySites = 0x1008FF67 +XK_XF86_New = 0x1008FF68 +XK_XF86_News = 0x1008FF69 +XK_XF86_OfficeHome = 0x1008FF6A +XK_XF86_Open = 0x1008FF6B +XK_XF86_Option = 0x1008FF6C +XK_XF86_Paste = 0x1008FF6D +XK_XF86_Phone = 0x1008FF6E +XK_XF86_Q = 0x1008FF70 +XK_XF86_Reply = 0x1008FF72 +XK_XF86_Reload = 0x1008FF73 +XK_XF86_RotateWindows = 0x1008FF74 +XK_XF86_RotationPB = 0x1008FF75 +XK_XF86_RotationKB = 0x1008FF76 +XK_XF86_Save = 0x1008FF77 +XK_XF86_ScrollUp = 0x1008FF78 +XK_XF86_ScrollDown = 0x1008FF79 +XK_XF86_ScrollClick = 0x1008FF7A +XK_XF86_Send = 0x1008FF7B +XK_XF86_Spell = 0x1008FF7C +XK_XF86_SplitScreen = 0x1008FF7D +XK_XF86_Support = 0x1008FF7E +XK_XF86_TaskPane = 0x1008FF7F +XK_XF86_Terminal = 0x1008FF80 +XK_XF86_Tools = 0x1008FF81 +XK_XF86_Travel = 0x1008FF82 +XK_XF86_UserPB = 0x1008FF84 +XK_XF86_User1KB = 0x1008FF85 +XK_XF86_User2KB = 0x1008FF86 +XK_XF86_Video = 0x1008FF87 +XK_XF86_WheelButton = 0x1008FF88 +XK_XF86_Word = 0x1008FF89 +XK_XF86_Xfer = 0x1008FF8A +XK_XF86_ZoomIn = 0x1008FF8B +XK_XF86_ZoomOut = 0x1008FF8C + +XK_XF86_Away = 0x1008FF8D +XK_XF86_Messenger = 0x1008FF8E +XK_XF86_WebCam = 0x1008FF8F +XK_XF86_MailForward = 0x1008FF90 +XK_XF86_Pictures = 0x1008FF91 +XK_XF86_Music = 0x1008FF92 + +XK_XF86_Battery = 0x1008FF93 +XK_XF86_Bluetooth = 0x1008FF94 +XK_XF86_WLAN = 0x1008FF95 +XK_XF86_UWB = 0x1008FF96 + +XK_XF86_AudioForward = 0x1008FF97 +XK_XF86_AudioRepeat = 0x1008FF98 +XK_XF86_AudioRandomPlay = 0x1008FF99 +XK_XF86_Subtitle = 0x1008FF9A +XK_XF86_AudioCycleTrack = 0x1008FF9B +XK_XF86_CycleAngle = 0x1008FF9C +XK_XF86_FrameBack = 0x1008FF9D +XK_XF86_FrameForward = 0x1008FF9E +XK_XF86_Time = 0x1008FF9F +XK_XF86_Select = 0x1008FFA0 +XK_XF86_View = 0x1008FFA1 +XK_XF86_TopMenu = 0x1008FFA2 + +XK_XF86_Red = 0x1008FFA3 +XK_XF86_Green = 0x1008FFA4 +XK_XF86_Yellow = 0x1008FFA5 +XK_XF86_Blue = 0x1008FFA6 + +XK_XF86_Suspend = 0x1008FFA7 +XK_XF86_Hibernate = 0x1008FFA8 +XK_XF86_TouchpadToggle = 0x1008FFA9 +XK_XF86_TouchpadOn = 0x1008FFB0 +XK_XF86_TouchpadOff = 0x1008FFB1 + +XK_XF86_AudioMicMute = 0x1008FFB2 + +XK_XF86_Keyboard = 0x1008FFB3 + +XK_XF86_WWAN = 0x1008FFB4 +XK_XF86_RFKill = 0x1008FFB5 + +XK_XF86_AudioPreset = 0x1008FFB6 + +XK_XF86_RotationLockToggle = 0x1008FFB7 + +XK_XF86_FullScreen = 0x1008FFB8 + +XK_XF86_Switch_VT_1 = 0x1008FE01 +XK_XF86_Switch_VT_2 = 0x1008FE02 +XK_XF86_Switch_VT_3 = 0x1008FE03 +XK_XF86_Switch_VT_4 = 0x1008FE04 +XK_XF86_Switch_VT_5 = 0x1008FE05 +XK_XF86_Switch_VT_6 = 0x1008FE06 +XK_XF86_Switch_VT_7 = 0x1008FE07 +XK_XF86_Switch_VT_8 = 0x1008FE08 +XK_XF86_Switch_VT_9 = 0x1008FE09 +XK_XF86_Switch_VT_10 = 0x1008FE0A +XK_XF86_Switch_VT_11 = 0x1008FE0B +XK_XF86_Switch_VT_12 = 0x1008FE0C + +XK_XF86_Ungrab = 0x1008FE20 +XK_XF86_ClearGrab = 0x1008FE21 +XK_XF86_Next_VMode = 0x1008FE22 +XK_XF86_Prev_VMode = 0x1008FE23 +XK_XF86_LogWindowTree = 0x1008FE24 +XK_XF86_LogGrabInfo = 0x1008FE25 diff --git a/Nagstamon/thirdparty/Xlib/protocol/__init__.py b/Nagstamon/thirdparty/Xlib/protocol/__init__.py index 8b71e06e7..4e2840a1b 100644 --- a/Nagstamon/thirdparty/Xlib/protocol/__init__.py +++ b/Nagstamon/thirdparty/Xlib/protocol/__init__.py @@ -2,19 +2,22 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA __all__ = [ 'display', diff --git a/Nagstamon/thirdparty/Xlib/protocol/display.py b/Nagstamon/thirdparty/Xlib/protocol/display.py index fdd2efe2e..56623c358 100644 --- a/Nagstamon/thirdparty/Xlib/protocol/display.py +++ b/Nagstamon/thirdparty/Xlib/protocol/display.py @@ -1,64 +1,95 @@ -# -*- coding: latin-1 -*- +# -*- coding: utf-8 -*- # # Xlib.protocol.display -- core display communication # # Copyright (C) 2000-2002 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA # Standard modules import errno +import math import select import socket import struct import sys +# Python 2/3 compatibility. +from six import PY3, byte2int, indexbytes + # Xlib modules -from Xlib import error +from .. import error +from ..ext import ge -from Xlib.support import lock, connect +from ..support import lock, connect # Xlib.protocol modules -from . import rq, event +from . import rq +from . import event + +if PY3: + + class bytesview(object): + + def __init__(self, data, offset=0, size=None): + if size is None: + size = len(data)-offset + if isinstance(data, bytes): + view = memoryview(data) + elif isinstance(data, bytesview): + view = data.view + else: + raise TypeError('unsupported type: {}'.format(type(data))) + self.view = view[offset:offset+size] + + def __len__(self): + return len(self.view) + + def __getitem__(self, key): + if isinstance(key, slice): + return bytes(self.view[key]) + return self.view[key] -# in Python 3, bytes are an actual array; in python 2, bytes are still -# string-like, so in order to get an array element we need to call ord() -if sys.version[0] >= '3': - def _bytes_item(x): - return x else: - def _bytes_item(x): - return ord(x) + def bytesview(data, offset=0, size=None): + if not isinstance(data, (bytes, buffer)): + raise TypeError('unsupported type: {}'.format(type(data))) + if size is None: + size = len(data)-offset + return buffer(data, offset, size) -class Display: - resource_classes = {} + +class Display(object): extension_major_opcodes = {} error_classes = error.xerror_class.copy() event_classes = event.event_class.copy() def __init__(self, display = None): - name, host, displayno, screenno = connect.get_display(display) + name, protocol, host, displayno, screenno = connect.get_display(display) self.display_name = name self.default_screen = screenno - self.socket = connect.get_socket(name, host, displayno) + self.socket = connect.get_socket(name, protocol, host, displayno) - auth_name, auth_data = connect.get_auth(self.socket, - name, host, displayno) + auth_name, auth_data = connect.get_auth(self.socket, name, + protocol, host, displayno) # Internal structures for communication, grouped # by their function and locks @@ -78,7 +109,7 @@ def __init__(self, display = None): self.request_serial = 1 self.request_queue = [] - # Send-and-recieve loop, see function send_and_recive + # Send-and-receive loop, see function send_and_receive # for a detailed explanation self.send_recv_lock = lock.allocate_lock() self.send_active = 0 @@ -89,9 +120,15 @@ def __init__(self, display = None): self.request_waiting = 0 self.request_wait_lock = lock.allocate_lock() - # Data used by the send-and-recieve loop + # Calculate optimal default buffer size for recv. + buffer_size = self.socket.getsockopt(socket.SOL_SOCKET, + socket.SO_RCVBUF) + buffer_size = math.pow(2, math.floor(math.log(buffer_size, 2))) + self.recv_buffer_size = int(buffer_size) + + # Data used by the send-and-receive loop self.sent_requests = [] - self.request_length = 0 + self.recv_packet_len = 0 self.data_send = b'' self.data_recv = b'' self.data_sent_bytes = 0 @@ -108,7 +145,7 @@ def __init__(self, display = None): # Right, now we're all set up for the connection setup # request with the server. - # Figure out which endianess the hardware uses + # Figure out which endianness the hardware uses self.big_endian = struct.unpack('BB', struct.pack('H', 0x0100))[0] if self.big_endian: @@ -166,7 +203,7 @@ def next_event(self): while not self.event_queue: - # Lock send_recv so no send_and_recieve + # Lock send_recv so no send_and_receive # can start or stop while we're checking # whether there are one active. self.send_recv_lock.acquire() @@ -288,8 +325,14 @@ def set_extension_major(self, extname, major): def get_extension_major(self, extname): return self.extension_major_opcodes[extname] - def add_extension_event(self, code, evt): - self.event_classes[code] = evt + def add_extension_event(self, code, evt, subcode=None): + if subcode == None: + self.event_classes[code] = evt + else: + if not code in self.event_classes: + self.event_classes[code] = {subcode: evt} + else: + self.event_classes[code][subcode] = evt def add_extension_error(self, code, err): self.error_classes[code] = err @@ -356,7 +399,7 @@ def send_and_recv(self, flush = None, event = None, request = None, recv = None) be true. Will return immediately if another thread is already doing send_and_recv. - To wait for an event to be recieved, event should be true. + To wait for an event to be received, event should be true. To wait for a response to a certain request (either an error or a response), request should be set the that request's @@ -380,6 +423,9 @@ def send_and_recv(self, flush = None, event = None, request = None, recv = None) # If waiting for an event, we want to recv # If just trying to receive anything we can, we want to recv + # FIXME: It would be good if we could also sleep when we're waiting on + # a response to a request that has already been sent. + if (((flush or request is not None) and self.send_active) or ((event or recv) and self.recv_active)): @@ -438,19 +484,19 @@ def send_and_recv(self, flush = None, event = None, request = None, recv = None) # There's no thread doing what we need to do. Find out exactly # what to do - # There must always be some thread recieving data, but it must not + # There must always be some thread receiving data, but it must not # necessarily be us if not self.recv_active: - recieving = 1 + receiving = 1 self.recv_active = 1 else: - recieving = 0 + receiving = 0 flush_bytes = None sending = 0 - # Loop, recieving and sending data. + # Loop, receiving and sending data. while 1: # We might want to start sending data @@ -481,6 +527,10 @@ def send_and_recv(self, flush = None, event = None, request = None, recv = None) # for the network to fire up self.send_recv_lock.release() + # There's no longer anything useful we can do here. + if not (sending or receiving): + break + # If we're flushing, figure out how many bytes we # have to send so that we're not caught in an interminable # loop if other threads continuously append requests. @@ -491,9 +541,9 @@ def send_and_recv(self, flush = None, event = None, request = None, recv = None) try: # We're only checking for the socket to be writable # if we're the sending thread. We always check for it - # to become readable: either we are the recieving thread - # and should take care of the data, or the recieving thread - # might finish recieving after having read the data + # to become readable: either we are the receiving thread + # and should take care of the data, or the receiving thread + # might finish receiving after having read the data if sending: writeset = [self.socket] @@ -510,11 +560,15 @@ def send_and_recv(self, flush = None, event = None, request = None, recv = None) rs, ws, es = select.select([self.socket], writeset, [], timeout) - # Ignore errors caused by a signal recieved while blocking. + # Ignore errors caused by a signal received while blocking. # All other errors are re-raised. - except OSError as err: - if err.errno != errno.EINTR: - raise err + except select.error as err: + if isinstance(err, OSError): + code = err.errno + else: + code = err[0] + if code != errno.EINTR: + raise # We must lock send_and_recv before we can loop to # the start of the loop @@ -527,8 +581,8 @@ def send_and_recv(self, flush = None, event = None, request = None, recv = None) if ws: try: i = self.socket.send(self.data_send) - except OSError as err: - self.close_internal('server: %s' % err[1]) + except socket.error as err: + self.close_internal('server: %s' % err) raise self.socket_error self.data_send = self.data_send[i:] @@ -539,12 +593,14 @@ def send_and_recv(self, flush = None, event = None, request = None, recv = None) gotreq = 0 if rs: - # We're the recieving thread, parse the data - if recieving: + # We're the receiving thread, parse the data + if receiving: try: - bytes_recv = self.socket.recv(4096) - except OSError as err: - self.close_internal('server: %s' % err.strerror) + count = self.recv_packet_len - len(self.data_recv) + count = max(self.recv_buffer_size, count) + bytes_recv = self.socket.recv(count) + except socket.error as err: + self.close_internal('server: %s' % err) raise self.socket_error if not bytes_recv: @@ -552,7 +608,7 @@ def send_and_recv(self, flush = None, event = None, request = None, recv = None) self.close_internal('server') raise self.socket_error - self.data_recv = self.data_recv + bytes_recv + self.data_recv = bytes(self.data_recv) + bytes_recv gotreq = self.parse_response(request) # Otherwise return, allowing the calling thread to figure @@ -570,7 +626,7 @@ def send_and_recv(self, flush = None, event = None, request = None, recv = None) # There are three different end of send-recv-loop conditions. # However, we don't leave the loop immediately, instead we - # try to send and recieve any data that might be left. We + # try to send and receive any data that might be left. We # do this by giving a timeout of 0 to select to poll # the socket. @@ -586,7 +642,7 @@ def send_and_recv(self, flush = None, event = None, request = None, recv = None) if request is not None and gotreq: break - # Always break if we just want to recieve as much as possible + # Always break if we just want to receive as much as possible if recv: break @@ -604,7 +660,7 @@ def send_and_recv(self, flush = None, event = None, request = None, recv = None) if sending: self.send_active = 0 - if recieving: + if receiving: self.recv_active = 0 if self.event_waiting: @@ -621,9 +677,9 @@ def send_and_recv(self, flush = None, event = None, request = None, recv = None) def parse_response(self, request): """Internal method. - Parse data recieved from server. If REQUEST is not None + Parse data received from server. If REQUEST is not None true is returned if the request with that serial number - was recieved, otherwise false is returned. + was received, otherwise false is returned. If REQUEST is -1, we're parsing the server connection setup response. @@ -635,47 +691,54 @@ def parse_response(self, request): # Parse ordinary server response gotreq = 0 while 1: - # Are we're waiting for additional data for a request response? - if self.request_length: - if len(self.data_recv) < self.request_length: + if self.data_recv: + # Check the first byte to find out what kind of response it is + rtype = byte2int(self.data_recv) + + # Are we're waiting for additional data for the current packet? + if self.recv_packet_len: + if len(self.data_recv) < self.recv_packet_len: return gotreq - else: - gotreq = self.parse_request_response(request) or gotreq + if rtype == 1: + gotreq = self.parse_request_response(request) or gotreq + continue + elif rtype & 0x7f == ge.GenericEventCode: + self.parse_event_response(rtype) + continue + else: + raise AssertionError(rtype) # Every response is at least 32 bytes long, so don't bother - # until we have recieved that much + # until we have received that much if len(self.data_recv) < 32: return gotreq - # Check the first byte to find out what kind of response it is - rtype = _bytes_item(self.data_recv[0]) - - # Error resposne + # Error response if rtype == 0: gotreq = self.parse_error_response(request) or gotreq - # Request response - elif rtype == 1: + # Request response or generic event. + elif rtype == 1 or rtype & 0x7f == ge.GenericEventCode: # Set reply length, and loop around to see if # we have got the full response rlen = int(struct.unpack('=L', self.data_recv[4:8])[0]) - self.request_length = 32 + rlen * 4 + self.recv_packet_len = 32 + rlen * 4 - # Else event response + # Else non-generic event else: self.parse_event_response(rtype) def parse_error_response(self, request): # Code is second byte - code = _bytes_item(self.data_recv[1]) + code = indexbytes(self.data_recv, 1) # Fetch error class estruct = self.error_classes.get(code, error.XError) e = estruct(self, self.data_recv[:32]) - self.data_recv = self.data_recv[32:] + self.data_recv = bytesview(self.data_recv, 32) # print 'recv Error:', e @@ -726,11 +789,11 @@ def parse_request_response(self, request): raise RuntimeError("Expected reply for request %s, but got %s. Can't happen!" % (req._serial, sno)) - req._parse_response(self.data_recv[:self.request_length]) + req._parse_response(self.data_recv[:self.recv_packet_len]) # print 'recv Request:', req - self.data_recv = self.data_recv[self.request_length:] - self.request_length = 0 + self.data_recv = bytesview(self.data_recv, self.recv_packet_len) + self.recv_packet_len = 0 # Unlock any response waiting threads @@ -748,21 +811,42 @@ def parse_request_response(self, request): def parse_event_response(self, etype): - # Skip bit 8 at lookup, that is set if this event came from an - # SendEvent - estruct = self.event_classes.get(etype & 0x7f, event.AnyEvent) + # Skip bit 8, that is set if this event came from an SendEvent + etype = etype & 0x7f + + if etype == ge.GenericEventCode: + length = self.recv_packet_len + else: + length = 32 + + estruct = self.event_classes.get(etype, event.AnyEvent) + if type(estruct) == dict: + subcode = self.data_recv[1] + + # Python2 compatibility + if type(subcode) == str: + subcode = ord(subcode) + + # this etype refers to a set of sub-events with individual subcodes + estruct = estruct[subcode] + + e = estruct(display = self, binarydata = self.data_recv[:length]) - e = estruct(display = self, binarydata = self.data_recv[:32]) + if etype == ge.GenericEventCode: + self.recv_packet_len = 0 - self.data_recv = self.data_recv[32:] + self.data_recv = bytesview(self.data_recv, length) # Drop all requests having an error handler, # but which obviously succeded. # Decrement it by one, so that we don't remove the request # that generated these events, if there is such a one. - # Bug reported by Ilpo Nyyss�nen - self.get_waiting_request((e.sequence_number - 1) % 65536) + # Bug reported by Ilpo Nyyssönen + # Note: not all events have a sequence_number field! + # (e.g. KeymapNotify). + if hasattr(e, 'sequence_number'): + self.get_waiting_request((e.sequence_number - 1) % 65536) # print 'recv Event:', e diff --git a/Nagstamon/thirdparty/Xlib/protocol/event.py b/Nagstamon/thirdparty/Xlib/protocol/event.py index a0386ded5..04743c6d0 100644 --- a/Nagstamon/thirdparty/Xlib/protocol/event.py +++ b/Nagstamon/thirdparty/Xlib/protocol/event.py @@ -2,26 +2,29 @@ # # Copyright (C) 2000-2002 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA # Xlib modules -from Xlib import X +from .. import X # Xlib.protocol modules -from Xlib.protocol import rq +from . import rq class AnyEvent(rq.Event): @@ -29,7 +32,7 @@ class AnyEvent(rq.Event): _fields = rq.Struct( rq.Card8('type'), rq.Card8('detail'), rq.Card16('sequence_number'), - rq.FixedString('data', 28), + rq.FixedBinary('data', 28), ) class KeyButtonPointer(rq.Event): diff --git a/Nagstamon/thirdparty/Xlib/protocol/request.py b/Nagstamon/thirdparty/Xlib/protocol/request.py index aeb598bcf..b431e137c 100644 --- a/Nagstamon/thirdparty/Xlib/protocol/request.py +++ b/Nagstamon/thirdparty/Xlib/protocol/request.py @@ -2,26 +2,30 @@ # # Copyright (C) 2000-2002 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA # Xlib modules -from Xlib import X +from .. import X # Xlib.protocol modules -from Xlib.protocol import rq, structs +from . import rq +from . import structs class CreateWindow(rq.Request): @@ -783,7 +787,7 @@ class ListFontsWithInfo(rq.ReplyRequest): def __init__(self, *args, **keys): self._fonts = [] - ReplyRequest.__init__(*(self, ) + args, **keys) + rq.ReplyRequest.__init__(self, *args, **keys) def _parse_response(self, data): @@ -1062,7 +1066,7 @@ class PutImage(rq.Request): rq.Card8('left_pad'), rq.Card8('depth'), rq.Pad(2), - rq.String8('data'), + rq.Binary('data'), ) class GetImage(rq.ReplyRequest): @@ -1085,7 +1089,7 @@ class GetImage(rq.ReplyRequest): rq.ReplyLength(), rq.Card32('visual'), rq.Pad(20), - rq.String8('data'), + rq.Binary('data'), ) class PolyText8(rq.Request): @@ -1636,7 +1640,8 @@ class ChangeHosts(rq.Request): rq.Opcode(109), rq.Set('mode', 1, (X.HostInsert, X.HostDelete)), rq.RequestLength(), - rq.Set('host_family', 1, (X.FamilyInternet, X.FamilyDECnet, X.FamilyChaos)), + rq.Set('host_family', 1, (X.FamilyInternet, X.FamilyDECnet, X.FamilyChaos, + X.FamilyServerInterpreted, X.FamilyInternetV6)), rq.Pad(1), rq.LengthOf('host', 2), rq.List('host', rq.Card8Obj) diff --git a/Nagstamon/thirdparty/Xlib/protocol/rq.py b/Nagstamon/thirdparty/Xlib/protocol/rq.py index 3bd342d54..86cb2def5 100644 --- a/Nagstamon/thirdparty/Xlib/protocol/rq.py +++ b/Nagstamon/thirdparty/Xlib/protocol/rq.py @@ -2,41 +2,48 @@ # # Copyright (C) 2000-2002 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - -from array import array -import struct +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +# Standard modules import sys import traceback +import struct +from array import array import types +# Python 2/3 compatibility. +from six import PY3, binary_type, byte2int, indexbytes, iterbytes + # Xlib modules -from Xlib import X -from Xlib.support import lock +from .. import X +from ..support import lock -_PY3 = sys.version[0] >= '3' +def decode_string(bs): + return bs.decode('latin1') -# in Python 3, bytes are an actual array; in python 2, bytes are still -# string-like, so in order to get an array element we need to call ord() -if _PY3: - def _bytes_item(x): - return x +if PY3: + def encode_array(a): + return a.tobytes() else: - def _bytes_item(x): - return ord(x) + def encode_array(a): + return a.tostring() + class BadDataError(Exception): pass @@ -58,7 +65,6 @@ class BadDataError(Exception): pass for c in 'bhil': size = array(c).itemsize - array_unsigned_codes[size] = c.upper() try: struct_to_array_codes[signed_codes[size]] = c @@ -69,7 +75,7 @@ class BadDataError(Exception): pass # print array_unsigned_codes, struct_to_array_codes -class Field: +class Field(object): """Field objects represent the data fields of a Struct. Field objects must have the following attributes: @@ -103,9 +109,7 @@ class Field: If `structcode' is None the Field must have the method f.parse_binary_value() instead. See its documentation string for details. - """ - name = None default = None @@ -134,16 +138,15 @@ def parse_binary_value(self, data, display, length, format): The decoded value is returned as VALUE, and the remaining part of DATA shold be returned as REMAINDATA. """ - - raise RuntimeError('Neither structcode or parse_binary_value provided for %s' - % self) + raise RuntimeError('Neither structcode or parse_binary_value ' \ + 'provided for {0}'.format(self)) class Pad(Field): def __init__(self, size): self.size = size self.value = b'\0' * size - self.structcode = '%dx' % size + self.structcode = '{0}x'.format(size) self.structvalues = 0 @@ -168,7 +171,9 @@ class LengthField(Field): may vary, e.g. List and String8. Its name should be the same as the name of the field whose size - it stores. + it stores. The other_fields attribute can be used to specify the + names of other fields whose sizes are stored by this field, so + a single length field can set the length of multiple fields. The lf.get_binary_value() method of LengthFields is not used, instead a lf.get_binary_length() should be provided. @@ -176,9 +181,9 @@ class LengthField(Field): Unless LengthField.get_binary_length() is overridden in child classes, there should also be a lf.calc_length(). """ - structcode = 'L' structvalues = 1 + other_fields = None def calc_length(self, length): """newlen = lf.calc_length(length) @@ -209,7 +214,11 @@ def calc_length(self, length): class LengthOf(LengthField): def __init__(self, name, size): - self.name = name + if isinstance(name, (list, tuple)): + self.name = name[0] + self.other_fields = name[1:] + else: + self.name = name self.structcode = unsigned_codes[size] @@ -287,9 +296,9 @@ def __init__(self, name, codes = (), default = None): self.codes = codes def check_value(self, value): - try: + if hasattr(value, self.cast_function): return getattr(value, self.cast_function)() - except AttributeError: + else: return value def parse_value(self, value, display): @@ -369,15 +378,15 @@ def __init__(self, name): X.SouthEastGravity)) -class FixedString(ValueField): +class FixedBinary(ValueField): structvalues = 1 def __init__(self, name, size): ValueField.__init__(self, name) - self.structcode = '%ds' % size + self.structcode = '{0}s'.format(size) -class String8(ValueField): +class Binary(ValueField): structcode = None def __init__(self, name, pad = 1): @@ -385,34 +394,57 @@ def __init__(self, name, pad = 1): self.pad = pad def pack_value(self, val): - slen = len(val) + val_bytes = val + slen = len(val_bytes) + + if self.pad: + return val_bytes + b'\0' * ((4 - slen % 4) % 4), slen, None + else: + return val_bytes, slen, None + + def parse_binary_value(self, data, display, length, format): + if length is None: + return data, b'' + + if self.pad: + slen = length + ((4 - length % 4) % 4) + else: + slen = length + + return data[:length], data[slen:] + + +class String8(ValueField): + structcode = None + + def __init__(self, name, pad = 1): + ValueField.__init__(self, name) + self.pad = pad - if _PY3 and type(val) is str: - val = val.encode('UTF-8') + def pack_value(self, val): + if isinstance(val, bytes): + val_bytes = val + else: + val_bytes = val.encode() + slen = len(val_bytes) if self.pad: - return val + b'\0' * ((4 - slen % 4) % 4), slen, None + return val_bytes + b'\0' * ((4 - slen % 4) % 4), slen, None else: - return val, slen, None + return val_bytes, slen, None def parse_binary_value(self, data, display, length, format): if length is None: - try: - return data.decode('UTF-8'), b'' - except UnicodeDecodeError: - return data, b'' + return decode_string(data), b'' if self.pad: slen = length + ((4 - length % 4) % 4) else: slen = length - s = data[:length] - try: - s = s.decode('UTF-8') - except UnicodeDecodeError: - pass # return as bytes - return s, data[slen:] + data_str = decode_string(data[:length]) + + return data_str, data[slen:] class String16(ValueField): @@ -423,9 +455,9 @@ def __init__(self, name, pad = 1): self.pad = pad def pack_value(self, val): - # Convert 8-byte string into 16-byte list - if type(val) is str: - val = [ord(c) for c in val] + """Convert 8-byte string into 16-byte list""" + if isinstance(val, bytes): + val = list(iterbytes(val)) slen = len(val) @@ -434,8 +466,7 @@ def pack_value(self, val): else: pad = b'' - return (struct.pack(*('>' + 'H' * slen, ) + tuple(val)) + pad, - slen, None) + return struct.pack('>' + 'H' * slen, *val) + pad, slen, None def parse_binary_value(self, data, display, length, format): if length == 'odd': @@ -448,8 +479,7 @@ def parse_binary_value(self, data, display, length, format): else: slen = length - return (struct.unpack('>' + 'H' * length, data[:length * 2]), - data[slen * 2:]) + return struct.unpack('>' + 'H' * length, data[:length * 2]), data[slen * 2:] @@ -499,18 +529,14 @@ def parse_binary_value(self, data, display, length, format): ret = [None] * int(length) if self.type.structcode is None: - for i in range(length): + for i in range(0, length): ret[i], data = self.type.parse_binary(data, display) else: scode = '=' + self.type.structcode slen = struct.calcsize(scode) pos = 0 for i in range(0, length): - # FIXME: remove try..except - try: - v = struct.unpack(scode, data[pos: pos + slen]) - except Exception: - v = b'\x00\x00\x00\x00' + v = struct.unpack(scode, data[pos: pos + slen]) if self.type.structvalues == 1: v = v[0] @@ -532,8 +558,10 @@ def parse_binary_value(self, data, display, length, format): def pack_value(self, val): # Single-char values, we'll assume that means integer lists. if self.type.structcode and len(self.type.structcode) == 1: - data = array(struct_to_array_codes[self.type.structcode], - val).tobytes() + if self.type.check_value is not None: + val = [self.type.check_value(v) for v in val] + a = array(struct_to_array_codes[self.type.structcode], val) + data = encode_array(a) else: data = [] for v in val: @@ -563,8 +591,6 @@ def pack_value(self, val): class Object(ValueField): - structcode = None - def __init__(self, name, type, default = None): ValueField.__init__(self, name, default) self.type = type @@ -572,43 +598,32 @@ def __init__(self, name, type, default = None): self.structvalues = self.type.structvalues def parse_binary_value(self, data, display, length, format): - if self.type.structcode is None: - return self.type.parse_binary(data, display) - - else: - scode = '=' + self.type.structcode - slen = struct.calcsize(scode) - - v = struct.unpack(scode, data[:slen]) - if self.type.structvalues == 1: - v = v[0] - - if self.type.parse_value is not None: - v = self.type.parse_value(v, display) - - return v, data[slen:] + return self.type.parse_binary(data, display) def parse_value(self, val, display): - if self.type.parse_value is None: - return val - else: - return self.type.parse_value(val, display) + return self.type.parse_value(val, display) def pack_value(self, val): - # Single-char values, we'll assume that mean an integer - if self.type.structcode and len(self.type.structcode) == 1: - return struct.pack('=' + self.type.structcode, val), None, None - else: - return self.type.pack_value(val) + return self.type.pack_value(val) def check_value(self, val): - if self.type.structcode is None: - return val - - if type(val) is tuple: - return val + if isinstance(val, tuple): + vals = [] + i = 0 + for f in self.type.fields: + if f.name: + if f.check_value is None: + v = val[i] + else: + v = f.check_value(val[i]) + if f.structvalues == 1: + vals.append(v) + else: + vals.extend(v) + i = i + 1 + return vals - if type(val) is dict: + if isinstance(val, dict): data = val elif isinstance(val, DictWrapper): data = val._data @@ -618,7 +633,14 @@ def check_value(self, val): vals = [] for f in self.type.fields: if f.name: - vals.append(data[f.name]) + if f.check_value is None: + v = data[f.name] + else: + v = f.check_value(data[f.name]) + if f.structvalues == 1: + vals.append(v) + else: + vals.extend(v) return vals @@ -634,7 +656,6 @@ def parse_binary_value(self, data, display, length, format): if format == 0: ret = None - return ret, data elif format == 8: ret = (8, data[:length]) @@ -648,24 +669,15 @@ def parse_binary_value(self, data, display, length, format): ret = (32, array(array_unsigned_codes[4], data[:4 * length])) data = data[4 * length:] - if type(ret[1]) is bytes: - try: - ret = (ret[0], ret[1].decode('UTF-8')) - except UnicodeDecodeError: - pass # return as bytes - return ret, data def pack_value(self, value): fmt, val = value if fmt not in (8, 16, 32): - raise BadDataError('Invalid property data format %d' % fmt) - - if _PY3 and type(val) is str: - val = val.encode('UTF-8') + raise BadDataError('Invalid property data format {0}'.format(fmt)) - if type(val) is bytes: + if isinstance(val, binary_type): size = fmt // 8 vlen = len(val) if vlen % size: @@ -677,11 +689,12 @@ def pack_value(self, value): dlen = vlen // size else: - if type(val) is tuple: + if isinstance(val, tuple): val = list(val) size = fmt // 8 - data = array(array_unsigned_codes[size], val).tobytes() + a = array(array_unsigned_codes[size], val) + data = encode_array(a) dlen = len(val) dl = len(data) @@ -716,7 +729,7 @@ class ValueList(Field): def __init__(self, name, mask, pad, *fields): self.name = name - self.maskcode = '=%s%dx' % (unsigned_codes[mask], pad) + self.maskcode = '={0}{1}x'.format(unsigned_codes[mask], pad).encode() self.maskcodelen = struct.calcsize(self.maskcode) self.fields = [] @@ -781,7 +794,7 @@ def parse_binary_value(self, data, display, length, format): else: dlen = 4 * length * format - a = array(array_unsigned_codes[4], data[:dlen]) + a = array(array_unsigned_codes[4], bytes(data[:dlen])) ret = [] for i in range(0, len(a), format): @@ -802,7 +815,7 @@ def pack_value(self, value): for i in range(len(v), keycodes): a.append(X.NoSymbol) - return a.tobytes(), len(value), keycodes + return encode_array(a), len(value), keycodes class ModifierMapping(ValueField): @@ -833,7 +846,7 @@ def pack_value(self, value): for i in range(len(v), keycodes): a.append(0) - return a.tobytes(), len(value), keycodes + return encode_array(a), len(value), keycodes class EventField(ValueField): structcode = None @@ -845,9 +858,12 @@ def pack_value(self, value): return value._binary, None, None def parse_binary_value(self, data, display, length, format): - from Xlib.protocol import event + from . import event - estruct = display.event_classes.get(_bytes_item(data[0]) & 0x7f, event.AnyEvent) + estruct = display.event_classes.get(byte2int(data) & 0x7f, event.AnyEvent) + if type(estruct) == dict: + # this etype refers to a set of sub-events with individual subcodes + estruct = estruct[indexbytes(data, 1)] return estruct(display = display, binarydata = data[:32]), data[32:] @@ -857,22 +873,24 @@ def parse_binary_value(self, data, display, length, format): # Struct is also usable. # -class ScalarObj: +class ScalarObj(object): def __init__(self, code): self.structcode = code self.structvalues = 1 self.parse_value = None + self.check_value = None Card8Obj = ScalarObj('B') Card16Obj = ScalarObj('H') Card32Obj = ScalarObj('L') -class ResourceObj: +class ResourceObj(object): structcode = 'L' structvalues = 1 def __init__(self, class_name): self.class_name = class_name + self.check_value = None def parse_value(self, value, display): # if not display: @@ -886,31 +904,20 @@ def parse_value(self, value, display): WindowObj = ResourceObj('window') ColormapObj = ResourceObj('colormap') -class StrClass: +class StrClass(object): structcode = None def pack_value(self, val): - if type(val) is not bytes: - val = val.encode('UTF-8') - if _PY3: - val = bytes([len(val)]) + val - else: - val = chr(len(val)) + val - return val + return (chr(len(val)) + val).encode() def parse_binary(self, data, display): - slen = _bytes_item(data[0]) + 1 - s = data[1:slen] - try: - s = s.decode('UTF-8') - except UnicodeDecodeError: - pass # return as bytes - return s, data[slen:] + slen = byte2int(data) + 1 + return decode_string(data[1:slen]), data[slen:] Str = StrClass() -class Struct: +class Struct(object): """Struct objects represents a binary data structure. It can contain both fields with static and dynamic sizes. However, all @@ -930,7 +937,6 @@ class Field. The fields of a structure are given as arguments These functions will be generated dynamically for each Struct object to make conversion as fast as possible. They are generated the first time the methods are called. - """ def __init__(self, *fields): @@ -975,7 +981,6 @@ def __init__(self, *fields): # object def to_binary(self, *varargs, **keys): - """data = s.to_binary(...) Convert Python values into the binary representation. The @@ -984,161 +989,86 @@ def to_binary(self, *varargs, **keys): exception: fields with default arguments will be last. Returns the binary representation as the string DATA. - """ - - code = '' - total_length = str(self.static_size) - joins = [] - args = [] - defargs = [] - kwarg = 0 + # Emulate Python function argument handling with our field names + names = [f.name for f in self.fields \ + if isinstance(f, ValueField) and f.name] + field_args = dict(zip(names, varargs)) + if set(field_args).intersection(keys): + dupes = ", ".join(set(field_args).intersection(keys)) + raise TypeError("{0} arguments were passed both positionally and by keyword".format(dupes)) + field_args.update(keys) + for f in self.fields: + if f.name and (f.name not in field_args): + if f.default is None: + raise TypeError("Missing required argument {0}".format(f.name)) + field_args[f.name] = f.default + # /argument handling # First pack all varfields so their lengths and formats are # available when we pack their static LengthFields and # FormatFields - i = 0 + total_length = self.static_size + var_vals = {} + lengths = {} + formats = {} + for f in self.var_fields: if f.keyword_args: - kwarg = 1 - kw = ', _keyword_args' + v, l, fm = f.pack_value(field_args[f.name], keys) else: - kw = '' - - # Call pack_value method for each field, storing - # the return values for later use - code = code + (' _%(name)s, _%(name)s_length, _%(name)s_format' - ' = self.var_fields[%(fno)d].pack_value(%(name)s%(kw)s)\n' - % { 'name': f.name, - 'fno': i, - 'kw': kw }) - - total_length = total_length + ' + len(_%s)' % f.name - joins.append('_%s' % f.name) + v, l, fm = f.pack_value(field_args[f.name]) + var_vals[f.name] = v + lengths[f.name] = l + formats[f.name] = fm - i = i + 1 + total_length += len(v) - # Construct argument list for struct.pack call, packing all - # static fields. First argument is the structcode, the - # remaining are values. + # Construct item list for struct.pack call, packing all static fields. + pack_items = [] - - pack_args = ['"%s"' % self.static_codes] - - i = 0 for f in self.static_fields: if isinstance(f, LengthField): # If this is a total length field, insert # the calculated field value here if isinstance(f, TotalLengthField): - if self.var_fields: - pack_args.append('self.static_fields[%d].calc_length(%s)' - % (i, total_length)) - else: - pack_args.append(str(f.calc_length(self.static_size))) + pack_items.append(f.calc_length(total_length)) else: - pack_args.append('self.static_fields[%d].calc_length(_%s_length)' - % (i, f.name)) + pack_items.append(f.calc_length(lengths[f.name])) # Format field, just insert the value we got previously elif isinstance(f, FormatField): - pack_args.append('_%s_format' % f.name) + pack_items.append(formats[f.name]) # A constant field, insert its value directly elif isinstance(f, ConstantField): - pack_args.append(str(f.value)) + pack_items.append(f.value) # Value fields else: if f.structvalues == 1: - # If there's a value check/convert function, call it if f.check_value is not None: - pack_args.append('self.static_fields[%d].check_value(%s)' - % (i, f.name)) - + pack_items.append(f.check_value(field_args[f.name])) # Else just use the argument as provided else: - pack_args.append(f.name) + pack_items.append(field_args[f.name]) # Multivalue field. Handled like single valuefield, - # but the value are tuple unpacked into seperate arguments - # which are appended to pack_args + # but the value are tuple unpacked into separate arguments + # which are appended to pack_items else: - a = [] - for j in range(f.structvalues): - a.append('_%s_%d' % (f.name, j)) - if f.check_value is not None: - code = code + (' %s = self.static_fields[%d].check_value(%s)\n' - % (', '.join(a), i, f.name)) + pack_items.extend(f.check_value(field_args[f.name])) else: - code = code + ' %s = %s\n' % (', '.join(a), f.name) - - pack_args = pack_args + a - - # Add field to argument list - if f.name: - if f.default is None: - args.append(f.name) - else: - defargs.append('%s = %s' % (f.name, repr(f.default))) - - i = i + 1 - - # Construct call to struct.pack - pack = 'struct.pack(%s)' % ', '.join(pack_args) - - # If there are any varfields, we append the packed strings to build - # the resulting binary value - if self.var_fields: - code = code + ' return %s + %s\n' % (pack, ' + '.join(joins)) - - # If there's only static fields, return the packed value - else: - code = code + ' return %s\n' % pack - - # Add all varsize fields to argument list. We do it here - # to ensure that they appear after the static fields. - for f in self.var_fields: - if f.name: - if f.default is None: - args.append(f.name) - else: - defargs.append('%s = %s' % (f.name, repr(f.default))) - - args = args + defargs - if kwarg: - args.append('**_keyword_args') + pack_items.extend(field_args[f.name]) - # Add function header - code = 'def to_binary(self, %s):\n' % ', '.join(args) + code - - # self._pack_code = code - - # print - # print code - # print - - # Finally, compile function by evaluating it. This will store - # the function in the local variable to_binary, thanks to the - # def: line. Convert it into a instance metod bound to self, - # and store it in self. - - # Unfortunately, this creates a circular reference. However, - # Structs are not really created dynamically so the potential - # memory leak isn't that serious. Besides, Python 2.0 has - # real garbage collect. - - g = globals().copy() - exec(code, g) - self.to_binary = types.MethodType(g['to_binary'], self) - - # Finally call it manually - return self.to_binary(*varargs, **keys) + static_part = struct.pack(self.static_codes, *pack_items) + var_parts = [var_vals[f.name] for f in self.var_fields] + return static_part + b''.join(var_parts) def pack_value(self, value): @@ -1150,11 +1080,11 @@ def pack_value(self, value): """ if type(value) is tuple: - return self.to_binary(*value, **{}) - elif type(value) is dict: - return self.to_binary(*(), **value) + return self.to_binary(*value) + elif isinstance(value, dict): + return self.to_binary(**value) elif isinstance(value, DictWrapper): - return self.to_binary(*(), **value._data) + return self.to_binary(**value._data) else: raise BadDataError('%s is not a tuple or a list' % (value)) @@ -1165,12 +1095,8 @@ def parse_value(self, val, display, rawdict = 0): Struct objects with no var_fields into Python values. """ - - code = ('def parse_value(self, val, display, rawdict = 0):\n' - ' ret = {}\n') - + ret = {} vno = 0 - fno = 0 for f in self.static_fields: # Fields without names should be ignored, and there should # not be any length or format fields if this function @@ -1189,43 +1115,22 @@ def parse_value(self, val, display, rawdict = 0): # Value fields else: - - # Get the index or range in val representing this field. - - if f.structvalues == 1: - vrange = str(vno) - else: - vrange = '%d:%d' % (vno, vno + f.structvalues) - # If this field has a parse_value method, call it, otherwise # use the unpacked value as is. - - if f.parse_value is None: - code = code + ' ret["%s"] = val[%s]\n' % (f.name, vrange) + if f.structvalues == 1: + field_val = val[vno] else: - code = code + (' ret["%s"] = self.static_fields[%d].' - 'parse_value(val[%s], display)\n' - % (f.name, fno, vrange)) - - fno = fno + 1 - vno = vno + f.structvalues - - code = code + ' if not rawdict: return DictWrapper(ret)\n' - code = code + ' return ret\n' + field_val = val[vno:vno+f.structvalues] - # print - # print code - # print + if f.parse_value is not None: + field_val = f.parse_value(field_val, display, rawdict=rawdict) + ret[f.name] = field_val - # Finally, compile function as for to_binary. - - g = globals().copy() - exec(code, g) - self.parse_value = types.MethodType(g['parse_value'], self) - - # Call it manually - return self.parse_value(val, display, rawdict) + vno = vno + f.structvalues + if not rawdict: + return DictWrapper(ret) + return ret def parse_binary(self, data, display, rawdict = 0): @@ -1246,17 +1151,12 @@ def parse_binary(self, data, display, rawdict = 0): REMDATA are the remaining binary data, unused by the Struct object. """ - - code = ('def parse_binary(self, data, display, rawdict = 0):\n' - ' ret = {}\n' - ' val = struct.unpack("%s", data[:%d])\n' - % (self.static_codes, self.static_size)) - + ret = {} + val = struct.unpack(self.static_codes, data[:self.static_size]) lengths = {} formats = {} vno = 0 - fno = 0 for f in self.static_fields: # Fields without name should be ignored. This is typically @@ -1269,63 +1169,45 @@ def parse_binary(self, data, display, rawdict = 0): # when treating varfields. elif isinstance(f, LengthField): - if f.parse_value is None: - lengths[f.name] = 'val[%d]' % vno - else: - lengths[f.name] = ('self.static_fields[%d].' - 'parse_value(val[%d], display)' - % (fno, vno)) + f_names = [f.name] + if f.other_fields: + f_names.extend(f.other_fields) + field_val = val[vno] + if f.parse_value is not None: + field_val = f.parse_value(field_val, display) + for f_name in f_names: + lengths[f_name] = field_val elif isinstance(f, FormatField): - formats[f.name] = 'val[%d]' % vno + formats[f.name] = val[vno] # Treat value fields the same was as in parse_value. else: if f.structvalues == 1: - vrange = str(vno) + field_val = val[vno] else: - vrange = '%d:%d' % (vno, vno + f.structvalues) + field_val = val[vno:vno+f.structvalues] - if f.parse_value is None: - code = code + ' ret["%s"] = val[%s]\n' % (f.name, vrange) - else: - code = code + (' ret["%s"] = self.static_fields[%d].' - 'parse_value(val[%s], display)\n' - % (f.name, fno, vrange)) + if f.parse_value is not None: + field_val = f.parse_value(field_val, display) + ret[f.name] = field_val - fno = fno + 1 vno = vno + f.structvalues - code = code + ' data = data[%d:]\n' % self.static_size + data = data[self.static_size:] # Call parse_binary_value for each var_field, passing the # length and format values from the unpacked val. - fno = 0 for f in self.var_fields: - code = code + (' ret["%s"], data = ' - 'self.var_fields[%d].parse_binary_value' - '(data, display, %s, %s)\n' - % (f.name, fno, - lengths.get(f.name, 'None'), - formats.get(f.name, 'None'))) - fno = fno + 1 - - code = code + ' if not rawdict: ret = DictWrapper(ret)\n' - code = code + ' return ret, data\n' - - # print - # print code - # print + ret[f.name], data = f.parse_binary_value(data, display, + lengths.get(f.name), + formats.get(f.name), + ) - # Finally, compile function as for to_binary. - - g = globals().copy() - exec(code, g) - self.parse_binary = types.MethodType(g['parse_binary'], self) - - # Call it manually - return self.parse_binary(data, display, rawdict) + if not rawdict: + ret = DictWrapper(ret) + return ret, data class TextElements8(ValueField): @@ -1339,37 +1221,33 @@ def pack_value(self, value): for v in value: # Let values be simple strings, meaning a delta of 0 - if _PY3 and type(v) is str: - v = v.encode('UTF-8') - - if type(v) is bytes: + if type(v) in (str, bytes): v = (0, v) # A tuple, it should be (delta, string) # Encode it as one or more textitems - if type(v) in (tuple, dict) or \ - isinstance(v, DictWrapper): + if isinstance(v, (tuple, dict, DictWrapper)): - if type(v) is tuple: - delta, s = v + if isinstance(v, tuple): + delta, m_str = v else: delta = v['delta'] - s = v['string'] + m_str = v['string'] - while delta or s: + while delta or m_str: args['delta'] = delta - args['string'] = s[:254] + args['string'] = m_str[:254] data = data + self.string_textitem.to_binary(*(), **args) delta = 0 - s = s[254:] + m_str = m_str[254:] # Else an integer, i.e. a font change else: # Use fontable cast function if instance - if hasattr(v, '__fontable__'): + if isinstance(v, Fontable): v = v.__fontable__() data = data + struct.pack('>BL', 255, v) @@ -1385,12 +1263,12 @@ def parse_binary_value(self, data, display, length, format): break # font change - if _bytes_item(data[0]) == 255: - values.append(struct.unpack('>L', data[1:5])[0]) + if byte2int(data) == 255: + values.append(struct.unpack('>L', bytes(data[1:5]))[0]) data = data[5:] # skip null strings - elif _bytes_item(data[0]) == 0 and _bytes_item(data[1]) == 0: + elif byte2int(data) == 0 and indexbytes(data, 1) == 0: data = data[2:] # string with delta @@ -1398,7 +1276,7 @@ def parse_binary_value(self, data, display, length, format): v, data = self.string_textitem.parse_binary(data, display) values.append(v) - return values, b'' + return values, '' @@ -1442,7 +1320,19 @@ def __str__(self): return str(self._data) def __repr__(self): - return '%s(%s)' % (self.__class__, repr(self._data)) + return '%s(%s)' % (self.__class__.__name__, repr(self._data)) + + def __lt__(self, other): + if isinstance(other, DictWrapper): + return self._data < other._data + else: + return self._data < other + + def __gt__(self, other): + if isinstance(other, DictWrapper): + return self._data > other._data + else: + return self._data > other def __eq__(self, other): if isinstance(other, DictWrapper): @@ -1450,10 +1340,8 @@ def __eq__(self, other): else: return self._data == other - def __ne__(self, other): - return not self.__eq__(other) -class Request: +class Request(object): def __init__(self, display, onerror = None, *args, **keys): self._errorhandler = onerror self._binary = self._request.to_binary(*args, **keys) @@ -1512,7 +1400,7 @@ def _set_error(self, error): return 1 def __repr__(self): - return '<%s serial = %s, data = %s, error = %s>' % (self.__class__, self._serial, self._data, self._error) + return '<%s serial = %s, data = %s, error = %s>' % (self.__class__.__name__, self._serial, self._data, self._error) class Event(GetAttrData): @@ -1531,7 +1419,7 @@ def __init__(self, binarydata = None, display = None, keys['sequence_number'] = 0 - self._binary = self._fields.to_binary(*(), **keys) + self._binary = self._fields.to_binary(**keys) keys['send_event'] = 0 self._data = keys @@ -1546,13 +1434,26 @@ def __repr__(self): kwlist.append('%s = %s' % (kw, repr(val))) kws = ', '.join(kwlist) - return '%s(%s)' % (self.__class__, kws) + return '%s(%s)' % (self.__class__.__name__, kws) + + def __lt__(self, other): + if isinstance(other, Event): + return self._data < other._data + else: + return self._data < other + + def __gt__(self, other): + if isinstance(other, Event): + return self._data > other._data + else: + return self._data > other def __eq__(self, other): if isinstance(other, Event): return self._data == other._data else: - return cmp(self._data, other) + return self._data == other + def call_error_handler(handler, error, request): try: diff --git a/Nagstamon/thirdparty/Xlib/protocol/structs.py b/Nagstamon/thirdparty/Xlib/protocol/structs.py index bc548f67d..1661440d6 100644 --- a/Nagstamon/thirdparty/Xlib/protocol/structs.py +++ b/Nagstamon/thirdparty/Xlib/protocol/structs.py @@ -2,25 +2,28 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA # Xlib modules -from Xlib import X +from .. import X # Xlib.protocol modules -from Xlib.protocol import rq +from . import rq def WindowValues(arg): return rq.ValueList( arg, 4, 0, diff --git a/Nagstamon/thirdparty/Xlib/rdb.py b/Nagstamon/thirdparty/Xlib/rdb.py index 773158a95..03b06e2a2 100644 --- a/Nagstamon/thirdparty/Xlib/rdb.py +++ b/Nagstamon/thirdparty/Xlib/rdb.py @@ -2,19 +2,22 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA # See end of file for an explanation of the algorithm and @@ -22,12 +25,11 @@ # Standard modules -import functools import re import sys # Xlib modules -from Xlib.support import lock +from .support import lock # Set up a few regexpes for parsing string representation of resources @@ -48,7 +50,7 @@ class OptionError(Exception): pass -class ResourceDB: +class ResourceDB(object): def __init__(self, file = None, string = None, resources = None): self.db = {} self.lock = lock.allocate_lock() @@ -68,7 +70,7 @@ def insert_file(self, file): """ - if type(file) is str: + if type(file) is bytes: file = open(file, 'r') self.insert_string(file.read()) @@ -188,15 +190,15 @@ def insert(self, resource, value): self.lock.release() - def __getitem__(self, nc): + def __getitem__(self, keys_tuple): """db[name, class] Return the value matching the resource identified by NAME and CLASS. If no match is found, KeyError is raised. """ - name, cls = nc # Split name and class into their parts + name, cls = keys_tuple namep = name.split('.') clsp = cls.split('.') @@ -376,8 +378,7 @@ def getopt(self, name, argv, opts): return argv -@functools.total_ordering -class _Match: +class _Match(object): def __init__(self, path, dbs): self.path = path @@ -389,12 +390,15 @@ def __init__(self, path, dbs): self.skip = 1 self.db = dbs - def __eq__(self, other): - return self.path == other.path - def __lt__(self, other): return self.path < other.path + def __gt__(self, other): + return self.path > other.path + + def __eq__(self, other): + return self.path == other.path + def match_length(self): return len(self.path) @@ -555,7 +559,7 @@ def output_escape(value): # Option type definitions # -class Option: +class Option(object): def __init__(self): pass @@ -695,7 +699,7 @@ def get_display_opts(options, argv = sys.argv): # the resource object. # Example: Inserting "foo.bar*gazonk: yep" into an otherwise empty -# resource database would give the folliwing structure: +# resource database would give the following structure: # { 'foo': ( { 'bar': ( { }, # { 'gazonk': ( { }, diff --git a/Nagstamon/thirdparty/Xlib/support/__init__.py b/Nagstamon/thirdparty/Xlib/support/__init__.py index 40d05b7d0..4c0d622bf 100644 --- a/Nagstamon/thirdparty/Xlib/support/__init__.py +++ b/Nagstamon/thirdparty/Xlib/support/__init__.py @@ -2,19 +2,22 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA __all__ = [ 'lock', diff --git a/Nagstamon/thirdparty/Xlib/support/connect.py b/Nagstamon/thirdparty/Xlib/support/connect.py index ca8e68b22..4db4c2f47 100644 --- a/Nagstamon/thirdparty/Xlib/support/connect.py +++ b/Nagstamon/thirdparty/Xlib/support/connect.py @@ -2,21 +2,25 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA import sys +import importlib # List the modules which contain the corresponding functions @@ -42,50 +46,57 @@ # Figure out which OS we're using. # sys.platform is either "OS-ARCH" or just "OS". -platform = sys.platform.split('-')[0] +_parts = sys.platform.split('-') +platform = _parts[0] +del _parts + + +def _relative_import(modname): + return importlib.import_module('..' + modname, __name__) def get_display(display): - """dname, host, dno, screen = get_display(display) + """dname, protocol, host, dno, screen = get_display(display) Parse DISPLAY into its components. If DISPLAY is None, use the default display. The return values are: - DNAME -- the full display name (string) - HOST -- the host name (string, possibly empty) - DNO -- display number (integer) - SCREEN -- default screen number (integer) + DNAME -- the full display name (string) + PROTOCOL -- the protocol to use (None if automatic) + HOST -- the host name (string, possibly empty) + DNO -- display number (integer) + SCREEN -- default screen number (integer) """ modname = _display_mods.get(platform, _default_display_mod) - mod = __import__(modname, globals(),level=1) + mod = _relative_import(modname) return mod.get_display(display) -def get_socket(dname, host, dno): - """socket = get_socket(dname, host, dno) +def get_socket(dname, protocol, host, dno): + """socket = get_socket(dname, protocol, host, dno) - Connect to the display specified by DNAME, HOST and DNO, which + Connect to the display specified by DNAME, PROTOCOL, HOST and DNO, which are the corresponding values from a previous call to get_display(). Return SOCKET, a new socket object connected to the X server. """ modname = _socket_mods.get(platform, _default_socket_mod) - mod = __import__(modname, globals(),level=1) - return mod.get_socket(dname, host, dno) + mod = _relative_import(modname) + return mod.get_socket(dname, protocol, host, dno) -def get_auth(sock, dname, host, dno): - """auth_name, auth_data = get_auth(sock, dname, host, dno) +def get_auth(sock, dname, protocol, host, dno): + """auth_name, auth_data = get_auth(sock, dname, protocol, host, dno) Return authentication data for the display on the other side of - SOCK, which was opened with DNAME, HOST and DNO. + SOCK, which was opened with DNAME, HOST and DNO, using PROTOCOL. Return AUTH_NAME and AUTH_DATA, two strings to be used in the connection setup request. """ modname = _auth_mods.get(platform, _default_auth_mod) - mod = __import__(modname, globals(),level=1) - return mod.get_auth(sock, dname, host, dno) + mod = _relative_import(modname) + return mod.get_auth(sock, dname, protocol, host, dno) diff --git a/Nagstamon/thirdparty/Xlib/support/lock.py b/Nagstamon/thirdparty/Xlib/support/lock.py index 4398f428f..6eee31f40 100644 --- a/Nagstamon/thirdparty/Xlib/support/lock.py +++ b/Nagstamon/thirdparty/Xlib/support/lock.py @@ -2,21 +2,24 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - -class _DummyLock: +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA + +class _DummyLock(object): def __init__(self): # This might be nerdy, but by assigning methods like this diff --git a/Nagstamon/thirdparty/Xlib/support/unix_connect.py b/Nagstamon/thirdparty/Xlib/support/unix_connect.py index 10ec0a141..c2261dae5 100644 --- a/Nagstamon/thirdparty/Xlib/support/unix_connect.py +++ b/Nagstamon/thirdparty/Xlib/support/unix_connect.py @@ -1,108 +1,160 @@ # Xlib.support.unix_connect -- Unix-type display connection functions # # Copyright (C) 2000,2002 Peter Liljenberg <petli@ctrl-c.liu.se> -# Copyright (C) 2013 LiuLang <gsushzhsosgsu@gmail.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA -import fcntl +import re import os import platform -import re import socket -F_SETFD = fcntl.F_SETFD -FD_CLOEXEC = fcntl.FD_CLOEXEC +# FCNTL is deprecated from Python 2.2, so only import it if we doesn't +# get the names we need. Furthermore, FD_CLOEXEC seems to be missing +# in Python 2.2. + +import fcntl + +if hasattr(fcntl, 'F_SETFD'): + F_SETFD = fcntl.F_SETFD + if hasattr(fcntl, 'FD_CLOEXEC'): + FD_CLOEXEC = fcntl.FD_CLOEXEC + else: + FD_CLOEXEC = 1 +else: + from FCNTL import F_SETFD, FD_CLOEXEC + from Xlib import error, xauth + +SUPPORTED_PROTOCOLS = (None, 'tcp', 'unix') + +# Darwin funky socket. uname = platform.uname() if (uname[0] == 'Darwin') and ([int(x) for x in uname[2].split('.')] >= [9, 0]): + SUPPORTED_PROTOCOLS += ('darwin',) + DARWIN_DISPLAY_RE = re.compile(r'^/private/tmp/[-:a-zA-Z0-9._]*:(?P<dno>[0-9]+)(\.(?P<screen>[0-9]+))?$') - display_re = re.compile(r'^([-a-zA-Z0-9._/]*):([0-9]+)(\.([0-9]+))?$') - -else: +DISPLAY_RE = re.compile(r'^((?P<proto>tcp|unix)/)?(?P<host>[-:a-zA-Z0-9._]*):(?P<dno>[0-9]+)(\.(?P<screen>[0-9]+))?$') - display_re = re.compile(r'^([-a-zA-Z0-9._]*):([0-9]+)(\.([0-9]+))?$') def get_display(display): # Use $DISPLAY if display isn't provided if display is None: display = os.environ.get('DISPLAY', '') - m = display_re.match(display) - if not m: + re_list = [(DISPLAY_RE, {})] + + if 'darwin' in SUPPORTED_PROTOCOLS: + re_list.insert(0, (DARWIN_DISPLAY_RE, {'protocol': 'darwin'})) + + for re, defaults in re_list: + m = re.match(display) + if m is not None: + protocol, host, dno, screen = [ + m.groupdict().get(field, defaults.get(field)) + for field in ('proto', 'host', 'dno', 'screen') + ] + break + else: raise error.DisplayNameError(display) - name = display - host = m.group(1) - if host == 'unix': - host = '' - dno = int(m.group(2)) - screen = m.group(4) + if protocol == 'tcp' and not host: + # Host is mandatory when protocol is TCP. + raise error.DisplayNameError(display) + + dno = int(dno) if screen: screen = int(screen) else: screen = 0 - return name, host, dno, screen + return display, protocol, host, dno, screen + +def _get_tcp_socket(host, dno): + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect((host, 6000 + dno)) + return s -def get_socket(dname, host, dno): +def _get_unix_socket(address): + s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + s.connect(address) + return s + +def get_socket(dname, protocol, host, dno): + assert protocol in SUPPORTED_PROTOCOLS try: - # Darwin funky socket - if (uname[0] == 'Darwin') and host and host.startswith('/tmp/'): - s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - s.connect(dname) + # Darwin funky socket. + if protocol == 'darwin': + s = _get_unix_socket(dname) - # If hostname (or IP) is provided, use TCP socket - elif host: - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.connect((host, 6000 + dno)) + # TCP socket, note the special case: `unix:0.0` is equivalent to `:0.0`. + elif (protocol is None or protocol != 'unix') and host and host != 'unix': + s = _get_tcp_socket(host, dno) - # Else use Unix socket + # Unix socket. else: - s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - s.connect('/tmp/.X11-unix/X%d' % dno) - except OSError as val: + address = '/tmp/.X11-unix/X%d' % dno + if not os.path.exists(address): + # Use abstract address. + address = '\0' + address + try: + s = _get_unix_socket(address) + except socket.error: + if not protocol and not host: + # If no protocol/host was specified, fallback to TCP. + s = _get_tcp_socket(host, dno) + else: + raise + except socket.error as val: raise error.DisplayConnectionError(dname, str(val)) - # Make sure that the connection isn't inherited in child processes + # Make sure that the connection isn't inherited in child processes. fcntl.fcntl(s.fileno(), F_SETFD, FD_CLOEXEC) return s -def new_get_auth(sock, dname, host, dno): +def new_get_auth(sock, dname, protocol, host, dno): + assert protocol in SUPPORTED_PROTOCOLS # Translate socket address into the xauth domain - if (uname[0] == 'Darwin') and host and host.startswith('/tmp/'): + if protocol == 'darwin': family = xauth.FamilyLocal addr = socket.gethostname() - elif host: + elif protocol == 'tcp': family = xauth.FamilyInternet # Convert the prettyprinted IP number into 4-octet string. # Sometimes these modules are too damn smart... octets = sock.getpeername()[0].split('.') - addr = ''.join(map(lambda x: chr(int(x)), octets)) + addr = bytearray(int(x) for x in octets) else: family = xauth.FamilyLocal - addr = socket.gethostname() + addr = socket.gethostname().encode() + + try: + au = xauth.Xauthority() + except error.XauthError: + return b'', b'' - au = xauth.Xauthority() while 1: try: return au.get_best_auth(family, addr, dno) @@ -113,16 +165,16 @@ def new_get_auth(sock, dname, host, dno): # $DISPLAY to localhost:10, but stores the xauth cookie as if # DISPLAY was :10. Hence, if localhost and not found, try # again as a Unix socket. - if family == xauth.FamilyInternet and addr == '\x7f\x00\x00\x01': + if family == xauth.FamilyInternet and addr == b'\x7f\x00\x00\x01': family = xauth.FamilyLocal - addr = socket.gethostname() + addr = socket.gethostname().encode() else: - return '', '' + return b'', b'' def old_get_auth(sock, dname, host, dno): # Find authorization cookie - auth_name = auth_data = '' + auth_name = auth_data = b'' try: # We could parse .Xauthority, but xauth is simpler @@ -139,7 +191,7 @@ def old_get_auth(sock, dname, host, dno): if len(parts) == 3: auth_name = parts[1] hexauth = parts[2] - auth = '' + auth = b'' # Translate hexcode into binary for i in range(0, len(hexauth), 2): @@ -149,14 +201,6 @@ def old_get_auth(sock, dname, host, dno): except os.error: pass - if not auth_data and host == 'localhost': - # 127.0.0.1 counts as FamilyLocal, not FamilyInternet - # See Xtransutil.c:ConvertAddress. - # There might be more ways to spell 127.0.0.1 but - # 'localhost', yet this code fixes a the case of - # OpenSSH tunneling X. - return get_auth('unix:%d' % dno, 'unix', dno) - return auth_name, auth_data get_auth = new_get_auth diff --git a/Nagstamon/thirdparty/Xlib/support/vms_connect.py b/Nagstamon/thirdparty/Xlib/support/vms_connect.py index 26d9eca90..3c53695f3 100644 --- a/Nagstamon/thirdparty/Xlib/support/vms_connect.py +++ b/Nagstamon/thirdparty/Xlib/support/vms_connect.py @@ -2,19 +2,22 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA import re import socket @@ -29,7 +32,7 @@ def get_display(display): # check DECW$DISPLAY instead, but that has to wait if display is None: - return ':0.0', 'localhost', 0, 0 + return ':0.0', None, 'localhost', 0, 0 m = display_re.match(display) if not m: @@ -49,10 +52,10 @@ def get_display(display): else: screen = 0 - return name, host, dno, screen + return name, None, host, dno, screen -def get_socket(dname, host, dno): +def get_socket(dname, protocol, host, dno): try: # Always use TCP/IP sockets. Later it would be nice to # be able to use DECNET och LOCAL connections. @@ -60,7 +63,7 @@ def get_socket(dname, host, dno): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((host, 6000 + dno)) - except OSError as val: + except socket.error as val: raise error.DisplayConnectionError(dname, str(val)) return s diff --git a/Nagstamon/thirdparty/Xlib/threaded.py b/Nagstamon/thirdparty/Xlib/threaded.py index a9c6fc0f7..44fcafebe 100644 --- a/Nagstamon/thirdparty/Xlib/threaded.py +++ b/Nagstamon/thirdparty/Xlib/threaded.py @@ -2,29 +2,27 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA -try: - # Python 3 - import _thread as thread -except ImportError: - # Python 2 - import thread +from six.moves import _thread # We change the allocate_lock function in Xlib.support.lock to # return a basic Python lock, instead of the default dummy lock from Xlib.support import lock -lock.allocate_lock = thread.allocate_lock +lock.allocate_lock = _thread.allocate_lock diff --git a/Nagstamon/thirdparty/Xlib/xauth.py b/Nagstamon/thirdparty/Xlib/xauth.py index 921985626..303bd4911 100644 --- a/Nagstamon/thirdparty/Xlib/xauth.py +++ b/Nagstamon/thirdparty/Xlib/xauth.py @@ -1,21 +1,23 @@ # Xlib.xauth -- ~/.Xauthority access # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> -# Copyright (C) 2013 LiuLang <gsushzhsosgsu@gmail.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA import os import struct @@ -25,9 +27,11 @@ FamilyInternet = X.FamilyInternet FamilyDECnet = X.FamilyDECnet FamilyChaos = X.FamilyChaos +FamilyServerInterpreted = X.FamilyServerInterpreted +FamilyInternetV6 = X.FamilyInternetV6 FamilyLocal = 256 -class Xauthority: +class Xauthority(object): def __init__(self, filename = None): if filename is None: filename = os.environ.get('XAUTHORITY') @@ -40,9 +44,10 @@ def __init__(self, filename = None): '$HOME not set, cannot find ~/.Xauthority') try: - raw = open(filename, 'rb').read() - except OSError as err: - raise error.XauthError('~/.Xauthority: %s' % err) + with open(filename, 'rb') as fp: + raw = fp.read() + except IOError as err: + raise error.XauthError('could not read from {0}: {1}'.format(filename, err)) self.entries = [] @@ -82,9 +87,9 @@ def __init__(self, filename = None): if len(data) != length: break - self.entries.append((family, addr, num, name, data, )) - except struct.error as e: - print("Xlib.xauth: warning, failed to parse part of xauthority file (%s), aborting all further parsing" % filename) + self.entries.append((family, addr, num, name, data)) + except struct.error: + print("Xlib.xauth: warning, failed to parse part of xauthority file {0}, aborting all further parsing".format(filename)) if len(self.entries) == 0: print("Xlib.xauth: warning, no xauthority details available") @@ -111,11 +116,12 @@ def get_best_auth(self, family, address, dispno, """ num = str(dispno).encode() - address = address.encode() matches = {} for efam, eaddr, enum, ename, edata in self.entries: + if enum == b'' and ename not in matches: + enum = num if efam == family and eaddr == address and num == enum: matches[ename] = edata diff --git a/Nagstamon/thirdparty/Xlib/xobject/__init__.py b/Nagstamon/thirdparty/Xlib/xobject/__init__.py index 7375b37d4..67d325493 100644 --- a/Nagstamon/thirdparty/Xlib/xobject/__init__.py +++ b/Nagstamon/thirdparty/Xlib/xobject/__init__.py @@ -2,19 +2,22 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA __all__ = [ 'colormap', diff --git a/Nagstamon/thirdparty/Xlib/xobject/colormap.py b/Nagstamon/thirdparty/Xlib/xobject/colormap.py index 954502837..033fb4936 100644 --- a/Nagstamon/thirdparty/Xlib/xobject/colormap.py +++ b/Nagstamon/thirdparty/Xlib/xobject/colormap.py @@ -2,25 +2,29 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - -import re +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA from Xlib import error from Xlib.protocol import request -from Xlib.xobject import resource + +from . import resource + +import re rgb_res = [ re.compile(r'\Argb:([0-9a-fA-F]{1,4})/([0-9a-fA-F]{1,4})/([0-9a-fA-F]{1,4})\Z'), diff --git a/Nagstamon/thirdparty/Xlib/xobject/cursor.py b/Nagstamon/thirdparty/Xlib/xobject/cursor.py index 0d373570f..432e4fd9c 100644 --- a/Nagstamon/thirdparty/Xlib/xobject/cursor.py +++ b/Nagstamon/thirdparty/Xlib/xobject/cursor.py @@ -2,22 +2,26 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA from Xlib.protocol import request -from Xlib.xobject import resource + +from . import resource class Cursor(resource.Resource): __cursor__ = resource.Resource.__resource__ @@ -28,9 +32,10 @@ def free(self, onerror = None): cursor = self.id) self.display.free_resource_id(self.id) - def recolor(self, f_rgb, b_rgb, onerror = None): - back_red, back_green, back_blue = b_rgb - fore_red, fore_green, fore_blue = f_rgb + def recolor(self, foreground, background, onerror=None): + fore_red, fore_green, fore_blue = foreground + back_red, back_green, back_blue = background + request.RecolorCursor(display = self.display, onerror = onerror, cursor = self.id, diff --git a/Nagstamon/thirdparty/Xlib/xobject/drawable.py b/Nagstamon/thirdparty/Xlib/xobject/drawable.py index efc40a913..c36a9738e 100644 --- a/Nagstamon/thirdparty/Xlib/xobject/drawable.py +++ b/Nagstamon/thirdparty/Xlib/xobject/drawable.py @@ -2,26 +2,34 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA from Xlib import X, Xatom, Xutil from Xlib.protocol import request, rq # Other X resource objects -from Xlib.xobject import resource, colormap, cursor, fontable, icccm +from . import resource +from . import colormap +from . import cursor +from . import fontable + +# Inter-client communication conventions +from . import icccm class Drawable(resource.Resource): __drawable__ = resource.Resource.__resource__ @@ -241,7 +249,7 @@ def put_pil_image(self, gc, x, y, image, onerror = None): else: subimage = image w, h = subimage.size - data = subimage.tostring("raw", rawmode, stride, 0) + data = subimage.tobytes("raw", rawmode, stride, 0) self.put_image(gc, x, y, w, h, format, depth, 0, data) y1 = y1 + h y = y + h @@ -312,6 +320,9 @@ def query_best_size(self, item_class, width, height): class Window(Drawable): __window__ = resource.Resource.__resource__ + _STRING_ENCODING = 'ISO-8859-1' + _UTF8_STRING_ENCODING = 'UTF-8' + def create_window(self, x, y, width, height, border_width, depth, window_class = X.CopyFromParent, visual = X.CopyFromParent, @@ -413,8 +424,7 @@ def query_tree(self): return request.QueryTree(display = self.display, window = self.id) - - def change_property(self, property, type, format, data, + def change_property(self, property, property_type, format, data, mode = X.PropModeReplace, onerror = None): request.ChangeProperty(display = self.display, @@ -422,21 +432,31 @@ def change_property(self, property, type, format, data, mode = mode, window = self.id, property = property, - type = type, + type = property_type, data = (format, data)) + def change_text_property(self, property, property_type, data, + mode = X.PropModeReplace, onerror = None): + if not isinstance(data, bytes): + if property_type == Xatom.STRING: + data = data.encode(self._STRING_ENCODING) + elif property_type == self.display.get_atom('UTF8_STRING'): + data = data.encode(self._UTF8_STRING_ENCODING) + self.change_property(property, property_type, 8, data, + mode=mode, onerror=onerror) + def delete_property(self, property, onerror = None): request.DeleteProperty(display = self.display, onerror = onerror, window = self.id, property = property) - def get_property(self, property, type, offset, length, delete = 0): + def get_property(self, property, property_type, offset, length, delete = 0): r = request.GetProperty(display = self.display, delete = delete, window = self.id, property = property, - type = type, + type = property_type, long_offset = offset, long_length = length) @@ -448,12 +468,12 @@ def get_property(self, property, type, offset, length, delete = 0): else: return None - def get_full_property(self, property, type, sizehint = 10): - prop = self.get_property(property, type, 0, sizehint) + def get_full_property(self, property, property_type, sizehint = 10): + prop = self.get_property(property, property_type, 0, sizehint) if prop: val = prop.value if prop.bytes_after: - prop = self.get_property(property, type, sizehint, + prop = self.get_property(property, property_type, sizehint, prop.bytes_after // 4 + 1) val = val + prop.value @@ -462,6 +482,19 @@ def get_full_property(self, property, type, sizehint = 10): else: return None + def get_full_text_property(self, property, property_type=X.AnyPropertyType, sizehint = 10): + prop = self.get_full_property(property, property_type, + sizehint=sizehint) + if prop is None or prop.format != 8: + return None + if prop.property_type == Xatom.STRING: + prop.value = prop.value.decode(self._STRING_ENCODING) + elif prop.property_type == self.display.get_atom('UTF8_STRING'): + prop.value = prop.value.decode(self._UTF8_STRING_ENCODING) + # FIXME: at least basic support for compound text would be nice. + # elif prop.property_type == self.display.get_atom('COMPOUND_TEXT'): + return prop.value + def list_properties(self): r = request.ListProperties(display = self.display, window = self.id) @@ -629,47 +662,37 @@ def rotate_properties(self, properties, delta, onerror = None): properties = properties) def set_wm_name(self, name, onerror = None): - self.change_property(Xatom.WM_NAME, Xatom.STRING, 8, name, - onerror = onerror) + self.change_text_property(Xatom.WM_NAME, Xatom.STRING, name, + onerror = onerror) def get_wm_name(self): - d = self.get_full_property(Xatom.WM_NAME, Xatom.STRING) - if d is None or d.format != 8: - return None - else: - return d.value + return self.get_full_text_property(Xatom.WM_NAME, Xatom.STRING) def set_wm_icon_name(self, name, onerror = None): - self.change_property(Xatom.WM_ICON_NAME, Xatom.STRING, 8, name, - onerror = onerror) + self.change_text_property(Xatom.WM_ICON_NAME, Xatom.STRING, name, + onerror = onerror) def get_wm_icon_name(self): - d = self.get_full_property(Xatom.WM_ICON_NAME, Xatom.STRING) - if d is None or d.format != 8: - return None - else: - return d.value - + return self.get_full_text_property(Xatom.WM_ICON_NAME, Xatom.STRING) def set_wm_class(self, inst, cls, onerror = None): - self.change_property(Xatom.WM_CLASS, Xatom.STRING, 8, - '%s\0%s\0' % (inst, cls), - onerror = onerror) + self.change_text_property(Xatom.WM_CLASS, Xatom.STRING, + '%s\0%s\0' % (inst, cls), + onerror = onerror) def get_wm_class(self): - d = self.get_full_property(Xatom.WM_CLASS, Xatom.STRING) - if d is None or d.format != 8: + value = self.get_full_text_property(Xatom.WM_CLASS, Xatom.STRING) + if value is None: + return None + parts = value.split('\0') + if len(parts) < 2: return None else: - parts = d.value.split('\0') - if len(parts) < 2: - return None - else: - return parts[0], parts[1] + return parts[0], parts[1] def set_wm_transient_for(self, window, onerror = None): self.change_property(Xatom.WM_TRANSIENT_FOR, Xatom.WINDOW, - 32, window.id, + 32, [window.id], onerror = onerror) def get_wm_transient_for(self): @@ -696,7 +719,7 @@ def get_wm_protocols(self): def set_wm_colormap_windows(self, windows, onerror = None): self.change_property(self.display.get_atom('WM_COLORMAP_WINDOWS'), Xatom.WINDOW, 32, - [w.id for w in windows], + map(lambda w: w.id, windows), onerror = onerror) def get_wm_colormap_windows(self): @@ -706,20 +729,16 @@ def get_wm_colormap_windows(self): return [] else: cls = self.display.get_resource_class('window', Window) - return list(map(lambda i, d = self.display, c = cls: c(d, i), - d.value)) + return map(lambda i, d = self.display, c = cls: c(d, i), + d.value) def set_wm_client_machine(self, name, onerror = None): - self.change_property(Xatom.WM_CLIENT_MACHINE, Xatom.STRING, 8, name, - onerror = onerror) + self.change_text_property(Xatom.WM_CLIENT_MACHINE, Xatom.STRING, name, + onerror = onerror) def get_wm_client_machine(self): - d = self.get_full_property(Xatom.WM_CLIENT_MACHINE, Xatom.STRING) - if d is None or d.format != 8: - return None - else: - return d.value + return self.get_full_text_property(Xatom.WM_CLIENT_MACHINE, Xatom.STRING) def set_wm_normal_hints(self, hints = {}, onerror = None, **keys): self._set_struct_prop(Xatom.WM_NORMAL_HINTS, Xatom.WM_SIZE_HINTS, @@ -760,7 +779,7 @@ def get_wm_icon_size(self): def _get_struct_prop(self, pname, ptype, pstruct): r = self.get_property(pname, ptype, 0, pstruct.static_size // 4) if r and r.format == 32: - value = r.value.tostring() + value = rq.encode_array(r.value) if len(value) == pstruct.static_size: return pstruct.parse_binary(value, self.display)[0] @@ -792,11 +811,9 @@ def free(self, onerror = None): self.display.free_resource_id(self.id) - def create_cursor(self, mask, - f_rgb, b_rgb, - x, y): - fore_red, fore_green, fore_blue = f_rgb - back_red, back_green, back_blue = b_rgb + def create_cursor(self, mask, foreground, background, x, y): + fore_red, fore_green, fore_blue = foreground + back_red, back_green, back_blue = background cid = self.display.allocate_resource_id() request.CreateCursor(display = self.display, cid = cid, diff --git a/Nagstamon/thirdparty/Xlib/xobject/fontable.py b/Nagstamon/thirdparty/Xlib/xobject/fontable.py index 964b3963f..892f2665a 100644 --- a/Nagstamon/thirdparty/Xlib/xobject/fontable.py +++ b/Nagstamon/thirdparty/Xlib/xobject/fontable.py @@ -2,22 +2,27 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA from Xlib.protocol import request -from Xlib.xobject import resource, cursor + +from . import resource +from . import cursor class Fontable(resource.Resource): __fontable__ = resource.Resource.__resource__ @@ -83,9 +88,9 @@ def close(self, onerror = None): self.display.free_resource_id(self.id) def create_glyph_cursor(self, mask, source_char, mask_char, - f_rgb, b_rgb): - fore_red, fore_green, fore_blue = f_rgb - back_red, back_green, back_blue = b_rgb + foreground, background): + fore_red, fore_green, fore_blue = foreground + back_red, back_green, back_blue = background cid = self.display.allocate_resource_id() request.CreateGlyphCursor(display = self.display, diff --git a/Nagstamon/thirdparty/Xlib/xobject/icccm.py b/Nagstamon/thirdparty/Xlib/xobject/icccm.py index 59aecb9e4..d445e6dfd 100644 --- a/Nagstamon/thirdparty/Xlib/xobject/icccm.py +++ b/Nagstamon/thirdparty/Xlib/xobject/icccm.py @@ -2,19 +2,22 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA from Xlib import X, Xutil from Xlib.protocol import rq diff --git a/Nagstamon/thirdparty/Xlib/xobject/resource.py b/Nagstamon/thirdparty/Xlib/xobject/resource.py index f8a39a597..ea256ca1e 100644 --- a/Nagstamon/thirdparty/Xlib/xobject/resource.py +++ b/Nagstamon/thirdparty/Xlib/xobject/resource.py @@ -2,23 +2,26 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public License +# as published by the Free Software Foundation; either version 2.1 +# of the License, or (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Lesser General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., +# 59 Temple Place, +# Suite 330, +# Boston, MA 02111-1307 USA from Xlib.protocol import request -class Resource: +class Resource(object): def __init__(self, display, rid, owner = 0): self.display = display self.id = rid @@ -32,18 +35,18 @@ def __eq__(self, obj): if self.display == obj.display: return self.id == obj.id else: - return self.display == obj.display + return False else: return id(self) == id(obj) + def __ne__(self, obj): + return not self == obj + def __hash__(self): return int(self.id) - def __str__(self): - return '%s(0x%08x)' % (self.__class__, self.id) - def __repr__(self): - return '<%s 0x%08x>' % (self.__class__, self.id) + return '<%s 0x%08x>' % (self.__class__.__name__, self.id) def kill_client(self, onerror = None): request.KillClient(display = self.display, diff --git a/build/debian/changelog b/build/debian/changelog index e1c80fa01..87dde2935 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.11-20221119) unstable; urgency=low +nagstamon (3.11-20221121) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Fri, 14 Nov 2022 08:00:00 +0100 From 6e1b0012eb1abd21fc91c4fc267043a143fe1585 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Mon, 21 Nov 2022 18:04:29 +0100 Subject: [PATCH 463/884] revert XLib (without dependency of tkinter) --- Nagstamon/Helpers.py | 16 + Nagstamon/thirdparty/Xlib/X.py | 27 +- Nagstamon/thirdparty/Xlib/XK.py | 25 +- Nagstamon/thirdparty/Xlib/Xatom.py | 25 +- Nagstamon/thirdparty/Xlib/Xcursorfont.py | 25 +- Nagstamon/thirdparty/Xlib/Xutil.py | 25 +- Nagstamon/thirdparty/Xlib/__init__.py | 27 +- Nagstamon/thirdparty/Xlib/display.py | 150 +- Nagstamon/thirdparty/Xlib/error.py | 35 +- Nagstamon/thirdparty/Xlib/ext/__init__.py | 38 +- Nagstamon/thirdparty/Xlib/ext/composite.py | 76 +- Nagstamon/thirdparty/Xlib/ext/damage.py | 182 - Nagstamon/thirdparty/Xlib/ext/dpms.py | 233 - Nagstamon/thirdparty/Xlib/ext/ge.py | 112 - Nagstamon/thirdparty/Xlib/ext/nvcontrol.py | 5393 ----------------- Nagstamon/thirdparty/Xlib/ext/randr.py | 192 +- Nagstamon/thirdparty/Xlib/ext/record.py | 31 +- Nagstamon/thirdparty/Xlib/ext/res.py | 288 - Nagstamon/thirdparty/Xlib/ext/screensaver.py | 198 - Nagstamon/thirdparty/Xlib/ext/security.py | 139 - Nagstamon/thirdparty/Xlib/ext/shape.py | 545 +- Nagstamon/thirdparty/Xlib/ext/xfixes.py | 201 - Nagstamon/thirdparty/Xlib/ext/xinerama.py | 28 +- Nagstamon/thirdparty/Xlib/ext/xinput.py | 777 --- Nagstamon/thirdparty/Xlib/ext/xtest.py | 25 +- .../thirdparty/Xlib/keysymdef/__init__.py | 25 +- Nagstamon/thirdparty/Xlib/keysymdef/xf86.py | 379 +- .../thirdparty/Xlib/protocol/__init__.py | 25 +- Nagstamon/thirdparty/Xlib/protocol/display.py | 256 +- Nagstamon/thirdparty/Xlib/protocol/event.py | 31 +- Nagstamon/thirdparty/Xlib/protocol/request.py | 39 +- Nagstamon/thirdparty/Xlib/protocol/rq.py | 641 +- Nagstamon/thirdparty/Xlib/protocol/structs.py | 29 +- Nagstamon/thirdparty/Xlib/rdb.py | 52 +- Nagstamon/thirdparty/Xlib/support/__init__.py | 25 +- Nagstamon/thirdparty/Xlib/support/connect.py | 67 +- Nagstamon/thirdparty/Xlib/support/lock.py | 29 +- .../thirdparty/Xlib/support/unix_connect.py | 168 +- .../thirdparty/Xlib/support/vms_connect.py | 33 +- Nagstamon/thirdparty/Xlib/threaded.py | 34 +- Nagstamon/thirdparty/Xlib/xauth.py | 46 +- Nagstamon/thirdparty/Xlib/xobject/__init__.py | 25 +- Nagstamon/thirdparty/Xlib/xobject/colormap.py | 32 +- Nagstamon/thirdparty/Xlib/xobject/cursor.py | 35 +- Nagstamon/thirdparty/Xlib/xobject/drawable.py | 145 +- Nagstamon/thirdparty/Xlib/xobject/fontable.py | 35 +- Nagstamon/thirdparty/Xlib/xobject/icccm.py | 25 +- Nagstamon/thirdparty/Xlib/xobject/resource.py | 37 +- 48 files changed, 1590 insertions(+), 9436 deletions(-) delete mode 100644 Nagstamon/thirdparty/Xlib/ext/damage.py delete mode 100644 Nagstamon/thirdparty/Xlib/ext/dpms.py delete mode 100644 Nagstamon/thirdparty/Xlib/ext/ge.py delete mode 100644 Nagstamon/thirdparty/Xlib/ext/nvcontrol.py delete mode 100644 Nagstamon/thirdparty/Xlib/ext/res.py delete mode 100644 Nagstamon/thirdparty/Xlib/ext/screensaver.py delete mode 100644 Nagstamon/thirdparty/Xlib/ext/security.py delete mode 100644 Nagstamon/thirdparty/Xlib/ext/xfixes.py delete mode 100644 Nagstamon/thirdparty/Xlib/ext/xinput.py diff --git a/Nagstamon/Helpers.py b/Nagstamon/Helpers.py index 55fff1aa7..1171d6c4a 100644 --- a/Nagstamon/Helpers.py +++ b/Nagstamon/Helpers.py @@ -26,7 +26,9 @@ import platform import re import sys +from sys import executable import traceback +from traceback import print_exc import webbrowser # import md5 for centreon url autologin encoding @@ -465,6 +467,20 @@ def get_distro(): dist_name, dist_version, dist_id = platform.dist() return dist_name.lower(), dist_version, dist_id +def restart(): + """ + restart Nagstamon, e.g. for GUI changes in macOS + """ + try: + process = psutil.Process(os.getpid()) + for file_handler in process.open_files() + process.connections(): + os.close(file_handler.fd) + except Exception as exception: + print_exc(file=sys.stdout) + + print('yeehaa') + + os.execl(executable, executable, *sys.argv) # depending on column different functions have to be used # 0 + 1 are column "Hosts", 1 + 2 are column "Service" due to extra font flag pictograms diff --git a/Nagstamon/thirdparty/Xlib/X.py b/Nagstamon/thirdparty/Xlib/X.py index 1a09e3922..80eeafdaa 100644 --- a/Nagstamon/thirdparty/Xlib/X.py +++ b/Nagstamon/thirdparty/Xlib/X.py @@ -2,22 +2,19 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA # Avoid overwriting None if doing "from Xlib.X import *" NONE = 0 @@ -197,8 +194,6 @@ FamilyInternet = 0 FamilyDECnet = 1 FamilyChaos = 2 -FamilyServerInterpreted = 5 -FamilyInternetV6 = 6 PropertyNewValue = 0 PropertyDelete = 1 ColormapUninstalled = 0 diff --git a/Nagstamon/thirdparty/Xlib/XK.py b/Nagstamon/thirdparty/Xlib/XK.py index 7603ccd03..76b20f09d 100644 --- a/Nagstamon/thirdparty/Xlib/XK.py +++ b/Nagstamon/thirdparty/Xlib/XK.py @@ -2,22 +2,19 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA # # This module defines some functions for working with X keysyms as well # as a modular keysym definition and loading mechanism. See the keysym diff --git a/Nagstamon/thirdparty/Xlib/Xatom.py b/Nagstamon/thirdparty/Xlib/Xatom.py index b0137779f..94bcebba7 100644 --- a/Nagstamon/thirdparty/Xlib/Xatom.py +++ b/Nagstamon/thirdparty/Xlib/Xatom.py @@ -2,22 +2,19 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA PRIMARY = 1 SECONDARY = 2 diff --git a/Nagstamon/thirdparty/Xlib/Xcursorfont.py b/Nagstamon/thirdparty/Xlib/Xcursorfont.py index 19919438f..589044a4c 100644 --- a/Nagstamon/thirdparty/Xlib/Xcursorfont.py +++ b/Nagstamon/thirdparty/Xlib/Xcursorfont.py @@ -2,22 +2,19 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA num_glyphs = 154 X_cursor = 0 diff --git a/Nagstamon/thirdparty/Xlib/Xutil.py b/Nagstamon/thirdparty/Xlib/Xutil.py index 768c5e2fd..ba3141f0e 100644 --- a/Nagstamon/thirdparty/Xlib/Xutil.py +++ b/Nagstamon/thirdparty/Xlib/Xutil.py @@ -2,22 +2,19 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA NoValue = 0x0000 diff --git a/Nagstamon/thirdparty/Xlib/__init__.py b/Nagstamon/thirdparty/Xlib/__init__.py index 9db8beec1..7e0e26547 100644 --- a/Nagstamon/thirdparty/Xlib/__init__.py +++ b/Nagstamon/thirdparty/Xlib/__init__.py @@ -2,24 +2,21 @@ # # Copyright (C) 2000-2002 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA -__version__ = (0, 32) +__version__ = (0, 15) __version_extra__ = '' diff --git a/Nagstamon/thirdparty/Xlib/display.py b/Nagstamon/thirdparty/Xlib/display.py index e0f7b5c8c..12387c80d 100644 --- a/Nagstamon/thirdparty/Xlib/display.py +++ b/Nagstamon/thirdparty/Xlib/display.py @@ -2,55 +2,46 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA # Python modules import types -# Python 2/3 compatibility. -from six import create_unbound_method - # Xlib modules -from . import error -from . import ext -from . import X +from . import error, ext, X # Xlib.protocol modules -from .protocol import display as protocol_display -from .protocol import request, event, rq +from Xlib.protocol import display, request, event, rq # Xlib.xobjects modules -from .xobject import resource -from .xobject import drawable -from .xobject import fontable -from .xobject import colormap -from .xobject import cursor +import Xlib.xobject.resource +import Xlib.xobject.drawable +import Xlib.xobject.fontable +import Xlib.xobject.colormap +import Xlib.xobject.cursor _resource_baseclasses = { - 'resource': resource.Resource, - 'drawable': drawable.Drawable, - 'window': drawable.Window, - 'pixmap': drawable.Pixmap, - 'fontable': fontable.Fontable, - 'font': fontable.Font, - 'gc': fontable.GC, - 'colormap': colormap.Colormap, - 'cursor': cursor.Cursor, + 'resource': Xlib.xobject.resource.Resource, + 'drawable': Xlib.xobject.drawable.Drawable, + 'window': Xlib.xobject.drawable.Window, + 'pixmap': Xlib.xobject.drawable.Pixmap, + 'fontable': Xlib.xobject.fontable.Fontable, + 'font': Xlib.xobject.fontable.Font, + 'gc': Xlib.xobject.fontable.GC, + 'colormap': Xlib.xobject.colormap.Colormap, + 'cursor': Xlib.xobject.cursor.Cursor, } _resource_hierarchy = { @@ -61,14 +52,14 @@ 'fontable': ('font', 'gc') } -class _BaseDisplay(protocol_display.Display): +class _BaseDisplay(display.Display): + resource_classes = _resource_baseclasses.copy() # Implement a cache of atom names, used by Window objects when # dealing with some ICCCM properties not defined in Xlib.Xatom def __init__(self, *args, **keys): - self.resource_classes = _resource_baseclasses.copy() - protocol_display.Display.__init__(self, *args, **keys) + display.Display.__init__(*(self, ) + args, **keys) self._atom_cache = {} def get_atom(self, atomname, only_if_exists=0): @@ -84,7 +75,7 @@ def get_atom(self, atomname, only_if_exists=0): return r.atom -class Display(object): +class Display: def __init__(self, display = None): self.display = _BaseDisplay(display) @@ -103,11 +94,6 @@ def __init__(self, display = None): self.class_extension_dicts = {} self.display_extension_methods = {} - # a dict that maps the event name to the code - # or, when it's an event with a subcode, to a tuple of (event,subcode) - # note this wraps the dict so you address it as - # extension_event.EXTENSION_EVENT_NAME rather than - # extension_event["EXTENSION_EVENT_NAME"] self.extension_event = rq.DictWrapper({}) exts = self.list_extensions() @@ -117,7 +103,7 @@ def __init__(self, display = None): if extname in exts: # Import the module and fetch it - __import__('Xlib.ext.' + modname) + __import__('ext.' + modname,globals(),level=1) mod = getattr(ext, modname) info = self.query_extension(extname) @@ -130,11 +116,11 @@ def __init__(self, display = None): # Finalize extensions by creating new classes - for class_name, dictionary in self.class_extension_dicts.items(): - origcls = self.display.resource_classes[class_name] - self.display.resource_classes[class_name] = type(origcls.__name__, - (origcls,), - dictionary) + for type_, dict in self.class_extension_dicts.items(): + origcls = self.display.resource_classes[type_] + self.display.resource_classes[type_] = type(origcls.__name__, + (origcls, object), + dict) # Problem: we have already created some objects without the # extensions: the screen roots and default colormaps. @@ -277,19 +263,17 @@ def extension_add_method(self, object, name, function): self.display_extension_methods[name] = function else: - class_list = (object, ) + _resource_hierarchy.get(object, ()) - for class_name in class_list: - cls = _resource_baseclasses[class_name] + types = (object, ) + _resource_hierarchy.get(object, ()) + for type in types: + cls = _resource_baseclasses[type] if hasattr(cls, name): - raise AssertionError('attempting to replace %s method: %s' % (class_name, name)) - - method = create_unbound_method(function, cls) + raise AssertionError('attempting to replace %s method: %s' % (type, name)) # Maybe should check extension overrides too try: - self.class_extension_dicts[class_name][name] = method + self.class_extension_dicts[type][name] = function except KeyError: - self.class_extension_dicts[class_name] = { name: method } + self.class_extension_dicts[type] = { name: function } def extension_add_event(self, code, evt, name = None): """extension_add_event(code, evt, [name]) @@ -303,8 +287,8 @@ def extension_add_event(self, code, evt, name = None): extension_event. """ - newevt = type(evt.__name__, evt.__bases__, - evt.__dict__.copy()) + newevt = type('{0}.SUB{1}'.format(evt.__name__, code), + evt.__bases__, evt.__dict__.copy()) newevt._code = code self.display.add_extension_event(code, newevt) @@ -314,34 +298,9 @@ def extension_add_event(self, code, evt, name = None): setattr(self.extension_event, name, code) - def extension_add_subevent(self, code, subcode, evt, name = None): - """extension_add_subevent(code, evt, [name]) - - Add an extension subevent. CODE is the numeric code, subcode - is the sub-ID of this event that shares the code ID with other - sub-events and EVT is the event class. EVT will be cloned, and - the attribute _code of the new event class will be set to CODE. - - If NAME is omitted, it will be set to the name of EVT. This - name is used to insert an entry in the DictWrapper - extension_event. - """ - - newevt = type(evt.__name__, evt.__bases__, - evt.__dict__.copy()) - newevt._code = code - - self.display.add_extension_event(code, newevt, subcode) - - if name is None: - name = evt.__name__ - - # store subcodes as a tuple of (event code, subcode) in the - # extension dict maintained in the display object - setattr(self.extension_event, name, (code,subcode)) - def extension_add_error(self, code, err): - """extension_add_error(code, err) + def add_extension_error(self, code, err): + """add_extension_error(code, err) Add an extension error. CODE is the numeric code, and ERR is the error class. @@ -389,7 +348,7 @@ def keysym_to_keycodes(self, keysym): lowest index and secondarily on the lowest keycode.""" try: # Copy the map list, reversing the arguments - return map(lambda x: (x[1], x[0]), self._keymap_syms[keysym]) + return [(x[1], x[0]) for x in self._keymap_syms[keysym]] except KeyError: return [] @@ -517,7 +476,7 @@ def send_event(self, destination, event, event_mask = 0, propagate = 0, event = event) def ungrab_pointer(self, time, onerror = None): - """Release a grabbed pointer and any queued events. See + """elease a grabbed pointer and any queued events. See XUngrabPointer(3X11).""" request.UngrabPointer(display = self.display, onerror = onerror, @@ -631,7 +590,7 @@ def open_font(self, name): self.display.free_resource_id(fid) return None else: - cls = self.display.get_resource_class('font', fontable.Font) + cls = self.display.get_resource_class('font', Xlib.xobject.fontable.Font) return cls(self.display, fid, owner = 1) def list_fonts(self, pattern, max_names): @@ -661,7 +620,7 @@ def list_fonts_with_info(self, pattern, max_names): font_ascent font_descent replies_hint - See the description of XFontStruct in XGetFontProperty(3X11) + See the descripton of XFontStruct in XGetFontProperty(3X11) for details on these values. properties A list of properties. Each entry has two attributes: @@ -814,7 +773,7 @@ def change_pointer_control(self, accel = None, threshold = None, onerror = None) request.ChangePointerControl(display = self.display, onerror = onerror, do_accel = do_accel, - do_thresh = do_threshold, + do_thres = do_threshold, accel_num = accel_num, accel_denum = accel_denum, threshold = threshold) @@ -849,8 +808,7 @@ def get_screen_saver(self): def change_hosts(self, mode, host_family, host, onerror = None): """mode is either X.HostInsert or X.HostDelete. host_family is - one of X.FamilyInternet, X.FamilyDECnet, X.FamilyChaos, - X.FamilyServerInterpreted or X.FamilyInternetV6. + one of X.FamilyInternet, X.FamilyDECnet or X.FamilyChaos. host is a list of bytes. For the Internet family, it should be the four bytes of an IPv4 address.""" @@ -869,7 +827,7 @@ def list_hosts(self): The hosts on the access list. Each entry has the following attributes: family - X.FamilyInternet, X.FamilyDECnet, X.FamilyChaos, X.FamilyServerInterpreted or X.FamilyInternetV6. + X.FamilyInternet, X.FamilyDECnet, or X.FamilyChaos. name A list of byte values, the coding depends on family. For the Internet family, it is the 4 bytes of an IPv4 address. diff --git a/Nagstamon/thirdparty/Xlib/error.py b/Nagstamon/thirdparty/Xlib/error.py index cb6d0d07a..5f5af5dfa 100644 --- a/Nagstamon/thirdparty/Xlib/error.py +++ b/Nagstamon/thirdparty/Xlib/error.py @@ -2,28 +2,25 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA # Xlib modules -from . import X +from Xlib import X # Xlib.protocol modules -from .protocol import rq +from Xlib.protocol import rq class DisplayError(Exception): @@ -76,9 +73,9 @@ def __str__(self): s = [] for f in ('code', 'resource_id', 'sequence_number', 'major_opcode', 'minor_opcode'): - s.append('{0} = {1}'.format(f, self._data[f])) + s.append('%s = %s' % (f, self._data[f])) - return '{0}: {1}'.format(self.__class__, ', '.join(s)) + return '%s: %s' % (self.__class__, ', '.join(s)) class XResourceError(XError): _fields = rq.Struct( rq.Card8('type'), # Always 0 @@ -129,7 +126,7 @@ class BadImplementation(XError): pass } -class CatchError(object): +class CatchError: def __init__(self, *errors): self.error_types = errors self.error = None diff --git a/Nagstamon/thirdparty/Xlib/ext/__init__.py b/Nagstamon/thirdparty/Xlib/ext/__init__.py index 37229bacf..176599b5c 100644 --- a/Nagstamon/thirdparty/Xlib/ext/__init__.py +++ b/Nagstamon/thirdparty/Xlib/ext/__init__.py @@ -2,45 +2,31 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA # __extensions__ is a list of tuples: (extname, extmod) # extname is the name of the extension according to the X # protocol. extmod is the name of the module in this package. __extensions__ = [ - # We load this first so other extensions can register generic event data - # structures. - ('Generic Event Extension', 'ge'), ('XTEST', 'xtest'), ('SHAPE', 'shape'), ('XINERAMA', 'xinerama'), ('RECORD', 'record'), ('Composite', 'composite'), ('RANDR', 'randr'), - ('XFIXES', 'xfixes'), - ('SECURITY', 'security'), - ('XInputExtension', 'xinput'), - ('NV-CONTROL', 'nvcontrol'), - ('DAMAGE', 'damage'), - ('DPMS', 'dpms'), - ('X-Resource', 'res'), - ('MIT-SCREEN-SAVER', 'screensaver'), ] -__all__ = map(lambda x: x[1], __extensions__) +__all__ = [x[1] for x in __extensions__] diff --git a/Nagstamon/thirdparty/Xlib/ext/composite.py b/Nagstamon/thirdparty/Xlib/ext/composite.py index 0e10b6350..347f67120 100644 --- a/Nagstamon/thirdparty/Xlib/ext/composite.py +++ b/Nagstamon/thirdparty/Xlib/ext/composite.py @@ -4,22 +4,19 @@ # # Copyright (C) 2007 Peter Liljenberg <peter.liljenberg@gmail.com> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """Composite extension, allowing windows to be rendered to off-screen storage. @@ -65,8 +62,6 @@ def query_version(self): return QueryVersion( display = self.display, opcode = self.display.get_extension_major(extname), - major_version=0, - minor_version=4 ) @@ -80,12 +75,11 @@ class RedirectWindow(rq.Request): rq.Pad(3), ) -def redirect_window(self, update, onerror = None): +def redirect_window(self, update): """Redirect the hierarchy starting at this window to off-screen storage. """ RedirectWindow(display = self.display, - onerror = onerror, opcode = self.display.get_extension_major(extname), window = self, update = update, @@ -102,12 +96,11 @@ class RedirectSubwindows(rq.Request): rq.Pad(3), ) -def redirect_subwindows(self, update, onerror = None): +def redirect_subwindows(self, update): """Redirect the hierarchies starting at all current and future children to this window to off-screen storage. """ RedirectSubwindows(display = self.display, - onerror = onerror, opcode = self.display.get_extension_major(extname), window = self, update = update, @@ -124,11 +117,10 @@ class UnredirectWindow(rq.Request): rq.Pad(3), ) -def unredirect_window(self, update, onerror = None): +def unredirect_window(self, update): """Stop redirecting this window hierarchy. """ UnredirectWindow(display = self.display, - onerror = onerror, opcode = self.display.get_extension_major(extname), window = self, update = update, @@ -145,11 +137,10 @@ class UnredirectSubindows(rq.Request): rq.Pad(3), ) -def unredirect_subwindows(self, update, onerror = None): +def unredirect_subwindows(self, update): """Stop redirecting the hierarchies of children to this window. """ RedirectWindow(display = self.display, - onerror = onerror, opcode = self.display.get_extension_major(extname), window = self, update = update, @@ -165,15 +156,14 @@ class CreateRegionFromBorderClip(rq.Request): rq.Window('window'), ) -def create_region_from_border_clip(self, onerror = None): +def create_region_from_border_clip(self): """Create a region of the border clip of the window, i.e. the area that is not clipped by the parent and any sibling windows. """ - + rid = self.display.allocate_resource_id() CreateRegionFromBorderClip( display = self.display, - onerror = onerror, opcode = self.display.get_extension_major(extname), region = rid, window = self, @@ -192,7 +182,7 @@ class NameWindowPixmap(rq.Request): rq.Pixmap('pixmap'), ) -def name_window_pixmap(self, onerror = None): +def name_window_pixmap(self): """Create a new pixmap that refers to the off-screen storage of the window, including its border. @@ -205,7 +195,6 @@ def name_window_pixmap(self, onerror = None): pid = self.display.allocate_resource_id() NameWindowPixmap(display = self.display, - onerror = onerror, opcode = self.display.get_extension_major(extname), window = self, pixmap = pid, @@ -213,30 +202,7 @@ def name_window_pixmap(self, onerror = None): cls = self.display.get_resource_class('pixmap', drawable.Pixmap) return cls(self.display, pid, owner = 1) - -class GetOverlayWindow(rq.ReplyRequest): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(7), - rq.RequestLength(), - rq.Window('window') - ) - _reply = rq.Struct( - rq.ReplyCode(), - rq.Pad(1), - rq.Card16('sequence_number'), - rq.ReplyLength(), - rq.Window('overlay_window'), - rq.Pad(20), - ) - -def get_overlay_window(self): - """Return the overlay window of the root window. - """ - - return GetOverlayWindow(display = self.display, - opcode = self.display.get_extension_major(extname), - window = self) + def init(disp, info): disp.extension_add_method('display', @@ -266,7 +232,3 @@ def init(disp, info): disp.extension_add_method('window', 'composite_name_window_pixmap', name_window_pixmap) - - disp.extension_add_method('window', - 'composite_get_overlay_window', - get_overlay_window) diff --git a/Nagstamon/thirdparty/Xlib/ext/damage.py b/Nagstamon/thirdparty/Xlib/ext/damage.py deleted file mode 100644 index 60c56606e..000000000 --- a/Nagstamon/thirdparty/Xlib/ext/damage.py +++ /dev/null @@ -1,182 +0,0 @@ -# Xlib.ext.damage -- DAMAGE extension module -# -# Copyright (C) 2018 Joseph Kogut <joseph.kogut@gmail.com> -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA - - -from Xlib import X -from Xlib.protocol import rq, structs -from Xlib.xobject import resource -from Xlib.error import XError - -extname = 'DAMAGE' - -# Event codes # -DamageNotifyCode = 0 - -# Error codes # -BadDamageCode = 0 - -class BadDamageError(XError): - pass - -# DamageReportLevel options -DamageReportRawRectangles = 0 -DamageReportDeltaRectangles = 1 -DamageReportBoundingBox = 2 -DamageReportNonEmpty = 3 - -DamageReportLevel = ( - DamageReportRawRectangles, - DamageReportDeltaRectangles, - DamageReportBoundingBox, - DamageReportNonEmpty, -) - -DAMAGE = rq.Card32 - -# Methods - -class QueryVersion(rq.ReplyRequest): - _request = rq.Struct(rq.Card8('opcode'), - rq.Opcode(0), - rq.RequestLength(), - rq.Card32('major_version'), - rq.Card32('minor_version'), - ) - - _reply = rq.Struct(rq.ReplyCode(), - rq.Pad(1), - rq.Card16('sequence_number'), - rq.ReplyLength(), - rq.Card32('major_version'), - rq.Card32('minor_version'), - rq.Pad(16), - ) - -def query_version(self): - return QueryVersion(display=self.display, - opcode=self.display.get_extension_major(extname), - major_version=1, - minor_version=1) - -class DamageCreate(rq.Request): - _request = rq.Struct(rq.Card8('opcode'), - rq.Opcode(1), - rq.RequestLength(), - DAMAGE('damage'), - rq.Drawable('drawable'), - rq.Set('level', 1, DamageReportLevel), - rq.Pad(3), - ) - -def damage_create(self, level): - did = self.display.allocate_resource_id() - DamageCreate(display=self.display, - opcode=self.display.get_extension_major(extname), - damage=did, - drawable=self.id, - level=level, - ) - return did - -class DamageDestroy(rq.Request): - _request = rq.Struct(rq.Card8('opcode'), - rq.Opcode(2), - rq.RequestLength(), - DAMAGE('damage') - ) - -def damage_destroy(self, damage): - DamageDestroy(display=self.display, - opcode=self.display.get_extension_major(extname), - damage=damage, - ) - - self.display.free_resource_id(damage) - -class DamageSubtract(rq.Request): - _request = rq.Struct(rq.Card8('opcode'), - rq.Opcode(3), - rq.RequestLength(), - DAMAGE('damage'), - rq.Card32('repair'), - rq.Card32('parts') - ) - -def damage_subtract(self, damage, repair=X.NONE, parts=X.NONE): - DamageSubtract(display=self.display, - opcode=self.display.get_extension_major(extname), - damage=damage, - repair=repair, - parts=parts) - -class DamageAdd(rq.Request): - _request = rq.Struct(rq.Card8('opcode'), - rq.Opcode(4), - rq.RequestLength(), - rq.Card32('repair'), - rq.Card32('parts'), - ) - -def damage_add(self, repair, parts): - DamageAdd(display=self.display, - opcode=self.display.get_extension_major(extname), - repair=repair, - parts=parts) - -# Events # - -class DamageNotify(rq.Event): - _code = None - _fields = rq.Struct( - rq.Card8('type'), - rq.Card8('level'), - rq.Card16('sequence_number'), - rq.Drawable('drawable'), - DAMAGE('damage'), - rq.Card32('timestamp'), - rq.Object('area', structs.Rectangle), - rq.Object('drawable_geometry', structs.Rectangle) - ) - -def init(disp, info): - disp.extension_add_method('display', - 'damage_query_version', - query_version) - - disp.extension_add_method('drawable', - 'damage_create', - damage_create) - - disp.extension_add_method('display', - 'damage_destroy', - damage_destroy) - - disp.extension_add_method('display', - 'damage_subtract', - damage_subtract) - - disp.extension_add_method('drawable', - 'damage_add', - damage_add) - - disp.extension_add_event(info.first_event + DamageNotifyCode, DamageNotify) - - disp.extension_add_error(code=BadDamageCode, err=BadDamageError) diff --git a/Nagstamon/thirdparty/Xlib/ext/dpms.py b/Nagstamon/thirdparty/Xlib/ext/dpms.py deleted file mode 100644 index 3ff9a246d..000000000 --- a/Nagstamon/thirdparty/Xlib/ext/dpms.py +++ /dev/null @@ -1,233 +0,0 @@ -# Xlib.ext.dpms -- X Display Power Management Signaling -# -# Copyright (C) 2020 Thiago Kenji Okada <thiagokokada@gmail.com> -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA - -''' -This extension provides X Protocol control over the VESA Display -Power Management Signaling (DPMS) characteristics of video boards -under control of the X Window System. - -Documentation: https://www.x.org/releases/X11R7.7/doc/xextproto/dpms.html -''' - -from Xlib import X -from Xlib.protocol import rq - -extname = 'DPMS' - - -# DPMS Extension Power Levels -# 0 DPMSModeOn In use -# 1 DPMSModeStandby Blanked, low power -# 2 DPMSModeSuspend Blanked, lower power -# 3 DPMSModeOff Shut off, awaiting activity -DPMSModeOn = 0 -DPMSModeStandby = 1 -DPMSModeSuspend = 2 -DPMSModeOff = 3 - -DPMSPowerLevel = ( - DPMSModeOn, - DPMSModeStandby, - DPMSModeSuspend, - DPMSModeOff, -) - - -class DPMSGetVersion(rq.ReplyRequest): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(0), - rq.RequestLength(), - rq.Card16('major_version'), - rq.Card16('minor_version'), - ) - - _reply = rq.Struct( - rq.ReplyCode(), - rq.Pad(1), - rq.Card16('sequence_number'), - rq.ReplyLength(), - rq.Card16('major_version'), - rq.Card16('minor_version'), - rq.Pad(20), - ) - - -def get_version(self): - return DPMSGetVersion(display=self.display, - opcode=self.display.get_extension_major(extname), - major_version=1, - minor_version=1) - - -class DPMSCapable(rq.ReplyRequest): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(1), - rq.RequestLength(), - ) - - _reply = rq.Struct( - rq.ReplyCode(), - rq.Pad(1), - rq.Card16('sequence_number'), - rq.ReplyLength(), - rq.Bool('capable'), - rq.Pad(23), - ) - - -def capable(self): - return DPMSCapable(display=self.display, - opcode=self.display.get_extension_major(extname), - major_version=1, - minor_version=1) - - -class DPMSGetTimeouts(rq.ReplyRequest): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(2), - rq.RequestLength(), - ) - - _reply = rq.Struct( - rq.ReplyCode(), - rq.Pad(1), - rq.Card16('sequence_number'), - rq.ReplyLength(), - rq.Card16('standby_timeout'), - rq.Card16('suspend_timeout'), - rq.Card16('off_timeout'), - rq.Pad(18), - ) - - -def get_timeouts(self): - return DPMSGetTimeouts(display=self.display, - opcode=self.display.get_extension_major(extname), - major_version=1, - minor_version=1) - - -class DPMSSetTimeouts(rq.Request): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(3), - rq.RequestLength(), - rq.Card16('standby_timeout'), - rq.Card16('suspend_timeout'), - rq.Card16('off_timeout'), - rq.Pad(2) - ) - - -def set_timeouts(self, standby_timeout, suspend_timeout, off_timeout): - return DPMSSetTimeouts(display=self.display, - opcode=self.display.get_extension_major(extname), - major_version=1, - minor_version=1, - standby_timeout=standby_timeout, - suspend_timeout=suspend_timeout, - off_timeout=off_timeout) - - -class DPMSEnable(rq.Request): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(4), - rq.RequestLength(), - ) - - -def enable(self): - return DPMSEnable(display=self.display, - opcode=self.display.get_extension_major(extname), - major_version=1, - minor_version=1) - - -class DPMSDisable(rq.Request): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(5), - rq.RequestLength(), - ) - - -def disable(self): - return DPMSDisable(display=self.display, - opcode=self.display.get_extension_major(extname), - major_version=1, - minor_version=1) - - -class DPMSForceLevel(rq.Request): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(6), - rq.RequestLength(), - rq.Resource('power_level', DPMSPowerLevel), - ) - - -def force_level(self, power_level): - return DPMSForceLevel(display=self.display, - opcode=self.display.get_extension_major(extname), - major_version=1, - minor_version=1, - power_level=power_level) - - -class DPMSInfo(rq.ReplyRequest): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(7), - rq.RequestLength(), - ) - - _reply = rq.Struct( - rq.ReplyCode(), - rq.Pad(1), - rq.Card16('sequence_number'), - rq.ReplyLength(), - rq.Card16('power_level'), - rq.Bool('state'), - rq.Pad(21), - ) - - -def info(self): - return DPMSInfo(display=self.display, - opcode=self.display.get_extension_major(extname), - major_version=1, - minor_version=1) - - -def init(disp, _info): - disp.extension_add_method('display', 'dpms_get_version', get_version) - disp.extension_add_method('display', 'dpms_capable', capable) - disp.extension_add_method('display', 'dpms_get_timeouts', get_timeouts) - disp.extension_add_method('display', 'dpms_set_timeouts', set_timeouts) - disp.extension_add_method('display', 'dpms_enable', enable) - disp.extension_add_method('display', 'dpms_disable', disable) - disp.extension_add_method('display', 'dpms_force_level', force_level) - disp.extension_add_method('display', 'dpms_info', info) diff --git a/Nagstamon/thirdparty/Xlib/ext/ge.py b/Nagstamon/thirdparty/Xlib/ext/ge.py deleted file mode 100644 index 291ffa9a4..000000000 --- a/Nagstamon/thirdparty/Xlib/ext/ge.py +++ /dev/null @@ -1,112 +0,0 @@ -# Xlib.ext.ge -- Generic Event extension module -# -# Copyright (C) 2012 Outpost Embedded, LLC -# Forest Bond <forest.bond@rapidrollout.com> -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA - -''' -ge - Generic Event Extension -''' - -from Xlib.protocol import rq - -extname = 'Generic Event Extension' - - -GenericEventCode = 35 - - -class GEQueryVersion(rq.ReplyRequest): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(0), - rq.RequestLength(), - rq.Card32('major_version'), - rq.Card32('minor_version'), - ) - _reply = rq.Struct( - rq.ReplyCode(), - rq.Pad(1), - rq.Card16('sequence_number'), - rq.ReplyLength(), - rq.Card32('major_version'), - rq.Card32('minor_version'), - rq.Pad(16), - ) - - -def query_version(self): - return GEQueryVersion( - display=self.display, - opcode=self.display.get_extension_major(extname), - major_version=1, - minor_version=0, - ) - - -class GenericEvent(rq.Event): - _code = GenericEventCode - _fields = rq.Struct( - rq.Card8('type'), - rq.Card8('extension'), - rq.Card16('sequence_number'), - rq.Card32('length'), - rq.Card16('evtype'), - # Some generic events make use of this space, but with - # others the data is simply discarded. In any case we - # don't need to explicitly pad this out as we are - # always given at least 32 bytes and we save - # everything after the first ten as the "data" field. - #rq.Pad(22), - ) - - def __init__(self, binarydata = None, display = None, **keys): - if binarydata: - data = binarydata[10:] - binarydata = binarydata[:10] - else: - data = '' - - rq.Event.__init__( - self, - binarydata=binarydata, - display=display, - **keys - ) - - if display: - ge_event_data = getattr(display, 'ge_event_data', None) - if ge_event_data: - estruct = ge_event_data.get((self.extension, self.evtype), None) - if estruct: - data, _ = estruct.parse_binary(data, display) - - self._data['data'] = data - - -def add_event_data(self, extension, evtype, estruct): - if not hasattr(self.display, 'ge_event_data'): - self.display.ge_event_data = {} - self.display.ge_event_data[(extension, evtype)] = estruct - - -def init(disp, info): - disp.extension_add_method('display', 'ge_query_version', query_version) - disp.extension_add_method('display', 'ge_add_event_data', add_event_data) - disp.extension_add_event(GenericEventCode, GenericEvent) diff --git a/Nagstamon/thirdparty/Xlib/ext/nvcontrol.py b/Nagstamon/thirdparty/Xlib/ext/nvcontrol.py deleted file mode 100644 index 7a21826c9..000000000 --- a/Nagstamon/thirdparty/Xlib/ext/nvcontrol.py +++ /dev/null @@ -1,5393 +0,0 @@ -# Xlib.ext.nvcontrol -- NV-CONTROL extension module -# -# Copyright (C) 2019 Roberto Leinardi <roberto@leinardi.com> -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA - - -"""NV-CONTROL - provide access to the NV-CONTROL extension information.""" - -from Xlib.protocol import rq - -extname = 'NV-CONTROL' - - -def query_target_count(self, target): - """Return the target count""" - reply = NVCtrlQueryTargetCountReplyRequest(display=self.display, - opcode=self.display.get_extension_major(extname), - target_type=target.type()) - return int(reply._data.get('count')) - - -def query_int_attribute(self, target, display_mask, attr): - """Return the value of an integer attribute""" - reply = NVCtrlQueryAttributeReplyRequest(display=self.display, - opcode=self.display.get_extension_major(extname), - target_id=target.id(), - target_type=target.type(), - display_mask=display_mask, - attr=attr) - if not reply._data.get('flags'): - return None - return int(reply._data.get('value')) - - -def set_int_attribute(self, target, display_mask, attr, value): - """Set the value of an integer attribute""" - reply = NVCtrlSetAttributeAndGetStatusReplyRequest(display=self.display, - opcode=self.display.get_extension_major(extname), - target_id=target.id(), - target_type=target.type(), - display_mask=display_mask, - attr=attr, - value=value) - return reply._data.get('flags') != 0 - - -def query_string_attribute(self, target, display_mask, attr): - """Return the value of a string attribute""" - reply = NVCtrlQueryStringAttributeReplyRequest(display=self.display, - opcode=self.display.get_extension_major(extname), - target_id=target.id(), - target_type=target.type(), - display_mask=display_mask, - attr=attr) - if not reply._data.get('flags'): - return None - return str(reply._data.get('string')).strip('\0') - - -def query_valid_attr_values(self, target, display_mask, attr): - """Return the value of an integer attribute""" - reply = NVCtrlQueryValidAttributeValuesReplyRequest(display=self.display, - opcode=self.display.get_extension_major(extname), - target_id=target.id(), - target_type=target.type(), - display_mask=display_mask, - attr=attr) - if not reply._data.get('flags'): - return None - return int(reply._data.get('min')), int(reply._data.get('max')) - - -def query_binary_data(self, target, display_mask, attr): - """Return binary data""" - reply = NVCtrlQueryBinaryDataReplyRequest(display=self.display, - opcode=self.display.get_extension_major(extname), - target_id=target.id(), - target_type=target.type(), - display_mask=display_mask, - attr=attr) - if not reply._data.get('flags'): - return None - return reply._data.get('data') - - -def get_coolers_used_by_gpu(self, target): - reply = NVCtrlQueryListCard32ReplyRequest(display=self.display, - opcode=self.display.get_extension_major(extname), - target_id=target.id(), - target_type=target.type(), - display_mask=0, - attr=NV_CTRL_BINARY_DATA_COOLERS_USED_BY_GPU) - if not reply._data.get('flags'): - return None - fans = reply._data.get('list') - if len(fans) > 1: - return fans[1:] - else: - return None - - -def get_gpu_count(self): - """Return the number of GPU's present in the system.""" - return int(query_target_count(self, Gpu())) - - -def get_name(self, target): - """Return the GPU product name on which the specified X screen is running""" - return query_string_attribute(self, target, 0, NV_CTRL_STRING_PRODUCT_NAME) - - -def get_driver_version(self, target): - """Return the NVIDIA (kernel level) driver version for the specified screen or GPU""" - return query_string_attribute(self, target, 0, NV_CTRL_STRING_NVIDIA_DRIVER_VERSION) - - -def get_vbios_version(self, target): - """Return the version of the VBIOS for the specified screen or GPU""" - return query_string_attribute(self, target, 0, NV_CTRL_STRING_VBIOS_VERSION) - - -def get_gpu_uuid(self, target): - return query_string_attribute(self, target, 0, NV_CTRL_STRING_GPU_UUID) - - -def get_utilization_rates(self, target): - string = query_string_attribute(self, target, 0, NV_CTRL_STRING_GPU_UTILIZATION) - result = {} - if string is not None and string != '': - for line in string.split(','): - [key, value] = line.split('=')[:2] - result[key.strip()] = int(value) if value.isdigit() else value - return result - - -def get_performance_modes(self, target): - string = query_string_attribute(self, target, 0, NV_CTRL_STRING_PERFORMANCE_MODES) - result = [] - if string is not None and string != '': - for perf in string.split(';'): - perf_dict = {} - for line in perf.split(','): - [key, value] = line.split('=')[:2] - perf_dict[key.strip()] = int(value) if value.isdigit() else value - result.append(perf_dict) - return result - - -def get_clock_info(self, target): - string = query_string_attribute(self, target, 0, NV_CTRL_STRING_GPU_CURRENT_CLOCK_FREQS) - result = {} - if string is not None and string != '': - for line in string.split(','): - [key, value] = line.split('=')[:2] - result[key.strip()] = int(value) if value.isdigit() else value - return result - - -def get_vram(self, target): - return query_int_attribute(self, target, 0, NV_CTRL_VIDEO_RAM) - - -def get_irq(self, target): - """Return the interrupt request line used by the GPU driving the screen""" - return query_int_attribute(self, target, 0, NV_CTRL_IRQ) - - -def supports_framelock(self, target): - """Return whether the underlying GPU supports Frame Lock. - - All of the other frame lock attributes are only applicable if this returns True. - """ - return query_int_attribute(self, target, 0, NV_CTRL_FRAMELOCK) == 1 - - -def gvo_supported(self, screen): - """Return whether this X screen supports GVO - - If this screen does not support GVO output, then all other GVO attributes are unavailable. - """ - return query_int_attribute(self, screen, [], NV_CTRL_GVO_SUPPORTED) - - -def get_core_temp(self, target): - """Return the current core temperature of the GPU driving the X screen.""" - return query_int_attribute(self, target, 0, NV_CTRL_GPU_CORE_TEMPERATURE) - - -def get_core_threshold(self, target): - """Return the current GPU core slowdown threshold temperature. - - It reflects the temperature at which the GPU is throttled to prevent overheating. - """ - return query_int_attribute(self, target, 0, NV_CTRL_GPU_CORE_THRESHOLD) - - -def get_default_core_threshold(self, target): - """Return the default core threshold temperature.""" - return query_int_attribute(self, target, 0, NV_CTRL_GPU_DEFAULT_CORE_THRESHOLD) - - -def get_max_core_threshold(self, target): - """Return the maximum core threshold temperature.""" - return query_int_attribute(self, target, 0, NV_CTRL_GPU_MAX_CORE_THRESHOLD) - - -def get_ambient_temp(self, target): - """Return the current temperature in the immediate neighbourhood of the GPU driving the X screen.""" - return query_int_attribute(self, target, 0, NV_CTRL_AMBIENT_TEMPERATURE) - - -def get_cuda_cores(self, target): - return query_int_attribute(self, target, 0, NV_CTRL_GPU_CORES) - - -def get_memory_bus_width(self, target): - return query_int_attribute(self, target, 0, NV_CTRL_GPU_MEMORY_BUS_WIDTH) - - -def get_total_dedicated_gpu_memory(self, target): - return query_int_attribute(self, target, 0, NV_CTRL_TOTAL_DEDICATED_GPU_MEMORY) - - -def get_used_dedicated_gpu_memory(self, target): - return query_int_attribute(self, target, 0, NV_CTRL_USED_DEDICATED_GPU_MEMORY) - - -def get_curr_pcie_link_width(self, target): - return query_int_attribute(self, target, 0, NV_CTRL_GPU_PCIE_CURRENT_LINK_WIDTH) - - -def get_max_pcie_link_width(self, target): - return query_int_attribute(self, target, 0, NV_CTRL_GPU_PCIE_MAX_LINK_WIDTH) - - -def get_curr_pcie_link_generation(self, target): - return query_int_attribute(self, target, 0, NV_CTRL_GPU_PCIE_GENERATION) - - -def get_encoder_utilization(self, target): - return query_int_attribute(self, target, 0, NV_CTRL_VIDEO_ENCODER_UTILIZATION) - - -def get_decoder_utilization(self, target): - return query_int_attribute(self, target, 0, NV_CTRL_VIDEO_DECODER_UTILIZATION) - - -def get_current_performance_level(self, target): - return query_int_attribute(self, target, 0, NV_CTRL_GPU_CURRENT_PERFORMANCE_LEVEL) - - -def get_gpu_nvclock_offset(self, target, perf_level): - return query_int_attribute(self, target, perf_level, NV_CTRL_GPU_NVCLOCK_OFFSET) - - -def set_gpu_nvclock_offset(self, target, perf_level, offset): - return set_int_attribute(self, target, perf_level, NV_CTRL_GPU_NVCLOCK_OFFSET, offset) - - -def set_gpu_nvclock_offset_all_levels(self, target, offset): - return set_int_attribute(self, target, 0, NV_CTRL_GPU_NVCLOCK_OFFSET_ALL_PERFORMANCE_LEVELS, offset) - - -def get_gpu_nvclock_offset_range(self, target, perf_level): - return query_valid_attr_values(self, target, perf_level, NV_CTRL_GPU_NVCLOCK_OFFSET) - - -def get_mem_transfer_rate_offset(self, target, perf_level): - return query_int_attribute(self, target, perf_level, NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET) - - -def set_mem_transfer_rate_offset(self, target, perf_level, offset): - return set_int_attribute(self, target, perf_level, NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET, offset) - - -def set_mem_transfer_rate_offset_all_levels(self, target, offset): - return set_int_attribute(self, target, 0, NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET_ALL_PERFORMANCE_LEVELS, offset) - - -def get_mem_transfer_rate_offset_range(self, target, perf_level): - return query_valid_attr_values(self, target, perf_level, NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET) - - -def get_cooler_manual_control_enabled(self, target): - return query_int_attribute(self, target, 0, NV_CTRL_GPU_COOLER_MANUAL_CONTROL) - - -def set_cooler_manual_control_enabled(self, target, enabled): - return set_int_attribute(self, target, 0, NV_CTRL_GPU_COOLER_MANUAL_CONTROL, 1 if enabled else 0) == 1 - - -def get_fan_duty(self, target): - return query_int_attribute(self, target, 0, NV_CTRL_THERMAL_COOLER_CURRENT_LEVEL) - - -def set_fan_duty(self, cooler, speed): - return set_int_attribute(self, cooler, 0, NV_CTRL_THERMAL_COOLER_LEVEL, speed) - - -def get_fan_rpm(self, target): - return query_int_attribute(self, target, 0, NV_CTRL_THERMAL_COOLER_SPEED) - - -def get_max_displays(self, target): - """Return the maximum number of display devices that can be driven simultaneously on a GPU. - - Note that this does not indicate the maximum number of bits that can be set in - NV_CTRL_CONNECTED_DISPLAYS, because more display devices can be connected than are actively - in use. - """ - return query_int_attribute(self, target, 0, NV_CTRL_MAX_DISPLAYS) - - -def _displaystr2num(st): - """Return a display number from a string""" - num = None - for s, n in [('DFP-', 16), ('TV-', 8), ('CRT-', 0)]: - if st.startswith(s): - try: - curnum = int(st[len(s):]) - if 0 <= curnum <= 7: - num = n + curnum - break - except Exception: - pass - if num is not None: - return num - else: - raise ValueError('Unrecognised display name: ' + st) - - -def _displays2mask(displays): - """Return a display mask from an array of display numbers.""" - mask = 0 - for d in displays: - mask += (1 << _displaystr2num(d)) - return mask - - -def init(disp, info): - disp.extension_add_method('display', 'nvcontrol_query_target_count', query_target_count) - disp.extension_add_method('display', 'nvcontrol_query_int_attribute', query_int_attribute) - disp.extension_add_method('display', 'nvcontrol_query_string_attribute', query_string_attribute) - disp.extension_add_method('display', 'nvcontrol_query_valid_attr_values', query_valid_attr_values) - disp.extension_add_method('display', 'nvcontrol_query_binary_data', query_binary_data) - disp.extension_add_method('display', 'nvcontrol_get_gpu_count', get_gpu_count) - disp.extension_add_method('display', 'nvcontrol_get_vram', get_vram) - disp.extension_add_method('display', 'nvcontrol_get_irq', get_irq) - disp.extension_add_method('display', 'nvcontrol_supports_framelock', supports_framelock) - disp.extension_add_method('display', 'nvcontrol_get_core_temp', get_core_temp) - disp.extension_add_method('display', 'nvcontrol_get_core_threshold', get_core_threshold) - disp.extension_add_method('display', 'nvcontrol_get_default_core_threshold', get_default_core_threshold) - disp.extension_add_method('display', 'nvcontrol_get_max_core_threshold', get_max_core_threshold) - disp.extension_add_method('display', 'nvcontrol_get_ambient_temp', get_ambient_temp) - disp.extension_add_method('display', 'nvcontrol_get_cuda_cores', get_cuda_cores) - disp.extension_add_method('display', 'nvcontrol_get_memory_bus_width', get_memory_bus_width) - disp.extension_add_method('display', 'nvcontrol_get_total_dedicated_gpu_memory', get_total_dedicated_gpu_memory) - disp.extension_add_method('display', 'nvcontrol_get_used_dedicated_gpu_memory', get_used_dedicated_gpu_memory) - disp.extension_add_method('display', 'nvcontrol_get_curr_pcie_link_width', get_curr_pcie_link_width) - disp.extension_add_method('display', 'nvcontrol_get_max_pcie_link_width', get_max_pcie_link_width) - disp.extension_add_method('display', 'nvcontrol_get_curr_pcie_link_generation', get_curr_pcie_link_generation) - disp.extension_add_method('display', 'nvcontrol_get_encoder_utilization', get_encoder_utilization) - disp.extension_add_method('display', 'nvcontrol_get_decoder_utilization', get_decoder_utilization) - disp.extension_add_method('display', 'nvcontrol_get_current_performance_level', get_current_performance_level) - disp.extension_add_method('display', 'nvcontrol_get_gpu_nvclock_offset', get_gpu_nvclock_offset) - disp.extension_add_method('display', 'nvcontrol_set_gpu_nvclock_offset', set_gpu_nvclock_offset) - disp.extension_add_method('display', 'nvcontrol_set_gpu_nvclock_offset_all_levels', set_gpu_nvclock_offset_all_levels) - disp.extension_add_method('display', 'nvcontrol_get_mem_transfer_rate_offset', get_mem_transfer_rate_offset) - disp.extension_add_method('display', 'nvcontrol_set_mem_transfer_rate_offset', set_mem_transfer_rate_offset) - disp.extension_add_method('display', 'nvcontrol_set_mem_transfer_rate_offset_all_levels', set_mem_transfer_rate_offset_all_levels) - disp.extension_add_method('display', 'nvcontrol_get_cooler_manual_control_enabled', - get_cooler_manual_control_enabled) - disp.extension_add_method('display', 'nvcontrol_get_fan_duty', get_fan_duty) - disp.extension_add_method('display', 'nvcontrol_set_fan_duty', set_fan_duty) - disp.extension_add_method('display', 'nvcontrol_get_fan_rpm', get_fan_rpm) - disp.extension_add_method('display', 'nvcontrol_get_coolers_used_by_gpu', get_coolers_used_by_gpu) - disp.extension_add_method('display', 'nvcontrol_get_max_displays', get_max_displays) - disp.extension_add_method('display', 'nvcontrol_get_name', get_name) - disp.extension_add_method('display', 'nvcontrol_get_driver_version', get_driver_version) - disp.extension_add_method('display', 'nvcontrol_get_vbios_version', get_vbios_version) - disp.extension_add_method('display', 'nvcontrol_get_gpu_uuid', get_gpu_uuid) - disp.extension_add_method('display', 'nvcontrol_get_utilization_rates', get_utilization_rates) - disp.extension_add_method('display', 'nvcontrol_get_performance_modes', get_performance_modes) - disp.extension_add_method('display', 'nvcontrol_get_clock_info', get_clock_info) - disp.extension_add_method('display', 'nvcontrol_set_cooler_manual_control_enabled', - set_cooler_manual_control_enabled) - disp.extension_add_method('display', 'nvcontrol_get_gpu_nvclock_offset_range', - get_gpu_nvclock_offset_range) - disp.extension_add_method('display', 'nvcontrol_get_mem_transfer_rate_offset_range', - get_mem_transfer_rate_offset_range) - - -############################################################################ -# -# Attributes -# -# Some attributes may only be read; some may require a display_mask -# argument and others may be valid only for specific target types. -# This information is encoded in the "permission" comment after each -# attribute #define, and can be queried at run time with -# XNVCTRLQueryValidAttributeValues() and/or -# XNVCTRLQueryValidTargetAttributeValues() -# -# Key to Integer Attribute "Permissions": -# -# R: The attribute is readable (in general, all attributes will be -# readable) -# -# W: The attribute is writable (attributes may not be writable for -# various reasons: they represent static system information, they -# can only be changed by changing an XF86Config option, etc). -# -# D: The attribute requires the display mask argument. The -# attributes NV_CTRL_CONNECTED_DISPLAYS and NV_CTRL_ENABLED_DISPLAYS -# will be a bitmask of what display devices are connected and what -# display devices are enabled for use in X, respectively. Each bit -# in the bitmask represents a display device; it is these bits which -# should be used as the display_mask when dealing with attributes -# designated with "D" below. For attributes that do not require the -# display mask, the argument is ignored. -# -# Alternatively, NV-CONTROL versions 1.27 and greater allow these -# attributes to be accessed via display target types, in which case -# the display_mask is ignored. -# -# G: The attribute may be queried using an NV_CTRL_TARGET_TYPE_GPU -# target type via XNVCTRLQueryTargetAttribute(). -# -# F: The attribute may be queried using an NV_CTRL_TARGET_TYPE_FRAMELOCK -# target type via XNVCTRLQueryTargetAttribute(). -# -# X: When Xinerama is enabled, this attribute is kept consistent across -# all Physical X Screens; assignment of this attribute will be -# broadcast by the NVIDIA X Driver to all X Screens. -# -# V: The attribute may be queried using an NV_CTRL_TARGET_TYPE_VCSC -# target type via XNVCTRLQueryTargetAttribute(). -# -# I: The attribute may be queried using an NV_CTRL_TARGET_TYPE_GVI target type -# via XNVCTRLQueryTargetAttribute(). -# -# Q: The attribute is a 64-bit integer attribute; use the 64-bit versions -# of the appropriate query interfaces. -# -# C: The attribute may be queried using an NV_CTRL_TARGET_TYPE_COOLER target -# type via XNVCTRLQueryTargetAttribute(). -# -# S: The attribute may be queried using an NV_CTRL_TARGET_TYPE_THERMAL_SENSOR -# target type via XNVCTRLQueryTargetAttribute(). -# -# T: The attribute may be queried using an -# NV_CTRL_TARGET_TYPE_3D_VISION_PRO_TRANSCEIVER target type -# via XNVCTRLQueryTargetAttribute(). -# -# NOTE: Unless mentioned otherwise, all attributes may be queried using -# an NV_CTRL_TARGET_TYPE_X_SCREEN target type via -# XNVCTRLQueryTargetAttribute(). -# - - -############################################################################ - -# -# Integer attributes: -# -# Integer attributes can be queried through the XNVCTRLQueryAttribute() and -# XNVCTRLQueryTargetAttribute() function calls. -# -# Integer attributes can be set through the XNVCTRLSetAttribute() and -# XNVCTRLSetTargetAttribute() function calls. -# -# Unless otherwise noted, all integer attributes can be queried/set -# using an NV_CTRL_TARGET_TYPE_X_SCREEN target. Attributes that cannot -# take an NV_CTRL_TARGET_TYPE_X_SCREEN also cannot be queried/set through -# XNVCTRLQueryAttribute()/XNVCTRLSetAttribute() (Since these assume -# an X Screen target). -# - - -# -# NV_CTRL_FLATPANEL_SCALING - not supported -# - -NV_CTRL_FLATPANEL_SCALING = 2 # not supported -NV_CTRL_FLATPANEL_SCALING_DEFAULT = 0 # not supported -NV_CTRL_FLATPANEL_SCALING_NATIVE = 1 # not supported -NV_CTRL_FLATPANEL_SCALING_SCALED = 2 # not supported -NV_CTRL_FLATPANEL_SCALING_CENTERED = 3 # not supported -NV_CTRL_FLATPANEL_SCALING_ASPECT_SCALED = 4 # not supported - -# -# NV_CTRL_FLATPANEL_DITHERING - not supported -# -# NV_CTRL_DITHERING should be used instead. -# - -NV_CTRL_FLATPANEL_DITHERING = 3 # not supported -NV_CTRL_FLATPANEL_DITHERING_DEFAULT = 0 # not supported -NV_CTRL_FLATPANEL_DITHERING_ENABLED = 1 # not supported -NV_CTRL_FLATPANEL_DITHERING_DISABLED = 2 # not supported - -# -# NV_CTRL_DITHERING - the requested dithering configuration; -# possible values are: -# -# 0: auto (the driver will decide when to dither) -# 1: enabled (the driver will always dither when possible) -# 2: disabled (the driver will never dither) -# - -NV_CTRL_DITHERING = 3 # RWDG -NV_CTRL_DITHERING_AUTO = 0 -NV_CTRL_DITHERING_ENABLED = 1 -NV_CTRL_DITHERING_DISABLED = 2 - -# -# NV_CTRL_DIGITAL_VIBRANCE - sets the digital vibrance level for the -# specified display device. -# - -NV_CTRL_DIGITAL_VIBRANCE = 4 # RWDG - -# -# NV_CTRL_BUS_TYPE - returns the bus type through which the specified device -# is connected to the computer. -# When this attribute is queried on an X screen target, the bus type of the -# GPU driving the X screen is returned. -# - -NV_CTRL_BUS_TYPE = 5 # R--GI -NV_CTRL_BUS_TYPE_AGP = 0 -NV_CTRL_BUS_TYPE_PCI = 1 -NV_CTRL_BUS_TYPE_PCI_EXPRESS = 2 -NV_CTRL_BUS_TYPE_INTEGRATED = 3 - -# -# NV_CTRL_TOTAL_GPU_MEMORY - returns the total amount of memory available -# to the specified GPU (or the GPU driving the specified X -# screen). Note: if the GPU supports TurboCache(TM), the value -# reported may exceed the amount of video memory installed on the -# GPU. The value reported for integrated GPUs may likewise exceed -# the amount of dedicated system memory set aside by the system -# BIOS for use by the integrated GPU. -# - -NV_CTRL_TOTAL_GPU_MEMORY = 6 # R--G -NV_CTRL_VIDEO_RAM = NV_CTRL_TOTAL_GPU_MEMORY - -# -# NV_CTRL_IRQ - returns the interrupt request line used by the specified -# device. -# When this attribute is queried on an X screen target, the IRQ of the GPU -# driving the X screen is returned. -# - -NV_CTRL_IRQ = 7 # R--GI - -# -# NV_CTRL_OPERATING_SYSTEM - returns the operating system on which -# the X server is running. -# - -NV_CTRL_OPERATING_SYSTEM = 8 # R--G -NV_CTRL_OPERATING_SYSTEM_LINUX = 0 -NV_CTRL_OPERATING_SYSTEM_FREEBSD = 1 -NV_CTRL_OPERATING_SYSTEM_SUNOS = 2 - -# -# NV_CTRL_SYNC_TO_VBLANK - enables sync to vblank for OpenGL clients. -# This setting is only applied to OpenGL clients that are started -# after this setting is applied. -# - -NV_CTRL_SYNC_TO_VBLANK = 9 # RW-X -NV_CTRL_SYNC_TO_VBLANK_OFF = 0 -NV_CTRL_SYNC_TO_VBLANK_ON = 1 - -# -# NV_CTRL_LOG_ANISO - enables anisotropic filtering for OpenGL -# clients; on some NVIDIA hardware, this can only be enabled or -# disabled; on other hardware different levels of anisotropic -# filtering can be specified. This setting is only applied to OpenGL -# clients that are started after this setting is applied. -# - -NV_CTRL_LOG_ANISO = 10 # RW-X - -# -# NV_CTRL_FSAA_MODE - the FSAA setting for OpenGL clients; possible -# FSAA modes: -# -# NV_CTRL_FSAA_MODE_2x "2x Bilinear Multisampling" -# NV_CTRL_FSAA_MODE_2x_5t "2x Quincunx Multisampling" -# NV_CTRL_FSAA_MODE_15x15 "1.5 x 1.5 Supersampling" -# NV_CTRL_FSAA_MODE_2x2 "2 x 2 Supersampling" -# NV_CTRL_FSAA_MODE_4x "4x Bilinear Multisampling" -# NV_CTRL_FSAA_MODE_4x_9t "4x Gaussian Multisampling" -# NV_CTRL_FSAA_MODE_8x "2x Bilinear Multisampling by 4x Supersampling" -# NV_CTRL_FSAA_MODE_16x "4x Bilinear Multisampling by 4x Supersampling" -# NV_CTRL_FSAA_MODE_8xS "4x Multisampling by 2x Supersampling" -# -# This setting is only applied to OpenGL clients that are started -# after this setting is applied. -# - -NV_CTRL_FSAA_MODE = 11 # RW-X -NV_CTRL_FSAA_MODE_NONE = 0 -NV_CTRL_FSAA_MODE_2x = 1 -NV_CTRL_FSAA_MODE_2x_5t = 2 -NV_CTRL_FSAA_MODE_15x15 = 3 -NV_CTRL_FSAA_MODE_2x2 = 4 -NV_CTRL_FSAA_MODE_4x = 5 -NV_CTRL_FSAA_MODE_4x_9t = 6 -NV_CTRL_FSAA_MODE_8x = 7 -NV_CTRL_FSAA_MODE_16x = 8 -NV_CTRL_FSAA_MODE_8xS = 9 -NV_CTRL_FSAA_MODE_8xQ = 10 -NV_CTRL_FSAA_MODE_16xS = 11 -NV_CTRL_FSAA_MODE_16xQ = 12 -NV_CTRL_FSAA_MODE_32xS = 13 -NV_CTRL_FSAA_MODE_32x = 14 -NV_CTRL_FSAA_MODE_64xS = 15 -NV_CTRL_FSAA_MODE_MAX = NV_CTRL_FSAA_MODE_64xS - -# -# NV_CTRL_UBB - returns whether UBB is enabled for the specified X -# screen. -# - -NV_CTRL_UBB = 13 # R-- -NV_CTRL_UBB_OFF = 0 -NV_CTRL_UBB_ON = 1 - -# -# NV_CTRL_OVERLAY - returns whether the RGB overlay is enabled for -# the specified X screen. -# - -NV_CTRL_OVERLAY = 14 # R-- -NV_CTRL_OVERLAY_OFF = 0 -NV_CTRL_OVERLAY_ON = 1 - -# -# NV_CTRL_STEREO - returns whether stereo (and what type) is enabled -# for the specified X screen. -# - -NV_CTRL_STEREO = 16 # R-- -NV_CTRL_STEREO_OFF = 0 -NV_CTRL_STEREO_DDC = 1 -NV_CTRL_STEREO_BLUELINE = 2 -NV_CTRL_STEREO_DIN = 3 -NV_CTRL_STEREO_PASSIVE_EYE_PER_DPY = 4 -NV_CTRL_STEREO_VERTICAL_INTERLACED = 5 -NV_CTRL_STEREO_COLOR_INTERLACED = 6 -NV_CTRL_STEREO_HORIZONTAL_INTERLACED = 7 -NV_CTRL_STEREO_CHECKERBOARD_PATTERN = 8 -NV_CTRL_STEREO_INVERSE_CHECKERBOARD_PATTERN = 9 -NV_CTRL_STEREO_3D_VISION = 10 -NV_CTRL_STEREO_3D_VISION_PRO = 11 -NV_CTRL_STEREO_HDMI_3D = 12 -NV_CTRL_STEREO_TRIDELITY_SL = 13 -NV_CTRL_STEREO_INBAND_STEREO_SIGNALING = 14 -NV_CTRL_STEREO_MAX = NV_CTRL_STEREO_INBAND_STEREO_SIGNALING - -# -# NV_CTRL_EMULATE - not supported -# - -NV_CTRL_EMULATE = 17 # not supported -NV_CTRL_EMULATE_NONE = 0 # not supported - -# -# NV_CTRL_TWINVIEW - returns whether TwinView is enabled for the -# specified X screen. -# - -NV_CTRL_TWINVIEW = 18 # R-- -NV_CTRL_TWINVIEW_NOT_ENABLED = 0 -NV_CTRL_TWINVIEW_ENABLED = 1 - -# -# NV_CTRL_CONNECTED_DISPLAYS - deprecated -# -# NV_CTRL_BINARY_DATA_DISPLAYS_CONNECTED_TO_GPU and -# NV_CTRL_BINARY_DATA_DISPLAYS_ASSIGNED_TO_XSCREEN should be used instead. -# - -NV_CTRL_CONNECTED_DISPLAYS = 19 # deprecated - -# -# NV_CTRL_ENABLED_DISPLAYS - Event that notifies when one or more display -# devices are enabled or disabled on a GPU and/or X screen. -# -# This attribute may be queried through XNVCTRLQueryTargetAttribute() -# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. -# -# Note: Querying this value has been deprecated. -# NV_CTRL_BINARY_DATA_DISPLAYS_CONNECTED_TO_GPU, -# NV_CTRL_DISPLAY_ENABLED, and -# NV_CTRL_BINARY_DATA_DISPLAYS_ENABLED_ON_XSCREEN should be used -# instead to obtain the list of enabled displays. -# - -NV_CTRL_ENABLED_DISPLAYS = 20 # ---G - -############################################################################ -# -# Integer attributes specific to configuring Frame Lock on boards that -# support it. -# - - -# -# NV_CTRL_FRAMELOCK - returns whether the underlying GPU supports -# Frame Lock. All of the other frame lock attributes are only -# applicable if NV_CTRL_FRAMELOCK is _SUPPORTED. -# -# This attribute may be queried through XNVCTRLQueryTargetAttribute() -# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. -# - -NV_CTRL_FRAMELOCK = 21 # R--G -NV_CTRL_FRAMELOCK_NOT_SUPPORTED = 0 -NV_CTRL_FRAMELOCK_SUPPORTED = 1 - -# -# NV_CTRL_FRAMELOCK_MASTER - deprecated -# -# NV_CTRL_FRAMELOCK_DISPLAY_CONFIG should be used instead. -# - -NV_CTRL_FRAMELOCK_MASTER = 22 # deprecated -NV_CTRL_FRAMELOCK_MASTER_FALSE = 0 # deprecated -NV_CTRL_FRAMELOCK_MASTER_TRUE = 1 # deprecated - -# -# NV_CTRL_FRAMELOCK_POLARITY - sync either to the rising edge of the -# frame lock pulse, the falling edge of the frame lock pulse or both. -# -# On Quadro Sync II, this attribute is ignored when -# NV_CTRL_USE_HOUSE_SYNC is OUTPUT. -# -# This attribute may be queried through XNVCTRLQueryTargetAttribute() -# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN -# target. -# - -NV_CTRL_FRAMELOCK_POLARITY = 23 # RW-F -NV_CTRL_FRAMELOCK_POLARITY_RISING_EDGE = 0x1 -NV_CTRL_FRAMELOCK_POLARITY_FALLING_EDGE = 0x2 -NV_CTRL_FRAMELOCK_POLARITY_BOTH_EDGES = 0x3 - -# -# NV_CTRL_FRAMELOCK_SYNC_DELAY - delay between the frame lock pulse -# and the GPU sync. This value must be multiplied by -# NV_CTRL_FRAMELOCK_SYNC_DELAY_RESOLUTION to determine the sync delay in -# nanoseconds. -# -# This attribute may be queried through XNVCTRLQueryTargetAttribute() -# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN -# target. -# -# USAGE NOTE: NV_CTRL_FRAMELOCK_SYNC_DELAY_MAX and -# NV_CTRL_FRAMELOCK_SYNC_DELAY_FACTOR are deprecated. -# The Sync Delay _MAX and _FACTOR are different for different -# Quadro Sync products and so, to be correct, the valid values for -# NV_CTRL_FRAMELOCK_SYNC_DELAY must be queried to get the range -# of acceptable sync delay values, and -# NV_CTRL_FRAMELOCK_SYNC_DELAY_RESOLUTION must be queried to -# obtain the correct factor. -# - -NV_CTRL_FRAMELOCK_SYNC_DELAY = 24 # RW-F -NV_CTRL_FRAMELOCK_SYNC_DELAY_MAX = 2047 # deprecated -NV_CTRL_FRAMELOCK_SYNC_DELAY_FACTOR = 7.81 # deprecated - -# -# NV_CTRL_FRAMELOCK_SYNC_INTERVAL - how many house sync pulses -# between the frame lock sync generation (0 == sync every house sync); -# this only applies to the master when receiving house sync. -# -# This attribute may be queried through XNVCTRLQueryTargetAttribute() -# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN -# target. -# - -NV_CTRL_FRAMELOCK_SYNC_INTERVAL = 25 # RW-F - -# -# NV_CTRL_FRAMELOCK_PORT0_STATUS - status of the rj45 port0. -# -# This attribute may be queried through XNVCTRLQueryTargetAttribute() -# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN -# target. -# - -NV_CTRL_FRAMELOCK_PORT0_STATUS = 26 # R--F -NV_CTRL_FRAMELOCK_PORT0_STATUS_INPUT = 0 -NV_CTRL_FRAMELOCK_PORT0_STATUS_OUTPUT = 1 - -# -# NV_CTRL_FRAMELOCK_PORT1_STATUS - status of the rj45 port1. -# -# This attribute may be queried through XNVCTRLQueryTargetAttribute() -# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN -# target. -# - -NV_CTRL_FRAMELOCK_PORT1_STATUS = 27 # R--F -NV_CTRL_FRAMELOCK_PORT1_STATUS_INPUT = 0 -NV_CTRL_FRAMELOCK_PORT1_STATUS_OUTPUT = 1 - -# -# NV_CTRL_FRAMELOCK_HOUSE_STATUS - returns whether or not the house -# sync input signal was detected on the BNC connector of the frame lock -# board. -# -# This attribute may be queried through XNVCTRLQueryTargetAttribute() -# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN -# target. -# - -NV_CTRL_FRAMELOCK_HOUSE_STATUS = 28 # R--F -NV_CTRL_FRAMELOCK_HOUSE_STATUS_NOT_DETECTED = 0 -NV_CTRL_FRAMELOCK_HOUSE_STATUS_DETECTED = 1 - -# -# NV_CTRL_FRAMELOCK_SYNC - enable/disable the syncing of display -# devices to the frame lock pulse as specified by previous calls to -# NV_CTRL_FRAMELOCK_DISPLAY_CONFIG. -# -# This attribute can only be queried through XNVCTRLQueryTargetAttribute() -# using a NV_CTRL_TARGET_TYPE_GPU target. This attribute cannot be -# queried using a NV_CTRL_TARGET_TYPE_X_SCREEN. -# - -NV_CTRL_FRAMELOCK_SYNC = 29 # RW-G -NV_CTRL_FRAMELOCK_SYNC_DISABLE = 0 -NV_CTRL_FRAMELOCK_SYNC_ENABLE = 1 - -# -# NV_CTRL_FRAMELOCK_SYNC_READY - reports whether a frame lock -# board is receiving sync (regardless of whether or not any display -# devices are using the sync). -# -# This attribute may be queried through XNVCTRLQueryTargetAttribute() -# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN -# target. -# - -NV_CTRL_FRAMELOCK_SYNC_READY = 30 # R--F -NV_CTRL_FRAMELOCK_SYNC_READY_FALSE = 0 -NV_CTRL_FRAMELOCK_SYNC_READY_TRUE = 1 - -# -# NV_CTRL_FRAMELOCK_STEREO_SYNC - this indicates that the GPU stereo -# signal is in sync with the frame lock stereo signal. -# -# This attribute may be queried through XNVCTRLQueryTargetAttribute() -# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN -# target. -# - -NV_CTRL_FRAMELOCK_STEREO_SYNC = 31 # R--G -NV_CTRL_FRAMELOCK_STEREO_SYNC_FALSE = 0 -NV_CTRL_FRAMELOCK_STEREO_SYNC_TRUE = 1 - -# -# NV_CTRL_FRAMELOCK_TEST_SIGNAL - to test the connections in the sync -# group, tell the master to enable a test signal, then query port[01] -# status and sync_ready on all slaves. When done, tell the master to -# disable the test signal. Test signal should only be manipulated -# while NV_CTRL_FRAMELOCK_SYNC is enabled. -# -# The TEST_SIGNAL is also used to reset the Universal Frame Count (as -# returned by the glXQueryFrameCountNV() function in the -# GLX_NV_swap_group extension). Note: for best accuracy of the -# Universal Frame Count, it is recommended to toggle the TEST_SIGNAL -# on and off after enabling frame lock. -# -# This attribute may be queried through XNVCTRLQueryTargetAttribute() -# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. -# - -NV_CTRL_FRAMELOCK_TEST_SIGNAL = 32 # RW-G -NV_CTRL_FRAMELOCK_TEST_SIGNAL_DISABLE = 0 -NV_CTRL_FRAMELOCK_TEST_SIGNAL_ENABLE = 1 - -# -# NV_CTRL_FRAMELOCK_ETHERNET_DETECTED - The frame lock boards are -# cabled together using regular cat5 cable, connecting to rj45 ports -# on the backplane of the card. There is some concern that users may -# think these are ethernet ports and connect them to a -# router/hub/etc. The hardware can detect this and will shut off to -# prevent damage (either to itself or to the router). -# NV_CTRL_FRAMELOCK_ETHERNET_DETECTED may be called to find out if -# ethernet is connected to one of the rj45 ports. An appropriate -# error message should then be displayed. The _PORT0 and _PORT1 -# values may be or'ed together. -# -# This attribute may be queried through XNVCTRLQueryTargetAttribute() -# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN -# target. -# - -NV_CTRL_FRAMELOCK_ETHERNET_DETECTED = 33 # R--F -NV_CTRL_FRAMELOCK_ETHERNET_DETECTED_NONE = 0 -NV_CTRL_FRAMELOCK_ETHERNET_DETECTED_PORT0 = 0x1 -NV_CTRL_FRAMELOCK_ETHERNET_DETECTED_PORT1 = 0x2 - -# -# NV_CTRL_FRAMELOCK_VIDEO_MODE - get/set what video mode is used -# to interperate the house sync signal. This should only be set -# on the master. -# -# This attribute may be queried through XNVCTRLQueryTargetAttribute() -# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN -# target. -# - -NV_CTRL_FRAMELOCK_VIDEO_MODE = 34 # RW-F -NV_CTRL_FRAMELOCK_VIDEO_MODE_NONE = 0 -NV_CTRL_FRAMELOCK_VIDEO_MODE_TTL = 1 -NV_CTRL_FRAMELOCK_VIDEO_MODE_NTSCPALSECAM = 2 -NV_CTRL_FRAMELOCK_VIDEO_MODE_HDTV = 3 - -# -# During FRAMELOCK bring-up, the above values were redefined to -# these: -# - -NV_CTRL_FRAMELOCK_VIDEO_MODE_COMPOSITE_AUTO = 0 -NV_CTRL_FRAMELOCK_VIDEO_MODE_COMPOSITE_BI_LEVEL = 2 -NV_CTRL_FRAMELOCK_VIDEO_MODE_COMPOSITE_TRI_LEVEL = 3 - -# -# NV_CTRL_FRAMELOCK_SYNC_RATE - this is the refresh rate that the -# frame lock board is sending to the GPU, in milliHz. -# -# This attribute may be queried through XNVCTRLQueryTargetAttribute() -# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN -# target. -# - -NV_CTRL_FRAMELOCK_SYNC_RATE = 35 # R--F - -############################################################################ - -# -# NV_CTRL_FORCE_GENERIC_CPU - not supported -# - -NV_CTRL_FORCE_GENERIC_CPU = 37 # not supported -NV_CTRL_FORCE_GENERIC_CPU_DISABLE = 0 # not supported -NV_CTRL_FORCE_GENERIC_CPU_ENABLE = 1 # not supported - -# -# NV_CTRL_OPENGL_AA_LINE_GAMMA - for OpenGL clients, allow -# Gamma-corrected antialiased lines to consider variances in the -# color display capabilities of output devices when rendering smooth -# lines. Only available on recent Quadro GPUs. This setting is only -# applied to OpenGL clients that are started after this setting is -# applied. -# - -NV_CTRL_OPENGL_AA_LINE_GAMMA = 38 # RW-X -NV_CTRL_OPENGL_AA_LINE_GAMMA_DISABLE = 0 -NV_CTRL_OPENGL_AA_LINE_GAMMA_ENABLE = 1 - -# -# NV_CTRL_FRAMELOCK_TIMING - this is TRUE when the gpu is both receiving -# and locked to an input timing signal. Timing information may come from -# the following places: Another frame lock device that is set to master, -# the house sync signal, or the GPU's internal timing from a display -# device. -# -# This attribute may be queried through XNVCTRLQueryTargetAttribute() -# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. -# - -NV_CTRL_FRAMELOCK_TIMING = 39 # R--G -NV_CTRL_FRAMELOCK_TIMING_FALSE = 0 -NV_CTRL_FRAMELOCK_TIMING_TRUE = 1 - -# -# NV_CTRL_FLIPPING_ALLOWED - when TRUE, OpenGL will swap by flipping -# when possible; when FALSE, OpenGL will always swap by blitting. -# - -NV_CTRL_FLIPPING_ALLOWED = 40 # RW-X -NV_CTRL_FLIPPING_ALLOWED_FALSE = 0 -NV_CTRL_FLIPPING_ALLOWED_TRUE = 1 - -# -# NV_CTRL_ARCHITECTURE - returns the architecture on which the X server is -# running. -# - -NV_CTRL_ARCHITECTURE = 41 # R-- -NV_CTRL_ARCHITECTURE_X86 = 0 -NV_CTRL_ARCHITECTURE_X86_64 = 1 -NV_CTRL_ARCHITECTURE_IA64 = 2 -NV_CTRL_ARCHITECTURE_ARM = 3 -NV_CTRL_ARCHITECTURE_AARCH64 = 4 -NV_CTRL_ARCHITECTURE_PPC64LE = 5 - -# -# NV_CTRL_TEXTURE_CLAMPING - texture clamping mode in OpenGL. By -# default, _SPEC is used, which forces OpenGL texture clamping to -# conform with the OpenGL specification. _EDGE forces NVIDIA's -# OpenGL implementation to remap GL_CLAMP to GL_CLAMP_TO_EDGE, -# which is not strictly conformant, but some applications rely on -# the non-conformant behavior. -# - -NV_CTRL_TEXTURE_CLAMPING = 42 # RW-X -NV_CTRL_TEXTURE_CLAMPING_EDGE = 0 -NV_CTRL_TEXTURE_CLAMPING_SPEC = 1 - -# -# The NV_CTRL_CURSOR_SHADOW - not supported -# -# use an ARGB cursor instead. -# - -NV_CTRL_CURSOR_SHADOW = 43 # not supported -NV_CTRL_CURSOR_SHADOW_DISABLE = 0 # not supported -NV_CTRL_CURSOR_SHADOW_ENABLE = 1 # not supported - -NV_CTRL_CURSOR_SHADOW_ALPHA = 44 # not supported -NV_CTRL_CURSOR_SHADOW_RED = 45 # not supported -NV_CTRL_CURSOR_SHADOW_GREEN = 46 # not supported -NV_CTRL_CURSOR_SHADOW_BLUE = 47 # not supported - -NV_CTRL_CURSOR_SHADOW_X_OFFSET = 48 # not supported -NV_CTRL_CURSOR_SHADOW_Y_OFFSET = 49 # not supported - -# -# When Application Control for FSAA is enabled, then what the -# application requests is used, and NV_CTRL_FSAA_MODE is ignored. If -# this is disabled, then any application setting is overridden with -# NV_CTRL_FSAA_MODE -# - -NV_CTRL_FSAA_APPLICATION_CONTROLLED = 50 # RW-X -NV_CTRL_FSAA_APPLICATION_CONTROLLED_ENABLED = 1 -NV_CTRL_FSAA_APPLICATION_CONTROLLED_DISABLED = 0 - -# -# When Application Control for LogAniso is enabled, then what the -# application requests is used, and NV_CTRL_LOG_ANISO is ignored. If -# this is disabled, then any application setting is overridden with -# NV_CTRL_LOG_ANISO -# - -NV_CTRL_LOG_ANISO_APPLICATION_CONTROLLED = 51 # RW-X -NV_CTRL_LOG_ANISO_APPLICATION_CONTROLLED_ENABLED = 1 -NV_CTRL_LOG_ANISO_APPLICATION_CONTROLLED_DISABLED = 0 - -# -# IMAGE_SHARPENING adjusts the sharpness of the display's image -# quality by amplifying high frequency content. Valid values will -# normally be in the range [0,32). Only available on GeForceFX or -# newer. -# - -NV_CTRL_IMAGE_SHARPENING = 52 # RWDG - -# -# NV_CTRL_TV_OVERSCAN - not supported -# - -NV_CTRL_TV_OVERSCAN = 53 # not supported - -# -# NV_CTRL_TV_FLICKER_FILTER - not supported -# - -NV_CTRL_TV_FLICKER_FILTER = 54 # not supported - -# -# NV_CTRL_TV_BRIGHTNESS - not supported -# - -NV_CTRL_TV_BRIGHTNESS = 55 # not supported - -# -# NV_CTRL_TV_HUE - not supported -# - -NV_CTRL_TV_HUE = 56 # not supported - -# -# NV_CTRL_TV_CONTRAST - not suppoerted -# - -NV_CTRL_TV_CONTRAST = 57 # not supported - -# -# NV_CTRL_TV_SATURATION - not supported -# - -NV_CTRL_TV_SATURATION = 58 # not supported - -# -# NV_CTRL_TV_RESET_SETTINGS - not supported -# - -NV_CTRL_TV_RESET_SETTINGS = 59 # not supported - -# -# NV_CTRL_GPU_CORE_TEMPERATURE reports the current core temperature -# of the GPU driving the X screen. -# - -NV_CTRL_GPU_CORE_TEMPERATURE = 60 # R--G - -# -# NV_CTRL_GPU_CORE_THRESHOLD reports the current GPU core slowdown -# threshold temperature, NV_CTRL_GPU_DEFAULT_CORE_THRESHOLD and -# NV_CTRL_GPU_MAX_CORE_THRESHOLD report the default and MAX core -# slowdown threshold temperatures. -# -# NV_CTRL_GPU_CORE_THRESHOLD reflects the temperature at which the -# GPU is throttled to prevent overheating. -# - -NV_CTRL_GPU_CORE_THRESHOLD = 61 # R--G -NV_CTRL_GPU_DEFAULT_CORE_THRESHOLD = 62 # R--G -NV_CTRL_GPU_MAX_CORE_THRESHOLD = 63 # R--G - -# -# NV_CTRL_AMBIENT_TEMPERATURE reports the current temperature in the -# immediate neighbourhood of the GPU driving the X screen. -# - -NV_CTRL_AMBIENT_TEMPERATURE = 64 # R--G - -# -# NV_CTRL_PBUFFER_SCANOUT_SUPPORTED - returns whether this X screen -# supports scanout of FP pbuffers; -# -# if this screen does not support PBUFFER_SCANOUT, then all other -# PBUFFER_SCANOUT attributes are unavailable. -# -# PBUFFER_SCANOUT is supported if and only if: -# - Twinview is configured with clone mode. The secondary screen is used to -# scanout the pbuffer. -# - The desktop is running in with 16 bits per pixel. -# -NV_CTRL_PBUFFER_SCANOUT_SUPPORTED = 65 # not supported -NV_CTRL_PBUFFER_SCANOUT_FALSE = 0 -NV_CTRL_PBUFFER_SCANOUT_TRUE = 1 - -# -# NV_CTRL_PBUFFER_SCANOUT_XID indicates the XID of the pbuffer used for -# scanout. -# -NV_CTRL_PBUFFER_SCANOUT_XID = 66 # not supported - -############################################################################ -# -# The NV_CTRL_GVO_* integer attributes are used to configure GVO -# (Graphics to Video Out). This functionality is available, for -# example, on the Quadro SDI Output card. -# -# The following is a typical usage pattern for the GVO attributes: -# -# - query NV_CTRL_GVO_SUPPORTED to determine if the X screen supports GV0. -# -# - specify NV_CTRL_GVO_SYNC_MODE (one of FREE_RUNNING, GENLOCK, or -# FRAMELOCK); if you specify GENLOCK or FRAMELOCK, you should also -# specify NV_CTRL_GVO_SYNC_SOURCE. -# -# - Use NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECTED and -# NV_CTRL_GVO_SDI_SYNC_INPUT_DETECTED to detect what input syncs are -# present. -# -# (If no analog sync is detected but it is known that a valid -# bi-level or tri-level sync is connected set -# NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECT_MODE appropriately and -# retest with NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECTED). -# -# - if syncing to input sync, query the -# NV_CTRL_GVIO_DETECTED_VIDEO_FORMAT attribute; note that Input video -# format can only be queried after SYNC_SOURCE is specified. -# -# - specify the NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT -# -# - specify the NV_CTRL_GVO_DATA_FORMAT -# -# - specify any custom Color Space Conversion (CSC) matrix, offset, -# and scale with XNVCTRLSetGvoColorConversion(). -# -# - if using the GLX_NV_video_out extension to display one or more -# pbuffers, call glXGetVideoDeviceNV() to lock the GVO output for use -# by the GLX client; then bind the pbuffer(s) to the GVO output with -# glXBindVideoImageNV() and send pbuffers to the GVO output with -# glXSendPbufferToVideoNV(); see the GLX_NV_video_out spec for more -# details. -# -# - if using the GLX_NV_present_video extension, call -# glXBindVideoDeviceNV() to bind the GVO video device to current -# OpenGL context. -# -# Note that setting most GVO attributes only causes the value to be -# cached in the X server. The values will be flushed to the hardware -# either when the next MetaMode is set that uses the GVO display -# device, or when a GLX pbuffer is bound to the GVO output (with -# glXBindVideoImageNV()). -# -# Note that GLX_NV_video_out/GLX_NV_present_video and X screen use -# are mutually exclusive. If a MetaMode is currently using the GVO -# device, then glXGetVideoDeviceNV and glXBindVideoImageNV() will -# fail. Similarly, if a GLX client has locked the GVO output (via -# glXGetVideoDeviceNV or glXBindVideoImageNV), then setting a -# MetaMode that uses the GVO device will fail. The -# NV_CTRL_GVO_GLX_LOCKED event will be sent when a GLX client locks -# the GVO output. -# -# - - -# -# NV_CTRL_GVO_SUPPORTED - returns whether this X screen supports GVO; -# if this screen does not support GVO output, then all other GVO -# attributes are unavailable. -# - -NV_CTRL_GVO_SUPPORTED = 67 # R-- -NV_CTRL_GVO_SUPPORTED_FALSE = 0 -NV_CTRL_GVO_SUPPORTED_TRUE = 1 - -# -# NV_CTRL_GVO_SYNC_MODE - selects the GVO sync mode; possible values -# are: -# -# FREE_RUNNING - GVO does not sync to any external signal -# -# GENLOCK - the GVO output is genlocked to an incoming sync signal; -# genlocking locks at hsync. This requires that the output video -# format exactly match the incoming sync video format. -# -# FRAMELOCK - the GVO output is frame locked to an incoming sync -# signal; frame locking locks at vsync. This requires that the output -# video format have the same refresh rate as the incoming sync video -# format. -# - -NV_CTRL_GVO_SYNC_MODE = 68 # RW- -NV_CTRL_GVO_SYNC_MODE_FREE_RUNNING = 0 -NV_CTRL_GVO_SYNC_MODE_GENLOCK = 1 -NV_CTRL_GVO_SYNC_MODE_FRAMELOCK = 2 - -# -# NV_CTRL_GVO_SYNC_SOURCE - if NV_CTRL_GVO_SYNC_MODE is set to either -# GENLOCK or FRAMELOCK, this controls which sync source is used as -# the incoming sync signal (either Composite or SDI). If -# NV_CTRL_GVO_SYNC_MODE is FREE_RUNNING, this attribute has no -# effect. -# - -NV_CTRL_GVO_SYNC_SOURCE = 69 # RW- -NV_CTRL_GVO_SYNC_SOURCE_COMPOSITE = 0 -NV_CTRL_GVO_SYNC_SOURCE_SDI = 1 - -# -# NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT - specifies the desired output video -# format for GVO devices or the desired input video format for GVI devices. -# -# Note that for GVO, the valid video formats may vary depending on -# the NV_CTRL_GVO_SYNC_MODE and the incoming sync video format. See -# the definition of NV_CTRL_GVO_SYNC_MODE. -# -# Note that when querying the ValidValues for this data type, the -# values are reported as bits within a bitmask -# (ATTRIBUTE_TYPE_INT_BITS); unfortunately, there are more valid -# value bits than will fit in a single 32-bit value. To solve this, -# query the ValidValues for NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT to -# check which of the first 31 VIDEO_FORMATS are valid, query the -# ValidValues for NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT2 to check which -# of the 32-63 VIDEO_FORMATS are valid, and query the ValidValues of -# NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT3 to check which of the 64-95 -# VIDEO_FORMATS are valid. -# -# Note: Setting this attribute on a GVI device may also result in the -# following NV-CONTROL attributes being reset on that device (to -# ensure the configuration remains valid): -# NV_CTRL_GVI_REQUESTED_STREAM_BITS_PER_COMPONENT -# NV_CTRL_GVI_REQUESTED_STREAM_COMPONENT_SAMPLING -# - -NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT = 70 # RW--I - -NV_CTRL_GVIO_VIDEO_FORMAT_NONE = 0 -NV_CTRL_GVIO_VIDEO_FORMAT_487I_59_94_SMPTE259_NTSC = 1 -NV_CTRL_GVIO_VIDEO_FORMAT_576I_50_00_SMPTE259_PAL = 2 -NV_CTRL_GVIO_VIDEO_FORMAT_720P_59_94_SMPTE296 = 3 -NV_CTRL_GVIO_VIDEO_FORMAT_720P_60_00_SMPTE296 = 4 -NV_CTRL_GVIO_VIDEO_FORMAT_1035I_59_94_SMPTE260 = 5 -NV_CTRL_GVIO_VIDEO_FORMAT_1035I_60_00_SMPTE260 = 6 -NV_CTRL_GVIO_VIDEO_FORMAT_1080I_50_00_SMPTE295 = 7 -NV_CTRL_GVIO_VIDEO_FORMAT_1080I_50_00_SMPTE274 = 8 -NV_CTRL_GVIO_VIDEO_FORMAT_1080I_59_94_SMPTE274 = 9 -NV_CTRL_GVIO_VIDEO_FORMAT_1080I_60_00_SMPTE274 = 10 -NV_CTRL_GVIO_VIDEO_FORMAT_1080P_23_976_SMPTE274 = 11 -NV_CTRL_GVIO_VIDEO_FORMAT_1080P_24_00_SMPTE274 = 12 -NV_CTRL_GVIO_VIDEO_FORMAT_1080P_25_00_SMPTE274 = 13 -NV_CTRL_GVIO_VIDEO_FORMAT_1080P_29_97_SMPTE274 = 14 -NV_CTRL_GVIO_VIDEO_FORMAT_1080P_30_00_SMPTE274 = 15 -NV_CTRL_GVIO_VIDEO_FORMAT_720P_50_00_SMPTE296 = 16 -NV_CTRL_GVIO_VIDEO_FORMAT_1080I_48_00_SMPTE274 = 17 -NV_CTRL_GVIO_VIDEO_FORMAT_1080I_47_96_SMPTE274 = 18 -NV_CTRL_GVIO_VIDEO_FORMAT_720P_30_00_SMPTE296 = 19 -NV_CTRL_GVIO_VIDEO_FORMAT_720P_29_97_SMPTE296 = 20 -NV_CTRL_GVIO_VIDEO_FORMAT_720P_25_00_SMPTE296 = 21 -NV_CTRL_GVIO_VIDEO_FORMAT_720P_24_00_SMPTE296 = 22 -NV_CTRL_GVIO_VIDEO_FORMAT_720P_23_98_SMPTE296 = 23 -NV_CTRL_GVIO_VIDEO_FORMAT_1080PSF_25_00_SMPTE274 = 24 -NV_CTRL_GVIO_VIDEO_FORMAT_1080PSF_29_97_SMPTE274 = 25 -NV_CTRL_GVIO_VIDEO_FORMAT_1080PSF_30_00_SMPTE274 = 26 -NV_CTRL_GVIO_VIDEO_FORMAT_1080PSF_24_00_SMPTE274 = 27 -NV_CTRL_GVIO_VIDEO_FORMAT_1080PSF_23_98_SMPTE274 = 28 -NV_CTRL_GVIO_VIDEO_FORMAT_2048P_30_00_SMPTE372 = 29 -NV_CTRL_GVIO_VIDEO_FORMAT_2048P_29_97_SMPTE372 = 30 -NV_CTRL_GVIO_VIDEO_FORMAT_2048I_60_00_SMPTE372 = 31 -NV_CTRL_GVIO_VIDEO_FORMAT_2048I_59_94_SMPTE372 = 32 -NV_CTRL_GVIO_VIDEO_FORMAT_2048P_25_00_SMPTE372 = 33 -NV_CTRL_GVIO_VIDEO_FORMAT_2048I_50_00_SMPTE372 = 34 -NV_CTRL_GVIO_VIDEO_FORMAT_2048P_24_00_SMPTE372 = 35 -NV_CTRL_GVIO_VIDEO_FORMAT_2048P_23_98_SMPTE372 = 36 -NV_CTRL_GVIO_VIDEO_FORMAT_2048I_48_00_SMPTE372 = 37 -NV_CTRL_GVIO_VIDEO_FORMAT_2048I_47_96_SMPTE372 = 38 -NV_CTRL_GVIO_VIDEO_FORMAT_1080P_50_00_3G_LEVEL_A_SMPTE274 = 39 -NV_CTRL_GVIO_VIDEO_FORMAT_1080P_59_94_3G_LEVEL_A_SMPTE274 = 40 -NV_CTRL_GVIO_VIDEO_FORMAT_1080P_60_00_3G_LEVEL_A_SMPTE274 = 41 -NV_CTRL_GVIO_VIDEO_FORMAT_1080P_60_00_3G_LEVEL_B_SMPTE274 = 42 -NV_CTRL_GVIO_VIDEO_FORMAT_1080I_60_00_3G_LEVEL_B_SMPTE274 = 43 -NV_CTRL_GVIO_VIDEO_FORMAT_2048I_60_00_3G_LEVEL_B_SMPTE372 = 44 -NV_CTRL_GVIO_VIDEO_FORMAT_1080P_50_00_3G_LEVEL_B_SMPTE274 = 45 -NV_CTRL_GVIO_VIDEO_FORMAT_1080I_50_00_3G_LEVEL_B_SMPTE274 = 46 -NV_CTRL_GVIO_VIDEO_FORMAT_2048I_50_00_3G_LEVEL_B_SMPTE372 = 47 -NV_CTRL_GVIO_VIDEO_FORMAT_1080P_30_00_3G_LEVEL_B_SMPTE274 = 48 -NV_CTRL_GVIO_VIDEO_FORMAT_2048P_30_00_3G_LEVEL_B_SMPTE372 = 49 -NV_CTRL_GVIO_VIDEO_FORMAT_1080P_25_00_3G_LEVEL_B_SMPTE274 = 50 -NV_CTRL_GVIO_VIDEO_FORMAT_2048P_25_00_3G_LEVEL_B_SMPTE372 = 51 -NV_CTRL_GVIO_VIDEO_FORMAT_1080P_24_00_3G_LEVEL_B_SMPTE274 = 52 -NV_CTRL_GVIO_VIDEO_FORMAT_2048P_24_00_3G_LEVEL_B_SMPTE372 = 53 -NV_CTRL_GVIO_VIDEO_FORMAT_1080I_48_00_3G_LEVEL_B_SMPTE274 = 54 -NV_CTRL_GVIO_VIDEO_FORMAT_2048I_48_00_3G_LEVEL_B_SMPTE372 = 55 -NV_CTRL_GVIO_VIDEO_FORMAT_1080P_59_94_3G_LEVEL_B_SMPTE274 = 56 -NV_CTRL_GVIO_VIDEO_FORMAT_1080I_59_94_3G_LEVEL_B_SMPTE274 = 57 -NV_CTRL_GVIO_VIDEO_FORMAT_2048I_59_94_3G_LEVEL_B_SMPTE372 = 58 -NV_CTRL_GVIO_VIDEO_FORMAT_1080P_29_97_3G_LEVEL_B_SMPTE274 = 59 -NV_CTRL_GVIO_VIDEO_FORMAT_2048P_29_97_3G_LEVEL_B_SMPTE372 = 60 -NV_CTRL_GVIO_VIDEO_FORMAT_1080P_23_98_3G_LEVEL_B_SMPTE274 = 61 -NV_CTRL_GVIO_VIDEO_FORMAT_2048P_23_98_3G_LEVEL_B_SMPTE372 = 62 -NV_CTRL_GVIO_VIDEO_FORMAT_1080I_47_96_3G_LEVEL_B_SMPTE274 = 63 -NV_CTRL_GVIO_VIDEO_FORMAT_2048I_47_96_3G_LEVEL_B_SMPTE372 = 64 - -# -# The following have been renamed; NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT and the -# corresponding NV_CTRL_GVIO_* formats should be used instead. -# -NV_CTRL_GVO_OUTPUT_VIDEO_FORMAT = 70 # renamed - -NV_CTRL_GVO_VIDEO_FORMAT_NONE = 0 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_487I_59_94_SMPTE259_NTSC = 1 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_576I_50_00_SMPTE259_PAL = 2 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_720P_59_94_SMPTE296 = 3 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_720P_60_00_SMPTE296 = 4 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_1035I_59_94_SMPTE260 = 5 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_1035I_60_00_SMPTE260 = 6 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_1080I_50_00_SMPTE295 = 7 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_1080I_50_00_SMPTE274 = 8 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_1080I_59_94_SMPTE274 = 9 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_1080I_60_00_SMPTE274 = 10 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_1080P_23_976_SMPTE274 = 11 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_1080P_24_00_SMPTE274 = 12 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_1080P_25_00_SMPTE274 = 13 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_1080P_29_97_SMPTE274 = 14 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_1080P_30_00_SMPTE274 = 15 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_720P_50_00_SMPTE296 = 16 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_1080I_48_00_SMPTE274 = 17 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_1080I_47_96_SMPTE274 = 18 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_720P_30_00_SMPTE296 = 19 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_720P_29_97_SMPTE296 = 20 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_720P_25_00_SMPTE296 = 21 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_720P_24_00_SMPTE296 = 22 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_720P_23_98_SMPTE296 = 23 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_1080PSF_25_00_SMPTE274 = 24 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_1080PSF_29_97_SMPTE274 = 25 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_1080PSF_30_00_SMPTE274 = 26 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_1080PSF_24_00_SMPTE274 = 27 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_1080PSF_23_98_SMPTE274 = 28 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_2048P_30_00_SMPTE372 = 29 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_2048P_29_97_SMPTE372 = 30 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_2048I_60_00_SMPTE372 = 31 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_2048I_59_94_SMPTE372 = 32 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_2048P_25_00_SMPTE372 = 33 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_2048I_50_00_SMPTE372 = 34 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_2048P_24_00_SMPTE372 = 35 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_2048P_23_98_SMPTE372 = 36 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_2048I_48_00_SMPTE372 = 37 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_2048I_47_96_SMPTE372 = 38 # renamed - -# -# NV_CTRL_GVIO_DETECTED_VIDEO_FORMAT - indicates the input video format -# detected for GVO or GVI devices; the possible values are the -# NV_CTRL_GVIO_VIDEO_FORMAT constants. -# -# For GVI devices, the jack number should be specified in the lower -# 16 bits of the "display_mask" parameter, while the channel number should be -# specified in the upper 16 bits. -# - -NV_CTRL_GVIO_DETECTED_VIDEO_FORMAT = 71 # R--I - -# -# NV_CTRL_GVO_INPUT_VIDEO_FORMAT - renamed -# -# NV_CTRL_GVIO_DETECTED_VIDEO_FORMAT should be used instead. -# - -NV_CTRL_GVO_INPUT_VIDEO_FORMAT = 71 # renamed - -# -# NV_CTRL_GVO_DATA_FORMAT - This controls how the data in the source -# (either the X screen or the GLX pbuffer) is interpretted and -# displayed. -# -# Note: some of the below DATA_FORMATS have been renamed. For -# example, R8G8B8_TO_RGB444 has been renamed to X8X8X8_444_PASSTHRU. -# This is to more accurately reflect DATA_FORMATS where the -# per-channel data could be either RGB or YCrCb -- the point is that -# the driver and GVO hardware do not perform any implicit color space -# conversion on the data; it is passed through to the SDI out. -# - -NV_CTRL_GVO_DATA_FORMAT = 72 # RW- -NV_CTRL_GVO_DATA_FORMAT_R8G8B8_TO_YCRCB444 = 0 -NV_CTRL_GVO_DATA_FORMAT_R8G8B8A8_TO_YCRCBA4444 = 1 -NV_CTRL_GVO_DATA_FORMAT_R8G8B8Z10_TO_YCRCBZ4444 = 2 -NV_CTRL_GVO_DATA_FORMAT_R8G8B8_TO_YCRCB422 = 3 -NV_CTRL_GVO_DATA_FORMAT_R8G8B8A8_TO_YCRCBA4224 = 4 -NV_CTRL_GVO_DATA_FORMAT_R8G8B8Z10_TO_YCRCBZ4224 = 5 -NV_CTRL_GVO_DATA_FORMAT_R8G8B8_TO_RGB444 = 6 # renamed -NV_CTRL_GVO_DATA_FORMAT_X8X8X8_444_PASSTHRU = 6 -NV_CTRL_GVO_DATA_FORMAT_R8G8B8A8_TO_RGBA4444 = 7 # renamed -NV_CTRL_GVO_DATA_FORMAT_X8X8X8A8_4444_PASSTHRU = 7 -NV_CTRL_GVO_DATA_FORMAT_R8G8B8Z10_TO_RGBZ4444 = 8 # renamed -NV_CTRL_GVO_DATA_FORMAT_X8X8X8Z8_4444_PASSTHRU = 8 -NV_CTRL_GVO_DATA_FORMAT_Y10CR10CB10_TO_YCRCB444 = 9 # renamed -NV_CTRL_GVO_DATA_FORMAT_X10X10X10_444_PASSTHRU = 9 -NV_CTRL_GVO_DATA_FORMAT_Y10CR8CB8_TO_YCRCB444 = 10 # renamed -NV_CTRL_GVO_DATA_FORMAT_X10X8X8_444_PASSTHRU = 10 -NV_CTRL_GVO_DATA_FORMAT_Y10CR8CB8A10_TO_YCRCBA4444 = 11 # renamed -NV_CTRL_GVO_DATA_FORMAT_X10X8X8A10_4444_PASSTHRU = 11 -NV_CTRL_GVO_DATA_FORMAT_Y10CR8CB8Z10_TO_YCRCBZ4444 = 12 # renamed -NV_CTRL_GVO_DATA_FORMAT_X10X8X8Z10_4444_PASSTHRU = 12 -NV_CTRL_GVO_DATA_FORMAT_DUAL_R8G8B8_TO_DUAL_YCRCB422 = 13 -NV_CTRL_GVO_DATA_FORMAT_DUAL_Y8CR8CB8_TO_DUAL_YCRCB422 = 14 # renamed -NV_CTRL_GVO_DATA_FORMAT_DUAL_X8X8X8_TO_DUAL_422_PASSTHRU = 14 -NV_CTRL_GVO_DATA_FORMAT_R10G10B10_TO_YCRCB422 = 15 -NV_CTRL_GVO_DATA_FORMAT_R10G10B10_TO_YCRCB444 = 16 -NV_CTRL_GVO_DATA_FORMAT_Y12CR12CB12_TO_YCRCB444 = 17 # renamed -NV_CTRL_GVO_DATA_FORMAT_X12X12X12_444_PASSTHRU = 17 -NV_CTRL_GVO_DATA_FORMAT_R12G12B12_TO_YCRCB444 = 18 -NV_CTRL_GVO_DATA_FORMAT_X8X8X8_422_PASSTHRU = 19 -NV_CTRL_GVO_DATA_FORMAT_X8X8X8A8_4224_PASSTHRU = 20 -NV_CTRL_GVO_DATA_FORMAT_X8X8X8Z8_4224_PASSTHRU = 21 -NV_CTRL_GVO_DATA_FORMAT_X10X10X10_422_PASSTHRU = 22 -NV_CTRL_GVO_DATA_FORMAT_X10X8X8_422_PASSTHRU = 23 -NV_CTRL_GVO_DATA_FORMAT_X10X8X8A10_4224_PASSTHRU = 24 -NV_CTRL_GVO_DATA_FORMAT_X10X8X8Z10_4224_PASSTHRU = 25 -NV_CTRL_GVO_DATA_FORMAT_X12X12X12_422_PASSTHRU = 26 -NV_CTRL_GVO_DATA_FORMAT_R12G12B12_TO_YCRCB422 = 27 - -# -# NV_CTRL_GVO_DISPLAY_X_SCREEN - not supported -# - -NV_CTRL_GVO_DISPLAY_X_SCREEN = 73 # not supported -NV_CTRL_GVO_DISPLAY_X_SCREEN_ENABLE = 1 # not supported -NV_CTRL_GVO_DISPLAY_X_SCREEN_DISABLE = 0 # not supported - -# -# NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECTED - indicates whether -# Composite Sync input is detected. -# - -NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECTED = 74 # R-- -NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECTED_FALSE = 0 -NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECTED_TRUE = 1 - -# -# NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECT_MODE - get/set the -# Composite Sync input detect mode. -# - -NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECT_MODE = 75 # RW- -NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECT_MODE_AUTO = 0 -NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECT_MODE_BI_LEVEL = 1 -NV_CTRL_GVO_COMPOSITE_SYNC_INPUT_DETECT_MODE_TRI_LEVEL = 2 - -# -# NV_CTRL_GVO_SYNC_INPUT_DETECTED - indicates whether SDI Sync input -# is detected, and what type. -# - -NV_CTRL_GVO_SDI_SYNC_INPUT_DETECTED = 76 # R-- -NV_CTRL_GVO_SDI_SYNC_INPUT_DETECTED_NONE = 0 -NV_CTRL_GVO_SDI_SYNC_INPUT_DETECTED_HD = 1 -NV_CTRL_GVO_SDI_SYNC_INPUT_DETECTED_SD = 2 - -# -# NV_CTRL_GVO_VIDEO_OUTPUTS - indicates which GVO video output -# connectors are currently outputing data. -# - -NV_CTRL_GVO_VIDEO_OUTPUTS = 77 # R-- -NV_CTRL_GVO_VIDEO_OUTPUTS_NONE = 0 -NV_CTRL_GVO_VIDEO_OUTPUTS_VIDEO1 = 1 -NV_CTRL_GVO_VIDEO_OUTPUTS_VIDEO2 = 2 -NV_CTRL_GVO_VIDEO_OUTPUTS_VIDEO_BOTH = 3 - -# -# NV_CTRL_GVO_FIRMWARE_VERSION - deprecated -# -# NV_CTRL_STRING_GVIO_FIRMWARE_VERSION should be used instead. -# - -NV_CTRL_GVO_FIRMWARE_VERSION = 78 # deprecated - -# -# NV_CTRL_GVO_SYNC_DELAY_PIXELS - controls the delay between the -# input sync and the output sync in numbers of pixels from hsync; -# this is a 12 bit value. -# -# If the NV_CTRL_GVO_CAPABILITIES_ADVANCE_SYNC_SKEW bit is set, -# then setting this value will set an advance instead of a delay. -# - -NV_CTRL_GVO_SYNC_DELAY_PIXELS = 79 # RW- - -# -# NV_CTRL_GVO_SYNC_DELAY_LINES - controls the delay between the input -# sync and the output sync in numbers of lines from vsync; this is a -# 12 bit value. -# -# If the NV_CTRL_GVO_CAPABILITIES_ADVANCE_SYNC_SKEW bit is set, -# then setting this value will set an advance instead of a delay. -# - -NV_CTRL_GVO_SYNC_DELAY_LINES = 80 # RW- - -# -# NV_CTRL_GVO_INPUT_VIDEO_FORMAT_REACQUIRE - must be set for a period -# of about 2 seconds for the new InputVideoFormat to be properly -# locked to. In nvidia-settings, we do a reacquire whenever genlock -# or frame lock mode is entered into, when the user clicks the -# "detect" button. This value can be written, but always reads back -# _FALSE. -# - -NV_CTRL_GVO_INPUT_VIDEO_FORMAT_REACQUIRE = 81 # -W- -NV_CTRL_GVO_INPUT_VIDEO_FORMAT_REACQUIRE_FALSE = 0 -NV_CTRL_GVO_INPUT_VIDEO_FORMAT_REACQUIRE_TRUE = 1 - -# -# NV_CTRL_GVO_GLX_LOCKED - deprecated -# -# NV_CTRL_GVO_LOCK_OWNER should be used instead. -# - -NV_CTRL_GVO_GLX_LOCKED = 82 # deprecated -NV_CTRL_GVO_GLX_LOCKED_FALSE = 0 # deprecated -NV_CTRL_GVO_GLX_LOCKED_TRUE = 1 # deprecated - -# -# NV_CTRL_GVIO_VIDEO_FORMAT_{WIDTH,HEIGHT,REFRESH_RATE} - query the -# width, height, and refresh rate for the specified -# NV_CTRL_GVIO_VIDEO_FORMAT_*. So that this can be queried with -# existing interfaces, XNVCTRLQueryAttribute() should be used, and -# the video format specified in the display_mask field; eg: -# -# XNVCTRLQueryAttribute (dpy, -# screen, -# NV_CTRL_GVIO_VIDEO_FORMAT_487I_59_94_SMPTE259_NTSC, -# NV_CTRL_GVIO_VIDEO_FORMAT_WIDTH, -# &value); -# -# Note that Refresh Rate is in milliHertz values -# - -NV_CTRL_GVIO_VIDEO_FORMAT_WIDTH = 83 # R--I -NV_CTRL_GVIO_VIDEO_FORMAT_HEIGHT = 84 # R--I -NV_CTRL_GVIO_VIDEO_FORMAT_REFRESH_RATE = 85 # R--I - -# The following have been renamed; use the NV_CTRL_GVIO_* versions, instead -NV_CTRL_GVO_VIDEO_FORMAT_WIDTH = 83 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_HEIGHT = 84 # renamed -NV_CTRL_GVO_VIDEO_FORMAT_REFRESH_RATE = 85 # renamed - -# -# NV_CTRL_GVO_X_SCREEN_PAN_[XY] - not supported -# - -NV_CTRL_GVO_X_SCREEN_PAN_X = 86 # not supported -NV_CTRL_GVO_X_SCREEN_PAN_Y = 87 # not supported - -# -# NV_CTRL_GPU_OVERCLOCKING_STATE - not supported -# - -NV_CTRL_GPU_OVERCLOCKING_STATE = 88 # not supported -NV_CTRL_GPU_OVERCLOCKING_STATE_NONE = 0 # not supported -NV_CTRL_GPU_OVERCLOCKING_STATE_MANUAL = 1 # not supported - -# -# NV_CTRL_GPU_{2,3}D_CLOCK_FREQS - not supported -# - -NV_CTRL_GPU_2D_CLOCK_FREQS = 89 # not supported -NV_CTRL_GPU_3D_CLOCK_FREQS = 90 # not supported - -# -# NV_CTRL_GPU_DEFAULT_{2,3}D_CLOCK_FREQS - not supported -# - -NV_CTRL_GPU_DEFAULT_2D_CLOCK_FREQS = 91 # not supported -NV_CTRL_GPU_DEFAULT_3D_CLOCK_FREQS = 92 # not supported - -# -# NV_CTRL_GPU_CURRENT_CLOCK_FREQS - query the current GPU and memory -# clocks of the graphics device driving the X screen. -# -# NV_CTRL_GPU_CURRENT_CLOCK_FREQS is a "packed" integer attribute; -# the GPU clock is stored in the upper 16 bits of the integer, and -# the memory clock is stored in the lower 16 bits of the integer. -# All clock values are in MHz. All clock values are in MHz. -# - -NV_CTRL_GPU_CURRENT_CLOCK_FREQS = 93 # R--G - -# -# NV_CTRL_GPU_OPTIMAL_CLOCK_FREQS - not supported -# - -NV_CTRL_GPU_OPTIMAL_CLOCK_FREQS = 94 # not supported -NV_CTRL_GPU_OPTIMAL_CLOCK_FREQS_INVALID = 0 # not supported - -# -# NV_CTRL_GPU_OPTIMAL_CLOCK_FREQS_DETECTION - not supported -# - -NV_CTRL_GPU_OPTIMAL_CLOCK_FREQS_DETECTION = 95 # not supported -NV_CTRL_GPU_OPTIMAL_CLOCK_FREQS_DETECTION_START = 0 # not supported -NV_CTRL_GPU_OPTIMAL_CLOCK_FREQS_DETECTION_CANCEL = 1 # not supported - -# -# NV_CTRL_GPU_OPTIMAL_CLOCK_FREQS_DETECTION_STATE - not supported -# - -NV_CTRL_GPU_OPTIMAL_CLOCK_FREQS_DETECTION_STATE = 96 # not supported -NV_CTRL_GPU_OPTIMAL_CLOCK_FREQS_DETECTION_STATE_IDLE = 0 # not supported -NV_CTRL_GPU_OPTIMAL_CLOCK_FREQS_DETECTION_STATE_BUSY = 1 # not supported - -# -# NV_CTRL_FLATPANEL_CHIP_LOCATION - for the specified display device, -# report whether the flat panel is driven by the on-chip controller, -# or a separate controller chip elsewhere on the graphics board. -# This attribute is only available for flat panels. -# - -NV_CTRL_FLATPANEL_CHIP_LOCATION = 215 # R-DG -NV_CTRL_FLATPANEL_CHIP_LOCATION_INTERNAL = 0 -NV_CTRL_FLATPANEL_CHIP_LOCATION_EXTERNAL = 1 - -# -# NV_CTRL_FLATPANEL_LINK - report the number of links for a DVI connection, or -# the main link's active lane count for DisplayPort. -# This attribute is only available for flat panels. -# - -NV_CTRL_FLATPANEL_LINK = 216 # R-DG -NV_CTRL_FLATPANEL_LINK_SINGLE = 0 -NV_CTRL_FLATPANEL_LINK_DUAL = 1 -NV_CTRL_FLATPANEL_LINK_QUAD = 3 - -# -# NV_CTRL_FLATPANEL_SIGNAL - for the specified display device, report -# whether the flat panel is driven by an LVDS, TMDS, or DisplayPort signal. -# This attribute is only available for flat panels. -# - -NV_CTRL_FLATPANEL_SIGNAL = 217 # R-DG -NV_CTRL_FLATPANEL_SIGNAL_LVDS = 0 -NV_CTRL_FLATPANEL_SIGNAL_TMDS = 1 -NV_CTRL_FLATPANEL_SIGNAL_DISPLAYPORT = 2 - -# -# NV_CTRL_USE_HOUSE_SYNC - when INPUT, the server (master) frame lock -# device will propagate the incoming house sync signal as the outgoing -# frame lock sync signal. If the frame lock device cannot detect a -# frame lock sync signal, it will default to using the internal timings -# from the GPU connected to the primary connector. -# -# When set to OUTPUT, the server (master) frame lock device will -# generate a house sync signal from its internal timing and output -# this signal over the BNC connector on the frame lock device. This -# is only allowed on a Quadro Sync II device. If an incoming house -# sync signal is present on the BNC connector, this setting will -# have no effect. -# -# This attribute may be queried through XNVCTRLQueryTargetAttribute() -# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN -# target. -# - -NV_CTRL_USE_HOUSE_SYNC = 218 # RW-F -NV_CTRL_USE_HOUSE_SYNC_DISABLED = 0 # aliases with FALSE -NV_CTRL_USE_HOUSE_SYNC_INPUT = 1 # aliases with TRUE -NV_CTRL_USE_HOUSE_SYNC_OUTPUT = 2 -NV_CTRL_USE_HOUSE_SYNC_FALSE = 0 -NV_CTRL_USE_HOUSE_SYNC_TRUE = 1 - -# -# NV_CTRL_EDID_AVAILABLE - report if an EDID is available for the -# specified display device. -# -# This attribute may also be queried through XNVCTRLQueryTargetAttribute() -# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN -# target. -# - -NV_CTRL_EDID_AVAILABLE = 219 # R-DG -NV_CTRL_EDID_AVAILABLE_FALSE = 0 -NV_CTRL_EDID_AVAILABLE_TRUE = 1 - -# -# NV_CTRL_FORCE_STEREO - when TRUE, OpenGL will force stereo flipping -# even when no stereo drawables are visible (if the device is configured -# to support it, see the "Stereo" X config option). -# When false, fall back to the default behavior of only flipping when a -# stereo drawable is visible. -# - -NV_CTRL_FORCE_STEREO = 220 # RW- -NV_CTRL_FORCE_STEREO_FALSE = 0 -NV_CTRL_FORCE_STEREO_TRUE = 1 - -# -# NV_CTRL_IMAGE_SETTINGS - the image quality setting for OpenGL clients. -# -# This setting is only applied to OpenGL clients that are started -# after this setting is applied. -# - -NV_CTRL_IMAGE_SETTINGS = 221 # RW-X -NV_CTRL_IMAGE_SETTINGS_HIGH_QUALITY = 0 -NV_CTRL_IMAGE_SETTINGS_QUALITY = 1 -NV_CTRL_IMAGE_SETTINGS_PERFORMANCE = 2 -NV_CTRL_IMAGE_SETTINGS_HIGH_PERFORMANCE = 3 - -# -# NV_CTRL_XINERAMA - return whether xinerama is enabled -# - -NV_CTRL_XINERAMA = 222 # R--G -NV_CTRL_XINERAMA_OFF = 0 -NV_CTRL_XINERAMA_ON = 1 - -# -# NV_CTRL_XINERAMA_STEREO - when TRUE, OpenGL will allow stereo flipping -# on multiple X screens configured with Xinerama. -# When FALSE, flipping is allowed only on one X screen at a time. -# - -NV_CTRL_XINERAMA_STEREO = 223 # RW- -NV_CTRL_XINERAMA_STEREO_FALSE = 0 -NV_CTRL_XINERAMA_STEREO_TRUE = 1 - -# -# NV_CTRL_BUS_RATE - if the bus type of the specified device is AGP, then -# NV_CTRL_BUS_RATE returns the configured AGP transfer rate. If the bus type -# is PCI Express, then this attribute returns the maximum link width. -# When this attribute is queried on an X screen target, the bus rate of the -# GPU driving the X screen is returned. -# - -NV_CTRL_BUS_RATE = 224 # R--GI - -# -# NV_CTRL_GPU_PCIE_MAX_LINK_WIDTH - returns the maximum -# PCIe link width, in number of lanes. -# -NV_CTRL_GPU_PCIE_MAX_LINK_WIDTH = NV_CTRL_BUS_RATE -# -# NV_CTRL_SHOW_SLI_VISUAL_INDICATOR - when TRUE, OpenGL will draw information -# about the current SLI mode. -# - -NV_CTRL_SHOW_SLI_VISUAL_INDICATOR = 225 # RW-X -NV_CTRL_SHOW_SLI_VISUAL_INDICATOR_FALSE = 0 -NV_CTRL_SHOW_SLI_VISUAL_INDICATOR_TRUE = 1 - -# -# NV_CTRL_SHOW_SLI_HUD - when TRUE, OpenGL will draw information about the -# current SLI mode. -# Renamed this attribute to NV_CTRL_SHOW_SLI_VISUAL_INDICATOR -# - -NV_CTRL_SHOW_SLI_HUD = NV_CTRL_SHOW_SLI_VISUAL_INDICATOR -NV_CTRL_SHOW_SLI_HUD_FALSE = NV_CTRL_SHOW_SLI_VISUAL_INDICATOR_FALSE -NV_CTRL_SHOW_SLI_HUD_TRUE = NV_CTRL_SHOW_SLI_VISUAL_INDICATOR_TRUE - -# -# NV_CTRL_XV_SYNC_TO_DISPLAY - deprecated -# -# NV_CTRL_XV_SYNC_TO_DISPLAY_ID should be used instead. -# - -NV_CTRL_XV_SYNC_TO_DISPLAY = 226 # deprecated - -# -# NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT2 - this attribute is only -# intended to be used to query the ValidValues for -# NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT for VIDEO_FORMAT values between -# 31 and 63. See NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT for details. -# - -NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT2 = 227 # ---GI - -# -# NV_CTRL_GVO_OUTPUT_VIDEO_FORMAT2 - renamed -# -# NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT2 should be used instead. -# -NV_CTRL_GVO_OUTPUT_VIDEO_FORMAT2 = 227 # renamed - -# -# NV_CTRL_GVO_OVERRIDE_HW_CSC - Override the SDI hardware's Color Space -# Conversion with the values controlled through -# XNVCTRLSetGvoColorConversion() and XNVCTRLGetGvoColorConversion(). If -# this attribute is FALSE, then the values specified through -# XNVCTRLSetGvoColorConversion() are ignored. -# - -NV_CTRL_GVO_OVERRIDE_HW_CSC = 228 # RW- -NV_CTRL_GVO_OVERRIDE_HW_CSC_FALSE = 0 -NV_CTRL_GVO_OVERRIDE_HW_CSC_TRUE = 1 - -# -# NV_CTRL_GVO_CAPABILITIES - this read-only attribute describes GVO -# capabilities that differ between NVIDIA SDI products. This value -# is a bitmask where each bit indicates whether that capability is -# available. -# -# APPLY_CSC_IMMEDIATELY - whether the CSC matrix, offset, and scale -# specified through XNVCTRLSetGvoColorConversion() will take affect -# immediately, or only after SDI output is disabled and enabled -# again. -# -# APPLY_CSC_TO_X_SCREEN - whether the CSC matrix, offset, and scale -# specified through XNVCTRLSetGvoColorConversion() will also apply -# to GVO output of an X screen, or only to OpenGL GVO output, as -# enabled through the GLX_NV_video_out extension. -# -# COMPOSITE_TERMINATION - whether the 75 ohm termination of the -# SDI composite input signal can be programmed through the -# NV_CTRL_GVO_COMPOSITE_TERMINATION attribute. -# -# SHARED_SYNC_BNC - whether the SDI device has a single BNC -# connector used for both (SDI & Composite) incoming signals. -# -# MULTIRATE_SYNC - whether the SDI device supports synchronization -# of input and output video modes that match in being odd or even -# modes (ie, AA.00 Hz modes can be synched to other BB.00 Hz modes and -# AA.XX Hz can match to BB.YY Hz where .XX and .YY are not .00) -# - -NV_CTRL_GVO_CAPABILITIES = 229 # R-- -NV_CTRL_GVO_CAPABILITIES_APPLY_CSC_IMMEDIATELY = 0x00000001 -NV_CTRL_GVO_CAPABILITIES_APPLY_CSC_TO_X_SCREEN = 0x00000002 -NV_CTRL_GVO_CAPABILITIES_COMPOSITE_TERMINATION = 0x00000004 -NV_CTRL_GVO_CAPABILITIES_SHARED_SYNC_BNC = 0x00000008 -NV_CTRL_GVO_CAPABILITIES_MULTIRATE_SYNC = 0x00000010 -NV_CTRL_GVO_CAPABILITIES_ADVANCE_SYNC_SKEW = 0x00000020 - -# -# NV_CTRL_GVO_COMPOSITE_TERMINATION - enable or disable 75 ohm -# termination of the SDI composite input signal. -# - -NV_CTRL_GVO_COMPOSITE_TERMINATION = 230 # RW- -NV_CTRL_GVO_COMPOSITE_TERMINATION_ENABLE = 1 -NV_CTRL_GVO_COMPOSITE_TERMINATION_DISABLE = 0 - -# -# NV_CTRL_ASSOCIATED_DISPLAY_DEVICES - deprecated -# -# NV_CTRL_BINARY_DATA_DISPLAYS_ASSIGNED_TO_XSCREEN should be used instead. -# - -NV_CTRL_ASSOCIATED_DISPLAY_DEVICES = 231 # deprecated - -# -# NV_CTRL_FRAMELOCK_SLAVES - deprecated -# -# NV_CTRL_FRAMELOCK_DISPLAY_CONFIG should be used instead. -# - -NV_CTRL_FRAMELOCK_SLAVES = 232 # deprecated - -# -# NV_CTRL_FRAMELOCK_MASTERABLE - deprecated -# -# NV_CTRL_FRAMELOCK_DISPLAY_CONFIG should be used instead. -# - -NV_CTRL_FRAMELOCK_MASTERABLE = 233 # deprecated - -# -# NV_CTRL_PROBE_DISPLAYS - re-probes the hardware to detect what -# display devices are connected to the GPU or GPU driving the -# specified X screen. The return value is deprecated and should not be used. -# -# This attribute may be queried through XNVCTRLQueryTargetAttribute() -# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. -# - -NV_CTRL_PROBE_DISPLAYS = 234 # R--G - -# -# NV_CTRL_REFRESH_RATE - Returns the refresh rate of the specified -# display device in 100# Hz (ie. to get the refresh rate in Hz, divide -# the returned value by 100.) -# -# This attribute may be queried through XNVCTRLQueryTargetAttribute() -# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. -# - -NV_CTRL_REFRESH_RATE = 235 # R-DG - -# -# NV_CTRL_GVO_FLIP_QUEUE_SIZE - The Graphics to Video Out interface -# exposed through NV-CONTROL and the GLX_NV_video_out extension uses -# an internal flip queue when pbuffers are sent to the video device -# (via glXSendPbufferToVideoNV()). The NV_CTRL_GVO_FLIP_QUEUE_SIZE -# can be used to query and assign the flip queue size. This -# attribute is applied to GLX when glXGetVideoDeviceNV() is called by -# the application. -# - -NV_CTRL_GVO_FLIP_QUEUE_SIZE = 236 # RW- - -# -# NV_CTRL_CURRENT_SCANLINE - query the current scanline for the -# specified display device. -# - -NV_CTRL_CURRENT_SCANLINE = 237 # R-DG - -# -# NV_CTRL_INITIAL_PIXMAP_PLACEMENT - Controls where X pixmaps are initially -# created. -# -# NV_CTRL_INITIAL_PIXMAP_PLACEMENT_FORCE_SYSMEM causes pixmaps to stay in -# system memory. These pixmaps can't be accelerated by the NVIDIA driver; this -# will cause blank windows if used with an OpenGL compositing manager. -# NV_CTRL_INITIAL_PIXMAP_PLACEMENT_SYSMEM creates pixmaps in system memory -# initially, but allows them to migrate to video memory. -# NV_CTRL_INITIAL_PIXMAP_PLACEMENT_VIDMEM creates pixmaps in video memory -# when enough resources are available. -# NV_CTRL_INITIAL_PIXMAP_PLACEMENT_RESERVED is currently reserved for future -# use. Behavior is undefined. -# NV_CTRL_INITIAL_PIXMAP_PLACEMENT_GPU_SYSMEM creates pixmaps in GPU accessible -# system memory when enough resources are available. -# - -NV_CTRL_INITIAL_PIXMAP_PLACEMENT = 238 # RW- -NV_CTRL_INITIAL_PIXMAP_PLACEMENT_FORCE_SYSMEM = 0 -NV_CTRL_INITIAL_PIXMAP_PLACEMENT_SYSMEM = 1 -NV_CTRL_INITIAL_PIXMAP_PLACEMENT_VIDMEM = 2 -NV_CTRL_INITIAL_PIXMAP_PLACEMENT_RESERVED = 3 -NV_CTRL_INITIAL_PIXMAP_PLACEMENT_GPU_SYSMEM = 4 - -# -# NV_CTRL_PCI_BUS - Returns the PCI bus number the specified device is using. -# - -NV_CTRL_PCI_BUS = 239 # R--GI - -# -# NV_CTRL_PCI_DEVICE - Returns the PCI device number the specified device is -# using. -# - -NV_CTRL_PCI_DEVICE = 240 # R--GI - -# -# NV_CTRL_PCI_FUNCTION - Returns the PCI function number the specified device -# is using. -# - -NV_CTRL_PCI_FUNCTION = 241 # R--GI - -# -# NV_CTRL_FRAMELOCK_FPGA_REVISION - Queries the FPGA revision of the -# Frame Lock device. -# -# This attribute must be queried through XNVCTRLQueryTargetAttribute() -# using a NV_CTRL_TARGET_TYPE_FRAMELOCK target. -# - -NV_CTRL_FRAMELOCK_FPGA_REVISION = 242 # R--F - -# -# NV_CTRL_MAX_SCREEN_{WIDTH,HEIGHT} - the maximum allowable size, in -# pixels, of either the specified X screen (if the target_type of the -# query is an X screen), or any X screen on the specified GPU (if the -# target_type of the query is a GPU). -# - -NV_CTRL_MAX_SCREEN_WIDTH = 243 # R--G -NV_CTRL_MAX_SCREEN_HEIGHT = 244 # R--G - -# -# NV_CTRL_MAX_DISPLAYS - The maximum number of display devices that -# can be driven simultaneously on a GPU (e.g., that can be used in a -# MetaMode at once). Note that this does not indicate the maximum -# number of displays that are listed in NV_CTRL_BINARY_DATA_DISPLAYS_ON_GPU -# and NV_CTRL_BINARY_DATA_DISPLAYS_CONNECTED_TO_GPU because more display -# devices can be connected than are actively in use. -# - -NV_CTRL_MAX_DISPLAYS = 245 # R--G - -# -# NV_CTRL_DYNAMIC_TWINVIEW - Returns whether or not the screen -# supports dynamic twinview. -# - -NV_CTRL_DYNAMIC_TWINVIEW = 246 # R-- - -# -# NV_CTRL_MULTIGPU_DISPLAY_OWNER - Returns the (NV-CONTROL) GPU ID of -# the GPU that has the display device(s) used for showing the X Screen. -# - -NV_CTRL_MULTIGPU_DISPLAY_OWNER = 247 # R-- - -# -# NV_CTRL_GPU_SCALING - not supported -# - -NV_CTRL_GPU_SCALING = 248 # not supported - -NV_CTRL_GPU_SCALING_TARGET_INVALID = 0 # not supported -NV_CTRL_GPU_SCALING_TARGET_FLATPANEL_BEST_FIT = 1 # not supported -NV_CTRL_GPU_SCALING_TARGET_FLATPANEL_NATIVE = 2 # not supported - -NV_CTRL_GPU_SCALING_METHOD_INVALID = 0 # not supported -NV_CTRL_GPU_SCALING_METHOD_STRETCHED = 1 # not supported -NV_CTRL_GPU_SCALING_METHOD_CENTERED = 2 # not supported -NV_CTRL_GPU_SCALING_METHOD_ASPECT_SCALED = 3 # not supported - -# -# NV_CTRL_FRONTEND_RESOLUTION - not supported -# - -NV_CTRL_FRONTEND_RESOLUTION = 249 # not supported - -# -# NV_CTRL_BACKEND_RESOLUTION - not supported -# - -NV_CTRL_BACKEND_RESOLUTION = 250 # not supported - -# -# NV_CTRL_FLATPANEL_NATIVE_RESOLUTION - not supported -# - -NV_CTRL_FLATPANEL_NATIVE_RESOLUTION = 251 # not supported - -# -# NV_CTRL_FLATPANEL_BEST_FIT_RESOLUTION - not supported -# - -NV_CTRL_FLATPANEL_BEST_FIT_RESOLUTION = 252 # not supported - -# -# NV_CTRL_GPU_SCALING_ACTIVE - not supported -# - -NV_CTRL_GPU_SCALING_ACTIVE = 253 # not supported - -# -# NV_CTRL_DFP_SCALING_ACTIVE - not supported -# - -NV_CTRL_DFP_SCALING_ACTIVE = 254 # not supported - -# -# NV_CTRL_FSAA_APPLICATION_ENHANCED - Controls how the NV_CTRL_FSAA_MODE -# is applied when NV_CTRL_FSAA_APPLICATION_CONTROLLED is set to -# NV_CTRL_APPLICATION_CONTROLLED_DISABLED. When -# NV_CTRL_FSAA_APPLICATION_ENHANCED is _DISABLED, OpenGL applications will -# be forced to use the FSAA mode specified by NV_CTRL_FSAA_MODE. when set -# to _ENABLED, only those applications that have selected a multisample -# FBConfig will be made to use the NV_CTRL_FSAA_MODE specified. -# -# This attribute is ignored when NV_CTRL_FSAA_APPLICATION_CONTROLLED is -# set to NV_CTRL_FSAA_APPLICATION_CONTROLLED_ENABLED. -# - -NV_CTRL_FSAA_APPLICATION_ENHANCED = 255 # RW-X -NV_CTRL_FSAA_APPLICATION_ENHANCED_ENABLED = 1 -NV_CTRL_FSAA_APPLICATION_ENHANCED_DISABLED = 0 - -# -# NV_CTRL_FRAMELOCK_SYNC_RATE_4 - This is the refresh rate that the -# frame lock board is sending to the GPU with 4 digits of precision. -# -# This attribute may be queried through XNVCTRLQueryTargetAttribute() -# using a NV_CTRL_TARGET_TYPE_FRAMELOCK. -# - -NV_CTRL_FRAMELOCK_SYNC_RATE_4 = 256 # R--F - -# -# NV_CTRL_GVO_LOCK_OWNER - indicates that the GVO device is available -# or in use (by GLX or an X screen). -# -# The GVO device is locked by GLX when either glXGetVideoDeviceNV -# (part of GLX_NV_video_out) or glXBindVideoDeviceNV (part of -# GLX_NV_present_video) is called. All GVO output resources are -# locked until released by the GLX_NV_video_out/GLX_NV_present_video -# client. -# -# The GVO device is locked/unlocked by an X screen, when the GVO device is -# used in a MetaMode on an X screen. -# -# When the GVO device is locked, setting of the following GVO NV-CONTROL -# attributes will not happen immediately and will instead be cached. The -# GVO resource will need to be disabled/released and re-enabled/claimed for -# the values to be flushed. These attributes are: -# -# NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT -# NV_CTRL_GVO_DATA_FORMAT -# NV_CTRL_GVO_FLIP_QUEUE_SIZE -# - -NV_CTRL_GVO_LOCK_OWNER = 257 # R-- -NV_CTRL_GVO_LOCK_OWNER_NONE = 0 -NV_CTRL_GVO_LOCK_OWNER_GLX = 1 -NV_CTRL_GVO_LOCK_OWNER_CLONE = 2 # not supported -NV_CTRL_GVO_LOCK_OWNER_X_SCREEN = 3 - -# -# NV_CTRL_HWOVERLAY - when a workstation overlay is in use, reports -# whether the hardware overlay is used, or if the overlay is emulated. -# - -NV_CTRL_HWOVERLAY = 258 # R-- -NV_CTRL_HWOVERLAY_FALSE = 0 -NV_CTRL_HWOVERLAY_TRUE = 1 - -# -# NV_CTRL_NUM_GPU_ERRORS_RECOVERED - Returns the number of GPU errors -# occured. This attribute may be queried through XNVCTRLQueryTargetAttribute() -# using a NV_CTRL_TARGET_TYPE_X_SCREEN target. -# - -NV_CTRL_NUM_GPU_ERRORS_RECOVERED = 259 # R--- - -# -# NV_CTRL_REFRESH_RATE_3 - Returns the refresh rate of the specified -# display device in 1000# Hz (ie. to get the refresh rate in Hz, divide -# the returned value by 1000.) -# -# This attribute may be queried through XNVCTRLQueryTargetAttribute() -# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. -# - -NV_CTRL_REFRESH_RATE_3 = 260 # R-DG - -# -# NV_CTRL_ONDEMAND_VBLANK_INTERRUPTS - not supported -# - -NV_CTRL_ONDEMAND_VBLANK_INTERRUPTS = 261 # not supported -NV_CTRL_ONDEMAND_VBLANK_INTERRUPTS_OFF = 0 # not supported -NV_CTRL_ONDEMAND_VBLANK_INTERRUPTS_ON = 1 # not supported - -# -# NV_CTRL_GPU_POWER_SOURCE reports the type of power source -# of the GPU driving the X screen. -# - -NV_CTRL_GPU_POWER_SOURCE = 262 # R--G -NV_CTRL_GPU_POWER_SOURCE_AC = 0 -NV_CTRL_GPU_POWER_SOURCE_BATTERY = 1 - -# -# NV_CTRL_GPU_CURRENT_PERFORMANCE_MODE - not supported -# - -NV_CTRL_GPU_CURRENT_PERFORMANCE_MODE = 263 # not supported -NV_CTRL_GPU_CURRENT_PERFORMANCE_MODE_DESKTOP = 0 # not supported -NV_CTRL_GPU_CURRENT_PERFORMANCE_MODE_MAXPERF = 1 # not supported - -# NV_CTRL_GLYPH_CACHE - Enables RENDER Glyph Caching to VRAM - -NV_CTRL_GLYPH_CACHE = 264 # RW- -NV_CTRL_GLYPH_CACHE_DISABLED = 0 -NV_CTRL_GLYPH_CACHE_ENABLED = 1 - -# -# NV_CTRL_GPU_CURRENT_PERFORMANCE_LEVEL reports the current -# Performance level of the GPU driving the X screen. Each -# Performance level has associated NVClock and Mem Clock values. -# - -NV_CTRL_GPU_CURRENT_PERFORMANCE_LEVEL = 265 # R--G - -# -# NV_CTRL_GPU_ADAPTIVE_CLOCK_STATE reports if Adaptive Clocking -# is Enabled on the GPU driving the X screen. -# - -NV_CTRL_GPU_ADAPTIVE_CLOCK_STATE = 266 # R--G -NV_CTRL_GPU_ADAPTIVE_CLOCK_STATE_DISABLED = 0 -NV_CTRL_GPU_ADAPTIVE_CLOCK_STATE_ENABLED = 1 - -# -# NV_CTRL_GVO_OUTPUT_VIDEO_LOCKED - Returns whether or not the GVO output -# video is locked to the GPU. -# - -NV_CTRL_GVO_OUTPUT_VIDEO_LOCKED = 267 # R--- -NV_CTRL_GVO_OUTPUT_VIDEO_LOCKED_FALSE = 0 -NV_CTRL_GVO_OUTPUT_VIDEO_LOCKED_TRUE = 1 - -# -# NV_CTRL_GVO_SYNC_LOCK_STATUS - Returns whether or not the GVO device -# is locked to the input ref signal. If the sync mode is set to -# NV_CTRL_GVO_SYNC_MODE_GENLOCK, then this returns the genlock -# sync status, and if the sync mode is set to NV_CTRL_GVO_SYNC_MODE_FRAMELOCK, -# then this reports the frame lock status. -# - -NV_CTRL_GVO_SYNC_LOCK_STATUS = 268 # R--- -NV_CTRL_GVO_SYNC_LOCK_STATUS_UNLOCKED = 0 -NV_CTRL_GVO_SYNC_LOCK_STATUS_LOCKED = 1 - -# -# NV_CTRL_GVO_ANC_TIME_CODE_GENERATION - Allows SDI device to generate -# time codes in the ANC region of the SDI video output stream. -# - -NV_CTRL_GVO_ANC_TIME_CODE_GENERATION = 269 # RW-- -NV_CTRL_GVO_ANC_TIME_CODE_GENERATION_DISABLE = 0 -NV_CTRL_GVO_ANC_TIME_CODE_GENERATION_ENABLE = 1 - -# -# NV_CTRL_GVO_COMPOSITE - Enables/Disables SDI compositing. This attribute -# is only available when an SDI input source is detected and is in genlock -# mode. -# - -NV_CTRL_GVO_COMPOSITE = 270 # RW-- -NV_CTRL_GVO_COMPOSITE_DISABLE = 0 -NV_CTRL_GVO_COMPOSITE_ENABLE = 1 - -# -# NV_CTRL_GVO_COMPOSITE_ALPHA_KEY - When compositing is enabled, this -# enables/disables alpha blending. -# - -NV_CTRL_GVO_COMPOSITE_ALPHA_KEY = 271 # RW-- -NV_CTRL_GVO_COMPOSITE_ALPHA_KEY_DISABLE = 0 -NV_CTRL_GVO_COMPOSITE_ALPHA_KEY_ENABLE = 1 - -# -# NV_CTRL_GVO_COMPOSITE_LUMA_KEY_RANGE - Set the values of a luma -# channel range. This is a packed int that has the following format -# (in order of high-bits to low bits): -# -# Range # (11 bits), (Enabled 1 bit), min value (10 bits), max value (10 bits) -# -# To query the current values, pass the range # throught the display_mask -# variable. -# - -NV_CTRL_GVO_COMPOSITE_LUMA_KEY_RANGE = 272 # RW-- - -# -# NV_CTRL_GVO_COMPOSITE_CR_KEY_RANGE - Set the values of a CR -# channel range. This is a packed int that has the following format -# (in order of high-bits to low bits): -# -# Range # (11 bits), (Enabled 1 bit), min value (10 bits), max value (10 bits) -# -# To query the current values, pass the range # throught he display_mask -# variable. -# - -NV_CTRL_GVO_COMPOSITE_CR_KEY_RANGE = 273 # RW-- - -# -# NV_CTRL_GVO_COMPOSITE_CB_KEY_RANGE - Set the values of a CB -# channel range. This is a packed int that has the following format -# (in order of high-bits to low bits): -# -# Range # (11 bits), (Enabled 1 bit), min value (10 bits), max value (10 bits) -# -# To query the current values, pass the range # throught he display_mask -# variable. -# - -NV_CTRL_GVO_COMPOSITE_CB_KEY_RANGE = 274 # RW-- - -# -# NV_CTRL_GVO_COMPOSITE_NUM_KEY_RANGES - Returns the number of ranges -# available for each channel (Y/Luma, Cr, and Cb.) -# - -NV_CTRL_GVO_COMPOSITE_NUM_KEY_RANGES = 275 # R--- - -# -# NV_CTRL_SWITCH_TO_DISPLAYS - not supported -# - -NV_CTRL_SWITCH_TO_DISPLAYS = 276 # not supported - -# -# NV_CTRL_NOTEBOOK_DISPLAY_CHANGE_LID_EVENT - not supported -# - -NV_CTRL_NOTEBOOK_DISPLAY_CHANGE_LID_EVENT = 277 # not supported - -# -# NV_CTRL_NOTEBOOK_INTERNAL_LCD - deprecated -# - -NV_CTRL_NOTEBOOK_INTERNAL_LCD = 278 # deprecated - -# -# NV_CTRL_DEPTH_30_ALLOWED - returns whether the NVIDIA X driver supports -# depth 30 on the specified X screen or GPU. -# - -NV_CTRL_DEPTH_30_ALLOWED = 279 # R--G - -# -# NV_CTRL_MODE_SET_EVENT This attribute is sent as an event -# when hotkey, ctrl-alt-+/- or randr event occurs. Note that -# This attribute cannot be set or queried and is meant to -# be received by clients that wish to be notified of when -# mode set events occur. -# - -NV_CTRL_MODE_SET_EVENT = 280 # --- - -# -# NV_CTRL_OPENGL_AA_LINE_GAMMA_VALUE - the gamma value used by -# OpenGL when NV_CTRL_OPENGL_AA_LINE_GAMMA is enabled -# - -NV_CTRL_OPENGL_AA_LINE_GAMMA_VALUE = 281 # RW-X - -# -# NV_CTRL_VCSC_HIGH_PERF_MODE - deprecated -# -# Is used to both query High Performance Mode status on the Visual Computing -# System, and also to enable or disable High Performance Mode. -# - -NV_CTRL_VCSC_HIGH_PERF_MODE = 282 # RW-V -NV_CTRL_VCSC_HIGH_PERF_MODE_DISABLE = 0 -NV_CTRL_VCSC_HIGH_PERF_MODE_ENABLE = 1 - -# -# NV_CTRL_DISPLAYPORT_LINK_RATE - returns the negotiated lane bandwidth of the -# DisplayPort main link. The numerical value of this attribute is the link -# rate in bps divided by 27000000. -# This attribute is only available for DisplayPort flat panels. -# - -NV_CTRL_DISPLAYPORT_LINK_RATE = 291 # R-DG -NV_CTRL_DISPLAYPORT_LINK_RATE_DISABLED = 0x0 -NV_CTRL_DISPLAYPORT_LINK_RATE_1_62GBPS = 0x6 # deprecated -NV_CTRL_DISPLAYPORT_LINK_RATE_2_70GBPS = 0xA # deprecated - -# -# NV_CTRL_STEREO_EYES_EXCHANGE - Controls whether or not the left and right -# eyes of a stereo image are flipped. -# - -NV_CTRL_STEREO_EYES_EXCHANGE = 292 # RW-X -NV_CTRL_STEREO_EYES_EXCHANGE_OFF = 0 -NV_CTRL_STEREO_EYES_EXCHANGE_ON = 1 - -# -# NV_CTRL_NO_SCANOUT - returns whether the special "NoScanout" mode is -# enabled on the specified X screen or GPU; for details on this mode, -# see the description of the "none" value for the "UseDisplayDevice" -# X configuration option in the NVIDIA driver README. -# - -NV_CTRL_NO_SCANOUT = 293 # R--G -NV_CTRL_NO_SCANOUT_DISABLED = 0 -NV_CTRL_NO_SCANOUT_ENABLED = 1 - -# -# NV_CTRL_GVO_CSC_CHANGED_EVENT This attribute is sent as an event -# when the color space conversion matrix has been altered by another -# client. -# - -NV_CTRL_GVO_CSC_CHANGED_EVENT = 294 # --- - -# -# NV_CTRL_FRAMELOCK_SLAVEABLE - deprecated -# -# NV_CTRL_FRAMELOCK_DISPLAY_CONFIG should be used instead. -# - -NV_CTRL_FRAMELOCK_SLAVEABLE = 295 # deprecated - -# -# NV_CTRL_GVO_SYNC_TO_DISPLAY This attribute controls whether or not -# the non-SDI display device will be sync'ed to the SDI display device -# (when configured in TwinView, Clone Mode or when using the SDI device -# with OpenGL). -# - -NV_CTRL_GVO_SYNC_TO_DISPLAY = 296 # --- -NV_CTRL_GVO_SYNC_TO_DISPLAY_DISABLE = 0 -NV_CTRL_GVO_SYNC_TO_DISPLAY_ENABLE = 1 - -# -# NV_CTRL_X_SERVER_UNIQUE_ID - returns a pseudo-unique identifier for this -# X server. Intended for use in cases where an NV-CONTROL client communicates -# with multiple X servers, and wants some level of confidence that two -# X Display connections correspond to the same or different X servers. -# - -NV_CTRL_X_SERVER_UNIQUE_ID = 297 # R--- - -# -# NV_CTRL_PIXMAP_CACHE - This attribute controls whether the driver attempts to -# store video memory pixmaps in a cache. The cache speeds up allocation and -# deallocation of pixmaps, but could use more memory than when the cache is -# disabled. -# - -NV_CTRL_PIXMAP_CACHE = 298 # RW-X -NV_CTRL_PIXMAP_CACHE_DISABLE = 0 -NV_CTRL_PIXMAP_CACHE_ENABLE = 1 - -# -# NV_CTRL_PIXMAP_CACHE_ROUNDING_SIZE_KB - When the pixmap cache is enabled and -# there is not enough free space in the cache to fit a new pixmap, the driver -# will round up to the next multiple of this number of kilobytes when -# allocating more memory for the cache. -# - -NV_CTRL_PIXMAP_CACHE_ROUNDING_SIZE_KB = 299 # RW-X - -# -# NV_CTRL_IS_GVO_DISPLAY - returns whether or not a given display is an -# SDI device. -# - -NV_CTRL_IS_GVO_DISPLAY = 300 # R-D -NV_CTRL_IS_GVO_DISPLAY_FALSE = 0 -NV_CTRL_IS_GVO_DISPLAY_TRUE = 1 - -# -# NV_CTRL_PCI_ID - Returns the PCI vendor and device ID of the specified -# device. -# -# NV_CTRL_PCI_ID is a "packed" integer attribute; the PCI vendor ID is stored -# in the upper 16 bits of the integer, and the PCI device ID is stored in the -# lower 16 bits of the integer. -# - -NV_CTRL_PCI_ID = 301 # R--GI - -# -# NV_CTRL_GVO_FULL_RANGE_COLOR - Allow full range color data [4-1019] -# without clamping to [64-940]. -# - -NV_CTRL_GVO_FULL_RANGE_COLOR = 302 # RW- -NV_CTRL_GVO_FULL_RANGE_COLOR_DISABLED = 0 -NV_CTRL_GVO_FULL_RANGE_COLOR_ENABLED = 1 - -# -# NV_CTRL_SLI_MOSAIC_MODE_AVAILABLE - Returns whether or not -# SLI Mosaic Mode supported. -# - -NV_CTRL_SLI_MOSAIC_MODE_AVAILABLE = 303 # R-- -NV_CTRL_SLI_MOSAIC_MODE_AVAILABLE_FALSE = 0 -NV_CTRL_SLI_MOSAIC_MODE_AVAILABLE_TRUE = 1 - -# -# NV_CTRL_GVO_ENABLE_RGB_DATA - Allows clients to specify when -# the GVO board should process colors as RGB when the output data -# format is one of the NV_CTRL_GVO_DATA_FORMAT_???_PASSTRHU modes. -# - -NV_CTRL_GVO_ENABLE_RGB_DATA = 304 # RW- -NV_CTRL_GVO_ENABLE_RGB_DATA_DISABLE = 0 -NV_CTRL_GVO_ENABLE_RGB_DATA_ENABLE = 1 - -# -# NV_CTRL_IMAGE_SHARPENING_DEFAULT - Returns default value of -# Image Sharpening. -# - -NV_CTRL_IMAGE_SHARPENING_DEFAULT = 305 # R-- - -# -# NV_CTRL_PCI_DOMAIN - Returns the PCI domain number the specified device is -# using. -# - -NV_CTRL_PCI_DOMAIN = 306 # R--GI - -# -# NV_CTRL_GVI_NUM_JACKS - Returns the number of input BNC jacks available -# on a GVI device. -# - -NV_CTRL_GVI_NUM_JACKS = 307 # R--I - -# -# NV_CTRL_GVI_MAX_LINKS_PER_STREAM - Returns the maximum supported number of -# links that can be tied to one stream. -# - -NV_CTRL_GVI_MAX_LINKS_PER_STREAM = 308 # R--I - -# -# NV_CTRL_GVI_DETECTED_CHANNEL_BITS_PER_COMPONENT - Returns the detected -# number of bits per component (BPC) of data on the given input jack+ -# channel. -# -# The jack number should be specified in the lower 16 bits of the -# "display_mask" parameter, while the channel number should be specified in -# the upper 16 bits. -# - -NV_CTRL_GVI_DETECTED_CHANNEL_BITS_PER_COMPONENT = 309 # R--I -NV_CTRL_GVI_BITS_PER_COMPONENT_UNKNOWN = 0 -NV_CTRL_GVI_BITS_PER_COMPONENT_8 = 1 -NV_CTRL_GVI_BITS_PER_COMPONENT_10 = 2 -NV_CTRL_GVI_BITS_PER_COMPONENT_12 = 3 - -# -# NV_CTRL_GVI_REQUESTED_STREAM_BITS_PER_COMPONENT - Specify the number of -# bits per component (BPC) of data for the captured stream. -# The stream number should be specified in the "display_mask" parameter. -# -# Note: Setting this attribute may also result in the following -# NV-CONTROL attributes being reset on the GVI device (to ensure -# the configuration remains valid): -# NV_CTRL_GVI_REQUESTED_STREAM_COMPONENT_SAMPLING -# - -NV_CTRL_GVI_REQUESTED_STREAM_BITS_PER_COMPONENT = 310 # RW-I - -# -# NV_CTRL_GVI_DETECTED_CHANNEL_COMPONENT_SAMPLING - Returns the detected -# sampling format for the input jack+channel. -# -# The jack number should be specified in the lower 16 bits of the -# "display_mask" parameter, while the channel number should be specified in -# the upper 16 bits. -# - -NV_CTRL_GVI_DETECTED_CHANNEL_COMPONENT_SAMPLING = 311 # R--I -NV_CTRL_GVI_COMPONENT_SAMPLING_UNKNOWN = 0 -NV_CTRL_GVI_COMPONENT_SAMPLING_4444 = 1 -NV_CTRL_GVI_COMPONENT_SAMPLING_4224 = 2 -NV_CTRL_GVI_COMPONENT_SAMPLING_444 = 3 -NV_CTRL_GVI_COMPONENT_SAMPLING_422 = 4 -NV_CTRL_GVI_COMPONENT_SAMPLING_420 = 5 - -# -# NV_CTRL_GVI_REQUESTED_COMPONENT_SAMPLING - Specify the sampling format for -# the captured stream. -# The possible values are the NV_CTRL_GVI_DETECTED_COMPONENT_SAMPLING -# constants. -# The stream number should be specified in the "display_mask" parameter. -# - -NV_CTRL_GVI_REQUESTED_STREAM_COMPONENT_SAMPLING = 312 # RW-I - -# -# NV_CTRL_GVI_CHROMA_EXPAND - Enable or disable 4:2:2 -> 4:4:4 chroma -# expansion for the captured stream. This value is ignored when a -# COMPONENT_SAMPLING format is selected that does not use chroma subsampling, -# or if a BITS_PER_COMPONENT value is selected that is not supported. -# The stream number should be specified in the "display_mask" parameter. -# - -NV_CTRL_GVI_REQUESTED_STREAM_CHROMA_EXPAND = 313 # RW-I -NV_CTRL_GVI_CHROMA_EXPAND_FALSE = 0 -NV_CTRL_GVI_CHROMA_EXPAND_TRUE = 1 - -# -# NV_CTRL_GVI_DETECTED_CHANNEL_COLOR_SPACE - Returns the detected color space -# of the input jack+channel. -# -# The jack number should be specified in the lower 16 bits of the -# "display_mask" parameter, while the channel number should be specified in -# the upper 16 bits. -# - -NV_CTRL_GVI_DETECTED_CHANNEL_COLOR_SPACE = 314 # R--I -NV_CTRL_GVI_COLOR_SPACE_UNKNOWN = 0 -NV_CTRL_GVI_COLOR_SPACE_GBR = 1 -NV_CTRL_GVI_COLOR_SPACE_GBRA = 2 -NV_CTRL_GVI_COLOR_SPACE_GBRD = 3 -NV_CTRL_GVI_COLOR_SPACE_YCBCR = 4 -NV_CTRL_GVI_COLOR_SPACE_YCBCRA = 5 -NV_CTRL_GVI_COLOR_SPACE_YCBCRD = 6 - -# -# NV_CTRL_GVI_DETECTED_CHANNEL_LINK_ID - Returns the detected link identifier -# for the given input jack+channel. -# -# The jack number should be specified in the lower 16 bits of the -# "display_mask" parameter, while the channel number should be specified in -# the upper 16 bits. -# - -NV_CTRL_GVI_DETECTED_CHANNEL_LINK_ID = 315 # R--I -NV_CTRL_GVI_LINK_ID_UNKNOWN = 0xFFFF - -# -# NV_CTRL_GVI_DETECTED_CHANNEL_SMPTE352_IDENTIFIER - Returns the 4-byte -# SMPTE 352 identifier from the given input jack+channel. -# -# The jack number should be specified in the lower 16 bits of the -# "display_mask" parameter, while the channel number should be specified in -# the upper 16 bits. -# - -NV_CTRL_GVI_DETECTED_CHANNEL_SMPTE352_IDENTIFIER = 316 # R--I - -# -# NV_CTRL_GVI_GLOBAL_IDENTIFIER - Returns a global identifier for the -# GVI device. This identifier can be used to relate GVI devices named -# in NV-CONTROL with those enumerated in OpenGL. -# - -NV_CTRL_GVI_GLOBAL_IDENTIFIER = 317 # R--I - -# -# NV_CTRL_FRAMELOCK_SYNC_DELAY_RESOLUTION - Returns the number of nanoseconds -# that one unit of NV_CTRL_FRAMELOCK_SYNC_DELAY corresponds to. -# -NV_CTRL_FRAMELOCK_SYNC_DELAY_RESOLUTION = 318 # R-- - -# -# NV_CTRL_GPU_COOLER_MANUAL_CONTROL - Query the current or set a new -# cooler control state; the value of this attribute controls the -# availability of additional cooler control attributes (see below). -# -# Note: this attribute is unavailable unless cooler control support -# has been enabled in the X server (by the user). -# - -NV_CTRL_GPU_COOLER_MANUAL_CONTROL = 319 # RW-G -NV_CTRL_GPU_COOLER_MANUAL_CONTROL_FALSE = 0 -NV_CTRL_GPU_COOLER_MANUAL_CONTROL_TRUE = 1 - -# -# NV_CTRL_THERMAL_COOLER_LEVEL - The cooler's target level. -# Normally, the driver dynamically adjusts the cooler based on -# the needs of the GPU. But when NV_CTRL_GPU_COOLER_MANUAL_CONTROL=TRUE, -# the driver will attempt to make the cooler achieve the setting in -# NV_CTRL_THERMAL_COOLER_LEVEL. The actual current level of the cooler -# is reported in NV_CTRL_THERMAL_COOLER_CURRENT_LEVEL. -# - -NV_CTRL_THERMAL_COOLER_LEVEL = 320 # RW-C - -# NV_CTRL_THERMAL_COOLER_LEVEL_SET_DEFAULT - Sets default values of -# cooler. -# - -NV_CTRL_THERMAL_COOLER_LEVEL_SET_DEFAULT = 321 # -W-C - -# -# NV_CTRL_THERMAL_COOLER_CONTROL_TYPE - -# Returns a cooler's control signal characteristics. -# The possible types are restricted, Variable and Toggle. -# - -NV_CTRL_THERMAL_COOLER_CONTROL_TYPE = 322 # R--C -NV_CTRL_THERMAL_COOLER_CONTROL_TYPE_NONE = 0 -NV_CTRL_THERMAL_COOLER_CONTROL_TYPE_TOGGLE = 1 -NV_CTRL_THERMAL_COOLER_CONTROL_TYPE_VARIABLE = 2 - -# -# NV_CTRL_THERMAL_COOLER_TARGET - Returns objects that cooler cools. -# Targets may be GPU, Memory, Power Supply or All of these. -# GPU_RELATED = GPU | MEMORY | POWER_SUPPLY -# -# - -NV_CTRL_THERMAL_COOLER_TARGET = 323 # R--C -NV_CTRL_THERMAL_COOLER_TARGET_NONE = 0 -NV_CTRL_THERMAL_COOLER_TARGET_GPU = 1 -NV_CTRL_THERMAL_COOLER_TARGET_MEMORY = 2 -NV_CTRL_THERMAL_COOLER_TARGET_POWER_SUPPLY = 4 -NV_CTRL_THERMAL_COOLER_TARGET_GPU_RELATED = NV_CTRL_THERMAL_COOLER_TARGET_GPU | NV_CTRL_THERMAL_COOLER_TARGET_MEMORY | NV_CTRL_THERMAL_COOLER_TARGET_POWER_SUPPLY - -# -# NV_CTRL_GPU_ECC_SUPPORTED - Reports whether ECC is supported by the -# targeted GPU. -# -NV_CTRL_GPU_ECC_SUPPORTED = 324 # R--G -NV_CTRL_GPU_ECC_SUPPORTED_FALSE = 0 -NV_CTRL_GPU_ECC_SUPPORTED_TRUE = 1 - -# -# NV_CTRL_GPU_ECC_STATUS - Returns the current hardware ECC setting -# for the targeted GPU. -# -NV_CTRL_GPU_ECC_STATUS = 325 # R--G -NV_CTRL_GPU_ECC_STATUS_DISABLED = 0 -NV_CTRL_GPU_ECC_STATUS_ENABLED = 1 - -# -# NV_CTRL_GPU_ECC_CONFIGURATION - Reports whether ECC can be configured -# dynamically for the GPU in question. -# -NV_CTRL_GPU_ECC_CONFIGURATION_SUPPORTED = 326 # R--G -NV_CTRL_GPU_ECC_CONFIGURATION_SUPPORTED_FALSE = 0 -NV_CTRL_GPU_ECC_CONFIGURATION_SUPPORTED_TRUE = 1 - -# -# NV_CTRL_GPU_ECC_CONFIGURATION_SETTING - Returns the current ECC -# configuration setting or specifies new settings. New settings do not -# take effect until the next POST. -# -NV_CTRL_GPU_ECC_CONFIGURATION = 327 # RW-G -NV_CTRL_GPU_ECC_CONFIGURATION_DISABLED = 0 -NV_CTRL_GPU_ECC_CONFIGURATION_ENABLED = 1 - -# -# NV_CTRL_GPU_ECC_DEFAULT_CONFIGURATION_SETTING - Returns the default -# ECC configuration setting. -# -NV_CTRL_GPU_ECC_DEFAULT_CONFIGURATION = 328 # R--G -NV_CTRL_GPU_ECC_DEFAULT_CONFIGURATION_DISABLED = 0 -NV_CTRL_GPU_ECC_DEFAULT_CONFIGURATION_ENABLED = 1 - -# -# NV_CTRL_GPU_ECC_SINGLE_BIT_ERRORS - Returns the number of single-bit -# ECC errors detected by the targeted GPU since the last POST. -# Note: this attribute is a 64-bit integer attribute. -# -NV_CTRL_GPU_ECC_SINGLE_BIT_ERRORS = 329 # R--GQ - -# -# NV_CTRL_GPU_ECC_DOUBLE_BIT_ERRORS - Returns the number of double-bit -# ECC errors detected by the targeted GPU since the last POST. -# Note: this attribute is a 64-bit integer attribute. -# -NV_CTRL_GPU_ECC_DOUBLE_BIT_ERRORS = 330 # R--GQ - -# -# NV_CTRL_GPU_ECC_AGGREGATE_SINGLE_BIT_ERRORS - Returns the number of -# single-bit ECC errors detected by the targeted GPU since the -# last counter reset. -# Note: this attribute is a 64-bit integer attribute. -# -NV_CTRL_GPU_ECC_AGGREGATE_SINGLE_BIT_ERRORS = 331 # R--GQ - -# -# NV_CTRL_GPU_ECC_AGGREGATE_DOUBLE_BIT_ERRORS - Returns the number of -# double-bit ECC errors detected by the targeted GPU since the -# last counter reset. -# Note: this attribute is a 64-bit integer attribute. -# -NV_CTRL_GPU_ECC_AGGREGATE_DOUBLE_BIT_ERRORS = 332 # R--GQ - -# -# NV_CTRL_GPU_ECC_RESET_ERROR_STATUS - Resets the volatile/aggregate -# single-bit and double-bit error counters. This attribute is a -# bitmask attribute. -# -NV_CTRL_GPU_ECC_RESET_ERROR_STATUS = 333 # -W-G -NV_CTRL_GPU_ECC_RESET_ERROR_STATUS_VOLATILE = 0x00000001 -NV_CTRL_GPU_ECC_RESET_ERROR_STATUS_AGGREGATE = 0x00000002 - -# -# NV_CTRL_GPU_POWER_MIZER_MODE - Provides a hint to the driver -# as to how to manage the performance of the GPU. -# -# ADAPTIVE - adjust GPU clocks based on GPU -# utilization -# PREFER_MAXIMUM_PERFORMANCE - raise GPU clocks to favor -# maximum performance, to the extent -# that thermal and other constraints -# allow -# AUTO - let the driver choose the performance -# policy -# PREFER_CONSISTENT_PERFORMANCE - lock to GPU base clocks -# -NV_CTRL_GPU_POWER_MIZER_MODE = 334 # RW-G -NV_CTRL_GPU_POWER_MIZER_MODE_ADAPTIVE = 0 -NV_CTRL_GPU_POWER_MIZER_MODE_PREFER_MAXIMUM_PERFORMANCE = 1 -NV_CTRL_GPU_POWER_MIZER_MODE_AUTO = 2 -NV_CTRL_GPU_POWER_MIZER_MODE_PREFER_CONSISTENT_PERFORMANCE = 3 - -# -# NV_CTRL_GVI_SYNC_OUTPUT_FORMAT - Returns the output sync signal -# from the GVI device. -# - -NV_CTRL_GVI_SYNC_OUTPUT_FORMAT = 335 # R--I - -# -# NV_CTRL_GVI_MAX_CHANNELS_PER_JACK - Returns the maximum -# supported number of (logical) channels within a single physical jack of -# a GVI device. For most SDI video formats, there is only one channel -# (channel 0). But for 3G video formats (as specified in SMPTE 425), -# as an example, there are two channels (channel 0 and channel 1) per -# physical jack. -# - -NV_CTRL_GVI_MAX_CHANNELS_PER_JACK = 336 # R--I - -# -# NV_CTRL_GVI_MAX_STREAMS - Returns the maximum number of streams -# that can be configured on the GVI device. -# - -NV_CTRL_GVI_MAX_STREAMS = 337 # R--I - -# -# NV_CTRL_GVI_NUM_CAPTURE_SURFACES - The GVI interface exposed through -# NV-CONTROL and the GLX_NV_video_input extension uses internal capture -# surfaces when frames are read from the GVI device. The -# NV_CTRL_GVI_NUM_CAPTURE_SURFACES can be used to query and assign the -# number of capture surfaces. This attribute is applied when -# glXBindVideoCaptureDeviceNV() is called by the application. -# -# A lower number of capture surfaces will mean less video memory is used, -# but can result in frames being dropped if the application cannot keep up -# with the capture device. A higher number will prevent frames from being -# dropped, making capture more reliable but will consume move video memory. -# -NV_CTRL_GVI_NUM_CAPTURE_SURFACES = 338 # RW-I - -# -# NV_CTRL_OVERSCAN_COMPENSATION - not supported -# -NV_CTRL_OVERSCAN_COMPENSATION = 339 # not supported - -# -# NV_CTRL_GPU_PCIE_GENERATION - Reports the current PCIe generation. -# -NV_CTRL_GPU_PCIE_GENERATION = 341 # R--GI -NV_CTRL_GPU_PCIE_GENERATION1 = 0x00000001 -NV_CTRL_GPU_PCIE_GENERATION2 = 0x00000002 -NV_CTRL_GPU_PCIE_GENERATION3 = 0x00000003 - -# -# NV_CTRL_GVI_BOUND_GPU - Returns the NV_CTRL_TARGET_TYPE_GPU target_id of -# the GPU currently bound to the GVI device. Returns -1 if no GPU is -# currently bound to the GVI device. -# -NV_CTRL_GVI_BOUND_GPU = 342 # R--I - -# -# NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT3 - this attribute is only -# intended to be used to query the ValidValues for -# NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT for VIDEO_FORMAT values between -# 64 and 95. See NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT for details. -# - -NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT3 = 343 # ---GI - -# -# NV_CTRL_ACCELERATE_TRAPEZOIDS - Toggles RENDER Trapezoid acceleration -# - -NV_CTRL_ACCELERATE_TRAPEZOIDS = 344 # RW- -NV_CTRL_ACCELERATE_TRAPEZOIDS_DISABLE = 0 -NV_CTRL_ACCELERATE_TRAPEZOIDS_ENABLE = 1 - -# -# NV_CTRL_GPU_CORES - Returns number of GPU cores supported by the graphics -# pipeline. -# - -NV_CTRL_GPU_CORES = 345 # R--G - -# -# NV_CTRL_GPU_MEMORY_BUS_WIDTH - Returns memory bus bandwidth on the associated -# subdevice. -# - -NV_CTRL_GPU_MEMORY_BUS_WIDTH = 346 # R--G - -# -# NV_CTRL_GVI_TEST_MODE - This attribute controls the GVI test mode. When -# enabled, the GVI device will generate fake data as quickly as possible. All -# GVI settings are still valid when this is enabled (e.g., the requested video -# format is honored and sets the video size). -# This may be used to test the pipeline. -# - -NV_CTRL_GVI_TEST_MODE = 347 # R--I -NV_CTRL_GVI_TEST_MODE_DISABLE = 0 -NV_CTRL_GVI_TEST_MODE_ENABLE = 1 - -# -# NV_CTRL_COLOR_SPACE - This option controls the preferred color space of the -# video signal. This may not match the current color space depending on the -# current mode on this display. -# -# NV_CTRL_CURRENT_COLOR_SPACE will reflect the actual color space in use. -# -NV_CTRL_COLOR_SPACE = 348 # RWDG -NV_CTRL_COLOR_SPACE_RGB = 0 -NV_CTRL_COLOR_SPACE_YCbCr422 = 1 -NV_CTRL_COLOR_SPACE_YCbCr444 = 2 - -# -# NV_CTRL_COLOR_RANGE - This option controls the preferred color range of the -# video signal. -# -# If the current color space requires it, the actual color range will be -# limited. -# -# NV_CTRL_CURRENT_COLOR_RANGE will reflect the actual color range in use. -# -NV_CTRL_COLOR_RANGE = 349 # RWDG -NV_CTRL_COLOR_RANGE_FULL = 0 -NV_CTRL_COLOR_RANGE_LIMITED = 1 - -# -# NV_CTRL_GPU_SCALING_DEFAULT_TARGET - not supported -# - -NV_CTRL_GPU_SCALING_DEFAULT_TARGET = 350 # not supported - -# -# NV_CTRL_GPU_SCALING_DEFAULT_METHOD - not supported -# - -NV_CTRL_GPU_SCALING_DEFAULT_METHOD = 351 # not supported - -# -# NV_CTRL_DITHERING_MODE - Controls the dithering mode, when -# NV_CTRL_CURRENT_DITHERING is Enabled. -# -# AUTO: allow the driver to choose the dithering mode automatically. -# -# DYNAMIC_2X2: use a 2x2 matrix to dither from the GPU's pixel -# pipeline to the bit depth of the flat panel. The matrix values -# are changed from frame to frame. -# -# STATIC_2X2: use a 2x2 matrix to dither from the GPU's pixel -# pipeline to the bit depth of the flat panel. The matrix values -# do not change from frame to frame. -# -# TEMPORAL: use a pseudorandom value from a uniform distribution calculated at -# every pixel to achieve stochastic dithering. This method produces a better -# visual result than 2x2 matrix approaches. -# -NV_CTRL_DITHERING_MODE = 352 # RWDG -NV_CTRL_DITHERING_MODE_AUTO = 0 -NV_CTRL_DITHERING_MODE_DYNAMIC_2X2 = 1 -NV_CTRL_DITHERING_MODE_STATIC_2X2 = 2 -NV_CTRL_DITHERING_MODE_TEMPORAL = 3 - -# -# NV_CTRL_CURRENT_DITHERING - Returns the current dithering state. -# -NV_CTRL_CURRENT_DITHERING = 353 # R-DG -NV_CTRL_CURRENT_DITHERING_DISABLED = 0 -NV_CTRL_CURRENT_DITHERING_ENABLED = 1 - -# -# NV_CTRL_CURRENT_DITHERING_MODE - Returns the current dithering -# mode. -# -NV_CTRL_CURRENT_DITHERING_MODE = 354 # R-DG -NV_CTRL_CURRENT_DITHERING_MODE_NONE = 0 -NV_CTRL_CURRENT_DITHERING_MODE_DYNAMIC_2X2 = 1 -NV_CTRL_CURRENT_DITHERING_MODE_STATIC_2X2 = 2 -NV_CTRL_CURRENT_DITHERING_MODE_TEMPORAL = 3 - -# -# NV_CTRL_THERMAL_SENSOR_READING - Returns the thermal sensor's current -# reading. -# -NV_CTRL_THERMAL_SENSOR_READING = 355 # R--S - -# -# NV_CTRL_THERMAL_SENSOR_PROVIDER - Returns the hardware device that -# provides the thermal sensor. -# -NV_CTRL_THERMAL_SENSOR_PROVIDER = 356 # R--S -NV_CTRL_THERMAL_SENSOR_PROVIDER_NONE = 0 -NV_CTRL_THERMAL_SENSOR_PROVIDER_GPU_INTERNAL = 1 -NV_CTRL_THERMAL_SENSOR_PROVIDER_ADM1032 = 2 -NV_CTRL_THERMAL_SENSOR_PROVIDER_ADT7461 = 3 -NV_CTRL_THERMAL_SENSOR_PROVIDER_MAX6649 = 4 -NV_CTRL_THERMAL_SENSOR_PROVIDER_MAX1617 = 5 -NV_CTRL_THERMAL_SENSOR_PROVIDER_LM99 = 6 -NV_CTRL_THERMAL_SENSOR_PROVIDER_LM89 = 7 -NV_CTRL_THERMAL_SENSOR_PROVIDER_LM64 = 8 -NV_CTRL_THERMAL_SENSOR_PROVIDER_G781 = 9 -NV_CTRL_THERMAL_SENSOR_PROVIDER_ADT7473 = 10 -NV_CTRL_THERMAL_SENSOR_PROVIDER_SBMAX6649 = 11 -NV_CTRL_THERMAL_SENSOR_PROVIDER_VBIOSEVT = 12 -NV_CTRL_THERMAL_SENSOR_PROVIDER_OS = 13 -NV_CTRL_THERMAL_SENSOR_PROVIDER_UNKNOWN = 0xFFFFFFFF - -# -# NV_CTRL_THERMAL_SENSOR_TARGET - Returns what hardware component -# the thermal sensor is measuring. -# -NV_CTRL_THERMAL_SENSOR_TARGET = 357 # R--S -NV_CTRL_THERMAL_SENSOR_TARGET_NONE = 0 -NV_CTRL_THERMAL_SENSOR_TARGET_GPU = 1 -NV_CTRL_THERMAL_SENSOR_TARGET_MEMORY = 2 -NV_CTRL_THERMAL_SENSOR_TARGET_POWER_SUPPLY = 4 -NV_CTRL_THERMAL_SENSOR_TARGET_BOARD = 8 -NV_CTRL_THERMAL_SENSOR_TARGET_UNKNOWN = 0xFFFFFFFF - -# -# NV_CTRL_SHOW_MULTIGPU_VISUAL_INDICATOR - when TRUE, OpenGL will -# draw information about the current MULTIGPU mode. -# -NV_CTRL_SHOW_MULTIGPU_VISUAL_INDICATOR = 358 # RW-X -NV_CTRL_SHOW_MULTIGPU_VISUAL_INDICATOR_FALSE = 0 -NV_CTRL_SHOW_MULTIGPU_VISUAL_INDICATOR_TRUE = 1 - -# -# NV_CTRL_GPU_CURRENT_PROCESSOR_CLOCK_FREQS - Returns GPU's processor -# clock freqs. -# -NV_CTRL_GPU_CURRENT_PROCESSOR_CLOCK_FREQS = 359 # RW-G - -# -# NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS - query the flags (various information -# for the specified NV_CTRL_GVIO_VIDEO_FORMAT_*. So that this can be -# queried with existing interfaces, the video format should be specified -# in the display_mask field; eg: -# -# XNVCTRLQueryTargetAttribute(dpy, -# NV_CTRL_TARGET_TYPE_GVI, -# gvi, -# NV_CTRL_GVIO_VIDEO_FORMAT_720P_60_00_SMPTE296, -# NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS, -# &flags); -# -# Note: The NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS_3G_1080P_NO_12BPC flag is set -# for those 1080P 3G modes (level A and B) that do not support -# 12 bits per component (when configuring a GVI stream.) -# - -NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS = 360 # R--I -NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS_NONE = 0x00000000 -NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS_INTERLACED = 0x00000001 -NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS_PROGRESSIVE = 0x00000002 -NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS_PSF = 0x00000004 -NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS_3G_LEVEL_A = 0x00000008 -NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS_3G_LEVEL_B = 0x00000010 -NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS_3G = NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS_3G_LEVEL_A | NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS_3G_LEVEL_B -NV_CTRL_GVIO_VIDEO_FORMAT_FLAGS_3G_1080P_NO_12BPC = 0x00000020 - -# -# NV_CTRL_GPU_PCIE_MAX_LINK_SPEED - returns maximum PCIe link speed, -# in gigatransfers per second (GT/s). -# - -NV_CTRL_GPU_PCIE_MAX_LINK_SPEED = 361 # R--GI - -# -# NV_CTRL_3D_VISION_PRO_RESET_TRANSCEIVER_TO_FACTORY_SETTINGS - Resets the -# 3D Vision Pro transceiver to its factory settings. -# -NV_CTRL_3D_VISION_PRO_RESET_TRANSCEIVER_TO_FACTORY_SETTINGS = 363 # -W-T - -# -# NV_CTRL_3D_VISION_PRO_TRANSCEIVER_CHANNEL - Controls the channel that is -# currently used by the 3D Vision Pro transceiver. -# -NV_CTRL_3D_VISION_PRO_TRANSCEIVER_CHANNEL = 364 # RW-T - -# -# NV_CTRL_3D_VISION_PRO_TRANSCEIVER_MODE - Controls the mode in which the -# 3D Vision Pro transceiver operates. -# NV_CTRL_3D_VISION_PRO_TM_LOW_RANGE is bidirectional -# NV_CTRL_3D_VISION_PRO_TM_MEDIUM_RANGE is bidirectional -# NV_CTRL_3D_VISION_PRO_TM_HIGH_RANGE may be bidirectional just up to a -# given range, and unidirectional beyond it -# NV_CTRL_3D_VISION_PRO_TM_COUNT is the total number of -# 3D Vision Pro transceiver modes -# -NV_CTRL_3D_VISION_PRO_TRANSCEIVER_MODE = 365 # RW-T -NV_CTRL_3D_VISION_PRO_TRANSCEIVER_MODE_INVALID = 0 -NV_CTRL_3D_VISION_PRO_TRANSCEIVER_MODE_LOW_RANGE = 1 -NV_CTRL_3D_VISION_PRO_TRANSCEIVER_MODE_MEDIUM_RANGE = 2 -NV_CTRL_3D_VISION_PRO_TRANSCEIVER_MODE_HIGH_RANGE = 3 -NV_CTRL_3D_VISION_PRO_TRANSCEIVER_MODE_COUNT = 4 - -# -# NV_CTRL_SYNCHRONOUS_PALETTE_UPDATES - controls whether updates to the color -# lookup table (LUT) are synchronous with respect to X rendering. For example, -# if an X client sends XStoreColors followed by XFillRectangle, the driver will -# guarantee that the FillRectangle request is not processed until after the -# updated LUT colors are actually visible on the screen if -# NV_CTRL_SYNCHRONOUS_PALETTE_UPDATES is enabled. Otherwise, the rendering may -# occur first. -# -# This makes a difference for applications that use the LUT to animate, such as -# XPilot. If you experience flickering in applications that use LUT -# animations, try enabling this attribute. -# -# When synchronous updates are enabled, XStoreColors requests will be processed -# at your screen's refresh rate. -# - -NV_CTRL_SYNCHRONOUS_PALETTE_UPDATES = 367 # RWDG -NV_CTRL_SYNCHRONOUS_PALETTE_UPDATES_DISABLE = 0 -NV_CTRL_SYNCHRONOUS_PALETTE_UPDATES_ENABLE = 1 - -# -# NV_CTRL_DITHERING_DEPTH - Controls the dithering depth when -# NV_CTRL_CURRENT_DITHERING is ENABLED. Some displays connected -# to the GPU via the DVI or LVDS interfaces cannot display the -# full color range of ten bits per channel, so the GPU will -# dither to either 6 or 8 bits per channel. -# -NV_CTRL_DITHERING_DEPTH = 368 # RWDG -NV_CTRL_DITHERING_DEPTH_AUTO = 0 -NV_CTRL_DITHERING_DEPTH_6_BITS = 1 -NV_CTRL_DITHERING_DEPTH_8_BITS = 2 - -# -# NV_CTRL_CURRENT_DITHERING_DEPTH - Returns the current dithering -# depth value. -# -NV_CTRL_CURRENT_DITHERING_DEPTH = 369 # R-DG -NV_CTRL_CURRENT_DITHERING_DEPTH_NONE = 0 -NV_CTRL_CURRENT_DITHERING_DEPTH_6_BITS = 1 -NV_CTRL_CURRENT_DITHERING_DEPTH_8_BITS = 2 - -# -# NV_CTRL_3D_VISION_PRO_TRANSCEIVER_CHANNEL_FREQUENCY - Returns the -# frequency of the channel(in kHz) of the 3D Vision Pro transceiver. -# Use the display_mask parameter to specify the channel number. -# -NV_CTRL_3D_VISION_PRO_TRANSCEIVER_CHANNEL_FREQUENCY = 370 # R--T - -# -# NV_CTRL_3D_VISION_PRO_TRANSCEIVER_CHANNEL_QUALITY - Returns the -# quality of the channel(in percentage) of the 3D Vision Pro transceiver. -# Use the display_mask parameter to specify the channel number. -# -NV_CTRL_3D_VISION_PRO_TRANSCEIVER_CHANNEL_QUALITY = 371 # R--T - -# -# NV_CTRL_3D_VISION_PRO_TRANSCEIVER_CHANNEL_COUNT - Returns the number of -# channels on the 3D Vision Pro transceiver. -# -NV_CTRL_3D_VISION_PRO_TRANSCEIVER_CHANNEL_COUNT = 372 # R--T - -# -# NV_CTRL_3D_VISION_PRO_PAIR_GLASSES - Puts the 3D Vision Pro -# transceiver into pairing mode to gather additional glasses. -# NV_CTRL_3D_VISION_PRO_PAIR_GLASSES_STOP - stops any pairing -# NV_CTRL_3D_VISION_PRO_PAIR_GLASSES_BEACON - starts continuous -# pairing via beacon mode -# Any other value, N - Puts the 3D Vision Pro transceiver into -# authenticated pairing mode for N seconds. -# -NV_CTRL_3D_VISION_PRO_PAIR_GLASSES = 373 # -W-T -NV_CTRL_3D_VISION_PRO_PAIR_GLASSES_STOP = 0 -NV_CTRL_3D_VISION_PRO_PAIR_GLASSES_BEACON = 0xFFFFFFFF - -# -# NV_CTRL_3D_VISION_PRO_UNPAIR_GLASSES - Tells a specific pair -# of glasses to unpair. The glasses will "forget" the address -# of the 3D Vision Pro transceiver to which they have been paired. -# To unpair all the currently paired glasses, specify -# the glasses id as 0. -# -NV_CTRL_3D_VISION_PRO_UNPAIR_GLASSES = 374 # -W-T - -# -# NV_CTRL_3D_VISION_PRO_DISCOVER_GLASSES - Tells the 3D Vision Pro -# transceiver about the glasses that have been paired using -# NV_CTRL_3D_VISION_PRO_PAIR_GLASSES_BEACON. Unless this is done, -# the 3D Vision Pro transceiver will not know about glasses paired in -# beacon mode. -# -NV_CTRL_3D_VISION_PRO_DISCOVER_GLASSES = 375 # -W-T - -# -# NV_CTRL_3D_VISION_PRO_IDENTIFY_GLASSES - Causes glasses LEDs to -# flash for a short period of time. -# -NV_CTRL_3D_VISION_PRO_IDENTIFY_GLASSES = 376 # -W-T - -# -# NV_CTRL_3D_VISION_PRO_GLASSES_SYNC_CYCLE - Controls the -# sync cycle duration(in milliseconds) of the glasses. -# Use the display_mask parameter to specify the glasses id. -# -NV_CTRL_3D_VISION_PRO_GLASSES_SYNC_CYCLE = 378 # RW-T - -# -# NV_CTRL_3D_VISION_PRO_GLASSES_MISSED_SYNC_CYCLES - Returns the -# number of state sync cycles recently missed by the glasses. -# Use the display_mask parameter to specify the glasses id. -# -NV_CTRL_3D_VISION_PRO_GLASSES_MISSED_SYNC_CYCLES = 379 # R--T - -# -# NV_CTRL_3D_VISION_PRO_GLASSES_BATTERY_LEVEL - Returns the -# battery level(in percentage) of the glasses. -# Use the display_mask parameter to specify the glasses id. -# -NV_CTRL_3D_VISION_PRO_GLASSES_BATTERY_LEVEL = 380 # R--T - -# -# NV_CTRL_GVO_ANC_PARITY_COMPUTATION - Controls the SDI device's computation -# of the parity bit (bit 8) for ANC data words. -# - -NV_CTRL_GVO_ANC_PARITY_COMPUTATION = 381 # RW--- -NV_CTRL_GVO_ANC_PARITY_COMPUTATION_AUTO = 0 -NV_CTRL_GVO_ANC_PARITY_COMPUTATION_ON = 1 -NV_CTRL_GVO_ANC_PARITY_COMPUTATION_OFF = 2 - -# -# NV_CTRL_3D_VISION_PRO_GLASSES_PAIR_EVENT - This attribute is sent -# as an event when glasses get paired in response to pair command -# from any of the clients. -# -NV_CTRL_3D_VISION_PRO_GLASSES_PAIR_EVENT = 382 # ---T - -# -# NV_CTRL_3D_VISION_PRO_GLASSES_UNPAIR_EVENT - This attribute is sent -# as an event when glasses get unpaired in response to unpair command -# from any of the clients. -# -NV_CTRL_3D_VISION_PRO_GLASSES_UNPAIR_EVENT = 383 # ---T - -# -# NV_CTRL_GPU_PCIE_CURRENT_LINK_WIDTH - returns the current -# PCIe link width, in number of lanes. -# -NV_CTRL_GPU_PCIE_CURRENT_LINK_WIDTH = 384 # R--GI - -# -# NV_CTRL_GPU_PCIE_CURRENT_LINK_SPEED - returns the current -# PCIe link speed, in megatransfers per second (GT/s). -# -NV_CTRL_GPU_PCIE_CURRENT_LINK_SPEED = 385 # R--GI - -# -# NV_CTRL_GVO_AUDIO_BLANKING - specifies whether the GVO device should delete -# audio ancillary data packets when frames are repeated. -# -# When a new frame is not ready in time, the current frame, including all -# ancillary data packets, is repeated. When this data includes audio packets, -# this can result in stutters or clicks. When this option is enabled, the GVO -# device will detect when frames are repeated, identify audio ancillary data -# packets, and mark them for deletion. -# -# This option is applied when the GVO device is bound. -# -NV_CTRL_GVO_AUDIO_BLANKING = 386 # RW- -NV_CTRL_GVO_AUDIO_BLANKING_DISABLE = 0 -NV_CTRL_GVO_AUDIO_BLANKING_ENABLE = 1 - -# -# NV_CTRL_CURRENT_METAMODE_ID - switch modes to the MetaMode with -# the specified ID. -# -NV_CTRL_CURRENT_METAMODE_ID = 387 # RW- - -# -# NV_CTRL_DISPLAY_ENABLED - Returns whether or not the display device -# is currently enabled. -# -NV_CTRL_DISPLAY_ENABLED = 388 # R-D -NV_CTRL_DISPLAY_ENABLED_TRUE = 1 -NV_CTRL_DISPLAY_ENABLED_FALSE = 0 - -# -# NV_CTRL_FRAMELOCK_INCOMING_HOUSE_SYNC_RATE: this is the rate -# of an incomming house sync signal to the frame lock board, in milliHz. -# -# This attribute may be queried through XNVCTRLQueryTargetAttribute() -# using a NV_CTRL_TARGET_TYPE_FRAMELOCK or NV_CTRL_TARGET_TYPE_X_SCREEN -# target. -# -NV_CTRL_FRAMELOCK_INCOMING_HOUSE_SYNC_RATE = 389 # R--F - -# -# NV_CTRL_FXAA - enables FXAA. A pixel shader based anti- -# aliasing method. -# -NV_CTRL_FXAA = 390 # RW-X -NV_CTRL_FXAA_DISABLE = 0 -NV_CTRL_FXAA_ENABLE = 1 - -# -# NV_CTRL_DISPLAY_RANDR_OUTPUT_ID - the RandR Output ID (type RROutput) -# that corresponds to the specified Display Device target. If a new -# enough version of RandR is not available in the X server, -# DISPLAY_RANDR_OUTPUT_ID will be 0. -# -NV_CTRL_DISPLAY_RANDR_OUTPUT_ID = 391 # R-D- - -# -# NV_CTRL_FRAMELOCK_DISPLAY_CONFIG - Configures whether the display device -# should listen, ignore or drive the framelock sync signal. -# -# Note that whether or not a display device may be set as a client/server -# depends on the current configuration. For example, only one server may be -# set per Quadro Sync device, and displays can only be configured as a client -# if their refresh rate sufficiently matches the refresh rate of the server -# device. -# -# Note that when querying the ValidValues for this data type, the values are -# reported as bits within a bitmask (ATTRIBUTE_TYPE_INT_BITS); -# -NV_CTRL_FRAMELOCK_DISPLAY_CONFIG = 392 # RWD -NV_CTRL_FRAMELOCK_DISPLAY_CONFIG_DISABLED = 0 -NV_CTRL_FRAMELOCK_DISPLAY_CONFIG_CLIENT = 1 -NV_CTRL_FRAMELOCK_DISPLAY_CONFIG_SERVER = 2 - -# -# NV_CTRL_TOTAL_DEDICATED_GPU_MEMORY - Returns the total amount of dedicated -# GPU video memory, in MB, on the specified GPU. This excludes any TurboCache -# padding included in the value returned by NV_CTRL_TOTAL_GPU_MEMORY. -# -NV_CTRL_TOTAL_DEDICATED_GPU_MEMORY = 393 # R--G - -# -# NV_CTRL_USED_DEDICATED_GPU_MEMORY- Returns the amount of video memory -# currently used on the graphics card in MB. -# -NV_CTRL_USED_DEDICATED_GPU_MEMORY = 394 # R--G - -# -# NV_CTRL_GPU_DOUBLE_PRECISION_BOOST_IMMEDIATE -# Some GPUs can make a tradeoff between double-precision floating-point -# performance and clock speed. Enabling double-precision floating point -# performance may benefit CUDA or OpenGL applications that require high -# bandwidth double-precision performance. Disabling this feature may benefit -# graphics applications that require higher clock speeds. -# -# This attribute is only available when toggling double precision boost -# can be done immediately (without need for a rebooot). -# -NV_CTRL_GPU_DOUBLE_PRECISION_BOOST_IMMEDIATE = 395 # RW-G -NV_CTRL_GPU_DOUBLE_PRECISION_BOOST_IMMEDIATE_DISABLED = 0 -NV_CTRL_GPU_DOUBLE_PRECISION_BOOST_IMMEDIATE_ENABLED = 1 - -# -# NV_CTRL_GPU_DOUBLE_PRECISION_BOOST_REBOOT -# Some GPUs can make a tradeoff between double-precision floating-point -# performance and clock speed. Enabling double-precision floating point -# performance may benefit CUDA or OpenGL applications that require high -# bandwidth double-precision performance. Disabling this feature may benefit -# graphics applications that require higher clock speeds. -# -# This attribute is only available when toggling double precision boost -# requires a reboot. -# - -NV_CTRL_GPU_DOUBLE_PRECISION_BOOST_REBOOT = 396 # RW-G -NV_CTRL_GPU_DOUBLE_PRECISION_BOOST_REBOOT_DISABLED = 0 -NV_CTRL_GPU_DOUBLE_PRECISION_BOOST_REBOOT_ENALED = 1 - -# -# NV_CTRL_DPY_HDMI_3D - Returns whether the specified display device is -# currently using HDMI 3D Frame Packed Stereo mode. Clients may use this -# to help interpret the refresh rate returned by NV_CTRL_REFRESH_RATE or -# NV_CTRL_REFRESH_RATE_3, which will be doubled when using HDMI 3D mode. -# -# This attribute may be queried through XNVCTRLQueryTargetAttribute() -# using a NV_CTRL_TARGET_TYPE_GPU target. -# - -NV_CTRL_DPY_HDMI_3D = 397 # R-DG -NV_CTRL_DPY_HDMI_3D_DISABLED = 0 -NV_CTRL_DPY_HDMI_3D_ENABLED = 1 - -# -# NV_CTRL_BASE_MOSAIC - Returns whether Base Mosaic is currently enabled on the -# given GPU. Querying the valid values of this attribute returns capabilities. -# - -NV_CTRL_BASE_MOSAIC = 398 # R--G -NV_CTRL_BASE_MOSAIC_DISABLED = 0 -NV_CTRL_BASE_MOSAIC_FULL = 1 -NV_CTRL_BASE_MOSAIC_LIMITED = 2 - -# -# NV_CTRL_MULTIGPU_MASTER_POSSIBLE - Returns whether the GPU can be configured -# as the master GPU in a Multi GPU configuration (SLI, SLI Mosaic, -# Base Mosaic). -# - -NV_CTRL_MULTIGPU_MASTER_POSSIBLE = 399 # R--G -NV_CTRL_MULTIGPU_MASTER_POSSIBLE_FALSE = 0 -NV_CTRL_MULTIGPU_MASTER_POSSIBLE_TRUE = 1 - -# -# NV_CTRL_GPU_POWER_MIZER_DEFAULT_MODE - Returns the default PowerMizer mode -# for the given GPU. -# -NV_CTRL_GPU_POWER_MIZER_DEFAULT_MODE = 400 # R--G - -# -# NV_CTRL_XV_SYNC_TO_DISPLAY_ID - When XVideo Sync To VBlank is enabled, this -# controls which display device will be synched to if the display is enabled. -# Returns NV_CTRL_XV_SYNC_TO_DISPLAY_ID_AUTO if no display has been -# selected. -# -NV_CTRL_XV_SYNC_TO_DISPLAY_ID = 401 # RW- -NV_CTRL_XV_SYNC_TO_DISPLAY_ID_AUTO = 0xFFFFFFFF - -# -# NV_CTRL_BACKLIGHT_BRIGHTNESS - The backlight brightness of an internal panel. -# -NV_CTRL_BACKLIGHT_BRIGHTNESS = 402 # RWD- - -# -# NV_CTRL_GPU_LOGO_BRIGHTNESS - Controls brightness -# of the logo on the GPU, if any. The value is variable from 0% - 100%. -# -NV_CTRL_GPU_LOGO_BRIGHTNESS = 403 # RW-G - -# -# NV_CTRL_GPU_SLI_LOGO_BRIGHTNESS - Controls brightness of the logo -# on the SLI bridge, if any. The value is variable from 0% - 100%. -# -NV_CTRL_GPU_SLI_LOGO_BRIGHTNESS = 404 # RW-G - -# -# NV_CTRL_THERMAL_COOLER_SPEED - Returns cooler's current operating speed in -# rotations per minute (RPM). -# - -NV_CTRL_THERMAL_COOLER_SPEED = 405 # R--C - -# -# NV_CTRL_PALETTE_UPDATE_EVENT - The Color Palette has been changed and the -# color correction info needs to be updated. -# - -NV_CTRL_PALETTE_UPDATE_EVENT = 406 # --- - -# -# NV_CTRL_VIDEO_ENCODER_UTILIZATION - Returns the video encoder engine -# utilization as a percentage. -# -NV_CTRL_VIDEO_ENCODER_UTILIZATION = 407 # R--G - -# -# NV_CTRL_GSYNC_ALLOWED - when TRUE, OpenGL will enable G-SYNC when possible; -# when FALSE, OpenGL will always use a fixed monitor refresh rate. -# - -NV_CTRL_GSYNC_ALLOWED = 408 # RW-X -NV_CTRL_GSYNC_ALLOWED_FALSE = 0 -NV_CTRL_GSYNC_ALLOWED_TRUE = 1 - -# -# NV_CTRL_GPU_NVCLOCK_OFFSET - This attribute controls the GPU clock offsets -# (in MHz) used for overclocking per performance level. -# Use the display_mask parameter to specify the performance level. -# -# Note: To enable overclocking support, set the X configuration -# option "Coolbits" to value "8". -# -# This offset can have any integer value between -# NVCTRLAttributeValidValues.u.range.min and -# NVCTRLAttributeValidValues.u.range.max (inclusive). -# -# This attribute is available on GeForce GTX 400 series and later -# Geforce GPUs. -# -NV_CTRL_GPU_NVCLOCK_OFFSET = 409 # RW-G - -# -# NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET - This attribute controls -# the memory transfer rate offsets (in MHz) used for overclocking -# per performance level. -# Use the display_mask parameter to specify the performance level. -# -# Note: To enable overclocking support, set the X configuration -# option "Coolbits" to value "8". -# -# This offset can have any integer value between -# NVCTRLAttributeValidValues.u.range.min and -# NVCTRLAttributeValidValues.u.range.max (inclusive). -# -# This attribute is available on GeForce GTX 400 series and later -# Geforce GPUs. -# -NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET = 410 # RW-G - -# -# NV_CTRL_VIDEO_DECODER_UTILIZATION - Returns the video decoder engine -# utilization as a percentage. -# -NV_CTRL_VIDEO_DECODER_UTILIZATION = 411 # R--G - -# -# NV_CTRL_GPU_OVER_VOLTAGE_OFFSET - This attribute controls -# the overvoltage offset in microvolts (uV). -# -# Note: To enable overvoltage support, set the X configuration -# option "Coolbits" to value "16". -# -# This offset can have any integer value between -# NVCTRLAttributeValidValues.u.range.min and -# NVCTRLAttributeValidValues.u.range.max (inclusive). -# -# This attribute is available on GeForce GTX 400 series and later -# Geforce GPUs. -# - -NV_CTRL_GPU_OVER_VOLTAGE_OFFSET = 412 # RW-G - -# -# NV_CTRL_GPU_CURRENT_CORE_VOLTAGE - This attribute returns the -# GPU's current operating voltage in microvolts (uV). -# -# This attribute is available on GPUs that support -# NV_CTRL_GPU_OVER_VOLTAGE_OFFSET. -# -NV_CTRL_GPU_CURRENT_CORE_VOLTAGE = 413 # R--G - -# -# NV_CTRL_CURRENT_COLOR_SPACE - Returns the current color space of the video -# signal. -# -# This will match NV_CTRL_COLOR_SPACE unless the current mode on this display -# device is an HDMI 2.0 4K@60Hz mode and the display device or GPU does not -# support driving this mode in RGB, in which case YCbCr420 will be returned. -# -NV_CTRL_CURRENT_COLOR_SPACE = 414 # R-DG -NV_CTRL_CURRENT_COLOR_SPACE_RGB = 0 -NV_CTRL_CURRENT_COLOR_SPACE_YCbCr422 = 1 -NV_CTRL_CURRENT_COLOR_SPACE_YCbCr444 = 2 -NV_CTRL_CURRENT_COLOR_SPACE_YCbCr420 = 3 - -# -# NV_CTRL_CURRENT_COLOR_RANGE - Returns the current color range of the video -# signal. -# -NV_CTRL_CURRENT_COLOR_RANGE = 415 # R-DG -NV_CTRL_CURRENT_COLOR_RANGE_FULL = 0 -NV_CTRL_CURRENT_COLOR_RANGE_LIMITED = 1 - -# -# NV_CTRL_SHOW_GSYNC_VISUAL_INDICATOR - when TRUE, OpenGL will indicate when -# G-SYNC is in use for full-screen applications. -# - -NV_CTRL_SHOW_GSYNC_VISUAL_INDICATOR = 416 # RW-X -NV_CTRL_SHOW_GSYNC_VISUAL_INDICATOR_FALSE = 0 -NV_CTRL_SHOW_GSYNC_VISUAL_INDICATOR_TRUE = 1 - -# -# NV_CTRL_THERMAL_COOLER_CURRENT_LEVEL - Returns cooler's current -# operating level. This may fluctuate dynamically. When -# NV_CTRL_GPU_COOLER_MANUAL_CONTROL=TRUE, the driver attempts -# to make this match NV_CTRL_THERMAL_COOLER_LEVEL. When -# NV_CTRL_GPU_COOLER_MANUAL_CONTROL=FALSE, the driver adjusts the -# current level based on the needs of the GPU. -# - -NV_CTRL_THERMAL_COOLER_CURRENT_LEVEL = 417 # R--C - -# -# NV_CTRL_STEREO_SWAP_MODE - This attribute controls the swap mode when -# Quad-Buffered stereo is used. -# NV_CTRL_STEREO_SWAP_MODE_APPLICATION_CONTROL : Stereo swap mode is derived -# from the value of swap interval. -# If it's odd, the per eye swap mode is used. -# If it's even, the per eye pair swap mode is used. -# NV_CTRL_STEREO_SWAP_MODE_PER_EYE : The driver swaps each eye as it is ready. -# NV_CTRL_STEREO_SWAP_MODE_PER_EYE_PAIR : The driver waits for both eyes to -# complete rendering before swapping. -# - -NV_CTRL_STEREO_SWAP_MODE = 418 # RW-X -NV_CTRL_STEREO_SWAP_MODE_APPLICATION_CONTROL = 0 -NV_CTRL_STEREO_SWAP_MODE_PER_EYE = 1 -NV_CTRL_STEREO_SWAP_MODE_PER_EYE_PAIR = 2 - -# -# NV_CTRL_CURRENT_XV_SYNC_TO_DISPLAY_ID - When XVideo Sync To VBlank is -# enabled, this returns the display id of the device currently synched to. -# Returns NV_CTRL_XV_SYNC_TO_DISPLAY_ID_AUTO if no display is currently -# set. -# - -NV_CTRL_CURRENT_XV_SYNC_TO_DISPLAY_ID = 419 # R-- - -# -# NV_CTRL_GPU_FRAMELOCK_FIRMWARE_UNSUPPORTED - Returns true if the -# Quadro Sync card connected to this GPU has a firmware version incompatible -# with this GPU. -# - -NV_CTRL_GPU_FRAMELOCK_FIRMWARE_UNSUPPORTED = 420 # R--G -NV_CTRL_GPU_FRAMELOCK_FIRMWARE_UNSUPPORTED_FALSE = 0 -NV_CTRL_GPU_FRAMELOCK_FIRMWARE_UNSUPPORTED_TRUE = 1 - -# -# NV_CTRL_DISPLAYPORT_CONNECTOR_TYPE - Returns the connector type used by -# a DisplayPort display. -# - -NV_CTRL_DISPLAYPORT_CONNECTOR_TYPE = 421 # R-DG -NV_CTRL_DISPLAYPORT_CONNECTOR_TYPE_UNKNOWN = 0 -NV_CTRL_DISPLAYPORT_CONNECTOR_TYPE_DISPLAYPORT = 1 -NV_CTRL_DISPLAYPORT_CONNECTOR_TYPE_HDMI = 2 -NV_CTRL_DISPLAYPORT_CONNECTOR_TYPE_DVI = 3 -NV_CTRL_DISPLAYPORT_CONNECTOR_TYPE_VGA = 4 - -# -# NV_CTRL_DISPLAYPORT_IS_MULTISTREAM - Returns multi-stream support for -# DisplayPort displays. -# -NV_CTRL_DISPLAYPORT_IS_MULTISTREAM = 422 # R-DG - -# -# NV_CTRL_DISPLAYPORT_SINK_IS_AUDIO_CAPABLE - Returns whether a DisplayPort -# device supports audio. -# -NV_CTRL_DISPLAYPORT_SINK_IS_AUDIO_CAPABLE = 423 # R-DG - -# -# NV_CTRL_GPU_NVCLOCK_OFFSET_ALL_PERFORMANCE_LEVELS - This attribute -# controls the GPU clock offsets (in MHz) used for overclocking. -# The offset is applied to all performance levels. -# -# Note: To enable overclocking support, set the X configuration -# option "Coolbits" to value "8". -# -# This offset can have any integer value between -# NVCTRLAttributeValidValues.u.range.min and -# NVCTRLAttributeValidValues.u.range.max (inclusive). -# -# This attribute is available on GeForce GTX 1000 series and later -# Geforce GPUs. -# -NV_CTRL_GPU_NVCLOCK_OFFSET_ALL_PERFORMANCE_LEVELS = 424 # RW-G - -# -# NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET_ALL_PERFORMANCE_LEVELS - This -# attribute controls the memory transfer rate offsets (in MHz) used -# for overclocking. The offset is applied to all performance levels. -# -# Note: To enable overclocking support, set the X configuration -# option "Coolbits" to value "8". -# -# This offset can have any integer value between -# NVCTRLAttributeValidValues.u.range.min and -# NVCTRLAttributeValidValues.u.range.max (inclusive). -# -# This attribute is available on GeForce GTX 1000 series and later -# Geforce GPUs. -# -NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET_ALL_PERFORMANCE_LEVELS = 425 # RW-G - -# -# NV_CTRL_FRAMELOCK_FIRMWARE_VERSION - Queries the firmware major version of -# the Frame Lock device. -# -# This attribute must be queried through XNVCTRLQueryTargetAttribute() -# using a NV_CTRL_TARGET_TYPE_FRAMELOCK target. -# - -NV_CTRL_FRAMELOCK_FIRMWARE_VERSION = 426 # R--F - -# -# NV_CTRL_FRAMELOCK_FIRMWARE_MINOR_VERSION - Queries the firmware minor -# version of the Frame Lock device. -# -# This attribute must be queried through XNVCTRLQueryTargetAttribute() -# using a NV_CTRL_TARGET_TYPE_FRAMELOCK target. -# - -NV_CTRL_FRAMELOCK_FIRMWARE_MINOR_VERSION = 427 # R--F - -# -# NV_CTRL_SHOW_GRAPHICS_VISUAL_INDICATOR - when TRUE, graphics APIs will -# indicate various runtime information such as flip/blit, vsync status, API -# in use. -# - -NV_CTRL_SHOW_GRAPHICS_VISUAL_INDICATOR = 428 # RW-X -NV_CTRL_SHOW_GRAPHICS_VISUAL_INDICATOR_FALSE = 0 -NV_CTRL_SHOW_GRAPHICS_VISUAL_INDICATOR_TRUE = 1 - -NV_CTRL_LAST_ATTRIBUTE = NV_CTRL_SHOW_GRAPHICS_VISUAL_INDICATOR - -############################################################################ - -# -# String Attributes: -# -# String attributes can be queryied through the XNVCTRLQueryStringAttribute() -# and XNVCTRLQueryTargetStringAttribute() function calls. -# -# String attributes can be set through the XNVCTRLSetStringAttribute() -# function call. (There are currently no string attributes that can be -# set on non-X Screen targets.) -# -# Unless otherwise noted, all string attributes can be queried/set using an -# NV_CTRL_TARGET_TYPE_X_SCREEN target. Attributes that cannot take an -# NV_CTRL_TARGET_TYPE_X_SCREEN target also cannot be queried/set through -# XNVCTRLQueryStringAttribute()/XNVCTRLSetStringAttribute() (Since -# these assume an X Screen target). -# - - -# -# NV_CTRL_STRING_PRODUCT_NAME - the product name on which the -# specified X screen is running, or the product name of the specified -# Frame Lock device. -# -# This attribute may be queried through XNVCTRLQueryTargetStringAttribute() -# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target to -# return the product name of the GPU, or a NV_CTRL_TARGET_TYPE_FRAMELOCK to -# return the product name of the Frame Lock device. -# - -NV_CTRL_STRING_PRODUCT_NAME = 0 # R--GF - -# -# NV_CTRL_STRING_VBIOS_VERSION - the video bios version on the GPU on -# which the specified X screen is running. -# - -NV_CTRL_STRING_VBIOS_VERSION = 1 # R--G - -# -# NV_CTRL_STRING_NVIDIA_DRIVER_VERSION - string representation of the -# NVIDIA driver version number for the NVIDIA X driver in use. -# - -NV_CTRL_STRING_NVIDIA_DRIVER_VERSION = 3 # R--G - -# -# NV_CTRL_STRING_DISPLAY_DEVICE_NAME - name of the display device -# specified in the display_mask argument. -# -# This attribute may be queried through XNVCTRLQueryTargetStringAttribute() -# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. -# - -NV_CTRL_STRING_DISPLAY_DEVICE_NAME = 4 # R-DG - -# -# NV_CTRL_STRING_TV_ENCODER_NAME - not supported -# - -NV_CTRL_STRING_TV_ENCODER_NAME = 5 # not supported - -# -# NV_CTRL_STRING_GVIO_FIRMWARE_VERSION - indicates the version of the -# Firmware on the GVIO device. -# - -NV_CTRL_STRING_GVIO_FIRMWARE_VERSION = 8 # R--I - -# -# NV_CTRL_STRING_GVO_FIRMWARE_VERSION - renamed -# -# NV_CTRL_STRING_GVIO_FIRMWARE_VERSION should be used instead. -# -NV_CTRL_STRING_GVO_FIRMWARE_VERSION = 8 # renamed - -# -# NV_CTRL_STRING_CURRENT_MODELINE - Return the ModeLine currently -# being used by the specified display device. -# -# This attribute may be queried through XNVCTRLQueryTargetStringAttribute() -# using an NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. -# -# The ModeLine string may be prepended with a comma-separated list of -# "token=value" pairs, separated from the ModeLine string by "::". -# This "token=value" syntax is the same as that used in -# NV_CTRL_BINARY_DATA_MODELINES -# - -NV_CTRL_STRING_CURRENT_MODELINE = 9 # R-DG - -# -# NV_CTRL_STRING_ADD_MODELINE - Adds a ModeLine to the specified -# display device. The ModeLine is not added if validation fails. -# -# The ModeLine string should have the same syntax as a ModeLine in -# the X configuration file; e.g., -# -# "1600x1200" 229.5 1600 1664 1856 2160 1200 1201 1204 1250 +HSync +VSync -# - -NV_CTRL_STRING_ADD_MODELINE = 10 # -WDG - -# -# NV_CTRL_STRING_DELETE_MODELINE - Deletes an existing ModeLine -# from the specified display device. The currently selected -# ModeLine cannot be deleted. (This also means you cannot delete -# the last ModeLine.) -# -# The ModeLine string should have the same syntax as a ModeLine in -# the X configuration file; e.g., -# -# "1600x1200" 229.5 1600 1664 1856 2160 1200 1201 1204 1250 +HSync +VSync -# - -NV_CTRL_STRING_DELETE_MODELINE = 11 # -WDG - -# -# NV_CTRL_STRING_CURRENT_METAMODE - Returns the metamode currently -# being used by the specified X screen. The MetaMode string has the -# same syntax as the MetaMode X configuration option, as documented -# in the NVIDIA driver README. -# -# The returned string may be prepended with a comma-separated list of -# "token=value" pairs, separated from the MetaMode string by "::". -# This "token=value" syntax is the same as that used in -# NV_CTRL_BINARY_DATA_METAMODES. -# - -NV_CTRL_STRING_CURRENT_METAMODE = 12 # RW-- -NV_CTRL_STRING_CURRENT_METAMODE_VERSION_1 = NV_CTRL_STRING_CURRENT_METAMODE - -# -# NV_CTRL_STRING_ADD_METAMODE - Adds a MetaMode to the specified -# X Screen. -# -# It is recommended to not use this attribute, but instead use -# NV_CTRL_STRING_OPERATION_ADD_METAMODE. -# - -NV_CTRL_STRING_ADD_METAMODE = 13 # -W-- - -# -# NV_CTRL_STRING_DELETE_METAMODE - Deletes an existing MetaMode from -# the specified X Screen. The currently selected MetaMode cannot be -# deleted. (This also means you cannot delete the last MetaMode). -# The MetaMode string should have the same syntax as the MetaMode X -# configuration option, as documented in the NVIDIA driver README. -# - -NV_CTRL_STRING_DELETE_METAMODE = 14 # -WD-- - -# -# NV_CTRL_STRING_VCSC_PRODUCT_NAME - deprecated -# -# Queries the product name of the VCSC device. -# -# This attribute must be queried through XNVCTRLQueryTargetStringAttribute() -# using a NV_CTRL_TARGET_TYPE_VCSC target. -# - -NV_CTRL_STRING_VCSC_PRODUCT_NAME = 15 # R---V - -# -# NV_CTRL_STRING_VCSC_PRODUCT_ID - deprecated -# -# Queries the product ID of the VCSC device. -# -# This attribute must be queried through XNVCTRLQueryTargetStringAttribute() -# using a NV_CTRL_TARGET_TYPE_VCSC target. -# - -NV_CTRL_STRING_VCSC_PRODUCT_ID = 16 # R---V - -# -# NV_CTRL_STRING_VCSC_SERIAL_NUMBER - deprecated -# -# Queries the unique serial number of the VCS device. -# -# This attribute must be queried through XNVCTRLQueryTargetStringAttribute() -# using a NV_CTRL_TARGET_TYPE_VCSC target. -# - -NV_CTRL_STRING_VCSC_SERIAL_NUMBER = 17 # R---V - -# -# NV_CTRL_STRING_VCSC_BUILD_DATE - deprecated -# -# Queries the date of the VCS device. the returned string is in the following -# format: "Week.Year" -# -# This attribute must be queried through XNVCTRLQueryTargetStringAttribute() -# using a NV_CTRL_TARGET_TYPE_VCSC target. -# - -NV_CTRL_STRING_VCSC_BUILD_DATE = 18 # R---V - -# -# NV_CTRL_STRING_VCSC_FIRMWARE_VERSION - deprecated -# -# Queries the firmware version of the VCS device. -# -# This attribute must be queried through XNVCTRLQueryTargetStringAttribute() -# using a NV_CTRL_TARGET_TYPE_VCSC target. -# - -NV_CTRL_STRING_VCSC_FIRMWARE_VERSION = 19 # R---V - -# -# NV_CTRL_STRING_VCSC_FIRMWARE_REVISION - deprecated -# -# Queries the firmware revision of the VCS device. -# -# This attribute must be queried through XNVCTRLQueryTargetStringAttribute() -# using a NV_CTRL_TARGET_TYPE_VCS target. -# - -NV_CTRL_STRING_VCSC_FIRMWARE_REVISION = 20 # R---V - -# -# NV_CTRL_STRING_VCSC_HARDWARE_VERSION - deprecated -# -# Queries the hardware version of the VCS device. -# -# This attribute must be queried through XNVCTRLQueryTargetStringAttribute() -# using a NV_CTRL_TARGET_TYPE_VCSC target. -# - -NV_CTRL_STRING_VCSC_HARDWARE_VERSION = 21 # R---V - -# -# NV_CTRL_STRING_VCSC_HARDWARE_REVISION - deprecated -# -# Queries the hardware revision of the VCS device. -# -# This attribute must be queried through XNVCTRLQueryTargetStringAttribute() -# using a NV_CTRL_TARGET_TYPE_VCSC target. -# - -NV_CTRL_STRING_VCSC_HARDWARE_REVISION = 22 # R---V - -# -# NV_CTRL_STRING_MOVE_METAMODE - Moves a MetaMode to the specified -# index location. The MetaMode must already exist in the X Screen's -# list of MetaModes (as returned by the NV_CTRL_BINARY_DATA_METAMODES -# attribute). If the index is larger than the number of MetaModes in -# the list, the MetaMode is moved to the end of the list. The -# MetaMode string should have the same syntax as the MetaMode X -# configuration option, as documented in the NVIDIA driver README. - -# The MetaMode string must be prepended with a comma-separated list -# of "token=value" pairs, separated from the MetaMode string by "::". -# Currently, the only valid token is "index", which indicates where -# in the MetaMode list the MetaMode should be moved to. -# -# Other tokens may be added in the future. -# -# E.g., -# "index=5 :: CRT-0: 1024x768 @1024x768 +0+0" -# - -NV_CTRL_STRING_MOVE_METAMODE = 23 # -W-- - -# -# NV_CTRL_STRING_VALID_HORIZ_SYNC_RANGES - returns the valid -# horizontal sync ranges used to perform mode validation for the -# specified display device. The ranges are in the same format as the -# "HorizSync" X config option: -# -# "horizsync-range may be a comma separated list of either discrete -# values or ranges of values. A range of values is two values -# separated by a dash." -# -# The values are in kHz. -# -# Additionally, the string may be prepended with a comma-separated -# list of "token=value" pairs, separated from the HorizSync string by -# "::". Valid tokens: -# -# Token Value -# "source" "edid" - HorizSync is from the display device's EDID -# "xconfig" - HorizSync is from the "HorizSync" entry in -# the Monitor section of the X config file -# "option" - HorizSync is from the "HorizSync" NVIDIA X -# config option -# "builtin" - HorizSync is from NVIDIA X driver builtin -# default values -# -# Additional tokens and/or values may be added in the future. -# -# Example: "source=edid :: 30.000-62.000" -# - -NV_CTRL_STRING_VALID_HORIZ_SYNC_RANGES = 24 # R-DG - -# -# NV_CTRL_STRING_VALID_VERT_REFRESH_RANGES - returns the valid -# vertical refresh ranges used to perform mode validation for the -# specified display device. The ranges are in the same format as the -# "VertRefresh" X config option: -# -# "vertrefresh-range may be a comma separated list of either discrete -# values or ranges of values. A range of values is two values -# separated by a dash." -# -# The values are in Hz. -# -# Additionally, the string may be prepended with a comma-separated -# list of "token=value" pairs, separated from the VertRefresh string by -# "::". Valid tokens: -# -# Token Value -# "source" "edid" - VertRefresh is from the display device's EDID -# "xconfig" - VertRefresh is from the "VertRefresh" entry in -# the Monitor section of the X config file -# "option" - VertRefresh is from the "VertRefresh" NVIDIA X -# config option -# "builtin" - VertRefresh is from NVIDIA X driver builtin -# default values -# -# Additional tokens and/or values may be added in the future. -# -# Example: "source=edid :: 50.000-75.000" -# - -NV_CTRL_STRING_VALID_VERT_REFRESH_RANGES = 25 # R-DG - -# -# NV_CTRL_STRING_SCREEN_RECTANGLE - returns the physical X Screen's -# initial position and size (in absolute coordinates) within the -# desktop as the "token=value" string: "x=#, y=#, width=#, height=#" -# -# Querying this attribute returns success only when Xinerama is enabled -# or the X server ABI is greater than equal to 12. -# - -NV_CTRL_STRING_SCREEN_RECTANGLE = 26 # R--- - -# -# NV_CTRL_STRING_XINERAMA_SCREEN_INFO - renamed -# -# NV_CTRL_STRING_SCREEN_RECTANGLE should be used instead. -# - -NV_CTRL_STRING_XINERAMA_SCREEN_INFO = 26 # renamed - -# -# NV_CTRL_STRING_TWINVIEW_XINERAMA_INFO_ORDER - used to specify the -# order that display devices will be returned via Xinerama when -# nvidiaXineramaInfo is enabled. Follows the same syntax as the -# nvidiaXineramaInfoOrder X config option. -# - -NV_CTRL_STRING_NVIDIA_XINERAMA_INFO_ORDER = 27 # RW-- - -NV_CTRL_STRING_TWINVIEW_XINERAMA_INFO_ORDER = NV_CTRL_STRING_NVIDIA_XINERAMA_INFO_ORDER # for backwards compatibility: - -# -# NV_CTRL_STRING_SLI_MODE - returns a string describing the current -# SLI mode, if any, or FALSE if SLI is not currently enabled. -# -# This string should be used for informational purposes only, and -# should not be used to distinguish between SLI modes, other than to -# recognize when SLI is disabled (FALSE is returned) or -# enabled (the returned string is non-NULL and describes the current -# SLI configuration). -# - -NV_CTRL_STRING_SLI_MODE = 28 # R---*/ - -# -# NV_CTRL_STRING_PERFORMANCE_MODES - returns a string with all the -# performance modes defined for this GPU along with their associated -# NV Clock and Memory Clock values. -# Not all tokens will be reported on all GPUs, and additional tokens -# may be added in the future. -# For backwards compatibility we still provide nvclock, memclock, and -# processorclock those are the same as nvclockmin, memclockmin and -# processorclockmin. -# -# Note: These clock values take into account the offset -# set by clients through NV_CTRL_GPU_NVCLOCK_OFFSET and -# NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET. -# -# Each performance modes are returned as a comma-separated list of -# "token=value" pairs. Each set of performance mode tokens are separated -# by a ";". Valid tokens: -# -# Token Value -# "perf" integer - the Performance level -# "nvclock" integer - the GPU clocks (in MHz) for the perf level -# "nvclockmin" integer - the GPU clocks min (in MHz) for the perf level -# "nvclockmax" integer - the GPU clocks max (in MHz) for the perf level -# "nvclockeditable" integer - if the GPU clock domain is editable -# for the perf level -# "memclock" integer - the memory clocks (in MHz) for the perf level -# "memclockmin" integer - the memory clocks min (in MHz) for the perf level -# "memclockmax" integer - the memory clocks max (in MHz) for the perf level -# "memclockeditable" integer - if the memory clock domain is editable -# for the perf level -# "memtransferrate" integer - the memory transfer rate (in MHz) -# for the perf level -# "memtransferratemin" integer - the memory transfer rate min (in MHz) -# for the perf level -# "memtransferratemax" integer - the memory transfer rate max (in MHz) -# for the perf level -# "memtransferrateeditable" integer - if the memory transfer rate is editable -# for the perf level -# "processorclock" integer - the processor clocks (in MHz) -# for the perf level -# "processorclockmin" integer - the processor clocks min (in MHz) -# for the perf level -# "processorclockmax" integer - the processor clocks max (in MHz) -# for the perf level -# "processorclockeditable" integer - if the processor clock domain is editable -# for the perf level -# -# Example: -# -# perf=0, nvclock=324, nvclockmin=324, nvclockmax=324, nvclockeditable=0, -# memclock=324, memclockmin=324, memclockmax=324, memclockeditable=0, -# memtransferrate=648, memtransferratemin=648, memtransferratemax=648, -# memtransferrateeditable=0 ; -# perf=1, nvclock=324, nvclockmin=324, nvclockmax=640, nvclockeditable=0, -# memclock=810, memclockmin=810, memclockmax=810, memclockeditable=0, -# memtransferrate=1620, memtransferrate=1620, memtransferrate=1620, -# memtransferrateeditable=0 ; -# -# This attribute may be queried through XNVCTRLQueryTargetStringAttribute() -# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. -# - -NV_CTRL_STRING_PERFORMANCE_MODES = 29 # R--G - -# -# NV_CTRL_STRING_VCSC_FAN_STATUS - deprecated -# -# Returns a string with status of all the fans in the Visual Computing System, -# if such a query is supported. Fan information is reported along with its -# tachometer reading (in RPM) and a flag indicating whether the fan has failed -# or not. -# -# Valid tokens: -# -# Token Value -# "fan" integer - the Fan index -# "speed" integer - the tachometer reading of the fan in rpm -# "fail" integer - flag to indicate whether the fan has failed -# -# Example: -# -# fan=0, speed=694, fail=0 ; fan=1, speed=693, fail=0 -# -# This attribute must be queried through XNVCTRLQueryTargetStringAttribute() -# using a NV_CTRL_TARGET_TYPE_VCSC target. -# -# - -NV_CTRL_STRING_VCSC_FAN_STATUS = 30 # R---V - -# -# NV_CTRL_STRING_VCSC_TEMPERATURES - Deprecated -# -# Returns a string with all Temperature readings in the Visual Computing -# System, if such a query is supported. Intake, Exhaust and Board Temperature -# values are reported in Celcius. -# -# Valid tokens: -# -# Token Value -# "intake" integer - the intake temperature for the VCS -# "exhaust" integer - the exhaust temperature for the VCS -# "board" integer - the board temperature of the VCS -# -# Example: -# -# intake=29, exhaust=46, board=41 -# -# This attribute must be queried through XNVCTRLQueryTargetStringAttribute() -# using a NV_CTRL_TARGET_TYPE_VCSC target. -# -# - -NV_CTRL_STRING_VCSC_TEMPERATURES = 31 # R---V - -# -# NV_CTRL_STRING_VCSC_PSU_INFO - Deprecated -# -# Returns a string with all Power Supply Unit related readings in the Visual -# Computing System, if such a query is supported. Current in amperes, Power -# in watts, Voltage in volts and PSU state may be reported. Not all PSU types -# support all of these values, and therefore some readings may be unknown. -# -# Valid tokens: -# -# Token Value -# "current" integer - the current drawn in amperes by the VCS -# "power" integer - the power drawn in watts by the VCS -# "voltage" integer - the voltage reading of the VCS -# "state" integer - flag to indicate whether PSU is operating normally -# -# Example: -# -# current=10, power=15, voltage=unknown, state=normal -# -# This attribute must be queried through XNVCTRLQueryTargetStringAttribute() -# using a NV_CTRL_TARGET_TYPE_VCSC target. -# -# - - -NV_CTRL_STRING_VCSC_PSU_INFO = 32 # R---V - -# -# NV_CTRL_STRING_GVIO_VIDEO_FORMAT_NAME - query the name for the specified -# NV_CTRL_GVIO_VIDEO_FORMAT_*. So that this can be queried with existing -# interfaces, XNVCTRLQueryStringAttribute() should be used, and the video -# format specified in the display_mask field; eg: -# -# XNVCTRLQueryStringAttribute(dpy, -# screen, -# NV_CTRL_GVIO_VIDEO_FORMAT_720P_60_00_SMPTE296, -# NV_CTRL_GVIO_VIDEO_FORMAT_NAME, -# &name); -# - -NV_CTRL_STRING_GVIO_VIDEO_FORMAT_NAME = 33 # R--GI - -# -# NV_CTRL_STRING_GVO_VIDEO_FORMAT_NAME - renamed -# -# NV_CTRL_STRING_GVIO_VIDEO_FORMAT_NAME should be used instead. -# -NV_CTRL_STRING_GVO_VIDEO_FORMAT_NAME = 33 # renamed - -# -# NV_CTRL_STRING_GPU_CURRENT_CLOCK_FREQS - returns a string with the -# associated NV Clock, Memory Clock and Processor Clock values. -# -# Current valid tokens are "nvclock", "nvclockmin", "nvclockmax", -# "memclock", "memclockmin", "memclockmax", "processorclock", -# "processorclockmin" and "processorclockmax". -# Not all tokens will be reported on all GPUs, and additional tokens -# may be added in the future. -# -# Note: These clock values take into account the offset -# set by clients through NV_CTRL_GPU_NVCLOCK_OFFSET and -# NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET. -# -# Clock values are returned as a comma-separated list of -# "token=value" pairs. -# Valid tokens: -# -# Token Value -# "nvclock" integer - the GPU clocks (in MHz) for the perf level -# "nvclockmin" integer - the GPU clocks min (in MHz) for the perf level -# "nvclockmax" integer - the GPU clocks max (in MHz) for the perf level -# "nvclockeditable" integer - if the GPU clock domain is editable -# for the perf level -# "memclock" integer - the memory clocks (in MHz) for the perf level -# "memclockmin" integer - the memory clocks min (in MHz) for the perf level -# "memclockmax" integer - the memory clocks (max in MHz) for the perf level -# "memclockeditable" integer - if the memory clock domain is editable -# for the perf level -# "memtransferrate" integer - the memory transfer rate (in MHz) -# for the perf level -# "memtransferratemin" integer - the memory transfer rate min (in MHz) -# for the perf level -# "memtransferratemax" integer - the memory transfer rate max (in MHz) -# for the perf level -# "memtransferrateeditable" integer - if the memory transfer rate is editable -# for the perf level -# "processorclock" integer - the processor clocks (in MHz) -# for the perf level -# "processorclockmin" integer - the processor clocks min (in MHz) -# for the perf level -# "processorclockmax" integer - the processor clocks max (in MHz) -# for the perf level -# "processorclockeditable" integer - if the processor clock domain is editable -# for the perf level -# -# Example: -# -# nvclock=324, nvclockmin=324, nvclockmax=324, nvclockeditable=0 -# memclock=324, memclockmin=324, memclockmax=324, memclockeditable=0 -# memtrasferrate=628 -# -# This attribute may be queried through XNVCTRLQueryTargetStringAttribute() -# using an NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. -# - -NV_CTRL_STRING_GPU_CURRENT_CLOCK_FREQS = 34 # RW-G - -# -# NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_HARDWARE_REVISION - Returns the -# hardware revision of the 3D Vision Pro transceiver. -# -NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_HARDWARE_REVISION = 35 # R--T - -# -# NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_FIRMWARE_VERSION_A - Returns the -# firmware version of chip A of the 3D Vision Pro transceiver. -# -NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_FIRMWARE_VERSION_A = 36 # R--T - -# -# NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_FIRMWARE_DATE_A - Returns the -# date of the firmware of chip A of the 3D Vision Pro transceiver. -# -NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_FIRMWARE_DATE_A = 37 # R--T - -# -# NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_FIRMWARE_VERSION_B - Returns the -# firmware version of chip B of the 3D Vision Pro transceiver. -# -NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_FIRMWARE_VERSION_B = 38 # R--T - -# -# NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_FIRMWARE_DATE_B - Returns the -# date of the firmware of chip B of the 3D Vision Pro transceiver. -# -NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_FIRMWARE_DATE_B = 39 # R--T - -# -# NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_ADDRESS - Returns the RF address -# of the 3D Vision Pro transceiver. -# -NV_CTRL_STRING_3D_VISION_PRO_TRANSCEIVER_ADDRESS = 40 # R--T - -# -# NV_CTRL_STRING_3D_VISION_PRO_GLASSES_FIRMWARE_VERSION_A - Returns the -# firmware version of chip A of the glasses. -# Use the display_mask parameter to specify the glasses id. -# -NV_CTRL_STRING_3D_VISION_PRO_GLASSES_FIRMWARE_VERSION_A = 41 # R--T - -# -# NV_CTRL_STRING_3D_VISION_PRO_GLASSES_FIRMWARE_DATE_A - Returns the -# date of the firmware of chip A of the glasses. -# Use the display_mask parameter to specify the glasses id. -# -NV_CTRL_STRING_3D_VISION_PRO_GLASSES_FIRMWARE_DATE_A = 42 # R--T - -# -# NV_CTRL_STRING_3D_VISION_PRO_GLASSES_ADDRESS - Returns the RF address -# of the glasses. -# Use the display_mask parameter to specify the glasses id. -# -NV_CTRL_STRING_3D_VISION_PRO_GLASSES_ADDRESS = 43 # R--T - -# -# NV_CTRL_STRING_3D_VISION_PRO_GLASSES_NAME - Controls the name the -# glasses should use. -# Use the display_mask parameter to specify the glasses id. -# Glasses' name should start and end with an alpha-numeric character. -# -NV_CTRL_STRING_3D_VISION_PRO_GLASSES_NAME = 44 # RW-T - -# -# NV_CTRL_STRING_CURRENT_METAMODE_VERSION_2 - Returns the metamode currently -# being used by the specified X screen. The MetaMode string has the same -# syntax as the MetaMode X configuration option, as documented in the NVIDIA -# driver README. Also, see NV_CTRL_BINARY_DATA_METAMODES_VERSION_2 for more -# details on the base syntax. -# -# The returned string may also be prepended with a comma-separated list of -# "token=value" pairs, separated from the MetaMode string by "::". -# -NV_CTRL_STRING_CURRENT_METAMODE_VERSION_2 = 45 # RW-- - -# -# NV_CTRL_STRING_DISPLAY_NAME_TYPE_BASENAME - Returns a type name for the -# display device ("CRT", "DFP", or "TV"). However, note that the determination -# of the name is based on the protocol through which the X driver communicates -# to the display device. E.g., if the driver communicates using VGA ,then the -# basename is "CRT"; if the driver communicates using TMDS, LVDS, or DP, then -# the name is "DFP". -# -NV_CTRL_STRING_DISPLAY_NAME_TYPE_BASENAME = 46 # R-D- - -# -# NV_CTRL_STRING_DISPLAY_NAME_TYPE_ID - Returns the type-based name + ID for -# the display device, e.g. "CRT-0", "DFP-1", "TV-2". If this device is a -# DisplayPort multistream device, then this name will also be prepended with the -# device's port address like so: "DFP-1.0.1.2.3". See -# NV_CTRL_STRING_DISPLAY_NAME_TYPE_BASENAME for more information about the -# construction of type-based names. -# -NV_CTRL_STRING_DISPLAY_NAME_TYPE_ID = 47 # R-D- - -# -# NV_CTRL_STRING_DISPLAY_NAME_DP_GUID - Returns the GUID of the DisplayPort -# display device. e.g. "DP-GUID-f16a5bde-79f3-11e1-b2ae-8b5a8969ba9c" -# -# The display device must be a DisplayPort 1.2 device. -# -NV_CTRL_STRING_DISPLAY_NAME_DP_GUID = 48 # R-D- - -# -# NV_CTRL_STRING_DISPLAY_NAME_EDID_HASH - Returns the SHA-1 hash of the -# display device's EDID in 8-4-4-4-12 UID format. e.g. -# "DPY-EDID-f16a5bde-79f3-11e1-b2ae-8b5a8969ba9c" -# -# The display device must have a valid EDID. -# -NV_CTRL_STRING_DISPLAY_NAME_EDID_HASH = 49 # R-D- - -# -# NV_CTRL_STRING_DISPLAY_NAME_TARGET_INDEX - Returns the current NV-CONTROL -# target ID (name) of the display device. e.g. "DPY-1", "DPY-4" -# -# This name for the display device is not guarenteed to be the same between -# different runs of the X server. -# -NV_CTRL_STRING_DISPLAY_NAME_TARGET_INDEX = 50 # R-D- - -# -# NV_CTRL_STRING_DISPLAY_NAME_RANDR - Returns the RandR output name for the -# display device. e.g. "VGA-1", "DVI-I-0", "DVI-D-3", "LVDS-1", "DP-2", -# "HDMI-3", "eDP-6". This name should match If this device is a DisplayPort -# 1.2 device, then this name will also be prepended with the device's port -# address like so: "DVI-I-3.0.1.2.3" -# -NV_CTRL_STRING_DISPLAY_NAME_RANDR = 51 # R-D- - -# -# NV_CTRL_STRING_GPU_UUID - Returns the UUID of the given GPU. -# -NV_CTRL_STRING_GPU_UUID = 52 # R--G - -# -# NV_CTRL_STRING_GPU_UTILIZATION - Returns the current percentage usage -# of the various components of the GPU. -# -# Current valid tokens are "graphics", "memory", "video" and "PCIe". -# Not all tokens will be reported on all GPUs, and additional tokens -# may be added in the future. -# -# Utilization values are returned as a comma-separated list of -# "token=value" pairs. -# Valid tokens: -# -# Token Value -# "graphics" integer - the percentage usage of graphics engine. -# "memory" integer - the percentage usage of FB. -# "video" integer - the percentage usage of video engine. -# "PCIe" integer - the percentage usage of PCIe bandwidth. -# -# -# Example: -# -# graphics=45, memory=6, video=0, PCIe=0 -# -# This attribute may be queried through XNVCTRLQueryTargetStringAttribute() -# using an NV_CTRL_TARGET_TYPE_GPU. -# -NV_CTRL_STRING_GPU_UTILIZATION = 53 # R--G - -# -# NV_CTRL_STRING_MULTIGPU_MODE - returns a string describing the current -# MULTIGPU mode, if any, or FALSE if MULTIGPU is not currently enabled. -# -NV_CTRL_STRING_MULTIGPU_MODE = 54 # R--- - -# -# NV_CTRL_STRING_PRIME_OUTPUTS_DATA - returns a semicolon delimited list of -# strings that describe all PRIME configured displays. -# -# ex. "xpos=1920, ypos=0, width=1280, height=1024, screen=0;xpos=3200, -# ypos=0, width=800, height=600, screen=0;" -# -NV_CTRL_STRING_PRIME_OUTPUTS_DATA = 55 # R--- - -NV_CTRL_STRING_LAST_ATTRIBUTE = NV_CTRL_STRING_PRIME_OUTPUTS_DATA - -############################################################################ - -# -# Binary Data Attributes: -# -# Binary data attributes can be queryied through the XNVCTRLQueryBinaryData() -# and XNVCTRLQueryTargetBinaryData() function calls. -# -# There are currently no binary data attributes that can be set. -# -# Unless otherwise noted, all Binary data attributes can be queried -# using an NV_CTRL_TARGET_TYPE_X_SCREEN target. Attributes that cannot take -# an NV_CTRL_TARGET_TYPE_X_SCREEN target also cannot be queried through -# XNVCTRLQueryBinaryData() (Since an X Screen target is assumed). -# - - -# -# NV_CTRL_BINARY_DATA_EDID - Returns a display device's EDID information -# data. -# -# This attribute may be queried through XNVCTRLQueryTargetBinaryData() -# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. -# - -NV_CTRL_BINARY_DATA_EDID = 0 # R-DG - -# -# NV_CTRL_BINARY_DATA_MODELINES - Returns a display device's supported -# ModeLines. ModeLines are returned in a buffer, separated by a single -# '\0' and terminated by two consecutive '\0' s like so: -# -# "ModeLine 1\0ModeLine 2\0ModeLine 3\0Last ModeLine\0\0" -# -# This attribute may be queried through XNVCTRLQueryTargetBinaryData() -# using a NV_CTRL_TARGET_TYPE_GPU or NV_CTRL_TARGET_TYPE_X_SCREEN target. -# -# Each ModeLine string may be prepended with a comma-separated list -# of "token=value" pairs, separated from the ModeLine string with a -# "::". Valid tokens: -# -# Token Value -# "source" "xserver" - the ModeLine is from the core X server -# "xconfig" - the ModeLine was specified in the X config file -# "builtin" - the NVIDIA driver provided this builtin ModeLine -# "vesa" - this is a VESA standard ModeLine -# "edid" - the ModeLine was in the display device's EDID -# "nv-control" - the ModeLine was specified via NV-CONTROL -# -# "xconfig-name" - for ModeLines that were specified in the X config -# file, this is the name the X config file -# gave for the ModeLine. -# -# Note that a ModeLine can have several sources; the "source" token -# can appear multiple times in the "token=value" pairs list. -# Additional source values may be specified in the future. -# -# Additional tokens may be added in the future, so it is recommended -# that any token parser processing the returned string from -# NV_CTRL_BINARY_DATA_MODELINES be implemented to gracefully ignore -# unrecognized tokens. -# -# E.g., -# -# "source=xserver, source=vesa, source=edid :: "1024x768_70" 75.0 1024 1048 1184 1328 768 771 777 806 -HSync -VSync" -# "source=xconfig, xconfig-name=1600x1200_60.00 :: "1600x1200_60_0" 161.0 1600 1704 1880 2160 1200 1201 1204 1242 -HSync +VSync" -# - -NV_CTRL_BINARY_DATA_MODELINES = 1 # R-DG - -# -# NV_CTRL_BINARY_DATA_METAMODES - Returns an X Screen's supported -# MetaModes. MetaModes are returned in a buffer separated by a -# single '\0' and terminated by two consecutive '\0' s like so: -# -# "MetaMode 1\0MetaMode 2\0MetaMode 3\0Last MetaMode\0\0" -# -# The MetaMode string should have the same syntax as the MetaMode X -# configuration option, as documented in the NVIDIA driver README. - -# Each MetaMode string may be prepended with a comma-separated list -# of "token=value" pairs, separated from the MetaMode string with -# "::". Currently, valid tokens are: -# -# Token Value -# "id" <number> - the id of this MetaMode; this is stored in -# the Vertical Refresh field, as viewed -# by the XRandR and XF86VidMode X# -# extensions. -# -# "switchable" "yes"/"no" - whether this MetaMode may be switched to via -# ctrl-alt-+/-; Implicit MetaModes (see -# the "IncludeImplicitMetaModes" X -# config option), for example, are not -# normally made available through -# ctrl-alt-+/-. -# -# "source" "xconfig" - the MetaMode was specified in the X -# config file. -# "implicit" - the MetaMode was implicitly added; see the -# "IncludeImplicitMetaModes" X config option -# for details. -# "nv-control" - the MetaMode was added via the NV-CONTROL X -# extension to the currently running X server. -# "RandR" - the MetaMode was modified in response to an -# RandR RRSetCrtcConfig request. -# -# Additional tokens may be added in the future, so it is recommended -# that any token parser processing the returned string from -# NV_CTRL_BINARY_DATA_METAMODES be implemented to gracefully ignore -# unrecognized tokens. -# -# E.g., -# -# "id=50, switchable=yes, source=xconfig :: CRT-0: 1024x768 @1024x768 +0+0" -# - -NV_CTRL_BINARY_DATA_METAMODES = 2 # R-D- -NV_CTRL_BINARY_DATA_METAMODES_VERSION_1 = NV_CTRL_BINARY_DATA_METAMODES - -# -# NV_CTRL_BINARY_DATA_XSCREENS_USING_GPU - Returns the list of X -# screens currently driven by the given GPU. -# -# The format of the returned data is: -# -# 4 CARD32 number of screens -# 4# n CARD32 screen indices -# -# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() -# using a NV_CTRL_TARGET_TYPE_GPU target. This attribute cannot be -# queried using a NV_CTRL_TARGET_TYPE_X_SCREEN. -# - -NV_CTRL_BINARY_DATA_XSCREENS_USING_GPU = 3 # R-DG - -# -# NV_CTRL_BINARY_DATA_GPUS_USED_BY_XSCREEN - Returns the list of GPUs -# currently in use by the given X screen. -# -# The format of the returned data is: -# -# 4 CARD32 number of GPUs -# 4# n CARD32 GPU indices -# - -NV_CTRL_BINARY_DATA_GPUS_USED_BY_XSCREEN = 4 # R--- - -# -# NV_CTRL_BINARY_DATA_GPUS_USING_FRAMELOCK - Returns the list of -# GPUs currently connected to the given frame lock board. -# -# The format of the returned data is: -# -# 4 CARD32 number of GPUs -# 4# n CARD32 GPU indices -# -# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() -# using a NV_CTRL_TARGET_TYPE_FRAMELOCK target. This attribute cannot be -# queried using a NV_CTRL_TARGET_TYPE_X_SCREEN. -# - -NV_CTRL_BINARY_DATA_GPUS_USING_FRAMELOCK = 5 # R-DF - -# -# NV_CTRL_BINARY_DATA_DISPLAY_VIEWPORT - Returns the Display Device's -# viewport box into the given X Screen (in X Screen coordinates.) -# -# The format of the returned data is: -# -# 4 CARD32 Offset X -# 4 CARD32 Offset Y -# 4 CARD32 Width -# 4 CARD32 Height -# - -NV_CTRL_BINARY_DATA_DISPLAY_VIEWPORT = 6 # R-DG - -# -# NV_CTRL_BINARY_DATA_FRAMELOCKS_USED_BY_GPU - Returns the list of -# Framelock devices currently connected to the given GPU. -# -# The format of the returned data is: -# -# 4 CARD32 number of Framelocks -# 4# n CARD32 Framelock indices -# -# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() -# using a NV_CTRL_TARGET_TYPE_GPU target. This attribute cannot be -# queried using a NV_CTRL_TARGET_TYPE_X_SCREEN. -# - -NV_CTRL_BINARY_DATA_FRAMELOCKS_USED_BY_GPU = 7 # R-DG - -# -# NV_CTRL_BINARY_DATA_GPUS_USING_VCSC - Deprecated -# -# Returns the list of GPU devices connected to the given VCS. -# -# The format of the returned data is: -# -# 4 CARD32 number of GPUs -# 4# n CARD32 GPU indices -# -# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() -# using a NV_CTRL_TARGET_TYPE_VCSC target. This attribute cannot be -# queried using a NV_CTRL_TARGET_TYPE_X_SCREEN and cannot be queried using -# a NV_CTRL_TARGET_TYPE_X_GPU -# - -NV_CTRL_BINARY_DATA_GPUS_USING_VCSC = 8 # R-DV - -# -# NV_CTRL_BINARY_DATA_VCSCS_USED_BY_GPU - Deprecated -# -# Returns the VCSC device that is controlling the given GPU. -# -# The format of the returned data is: -# -# 4 CARD32 number of VCS (always 1) -# 4# n CARD32 VCS indices -# -# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() -# using a NV_CTRL_TARGET_TYPE_GPU target. This attribute cannot be -# queried using a NV_CTRL_TARGET_TYPE_X_SCREEN -# - -NV_CTRL_BINARY_DATA_VCSCS_USED_BY_GPU = 9 # R-DG - -# -# NV_CTRL_BINARY_DATA_COOLERS_USED_BY_GPU - Returns the coolers that -# are cooling the given GPU. -# -# The format of the returned data is: -# -# 4 CARD32 number of COOLER -# 4# n CARD32 COOLER indices -# -# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() -# using a NV_CTRL_TARGET_TYPE_GPU target. This attribute cannot be -# queried using a NV_CTRL_TARGET_TYPE_X_SCREEN -# - -NV_CTRL_BINARY_DATA_COOLERS_USED_BY_GPU = 10 # R-DG - -# -# NV_CTRL_BINARY_DATA_GPUS_USED_BY_LOGICAL_XSCREEN - Returns the list of -# GPUs currently driving the given X screen. If Xinerama is enabled, this -# will return all GPUs that are driving any X screen. -# -# The format of the returned data is: -# -# 4 CARD32 number of GPUs -# 4# n CARD32 GPU indices -# - -NV_CTRL_BINARY_DATA_GPUS_USED_BY_LOGICAL_XSCREEN = 11 # R--- - -# -# NV_CTRL_BINARY_DATA_THERMAL_SENSORS_USED_BY_GPU - Returns the sensors that -# are attached to the given GPU. -# -# The format of the returned data is: -# -# 4 CARD32 number of SENSOR -# 4# n CARD32 SENSOR indices -# -# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() -# using a NV_CTRL_TARGET_TYPE_GPU target. This attribute cannot be -# queried using a NV_CTRL_TARGET_TYPE_X_SCREEN -# - -NV_CTRL_BINARY_DATA_THERMAL_SENSORS_USED_BY_GPU = 12 # R--G - -# -# NV_CTRL_BINARY_DATA_GLASSES_PAIRED_TO_3D_VISION_PRO_TRANSCEIVER - Returns -# the id of the glasses that are currently paired to the given -# 3D Vision Pro transceiver. -# -# The format of the returned data is: -# -# 4 CARD32 number of glasses -# 4# n CARD32 id of glasses -# -# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() -# using a NV_CTRL_TARGET_TYPE_3D_VISION_PRO_TRANSCEIVER target. -# -NV_CTRL_BINARY_DATA_GLASSES_PAIRED_TO_3D_VISION_PRO_TRANSCEIVER = 13 # R--T - -# -# NV_CTRL_BINARY_DATA_DISPLAY_TARGETS - Returns all the display devices -# currently connected to any GPU on the X server. -# -# The format of the returned data is: -# -# 4 CARD32 number of display devices -# 4# n CARD32 display device indices -# -# This attribute can only be queried through XNVCTRLQueryTargetBinaryData(). -# - -NV_CTRL_BINARY_DATA_DISPLAY_TARGETS = 14 # R--- - -# -# NV_CTRL_BINARY_DATA_DISPLAYS_CONNECTED_TO_GPU - Returns the list of -# display devices that are connected to the GPU target. -# -# The format of the returned data is: -# -# 4 CARD32 number of display devices -# 4# n CARD32 display device indices -# -# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() -# using a NV_CTRL_TARGET_TYPE_GPU target. -# - -NV_CTRL_BINARY_DATA_DISPLAYS_CONNECTED_TO_GPU = 15 # R--G - -# -# NV_CTRL_BINARY_DATA_METAMODES_VERSION_2 - Returns values similar to -# NV_CTRL_BINARY_DATA_METAMODES(_VERSION_1) but also returns extended syntax -# information to indicate a specific display device, as well as other per- -# display deviceflags as "token=value" pairs. For example: -# -# "DPY-1: 1280x1024 {Stereo=PassiveLeft}, -# DPY-2: 1280x1024 {Stereo=PassiveRight}," -# -# The display device names have the form "DPY-%d", where the integer -# part of the name is the NV-CONTROL target ID for that display device -# for this instance of the X server. Note that display device NV-CONTROL -# target IDs are not guaranteed to be the same from one run of the X -# server to the next. -# - -NV_CTRL_BINARY_DATA_METAMODES_VERSION_2 = 16 # R-D- - -# -# NV_CTRL_BINARY_DATA_DISPLAYS_ENABLED_ON_XSCREEN - Returns the list of -# display devices that are currently scanning out the X screen target. -# -# The format of the returned data is: -# -# 4 CARD32 number of display devices -# 4# n CARD32 display device indices -# -# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() -# using a NV_CTRL_TARGET_TYPE_X_SCREEN target. -# - -NV_CTRL_BINARY_DATA_DISPLAYS_ENABLED_ON_XSCREEN = 17 # R--- - -# -# NV_CTRL_BINARY_DATA_DISPLAYS_ASSIGNED_TO_XSCREEN - Returns the list of -# display devices that are currently assigned the X screen target. -# -# The format of the returned data is: -# -# 4 CARD32 number of display devices -# 4# n CARD32 display device indices -# -# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() -# using a NV_CTRL_TARGET_TYPE_X_SCREEN target. -# - -NV_CTRL_BINARY_DATA_DISPLAYS_ASSIGNED_TO_XSCREEN = 18 # R--- - -# -# NV_CTRL_BINARY_DATA_GPU_FLAGS - Returns a list of flags for the -# given GPU. A flag can, for instance, be a capability which enables -# or disables some features according to the GPU state. -# -# The format of the returned data is: -# -# 4 CARD32 number of GPU flags -# 4# n CARD32 GPU flag -# -# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() -# using a NV_CTRL_TARGET_TYPE_GPU target. -# -NV_CTRL_BINARY_DATA_GPU_FLAGS = 19 # R--- - -# Stereo and display composition transformations are mutually exclusive. -NV_CTRL_BINARY_DATA_GPU_FLAGS_STEREO_DISPLAY_TRANSFORM_EXCLUSIVE = 0 -# Overlay and display composition transformations are mutually exclusive. -NV_CTRL_BINARY_DATA_GPU_FLAGS_OVERLAY_DISPLAY_TRANSFORM_EXCLUSIVE = 1 -# Depth 8 and display composition transformations are mutually exclusive. -NV_CTRL_BINARY_DATA_GPU_FLAGS_DEPTH_8_DISPLAY_TRANSFORM_EXCLUSIVE = 2 - -# -# NV_CTRL_BINARY_DATA_DISPLAYS_ON_GPU - Returns the list of valid -# display devices that can be connected to the GPU target. -# -# The format of the returned data is: -# -# 4 CARD32 number of display devices -# 4# n CARD32 display device indices -# -# This attribute can only be queried through XNVCTRLQueryTargetBinaryData() -# using a NV_CTRL_TARGET_TYPE_GPU target. -# - -NV_CTRL_BINARY_DATA_DISPLAYS_ON_GPU = 20 # R--G - -NV_CTRL_BINARY_DATA_LAST_ATTRIBUTE = NV_CTRL_BINARY_DATA_DISPLAYS_ON_GPU - -############################################################################ - -# -# String Operation Attributes: -# -# These attributes are used with the XNVCTRLStringOperation() -# function; a string is specified as input, and a string is returned -# as output. -# -# Unless otherwise noted, all attributes can be operated upon using -# an NV_CTRL_TARGET_TYPE_X_SCREEN target. -# - - -# -# NV_CTRL_STRING_OPERATION_ADD_METAMODE - provide a MetaMode string -# as input, and returns a string containing comma-separated list of -# "token=value" pairs as output. Currently, the only output token is -# "id", which indicates the id that was assigned to the MetaMode. -# -# All ModeLines referenced in the MetaMode must already exist for -# each display device (as returned by the -# NV_CTRL_BINARY_DATA_MODELINES attribute). -# -# The MetaMode string should have the same syntax as the MetaMode X -# configuration option, as documented in the NVIDIA driver README. -# -# The input string can optionally be prepended with a string of -# comma-separated "token=value" pairs, separated from the MetaMode -# string by "::". Currently, the only valid token is "index" which -# indicates the insertion index for the MetaMode. -# -# E.g., -# -# Input: "index=5 :: 1600x1200+0+0, 1600x1200+1600+0" -# Output: "id=58" -# -# which causes the MetaMode to be inserted at position 5 in the -# MetaMode list (all entries after 5 will be shifted down one slot in -# the list), and the X server's containing mode stores 58 as the -# VRefresh, so that the MetaMode can be uniquely identifed through -# XRandR and XF86VidMode. -# - -NV_CTRL_STRING_OPERATION_ADD_METAMODE = 0 # ---- - -# -# NV_CTRL_STRING_OPERATION_GTF_MODELINE - provide as input a string -# of comma-separated "token=value" pairs, and returns a ModeLine -# string, computed using the GTF formula using the parameters from -# the input string. Valid tokens for the input string are "width", -# "height", and "refreshrate". -# -# E.g., -# -# Input: "width=1600, height=1200, refreshrate=60" -# Output: "160.96 1600 1704 1880 2160 1200 1201 1204 1242 -HSync +VSync" -# -# This operation does not have any impact on any display device's -# modePool, and the ModeLine is not validated; it is simply intended -# for generating ModeLines. -# - -NV_CTRL_STRING_OPERATION_GTF_MODELINE = 1 # --- - -# -# NV_CTRL_STRING_OPERATION_CVT_MODELINE - provide as input a string -# of comma-separated "token=value" pairs, and returns a ModeLine -# string, computed using the CVT formula using the parameters from -# the input string. Valid tokens for the input string are "width", -# "height", "refreshrate", and "reduced-blanking". The -# "reduced-blanking" argument can be "0" or "1", to enable or disable -# use of reduced blanking for the CVT formula. -# -# E.g., -# -# Input: "width=1600, height=1200, refreshrate=60, reduced-blanking=1" -# Output: "130.25 1600 1648 1680 1760 1200 1203 1207 1235 +HSync -VSync" -# -# This operation does not have any impact on any display device's -# modePool, and the ModeLine is not validated; it is simply intended -# for generating ModeLines. -# - -NV_CTRL_STRING_OPERATION_CVT_MODELINE = 2 # --- - -# -# NV_CTRL_STRING_OPERATION_BUILD_MODEPOOL - build a ModePool for the -# specified display device on the specified target (either an X -# screen or a GPU). This is typically used to generate a ModePool -# for a display device on a GPU on which no X screens are present. -# -# Currently, a display device's ModePool is static for the life of -# the X server, so XNVCTRLStringOperation will return FALSE if -# requested to build a ModePool on a display device that already has -# a ModePool. -# -# The string input to BUILD_MODEPOOL may be NULL. If it is not NULL, -# then it is interpreted as a double-colon ("::") separated list -# of "option=value" pairs, where the options and the syntax of their -# values are the X configuration options that impact the behavior of -# modePool construction; namely: -# -# "ModeValidation" -# "HorizSync" -# "VertRefresh" -# "FlatPanelProperties" -# "ExactModeTimingsDVI" -# "UseEdidFreqs" -# -# An example input string might look like: -# -# "ModeValidation=NoVesaModes :: HorizSync=50-110 :: VertRefresh=50-150" -# -# This request currently does not return a string. -# - -NV_CTRL_STRING_OPERATION_BUILD_MODEPOOL = 3 # DG - -# -# NV_CTRL_STRING_OPERATION_GVI_CONFIGURE_STREAMS - Configure the streams- -# to-jack+channel topology for a GVI (Graphics capture board). -# -# The string input to GVI_CONFIGURE_STREAMS may be NULL. If this is the -# case, then the current topology is returned. -# -# If the input string to GVI_CONFIGURE_STREAMS is not NULL, the string -# is interpreted as a semicolon (";") separated list of comma-separated -# lists of "option=value" pairs that define a stream's composition. The -# available options and their values are: -# -# "stream": Defines which stream this comma-separated list describes. -# Valid values are the integers between 0 and -# NV_CTRL_GVI_NUM_STREAMS-1 (inclusive). -# -# "linkN": Defines a jack+channel pair to use for the given link N. -# Valid options are the string "linkN", where N is an integer -# between 0 and NV_CTRL_GVI_MAX_LINKS_PER_STREAM-1 (inclusive). -# Valid values for these options are strings of the form -# "jackX" and/or "jackX.Y", where X is an integer between 0 and -# NV_CTRL_GVI_NUM_JACKS-1 (inclusive), and Y (optional) is an -# integer between 0 and NV_CTRL_GVI_MAX_CHANNELS_PER_JACK-1 -# (inclusive). -# -# An example input string might look like: -# -# "stream=0, link0=jack0, link1=jack1; stream=1, link0=jack2.1" -# -# This example specifies two streams, stream 0 and stream 1. Stream 0 -# is defined to capture link0 data from the first channel (channel 0) of -# BNC jack 0 and link1 data from the first channel of BNC jack 1. The -# second stream (Stream 1) is defined to capture link0 data from channel 1 -# (second channel) of BNC jack 2. -# -# This example shows a possible configuration for capturing 3G input: -# -# "stream=0, link0=jack0.0, link1=jack0.1" -# -# Applications should query the following attributes to determine -# possible combinations: -# -# NV_CTRL_GVI_MAX_STREAMS -# NV_CTRL_GVI_MAX_LINKS_PER_STREAM -# NV_CTRL_GVI_NUM_JACKS -# NV_CTRL_GVI_MAX_CHANNELS_PER_JACK -# -# Note: A jack+channel pair can only be tied to one link/stream. -# -# Upon successful configuration or querying of this attribute, a string -# representing the current topology for all known streams on the device -# will be returned. On failure, NULL is returned. -# -# Note: Setting this attribute may also result in the following -# NV-CONTROL attributes being reset on the GVI device (to ensure -# the configuration remains valid): -# NV_CTRL_GVIO_REQUESTED_VIDEO_FORMAT -# NV_CTRL_GVI_REQUESTED_STREAM_BITS_PER_COMPONENT -# NV_CTRL_GVI_REQUESTED_STREAM_COMPONENT_SAMPLING -# - -NV_CTRL_STRING_OPERATION_GVI_CONFIGURE_STREAMS = 4 # RW-I - -# -# NV_CTRL_STRING_OPERATION_PARSE_METAMODE - Parses the given MetaMode string -# and returns the validated MetaMode string - possibly re-calculating various -# values such as ViewPortIn. If the MetaMode matches an existing MetaMode, -# the details of the existing MetaMode are returned. If the MetaMode fails to -# be parsed, NULL is returned. -# -NV_CTRL_STRING_OPERATION_PARSE_METAMODE = 5 # R--- - -NV_CTRL_STRING_OPERATION_LAST_ATTRIBUTE = NV_CTRL_STRING_OPERATION_PARSE_METAMODE - -############################################################################### -# NV-CONTROL major op numbers. these constants identify the request type -# -X_nvCtrlQueryExtension = 0 -X_nvCtrlQueryAttribute = 2 -X_nvCtrlQueryStringAttribute = 4 -X_nvCtrlQueryValidAttributeValues = 5 -X_nvCtrlSetStringAttribute = 9 -X_nvCtrlSetAttributeAndGetStatus = 19 -X_nvCtrlQueryBinaryData = 20 -X_nvCtrlQueryTargetCount = 24 -X_nvCtrlStringOperation = 25 - -############################################################################### -# various lists that go with attrs, but are handled more compactly -# this way. these lists are indexed by the possible values of their attrs -# and are explained in NVCtrl.h -# - -ATTRIBUTE_TYPE_UNKNOWN = 0 -ATTRIBUTE_TYPE_INTEGER = 1 -ATTRIBUTE_TYPE_BITMASK = 2 -ATTRIBUTE_TYPE_BOOL = 3 -ATTRIBUTE_TYPE_RANGE = 4 -ATTRIBUTE_TYPE_INT_BITS = 5 - -ATTRIBUTE_TYPE_READ = 0x01 -ATTRIBUTE_TYPE_WRITE = 0x02 -ATTRIBUTE_TYPE_DISPLAY = 0x04 -ATTRIBUTE_TYPE_GPU = 0x08 -ATTRIBUTE_TYPE_FRAMELOCK = 0x10 -ATTRIBUTE_TYPE_X_SCREEN = 0x20 -ATTRIBUTE_TYPE_XINERAMA = 0x40 -ATTRIBUTE_TYPE_VCSC = 0x80 - -############################################################################ - -# -# Attribute Targets -# -# Targets define attribute groups. For example, some attributes are only -# valid to set on a GPU, others are only valid when talking about an -# X Screen. Target types are then what is used to identify the target -# group of the attribute you wish to set/query. -# -# Here are the supported target types: -# - -NV_CTRL_TARGET_TYPE_X_SCREEN = 0 -NV_CTRL_TARGET_TYPE_GPU = 1 -NV_CTRL_TARGET_TYPE_FRAMELOCK = 2 -# Visual Computing System - deprecated. To be removed along with all -# VCS-specific attributes in a later release. -NV_CTRL_TARGET_TYPE_VCSC = 3 -NV_CTRL_TARGET_TYPE_GVI = 4 -NV_CTRL_TARGET_TYPE_COOLER = 5 # e.g., fan -NV_CTRL_TARGET_TYPE_THERMAL_SENSOR = 6 -NV_CTRL_TARGET_TYPE_3D_VISION_PRO_TRANSCEIVER = 7 -NV_CTRL_TARGET_TYPE_DISPLAY = 8 - - -############################################################################### -# Targets, to indicate where a command should be executed. -# -class Target(object): - def __init__(self): - self._id = -1 - self._type = -1 - self._name = '' - - def id(self): - return self._id - - def type(self): - return self._type - - def __str__(self): - return '<nVidia {} #{}>'.format(self._name, self.id()) - - -class Gpu(Target): - def __init__(self, ngpu=0): - """Target a GPU""" - super(self.__class__, self).__init__() - self._id = ngpu - self._type = NV_CTRL_TARGET_TYPE_GPU - self._name = 'GPU' - - -class Screen(Target): - def __init__(self, nscr=0): - """Target an X screen""" - super(self.__class__, self).__init__() - self._id = nscr - self._type = NV_CTRL_TARGET_TYPE_X_SCREEN - self._name = 'X screen' - - -class Cooler(Target): - def __init__(self, nfan=0): - """Target a fann""" - super(self.__class__, self).__init__() - self._id = nfan - self._type = NV_CTRL_TARGET_TYPE_COOLER - self._name = 'Cooler' - - -class NVCtrlQueryTargetCountReplyRequest(rq.ReplyRequest): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(X_nvCtrlQueryTargetCount), - rq.RequestLength(), - rq.Card32('target_type'), - ) - _reply = rq.Struct( - rq.ReplyCode(), - rq.Card8('padb1'), - rq.Card16('sequence_number'), - rq.ReplyLength(), - rq.Card32('count'), - rq.Card32('pad4'), - rq.Card32('pad5'), - rq.Card32('pad6'), - rq.Card32('pad7'), - rq.Card32('pad8'), - ) - - -class NVCtrlQueryAttributeReplyRequest(rq.ReplyRequest): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(X_nvCtrlQueryAttribute), - rq.RequestLength(), - rq.Card16('target_id'), - rq.Card16('target_type'), - rq.Card32('display_mask'), - rq.Card32('attr'), - ) - _reply = rq.Struct( - rq.ReplyCode(), - rq.Card8('pad0'), - rq.Card16('sequence_number'), - rq.ReplyLength(), - rq.Card32('flags'), - rq.Int32('value'), - rq.Card32('pad4'), - rq.Card32('pad5'), - rq.Card32('pad6'), - rq.Card32('pad7'), - ) - - -class NVCtrlSetAttributeAndGetStatusReplyRequest(rq.ReplyRequest): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(X_nvCtrlSetAttributeAndGetStatus), - rq.RequestLength(), - rq.Card16('target_id'), - rq.Card16('target_type'), - rq.Card32('display_mask'), - rq.Card32('attr'), - rq.Int32('value') - ) - _reply = rq.Struct( - rq.ReplyCode(), - rq.Card8('pad0'), - rq.Card16('sequence_number'), - rq.ReplyLength(), - rq.Card32('flags'), - rq.Card32('pad3'), - rq.Card32('pad4'), - rq.Card32('pad5'), - rq.Card32('pad6'), - rq.Card32('pad7'), - ) - - -class NVCtrlQueryStringAttributeReplyRequest(rq.ReplyRequest): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(X_nvCtrlQueryStringAttribute), - rq.RequestLength(), - rq.Card16('target_id'), - rq.Card16('target_type'), - rq.Card32('display_mask'), - rq.Card32('attr'), - ) - _reply = rq.Struct( - rq.ReplyCode(), - rq.Card8('pad0'), - rq.Card16('sequence_number'), - rq.ReplyLength(), - rq.Card32('flags'), - rq.Card32('string', 4), - rq.Card32('pad4'), - rq.Card32('pad5'), - rq.Card32('pad6'), - rq.Card32('pad7'), - rq.String8('string'), - ) - - -class NVCtrlQueryValidAttributeValuesReplyRequest(rq.ReplyRequest): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(X_nvCtrlQueryValidAttributeValues), - rq.RequestLength(), - rq.Card16('target_id'), - rq.Card16('target_type'), - rq.Card32('display_mask'), - rq.Card32('attr'), - ) - _reply = rq.Struct( - rq.ReplyCode(), - rq.Card8('pad0'), - rq.Card16('sequence_number'), - rq.ReplyLength(), - rq.Card32('flags'), - rq.Int32('attr_type'), - rq.Int32('min'), - rq.Int32('max'), - rq.Card32('bits'), - rq.Card32('perms'), - ) - - -class NVCtrlQueryBinaryDataReplyRequest(rq.ReplyRequest): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(X_nvCtrlQueryBinaryData), - rq.RequestLength(), - rq.Card16('target_id'), - rq.Card16('target_type'), - rq.Card32('display_mask'), - rq.Card32('attr'), - ) - _reply = rq.Struct( - rq.ReplyCode(), - rq.Card8('pad0'), - rq.Card16('sequence_number'), - rq.ReplyLength(), - rq.Card32('flags'), - rq.Card32('data', 4), - rq.Card32('pad4'), - rq.Card32('pad5'), - rq.Card32('pad6'), - rq.Card32('pad7'), - rq.Binary('data'), - ) - - -class NVCtrlQueryListCard32ReplyRequest(rq.ReplyRequest): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(X_nvCtrlQueryBinaryData), - rq.RequestLength(), - rq.Card16('target_id'), - rq.Card16('target_type'), - rq.Card32('display_mask'), - rq.Card32('attr'), - ) - _reply = rq.Struct( - rq.ReplyCode(), - rq.Card8('pad0'), - rq.Card16('sequence_number'), - rq.ReplyLength(), - rq.Card32('flags'), - rq.Card32('list', 4), - rq.Card32('pad4'), - rq.Card32('pad5'), - rq.Card32('pad6'), - rq.Card32('pad7'), - rq.List('list', rq.Card32), - ) diff --git a/Nagstamon/thirdparty/Xlib/ext/randr.py b/Nagstamon/thirdparty/Xlib/ext/randr.py index 256e3deee..c06e06036 100644 --- a/Nagstamon/thirdparty/Xlib/ext/randr.py +++ b/Nagstamon/thirdparty/Xlib/ext/randr.py @@ -2,39 +2,33 @@ # # Copyright (C) 2006 Mike Meyer <mwm@mired.org> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + """RandR - provide access to the RandR extension information. -This implementation is based off version 1.5 of the XRandR protocol, and may +This implementation is based off version 1.3 of the XRandR protocol, and may not be compatible with other versions. -Version 1.5 of the protocol is documented at: +Version 1.2 of the protocol is documented at: http://cgit.freedesktop.org/xorg/proto/randrproto/tree/randrproto.txt -Version 1.3.1 here: -http://www.x.org/releases/X11R7.5/doc/randrproto/randrproto.txt - """ -from tkinter import W from Xlib import X from Xlib.protocol import rq, structs @@ -123,12 +117,6 @@ BadRRCrtc = 1 BadRRMode = 2 -# Error classes # -class BadRROutputError(Exception): pass - -class BadRRCrtcError(Exception): pass - -class BadRRModeError(Exception): pass # Data Structures # @@ -175,19 +163,6 @@ class BadRRModeError(Exception): pass rq.Card32('matrix33'), ) -MonitorInfo = rq.Struct( - rq.Card32('name'), - rq.Bool('primary'), - rq.Bool('automatic'), - rq.LengthOf('crtcs', 2), - rq.Int16('x'), - rq.Int16('y'), - rq.Card16('width_in_pixels'), - rq.Card16('height_in_pixels'), - rq.Card32('width_in_millimeters'), - rq.Card32('height_in_millimeters'), - rq.List('crtcs', rq.Card32Obj) -) # Requests # @@ -217,7 +192,7 @@ def query_version(self): display=self.display, opcode=self.display.get_extension_major(extname), major_version=1, - minor_version=5, + minor_version=3, ) @@ -469,11 +444,12 @@ class GetOutputInfo(rq.ReplyRequest): rq.Card8('subpixel_order'), rq.LengthOf('crtcs', 2), rq.LengthOf('modes', 2), - rq.Card16('num_preferred'), + rq.LengthOf('preferred', 2), rq.LengthOf('clones', 2), rq.LengthOf('name', 2), rq.List('crtcs', rq.Card32Obj), rq.List('modes', rq.Card32Obj), + rq.List('preferred', rq.Card32Obj), rq.List('clones', rq.Card32Obj), rq.String8('name'), ) @@ -575,18 +551,19 @@ class ChangeOutputProperty(rq.Request): rq.Card8('mode'), rq.Pad(2), rq.LengthOf('value', 4), - rq.PropertyData('value'), + rq.List('value', rq.Card8Obj), ) -def change_output_property(self, output, property, type, mode, value): +def change_output_property(self, output, property, type, format, mode, nUnits): return ChangeOutputProperty( display=self.display, opcode=self.display.get_extension_major(extname), output=output, property=property, type=type, + format=format, mode=mode, - value=value, + nUnits=nUnits, ) @@ -634,17 +611,15 @@ class GetOutputProperty(rq.ReplyRequest): rq.List('value', rq.Card8Obj), ) -def get_output_property(self, output, property, type, long_offset, long_length, delete=False, pending=False): +def get_output_property(self, output, property, type, longOffset, longLength): return GetOutputProperty( display=self.display, opcode=self.display.get_extension_major(extname), output=output, property=property, type=type, - long_offset=long_offset, - long_length=long_length, - delete=delete, - pending=pending, + longOffset=longOffset, + longLength=longLength, ) @@ -666,13 +641,11 @@ class CreateMode(rq.ReplyRequest): rq.Pad(20), ) -def create_mode(self, mode, name): +def create_mode(self): return CreateMode ( display=self.display, opcode=self.display.get_extension_major(extname), window=self, - mode=mode, - name=name, ) @@ -701,7 +674,7 @@ class AddOutputMode(rq.Request): rq.Card32('mode'), ) -def add_output_mode(self, output, mode): +def add_output_mode(self): return AddOutputMode( display=self.display, opcode=self.display.get_extension_major(extname), @@ -719,7 +692,7 @@ class DeleteOutputMode(rq.Request): rq.Card32('mode'), ) -def delete_output_mode(self, output, mode): +def delete_output_mode(self): return DeleteOutputMode( display=self.display, opcode=self.display.get_extension_major(extname), @@ -742,8 +715,6 @@ class GetCrtcInfo(rq.ReplyRequest): rq.Card16('sequence_number'), rq.ReplyLength(), rq.Card32('timestamp'), - rq.Int16('x'), - rq.Int16('y'), rq.Card16('width'), rq.Card16('height'), rq.Card32('mode'), @@ -788,17 +759,14 @@ class SetCrtcConfig(rq.ReplyRequest): rq.Pad(20), ) -def set_crtc_config(self, crtc, config_timestamp, x, y, mode, rotation, outputs, timestamp=X.CurrentTime): +def set_crtc_config(self, crtc, config_timestamp, mode, rotation, timestamp=X.CurrentTime): return SetCrtcConfig ( display=self.display, opcode=self.display.get_extension_major(extname), crtc=crtc, config_timestamp=config_timestamp, - x=x, - y=y, mode=mode, rotation=rotation, - outputs=outputs, timestamp=timestamp, ) @@ -839,7 +807,7 @@ class GetCrtcGamma(rq.ReplyRequest): rq.Card8('status'), rq.Card16('sequence_number'), rq.ReplyLength(), - rq.LengthOf(('red', 'green', 'blue'), 2), + rq.Card16('size'), rq.Pad(22), rq.List('red', rq.Card16Obj), rq.List('green', rq.Card16Obj), @@ -867,15 +835,12 @@ class SetCrtcGamma(rq.Request): rq.List('blue', rq.Card16Obj), ) -def set_crtc_gamma(self, crtc, size, red, green, blue): +def set_crtc_gamma(self, crtc, size): return SetCrtcGamma( display=self.display, opcode=self.display.get_extension_major(extname), crtc=crtc, size=size, - red=red, - green=green, - blue=blue, ) @@ -1098,76 +1063,6 @@ def get_output_primary(self): ) -# Version 1.5 methods - -class GetMonitors(rq.ReplyRequest): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(42), - rq.RequestLength(), - rq.Window('window'), - rq.Bool('is_active'), - rq.Pad(3) - ) - - _reply = rq.Struct( - rq.ReplyCode(), - rq.Pad(1), - rq.Card16('sequence_number'), - rq.ReplyLength(), - rq.Card32('timestamp'), - rq.LengthOf('monitors', 4), - rq.Card32('outputs'), - rq.Pad(12), - rq.List('monitors', MonitorInfo) - ) - - -def get_monitors(self, is_active=True): - return GetMonitors( - display=self.display, - opcode=self.display.get_extension_major(extname), - window=self, - is_active=is_active - ) - -class SetMonitor(rq.Request): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(43), - rq.RequestLength(), - rq.Window('window'), - rq.Object('monitor_info', MonitorInfo) - ) - - -def set_monitor(self, monitor_info): - return SetMonitor( - display=self.display, - opcode=self.display.get_extension_major(extname), - window=self, - monitor_info=monitor_info - ) - - -class DeleteMonitor(rq.Request): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(44), - rq.RequestLength(), - rq.Window('window'), - rq.Card32('name') - ) - - -def delete_monitor(self, name): - return DeleteMonitor( - display=self.display, - opcode=self.display.get_extension_major(extname), - window=self, - name=name - ) - # Events # class ScreenChangeNotify(rq.Event): @@ -1239,6 +1134,8 @@ class OutputPropertyNotify(rq.Event): rq.Card8('state'), rq.Pad(11), ) + + # Initialization # def init(disp, info): @@ -1274,20 +1171,11 @@ def init(disp, info): disp.extension_add_method('display', 'xrandr_get_panning', get_panning) disp.extension_add_method('display', 'xrandr_set_panning', set_panning) - # If the server is running RANDR 1.5+, enable 1.5 compatible methods and events - version = query_version(disp) - if version.major_version == 1 and version.minor_version >= 5: - # version 1.5 compatible - disp.extension_add_method('window', 'xrandr_get_monitors', get_monitors) - disp.extension_add_method('window', 'xrandr_set_monitor', set_monitor) - disp.extension_add_method('window', 'xrandr_delete_monitor', delete_monitor) - - disp.extension_add_event(info.first_event + RRScreenChangeNotify, ScreenChangeNotify) - # add RRNotify events (1 event code with 3 subcodes) - disp.extension_add_subevent(info.first_event + RRNotify, RRNotify_CrtcChange, CrtcChangeNotify) - disp.extension_add_subevent(info.first_event + RRNotify, RRNotify_OutputChange, OutputChangeNotify) - disp.extension_add_subevent(info.first_event + RRNotify, RRNotify_OutputProperty, OutputPropertyNotify) - - disp.extension_add_error(BadRROutput, BadRROutputError) - disp.extension_add_error(BadRRCrtc, BadRRCrtcError) - disp.extension_add_error(BadRRMode, BadRRModeError) + disp.extension_add_event(info.first_event, ScreenChangeNotify) + disp.extension_add_event(info.first_event + 1, CrtcChangeNotify) + disp.extension_add_event(info.first_event + 2, OutputChangeNotify) + disp.extension_add_event(info.first_event + 3, OutputPropertyNotify) + + #disp.extension_add_error(BadRROutput, BadRROutputError) + #disp.extension_add_error(BadRRCrtc, BadRRCrtcError) + #disp.extension_add_error(BadRRMode, BadRRModeError) diff --git a/Nagstamon/thirdparty/Xlib/ext/record.py b/Nagstamon/thirdparty/Xlib/ext/record.py index bb53ec191..3c3f164d1 100644 --- a/Nagstamon/thirdparty/Xlib/ext/record.py +++ b/Nagstamon/thirdparty/Xlib/ext/record.py @@ -2,22 +2,19 @@ # # Copyright (C) 2006 Alex Badea <vamposdecampos@gmail.com> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA from Xlib import X from Xlib.protocol import rq @@ -46,10 +43,8 @@ rq.Card16('first'), rq.Card16('last')) Record_ExtRange = rq.Struct( - rq.Card8('major_range_first'), - rq.Card8('major_range_last'), - rq.Card16('minor_range_first'), - rq.Card16('minor_range_last')) + rq.Object('major_range', Record_Range8), + rq.Object('minor_range', Record_Range16)) Record_Range = rq.Struct( rq.Object('core_requests', Record_Range8), rq.Object('core_replies', Record_Range8), diff --git a/Nagstamon/thirdparty/Xlib/ext/res.py b/Nagstamon/thirdparty/Xlib/ext/res.py deleted file mode 100644 index f2c4e9feb..000000000 --- a/Nagstamon/thirdparty/Xlib/ext/res.py +++ /dev/null @@ -1,288 +0,0 @@ -# Xlib.ext.res -- X-Resource extension module -# -# Copyright (C) 2021 Aleksei Bavshin <alebastr89@gmail.com> -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 51 Franklin Street, -# Fifth Floor, -# Boston, MA 02110-1301 USA - -"""X-Resource extension allows a client to query the X server about its usage -of various resources. - -For detailed description see any of the following documents. -Protocol specification: - https://www.x.org/releases/current/doc/resourceproto/resproto.txt -XCB Protocol specification: - https://cgit.freedesktop.org/xcb/proto/tree/src/res.xml -""" -from Xlib.protocol import rq - -RES_MAJOR_VERSION = 1 -RES_MINOR_VERSION = 2 - -extname = "X-Resource" - -# v1.0 -ResQueryVersion = 0 -ResQueryClients = 1 -ResQueryClientResources = 2 -ResQueryClientPixmapBytes = 3 -# v1.2 -ResQueryClientIds = 4 -ResQueryResourceBytes = 5 - - -class QueryVersion(rq.ReplyRequest): - _request = rq.Struct( - rq.Card8("opcode"), - rq.Opcode(ResQueryVersion), - rq.RequestLength(), - rq.Card8("client_major"), - rq.Card8("client_minor"), - rq.Pad(2)) - _reply = rq.Struct( - rq.ReplyCode(), - rq.Pad(1), - rq.Card16("sequence_number"), - rq.ReplyLength(), - rq.Card16("server_major"), - rq.Card16("server_minor"), - rq.Pad(20)) - - -def query_version(self, client_major=RES_MAJOR_VERSION, - client_minor=RES_MINOR_VERSION): - """ Query the protocol version supported by the X server. - - The client sends the highest supported version to the server and the - server sends the highest version it supports, but no higher than the - requested version.""" - return QueryVersion( - display=self.display, - opcode=self.display.get_extension_major(extname), - client_major=client_major, - client_minor=client_minor) - - -Client = rq.Struct( - rq.Card32("resource_base"), - rq.Card32("resource_mask")) - - -class QueryClients(rq.ReplyRequest): - _request = rq.Struct( - rq.Card8("opcode"), - rq.Opcode(ResQueryClients), - rq.RequestLength()) - _reply = rq.Struct( - rq.ReplyCode(), - rq.Pad(1), - rq.Card16("sequence_number"), - rq.ReplyLength(), - rq.LengthOf("clients", 4), - rq.Pad(20), - rq.List("clients", Client)) - - -def query_clients(self): - """Request the list of all currently connected clients.""" - return QueryClients( - display=self.display, - opcode=self.display.get_extension_major(extname)) - - -Type = rq.Struct( - rq.Card32("resource_type"), - rq.Card32("count")) - - -class QueryClientResources(rq.ReplyRequest): - _request = rq.Struct( - rq.Card8("opcode"), - rq.Opcode(ResQueryClientResources), - rq.RequestLength(), - rq.Card32("client")) - _reply = rq.Struct( - rq.ReplyCode(), - rq.Pad(1), - rq.Card16("sequence_number"), - rq.ReplyLength(), - rq.LengthOf("types", 4), - rq.Pad(20), - rq.List("types", Type)) - - -def query_client_resources(self, client): - """Request the number of resources owned by a client. - - The server will return the counts of each type of resource. - """ - return QueryClientResources( - display=self.display, - opcode=self.display.get_extension_major(extname), - client=client) - - -class QueryClientPixmapBytes(rq.ReplyRequest): - _request = rq.Struct( - rq.Card8("opcode"), - rq.Opcode(ResQueryClientPixmapBytes), - rq.RequestLength(), - rq.Card32("client")) - _reply = rq.Struct( - rq.ReplyCode(), - rq.Pad(1), - rq.Card16("sequence_number"), - rq.ReplyLength(), - rq.Card32("bytes"), - rq.Card32("bytes_overflow"), - rq.Pad(16)) - - -def query_client_pixmap_bytes(self, client): - """Query the pixmap usage of some client. - - The returned number is a sum of memory usage of each pixmap that can be - attributed to the given client. - """ - return QueryClientPixmapBytes( - display=self.display, - opcode=self.display.get_extension_major(extname), - client=client) - - -class SizeOf(rq.LengthOf): - """A SizeOf stores the size in bytes of some other Field whose size - may vary, e.g. List - """ - def __init__(self, name, size, item_size): - rq.LengthOf.__init__(self, name, size) - self.item_size = item_size - - def parse_value(self, length, display): - return length // self.item_size - - -ClientXIDMask = 1 << 0 -LocalClientPIDMask = 1 << 1 - - -ClientIdSpec = rq.Struct( - rq.Card32("client"), - rq.Card32("mask")) - - -ClientIdValue = rq.Struct( - rq.Object("spec", ClientIdSpec), - SizeOf("value", 4, 4), - rq.List("value", rq.Card32Obj)) - - -class QueryClientIds(rq.ReplyRequest): - _request = rq.Struct( - rq.Card8("opcode"), - rq.Opcode(ResQueryClientIds), - rq.RequestLength(), - rq.LengthOf("specs", 4), - rq.List("specs", ClientIdSpec)) - _reply = rq.Struct( - rq.ReplyCode(), - rq.Pad(1), - rq.Card16("sequence_number"), - rq.ReplyLength(), - rq.LengthOf("ids", 4), - rq.Pad(20), - rq.List("ids", ClientIdValue)) - - -def query_client_ids(self, specs): - """Request to identify a given set of clients with some identification method. - - The request sends a list of specifiers that select clients and - identification methods to server. The server then tries to identify the - chosen clients using the identification methods specified for each client. - The server returns IDs for those clients that were successfully identified. - """ - return QueryClientIds( - display=self.display, - opcode=self.display.get_extension_major(extname), - specs=specs) - - -ResourceIdSpec = rq.Struct( - rq.Card32("resource"), - rq.Card32("type")) - - -ResourceSizeSpec = rq.Struct( - # inline struct ResourceIdSpec to work around - # a parser bug with nested objects - rq.Card32("resource"), - rq.Card32("type"), - rq.Card32("bytes"), - rq.Card32("ref_count"), - rq.Card32("use_count")) - - -ResourceSizeValue = rq.Struct( - rq.Object("size", ResourceSizeSpec), - rq.LengthOf("cross_references", 4), - rq.List("cross_references", ResourceSizeSpec)) - - -class QueryResourceBytes(rq.ReplyRequest): - _request = rq.Struct( - rq.Card8("opcode"), - rq.Opcode(ResQueryResourceBytes), - rq.RequestLength(), - rq.Card32("client"), - rq.LengthOf("specs", 4), - rq.List("specs", ResourceIdSpec)) - _reply = rq.Struct( - rq.ReplyCode(), - rq.Pad(1), - rq.Card16("sequence_number"), - rq.ReplyLength(), - rq.LengthOf("sizes", 4), - rq.Pad(20), - rq.List("sizes", ResourceSizeValue)) - - -def query_resource_bytes(self, client, specs): - """Query the sizes of resources from X server. - - The request sends a list of specifiers that selects resources for size - calculation. The server tries to calculate the sizes of chosen resources - and returns an estimate for a resource only if the size could be determined - """ - return QueryResourceBytes( - display=self.display, - opcode=self.display.get_extension_major(extname), - client=client, - specs=specs) - - -def init(disp, info): - disp.extension_add_method("display", "res_query_version", query_version) - disp.extension_add_method("display", "res_query_clients", query_clients) - disp.extension_add_method("display", "res_query_client_resources", - query_client_resources) - disp.extension_add_method("display", "res_query_client_pixmap_bytes", - query_client_pixmap_bytes) - disp.extension_add_method("display", "res_query_client_ids", - query_client_ids) - disp.extension_add_method("display", "res_query_resource_bytes", - query_resource_bytes) diff --git a/Nagstamon/thirdparty/Xlib/ext/screensaver.py b/Nagstamon/thirdparty/Xlib/ext/screensaver.py deleted file mode 100644 index 12f4325c4..000000000 --- a/Nagstamon/thirdparty/Xlib/ext/screensaver.py +++ /dev/null @@ -1,198 +0,0 @@ -# Xlib.ext.screensaver -- X ScreenSaver extension module -# -# Copyright (C) 2022 Vladimir Panteleev <git@cy.md> -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 51 Franklin Street, -# Fifth Floor, -# Boston, MA 02110-1301 USA - -"""This extension allows registering the client as an X screensaver, -or query information about the current screensaver. - -For detailed description see any of the following documents. -Protocol specification: - https://www.x.org/releases/X11R7.7/doc/scrnsaverproto/saver.html -XCB Protocol specification: - https://cgit.freedesktop.org/xcb/proto/tree/src/screensaver.xml - -""" - -from Xlib import X -from Xlib.protocol import rq, structs - -extname = 'MIT-SCREEN-SAVER' - -# Event members -NotifyMask = 1 -CycleMask = 2 - -# Notify state -StateOff = 0 -StateOn = 1 -StateCycle = 2 - -# Notify kind -KindBlanked = 0 -KindInternal = 1 -KindExternal = 2 - -class QueryVersion(rq.ReplyRequest): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(0), - rq.RequestLength(), - rq.Card8('major_version'), - rq.Card8('minor_version'), - rq.Pad(2), - ) - - _reply = rq.Struct( - rq.ReplyCode(), - rq.Pad(1), - rq.Card16('sequence_number'), - rq.ReplyLength(), - rq.Card16('major_version'), - rq.Card16('minor_version'), - rq.Pad(20), - ) - -def query_version(self): - return QueryVersion(display=self.display, - opcode=self.display.get_extension_major(extname), - major_version=1, - minor_version=0) - - -class QueryInfo(rq.ReplyRequest): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(1), - rq.RequestLength(), - rq.Drawable('drawable'), - ) - - _reply = rq.Struct( - rq.ReplyCode(), - rq.Card8('state'), - rq.Card16('sequence_number'), - rq.ReplyLength(), - rq.Window('saver_window'), - rq.Card32('til_or_since'), - rq.Card32('idle'), - rq.Card32('event_mask'), # rq.Set('event_mask', 4, (NotifyMask, CycleMask)), - rq.Card8('kind'), - rq.Pad(7), - ) - -def query_info(self): - return QueryInfo(display=self.display, - opcode=self.display.get_extension_major(extname), - drawable=self, - ) - - -class SelectInput(rq.Request): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(2), - rq.RequestLength(), - rq.Drawable('drawable'), - rq.Card32('event_mask'), # rq.Set('event_mask', 4, (NotifyMask, CycleMask)), - ) - -def select_input(self, mask): - return SelectInput(display=self.display, - opcode=self.display.get_extension_major(extname), - drawable=self, - event_mask=mask, - ) - - -class SetAttributes(rq.Request): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(3), - rq.RequestLength(), - rq.Drawable('drawable'), - rq.Int16('x'), - rq.Int16('y'), - rq.Card16('width'), - rq.Card16('height'), - rq.Card16('border_width'), - rq.Set('window_class', 1, (X.CopyFromParent, X.InputOutput, X.InputOnly)), - rq.Card8('depth'), - rq.Card32('visual'), - structs.WindowValues('attrs'), - ) - -def set_attributes(self, x, y, width, height, border_width, - window_class = X.CopyFromParent, - depth = X.CopyFromParent, - visual = X.CopyFromParent, - onerror = None, - **keys): - return SetAttributes(display=self.display, - onerror = onerror, - opcode=self.display.get_extension_major(extname), - drawable=self, - x = x, - y = y, - width = width, - height = height, - border_width = border_width, - window_class = window_class, - depth = depth, - visual = visual, - attrs = keys) - - -class UnsetAttributes(rq.Request): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(4), - rq.RequestLength(), - rq.Drawable('drawable'), - ) - -def unset_attributes(self, onerror = None): - return UnsetAttributes(display=self.display, - onerror = onerror, - opcode=self.display.get_extension_major(extname), - drawable=self) - - -class Notify(rq.Event): - _code = None - _fields = rq.Struct( - rq.Card8('type'), - rq.Set('state', 1, (StateOff, StateOn, StateCycle)), - rq.Card16('sequence_number'), - rq.Card32('timestamp'), - rq.Window('root'), - rq.Window('window'), - rq.Set('kind', 1, (KindBlanked, KindInternal, KindExternal)), - rq.Bool('forced'), - rq.Pad(14), - ) - -def init(disp, info): - disp.extension_add_method('display', 'screensaver_query_version', query_version) - disp.extension_add_method('drawable', 'screensaver_query_info', query_info) - disp.extension_add_method('drawable', 'screensaver_select_input', select_input) - disp.extension_add_method('drawable', 'screensaver_set_attributes', set_attributes) - disp.extension_add_method('drawable', 'screensaver_unset_attributes', unset_attributes) - - disp.extension_add_event(info.first_event + 0, Notify) diff --git a/Nagstamon/thirdparty/Xlib/ext/security.py b/Nagstamon/thirdparty/Xlib/ext/security.py deleted file mode 100644 index a82101710..000000000 --- a/Nagstamon/thirdparty/Xlib/ext/security.py +++ /dev/null @@ -1,139 +0,0 @@ -# Xlib.ext.security -- SECURITY extension module -# -# Copyright (C) 2010-2013 Outpost Embedded, LLC -# Forest Bond <forest.bond@rapidrollout.com> -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA - -''' -A partial implementation of the SECURITY extension. Support for the -SecurityAuthorizationRevoked event is not implemented. -''' - -from Xlib.protocol import rq - - -extname = 'SECURITY' - - -SecurityClientTrusted = 0 -SecurityClientUntrusted = 1 - -SecurityAuthorizationRevokedMask = 1 - - -AUTHID = rq.Card32 - - -class QueryVersion(rq.ReplyRequest): - _request = rq.Struct(rq.Card8('opcode'), - rq.Opcode(0), - rq.RequestLength(), - rq.Card16('major_version'), - rq.Card16('minor_version') - ) - _reply = rq.Struct(rq.ReplyCode(), - rq.Pad(1), - rq.Card16('sequence_number'), - rq.ReplyLength(), - rq.Card16('major_version'), - rq.Card16('minor_version'), - rq.Pad(20) - ) - - -def query_version(self): - return QueryVersion(display=self.display, - opcode=self.display.get_extension_major(extname), - major_version=1, - minor_version=0) - - -class SecurityGenerateAuthorization(rq.ReplyRequest): - # The order of fields here does not match the specifications I've seen - # online, but it *does* match with the X.org implementation. I guess the - # spec is out-of-date. - _request = rq.Struct(rq.Card8('opcode'), - rq.Opcode(1), - rq.RequestLength(), - rq.LengthOf('auth_proto', 2), - rq.LengthOf('auth_data', 2), - rq.Card32('value_mask'), - rq.String8('auth_proto'), - rq.Binary('auth_data'), - rq.List('values', rq.Card32Obj) - ) - _reply = rq.Struct(rq.ReplyCode(), - rq.Pad(1), - rq.Card16('sequence_number'), - rq.ReplyLength(), - AUTHID('authid'), - rq.LengthOf('auth_data_return', 2), - rq.Pad(18), - rq.Binary('auth_data_return') - ) - - -def generate_authorization(self, auth_proto, auth_data=b'', timeout=None, - trust_level=None, group=None, event_mask=None): - value_mask = 0 - values = [] - if timeout is not None: - value_mask |= 1 - values.append(timeout) - if trust_level is not None: - value_mask |= 2 - values.append(trust_level) - if group is not None: - value_mask |= 4 - values.append(group) - if event_mask is not None: - value_mask |= 8 - values.append(event_mask) - return SecurityGenerateAuthorization(display=self.display, - opcode=self.display.get_extension_major(extname), - value_mask=value_mask, - auth_proto=auth_proto, - auth_data=auth_data, - values=values) - - -class SecurityRevokeAuthorization(rq.Request): - _request = rq.Struct(rq.Card8('opcode'), - rq.Opcode(2), - rq.RequestLength(), - AUTHID('authid') - ) - - -def revoke_authorization(self, authid): - return SecurityRevokeAuthorization(display=self.display, - opcode=self.display.get_extension_major(extname), - authid=authid) - - -def init(disp, info): - disp.extension_add_method('display', - 'security_query_version', - query_version) - disp.extension_add_method('display', - 'security_generate_authorization', - generate_authorization) - disp.extension_add_method('display', - 'security_revoke_authorization', - revoke_authorization) diff --git a/Nagstamon/thirdparty/Xlib/ext/shape.py b/Nagstamon/thirdparty/Xlib/ext/shape.py index 05a517acc..7394e676d 100644 --- a/Nagstamon/thirdparty/Xlib/ext/shape.py +++ b/Nagstamon/thirdparty/Xlib/ext/shape.py @@ -1,297 +1,334 @@ -# Automatically generated file; DO NOT EDIT. -# Generated from: /usr/share/xcb/shape.xml - +# Xlib.ext.shape -- SHAPE extension module +# +# Copyright (C) 2002 Jeffrey Boser <verin@lvcm.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + +# Constants to use +# +# Regions of a window +ShapeBounding = 0 # the 'edge' of a shaped window +ShapeClip = 1 # the clipping region +# Shape Operations +ShapeSet = 0 # Set the region unmodified (dest=src) +ShapeUnion = 1 # Add the new region to the old (dest=src|dest) +ShapeIntersect = 2 # Use the intersection (dest=src&dest) +ShapeSubtract = 3 # remove region (dest = dest - intersect) +ShapeInvert = 4 # opposite of subtract (dest = src - intersect) +# Events +ShapeNotifyMask = (1<<0) #a keypress mask? +ShapeNotify = 0 #still unsure of these values + +# How to Use +# The basic functions that change the shapes of things are: +# shape_rectangles (uses a set of rectangles as the source) +# operation, region, ordering, rects +# shape_mask (uses a bitmap as the source) +# operation, region, x_offset, y_offset, bitmap +# shape_combine (uses a window as the source) +# operation, src_region, dest_region, x_offset, y_offset, src_window +# shape_offset (moves the region) +# region, x_offset, y_offset +# The functions to find stuff out (these three return mappings of field/values): +# shape_query_version (shape extension version) +# major_version, minor_version +# shape_query_extents (rectangle boundaries of a window's regions) +# clip_shaped, clip_x, clip_y, clip_width, clip_height, +# bounding_shaped, bounding_x, bounding_y, bounding_width, bounding_height +# shape_input_selected (if the window products shapenotify events) +# enabled +# shape_get_rectangles (the rectangles set by shape_rectangles) +# ordering, rects +# And to turn on shape notify events: +# shape_select_input +# enable + + + +from Xlib import X from Xlib.protocol import rq, structs - extname = 'SHAPE' -OP = rq.Card8 - -class SO: - Set = 0 - Union = 1 - Intersect = 2 - Subtract = 3 - Invert = 4 - -class SK: - Bounding = 0 - Clip = 1 - Input = 2 - -class KIND(rq.Set): - - def __init__(self, name): - super(KIND, self).__init__(name, 1, - values=(SK.Bounding, - SK.Clip, - SK.Input)) +class QueryVersion(rq.ReplyRequest): + _request = rq.Struct( + rq.Card8('opcode'), + rq.Opcode(0), + rq.RequestLength(), + ) + _reply = rq.Struct( + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Card16('major_version'), + rq.Card16('minor_version'), + rq.Pad(20), + ) + +def query_version(self): + return QueryVersion( + display = self.display, + opcode = self.display.get_extension_major(extname), + ) -class NotifyEventData(rq.Event): - _code = None - _fields = rq.Struct( - rq.Card8('type'), - KIND('shape_kind'), - rq.Card16('sequence_number'), - rq.Window('affected_window'), - rq.Int16('extents_x'), - rq.Int16('extents_y'), - rq.Card16('extents_width'), - rq.Card16('extents_height'), - rq.Card32('server_time'), - rq.Card8('shaped'), - rq.Pad(11), - ) -class QueryVersion(rq.ReplyRequest): +class Rectangles(rq.Request): _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(0), - rq.RequestLength(), - ) + rq.Card8('opcode'), + rq.Opcode(1), + rq.RequestLength(), + rq.Card8('operation'), + rq.Set('region', 1, (ShapeBounding, ShapeClip)), + rq.Card8('ordering'), + rq.Pad(1), + rq.Window('window'), + rq.Int16('x'), + rq.Int16('y'), + rq.List('rectangles', structs.Rectangle), + ) + +def rectangles(self, region, operation, ordering, x, y, rectangles): + Rectangles( + display = self.display, + opcode = self.display.get_extension_major(extname), + operation = operation, + region = region, + ordering = ordering, + window = self.id, + x = x, + y = y, + rectangles = rectangles, + ) - _reply = rq.Struct( - rq.ReplyCode(), - rq.Pad(1), - rq.Card16('sequence_number'), - rq.ReplyLength(), - rq.Card16('major_version'), - rq.Card16('minor_version'), - ) -class Rectangles(rq.Request): +class Mask(rq.Request): _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(1), - rq.RequestLength(), - OP('operation'), - KIND('destination_kind'), - rq.Card8('ordering'), - rq.Pad(1), - rq.Window('destination_window'), - rq.Int16('x_offset'), - rq.Int16('y_offset'), - rq.List('rectangles', structs.Rectangle, pad=0), - ) + rq.Card8('opcode'), + rq.Opcode(2), + rq.RequestLength(), + rq.Card8('operation'), + rq.Set('region', 1, (ShapeBounding, ShapeClip)), + rq.Pad(2), + rq.Window('window'), + rq.Int16('x'), + rq.Int16('y'), + rq.Pixmap('source', (X.NONE, )), + ) + +def mask(self, operation, region, x, y, source): + Mask(display = self.display, + opcode = self.display.get_extension_major(extname), + window = self.id, + operation = operation, + region = region, + x = x, + y = y, + source = source, + ) -class Mask(rq.Request): +class Combine(rq.Request): _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(2), - rq.RequestLength(), - OP('operation'), - KIND('destination_kind'), - rq.Pad(2), - rq.Window('destination_window'), - rq.Int16('x_offset'), - rq.Int16('y_offset'), - rq.Pixmap('source_bitmap'), - ) + rq.Card8('opcode'), + rq.Opcode(3), + rq.RequestLength(), + rq.Card8('operation'), + rq.Set('dest_region', 1, (ShapeBounding, ShapeClip)), + rq.Set('source_region', 1, (ShapeBounding, ShapeClip)), + rq.Pad(1), + rq.Window('dest'), + rq.Int16('x'), + rq.Int16('y'), + rq.Window('source'), + ) + +def combine(self, operation, region, source, source_region, x, y): + Combine( + display = self.display, + opcode = self.display.get_extension_major(extname), + operation = operation, + dest_region = region, + source_region = source_region, + dest = self.id, + x = x, + y = y, + source = source, + ) -class Combine(rq.Request): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(3), - rq.RequestLength(), - OP('operation'), - KIND('destination_kind'), - KIND('source_kind'), - rq.Pad(1), - rq.Window('destination_window'), - rq.Int16('x_offset'), - rq.Int16('y_offset'), - rq.Window('source_window'), - ) class Offset(rq.Request): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(4), - rq.RequestLength(), - KIND('destination_kind'), - rq.Pad(3), - rq.Window('destination_window'), - rq.Int16('x_offset'), - rq.Int16('y_offset'), - ) + rq.Card8('opcode'), + rq.Opcode(4), + rq.RequestLength(), + rq.Set('region', 1, (ShapeBounding, ShapeClip)), + rq.Pad(3), + rq.Window('window'), + rq.Int16('x'), + rq.Int16('y'), + ) + +def offset(self, region, x, y): + Offset( + display = self.display, + opcode = self.display.get_extension_major(extname), + region = region, + window = self.id, + x = x, + y = y, + ) -class QueryExtents(rq.ReplyRequest): + +class QueryExtents(rq.ReplyRequest): _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(5), - rq.RequestLength(), - rq.Window('destination_window'), - ) + rq.Card8('opcode'), + rq.Opcode(5), + rq.RequestLength(), + rq.Window('window'), + ) _reply = rq.Struct( - rq.ReplyCode(), - rq.Pad(1), - rq.Card16('sequence_number'), - rq.ReplyLength(), - rq.Card8('bounding_shaped'), - rq.Card8('clip_shaped'), - rq.Pad(2), - rq.Int16('bounding_shape_extents_x'), - rq.Int16('bounding_shape_extents_y'), - rq.Card16('bounding_shape_extents_width'), - rq.Card16('bounding_shape_extents_height'), - rq.Int16('clip_shape_extents_x'), - rq.Int16('clip_shape_extents_y'), - rq.Card16('clip_shape_extents_width'), - rq.Card16('clip_shape_extents_height'), - ) + rq.ReplyCode(), + rq.Pad(1), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Bool('bounding_shaped'), + rq.Bool('clip_shaped'), + rq.Pad(2), + rq.Int16('bounding_x'), + rq.Int16('bounding_y'), + rq.Card16('bounding_width'), + rq.Card16('bounding_height'), + rq.Int16('clip_x'), + rq.Int16('clip_y'), + rq.Card16('clip_width'), + rq.Card16('clip_height'), + rq.Pad(4), + ) + +def query_extents(self): + return QueryExtents( + display = self.display, + opcode = self.display.get_extension_major(extname), + window = self.id, + ) -class SelectInput(rq.Request): +class SelectInput(rq.Request): _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(6), - rq.RequestLength(), - rq.Window('destination_window'), - rq.Card8('enable'), - rq.Pad(3), - ) + rq.Card8('opcode'), + rq.Opcode(6), + rq.RequestLength(), + rq.Window('window'), + rq.Bool('enable'), + rq.Pad(3), + ) + +def select_input(self, enable = 1): + SelectInput( + display = self.display, + opcode = self.display.get_extension_major(extname), + window = self.id, + enable = enable, + ) -class InputSelected(rq.ReplyRequest): +class InputSelected(rq.ReplyRequest): _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(7), - rq.RequestLength(), - rq.Window('destination_window'), - ) + rq.Card8('opcode'), + rq.Opcode(7), + rq.RequestLength(), + rq.Window('window'), + ) _reply = rq.Struct( - rq.ReplyCode(), - rq.Card8('enabled'), - rq.Card16('sequence_number'), - rq.ReplyLength(), - ) + rq.ReplyCode(), + rq.Bool('enabled'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.Pad(24), + ) + +def input_selected(self): + reply = InputSelected( + display = self.display, + opcode = self.display.get_extension_major(extname), + window = self.id, + ) + return reply.enabled -class GetRectangles(rq.ReplyRequest): +class GetRectangles(rq.ReplyRequest): _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(8), - rq.RequestLength(), - rq.Window('window'), - KIND('source_kind'), - rq.Pad(3), - ) + rq.Card8('opcode'), + rq.Opcode(8), + rq.RequestLength(), + rq.Window('window'), + rq.Set('region', 1, (ShapeBounding, ShapeClip)), + rq.Pad(3), + ) _reply = rq.Struct( - rq.ReplyCode(), - rq.Card8('ordering'), - rq.Card16('sequence_number'), - rq.ReplyLength(), - rq.LengthOf('rectangles', 4), - rq.Pad(20), - rq.List('rectangles', structs.Rectangle, pad=0), - ) - -class Event: - # Sub events. - Notify = 0 - -def combine(self, operation, destination_kind, source_kind, x_offset, y_offset): - Combine( - display=self.display, - opcode=self.display.get_extension_major(extname), - source_window=self, - operation=operation, - destination_kind=destination_kind, - source_kind=source_kind, - x_offset=x_offset, - y_offset=y_offset, - ) - -def get_rectangles(self, source_kind): + rq.ReplyCode(), + rq.Card8('ordering'), + rq.Card16('sequence_number'), + rq.ReplyLength(), + rq.LengthOf('rectangles', 4), + rq.Pad(20), + rq.List('rectangles', structs.Rectangle), + ) + +def get_rectangles(self, region): return GetRectangles( - display=self.display, - opcode=self.display.get_extension_major(extname), - window=self, - source_kind=source_kind, - ) - -def input_selected(self, ): - return InputSelected( - display=self.display, - opcode=self.display.get_extension_major(extname), - destination_window=self, - ) - -def mask(self, operation, destination_kind, x_offset, y_offset, source_bitmap): - Mask( - display=self.display, - opcode=self.display.get_extension_major(extname), - destination_window=self, - operation=operation, - destination_kind=destination_kind, - x_offset=x_offset, - y_offset=y_offset, - source_bitmap=source_bitmap, - ) - -def offset(self, destination_kind, x_offset, y_offset): - Offset( - display=self.display, - opcode=self.display.get_extension_major(extname), - destination_window=self, - destination_kind=destination_kind, - x_offset=x_offset, - y_offset=y_offset, - ) - -def query_extents(self, ): - return QueryExtents( - display=self.display, - opcode=self.display.get_extension_major(extname), - destination_window=self, - ) + display = self.display, + opcode = self.display.get_extension_major(extname), + window = self.id, + region = region, + ) -def query_version(self, ): - return QueryVersion( - display=self.display, - opcode=self.display.get_extension_major(extname), - ) -def rectangles(self, operation, destination_kind, ordering, x_offset, y_offset, rectangles): - Rectangles( - display=self.display, - opcode=self.display.get_extension_major(extname), - destination_window=self, - operation=operation, - destination_kind=destination_kind, - ordering=ordering, - x_offset=x_offset, - y_offset=y_offset, - rectangles=rectangles, - ) - -def select_input(self, enable): - SelectInput( - display=self.display, - opcode=self.display.get_extension_major(extname), - destination_window=self, - enable=enable, - ) +class ShapeNotify(rq.Event): + _code = None + _fields = rq.Struct( rq.Card8('type'), + rq.Set('region', 1, (ShapeBounding, ShapeClip)), + rq.Card16('sequence_number'), + rq.Window('window'), + rq.Int16('x'), + rq.Int16('y'), + rq.Card16('width'), + rq.Card16('height'), + rq.Card32('time'), + rq.Bool('shaped'), + rq.Pad(11), + ) def init(disp, info): - disp.extension_add_method('window', 'shape_combine', combine) - disp.extension_add_method('window', 'shape_get_rectangles', get_rectangles) - disp.extension_add_method('window', 'shape_input_selected', input_selected) - disp.extension_add_method('window', 'shape_mask', mask) - disp.extension_add_method('window', 'shape_offset', offset) - disp.extension_add_method('window', 'shape_query_extents', query_extents) - disp.extension_add_method('display', 'shape_query_version', query_version) - disp.extension_add_method('window', 'shape_rectangles', rectangles) - disp.extension_add_method('window', 'shape_select_input', select_input) - disp.extension_add_event(info.first_event + Event.Notify, NotifyEventData, 'ShapeNotify') - + disp.extension_add_method('display', 'shape_query_version', query_version ) + disp.extension_add_method('window', 'shape_rectangles', rectangles ) + disp.extension_add_method('window', 'shape_mask', mask ) + disp.extension_add_method('window', 'shape_combine', combine ) + disp.extension_add_method('window', 'shape_offset', offset ) + disp.extension_add_method('window', 'shape_query_extents', query_extents ) + disp.extension_add_method('window', 'shape_select_input', select_input ) + disp.extension_add_method('window', 'shape_input_selected', input_selected ) + disp.extension_add_method('window', 'shape_get_rectangles', get_rectangles ) + + disp.extension_add_event(info.first_event, ShapeNotify) diff --git a/Nagstamon/thirdparty/Xlib/ext/xfixes.py b/Nagstamon/thirdparty/Xlib/ext/xfixes.py deleted file mode 100644 index 59f9277b4..000000000 --- a/Nagstamon/thirdparty/Xlib/ext/xfixes.py +++ /dev/null @@ -1,201 +0,0 @@ -# Xlib.ext.xfixes -- XFIXES extension module -# -# Copyright (C) 2010-2011 Outpost Embedded, LLC -# Forest Bond <forest.bond@rapidrollout.com> -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA - -''' -A partial implementation of the XFIXES extension. Only the HideCursor and -ShowCursor requests and SelectionNotify events are provided. -''' - -from Xlib import X -from Xlib.protocol import rq, structs - -extname = 'XFIXES' - -XFixesSelectionNotify = 0 -XFixesCursorNotify = 1 - -XFixesSetSelectionOwnerNotifyMask = (1 << 0) -XFixesSelectionWindowDestroyNotifyMask = (1 << 1) -XFixesSelectionClientCloseNotifyMask = (1 << 2) -XFixesDisplayCursorNotifyMask = (1 << 0) - -XFixesSetSelectionOwnerNotify = 0 -XFixesSelectionWindowDestroyNotify = 1 -XFixesSelectionClientCloseNotify = 2 -XFixesDisplayCursorNotify = 0 - -class QueryVersion(rq.ReplyRequest): - _request = rq.Struct(rq.Card8('opcode'), - rq.Opcode(0), - rq.RequestLength(), - rq.Card32('major_version'), - rq.Card32('minor_version') - ) - _reply = rq.Struct(rq.ReplyCode(), - rq.Pad(1), - rq.Card16('sequence_number'), - rq.ReplyLength(), - rq.Card32('major_version'), - rq.Card32('minor_version'), - rq.Pad(16) - ) - - -def query_version(self): - return QueryVersion(display=self.display, - opcode=self.display.get_extension_major(extname), - major_version=4, - minor_version=0) - - -class HideCursor(rq.Request): - _request = rq.Struct(rq.Card8('opcode'), - rq.Opcode(29), - rq.RequestLength(), - rq.Window('window') - ) - -def hide_cursor(self): - HideCursor(display=self.display, - opcode=self.display.get_extension_major(extname), - window=self) - - -class ShowCursor(rq.Request): - _request = rq.Struct(rq.Card8('opcode'), - rq.Opcode(30), - rq.RequestLength(), - rq.Window('window') - ) - - -def show_cursor(self): - ShowCursor(display=self.display, - opcode=self.display.get_extension_major(extname), - window=self) - -class SelectSelectionInput(rq.Request): - _request = rq.Struct(rq.Card8('opcode'), - rq.Opcode(2), - rq.RequestLength(), - rq.Window('window'), - rq.Card32('selection'), - rq.Card32('mask') - ) - -def select_selection_input(self, window, selection, mask): - return SelectSelectionInput(opcode=self.display.get_extension_major(extname), - display=self.display, - window=window, - selection=selection, - mask=mask) - - -class SelectionNotify(rq.Event): - _code = None - _fields = rq.Struct(rq.Card8('type'), - rq.Card8('sub_code'), - rq.Card16('sequence_number'), - rq.Window('window'), - rq.Window('owner'), - rq.Card32('selection'), - rq.Card32('timestamp'), - rq.Card32('selection_timestamp'), - rq.Pad(8)) - - -class SetSelectionOwnerNotify(SelectionNotify): - pass - - -class SelectionWindowDestroyNotify(SelectionNotify): - pass - - -class SelectionClientCloseNotify(SelectionNotify): - pass - - -class SelectCursorInput(rq.Request): - _request = rq.Struct(rq.Card8('opcode'), - rq.Opcode(3), - rq.RequestLength(), - rq.Window('window'), - rq.Card32('mask') - ) - -def select_cursor_input(self, window, mask): - return SelectCursorInput(opcode=self.display.get_extension_major(extname), - display=self.display, - window=window, - cursor_serial=0, - mask=mask) - - -class GetCursorImage(rq.ReplyRequest): - _request = rq.Struct(rq.Card8('opcode'), - rq.Opcode(4), - rq.RequestLength() - ) - _reply = rq.Struct(rq.ReplyCode(), - rq.Pad(1), - rq.Card16('sequence_number'), - rq.ReplyLength(), - rq.Int16('x'), - rq.Int16('y'), - rq.Card16('width'), - rq.Card16('height'), - rq.Card16('xhot'), - rq.Card16('yhot'), - rq.Card32('cursor_serial'), - rq.Pad(8), - rq.List('cursor_image', rq.Card32) - ) - -def get_cursor_image(self, window): - return GetCursorImage(opcode=self.display.get_extension_major(extname), - display=self.display, - ) - - -class DisplayCursorNotify(rq.Event): - _code = None - _fields = rq.Struct(rq.Card8('type'), - rq.Card8('sub_code'), - rq.Card16('sequence_number'), - rq.Window('window'), - rq.Card32('cursor_serial'), - rq.Card32('timestamp')) - - -def init(disp, info): - disp.extension_add_method('display', 'xfixes_select_selection_input', select_selection_input) - disp.extension_add_method('display', 'xfixes_query_version', query_version) - disp.extension_add_method('window', 'xfixes_hide_cursor', hide_cursor) - disp.extension_add_method('window', 'xfixes_show_cursor', show_cursor) - disp.extension_add_method('display', 'xfixes_select_cursor_input', select_cursor_input) - disp.extension_add_method('display', 'xfixes_get_cursor_image', get_cursor_image) - - disp.extension_add_subevent(info.first_event + XFixesSelectionNotify, XFixesSetSelectionOwnerNotify, SetSelectionOwnerNotify) - disp.extension_add_subevent(info.first_event + XFixesSelectionNotify, XFixesSelectionWindowDestroyNotify, SelectionWindowDestroyNotify) - disp.extension_add_subevent(info.first_event + XFixesSelectionNotify, XFixesSelectionClientCloseNotify, SelectionClientCloseNotify) - disp.extension_add_subevent(info.first_event + XFixesCursorNotify, XFixesDisplayCursorNotify, DisplayCursorNotify) diff --git a/Nagstamon/thirdparty/Xlib/ext/xinerama.py b/Nagstamon/thirdparty/Xlib/ext/xinerama.py index f0546707b..2f64e8112 100644 --- a/Nagstamon/thirdparty/Xlib/ext/xinerama.py +++ b/Nagstamon/thirdparty/Xlib/ext/xinerama.py @@ -2,22 +2,20 @@ # # Copyright (C) 2006 Mike Meyer <mwm@mired.org> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + """Xinerama - provide access to the Xinerama extension information. @@ -30,7 +28,7 @@ this is untested because I don't have a server that implements it. The functions loosely follow the libXineram functions. Mostly, they -return an rq.Struct in lieu of passing in pointers that get data from +return an rq.Struct in lieue of passing in pointers that get data from the rq.Struct crammed into them. The exception is isActive, which returns the state information - because that's what libXinerama does.""" diff --git a/Nagstamon/thirdparty/Xlib/ext/xinput.py b/Nagstamon/thirdparty/Xlib/ext/xinput.py deleted file mode 100644 index f92180647..000000000 --- a/Nagstamon/thirdparty/Xlib/ext/xinput.py +++ /dev/null @@ -1,777 +0,0 @@ -# Xlib.ext.xinput -- XInput extension module -# -# Copyright (C) 2012 Outpost Embedded, LLC -# Forest Bond <forest.bond@rapidrollout.com> -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA - -''' -A very incomplete implementation of the XInput extension. -''' - -import sys -import array -import struct - -# Python 2/3 compatibility. -from six import integer_types - -from Xlib.protocol import rq -from Xlib import X - - -extname = 'XInputExtension' - -PropertyDeleted = 0 -PropertyCreated = 1 -PropertyModified = 2 - -NotifyNormal = 0 -NotifyGrab = 1 -NotifyUngrab = 2 -NotifyWhileGrabbed = 3 -NotifyPassiveGrab = 4 -NotifyPassiveUngrab = 5 - -NotifyAncestor = 0 -NotifyVirtual = 1 -NotifyInferior = 2 -NotifyNonlinear = 3 -NotifyNonlinearVirtual = 4 -NotifyPointer = 5 -NotifyPointerRoot = 6 -NotifyDetailNone = 7 - -GrabtypeButton = 0 -GrabtypeKeycode = 1 -GrabtypeEnter = 2 -GrabtypeFocusIn = 3 -GrabtypeTouchBegin = 4 - -AnyModifier = (1 << 31) -AnyButton = 0 -AnyKeycode = 0 - -AsyncDevice = 0 -SyncDevice = 1 -ReplayDevice = 2 -AsyncPairedDevice = 3 -AsyncPair = 4 -SyncPair = 5 - -SlaveSwitch = 1 -DeviceChange = 2 - -MasterAdded = (1 << 0) -MasterRemoved = (1 << 1) -SlaveAdded = (1 << 2) -SlaveRemoved = (1 << 3) -SlaveAttached = (1 << 4) -SlaveDetached = (1 << 5) -DeviceEnabled = (1 << 6) -DeviceDisabled = (1 << 7) - -AddMaster = 1 -RemoveMaster = 2 -AttachSlave = 3 -DetachSlave = 4 - -AttachToMaster = 1 -Floating = 2 - -ModeRelative = 0 -ModeAbsolute = 1 - -MasterPointer = 1 -MasterKeyboard = 2 -SlavePointer = 3 -SlaveKeyboard = 4 -FloatingSlave = 5 - -KeyClass = 0 -ButtonClass = 1 -ValuatorClass = 2 -ScrollClass = 3 -TouchClass = 8 - -KeyRepeat = (1 << 16) - -AllDevices = 0 -AllMasterDevices = 1 - -DeviceChanged = 1 -KeyPress = 2 -KeyRelease = 3 -ButtonPress = 4 -ButtonRelease = 5 -Motion = 6 -Enter = 7 -Leave = 8 -FocusIn = 9 -FocusOut = 10 -HierarchyChanged = 11 -PropertyEvent = 12 -RawKeyPress = 13 -RawKeyRelease = 14 -RawButtonPress = 15 -RawButtonRelease = 16 -RawMotion = 17 - -DeviceChangedMask = (1 << DeviceChanged) -KeyPressMask = (1 << KeyPress) -KeyReleaseMask = (1 << KeyRelease) -ButtonPressMask = (1 << ButtonPress) -ButtonReleaseMask = (1 << ButtonRelease) -MotionMask = (1 << Motion) -EnterMask = (1 << Enter) -LeaveMask = (1 << Leave) -FocusInMask = (1 << FocusIn) -FocusOutMask = (1 << FocusOut) -HierarchyChangedMask = (1 << HierarchyChanged) -PropertyEventMask = (1 << PropertyEvent) -RawKeyPressMask = (1 << RawKeyPress) -RawKeyReleaseMask = (1 << RawKeyRelease) -RawButtonPressMask = (1 << RawButtonPress) -RawButtonReleaseMask = (1 << RawButtonRelease) -RawMotionMask = (1 << RawMotion) - -GrabModeSync = 0 -GrabModeAsync = 1 -GrabModeTouch = 2 - -DEVICEID = rq.Card16 -DEVICE = rq.Card16 -DEVICEUSE = rq.Card8 - -PROPERTY_TYPE_FLOAT = 'FLOAT' - -class FP1616(rq.Int32): - - def check_value(self, value): - return int(value * 65536.0) - - def parse_value(self, value, display): - return float(value) / float(1 << 16) - -class FP3232(rq.ValueField): - structcode = 'lL' - structvalues = 2 - - def check_value(self, value): - return value - - def parse_value(self, value, display): - integral, frac = value - ret = float(integral) - # optimised math.ldexp(float(frac), -32) - ret += float(frac) * (1.0 / (1 << 32)) - return ret - -class XIQueryVersion(rq.ReplyRequest): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(47), - rq.RequestLength(), - rq.Card16('major_version'), - rq.Card16('minor_version'), - ) - _reply = rq.Struct( - rq.ReplyCode(), - rq.Pad(1), - rq.Card16('sequence_number'), - rq.ReplyLength(), - rq.Card16('major_version'), - rq.Card16('minor_version'), - rq.Pad(20), - ) - - -def query_version(self): - return XIQueryVersion( - display=self.display, - opcode=self.display.get_extension_major(extname), - major_version=2, - minor_version=0, - ) - -class Mask(rq.List): - - def __init__(self, name): - rq.List.__init__(self, name, rq.Card32, pad=0) - - def pack_value(self, val): - - mask_seq = array.array(rq.struct_to_array_codes['L']) - - if isinstance(val, integer_types): - # We need to build a "binary mask" that (as far as I can tell) is - # encoded in native byte order from end to end. The simple case is - # with a single unsigned 32-bit value, for which we construct an - # array with just one item. For values too big to fit inside 4 - # bytes we build a longer array, being careful to maintain native - # byte order across the entire set of values. - if sys.byteorder == 'little': - def fun(val): - mask_seq.insert(0, val) - elif sys.byteorder == 'big': - fun = mask_seq.append - else: - raise AssertionError(sys.byteorder) - while val: - fun(val & 0xFFFFFFFF) - val = val >> 32 - else: - mask_seq.extend(val) - - return rq.encode_array(mask_seq), len(mask_seq), None - -EventMask = rq.Struct( - DEVICE('deviceid'), - rq.LengthOf('mask', 2), - Mask('mask'), -) - - -class XISelectEvents(rq.Request): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(46), - rq.RequestLength(), - rq.Window('window'), - rq.LengthOf('masks', 2), - rq.Pad(2), - rq.List('masks', EventMask), - ) - -def select_events(self, event_masks): - ''' - select_events(event_masks) - - event_masks: - Sequence of (deviceid, mask) pairs, where deviceid is a numerical device - ID, or AllDevices or AllMasterDevices, and mask is either an unsigned - integer or sequence of 32 bits unsigned values - ''' - return XISelectEvents( - display=self.display, - opcode=self.display.get_extension_major(extname), - window=self, - masks=event_masks, - ) - -AnyInfo = rq.Struct( - rq.Card16('type'), - rq.Card16('length'), - rq.Card16('sourceid'), - rq.Pad(2), -) - -class ButtonMask(object): - - def __init__(self, value, length): - self._value = value - self._length = length - - def __len__(self): - return self._length - - def __getitem__(self, key): - return self._value & (1 << key) - - def __str__(self): - return repr(self) - - def __repr__(self): - return '0b{value:0{width}b}'.format(value=self._value, - width=self._length) - -class ButtonState(rq.ValueField): - - structcode = None - - def __init__(self, name): - rq.ValueField.__init__(self, name) - - def parse_binary_value(self, data, display, length, fmt): - # Mask: bitfield of <length> button states. - mask_len = 4 * ((((length + 7) >> 3) + 3) >> 2) - mask_data = data[:mask_len] - mask_value = 0 - for byte in reversed(struct.unpack('={0:d}B'.format(mask_len), mask_data)): - mask_value <<= 8 - mask_value |= byte - data = data[mask_len:] - assert (mask_value & 1) == 0 - return ButtonMask(mask_value >> 1, length), data - -ButtonInfo = rq.Struct( - rq.Card16('type'), - rq.Card16('length'), - rq.Card16('sourceid'), - rq.LengthOf(('state', 'labels'), 2), - ButtonState('state'), - rq.List('labels', rq.Card32), -) - -KeyInfo = rq.Struct( - rq.Card16('type'), - rq.Card16('length'), - rq.Card16('sourceid'), - rq.LengthOf('keycodes', 2), - rq.List('keycodes', rq.Card32), -) - -ValuatorInfo = rq.Struct( - rq.Card16('type'), - rq.Card16('length'), - rq.Card16('sourceid'), - rq.Card16('number'), - rq.Card32('label'), - FP3232('min'), - FP3232('max'), - FP3232('value'), - rq.Card32('resolution'), - rq.Card8('mode'), - rq.Pad(3), -) - -ScrollInfo = rq.Struct( - rq.Card16('type'), - rq.Card16('length'), - rq.Card16('sourceid'), - rq.Card16('number'), - rq.Card16('scroll_type'), - rq.Pad(2), - rq.Card32('flags'), - FP3232('increment'), -) - -TouchInfo = rq.Struct( - rq.Card16('type'), - rq.Card16('length'), - rq.Card16('sourceid'), - rq.Card8('mode'), - rq.Card8('num_touches'), -) - -INFO_CLASSES = { - KeyClass: KeyInfo, - ButtonClass: ButtonInfo, - ValuatorClass: ValuatorInfo, - ScrollClass: ScrollInfo, - TouchClass: TouchInfo, -} - -class ClassInfoClass(object): - - structcode = None - - def parse_binary(self, data, display): - class_type, length = struct.unpack('=HH', data[:4]) - class_struct = INFO_CLASSES.get(class_type, AnyInfo) - class_data, _ = class_struct.parse_binary(data, display) - data = data[length * 4:] - return class_data, data - -ClassInfo = ClassInfoClass() - -DeviceInfo = rq.Struct( - DEVICEID('deviceid'), - rq.Card16('use'), - rq.Card16('attachment'), - rq.LengthOf('classes', 2), - rq.LengthOf('name', 2), - rq.Bool('enabled'), - rq.Pad(1), - rq.String8('name', 4), - rq.List('classes', ClassInfo), -) - -class XIQueryDevice(rq.ReplyRequest): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(48), - rq.RequestLength(), - DEVICEID('deviceid'), - rq.Pad(2), - ) - - _reply = rq.Struct( - rq.ReplyCode(), - rq.Pad(1), - rq.Card16('sequence_number'), - rq.ReplyLength(), - rq.LengthOf('devices', 2), - rq.Pad(22), - rq.List('devices', DeviceInfo), - ) - -def query_device(self, deviceid): - return XIQueryDevice( - display=self.display, - opcode=self.display.get_extension_major(extname), - deviceid=deviceid, - ) - -class XIListProperties(rq.ReplyRequest): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(56), - rq.RequestLength(), - DEVICEID('deviceid'), - rq.Pad(2), - ) - - _reply = rq.Struct( - rq.ReplyCode(), - rq.Pad(1), - rq.Card16('sequence_number'), - rq.ReplyLength(), - rq.LengthOf('atoms', 2), - rq.Pad(22), - rq.List('atoms', rq.Card32Obj), - ) - -def list_device_properties(self, deviceid): - return XIListProperties( - display=self.display, - opcode=self.display.get_extension_major(extname), - deviceid=deviceid, - ) - -class XIGetProperty(rq.ReplyRequest): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(59), - rq.RequestLength(), - DEVICEID('deviceid'), - rq.Card8('delete'), - rq.Pad(1), - rq.Card32('property'), - rq.Card32('type'), - rq.Card32('offset'), - rq.Card32('length'), - ) - - _reply = rq.Struct( - rq.ReplyCode(), - rq.Pad(1), - rq.Card16('sequence_number'), - rq.ReplyLength(), - rq.Card32('type'), - rq.Card32('bytes_after'), - rq.LengthOf('value', 4), - rq.Format('value', 1), - rq.Pad(11), - rq.PropertyData('value') - ) - -def get_device_property(self, deviceid, property, type, offset, length, delete=False): - return XIGetProperty( - display=self.display, - opcode=self.display.get_extension_major(extname), - deviceid=deviceid, - property=property, - type=type, - offset=offset, - length=length, - delete=delete, - ) - -class XIChangeProperty(rq.Request): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(57), - rq.RequestLength(), - DEVICEID('deviceid'), - rq.Card8('mode'), - rq.Format('value', 1), - rq.Card32('property'), - rq.Card32('type'), - rq.LengthOf('value', 4), - rq.PropertyData('value'), - ) - -def change_device_property(self, deviceid, property, type, mode, value): - return XIChangeProperty( - display=self.display, - opcode=self.display.get_extension_major(extname), - deviceid=deviceid, - property=property, - type=type, - mode=mode, - value=value, - ) - -class XIDeleteProperty(rq.Request): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(58), - rq.RequestLength(), - DEVICEID('deviceid'), - rq.Pad(2), - rq.Card32('property'), - ) - -def delete_device_property(self, deviceid, property): - return XIDeleteProperty( - display=self.display, - opcode=self.display.get_extension_major(extname), - deviceid=deviceid, - property=property, - ) - -class XIGrabDevice(rq.ReplyRequest): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(51), - rq.RequestLength(), - rq.Window('grab_window'), - rq.Card32('time'), - rq.Cursor('cursor', (X.NONE, )), - DEVICEID('deviceid'), - rq.Set('grab_mode', 1, (GrabModeSync, GrabModeAsync)), - rq.Set('paired_device_mode', 1, (GrabModeSync, GrabModeAsync)), - rq.Bool('owner_events'), - rq.Pad(1), - rq.LengthOf('mask', 2), - Mask('mask'), - ) - - _reply = rq.Struct( - rq.ReplyCode(), - rq.Pad(1), - rq.Card16('sequence_number'), - rq.ReplyLength(), - rq.Card8('status'), - rq.Pad(23), - ) - -def grab_device(self, deviceid, time, grab_mode, paired_device_mode, owner_events, event_mask): - return XIGrabDevice( - display=self.display, - opcode=self.display.get_extension_major(extname), - deviceid=deviceid, - grab_window=self, - time=time, - cursor=X.NONE, - grab_mode=grab_mode, - paired_device_mode=paired_device_mode, - owner_events=owner_events, - mask=event_mask, - ) - -class XIUngrabDevice(rq.Request): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(52), - rq.RequestLength(), - rq.Card32('time'), - DEVICEID('deviceid'), - rq.Pad(2), - ) - -def ungrab_device(self, deviceid, time): - return XIUngrabDevice( - display=self.display, - opcode=self.display.get_extension_major(extname), - time=time, - deviceid=deviceid, - ) - -class XIPassiveGrabDevice(rq.ReplyRequest): - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(54), - rq.RequestLength(), - rq.Card32('time'), - rq.Window('grab_window'), - rq.Cursor('cursor', (X.NONE, )), - rq.Card32('detail'), - DEVICEID('deviceid'), - rq.LengthOf('modifiers', 2), - rq.LengthOf('mask', 2), - rq.Set('grab_type', 1, (GrabtypeButton, GrabtypeKeycode, GrabtypeEnter, - GrabtypeFocusIn, GrabtypeTouchBegin)), - rq.Set('grab_mode', 1, (GrabModeSync, GrabModeAsync)), - rq.Set('paired_device_mode', 1, (GrabModeSync, GrabModeAsync)), - rq.Bool('owner_events'), - rq.Pad(2), - Mask('mask'), - rq.List('modifiers', rq.Card32), - ) - - _reply = rq.Struct( - rq.ReplyCode(), - rq.Pad(1), - rq.Card16('sequence_number'), - rq.ReplyLength(), - rq.LengthOf('modifiers', 2), - rq.Pad(22), - rq.List('modifiers', rq.Card32), - ) - -def passive_grab_device(self, deviceid, time, detail, - grab_type, grab_mode, paired_device_mode, - owner_events, event_mask, modifiers): - return XIPassiveGrabDevice( - display=self.display, - opcode=self.display.get_extension_major(extname), - deviceid=deviceid, - grab_window=self, - time=time, - cursor=X.NONE, - detail=detail, - grab_type=grab_type, - grab_mode=grab_mode, - paired_device_mode=paired_device_mode, - owner_events=owner_events, - mask=event_mask, - modifiers=modifiers, - ) - -def grab_keycode(self, deviceid, time, keycode, - grab_mode, paired_device_mode, - owner_events, event_mask, modifiers): - return passive_grab_device(self, deviceid, time, keycode, - GrabtypeKeycode, - grab_mode, paired_device_mode, - owner_events, event_mask, modifiers) - -class XIPassiveUngrabDevice(rq.Request): - - _request = rq.Struct( - rq.Card8('opcode'), - rq.Opcode(55), - rq.RequestLength(), - rq.Window('grab_window'), - rq.Card32('detail'), - DEVICEID('deviceid'), - rq.LengthOf('modifiers', 2), - rq.Set('grab_type', 1, (GrabtypeButton, GrabtypeKeycode, - GrabtypeEnter, GrabtypeFocusIn, - GrabtypeTouchBegin)), - rq.Pad(3), - rq.List('modifiers', rq.Card32), - ) - -def passive_ungrab_device(self, deviceid, detail, grab_type, modifiers): - return XIPassiveUngrabDevice( - display=self.display, - opcode=self.display.get_extension_major(extname), - deviceid=deviceid, - grab_window=self, - detail=detail, - grab_type=grab_type, - modifiers=modifiers, - ) - -def ungrab_keycode(self, deviceid, keycode, modifiers): - return passive_ungrab_device(self, deviceid, keycode, - GrabtypeKeycode, modifiers) - -HierarchyInfo = rq.Struct( - DEVICEID('deviceid'), - DEVICEID('attachment'), - DEVICEUSE('type'), - rq.Bool('enabled'), - rq.Pad(2), - rq.Card32('flags'), -) - - -HierarchyEventData = rq.Struct( - DEVICEID('deviceid'), - rq.Card32('time'), - rq.Card32('flags'), - rq.LengthOf('info', 2), - rq.Pad(10), - rq.List('info', HierarchyInfo), -) - -ModifierInfo = rq.Struct( - rq.Card32('base_mods'), - rq.Card32('latched_mods'), - rq.Card32('locked_mods'), - rq.Card32('effective_mods'), -) - -GroupInfo = rq.Struct( - rq.Card8('base_group'), - rq.Card8('latched_group'), - rq.Card8('locked_group'), - rq.Card8('effective_group'), -) - -DeviceEventData = rq.Struct( - DEVICEID('deviceid'), - rq.Card32('time'), - rq.Card32('detail'), - rq.Window('root'), - rq.Window('event'), - rq.Window('child'), - FP1616('root_x'), - FP1616('root_y'), - FP1616('event_x'), - FP1616('event_y'), - rq.LengthOf('buttons', 2), - rq.Card16('valulators_len'), - DEVICEID('sourceid'), - rq.Pad(2), - rq.Card32('flags'), - rq.Object('mods', ModifierInfo), - rq.Object('groups', GroupInfo), - ButtonState('buttons'), -) - -DeviceChangedEventData = rq.Struct( - DEVICEID('deviceid'), - rq.Card32('time'), - rq.LengthOf('classes', 2), - DEVICEID('sourceid'), - rq.Card8('reason'), - rq.Pad(11), - rq.List('classes', ClassInfo), -) - -PropertyEventData = rq.Struct( - DEVICEID('deviceid'), - rq.Card32('time'), - rq.Card32('property'), - rq.Card8('what'), - rq.Pad(11), -) - -def init(disp, info): - disp.extension_add_method('display', 'xinput_query_version', query_version) - disp.extension_add_method('window', 'xinput_select_events', select_events) - disp.extension_add_method('display', 'xinput_query_device', query_device) - disp.extension_add_method('window', 'xinput_grab_device', grab_device) - disp.extension_add_method('display', 'xinput_ungrab_device', ungrab_device) - disp.extension_add_method('window', 'xinput_grab_keycode', grab_keycode) - disp.extension_add_method('window', 'xinput_ungrab_keycode', ungrab_keycode) - disp.extension_add_method('display', 'xinput_get_device_property', get_device_property) - disp.extension_add_method('display', 'xinput_list_device_properties', list_device_properties) - disp.extension_add_method('display', 'xinput_change_device_property', change_device_property) - disp.extension_add_method('display', 'xinput_delete_device_property', delete_device_property) - if hasattr(disp,"ge_add_event_data"): - for device_event in (ButtonPress, ButtonRelease, KeyPress, KeyRelease, Motion): - disp.ge_add_event_data(info.major_opcode, device_event, DeviceEventData) - disp.ge_add_event_data(info.major_opcode, DeviceChanged, DeviceEventData) - disp.ge_add_event_data(info.major_opcode, HierarchyChanged, HierarchyEventData) - disp.ge_add_event_data(info.major_opcode, PropertyEvent, PropertyEventData) diff --git a/Nagstamon/thirdparty/Xlib/ext/xtest.py b/Nagstamon/thirdparty/Xlib/ext/xtest.py index 602df2ae8..6146e68ab 100644 --- a/Nagstamon/thirdparty/Xlib/ext/xtest.py +++ b/Nagstamon/thirdparty/Xlib/ext/xtest.py @@ -2,22 +2,19 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA from Xlib import X from Xlib.protocol import rq diff --git a/Nagstamon/thirdparty/Xlib/keysymdef/__init__.py b/Nagstamon/thirdparty/Xlib/keysymdef/__init__.py index 4ff14416e..75fe2bc67 100644 --- a/Nagstamon/thirdparty/Xlib/keysymdef/__init__.py +++ b/Nagstamon/thirdparty/Xlib/keysymdef/__init__.py @@ -2,22 +2,19 @@ # # Copyright (C) 2001 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA __all__ = [ 'apl', diff --git a/Nagstamon/thirdparty/Xlib/keysymdef/xf86.py b/Nagstamon/thirdparty/Xlib/keysymdef/xf86.py index 4ccf12a71..a0d79aef4 100644 --- a/Nagstamon/thirdparty/Xlib/keysymdef/xf86.py +++ b/Nagstamon/thirdparty/Xlib/keysymdef/xf86.py @@ -1,202 +1,179 @@ -XK_XF86_ModeLock = 0x1008FF01 +XK_XF86_MonBrightnessUp = 0x1008FF02 +XK_XF86_MonBrightnessDown = 0x1008FF03 +XK_XF86_KbdLightOnOff = 0x1008FF04 +XK_XF86_KbdBrightnessUp = 0x1008FF05 +XK_XF86_KbdBrightnessDown = 0x1008FF06 + +XK_XF86_Standby = 0x1008FF10 +XK_XF86_AudioLowerVolume = 0x1008FF11 +XK_XF86_AudioMute = 0x1008FF12 +XK_XF86_AudioRaiseVolume = 0x1008FF13 +XK_XF86_AudioPlay = 0x1008FF14 +XK_XF86_AudioStop = 0x1008FF15 +XK_XF86_AudioPrev = 0x1008FF16 +XK_XF86_AudioNext = 0x1008FF17 +XK_XF86_HomePage = 0x1008FF18 +XK_XF86_Mail = 0x1008FF19 +XK_XF86_Start = 0x1008FF1A +XK_XF86_Search = 0x1008FF1B +XK_XF86_AudioRecord = 0x1008FF1C + +XK_XF86_Calculator = 0x1008FF1D +XK_XF86_Memo = 0x1008FF1E +XK_XF86_ToDoList = 0x1008FF1F +XK_XF86_Calendar = 0x1008FF20 +XK_XF86_PowerDown = 0x1008FF21 +XK_XF86_ContrastAdjust = 0x1008FF22 +XK_XF86_RockerUp = 0x1008FF23 +XK_XF86_RockerDown = 0x1008FF24 +XK_XF86_RockerEnter = 0x1008FF25 + +XK_XF86_Back = 0x1008FF26 +XK_XF86_Forward = 0x1008FF27 +XK_XF86_Stop = 0x1008FF28 +XK_XF86_Refresh = 0x1008FF29 +XK_XF86_PowerOff = 0x1008FF2A +XK_XF86_WakeUp = 0x1008FF2B +XK_XF86_Eject = 0x1008FF2C +XK_XF86_ScreenSaver = 0x1008FF2D +XK_XF86_WWW = 0x1008FF2E +XK_XF86_Sleep = 0x1008FF2F +XK_XF86_Favorites = 0x1008FF30 +XK_XF86_AudioPause = 0x1008FF31 +XK_XF86_AudioMedia = 0x1008FF32 +XK_XF86_MyComputer = 0x1008FF33 +XK_XF86_VendorHome = 0x1008FF34 +XK_XF86_LightBulb = 0x1008FF35 +XK_XF86_Shop = 0x1008FF36 +XK_XF86_History = 0x1008FF37 +XK_XF86_OpenURL = 0x1008FF38 +XK_XF86_AddFavorite = 0x1008FF39 +XK_XF86_HotLinks = 0x1008FF3A +XK_XF86_BrightnessAdjust = 0x1008FF3B +XK_XF86_Finance = 0x1008FF3C +XK_XF86_Community = 0x1008FF3D +XK_XF86_AudioRewind = 0x1008FF3E +XK_XF86_XF86BackForward = 0x1008FF3F +XK_XF86_Launch0 = 0x1008FF40 +XK_XF86_Launch1 = 0x1008FF41 +XK_XF86_Launch2 = 0x1008FF42 +XK_XF86_Launch3 = 0x1008FF43 +XK_XF86_Launch4 = 0x1008FF44 +XK_XF86_Launch5 = 0x1008FF45 +XK_XF86_Launch6 = 0x1008FF46 +XK_XF86_Launch7 = 0x1008FF47 +XK_XF86_Launch8 = 0x1008FF48 +XK_XF86_Launch9 = 0x1008FF49 +XK_XF86_LaunchA = 0x1008FF4A +XK_XF86_LaunchB = 0x1008FF4B +XK_XF86_LaunchC = 0x1008FF4C +XK_XF86_LaunchD = 0x1008FF4D +XK_XF86_LaunchE = 0x1008FF4E +XK_XF86_LaunchF = 0x1008FF4F + +XK_XF86_ApplicationLeft = 0x1008FF50 +XK_XF86_ApplicationRight = 0x1008FF51 +XK_XF86_Book = 0x1008FF52 +XK_XF86_CD = 0x1008FF53 +XK_XF86_Calculater = 0x1008FF54 +XK_XF86_Clear = 0x1008FF55 +XK_XF86_Close = 0x1008FF56 +XK_XF86_Copy = 0x1008FF57 +XK_XF86_Cut = 0x1008FF58 +XK_XF86_Display = 0x1008FF59 +XK_XF86_DOS = 0x1008FF5A +XK_XF86_Documents = 0x1008FF5B +XK_XF86_Excel = 0x1008FF5C +XK_XF86_Explorer = 0x1008FF5D +XK_XF86_Game = 0x1008FF5E +XK_XF86_Go = 0x1008FF5F +XK_XF86_iTouch = 0x1008FF60 +XK_XF86_LogOff = 0x1008FF61 +XK_XF86_Market = 0x1008FF62 +XK_XF86_Meeting = 0x1008FF63 +XK_XF86_MenuKB = 0x1008FF65 +XK_XF86_MenuPB = 0x1008FF66 +XK_XF86_MySites = 0x1008FF67 +XK_XF86_New = 0x1008FF68 +XK_XF86_News = 0x1008FF69 +XK_XF86_OfficeHome = 0x1008FF6A +XK_XF86_Open = 0x1008FF6B +XK_XF86_Option = 0x1008FF6C +XK_XF86_Paste = 0x1008FF6D +XK_XF86_Phone = 0x1008FF6E +XK_XF86_Q = 0x1008FF70 +XK_XF86_Reply = 0x1008FF72 +XK_XF86_Reload = 0x1008FF73 +XK_XF86_RotateWindows = 0x1008FF74 +XK_XF86_RotationPB = 0x1008FF75 +XK_XF86_RotationKB = 0x1008FF76 +XK_XF86_Save = 0x1008FF77 +XK_XF86_ScrollUp = 0x1008FF78 +XK_XF86_ScrollDown = 0x1008FF79 +XK_XF86_ScrollClick = 0x1008FF7A +XK_XF86_Send = 0x1008FF7B +XK_XF86_Spell = 0x1008FF7C +XK_XF86_SplitScreen = 0x1008FF7D +XK_XF86_Support = 0x1008FF7E +XK_XF86_TaskPane = 0x1008FF7F +XK_XF86_Terminal = 0x1008FF80 +XK_XF86_Tools = 0x1008FF81 +XK_XF86_Travel = 0x1008FF82 +XK_XF86_UserPB = 0x1008FF84 +XK_XF86_User1KB = 0x1008FF85 +XK_XF86_User2KB = 0x1008FF86 +XK_XF86_Video = 0x1008FF87 +XK_XF86_WheelButton = 0x1008FF88 +XK_XF86_Word = 0x1008FF89 +XK_XF86_Xfer = 0x1008FF8A +XK_XF86_ZoomIn = 0x1008FF8B +XK_XF86_ZoomOut = 0x1008FF8C + +XK_XF86_Away = 0x1008FF8D +XK_XF86_Messenger = 0x1008FF8E +XK_XF86_WebCam = 0x1008FF8F +XK_XF86_MailForward = 0x1008FF90 +XK_XF86_Pictures = 0x1008FF91 +XK_XF86_Music = 0x1008FF92 + +XK_XF86_Battery = 0x1008FF93 +XK_XF86_Bluetooth = 0x1008FF94 +XK_XF86_WLAN = 0x1008FF95 +XK_XF86_UWB = 0x1008FF96 + +XK_XF86_AudioForward = 0x1008FF97 +XK_XF86_AudioRepeat = 0x1008FF98 +XK_XF86_AudioRandomPlay = 0x1008FF99 +XK_XF86_Subtitle = 0x1008FF9A +XK_XF86_AudioCycleTrack = 0x1008FF9B +XK_XF86_CycleAngle = 0x1008FF9C +XK_XF86_FrameBack = 0x1008FF9D +XK_XF86_FrameForward = 0x1008FF9E +XK_XF86_Time = 0x1008FF9F +XK_XF86_Select = 0x1008FFA0 +XK_XF86_View = 0x1008FFA1 +XK_XF86_TopMenu = 0x1008FFA2 + +XK_XF86_Red = 0x1008FFA3 +XK_XF86_Green = 0x1008FFA4 +XK_XF86_Yellow = 0x1008FFA5 +XK_XF86_Blue = 0x1008FFA6 + +XK_XF86_Switch_VT_1 = 0x1008FE01 +XK_XF86_Switch_VT_2 = 0x1008FE02 +XK_XF86_Switch_VT_3 = 0x1008FE03 +XK_XF86_Switch_VT_4 = 0x1008FE04 +XK_XF86_Switch_VT_5 = 0x1008FE05 +XK_XF86_Switch_VT_6 = 0x1008FE06 +XK_XF86_Switch_VT_7 = 0x1008FE07 +XK_XF86_Switch_VT_8 = 0x1008FE08 +XK_XF86_Switch_VT_9 = 0x1008FE09 +XK_XF86_Switch_VT_10 = 0x1008FE0A +XK_XF86_Switch_VT_11 = 0x1008FE0B +XK_XF86_Switch_VT_12 = 0x1008FE0C + +XK_XF86_Ungrab = 0x1008FE20 +XK_XF86_ClearGrab = 0x1008FE21 +XK_XF86_Next_VMode = 0x1008FE22 +XK_XF86_Prev_VMode = 0x1008FE23 -XK_XF86_MonBrightnessUp = 0x1008FF02 -XK_XF86_MonBrightnessDown = 0x1008FF03 -XK_XF86_KbdLightOnOff = 0x1008FF04 -XK_XF86_KbdBrightnessUp = 0x1008FF05 -XK_XF86_KbdBrightnessDown = 0x1008FF06 -XK_XF86_MonBrightnessCycle = 0x1008FF07 - -XK_XF86_Standby = 0x1008FF10 -XK_XF86_AudioLowerVolume = 0x1008FF11 -XK_XF86_AudioMute = 0x1008FF12 -XK_XF86_AudioRaiseVolume = 0x1008FF13 -XK_XF86_AudioPlay = 0x1008FF14 -XK_XF86_AudioStop = 0x1008FF15 -XK_XF86_AudioPrev = 0x1008FF16 -XK_XF86_AudioNext = 0x1008FF17 -XK_XF86_HomePage = 0x1008FF18 -XK_XF86_Mail = 0x1008FF19 -XK_XF86_Start = 0x1008FF1A -XK_XF86_Search = 0x1008FF1B -XK_XF86_AudioRecord = 0x1008FF1C - -XK_XF86_Calculator = 0x1008FF1D -XK_XF86_Memo = 0x1008FF1E -XK_XF86_ToDoList = 0x1008FF1F -XK_XF86_Calendar = 0x1008FF20 -XK_XF86_PowerDown = 0x1008FF21 -XK_XF86_ContrastAdjust = 0x1008FF22 -XK_XF86_RockerUp = 0x1008FF23 -XK_XF86_RockerDown = 0x1008FF24 -XK_XF86_RockerEnter = 0x1008FF25 - -XK_XF86_Back = 0x1008FF26 -XK_XF86_Forward = 0x1008FF27 -XK_XF86_Stop = 0x1008FF28 -XK_XF86_Refresh = 0x1008FF29 -XK_XF86_PowerOff = 0x1008FF2A -XK_XF86_WakeUp = 0x1008FF2B -XK_XF86_Eject = 0x1008FF2C -XK_XF86_ScreenSaver = 0x1008FF2D -XK_XF86_WWW = 0x1008FF2E -XK_XF86_Sleep = 0x1008FF2F -XK_XF86_Favorites = 0x1008FF30 -XK_XF86_AudioPause = 0x1008FF31 -XK_XF86_AudioMedia = 0x1008FF32 -XK_XF86_MyComputer = 0x1008FF33 -XK_XF86_VendorHome = 0x1008FF34 -XK_XF86_LightBulb = 0x1008FF35 -XK_XF86_Shop = 0x1008FF36 -XK_XF86_History = 0x1008FF37 -XK_XF86_OpenURL = 0x1008FF38 -XK_XF86_AddFavorite = 0x1008FF39 -XK_XF86_HotLinks = 0x1008FF3A -XK_XF86_BrightnessAdjust = 0x1008FF3B -XK_XF86_Finance = 0x1008FF3C -XK_XF86_Community = 0x1008FF3D -XK_XF86_AudioRewind = 0x1008FF3E -XK_XF86_XF86BackForward = 0x1008FF3F -XK_XF86_Launch0 = 0x1008FF40 -XK_XF86_Launch1 = 0x1008FF41 -XK_XF86_Launch2 = 0x1008FF42 -XK_XF86_Launch3 = 0x1008FF43 -XK_XF86_Launch4 = 0x1008FF44 -XK_XF86_Launch5 = 0x1008FF45 -XK_XF86_Launch6 = 0x1008FF46 -XK_XF86_Launch7 = 0x1008FF47 -XK_XF86_Launch8 = 0x1008FF48 -XK_XF86_Launch9 = 0x1008FF49 -XK_XF86_LaunchA = 0x1008FF4A -XK_XF86_LaunchB = 0x1008FF4B -XK_XF86_LaunchC = 0x1008FF4C -XK_XF86_LaunchD = 0x1008FF4D -XK_XF86_LaunchE = 0x1008FF4E -XK_XF86_LaunchF = 0x1008FF4F - -XK_XF86_ApplicationLeft = 0x1008FF50 -XK_XF86_ApplicationRight = 0x1008FF51 -XK_XF86_Book = 0x1008FF52 -XK_XF86_CD = 0x1008FF53 -XK_XF86_Calculater = 0x1008FF54 -XK_XF86_Clear = 0x1008FF55 -XK_XF86_Close = 0x1008FF56 -XK_XF86_Copy = 0x1008FF57 -XK_XF86_Cut = 0x1008FF58 -XK_XF86_Display = 0x1008FF59 -XK_XF86_DOS = 0x1008FF5A -XK_XF86_Documents = 0x1008FF5B -XK_XF86_Excel = 0x1008FF5C -XK_XF86_Explorer = 0x1008FF5D -XK_XF86_Game = 0x1008FF5E -XK_XF86_Go = 0x1008FF5F -XK_XF86_iTouch = 0x1008FF60 -XK_XF86_LogOff = 0x1008FF61 -XK_XF86_Market = 0x1008FF62 -XK_XF86_Meeting = 0x1008FF63 -XK_XF86_MenuKB = 0x1008FF65 -XK_XF86_MenuPB = 0x1008FF66 -XK_XF86_MySites = 0x1008FF67 -XK_XF86_New = 0x1008FF68 -XK_XF86_News = 0x1008FF69 -XK_XF86_OfficeHome = 0x1008FF6A -XK_XF86_Open = 0x1008FF6B -XK_XF86_Option = 0x1008FF6C -XK_XF86_Paste = 0x1008FF6D -XK_XF86_Phone = 0x1008FF6E -XK_XF86_Q = 0x1008FF70 -XK_XF86_Reply = 0x1008FF72 -XK_XF86_Reload = 0x1008FF73 -XK_XF86_RotateWindows = 0x1008FF74 -XK_XF86_RotationPB = 0x1008FF75 -XK_XF86_RotationKB = 0x1008FF76 -XK_XF86_Save = 0x1008FF77 -XK_XF86_ScrollUp = 0x1008FF78 -XK_XF86_ScrollDown = 0x1008FF79 -XK_XF86_ScrollClick = 0x1008FF7A -XK_XF86_Send = 0x1008FF7B -XK_XF86_Spell = 0x1008FF7C -XK_XF86_SplitScreen = 0x1008FF7D -XK_XF86_Support = 0x1008FF7E -XK_XF86_TaskPane = 0x1008FF7F -XK_XF86_Terminal = 0x1008FF80 -XK_XF86_Tools = 0x1008FF81 -XK_XF86_Travel = 0x1008FF82 -XK_XF86_UserPB = 0x1008FF84 -XK_XF86_User1KB = 0x1008FF85 -XK_XF86_User2KB = 0x1008FF86 -XK_XF86_Video = 0x1008FF87 -XK_XF86_WheelButton = 0x1008FF88 -XK_XF86_Word = 0x1008FF89 -XK_XF86_Xfer = 0x1008FF8A -XK_XF86_ZoomIn = 0x1008FF8B -XK_XF86_ZoomOut = 0x1008FF8C - -XK_XF86_Away = 0x1008FF8D -XK_XF86_Messenger = 0x1008FF8E -XK_XF86_WebCam = 0x1008FF8F -XK_XF86_MailForward = 0x1008FF90 -XK_XF86_Pictures = 0x1008FF91 -XK_XF86_Music = 0x1008FF92 - -XK_XF86_Battery = 0x1008FF93 -XK_XF86_Bluetooth = 0x1008FF94 -XK_XF86_WLAN = 0x1008FF95 -XK_XF86_UWB = 0x1008FF96 - -XK_XF86_AudioForward = 0x1008FF97 -XK_XF86_AudioRepeat = 0x1008FF98 -XK_XF86_AudioRandomPlay = 0x1008FF99 -XK_XF86_Subtitle = 0x1008FF9A -XK_XF86_AudioCycleTrack = 0x1008FF9B -XK_XF86_CycleAngle = 0x1008FF9C -XK_XF86_FrameBack = 0x1008FF9D -XK_XF86_FrameForward = 0x1008FF9E -XK_XF86_Time = 0x1008FF9F -XK_XF86_Select = 0x1008FFA0 -XK_XF86_View = 0x1008FFA1 -XK_XF86_TopMenu = 0x1008FFA2 - -XK_XF86_Red = 0x1008FFA3 -XK_XF86_Green = 0x1008FFA4 -XK_XF86_Yellow = 0x1008FFA5 -XK_XF86_Blue = 0x1008FFA6 - -XK_XF86_Suspend = 0x1008FFA7 -XK_XF86_Hibernate = 0x1008FFA8 -XK_XF86_TouchpadToggle = 0x1008FFA9 -XK_XF86_TouchpadOn = 0x1008FFB0 -XK_XF86_TouchpadOff = 0x1008FFB1 - -XK_XF86_AudioMicMute = 0x1008FFB2 - -XK_XF86_Keyboard = 0x1008FFB3 - -XK_XF86_WWAN = 0x1008FFB4 -XK_XF86_RFKill = 0x1008FFB5 - -XK_XF86_AudioPreset = 0x1008FFB6 - -XK_XF86_RotationLockToggle = 0x1008FFB7 - -XK_XF86_FullScreen = 0x1008FFB8 - -XK_XF86_Switch_VT_1 = 0x1008FE01 -XK_XF86_Switch_VT_2 = 0x1008FE02 -XK_XF86_Switch_VT_3 = 0x1008FE03 -XK_XF86_Switch_VT_4 = 0x1008FE04 -XK_XF86_Switch_VT_5 = 0x1008FE05 -XK_XF86_Switch_VT_6 = 0x1008FE06 -XK_XF86_Switch_VT_7 = 0x1008FE07 -XK_XF86_Switch_VT_8 = 0x1008FE08 -XK_XF86_Switch_VT_9 = 0x1008FE09 -XK_XF86_Switch_VT_10 = 0x1008FE0A -XK_XF86_Switch_VT_11 = 0x1008FE0B -XK_XF86_Switch_VT_12 = 0x1008FE0C - -XK_XF86_Ungrab = 0x1008FE20 -XK_XF86_ClearGrab = 0x1008FE21 -XK_XF86_Next_VMode = 0x1008FE22 -XK_XF86_Prev_VMode = 0x1008FE23 -XK_XF86_LogWindowTree = 0x1008FE24 -XK_XF86_LogGrabInfo = 0x1008FE25 diff --git a/Nagstamon/thirdparty/Xlib/protocol/__init__.py b/Nagstamon/thirdparty/Xlib/protocol/__init__.py index 4e2840a1b..8b71e06e7 100644 --- a/Nagstamon/thirdparty/Xlib/protocol/__init__.py +++ b/Nagstamon/thirdparty/Xlib/protocol/__init__.py @@ -2,22 +2,19 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA __all__ = [ 'display', diff --git a/Nagstamon/thirdparty/Xlib/protocol/display.py b/Nagstamon/thirdparty/Xlib/protocol/display.py index 56623c358..fdd2efe2e 100644 --- a/Nagstamon/thirdparty/Xlib/protocol/display.py +++ b/Nagstamon/thirdparty/Xlib/protocol/display.py @@ -1,95 +1,64 @@ -# -*- coding: utf-8 -*- +# -*- coding: latin-1 -*- # # Xlib.protocol.display -- core display communication # # Copyright (C) 2000-2002 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA # Standard modules import errno -import math import select import socket import struct import sys -# Python 2/3 compatibility. -from six import PY3, byte2int, indexbytes - # Xlib modules -from .. import error -from ..ext import ge +from Xlib import error -from ..support import lock, connect +from Xlib.support import lock, connect # Xlib.protocol modules -from . import rq -from . import event - -if PY3: - - class bytesview(object): - - def __init__(self, data, offset=0, size=None): - if size is None: - size = len(data)-offset - if isinstance(data, bytes): - view = memoryview(data) - elif isinstance(data, bytesview): - view = data.view - else: - raise TypeError('unsupported type: {}'.format(type(data))) - self.view = view[offset:offset+size] - - def __len__(self): - return len(self.view) - - def __getitem__(self, key): - if isinstance(key, slice): - return bytes(self.view[key]) - return self.view[key] +from . import rq, event +# in Python 3, bytes are an actual array; in python 2, bytes are still +# string-like, so in order to get an array element we need to call ord() +if sys.version[0] >= '3': + def _bytes_item(x): + return x else: + def _bytes_item(x): + return ord(x) - def bytesview(data, offset=0, size=None): - if not isinstance(data, (bytes, buffer)): - raise TypeError('unsupported type: {}'.format(type(data))) - if size is None: - size = len(data)-offset - return buffer(data, offset, size) - -class Display(object): +class Display: + resource_classes = {} extension_major_opcodes = {} error_classes = error.xerror_class.copy() event_classes = event.event_class.copy() def __init__(self, display = None): - name, protocol, host, displayno, screenno = connect.get_display(display) + name, host, displayno, screenno = connect.get_display(display) self.display_name = name self.default_screen = screenno - self.socket = connect.get_socket(name, protocol, host, displayno) + self.socket = connect.get_socket(name, host, displayno) - auth_name, auth_data = connect.get_auth(self.socket, name, - protocol, host, displayno) + auth_name, auth_data = connect.get_auth(self.socket, + name, host, displayno) # Internal structures for communication, grouped # by their function and locks @@ -109,7 +78,7 @@ def __init__(self, display = None): self.request_serial = 1 self.request_queue = [] - # Send-and-receive loop, see function send_and_receive + # Send-and-recieve loop, see function send_and_recive # for a detailed explanation self.send_recv_lock = lock.allocate_lock() self.send_active = 0 @@ -120,15 +89,9 @@ def __init__(self, display = None): self.request_waiting = 0 self.request_wait_lock = lock.allocate_lock() - # Calculate optimal default buffer size for recv. - buffer_size = self.socket.getsockopt(socket.SOL_SOCKET, - socket.SO_RCVBUF) - buffer_size = math.pow(2, math.floor(math.log(buffer_size, 2))) - self.recv_buffer_size = int(buffer_size) - - # Data used by the send-and-receive loop + # Data used by the send-and-recieve loop self.sent_requests = [] - self.recv_packet_len = 0 + self.request_length = 0 self.data_send = b'' self.data_recv = b'' self.data_sent_bytes = 0 @@ -145,7 +108,7 @@ def __init__(self, display = None): # Right, now we're all set up for the connection setup # request with the server. - # Figure out which endianness the hardware uses + # Figure out which endianess the hardware uses self.big_endian = struct.unpack('BB', struct.pack('H', 0x0100))[0] if self.big_endian: @@ -203,7 +166,7 @@ def next_event(self): while not self.event_queue: - # Lock send_recv so no send_and_receive + # Lock send_recv so no send_and_recieve # can start or stop while we're checking # whether there are one active. self.send_recv_lock.acquire() @@ -325,14 +288,8 @@ def set_extension_major(self, extname, major): def get_extension_major(self, extname): return self.extension_major_opcodes[extname] - def add_extension_event(self, code, evt, subcode=None): - if subcode == None: - self.event_classes[code] = evt - else: - if not code in self.event_classes: - self.event_classes[code] = {subcode: evt} - else: - self.event_classes[code][subcode] = evt + def add_extension_event(self, code, evt): + self.event_classes[code] = evt def add_extension_error(self, code, err): self.error_classes[code] = err @@ -399,7 +356,7 @@ def send_and_recv(self, flush = None, event = None, request = None, recv = None) be true. Will return immediately if another thread is already doing send_and_recv. - To wait for an event to be received, event should be true. + To wait for an event to be recieved, event should be true. To wait for a response to a certain request (either an error or a response), request should be set the that request's @@ -423,9 +380,6 @@ def send_and_recv(self, flush = None, event = None, request = None, recv = None) # If waiting for an event, we want to recv # If just trying to receive anything we can, we want to recv - # FIXME: It would be good if we could also sleep when we're waiting on - # a response to a request that has already been sent. - if (((flush or request is not None) and self.send_active) or ((event or recv) and self.recv_active)): @@ -484,19 +438,19 @@ def send_and_recv(self, flush = None, event = None, request = None, recv = None) # There's no thread doing what we need to do. Find out exactly # what to do - # There must always be some thread receiving data, but it must not + # There must always be some thread recieving data, but it must not # necessarily be us if not self.recv_active: - receiving = 1 + recieving = 1 self.recv_active = 1 else: - receiving = 0 + recieving = 0 flush_bytes = None sending = 0 - # Loop, receiving and sending data. + # Loop, recieving and sending data. while 1: # We might want to start sending data @@ -527,10 +481,6 @@ def send_and_recv(self, flush = None, event = None, request = None, recv = None) # for the network to fire up self.send_recv_lock.release() - # There's no longer anything useful we can do here. - if not (sending or receiving): - break - # If we're flushing, figure out how many bytes we # have to send so that we're not caught in an interminable # loop if other threads continuously append requests. @@ -541,9 +491,9 @@ def send_and_recv(self, flush = None, event = None, request = None, recv = None) try: # We're only checking for the socket to be writable # if we're the sending thread. We always check for it - # to become readable: either we are the receiving thread - # and should take care of the data, or the receiving thread - # might finish receiving after having read the data + # to become readable: either we are the recieving thread + # and should take care of the data, or the recieving thread + # might finish recieving after having read the data if sending: writeset = [self.socket] @@ -560,15 +510,11 @@ def send_and_recv(self, flush = None, event = None, request = None, recv = None) rs, ws, es = select.select([self.socket], writeset, [], timeout) - # Ignore errors caused by a signal received while blocking. + # Ignore errors caused by a signal recieved while blocking. # All other errors are re-raised. - except select.error as err: - if isinstance(err, OSError): - code = err.errno - else: - code = err[0] - if code != errno.EINTR: - raise + except OSError as err: + if err.errno != errno.EINTR: + raise err # We must lock send_and_recv before we can loop to # the start of the loop @@ -581,8 +527,8 @@ def send_and_recv(self, flush = None, event = None, request = None, recv = None) if ws: try: i = self.socket.send(self.data_send) - except socket.error as err: - self.close_internal('server: %s' % err) + except OSError as err: + self.close_internal('server: %s' % err[1]) raise self.socket_error self.data_send = self.data_send[i:] @@ -593,14 +539,12 @@ def send_and_recv(self, flush = None, event = None, request = None, recv = None) gotreq = 0 if rs: - # We're the receiving thread, parse the data - if receiving: + # We're the recieving thread, parse the data + if recieving: try: - count = self.recv_packet_len - len(self.data_recv) - count = max(self.recv_buffer_size, count) - bytes_recv = self.socket.recv(count) - except socket.error as err: - self.close_internal('server: %s' % err) + bytes_recv = self.socket.recv(4096) + except OSError as err: + self.close_internal('server: %s' % err.strerror) raise self.socket_error if not bytes_recv: @@ -608,7 +552,7 @@ def send_and_recv(self, flush = None, event = None, request = None, recv = None) self.close_internal('server') raise self.socket_error - self.data_recv = bytes(self.data_recv) + bytes_recv + self.data_recv = self.data_recv + bytes_recv gotreq = self.parse_response(request) # Otherwise return, allowing the calling thread to figure @@ -626,7 +570,7 @@ def send_and_recv(self, flush = None, event = None, request = None, recv = None) # There are three different end of send-recv-loop conditions. # However, we don't leave the loop immediately, instead we - # try to send and receive any data that might be left. We + # try to send and recieve any data that might be left. We # do this by giving a timeout of 0 to select to poll # the socket. @@ -642,7 +586,7 @@ def send_and_recv(self, flush = None, event = None, request = None, recv = None) if request is not None and gotreq: break - # Always break if we just want to receive as much as possible + # Always break if we just want to recieve as much as possible if recv: break @@ -660,7 +604,7 @@ def send_and_recv(self, flush = None, event = None, request = None, recv = None) if sending: self.send_active = 0 - if receiving: + if recieving: self.recv_active = 0 if self.event_waiting: @@ -677,9 +621,9 @@ def send_and_recv(self, flush = None, event = None, request = None, recv = None) def parse_response(self, request): """Internal method. - Parse data received from server. If REQUEST is not None + Parse data recieved from server. If REQUEST is not None true is returned if the request with that serial number - was received, otherwise false is returned. + was recieved, otherwise false is returned. If REQUEST is -1, we're parsing the server connection setup response. @@ -691,54 +635,47 @@ def parse_response(self, request): # Parse ordinary server response gotreq = 0 while 1: - if self.data_recv: - # Check the first byte to find out what kind of response it is - rtype = byte2int(self.data_recv) - - # Are we're waiting for additional data for the current packet? - if self.recv_packet_len: - if len(self.data_recv) < self.recv_packet_len: + # Are we're waiting for additional data for a request response? + if self.request_length: + if len(self.data_recv) < self.request_length: return gotreq - - if rtype == 1: - gotreq = self.parse_request_response(request) or gotreq - continue - elif rtype & 0x7f == ge.GenericEventCode: - self.parse_event_response(rtype) - continue else: - raise AssertionError(rtype) + gotreq = self.parse_request_response(request) or gotreq + # Every response is at least 32 bytes long, so don't bother - # until we have received that much + # until we have recieved that much if len(self.data_recv) < 32: return gotreq - # Error response + # Check the first byte to find out what kind of response it is + rtype = _bytes_item(self.data_recv[0]) + + # Error resposne if rtype == 0: gotreq = self.parse_error_response(request) or gotreq - # Request response or generic event. - elif rtype == 1 or rtype & 0x7f == ge.GenericEventCode: + # Request response + elif rtype == 1: # Set reply length, and loop around to see if # we have got the full response rlen = int(struct.unpack('=L', self.data_recv[4:8])[0]) - self.recv_packet_len = 32 + rlen * 4 + self.request_length = 32 + rlen * 4 - # Else non-generic event + # Else event response else: self.parse_event_response(rtype) def parse_error_response(self, request): # Code is second byte - code = indexbytes(self.data_recv, 1) + code = _bytes_item(self.data_recv[1]) # Fetch error class estruct = self.error_classes.get(code, error.XError) e = estruct(self, self.data_recv[:32]) - self.data_recv = bytesview(self.data_recv, 32) + self.data_recv = self.data_recv[32:] # print 'recv Error:', e @@ -789,11 +726,11 @@ def parse_request_response(self, request): raise RuntimeError("Expected reply for request %s, but got %s. Can't happen!" % (req._serial, sno)) - req._parse_response(self.data_recv[:self.recv_packet_len]) + req._parse_response(self.data_recv[:self.request_length]) # print 'recv Request:', req - self.data_recv = bytesview(self.data_recv, self.recv_packet_len) - self.recv_packet_len = 0 + self.data_recv = self.data_recv[self.request_length:] + self.request_length = 0 # Unlock any response waiting threads @@ -811,42 +748,21 @@ def parse_request_response(self, request): def parse_event_response(self, etype): - # Skip bit 8, that is set if this event came from an SendEvent - etype = etype & 0x7f - - if etype == ge.GenericEventCode: - length = self.recv_packet_len - else: - length = 32 - - estruct = self.event_classes.get(etype, event.AnyEvent) - if type(estruct) == dict: - subcode = self.data_recv[1] - - # Python2 compatibility - if type(subcode) == str: - subcode = ord(subcode) - - # this etype refers to a set of sub-events with individual subcodes - estruct = estruct[subcode] - - e = estruct(display = self, binarydata = self.data_recv[:length]) + # Skip bit 8 at lookup, that is set if this event came from an + # SendEvent + estruct = self.event_classes.get(etype & 0x7f, event.AnyEvent) - if etype == ge.GenericEventCode: - self.recv_packet_len = 0 + e = estruct(display = self, binarydata = self.data_recv[:32]) - self.data_recv = bytesview(self.data_recv, length) + self.data_recv = self.data_recv[32:] # Drop all requests having an error handler, # but which obviously succeded. # Decrement it by one, so that we don't remove the request # that generated these events, if there is such a one. - # Bug reported by Ilpo Nyyssönen - # Note: not all events have a sequence_number field! - # (e.g. KeymapNotify). - if hasattr(e, 'sequence_number'): - self.get_waiting_request((e.sequence_number - 1) % 65536) + # Bug reported by Ilpo Nyyss�nen + self.get_waiting_request((e.sequence_number - 1) % 65536) # print 'recv Event:', e diff --git a/Nagstamon/thirdparty/Xlib/protocol/event.py b/Nagstamon/thirdparty/Xlib/protocol/event.py index 04743c6d0..a0386ded5 100644 --- a/Nagstamon/thirdparty/Xlib/protocol/event.py +++ b/Nagstamon/thirdparty/Xlib/protocol/event.py @@ -2,29 +2,26 @@ # # Copyright (C) 2000-2002 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA # Xlib modules -from .. import X +from Xlib import X # Xlib.protocol modules -from . import rq +from Xlib.protocol import rq class AnyEvent(rq.Event): @@ -32,7 +29,7 @@ class AnyEvent(rq.Event): _fields = rq.Struct( rq.Card8('type'), rq.Card8('detail'), rq.Card16('sequence_number'), - rq.FixedBinary('data', 28), + rq.FixedString('data', 28), ) class KeyButtonPointer(rq.Event): diff --git a/Nagstamon/thirdparty/Xlib/protocol/request.py b/Nagstamon/thirdparty/Xlib/protocol/request.py index b431e137c..aeb598bcf 100644 --- a/Nagstamon/thirdparty/Xlib/protocol/request.py +++ b/Nagstamon/thirdparty/Xlib/protocol/request.py @@ -2,30 +2,26 @@ # # Copyright (C) 2000-2002 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA # Xlib modules -from .. import X +from Xlib import X # Xlib.protocol modules -from . import rq -from . import structs +from Xlib.protocol import rq, structs class CreateWindow(rq.Request): @@ -787,7 +783,7 @@ class ListFontsWithInfo(rq.ReplyRequest): def __init__(self, *args, **keys): self._fonts = [] - rq.ReplyRequest.__init__(self, *args, **keys) + ReplyRequest.__init__(*(self, ) + args, **keys) def _parse_response(self, data): @@ -1066,7 +1062,7 @@ class PutImage(rq.Request): rq.Card8('left_pad'), rq.Card8('depth'), rq.Pad(2), - rq.Binary('data'), + rq.String8('data'), ) class GetImage(rq.ReplyRequest): @@ -1089,7 +1085,7 @@ class GetImage(rq.ReplyRequest): rq.ReplyLength(), rq.Card32('visual'), rq.Pad(20), - rq.Binary('data'), + rq.String8('data'), ) class PolyText8(rq.Request): @@ -1640,8 +1636,7 @@ class ChangeHosts(rq.Request): rq.Opcode(109), rq.Set('mode', 1, (X.HostInsert, X.HostDelete)), rq.RequestLength(), - rq.Set('host_family', 1, (X.FamilyInternet, X.FamilyDECnet, X.FamilyChaos, - X.FamilyServerInterpreted, X.FamilyInternetV6)), + rq.Set('host_family', 1, (X.FamilyInternet, X.FamilyDECnet, X.FamilyChaos)), rq.Pad(1), rq.LengthOf('host', 2), rq.List('host', rq.Card8Obj) diff --git a/Nagstamon/thirdparty/Xlib/protocol/rq.py b/Nagstamon/thirdparty/Xlib/protocol/rq.py index 86cb2def5..3bd342d54 100644 --- a/Nagstamon/thirdparty/Xlib/protocol/rq.py +++ b/Nagstamon/thirdparty/Xlib/protocol/rq.py @@ -2,48 +2,41 @@ # # Copyright (C) 2000-2002 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA - -# Standard modules +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +from array import array +import struct import sys import traceback -import struct -from array import array import types -# Python 2/3 compatibility. -from six import PY3, binary_type, byte2int, indexbytes, iterbytes - # Xlib modules -from .. import X -from ..support import lock +from Xlib import X +from Xlib.support import lock -def decode_string(bs): - return bs.decode('latin1') +_PY3 = sys.version[0] >= '3' -if PY3: - def encode_array(a): - return a.tobytes() +# in Python 3, bytes are an actual array; in python 2, bytes are still +# string-like, so in order to get an array element we need to call ord() +if _PY3: + def _bytes_item(x): + return x else: - def encode_array(a): - return a.tostring() - + def _bytes_item(x): + return ord(x) class BadDataError(Exception): pass @@ -65,6 +58,7 @@ class BadDataError(Exception): pass for c in 'bhil': size = array(c).itemsize + array_unsigned_codes[size] = c.upper() try: struct_to_array_codes[signed_codes[size]] = c @@ -75,7 +69,7 @@ class BadDataError(Exception): pass # print array_unsigned_codes, struct_to_array_codes -class Field(object): +class Field: """Field objects represent the data fields of a Struct. Field objects must have the following attributes: @@ -109,7 +103,9 @@ class Field(object): If `structcode' is None the Field must have the method f.parse_binary_value() instead. See its documentation string for details. + """ + name = None default = None @@ -138,15 +134,16 @@ def parse_binary_value(self, data, display, length, format): The decoded value is returned as VALUE, and the remaining part of DATA shold be returned as REMAINDATA. """ - raise RuntimeError('Neither structcode or parse_binary_value ' \ - 'provided for {0}'.format(self)) + + raise RuntimeError('Neither structcode or parse_binary_value provided for %s' + % self) class Pad(Field): def __init__(self, size): self.size = size self.value = b'\0' * size - self.structcode = '{0}x'.format(size) + self.structcode = '%dx' % size self.structvalues = 0 @@ -171,9 +168,7 @@ class LengthField(Field): may vary, e.g. List and String8. Its name should be the same as the name of the field whose size - it stores. The other_fields attribute can be used to specify the - names of other fields whose sizes are stored by this field, so - a single length field can set the length of multiple fields. + it stores. The lf.get_binary_value() method of LengthFields is not used, instead a lf.get_binary_length() should be provided. @@ -181,9 +176,9 @@ class LengthField(Field): Unless LengthField.get_binary_length() is overridden in child classes, there should also be a lf.calc_length(). """ + structcode = 'L' structvalues = 1 - other_fields = None def calc_length(self, length): """newlen = lf.calc_length(length) @@ -214,11 +209,7 @@ def calc_length(self, length): class LengthOf(LengthField): def __init__(self, name, size): - if isinstance(name, (list, tuple)): - self.name = name[0] - self.other_fields = name[1:] - else: - self.name = name + self.name = name self.structcode = unsigned_codes[size] @@ -296,9 +287,9 @@ def __init__(self, name, codes = (), default = None): self.codes = codes def check_value(self, value): - if hasattr(value, self.cast_function): + try: return getattr(value, self.cast_function)() - else: + except AttributeError: return value def parse_value(self, value, display): @@ -378,40 +369,12 @@ def __init__(self, name): X.SouthEastGravity)) -class FixedBinary(ValueField): +class FixedString(ValueField): structvalues = 1 def __init__(self, name, size): ValueField.__init__(self, name) - self.structcode = '{0}s'.format(size) - - -class Binary(ValueField): - structcode = None - - def __init__(self, name, pad = 1): - ValueField.__init__(self, name) - self.pad = pad - - def pack_value(self, val): - val_bytes = val - slen = len(val_bytes) - - if self.pad: - return val_bytes + b'\0' * ((4 - slen % 4) % 4), slen, None - else: - return val_bytes, slen, None - - def parse_binary_value(self, data, display, length, format): - if length is None: - return data, b'' - - if self.pad: - slen = length + ((4 - length % 4) % 4) - else: - slen = length - - return data[:length], data[slen:] + self.structcode = '%ds' % size class String8(ValueField): @@ -422,29 +385,34 @@ def __init__(self, name, pad = 1): self.pad = pad def pack_value(self, val): - if isinstance(val, bytes): - val_bytes = val - else: - val_bytes = val.encode() - slen = len(val_bytes) + slen = len(val) + + if _PY3 and type(val) is str: + val = val.encode('UTF-8') if self.pad: - return val_bytes + b'\0' * ((4 - slen % 4) % 4), slen, None + return val + b'\0' * ((4 - slen % 4) % 4), slen, None else: - return val_bytes, slen, None + return val, slen, None def parse_binary_value(self, data, display, length, format): if length is None: - return decode_string(data), b'' + try: + return data.decode('UTF-8'), b'' + except UnicodeDecodeError: + return data, b'' if self.pad: slen = length + ((4 - length % 4) % 4) else: slen = length - data_str = decode_string(data[:length]) - - return data_str, data[slen:] + s = data[:length] + try: + s = s.decode('UTF-8') + except UnicodeDecodeError: + pass # return as bytes + return s, data[slen:] class String16(ValueField): @@ -455,9 +423,9 @@ def __init__(self, name, pad = 1): self.pad = pad def pack_value(self, val): - """Convert 8-byte string into 16-byte list""" - if isinstance(val, bytes): - val = list(iterbytes(val)) + # Convert 8-byte string into 16-byte list + if type(val) is str: + val = [ord(c) for c in val] slen = len(val) @@ -466,7 +434,8 @@ def pack_value(self, val): else: pad = b'' - return struct.pack('>' + 'H' * slen, *val) + pad, slen, None + return (struct.pack(*('>' + 'H' * slen, ) + tuple(val)) + pad, + slen, None) def parse_binary_value(self, data, display, length, format): if length == 'odd': @@ -479,7 +448,8 @@ def parse_binary_value(self, data, display, length, format): else: slen = length - return struct.unpack('>' + 'H' * length, data[:length * 2]), data[slen * 2:] + return (struct.unpack('>' + 'H' * length, data[:length * 2]), + data[slen * 2:]) @@ -529,14 +499,18 @@ def parse_binary_value(self, data, display, length, format): ret = [None] * int(length) if self.type.structcode is None: - for i in range(0, length): + for i in range(length): ret[i], data = self.type.parse_binary(data, display) else: scode = '=' + self.type.structcode slen = struct.calcsize(scode) pos = 0 for i in range(0, length): - v = struct.unpack(scode, data[pos: pos + slen]) + # FIXME: remove try..except + try: + v = struct.unpack(scode, data[pos: pos + slen]) + except Exception: + v = b'\x00\x00\x00\x00' if self.type.structvalues == 1: v = v[0] @@ -558,10 +532,8 @@ def parse_binary_value(self, data, display, length, format): def pack_value(self, val): # Single-char values, we'll assume that means integer lists. if self.type.structcode and len(self.type.structcode) == 1: - if self.type.check_value is not None: - val = [self.type.check_value(v) for v in val] - a = array(struct_to_array_codes[self.type.structcode], val) - data = encode_array(a) + data = array(struct_to_array_codes[self.type.structcode], + val).tobytes() else: data = [] for v in val: @@ -591,6 +563,8 @@ def pack_value(self, val): class Object(ValueField): + structcode = None + def __init__(self, name, type, default = None): ValueField.__init__(self, name, default) self.type = type @@ -598,32 +572,43 @@ def __init__(self, name, type, default = None): self.structvalues = self.type.structvalues def parse_binary_value(self, data, display, length, format): - return self.type.parse_binary(data, display) + if self.type.structcode is None: + return self.type.parse_binary(data, display) + + else: + scode = '=' + self.type.structcode + slen = struct.calcsize(scode) + + v = struct.unpack(scode, data[:slen]) + if self.type.structvalues == 1: + v = v[0] + + if self.type.parse_value is not None: + v = self.type.parse_value(v, display) + + return v, data[slen:] def parse_value(self, val, display): - return self.type.parse_value(val, display) + if self.type.parse_value is None: + return val + else: + return self.type.parse_value(val, display) def pack_value(self, val): - return self.type.pack_value(val) + # Single-char values, we'll assume that mean an integer + if self.type.structcode and len(self.type.structcode) == 1: + return struct.pack('=' + self.type.structcode, val), None, None + else: + return self.type.pack_value(val) def check_value(self, val): - if isinstance(val, tuple): - vals = [] - i = 0 - for f in self.type.fields: - if f.name: - if f.check_value is None: - v = val[i] - else: - v = f.check_value(val[i]) - if f.structvalues == 1: - vals.append(v) - else: - vals.extend(v) - i = i + 1 - return vals + if self.type.structcode is None: + return val - if isinstance(val, dict): + if type(val) is tuple: + return val + + if type(val) is dict: data = val elif isinstance(val, DictWrapper): data = val._data @@ -633,14 +618,7 @@ def check_value(self, val): vals = [] for f in self.type.fields: if f.name: - if f.check_value is None: - v = data[f.name] - else: - v = f.check_value(data[f.name]) - if f.structvalues == 1: - vals.append(v) - else: - vals.extend(v) + vals.append(data[f.name]) return vals @@ -656,6 +634,7 @@ def parse_binary_value(self, data, display, length, format): if format == 0: ret = None + return ret, data elif format == 8: ret = (8, data[:length]) @@ -669,15 +648,24 @@ def parse_binary_value(self, data, display, length, format): ret = (32, array(array_unsigned_codes[4], data[:4 * length])) data = data[4 * length:] + if type(ret[1]) is bytes: + try: + ret = (ret[0], ret[1].decode('UTF-8')) + except UnicodeDecodeError: + pass # return as bytes + return ret, data def pack_value(self, value): fmt, val = value if fmt not in (8, 16, 32): - raise BadDataError('Invalid property data format {0}'.format(fmt)) + raise BadDataError('Invalid property data format %d' % fmt) + + if _PY3 and type(val) is str: + val = val.encode('UTF-8') - if isinstance(val, binary_type): + if type(val) is bytes: size = fmt // 8 vlen = len(val) if vlen % size: @@ -689,12 +677,11 @@ def pack_value(self, value): dlen = vlen // size else: - if isinstance(val, tuple): + if type(val) is tuple: val = list(val) size = fmt // 8 - a = array(array_unsigned_codes[size], val) - data = encode_array(a) + data = array(array_unsigned_codes[size], val).tobytes() dlen = len(val) dl = len(data) @@ -729,7 +716,7 @@ class ValueList(Field): def __init__(self, name, mask, pad, *fields): self.name = name - self.maskcode = '={0}{1}x'.format(unsigned_codes[mask], pad).encode() + self.maskcode = '=%s%dx' % (unsigned_codes[mask], pad) self.maskcodelen = struct.calcsize(self.maskcode) self.fields = [] @@ -794,7 +781,7 @@ def parse_binary_value(self, data, display, length, format): else: dlen = 4 * length * format - a = array(array_unsigned_codes[4], bytes(data[:dlen])) + a = array(array_unsigned_codes[4], data[:dlen]) ret = [] for i in range(0, len(a), format): @@ -815,7 +802,7 @@ def pack_value(self, value): for i in range(len(v), keycodes): a.append(X.NoSymbol) - return encode_array(a), len(value), keycodes + return a.tobytes(), len(value), keycodes class ModifierMapping(ValueField): @@ -846,7 +833,7 @@ def pack_value(self, value): for i in range(len(v), keycodes): a.append(0) - return encode_array(a), len(value), keycodes + return a.tobytes(), len(value), keycodes class EventField(ValueField): structcode = None @@ -858,12 +845,9 @@ def pack_value(self, value): return value._binary, None, None def parse_binary_value(self, data, display, length, format): - from . import event + from Xlib.protocol import event - estruct = display.event_classes.get(byte2int(data) & 0x7f, event.AnyEvent) - if type(estruct) == dict: - # this etype refers to a set of sub-events with individual subcodes - estruct = estruct[indexbytes(data, 1)] + estruct = display.event_classes.get(_bytes_item(data[0]) & 0x7f, event.AnyEvent) return estruct(display = display, binarydata = data[:32]), data[32:] @@ -873,24 +857,22 @@ def parse_binary_value(self, data, display, length, format): # Struct is also usable. # -class ScalarObj(object): +class ScalarObj: def __init__(self, code): self.structcode = code self.structvalues = 1 self.parse_value = None - self.check_value = None Card8Obj = ScalarObj('B') Card16Obj = ScalarObj('H') Card32Obj = ScalarObj('L') -class ResourceObj(object): +class ResourceObj: structcode = 'L' structvalues = 1 def __init__(self, class_name): self.class_name = class_name - self.check_value = None def parse_value(self, value, display): # if not display: @@ -904,20 +886,31 @@ def parse_value(self, value, display): WindowObj = ResourceObj('window') ColormapObj = ResourceObj('colormap') -class StrClass(object): +class StrClass: structcode = None def pack_value(self, val): - return (chr(len(val)) + val).encode() + if type(val) is not bytes: + val = val.encode('UTF-8') + if _PY3: + val = bytes([len(val)]) + val + else: + val = chr(len(val)) + val + return val def parse_binary(self, data, display): - slen = byte2int(data) + 1 - return decode_string(data[1:slen]), data[slen:] + slen = _bytes_item(data[0]) + 1 + s = data[1:slen] + try: + s = s.decode('UTF-8') + except UnicodeDecodeError: + pass # return as bytes + return s, data[slen:] Str = StrClass() -class Struct(object): +class Struct: """Struct objects represents a binary data structure. It can contain both fields with static and dynamic sizes. However, all @@ -937,6 +930,7 @@ class Field. The fields of a structure are given as arguments These functions will be generated dynamically for each Struct object to make conversion as fast as possible. They are generated the first time the methods are called. + """ def __init__(self, *fields): @@ -981,6 +975,7 @@ def __init__(self, *fields): # object def to_binary(self, *varargs, **keys): + """data = s.to_binary(...) Convert Python values into the binary representation. The @@ -989,86 +984,161 @@ def to_binary(self, *varargs, **keys): exception: fields with default arguments will be last. Returns the binary representation as the string DATA. + """ - # Emulate Python function argument handling with our field names - names = [f.name for f in self.fields \ - if isinstance(f, ValueField) and f.name] - field_args = dict(zip(names, varargs)) - if set(field_args).intersection(keys): - dupes = ", ".join(set(field_args).intersection(keys)) - raise TypeError("{0} arguments were passed both positionally and by keyword".format(dupes)) - field_args.update(keys) - for f in self.fields: - if f.name and (f.name not in field_args): - if f.default is None: - raise TypeError("Missing required argument {0}".format(f.name)) - field_args[f.name] = f.default - # /argument handling + + code = '' + total_length = str(self.static_size) + joins = [] + args = [] + defargs = [] + kwarg = 0 # First pack all varfields so their lengths and formats are # available when we pack their static LengthFields and # FormatFields - total_length = self.static_size - var_vals = {} - lengths = {} - formats = {} - + i = 0 for f in self.var_fields: if f.keyword_args: - v, l, fm = f.pack_value(field_args[f.name], keys) + kwarg = 1 + kw = ', _keyword_args' else: - v, l, fm = f.pack_value(field_args[f.name]) - var_vals[f.name] = v - lengths[f.name] = l - formats[f.name] = fm + kw = '' + + # Call pack_value method for each field, storing + # the return values for later use + code = code + (' _%(name)s, _%(name)s_length, _%(name)s_format' + ' = self.var_fields[%(fno)d].pack_value(%(name)s%(kw)s)\n' + % { 'name': f.name, + 'fno': i, + 'kw': kw }) + + total_length = total_length + ' + len(_%s)' % f.name + joins.append('_%s' % f.name) - total_length += len(v) + i = i + 1 - # Construct item list for struct.pack call, packing all static fields. - pack_items = [] + # Construct argument list for struct.pack call, packing all + # static fields. First argument is the structcode, the + # remaining are values. + + pack_args = ['"%s"' % self.static_codes] + + i = 0 for f in self.static_fields: if isinstance(f, LengthField): # If this is a total length field, insert # the calculated field value here if isinstance(f, TotalLengthField): - pack_items.append(f.calc_length(total_length)) + if self.var_fields: + pack_args.append('self.static_fields[%d].calc_length(%s)' + % (i, total_length)) + else: + pack_args.append(str(f.calc_length(self.static_size))) else: - pack_items.append(f.calc_length(lengths[f.name])) + pack_args.append('self.static_fields[%d].calc_length(_%s_length)' + % (i, f.name)) # Format field, just insert the value we got previously elif isinstance(f, FormatField): - pack_items.append(formats[f.name]) + pack_args.append('_%s_format' % f.name) # A constant field, insert its value directly elif isinstance(f, ConstantField): - pack_items.append(f.value) + pack_args.append(str(f.value)) # Value fields else: if f.structvalues == 1: + # If there's a value check/convert function, call it if f.check_value is not None: - pack_items.append(f.check_value(field_args[f.name])) + pack_args.append('self.static_fields[%d].check_value(%s)' + % (i, f.name)) + # Else just use the argument as provided else: - pack_items.append(field_args[f.name]) + pack_args.append(f.name) # Multivalue field. Handled like single valuefield, - # but the value are tuple unpacked into separate arguments - # which are appended to pack_items + # but the value are tuple unpacked into seperate arguments + # which are appended to pack_args else: + a = [] + for j in range(f.structvalues): + a.append('_%s_%d' % (f.name, j)) + if f.check_value is not None: - pack_items.extend(f.check_value(field_args[f.name])) + code = code + (' %s = self.static_fields[%d].check_value(%s)\n' + % (', '.join(a), i, f.name)) else: - pack_items.extend(field_args[f.name]) + code = code + ' %s = %s\n' % (', '.join(a), f.name) + + pack_args = pack_args + a + + # Add field to argument list + if f.name: + if f.default is None: + args.append(f.name) + else: + defargs.append('%s = %s' % (f.name, repr(f.default))) + + i = i + 1 + + # Construct call to struct.pack + pack = 'struct.pack(%s)' % ', '.join(pack_args) + + # If there are any varfields, we append the packed strings to build + # the resulting binary value + if self.var_fields: + code = code + ' return %s + %s\n' % (pack, ' + '.join(joins)) + + # If there's only static fields, return the packed value + else: + code = code + ' return %s\n' % pack + + # Add all varsize fields to argument list. We do it here + # to ensure that they appear after the static fields. + for f in self.var_fields: + if f.name: + if f.default is None: + args.append(f.name) + else: + defargs.append('%s = %s' % (f.name, repr(f.default))) + + args = args + defargs + if kwarg: + args.append('**_keyword_args') - static_part = struct.pack(self.static_codes, *pack_items) - var_parts = [var_vals[f.name] for f in self.var_fields] - return static_part + b''.join(var_parts) + # Add function header + code = 'def to_binary(self, %s):\n' % ', '.join(args) + code + + # self._pack_code = code + + # print + # print code + # print + + # Finally, compile function by evaluating it. This will store + # the function in the local variable to_binary, thanks to the + # def: line. Convert it into a instance metod bound to self, + # and store it in self. + + # Unfortunately, this creates a circular reference. However, + # Structs are not really created dynamically so the potential + # memory leak isn't that serious. Besides, Python 2.0 has + # real garbage collect. + + g = globals().copy() + exec(code, g) + self.to_binary = types.MethodType(g['to_binary'], self) + + # Finally call it manually + return self.to_binary(*varargs, **keys) def pack_value(self, value): @@ -1080,11 +1150,11 @@ def pack_value(self, value): """ if type(value) is tuple: - return self.to_binary(*value) - elif isinstance(value, dict): - return self.to_binary(**value) + return self.to_binary(*value, **{}) + elif type(value) is dict: + return self.to_binary(*(), **value) elif isinstance(value, DictWrapper): - return self.to_binary(**value._data) + return self.to_binary(*(), **value._data) else: raise BadDataError('%s is not a tuple or a list' % (value)) @@ -1095,8 +1165,12 @@ def parse_value(self, val, display, rawdict = 0): Struct objects with no var_fields into Python values. """ - ret = {} + + code = ('def parse_value(self, val, display, rawdict = 0):\n' + ' ret = {}\n') + vno = 0 + fno = 0 for f in self.static_fields: # Fields without names should be ignored, and there should # not be any length or format fields if this function @@ -1115,22 +1189,43 @@ def parse_value(self, val, display, rawdict = 0): # Value fields else: - # If this field has a parse_value method, call it, otherwise - # use the unpacked value as is. + + # Get the index or range in val representing this field. + if f.structvalues == 1: - field_val = val[vno] + vrange = str(vno) else: - field_val = val[vno:vno+f.structvalues] + vrange = '%d:%d' % (vno, vno + f.structvalues) - if f.parse_value is not None: - field_val = f.parse_value(field_val, display, rawdict=rawdict) - ret[f.name] = field_val + # If this field has a parse_value method, call it, otherwise + # use the unpacked value as is. + if f.parse_value is None: + code = code + ' ret["%s"] = val[%s]\n' % (f.name, vrange) + else: + code = code + (' ret["%s"] = self.static_fields[%d].' + 'parse_value(val[%s], display)\n' + % (f.name, fno, vrange)) + + fno = fno + 1 vno = vno + f.structvalues - if not rawdict: - return DictWrapper(ret) - return ret + code = code + ' if not rawdict: return DictWrapper(ret)\n' + code = code + ' return ret\n' + + # print + # print code + # print + + # Finally, compile function as for to_binary. + + g = globals().copy() + exec(code, g) + self.parse_value = types.MethodType(g['parse_value'], self) + + # Call it manually + return self.parse_value(val, display, rawdict) + def parse_binary(self, data, display, rawdict = 0): @@ -1151,12 +1246,17 @@ def parse_binary(self, data, display, rawdict = 0): REMDATA are the remaining binary data, unused by the Struct object. """ - ret = {} - val = struct.unpack(self.static_codes, data[:self.static_size]) + + code = ('def parse_binary(self, data, display, rawdict = 0):\n' + ' ret = {}\n' + ' val = struct.unpack("%s", data[:%d])\n' + % (self.static_codes, self.static_size)) + lengths = {} formats = {} vno = 0 + fno = 0 for f in self.static_fields: # Fields without name should be ignored. This is typically @@ -1169,45 +1269,63 @@ def parse_binary(self, data, display, rawdict = 0): # when treating varfields. elif isinstance(f, LengthField): - f_names = [f.name] - if f.other_fields: - f_names.extend(f.other_fields) - field_val = val[vno] - if f.parse_value is not None: - field_val = f.parse_value(field_val, display) - for f_name in f_names: - lengths[f_name] = field_val + if f.parse_value is None: + lengths[f.name] = 'val[%d]' % vno + else: + lengths[f.name] = ('self.static_fields[%d].' + 'parse_value(val[%d], display)' + % (fno, vno)) elif isinstance(f, FormatField): - formats[f.name] = val[vno] + formats[f.name] = 'val[%d]' % vno # Treat value fields the same was as in parse_value. else: if f.structvalues == 1: - field_val = val[vno] + vrange = str(vno) else: - field_val = val[vno:vno+f.structvalues] + vrange = '%d:%d' % (vno, vno + f.structvalues) - if f.parse_value is not None: - field_val = f.parse_value(field_val, display) - ret[f.name] = field_val + if f.parse_value is None: + code = code + ' ret["%s"] = val[%s]\n' % (f.name, vrange) + else: + code = code + (' ret["%s"] = self.static_fields[%d].' + 'parse_value(val[%s], display)\n' + % (f.name, fno, vrange)) + fno = fno + 1 vno = vno + f.structvalues - data = data[self.static_size:] + code = code + ' data = data[%d:]\n' % self.static_size # Call parse_binary_value for each var_field, passing the # length and format values from the unpacked val. + fno = 0 for f in self.var_fields: - ret[f.name], data = f.parse_binary_value(data, display, - lengths.get(f.name), - formats.get(f.name), - ) + code = code + (' ret["%s"], data = ' + 'self.var_fields[%d].parse_binary_value' + '(data, display, %s, %s)\n' + % (f.name, fno, + lengths.get(f.name, 'None'), + formats.get(f.name, 'None'))) + fno = fno + 1 - if not rawdict: - ret = DictWrapper(ret) - return ret, data + code = code + ' if not rawdict: ret = DictWrapper(ret)\n' + code = code + ' return ret, data\n' + + # print + # print code + # print + + # Finally, compile function as for to_binary. + + g = globals().copy() + exec(code, g) + self.parse_binary = types.MethodType(g['parse_binary'], self) + + # Call it manually + return self.parse_binary(data, display, rawdict) class TextElements8(ValueField): @@ -1221,33 +1339,37 @@ def pack_value(self, value): for v in value: # Let values be simple strings, meaning a delta of 0 - if type(v) in (str, bytes): + if _PY3 and type(v) is str: + v = v.encode('UTF-8') + + if type(v) is bytes: v = (0, v) # A tuple, it should be (delta, string) # Encode it as one or more textitems - if isinstance(v, (tuple, dict, DictWrapper)): + if type(v) in (tuple, dict) or \ + isinstance(v, DictWrapper): - if isinstance(v, tuple): - delta, m_str = v + if type(v) is tuple: + delta, s = v else: delta = v['delta'] - m_str = v['string'] + s = v['string'] - while delta or m_str: + while delta or s: args['delta'] = delta - args['string'] = m_str[:254] + args['string'] = s[:254] data = data + self.string_textitem.to_binary(*(), **args) delta = 0 - m_str = m_str[254:] + s = s[254:] # Else an integer, i.e. a font change else: # Use fontable cast function if instance - if isinstance(v, Fontable): + if hasattr(v, '__fontable__'): v = v.__fontable__() data = data + struct.pack('>BL', 255, v) @@ -1263,12 +1385,12 @@ def parse_binary_value(self, data, display, length, format): break # font change - if byte2int(data) == 255: - values.append(struct.unpack('>L', bytes(data[1:5]))[0]) + if _bytes_item(data[0]) == 255: + values.append(struct.unpack('>L', data[1:5])[0]) data = data[5:] # skip null strings - elif byte2int(data) == 0 and indexbytes(data, 1) == 0: + elif _bytes_item(data[0]) == 0 and _bytes_item(data[1]) == 0: data = data[2:] # string with delta @@ -1276,7 +1398,7 @@ def parse_binary_value(self, data, display, length, format): v, data = self.string_textitem.parse_binary(data, display) values.append(v) - return values, '' + return values, b'' @@ -1320,19 +1442,7 @@ def __str__(self): return str(self._data) def __repr__(self): - return '%s(%s)' % (self.__class__.__name__, repr(self._data)) - - def __lt__(self, other): - if isinstance(other, DictWrapper): - return self._data < other._data - else: - return self._data < other - - def __gt__(self, other): - if isinstance(other, DictWrapper): - return self._data > other._data - else: - return self._data > other + return '%s(%s)' % (self.__class__, repr(self._data)) def __eq__(self, other): if isinstance(other, DictWrapper): @@ -1340,8 +1450,10 @@ def __eq__(self, other): else: return self._data == other + def __ne__(self, other): + return not self.__eq__(other) -class Request(object): +class Request: def __init__(self, display, onerror = None, *args, **keys): self._errorhandler = onerror self._binary = self._request.to_binary(*args, **keys) @@ -1400,7 +1512,7 @@ def _set_error(self, error): return 1 def __repr__(self): - return '<%s serial = %s, data = %s, error = %s>' % (self.__class__.__name__, self._serial, self._data, self._error) + return '<%s serial = %s, data = %s, error = %s>' % (self.__class__, self._serial, self._data, self._error) class Event(GetAttrData): @@ -1419,7 +1531,7 @@ def __init__(self, binarydata = None, display = None, keys['sequence_number'] = 0 - self._binary = self._fields.to_binary(**keys) + self._binary = self._fields.to_binary(*(), **keys) keys['send_event'] = 0 self._data = keys @@ -1434,26 +1546,13 @@ def __repr__(self): kwlist.append('%s = %s' % (kw, repr(val))) kws = ', '.join(kwlist) - return '%s(%s)' % (self.__class__.__name__, kws) - - def __lt__(self, other): - if isinstance(other, Event): - return self._data < other._data - else: - return self._data < other - - def __gt__(self, other): - if isinstance(other, Event): - return self._data > other._data - else: - return self._data > other + return '%s(%s)' % (self.__class__, kws) def __eq__(self, other): if isinstance(other, Event): return self._data == other._data else: - return self._data == other - + return cmp(self._data, other) def call_error_handler(handler, error, request): try: diff --git a/Nagstamon/thirdparty/Xlib/protocol/structs.py b/Nagstamon/thirdparty/Xlib/protocol/structs.py index 1661440d6..bc548f67d 100644 --- a/Nagstamon/thirdparty/Xlib/protocol/structs.py +++ b/Nagstamon/thirdparty/Xlib/protocol/structs.py @@ -2,28 +2,25 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA # Xlib modules -from .. import X +from Xlib import X # Xlib.protocol modules -from . import rq +from Xlib.protocol import rq def WindowValues(arg): return rq.ValueList( arg, 4, 0, diff --git a/Nagstamon/thirdparty/Xlib/rdb.py b/Nagstamon/thirdparty/Xlib/rdb.py index 03b06e2a2..773158a95 100644 --- a/Nagstamon/thirdparty/Xlib/rdb.py +++ b/Nagstamon/thirdparty/Xlib/rdb.py @@ -2,22 +2,19 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA # See end of file for an explanation of the algorithm and @@ -25,11 +22,12 @@ # Standard modules +import functools import re import sys # Xlib modules -from .support import lock +from Xlib.support import lock # Set up a few regexpes for parsing string representation of resources @@ -50,7 +48,7 @@ class OptionError(Exception): pass -class ResourceDB(object): +class ResourceDB: def __init__(self, file = None, string = None, resources = None): self.db = {} self.lock = lock.allocate_lock() @@ -70,7 +68,7 @@ def insert_file(self, file): """ - if type(file) is bytes: + if type(file) is str: file = open(file, 'r') self.insert_string(file.read()) @@ -190,15 +188,15 @@ def insert(self, resource, value): self.lock.release() - def __getitem__(self, keys_tuple): + def __getitem__(self, nc): """db[name, class] Return the value matching the resource identified by NAME and CLASS. If no match is found, KeyError is raised. """ + name, cls = nc # Split name and class into their parts - name, cls = keys_tuple namep = name.split('.') clsp = cls.split('.') @@ -378,7 +376,8 @@ def getopt(self, name, argv, opts): return argv -class _Match(object): +@functools.total_ordering +class _Match: def __init__(self, path, dbs): self.path = path @@ -390,15 +389,12 @@ def __init__(self, path, dbs): self.skip = 1 self.db = dbs - def __lt__(self, other): - return self.path < other.path - - def __gt__(self, other): - return self.path > other.path - def __eq__(self, other): return self.path == other.path + def __lt__(self, other): + return self.path < other.path + def match_length(self): return len(self.path) @@ -559,7 +555,7 @@ def output_escape(value): # Option type definitions # -class Option(object): +class Option: def __init__(self): pass @@ -699,7 +695,7 @@ def get_display_opts(options, argv = sys.argv): # the resource object. # Example: Inserting "foo.bar*gazonk: yep" into an otherwise empty -# resource database would give the following structure: +# resource database would give the folliwing structure: # { 'foo': ( { 'bar': ( { }, # { 'gazonk': ( { }, diff --git a/Nagstamon/thirdparty/Xlib/support/__init__.py b/Nagstamon/thirdparty/Xlib/support/__init__.py index 4c0d622bf..40d05b7d0 100644 --- a/Nagstamon/thirdparty/Xlib/support/__init__.py +++ b/Nagstamon/thirdparty/Xlib/support/__init__.py @@ -2,22 +2,19 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA __all__ = [ 'lock', diff --git a/Nagstamon/thirdparty/Xlib/support/connect.py b/Nagstamon/thirdparty/Xlib/support/connect.py index 4db4c2f47..ca8e68b22 100644 --- a/Nagstamon/thirdparty/Xlib/support/connect.py +++ b/Nagstamon/thirdparty/Xlib/support/connect.py @@ -2,25 +2,21 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA import sys -import importlib # List the modules which contain the corresponding functions @@ -46,57 +42,50 @@ # Figure out which OS we're using. # sys.platform is either "OS-ARCH" or just "OS". -_parts = sys.platform.split('-') -platform = _parts[0] -del _parts - - -def _relative_import(modname): - return importlib.import_module('..' + modname, __name__) +platform = sys.platform.split('-')[0] def get_display(display): - """dname, protocol, host, dno, screen = get_display(display) + """dname, host, dno, screen = get_display(display) Parse DISPLAY into its components. If DISPLAY is None, use the default display. The return values are: - DNAME -- the full display name (string) - PROTOCOL -- the protocol to use (None if automatic) - HOST -- the host name (string, possibly empty) - DNO -- display number (integer) - SCREEN -- default screen number (integer) + DNAME -- the full display name (string) + HOST -- the host name (string, possibly empty) + DNO -- display number (integer) + SCREEN -- default screen number (integer) """ modname = _display_mods.get(platform, _default_display_mod) - mod = _relative_import(modname) + mod = __import__(modname, globals(),level=1) return mod.get_display(display) -def get_socket(dname, protocol, host, dno): - """socket = get_socket(dname, protocol, host, dno) +def get_socket(dname, host, dno): + """socket = get_socket(dname, host, dno) - Connect to the display specified by DNAME, PROTOCOL, HOST and DNO, which + Connect to the display specified by DNAME, HOST and DNO, which are the corresponding values from a previous call to get_display(). Return SOCKET, a new socket object connected to the X server. """ modname = _socket_mods.get(platform, _default_socket_mod) - mod = _relative_import(modname) - return mod.get_socket(dname, protocol, host, dno) + mod = __import__(modname, globals(),level=1) + return mod.get_socket(dname, host, dno) -def get_auth(sock, dname, protocol, host, dno): - """auth_name, auth_data = get_auth(sock, dname, protocol, host, dno) +def get_auth(sock, dname, host, dno): + """auth_name, auth_data = get_auth(sock, dname, host, dno) Return authentication data for the display on the other side of - SOCK, which was opened with DNAME, HOST and DNO, using PROTOCOL. + SOCK, which was opened with DNAME, HOST and DNO. Return AUTH_NAME and AUTH_DATA, two strings to be used in the connection setup request. """ modname = _auth_mods.get(platform, _default_auth_mod) - mod = _relative_import(modname) - return mod.get_auth(sock, dname, protocol, host, dno) + mod = __import__(modname, globals(),level=1) + return mod.get_auth(sock, dname, host, dno) diff --git a/Nagstamon/thirdparty/Xlib/support/lock.py b/Nagstamon/thirdparty/Xlib/support/lock.py index 6eee31f40..4398f428f 100644 --- a/Nagstamon/thirdparty/Xlib/support/lock.py +++ b/Nagstamon/thirdparty/Xlib/support/lock.py @@ -2,24 +2,21 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA - -class _DummyLock(object): +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +class _DummyLock: def __init__(self): # This might be nerdy, but by assigning methods like this diff --git a/Nagstamon/thirdparty/Xlib/support/unix_connect.py b/Nagstamon/thirdparty/Xlib/support/unix_connect.py index c2261dae5..10ec0a141 100644 --- a/Nagstamon/thirdparty/Xlib/support/unix_connect.py +++ b/Nagstamon/thirdparty/Xlib/support/unix_connect.py @@ -1,160 +1,108 @@ # Xlib.support.unix_connect -- Unix-type display connection functions # # Copyright (C) 2000,2002 Peter Liljenberg <petli@ctrl-c.liu.se> +# Copyright (C) 2013 LiuLang <gsushzhsosgsu@gmail.com> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA -import re +import fcntl import os import platform +import re import socket -# FCNTL is deprecated from Python 2.2, so only import it if we doesn't -# get the names we need. Furthermore, FD_CLOEXEC seems to be missing -# in Python 2.2. - -import fcntl - -if hasattr(fcntl, 'F_SETFD'): - F_SETFD = fcntl.F_SETFD - if hasattr(fcntl, 'FD_CLOEXEC'): - FD_CLOEXEC = fcntl.FD_CLOEXEC - else: - FD_CLOEXEC = 1 -else: - from FCNTL import F_SETFD, FD_CLOEXEC - +F_SETFD = fcntl.F_SETFD +FD_CLOEXEC = fcntl.FD_CLOEXEC from Xlib import error, xauth - -SUPPORTED_PROTOCOLS = (None, 'tcp', 'unix') - -# Darwin funky socket. uname = platform.uname() if (uname[0] == 'Darwin') and ([int(x) for x in uname[2].split('.')] >= [9, 0]): - SUPPORTED_PROTOCOLS += ('darwin',) - DARWIN_DISPLAY_RE = re.compile(r'^/private/tmp/[-:a-zA-Z0-9._]*:(?P<dno>[0-9]+)(\.(?P<screen>[0-9]+))?$') -DISPLAY_RE = re.compile(r'^((?P<proto>tcp|unix)/)?(?P<host>[-:a-zA-Z0-9._]*):(?P<dno>[0-9]+)(\.(?P<screen>[0-9]+))?$') + display_re = re.compile(r'^([-a-zA-Z0-9._/]*):([0-9]+)(\.([0-9]+))?$') + +else: + display_re = re.compile(r'^([-a-zA-Z0-9._]*):([0-9]+)(\.([0-9]+))?$') def get_display(display): # Use $DISPLAY if display isn't provided if display is None: display = os.environ.get('DISPLAY', '') - re_list = [(DISPLAY_RE, {})] - - if 'darwin' in SUPPORTED_PROTOCOLS: - re_list.insert(0, (DARWIN_DISPLAY_RE, {'protocol': 'darwin'})) - - for re, defaults in re_list: - m = re.match(display) - if m is not None: - protocol, host, dno, screen = [ - m.groupdict().get(field, defaults.get(field)) - for field in ('proto', 'host', 'dno', 'screen') - ] - break - else: + m = display_re.match(display) + if not m: raise error.DisplayNameError(display) - if protocol == 'tcp' and not host: - # Host is mandatory when protocol is TCP. - raise error.DisplayNameError(display) - - dno = int(dno) + name = display + host = m.group(1) + if host == 'unix': + host = '' + dno = int(m.group(2)) + screen = m.group(4) if screen: screen = int(screen) else: screen = 0 - return display, protocol, host, dno, screen - + return name, host, dno, screen -def _get_tcp_socket(host, dno): - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.connect((host, 6000 + dno)) - return s -def _get_unix_socket(address): - s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - s.connect(address) - return s - -def get_socket(dname, protocol, host, dno): - assert protocol in SUPPORTED_PROTOCOLS +def get_socket(dname, host, dno): try: - # Darwin funky socket. - if protocol == 'darwin': - s = _get_unix_socket(dname) + # Darwin funky socket + if (uname[0] == 'Darwin') and host and host.startswith('/tmp/'): + s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + s.connect(dname) - # TCP socket, note the special case: `unix:0.0` is equivalent to `:0.0`. - elif (protocol is None or protocol != 'unix') and host and host != 'unix': - s = _get_tcp_socket(host, dno) + # If hostname (or IP) is provided, use TCP socket + elif host: + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect((host, 6000 + dno)) - # Unix socket. + # Else use Unix socket else: - address = '/tmp/.X11-unix/X%d' % dno - if not os.path.exists(address): - # Use abstract address. - address = '\0' + address - try: - s = _get_unix_socket(address) - except socket.error: - if not protocol and not host: - # If no protocol/host was specified, fallback to TCP. - s = _get_tcp_socket(host, dno) - else: - raise - except socket.error as val: + s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + s.connect('/tmp/.X11-unix/X%d' % dno) + except OSError as val: raise error.DisplayConnectionError(dname, str(val)) - # Make sure that the connection isn't inherited in child processes. + # Make sure that the connection isn't inherited in child processes fcntl.fcntl(s.fileno(), F_SETFD, FD_CLOEXEC) return s -def new_get_auth(sock, dname, protocol, host, dno): - assert protocol in SUPPORTED_PROTOCOLS +def new_get_auth(sock, dname, host, dno): # Translate socket address into the xauth domain - if protocol == 'darwin': + if (uname[0] == 'Darwin') and host and host.startswith('/tmp/'): family = xauth.FamilyLocal addr = socket.gethostname() - elif protocol == 'tcp': + elif host: family = xauth.FamilyInternet # Convert the prettyprinted IP number into 4-octet string. # Sometimes these modules are too damn smart... octets = sock.getpeername()[0].split('.') - addr = bytearray(int(x) for x in octets) + addr = ''.join(map(lambda x: chr(int(x)), octets)) else: family = xauth.FamilyLocal - addr = socket.gethostname().encode() - - try: - au = xauth.Xauthority() - except error.XauthError: - return b'', b'' + addr = socket.gethostname() + au = xauth.Xauthority() while 1: try: return au.get_best_auth(family, addr, dno) @@ -165,16 +113,16 @@ def new_get_auth(sock, dname, protocol, host, dno): # $DISPLAY to localhost:10, but stores the xauth cookie as if # DISPLAY was :10. Hence, if localhost and not found, try # again as a Unix socket. - if family == xauth.FamilyInternet and addr == b'\x7f\x00\x00\x01': + if family == xauth.FamilyInternet and addr == '\x7f\x00\x00\x01': family = xauth.FamilyLocal - addr = socket.gethostname().encode() + addr = socket.gethostname() else: - return b'', b'' + return '', '' def old_get_auth(sock, dname, host, dno): # Find authorization cookie - auth_name = auth_data = b'' + auth_name = auth_data = '' try: # We could parse .Xauthority, but xauth is simpler @@ -191,7 +139,7 @@ def old_get_auth(sock, dname, host, dno): if len(parts) == 3: auth_name = parts[1] hexauth = parts[2] - auth = b'' + auth = '' # Translate hexcode into binary for i in range(0, len(hexauth), 2): @@ -201,6 +149,14 @@ def old_get_auth(sock, dname, host, dno): except os.error: pass + if not auth_data and host == 'localhost': + # 127.0.0.1 counts as FamilyLocal, not FamilyInternet + # See Xtransutil.c:ConvertAddress. + # There might be more ways to spell 127.0.0.1 but + # 'localhost', yet this code fixes a the case of + # OpenSSH tunneling X. + return get_auth('unix:%d' % dno, 'unix', dno) + return auth_name, auth_data get_auth = new_get_auth diff --git a/Nagstamon/thirdparty/Xlib/support/vms_connect.py b/Nagstamon/thirdparty/Xlib/support/vms_connect.py index 3c53695f3..26d9eca90 100644 --- a/Nagstamon/thirdparty/Xlib/support/vms_connect.py +++ b/Nagstamon/thirdparty/Xlib/support/vms_connect.py @@ -2,22 +2,19 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA import re import socket @@ -32,7 +29,7 @@ def get_display(display): # check DECW$DISPLAY instead, but that has to wait if display is None: - return ':0.0', None, 'localhost', 0, 0 + return ':0.0', 'localhost', 0, 0 m = display_re.match(display) if not m: @@ -52,10 +49,10 @@ def get_display(display): else: screen = 0 - return name, None, host, dno, screen + return name, host, dno, screen -def get_socket(dname, protocol, host, dno): +def get_socket(dname, host, dno): try: # Always use TCP/IP sockets. Later it would be nice to # be able to use DECNET och LOCAL connections. @@ -63,7 +60,7 @@ def get_socket(dname, protocol, host, dno): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((host, 6000 + dno)) - except socket.error as val: + except OSError as val: raise error.DisplayConnectionError(dname, str(val)) return s diff --git a/Nagstamon/thirdparty/Xlib/threaded.py b/Nagstamon/thirdparty/Xlib/threaded.py index 44fcafebe..a9c6fc0f7 100644 --- a/Nagstamon/thirdparty/Xlib/threaded.py +++ b/Nagstamon/thirdparty/Xlib/threaded.py @@ -2,27 +2,29 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA -from six.moves import _thread +try: + # Python 3 + import _thread as thread +except ImportError: + # Python 2 + import thread # We change the allocate_lock function in Xlib.support.lock to # return a basic Python lock, instead of the default dummy lock from Xlib.support import lock -lock.allocate_lock = _thread.allocate_lock +lock.allocate_lock = thread.allocate_lock diff --git a/Nagstamon/thirdparty/Xlib/xauth.py b/Nagstamon/thirdparty/Xlib/xauth.py index 303bd4911..921985626 100644 --- a/Nagstamon/thirdparty/Xlib/xauth.py +++ b/Nagstamon/thirdparty/Xlib/xauth.py @@ -1,23 +1,21 @@ # Xlib.xauth -- ~/.Xauthority access # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> +# Copyright (C) 2013 LiuLang <gsushzhsosgsu@gmail.com> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA import os import struct @@ -27,11 +25,9 @@ FamilyInternet = X.FamilyInternet FamilyDECnet = X.FamilyDECnet FamilyChaos = X.FamilyChaos -FamilyServerInterpreted = X.FamilyServerInterpreted -FamilyInternetV6 = X.FamilyInternetV6 FamilyLocal = 256 -class Xauthority(object): +class Xauthority: def __init__(self, filename = None): if filename is None: filename = os.environ.get('XAUTHORITY') @@ -44,10 +40,9 @@ def __init__(self, filename = None): '$HOME not set, cannot find ~/.Xauthority') try: - with open(filename, 'rb') as fp: - raw = fp.read() - except IOError as err: - raise error.XauthError('could not read from {0}: {1}'.format(filename, err)) + raw = open(filename, 'rb').read() + except OSError as err: + raise error.XauthError('~/.Xauthority: %s' % err) self.entries = [] @@ -87,9 +82,9 @@ def __init__(self, filename = None): if len(data) != length: break - self.entries.append((family, addr, num, name, data)) - except struct.error: - print("Xlib.xauth: warning, failed to parse part of xauthority file {0}, aborting all further parsing".format(filename)) + self.entries.append((family, addr, num, name, data, )) + except struct.error as e: + print("Xlib.xauth: warning, failed to parse part of xauthority file (%s), aborting all further parsing" % filename) if len(self.entries) == 0: print("Xlib.xauth: warning, no xauthority details available") @@ -116,12 +111,11 @@ def get_best_auth(self, family, address, dispno, """ num = str(dispno).encode() + address = address.encode() matches = {} for efam, eaddr, enum, ename, edata in self.entries: - if enum == b'' and ename not in matches: - enum = num if efam == family and eaddr == address and num == enum: matches[ename] = edata diff --git a/Nagstamon/thirdparty/Xlib/xobject/__init__.py b/Nagstamon/thirdparty/Xlib/xobject/__init__.py index 67d325493..7375b37d4 100644 --- a/Nagstamon/thirdparty/Xlib/xobject/__init__.py +++ b/Nagstamon/thirdparty/Xlib/xobject/__init__.py @@ -2,22 +2,19 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA __all__ = [ 'colormap', diff --git a/Nagstamon/thirdparty/Xlib/xobject/colormap.py b/Nagstamon/thirdparty/Xlib/xobject/colormap.py index 033fb4936..954502837 100644 --- a/Nagstamon/thirdparty/Xlib/xobject/colormap.py +++ b/Nagstamon/thirdparty/Xlib/xobject/colormap.py @@ -2,29 +2,25 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +import re from Xlib import error from Xlib.protocol import request - -from . import resource - -import re +from Xlib.xobject import resource rgb_res = [ re.compile(r'\Argb:([0-9a-fA-F]{1,4})/([0-9a-fA-F]{1,4})/([0-9a-fA-F]{1,4})\Z'), diff --git a/Nagstamon/thirdparty/Xlib/xobject/cursor.py b/Nagstamon/thirdparty/Xlib/xobject/cursor.py index 432e4fd9c..0d373570f 100644 --- a/Nagstamon/thirdparty/Xlib/xobject/cursor.py +++ b/Nagstamon/thirdparty/Xlib/xobject/cursor.py @@ -2,26 +2,22 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA from Xlib.protocol import request - -from . import resource +from Xlib.xobject import resource class Cursor(resource.Resource): __cursor__ = resource.Resource.__resource__ @@ -32,10 +28,9 @@ def free(self, onerror = None): cursor = self.id) self.display.free_resource_id(self.id) - def recolor(self, foreground, background, onerror=None): - fore_red, fore_green, fore_blue = foreground - back_red, back_green, back_blue = background - + def recolor(self, f_rgb, b_rgb, onerror = None): + back_red, back_green, back_blue = b_rgb + fore_red, fore_green, fore_blue = f_rgb request.RecolorCursor(display = self.display, onerror = onerror, cursor = self.id, diff --git a/Nagstamon/thirdparty/Xlib/xobject/drawable.py b/Nagstamon/thirdparty/Xlib/xobject/drawable.py index c36a9738e..efc40a913 100644 --- a/Nagstamon/thirdparty/Xlib/xobject/drawable.py +++ b/Nagstamon/thirdparty/Xlib/xobject/drawable.py @@ -2,34 +2,26 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + from Xlib import X, Xatom, Xutil from Xlib.protocol import request, rq # Other X resource objects -from . import resource -from . import colormap -from . import cursor -from . import fontable - -# Inter-client communication conventions -from . import icccm +from Xlib.xobject import resource, colormap, cursor, fontable, icccm class Drawable(resource.Resource): __drawable__ = resource.Resource.__resource__ @@ -249,7 +241,7 @@ def put_pil_image(self, gc, x, y, image, onerror = None): else: subimage = image w, h = subimage.size - data = subimage.tobytes("raw", rawmode, stride, 0) + data = subimage.tostring("raw", rawmode, stride, 0) self.put_image(gc, x, y, w, h, format, depth, 0, data) y1 = y1 + h y = y + h @@ -320,9 +312,6 @@ def query_best_size(self, item_class, width, height): class Window(Drawable): __window__ = resource.Resource.__resource__ - _STRING_ENCODING = 'ISO-8859-1' - _UTF8_STRING_ENCODING = 'UTF-8' - def create_window(self, x, y, width, height, border_width, depth, window_class = X.CopyFromParent, visual = X.CopyFromParent, @@ -424,7 +413,8 @@ def query_tree(self): return request.QueryTree(display = self.display, window = self.id) - def change_property(self, property, property_type, format, data, + + def change_property(self, property, type, format, data, mode = X.PropModeReplace, onerror = None): request.ChangeProperty(display = self.display, @@ -432,31 +422,21 @@ def change_property(self, property, property_type, format, data, mode = mode, window = self.id, property = property, - type = property_type, + type = type, data = (format, data)) - def change_text_property(self, property, property_type, data, - mode = X.PropModeReplace, onerror = None): - if not isinstance(data, bytes): - if property_type == Xatom.STRING: - data = data.encode(self._STRING_ENCODING) - elif property_type == self.display.get_atom('UTF8_STRING'): - data = data.encode(self._UTF8_STRING_ENCODING) - self.change_property(property, property_type, 8, data, - mode=mode, onerror=onerror) - def delete_property(self, property, onerror = None): request.DeleteProperty(display = self.display, onerror = onerror, window = self.id, property = property) - def get_property(self, property, property_type, offset, length, delete = 0): + def get_property(self, property, type, offset, length, delete = 0): r = request.GetProperty(display = self.display, delete = delete, window = self.id, property = property, - type = property_type, + type = type, long_offset = offset, long_length = length) @@ -468,12 +448,12 @@ def get_property(self, property, property_type, offset, length, delete = 0): else: return None - def get_full_property(self, property, property_type, sizehint = 10): - prop = self.get_property(property, property_type, 0, sizehint) + def get_full_property(self, property, type, sizehint = 10): + prop = self.get_property(property, type, 0, sizehint) if prop: val = prop.value if prop.bytes_after: - prop = self.get_property(property, property_type, sizehint, + prop = self.get_property(property, type, sizehint, prop.bytes_after // 4 + 1) val = val + prop.value @@ -482,19 +462,6 @@ def get_full_property(self, property, property_type, sizehint = 10): else: return None - def get_full_text_property(self, property, property_type=X.AnyPropertyType, sizehint = 10): - prop = self.get_full_property(property, property_type, - sizehint=sizehint) - if prop is None or prop.format != 8: - return None - if prop.property_type == Xatom.STRING: - prop.value = prop.value.decode(self._STRING_ENCODING) - elif prop.property_type == self.display.get_atom('UTF8_STRING'): - prop.value = prop.value.decode(self._UTF8_STRING_ENCODING) - # FIXME: at least basic support for compound text would be nice. - # elif prop.property_type == self.display.get_atom('COMPOUND_TEXT'): - return prop.value - def list_properties(self): r = request.ListProperties(display = self.display, window = self.id) @@ -662,37 +629,47 @@ def rotate_properties(self, properties, delta, onerror = None): properties = properties) def set_wm_name(self, name, onerror = None): - self.change_text_property(Xatom.WM_NAME, Xatom.STRING, name, - onerror = onerror) + self.change_property(Xatom.WM_NAME, Xatom.STRING, 8, name, + onerror = onerror) def get_wm_name(self): - return self.get_full_text_property(Xatom.WM_NAME, Xatom.STRING) + d = self.get_full_property(Xatom.WM_NAME, Xatom.STRING) + if d is None or d.format != 8: + return None + else: + return d.value def set_wm_icon_name(self, name, onerror = None): - self.change_text_property(Xatom.WM_ICON_NAME, Xatom.STRING, name, - onerror = onerror) + self.change_property(Xatom.WM_ICON_NAME, Xatom.STRING, 8, name, + onerror = onerror) def get_wm_icon_name(self): - return self.get_full_text_property(Xatom.WM_ICON_NAME, Xatom.STRING) + d = self.get_full_property(Xatom.WM_ICON_NAME, Xatom.STRING) + if d is None or d.format != 8: + return None + else: + return d.value + def set_wm_class(self, inst, cls, onerror = None): - self.change_text_property(Xatom.WM_CLASS, Xatom.STRING, - '%s\0%s\0' % (inst, cls), - onerror = onerror) + self.change_property(Xatom.WM_CLASS, Xatom.STRING, 8, + '%s\0%s\0' % (inst, cls), + onerror = onerror) def get_wm_class(self): - value = self.get_full_text_property(Xatom.WM_CLASS, Xatom.STRING) - if value is None: - return None - parts = value.split('\0') - if len(parts) < 2: + d = self.get_full_property(Xatom.WM_CLASS, Xatom.STRING) + if d is None or d.format != 8: return None else: - return parts[0], parts[1] + parts = d.value.split('\0') + if len(parts) < 2: + return None + else: + return parts[0], parts[1] def set_wm_transient_for(self, window, onerror = None): self.change_property(Xatom.WM_TRANSIENT_FOR, Xatom.WINDOW, - 32, [window.id], + 32, window.id, onerror = onerror) def get_wm_transient_for(self): @@ -719,7 +696,7 @@ def get_wm_protocols(self): def set_wm_colormap_windows(self, windows, onerror = None): self.change_property(self.display.get_atom('WM_COLORMAP_WINDOWS'), Xatom.WINDOW, 32, - map(lambda w: w.id, windows), + [w.id for w in windows], onerror = onerror) def get_wm_colormap_windows(self): @@ -729,16 +706,20 @@ def get_wm_colormap_windows(self): return [] else: cls = self.display.get_resource_class('window', Window) - return map(lambda i, d = self.display, c = cls: c(d, i), - d.value) + return list(map(lambda i, d = self.display, c = cls: c(d, i), + d.value)) def set_wm_client_machine(self, name, onerror = None): - self.change_text_property(Xatom.WM_CLIENT_MACHINE, Xatom.STRING, name, - onerror = onerror) + self.change_property(Xatom.WM_CLIENT_MACHINE, Xatom.STRING, 8, name, + onerror = onerror) def get_wm_client_machine(self): - return self.get_full_text_property(Xatom.WM_CLIENT_MACHINE, Xatom.STRING) + d = self.get_full_property(Xatom.WM_CLIENT_MACHINE, Xatom.STRING) + if d is None or d.format != 8: + return None + else: + return d.value def set_wm_normal_hints(self, hints = {}, onerror = None, **keys): self._set_struct_prop(Xatom.WM_NORMAL_HINTS, Xatom.WM_SIZE_HINTS, @@ -779,7 +760,7 @@ def get_wm_icon_size(self): def _get_struct_prop(self, pname, ptype, pstruct): r = self.get_property(pname, ptype, 0, pstruct.static_size // 4) if r and r.format == 32: - value = rq.encode_array(r.value) + value = r.value.tostring() if len(value) == pstruct.static_size: return pstruct.parse_binary(value, self.display)[0] @@ -811,9 +792,11 @@ def free(self, onerror = None): self.display.free_resource_id(self.id) - def create_cursor(self, mask, foreground, background, x, y): - fore_red, fore_green, fore_blue = foreground - back_red, back_green, back_blue = background + def create_cursor(self, mask, + f_rgb, b_rgb, + x, y): + fore_red, fore_green, fore_blue = f_rgb + back_red, back_green, back_blue = b_rgb cid = self.display.allocate_resource_id() request.CreateCursor(display = self.display, cid = cid, diff --git a/Nagstamon/thirdparty/Xlib/xobject/fontable.py b/Nagstamon/thirdparty/Xlib/xobject/fontable.py index 892f2665a..964b3963f 100644 --- a/Nagstamon/thirdparty/Xlib/xobject/fontable.py +++ b/Nagstamon/thirdparty/Xlib/xobject/fontable.py @@ -2,27 +2,22 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA from Xlib.protocol import request - -from . import resource -from . import cursor +from Xlib.xobject import resource, cursor class Fontable(resource.Resource): __fontable__ = resource.Resource.__resource__ @@ -88,9 +83,9 @@ def close(self, onerror = None): self.display.free_resource_id(self.id) def create_glyph_cursor(self, mask, source_char, mask_char, - foreground, background): - fore_red, fore_green, fore_blue = foreground - back_red, back_green, back_blue = background + f_rgb, b_rgb): + fore_red, fore_green, fore_blue = f_rgb + back_red, back_green, back_blue = b_rgb cid = self.display.allocate_resource_id() request.CreateGlyphCursor(display = self.display, diff --git a/Nagstamon/thirdparty/Xlib/xobject/icccm.py b/Nagstamon/thirdparty/Xlib/xobject/icccm.py index d445e6dfd..59aecb9e4 100644 --- a/Nagstamon/thirdparty/Xlib/xobject/icccm.py +++ b/Nagstamon/thirdparty/Xlib/xobject/icccm.py @@ -2,22 +2,19 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA from Xlib import X, Xutil from Xlib.protocol import rq diff --git a/Nagstamon/thirdparty/Xlib/xobject/resource.py b/Nagstamon/thirdparty/Xlib/xobject/resource.py index ea256ca1e..f8a39a597 100644 --- a/Nagstamon/thirdparty/Xlib/xobject/resource.py +++ b/Nagstamon/thirdparty/Xlib/xobject/resource.py @@ -2,26 +2,23 @@ # # Copyright (C) 2000 Peter Liljenberg <petli@ctrl-c.liu.se> # -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 -# of the License, or (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU Lesser General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the -# Free Software Foundation, Inc., -# 59 Temple Place, -# Suite 330, -# Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA from Xlib.protocol import request -class Resource(object): +class Resource: def __init__(self, display, rid, owner = 0): self.display = display self.id = rid @@ -35,18 +32,18 @@ def __eq__(self, obj): if self.display == obj.display: return self.id == obj.id else: - return False + return self.display == obj.display else: return id(self) == id(obj) - def __ne__(self, obj): - return not self == obj - def __hash__(self): return int(self.id) + def __str__(self): + return '%s(0x%08x)' % (self.__class__, self.id) + def __repr__(self): - return '<%s 0x%08x>' % (self.__class__.__name__, self.id) + return '<%s 0x%08x>' % (self.__class__, self.id) def kill_client(self, onerror = None): request.KillClient(display = self.display, From bcc82b655331e0264c032ed0f8d688a2fa1c8d24 Mon Sep 17 00:00:00 2001 From: Fluffy Satoshi <fluffysatoshi@mailbox.org> Date: Fri, 25 Nov 2022 15:47:01 +0100 Subject: [PATCH 464/884] Avoid using EWMH under Wayland Fixes #877. --- Nagstamon/QUI/__init__.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index e9fb314c4..e1aeabc76 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -67,8 +67,7 @@ # only on X11/Linux thirdparty path should be added because it contains the Xlib module # needed to tell window manager via EWMH to keep Nagstamon window on all virtual desktops -# TODO: test if X11 or Wayland is used -if OS not in OS_NON_LINUX: +if OS not in OS_NON_LINUX and not DESKTOP_WAYLAND: # extract thirdparty path from resources path - make submodules accessible by thirdparty modules THIRDPARTY = os.sep.join(RESOURCES.split(os.sep)[0:-1] + ['thirdparty']) sys.path.insert(0, THIRDPARTY) @@ -79,7 +78,8 @@ from Nagstamon.thirdparty.ewmh import EWMH - # DBus only interesting for Linux too +# DBus only interesting for Linux too +if OS not in OS_NON_LINUX: try: from dbus import (Interface, SessionBus) @@ -951,7 +951,7 @@ def __init__(self): self.hide() # ewmh.py in thirdparty directory needed to keep floating statusbar on all desktops in Linux - if not OS in OS_NON_LINUX: + if not OS in OS_NON_LINUX and not DESKTOP_WAYLAND: self.ewmh = EWMH() # avoid quitting when using Qt.Tool flag and closing settings dialog @@ -1220,7 +1220,7 @@ def set_mode(self): self.show() # X11/Linux needs some special treatment to get the statusbar floating on all virtual desktops - if OS not in OS_NON_LINUX: + if OS not in OS_NON_LINUX and not DESKTOP_WAYLAND: # get all windows... winid = self.winId().__int__() self.ewmh.setWmDesktop(winid, 0xffffffff) @@ -2060,7 +2060,7 @@ def raise_window_on_all_desktops(self): if conf.windowed: return # X11/Linux needs some special treatment to get the statusbar floating on all virtual desktops - if OS not in OS_NON_LINUX: + if OS not in OS_NON_LINUX and not DESKTOP_WAYLAND: # get all windows... winid = self.winId().__int__() self.ewmh.setWmDesktop(winid, 0xffffffff) From d4587cc63ac6dce0c4fe13e92f782631c7217a8a Mon Sep 17 00:00:00 2001 From: Robert Scheck <robert@fedoraproject.org> Date: Fri, 2 Dec 2022 19:58:30 +0100 Subject: [PATCH 465/884] Handle absence of attribute 'screenAt' at 'QApplication' QApplication (using the default Qt5 and its default Qt5 Python binding on RHEL/CentOS 7) has no attribute 'screenAt'. --- Nagstamon/QUI/__init__.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index e9fb314c4..7b5ade549 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -6976,11 +6976,16 @@ def get_screen_name(x, y): # integerify these values as they *might* be strings x = int(x) y = int(y) - screen = APP.screenAt(QPoint(x, y)) - del x, y - if screen: - return screen.name - else: + + # QApplication (using Qt5 and/or its Python binding on RHEL/CentOS 7) has no attribute 'screenAt' + try: + screen = APP.screenAt(QPoint(x, y)) + del x, y + if screen: + return screen.name + else: + return None + except: return None From 1ebdedc29bcf7efaa085383b58aa6d275d72bcd4 Mon Sep 17 00:00:00 2001 From: Robert Scheck <robert@fedoraproject.org> Date: Fri, 2 Dec 2022 21:02:30 +0100 Subject: [PATCH 466/884] Fallback from requests_kerberos to requests_gssapi RHEL/CentOS 7 has no requests_kerberos, only requests_gssapi --- Nagstamon/Servers/Generic.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index 5663e0744..4200a8530 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -69,9 +69,13 @@ except ImportError: from requests_kerberos import HTTPKerberosAuth as HTTPSKerberos else: - # requests_gssapi needs installation of KfW - Kerberos for Windows - # requests_kerberoes doesn't - from requests_kerberos import HTTPKerberosAuth as HTTPSKerberos + # requests_gssapi is newer but not available everywhere + try: + # requests_gssapi needs installation of KfW - Kerberos for Windows + # requests_kerberoes doesn't + from requests_kerberos import HTTPKerberosAuth as HTTPSKerberos + except ImportError: + from requests_gssapi import HTTPSPNEGOAuth as HTTPSKerberos # disable annoying SubjectAltNameWarning warnings try: From baaca995c8facd4febb9dd634f5636457f64b738 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 3 Dec 2022 14:15:45 +0100 Subject: [PATCH 467/884] 3.11-20221202 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 1e44376c3..3aa71abbf 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.11-20221114' + VERSION = '3.11-20221202' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index e381e3fe3..5601f90a4 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.11-20221114) unstable; urgency=low +nagstamon (3.11-20221202) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Fri, 14 Nov 2022 08:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Fri, 02 Dec 2022 08:00:00 +0100 nagstamon (3.10.1) stable; urgency=low * New upstream From 1a05c1228440914610b60eb3b48590a28d662fb8 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 3 Dec 2022 14:21:02 +0100 Subject: [PATCH 468/884] 3.11-20221202 on master --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index c1c40af5f..f593dcac6 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -4,7 +4,7 @@ on: tags-ignore: 'v*' branches: - '**' - - '!master' + #- '!master' - '!*.*.*' env: From 59dd26953a8feffb4a294ab42f043a3f6a846287 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 3 Dec 2022 14:31:05 +0100 Subject: [PATCH 469/884] OS_DARWIN -> OS_MACOS, checkbox hide dock icon --- Nagstamon/Config.py | 6 ++--- Nagstamon/Helpers.py | 4 +-- Nagstamon/QUI/__init__.py | 34 ++++++++++++------------ Nagstamon/Servers/Generic.py | 8 +++--- Nagstamon/resources/qui/settings_main.ui | 17 +++++++++--- 5 files changed, 39 insertions(+), 30 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index cd91a92e9..43b2b12f3 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -45,9 +45,9 @@ # instead of calling platform.system() every now and then just do it once here OS = platform.system() # needed when OS-specific decisions have to be made, mostly Linux/non-Linux -OS_DARWIN = 'Darwin' +OS_MACOS = 'Darwin' OS_WINDOWS = 'Windows' -OS_NON_LINUX = (OS_DARWIN, OS_WINDOWS) +OS_NON_LINUX = (OS_MACOS, OS_WINDOWS) # simple Wayland detection if 'WAYLAND_DISPLAY' in os.environ or\ @@ -753,7 +753,7 @@ def _DefaultActions(self): "SSH": Action(name="SSH", description="Connect via SSH.", type="command", string="C:\Program Files\PuTTY\putty.exe -l root $ADDRESS$")} - elif OS == OS_DARWIN: + elif OS == OS_MACOS: defaultactions = {"RDP": Action(name="RDP", description="Connect via RDP.", type="command", string="open rdp://$ADDRESS$"), "VNC": Action(name="VNC", description="Connect via VNC.", diff --git a/Nagstamon/Helpers.py b/Nagstamon/Helpers.py index 55fff1aa7..62ae2bd5b 100644 --- a/Nagstamon/Helpers.py +++ b/Nagstamon/Helpers.py @@ -34,7 +34,7 @@ from Nagstamon.Config import (conf, OS, - OS_DARWIN) + OS_MACOS) # states needed for gravity comparison for notification and Generic.py STATES = ['UP', @@ -103,7 +103,7 @@ def __getitem__(self, key): :param key: :return: """ - if OS == OS_DARWIN: + if OS == OS_MACOS: if not os.path.exists(key): # pyinstaller temp folder seems to be emptied completely after a while # so the directories containing the resources have to be recreated too diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 4579a3eee..c94f716eb 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -44,7 +44,7 @@ KEYRING, OS_NON_LINUX, OS, - OS_DARWIN, + OS_MACOS, OS_WINDOWS, RESOURCES, Server, @@ -95,7 +95,7 @@ DBUS_AVAILABLE = False # make icon status in macOS dock accessible via NSApp, used by set_macos_dock_icon_visible() -if OS == OS_DARWIN: +if OS == OS_MACOS: from AppKit import (NSApp, NSApplicationPresentationDefault, NSApplicationPresentationHideDock) @@ -343,7 +343,7 @@ def set_menu(self, menu): # MacOSX does not distinguish between left and right click so menu will go to upper menu bar # update: apparently not, but own context menu will be shown when icon is clicked an all is OK = green - if OS != OS_DARWIN: + if OS != OS_MACOS: self.setContextMenu(self.menu) @Slot() @@ -401,7 +401,7 @@ def icon_clicked(self, reason): # when green icon is displayed and no popwin is about to pop up... if get_worst_status() == 'UP': # ...nothing to do except on macOS where menu should be shown - if OS == OS_DARWIN: + if OS == OS_MACOS: self.menu.show_at_cursor() else: # show status window if there is something to tell @@ -616,7 +616,7 @@ def initialize(self): """ MenuContext.initialize(self) # makes even less sense on OSX - if OS != OS_DARWIN: + if OS != OS_MACOS: self.action_status = QAction('Show status window', self) self.action_status.triggered.connect(statuswindow.show_window_systrayicon) self.insertAction(self.action_refresh, self.action_status) @@ -636,7 +636,7 @@ def __init__(self, text='', parent=None, server=None, url_type=''): # OSX does not support flat QToolButtons so keep the neat default ones -if OS == OS_DARWIN: +if OS == OS_MACOS: Button = QPushButton CSS_CLOSE_BUTTON = '''QPushButton {border-width: 0px; border-style: none; @@ -966,7 +966,7 @@ def __init__(self): # show tooltips even if popup window has no focus self.setAttribute(Qt.WidgetAttribute.WA_AlwaysShowToolTips) - if OS == OS_DARWIN: + if OS == OS_MACOS: # avoid hiding window if it has no focus - necessary on OSX if using flag Qt.Tool self.setAttribute(Qt.WidgetAttribute.WA_MacAlwaysShowToolWindow) @@ -1003,7 +1003,7 @@ def __init__(self): self.servers_vbox.addWidget(self.label_all_ok) # test with OSX top menubar - if OS == OS_DARWIN: + if OS == OS_MACOS: self.menubar = QMenuBar() action_exit = QAction('exit', self.menubar) action_settings = QAction('settings', self.menubar) @@ -1202,7 +1202,7 @@ def set_mode(self): if conf.statusbar_floating: # no need for icon in dock if floating - apply first to avoid window in background - if OS == OS_DARWIN: + if OS == OS_MACOS: set_macos_dock_icon_visibility(False) # no need for systray @@ -1251,7 +1251,7 @@ def set_mode(self): elif conf.icon_in_systray: # no need for icon in dock if in systray - if OS == OS_DARWIN: + if OS == OS_MACOS: set_macos_dock_icon_visibility(False) # statusbar and detail window should be frameless and stay on top @@ -1288,7 +1288,7 @@ def set_mode(self): # newer Qt5 seem to be better regarding fullscreen mode on non-OSX self.show_window() # fullscreen mode is rather buggy on everything other than OSX so just use a maximized window - if OS == OS_DARWIN: + if OS == OS_MACOS: self.showFullScreen() # no need for icon in dock if fullscreen set_macos_dock_icon_visibility(False) @@ -1301,7 +1301,7 @@ def set_mode(self): elif conf.windowed: # show icon in dock if window is set - if OS == OS_DARWIN: + if OS == OS_MACOS: set_macos_dock_icon_visibility(True) systrayicon.hide() @@ -1551,7 +1551,7 @@ def show_window(self, event=None): # ...and practice self.resize_window(width, height, x, y) # switch on - if OS == OS_DARWIN: + if OS == OS_MACOS: # delayed because of flickering window in OSX self.timer.singleShot(200, self.set_shown) else: @@ -1649,7 +1649,7 @@ def hide_window(self): self.servers_scrollarea.hide() # macOS needs this since Qt6 to avoid statuswindow size changeability # looks silly but works to force using the own hint as hint - if OS == OS_DARWIN: + if OS == OS_MACOS: self.setMinimumSize(self.sizeHint()) self.setMaximumSize(self.sizeHint()) else: @@ -2759,7 +2759,7 @@ def change(self, text, style=''): # set stylesheet depending on submitted style if style in COLOR_STATUS_LABEL: - if OS == OS_DARWIN: + if OS == OS_MACOS: self.setStyleSheet('''background: {0}; border-radius: 3px; '''.format(COLOR_STATUS_LABEL[style])) @@ -3024,7 +3024,7 @@ def authenticate_server(self): def update_label(self): self.label.setText('<big><b> {0}@{1}</b></big>'.format(self.server.username, self.server.name)) # let label padding keep top and bottom space - apparently not necessary on OSX - if OS != OS_DARWIN: + if OS != OS_MACOS: self.label.setStyleSheet('''padding-top: {0}px; padding-bottom: {0}px;'''.format(SPACE)) @@ -5529,7 +5529,7 @@ def choose_browser_executable(self): if OS == OS_WINDOWS: filter = 'Executables (*.exe *.EXE);; All files (*)' directory = os.environ['ProgramFiles'] - elif OS == OS_DARWIN: + elif OS == OS_MACOS: filter = '' directory = '/Applications' else: diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index 5663e0744..69c83facd 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -56,10 +56,10 @@ conf, debug_queue, OS, - OS_DARWIN, + OS_MACOS, RESOURCES) -if OS == OS_DARWIN: +if OS == OS_MACOS: # requests_gssapi is newer but not available everywhere try: # extra imports needed to get it compiled on macOS @@ -216,7 +216,7 @@ def __init__(self, **kwds): # macOS pyinstaller onefile conglomerate tends to lose cacert.pem due to macOS temp folder cleaning self.cacert_path = self.cacert_content = False - if OS == OS_DARWIN: + if OS == OS_MACOS: # trying to find root path when run by pyinstaller onefile, must be something like # /var/folders/7w/hfvrg7v92x3gjt95cqh974240000gn/T/_MEIQ3l3u3 root_path = Path(RESOURCES).parent.parent @@ -1441,7 +1441,7 @@ def FetchURL(self, url, giveback='obj', cgi_data=None, no_auth=False, multipart= cgi_data_log = cgi_data self.Debug(server=self.get_name(), debug='FetchURL: ' + url + ' CGI Data: ' + str(cgi_data_log)) - if OS == OS_DARWIN and self.cacert_path and not self.cacert_path.is_file(): + if OS == OS_MACOS and self.cacert_path and not self.cacert_path.is_file(): # pyinstaller temp folder seems to be emptied completely after a while # so the directories containing the resources have to be recreated too self.cacert_path.parent.mkdir(exist_ok=True) diff --git a/Nagstamon/resources/qui/settings_main.ui b/Nagstamon/resources/qui/settings_main.ui index 279cd966a..21ff58a48 100644 --- a/Nagstamon/resources/qui/settings_main.ui +++ b/Nagstamon/resources/qui/settings_main.ui @@ -42,7 +42,7 @@ </sizepolicy> </property> <property name="currentIndex"> - <number>6</number> + <number>1</number> </property> <widget class="QWidget" name="tab_servers"> <property name="sizePolicy"> @@ -623,6 +623,13 @@ </property> </widget> </item> + <item row="4" column="0"> + <widget class="QRadioButton" name="input_radiobutton_windowed"> + <property name="text"> + <string>Window</string> + </property> + </widget> + </item> <item row="12" column="0"> <layout class="QGridLayout" name="gridLayout_20"> <item row="1" column="0"> @@ -661,10 +668,10 @@ </property> </widget> </item> - <item row="4" column="0"> - <widget class="QRadioButton" name="input_radiobutton_windowed"> + <item row="13" column="0"> + <widget class="QCheckBox" name="input_checkbox_hide_dock_icon_on_macos"> <property name="text"> - <string>Window</string> + <string>Hide Dock icon</string> </property> </widget> </item> @@ -3180,6 +3187,7 @@ <tabstop>input_checkbox_systray_offset_use</tabstop> <tabstop>input_spinbox_systray_offset</tabstop> <tabstop>input_combobox_fullscreen_display</tabstop> + <tabstop>input_checkbox_hide_dock_icon_on_macos</tabstop> <tabstop>button_fontchooser</tabstop> <tabstop>button_default_font</tabstop> <tabstop>input_radiobutton_popup_details_hover</tabstop> @@ -3323,6 +3331,7 @@ <tabstop>input_radiobutton_windowed</tabstop> <tabstop>input_lineedit_notification_custom_action_separator</tabstop> <tabstop>input_checkbox_notification_custom_action_single</tabstop> + <tabstop>input_checkbox_filter_all_unreachable_services</tabstop> </tabstops> <resources/> <connections> From b57f018a40350401cc3196efe475e0ce1ae73456 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 3 Dec 2022 14:33:03 +0100 Subject: [PATCH 470/884] ignore .DS_Store --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index bc74b9248..f1f64bf6f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ __pycache__/ .directory +.DS_Store .idea/ .metadata/ .vscode/ From 4a01bc2a60fffeab45b9644c18896151136e3ddd Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 3 Dec 2022 14:46:57 +0100 Subject: [PATCH 471/884] self.hide_dock_icon_on_macos = False --- Nagstamon/Config.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 43b2b12f3..7745d197e 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -286,6 +286,7 @@ def __init__(self): self.fullscreen_display = 0 self.systray_offset_use = False self.systray_offset = 10 + self.hide_dock_icon_on_macos = False self.font = '' self.defaults_acknowledge_sticky = False self.defaults_acknowledge_send_notification = False From f05da44782d1d4235f24e740ff0516dd65aeeefc Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 20 Nov 2022 20:14:22 +0100 Subject: [PATCH 472/884] fix --- Nagstamon/Helpers.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/Nagstamon/Helpers.py b/Nagstamon/Helpers.py index 1171d6c4a..356301679 100644 --- a/Nagstamon/Helpers.py +++ b/Nagstamon/Helpers.py @@ -26,9 +26,7 @@ import platform import re import sys -from sys import executable import traceback -from traceback import print_exc import webbrowser # import md5 for centreon url autologin encoding @@ -467,21 +465,6 @@ def get_distro(): dist_name, dist_version, dist_id = platform.dist() return dist_name.lower(), dist_version, dist_id -def restart(): - """ - restart Nagstamon, e.g. for GUI changes in macOS - """ - try: - process = psutil.Process(os.getpid()) - for file_handler in process.open_files() + process.connections(): - os.close(file_handler.fd) - except Exception as exception: - print_exc(file=sys.stdout) - - print('yeehaa') - - os.execl(executable, executable, *sys.argv) - # depending on column different functions have to be used # 0 + 1 are column "Hosts", 1 + 2 are column "Service" due to extra font flag pictograms SORT_COLUMNS_FUNCTIONS = {0: compare_host, From f4d205888cfc8cf8f60b82d6ef18b6bb1ceee126 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 3 Dec 2022 23:31:07 +0100 Subject: [PATCH 473/884] fix --- Nagstamon/Config.py | 4 +-- Nagstamon/QUI/__init__.py | 32 ++++++++++++++++++------ Nagstamon/resources/qui/settings_main.ui | 6 ++--- build/debian/changelog | 4 +-- 4 files changed, 31 insertions(+), 15 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 7745d197e..da56abb52 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.11-20221121' + VERSION = '3.11-20221203' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' @@ -286,7 +286,7 @@ def __init__(self): self.fullscreen_display = 0 self.systray_offset_use = False self.systray_offset = 10 - self.hide_dock_icon_on_macos = False + self.hide_macos_dock_icon = False self.font = '' self.defaults_acknowledge_sticky = False self.defaults_acknowledge_send_notification = False diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index c94f716eb..3fd83ebc1 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -97,6 +97,7 @@ # make icon status in macOS dock accessible via NSApp, used by set_macos_dock_icon_visible() if OS == OS_MACOS: from AppKit import (NSApp, + NSBundle, NSApplicationPresentationDefault, NSApplicationPresentationHideDock) @@ -1203,7 +1204,7 @@ def set_mode(self): if conf.statusbar_floating: # no need for icon in dock if floating - apply first to avoid window in background if OS == OS_MACOS: - set_macos_dock_icon_visibility(False) + hide_macos_dock_icon(conf.hide_macos_dock_icon) # no need for systray systrayicon.hide() @@ -1252,7 +1253,7 @@ def set_mode(self): elif conf.icon_in_systray: # no need for icon in dock if in systray if OS == OS_MACOS: - set_macos_dock_icon_visibility(False) + hide_macos_dock_icon(conf.hide_macos_dock_icon) # statusbar and detail window should be frameless and stay on top # tool flag helps to be invisible in taskbar @@ -1291,7 +1292,7 @@ def set_mode(self): if OS == OS_MACOS: self.showFullScreen() # no need for icon in dock if fullscreen - set_macos_dock_icon_visibility(False) + hide_macos_dock_icon(conf.hide_macos_dock_icon) else: self.show() self.showMaximized() @@ -1302,7 +1303,8 @@ def set_mode(self): elif conf.windowed: # show icon in dock if window is set if OS == OS_MACOS: - set_macos_dock_icon_visibility(True) + #hide_macos_dock_icon(conf.hide_macos_dock_icon) + hide_macos_dock_icon(False) systrayicon.hide() @@ -4954,6 +4956,10 @@ def initialize(self): else: self.window.input_checkbox_use_system_keyring.hide() + # hide 'Hide macOS Dock icon' if not on macOS + if OS != OS_MACOS: + self.window.input_checkbox_hide_macos_dock_icon.hide() + # important final size adjustment self.window.adjustSize() @@ -7054,15 +7060,16 @@ def check_servers(): dialogs.server_missing.show() dialogs.server_missing.initialize('no_server_enabled') -def set_macos_dock_icon_visibility(visible=False): +def hide_macos_dock_icon(hide=False): """ small helper to make dock icon visible or not in macOS inspired by https://stackoverflow.com/questions/6796028/start-a-gui-process-in-mac-os-x-without-dock-icon """ - if visible: - NSApp.setActivationPolicy_(NSApplicationPresentationDefault) - else: + if hide: NSApp.setActivationPolicy_(NSApplicationPresentationHideDock) + else: + NSApp.setActivationPolicy_(NSApplicationPresentationDefault) + # check for updates check_version = CheckVersion() @@ -7097,5 +7104,14 @@ def set_macos_dock_icon_visibility(visible=False): elif conf.icon_in_systray: systrayicon.set_menu(menu) +# set flag to be LSUIElement like in file info.properties +if OS == OS_MACOS: + if conf.hide_macos_dock_icon: + lsuielement = '1' + else: + lsuielement = '0' + macos_info_dictionary = NSBundle.mainBundle().infoDictionary() + macos_info_dictionary['LSUIElement'] = lsuielement + # versatile mediaplayer mediaplayer = MediaPlayer(statuswindow, RESOURCE_FILES) diff --git a/Nagstamon/resources/qui/settings_main.ui b/Nagstamon/resources/qui/settings_main.ui index 21ff58a48..b81424b30 100644 --- a/Nagstamon/resources/qui/settings_main.ui +++ b/Nagstamon/resources/qui/settings_main.ui @@ -669,9 +669,9 @@ </widget> </item> <item row="13" column="0"> - <widget class="QCheckBox" name="input_checkbox_hide_dock_icon_on_macos"> + <widget class="QCheckBox" name="input_checkbox_hide_macos_dock_icon"> <property name="text"> - <string>Hide Dock icon</string> + <string>Hide macOS Dock icon - needs restart</string> </property> </widget> </item> @@ -3187,7 +3187,7 @@ <tabstop>input_checkbox_systray_offset_use</tabstop> <tabstop>input_spinbox_systray_offset</tabstop> <tabstop>input_combobox_fullscreen_display</tabstop> - <tabstop>input_checkbox_hide_dock_icon_on_macos</tabstop> + <tabstop>input_checkbox_hide_macos_dock_icon</tabstop> <tabstop>button_fontchooser</tabstop> <tabstop>button_default_font</tabstop> <tabstop>input_radiobutton_popup_details_hover</tabstop> diff --git a/build/debian/changelog b/build/debian/changelog index 87dde2935..395291fce 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.11-20221121) unstable; urgency=low +nagstamon (3.11-20221203 unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Fri, 14 Nov 2022 08:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Sat, 03 Dec 2022 08:00:00 +0100 nagstamon (3.10.1) stable; urgency=low * New upstream From 5c68693b3d4065dcdd18fe43de9192fa0d8763c4 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 3 Dec 2022 23:36:14 +0100 Subject: [PATCH 474/884] fix unstable version check --- Nagstamon/QUI/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 3fd83ebc1..180c75c8a 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -6872,7 +6872,7 @@ def check(self): if latest_version != 'unavailable': if latest_version == AppInfo.VERSION: message = 'You are using the latest version <b>Nagstamon {0}</b>.'.format(AppInfo.VERSION) - elif latest_version != AppInfo.VERSION: + elif latest_version > AppInfo.VERSION: message = 'The new version <b>Nagstamon {0}</b> is available.<p>' \ 'Get it at <a href={1}>{1}</a>.'.format(latest_version, AppInfo.WEBSITE + '/download') From dc6acd932c6cddae4e32c0d058ece355c3b449c1 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 3 Dec 2022 23:40:31 +0100 Subject: [PATCH 475/884] fix debian build error --- build/debian/changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/debian/changelog b/build/debian/changelog index 395291fce..6e71265cd 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ nagstamon (3.11-20221203 unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Sat, 03 Dec 2022 08:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Sat, Dec 03 2022 08:00:00 +0100 nagstamon (3.10.1) stable; urgency=low * New upstream From 03efc373aa09d3de3a110bf2f44d8236c28b9e58 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 3 Dec 2022 23:49:48 +0100 Subject: [PATCH 476/884] fix debian build error ) --- build/debian/changelog | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/build/debian/changelog b/build/debian/changelog index 6e71265cd..b014e5350 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.11-20221203 unstable; urgency=low +nagstamon (3.11-20221203) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Sat, Dec 03 2022 08:00:00 +0100 @@ -8,7 +8,7 @@ nagstamon (3.10.1) stable; urgency=low - fixes Centreon flapping bug - fixes Kubuntu 20.04 not starting bug - -- Henri Wahl <henri@nagstamon.de> Fri, 04 Nov 2022 08:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Fri, Nov 04 2022 08:00:00 +0100 nagstamon (3.10.0) stable; urgency=low * New upstream @@ -21,7 +21,7 @@ nagstamon (3.10.0) stable; urgency=low - fixes for TLS - fixes for Checkmk - -- Henri Wahl <henri@nagstamon.de> Thu, 27 Oct 2022 08:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Thu, Oct 27 2022 08:00:00 +0100 nagstamon (3.8.0) stable; urgency=low * New upstream @@ -33,7 +33,7 @@ nagstamon (3.8.0) stable; urgency=low - Zabbix fixes - dependency updates - -- Henri Wahl <henri@nagstamon.de> Mon, 15 Nov 2021 19:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Mon, Nov 15 2021 19:00:00 +0100 nagstamon (3.6.0) stable; urgency=low * New upstream @@ -51,7 +51,7 @@ nagstamon (3.6.0) stable; urgency=low - fix for HighDPI - fix for distro detection - -- Henri Wahl <henri@nagstamon.de> Sun, 05 Apr 2021 17:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Sun, Apr 05 2021 17:00:00 +0100 nagstamon (3.4.1) stable; urgency=low * New upstream From 0fa5cb1b9c0d4184bc1be90c85eaa1d580110317 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 3 Dec 2022 23:58:41 +0100 Subject: [PATCH 477/884] test docker push --- .github/workflows/build-release-latest.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index cc42ed270..352febc17 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -10,6 +10,7 @@ on: env: python_win_version: 3.10.8 repo_dir: nagstamon-jekyll/docs/repo + cr_image: ghcr.io/henriwahl/build-nagstamon jobs: test: @@ -45,9 +46,15 @@ jobs: runs-on: ubuntu-latest needs: test steps: + - uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - uses: actions/checkout@v3 - - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon -e DEB_BUILD_OPTIONS=nocheck build-nagstamon + - run: /usr/bin/docker build -t ${{ cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ cr_image }}-${{ github.job }} + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon -e DEB_BUILD_OPTIONS=nocheck ${{ cr_image }}-${{ github.job }} - uses: actions/upload-artifact@v3 with: path: build/*.deb From 33fafb19df1db17bb5b671237b26efcc101db974 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 4 Dec 2022 00:06:34 +0100 Subject: [PATCH 478/884] fix fixed version check --- Nagstamon/QUI/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 180c75c8a..4127131de 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -6875,6 +6875,9 @@ def check(self): elif latest_version > AppInfo.VERSION: message = 'The new version <b>Nagstamon {0}</b> is available.<p>' \ 'Get it at <a href={1}>{1}</a>.'.format(latest_version, AppInfo.WEBSITE + '/download') + elif latest_version < AppInfo.VERSION: + # for some reason the local version is newer than that remote one - just ignore + message = '' # check if there is anything to tell if message != '': From 79d63d1e8398ce1482668edd663b95ca0df421ab Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 4 Dec 2022 00:08:35 +0100 Subject: [PATCH 479/884] env. --- .github/workflows/build-release-latest.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 352febc17..d12fe4346 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -52,9 +52,9 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: actions/checkout@v3 - - run: /usr/bin/docker build -t ${{ cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . - - run: docker push ${{ cr_image }}-${{ github.job }} - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon -e DEB_BUILD_OPTIONS=nocheck ${{ cr_image }}-${{ github.job }} + - run: /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }} + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon -e DEB_BUILD_OPTIONS=nocheck ${{ env.cr_image }}-${{ github.job }} - uses: actions/upload-artifact@v3 with: path: build/*.deb From e7bb73a4769a508caa62af7f96e6fe8688685824 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 4 Dec 2022 00:15:11 +0100 Subject: [PATCH 480/884] docker pull ${{ env.cr_image }}-${{ github.job }} --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index d12fe4346..49d7f3374 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -52,7 +52,7 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: actions/checkout@v3 - - run: /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . - run: docker push ${{ env.cr_image }}-${{ github.job }} - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon -e DEB_BUILD_OPTIONS=nocheck ${{ env.cr_image }}-${{ github.job }} - uses: actions/upload-artifact@v3 From 4d4b726747d0460c76b08811be513621d303646b Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 4 Dec 2022 00:29:35 +0100 Subject: [PATCH 481/884] fedora docker pull too --- .github/workflows/build-release-latest.yml | 42 +++++++++++++++++----- Nagstamon/QUI/__init__.py | 10 ++++-- 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 49d7f3374..4466c1191 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -46,12 +46,12 @@ jobs: runs-on: ubuntu-latest needs: test steps: + - uses: actions/checkout@v3 - uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - uses: actions/checkout@v3 - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . - run: docker push ${{ env.cr_image }}-${{ github.job }} - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon -e DEB_BUILD_OPTIONS=nocheck ${{ env.cr_image }}-${{ github.job }} @@ -66,8 +66,14 @@ jobs: needs: test steps: - uses: actions/checkout@v3 - - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + - uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }} + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} - uses: actions/upload-artifact@v3 with: path: build/*.rpm @@ -79,8 +85,14 @@ jobs: needs: test steps: - uses: actions/checkout@v3 - - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + - uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }} + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} - uses: actions/upload-artifact@v3 with: path: build/*.rpm @@ -92,8 +104,14 @@ jobs: needs: test steps: - uses: actions/checkout@v3 - - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + - uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }} + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} - uses: actions/upload-artifact@v3 with: path: build/*.rpm @@ -105,8 +123,14 @@ jobs: needs: test steps: - uses: actions/checkout@v3 - - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + - uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }} + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} - uses: actions/upload-artifact@v3 with: path: build/*.rpm diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 180c75c8a..553fdf5d1 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -1291,8 +1291,8 @@ def set_mode(self): # fullscreen mode is rather buggy on everything other than OSX so just use a maximized window if OS == OS_MACOS: self.showFullScreen() - # no need for icon in dock if fullscreen - hide_macos_dock_icon(conf.hide_macos_dock_icon) + # in fullscreen mode dock icon does not disturb because the dock is away anyway + hide_macos_dock_icon(False) else: self.show() self.showMaximized() @@ -1303,7 +1303,7 @@ def set_mode(self): elif conf.windowed: # show icon in dock if window is set if OS == OS_MACOS: - #hide_macos_dock_icon(conf.hide_macos_dock_icon) + # in windowed mode always show dock icon hide_macos_dock_icon(False) systrayicon.hide() @@ -4672,6 +4672,10 @@ def __init__(self, dialog): # display to use in fullscreen mode self.window.input_radiobutton_fullscreen: [self.window.label_fullscreen_display, self.window.input_combobox_fullscreen_display], + # offer option to hide icon in dock on macOS + self.window.input_radiobutton_icon_in_systray: [self.window.input_checkbox_hide_macos_dock_icon], + self.window.input_radiobutton_statusbar_floating: [self.window.input_checkbox_hide_macos_dock_icon], + # notifications in general self.window.input_checkbox_notification: [self.window.notification_groupbox], # sound at all From 3d2399068fb93b6ced51f509521c23f4c733f4cd Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 4 Dec 2022 00:46:48 +0100 Subject: [PATCH 482/884] python test cr --- .github/workflows/build-release-latest.yml | 7 +++++++ build/docker/Dockerfile-python | 10 ++++++++++ 2 files changed, 17 insertions(+) create mode 100644 build/docker/Dockerfile-python diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 4466c1191..ca099f178 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -25,6 +25,13 @@ jobs: uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} + - uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - run: docker pull ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }} -v .:/build -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }} - name: Install dependencies run: | sudo apt update diff --git a/build/docker/Dockerfile-python b/build/docker/Dockerfile-python new file mode 100644 index 000000000..427dd15dd --- /dev/null +++ b/build/docker/Dockerfile-python @@ -0,0 +1,10 @@ +ARG VERSION + +FROM python:${VERSION} +LABEL maintainer=henri@nagstamon.de + +RUN apt -y update +RUN apt -y install libdbus-1-dev libkrb5-dev +RUN python -m pip install --upgrade pip +RUN pip install pytest pylint wheel +RUN pip install --requirements /build/requirements/linux.txt \ No newline at end of file From 5dc8f02cda9431d2b477eb385677323e70333631 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 4 Dec 2022 01:01:00 +0100 Subject: [PATCH 483/884] transport requirements into build --- .github/workflows/build-release-latest.yml | 2 +- build/docker/Dockerfile-python | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index ca099f178..b2f83dfb8 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -30,7 +30,7 @@ jobs: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - run: docker pull ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }} -v .:/build -f build/docker/Dockerfile-${{ github.job }} . + - run: docker pull ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }} --build-arg VERSION=${{ matrix.python-version }} --build-arg REQUIREMENTS=$(cat build/requirements/linux.txt | tr '\n' '|') -f build/docker/Dockerfile-${{ github.job }} . - run: docker push ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }} - name: Install dependencies run: | diff --git a/build/docker/Dockerfile-python b/build/docker/Dockerfile-python index 427dd15dd..b27115355 100644 --- a/build/docker/Dockerfile-python +++ b/build/docker/Dockerfile-python @@ -1,10 +1,14 @@ ARG VERSION +ARG REQUIREMENTS FROM python:${VERSION} LABEL maintainer=henri@nagstamon.de +RUN echo ${REQUIREMENTS} RUN apt -y update RUN apt -y install libdbus-1-dev libkrb5-dev RUN python -m pip install --upgrade pip RUN pip install pytest pylint wheel -RUN pip install --requirements /build/requirements/linux.txt \ No newline at end of file + +RUN echo ${REQUIREMENTS} | tr '|' '\n' > requirements.txt +RUN pip install -r requirements \ No newline at end of file From 82658a8147b5302ea49a861e1b6d7033d4af2e6d Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 4 Dec 2022 01:01:25 +0100 Subject: [PATCH 484/884] transport requirements into build with correct filename --- build/docker/Dockerfile-python | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/docker/Dockerfile-python b/build/docker/Dockerfile-python index b27115355..1d0a2545d 100644 --- a/build/docker/Dockerfile-python +++ b/build/docker/Dockerfile-python @@ -11,4 +11,4 @@ RUN python -m pip install --upgrade pip RUN pip install pytest pylint wheel RUN echo ${REQUIREMENTS} | tr '|' '\n' > requirements.txt -RUN pip install -r requirements \ No newline at end of file +RUN pip install -r requirements.txt \ No newline at end of file From 5c65e11645feaf64f988979a96aa3f313badb853 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 4 Dec 2022 01:02:34 +0100 Subject: [PATCH 485/884] Dockerfile-test --- build/docker/{Dockerfile-python => Dockerfile-test} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename build/docker/{Dockerfile-python => Dockerfile-test} (100%) diff --git a/build/docker/Dockerfile-python b/build/docker/Dockerfile-test similarity index 100% rename from build/docker/Dockerfile-python rename to build/docker/Dockerfile-test From 57fc2b925395e89a8be4d0b2e1dcb22d5c12eae1 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 4 Dec 2022 01:06:38 +0100 Subject: [PATCH 486/884] Dockerfile-test debug --- build/docker/Dockerfile-test | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build/docker/Dockerfile-test b/build/docker/Dockerfile-test index 1d0a2545d..53a91089d 100644 --- a/build/docker/Dockerfile-test +++ b/build/docker/Dockerfile-test @@ -6,9 +6,13 @@ LABEL maintainer=henri@nagstamon.de RUN echo ${REQUIREMENTS} RUN apt -y update +RUN apt -y install apt-utils + RUN apt -y install libdbus-1-dev libkrb5-dev RUN python -m pip install --upgrade pip RUN pip install pytest pylint wheel RUN echo ${REQUIREMENTS} | tr '|' '\n' > requirements.txt +RUN echo ${REQUIREMENTS} +RUN cat requirements.txt RUN pip install -r requirements.txt \ No newline at end of file From d2c6f8e5b5b6e98ae04aadf5bb2da34b2d4acb75 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 4 Dec 2022 01:18:00 +0100 Subject: [PATCH 487/884] "" --- .github/workflows/build-release-latest.yml | 2 +- build/docker/Dockerfile-test | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index b2f83dfb8..150a2b357 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -30,7 +30,7 @@ jobs: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - run: docker pull ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }} --build-arg VERSION=${{ matrix.python-version }} --build-arg REQUIREMENTS=$(cat build/requirements/linux.txt | tr '\n' '|') -f build/docker/Dockerfile-${{ github.job }} . + - run: docker pull ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }} --build-arg VERSION=${{ matrix.python-version }} --build-arg REQUIREMENTS="$(cat build/requirements/linux.txt | tr '\n' '|')" -f build/docker/Dockerfile-${{ github.job }} . - run: docker push ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }} - name: Install dependencies run: | diff --git a/build/docker/Dockerfile-test b/build/docker/Dockerfile-test index 53a91089d..4218fcab9 100644 --- a/build/docker/Dockerfile-test +++ b/build/docker/Dockerfile-test @@ -4,7 +4,7 @@ ARG REQUIREMENTS FROM python:${VERSION} LABEL maintainer=henri@nagstamon.de -RUN echo ${REQUIREMENTS} +RUN echo "${REQUIREMENTS}" RUN apt -y update RUN apt -y install apt-utils @@ -12,7 +12,7 @@ RUN apt -y install libdbus-1-dev libkrb5-dev RUN python -m pip install --upgrade pip RUN pip install pytest pylint wheel -RUN echo ${REQUIREMENTS} | tr '|' '\n' > requirements.txt +RUN echo "${REQUIREMENTS}" | tr '+' '\n' > requirements.txt RUN echo ${REQUIREMENTS} RUN cat requirements.txt RUN pip install -r requirements.txt \ No newline at end of file From 18f77f228034812b6c3ce9775d8d93204e07c521 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 4 Dec 2022 01:42:39 +0100 Subject: [PATCH 488/884] base64 --- .github/workflows/build-release-latest.yml | 2 +- build/docker/Dockerfile-test | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 150a2b357..809aa967e 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -30,7 +30,7 @@ jobs: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - run: docker pull ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }} --build-arg VERSION=${{ matrix.python-version }} --build-arg REQUIREMENTS="$(cat build/requirements/linux.txt | tr '\n' '|')" -f build/docker/Dockerfile-${{ github.job }} . + - run: docker pull ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }} --build-arg VERSION=${{ matrix.python-version }} --build-arg REQUIREMENTS="$(cat build/requirements/linux.txt | base64 --wrap=0)" -f build/docker/Dockerfile-${{ github.job }} . - run: docker push ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }} - name: Install dependencies run: | diff --git a/build/docker/Dockerfile-test b/build/docker/Dockerfile-test index 4218fcab9..8c909ff92 100644 --- a/build/docker/Dockerfile-test +++ b/build/docker/Dockerfile-test @@ -1,10 +1,10 @@ ARG VERSION -ARG REQUIREMENTS - FROM python:${VERSION} LABEL maintainer=henri@nagstamon.de +ARG REQUIREMENTS RUN echo "${REQUIREMENTS}" + RUN apt -y update RUN apt -y install apt-utils @@ -12,7 +12,7 @@ RUN apt -y install libdbus-1-dev libkrb5-dev RUN python -m pip install --upgrade pip RUN pip install pytest pylint wheel -RUN echo "${REQUIREMENTS}" | tr '+' '\n' > requirements.txt -RUN echo ${REQUIREMENTS} +RUN echo ${REQUIREMENTS} | base64 --decode > requirements.txt RUN cat requirements.txt + RUN pip install -r requirements.txt \ No newline at end of file From b1802491e01152ddc55d718f00b6f7728f36509d Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 4 Dec 2022 01:48:34 +0100 Subject: [PATCH 489/884] docker built test --- .github/workflows/build-release-latest.yml | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 809aa967e..977985059 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -32,13 +32,13 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - run: docker pull ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }} --build-arg VERSION=${{ matrix.python-version }} --build-arg REQUIREMENTS="$(cat build/requirements/linux.txt | base64 --wrap=0)" -f build/docker/Dockerfile-${{ github.job }} . - run: docker push ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }} - - name: Install dependencies - run: | - sudo apt update - sudo apt -y install libdbus-1-dev libkrb5-dev - python -m pip install --upgrade pip - pip install pytest pylint wheel #flake8 - if [ -f build/requirements/linux.txt ]; then pip install -r build/requirements/linux.txt; fi + #- name: Install dependencies + # run: | + # sudo apt update + # sudo apt -y install libdbus-1-dev libkrb5-dev + # python -m pip install --upgrade pip + # pip install pytest pylint wheel #flake8 + # if [ -f build/requirements/linux.txt ]; then pip install -r build/requirements/linux.txt; fi # - name: Lint with flake8 # run: | # # stop the build if there are Python syntax errors or undefined names @@ -46,8 +46,7 @@ jobs: # # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide # flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - name: Test with unittest - run: | - python -m unittest tests/test_*.py + run: docker run --rm -v $PWD:/src ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }} -m unittest /src/tests/test_*.py debian: runs-on: ubuntu-latest From b974b90150e607e8b82b0fdc3a30831d5fdad108 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 4 Dec 2022 01:54:06 +0100 Subject: [PATCH 490/884] -m --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 977985059..3be76bb4f 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -46,7 +46,7 @@ jobs: # # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide # flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - name: Test with unittest - run: docker run --rm -v $PWD:/src ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }} -m unittest /src/tests/test_*.py + run: docker run --rm -v $PWD:/src ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }} python -m unittest /src/tests/test_*.py debian: runs-on: ubuntu-latest From c7af18d1f16ac79ccee84608c92ba90889468811 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 4 Dec 2022 01:57:33 +0100 Subject: [PATCH 491/884] --workdir /src --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 3be76bb4f..a0c453903 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -46,7 +46,7 @@ jobs: # # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide # flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - name: Test with unittest - run: docker run --rm -v $PWD:/src ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }} python -m unittest /src/tests/test_*.py + run: docker run --rm -v $PWD:/src --workdir /src ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }} python -m unittest tests/test_*.py debian: runs-on: ubuntu-latest From 69eff509849b08011ce01cd3fc2053738a1332b2 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 4 Dec 2022 02:02:42 +0100 Subject: [PATCH 492/884] no comment --- .github/workflows/build-release-latest.yml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index a0c453903..5d0cb73b9 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -30,15 +30,8 @@ jobs: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - run: docker pull ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }} --build-arg VERSION=${{ matrix.python-version }} --build-arg REQUIREMENTS="$(cat build/requirements/linux.txt | base64 --wrap=0)" -f build/docker/Dockerfile-${{ github.job }} . + - run: docker pull ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }}- || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }} --build-arg VERSION=${{ matrix.python-version }} --build-arg REQUIREMENTS="$(cat build/requirements/linux.txt | base64 --wrap=0)" -f build/docker/Dockerfile-${{ github.job }} . - run: docker push ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }} - #- name: Install dependencies - # run: | - # sudo apt update - # sudo apt -y install libdbus-1-dev libkrb5-dev - # python -m pip install --upgrade pip - # pip install pytest pylint wheel #flake8 - # if [ -f build/requirements/linux.txt ]; then pip install -r build/requirements/linux.txt; fi # - name: Lint with flake8 # run: | # # stop the build if there are Python syntax errors or undefined names From d584b8716346df2f19e19f46767cfe39671f3cc7 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 4 Dec 2022 02:13:09 +0100 Subject: [PATCH 493/884] -${{ steps.requirements_hash.outputs.HASH }} --- .github/workflows/build-release-latest.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 5d0cb73b9..235a80b4c 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -25,13 +25,15 @@ jobs: uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} + - id: requirements_hash + run: set HASH="$(md5sum build/requirements/linux.txt | cut -d\ -f1)" - uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - run: docker pull ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }}- || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }} --build-arg VERSION=${{ matrix.python-version }} --build-arg REQUIREMENTS="$(cat build/requirements/linux.txt | base64 --wrap=0)" -f build/docker/Dockerfile-${{ github.job }} . - - run: docker push ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }} + - run: docker pull ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }}-${{ steps.requirements_hash.outputs.HASH }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }}-${{ steps.requirements_hash.outputs.HASH }} --build-arg VERSION=${{ matrix.python-version }} --build-arg REQUIREMENTS="$(cat build/requirements/linux.txt | base64 --wrap=0)" -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }}-${{ steps.requirements_hash.outputs.HASH }} # - name: Lint with flake8 # run: | # # stop the build if there are Python syntax errors or undefined names @@ -39,7 +41,7 @@ jobs: # # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide # flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - name: Test with unittest - run: docker run --rm -v $PWD:/src --workdir /src ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }} python -m unittest tests/test_*.py + run: docker run --rm -v $PWD:/src --workdir /src ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }}-${{ steps.requirements_hash.outputs.HASH }} python -m unittest tests/test_*.py debian: runs-on: ubuntu-latest From 8ab65b15c8d9848365d6a6dc605899ac93e186b3 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 4 Dec 2022 02:15:39 +0100 Subject: [PATCH 494/884] >> $GITHUB_OUTPUT --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 235a80b4c..f5727f73f 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -26,7 +26,7 @@ jobs: with: python-version: ${{ matrix.python-version }} - id: requirements_hash - run: set HASH="$(md5sum build/requirements/linux.txt | cut -d\ -f1)" + run: echo "HASH=$(md5sum build/requirements/linux.txt | cut -d\ -f1)" >> $GITHUB_OUTPUT - uses: docker/login-action@v2 with: registry: ghcr.io From dd3a438203874fc1ebdaaad980ef822e776c0cf4 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 4 Dec 2022 02:16:59 +0100 Subject: [PATCH 495/884] cut --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index f5727f73f..5f5990558 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -26,7 +26,7 @@ jobs: with: python-version: ${{ matrix.python-version }} - id: requirements_hash - run: echo "HASH=$(md5sum build/requirements/linux.txt | cut -d\ -f1)" >> $GITHUB_OUTPUT + run: echo "HASH=$(md5sum build/requirements/linux.txt | cut -d\ -f1)" >> $GITHUB_OUTPUT - uses: docker/login-action@v2 with: registry: ghcr.io From f72ed4af6989bb5f9e64d3f0085f209e0275f43d Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 4 Dec 2022 02:28:50 +0100 Subject: [PATCH 496/884] needs: [fedora-34, fedora-35, fedora-36, fedora-37] --- .github/workflows/build-release-latest.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 5f5990558..a2d9dd3b4 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -229,7 +229,8 @@ jobs: repo-fedora: runs-on: ubuntu-latest # if not all are ready there might be trouble when downloading artifacts - needs: [debian, fedora-34, fedora-35, fedora-36, fedora-37, macos, windows-32, windows-64, windows-64-debug] + # maybe faster now with build containers + needs: [fedora-34, fedora-35, fedora-36, fedora-37] steps: # get binaries created by other jobs - uses: actions/download-artifact@v3 From 52c07b2436f9c866d1d67f9588fb3b198138e59b Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 4 Dec 2022 03:22:31 +0100 Subject: [PATCH 497/884] no hide option on non macos --- Nagstamon/QUI/__init__.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 0e6de2fe7..c7ae33ae0 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -4672,10 +4672,6 @@ def __init__(self, dialog): # display to use in fullscreen mode self.window.input_radiobutton_fullscreen: [self.window.label_fullscreen_display, self.window.input_combobox_fullscreen_display], - # offer option to hide icon in dock on macOS - self.window.input_radiobutton_icon_in_systray: [self.window.input_checkbox_hide_macos_dock_icon], - self.window.input_radiobutton_statusbar_floating: [self.window.input_checkbox_hide_macos_dock_icon], - # notifications in general self.window.input_checkbox_notification: [self.window.notification_groupbox], # sound at all @@ -4726,6 +4722,13 @@ def __init__(self, dialog): self.TOGGLE_DEPS_INVERTED = [self.window.input_checkbox_notification_custom_action_single] + # because this makes only sense in macOS these dependencies will be added here + if OS == OS_MACOS: + # offer option to hide icon in dock on macOS + self.TOGGLE_DEPS.update({ + self.window.input_radiobutton_icon_in_systray: [self.window.input_checkbox_hide_macos_dock_icon], + self.window.input_radiobutton_statusbar_floating: [self.window.input_checkbox_hide_macos_dock_icon]}) + # set title to current version self.window.setWindowTitle(' '.join((AppInfo.NAME, AppInfo.VERSION))) From 13f3af232610c343b65b0b6766c3011b5df7dce6 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 4 Dec 2022 09:33:13 +0100 Subject: [PATCH 498/884] no offset setting on non systray icon --- Nagstamon/QUI/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index c7ae33ae0..e0dddca91 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -4747,6 +4747,7 @@ def __init__(self, dialog): self.check_for_new_version.connect(check_version.check) # avoid offset spinbox if offset is not enabled + self.window.input_radiobutton_windowed.clicked.connect(self.toggle_systray_icon_offset) self.window.input_radiobutton_fullscreen.clicked.connect(self.toggle_systray_icon_offset) self.window.input_radiobutton_icon_in_systray.clicked.connect(self.toggle_systray_icon_offset) self.window.input_radiobutton_statusbar_floating.clicked.connect(self.toggle_systray_icon_offset) From 05044a867cf2456e7689cd7eeec1e581d58d1ae1 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 4 Dec 2022 13:00:11 +0100 Subject: [PATCH 499/884] no releases on master (again) --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index f34ec6a58..a2d9dd3b4 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -4,7 +4,7 @@ on: tags-ignore: 'v*' branches: - '**' - #- '!master' + - '!master' - '!*.*.*' env: From a5d3450488b0da75b3c1b457a220ea436b43e1dc Mon Sep 17 00:00:00 2001 From: Robert Scheck <robert@fedoraproject.org> Date: Sun, 4 Dec 2022 16:09:11 +0100 Subject: [PATCH 500/884] Add Nagstamon RPM packages for RHEL 9 (and derivatives/clones) --- .github/workflows/build-release-latest.yml | 15 ++++++++++- .github/workflows/build-release-stable.yml | 15 ++++++++++- Nagstamon/Helpers.py | 7 +++++ build/build.py | 3 ++- build/docker/Dockerfile-rhel-9 | 30 ++++++++++++++++++++++ 5 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 build/docker/Dockerfile-rhel-9 diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index a2d9dd3b4..13cc04c42 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -138,6 +138,19 @@ jobs: retention-days: 1 if-no-files-found: error + rhel-9: + runs-on: ubuntu-latest + needs: test + steps: + - uses: actions/checkout@v3 + - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + - uses: actions/upload-artifact@v3 + with: + path: build/*.rpm + retention-days: 1 + if-no-files-found: error + macos: runs-on: macos-10.15 needs: test @@ -252,7 +265,7 @@ jobs: github-release: runs-on: ubuntu-latest - needs: [debian, fedora-34, fedora-35, fedora-36, fedora-37, macos, windows-32, windows-64, windows-64-debug] + needs: [debian, fedora-34, fedora-35, fedora-36, fedora-37, rhel-9, macos, windows-32, windows-64, windows-64-debug] steps: - uses: actions/download-artifact@v3 - run: cd artifact && md5sum *agstamon* > md5sums.txt diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index 33a9b5f22..8d4fc3b65 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -101,6 +101,19 @@ jobs: retention-days: 1 if-no-files-found: error + rhel-9: + runs-on: ubuntu-latest + needs: test + steps: + - uses: actions/checkout@v3 + - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + - uses: actions/upload-artifact@v3 + with: + path: build/*.rpm + retention-days: 1 + if-no-files-found: error + macos: runs-on: macos-10.15 needs: test @@ -188,7 +201,7 @@ jobs: github-release: runs-on: ubuntu-latest - needs: [debian, fedora-34, fedora-35, fedora-36, fedora-37, macos, windows-32, windows-64] + needs: [debian, fedora-34, fedora-35, fedora-36, fedora-37, rhel-9, macos, windows-32, windows-64] steps: - uses: actions/download-artifact@v3 - run: cd artifact && md5sum *agstamon* > md5sums.txt diff --git a/Nagstamon/Helpers.py b/Nagstamon/Helpers.py index cf9ebbc3c..b03b09e1d 100644 --- a/Nagstamon/Helpers.py +++ b/Nagstamon/Helpers.py @@ -455,6 +455,13 @@ def get_distro(): if not line.startswith('#'): key, value = line.split('=', 1) os_release_dict[key] = value.strip('"').strip("'") + # Since CentOS Linux got retired by Red Hat, there are various RHEL derivatives/clones; flow is: + # CentOS Stream -> Red Hat Enterprise Linux -> (AlmaLinux, EuroLinux, Oracle Linux, Rocky Linux) + # Goal of this hack is to rule them all as Red Hat Enterprise Linux, the baseline distribution. + if re.search('^platform:el\d+$', os_release_dict.get('PLATFORM_ID', 'unknown')): + os_release_dict['ID'] = 'rhel' + os_release_dict['VERSION_ID'] = os_release_dict.get('VERSION_ID', 'unknown').split('.', 1)[0] + os_release_dict['NAME'] = 'Red Hat Enterprise Linux' return (os_release_dict.get('ID').lower(), os_release_dict.get('VERSION_ID', 'unknown').lower(), os_release_dict.get('NAME').lower()) diff --git a/build/build.py b/build/build.py index 48a60a1a5..ba53112b4 100644 --- a/build/build.py +++ b/build/build.py @@ -226,7 +226,8 @@ def rpmmain(): 'debian': debmain, 'ubuntu': debmain, 'linuxmint': debmain, - 'fedora': rpmmain + 'fedora': rpmmain, + 'rhel': rpmmain } if __name__ == '__main__': diff --git a/build/docker/Dockerfile-rhel-9 b/build/docker/Dockerfile-rhel-9 new file mode 100644 index 000000000..c8bcbaf05 --- /dev/null +++ b/build/docker/Dockerfile-rhel-9 @@ -0,0 +1,30 @@ +FROM rockylinux:9 +LABEL maintainer=henri@nagstamon.de + +RUN dnf -y install 'dnf-command(config-manager)' \ + epel-release && \ + crb enable && \ + dnf -y install desktop-file-utils \ + git \ + python3 \ + python3-beautifulsoup4 \ + python3-cryptography \ + python3-dateutil \ + python3-devel \ + python3-keyring \ + python3-lxml \ + python3-psutil \ + python3-qt5 \ + python3-qt5-devel \ + python3-requests \ + python3-requests-kerberos \ + python3-SecretStorage \ + qt5-qtsvg \ + qt5-qtmultimedia \ + rpm-build + +# ugly workaround for legacy Qt5 on RHEL < 10 +CMD cd /nagstamon/build && \ + sed -i s/pyqt6/pyqt5/g redhat/nagstamon.spec && \ + sed -i s/qt6/qt5/g redhat/nagstamon.spec && \ + /usr/bin/python3 build.py From 58a6d2d06932b2694a2e9bb6bc0201916bf3614e Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 6 Dec 2022 07:19:13 +0100 Subject: [PATCH 501/884] rhel --- .github/workflows/build-release-latest.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 13cc04c42..d7ade4aca 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -143,8 +143,14 @@ jobs: needs: test steps: - uses: actions/checkout@v3 - - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + - uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }} + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} - uses: actions/upload-artifact@v3 with: path: build/*.rpm From 1e12215323fb70c0a6b077edd444556590af9b54 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 6 Dec 2022 07:26:23 +0100 Subject: [PATCH 502/884] rhel rpms added --- .github/workflows/build-release-latest.yml | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index d7ade4aca..c0e3b0ffc 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -245,11 +245,14 @@ jobs: retention-days: 1 if-no-files-found: error - repo-fedora: + repo-rpm: runs-on: ubuntu-latest # if not all are ready there might be trouble when downloading artifacts # maybe faster now with build containers - needs: [fedora-34, fedora-35, fedora-36, fedora-37] + needs: [fedora-34, fedora-35, fedora-36, fedora-37, rhel-9] + strategy: + matrix: + os_family: [fedora, rhel] steps: # get binaries created by other jobs - uses: actions/download-artifact@v3 @@ -259,12 +262,12 @@ jobs: - run: chmod -R go-rwx ~/.ssh # get and prepare nagstamon-jekyll - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - - run: rm -rf ${{ env.repo_dir }}/fedora/latest - - run: mkdir -p ${{ env.repo_dir }}/fedora/latest + - run: rm -rf ${{ env.repo_dir }}/${{ os_family }} + - run: mkdir -p ${{ env.repo_dir }}/${{ os_family }}/latest # copy *.rpm files into nagstamon-jekyll - - run: cp -r artifact/*.rpm ${{ env.repo_dir }}/fedora/latest + - run: cp -r artifact/*.rpm ${{ env.repo_dir }}/${{ os_family }}/latest # create rpm repo via Fedora container - - run: docker run --rm -v $PWD/${{ env.repo_dir }}/fedora/latest:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo" + - run: docker run --rm -v $PWD/${{ env.repo_dir }}/${{ os_family }}/latest:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo" # commit and push new binaries to nagstamon-repo - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - run: cd ${{ env.repo_dir }} && git add . && git commit -am "new latest repo" && git push From 1d9012ad0257f06c33a09925434968c96dc5a910 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 6 Dec 2022 07:28:10 +0100 Subject: [PATCH 503/884] rhel rpms added matrix --- .github/workflows/build-release-latest.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index c0e3b0ffc..a80f5786c 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -262,12 +262,12 @@ jobs: - run: chmod -R go-rwx ~/.ssh # get and prepare nagstamon-jekyll - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - - run: rm -rf ${{ env.repo_dir }}/${{ os_family }} - - run: mkdir -p ${{ env.repo_dir }}/${{ os_family }}/latest + - run: rm -rf ${{ env.repo_dir }}/${{ matrix.os_family }} + - run: mkdir -p ${{ env.repo_dir }}/${{ matrix.os_family }}/latest # copy *.rpm files into nagstamon-jekyll - - run: cp -r artifact/*.rpm ${{ env.repo_dir }}/${{ os_family }}/latest + - run: cp -r artifact/*.rpm ${{ env.repo_dir }}/${{ matrix.os_family }}/latest # create rpm repo via Fedora container - - run: docker run --rm -v $PWD/${{ env.repo_dir }}/${{ os_family }}/latest:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo" + - run: docker run --rm -v $PWD/${{ env.repo_dir }}/${{ matrix.os_family }}/latest:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo" # commit and push new binaries to nagstamon-repo - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - run: cd ${{ env.repo_dir }} && git add . && git commit -am "new latest repo" && git push From 106cc8b88bda6daaed8da1d47aafcf27214a61f2 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 6 Dec 2022 07:35:06 +0100 Subject: [PATCH 504/884] git pull --- .github/workflows/build-release-latest.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index a80f5786c..68c3899f2 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -21,10 +21,11 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 - with: - python-version: ${{ matrix.python-version }} + #- name: Set up Python ${{ matrix.python-version }} + # uses: actions/setup-python@v3 + # with: + # python-version: ${{ matrix.python-version }} + # somehow weird way to get the hash over the requirements to be aware if they changed - id: requirements_hash run: echo "HASH=$(md5sum build/requirements/linux.txt | cut -d\ -f1)" >> $GITHUB_OUTPUT - uses: docker/login-action@v2 @@ -270,7 +271,7 @@ jobs: - run: docker run --rm -v $PWD/${{ env.repo_dir }}/${{ matrix.os_family }}/latest:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo" # commit and push new binaries to nagstamon-repo - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - - run: cd ${{ env.repo_dir }} && git add . && git commit -am "new latest repo" && git push + - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ matrix.os_family }}" && git push github-release: runs-on: ubuntu-latest From e6eb185d708a295c9e3724374e08c14b3fcf3ce9 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 6 Dec 2022 07:44:24 +0100 Subject: [PATCH 505/884] only family rpm per repo --- .github/workflows/build-release-latest.yml | 27 ++++++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 68c3899f2..3ef5cf3ce 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -21,18 +21,16 @@ jobs: steps: - uses: actions/checkout@v3 - #- name: Set up Python ${{ matrix.python-version }} - # uses: actions/setup-python@v3 - # with: - # python-version: ${{ matrix.python-version }} # somehow weird way to get the hash over the requirements to be aware if they changed - id: requirements_hash run: echo "HASH=$(md5sum build/requirements/linux.txt | cut -d\ -f1)" >> $GITHUB_OUTPUT + # docker login is needed for pushing the test image - uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + # if image defined by hash over requirements is not pullable aka does not exist it will be created and pushed - run: docker pull ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }}-${{ steps.requirements_hash.outputs.HASH }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }}-${{ steps.requirements_hash.outputs.HASH }} --build-arg VERSION=${{ matrix.python-version }} --build-arg REQUIREMENTS="$(cat build/requirements/linux.txt | base64 --wrap=0)" -f build/docker/Dockerfile-${{ github.job }} . - run: docker push ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }}-${{ steps.requirements_hash.outputs.HASH }} # - name: Lint with flake8 @@ -42,6 +40,7 @@ jobs: # # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide # flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - name: Test with unittest + # using the tests in precompiled image makes them way faster instead of creating the test environment every time from scratch run: docker run --rm -v $PWD:/src --workdir /src ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }}-${{ steps.requirements_hash.outputs.HASH }} python -m unittest tests/test_*.py debian: @@ -49,13 +48,16 @@ jobs: needs: test steps: - uses: actions/checkout@v3 + # docker login is needed for pushing the build image - uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + # if image defined by hash over requirements is not pullable aka does not exist it will be created and pushed - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . - run: docker push ${{ env.cr_image }}-${{ github.job }} + # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon -e DEB_BUILD_OPTIONS=nocheck ${{ env.cr_image }}-${{ github.job }} - uses: actions/upload-artifact@v3 with: @@ -68,13 +70,16 @@ jobs: needs: test steps: - uses: actions/checkout@v3 + # docker login is needed for pushing the build image - uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + # if image defined by hash over requirements is not pullable aka does not exist it will be created and pushed - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . - run: docker push ${{ env.cr_image }}-${{ github.job }} + # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} - uses: actions/upload-artifact@v3 with: @@ -87,13 +92,16 @@ jobs: needs: test steps: - uses: actions/checkout@v3 + # docker login is needed for pushing the build image - uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + # if image defined by hash over requirements is not pullable aka does not exist it will be created and pushed - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . - run: docker push ${{ env.cr_image }}-${{ github.job }} + # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} - uses: actions/upload-artifact@v3 with: @@ -106,13 +114,16 @@ jobs: needs: test steps: - uses: actions/checkout@v3 + # docker login is needed for pushing the build image - uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + # if image defined by hash over requirements is not pullable aka does not exist it will be created and pushed - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . - run: docker push ${{ env.cr_image }}-${{ github.job }} + # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} - uses: actions/upload-artifact@v3 with: @@ -125,13 +136,16 @@ jobs: needs: test steps: - uses: actions/checkout@v3 + # docker login is needed for pushing the build image - uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + # if image defined by hash over requirements is not pullable aka does not exist it will be created and pushed - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . - run: docker push ${{ env.cr_image }}-${{ github.job }} + # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} - uses: actions/upload-artifact@v3 with: @@ -144,13 +158,16 @@ jobs: needs: test steps: - uses: actions/checkout@v3 + # docker login is needed for pushing the build image - uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + # if image defined by hash over requirements is not pullable aka does not exist it will be created and pushed - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . - run: docker push ${{ env.cr_image }}-${{ github.job }} + # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} - uses: actions/upload-artifact@v3 with: @@ -266,7 +283,7 @@ jobs: - run: rm -rf ${{ env.repo_dir }}/${{ matrix.os_family }} - run: mkdir -p ${{ env.repo_dir }}/${{ matrix.os_family }}/latest # copy *.rpm files into nagstamon-jekyll - - run: cp -r artifact/*.rpm ${{ env.repo_dir }}/${{ matrix.os_family }}/latest + - run: cp -r artifact/*.{{ matrix.os_family }}*.rpm ${{ env.repo_dir }}/${{ matrix.os_family }}/latest # create rpm repo via Fedora container - run: docker run --rm -v $PWD/${{ env.repo_dir }}/${{ matrix.os_family }}/latest:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo" # commit and push new binaries to nagstamon-repo From b797a735537ffac2a2f304cbd151e4b9db3f3142 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 6 Dec 2022 07:48:14 +0100 Subject: [PATCH 506/884] =?UTF-8?q?=C2=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 3ef5cf3ce..83ff34580 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -283,7 +283,7 @@ jobs: - run: rm -rf ${{ env.repo_dir }}/${{ matrix.os_family }} - run: mkdir -p ${{ env.repo_dir }}/${{ matrix.os_family }}/latest # copy *.rpm files into nagstamon-jekyll - - run: cp -r artifact/*.{{ matrix.os_family }}*.rpm ${{ env.repo_dir }}/${{ matrix.os_family }}/latest + - run: cp -r artifact/*.${{ matrix.os_family }}*.rpm ${{ env.repo_dir }}/${{ matrix.os_family }}/latest # create rpm repo via Fedora container - run: docker run --rm -v $PWD/${{ env.repo_dir }}/${{ matrix.os_family }}/latest:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo" # commit and push new binaries to nagstamon-repo From db0d617e5248c966412e4e1c27cd6b234eeabc7a Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 6 Dec 2022 07:53:52 +0100 Subject: [PATCH 507/884] rm only latest --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 83ff34580..06baa24a4 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -280,7 +280,7 @@ jobs: - run: chmod -R go-rwx ~/.ssh # get and prepare nagstamon-jekyll - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - - run: rm -rf ${{ env.repo_dir }}/${{ matrix.os_family }} + - run: rm -rf ${{ env.repo_dir }}/${{ matrix.os_family }}/latest - run: mkdir -p ${{ env.repo_dir }}/${{ matrix.os_family }}/latest # copy *.rpm files into nagstamon-jekyll - run: cp -r artifact/*.${{ matrix.os_family }}*.rpm ${{ env.repo_dir }}/${{ matrix.os_family }}/latest From 1f2cfcbdba0ebfaa0f67065c4732ca8fe1f5bdc9 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Fri, 9 Dec 2022 21:25:00 +0100 Subject: [PATCH 508/884] qt5 for rhel9 and no python3-crypto --- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- build/docker/Dockerfile-fedora-34 | 1 - build/docker/Dockerfile-fedora-35 | 1 - build/docker/Dockerfile-fedora-36 | 1 - build/docker/Dockerfile-fedora-37 | 1 - build/redhat/nagstamon.spec | 1 - setup.py | 5 ++--- 8 files changed, 5 insertions(+), 11 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index da56abb52..224d66432 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.11-20221203' + VERSION = '3.11-20221209' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index b014e5350..40591f79a 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.11-20221203) unstable; urgency=low +nagstamon (3.11-20221209) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Sat, Dec 03 2022 08:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Fri, Dec 09 2022 08:00:00 +0100 nagstamon (3.10.1) stable; urgency=low * New upstream diff --git a/build/docker/Dockerfile-fedora-34 b/build/docker/Dockerfile-fedora-34 index 14b8ad971..8e6917664 100644 --- a/build/docker/Dockerfile-fedora-34 +++ b/build/docker/Dockerfile-fedora-34 @@ -5,7 +5,6 @@ RUN dnf -y install desktop-file-utils \ git \ python3 \ python3-beautifulsoup4 \ - python3-crypto \ python3-cryptography \ python3-dateutil \ python3-devel \ diff --git a/build/docker/Dockerfile-fedora-35 b/build/docker/Dockerfile-fedora-35 index e9d5f2ed2..4e5247c53 100644 --- a/build/docker/Dockerfile-fedora-35 +++ b/build/docker/Dockerfile-fedora-35 @@ -5,7 +5,6 @@ RUN dnf -y install desktop-file-utils \ git \ python3 \ python3-beautifulsoup4 \ - python3-crypto \ python3-cryptography \ python3-dateutil \ python3-devel \ diff --git a/build/docker/Dockerfile-fedora-36 b/build/docker/Dockerfile-fedora-36 index 11b08bf0d..b22d17f72 100644 --- a/build/docker/Dockerfile-fedora-36 +++ b/build/docker/Dockerfile-fedora-36 @@ -5,7 +5,6 @@ RUN dnf -y install desktop-file-utils \ git \ python3 \ python3-beautifulsoup4 \ - python3-crypto \ python3-cryptography \ python3-dateutil \ python3-devel \ diff --git a/build/docker/Dockerfile-fedora-37 b/build/docker/Dockerfile-fedora-37 index 7bf37c740..d41e42067 100644 --- a/build/docker/Dockerfile-fedora-37 +++ b/build/docker/Dockerfile-fedora-37 @@ -5,7 +5,6 @@ RUN dnf -y install desktop-file-utils \ git \ python3 \ python3-beautifulsoup4 \ - python3-crypto \ python3-cryptography \ python3-dateutil \ python3-devel \ diff --git a/build/redhat/nagstamon.spec b/build/redhat/nagstamon.spec index cfea8acfb..fce111e0e 100644 --- a/build/redhat/nagstamon.spec +++ b/build/redhat/nagstamon.spec @@ -17,7 +17,6 @@ BuildRequires: python3-pyqt6-devel BuildRequires: desktop-file-utils Requires: python3 Requires: python3-beautifulsoup4 -Requires: python3-crypto Requires: python3-cryptography Requires: python3-dateutil Requires: python3-dbus diff --git a/setup.py b/setup.py index 8535cfc39..d02ecbd62 100644 --- a/setup.py +++ b/setup.py @@ -87,10 +87,10 @@ applications_shortcut=False) # older Fedora needs Qt5 -if DIST.lower() == 'fedora' and int(DIST_VERSION) < 36: +if DIST.lower() == 'fedora' and int(DIST_VERSION) < 36 or \ + DIST.lower() == 'rhel' and int(DIST_VERSION) <= 9: bdist_rpm_options = dict(requires='python3 ' 'python3-beautifulsoup4 ' - 'python3-crypto ' 'python3-cryptography ' 'python3-dateutil ' 'python3-keyring ' @@ -107,7 +107,6 @@ else: bdist_rpm_options = dict(requires='python3 ' 'python3-beautifulsoup4 ' - 'python3-crypto ' 'python3-cryptography ' 'python3-dateutil ' 'python3-keyring ' From bc08ff9bd5b28077409b54e219a7f5b1ce76deb6 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 15 Dec 2022 12:47:23 +0100 Subject: [PATCH 509/884] try to be compatible with X11 session management --- Nagstamon/Config.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 224d66432..0f8b466ca 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -18,10 +18,10 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA +from argparse import ArgumentParser import os import platform import sys -import argparse import configparser import base64 import zlib @@ -321,16 +321,20 @@ def __init__(self): # would not find a config file self.unconfigured = True - # when more tha a config directory was given something is wrong - if len(sys.argv) > 2: - print('Currently Nagstamon supports only 1 config directory.') - self.configdir = sys.argv[1] - - # try to use a given config file - there must be one given - elif len(sys.argv) == 2: - self.configdir = sys.argv[1] - - # otherwise if there exits a configdir in current working directory it should be used + # get CLI arguments + parser = ArgumentParser(prog='nagstamon') + # mitigate session restore problem https://github.com/HenriWahl/Nagstamon/issues/878 + if len(sys.argv) == 2 or len(sys.argv) >= 6: + # only add configdir if it might be included at all + parser.add_argument('configdir', default=None) + # -session and -name are used by X11 session management and could be + # safely ignored because nagstamon keeps its own session info + parser.add_argument('-session', default=None, required=False) + parser.add_argument('-name', default=None, required=False) + arguments = parser.parse_args() + if 'configdir' in arguments.__dict__: + self.configdir = arguments.configdir + # otherwise if there exists a configdir in current working directory it should be used elif os.path.exists(os.getcwd() + os.sep + 'nagstamon.config'): self.configdir = os.getcwd() + os.sep + 'nagstamon.config' else: From 7cc15c17b9e0785f540107e08307070868ee52f4 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 15 Dec 2022 12:48:06 +0100 Subject: [PATCH 510/884] 3.11-20221215 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 0f8b466ca..98698ccac 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -125,7 +125,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.11-20221209' + VERSION = '3.11-20221215' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 40591f79a..664567697 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.11-20221209) unstable; urgency=low +nagstamon (3.11-20221215) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Fri, Dec 09 2022 08:00:00 +0100 From 74bbddcf6df184a7eefe447edad626156f3ea980 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 15 Dec 2022 13:04:34 +0100 Subject: [PATCH 511/884] started DESKTOP_NEEDS_FIX --- Nagstamon/Config.py | 18 +++++++----------- Nagstamon/QUI/__init__.py | 6 +++--- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 98698ccac..f55b713c7 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -56,18 +56,14 @@ else: DESKTOP_WAYLAND = False -# Cinnamon detection -# CINNAMON_VERSION 4.2.4 -# DESKTOP_SESSION cinnamon -# GDMSESSION cinnamon -# XDG_CURRENT_DESKTOP X-Cinnamon -# XDG_SESSION_DESKTOP cinnamon -if 'CINNAMON_VERSION' in os.environ or\ - 'DESKTOP_SESSION' in os.environ and os.environ['DESKTOP_SESSION'] == 'cinnamon' or \ - 'XDG_SESSION_DESKTOP' in os.environ and os.environ['XDG_SESSION_DESKTOP'] == 'cinnamon': - DESKTOP_CINNAMON = True +# detection of somehow quirky desktop enviroments which might need a fix +QUIRKY_DESKTOPS = ('cinnamon', 'gnome-flashback-metacity') +if os.environ.get('CINNAMON_VERSION') or\ + os.environ.get('DESKTOP_SESSION') in QUIRKY_DESKTOPS or \ + os.environ.get('XDG_SESSION_DESKTOP') in QUIRKY_DESKTOPS: + DESKTOP_NEEDS_FIX = True else: - DESKTOP_CINNAMON = False + DESKTOP_NEEDS_FIX = False # queue.Queue() needs threading module which might be not such a good idea to be used # because QThread is already in use diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 8a2be4a90..eb3389b17 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -40,7 +40,7 @@ conf, CONFIG_STRINGS, debug_queue, - DESKTOP_CINNAMON, + DESKTOP_NEEDS_FIX, KEYRING, OS_NON_LINUX, OS, @@ -1740,8 +1740,8 @@ def calculate_size(self): available_x = self.get_screen().availableGeometry().x() available_y = self.get_screen().availableGeometry().y() - # Workaround for Cinnamon - if OS not in OS_NON_LINUX and DESKTOP_CINNAMON: + # Workaround for Cinnamon + GNOME Flashback + if OS not in OS_NON_LINUX and DESKTOP_NEEDS_FIX: if available_x == 0: available_x = available_width if available_y == 0: From 3666c72364f5ff8f8cebd61517b189565f461cca Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 15 Dec 2022 13:10:37 +0100 Subject: [PATCH 512/884] ignore unkown arguments --- Nagstamon/Config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index f55b713c7..606f6346f 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -327,8 +327,8 @@ def __init__(self): # safely ignored because nagstamon keeps its own session info parser.add_argument('-session', default=None, required=False) parser.add_argument('-name', default=None, required=False) - arguments = parser.parse_args() - if 'configdir' in arguments.__dict__: + arguments, unknown_arguments = parser.parse_known_args() + if 'configdir' in arguments: self.configdir = arguments.configdir # otherwise if there exists a configdir in current working directory it should be used elif os.path.exists(os.getcwd() + os.sep + 'nagstamon.config'): From c82a72b2971a82d558249c6ce3d608ab1650b70b Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 15 Dec 2022 23:41:08 +0100 Subject: [PATCH 513/884] option for desktop env fix --- Nagstamon/Config.py | 5 + Nagstamon/QUI/__init__.py | 8 +- Nagstamon/resources/qui/settings_main.ui | 202 ++++++++++++----------- 3 files changed, 117 insertions(+), 98 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 606f6346f..c01f18eb8 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -283,6 +283,11 @@ def __init__(self): self.systray_offset_use = False self.systray_offset = 10 self.hide_macos_dock_icon = False + # as default enable on Linux Desktops like Cinnamon and Gnome Flashback + if DESKTOP_NEEDS_FIX: + self.enable_position_fix = True + else: + self.enable_position_fix = False self.font = '' self.defaults_acknowledge_sticky = False self.defaults_acknowledge_send_notification = False diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index eb3389b17..0ab4624f5 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -1741,7 +1741,7 @@ def calculate_size(self): available_y = self.get_screen().availableGeometry().y() # Workaround for Cinnamon + GNOME Flashback - if OS not in OS_NON_LINUX and DESKTOP_NEEDS_FIX: + if OS not in OS_NON_LINUX and conf.enable_position_fix: if available_x == 0: available_x = available_width if available_y == 0: @@ -4729,6 +4729,12 @@ def __init__(self, dialog): self.window.input_radiobutton_icon_in_systray: [self.window.input_checkbox_hide_macos_dock_icon], self.window.input_radiobutton_statusbar_floating: [self.window.input_checkbox_hide_macos_dock_icon]}) + # show option to enable position fix only on Unices + if not OS in OS_NON_LINUX: + self.window.input_checkbox_enable_position_fix.show() + else: + self.window.input_checkbox_enable_position_fix.hide() + # set title to current version self.window.setWindowTitle(' '.join((AppInfo.NAME, AppInfo.VERSION))) diff --git a/Nagstamon/resources/qui/settings_main.ui b/Nagstamon/resources/qui/settings_main.ui index b81424b30..2244e3fb8 100644 --- a/Nagstamon/resources/qui/settings_main.ui +++ b/Nagstamon/resources/qui/settings_main.ui @@ -582,102 +582,6 @@ </layout> </widget> </item> - <item row="2" column="0"> - <widget class="QGroupBox" name="groupbox_appearance"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="title"> - <string>Appearance:</string> - </property> - <layout class="QGridLayout" name="gridLayout_17"> - <property name="leftMargin"> - <number>10</number> - </property> - <property name="topMargin"> - <number>10</number> - </property> - <property name="rightMargin"> - <number>10</number> - </property> - <property name="bottomMargin"> - <number>10</number> - </property> - <property name="spacing"> - <number>5</number> - </property> - <item row="1" column="0"> - <widget class="QRadioButton" name="input_radiobutton_icon_in_systray"> - <property name="text"> - <string>Icon in systray</string> - </property> - </widget> - </item> - <item row="7" column="0" colspan="2"> - <widget class="QCheckBox" name="input_checkbox_systray_offset_use"> - <property name="text"> - <string>Use offset to correct position problems</string> - </property> - </widget> - </item> - <item row="4" column="0"> - <widget class="QRadioButton" name="input_radiobutton_windowed"> - <property name="text"> - <string>Window</string> - </property> - </widget> - </item> - <item row="12" column="0"> - <layout class="QGridLayout" name="gridLayout_20"> - <item row="1" column="0"> - <widget class="QLabel" name="label_fullscreen_display"> - <property name="text"> - <string>Display to use:</string> - </property> - </widget> - </item> - <item row="0" column="0"> - <widget class="QLabel" name="label_offset_statuswindow"> - <property name="text"> - <string>Offset for statuswindow:</string> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="QSpinBox" name="input_spinbox_systray_offset"/> - </item> - <item row="1" column="1"> - <widget class="QComboBox" name="input_combobox_fullscreen_display"/> - </item> - </layout> - </item> - <item row="5" column="0"> - <widget class="QRadioButton" name="input_radiobutton_fullscreen"> - <property name="text"> - <string>Fullscreen</string> - </property> - </widget> - </item> - <item row="0" column="0"> - <widget class="QRadioButton" name="input_radiobutton_statusbar_floating"> - <property name="text"> - <string>Floating statusbar</string> - </property> - </widget> - </item> - <item row="13" column="0"> - <widget class="QCheckBox" name="input_checkbox_hide_macos_dock_icon"> - <property name="text"> - <string>Hide macOS Dock icon - needs restart</string> - </property> - </widget> - </item> - </layout> - </widget> - </item> <item row="4" column="0"> <widget class="QGroupBox" name="groupbox_font"> <property name="sizePolicy"> @@ -773,6 +677,109 @@ </layout> </widget> </item> + <item row="2" column="0"> + <widget class="QGroupBox" name="groupbox_appearance"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Appearance:</string> + </property> + <layout class="QGridLayout" name="gridLayout_17"> + <property name="leftMargin"> + <number>10</number> + </property> + <property name="topMargin"> + <number>10</number> + </property> + <property name="rightMargin"> + <number>10</number> + </property> + <property name="bottomMargin"> + <number>10</number> + </property> + <property name="spacing"> + <number>5</number> + </property> + <item row="8" column="0" colspan="2"> + <widget class="QCheckBox" name="input_checkbox_systray_offset_use"> + <property name="text"> + <string>Use offset to correct position problems</string> + </property> + </widget> + </item> + <item row="6" column="0"> + <widget class="QRadioButton" name="input_radiobutton_fullscreen"> + <property name="text"> + <string>Fullscreen</string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QRadioButton" name="input_radiobutton_statusbar_floating"> + <property name="text"> + <string>Floating statusbar</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QRadioButton" name="input_radiobutton_icon_in_systray"> + <property name="text"> + <string>Icon in systray</string> + </property> + </widget> + </item> + <item row="5" column="0"> + <widget class="QRadioButton" name="input_radiobutton_windowed"> + <property name="text"> + <string>Window</string> + </property> + </widget> + </item> + <item row="13" column="0"> + <layout class="QGridLayout" name="gridLayout_20"> + <item row="1" column="0"> + <widget class="QLabel" name="label_fullscreen_display"> + <property name="text"> + <string>Display to use:</string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label_offset_statuswindow"> + <property name="text"> + <string>Offset for statuswindow:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QSpinBox" name="input_spinbox_systray_offset"/> + </item> + <item row="1" column="1"> + <widget class="QComboBox" name="input_combobox_fullscreen_display"/> + </item> + </layout> + </item> + <item row="14" column="0"> + <widget class="QCheckBox" name="input_checkbox_hide_macos_dock_icon"> + <property name="text"> + <string>Hide macOS Dock icon - needs restart</string> + </property> + </widget> + </item> + <item row="15" column="0"> + <widget class="QCheckBox" name="input_checkbox_enable_position_fix"> + <property name="text"> + <string>Enable position fix - might be useful in Cinnamon</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> </layout> </widget> <widget class="QWidget" name="tab_filters"> @@ -3183,11 +3190,13 @@ <tabstop>input_radiobutton_short_display</tabstop> <tabstop>input_radiobutton_statusbar_floating</tabstop> <tabstop>input_radiobutton_icon_in_systray</tabstop> + <tabstop>input_radiobutton_windowed</tabstop> <tabstop>input_radiobutton_fullscreen</tabstop> <tabstop>input_checkbox_systray_offset_use</tabstop> <tabstop>input_spinbox_systray_offset</tabstop> <tabstop>input_combobox_fullscreen_display</tabstop> <tabstop>input_checkbox_hide_macos_dock_icon</tabstop> + <tabstop>input_checkbox_enable_position_fix</tabstop> <tabstop>button_fontchooser</tabstop> <tabstop>button_default_font</tabstop> <tabstop>input_radiobutton_popup_details_hover</tabstop> @@ -3328,7 +3337,6 @@ <tabstop>input_checkbox_show_grid</tabstop> <tabstop>input_checkbox_grid_use_custom_intensity</tabstop> <tabstop>input_slider_grid_alternation_intensity</tabstop> - <tabstop>input_radiobutton_windowed</tabstop> <tabstop>input_lineedit_notification_custom_action_separator</tabstop> <tabstop>input_checkbox_notification_custom_action_single</tabstop> <tabstop>input_checkbox_filter_all_unreachable_services</tabstop> From a7b456b5801445f2121635afac6fad182e4fc9ef Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 15 Dec 2022 23:48:15 +0100 Subject: [PATCH 514/884] 3.11-20221216 --- Nagstamon/Config.py | 2 +- Nagstamon/QUI/__init__.py | 4 ++++ build/debian/changelog | 5 +++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index c01f18eb8..765547433 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -121,7 +121,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.11-20221215' + VERSION = '3.11-20221216' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 0ab4624f5..19e2de867 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -4974,6 +4974,10 @@ def initialize(self): if OS != OS_MACOS: self.window.input_checkbox_hide_macos_dock_icon.hide() + # avoid showing offset setting if not icon in systray is configured + if not OS in OS_NON_LINUX and not conf.icon_in_systray: + self.toggle_systray_icon_offset() + # important final size adjustment self.window.adjustSize() diff --git a/build/debian/changelog b/build/debian/changelog index 664567697..be076e339 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,8 @@ -nagstamon (3.11-20221215) unstable; urgency=low +nagstamon (3.11-20221216) unstable; urgency=low * New upstream + - stuff - -- Henri Wahl <henri@nagstamon.de> Fri, Dec 09 2022 08:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Fri, Dec 16 2022 08:00:00 +0100 nagstamon (3.10.1) stable; urgency=low * New upstream From bea9e5850d4d52a2cebc998f0a17b82d258e764c Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Fri, 16 Dec 2022 07:41:38 +0100 Subject: [PATCH 515/884] shorter option description --- Nagstamon/resources/qui/settings_main.ui | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Nagstamon/resources/qui/settings_main.ui b/Nagstamon/resources/qui/settings_main.ui index 2244e3fb8..48793167e 100644 --- a/Nagstamon/resources/qui/settings_main.ui +++ b/Nagstamon/resources/qui/settings_main.ui @@ -704,10 +704,10 @@ <property name="spacing"> <number>5</number> </property> - <item row="8" column="0" colspan="2"> - <widget class="QCheckBox" name="input_checkbox_systray_offset_use"> + <item row="15" column="0"> + <widget class="QCheckBox" name="input_checkbox_enable_position_fix"> <property name="text"> - <string>Use offset to correct position problems</string> + <string>Enable position fix</string> </property> </widget> </item> @@ -770,10 +770,10 @@ </property> </widget> </item> - <item row="15" column="0"> - <widget class="QCheckBox" name="input_checkbox_enable_position_fix"> + <item row="8" column="0" colspan="2"> + <widget class="QCheckBox" name="input_checkbox_systray_offset_use"> <property name="text"> - <string>Enable position fix - might be useful in Cinnamon</string> + <string>Use offset to correct position problems</string> </property> </widget> </item> From 58b318c8fd0599a2761bf9ef744b43baf80e362f Mon Sep 17 00:00:00 2001 From: Benoit Poulet <benoit.poulet@businessdecision.com> Date: Fri, 20 Jan 2023 15:12:04 +0100 Subject: [PATCH 516/884] Fix auth on 21.10 API v2 in 21.10 throw an error 500 when you have no valid token. --- Nagstamon/Servers/Centreon/CentreonAPI.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Servers/Centreon/CentreonAPI.py b/Nagstamon/Servers/Centreon/CentreonAPI.py index 93bcdf2bb..2a12945af 100644 --- a/Nagstamon/Servers/Centreon/CentreonAPI.py +++ b/Nagstamon/Servers/Centreon/CentreonAPI.py @@ -734,8 +734,13 @@ def check_session(self): result = self.FetchURL(self.urls_centreon['resources'] + '?' + urllib.parse.urlencode(cgi_data), giveback="raw") - # If we got an 403 or 401, the token expired and must be renewed - if result.status_code == 403 or result.status_code == 401: + # If we got an 403 or 401 (and 500 for version 21.), the token expired and must be renewed + if self.centreon_version_major == 21: + ressources_response_list = [401, 403, 500] + else: + ressources_response_list = [401, 403] + + if result.status_code in ressources_response_list: self.token = self.get_token().result if conf.debug_mode == True: self.Debug(server='[' + self.get_name() + ']', debug='Check-session, session renewed') From 6cce602f9a83db92975529a7dc403d061bcf88c9 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 21 Jan 2023 11:24:03 +0100 Subject: [PATCH 517/884] 3.11-20230121 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 765547433..a441a34ed 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -121,7 +121,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.11-20221216' + VERSION = '3.11-20230121' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index be076e339..eba4361a2 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,8 +1,8 @@ -nagstamon (3.11-20221216) unstable; urgency=low +nagstamon (3.11-20230121) unstable; urgency=low * New upstream - stuff - -- Henri Wahl <henri@nagstamon.de> Fri, Dec 16 2022 08:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Sat, Jan 21 2023 08:00:00 +0100 nagstamon (3.10.1) stable; urgency=low * New upstream From 26fe23f1b6b61fe1041b99fe6029cee04983f334 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 22 Jan 2023 17:12:30 +0100 Subject: [PATCH 518/884] icinga #891 --- Nagstamon/Config.py | 2 +- Nagstamon/Servers/IcingaDBWeb.py | 2 +- Nagstamon/Servers/IcingaWeb2.py | 2 +- build/debian/changelog | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index a441a34ed..26e1db914 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -121,7 +121,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.11-20230121' + VERSION = '3.11-20230122' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2022 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index c1d753470..092fe0e31 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -104,7 +104,7 @@ def init_HTTP(self): form = login.result.find('form') form_inputs = {} for form_input in ('redirect', 'formUID', 'CSRFToken', 'btn_submit'): - if not form.find('input', {'name': form_input}) is None: + if form is not None and not form.find('input', {'name': form_input}) is None: form_inputs[form_input] = form.find('input', {'name': form_input})['value'] else: form_inputs[form_input] = '' diff --git a/Nagstamon/Servers/IcingaWeb2.py b/Nagstamon/Servers/IcingaWeb2.py index 7008564c1..30aa36388 100644 --- a/Nagstamon/Servers/IcingaWeb2.py +++ b/Nagstamon/Servers/IcingaWeb2.py @@ -104,7 +104,7 @@ def init_HTTP(self): form = login.result.find('form') form_inputs = {} for form_input in ('redirect', 'formUID', 'CSRFToken', 'btn_submit'): - if not form.find('input', {'name': form_input}) is None: + if form is not None and not form.find('input', {'name': form_input}) is None: form_inputs[form_input] = form.find('input', {'name': form_input})['value'] else: form_inputs[form_input] = '' diff --git a/build/debian/changelog b/build/debian/changelog index eba4361a2..83e64f3fd 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,8 +1,8 @@ -nagstamon (3.11-20230121) unstable; urgency=low +nagstamon (3.11-20230122) unstable; urgency=low * New upstream - stuff - -- Henri Wahl <henri@nagstamon.de> Sat, Jan 21 2023 08:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Sun, Jan 22 2023 08:00:00 +0100 nagstamon (3.10.1) stable; urgency=low * New upstream From 58d96590d320483891ec0999c87549d5770128fd Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Sun, 22 Jan 2023 18:02:42 +0100 Subject: [PATCH 519/884] getproxies() to get proxies --- Nagstamon/Servers/Generic.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index fa401458a..f2fc728b8 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -26,6 +26,7 @@ import sys import traceback import urllib.parse +from urllib.request import getproxies from bs4 import BeautifulSoup import requests @@ -332,9 +333,13 @@ def proxify(self, requester): # check if proxies have to be used if self.use_proxy is True: if self.use_proxy_from_os is True: - # if .trust_enf is true the system environment will be evaluated - requester.trust_env = True - requester.proxies = dict() + # get proxies from system directly instead of via trust_env + requester.proxies = getproxies() + # check for missing '/' to make proxies work + for scheme, proxy_url in requester.proxies.items(): + if not proxy_url.endswith('/'): + requester.proxies[scheme] = proxy_url + '/' + pass else: # check if username and password are given and provide credentials if needed if self.proxy_username == self.proxy_password == '': @@ -354,8 +359,6 @@ def proxify(self, requester): # fill session.proxies for both protocols requester.proxies = {'http': proxy_url, 'https': proxy_url} else: - # disable evaluation of environment variables - requester.trust_env = False requester.proxies = None def reset_HTTP(self): From 8f4153da44f6e6318b964abddae358ddb49dd218 Mon Sep 17 00:00:00 2001 From: Johan Thoren <johan@thoren.xyz> Date: Thu, 26 Jan 2023 11:36:45 +0100 Subject: [PATCH 520/884] This commit fixes the 'Monitor' links for Opsview. Previously, these links went to /extinfo.cgi?/... which is not correct and would result in a 404. I've replaced the links for hosts and services to instead use /monitoring/#!? with autoSelectHost and autoSelectService respectively. Clicking 'Monitor' on a host or service in the tablewidget context menu now works. --- Nagstamon/Servers/Opsview.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Nagstamon/Servers/Opsview.py b/Nagstamon/Servers/Opsview.py index c7d59f564..5502d611f 100644 --- a/Nagstamon/Servers/Opsview.py +++ b/Nagstamon/Servers/Opsview.py @@ -273,6 +273,24 @@ def _get_status(self): #dummy return in case all is OK return Result() + def open_monitor(self, host, service=''): + ''' + open monitor from tablewidget context menu + ''' + base_url = self.monitor_url + '/monitoring/#!?' + host_url = base_url + urllib.parse.urlencode({'autoSelectHost': host}) + service_url = base_url + urllib.parse.urlencode({'autoSelectHost': host, + 'autoSelectService': service}, + quote_via=urllib.parse.quote) + if service == '': + if conf.debug_mode: + self.Debug(server=self.get_name(), host=host, service=service, + debug='Open host monitor web page ' + host_url) + webbrowser_open(host_url) + else: + self.Debug(server=self.get_name(), host=host, service=service, + debug='Open service monitor web page ' + service_url) + webbrowser_open(service_url) def open_monitor_webpage(self, host, service): webbrowser_open('%s/monitoring/#!?autoSelectHost=%s' % (self.monitor_url, host)) From 9a93f46febcd8566f71d9b6bcde0c3782dee7cce Mon Sep 17 00:00:00 2001 From: Johan Thoren <johan@thoren.xyz> Date: Thu, 26 Jan 2023 15:52:04 +0100 Subject: [PATCH 521/884] Add support for filtering on Opsview Hashtags. - Adds a new optional configuration field for Opsview hashtags. - Takes a comma-separated list of hashtags. - Will filter all objects to only contain those with the selected tags. --- Nagstamon/Config.py | 3 +++ Nagstamon/QUI/__init__.py | 2 ++ Nagstamon/Servers/Generic.py | 3 +++ Nagstamon/Servers/Opsview.py | 18 +++++++++++++++++- Nagstamon/Servers/__init__.py | 3 +++ Nagstamon/resources/qui/settings_server.ui | 20 ++++++++++++++++++++ 6 files changed, 48 insertions(+), 1 deletion(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index a441a34ed..db55e71cb 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -941,6 +941,9 @@ def __init__(self): self.host_filter = 'state !=0' self.service_filter = 'state !=0 or host.state != 0' + # Opsview hashtag filter + self.hashtag_filter = '' + # Sensu/Uchiwa/??? Datacenter/Site config self.monitor_site = 'Site 1' diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 19e2de867..8fc031c7a 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -5696,6 +5696,8 @@ def __init__(self, dialog): self.window.input_lineedit_service_filter: ['op5Monitor'], self.window.label_service_filter: ['op5Monitor'], self.window.label_host_filter: ['op5Monitor'], + self.window.input_lineedit_hashtag_filter: ['Opsview'], + self.window.label_hashtag_filter: ['Opsview'], self.window.label_monitor_site: ['Sensu'], self.window.input_lineedit_monitor_site: ['Sensu'], self.window.label_map_to_hostname: ['Prometheus', 'Alertmanager'], diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index fa401458a..ff9759dd5 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -245,6 +245,9 @@ def __init__(self, **kwds): self.host_filter = 'state !=0' self.service_filter = 'state !=0 or host.state != 0' + # Opsview hashtag filter + self.hashtag_filter = '' + # Sensu/Uchiwa/??? Datacenter/Site config self.monitor_site = 'Site 1' diff --git a/Nagstamon/Servers/Opsview.py b/Nagstamon/Servers/Opsview.py index c7d59f564..8c7a0fd68 100644 --- a/Nagstamon/Servers/Opsview.py +++ b/Nagstamon/Servers/Opsview.py @@ -23,6 +23,7 @@ import urllib.request, urllib.parse, urllib.error import copy import pprint +import re import json from datetime import datetime, timedelta @@ -201,11 +202,26 @@ def _get_status(self): """ Get status from Opsview Server """ + if self.hashtag_filter == '': + keywords = '' + else: + self.Debug(server=self.get_name(), debug="Raw hashtag filter string: " + self.hashtag_filter) + + trimmed_hashtags = re.sub(r'[#|\s]', '', self.hashtag_filter).split(",") + list_of_non_empty_hashtags = [i for i in trimmed_hashtags if i] + self.Debug(server=self.get_name(), debug="List of trimmed hashtags" + pprint.pformat(list_of_non_empty_hashtags)) + + keywords = "&keyword=" + "&keyword=".join(list_of_non_empty_hashtags) + self.Debug(server=self.get_name(), debug="Keyword string" + pprint.pformat(keywords)) # following XXXX to get ALL services in ALL states except OK # because we filter them out later # the REST API gets all host and service info in one call try: - result = self.FetchURL(self.monitor_url + "/rest/status/service?state=1&state=2&state=3", giveback="raw") + if keywords == '': + result = self.FetchURL(self.monitor_url + "/rest/status/service?state=1&state=2&state=3", giveback="raw") + else: + result = self.FetchURL(self.monitor_url + "/rest/status/service?state=1&state=2&state=3" + keywords, giveback="raw") + data, error, status_code = json.loads(result.result), result.error, result.status_code # check if any error occured diff --git a/Nagstamon/Servers/__init__.py b/Nagstamon/Servers/__init__.py index f157bc6c0..4a7b4489e 100644 --- a/Nagstamon/Servers/__init__.py +++ b/Nagstamon/Servers/__init__.py @@ -199,6 +199,9 @@ def create_server(server=None): new_server.host_filter = server.host_filter new_server.service_filter = server.service_filter + # Opsview hashtag filters + new_server.hashtag_filter = server.hashtag_filter + # Zabbix new_server.use_description_name_service = server.use_description_name_service diff --git a/Nagstamon/resources/qui/settings_server.ui b/Nagstamon/resources/qui/settings_server.ui index a7b7a1ffe..1ba343165 100644 --- a/Nagstamon/resources/qui/settings_server.ui +++ b/Nagstamon/resources/qui/settings_server.ui @@ -618,6 +618,19 @@ </property> </widget> </item> + <item row="10" column="1"> + <widget class="QLabel" name="label_hashtag_filter"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Hashtag filter:</string> + </property> + </widget> + </item> <item row="0" column="1" colspan="3"> <widget class="QCheckBox" name="input_checkbox_ignore_cert"> <property name="text"> @@ -635,6 +648,13 @@ </property> </widget> </item> + <item row="10" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_hashtag_filter"> + <property name="text"> + <string/> + </property> + </widget> + </item> <item row="2" column="1"> <widget class="QLabel" name="label_custom_ca_file"> <property name="text"> From ff463243aaef4ee647282e2798c80dfed01bead7 Mon Sep 17 00:00:00 2001 From: Johan Thoren <johan@thoren.xyz> Date: Thu, 26 Jan 2023 16:01:27 +0100 Subject: [PATCH 522/884] Reverse logic when checking self.hashtag_filter. --- Nagstamon/Servers/Opsview.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Servers/Opsview.py b/Nagstamon/Servers/Opsview.py index 8c7a0fd68..7eea0be6d 100644 --- a/Nagstamon/Servers/Opsview.py +++ b/Nagstamon/Servers/Opsview.py @@ -202,9 +202,7 @@ def _get_status(self): """ Get status from Opsview Server """ - if self.hashtag_filter == '': - keywords = '' - else: + if self.hashtag_filter != '': self.Debug(server=self.get_name(), debug="Raw hashtag filter string: " + self.hashtag_filter) trimmed_hashtags = re.sub(r'[#|\s]', '', self.hashtag_filter).split(",") @@ -213,6 +211,8 @@ def _get_status(self): keywords = "&keyword=" + "&keyword=".join(list_of_non_empty_hashtags) self.Debug(server=self.get_name(), debug="Keyword string" + pprint.pformat(keywords)) + else: + keywords = '' # following XXXX to get ALL services in ALL states except OK # because we filter them out later # the REST API gets all host and service info in one call From 3ff4bdaa23b78a7de788025a53b51a7f2fd07c1b Mon Sep 17 00:00:00 2001 From: Johan Thoren <johan@thoren.xyz> Date: Thu, 26 Jan 2023 16:13:36 +0100 Subject: [PATCH 523/884] Remove faulty | from regexp. --- Nagstamon/Servers/Opsview.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/Servers/Opsview.py b/Nagstamon/Servers/Opsview.py index 7eea0be6d..a1474af36 100644 --- a/Nagstamon/Servers/Opsview.py +++ b/Nagstamon/Servers/Opsview.py @@ -205,7 +205,7 @@ def _get_status(self): if self.hashtag_filter != '': self.Debug(server=self.get_name(), debug="Raw hashtag filter string: " + self.hashtag_filter) - trimmed_hashtags = re.sub(r'[#|\s]', '', self.hashtag_filter).split(",") + trimmed_hashtags = re.sub(r'[#\s]', '', self.hashtag_filter).split(",") list_of_non_empty_hashtags = [i for i in trimmed_hashtags if i] self.Debug(server=self.get_name(), debug="List of trimmed hashtags" + pprint.pformat(list_of_non_empty_hashtags)) From 4f7b2ca39109d47335553b06cbe1904507d0aeb6 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 26 Jan 2023 23:18:52 +0100 Subject: [PATCH 524/884] 3.11-20230126 + 2023 --- COPYRIGHT | 2 +- Nagstamon/Config.py | 6 +++--- Nagstamon/Helpers.py | 2 +- Nagstamon/Objects.py | 2 +- Nagstamon/QUI/__init__.py | 2 +- Nagstamon/QUI/qt.py | 2 +- Nagstamon/Servers/Alertmanager/LICENSE | 2 +- Nagstamon/Servers/Centreon/CentreonAPI.py | 2 +- Nagstamon/Servers/Centreon/CentreonLegacy.py | 2 +- Nagstamon/Servers/Centreon/__init__.py | 2 +- Nagstamon/Servers/Generic.py | 2 +- Nagstamon/Servers/Icinga.py | 2 +- Nagstamon/Servers/Icinga2API.py | 2 +- Nagstamon/Servers/IcingaDBWeb.py | 2 +- Nagstamon/Servers/IcingaWeb2.py | 2 +- Nagstamon/Servers/Livestatus.py | 2 +- Nagstamon/Servers/Monitos3.py | 2 +- Nagstamon/Servers/Monitos4x.py | 2 +- Nagstamon/Servers/Multisite.py | 2 +- Nagstamon/Servers/Nagios.py | 2 +- Nagstamon/Servers/Opsview.py | 2 +- Nagstamon/Servers/Prometheus.py | 2 +- Nagstamon/Servers/SnagView3.py | 2 +- Nagstamon/Servers/Thruk.py | 2 +- Nagstamon/Servers/__init__.py | 2 +- Nagstamon/Servers/op5Monitor.py | 2 +- Nagstamon/__init__.py | 2 +- Nagstamon/resources/qui/dialog_about.ui | 2 +- Nagstamon/thirdparty/__init__.py | 2 +- build/build.py | 2 +- build/debian/changelog | 4 ++-- nagstamon.py | 2 +- setup.py | 2 +- 33 files changed, 36 insertions(+), 36 deletions(-) diff --git a/COPYRIGHT b/COPYRIGHT index 3132671ff..29bb3cea2 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -1 +1 @@ -Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 047af2af9..561ef2408 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -2,7 +2,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -121,9 +121,9 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.11-20230122' + VERSION = '3.11-20230126' WEBSITE = 'https://nagstamon.de' - COPYRIGHT = '©2008-2022 Henri Wahl et al.' + COPYRIGHT = '©2008-2023 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' # dict of servers to offer for downloads if an update is available DOWNLOAD_SERVERS = {'nagstamon.de': 'https://github.com/HenriWahl/Nagstamon/releases'} diff --git a/Nagstamon/Helpers.py b/Nagstamon/Helpers.py index b03b09e1d..408ce4a9b 100644 --- a/Nagstamon/Helpers.py +++ b/Nagstamon/Helpers.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Objects.py b/Nagstamon/Objects.py index 15a03c747..efc56678b 100644 --- a/Nagstamon/Objects.py +++ b/Nagstamon/Objects.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 8fc031c7a..0b4e7da0d 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -1,6 +1,6 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/QUI/qt.py b/Nagstamon/QUI/qt.py index add133648..64f0f3e46 100644 --- a/Nagstamon/QUI/qt.py +++ b/Nagstamon/QUI/qt.py @@ -1,5 +1,5 @@ # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Alertmanager/LICENSE b/Nagstamon/Servers/Alertmanager/LICENSE index d00a5ca36..43951f79b 100644 --- a/Nagstamon/Servers/Alertmanager/LICENSE +++ b/Nagstamon/Servers/Alertmanager/LICENSE @@ -1,5 +1,5 @@ Nagstamon - Nagios status monitor for your desktop -Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Centreon/CentreonAPI.py b/Nagstamon/Servers/Centreon/CentreonAPI.py index 2a12945af..c20d72b31 100644 --- a/Nagstamon/Servers/Centreon/CentreonAPI.py +++ b/Nagstamon/Servers/Centreon/CentreonAPI.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Centreon/CentreonLegacy.py b/Nagstamon/Servers/Centreon/CentreonLegacy.py index f17cfb39a..21d9f542a 100644 --- a/Nagstamon/Servers/Centreon/CentreonLegacy.py +++ b/Nagstamon/Servers/Centreon/CentreonLegacy.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Centreon/__init__.py b/Nagstamon/Servers/Centreon/__init__.py index 917db9b82..31eacf5f1 100644 --- a/Nagstamon/Servers/Centreon/__init__.py +++ b/Nagstamon/Servers/Centreon/__init__.py @@ -1,5 +1,5 @@ # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index 52d0545b9..386bce38c 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Icinga.py b/Nagstamon/Servers/Icinga.py index 0f3431fd2..99d1ae89c 100644 --- a/Nagstamon/Servers/Icinga.py +++ b/Nagstamon/Servers/Icinga.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Icinga2API.py b/Nagstamon/Servers/Icinga2API.py index dad173f0a..4d1fd814d 100644 --- a/Nagstamon/Servers/Icinga2API.py +++ b/Nagstamon/Servers/Icinga2API.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index 092fe0e31..da90b57b4 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/IcingaWeb2.py b/Nagstamon/Servers/IcingaWeb2.py index 30aa36388..3a4627eb3 100644 --- a/Nagstamon/Servers/IcingaWeb2.py +++ b/Nagstamon/Servers/IcingaWeb2.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Livestatus.py b/Nagstamon/Servers/Livestatus.py index c9a1f6c66..d8e5edf7b 100644 --- a/Nagstamon/Servers/Livestatus.py +++ b/Nagstamon/Servers/Livestatus.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Monitos3.py b/Nagstamon/Servers/Monitos3.py index 18b383e93..9378ba5d0 100644 --- a/Nagstamon/Servers/Monitos3.py +++ b/Nagstamon/Servers/Monitos3.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Monitos4x.py b/Nagstamon/Servers/Monitos4x.py index adf582f11..75be4d918 100644 --- a/Nagstamon/Servers/Monitos4x.py +++ b/Nagstamon/Servers/Monitos4x.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Multisite.py b/Nagstamon/Servers/Multisite.py index df34589a6..7539da7ff 100644 --- a/Nagstamon/Servers/Multisite.py +++ b/Nagstamon/Servers/Multisite.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Nagios.py b/Nagstamon/Servers/Nagios.py index 68469bbd3..78e4a7df0 100644 --- a/Nagstamon/Servers/Nagios.py +++ b/Nagstamon/Servers/Nagios.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Opsview.py b/Nagstamon/Servers/Opsview.py index 0a8d1016b..ca725e341 100644 --- a/Nagstamon/Servers/Opsview.py +++ b/Nagstamon/Servers/Opsview.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. # # Based on https://github.com/duncs/Nagstamon by @duncs # diff --git a/Nagstamon/Servers/Prometheus.py b/Nagstamon/Servers/Prometheus.py index 3d66ae1f0..53e8b777b 100644 --- a/Nagstamon/Servers/Prometheus.py +++ b/Nagstamon/Servers/Prometheus.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/SnagView3.py b/Nagstamon/Servers/SnagView3.py index dc9f03e8a..4240ded4f 100644 --- a/Nagstamon/Servers/SnagView3.py +++ b/Nagstamon/Servers/SnagView3.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Thruk.py b/Nagstamon/Servers/Thruk.py index a86cc2e1a..3bfda83b2 100644 --- a/Nagstamon/Servers/Thruk.py +++ b/Nagstamon/Servers/Thruk.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. # Thruk additions copyright by dcec@Github # # This program is free software; you can redistribute it and/or modify diff --git a/Nagstamon/Servers/__init__.py b/Nagstamon/Servers/__init__.py index 4a7b4489e..2022583e9 100644 --- a/Nagstamon/Servers/__init__.py +++ b/Nagstamon/Servers/__init__.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/op5Monitor.py b/Nagstamon/Servers/op5Monitor.py index 06cd89ece..b5fab964b 100644 --- a/Nagstamon/Servers/op5Monitor.py +++ b/Nagstamon/Servers/op5Monitor.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/__init__.py b/Nagstamon/__init__.py index 997747a19..66f815d12 100644 --- a/Nagstamon/__init__.py +++ b/Nagstamon/__init__.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/resources/qui/dialog_about.ui b/Nagstamon/resources/qui/dialog_about.ui index d429be96a..e3e2665e2 100644 --- a/Nagstamon/resources/qui/dialog_about.ui +++ b/Nagstamon/resources/qui/dialog_about.ui @@ -75,7 +75,7 @@ <item> <widget class="QLabel" name="label_copyright"> <property name="text"> - <string>©2008-2022 Henri Wahl et al.</string> + <string>©2008-2023 Henri Wahl et al.</string> </property> <property name="alignment"> <set>Qt::AlignCenter</set> diff --git a/Nagstamon/thirdparty/__init__.py b/Nagstamon/thirdparty/__init__.py index f0f9b3d62..cb8df1192 100644 --- a/Nagstamon/thirdparty/__init__.py +++ b/Nagstamon/thirdparty/__init__.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/build/build.py b/build/build.py index ba53112b4..397e2e39c 100644 --- a/build/build.py +++ b/build/build.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/build/debian/changelog b/build/debian/changelog index 83e64f3fd..676400d61 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,8 +1,8 @@ -nagstamon (3.11-20230122) unstable; urgency=low +nagstamon (3.11-20230126) unstable; urgency=low * New upstream - stuff - -- Henri Wahl <henri@nagstamon.de> Sun, Jan 22 2023 08:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Thu, Jan 26 2023 08:00:00 +0100 nagstamon (3.10.1) stable; urgency=low * New upstream diff --git a/nagstamon.py b/nagstamon.py index 62b6b8623..4eaac5ff4 100755 --- a/nagstamon.py +++ b/nagstamon.py @@ -2,7 +2,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/setup.py b/setup.py index d02ecbd62..5ff72dda7 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2022 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by From 8e89e87b07786a9ecf56b9067e761e87206d427f Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 26 Jan 2023 23:22:01 +0100 Subject: [PATCH 525/884] fix Opsview indent --- Nagstamon/Servers/Opsview.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Servers/Opsview.py b/Nagstamon/Servers/Opsview.py index ca725e341..c7fcb4e9e 100644 --- a/Nagstamon/Servers/Opsview.py +++ b/Nagstamon/Servers/Opsview.py @@ -299,9 +299,9 @@ def open_monitor(self, host, service=''): 'autoSelectService': service}, quote_via=urllib.parse.quote) if service == '': - if conf.debug_mode: - self.Debug(server=self.get_name(), host=host, service=service, - debug='Open host monitor web page ' + host_url) + if conf.debug_mode: + self.Debug(server=self.get_name(), host=host, service=service, + debug='Open host monitor web page ' + host_url) webbrowser_open(host_url) else: self.Debug(server=self.get_name(), host=host, service=service, From 89a9b1f2da767bffbf406440264b159b7c5495fe Mon Sep 17 00:00:00 2001 From: Johan Thoren <johan@thoren.xyz> Date: Fri, 3 Feb 2023 10:29:44 +0100 Subject: [PATCH 526/884] Add the can_change_only option for Opsview. This commit adds a new checkbox for Opsview servers that will filter out all services that the user cannot either change or set downtimes on. It also contains some fixes where the `if conf.debug_mode` logic was missing from some Debug calls. Also, it fixes a bug where clicking the link of the Opsview server address in the 'right click menu' would throw an error and crash the application. --- Nagstamon/Config.py | 10 +++++- Nagstamon/QUI/__init__.py | 1 + Nagstamon/Servers/Generic.py | 3 ++ Nagstamon/Servers/Opsview.py | 37 +++++++++++++++------- Nagstamon/Servers/__init__.py | 3 +- Nagstamon/resources/qui/settings_server.ui | 7 ++++ 6 files changed, 48 insertions(+), 13 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 561ef2408..1148a85c2 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -941,8 +941,16 @@ def __init__(self): self.host_filter = 'state !=0' self.service_filter = 'state !=0 or host.state != 0' - # Opsview hashtag filter + # For more information about he Opsview options below, see this link: + # https://knowledge.opsview.com/reference/api-status-filtering-service-objects + + # The Opsview hashtag filter will filter out any services NOT having the + # listed hashtags (previously known as keywords). self.hashtag_filter = '' + # The Opsview can_change_only option allows a user to show only + # services for which the user has permissions to make changes OR set + # downtimes. + self.can_change_only = False # Sensu/Uchiwa/??? Datacenter/Site config self.monitor_site = 'Site 1' diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 0b4e7da0d..7cca1c93a 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -5698,6 +5698,7 @@ def __init__(self, dialog): self.window.label_host_filter: ['op5Monitor'], self.window.input_lineedit_hashtag_filter: ['Opsview'], self.window.label_hashtag_filter: ['Opsview'], + self.window.input_checkbox_can_change_only: ['Opsview'], self.window.label_monitor_site: ['Sensu'], self.window.input_lineedit_monitor_site: ['Sensu'], self.window.label_map_to_hostname: ['Prometheus', 'Alertmanager'], diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index 386bce38c..f4a30846d 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -249,6 +249,9 @@ def __init__(self, **kwds): # Opsview hashtag filter self.hashtag_filter = '' + # Opsview can_change_only option + self.can_change_only = False + # Sensu/Uchiwa/??? Datacenter/Site config self.monitor_site = 'Site 1' diff --git a/Nagstamon/Servers/Opsview.py b/Nagstamon/Servers/Opsview.py index c7fcb4e9e..b7e1534b1 100644 --- a/Nagstamon/Servers/Opsview.py +++ b/Nagstamon/Servers/Opsview.py @@ -202,25 +202,42 @@ def _get_status(self): """ Get status from Opsview Server """ + if self.can_change_only: + if conf.debug_mode: + self.Debug(server=self.get_name(), + debug="Showing only objects that the user can change or put in downtime") + + can_change = '&can_change=true' + else: + can_change = '' + if self.hashtag_filter != '': - self.Debug(server=self.get_name(), debug="Raw hashtag filter string: " + self.hashtag_filter) + if conf.debug_mode: + self.Debug(server=self.get_name(), + debug="Raw hashtag filter string: " + + self.hashtag_filter) trimmed_hashtags = re.sub(r'[#\s]', '', self.hashtag_filter).split(",") list_of_non_empty_hashtags = [i for i in trimmed_hashtags if i] - self.Debug(server=self.get_name(), debug="List of trimmed hashtags" + pprint.pformat(list_of_non_empty_hashtags)) + + if conf.debug_mode: + self.Debug(server=self.get_name(), + debug="List of trimmed hashtags" + + pprint.pformat(list_of_non_empty_hashtags)) keywords = "&keyword=" + "&keyword=".join(list_of_non_empty_hashtags) - self.Debug(server=self.get_name(), debug="Keyword string" + pprint.pformat(keywords)) + + if conf.debug_mode: + self.Debug(server=self.get_name(), + debug="Keyword string" + pprint.pformat(keywords)) else: keywords = '' + # following XXXX to get ALL services in ALL states except OK # because we filter them out later # the REST API gets all host and service info in one call try: - if keywords == '': - result = self.FetchURL(self.monitor_url + "/rest/status/service?state=1&state=2&state=3", giveback="raw") - else: - result = self.FetchURL(self.monitor_url + "/rest/status/service?state=1&state=2&state=3" + keywords, giveback="raw") + result = self.FetchURL(self.monitor_url + "/rest/status/service?state=1&state=2&state=3" + can_change + keywords, giveback="raw") data, error, status_code = json.loads(result.result), result.error, result.status_code @@ -308,7 +325,5 @@ def open_monitor(self, host, service=''): debug='Open service monitor web page ' + service_url) webbrowser_open(service_url) - def open_monitor_webpage(self, host, service): - webbrowser_open('%s/monitoring/#!?autoSelectHost=%s' % (self.monitor_url, host)) - - + def open_monitor_webpage(self): + webbrowser_open('%s/monitoring/' % (self.monitor_url)) diff --git a/Nagstamon/Servers/__init__.py b/Nagstamon/Servers/__init__.py index 2022583e9..9ee1f3add 100644 --- a/Nagstamon/Servers/__init__.py +++ b/Nagstamon/Servers/__init__.py @@ -199,8 +199,9 @@ def create_server(server=None): new_server.host_filter = server.host_filter new_server.service_filter = server.service_filter - # Opsview hashtag filters + # Opsview hashtag filter and can_change_only option new_server.hashtag_filter = server.hashtag_filter + new_server.can_change_only = server.can_change_only # Zabbix new_server.use_description_name_service = server.use_description_name_service diff --git a/Nagstamon/resources/qui/settings_server.ui b/Nagstamon/resources/qui/settings_server.ui index 1ba343165..f491a4f7b 100644 --- a/Nagstamon/resources/qui/settings_server.ui +++ b/Nagstamon/resources/qui/settings_server.ui @@ -631,6 +631,13 @@ </property> </widget> </item> + <item row="11" column="1" colspan="3"> + <widget class="QCheckBox" name="input_checkbox_can_change_only"> + <property name="text"> + <string>Only show services that the user can change or set downtimes on</string> + </property> + </widget> + </item> <item row="0" column="1" colspan="3"> <widget class="QCheckBox" name="input_checkbox_ignore_cert"> <property name="text"> From 0e7178f364227981fcfc391e1db26dc893d892dd Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri@nagstamon.de> Date: Tue, 7 Feb 2023 22:30:47 +0100 Subject: [PATCH 527/884] windows pip-system-certs --- Nagstamon/Config.py | 2 +- build/Nagstamon.spec | 51 ++++++++++++++++++++++++++++++++++ build/debian/changelog | 4 +-- build/requirements/windows.txt | 1 + nagstamon.py | 6 +++- 5 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 build/Nagstamon.spec diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 1148a85c2..4c9154705 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -121,7 +121,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.11-20230126' + VERSION = '3.11-20230205' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2023 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/Nagstamon.spec b/build/Nagstamon.spec new file mode 100644 index 000000000..3a535bcad --- /dev/null +++ b/build/Nagstamon.spec @@ -0,0 +1,51 @@ +# -*- mode: python ; coding: utf-8 -*- + + +block_cipher = None + + +a = Analysis( + ['..\\nagstamon.py'], + pathex=[], + binaries=[], + datas=[('..\\Nagstamon/resources', 'resources')], + hiddenimports=['PyQt6.uic.plugins', 'win32timezone'], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher, + noarchive=False, +) +pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) + +exe = EXE( + pyz, + a.scripts, + [], + exclude_binaries=True, + name='Nagstamon', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + console=False, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, + icon=['..\\Nagstamon\\resources\\nagstamon.ico'], +) +coll = COLLECT( + exe, + a.binaries, + a.zipfiles, + a.datas, + strip=False, + upx=True, + upx_exclude=[], + name='Nagstamon', +) diff --git a/build/debian/changelog b/build/debian/changelog index 676400d61..fa12edd78 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,8 +1,8 @@ -nagstamon (3.11-20230126) unstable; urgency=low +nagstamon (3.11-20230205) unstable; urgency=low * New upstream - stuff - -- Henri Wahl <henri@nagstamon.de> Thu, Jan 26 2023 08:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Thu, Feb 05 2023 08:00:00 +0100 nagstamon (3.10.1) stable; urgency=low * New upstream diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index 8580cc3d1..591089e81 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -3,6 +3,7 @@ beautifulsoup4 icinga2api keyring lxml +pip-system-certs psutil pyinstaller pypiwin32 diff --git a/nagstamon.py b/nagstamon.py index 4eaac5ff4..aeb02d417 100755 --- a/nagstamon.py +++ b/nagstamon.py @@ -29,7 +29,11 @@ try: if __name__ == '__main__': - from Nagstamon.Config import conf + from Nagstamon.Config import (conf, + OS, + OS_WINDOWS) + if OS == OS_WINDOWS: + import pip_system_certs.wrapt_requests from Nagstamon.Helpers import lock_config_folder From 753b6799e7ff2ac6f566c54cc31919cc66aaae57 Mon Sep 17 00:00:00 2001 From: TH <thomas.hoffmann@speed4trade.com> Date: Thu, 9 Feb 2023 11:25:51 +0100 Subject: [PATCH 528/884] register nagstamon version on windows when installing via setup (needed for handling updates and upgrades e.g. via winget) Ticket-Nr: 902 --- build/windows/nagstamon.iss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/windows/nagstamon.iss b/build/windows/nagstamon.iss index c9db9538a..a6836e098 100644 --- a/build/windows/nagstamon.iss +++ b/build/windows/nagstamon.iss @@ -1,7 +1,7 @@ [Setup] AppName=Nagstamon -AppVerName=Nagstamon {#version} -AppVersion={#version} +AppVerName=Nagstamon {#version_is} +AppVersion={#version_is} AppPublisher=Henri Wahl DefaultDirName={commonpf}\Nagstamon DefaultGroupName=Nagstamon From a1a3516309bd45ec1783321d262b0e2f17a2fc4a Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 12 Feb 2023 22:43:43 +0100 Subject: [PATCH 529/884] version without OS --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 5ff72dda7..271072e3d 100644 --- a/setup.py +++ b/setup.py @@ -45,8 +45,8 @@ else: DIST, DIST_VERSION, DIST_NAME = platform.dist() NAME = NAME.lower() -VERSION = AppInfo.VERSION.replace('-', '.') + '.' + DIST + DIST_VERSION - +#VERSION = AppInfo.VERSION.replace('-', '.') + '.' + DIST + DIST_VERSION +VERSION = AppInfo.VERSION.replace('-', '.') NAGSTAMON_SCRIPT = 'nagstamon.py' from setuptools import setup From 146ed3444b4f04b0d6151314f8a68936f73ebf4f Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 12 Feb 2023 22:44:20 +0100 Subject: [PATCH 530/884] version without OS --- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 4c9154705..3296558c2 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -121,7 +121,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.11-20230205' + VERSION = '3.11-20230212' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2023 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index fa12edd78..a55f2dc8e 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,8 +1,8 @@ -nagstamon (3.11-20230205) unstable; urgency=low +nagstamon (3.11-20230212) unstable; urgency=low * New upstream - stuff - -- Henri Wahl <henri@nagstamon.de> Thu, Feb 05 2023 08:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Sun, Feb 12 2023 08:00:00 +0100 nagstamon (3.10.1) stable; urgency=low * New upstream From a7446218e153c4b0f8b2ad34417b23df5627c3a2 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 12 Feb 2023 23:24:00 +0100 Subject: [PATCH 531/884] version with OS for redhatty OS --- Nagstamon/resources/nagstamon.1.gz | Bin 783 -> 783 bytes build/build.py | 9 +++++++++ 2 files changed, 9 insertions(+) diff --git a/Nagstamon/resources/nagstamon.1.gz b/Nagstamon/resources/nagstamon.1.gz index 437d01d93a4d19710dca54e5efe2c1b54e1577aa..cfb7f1f800a0ec0a8cef572c1440293e7caf5826 100644 GIT binary patch delta 17 YcmeBY>u2MT@8;n6n))(%BL_P(058u4RsaA1 delta 17 YcmeBY>u2MT@8;mRw&+*VMh<pn05IbPga7~l diff --git a/build/build.py b/build/build.py index 397e2e39c..a0125c02d 100644 --- a/build/build.py +++ b/build/build.py @@ -27,6 +27,7 @@ import zipfile import glob +from Nagstamon.Helpers import get_distro CURRENT_DIR = os.getcwd() NAGSTAMON_DIR = os.path.normpath('{0}{1}..{1}'.format(CURRENT_DIR, os.sep)) @@ -45,6 +46,8 @@ PYTHON_VERSION = '{0}.{1}'.format(sys.version_info[0], sys.version_info[1]) +DIST_NAME, DIST_VERSION, DIST_ID = get_distro() + # depending on debug build or not a console window will be shown or not if len(sys.argv) > 1 and sys.argv[1] == 'debug': DEBUG = True @@ -221,6 +224,12 @@ def rpmmain(): # run setup.py for rpm creation subprocess.call(['python3', 'setup.py', 'bdist_rpm'], shell=False) + current_dir = Path(CURRENT_DIR) + for file in current_dir.iterdir(): + if VERSION.replace('-', '.') in file.name: + file.rename(file.name.replace('src.rpm', f'{DIST_NAME}{DIST_VERSION}.src.rpm')) + file.rename(file.name.replace('noarch.rpm', f'{DIST_NAME}{DIST_VERSION}.noarch.rpm')) + DISTS = { 'debian': debmain, From 4a8559c3b411b18770540119f3bbe08843082178 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 13 Feb 2023 00:21:59 +0100 Subject: [PATCH 532/884] redhat packages --- Nagstamon/resources/nagstamon.1.gz | Bin 783 -> 783 bytes build/build.py | 15 ++++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Nagstamon/resources/nagstamon.1.gz b/Nagstamon/resources/nagstamon.1.gz index cfb7f1f800a0ec0a8cef572c1440293e7caf5826..30cea922e17e419128406e312834d3b53feb0783 100644 GIT binary patch delta 15 WcmeBY>t|z=@8;mJEZNA$$qWD;rvqC6 delta 15 WcmeBY>t|z=@8;n6n!1sVlNkUZ!~_Tc diff --git a/build/build.py b/build/build.py index a0125c02d..e87b0b95d 100644 --- a/build/build.py +++ b/build/build.py @@ -27,11 +27,9 @@ import zipfile import glob -from Nagstamon.Helpers import get_distro - CURRENT_DIR = os.getcwd() NAGSTAMON_DIR = os.path.normpath('{0}{1}..{1}'.format(CURRENT_DIR, os.sep)) -sys.path.append(NAGSTAMON_DIR) +sys.path.insert(1, NAGSTAMON_DIR) SCRIPTS_DIR = '{0}{1}scripts-{2}.{3}'.format(CURRENT_DIR, os.sep, sys.version_info.major, sys.version_info.minor) @@ -88,7 +86,8 @@ def winmain(): # old-school formatstrings needed for old Debian build base distro jessie and its old python ISCC = r'{0}{1}Inno Setup 6{1}iscc.exe'.format(os.environ['PROGRAMFILES{0}'.format(ARCH_OPTS[ARCH][2])], os.sep) DIR_BUILD_EXE = '{0}{1}dist{1}Nagstamon'.format(CURRENT_DIR, os.sep, ARCH_OPTS[ARCH][0], PYTHON_VERSION) - DIR_BUILD_NAGSTAMON = '{0}{1}dist{1}Nagstamon-{2}-win{3}{4}'.format(CURRENT_DIR, os.sep, VERSION, ARCH, FILENAME_SUFFIX) + DIR_BUILD_NAGSTAMON = '{0}{1}dist{1}Nagstamon-{2}-win{3}{4}'.format(CURRENT_DIR, os.sep, VERSION, ARCH, + FILENAME_SUFFIX) FILE_ZIP = '{0}.zip'.format(DIR_BUILD_NAGSTAMON) # clean older binaries @@ -226,9 +225,11 @@ def rpmmain(): current_dir = Path(CURRENT_DIR) for file in current_dir.iterdir(): - if VERSION.replace('-', '.') in file.name: - file.rename(file.name.replace('src.rpm', f'{DIST_NAME}{DIST_VERSION}.src.rpm')) - file.rename(file.name.replace('noarch.rpm', f'{DIST_NAME}{DIST_VERSION}.noarch.rpm')) + if VERSION.replace('-', '.') in file.name and ('noarch' in file.name or 'src' in file.name): + for file_type in ['noarch', 'src']: + if file_type in file.name: + file.replace(file.parent / Path(file.name.replace(f'{file_type}.rpm', + f'{DIST_NAME}{DIST_VERSION}.{file_type}.rpm'))) DISTS = { From 7e61cffbe7aa5ce057728601381aa3f259d49522 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 13 Feb 2023 00:36:09 +0100 Subject: [PATCH 533/884] rhel build repo dependency --- .github/workflows/build-release-latest.yml | 42 +++++++++++++++++----- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 06baa24a4..4adaac89c 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -263,14 +263,39 @@ jobs: retention-days: 1 if-no-files-found: error - repo-rpm: + repo-rpm-fedora: runs-on: ubuntu-latest # if not all are ready there might be trouble when downloading artifacts # maybe faster now with build containers needs: [fedora-34, fedora-35, fedora-36, fedora-37, rhel-9] - strategy: - matrix: - os_family: [fedora, rhel] + env: + FAMILY: fedora + steps: + # get binaries created by other jobs + - uses: actions/download-artifact@v3 + # organize SSH deploy key for nagstamon-repo + - run: mkdir ~/.ssh + - run: echo "${{ secrets.NAGSTAMON_REPO_KEY_WEB }}" > ~/.ssh/id_ed25519 + - run: chmod -R go-rwx ~/.ssh + # get and prepare nagstamon-jekyll + - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git + - run: rm -rf ${{ env.repo_dir }}/${{ FAMILY }}/latest + - run: mkdir -p ${{ env.repo_dir }}/${{ FAMILY }}/latest + # copy *.rpm files into nagstamon-jekyll + - run: cp -r artifact/*.${{ FAMILY }}*.rpm ${{ env.repo_dir }}/${{ FAMILY }}/latest + # create rpm repo via Fedora container + - run: docker run --rm -v $PWD/${{ env.repo_dir }}/${{ FAMILY }}/latest:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo" + # commit and push new binaries to nagstamon-repo + - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" + - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ FAMILY }}" && git push + + repo-rpm-rhel: + runs-on: ubuntu-latest + # if not all are ready there might be trouble when downloading artifacts + # maybe faster now with build containers + needs: [fedora-34, fedora-35, fedora-36, fedora-37, rhel-9, repo-rpm-fedora] + env: + FAMILY: rhel steps: # get binaries created by other jobs - uses: actions/download-artifact@v3 @@ -280,15 +305,16 @@ jobs: - run: chmod -R go-rwx ~/.ssh # get and prepare nagstamon-jekyll - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - - run: rm -rf ${{ env.repo_dir }}/${{ matrix.os_family }}/latest - - run: mkdir -p ${{ env.repo_dir }}/${{ matrix.os_family }}/latest + - run: rm -rf ${{ env.repo_dir }}/${{ FAMILY }}/latest + - run: mkdir -p ${{ env.repo_dir }}/${{ FAMILY }}/latest # copy *.rpm files into nagstamon-jekyll - - run: cp -r artifact/*.${{ matrix.os_family }}*.rpm ${{ env.repo_dir }}/${{ matrix.os_family }}/latest + - run: cp -r artifact/*.${{ FAMILY }}*.rpm ${{ env.repo_dir }}/${{ FAMILY }}/latest # create rpm repo via Fedora container - run: docker run --rm -v $PWD/${{ env.repo_dir }}/${{ matrix.os_family }}/latest:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo" # commit and push new binaries to nagstamon-repo - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ matrix.os_family }}" && git push + - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ FAMILY }}" && git push + github-release: runs-on: ubuntu-latest From de57807a949e707e62bfc7849d0d42aa17f67f5a Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 13 Feb 2023 00:38:52 +0100 Subject: [PATCH 534/884] rhel build repo dependency env.family --- .github/workflows/build-release-latest.yml | 24 +++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 4adaac89c..334462f6d 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -269,7 +269,7 @@ jobs: # maybe faster now with build containers needs: [fedora-34, fedora-35, fedora-36, fedora-37, rhel-9] env: - FAMILY: fedora + family: fedora steps: # get binaries created by other jobs - uses: actions/download-artifact@v3 @@ -279,15 +279,15 @@ jobs: - run: chmod -R go-rwx ~/.ssh # get and prepare nagstamon-jekyll - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - - run: rm -rf ${{ env.repo_dir }}/${{ FAMILY }}/latest - - run: mkdir -p ${{ env.repo_dir }}/${{ FAMILY }}/latest + - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/latest + - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/latest # copy *.rpm files into nagstamon-jekyll - - run: cp -r artifact/*.${{ FAMILY }}*.rpm ${{ env.repo_dir }}/${{ FAMILY }}/latest + - run: cp -r artifact/*.${{ env.family }}*.rpm ${{ env.repo_dir }}/${{ env.family }}/latest # create rpm repo via Fedora container - - run: docker run --rm -v $PWD/${{ env.repo_dir }}/${{ FAMILY }}/latest:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo" + - run: docker run --rm -v $PWD/${{ env.repo_dir }}/${{ env.family }}/latest:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo" # commit and push new binaries to nagstamon-repo - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ FAMILY }}" && git push + - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push repo-rpm-rhel: runs-on: ubuntu-latest @@ -295,7 +295,7 @@ jobs: # maybe faster now with build containers needs: [fedora-34, fedora-35, fedora-36, fedora-37, rhel-9, repo-rpm-fedora] env: - FAMILY: rhel + family: rhel steps: # get binaries created by other jobs - uses: actions/download-artifact@v3 @@ -305,15 +305,15 @@ jobs: - run: chmod -R go-rwx ~/.ssh # get and prepare nagstamon-jekyll - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - - run: rm -rf ${{ env.repo_dir }}/${{ FAMILY }}/latest - - run: mkdir -p ${{ env.repo_dir }}/${{ FAMILY }}/latest + - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/latest + - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/latest # copy *.rpm files into nagstamon-jekyll - - run: cp -r artifact/*.${{ FAMILY }}*.rpm ${{ env.repo_dir }}/${{ FAMILY }}/latest + - run: cp -r artifact/*.${{ env.family }}*.rpm ${{ env.repo_dir }}/${{ env.family }}/latest # create rpm repo via Fedora container - - run: docker run --rm -v $PWD/${{ env.repo_dir }}/${{ matrix.os_family }}/latest:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo" + - run: docker run --rm -v $PWD/${{ env.repo_dir }}/${{ env.family }}/latest:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo" # commit and push new binaries to nagstamon-repo - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ FAMILY }}" && git push + - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push github-release: From eb5a843212b4818941abcbb0308547622f74cb41 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Wed, 22 Feb 2023 13:18:46 +0100 Subject: [PATCH 535/884] detect version --- Nagstamon/Config.py | 2 +- Nagstamon/resources/nagstamon.1.gz | Bin 783 -> 783 bytes Nagstamon/thirdparty/zabbix_api.py | 11 +++++++++-- build/debian/changelog | 4 ++-- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 3296558c2..cc2156809 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -121,7 +121,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.11-20230212' + VERSION = '3.11-20230222' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2023 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/resources/nagstamon.1.gz b/Nagstamon/resources/nagstamon.1.gz index 30cea922e17e419128406e312834d3b53feb0783..a3df1331af80cc25875c19fa74cf6c3dd49efa36 100644 GIT binary patch delta 16 XcmeBY>t|z^@8;mhWd631or4(wBg6y| delta 16 XcmeBY>t|z^@8;mJEP1(+or4(wB-sQ- diff --git a/Nagstamon/thirdparty/zabbix_api.py b/Nagstamon/thirdparty/zabbix_api.py index afbc451ff..16bb94657 100644 --- a/Nagstamon/thirdparty/zabbix_api.py +++ b/Nagstamon/thirdparty/zabbix_api.py @@ -200,11 +200,17 @@ def login(self, user='', password='', save=True): else: raise ZabbixAPIException("No authentication information available.") + # check version to use the correct keyword for username which changed since 6.4 + if self.api_version() < '6.4': + username_keyword = 'user' + else: + username_keyword = 'username' + # don't print the raw password. hashed_pw_string = "sha256(" + hashlib.sha256(l_password.encode('utf-8')).hexdigest() + ")" self.debug(logging.DEBUG, "Trying to login with %s:%s" % (repr(l_user), repr(hashed_pw_string))) - obj = self.json_obj('user.login', {'user': l_user, 'password': l_password}, auth=False) + obj = self.json_obj('user.login', {username_keyword: l_user, 'password': l_password}, auth=False) result = self.do_request(obj) self.auth = result['result'] @@ -296,7 +302,8 @@ def logged_in(self): return False def api_version(self, **options): - self.__checkauth__() + # kicked out check auth to be able to check vesion before being logged in to use the correct username keyword + #self.__checkauth__() obj = self.do_request(self.json_obj('apiinfo.version', options, auth=False)) return obj['result'] diff --git a/build/debian/changelog b/build/debian/changelog index a55f2dc8e..ae04afb5d 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,8 +1,8 @@ -nagstamon (3.11-20230212) unstable; urgency=low +nagstamon (3.11-20230222) unstable; urgency=low * New upstream - stuff - -- Henri Wahl <henri@nagstamon.de> Sun, Feb 12 2023 08:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Wed, Feb 22 2023 08:00:00 +0100 nagstamon (3.10.1) stable; urgency=low * New upstream From 9238f4538633b6a964de9eca8eb0eebec09c2e68 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Wed, 22 Feb 2023 13:40:11 +0100 Subject: [PATCH 536/884] target_architecture='universal2' --- build/macos/nagstamon.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/macos/nagstamon.spec b/build/macos/nagstamon.spec index 5c02b1c39..f897784fa 100644 --- a/build/macos/nagstamon.spec +++ b/build/macos/nagstamon.spec @@ -34,7 +34,7 @@ exe = EXE(pyz, upx_exclude=[], runtime_tmpdir=None, console=False, - target_arch=None, + target_architecture='universal2', codesign_identity=None, entitlements_file=None, icon='../../Nagstamon/resources/nagstamon.icns') From c484bff6dbb2ebb201212559190821cd6063b7db Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 25 Feb 2023 13:06:31 +0100 Subject: [PATCH 537/884] try to fix Zabbix crash --- Nagstamon/Config.py | 2 +- Nagstamon/QUI/__init__.py | 4 ++-- build/debian/changelog | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index cc2156809..f8775334a 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -121,7 +121,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.11-20230222' + VERSION = '3.11-20230225' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2023 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 7cca1c93a..df0c2a547 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -3870,7 +3870,7 @@ def action_clipboard_action_statusinformation(self): @Slot() def action_clipboard_action_all(self): """ - + copy all information to clipboard """ list_host = [] @@ -3896,7 +3896,7 @@ def action_clipboard_action_all(self): item = self.server.hosts[host] text += 'Host: {0}\n'.format(host) # if it is a service switch to service object - if service != '': + if service != '' and item.services.get(service): item = item.services[service] text += 'Service: {0}\n'.format(service) # the other properties belong to both hosts and services diff --git a/build/debian/changelog b/build/debian/changelog index ae04afb5d..b4e9a24b0 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,8 +1,8 @@ -nagstamon (3.11-20230222) unstable; urgency=low +nagstamon (3.11-20230225) unstable; urgency=low * New upstream - stuff - -- Henri Wahl <henri@nagstamon.de> Wed, Feb 22 2023 08:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Sat, Feb 25 2023 08:00:00 +0100 nagstamon (3.10.1) stable; urgency=low * New upstream From f36d2bebff1abf51e7694977dc9b5d76ef46878c Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 25 Feb 2023 14:30:25 +0100 Subject: [PATCH 538/884] massively show hidden dock icon on macos dialogs --- Nagstamon/QUI/__init__.py | 64 +++++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 22 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 7cca1c93a..5ac77b077 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -4546,6 +4546,12 @@ def show(self, tab=0): """ simple how method, to be enriched """ + + # in case dock icon is configured invisible in macOS it has to be shown while dialog is shown + # to be able to get keyboard focus + if OS == OS_MACOS and conf.hide_macos_dock_icon: + hide_macos_dock_icon(False) + # tell the world that dialog pops up self.show_dialog.emit() @@ -4612,16 +4618,26 @@ def fill_list(self, listwidget, config): @Slot() def ok(self): - # dummy OK treatment - pass + """ + as default closes dialog - might be refined, for example by settings dialog + """ + # hide macOS dock icon again if it is configured to be hidden + # was only necessary to show up to let dialog get keyboard focus + if OS == OS_MACOS and conf.hide_macos_dock_icon: + hide_macos_dock_icon(True) + self.window.close() @Slot() def cancel(self): """ as default closes dialog - might be refined, for example by settings dialog """ + # hide macOS dock icon again if it is configured to be hidden + # was only necessary to show up to let dialog get keyboard focus + if OS == OS_MACOS and conf.hide_macos_dock_icon: + hide_macos_dock_icon(True) self.window.close() - + class Dialog_Settings(Dialog): """ @@ -5138,12 +5154,16 @@ def ok(self): # see if there are any servers created and enabled check_servers() + # call close and macOS dock icon treatment from ancestor + super().ok() + @Slot() def cancel(self): """ check if there are any usable servers configured """ - self.window.close() + # call close and macOS dock icon treatment from ancestor + super().ok() check_servers() @Slot() @@ -6020,8 +6040,6 @@ def ok(self): # tell main window about changes (Zabbix, Opsview for example) self.edited.emit() - self.window.close() - # delete old server .conf file to reflect name changes # new one will be written soon if self.previous_server_conf is not None: @@ -6030,6 +6048,9 @@ def ok(self): # store server settings conf.SaveMultipleConfig('servers', 'server') + # call close and macOS dock icon treatment from ancestor + super().ok() + @Slot() def choose_custom_cert_ca_file(self): """ @@ -6241,6 +6262,9 @@ def ok(self): # store server settings conf.SaveMultipleConfig('actions', 'action') + # call close and macOS dock icon treatment from ancestor + super().ok() + class Dialog_Acknowledge(Dialog): """ @@ -6372,7 +6396,8 @@ def ok(self): 'acknowledge_all_services': acknowledge_all_services, 'all_services': all_services, 'expire_time': expire_datetime}) - + # call close and macOS dock icon treatment from ancestor + super().ok() class Dialog_Downtime(Dialog): """ @@ -6461,9 +6486,10 @@ def ok(self): 'end_time': self.window.input_lineedit_end_time.text(), 'hours': int(self.window.input_spinbox_duration_hours.value()), 'minutes': int(self.window.input_spinbox_duration_minutes.value())}) + # call close and macOS dock icon treatment from ancestor + super().ok() - Slot(str, str) - + @Slot(str, str) def set_start_end(self, start, end): """ put values sent by worker into start and end fields @@ -6471,8 +6497,7 @@ def set_start_end(self, start, end): self.window.input_lineedit_start_time.setText(start) self.window.input_lineedit_end_time.setText(end) - Slot() - + @Slot() def set_type_fixed(self): """ enable/disable appropriate widgets if type is "Fixed" @@ -6483,8 +6508,7 @@ def set_type_fixed(self): self.window.input_spinbox_duration_hours.hide() self.window.input_spinbox_duration_minutes.hide() - Slot() - + @Slot() def set_type_flexible(self): """ enable/disable appropriate widgets if type is "Flexible" @@ -6571,6 +6595,8 @@ def ok(self): 'comment': self.window.input_lineedit_comment.text(), 'check_output': self.window.input_lineedit_check_output.text(), 'performance_data': self.window.input_lineedit_performance_data.text()}) + # call close and macOS dock icon treatment from ancestor + super().ok() class Dialog_Authentication(Dialog): @@ -6664,6 +6690,9 @@ def ok(self): # update server_vbox label self.update.emit(self.server.name) + # call close and macOS dock icon treatment from ancestor + super().ok() + @Slot() def toggle_autologin(self): """ @@ -7133,14 +7162,5 @@ def hide_macos_dock_icon(hide=False): elif conf.icon_in_systray: systrayicon.set_menu(menu) -# set flag to be LSUIElement like in file info.properties -if OS == OS_MACOS: - if conf.hide_macos_dock_icon: - lsuielement = '1' - else: - lsuielement = '0' - macos_info_dictionary = NSBundle.mainBundle().infoDictionary() - macos_info_dictionary['LSUIElement'] = lsuielement - # versatile mediaplayer mediaplayer = MediaPlayer(statuswindow, RESOURCE_FILES) From ab58ce6b03946908f6308227fd6297cd8fd63bb5 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 25 Feb 2023 14:39:38 +0100 Subject: [PATCH 539/884] massively show hidden dock icon on macos dialogs part II --- Nagstamon/QUI/__init__.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 5ac77b077..0d8b23742 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -6792,7 +6792,15 @@ def __init__(self, dialog): self.window.tabs.setCurrentIndex(0) def show(self): + # in case dock icon is configured invisible in macOS it has to be shown while dialog is shown + # to be able to get keyboard focus + if OS == OS_MACOS and conf.hide_macos_dock_icon: + hide_macos_dock_icon(False) self.window.exec() + # hide macOS dock icon again if it is configured to be hidden + # was only necessary to show up to let dialog get keyboard focus + if OS == OS_MACOS and conf.hide_macos_dock_icon: + hide_macos_dock_icon(True) class CheckVersion(QObject): From cf3690fa7a82b9daab93a17800932eb3b70a4d16 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 25 Feb 2023 14:46:37 +0100 Subject: [PATCH 540/884] massively show hidden dock icon on macos dialogs part III --- Nagstamon/QUI/__init__.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 0d8b23742..cd68d7b00 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -4536,6 +4536,11 @@ def __init__(self, dialog): # try to get and keep focus self.window.setWindowModality(Qt.WindowModality.ApplicationModal) + # in case dock icon is configured invisible in macOS it has to be shown while dialog is shown + # to be able to get keyboard focus + if OS == OS_MACOS and conf.hide_macos_dock_icon: + hide_macos_dock_icon(False) + def initialize(self): """ dummy initialize method @@ -4546,12 +4551,6 @@ def show(self, tab=0): """ simple how method, to be enriched """ - - # in case dock icon is configured invisible in macOS it has to be shown while dialog is shown - # to be able to get keyboard focus - if OS == OS_MACOS and conf.hide_macos_dock_icon: - hide_macos_dock_icon(False) - # tell the world that dialog pops up self.show_dialog.emit() @@ -6792,10 +6791,6 @@ def __init__(self, dialog): self.window.tabs.setCurrentIndex(0) def show(self): - # in case dock icon is configured invisible in macOS it has to be shown while dialog is shown - # to be able to get keyboard focus - if OS == OS_MACOS and conf.hide_macos_dock_icon: - hide_macos_dock_icon(False) self.window.exec() # hide macOS dock icon again if it is configured to be hidden # was only necessary to show up to let dialog get keyboard focus From e7539956374d662be0077d5f8a1449d2af92d4e8 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 25 Feb 2023 14:50:22 +0100 Subject: [PATCH 541/884] massively show hidden dock icon on macos dialogs part IV --- Nagstamon/QUI/__init__.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index cd68d7b00..d26d98855 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -4536,11 +4536,6 @@ def __init__(self, dialog): # try to get and keep focus self.window.setWindowModality(Qt.WindowModality.ApplicationModal) - # in case dock icon is configured invisible in macOS it has to be shown while dialog is shown - # to be able to get keyboard focus - if OS == OS_MACOS and conf.hide_macos_dock_icon: - hide_macos_dock_icon(False) - def initialize(self): """ dummy initialize method @@ -4551,6 +4546,12 @@ def show(self, tab=0): """ simple how method, to be enriched """ + + # in case dock icon is configured invisible in macOS it has to be shown while dialog is shown + # to be able to get keyboard focus + if OS == OS_MACOS and conf.hide_macos_dock_icon: + hide_macos_dock_icon(False) + # tell the world that dialog pops up self.show_dialog.emit() @@ -4558,6 +4559,11 @@ def show(self, tab=0): self.window.adjustSize() self.window.show() + # in case dock icon is configured invisible in macOS it has to be shown while dialog is shown + # to be able to get keyboard focus + if OS == OS_MACOS and conf.hide_macos_dock_icon: + hide_macos_dock_icon(False) + def toggle_visibility(self, checkbox, widgets=[]): """ state of checkbox toggles visibility of widgets From 8900f881074cc3738fb7f4d11acd00b4776da552 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 25 Feb 2023 14:52:21 +0100 Subject: [PATCH 542/884] massively show hidden dock icon on macos dialogs part V --- Nagstamon/QUI/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index d26d98855..4b209343d 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -6402,7 +6402,7 @@ def ok(self): 'all_services': all_services, 'expire_time': expire_datetime}) # call close and macOS dock icon treatment from ancestor - super().ok() + #super().ok() class Dialog_Downtime(Dialog): """ From e1de50614d96388265d7010ce60c47bfa5cf2c4e Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 25 Feb 2023 18:58:52 +0100 Subject: [PATCH 543/884] massively show hidden dock icon on macos dialogs part VI --- Nagstamon/QUI/__init__.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index b44326fa3..d0c48cc09 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -4557,7 +4557,7 @@ def show(self, tab=0): # reset window if only needs smaller screen estate self.window.adjustSize() - self.window.show() + self.window.exec() # in case dock icon is configured invisible in macOS it has to be shown while dialog is shown # to be able to get keyboard focus @@ -5021,8 +5021,9 @@ def show(self, tab=0): self.window.tabs.setCurrentIndex(tab) # reset window if only needs smaller screen estate - self.window.adjustSize() - self.window.exec() + #self.window.adjustSize() + #self.window.exec() + super().show() @Slot() def show_new_server(self): @@ -6054,7 +6055,7 @@ def ok(self): conf.SaveMultipleConfig('servers', 'server') # call close and macOS dock icon treatment from ancestor - super().ok() + #super().ok() @Slot() def choose_custom_cert_ca_file(self): @@ -6268,7 +6269,7 @@ def ok(self): conf.SaveMultipleConfig('actions', 'action') # call close and macOS dock icon treatment from ancestor - super().ok() + #super().ok() class Dialog_Acknowledge(Dialog): @@ -6492,7 +6493,7 @@ def ok(self): 'hours': int(self.window.input_spinbox_duration_hours.value()), 'minutes': int(self.window.input_spinbox_duration_minutes.value())}) # call close and macOS dock icon treatment from ancestor - super().ok() + #super().ok() @Slot(str, str) def set_start_end(self, start, end): @@ -6601,7 +6602,7 @@ def ok(self): 'check_output': self.window.input_lineedit_check_output.text(), 'performance_data': self.window.input_lineedit_performance_data.text()}) # call close and macOS dock icon treatment from ancestor - super().ok() + #super().ok() class Dialog_Authentication(Dialog): @@ -6696,7 +6697,7 @@ def ok(self): self.update.emit(self.server.name) # call close and macOS dock icon treatment from ancestor - super().ok() + #super().ok() @Slot() def toggle_autologin(self): @@ -6796,12 +6797,12 @@ def __init__(self, dialog): self.window.tabs.setCurrentIndex(0) - def show(self): - self.window.exec() - # hide macOS dock icon again if it is configured to be hidden - # was only necessary to show up to let dialog get keyboard focus - if OS == OS_MACOS and conf.hide_macos_dock_icon: - hide_macos_dock_icon(True) + # def show(self): + # self.window.exec() + # # hide macOS dock icon again if it is configured to be hidden + # # was only necessary to show up to let dialog get keyboard focus + # if OS == OS_MACOS and conf.hide_macos_dock_icon: + # hide_macos_dock_icon(True) class CheckVersion(QObject): @@ -7137,7 +7138,6 @@ def hide_macos_dock_icon(hide=False): else: NSApp.setActivationPolicy_(NSApplicationPresentationDefault) - # check for updates check_version = CheckVersion() From a30719c329a9fd6deedf55ac0bd376af7b57085b Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 25 Feb 2023 19:00:48 +0100 Subject: [PATCH 544/884] massively show hidden dock icon on macos dialogs part VII --- Nagstamon/QUI/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index d0c48cc09..3997efa44 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -4562,7 +4562,7 @@ def show(self, tab=0): # in case dock icon is configured invisible in macOS it has to be shown while dialog is shown # to be able to get keyboard focus if OS == OS_MACOS and conf.hide_macos_dock_icon: - hide_macos_dock_icon(False) + hide_macos_dock_icon(True) def toggle_visibility(self, checkbox, widgets=[]): """ From 5bb7b4e54ef867a3d5483fc51b4b7d5366c3d213 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 25 Feb 2023 19:09:09 +0100 Subject: [PATCH 545/884] massively show hidden dock icon on macos dialogs part VIII --- Nagstamon/QUI/__init__.py | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 3997efa44..fe1af413c 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -4557,12 +4557,7 @@ def show(self, tab=0): # reset window if only needs smaller screen estate self.window.adjustSize() - self.window.exec() - - # in case dock icon is configured invisible in macOS it has to be shown while dialog is shown - # to be able to get keyboard focus - if OS == OS_MACOS and conf.hide_macos_dock_icon: - hide_macos_dock_icon(True) + self.window.show() def toggle_visibility(self, checkbox, widgets=[]): """ @@ -5169,7 +5164,7 @@ def cancel(self): check if there are any usable servers configured """ # call close and macOS dock icon treatment from ancestor - super().ok() + super().cancel() check_servers() @Slot() @@ -6055,7 +6050,7 @@ def ok(self): conf.SaveMultipleConfig('servers', 'server') # call close and macOS dock icon treatment from ancestor - #super().ok() + super().ok() @Slot() def choose_custom_cert_ca_file(self): @@ -6269,7 +6264,7 @@ def ok(self): conf.SaveMultipleConfig('actions', 'action') # call close and macOS dock icon treatment from ancestor - #super().ok() + super().ok() class Dialog_Acknowledge(Dialog): @@ -6403,7 +6398,7 @@ def ok(self): 'all_services': all_services, 'expire_time': expire_datetime}) # call close and macOS dock icon treatment from ancestor - #super().ok() + super().ok() class Dialog_Downtime(Dialog): """ @@ -6493,7 +6488,7 @@ def ok(self): 'hours': int(self.window.input_spinbox_duration_hours.value()), 'minutes': int(self.window.input_spinbox_duration_minutes.value())}) # call close and macOS dock icon treatment from ancestor - #super().ok() + super().ok() @Slot(str, str) def set_start_end(self, start, end): @@ -6602,7 +6597,7 @@ def ok(self): 'check_output': self.window.input_lineedit_check_output.text(), 'performance_data': self.window.input_lineedit_performance_data.text()}) # call close and macOS dock icon treatment from ancestor - #super().ok() + super().ok() class Dialog_Authentication(Dialog): @@ -6697,7 +6692,7 @@ def ok(self): self.update.emit(self.server.name) # call close and macOS dock icon treatment from ancestor - #super().ok() + super().ok() @Slot() def toggle_autologin(self): @@ -6797,12 +6792,12 @@ def __init__(self, dialog): self.window.tabs.setCurrentIndex(0) - # def show(self): - # self.window.exec() - # # hide macOS dock icon again if it is configured to be hidden - # # was only necessary to show up to let dialog get keyboard focus - # if OS == OS_MACOS and conf.hide_macos_dock_icon: - # hide_macos_dock_icon(True) + def show(self): + self.window.exec() + # hide macOS dock icon again if it is configured to be hidden + # was only necessary to show up to let dialog get keyboard focus + if OS == OS_MACOS and conf.hide_macos_dock_icon: + hide_macos_dock_icon(True) class CheckVersion(QObject): From 9832773e2a66ce83ca1442bd21925f48a2b62e31 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 26 Feb 2023 23:22:51 +0100 Subject: [PATCH 546/884] massively show hidden dock icon on macos dialogs part IX --- Nagstamon/QUI/__init__.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index fe1af413c..d2598b671 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -4436,27 +4436,33 @@ class Dialogs(object): """ class for accessing all dialogs """ + windows = list() def __init__(self): # settings main dialog self.settings = Dialog_Settings('settings_main') self.settings.initialize() + self.windows.append(self.settings.window) # server settings dialog self.server = Dialog_Server('settings_server') self.server.initialize() + self.windows.append(self.server.window) # action settings dialog self.action = Dialog_Action('settings_action') self.action.initialize() + self.windows.append(self.action.window) # acknowledge dialog for miserable item context menu self.acknowledge = Dialog_Acknowledge('dialog_acknowledge') self.acknowledge.initialize() + self.windows.append(self.acknowledge.window) # downtime dialog for miserable item context menu self.downtime = Dialog_Downtime('dialog_downtime') self.downtime.initialize() + self.windows.append(self.downtime.window) # open defaults settings on button click self.downtime.window.button_change_defaults_downtime.clicked.connect(self.settings.show_defaults) @@ -4467,20 +4473,25 @@ def __init__(self): # downtime dialog for miserable item context menu self.submit = Dialog_Submit('dialog_submit') self.submit.initialize() + self.windows.append(self.submit.window) # authentication dialog for username/password self.authentication = Dialog_Authentication('dialog_authentication') self.authentication.initialize() + self.windows.append(self.authentication.window) # dialog for asking about disabled or not configured servers self.server_missing = Dialog_Server_missing('dialog_server_missing') self.server_missing.initialize() + self.windows.append(self.server_missing.window) + # open server creation dialog self.server_missing.window.button_create_server.clicked.connect(self.settings.show_new_server) self.server_missing.window.button_enable_server.clicked.connect(self.settings.show) # about dialog self.about = Dialog_About('dialog_about') + self.windows.append(self.about.window) # file chooser Dialog self.file_chooser = QFileDialog() @@ -4490,6 +4501,12 @@ def __init__(self): self.server.edited.connect(self.settings.toggle_op5monitor_widgets) self.server.edited.connect(self.settings.toggle_expire_time_widgets) + def get_shown_dialogs(self): + print(self.windows) + for x in self.windows: + print(x) + print([x for x in self.windows if x.isVisible()]) + class Dialog(QObject): """ @@ -4547,6 +4564,8 @@ def show(self, tab=0): simple how method, to be enriched """ + dialogs.get_shown_dialogs() + # in case dock icon is configured invisible in macOS it has to be shown while dialog is shown # to be able to get keyboard focus if OS == OS_MACOS and conf.hide_macos_dock_icon: @@ -5884,6 +5903,8 @@ def decoration_function(self, **kwargs): # important final size adjustment self.window.adjustSize() + dialogs.get_shown_dialogs() + # self.window.show() self.window.exec() @@ -5918,7 +5939,7 @@ def edit(self, server_name=None, show_options=False): self.previous_server_conf = deepcopy(self.server_conf) # set window title self.window.setWindowTitle('Edit %s' % (self.server_conf.name)) - # set self.shot_optios to give value to decorator + # set self.show_options to give value to decorator self.show_options = show_options @dialog_decoration From a3439b014b9735b3d10d0f8e549973a335d06cb9 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 27 Feb 2023 23:01:28 +0100 Subject: [PATCH 547/884] massively show hidden dock icon on macos dialogs part X --- Nagstamon/QUI/__init__.py | 53 +++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index d2598b671..7fddfaa36 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -4502,10 +4502,7 @@ def __init__(self): self.server.edited.connect(self.settings.toggle_expire_time_widgets) def get_shown_dialogs(self): - print(self.windows) - for x in self.windows: - print(x) - print([x for x in self.windows if x.isVisible()]) + return [x for x in self.windows if x.isVisible()] class Dialog(QObject): @@ -4564,7 +4561,7 @@ def show(self, tab=0): simple how method, to be enriched """ - dialogs.get_shown_dialogs() + self.show_macos_dock_icon_if_necessary() # in case dock icon is configured invisible in macOS it has to be shown while dialog is shown # to be able to get keyboard focus @@ -4640,23 +4637,36 @@ def ok(self): """ as default closes dialog - might be refined, for example by settings dialog """ - # hide macOS dock icon again if it is configured to be hidden - # was only necessary to show up to let dialog get keyboard focus - if OS == OS_MACOS and conf.hide_macos_dock_icon: - hide_macos_dock_icon(True) - self.window.close() + self.hide_macos_dock_icon_if_necessary() + @Slot() def cancel(self): """ as default closes dialog - might be refined, for example by settings dialog """ - # hide macOS dock icon again if it is configured to be hidden - # was only necessary to show up to let dialog get keyboard focus - if OS == OS_MACOS and conf.hide_macos_dock_icon: - hide_macos_dock_icon(True) - self.window.close() - + self.hide_macos_dock_icon_if_necessary() + + def show_macos_dock_icon_if_necessary(self): + """ + show macOS dock icon again if it is configured to be hidden + was only necessary to show up to let dialog get keyboard focus + """ + if not len(dialogs.get_shown_dialogs()): + print('show') + if OS == OS_MACOS: + hide_macos_dock_icon(False) + + def hide_macos_dock_icon_if_necessary(self): + """ + hide macOS dock icon again if it is configured to be hidden + was only necessary to show up to let dialog get keyboard focus + """ + if not len(dialogs.get_shown_dialogs()): + print('hide') + if OS == OS_MACOS: + hide_macos_dock_icon(True) + class Dialog_Settings(Dialog): """ @@ -5903,7 +5913,7 @@ def decoration_function(self, **kwargs): # important final size adjustment self.window.adjustSize() - dialogs.get_shown_dialogs() + self.show_macos_dock_icon_if_necessary() # self.window.show() self.window.exec() @@ -6180,7 +6190,6 @@ def decoration_function(self): # important final size adjustment self.window.adjustSize() - # self.window.show() self.window.exec() # give back decorated function @@ -6813,12 +6822,8 @@ def __init__(self, dialog): self.window.tabs.setCurrentIndex(0) - def show(self): - self.window.exec() - # hide macOS dock icon again if it is configured to be hidden - # was only necessary to show up to let dialog get keyboard focus - if OS == OS_MACOS and conf.hide_macos_dock_icon: - hide_macos_dock_icon(True) + #def show(self): + # self.window.exec() class CheckVersion(QObject): From 8d69741a803bf9510ecfdf3606749d82d52ba42e Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 27 Feb 2023 23:04:26 +0100 Subject: [PATCH 548/884] massively show hidden dock icon on macos dialogs part XI --- Nagstamon/QUI/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 7fddfaa36..49c1669e7 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -4637,6 +4637,7 @@ def ok(self): """ as default closes dialog - might be refined, for example by settings dialog """ + self.window.close() self.hide_macos_dock_icon_if_necessary() @@ -4645,6 +4646,7 @@ def cancel(self): """ as default closes dialog - might be refined, for example by settings dialog """ + self.window.close() self.hide_macos_dock_icon_if_necessary() def show_macos_dock_icon_if_necessary(self): @@ -5918,6 +5920,8 @@ def decoration_function(self, **kwargs): # self.window.show() self.window.exec() + self.hide_macos_dock_icon_if_necessary() + # give back decorated function return (decoration_function) From 1ce85e5a8e713460ab637cbbefa53093002b414a Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 27 Feb 2023 23:08:33 +0100 Subject: [PATCH 549/884] massively show hidden dock icon on macos dialogs part XII --- Nagstamon/QUI/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 49c1669e7..91cd11b3f 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -5917,7 +5917,6 @@ def decoration_function(self, **kwargs): self.show_macos_dock_icon_if_necessary() - # self.window.show() self.window.exec() self.hide_macos_dock_icon_if_necessary() From dbe781ba5a46d49ca4d270d2cbd4bc1a24f52bed Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 27 Feb 2023 23:18:28 +0100 Subject: [PATCH 550/884] massively show hidden dock icon on macos dialogs part XIII - looks like final --- Nagstamon/QUI/__init__.py | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 91cd11b3f..a1fea9e86 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -4502,6 +4502,9 @@ def __init__(self): self.server.edited.connect(self.settings.toggle_expire_time_widgets) def get_shown_dialogs(self): + """ + get list of currently show dialog windows - needed for macOS hide dock icon stuff + """ return [x for x in self.windows if x.isVisible()] @@ -4560,7 +4563,8 @@ def show(self, tab=0): """ simple how method, to be enriched """ - + # if running on macOS with disabled dock icon the dock icon might have to be made visible + # to make Nagstamon accept keyboard input self.show_macos_dock_icon_if_necessary() # in case dock icon is configured invisible in macOS it has to be shown while dialog is shown @@ -4638,15 +4642,16 @@ def ok(self): as default closes dialog - might be refined, for example by settings dialog """ self.window.close() + # en reverse the dock icon might be hidden again after a potential keyboard input self.hide_macos_dock_icon_if_necessary() - @Slot() def cancel(self): """ as default closes dialog - might be refined, for example by settings dialog """ self.window.close() + # en reverse the dock icon might be hidden again after a potential keyboard input self.hide_macos_dock_icon_if_necessary() def show_macos_dock_icon_if_necessary(self): @@ -4654,9 +4659,9 @@ def show_macos_dock_icon_if_necessary(self): show macOS dock icon again if it is configured to be hidden was only necessary to show up to let dialog get keyboard focus """ - if not len(dialogs.get_shown_dialogs()): - print('show') - if OS == OS_MACOS: + if OS == OS_MACOS and conf.hide_macos_dock_icon: + # if no window is shown already show dock icon + if not len(dialogs.get_shown_dialogs()): hide_macos_dock_icon(False) def hide_macos_dock_icon_if_necessary(self): @@ -4664,9 +4669,9 @@ def hide_macos_dock_icon_if_necessary(self): hide macOS dock icon again if it is configured to be hidden was only necessary to show up to let dialog get keyboard focus """ - if not len(dialogs.get_shown_dialogs()): - print('hide') - if OS == OS_MACOS: + if OS == OS_MACOS and conf.hide_macos_dock_icon: + # if no window is shown anymore hide dock icon + if not len(dialogs.get_shown_dialogs()): hide_macos_dock_icon(True) @@ -5915,10 +5920,13 @@ def decoration_function(self, **kwargs): # important final size adjustment self.window.adjustSize() + # if running on macOS with disabled dock icon the dock icon might have to be made visible + # to make Nagstamon accept keyboard input self.show_macos_dock_icon_if_necessary() self.window.exec() + # en reverse the dock icon might be hidden again after a potential keyboard input self.hide_macos_dock_icon_if_necessary() # give back decorated function @@ -6825,9 +6833,6 @@ def __init__(self, dialog): self.window.tabs.setCurrentIndex(0) - #def show(self): - # self.window.exec() - class CheckVersion(QObject): """ From f0b34393a5e1a576f6b07fcd98b93982827821d3 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 27 Feb 2023 23:19:05 +0100 Subject: [PATCH 551/884] 3.11-20230227 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index f8775334a..44a8f81d6 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -121,7 +121,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.11-20230225' + VERSION = '3.11-20230227' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2023 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index b4e9a24b0..db811524c 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,8 +1,8 @@ -nagstamon (3.11-20230225) unstable; urgency=low +nagstamon (3.11-20230227) unstable; urgency=low * New upstream - stuff - -- Henri Wahl <henri@nagstamon.de> Sat, Feb 25 2023 08:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Mon, Feb 27 2023 08:00:00 +0100 nagstamon (3.10.1) stable; urgency=low * New upstream From 4a000fb2552652a61eb6365b2c78541b74d83323 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 27 Feb 2023 23:38:53 +0100 Subject: [PATCH 552/884] 3.11-20230227 --- Nagstamon/QUI/__init__.py | 14 +++++++++++++- Nagstamon/resources/qui/dialog_about.ui | 8 ++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index a1fea9e86..8d0a25bb5 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -4663,6 +4663,8 @@ def show_macos_dock_icon_if_necessary(self): # if no window is shown already show dock icon if not len(dialogs.get_shown_dialogs()): hide_macos_dock_icon(False) + else: + print('show') def hide_macos_dock_icon_if_necessary(self): """ @@ -4673,7 +4675,8 @@ def hide_macos_dock_icon_if_necessary(self): # if no window is shown anymore hide dock icon if not len(dialogs.get_shown_dialogs()): hide_macos_dock_icon(True) - + else: + print('hide') class Dialog_Settings(Dialog): """ @@ -6201,8 +6204,15 @@ def decoration_function(self): # important final size adjustment self.window.adjustSize() + # if running on macOS with disabled dock icon the dock icon might have to be made visible + # to make Nagstamon accept keyboard input + self.show_macos_dock_icon_if_necessary() + self.window.exec() + # en reverse the dock icon might be hidden again after a potential keyboard input + self.hide_macos_dock_icon_if_necessary() + # give back decorated function return (decoration_function) @@ -6774,8 +6784,10 @@ def __init__(self, dialog): # hide dialog when server is to be created or enabled self.window.button_create_server.clicked.connect(self.window.hide) self.window.button_enable_server.clicked.connect(self.window.hide) + self.window.button_ignore.clicked.connect(self.ok) # simply hide window if ignore button chosen self.window.button_ignore.clicked.connect(self.window.hide) + self.window.button_ignore.clicked.connect(self.cancel) # byebye if exit button was pressed self.window.button_exit.clicked.connect(self.window.hide) self.window.button_exit.clicked.connect(exit) diff --git a/Nagstamon/resources/qui/dialog_about.ui b/Nagstamon/resources/qui/dialog_about.ui index e3e2665e2..82adcc210 100644 --- a/Nagstamon/resources/qui/dialog_about.ui +++ b/Nagstamon/resources/qui/dialog_about.ui @@ -7,7 +7,7 @@ <x>0</x> <y>0</y> <width>500</width> - <height>322</height> + <height>347</height> </rect> </property> <property name="sizePolicy"> @@ -150,7 +150,7 @@ </widget> </item> <item row="1" column="0"> - <widget class="QDialogButtonBox" name="buttonBox"> + <widget class="QDialogButtonBox" name="button_box"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> @@ -164,7 +164,7 @@ <resources/> <connections> <connection> - <sender>buttonBox</sender> + <sender>button_box</sender> <signal>accepted()</signal> <receiver>dialog_about</receiver> <slot>accept()</slot> @@ -180,7 +180,7 @@ </hints> </connection> <connection> - <sender>buttonBox</sender> + <sender>button_box</sender> <signal>rejected()</signal> <receiver>dialog_about</receiver> <slot>reject()</slot> From b570ecfaa6c32a00fa2e1a05ad7d15c87c48e8d0 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 27 Feb 2023 23:49:43 +0100 Subject: [PATCH 553/884] 3.11-20230227 attempt III --- Nagstamon/QUI/__init__.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 8d0a25bb5..78df6731c 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -6703,7 +6703,14 @@ def show_auth_dialog(self, server): if not statuswindow is None: statuswindow.hide_window() self.window.adjustSize() + + # the dock icon might be needed to be shown for a potential keyboard input + self.show_macos_dock_icon_if_necessary() + self.window.exec() + + # en reverse the dock icon might be hidden again after a potential keyboard input + self.hide_macos_dock_icon_if_necessary() def ok(self): """ From dacb08bf1fd21f5f47a1b6b86cf3c7611baa801b Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 28 Feb 2023 00:02:10 +0100 Subject: [PATCH 554/884] 3.11-20230227 attempt IV --- Nagstamon/QUI/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 78df6731c..f7da54e74 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -402,7 +402,7 @@ def icon_clicked(self, reason): # when green icon is displayed and no popwin is about to pop up... if get_worst_status() == 'UP': # ...nothing to do except on macOS where menu should be shown - if OS == OS_MACOS: + if OS == OS_MACOS and not self.error_shown: self.menu.show_at_cursor() else: # show status window if there is something to tell @@ -421,7 +421,7 @@ def show_state(self): self.setIcon(self.icons[worst_status]) # set current icon for flashing self.current_icon = self.icons[worst_status] - del (worst_status) + del worst_status else: self.setIcon(self.icons['ERROR']) From 350ca9f5335c538b1b02959c4618e858ee0cb86b Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 28 Feb 2023 00:17:49 +0100 Subject: [PATCH 555/884] 3.11-20230227 attempt V --- Nagstamon/QUI/__init__.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index f7da54e74..a481cc73e 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -402,8 +402,12 @@ def icon_clicked(self, reason): # when green icon is displayed and no popwin is about to pop up... if get_worst_status() == 'UP': # ...nothing to do except on macOS where menu should be shown - if OS == OS_MACOS and not self.error_shown: - self.menu.show_at_cursor() + if OS == OS_MACOS: + # in case there is some error show popwin rather than context menu + if not self.error_shown: + self.menu.show_at_cursor() + else: + self.show_popwin.emit() else: # show status window if there is something to tell if statuswindow.is_shown: From bfd28fbd7d2cc64fbc6040bf8406c98dfcb51d58 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 28 Feb 2023 00:23:01 +0100 Subject: [PATCH 556/884] 3.11-20230227 removed debug --- Nagstamon/QUI/__init__.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index a481cc73e..81ea598ee 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -4667,8 +4667,6 @@ def show_macos_dock_icon_if_necessary(self): # if no window is shown already show dock icon if not len(dialogs.get_shown_dialogs()): hide_macos_dock_icon(False) - else: - print('show') def hide_macos_dock_icon_if_necessary(self): """ @@ -4679,8 +4677,7 @@ def hide_macos_dock_icon_if_necessary(self): # if no window is shown anymore hide dock icon if not len(dialogs.get_shown_dialogs()): hide_macos_dock_icon(True) - else: - print('hide') + class Dialog_Settings(Dialog): """ From 0433f40aa16493dbdee342c3af002c5e227e746a Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Wed, 8 Mar 2023 19:00:54 +0100 Subject: [PATCH 557/884] might help to avoid vanished window --- Nagstamon/Config.py | 2 +- Nagstamon/QUI/__init__.py | 8 ++++++-- build/debian/changelog | 4 ++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 44a8f81d6..bc5008786 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -121,7 +121,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.11-20230227' + VERSION = '3.11-20230308' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2023 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 81ea598ee..03a9b43e7 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -4663,7 +4663,9 @@ def show_macos_dock_icon_if_necessary(self): show macOS dock icon again if it is configured to be hidden was only necessary to show up to let dialog get keyboard focus """ - if OS == OS_MACOS and conf.hide_macos_dock_icon: + if OS == OS_MACOS and \ + conf.icon_in_systray and \ + conf.hide_macos_dock_icon: # if no window is shown already show dock icon if not len(dialogs.get_shown_dialogs()): hide_macos_dock_icon(False) @@ -4673,7 +4675,9 @@ def hide_macos_dock_icon_if_necessary(self): hide macOS dock icon again if it is configured to be hidden was only necessary to show up to let dialog get keyboard focus """ - if OS == OS_MACOS and conf.hide_macos_dock_icon: + if OS == OS_MACOS and \ + conf.icon_in_systray and \ + conf.hide_macos_dock_icon: # if no window is shown anymore hide dock icon if not len(dialogs.get_shown_dialogs()): hide_macos_dock_icon(True) diff --git a/build/debian/changelog b/build/debian/changelog index db811524c..f6c714636 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,8 +1,8 @@ -nagstamon (3.11-20230227) unstable; urgency=low +nagstamon (3.11-20230308) unstable; urgency=low * New upstream - stuff - -- Henri Wahl <henri@nagstamon.de> Mon, Feb 27 2023 08:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Wed, Mar 08 2023 08:00:00 +0100 nagstamon (3.10.1) stable; urgency=low * New upstream From 32697c70ddcc0f3b38b4409b65d345737a0e0c7b Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Wed, 8 Mar 2023 19:17:09 +0100 Subject: [PATCH 558/884] remove reboot advice --- Nagstamon/resources/qui/settings_main.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/resources/qui/settings_main.ui b/Nagstamon/resources/qui/settings_main.ui index 48793167e..9b4f2fa7d 100644 --- a/Nagstamon/resources/qui/settings_main.ui +++ b/Nagstamon/resources/qui/settings_main.ui @@ -766,7 +766,7 @@ <item row="14" column="0"> <widget class="QCheckBox" name="input_checkbox_hide_macos_dock_icon"> <property name="text"> - <string>Hide macOS Dock icon - needs restart</string> + <string>Hide macOS Dock icon</string> </property> </widget> </item> From 9f4294d1e4ed77e8909f20c6d32df7bc01424741 Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Mon, 27 Mar 2023 23:56:08 +0200 Subject: [PATCH 559/884] play with signing --- build/build.py | 12 ++++++++++++ build/requirements/windows.txt | 4 ++-- build/windows/code_signing.ps1 | 6 ++++++ 3 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 build/windows/code_signing.ps1 diff --git a/build/build.py b/build/build.py index e87b0b95d..1bf8d6531 100644 --- a/build/build.py +++ b/build/build.py @@ -21,6 +21,7 @@ from pathlib import Path import platform import os, os.path +from os import environ import sys import shutil import subprocess @@ -60,11 +61,22 @@ # also no need for filename suffix FILENAME_SUFFIX = '' +SIGNING = False +if 'WIN_SIGNING_CERT_BASE64' in environ \ + and 'WIN_SIGNING_PASSWORD' in environ: + SIGNING = True + def winmain(): """ execute steps necessary for compilation of Windows binaries and setup.exe """ + + + if SIGNING: + subprocess.call(['powershell', './windows/code_signing.ps1', 'build/Nagstamon/Nagstamon.exe']) + sys.exit(0) + # InnoSetup does not like VersionInfoVersion with letters, only 0.0.0.0 schemed numbers if 'alpha' in VERSION.lower() or 'beta' in VERSION.lower() or 'rc' in VERSION.lower() or '-' in VERSION.lower(): VERSION_IS = VERSION.replace('alpha', '').replace('beta', '').replace('rc', '').replace('-', '.').replace('..', diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index 591089e81..d73e7b084 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -7,8 +7,8 @@ pip-system-certs psutil pyinstaller pypiwin32 -pyqt6==6.3.1 -pyqt6-qt6==6.3.1 +pyqt6==6.4.2 +pyqt6-qt6==6.4.2 pysocks python-dateutil requests diff --git a/build/windows/code_signing.ps1 b/build/windows/code_signing.ps1 new file mode 100644 index 000000000..320642f7c --- /dev/null +++ b/build/windows/code_signing.ps1 @@ -0,0 +1,6 @@ + +$file = $args[0] +$cert_buffer = [System.Convert]::FromBase64String($env:WIN_SIGNING_CERT_BASE64) + +$cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::New($cert_buffer, $env:WIN_SIGNING_PASSWORD) +Set-AuthenticodeSignature -HashAlgorithm SHA256 -Certificate $cert -TimestampServer http://timestamp.digicert.com -FilePath $file \ No newline at end of file From ee718d9f31aa34d1f9e3ce9c7230a9335c9ef2f2 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 28 Mar 2023 00:39:57 +0200 Subject: [PATCH 560/884] fix version --- Nagstamon/Config.py | 2 +- build/debian/changelog | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index bc5008786..647d37699 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -121,7 +121,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.11-20230308' + VERSION = '3.11-20230309' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2023 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index f6c714636..650896330 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.11-20230308) unstable; urgency=low +nagstamon (3.11-20230309) unstable; urgency=low * New upstream - stuff From ff4460877e3390e4126d9e321b1102d4cb11f273 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 28 Mar 2023 06:39:26 +0200 Subject: [PATCH 561/884] added fixes for macos --- Nagstamon/QUI/__init__.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 03a9b43e7..06d511e34 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -1206,9 +1206,11 @@ def set_mode(self): self.servers_scrollarea.hide() if conf.statusbar_floating: - # no need for icon in dock if floating - apply first to avoid window in background + # show icon in dock if window is set if OS == OS_MACOS: - hide_macos_dock_icon(conf.hide_macos_dock_icon) + # in floating mode always show dock icon - right now I am not able to + # get the icon hidden + hide_macos_dock_icon(False) # no need for systray systrayicon.hide() @@ -4573,7 +4575,9 @@ def show(self, tab=0): # in case dock icon is configured invisible in macOS it has to be shown while dialog is shown # to be able to get keyboard focus - if OS == OS_MACOS and conf.hide_macos_dock_icon: + if OS == OS_MACOS and \ + conf.icon_in_systray and \ + conf.hide_macos_dock_icon: hide_macos_dock_icon(False) # tell the world that dialog pops up @@ -4786,8 +4790,7 @@ def __init__(self, dialog): if OS == OS_MACOS: # offer option to hide icon in dock on macOS self.TOGGLE_DEPS.update({ - self.window.input_radiobutton_icon_in_systray: [self.window.input_checkbox_hide_macos_dock_icon], - self.window.input_radiobutton_statusbar_floating: [self.window.input_checkbox_hide_macos_dock_icon]}) + self.window.input_radiobutton_icon_in_systray: [self.window.input_checkbox_hide_macos_dock_icon]}) # show option to enable position fix only on Unices if not OS in OS_NON_LINUX: From 829f83606a3fcd8adf66350bd43b642a81dbcbdb Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Tue, 28 Mar 2023 23:12:46 +0200 Subject: [PATCH 562/884] signing works --- .github/workflows/build-release-latest.yml | 6 ++++++ build/build.py | 14 +++++++++----- build/windows/code_signing.ps1 | 9 +++++++-- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 334462f6d..91cc87be7 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -208,6 +208,8 @@ jobs: - run: cd ${{ github.workspace }}/build; python build.py env: PYTHONPATH: ${{ github.workspace }} + WIN_SIGNING_CERT_BASE64: ${{ secrets.SIGNING_CERT_BASE64 }} + WIN_SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - uses: actions/upload-artifact@v3 with: path: | @@ -232,6 +234,8 @@ jobs: - run: cd ${{ github.workspace }}/build; python build.py env: PYTHONPATH: ${{ github.workspace }} + WIN_SIGNING_CERT_BASE64: ${{ secrets.SIGNING_CERT_BASE64 }} + WIN_SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - uses: actions/upload-artifact@v3 with: path: | @@ -256,6 +260,8 @@ jobs: - run: cd ${{ github.workspace }}/build; python build.py debug env: PYTHONPATH: ${{ github.workspace }} + WIN_SIGNING_CERT_BASE64: ${{ secrets.SIGNING_CERT_BASE64 }} + WIN_SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - uses: actions/upload-artifact@v3 with: path: | diff --git a/build/build.py b/build/build.py index 1bf8d6531..da833dd11 100644 --- a/build/build.py +++ b/build/build.py @@ -61,6 +61,8 @@ # also no need for filename suffix FILENAME_SUFFIX = '' +# when run by GitHub Actions with PFX and password as environment variables +# signing will be done SIGNING = False if 'WIN_SIGNING_CERT_BASE64' in environ \ and 'WIN_SIGNING_PASSWORD' in environ: @@ -72,11 +74,6 @@ def winmain(): execute steps necessary for compilation of Windows binaries and setup.exe """ - - if SIGNING: - subprocess.call(['powershell', './windows/code_signing.ps1', 'build/Nagstamon/Nagstamon.exe']) - sys.exit(0) - # InnoSetup does not like VersionInfoVersion with letters, only 0.0.0.0 schemed numbers if 'alpha' in VERSION.lower() or 'beta' in VERSION.lower() or 'rc' in VERSION.lower() or '-' in VERSION.lower(): VERSION_IS = VERSION.replace('alpha', '').replace('beta', '').replace('rc', '').replace('-', '.').replace('..', @@ -122,6 +119,10 @@ def winmain(): '..\\nagstamon.py'], shell=True) + if SIGNING: + # environment variables will be used by powershell script for signing + subprocess.call(['powershell', './windows/code_signing.ps1', 'build/Nagstamon/Nagstamon.exe']) + # rename output os.rename(DIR_BUILD_EXE, DIR_BUILD_NAGSTAMON) @@ -157,6 +158,9 @@ def winmain(): r'/O{0}{1}dist'.format(CURRENT_DIR, os.sep), r'{0}{1}windows{1}nagstamon.iss'.format(CURRENT_DIR, os.sep)], shell=True) + if SIGNING: + # environment variables will be used by powershell script for signing + subprocess.call(['powershell', '../windows/code_signing.ps1', '*.exe']) def macmain(): """ diff --git a/build/windows/code_signing.ps1 b/build/windows/code_signing.ps1 index 320642f7c..81f13d5d8 100644 --- a/build/windows/code_signing.ps1 +++ b/build/windows/code_signing.ps1 @@ -1,6 +1,11 @@ - +# get file to be signed from first argument $file = $args[0] + +# decode base64 PFX from environment variable $cert_buffer = [System.Convert]::FromBase64String($env:WIN_SIGNING_CERT_BASE64) +# open cert from PFX with password $cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::New($cert_buffer, $env:WIN_SIGNING_PASSWORD) -Set-AuthenticodeSignature -HashAlgorithm SHA256 -Certificate $cert -TimestampServer http://timestamp.digicert.com -FilePath $file \ No newline at end of file + +# finally sign the given file +Set-AuthenticodeSignature -HashAlgorithm SHA256 -Certificate $cert -TimestampServer http://timestamp.sectigo.com -FilePath $file \ No newline at end of file From 3a78330acda08755032e574fdff3a2154649271f Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Tue, 28 Mar 2023 23:17:30 +0200 Subject: [PATCH 563/884] signing works --- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 647d37699..348bf1a8a 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -121,7 +121,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.11-20230309' + VERSION = '3.11-20230328' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2023 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 650896330..4472ac3be 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,8 +1,8 @@ -nagstamon (3.11-20230309) unstable; urgency=low +nagstamon (3.11-20230328) unstable; urgency=low * New upstream - stuff - -- Henri Wahl <henri@nagstamon.de> Wed, Mar 08 2023 08:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Tue, Mar 28 2023 08:00:00 +0100 nagstamon (3.10.1) stable; urgency=low * New upstream From 97741424c99ffb0d455fe21f18bc2e46347ded6e Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 30 Mar 2023 23:20:55 +0200 Subject: [PATCH 564/884] raise dialog --- Nagstamon/Config.py | 2 +- Nagstamon/QUI/__init__.py | 2 ++ build/debian/changelog | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 348bf1a8a..3e8620bb4 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -121,7 +121,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.11-20230328' + VERSION = '3.11-20230330' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2023 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 06d511e34..17c529842 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -4586,6 +4586,8 @@ def show(self, tab=0): # reset window if only needs smaller screen estate self.window.adjustSize() self.window.show() + # make sure dialog window will be the topmost + self.window.raise_() def toggle_visibility(self, checkbox, widgets=[]): """ diff --git a/build/debian/changelog b/build/debian/changelog index 4472ac3be..278ccc2d6 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,8 +1,8 @@ -nagstamon (3.11-20230328) unstable; urgency=low +nagstamon (3.11-20230330) unstable; urgency=low * New upstream - stuff - -- Henri Wahl <henri@nagstamon.de> Tue, Mar 28 2023 08:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Thu, Mar 30 2023 08:00:00 +0100 nagstamon (3.10.1) stable; urgency=low * New upstream From 0a72cd1ba4f6774f153e0dd0b9ff8fde97e84b0e Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 30 Mar 2023 23:26:29 +0200 Subject: [PATCH 565/884] added fedora-38 --- .github/workflows/build-release-latest.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 91cc87be7..b9e761104 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -65,7 +65,7 @@ jobs: retention-days: 1 if-no-files-found: error - fedora-34: + fedora-35: runs-on: ubuntu-latest needs: test steps: @@ -87,7 +87,7 @@ jobs: retention-days: 1 if-no-files-found: error - fedora-35: + fedora-36: runs-on: ubuntu-latest needs: test steps: @@ -109,7 +109,7 @@ jobs: retention-days: 1 if-no-files-found: error - fedora-36: + fedora-37: runs-on: ubuntu-latest needs: test steps: @@ -131,7 +131,7 @@ jobs: retention-days: 1 if-no-files-found: error - fedora-37: + fedora-38: runs-on: ubuntu-latest needs: test steps: @@ -273,7 +273,7 @@ jobs: runs-on: ubuntu-latest # if not all are ready there might be trouble when downloading artifacts # maybe faster now with build containers - needs: [fedora-34, fedora-35, fedora-36, fedora-37, rhel-9] + needs: [fedora-35, fedora-36, fedora-37, fedora-38, rhel-9] env: family: fedora steps: @@ -299,7 +299,7 @@ jobs: runs-on: ubuntu-latest # if not all are ready there might be trouble when downloading artifacts # maybe faster now with build containers - needs: [fedora-34, fedora-35, fedora-36, fedora-37, rhel-9, repo-rpm-fedora] + needs: [fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, repo-rpm-fedora] env: family: rhel steps: @@ -321,10 +321,9 @@ jobs: - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push - github-release: runs-on: ubuntu-latest - needs: [debian, fedora-34, fedora-35, fedora-36, fedora-37, rhel-9, macos, windows-32, windows-64, windows-64-debug] + needs: [debian, fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, macos, windows-32, windows-64, windows-64-debug] steps: - uses: actions/download-artifact@v3 - run: cd artifact && md5sum *agstamon* > md5sums.txt From edf14d0a2c15459f1bc92f6948d7265f699063a3 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 30 Mar 2023 23:31:06 +0200 Subject: [PATCH 566/884] added fedora-38 Dockerfile --- .../{Dockerfile-fedora-34 => Dockerfile-fedora-38} | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) rename build/docker/{Dockerfile-fedora-34 => Dockerfile-fedora-38} (66%) diff --git a/build/docker/Dockerfile-fedora-34 b/build/docker/Dockerfile-fedora-38 similarity index 66% rename from build/docker/Dockerfile-fedora-34 rename to build/docker/Dockerfile-fedora-38 index 8e6917664..6c21a1675 100644 --- a/build/docker/Dockerfile-fedora-34 +++ b/build/docker/Dockerfile-fedora-38 @@ -1,4 +1,4 @@ -FROM fedora:34 +FROM fedora:38 LABEL maintainer=henri@nagstamon.de RUN dnf -y install desktop-file-utils \ @@ -11,17 +11,14 @@ RUN dnf -y install desktop-file-utils \ python3-keyring \ python3-lxml \ python3-psutil \ - python3-qt5 \ - python3-qt5-devel \ + python3-pyqt6 \ + python3-pyqt6-devel \ python3-requests \ python3-requests-kerberos \ python3-SecretStorage \ - qt5-qtsvg \ - qt5-qtmultimedia \ + qt6-qtsvg \ + qt6-qtmultimedia \ rpm-build -# ugly workaround for legacy Qt5 on Fedora < 36 CMD cd /nagstamon/build && \ - sed -i s/pyqt6/pyqt5/g redhat/nagstamon.spec && \ - sed -i s/qt6/qt5/g redhat/nagstamon.spec && \ /usr/bin/python3 build.py From e37be439128e55a653e3ded5f63f9f075a8aaa1d Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Fri, 31 Mar 2023 09:31:02 +0200 Subject: [PATCH 567/884] macos dock icon activate --- Nagstamon/Config.py | 2 +- Nagstamon/QUI/__init__.py | 6 +++++- build/debian/changelog | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 3e8620bb4..c475940a8 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -121,7 +121,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.11-20230330' + VERSION = '3.11-20230331' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2023 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 17c529842..7aef90630 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -97,7 +97,6 @@ # make icon status in macOS dock accessible via NSApp, used by set_macos_dock_icon_visible() if OS == OS_MACOS: from AppKit import (NSApp, - NSBundle, NSApplicationPresentationDefault, NSApplicationPresentationHideDock) @@ -4588,6 +4587,11 @@ def show(self, tab=0): self.window.show() # make sure dialog window will be the topmost self.window.raise_() + # hidden dock icon on macOS needs extra activation + if OS == OS_MACOS and \ + conf.icon_in_systray and \ + conf.hide_macos_dock_icon: + NSApp.activateIgnoringOtherApps_(True) def toggle_visibility(self, checkbox, widgets=[]): """ diff --git a/build/debian/changelog b/build/debian/changelog index 278ccc2d6..534c3fef6 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,8 +1,8 @@ -nagstamon (3.11-20230330) unstable; urgency=low +nagstamon (3.11-20230331) unstable; urgency=low * New upstream - stuff - -- Henri Wahl <henri@nagstamon.de> Thu, Mar 30 2023 08:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Fri, Mar 31 2023 08:00:00 +0100 nagstamon (3.10.1) stable; urgency=low * New upstream From 772c63cc7e0c0c0d3d0f3aab8d4d34959b4f48fa Mon Sep 17 00:00:00 2001 From: nautics889 <cyberukr@gmail.com> Date: Wed, 12 Apr 2023 21:58:44 +0300 Subject: [PATCH 568/884] Fix: minor updates in Monitos4x.py module Remove unused variable `form_data` (the variable was declared redundantly). Rename the variable `type` to `type_` since the name `type` shadows name for the built-in function `type()` which isn't a good practice. --- Nagstamon/Servers/Monitos4x.py | 44 +++++++++++++--------------------- 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/Nagstamon/Servers/Monitos4x.py b/Nagstamon/Servers/Monitos4x.py index 75be4d918..7e139259d 100644 --- a/Nagstamon/Servers/Monitos4x.py +++ b/Nagstamon/Servers/Monitos4x.py @@ -164,8 +164,6 @@ def _get_status(self): # hosts try: - form_data = dict() - page = 1 # loop trough all api pages @@ -300,8 +298,6 @@ def _get_status(self): # services try: - form_data = dict() - page = 1 # loop trough all api pages @@ -442,19 +438,17 @@ def _set_recheck(self, host, service): :param host: String - Host name :param service: String - Service name """ - form_data = dict() - - type = 'host' + type_ = 'host' if service == '': uuid = self.hosts[host].uuid else: - type = 'serviceinstance' + type_ = 'serviceinstance' uuid = self.hosts[host].services[service].uuid if self.use_autologin is True: - self.session.post('{0}/api/{1}/{2}/reschedule?authtoken={3}'.format(self.monitor_url, type ,uuid, self.autologin_key)) + self.session.post('{0}/api/{1}/{2}/reschedule?authtoken={3}'.format(self.monitor_url, type_, uuid, self.autologin_key)) else: - self.session.post('{0}/api/{1}/{2}/reschedule'.format(self.monitor_url, type ,uuid)) + self.session.post('{0}/api/{1}/{2}/reschedule'.format(self.monitor_url, type_, uuid)) def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=None): """ @@ -469,9 +463,7 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi :param persistent: Bool - Persistent comment :param all_services: Optional[Array] - List of all services (filled only if 'Acknowledge all services on host' is set) """ - form_data = dict() - - type = 'host' + type_ = 'host' if all_services: # Host & all Services uuid = self.hosts[host].uuid @@ -485,15 +477,15 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi 'sticky': int(sticky), 'includeServices': 0}) else: # Service uuid = self.hosts[host].services[service].uuid - type = 'serviceinstance' + type_ = 'serviceinstance' form_data = json.dumps( {'comment': comment, 'notify': int(notify), 'persistent': int(persistent), 'sticky': int(sticky)}) if self.use_autologin is True: - self.session.post('{0}/api/{1}/{2}/acknowledge?authtoken={3}'.format(self.monitor_url, type ,uuid, self.autologin_key), data=form_data) + self.session.post('{0}/api/{1}/{2}/acknowledge?authtoken={3}'.format(self.monitor_url, type_ ,uuid, self.autologin_key), data=form_data) else: - self.session.post('{0}/api/{1}/{2}/acknowledge'.format(self.monitor_url, type ,uuid), data=form_data) + self.session.post('{0}/api/{1}/{2}/acknowledge'.format(self.monitor_url, type_,uuid), data=form_data) def _set_submit_check_result(self, host, service, state, comment, check_output, performance_data): """ @@ -508,9 +500,7 @@ def _set_submit_check_result(self, host, service, state, comment, check_output, """ state = state.upper() - form_data = dict() - - type = 'host' + type_ = 'host' if service == '': # Host uuid = self.hosts[host].uuid @@ -534,7 +524,7 @@ def _set_submit_check_result(self, host, service, state, comment, check_output, log.info('Setting UNREACHABLE to CRITICAL') state = 'CRITICAL' - type = 'serviceinstance' + type_ = 'serviceinstance' uuid = self.hosts[host].services[service].uuid state_number = self.STATES_MAPPING_REV['services'][state] @@ -547,9 +537,9 @@ def _set_submit_check_result(self, host, service, state, comment, check_output, {'exit_status': state_number, 'plugin_output': check_output, 'performance_data': performance_data}) if self.use_autologin is True: - self.session.post('{0}/api/{1}/{2}/checkresult?authtoken={3}'.format(self.monitor_url, type ,uuid, self.autologin_key), data=form_data) + self.session.post('{0}/api/{1}/{2}/checkresult?authtoken={3}'.format(self.monitor_url, type_ ,uuid, self.autologin_key), data=form_data) else: - self.session.post('{0}/api/{1}/{2}/checkresult'.format(self.monitor_url, type ,uuid), data=form_data) + self.session.post('{0}/api/{1}/{2}/checkresult'.format(self.monitor_url, type_ ,uuid), data=form_data) def _set_downtime(self, host, service, author, comment, fixed, start_time, end_time, hours, minutes): """ @@ -565,13 +555,11 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t :param hours: Integer - Flexible Downtime :param minutes: Integer - Flexible Downtime """ - form_data = dict() - if service == '': - type = 'sv_host' + type_ = 'sv_host' uuid = self.hosts[host].uuid else: - type = 'sv_service_status' + type_ = 'sv_service_status' uuid = self.hosts[host].services[service].uuid # Format start_time and end_time from user-friendly format to timestamp @@ -589,13 +577,13 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t form_data = json.dumps({'start': start_time, 'end': end_time, 'comment': comment, 'is_recurring': 'FALSE', 'includeServices': 'TRUE', 'includeChildren': 'FALSE', 'schedule_now': 'FALSE', - 'id': uuid, 'type': type}) + 'id': uuid, 'type': type_}) else: form_data = json.dumps({'start': start_time, 'end': end_time, 'comment': comment, 'is_recurring': 'FALSE', 'includeServices': 'TRUE', 'includeChildren': 'FALSE', 'schedule_now': 'FALSE', - 'id': uuid, 'duration': duration, 'type': type}) + 'id': uuid, 'duration': duration, 'type': type_}) if self.use_autologin is True: self.session.post('{0}/api/downtime?authtoken={1}'.format(self.monitor_url, self.autologin_key), data=form_data) From 445ff88b728f7828d75133da7436851d753e5b18 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Fri, 28 Apr 2023 23:49:48 +0200 Subject: [PATCH 569/884] prepare_3.12 --- .github/workflows/build-release-stable.yml | 188 +++++++++++++++++---- ChangeLog | 18 ++ Nagstamon/Config.py | 2 +- Nagstamon/QUI/__init__.py | 11 +- Nagstamon/resources/qui/dialog_about.ui | 31 +++- build/debian/changelog | 20 ++- build/requirements/linux.txt | 4 +- 7 files changed, 226 insertions(+), 48 deletions(-) diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index 8d4fc3b65..a128e173a 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -4,8 +4,9 @@ on: tags: 'v*' env: - python_win_version: 3.10.7 + python_win_version: 3.11.3 repo_dir: nagstamon-jekyll/docs/repo + cr_image: ghcr.io/henriwahl/build-nagstamon jobs: test: @@ -16,16 +17,18 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 + # somehow weird way to get the hash over the requirements to be aware if they changed + - id: requirements_hash + run: echo "HASH=$(md5sum build/requirements/linux.txt | cut -d\ -f1)" >> $GITHUB_OUTPUT + # docker login is needed for pushing the test image + - uses: docker/login-action@v2 with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - sudo apt-get install libdbus-1-dev libkrb5-dev - python -m pip install --upgrade pip - pip install pytest pylint wheel #flake8 - if [ -f build/requirements/linux.txt ]; then pip install -r build/requirements/linux.txt; fi + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + # if image defined by hash over requirements is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }}-${{ steps.requirements_hash.outputs.HASH }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }}-${{ steps.requirements_hash.outputs.HASH }} --build-arg VERSION=${{ matrix.python-version }} --build-arg REQUIREMENTS="$(cat build/requirements/linux.txt | base64 --wrap=0)" -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }}-${{ steps.requirements_hash.outputs.HASH }} # - name: Lint with flake8 # run: | # # stop the build if there are Python syntax errors or undefined names @@ -33,68 +36,113 @@ jobs: # # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide # flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - name: Test with unittest - run: | - python -m unittest tests/test_*.py + # using the tests in precompiled image makes them way faster instead of creating the test environment every time from scratch + run: docker run --rm -v $PWD:/src --workdir /src ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }}-${{ steps.requirements_hash.outputs.HASH }} python -m unittest tests/test_*.py debian: runs-on: ubuntu-latest needs: test steps: - uses: actions/checkout@v3 - - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + # docker login is needed for pushing the build image + - uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + # if image defined by hash over requirements is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }} + # building in precompiled image makes them way faster instead of creating the build environment every time from scratch + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon -e DEB_BUILD_OPTIONS=nocheck ${{ env.cr_image }}-${{ github.job }} - uses: actions/upload-artifact@v3 with: path: build/*.deb retention-days: 1 if-no-files-found: error - fedora-34: + fedora-35: runs-on: ubuntu-latest needs: test steps: - uses: actions/checkout@v3 - - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + # docker login is needed for pushing the build image + - uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + # if image defined by hash over requirements is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }} + # building in precompiled image makes them way faster instead of creating the build environment every time from scratch + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} - uses: actions/upload-artifact@v3 with: path: build/*.rpm retention-days: 1 if-no-files-found: error - fedora-35: + fedora-36: runs-on: ubuntu-latest needs: test steps: - uses: actions/checkout@v3 - - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + # docker login is needed for pushing the build image + - uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + # if image defined by hash over requirements is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }} + # building in precompiled image makes them way faster instead of creating the build environment every time from scratch + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} - uses: actions/upload-artifact@v3 with: path: build/*.rpm retention-days: 1 if-no-files-found: error - fedora-36: + fedora-37: runs-on: ubuntu-latest needs: test steps: - uses: actions/checkout@v3 - - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + # docker login is needed for pushing the build image + - uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + # if image defined by hash over requirements is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }} + # building in precompiled image makes them way faster instead of creating the build environment every time from scratch + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} - uses: actions/upload-artifact@v3 with: path: build/*.rpm retention-days: 1 if-no-files-found: error - fedora-37: + fedora-38: runs-on: ubuntu-latest needs: test steps: - uses: actions/checkout@v3 - - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + # docker login is needed for pushing the build image + - uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + # if image defined by hash over requirements is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }} + # building in precompiled image makes them way faster instead of creating the build environment every time from scratch + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} - uses: actions/upload-artifact@v3 with: path: build/*.rpm @@ -106,8 +154,17 @@ jobs: needs: test steps: - uses: actions/checkout@v3 - - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + # docker login is needed for pushing the build image + - uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + # if image defined by hash over requirements is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }} + # building in precompiled image makes them way faster instead of creating the build environment every time from scratch + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} - uses: actions/upload-artifact@v3 with: path: build/*.rpm @@ -147,6 +204,8 @@ jobs: - run: cd ${{ github.workspace }}/build; python build.py env: PYTHONPATH: ${{ github.workspace }} + WIN_SIGNING_CERT_BASE64: ${{ secrets.SIGNING_CERT_BASE64 }} + WIN_SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - uses: actions/upload-artifact@v3 with: path: | @@ -171,6 +230,8 @@ jobs: - run: cd ${{ github.workspace }}/build; python build.py env: PYTHONPATH: ${{ github.workspace }} + WIN_SIGNING_CERT_BASE64: ${{ secrets.SIGNING_CERT_BASE64 }} + WIN_SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - uses: actions/upload-artifact@v3 with: path: | @@ -179,10 +240,64 @@ jobs: retention-days: 1 if-no-files-found: error - repo-fedora: + windows-64-debug: + # better depend on stable build image + runs-on: windows-2019 + needs: test + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 + with: + python-version: ${{ env.python_win_version }} + architecture: x64 + - run: python -m pip install --no-warn-script-location -r build/requirements/windows.txt + # pretty hacky but no other idea to avoid gssapi being installed which breaks requests-kerberos + - run: python -m pip uninstall -y gssapi requests-gssapi + - run: cd ${{ github.workspace }}/build; python build.py debug + env: + PYTHONPATH: ${{ github.workspace }} + WIN_SIGNING_CERT_BASE64: ${{ secrets.SIGNING_CERT_BASE64 }} + WIN_SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} + - uses: actions/upload-artifact@v3 + with: + path: | + build/dist/*.zip + retention-days: 1 + if-no-files-found: error + + repo-rpm-fedora: + runs-on: ubuntu-latest + # if not all are ready there might be trouble when downloading artifacts + # maybe faster now with build containers + needs: [fedora-35, fedora-36, fedora-37, fedora-38, rhel-9] + env: + family: fedora + steps: + # get binaries created by other jobs + - uses: actions/download-artifact@v3 + # organize SSH deploy key for nagstamon-repo + - run: mkdir ~/.ssh + - run: echo "${{ secrets.NAGSTAMON_REPO_KEY_WEB }}" > ~/.ssh/id_ed25519 + - run: chmod -R go-rwx ~/.ssh + # get and prepare nagstamon-jekyll + - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git + - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/latest + - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/latest + # copy *.rpm files into nagstamon-jekyll + - run: cp -r artifact/*.${{ env.family }}*.rpm ${{ env.repo_dir }}/${{ env.family }}/latest + # create rpm repo via Fedora container + - run: docker run --rm -v $PWD/${{ env.repo_dir }}/${{ env.family }}/latest:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo" + # commit and push new binaries to nagstamon-repo + - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" + - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push + + repo-rpm-rhel: runs-on: ubuntu-latest # if not all are ready there might be trouble when downloading artifacts - needs: [debian, fedora-34, fedora-35, fedora-36, fedora-37, macos, windows-32, windows-64] + # maybe faster now with build containers + needs: [fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, repo-rpm-fedora] + env: + family: rhel steps: # get binaries created by other jobs - uses: actions/download-artifact@v3 @@ -192,16 +307,19 @@ jobs: - run: chmod -R go-rwx ~/.ssh # get and prepare nagstamon-jekyll - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - - run: rm -rf ${{ env.repo_dir }}/fedora/?? - # copy *.rpm files into nagstamon-jekyll and create rpm repo via Fedora container - - run: for noarch_rpm in artifact/*.noarch.rpm; do version=$(echo $noarch_rpm | python3 -c "file=input(); print(file.split('fedora')[1].split('-')[0])"); mkdir -p mkdir -p ${{ env.repo_dir }}/fedora/$version; cp -r artifact/*.fedora$version-*.rpm ${{ env.repo_dir }}/fedora/$version; docker run --rm -v $PWD/${{ env.repo_dir }}/fedora/$version:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo"; done + - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/latest + - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/latest + # copy *.rpm files into nagstamon-jekyll + - run: cp -r artifact/*.${{ env.family }}*.rpm ${{ env.repo_dir }}/${{ env.family }}/latest + # create rpm repo via Fedora container + - run: docker run --rm -v $PWD/${{ env.repo_dir }}/${{ env.family }}/latest:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo" # commit and push new binaries to nagstamon-repo - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - - run: cd ${{ env.repo_dir }} && git add . && git commit -am "new stable repo" && git push + - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push github-release: runs-on: ubuntu-latest - needs: [debian, fedora-34, fedora-35, fedora-36, fedora-37, rhel-9, macos, windows-32, windows-64] + needs: [debian, fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, macos, windows-32, windows-64, windows-64-debug] steps: - uses: actions/download-artifact@v3 - run: cd artifact && md5sum *agstamon* > md5sums.txt diff --git a/ChangeLog b/ChangeLog index dbece0a16..1ac9d7eef 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +nagstamon (3.11.20230428) unstable; urgency=low + * New upstream + - added option to hide dock icon on macOS when using systray + - added fallback GSSAPI usage when no Kerberos is available + - added usage of local certificate store on Windows + - added codesigning for Windows builds + - added support for RHEL9 + - added Windows debugging version + - fixed Wayland support without using EWMH + - fixes for Icinga + - fixes for Zabbix + - fixes for Centreon + - fixes for Opsview + - fixes for Monitos + - improved build pipeline on GitHub Actions + + -- Henri Wahl <henri@nagstamon.de> Fri, Apr 28 2023 08:00:00 +0100 + nagstamon (3.10.1) stable; urgency=low * New upstream - fixes Centreon flapping bug diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index c475940a8..61953a715 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -121,7 +121,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.11-20230331' + VERSION = '3.11-20230428' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2023 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 7aef90630..84832cbe6 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -6837,17 +6837,18 @@ class Dialog_About(Dialog): def __init__(self, dialog): Dialog.__init__(self, dialog) # first add the logo on top - no idea how to achive in Qt Designer - logo = QSvgWidget('{0}{1}nagstamon.svg'.format(RESOURCES, os.sep)) + logo = QSvgWidget(f'{RESOURCES}{os.sep}nagstamon.svg') logo.setFixedSize(100, 100) self.window.vbox_about.insertWidget(1, logo, 0, Qt.AlignmentFlag.AlignHCenter) # update version information - self.window.label_nagstamon.setText('<h1>{0} {1}</h1>'.format(AppInfo.NAME, AppInfo.VERSION)) + self.window.label_nagstamon.setText(f'<h1>{AppInfo.NAME} {AppInfo.VERSION}</h1>') self.window.label_nagstamon_long.setText('<h2>Nagios¹ status monitor for your desktop</2>') self.window.label_copyright.setText(AppInfo.COPYRIGHT) - self.window.label_website.setText('<a href={0}>{0}</a>'.format(AppInfo.WEBSITE)) + self.window.label_website.setText(f'<a href={AppInfo.WEBSITE}>{AppInfo.WEBSITE}</a>') self.window.label_website.setOpenExternalLinks(True) self.window.label_versions.setText(f'Python: {platform.python_version()}, Qt: {QT_VERSION_STR}') - self.window.label_footnote.setText('<small>¹ plus Checkmk, Op5, Icinga, Centreon and more</small>') + self.window.label_contribution.setText(f'<a href={AppInfo.WEBSITE}/contribution>Contribution</a> | <a href=https://paypal.me/nagstamon>Donation</a>') + self.window.label_footnote.setText('<small>¹ meanwhile way more...</small>') # fill in license information license_file = open('{0}{1}LICENSE'.format(RESOURCES, os.sep)) @@ -6857,7 +6858,7 @@ def __init__(self, dialog): self.window.textedit_license.setReadOnly(True) # fill in credits information - credits_file = open('{0}{1}CREDITS'.format(RESOURCES, os.sep), encoding='utf-8') + credits_file = open(f'{RESOURCES}{os.sep}CREDITS', encoding='utf-8') credits = credits_file.read() credits_file.close() self.window.textedit_credits.setText(credits) diff --git a/Nagstamon/resources/qui/dialog_about.ui b/Nagstamon/resources/qui/dialog_about.ui index 82adcc210..a098f1a86 100644 --- a/Nagstamon/resources/qui/dialog_about.ui +++ b/Nagstamon/resources/qui/dialog_about.ui @@ -90,6 +90,9 @@ <property name="alignment"> <set>Qt::AlignCenter</set> </property> + <property name="openExternalLinks"> + <bool>true</bool> + </property> </widget> </item> <item> @@ -103,7 +106,33 @@ </widget> </item> <item> - <spacer name="verticalSpacer"> + <spacer name="verticalSpacer_3"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="label_contribution"> + <property name="text"> + <string>Contribution | Donation </string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <property name="openExternalLinks"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer_1"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> diff --git a/build/debian/changelog b/build/debian/changelog index 534c3fef6..ab1cc3409 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,8 +1,20 @@ -nagstamon (3.11-20230331) unstable; urgency=low +nagstamon (3.11.20230428) unstable; urgency=low * New upstream - - stuff - - -- Henri Wahl <henri@nagstamon.de> Fri, Mar 31 2023 08:00:00 +0100 + - added option to hide dock icon on macOS when using systray + - added fallback GSSAPI usage when no Kerberos is available + - added usage of local certificate store on Windows + - added codesigning for Windows builds + - added support for RHEL9 + - added Windows debugging version + - fixed Wayland support without using EWMH + - fixes for Icinga + - fixes for Zabbix + - fixes for Centreon + - fixes for Opsview + - fixes for Monitos + - improved build pipeline on GitHub Actions + + -- Henri Wahl <henri@nagstamon.de> Fri, Apr 28 2023 08:00:00 +0100 nagstamon (3.10.1) stable; urgency=low * New upstream diff --git a/build/requirements/linux.txt b/build/requirements/linux.txt index 6295f165e..63b8646b2 100644 --- a/build/requirements/linux.txt +++ b/build/requirements/linux.txt @@ -4,8 +4,8 @@ dbus-python keyring lxml psutil -pyqt6==6.3.1 -pyqt6-qt6==6.3.1 +pyqt6==6.4.2 +pyqt6-qt6==6.4.2 pysocks python-dateutil requests From 30b902af2d9053fba3e98fdfaaeadf9eda89f575 Mon Sep 17 00:00:00 2001 From: nautics889 <cyberukr@gmail.com> Date: Sat, 29 Apr 2023 22:19:40 +0300 Subject: [PATCH 570/884] Fix: minor fixes; code styling Simplified condition statements in python-modules of Nagstamon/Servers. --- Nagstamon/Servers/Centreon/CentreonAPI.py | 14 ++-- Nagstamon/Servers/Centreon/CentreonLegacy.py | 72 ++++++++++---------- Nagstamon/Servers/Icinga.py | 24 +++---- Nagstamon/Servers/IcingaDBWeb.py | 4 +- Nagstamon/Servers/IcingaWeb2.py | 4 +- Nagstamon/Servers/Multisite.py | 12 ++-- Nagstamon/Servers/Opsview.py | 4 +- Nagstamon/Servers/Thruk.py | 10 +-- Nagstamon/Servers/Zabbix.py | 2 +- Nagstamon/Servers/ZabbixProblemBased.py | 2 +- Nagstamon/Servers/op5Monitor.py | 4 +- 11 files changed, 76 insertions(+), 76 deletions(-) diff --git a/Nagstamon/Servers/Centreon/CentreonAPI.py b/Nagstamon/Servers/Centreon/CentreonAPI.py index c20d72b31..ee50a635c 100644 --- a/Nagstamon/Servers/Centreon/CentreonAPI.py +++ b/Nagstamon/Servers/Centreon/CentreonAPI.py @@ -167,7 +167,7 @@ def get_token(self): # ID of the user is needed by some requests user_id = data["contact"]["id"] - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server='[' + self.get_name() + ']', debug='API login : ' + self.username + ' / ' + self.password + ' > Token : ' + token + ' > User ID : ' + str( user_id)) @@ -200,7 +200,7 @@ def GetHost(self, host): fqdn = str(data["result"][0]["fqdn"]) - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server='[' + self.get_name() + ']', debug='Get Host FQDN or address : ' + host + " / " + fqdn) @@ -235,7 +235,7 @@ def get_host_and_service_id(self, host, service=''): host_id = data["result"][0]["id"] - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server='[' + self.get_name() + ']', debug='Get Host ID : ' + host + " / " + str(host_id)) return host_id @@ -273,7 +273,7 @@ def get_host_and_service_id(self, host, service=''): host_id = data["result"][0]["parent"]["id"] service_id = data["result"][0]["id"] - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server='[' + self.get_name() + ']', debug='Get Host / Service ID : ' + str(host_id) + " / " + str(service_id)) return host_id, service_id @@ -719,11 +719,11 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t return Result(result=result, error=error) def check_session(self): - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server='[' + self.get_name() + ']', debug='Checking session status') try: - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server='[' + self.get_name() + ']', debug='Check-session, the token expire if not been used for more than one hour. Current Token = ' + str( self.token)) @@ -742,7 +742,7 @@ def check_session(self): if result.status_code in ressources_response_list: self.token = self.get_token().result - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server='[' + self.get_name() + ']', debug='Check-session, session renewed') result = self.FetchURL(self.urls_centreon['resources'] + '?' + urllib.parse.urlencode(cgi_data), giveback="raw") diff --git a/Nagstamon/Servers/Centreon/CentreonLegacy.py b/Nagstamon/Servers/Centreon/CentreonLegacy.py index 21d9f542a..0482ff014 100644 --- a/Nagstamon/Servers/Centreon/CentreonLegacy.py +++ b/Nagstamon/Servers/Centreon/CentreonLegacy.py @@ -236,14 +236,14 @@ def _get_sid(self): ''' try: # Aulogin with key, BROWSER_URLS needs the key - if self.use_autologin == True: + if self.use_autologin: auth = '&autologin=1&useralias=' + self.username + '&token=' + self.autologin_key self.BROWSER_URLS= { 'monitor': self.BROWSER_URLS['monitor'] + auth,\ 'hosts': self.BROWSER_URLS['hosts'] + auth,\ 'services': self.BROWSER_URLS['services'] + auth,\ 'history': self.BROWSER_URLS['history'] + auth} raw = self.FetchURL(self.monitor_cgi_url + '/index.php?p=101&autologin=1&useralias=' + self.username + '&token=' + self.autologin_key, giveback='raw') - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), debug='Autologin : ' + self.username + ' : ' + self.autologin_key) # Gathering of the token who will be used to interact with Centreon (start with 2.66) if self.centreon_version >= 2.66 and self.centreon_version < 19.04: @@ -269,11 +269,11 @@ def _get_sid(self): else: login_data = {"useralias" : self.username, "password" : self.password, "submit" : "Login"} raw = self.FetchURL(self.monitor_cgi_url + "/index.php",cgi_data=login_data, giveback="raw") - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), debug='Password login : ' + self.username + ' : ' + self.password) sid = self.session.cookies.get('PHPSESSID', '') - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), debug='SID : ' + sid) if self.centreon_version >= 2.66 and self.centreon_version < 19.04: self.Debug(server=self.get_name(), debug='Centreon Token : ' + self.centreon_token) @@ -364,12 +364,12 @@ def GetHost(self, host): ip = str(xmlobj.l.a.text) # when connection by DNS is not configured do it by IP try: - if conf.connect_by_dns == True: + if conf.connect_by_dns: # try to get DNS name for ip (reverse DNS), if not available use ip try: address = socket.gethostbyaddr(ip)[0] except: - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), debug='Unable to do a reverse DNS lookup on IP: ' + ip) address = ip else: @@ -385,7 +385,7 @@ def GetHost(self, host): del xmlobj # print IP in debug mode - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), debug='IP of %s:' % (host) + ' ' + address) # give back host or ip @@ -408,23 +408,23 @@ def _get_xml_path(self, sid): if error == '': if re.search('var _addrXML.*xml\/ndo\/host', raw): self.XML_PATH = 'xml/ndo' - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), debug='Detected broker : NDO') elif re.search('var _addrXML.*xml\/broker\/host', raw): self.XML_PATH = 'xml/broker' - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), debug='Detected broker : C. Broker') else: - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), debug='Could not detect the broker for Centeron 2.[3-6]. Using Centreon Broker') self.XML_PATH = 'xml/broker' del raw else: - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), debug='Unable to fetch the main page to detect the broker : ' + error) del result, error else: - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), debug='Only Centreon Broker is supported in Centeon >= 2.7 -> XML_PATH='+ self.XML_PATH) @@ -488,7 +488,7 @@ def _define_url(self): # 18.10 and beyond elif self.centreon_version >= 18.10: self.urls_centreon = urls_centreon_18_10 - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), debug='URLs defined for Centreon %s' % (self.centreon_version)) @@ -510,7 +510,7 @@ def _get_host_id(self, host): host_id = raw.partition("var host_id = '")[2].partition("'")[0] del raw else: - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), debug='Host ID could not be retrieved.') # some cleanup @@ -519,7 +519,7 @@ def _get_host_id(self, host): # only if host_id is an usable integer return it try: if int(host_id): - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), host=host, debug='Host ID is ' + host_id) return host_id else: @@ -545,10 +545,10 @@ def _get_host_and_service_id(self, host, service): host_id = raw.partition("var host_id = '")[2].partition("'")[0] svc_id = raw.partition("var svc_id = '")[2].partition("'")[0] del raw - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), host=host, service=service, debug='- Get host/svc ID : ' + host_id + '/' + svc_id) else: - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), host=host, service=service, debug='- IDs could not be retrieved.') # some cleanup @@ -557,7 +557,7 @@ def _get_host_and_service_id(self, host, service): # only if host_id is an usable integer return it try: if int(host_id) and int(svc_id): - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), host=host, service=service, debug='- Host & Service ID are valid (int)') return host_id,svc_id else: @@ -604,19 +604,19 @@ def _get_status(self): errors_occured = self.check_for_error(xmlobj, error, status_code) # if there are errors return them - if errors_occured != False: + if errors_occured: return(errors_occured) # Check if the result is not empty if len(xmlobj) == 0: - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), debug='Empty host XML result') return Result(result=None, error="Empty host XML result") # in case there are no children session ID is expired if xmlobj.text.lower() == 'bad session id': del xmlobj - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), debug='Bad session ID, retrieving new one...') # try again... @@ -625,12 +625,12 @@ def _get_status(self): xmlobj, error, status_code = result.result, result.error, result.status_code errors_occured = self.check_for_error(xmlobj, error, status_code) # if there are errors return them - if errors_occured != False: + if errors_occured: return(errors_occured) # a second time a bad session id should raise an error if xmlobj.text.lower() == 'bad session id': - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), debug='Even after renewing session ID, unable to get the XML') return Result(result='ERROR', error='Bad session ID', @@ -690,19 +690,19 @@ def _get_status(self): # check if any error occured errors_occured = self.check_for_error(xmlobj, error, status_code) # if there are errors return them - if errors_occured != False: + if errors_occured: return(errors_occured) # Check if the result is not empty if len(xmlobj) == 0: - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), debug='Empty service XML result') return Result(result=None, error="Empty service XML result") # in case there are no children session id is invalid if xmlobj.text.lower() == 'bad session id': # debug - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), debug='Bad session ID, retrieving new one...') # try again... self.SID = self._get_sid().result @@ -710,7 +710,7 @@ def _get_status(self): xmlobj, error, status_code = result.result, result.error, result.status_code errors_occured = self.check_for_error(xmlobj, error, status_code) # if there are errors return them - if errors_occured != False: + if errors_occured: return(errors_occured) # a second time a bad session id should raise an error @@ -735,12 +735,12 @@ def _get_status(self): errors_occured = self.check_for_error(xmlobj_meta, error_meta, status_code_meta) # if there are errors return them - if errors_occured != False: + if errors_occured: return(errors_occured) # a second time a bad session id should raise an error if xmlobj_meta.text.lower() == 'bad session id': - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), debug='Even after renewing session ID, unable to get the XML') return Result(result='ERROR', @@ -801,7 +801,7 @@ def _get_status(self): self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_type =\ self.HARD_SOFT[self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_type] - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), debug='Parsing service XML (Host/Service/Status_type) ' + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].host + '/' + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].name + '/' + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_type) self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].last_check = str(l.lc.text) self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].duration = str(l.d.text) @@ -931,7 +931,7 @@ def _set_recheck(self, host, service): # decision about host or service - they have different URLs # Meta if host == '_Module_Meta': - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), debug='Recheck on a Meta service, more work to be done') m = re.search(r'^.+ \((?P<rsd>.+)\)$', service) if m: @@ -1078,7 +1078,7 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t def _check_session(self): - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), debug='Checking session status') if 'url_centreon' not in self.__dict__: self.init_config() @@ -1088,23 +1088,23 @@ def _check_session(self): result = self.FetchURL(self.urls_centreon['keepAlive'], giveback='raw') self.raw, self.error, self.status_code = result.result, result.error, result.status_code # Return 200 & null a session is open - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), debug='Session status : ' + self.raw + ', http code : ' + str(self.status_code)) # 401 if no valid session is present if self.status_code == 401: self.SID = self._get_sid().result - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), debug='Session renewed') else: result = self.FetchURL(self.urls_centreon['autologoutXMLresponse'], giveback='xml') xmlobj, error, status_code = result.result, result.error, result.status_code self.session_state = xmlobj.find("state").text.lower() - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), debug='Session status : ' + self.session_state) if self.session_state == "nok": self.SID = self._get_sid().result - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), debug='Session renewed') except: import traceback diff --git a/Nagstamon/Servers/Icinga.py b/Nagstamon/Servers/Icinga.py index 99d1ae89c..ccbc0a3f4 100644 --- a/Nagstamon/Servers/Icinga.py +++ b/Nagstamon/Servers/Icinga.py @@ -123,13 +123,13 @@ def _get_status(self): # hosts (up or down or unreachable) self.cgiurl_hosts = {'hard': self.monitor_cgi_url + '/status.cgi?style=hostdetail&hoststatustypes=12&hostprops=262144', \ 'soft': self.monitor_cgi_url + '/status.cgi?style=hostdetail&hoststatustypes=12&hostprops=524288'} - if self.json == True: + if self.json: for status_type in 'hard', 'soft': self.cgiurl_services[status_type] += '&jsonoutput' self.cgiurl_hosts[status_type] += '&jsonoutput' # get status depending on JSONablility - if self.json == True: + if self.json: return(self._get_status_JSON()) else: return(self._get_status_HTML()) @@ -167,7 +167,7 @@ def _get_status_JSON(self): # check if any error occured errors_occured = self.check_for_error(jsonraw, error, status_code) # if there are errors return them - if errors_occured != False: + if errors_occured: return(errors_occured) jsondict = json.loads(jsonraw) @@ -178,7 +178,7 @@ def _get_status_JSON(self): h = dict(host.items()) # host - if self.use_display_name_host == False: + if not self.use_display_name_host: # according to http://sourceforge.net/p/nagstamon/bugs/83/ it might # better be host_name instead of host_display_name # legacy Icinga adjustments @@ -231,7 +231,7 @@ def _get_status_JSON(self): # check if any error occured errors_occured = self.check_for_error(jsonraw, error, status_code) # if there are errors return them - if errors_occured != False: + if errors_occured: return(errors_occured) jsondict = json.loads(jsonraw) @@ -241,7 +241,7 @@ def _get_status_JSON(self): # make dict of tuples for better reading s = dict(service.items()) - if self.use_display_name_host == False: + if not self.use_display_name_host: # according to http://sourceforge.net/p/nagstamon/bugs/83/ it might # better be host_name instead of host_display_name # legacy Icinga adjustments @@ -266,7 +266,7 @@ def _get_status_JSON(self): elif 'host' in s: self.new_hosts[host_name].real_name = s['host'] - if self.use_display_name_host == False: + if not self.use_display_name_host: # legacy Icinga adjustments if 'service_description' in s: service_name = s['service_description'] elif 'description' in s: service_name = s['description'] @@ -339,7 +339,7 @@ def _get_status_HTML(self): # check if any error occured errors_occured = self.check_for_error(htobj, error, status_code) # if there are errors return them - if errors_occured != False: + if errors_occured: return(errors_occured) # put a copy of a part of htobj into table to be able to delete htobj @@ -468,7 +468,7 @@ def _get_status_HTML(self): # check if any error occured errors_occured = self.check_for_error(htobj, error, status_code) # if there are errors return them - if errors_occured != False: + if errors_occured: return(errors_occured) table = htobj('table', {'class': 'status'})[0] @@ -670,11 +670,11 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi cgi_data['com_author'] = author cgi_data['com_data'] = comment cgi_data['btnSubmit'] = 'Commit' - if notify == True: + if notify: cgi_data['send_notification'] = '1' - if persistent == True: + if persistent: cgi_data['persistent'] = 'on' - if sticky == True: + if sticky: cgi_data['sticky_ack'] = '1' self.FetchURL(url, giveback='raw', cgi_data=cgi_data) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index da90b57b4..4bdc3c79c 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -191,7 +191,7 @@ def _get_status(self): h = dict(host.items()) # host - if self.use_display_name_host == False: + if not self.use_display_name_host: # according to http://sourceforge.net/p/nagstamon/bugs/83/ it might # better be name instead of display_name host_name = h['name'] @@ -272,7 +272,7 @@ def _get_status(self): # make dict of tuples for better reading s = dict(service.items()) - if self.use_display_name_host == False: + if not self.use_display_name_host: # according to http://sourceforge.net/p/nagstamon/bugs/83/ it might # better be name instead of display_name host_name = s['host']['name'] diff --git a/Nagstamon/Servers/IcingaWeb2.py b/Nagstamon/Servers/IcingaWeb2.py index 3a4627eb3..369b96993 100644 --- a/Nagstamon/Servers/IcingaWeb2.py +++ b/Nagstamon/Servers/IcingaWeb2.py @@ -189,7 +189,7 @@ def _get_status(self): h = dict(host.items()) # host - if self.use_display_name_host == False: + if not self.use_display_name_host: # according to http://sourceforge.net/p/nagstamon/bugs/83/ it might # better be host_name instead of host_display_name # legacy Icinga adjustments @@ -269,7 +269,7 @@ def _get_status(self): # make dict of tuples for better reading s = dict(service.items()) - if self.use_display_name_host == False: + if not self.use_display_name_host: # according to http://sourceforge.net/p/nagstamon/bugs/83/ it might # better be host_name instead of host_display_name # legacy Icinga adjustments diff --git a/Nagstamon/Servers/Multisite.py b/Nagstamon/Servers/Multisite.py index 7539da7ff..79bf74a7b 100644 --- a/Nagstamon/Servers/Multisite.py +++ b/Nagstamon/Servers/Multisite.py @@ -301,7 +301,7 @@ def _get_status(self): return Result(result=result, error=error) # Add filters to the url which should only be applied to the service request - if conf.filter_services_on_unreachable_hosts == True: + if conf.filter_services_on_unreachable_hosts: # thanks to https://github.com/HenriWahl/Nagstamon/issues/510 url_params += '&hst0=On&hst1=On' @@ -407,7 +407,7 @@ def open_monitor(self, host, service=''): else: url = self.urls['human_service'] + urllib.parse.urlencode({'x': 'site='+self.hosts[host].site+'&host='+host+'&service='+service}).replace('x=', '%26') - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), host=host, service=service, debug='Open host/service monitor web page ' + url) webbrowser_open(url) @@ -428,10 +428,10 @@ def GetHost(self, host): if host in self.hosts: ip = self.hosts[host].address - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), host=host, debug ='IP of %s:' % (host) + ' ' + ip) - if conf.connect_by_dns == True: + if conf.connect_by_dns: try: address = socket.gethostbyaddr(ip)[0] except: @@ -469,7 +469,7 @@ def _action(self, site, host, service, specific_params): transid = self._get_transid(host, service) url = url.replace('?_transid=-1&', '?_transid=%s&' % (transid)) - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), host=host, debug ='Submitting action: ' + url + '&' + urllib.parse.urlencode(params)) # apply action @@ -552,7 +552,7 @@ def recheck_all(self): params['_resched_checks'] = 'Reschedule active checks' url = self.urls['api_svcprob_act'] - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), debug ='Rechecking all action: ' + url + '&' + urllib.parse.urlencode(params)) result = self.FetchURL(url + '&' + urllib.parse.urlencode(params), giveback = 'raw') diff --git a/Nagstamon/Servers/Opsview.py b/Nagstamon/Servers/Opsview.py index b7e1534b1..fe47ea0ef 100644 --- a/Nagstamon/Servers/Opsview.py +++ b/Nagstamon/Servers/Opsview.py @@ -244,8 +244,8 @@ def _get_status(self): # check if any error occured errors_occured = self.check_for_error(data, error, status_code) # if there are errors return them - if errors_occured != False: - return(errors_occured) + if errors_occured: + return errors_occured if conf.debug_mode: self.Debug(server=self.get_name(), debug="Fetched JSON: " + pprint.pformat(data)) diff --git a/Nagstamon/Servers/Thruk.py b/Nagstamon/Servers/Thruk.py index 3bfda83b2..1d0b33959 100644 --- a/Nagstamon/Servers/Thruk.py +++ b/Nagstamon/Servers/Thruk.py @@ -117,7 +117,7 @@ def login(self): if self.use_autologin is True: req = self.session.post(self.monitor_cgi_url + '/user.cgi?', data={}, headers={'X-Thruk-Auth-Key':self.autologin_key.strip()}) - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), debug='Auto Login status: ' + req.url + ' http code : ' + str(req.status_code)) if req.status_code != 200: self.refresh_authentication = True @@ -129,7 +129,7 @@ def login(self): data={'login': self.get_username(), 'password': self.get_password(), 'submit': 'Login'}) - if conf.debug_mode == True: + if conf.debug_mode: self.Debug(server=self.get_name(), debug='Login status: ' + req.url + ' http code : ' + str(req.status_code)) if req.status_code != 200: self.refresh_authentication = True @@ -279,7 +279,7 @@ def _get_status(self): # check if any error occured errors_occured = self.check_for_error(jsonraw, error, status_code) # if there are errors return them - if errors_occured != False: + if errors_occured: return(errors_occured) # in case basic auth did not work try form login cookie based login @@ -327,7 +327,7 @@ def _get_status(self): # check if any error occured errors_occured = self.check_for_error(jsonraw, error, status_code) # if there are errors return them - if errors_occured != False: + if errors_occured: return(errors_occured) # in case basic auth did not work try form login cookie based login @@ -347,7 +347,7 @@ def _get_status(self): self.new_hosts[s["host_name"]].server = self.name self.new_hosts[s["host_name"]].status = "UP" - if self.use_display_name_service == True: + if self.use_display_name_service: entry = s["display_name"] else: entry = s["description"] diff --git a/Nagstamon/Servers/Zabbix.py b/Nagstamon/Servers/Zabbix.py index 1a948472f..a4bf338f2 100644 --- a/Nagstamon/Servers/Zabbix.py +++ b/Nagstamon/Servers/Zabbix.py @@ -185,7 +185,7 @@ def _get_status(self): # https://github.com/HenriWahl/Nagstamon/issues/826 Zabbix 5.0 may have an empty list for # the 'lastEvent' key if the trigger has no associated events for service in services: - if service['lastEvent'] == []: + if not service['lastEvent']: service['lastEvent'] = { 'eventid': -1, 'acknowledged': '0', diff --git a/Nagstamon/Servers/ZabbixProblemBased.py b/Nagstamon/Servers/ZabbixProblemBased.py index 02e78a89d..ffb216a3f 100644 --- a/Nagstamon/Servers/ZabbixProblemBased.py +++ b/Nagstamon/Servers/ZabbixProblemBased.py @@ -52,7 +52,7 @@ def do_request(self, method, params={}, no_auth=False): "id": self.zbx_req_id } - if no_auth == False: + if not no_auth: zabbix_rpc_obj["auth"] = self.zbx_auth self.zbx_req_id += 1 diff --git a/Nagstamon/Servers/op5Monitor.py b/Nagstamon/Servers/op5Monitor.py index b5fab964b..80f063c6e 100644 --- a/Nagstamon/Servers/op5Monitor.py +++ b/Nagstamon/Servers/op5Monitor.py @@ -151,7 +151,7 @@ def _get_status(self): # check if any error occured errors_occured = self.check_for_error(data, error, status_code) # if there are errors return them - if errors_occured != False: + if errors_occured: return(errors_occured) if data['count']: @@ -212,7 +212,7 @@ def _get_status(self): # check if any error occured errors_occured = self.check_for_error(data, error, status_code) # if there are errors return them - if errors_occured != False: + if errors_occured: return(errors_occured) if data['count']: From 97a977caa4273febde1a615abd083f0b2e95aed9 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 30 Apr 2023 18:17:28 +0200 Subject: [PATCH 571/884] play with debian repo --- .github/workflows/build-release-latest.yml | 35 ++++++++++++++++++++++ .github/workflows/build-release-stable.yml | 14 ++++----- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index b9e761104..e3f37f264 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -269,6 +269,41 @@ jobs: retention-days: 1 if-no-files-found: error +# borrowed from dhcpy6d for testing + repo-debian: + runs-on: ubuntu-latest + needs: [debian] + env: + family: debian + release: latest + steps: + - uses: actions/checkout@v2 + # get binaries created by other jobs + - uses: actions/download-artifact@v2 + + # make entrypoints executable + - run: chmod +x build/entrypoint-*.sh + # get secret signing key + - run: echo "${{ secrets.PACKAGE_SIGNING_KEY }}" > signing_key.asc + # organize SSH deploy key for nagstamon-jekyll repo + - run: mkdir ~/.ssh + - run: echo "${{ secrets.NAGSTAMON_REPO_KEY_WEB }}" > ~/.ssh/id_ed25519 + - run: chmod -R go-rwx ~/.ssh + # get and prepare nagstamon-jekyll + - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git + - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/${{ env.dist }}/{{ env.release }} + - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${{ env.dist }}/{{ env.release }} + # execute container with matching entrypoint + - run: | + /usr/bin/docker run --volume ${{ github.workspace }}:/dhcpy6d \ + --volume ${{ github.workspace }}/build/entrypoint-${{ github.job }}.sh:/entrypoint.sh \ + --entrypoint /entrypoint.sh \ + --env RELEASE=${{ env.release }} \ + ${{ github.job }} + # commit and push new binaries to dhcpyd-jekyll + - run: git config --global user.email "repo@dnagstamon.de" && git config --global user.name "Nagstamon Repository" + - run: cd ${{ env.repo_dir }} && git add . && git commit -am "new ${{ env.release }} repo ${{ env.family }}" && git push + repo-rpm-fedora: runs-on: ubuntu-latest # if not all are ready there might be trouble when downloading artifacts diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index a128e173a..5d1159978 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -281,10 +281,9 @@ jobs: - run: chmod -R go-rwx ~/.ssh # get and prepare nagstamon-jekyll - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/latest - - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/latest + - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/?? # copy *.rpm files into nagstamon-jekyll - - run: cp -r artifact/*.${{ env.family }}*.rpm ${{ env.repo_dir }}/${{ env.family }}/latest + - run: for noarch_rpm in artifact/*.noarch.rpm; do version=$(echo $noarch_rpm | python3 -c "file=input(); print(file.split('fedora')[1].split('-')[0])"); mkdir -p mkdir -p ${{ env.repo_dir }}/fedora/$version; cp -r artifact/*.fedora$version-*.rpm ${{ env.repo_dir }}/fedora/$version; docker run --rm -v $PWD/${{ env.repo_dir }}/fedora/$version:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo"; done # create rpm repo via Fedora container - run: docker run --rm -v $PWD/${{ env.repo_dir }}/${{ env.family }}/latest:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo" # commit and push new binaries to nagstamon-repo @@ -298,6 +297,7 @@ jobs: needs: [fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, repo-rpm-fedora] env: family: rhel + version: 9 steps: # get binaries created by other jobs - uses: actions/download-artifact@v3 @@ -307,12 +307,12 @@ jobs: - run: chmod -R go-rwx ~/.ssh # get and prepare nagstamon-jekyll - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/latest - - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/latest + - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/${{ env.version }} + - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${{ env.version }} # copy *.rpm files into nagstamon-jekyll - - run: cp -r artifact/*.${{ env.family }}*.rpm ${{ env.repo_dir }}/${{ env.family }}/latest + - run: cp -r artifact/*.${{ env.family }}*.rpm ${{ env.repo_dir }}/${{ env.family }}/${{ env.version }} # create rpm repo via Fedora container - - run: docker run --rm -v $PWD/${{ env.repo_dir }}/${{ env.family }}/latest:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo" + - run: docker run --rm -v $PWD/${{ env.repo_dir }}/${{ env.family }}/${{ env.version }}:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo" # commit and push new binaries to nagstamon-repo - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push From c359fd5fd35999d15954426f6d55586b61169da1 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 30 Apr 2023 19:06:25 +0200 Subject: [PATCH 572/884] sign debian repo --- .github/workflows/build-release-latest.yml | 59 +++++++++++++--------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index e3f37f264..17ce0e545 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -3,9 +3,10 @@ on: push: tags-ignore: 'v*' branches: - - '**' - - '!master' - - '!*.*.*' + #- '**' + #- '!master' + #- '!*.*.*' + - 'debian_repo' env: python_win_version: 3.10.8 @@ -293,15 +294,23 @@ jobs: - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/${{ env.dist }}/{{ env.release }} - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${{ env.dist }}/{{ env.release }} - # execute container with matching entrypoint + # create deb repo via Debian build container - run: | - /usr/bin/docker run --volume ${{ github.workspace }}:/dhcpy6d \ - --volume ${{ github.workspace }}/build/entrypoint-${{ github.job }}.sh:/entrypoint.sh \ - --entrypoint /entrypoint.sh \ - --env RELEASE=${{ env.release }} \ - ${{ github.job }} - # commit and push new binaries to dhcpyd-jekyll - - run: git config --global user.email "repo@dnagstamon.de" && git config --global user.name "Nagstamon Repository" + /usr/bin/docker run --rm \ + -v ${{ github.workspace }}:/nagstamon \ + -v $PWD/${{ env.repo_dir }}/${{ env.family }}/{{ env.release }}:/repo ${{ env.cr_image }}-${{ env.family }} \ + /bin/sh -c "cd /nagstamon && \ + gpg --import signing_key.asc && \ + cd nagstamon-jekyll/docs/repo/{{ env.family }}/{{ env.release }} + cp -r artifact/*.deb . && \ + dpkg-scanpackages . > Packages && \ + gzip -k -f Packages && \ + apt-ftparchive release . > Release && \ + gpg -abs -o Release.gpg Release && \ + gpg --clearsign -o InRelease Release && \ + gpg --output key.gpg --armor --export" + # commit and push new binaries to nagstamon-jekyll + - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - run: cd ${{ env.repo_dir }} && git add . && git commit -am "new ${{ env.release }} repo ${{ env.family }}" && git push repo-rpm-fedora: @@ -356,17 +365,17 @@ jobs: - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push - github-release: - runs-on: ubuntu-latest - needs: [debian, fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, macos, windows-32, windows-64, windows-64-debug] - steps: - - uses: actions/download-artifact@v3 - - run: cd artifact && md5sum *agstamon* > md5sums.txt - - run: cd artifact && sha256sum *agstamon* > sha256sums.txt - - uses: marvinpinto/action-automatic-releases@latest - with: - repo_token: "${{ secrets.GITHUB_TOKEN }}" - automatic_release_tag: "latest" - prerelease: true - files: | - artifact/* +# github-release: +# runs-on: ubuntu-latest +# needs: [debian, fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, macos, windows-32, windows-64, windows-64-debug] +# steps: +# - uses: actions/download-artifact@v3 +# - run: cd artifact && md5sum *agstamon* > md5sums.txt +# - run: cd artifact && sha256sum *agstamon* > sha256sums.txt +# - uses: marvinpinto/action-automatic-releases@latest +# with: +# repo_token: "${{ secrets.GITHUB_TOKEN }}" +# automatic_release_tag: "latest" +# prerelease: true +# files: | +# artifact/* From 9d7c109073d8eee65c3cb3106963e796fbfc7207 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 30 Apr 2023 19:08:50 +0200 Subject: [PATCH 573/884] no entrypoint for debian repo --- .github/workflows/build-release-latest.yml | 3 --- build/requirements/macos.txt | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 17ce0e545..e0e6b42b1 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -281,9 +281,6 @@ jobs: - uses: actions/checkout@v2 # get binaries created by other jobs - uses: actions/download-artifact@v2 - - # make entrypoints executable - - run: chmod +x build/entrypoint-*.sh # get secret signing key - run: echo "${{ secrets.PACKAGE_SIGNING_KEY }}" > signing_key.asc # organize SSH deploy key for nagstamon-jekyll repo diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index a7ffe0283..7f8ea8e60 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -5,8 +5,8 @@ lxml psutil pyinstaller pyobjc-framework-ApplicationServices -pyqt6==6.3.1 -pyqt6-qt6==6.3.1 +pyqt6==6.4.2 +pyqt6-qt6==6.4.2 pysocks python-dateutil requests From 21784199c5b8240e283fe35c51fae1574bc8b093 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 30 Apr 2023 19:12:02 +0200 Subject: [PATCH 574/884] debian missing dollar --- .github/workflows/build-release-latest.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index e0e6b42b1..dd6f56f41 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -289,13 +289,13 @@ jobs: - run: chmod -R go-rwx ~/.ssh # get and prepare nagstamon-jekyll - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/${{ env.dist }}/{{ env.release }} - - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${{ env.dist }}/{{ env.release }} + - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/${{ env.dist }}/${{ env.release }} + - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${{ env.dist }}/${{ env.release }} # create deb repo via Debian build container - run: | /usr/bin/docker run --rm \ -v ${{ github.workspace }}:/nagstamon \ - -v $PWD/${{ env.repo_dir }}/${{ env.family }}/{{ env.release }}:/repo ${{ env.cr_image }}-${{ env.family }} \ + -v $PWD/${{ env.repo_dir }}/${{ env.family }}/${{ env.release }}:/repo ${{ env.cr_image }}-${{ env.family }} \ /bin/sh -c "cd /nagstamon && \ gpg --import signing_key.asc && \ cd nagstamon-jekyll/docs/repo/{{ env.family }}/{{ env.release }} From ef300f61ba746d6472a031f010fdfbf2f451445d Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 30 Apr 2023 19:12:57 +0200 Subject: [PATCH 575/884] no tests for debian --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index dd6f56f41..477e3a810 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -46,7 +46,7 @@ jobs: debian: runs-on: ubuntu-latest - needs: test + #needs: test steps: - uses: actions/checkout@v3 # docker login is needed for pushing the build image From 800815c7c001fee9ef8cf9292760e93f4e16b66e Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 30 Apr 2023 19:20:57 +0200 Subject: [PATCH 576/884] fix ci order --- .github/workflows/build-release-latest.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 477e3a810..41e7b40ab 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -294,12 +294,12 @@ jobs: # create deb repo via Debian build container - run: | /usr/bin/docker run --rm \ - -v ${{ github.workspace }}:/nagstamon \ + -v ${{ github.workspace }}:/workspace \ -v $PWD/${{ env.repo_dir }}/${{ env.family }}/${{ env.release }}:/repo ${{ env.cr_image }}-${{ env.family }} \ - /bin/sh -c "cd /nagstamon && \ + /bin/sh -c "cd /workspace && \ gpg --import signing_key.asc && \ + cp -r artifact/*.deb nagstamon-jekyll/docs/repo/{{ env.family }}/{{ env.release }} && \ cd nagstamon-jekyll/docs/repo/{{ env.family }}/{{ env.release }} - cp -r artifact/*.deb . && \ dpkg-scanpackages . > Packages && \ gzip -k -f Packages && \ apt-ftparchive release . > Release && \ From 061a8081d10a8e27c2a04ba9c1a2be7b001ef712 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 30 Apr 2023 19:24:24 +0200 Subject: [PATCH 577/884] more $ --- .github/workflows/build-release-latest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 41e7b40ab..e7a94a6e1 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -298,8 +298,8 @@ jobs: -v $PWD/${{ env.repo_dir }}/${{ env.family }}/${{ env.release }}:/repo ${{ env.cr_image }}-${{ env.family }} \ /bin/sh -c "cd /workspace && \ gpg --import signing_key.asc && \ - cp -r artifact/*.deb nagstamon-jekyll/docs/repo/{{ env.family }}/{{ env.release }} && \ - cd nagstamon-jekyll/docs/repo/{{ env.family }}/{{ env.release }} + cp -r artifact/*.deb nagstamon-jekyll/docs/repo/${{ env.family }}/${{ env.release }} && \ + cd nagstamon-jekyll/docs/repo/${{ env.family }}/${{ env.release }} dpkg-scanpackages . > Packages && \ gzip -k -f Packages && \ apt-ftparchive release . > Release && \ From dce7928439b7b058e35e3fa8e0b1695cceac3a5e Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 30 Apr 2023 19:51:16 +0200 Subject: [PATCH 578/884] debian repo latest ready --- .github/workflows/build-release-latest.yml | 41 +++++++++++----------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index e7a94a6e1..2fb6bfa05 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -3,10 +3,9 @@ on: push: tags-ignore: 'v*' branches: - #- '**' - #- '!master' - #- '!*.*.*' - - 'debian_repo' + - '**' + - '!master' + - '!*.*.*' env: python_win_version: 3.10.8 @@ -46,7 +45,7 @@ jobs: debian: runs-on: ubuntu-latest - #needs: test + needs: test steps: - uses: actions/checkout@v3 # docker login is needed for pushing the build image @@ -314,7 +313,7 @@ jobs: runs-on: ubuntu-latest # if not all are ready there might be trouble when downloading artifacts # maybe faster now with build containers - needs: [fedora-35, fedora-36, fedora-37, fedora-38, rhel-9] + needs: [fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, repo-debian] env: family: fedora steps: @@ -340,7 +339,7 @@ jobs: runs-on: ubuntu-latest # if not all are ready there might be trouble when downloading artifacts # maybe faster now with build containers - needs: [fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, repo-rpm-fedora] + needs: [fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, repo-rpm-fedora, repo-debian] env: family: rhel steps: @@ -362,17 +361,17 @@ jobs: - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push -# github-release: -# runs-on: ubuntu-latest -# needs: [debian, fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, macos, windows-32, windows-64, windows-64-debug] -# steps: -# - uses: actions/download-artifact@v3 -# - run: cd artifact && md5sum *agstamon* > md5sums.txt -# - run: cd artifact && sha256sum *agstamon* > sha256sums.txt -# - uses: marvinpinto/action-automatic-releases@latest -# with: -# repo_token: "${{ secrets.GITHUB_TOKEN }}" -# automatic_release_tag: "latest" -# prerelease: true -# files: | -# artifact/* + github-release: + runs-on: ubuntu-latest + needs: [debian, fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, macos, windows-32, windows-64, windows-64-debug] + steps: + - uses: actions/download-artifact@v3 + - run: cd artifact && md5sum *agstamon* > md5sums.txt + - run: cd artifact && sha256sum *agstamon* > sha256sums.txt + - uses: marvinpinto/action-automatic-releases@latest + with: + repo_token: "${{ secrets.GITHUB_TOKEN }}" + automatic_release_tag: "latest" + prerelease: true + files: | + artifact/* From 671ec90389f4d12d3a3d9891cf02425c382bc1ab Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 30 Apr 2023 19:53:16 +0200 Subject: [PATCH 579/884] python_win_version: 3.11.3 --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 2fb6bfa05..8b7afa2f7 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -8,7 +8,7 @@ on: - '!*.*.*' env: - python_win_version: 3.10.8 + python_win_version: 3.11.3 repo_dir: nagstamon-jekyll/docs/repo cr_image: ghcr.io/henriwahl/build-nagstamon From 885682e1a2be06c45c9d9da01ff8a1cf70051675 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 30 Apr 2023 20:41:39 +0200 Subject: [PATCH 580/884] checkout + download v3 --- .github/workflows/build-release-latest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 8b7afa2f7..957175205 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -277,9 +277,9 @@ jobs: family: debian release: latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 # get binaries created by other jobs - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 # get secret signing key - run: echo "${{ secrets.PACKAGE_SIGNING_KEY }}" > signing_key.asc # organize SSH deploy key for nagstamon-jekyll repo From bd38137a68439dd50c391b32d114dbf95129d965 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 30 Apr 2023 20:44:59 +0200 Subject: [PATCH 581/884] repo-debian wait.... --- .github/workflows/build-release-latest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 957175205..1edec29ab 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -269,10 +269,10 @@ jobs: retention-days: 1 if-no-files-found: error -# borrowed from dhcpy6d for testing +# borrowed from dhcpy6d repo-debian: runs-on: ubuntu-latest - needs: [debian] + needs: [fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, debian] env: family: debian release: latest From 6f35a531e89f08281edc018d9760d992890edbb1 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Fri, 28 Apr 2023 23:49:48 +0200 Subject: [PATCH 582/884] prepare_3.12 --- .github/workflows/build-release-stable.yml | 188 +++++++++++++++++---- ChangeLog | 18 ++ Nagstamon/Config.py | 2 +- Nagstamon/QUI/__init__.py | 11 +- Nagstamon/resources/qui/dialog_about.ui | 31 +++- build/debian/changelog | 20 ++- build/requirements/linux.txt | 4 +- 7 files changed, 226 insertions(+), 48 deletions(-) diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index 8d4fc3b65..a128e173a 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -4,8 +4,9 @@ on: tags: 'v*' env: - python_win_version: 3.10.7 + python_win_version: 3.11.3 repo_dir: nagstamon-jekyll/docs/repo + cr_image: ghcr.io/henriwahl/build-nagstamon jobs: test: @@ -16,16 +17,18 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 + # somehow weird way to get the hash over the requirements to be aware if they changed + - id: requirements_hash + run: echo "HASH=$(md5sum build/requirements/linux.txt | cut -d\ -f1)" >> $GITHUB_OUTPUT + # docker login is needed for pushing the test image + - uses: docker/login-action@v2 with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - sudo apt-get install libdbus-1-dev libkrb5-dev - python -m pip install --upgrade pip - pip install pytest pylint wheel #flake8 - if [ -f build/requirements/linux.txt ]; then pip install -r build/requirements/linux.txt; fi + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + # if image defined by hash over requirements is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }}-${{ steps.requirements_hash.outputs.HASH }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }}-${{ steps.requirements_hash.outputs.HASH }} --build-arg VERSION=${{ matrix.python-version }} --build-arg REQUIREMENTS="$(cat build/requirements/linux.txt | base64 --wrap=0)" -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }}-${{ steps.requirements_hash.outputs.HASH }} # - name: Lint with flake8 # run: | # # stop the build if there are Python syntax errors or undefined names @@ -33,68 +36,113 @@ jobs: # # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide # flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - name: Test with unittest - run: | - python -m unittest tests/test_*.py + # using the tests in precompiled image makes them way faster instead of creating the test environment every time from scratch + run: docker run --rm -v $PWD:/src --workdir /src ${{ env.cr_image }}-${{ github.job }}-${{ matrix.python-version }}-${{ steps.requirements_hash.outputs.HASH }} python -m unittest tests/test_*.py debian: runs-on: ubuntu-latest needs: test steps: - uses: actions/checkout@v3 - - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + # docker login is needed for pushing the build image + - uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + # if image defined by hash over requirements is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }} + # building in precompiled image makes them way faster instead of creating the build environment every time from scratch + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon -e DEB_BUILD_OPTIONS=nocheck ${{ env.cr_image }}-${{ github.job }} - uses: actions/upload-artifact@v3 with: path: build/*.deb retention-days: 1 if-no-files-found: error - fedora-34: + fedora-35: runs-on: ubuntu-latest needs: test steps: - uses: actions/checkout@v3 - - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + # docker login is needed for pushing the build image + - uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + # if image defined by hash over requirements is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }} + # building in precompiled image makes them way faster instead of creating the build environment every time from scratch + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} - uses: actions/upload-artifact@v3 with: path: build/*.rpm retention-days: 1 if-no-files-found: error - fedora-35: + fedora-36: runs-on: ubuntu-latest needs: test steps: - uses: actions/checkout@v3 - - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + # docker login is needed for pushing the build image + - uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + # if image defined by hash over requirements is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }} + # building in precompiled image makes them way faster instead of creating the build environment every time from scratch + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} - uses: actions/upload-artifact@v3 with: path: build/*.rpm retention-days: 1 if-no-files-found: error - fedora-36: + fedora-37: runs-on: ubuntu-latest needs: test steps: - uses: actions/checkout@v3 - - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + # docker login is needed for pushing the build image + - uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + # if image defined by hash over requirements is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }} + # building in precompiled image makes them way faster instead of creating the build environment every time from scratch + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} - uses: actions/upload-artifact@v3 with: path: build/*.rpm retention-days: 1 if-no-files-found: error - fedora-37: + fedora-38: runs-on: ubuntu-latest needs: test steps: - uses: actions/checkout@v3 - - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + # docker login is needed for pushing the build image + - uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + # if image defined by hash over requirements is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }} + # building in precompiled image makes them way faster instead of creating the build environment every time from scratch + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} - uses: actions/upload-artifact@v3 with: path: build/*.rpm @@ -106,8 +154,17 @@ jobs: needs: test steps: - uses: actions/checkout@v3 - - run: /usr/bin/docker build -t build-nagstamon -f build/docker/Dockerfile-${{ github.job }} . - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon build-nagstamon + # docker login is needed for pushing the build image + - uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + # if image defined by hash over requirements is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }} + # building in precompiled image makes them way faster instead of creating the build environment every time from scratch + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} - uses: actions/upload-artifact@v3 with: path: build/*.rpm @@ -147,6 +204,8 @@ jobs: - run: cd ${{ github.workspace }}/build; python build.py env: PYTHONPATH: ${{ github.workspace }} + WIN_SIGNING_CERT_BASE64: ${{ secrets.SIGNING_CERT_BASE64 }} + WIN_SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - uses: actions/upload-artifact@v3 with: path: | @@ -171,6 +230,8 @@ jobs: - run: cd ${{ github.workspace }}/build; python build.py env: PYTHONPATH: ${{ github.workspace }} + WIN_SIGNING_CERT_BASE64: ${{ secrets.SIGNING_CERT_BASE64 }} + WIN_SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - uses: actions/upload-artifact@v3 with: path: | @@ -179,10 +240,64 @@ jobs: retention-days: 1 if-no-files-found: error - repo-fedora: + windows-64-debug: + # better depend on stable build image + runs-on: windows-2019 + needs: test + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 + with: + python-version: ${{ env.python_win_version }} + architecture: x64 + - run: python -m pip install --no-warn-script-location -r build/requirements/windows.txt + # pretty hacky but no other idea to avoid gssapi being installed which breaks requests-kerberos + - run: python -m pip uninstall -y gssapi requests-gssapi + - run: cd ${{ github.workspace }}/build; python build.py debug + env: + PYTHONPATH: ${{ github.workspace }} + WIN_SIGNING_CERT_BASE64: ${{ secrets.SIGNING_CERT_BASE64 }} + WIN_SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} + - uses: actions/upload-artifact@v3 + with: + path: | + build/dist/*.zip + retention-days: 1 + if-no-files-found: error + + repo-rpm-fedora: + runs-on: ubuntu-latest + # if not all are ready there might be trouble when downloading artifacts + # maybe faster now with build containers + needs: [fedora-35, fedora-36, fedora-37, fedora-38, rhel-9] + env: + family: fedora + steps: + # get binaries created by other jobs + - uses: actions/download-artifact@v3 + # organize SSH deploy key for nagstamon-repo + - run: mkdir ~/.ssh + - run: echo "${{ secrets.NAGSTAMON_REPO_KEY_WEB }}" > ~/.ssh/id_ed25519 + - run: chmod -R go-rwx ~/.ssh + # get and prepare nagstamon-jekyll + - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git + - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/latest + - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/latest + # copy *.rpm files into nagstamon-jekyll + - run: cp -r artifact/*.${{ env.family }}*.rpm ${{ env.repo_dir }}/${{ env.family }}/latest + # create rpm repo via Fedora container + - run: docker run --rm -v $PWD/${{ env.repo_dir }}/${{ env.family }}/latest:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo" + # commit and push new binaries to nagstamon-repo + - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" + - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push + + repo-rpm-rhel: runs-on: ubuntu-latest # if not all are ready there might be trouble when downloading artifacts - needs: [debian, fedora-34, fedora-35, fedora-36, fedora-37, macos, windows-32, windows-64] + # maybe faster now with build containers + needs: [fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, repo-rpm-fedora] + env: + family: rhel steps: # get binaries created by other jobs - uses: actions/download-artifact@v3 @@ -192,16 +307,19 @@ jobs: - run: chmod -R go-rwx ~/.ssh # get and prepare nagstamon-jekyll - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - - run: rm -rf ${{ env.repo_dir }}/fedora/?? - # copy *.rpm files into nagstamon-jekyll and create rpm repo via Fedora container - - run: for noarch_rpm in artifact/*.noarch.rpm; do version=$(echo $noarch_rpm | python3 -c "file=input(); print(file.split('fedora')[1].split('-')[0])"); mkdir -p mkdir -p ${{ env.repo_dir }}/fedora/$version; cp -r artifact/*.fedora$version-*.rpm ${{ env.repo_dir }}/fedora/$version; docker run --rm -v $PWD/${{ env.repo_dir }}/fedora/$version:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo"; done + - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/latest + - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/latest + # copy *.rpm files into nagstamon-jekyll + - run: cp -r artifact/*.${{ env.family }}*.rpm ${{ env.repo_dir }}/${{ env.family }}/latest + # create rpm repo via Fedora container + - run: docker run --rm -v $PWD/${{ env.repo_dir }}/${{ env.family }}/latest:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo" # commit and push new binaries to nagstamon-repo - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - - run: cd ${{ env.repo_dir }} && git add . && git commit -am "new stable repo" && git push + - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push github-release: runs-on: ubuntu-latest - needs: [debian, fedora-34, fedora-35, fedora-36, fedora-37, rhel-9, macos, windows-32, windows-64] + needs: [debian, fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, macos, windows-32, windows-64, windows-64-debug] steps: - uses: actions/download-artifact@v3 - run: cd artifact && md5sum *agstamon* > md5sums.txt diff --git a/ChangeLog b/ChangeLog index dbece0a16..1ac9d7eef 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +nagstamon (3.11.20230428) unstable; urgency=low + * New upstream + - added option to hide dock icon on macOS when using systray + - added fallback GSSAPI usage when no Kerberos is available + - added usage of local certificate store on Windows + - added codesigning for Windows builds + - added support for RHEL9 + - added Windows debugging version + - fixed Wayland support without using EWMH + - fixes for Icinga + - fixes for Zabbix + - fixes for Centreon + - fixes for Opsview + - fixes for Monitos + - improved build pipeline on GitHub Actions + + -- Henri Wahl <henri@nagstamon.de> Fri, Apr 28 2023 08:00:00 +0100 + nagstamon (3.10.1) stable; urgency=low * New upstream - fixes Centreon flapping bug diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index c475940a8..61953a715 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -121,7 +121,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.11-20230331' + VERSION = '3.11-20230428' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2023 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 7aef90630..84832cbe6 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -6837,17 +6837,18 @@ class Dialog_About(Dialog): def __init__(self, dialog): Dialog.__init__(self, dialog) # first add the logo on top - no idea how to achive in Qt Designer - logo = QSvgWidget('{0}{1}nagstamon.svg'.format(RESOURCES, os.sep)) + logo = QSvgWidget(f'{RESOURCES}{os.sep}nagstamon.svg') logo.setFixedSize(100, 100) self.window.vbox_about.insertWidget(1, logo, 0, Qt.AlignmentFlag.AlignHCenter) # update version information - self.window.label_nagstamon.setText('<h1>{0} {1}</h1>'.format(AppInfo.NAME, AppInfo.VERSION)) + self.window.label_nagstamon.setText(f'<h1>{AppInfo.NAME} {AppInfo.VERSION}</h1>') self.window.label_nagstamon_long.setText('<h2>Nagios¹ status monitor for your desktop</2>') self.window.label_copyright.setText(AppInfo.COPYRIGHT) - self.window.label_website.setText('<a href={0}>{0}</a>'.format(AppInfo.WEBSITE)) + self.window.label_website.setText(f'<a href={AppInfo.WEBSITE}>{AppInfo.WEBSITE}</a>') self.window.label_website.setOpenExternalLinks(True) self.window.label_versions.setText(f'Python: {platform.python_version()}, Qt: {QT_VERSION_STR}') - self.window.label_footnote.setText('<small>¹ plus Checkmk, Op5, Icinga, Centreon and more</small>') + self.window.label_contribution.setText(f'<a href={AppInfo.WEBSITE}/contribution>Contribution</a> | <a href=https://paypal.me/nagstamon>Donation</a>') + self.window.label_footnote.setText('<small>¹ meanwhile way more...</small>') # fill in license information license_file = open('{0}{1}LICENSE'.format(RESOURCES, os.sep)) @@ -6857,7 +6858,7 @@ def __init__(self, dialog): self.window.textedit_license.setReadOnly(True) # fill in credits information - credits_file = open('{0}{1}CREDITS'.format(RESOURCES, os.sep), encoding='utf-8') + credits_file = open(f'{RESOURCES}{os.sep}CREDITS', encoding='utf-8') credits = credits_file.read() credits_file.close() self.window.textedit_credits.setText(credits) diff --git a/Nagstamon/resources/qui/dialog_about.ui b/Nagstamon/resources/qui/dialog_about.ui index 82adcc210..a098f1a86 100644 --- a/Nagstamon/resources/qui/dialog_about.ui +++ b/Nagstamon/resources/qui/dialog_about.ui @@ -90,6 +90,9 @@ <property name="alignment"> <set>Qt::AlignCenter</set> </property> + <property name="openExternalLinks"> + <bool>true</bool> + </property> </widget> </item> <item> @@ -103,7 +106,33 @@ </widget> </item> <item> - <spacer name="verticalSpacer"> + <spacer name="verticalSpacer_3"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="label_contribution"> + <property name="text"> + <string>Contribution | Donation </string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <property name="openExternalLinks"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer_1"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> diff --git a/build/debian/changelog b/build/debian/changelog index 534c3fef6..ab1cc3409 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,8 +1,20 @@ -nagstamon (3.11-20230331) unstable; urgency=low +nagstamon (3.11.20230428) unstable; urgency=low * New upstream - - stuff - - -- Henri Wahl <henri@nagstamon.de> Fri, Mar 31 2023 08:00:00 +0100 + - added option to hide dock icon on macOS when using systray + - added fallback GSSAPI usage when no Kerberos is available + - added usage of local certificate store on Windows + - added codesigning for Windows builds + - added support for RHEL9 + - added Windows debugging version + - fixed Wayland support without using EWMH + - fixes for Icinga + - fixes for Zabbix + - fixes for Centreon + - fixes for Opsview + - fixes for Monitos + - improved build pipeline on GitHub Actions + + -- Henri Wahl <henri@nagstamon.de> Fri, Apr 28 2023 08:00:00 +0100 nagstamon (3.10.1) stable; urgency=low * New upstream diff --git a/build/requirements/linux.txt b/build/requirements/linux.txt index 6295f165e..63b8646b2 100644 --- a/build/requirements/linux.txt +++ b/build/requirements/linux.txt @@ -4,8 +4,8 @@ dbus-python keyring lxml psutil -pyqt6==6.3.1 -pyqt6-qt6==6.3.1 +pyqt6==6.4.2 +pyqt6-qt6==6.4.2 pysocks python-dateutil requests From 94e784d68b8f4419d18e3e8c4b8ff40b7a8a2c45 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 30 Apr 2023 18:17:28 +0200 Subject: [PATCH 583/884] play with debian repo --- .github/workflows/build-release-latest.yml | 35 ++++++++++++++++++++++ .github/workflows/build-release-stable.yml | 14 ++++----- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index b9e761104..e3f37f264 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -269,6 +269,41 @@ jobs: retention-days: 1 if-no-files-found: error +# borrowed from dhcpy6d for testing + repo-debian: + runs-on: ubuntu-latest + needs: [debian] + env: + family: debian + release: latest + steps: + - uses: actions/checkout@v2 + # get binaries created by other jobs + - uses: actions/download-artifact@v2 + + # make entrypoints executable + - run: chmod +x build/entrypoint-*.sh + # get secret signing key + - run: echo "${{ secrets.PACKAGE_SIGNING_KEY }}" > signing_key.asc + # organize SSH deploy key for nagstamon-jekyll repo + - run: mkdir ~/.ssh + - run: echo "${{ secrets.NAGSTAMON_REPO_KEY_WEB }}" > ~/.ssh/id_ed25519 + - run: chmod -R go-rwx ~/.ssh + # get and prepare nagstamon-jekyll + - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git + - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/${{ env.dist }}/{{ env.release }} + - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${{ env.dist }}/{{ env.release }} + # execute container with matching entrypoint + - run: | + /usr/bin/docker run --volume ${{ github.workspace }}:/dhcpy6d \ + --volume ${{ github.workspace }}/build/entrypoint-${{ github.job }}.sh:/entrypoint.sh \ + --entrypoint /entrypoint.sh \ + --env RELEASE=${{ env.release }} \ + ${{ github.job }} + # commit and push new binaries to dhcpyd-jekyll + - run: git config --global user.email "repo@dnagstamon.de" && git config --global user.name "Nagstamon Repository" + - run: cd ${{ env.repo_dir }} && git add . && git commit -am "new ${{ env.release }} repo ${{ env.family }}" && git push + repo-rpm-fedora: runs-on: ubuntu-latest # if not all are ready there might be trouble when downloading artifacts diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index a128e173a..5d1159978 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -281,10 +281,9 @@ jobs: - run: chmod -R go-rwx ~/.ssh # get and prepare nagstamon-jekyll - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/latest - - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/latest + - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/?? # copy *.rpm files into nagstamon-jekyll - - run: cp -r artifact/*.${{ env.family }}*.rpm ${{ env.repo_dir }}/${{ env.family }}/latest + - run: for noarch_rpm in artifact/*.noarch.rpm; do version=$(echo $noarch_rpm | python3 -c "file=input(); print(file.split('fedora')[1].split('-')[0])"); mkdir -p mkdir -p ${{ env.repo_dir }}/fedora/$version; cp -r artifact/*.fedora$version-*.rpm ${{ env.repo_dir }}/fedora/$version; docker run --rm -v $PWD/${{ env.repo_dir }}/fedora/$version:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo"; done # create rpm repo via Fedora container - run: docker run --rm -v $PWD/${{ env.repo_dir }}/${{ env.family }}/latest:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo" # commit and push new binaries to nagstamon-repo @@ -298,6 +297,7 @@ jobs: needs: [fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, repo-rpm-fedora] env: family: rhel + version: 9 steps: # get binaries created by other jobs - uses: actions/download-artifact@v3 @@ -307,12 +307,12 @@ jobs: - run: chmod -R go-rwx ~/.ssh # get and prepare nagstamon-jekyll - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/latest - - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/latest + - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/${{ env.version }} + - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${{ env.version }} # copy *.rpm files into nagstamon-jekyll - - run: cp -r artifact/*.${{ env.family }}*.rpm ${{ env.repo_dir }}/${{ env.family }}/latest + - run: cp -r artifact/*.${{ env.family }}*.rpm ${{ env.repo_dir }}/${{ env.family }}/${{ env.version }} # create rpm repo via Fedora container - - run: docker run --rm -v $PWD/${{ env.repo_dir }}/${{ env.family }}/latest:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo" + - run: docker run --rm -v $PWD/${{ env.repo_dir }}/${{ env.family }}/${{ env.version }}:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo" # commit and push new binaries to nagstamon-repo - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push From 98fecf196836b3cc45312802ede1dc9bc46c399d Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 30 Apr 2023 19:06:25 +0200 Subject: [PATCH 584/884] sign debian repo --- .github/workflows/build-release-latest.yml | 59 +++++++++++++--------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index e3f37f264..17ce0e545 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -3,9 +3,10 @@ on: push: tags-ignore: 'v*' branches: - - '**' - - '!master' - - '!*.*.*' + #- '**' + #- '!master' + #- '!*.*.*' + - 'debian_repo' env: python_win_version: 3.10.8 @@ -293,15 +294,23 @@ jobs: - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/${{ env.dist }}/{{ env.release }} - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${{ env.dist }}/{{ env.release }} - # execute container with matching entrypoint + # create deb repo via Debian build container - run: | - /usr/bin/docker run --volume ${{ github.workspace }}:/dhcpy6d \ - --volume ${{ github.workspace }}/build/entrypoint-${{ github.job }}.sh:/entrypoint.sh \ - --entrypoint /entrypoint.sh \ - --env RELEASE=${{ env.release }} \ - ${{ github.job }} - # commit and push new binaries to dhcpyd-jekyll - - run: git config --global user.email "repo@dnagstamon.de" && git config --global user.name "Nagstamon Repository" + /usr/bin/docker run --rm \ + -v ${{ github.workspace }}:/nagstamon \ + -v $PWD/${{ env.repo_dir }}/${{ env.family }}/{{ env.release }}:/repo ${{ env.cr_image }}-${{ env.family }} \ + /bin/sh -c "cd /nagstamon && \ + gpg --import signing_key.asc && \ + cd nagstamon-jekyll/docs/repo/{{ env.family }}/{{ env.release }} + cp -r artifact/*.deb . && \ + dpkg-scanpackages . > Packages && \ + gzip -k -f Packages && \ + apt-ftparchive release . > Release && \ + gpg -abs -o Release.gpg Release && \ + gpg --clearsign -o InRelease Release && \ + gpg --output key.gpg --armor --export" + # commit and push new binaries to nagstamon-jekyll + - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - run: cd ${{ env.repo_dir }} && git add . && git commit -am "new ${{ env.release }} repo ${{ env.family }}" && git push repo-rpm-fedora: @@ -356,17 +365,17 @@ jobs: - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push - github-release: - runs-on: ubuntu-latest - needs: [debian, fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, macos, windows-32, windows-64, windows-64-debug] - steps: - - uses: actions/download-artifact@v3 - - run: cd artifact && md5sum *agstamon* > md5sums.txt - - run: cd artifact && sha256sum *agstamon* > sha256sums.txt - - uses: marvinpinto/action-automatic-releases@latest - with: - repo_token: "${{ secrets.GITHUB_TOKEN }}" - automatic_release_tag: "latest" - prerelease: true - files: | - artifact/* +# github-release: +# runs-on: ubuntu-latest +# needs: [debian, fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, macos, windows-32, windows-64, windows-64-debug] +# steps: +# - uses: actions/download-artifact@v3 +# - run: cd artifact && md5sum *agstamon* > md5sums.txt +# - run: cd artifact && sha256sum *agstamon* > sha256sums.txt +# - uses: marvinpinto/action-automatic-releases@latest +# with: +# repo_token: "${{ secrets.GITHUB_TOKEN }}" +# automatic_release_tag: "latest" +# prerelease: true +# files: | +# artifact/* From e68a3c960c25733ee300cf5f4ab88be79043d63d Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 30 Apr 2023 19:08:50 +0200 Subject: [PATCH 585/884] no entrypoint for debian repo --- .github/workflows/build-release-latest.yml | 3 --- build/requirements/macos.txt | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 17ce0e545..e0e6b42b1 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -281,9 +281,6 @@ jobs: - uses: actions/checkout@v2 # get binaries created by other jobs - uses: actions/download-artifact@v2 - - # make entrypoints executable - - run: chmod +x build/entrypoint-*.sh # get secret signing key - run: echo "${{ secrets.PACKAGE_SIGNING_KEY }}" > signing_key.asc # organize SSH deploy key for nagstamon-jekyll repo diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index a7ffe0283..7f8ea8e60 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -5,8 +5,8 @@ lxml psutil pyinstaller pyobjc-framework-ApplicationServices -pyqt6==6.3.1 -pyqt6-qt6==6.3.1 +pyqt6==6.4.2 +pyqt6-qt6==6.4.2 pysocks python-dateutil requests From 1aac293fe89b31ea32b8977e97e85fbcb65b9379 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 30 Apr 2023 19:12:02 +0200 Subject: [PATCH 586/884] debian missing dollar --- .github/workflows/build-release-latest.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index e0e6b42b1..dd6f56f41 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -289,13 +289,13 @@ jobs: - run: chmod -R go-rwx ~/.ssh # get and prepare nagstamon-jekyll - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/${{ env.dist }}/{{ env.release }} - - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${{ env.dist }}/{{ env.release }} + - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/${{ env.dist }}/${{ env.release }} + - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${{ env.dist }}/${{ env.release }} # create deb repo via Debian build container - run: | /usr/bin/docker run --rm \ -v ${{ github.workspace }}:/nagstamon \ - -v $PWD/${{ env.repo_dir }}/${{ env.family }}/{{ env.release }}:/repo ${{ env.cr_image }}-${{ env.family }} \ + -v $PWD/${{ env.repo_dir }}/${{ env.family }}/${{ env.release }}:/repo ${{ env.cr_image }}-${{ env.family }} \ /bin/sh -c "cd /nagstamon && \ gpg --import signing_key.asc && \ cd nagstamon-jekyll/docs/repo/{{ env.family }}/{{ env.release }} From 3cafb47fb06d56c125728aa7f2e735faa84959b6 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 30 Apr 2023 19:12:57 +0200 Subject: [PATCH 587/884] no tests for debian --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index dd6f56f41..477e3a810 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -46,7 +46,7 @@ jobs: debian: runs-on: ubuntu-latest - needs: test + #needs: test steps: - uses: actions/checkout@v3 # docker login is needed for pushing the build image From 8de397539c47c523fea94029f561d7b67ca7d005 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 30 Apr 2023 19:20:57 +0200 Subject: [PATCH 588/884] fix ci order --- .github/workflows/build-release-latest.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 477e3a810..41e7b40ab 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -294,12 +294,12 @@ jobs: # create deb repo via Debian build container - run: | /usr/bin/docker run --rm \ - -v ${{ github.workspace }}:/nagstamon \ + -v ${{ github.workspace }}:/workspace \ -v $PWD/${{ env.repo_dir }}/${{ env.family }}/${{ env.release }}:/repo ${{ env.cr_image }}-${{ env.family }} \ - /bin/sh -c "cd /nagstamon && \ + /bin/sh -c "cd /workspace && \ gpg --import signing_key.asc && \ + cp -r artifact/*.deb nagstamon-jekyll/docs/repo/{{ env.family }}/{{ env.release }} && \ cd nagstamon-jekyll/docs/repo/{{ env.family }}/{{ env.release }} - cp -r artifact/*.deb . && \ dpkg-scanpackages . > Packages && \ gzip -k -f Packages && \ apt-ftparchive release . > Release && \ From 2cfabab4e18f2b223d0e5126d95075f21274b306 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 30 Apr 2023 19:24:24 +0200 Subject: [PATCH 589/884] more $ --- .github/workflows/build-release-latest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 41e7b40ab..e7a94a6e1 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -298,8 +298,8 @@ jobs: -v $PWD/${{ env.repo_dir }}/${{ env.family }}/${{ env.release }}:/repo ${{ env.cr_image }}-${{ env.family }} \ /bin/sh -c "cd /workspace && \ gpg --import signing_key.asc && \ - cp -r artifact/*.deb nagstamon-jekyll/docs/repo/{{ env.family }}/{{ env.release }} && \ - cd nagstamon-jekyll/docs/repo/{{ env.family }}/{{ env.release }} + cp -r artifact/*.deb nagstamon-jekyll/docs/repo/${{ env.family }}/${{ env.release }} && \ + cd nagstamon-jekyll/docs/repo/${{ env.family }}/${{ env.release }} dpkg-scanpackages . > Packages && \ gzip -k -f Packages && \ apt-ftparchive release . > Release && \ From 34f793dd42a73aa36b3c29ef661b01f1dfdd089c Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 30 Apr 2023 19:51:16 +0200 Subject: [PATCH 590/884] debian repo latest ready --- .github/workflows/build-release-latest.yml | 41 +++++++++++----------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index e7a94a6e1..2fb6bfa05 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -3,10 +3,9 @@ on: push: tags-ignore: 'v*' branches: - #- '**' - #- '!master' - #- '!*.*.*' - - 'debian_repo' + - '**' + - '!master' + - '!*.*.*' env: python_win_version: 3.10.8 @@ -46,7 +45,7 @@ jobs: debian: runs-on: ubuntu-latest - #needs: test + needs: test steps: - uses: actions/checkout@v3 # docker login is needed for pushing the build image @@ -314,7 +313,7 @@ jobs: runs-on: ubuntu-latest # if not all are ready there might be trouble when downloading artifacts # maybe faster now with build containers - needs: [fedora-35, fedora-36, fedora-37, fedora-38, rhel-9] + needs: [fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, repo-debian] env: family: fedora steps: @@ -340,7 +339,7 @@ jobs: runs-on: ubuntu-latest # if not all are ready there might be trouble when downloading artifacts # maybe faster now with build containers - needs: [fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, repo-rpm-fedora] + needs: [fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, repo-rpm-fedora, repo-debian] env: family: rhel steps: @@ -362,17 +361,17 @@ jobs: - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push -# github-release: -# runs-on: ubuntu-latest -# needs: [debian, fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, macos, windows-32, windows-64, windows-64-debug] -# steps: -# - uses: actions/download-artifact@v3 -# - run: cd artifact && md5sum *agstamon* > md5sums.txt -# - run: cd artifact && sha256sum *agstamon* > sha256sums.txt -# - uses: marvinpinto/action-automatic-releases@latest -# with: -# repo_token: "${{ secrets.GITHUB_TOKEN }}" -# automatic_release_tag: "latest" -# prerelease: true -# files: | -# artifact/* + github-release: + runs-on: ubuntu-latest + needs: [debian, fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, macos, windows-32, windows-64, windows-64-debug] + steps: + - uses: actions/download-artifact@v3 + - run: cd artifact && md5sum *agstamon* > md5sums.txt + - run: cd artifact && sha256sum *agstamon* > sha256sums.txt + - uses: marvinpinto/action-automatic-releases@latest + with: + repo_token: "${{ secrets.GITHUB_TOKEN }}" + automatic_release_tag: "latest" + prerelease: true + files: | + artifact/* From 4817992e715e5ecd9b9729fbf9ce95387f05b31a Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 30 Apr 2023 19:53:16 +0200 Subject: [PATCH 591/884] python_win_version: 3.11.3 --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 2fb6bfa05..8b7afa2f7 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -8,7 +8,7 @@ on: - '!*.*.*' env: - python_win_version: 3.10.8 + python_win_version: 3.11.3 repo_dir: nagstamon-jekyll/docs/repo cr_image: ghcr.io/henriwahl/build-nagstamon From 85cb733d766d53ddb11025bc36827d673422ea3a Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 30 Apr 2023 20:41:39 +0200 Subject: [PATCH 592/884] checkout + download v3 --- .github/workflows/build-release-latest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 8b7afa2f7..957175205 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -277,9 +277,9 @@ jobs: family: debian release: latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 # get binaries created by other jobs - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 # get secret signing key - run: echo "${{ secrets.PACKAGE_SIGNING_KEY }}" > signing_key.asc # organize SSH deploy key for nagstamon-jekyll repo From 1ade6ebe107088279b52e905f5754aabb3d1cc89 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 30 Apr 2023 20:44:59 +0200 Subject: [PATCH 593/884] repo-debian wait.... --- .github/workflows/build-release-latest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 957175205..1edec29ab 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -269,10 +269,10 @@ jobs: retention-days: 1 if-no-files-found: error -# borrowed from dhcpy6d for testing +# borrowed from dhcpy6d repo-debian: runs-on: ubuntu-latest - needs: [debian] + needs: [fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, debian] env: family: debian release: latest From 97f44df276675034845f7c762bdf763ba874c1fb Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 6 May 2023 17:30:13 +0200 Subject: [PATCH 594/884] Debian repo for stable 3.12 --- .github/workflows/build-release-stable.yml | 44 +++++++++++++++++++++- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index 5d1159978..b32529843 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -265,11 +265,51 @@ jobs: retention-days: 1 if-no-files-found: error +# borrowed from dhcpy6d + repo-debian: + runs-on: ubuntu-latest + needs: [fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, debian] + env: + family: debian + release: stable + steps: + - uses: actions/checkout@v3 + # get binaries created by other jobs + - uses: actions/download-artifact@v3 + # get secret signing key + - run: echo "${{ secrets.PACKAGE_SIGNING_KEY }}" > signing_key.asc + # organize SSH deploy key for nagstamon-jekyll repo + - run: mkdir ~/.ssh + - run: echo "${{ secrets.NAGSTAMON_REPO_KEY_WEB }}" > ~/.ssh/id_ed25519 + - run: chmod -R go-rwx ~/.ssh + # get and prepare nagstamon-jekyll + - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git + - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/${{ env.dist }}/${{ env.release }} + - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${{ env.dist }}/${{ env.release }} + # create deb repo via Debian build container + - run: | + /usr/bin/docker run --rm \ + -v ${{ github.workspace }}:/workspace \ + -v $PWD/${{ env.repo_dir }}/${{ env.family }}/${{ env.release }}:/repo ${{ env.cr_image }}-${{ env.family }} \ + /bin/sh -c "cd /workspace && \ + gpg --import signing_key.asc && \ + cp -r artifact/*.deb nagstamon-jekyll/docs/repo/${{ env.family }}/${{ env.release }} && \ + cd nagstamon-jekyll/docs/repo/${{ env.family }}/${{ env.release }} + dpkg-scanpackages . > Packages && \ + gzip -k -f Packages && \ + apt-ftparchive release . > Release && \ + gpg -abs -o Release.gpg Release && \ + gpg --clearsign -o InRelease Release && \ + gpg --output key.gpg --armor --export" + # commit and push new binaries to nagstamon-jekyll + - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" + - run: cd ${{ env.repo_dir }} && git add . && git commit -am "new ${{ env.release }} repo ${{ env.family }}" && git push + repo-rpm-fedora: runs-on: ubuntu-latest # if not all are ready there might be trouble when downloading artifacts # maybe faster now with build containers - needs: [fedora-35, fedora-36, fedora-37, fedora-38, rhel-9] + needs: [fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, repo-debian] env: family: fedora steps: @@ -294,7 +334,7 @@ jobs: runs-on: ubuntu-latest # if not all are ready there might be trouble when downloading artifacts # maybe faster now with build containers - needs: [fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, repo-rpm-fedora] + needs: [fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, repo-rpm-fedora, repo-debian] env: family: rhel version: 9 From 63b9503b0d6770dc9c743ac571711813690496c5 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 6 May 2023 17:33:19 +0200 Subject: [PATCH 595/884] meanwhile many more monitors --- ChangeLog | 4 ++-- Nagstamon/Config.py | 2 +- Nagstamon/QUI/__init__.py | 2 +- build/debian/changelog | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1ac9d7eef..a64171fbb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,4 @@ -nagstamon (3.11.20230428) unstable; urgency=low +nagstamon (3.12.0) stable; urgency=low * New upstream - added option to hide dock icon on macOS when using systray - added fallback GSSAPI usage when no Kerberos is available @@ -14,7 +14,7 @@ nagstamon (3.11.20230428) unstable; urgency=low - fixes for Monitos - improved build pipeline on GitHub Actions - -- Henri Wahl <henri@nagstamon.de> Fri, Apr 28 2023 08:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Sat, May 06 2023 08:00:00 +0200 nagstamon (3.10.1) stable; urgency=low * New upstream diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 61953a715..48cf1d571 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -121,7 +121,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.11-20230428' + VERSION = '3.12.0' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2023 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 84832cbe6..29fcfa065 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -6848,7 +6848,7 @@ def __init__(self, dialog): self.window.label_website.setOpenExternalLinks(True) self.window.label_versions.setText(f'Python: {platform.python_version()}, Qt: {QT_VERSION_STR}') self.window.label_contribution.setText(f'<a href={AppInfo.WEBSITE}/contribution>Contribution</a> | <a href=https://paypal.me/nagstamon>Donation</a>') - self.window.label_footnote.setText('<small>¹ meanwhile way more...</small>') + self.window.label_footnote.setText('<small>¹ meanwhile many more monitors...</small>') # fill in license information license_file = open('{0}{1}LICENSE'.format(RESOURCES, os.sep)) diff --git a/build/debian/changelog b/build/debian/changelog index ab1cc3409..0fc85bcde 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.11.20230428) unstable; urgency=low +nagstamon (3.12.0) stable; urgency=low * New upstream - added option to hide dock icon on macOS when using systray - added fallback GSSAPI usage when no Kerberos is available @@ -14,7 +14,7 @@ nagstamon (3.11.20230428) unstable; urgency=low - fixes for Monitos - improved build pipeline on GitHub Actions - -- Henri Wahl <henri@nagstamon.de> Fri, Apr 28 2023 08:00:00 +0100 + -- Henri Wahl <henri@nagstamon.de> Sat, May 06 2023 08:00:00 +0200 nagstamon (3.10.1) stable; urgency=low * New upstream From 4810d65f700e1ed0647bf7fa7c8d1bbf937c32f9 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 6 May 2023 17:47:16 +0200 Subject: [PATCH 596/884] rage against race condition --- .github/workflows/build-release-latest.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 1edec29ab..227a7a694 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -272,7 +272,8 @@ jobs: # borrowed from dhcpy6d repo-debian: runs-on: ubuntu-latest - needs: [fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, debian] + # try to avoid race condition and start uploading only after the last install package has been build + needs: [debian, fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, macos, windows-32, windows-64, windows-64-debug] env: family: debian release: latest @@ -313,7 +314,7 @@ jobs: runs-on: ubuntu-latest # if not all are ready there might be trouble when downloading artifacts # maybe faster now with build containers - needs: [fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, repo-debian] + needs: [repo-debian] env: family: fedora steps: @@ -339,7 +340,7 @@ jobs: runs-on: ubuntu-latest # if not all are ready there might be trouble when downloading artifacts # maybe faster now with build containers - needs: [fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, repo-rpm-fedora, repo-debian] + needs: [repo-rpm-fedora] env: family: rhel steps: @@ -363,7 +364,7 @@ jobs: github-release: runs-on: ubuntu-latest - needs: [debian, fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, macos, windows-32, windows-64, windows-64-debug] + needs: [repo-fedora] steps: - uses: actions/download-artifact@v3 - run: cd artifact && md5sum *agstamon* > md5sums.txt From d8d549d8f0c52352defda22b535c3cdaa856857c Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 6 May 2023 17:48:12 +0200 Subject: [PATCH 597/884] rage against race condition part II --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 227a7a694..3eab1c349 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -364,7 +364,7 @@ jobs: github-release: runs-on: ubuntu-latest - needs: [repo-fedora] + needs: [repo-rpm-fedora] steps: - uses: actions/download-artifact@v3 - run: cd artifact && md5sum *agstamon* > md5sums.txt From 801976d4cede9062d2f1eaf1827b3b9db4a00e79 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 6 May 2023 17:53:44 +0200 Subject: [PATCH 598/884] macos-12 --- .github/workflows/build-release-latest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 3eab1c349..aebdf93bf 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -176,7 +176,7 @@ jobs: if-no-files-found: error macos: - runs-on: macos-10.15 + runs-on: macos-12 needs: test steps: - uses: actions/checkout@v3 @@ -364,7 +364,7 @@ jobs: github-release: runs-on: ubuntu-latest - needs: [repo-rpm-fedora] + needs: [repo-rpm-rhel] steps: - uses: actions/download-artifact@v3 - run: cd artifact && md5sum *agstamon* > md5sums.txt From f882748fa48d84b1ac9ce816633424a45d427c24 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 6 May 2023 17:56:57 +0200 Subject: [PATCH 599/884] macos-11 --- .github/workflows/build-release-latest.yml | 2 +- .github/workflows/build-release-stable.yml | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index aebdf93bf..995c3c5b3 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -176,7 +176,7 @@ jobs: if-no-files-found: error macos: - runs-on: macos-12 + runs-on: macos-11 needs: test steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index b32529843..a2072c8ac 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -172,7 +172,7 @@ jobs: if-no-files-found: error macos: - runs-on: macos-10.15 + runs-on: macos-12 needs: test steps: - uses: actions/checkout@v3 @@ -268,7 +268,8 @@ jobs: # borrowed from dhcpy6d repo-debian: runs-on: ubuntu-latest - needs: [fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, debian] + # try to avoid race condition and start uploading only after the last install package has been build + needs: [debian, fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, macos, windows-32, windows-64, windows-64-debug] env: family: debian release: stable @@ -309,7 +310,7 @@ jobs: runs-on: ubuntu-latest # if not all are ready there might be trouble when downloading artifacts # maybe faster now with build containers - needs: [fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, repo-debian] + needs: [repo-debian] env: family: fedora steps: @@ -334,7 +335,7 @@ jobs: runs-on: ubuntu-latest # if not all are ready there might be trouble when downloading artifacts # maybe faster now with build containers - needs: [fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, repo-rpm-fedora, repo-debian] + needs: [repo-rpm-fedora] env: family: rhel version: 9 @@ -359,7 +360,7 @@ jobs: github-release: runs-on: ubuntu-latest - needs: [debian, fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, macos, windows-32, windows-64, windows-64-debug] + needs: [repo-rpm-rhel] steps: - uses: actions/download-artifact@v3 - run: cd artifact && md5sum *agstamon* > md5sums.txt From 43db50f96969130bfd8c2188e3e9d976701514ac Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 6 May 2023 18:35:31 +0200 Subject: [PATCH 600/884] merged 3.12 --- .github/workflows/build-release-stable.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index a2072c8ac..b9ffa4bd6 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -172,7 +172,7 @@ jobs: if-no-files-found: error macos: - runs-on: macos-12 + runs-on: macos-11 needs: test steps: - uses: actions/checkout@v3 From 9f794d5f6430c3586774395da8e388b4d59241e9 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 7 May 2023 15:36:49 +0200 Subject: [PATCH 601/884] more flexibility --- .github/workflows/build-release-latest.yml | 69 ++++++++++++---------- 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 995c3c5b3..1e2290e50 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -11,6 +11,7 @@ env: python_win_version: 3.11.3 repo_dir: nagstamon-jekyll/docs/repo cr_image: ghcr.io/henriwahl/build-nagstamon + cr_image_version: 1 jobs: test: @@ -54,11 +55,11 @@ jobs: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - # if image defined by hash over requirements is not pullable aka does not exist it will be created and pushed - - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . - - run: docker push ${{ env.cr_image }}-${{ github.job }} + # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon -e DEB_BUILD_OPTIONS=nocheck ${{ env.cr_image }}-${{ github.job }} + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon -e DEB_BUILD_OPTIONS=nocheck ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - uses: actions/upload-artifact@v3 with: path: build/*.deb @@ -76,11 +77,11 @@ jobs: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - # if image defined by hash over requirements is not pullable aka does not exist it will be created and pushed - - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . - - run: docker push ${{ env.cr_image }}-${{ github.job }} + # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - uses: actions/upload-artifact@v3 with: path: build/*.rpm @@ -98,11 +99,11 @@ jobs: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - # if image defined by hash over requirements is not pullable aka does not exist it will be created and pushed - - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . - - run: docker push ${{ env.cr_image }}-${{ github.job }} + # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - uses: actions/upload-artifact@v3 with: path: build/*.rpm @@ -120,11 +121,11 @@ jobs: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - # if image defined by hash over requirements is not pullable aka does not exist it will be created and pushed - - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . - - run: docker push ${{ env.cr_image }}-${{ github.job }} + # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - uses: actions/upload-artifact@v3 with: path: build/*.rpm @@ -142,11 +143,11 @@ jobs: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - # if image defined by hash over requirements is not pullable aka does not exist it will be created and pushed - - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . - - run: docker push ${{ env.cr_image }}-${{ github.job }} + # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - uses: actions/upload-artifact@v3 with: path: build/*.rpm @@ -164,7 +165,7 @@ jobs: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - # if image defined by hash over requirements is not pullable aka does not exist it will be created and pushed + # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . - run: docker push ${{ env.cr_image }}-${{ github.job }} # building in precompiled image makes them way faster instead of creating the build environment every time from scratch @@ -295,7 +296,7 @@ jobs: - run: | /usr/bin/docker run --rm \ -v ${{ github.workspace }}:/workspace \ - -v $PWD/${{ env.repo_dir }}/${{ env.family }}/${{ env.release }}:/repo ${{ env.cr_image }}-${{ env.family }} \ + -v $PWD/${{ env.repo_dir }}/${{ env.family }}/${{ env.release }}:/repo ${{ env.cr_image }}-${{ env.family }}:${{ env.cr_image_version }} \ /bin/sh -c "cd /workspace && \ gpg --import signing_key.asc && \ cp -r artifact/*.deb nagstamon-jekyll/docs/repo/${{ env.family }}/${{ env.release }} && \ @@ -308,7 +309,7 @@ jobs: gpg --output key.gpg --armor --export" # commit and push new binaries to nagstamon-jekyll - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - - run: cd ${{ env.repo_dir }} && git add . && git commit -am "new ${{ env.release }} repo ${{ env.family }}" && git push + #- run: cd ${{ env.repo_dir }} && git add . && git commit -am "new ${{ env.release }} repo ${{ env.family }}" && git push repo-rpm-fedora: runs-on: ubuntu-latest @@ -328,13 +329,21 @@ jobs: - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/latest - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/latest - # copy *.rpm files into nagstamon-jekyll - - run: cp -r artifact/*.${{ env.family }}*.rpm ${{ env.repo_dir }}/${{ env.family }}/latest - # create rpm repo via Fedora container - - run: docker run --rm -v $PWD/${{ env.repo_dir }}/${{ env.family }}/latest:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo" - # commit and push new binaries to nagstamon-repo + # copy *.rpm files into nagstamon-jekyll and create repodata + - run: | + for noarch_rpm in artifact/*${{ env.family }}*.noarch.rpm; \ + do \ + version=$(echo ${noarch_rpm} | python3 -c "file=input(); print(file.split('${{ env.family }}')[1].split('.')[0])") && \ + mkdir -p mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${version} && \ + cp -r artifact/*.${{ env.family }}${version}* ${{ env.repo_dir }}/${{ env.family }}/${version} && \ + docker run --rm -v ${PWD}/${{ env.repo_dir }}/${{ env.family }}/${version}:/repo \ + ${{ env.cr_image }}-${{ env.family }}-${version} \ + /bin/bash -c "createrepo --verbose --workers 1 /repo" && \ + ls -laR ${PWD}/${{ env.repo_dir }}/${{ env.family }}/${version}; \ + done +# commit and push new binaries to nagstamon-repo - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push + # - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push repo-rpm-rhel: runs-on: ubuntu-latest @@ -360,7 +369,7 @@ jobs: - run: docker run --rm -v $PWD/${{ env.repo_dir }}/${{ env.family }}/latest:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo" # commit and push new binaries to nagstamon-repo - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push + # - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push github-release: runs-on: ubuntu-latest From d459408a932e97530bcbbf08e5b531b3d6a5f303 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 7 May 2023 16:00:14 +0200 Subject: [PATCH 602/884] test ci 1 --- .github/workflows/build-release-latest.yml | 5 +- .github/workflows/build-release-stable.yml | 123 +++++++++++++-------- 2 files changed, 77 insertions(+), 51 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 1e2290e50..8a8124370 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -12,6 +12,7 @@ env: repo_dir: nagstamon-jekyll/docs/repo cr_image: ghcr.io/henriwahl/build-nagstamon cr_image_version: 1 + release:latest jobs: test: @@ -166,8 +167,8 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed - - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . - - run: docker push ${{ env.cr_image }}-${{ github.job }} + - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} - uses: actions/upload-artifact@v3 diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index a2072c8ac..0fc08d512 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -1,12 +1,19 @@ name: build-release-stable on: push: - tags: 'v*' + #tags: 'v*' + tags-ignore: 'v*' + branches: + - '**' + - '!master' + - '!*.*.*' env: python_win_version: 3.11.3 repo_dir: nagstamon-jekyll/docs/repo cr_image: ghcr.io/henriwahl/build-nagstamon + cr_image_version: 1 + release: stable jobs: test: @@ -50,11 +57,11 @@ jobs: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - # if image defined by hash over requirements is not pullable aka does not exist it will be created and pushed - - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . - - run: docker push ${{ env.cr_image }}-${{ github.job }} + # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon -e DEB_BUILD_OPTIONS=nocheck ${{ env.cr_image }}-${{ github.job }} + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon -e DEB_BUILD_OPTIONS=nocheck ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - uses: actions/upload-artifact@v3 with: path: build/*.deb @@ -72,11 +79,11 @@ jobs: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - # if image defined by hash over requirements is not pullable aka does not exist it will be created and pushed - - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . - - run: docker push ${{ env.cr_image }}-${{ github.job }} + # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - uses: actions/upload-artifact@v3 with: path: build/*.rpm @@ -94,11 +101,11 @@ jobs: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - # if image defined by hash over requirements is not pullable aka does not exist it will be created and pushed - - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . - - run: docker push ${{ env.cr_image }}-${{ github.job }} + # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - uses: actions/upload-artifact@v3 with: path: build/*.rpm @@ -116,11 +123,11 @@ jobs: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - # if image defined by hash over requirements is not pullable aka does not exist it will be created and pushed - - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . - - run: docker push ${{ env.cr_image }}-${{ github.job }} + # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - uses: actions/upload-artifact@v3 with: path: build/*.rpm @@ -138,11 +145,11 @@ jobs: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - # if image defined by hash over requirements is not pullable aka does not exist it will be created and pushed - - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . - - run: docker push ${{ env.cr_image }}-${{ github.job }} + # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - uses: actions/upload-artifact@v3 with: path: build/*.rpm @@ -160,9 +167,9 @@ jobs: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - # if image defined by hash over requirements is not pullable aka does not exist it will be created and pushed - - run: docker pull ${{ env.cr_image }}-${{ github.job }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }} -f build/docker/Dockerfile-${{ github.job }} . - - run: docker push ${{ env.cr_image }}-${{ github.job }} + # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} - uses: actions/upload-artifact@v3 @@ -172,7 +179,7 @@ jobs: if-no-files-found: error macos: - runs-on: macos-12 + runs-on: macos-11 needs: test steps: - uses: actions/checkout@v3 @@ -272,7 +279,6 @@ jobs: needs: [debian, fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, macos, windows-32, windows-64, windows-64-debug] env: family: debian - release: stable steps: - uses: actions/checkout@v3 # get binaries created by other jobs @@ -291,7 +297,7 @@ jobs: - run: | /usr/bin/docker run --rm \ -v ${{ github.workspace }}:/workspace \ - -v $PWD/${{ env.repo_dir }}/${{ env.family }}/${{ env.release }}:/repo ${{ env.cr_image }}-${{ env.family }} \ + -v $PWD/${{ env.repo_dir }}/${{ env.family }}/${{ env.release }}:/repo ${{ env.cr_image }}-${{ env.family }}:${{ env.cr_image_version }} \ /bin/sh -c "cd /workspace && \ gpg --import signing_key.asc && \ cp -r artifact/*.deb nagstamon-jekyll/docs/repo/${{ env.family }}/${{ env.release }} && \ @@ -304,7 +310,7 @@ jobs: gpg --output key.gpg --armor --export" # commit and push new binaries to nagstamon-jekyll - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - - run: cd ${{ env.repo_dir }} && git add . && git commit -am "new ${{ env.release }} repo ${{ env.family }}" && git push + #- run: cd ${{ env.repo_dir }} && git add . && git commit -am "new ${{ env.release }} repo ${{ env.family }}" && git push repo-rpm-fedora: runs-on: ubuntu-latest @@ -323,13 +329,21 @@ jobs: # get and prepare nagstamon-jekyll - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/?? - # copy *.rpm files into nagstamon-jekyll - - run: for noarch_rpm in artifact/*.noarch.rpm; do version=$(echo $noarch_rpm | python3 -c "file=input(); print(file.split('fedora')[1].split('-')[0])"); mkdir -p mkdir -p ${{ env.repo_dir }}/fedora/$version; cp -r artifact/*.fedora$version-*.rpm ${{ env.repo_dir }}/fedora/$version; docker run --rm -v $PWD/${{ env.repo_dir }}/fedora/$version:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo"; done - # create rpm repo via Fedora container - - run: docker run --rm -v $PWD/${{ env.repo_dir }}/${{ env.family }}/latest:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo" - # commit and push new binaries to nagstamon-repo + # copy *.rpm files into nagstamon-jekyll and create repodata + - run: | + for noarch_rpm in artifact/*${{ env.family }}*.noarch.rpm; \ + do \ + version=$(echo ${noarch_rpm} | python3 -c "file=input(); print(file.split('${{ env.family }}')[1].split('.')[0])") && \ + mkdir -p mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${version} && \ + cp -r artifact/*.${{ env.family }}${version}* ${{ env.repo_dir }}/${{ env.family }}/${version} && \ + docker run --rm -v ${PWD}/${{ env.repo_dir }}/${{ env.family }}/${version}:/repo \ + ${{ env.cr_image }}-${{ env.family }}-${version}:${{ env.cr_image_version }} \ + /bin/bash -c "createrepo --verbose --workers 1 /repo" && \ + ls -laR ${PWD}/${{ env.repo_dir }}/${{ env.family }}/${version}; \ + done +# commit and push new binaries to nagstamon-repo - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push + # - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push repo-rpm-rhel: runs-on: ubuntu-latest @@ -354,21 +368,32 @@ jobs: - run: cp -r artifact/*.${{ env.family }}*.rpm ${{ env.repo_dir }}/${{ env.family }}/${{ env.version }} # create rpm repo via Fedora container - run: docker run --rm -v $PWD/${{ env.repo_dir }}/${{ env.family }}/${{ env.version }}:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo" + - run: | + for noarch_rpm in artifact/*${{ env.family }}*.noarch.rpm; \ + do \ + version=$(echo ${noarch_rpm} | python3 -c "file=input(); print(file.split('${{ env.family }}')[1].split('.')[0])") && \ + mkdir -p mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${version} && \ + cp -r artifact/*.${{ env.family }}${version}* ${{ env.repo_dir }}/${{ env.family }}/${version} && \ + docker run --rm -v ${PWD}/${{ env.repo_dir }}/${{ env.family }}/${version}:/repo \ + ${{ env.cr_image }}-${{ env.family }}-${version}:${{ env.cr_image_version }} \ + /bin/bash -c "createrepo --verbose --workers 1 /repo" && \ + ls -laR ${PWD}/${{ env.repo_dir }}/${{ env.family }}/${version}; \ + done # commit and push new binaries to nagstamon-repo - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push + #- run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push - github-release: - runs-on: ubuntu-latest - needs: [repo-rpm-rhel] - steps: - - uses: actions/download-artifact@v3 - - run: cd artifact && md5sum *agstamon* > md5sums.txt - - run: cd artifact && sha256sum *agstamon* > sha256sums.txt - - uses: marvinpinto/action-automatic-releases@latest - with: - repo_token: "${{ secrets.GITHUB_TOKEN }}" - prerelease: false - draft: true - files: | - artifact/* +# github-release: +# runs-on: ubuntu-latest +# needs: [repo-rpm-rhel] +# steps: +# - uses: actions/download-artifact@v3 +# - run: cd artifact && md5sum *agstamon* > md5sums.txt +# - run: cd artifact && sha256sum *agstamon* > sha256sums.txt +# - uses: marvinpinto/action-automatic-releases@latest +# with: +# repo_token: "${{ secrets.GITHUB_TOKEN }}" +# prerelease: false +# draft: true +# files: | +# artifact/* From 66900717739da365c681a49178f7d62c317b3426 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 7 May 2023 16:20:02 +0200 Subject: [PATCH 603/884] test ci 2 --- .github/workflows/build-release-latest.yml | 73 +++++++++++----------- .github/workflows/build-release-stable.yml | 3 +- build/docker/Dockerfile-fedora-35 | 3 +- build/docker/Dockerfile-fedora-36 | 3 +- build/docker/Dockerfile-fedora-37 | 3 +- build/docker/Dockerfile-fedora-38 | 3 +- build/docker/Dockerfile-rhel-9 | 3 +- 7 files changed, 49 insertions(+), 42 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 8a8124370..c52b2cb44 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -12,7 +12,7 @@ env: repo_dir: nagstamon-jekyll/docs/repo cr_image: ghcr.io/henriwahl/build-nagstamon cr_image_version: 1 - release:latest + release: latest jobs: test: @@ -170,7 +170,7 @@ jobs: - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - uses: actions/upload-artifact@v3 with: path: build/*.rpm @@ -278,7 +278,6 @@ jobs: needs: [debian, fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, macos, windows-32, windows-64, windows-64-debug] env: family: debian - release: latest steps: - uses: actions/checkout@v3 # get binaries created by other jobs @@ -328,21 +327,18 @@ jobs: - run: chmod -R go-rwx ~/.ssh # get and prepare nagstamon-jekyll - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/latest - - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/latest + - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/${{ env.release }} + - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${{ env.release }} # copy *.rpm files into nagstamon-jekyll and create repodata - run: | - for noarch_rpm in artifact/*${{ env.family }}*.noarch.rpm; \ - do \ - version=$(echo ${noarch_rpm} | python3 -c "file=input(); print(file.split('${{ env.family }}')[1].split('.')[0])") && \ - mkdir -p mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${version} && \ - cp -r artifact/*.${{ env.family }}${version}* ${{ env.repo_dir }}/${{ env.family }}/${version} && \ - docker run --rm -v ${PWD}/${{ env.repo_dir }}/${{ env.family }}/${version}:/repo \ - ${{ env.cr_image }}-${{ env.family }}-${version} \ - /bin/bash -c "createrepo --verbose --workers 1 /repo" && \ - ls -laR ${PWD}/${{ env.repo_dir }}/${{ env.family }}/${version}; \ - done -# commit and push new binaries to nagstamon-repo + version=${{ env.release }} && \ + mkdir -p mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${version} && \ + cp -r artifact/*.${{ env.family }}${version}* ${{ env.repo_dir }}/${{ env.family }}/${version} && \ + docker run --rm -v ${PWD}/${{ env.repo_dir }}/${{ env.family }}/${version}:/repo \ + ${{ env.cr_image }}-${{ env.family }}-${version}:${{ env.cr_image_version }} \ + /bin/bash -c "createrepo --verbose --workers 1 /repo" && \ + ls -laR ${PWD}/${{ env.repo_dir }}/${{ env.family }}/${version} + # commit and push new binaries to nagstamon-repo - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" # - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push @@ -362,27 +358,32 @@ jobs: - run: chmod -R go-rwx ~/.ssh # get and prepare nagstamon-jekyll - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/latest - - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/latest - # copy *.rpm files into nagstamon-jekyll - - run: cp -r artifact/*.${{ env.family }}*.rpm ${{ env.repo_dir }}/${{ env.family }}/latest - # create rpm repo via Fedora container - - run: docker run --rm -v $PWD/${{ env.repo_dir }}/${{ env.family }}/latest:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo" + - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/${{ env.release }} + - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${{ env.release }} + # copy *.rpm files into nagstamon-jekyll and create repodata + - run: | + version=${{ env.release }} && \ + mkdir -p mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${version} && \ + cp -r artifact/*.${{ env.family }}${version}* ${{ env.repo_dir }}/${{ env.family }}/${version} && \ + docker run --rm -v ${PWD}/${{ env.repo_dir }}/${{ env.family }}/${version}:/repo \ + ${{ env.cr_image }}-${{ env.family }}-${version}:${{ env.cr_image_version }} \ + /bin/bash -c "createrepo --verbose --workers 1 /repo" && \ + ls -laR ${PWD}/${{ env.repo_dir }}/${{ env.family }}/${version} # commit and push new binaries to nagstamon-repo - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" # - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push - github-release: - runs-on: ubuntu-latest - needs: [repo-rpm-rhel] - steps: - - uses: actions/download-artifact@v3 - - run: cd artifact && md5sum *agstamon* > md5sums.txt - - run: cd artifact && sha256sum *agstamon* > sha256sums.txt - - uses: marvinpinto/action-automatic-releases@latest - with: - repo_token: "${{ secrets.GITHUB_TOKEN }}" - automatic_release_tag: "latest" - prerelease: true - files: | - artifact/* +# github-release: +# runs-on: ubuntu-latest +# needs: [repo-rpm-rhel] +# steps: +# - uses: actions/download-artifact@v3 +# - run: cd artifact && md5sum *agstamon* > md5sums.txt +# - run: cd artifact && sha256sum *agstamon* > sha256sums.txt +# - uses: marvinpinto/action-automatic-releases@latest +# with: +# repo_token: "${{ secrets.GITHUB_TOKEN }}" +# automatic_release_tag: "latest" +# prerelease: true +# files: | +# artifact/* diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index 0fc08d512..ab09353de 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -168,7 +168,8 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed - - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + #- run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} diff --git a/build/docker/Dockerfile-fedora-35 b/build/docker/Dockerfile-fedora-35 index 4e5247c53..a30ad8302 100644 --- a/build/docker/Dockerfile-fedora-35 +++ b/build/docker/Dockerfile-fedora-35 @@ -1,7 +1,8 @@ FROM fedora:35 LABEL maintainer=henri@nagstamon.de -RUN dnf -y install desktop-file-utils \ +RUN dnf -y install createrepo_c \ + desktop-file-utils \ git \ python3 \ python3-beautifulsoup4 \ diff --git a/build/docker/Dockerfile-fedora-36 b/build/docker/Dockerfile-fedora-36 index b22d17f72..ecad4993c 100644 --- a/build/docker/Dockerfile-fedora-36 +++ b/build/docker/Dockerfile-fedora-36 @@ -1,7 +1,8 @@ FROM fedora:36 LABEL maintainer=henri@nagstamon.de -RUN dnf -y install desktop-file-utils \ +RUN dnf -y install createrepo_c \ + desktop-file-utils \ git \ python3 \ python3-beautifulsoup4 \ diff --git a/build/docker/Dockerfile-fedora-37 b/build/docker/Dockerfile-fedora-37 index d41e42067..c59d2d130 100644 --- a/build/docker/Dockerfile-fedora-37 +++ b/build/docker/Dockerfile-fedora-37 @@ -1,7 +1,8 @@ FROM fedora:37 LABEL maintainer=henri@nagstamon.de -RUN dnf -y install desktop-file-utils \ +RUN dnf -y install createrepo_c \ + desktop-file-utils \ git \ python3 \ python3-beautifulsoup4 \ diff --git a/build/docker/Dockerfile-fedora-38 b/build/docker/Dockerfile-fedora-38 index 6c21a1675..bafd08e66 100644 --- a/build/docker/Dockerfile-fedora-38 +++ b/build/docker/Dockerfile-fedora-38 @@ -1,7 +1,8 @@ FROM fedora:38 LABEL maintainer=henri@nagstamon.de -RUN dnf -y install desktop-file-utils \ +RUN dnf -y install createrepo_c \ + desktop-file-utils \ git \ python3 \ python3-beautifulsoup4 \ diff --git a/build/docker/Dockerfile-rhel-9 b/build/docker/Dockerfile-rhel-9 index c8bcbaf05..f8ca54e87 100644 --- a/build/docker/Dockerfile-rhel-9 +++ b/build/docker/Dockerfile-rhel-9 @@ -4,7 +4,8 @@ LABEL maintainer=henri@nagstamon.de RUN dnf -y install 'dnf-command(config-manager)' \ epel-release && \ crb enable && \ - dnf -y install desktop-file-utils \ + dnf -y install createrepo_c \ + desktop-file-utils \ git \ python3 \ python3-beautifulsoup4 \ From fb7b313ef0edf3e51836fcafa28b435b4e7ad2e0 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 7 May 2023 16:24:05 +0200 Subject: [PATCH 604/884] test ci 3 --- .github/workflows/build-release-latest.yml | 3 ++- .github/workflows/build-release-stable.yml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index c52b2cb44..2f6776360 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -167,7 +167,8 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed - - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + #- run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index ab09353de..85dd797b8 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -169,7 +169,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed #- run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . - - /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} From e4b0681aeb59e0f86bfa27602dd0706ba4da5acf Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 7 May 2023 16:35:25 +0200 Subject: [PATCH 605/884] test ci 4 --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 2f6776360..cae038a2d 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -334,7 +334,7 @@ jobs: - run: | version=${{ env.release }} && \ mkdir -p mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${version} && \ - cp -r artifact/*.${{ env.family }}${version}* ${{ env.repo_dir }}/${{ env.family }}/${version} && \ + cp -r artifact/*.${{ env.family }}* ${{ env.repo_dir }}/${{ env.family }}/${version} && \ docker run --rm -v ${PWD}/${{ env.repo_dir }}/${{ env.family }}/${version}:/repo \ ${{ env.cr_image }}-${{ env.family }}-${version}:${{ env.cr_image_version }} \ /bin/bash -c "createrepo --verbose --workers 1 /repo" && \ From 6de874f5ed80e5b673f59e11f2faf832fcee28b4 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 7 May 2023 16:39:10 +0200 Subject: [PATCH 606/884] test ci 5 --- .github/workflows/build-release-latest.yml | 5 ++--- .github/workflows/build-release-stable.yml | 6 ++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index cae038a2d..c03c02d1d 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -167,8 +167,7 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed - #- run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . - - run: /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} @@ -365,7 +364,7 @@ jobs: - run: | version=${{ env.release }} && \ mkdir -p mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${version} && \ - cp -r artifact/*.${{ env.family }}${version}* ${{ env.repo_dir }}/${{ env.family }}/${version} && \ + cp -r artifact/*.${{ env.family }}* ${{ env.repo_dir }}/${{ env.family }}/${version} && \ docker run --rm -v ${PWD}/${{ env.repo_dir }}/${{ env.family }}/${version}:/repo \ ${{ env.cr_image }}-${{ env.family }}-${version}:${{ env.cr_image_version }} \ /bin/bash -c "createrepo --verbose --workers 1 /repo" && \ diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index 85dd797b8..38e0c067f 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -168,8 +168,7 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed - #- run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . - - run: /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} @@ -367,8 +366,7 @@ jobs: - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${{ env.version }} # copy *.rpm files into nagstamon-jekyll - run: cp -r artifact/*.${{ env.family }}*.rpm ${{ env.repo_dir }}/${{ env.family }}/${{ env.version }} - # create rpm repo via Fedora container - - run: docker run --rm -v $PWD/${{ env.repo_dir }}/${{ env.family }}/${{ env.version }}:/repo fedora /bin/sh -c "dnf -y install createrepo_c && createrepo /repo" + # copy *.rpm files into nagstamon-jekyll and create repodata - run: | for noarch_rpm in artifact/*${{ env.family }}*.noarch.rpm; \ do \ From 422239c452d6cefdffeb31ae88b09ae74840f40b Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 7 May 2023 18:47:22 +0200 Subject: [PATCH 607/884] test ci 6 --- .github/workflows/build-release-latest.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index c03c02d1d..53cff703d 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -318,6 +318,7 @@ jobs: needs: [repo-debian] env: family: fedora + cr_image_latest: 38 steps: # get binaries created by other jobs - uses: actions/download-artifact@v3 @@ -335,7 +336,7 @@ jobs: mkdir -p mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${version} && \ cp -r artifact/*.${{ env.family }}* ${{ env.repo_dir }}/${{ env.family }}/${version} && \ docker run --rm -v ${PWD}/${{ env.repo_dir }}/${{ env.family }}/${version}:/repo \ - ${{ env.cr_image }}-${{ env.family }}-${version}:${{ env.cr_image_version }} \ + ${{ env.cr_image }}-${{ env.family }}-${{ env.cr_image_latest }}:${{ env.cr_image_version }} \ /bin/bash -c "createrepo --verbose --workers 1 /repo" && \ ls -laR ${PWD}/${{ env.repo_dir }}/${{ env.family }}/${version} # commit and push new binaries to nagstamon-repo @@ -349,6 +350,7 @@ jobs: needs: [repo-rpm-fedora] env: family: rhel + cr_image_latest: 9 steps: # get binaries created by other jobs - uses: actions/download-artifact@v3 @@ -366,7 +368,7 @@ jobs: mkdir -p mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${version} && \ cp -r artifact/*.${{ env.family }}* ${{ env.repo_dir }}/${{ env.family }}/${version} && \ docker run --rm -v ${PWD}/${{ env.repo_dir }}/${{ env.family }}/${version}:/repo \ - ${{ env.cr_image }}-${{ env.family }}-${version}:${{ env.cr_image_version }} \ + ${{ env.cr_image }}-${{ env.family }}-${{ env.cr_image_latest }}:${{ env.cr_image_version }} \ /bin/bash -c "createrepo --verbose --workers 1 /repo" && \ ls -laR ${PWD}/${{ env.repo_dir }}/${{ env.family }}/${version} # commit and push new binaries to nagstamon-repo From fab47285cccdcac26828049961045588a28bd959 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 7 May 2023 19:01:58 +0200 Subject: [PATCH 608/884] test ci 7 --- .github/workflows/build-release-latest.yml | 8 ++++---- Nagstamon/Config.py | 2 +- build/debian/changelog | 5 +++++ 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 53cff703d..32df1ba73 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.7, 3.9] + python-version: [3.7, 3.9, 3.11] steps: - uses: actions/checkout@v3 @@ -309,7 +309,7 @@ jobs: gpg --output key.gpg --armor --export" # commit and push new binaries to nagstamon-jekyll - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - #- run: cd ${{ env.repo_dir }} && git add . && git commit -am "new ${{ env.release }} repo ${{ env.family }}" && git push + - run: cd ${{ env.repo_dir }} && git add . && git commit -am "new ${{ env.release }} repo ${{ env.family }}" && git push repo-rpm-fedora: runs-on: ubuntu-latest @@ -341,7 +341,7 @@ jobs: ls -laR ${PWD}/${{ env.repo_dir }}/${{ env.family }}/${version} # commit and push new binaries to nagstamon-repo - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - # - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push + - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push repo-rpm-rhel: runs-on: ubuntu-latest @@ -373,7 +373,7 @@ jobs: ls -laR ${PWD}/${{ env.repo_dir }}/${{ env.family }}/${version} # commit and push new binaries to nagstamon-repo - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - # - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push + - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push # github-release: # runs-on: ubuntu-latest diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 48cf1d571..dbf7c9e65 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -121,7 +121,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.12.0' + VERSION = '3.13-20230506' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2023 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 0fc85bcde..f5f3e7764 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,3 +1,8 @@ +nagstamon (3.13-20230506) unstable; urgency=low + * New upstream + + -- Henri Wahl <henri@nagstamon.de> Sat, May 06 2023 08:00:00 +0200 + nagstamon (3.12.0) stable; urgency=low * New upstream - added option to hide dock icon on macOS when using systray From f6a84235e4f3b8b42fc1615306a8c4b103eff9da Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 7 May 2023 19:19:32 +0200 Subject: [PATCH 609/884] test ci 8 --- .github/workflows/build-release-latest.yml | 31 +++++++++++----------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 32df1ba73..6ea6e1715 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -296,7 +296,8 @@ jobs: - run: | /usr/bin/docker run --rm \ -v ${{ github.workspace }}:/workspace \ - -v $PWD/${{ env.repo_dir }}/${{ env.family }}/${{ env.release }}:/repo ${{ env.cr_image }}-${{ env.family }}:${{ env.cr_image_version }} \ + -v $PWD/${{ env.repo_dir }}/${{ env.family }}/${{ env.release }}:/repo \ + ${{ env.cr_image }}-${{ env.family }}:${{ env.cr_image_version }} \ /bin/sh -c "cd /workspace && \ gpg --import signing_key.asc && \ cp -r artifact/*.deb nagstamon-jekyll/docs/repo/${{ env.family }}/${{ env.release }} && \ @@ -375,17 +376,17 @@ jobs: - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push -# github-release: -# runs-on: ubuntu-latest -# needs: [repo-rpm-rhel] -# steps: -# - uses: actions/download-artifact@v3 -# - run: cd artifact && md5sum *agstamon* > md5sums.txt -# - run: cd artifact && sha256sum *agstamon* > sha256sums.txt -# - uses: marvinpinto/action-automatic-releases@latest -# with: -# repo_token: "${{ secrets.GITHUB_TOKEN }}" -# automatic_release_tag: "latest" -# prerelease: true -# files: | -# artifact/* + github-release: + runs-on: ubuntu-latest + needs: [repo-rpm-rhel] + steps: + - uses: actions/download-artifact@v3 + - run: cd artifact && md5sum *agstamon* > md5sums.txt + - run: cd artifact && sha256sum *agstamon* > sha256sums.txt + - uses: marvinpinto/action-automatic-releases@latest + with: + repo_token: "${{ secrets.GITHUB_TOKEN }}" + automatic_release_tag: "latest" + prerelease: true + files: | + artifact/* From a84f4dfe812e335a0346d9da7271fac0e614281b Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Mon, 8 May 2023 09:43:12 +0200 Subject: [PATCH 610/884] dciborow/action-github-releases --- .github/workflows/build-release-latest.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 6ea6e1715..f350f6b10 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -11,7 +11,9 @@ env: python_win_version: 3.11.3 repo_dir: nagstamon-jekyll/docs/repo cr_image: ghcr.io/henriwahl/build-nagstamon + # CR image version will be increased if further updates are necessary cr_image_version: 1 + # release type this file is used for release: latest jobs: @@ -271,7 +273,7 @@ jobs: retention-days: 1 if-no-files-found: error -# borrowed from dhcpy6d + # borrowed from dhcpy6d repo-debian: runs-on: ubuntu-latest # try to avoid race condition and start uploading only after the last install package has been build @@ -383,7 +385,7 @@ jobs: - uses: actions/download-artifact@v3 - run: cd artifact && md5sum *agstamon* > md5sums.txt - run: cd artifact && sha256sum *agstamon* > sha256sums.txt - - uses: marvinpinto/action-automatic-releases@latest + - uses: dciborow/action-github-releases@latest with: repo_token: "${{ secrets.GITHUB_TOKEN }}" automatic_release_tag: "latest" From 3336fd08a8e972f77cf4a39e643e56d1d8e27311 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Mon, 8 May 2023 10:04:19 +0200 Subject: [PATCH 611/884] dciborow/action-github-releases v1.0.1 --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index f350f6b10..9264e3384 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -385,7 +385,7 @@ jobs: - uses: actions/download-artifact@v3 - run: cd artifact && md5sum *agstamon* > md5sums.txt - run: cd artifact && sha256sum *agstamon* > sha256sums.txt - - uses: dciborow/action-github-releases@latest + - uses: dciborow/action-github-releases@v1.0.1 with: repo_token: "${{ secrets.GITHUB_TOKEN }}" automatic_release_tag: "latest" From 1547894d4737264e095b59f9d4ed46486a7eab61 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 9 May 2023 22:31:15 +0200 Subject: [PATCH 612/884] prepare ci build script for stable --- .github/workflows/build-release-latest.yml | 6 ++- .github/workflows/build-release-stable.yml | 50 +++++++++++----------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 9264e3384..7232146e2 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -11,7 +11,7 @@ env: python_win_version: 3.11.3 repo_dir: nagstamon-jekyll/docs/repo cr_image: ghcr.io/henriwahl/build-nagstamon - # CR image version will be increased if further updates are necessary + # to be increased if new updates of build images are necessary cr_image_version: 1 # release type this file is used for release: latest @@ -273,7 +273,7 @@ jobs: retention-days: 1 if-no-files-found: error - # borrowed from dhcpy6d +# borrowed from dhcpy6d repo-debian: runs-on: ubuntu-latest # try to avoid race condition and start uploading only after the last install package has been build @@ -321,6 +321,7 @@ jobs: needs: [repo-debian] env: family: fedora + # which image to use for packaging cr_image_latest: 38 steps: # get binaries created by other jobs @@ -353,6 +354,7 @@ jobs: needs: [repo-rpm-fedora] env: family: rhel + # which image to use for packaging cr_image_latest: 9 steps: # get binaries created by other jobs diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index 38e0c067f..4ca88f805 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -1,17 +1,13 @@ name: build-release-stable on: push: - #tags: 'v*' - tags-ignore: 'v*' - branches: - - '**' - - '!master' - - '!*.*.*' + tags: 'v*' env: python_win_version: 3.11.3 repo_dir: nagstamon-jekyll/docs/repo cr_image: ghcr.io/henriwahl/build-nagstamon + # to be increased if new updates of build images are necessary cr_image_version: 1 release: stable @@ -20,7 +16,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.7, 3.9] + python-version: [3.7, 3.9, 3.11] steps: - uses: actions/checkout@v3 @@ -171,7 +167,7 @@ jobs: - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }} + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - uses: actions/upload-artifact@v3 with: path: build/*.rpm @@ -297,7 +293,8 @@ jobs: - run: | /usr/bin/docker run --rm \ -v ${{ github.workspace }}:/workspace \ - -v $PWD/${{ env.repo_dir }}/${{ env.family }}/${{ env.release }}:/repo ${{ env.cr_image }}-${{ env.family }}:${{ env.cr_image_version }} \ + -v $PWD/${{ env.repo_dir }}/${{ env.family }}/${{ env.release }}:/repo \ + ${{ env.cr_image }}-${{ env.family }}:${{ env.cr_image_version }} \ /bin/sh -c "cd /workspace && \ gpg --import signing_key.asc && \ cp -r artifact/*.deb nagstamon-jekyll/docs/repo/${{ env.family }}/${{ env.release }} && \ @@ -310,7 +307,7 @@ jobs: gpg --output key.gpg --armor --export" # commit and push new binaries to nagstamon-jekyll - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - #- run: cd ${{ env.repo_dir }} && git add . && git commit -am "new ${{ env.release }} repo ${{ env.family }}" && git push + - run: cd ${{ env.repo_dir }} && git add . && git commit -am "new ${{ env.release }} repo ${{ env.family }}" && git push repo-rpm-fedora: runs-on: ubuntu-latest @@ -343,7 +340,7 @@ jobs: done # commit and push new binaries to nagstamon-repo - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - # - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push + - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push repo-rpm-rhel: runs-on: ubuntu-latest @@ -352,6 +349,7 @@ jobs: needs: [repo-rpm-fedora] env: family: rhel + # currently just one version available version: 9 steps: # get binaries created by other jobs @@ -380,19 +378,19 @@ jobs: done # commit and push new binaries to nagstamon-repo - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - #- run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push + - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push -# github-release: -# runs-on: ubuntu-latest -# needs: [repo-rpm-rhel] -# steps: -# - uses: actions/download-artifact@v3 -# - run: cd artifact && md5sum *agstamon* > md5sums.txt -# - run: cd artifact && sha256sum *agstamon* > sha256sums.txt -# - uses: marvinpinto/action-automatic-releases@latest -# with: -# repo_token: "${{ secrets.GITHUB_TOKEN }}" -# prerelease: false -# draft: true -# files: | -# artifact/* + github-release: + runs-on: ubuntu-latest + needs: [repo-rpm-rhel] + steps: + - uses: actions/download-artifact@v3 + - run: cd artifact && md5sum *agstamon* > md5sums.txt + - run: cd artifact && sha256sum *agstamon* > sha256sums.txt + - uses: marvinpinto/action-automatic-releases@latest + with: + repo_token: "${{ secrets.GITHUB_TOKEN }}" + prerelease: false + draft: true + files: | + artifact/* From ad11a3d137756ccbf7b0d030a5718cea66f82df7 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 9 May 2023 22:35:25 +0200 Subject: [PATCH 613/884] prepare ci build script for stable part II --- .github/workflows/build-release-stable.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index 4ca88f805..cd347432e 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -387,7 +387,7 @@ jobs: - uses: actions/download-artifact@v3 - run: cd artifact && md5sum *agstamon* > md5sums.txt - run: cd artifact && sha256sum *agstamon* > sha256sums.txt - - uses: marvinpinto/action-automatic-releases@latest + - uses: dciborow/action-github-releases@v1.0.1 with: repo_token: "${{ secrets.GITHUB_TOKEN }}" prerelease: false From 7e6fd5b8f57b369e7fe77c99da47671d5a12273b Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 9 May 2023 22:56:23 +0200 Subject: [PATCH 614/884] return to marvinpinto releases action --- .github/workflows/build-release-latest.yml | 2 +- .github/workflows/build-release-stable.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 7232146e2..55a51c7ef 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -387,7 +387,7 @@ jobs: - uses: actions/download-artifact@v3 - run: cd artifact && md5sum *agstamon* > md5sums.txt - run: cd artifact && sha256sum *agstamon* > sha256sums.txt - - uses: dciborow/action-github-releases@v1.0.1 + - uses: marvinpinto/action-automatic-releases@latest with: repo_token: "${{ secrets.GITHUB_TOKEN }}" automatic_release_tag: "latest" diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index cd347432e..4ca88f805 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -387,7 +387,7 @@ jobs: - uses: actions/download-artifact@v3 - run: cd artifact && md5sum *agstamon* > md5sums.txt - run: cd artifact && sha256sum *agstamon* > sha256sums.txt - - uses: dciborow/action-github-releases@v1.0.1 + - uses: marvinpinto/action-automatic-releases@latest with: repo_token: "${{ secrets.GITHUB_TOKEN }}" prerelease: false From 0e6e0a798418c35b91457d9e32b685cd7c743432 Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Sat, 10 Jun 2023 00:22:02 +0200 Subject: [PATCH 615/884] Qt6 6.5.1 --- build/debian/changelog | 4 ++-- build/requirements/macos.txt | 3 +-- build/requirements/windows.txt | 3 +-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/build/debian/changelog b/build/debian/changelog index f5f3e7764..4489fe494 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.13-20230506) unstable; urgency=low +nagstamon (3.13-20230609) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Sat, May 06 2023 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Fri, June 09 2023 08:00:00 +0200 nagstamon (3.12.0) stable; urgency=low * New upstream diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index 7f8ea8e60..696e2320e 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -5,8 +5,7 @@ lxml psutil pyinstaller pyobjc-framework-ApplicationServices -pyqt6==6.4.2 -pyqt6-qt6==6.4.2 +pyqt6==6.5.1 pysocks python-dateutil requests diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index d73e7b084..812c1de69 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -7,8 +7,7 @@ pip-system-certs psutil pyinstaller pypiwin32 -pyqt6==6.4.2 -pyqt6-qt6==6.4.2 +pyqt6==6.5.1 pysocks python-dateutil requests From 803c9f4200c4b81b0f55c7ed6f72f24ad73d5398 Mon Sep 17 00:00:00 2001 From: Jan Kantert <jan-github2@kantert.net> Date: Thu, 15 Jun 2023 18:00:37 +0200 Subject: [PATCH 616/884] prevent setup.py crash on win and mac --- setup.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup.py b/setup.py index 271072e3d..36774a10b 100644 --- a/setup.py +++ b/setup.py @@ -45,6 +45,8 @@ else: DIST, DIST_VERSION, DIST_NAME = platform.dist() NAME = NAME.lower() +else: + DIST = "" #VERSION = AppInfo.VERSION.replace('-', '.') + '.' + DIST + DIST_VERSION VERSION = AppInfo.VERSION.replace('-', '.') NAGSTAMON_SCRIPT = 'nagstamon.py' From 68cffad684a5103dd4889f409fb2dea6ecaccd47 Mon Sep 17 00:00:00 2001 From: Jan Kantert <jan-github2@kantert.net> Date: Thu, 15 Jun 2023 18:00:56 +0200 Subject: [PATCH 617/884] refactor error handling and actually use the return code in IcingaDBWeb --- Nagstamon/Servers/Generic.py | 10 ++++++---- Nagstamon/Servers/IcingaDBWeb.py | 9 +++------ 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index f4a30846d..96a07ed8d 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -26,6 +26,7 @@ import sys import traceback import urllib.parse +from typing import Optional from urllib.request import getproxies from bs4 import BeautifulSoup @@ -1687,16 +1688,17 @@ def get_events_history_count(self): """ return(len(list((e for e in self.events_history if self.events_history[e] is True)))) - def check_for_error(self, result, error, status_code): + @staticmethod + def check_for_error(result, error, status_code) -> Optional[Result]: """ check if any error occured - if so, return error """ if error != '' or status_code > 400: - return(Result(result=copy.deepcopy(result), + return Result(result=copy.deepcopy(result), error=copy.deepcopy(error), - status_code=copy.deepcopy(status_code))) + status_code=copy.deepcopy(status_code)) else: - return(False) + return None def get_worst_status_current(self): """ diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index 4bdc3c79c..931658e99 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -159,13 +159,10 @@ def _get_status(self): copy.deepcopy(result.error),\ result.status_code - if error != '' or status_code >= 400: - return Result(result=jsonraw, - error=error, - status_code=status_code) - # check if any error occured - self.check_for_error(jsonraw, error, status_code) + potential_error = self.check_for_error(jsonraw, error, status_code) + if potential_error: + return potential_error # Check if the backend is running # If it isn't running the last values stored in the database are returned/shown From faf9d145205925fe1825b10f626519339e77920a Mon Sep 17 00:00:00 2001 From: Jan Kantert <jan-github2@kantert.net> Date: Thu, 15 Jun 2023 18:02:17 +0200 Subject: [PATCH 618/884] fix python syntax --- Nagstamon/Servers/IcingaDBWeb.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index 931658e99..9c8407b52 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -60,13 +60,13 @@ class IcingaDBWebServer(GenericServer): """ TYPE = 'IcingaDBWeb' MENU_ACTIONS = ['Monitor', 'Recheck', 'Acknowledge', 'Submit check result', 'Downtime'] - STATES_MAPPING = {'hosts' : {0 : 'UP', 1 : 'DOWN', 2 : 'UNREACHABLE'}, \ + STATES_MAPPING = {'hosts' : {0 : 'UP', 1 : 'DOWN', 2 : 'UNREACHABLE'}, 'services' : {0 : 'OK', 1 : 'WARNING', 2 : 'CRITICAL', 3 : 'UNKNOWN'}} - STATES_MAPPING_REV = {'hosts' : { 'UP': 0, 'DOWN': 1, 'UNREACHABLE': 2}, \ + STATES_MAPPING_REV = {'hosts' : { 'UP': 0, 'DOWN': 1, 'UNREACHABLE': 2}, 'services' : {'OK': 0, 'WARNING': 1, 'CRITICAL': 2, 'UNKNOWN': 3}} - BROWSER_URLS = { 'monitor': '$MONITOR-CGI$/dashboard', \ - 'hosts': '$MONITOR-CGI$/icingadb/hosts', \ - 'services': '$MONITOR-CGI$/icingadb/services', \ + BROWSER_URLS = { 'monitor': '$MONITOR-CGI$/dashboard', + 'hosts': '$MONITOR-CGI$/icingadb/hosts', + 'services': '$MONITOR-CGI$/icingadb/services', 'history': '$MONITOR-CGI$/icingadb/history'} @@ -120,7 +120,7 @@ def _get_status(self): Get status from Icinga Server - only JSON """ # define CGI URLs for hosts and services - if self.cgiurl_hosts == self.cgiurl_services == self.cgiurl_monitoring_health == None: + if self.cgiurl_hosts is None and self.cgiurl_services is None and self.cgiurl_monitoring_health is None: # services (unknown, warning or critical?) self.cgiurl_services = {'hard': self.monitor_cgi_url + '/icingadb/services?service.state.is_problem=y&service.state.in_downtime=n&service.state.state_type=hard&columns=service.state.last_update,service.state.is_reachable&format=json', \ 'soft': self.monitor_cgi_url + '/icingadb/services?service.state.is_problem=y&service.state.in_downtime=n&service.state.state_type=soft&columns=service.state.last_update,service.state.is_reachable&format=json'} From 8f6e3e8f3568592a66b23391bd49aed30ca4b190 Mon Sep 17 00:00:00 2001 From: Jan Kantert <jan-github2@kantert.net> Date: Thu, 15 Jun 2023 22:24:25 +0200 Subject: [PATCH 619/884] fix crash when IcingaWeb does not return well formatted html This happens frequently for us with IcingaDB because when it emits exceptions due to its incomplete perfdata parser --- Nagstamon/Servers/IcingaDBWeb.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index 4bdc3c79c..90b9f1601 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -442,15 +442,21 @@ def set_acknowledge(self, info_dict): #if not info_dict['expire_time']: # info_dict['expire_time'] = None - self._set_acknowledge(info_dict['host'], - info_dict['service'], - info_dict['author'], - info_dict['comment'], - info_dict['sticky'], - info_dict['notify'], - info_dict['persistent'], - all_services, - info_dict['expire_time']) + try: + self._set_acknowledge(info_dict['host'], + info_dict['service'], + info_dict['author'], + info_dict['comment'], + info_dict['sticky'], + info_dict['notify'], + info_dict['persistent'], + all_services, + info_dict['expire_time']) + except: + import traceback + traceback.print_exc(file=sys.stdout) + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=None, expire_time=None): From 2a0c9fab807fdd64c4a828714d436396824faa72 Mon Sep 17 00:00:00 2001 From: Jan Kantert <jan-github2@kantert.net> Date: Thu, 15 Jun 2023 22:24:25 +0200 Subject: [PATCH 620/884] fix crash when IcingaWeb does not return well formatted html This happens frequently for us with IcingaDB because when it emits exceptions due to its incomplete perfdata parser --- Nagstamon/Servers/IcingaDBWeb.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index 4bdc3c79c..90b9f1601 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -442,15 +442,21 @@ def set_acknowledge(self, info_dict): #if not info_dict['expire_time']: # info_dict['expire_time'] = None - self._set_acknowledge(info_dict['host'], - info_dict['service'], - info_dict['author'], - info_dict['comment'], - info_dict['sticky'], - info_dict['notify'], - info_dict['persistent'], - all_services, - info_dict['expire_time']) + try: + self._set_acknowledge(info_dict['host'], + info_dict['service'], + info_dict['author'], + info_dict['comment'], + info_dict['sticky'], + info_dict['notify'], + info_dict['persistent'], + all_services, + info_dict['expire_time']) + except: + import traceback + traceback.print_exc(file=sys.stdout) + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=None, expire_time=None): From 01a1d5d64e9d7d1f93a514f0966168b8ea48057b Mon Sep 17 00:00:00 2001 From: Jan Kantert <jan-github2@kantert.net> Date: Thu, 15 Jun 2023 18:02:17 +0200 Subject: [PATCH 621/884] fix python syntax --- Nagstamon/Servers/IcingaDBWeb.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index 90b9f1601..a4d4bc77a 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -60,13 +60,13 @@ class IcingaDBWebServer(GenericServer): """ TYPE = 'IcingaDBWeb' MENU_ACTIONS = ['Monitor', 'Recheck', 'Acknowledge', 'Submit check result', 'Downtime'] - STATES_MAPPING = {'hosts' : {0 : 'UP', 1 : 'DOWN', 2 : 'UNREACHABLE'}, \ + STATES_MAPPING = {'hosts' : {0 : 'UP', 1 : 'DOWN', 2 : 'UNREACHABLE'}, 'services' : {0 : 'OK', 1 : 'WARNING', 2 : 'CRITICAL', 3 : 'UNKNOWN'}} - STATES_MAPPING_REV = {'hosts' : { 'UP': 0, 'DOWN': 1, 'UNREACHABLE': 2}, \ + STATES_MAPPING_REV = {'hosts' : { 'UP': 0, 'DOWN': 1, 'UNREACHABLE': 2}, 'services' : {'OK': 0, 'WARNING': 1, 'CRITICAL': 2, 'UNKNOWN': 3}} - BROWSER_URLS = { 'monitor': '$MONITOR-CGI$/dashboard', \ - 'hosts': '$MONITOR-CGI$/icingadb/hosts', \ - 'services': '$MONITOR-CGI$/icingadb/services', \ + BROWSER_URLS = { 'monitor': '$MONITOR-CGI$/dashboard', + 'hosts': '$MONITOR-CGI$/icingadb/hosts', + 'services': '$MONITOR-CGI$/icingadb/services', 'history': '$MONITOR-CGI$/icingadb/history'} @@ -120,7 +120,7 @@ def _get_status(self): Get status from Icinga Server - only JSON """ # define CGI URLs for hosts and services - if self.cgiurl_hosts == self.cgiurl_services == self.cgiurl_monitoring_health == None: + if self.cgiurl_hosts is None and self.cgiurl_services is None and self.cgiurl_monitoring_health is None: # services (unknown, warning or critical?) self.cgiurl_services = {'hard': self.monitor_cgi_url + '/icingadb/services?service.state.is_problem=y&service.state.in_downtime=n&service.state.state_type=hard&columns=service.state.last_update,service.state.is_reachable&format=json', \ 'soft': self.monitor_cgi_url + '/icingadb/services?service.state.is_problem=y&service.state.in_downtime=n&service.state.state_type=soft&columns=service.state.last_update,service.state.is_reachable&format=json'} From 1628330d02241a569f775d66f4f405094d28ab08 Mon Sep 17 00:00:00 2001 From: Jan Kantert <jan-github2@kantert.net> Date: Thu, 15 Jun 2023 23:17:55 +0200 Subject: [PATCH 622/884] support reading icinga checks via notification api This API will respect the Icinga notification logic with times, escalations etc. This allows more complex ops setup with one team getting paged first and then an escalation to another team and much more. --- Nagstamon/Config.py | 4 + Nagstamon/QUI/__init__.py | 6 +- Nagstamon/Servers/Generic.py | 4 + Nagstamon/Servers/IcingaDBWebNotifications.py | 217 ++++++++++++++++++ Nagstamon/Servers/__init__.py | 6 + Nagstamon/resources/qui/settings_server.ui | 40 ++++ 6 files changed, 276 insertions(+), 1 deletion(-) create mode 100644 Nagstamon/Servers/IcingaDBWebNotifications.py diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index dbf7c9e65..2f433232f 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -969,6 +969,10 @@ def __init__(self): self.map_to_unknown = 'unknown' self.map_to_ok = 'ok' + # IcingaDBWebNotificationsServer + self.notification_filter = "user.name=*" + self.notification_lookback = "30 minutes" + class Action(object): """ class for custom actions, which whill be thrown into one config dictionary like the servers diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 29fcfa065..d86e55a81 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -5790,7 +5790,11 @@ def __init__(self, dialog): self.window.label_map_to_warning: ['Alertmanager'], self.window.input_lineedit_map_to_warning: ['Alertmanager'], self.window.label_map_to_critical: ['Alertmanager'], - self.window.input_lineedit_map_to_critical: ['Alertmanager'] + self.window.input_lineedit_map_to_critical: ['Alertmanager'], + self.window.input_lineedit_notification_filter: ['IcingaDBWebNotifications'], + self.window.label_notification_filter: ['IcingaDBWebNotifications'], + self.window.input_lineedit_notification_lookback: ['IcingaDBWebNotifications'], + self.window.label_notification_lookback: ['IcingaDBWebNotifications'], } # to be used when selecting authentication method Kerberos diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index f4a30846d..687ff6758 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -258,6 +258,10 @@ def __init__(self, **kwds): # Zabbix self.use_description_name_service = None + # IcingaDBWebNotifications + self.notification_filter = None + self.notification_lookback = None + def init_config(self): ''' set URLs for CGI - they are static and there is no need to set them with every cycle diff --git a/Nagstamon/Servers/IcingaDBWebNotifications.py b/Nagstamon/Servers/IcingaDBWebNotifications.py new file mode 100644 index 000000000..4ee787ea9 --- /dev/null +++ b/Nagstamon/Servers/IcingaDBWebNotifications.py @@ -0,0 +1,217 @@ +# encoding: utf-8 + +# Nagstamon - Nagios status monitor for your desktop +# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +# Initial implementation by Marcus Mönnig +# +# This Server class connects against IcingaWeb2. The monitor URL in the setup should be +# something like http://icinga2/icingaweb2 +# +# Status/TODOs: +# +# * The IcingaWeb2 API is not implemented yet, so currently this implementation uses +# two HTTP requests per action. The first fetches the HTML, then the form data is extracted and +# then a second HTTP POST request is made which actually executed the action. +# Once IcingaWeb2 has an API, it's probably the better choice. + + +from Nagstamon.Servers.Generic import GenericServer +import urllib.parse +import sys +import json +import datetime +import socket + +from bs4 import BeautifulSoup +from Nagstamon.Objects import (GenericHost, + GenericService, + Result) +from Nagstamon.Config import (conf, + AppInfo) +from Nagstamon.Helpers import webbrowser_open +from Nagstamon.Servers.IcingaDBWeb import IcingaDBWebServer + + +def strfdelta(tdelta, fmt): + d = {'days': tdelta.days} + d['hours'], rem = divmod(tdelta.seconds, 3600) + d['minutes'], d['seconds'] = divmod(rem, 60) + return fmt.format(**d) + + +class IcingaDBWebNotificationsServer(IcingaDBWebServer): + + """Read data from IcingaDB in IcingaWeb via the Notification endpoint.""" + + TYPE = 'IcingaDBWebNotifications' + + def _get_status(self) -> Result: + """Update internal status variables. + + This method updates self.new_host. It will not return any status. + It will return an empty Result object on success or a Result with an error on error. + """ + try: + return self._update_new_host_content() + except: + import traceback + traceback.print_exc(file=sys.stdout) + + # set checking flag back to False + self.isChecking = False + result, error = self.Error(sys.exc_info()) + return Result(result=result, error=error) + + def _update_new_host_content(self) -> Result: + """Update self.new_host based on icinga notifications.""" + notification_url = "{}/icingadb/notifications?{}&history.event_time>{} ago&format=json".format( + self.monitor_cgi_url, self.notification_filter, self.notification_lookback) + + result = self.FetchURL(notification_url, giveback='raw') + + # check if any error occured + potential_error = self.check_for_error(result.result, result.error, result.status_code) + if potential_error: + return potential_error + + self.new_hosts = {} + + notifications = json.loads(result.result) + + for notification in reversed(notifications): + if notification["type"] not in ("problem", "recovery"): + continue + if notification["object_type"] == "host": + # host + if not self.use_display_name_host: + # according to http://sourceforge.net/p/nagstamon/bugs/83/ it might + # better be name instead of display_name + host_name = notification['host']['name'] + else: + # https://github.com/HenriWahl/Nagstamon/issues/46 on the other hand has + # problems with that so here we go with extra display_name option + host_name = notification['host']['display_name'] + + status_type = notification['host']["state_type"] + self.new_hosts[host_name] = GenericHost() + self.new_hosts[host_name].name = host_name + self.new_hosts[host_name].server = self.name + self.new_hosts[host_name].status_type = status_type + + if status_type == 'hard': + self.new_hosts[host_name].status = self.STATES_MAPPING['hosts'][int(notification['host']['state']['hard_state'])] + else: + self.new_hosts[host_name].status = self.STATES_MAPPING['hosts'][int(notification['host']['state']['soft_state'])] + + self.new_hosts[host_name].last_check = datetime.datetime.fromtimestamp(int(float(notification['host']['state']['last_update']))) + self.new_hosts[host_name].attempt = "{}/{}".format(notification['host']['state']['check_attempt'],notification['host']['max_check_attempts']) + self.new_hosts[host_name].status_information = BeautifulSoup(notification['host']['state']['output'].replace('\n', ' ').strip(), 'html.parser').text + self.new_hosts[host_name].passiveonly = not int(notification['host'].get('active_checks_enabled') or '0') + self.new_hosts[host_name].notifications_disabled = not int(notification['host'].get('notifications_enabled') or '0') + self.new_hosts[host_name].flapping = bool(int(notification['host']['state']['is_flapping'] or 0)) + #s['state']['is_acknowledged'] can be null, 0, 1, or 'sticky' + self.new_hosts[host_name].acknowledged = bool(int(notification['host']['state']['is_acknowledged'].replace('sticky', '1') or 0)) + self.new_hosts[host_name].scheduled_downtime = bool(int(notification['host']['state']['in_downtime'] or 0)) + + # extra Icinga properties to solve https://github.com/HenriWahl/Nagstamon/issues/192 + # acknowledge needs host_description and no display name + self.new_hosts[host_name].real_name = notification['host']['name'] + + # Icinga only updates the attempts for soft states. When hard state is reached, a flag is set and + # attemt is set to 1/x. + if status_type == 'hard': + try: + self.new_hosts[host_name].attempt = "{0}/{0}".format(notification['host']['max_check_attempts']) + except Exception: + self.new_hosts[host_name].attempt = "HARD" + + # extra duration needed for calculation + if notification['host']['state']['last_state_change'] is not None and notification['host']['state']['last_state_change'] != 0: + duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(float(notification['host']['state']['last_state_change']))) + self.new_hosts[host_name].duration = strfdelta(duration,'{days}d {hours}h {minutes}m {seconds}s') + else: + self.new_hosts[host_name].duration = 'n/a' + elif notification["object_type"] == "service": + if not self.use_display_name_host: + # according to http://sourceforge.net/p/nagstamon/bugs/83/ it might + # better be name instead of display_name + host_name = notification['host']['name'] + else: + # https://github.com/HenriWahl/Nagstamon/issues/46 on the other hand has + # problems with that so here we go with extra display_name option + host_name = notification['host']['display_name'] + + # host objects contain service objects + if not host_name in self.new_hosts: + self.new_hosts[host_name] = GenericHost() + self.new_hosts[host_name].name = host_name + self.new_hosts[host_name].status = 'UP' + # extra Icinga properties to solve https://github.com/HenriWahl/Nagstamon/issues/192 + # acknowledge needs host_description and no display name + self.new_hosts[host_name].real_name = notification['host']['name'] + + service_name = notification['service']['display_name'] + + status_type = notification['service']["state"]["state_type"] + + # if a service does not exist create its object + self.new_hosts[host_name].services[service_name] = GenericService() + self.new_hosts[host_name].services[service_name].host = host_name + self.new_hosts[host_name].services[service_name].name = service_name + self.new_hosts[host_name].services[service_name].server = self.name + self.new_hosts[host_name].services[service_name].status_type = status_type + if status_type == 'hard': + self.new_hosts[host_name].services[service_name].status = self.STATES_MAPPING['services'][int(notification['service']['state']['hard_state'])] + else: + self.new_hosts[host_name].services[service_name].status = self.STATES_MAPPING['services'][int(notification['service']['state']['soft_state'])] + self.new_hosts[host_name].services[service_name].last_check = datetime.datetime.fromtimestamp(int(float(notification['service']['state']['last_update']))) + self.new_hosts[host_name].services[service_name].status_information = BeautifulSoup(notification['service']['state']['output'].replace('\n', ' ').strip(), 'html.parser').text + self.new_hosts[host_name].services[service_name].passiveonly = not int(notification['service'].get('active_checks_enabled') or '0') + self.new_hosts[host_name].services[service_name].notifications_disabled = not int(notification['service'].get('notifications_enabled') or '0') + self.new_hosts[host_name].services[service_name].flapping = bool(int(notification['service']['state']['is_flapping'] or 0)) + #s['state']['is_acknowledged'] can be null, 0, 1, or 'sticky' + self.new_hosts[host_name].services[service_name].acknowledged = bool(int(notification['service']['state']['is_acknowledged'].replace('sticky', '1') or 0)) + self.new_hosts[host_name].services[service_name].scheduled_downtime = bool(int(notification['service']['state']['in_downtime'] or 0)) + self.new_hosts[host_name].services[service_name].unreachable = not bool(int(notification['service']['state']['is_reachable'] or 0)) + + if self.new_hosts[host_name].services[service_name].unreachable: + self.new_hosts[host_name].services[service_name].status_information += " (SERVICE UNREACHABLE)" + + # extra Icinga properties to solve https://github.com/HenriWahl/Nagstamon/issues/192 + # acknowledge needs service_description and no display name + self.new_hosts[host_name].services[service_name].real_name = notification['service']['name'] + + if status_type == 'hard': + # Icinga only updates the attempts for soft states. When hard state is reached, a flag is set and + # attempt is set to 1/x. + self.new_hosts[host_name].services[service_name].attempt = "{0}/{0}".format( + notification['service']['max_check_attempts']) + else: + self.new_hosts[host_name].services[service_name].attempt = "{}/{}".format( + notification['service']['state']['check_attempt'], + notification['service']['max_check_attempts']) + + # extra duration needed for calculation + if notification['service']['state']['last_state_change'] is not None and notification['service']['state']['last_state_change'] != 0: + duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(float(notification['service']['state']['last_state_change']))) + self.new_hosts[host_name].services[service_name].duration = strfdelta(duration, '{days}d {hours}h {minutes}m {seconds}s') + else: + self.new_hosts[host_name].services[service_name].duration = 'n/a' + + # return success + return Result() \ No newline at end of file diff --git a/Nagstamon/Servers/__init__.py b/Nagstamon/Servers/__init__.py index 9ee1f3add..51f57914e 100644 --- a/Nagstamon/Servers/__init__.py +++ b/Nagstamon/Servers/__init__.py @@ -38,6 +38,7 @@ from Nagstamon.Servers.Icinga import IcingaServer from Nagstamon.Servers.IcingaWeb2 import IcingaWeb2Server from Nagstamon.Servers.IcingaDBWeb import IcingaDBWebServer +from Nagstamon.Servers.IcingaDBWebNotifications import IcingaDBWebNotificationsServer from Nagstamon.Servers.Multisite import MultisiteServer from Nagstamon.Servers.op5Monitor import Op5MonitorServer from Nagstamon.Servers.Opsview import OpsviewServer @@ -190,6 +191,10 @@ def create_server(server=None): # IcingaWeb2 new_server.no_cookie_auth = server.no_cookie_auth + # IcingaDBWebNotifications + new_server.notification_filter = server.notification_filter + new_server.notification_lookback = server.notification_lookback + # Checkmk Multisite new_server.force_authuser = server.force_authuser new_server.checkmk_view_hosts = server.checkmk_view_hosts @@ -237,6 +242,7 @@ def create_server(server=None): CentreonServer, IcingaServer, IcingaDBWebServer, + IcingaDBWebNotificationsServer, IcingaWeb2Server, LivestatusServer, Monitos3Server, diff --git a/Nagstamon/resources/qui/settings_server.ui b/Nagstamon/resources/qui/settings_server.ui index f491a4f7b..170f57e2c 100644 --- a/Nagstamon/resources/qui/settings_server.ui +++ b/Nagstamon/resources/qui/settings_server.ui @@ -443,6 +443,20 @@ </property> </widget> </item> + <item row="11" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_notification_filter"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="12" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_notification_lookback"> + <property name="text"> + <string/> + </property> + </widget> + </item> <item row="6" column="1"> <widget class="QLabel" name="label_timeout"> <property name="text"> @@ -516,6 +530,32 @@ </property> </widget> </item> + <item row="11" column="1"> + <widget class="QLabel" name="label_notification_filter"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Notification filter:</string> + </property> + </widget> + </item> + <item row="12" column="1"> + <widget class="QLabel" name="label_notification_lookback"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Notification lookback horizon:</string> + </property> + </widget> + </item> <item row="6" column="2"> <layout class="QHBoxLayout" name="horizontalLayout_timeout_seconds"> <property name="spacing"> From d29ccc81508bf8d301f79f67257721d000cf391b Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 17 Jun 2023 10:58:37 +0200 Subject: [PATCH 623/884] fix setup.py for non-Linux --- setup.py | 67 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/setup.py b/setup.py index 271072e3d..820a0f621 100644 --- a/setup.py +++ b/setup.py @@ -87,39 +87,40 @@ applications_shortcut=False) # older Fedora needs Qt5 -if DIST.lower() == 'fedora' and int(DIST_VERSION) < 36 or \ - DIST.lower() == 'rhel' and int(DIST_VERSION) <= 9: - bdist_rpm_options = dict(requires='python3 ' - 'python3-beautifulsoup4 ' - 'python3-cryptography ' - 'python3-dateutil ' - 'python3-keyring ' - 'python3-lxml ' - 'python3-psutil ' - 'python3-pysocks ' - 'python3-qt5 ' - 'python3-requests ' - 'python3-requests-kerberos ' - 'python3-SecretStorage ' - 'qt5-qtmultimedia ' - 'qt5-qtsvg ', - dist_dir='./build') -else: - bdist_rpm_options = dict(requires='python3 ' - 'python3-beautifulsoup4 ' - 'python3-cryptography ' - 'python3-dateutil ' - 'python3-keyring ' - 'python3-lxml ' - 'python3-psutil ' - 'python3-pysocks ' - 'python3-pyqt6 ' - 'python3-requests ' - 'python3-requests-kerberos ' - 'python3-SecretStorage ' - 'qt6-qtmultimedia ' - 'qt6-qtsvg ', - dist_dir='./build') +if OS not in ['Windows', 'Darwin']: + if DIST.lower() == 'fedora' and int(DIST_VERSION) < 36 or \ + DIST.lower() == 'rhel' and int(DIST_VERSION) <= 9: + bdist_rpm_options = dict(requires='python3 ' + 'python3-beautifulsoup4 ' + 'python3-cryptography ' + 'python3-dateutil ' + 'python3-keyring ' + 'python3-lxml ' + 'python3-psutil ' + 'python3-pysocks ' + 'python3-qt5 ' + 'python3-requests ' + 'python3-requests-kerberos ' + 'python3-SecretStorage ' + 'qt5-qtmultimedia ' + 'qt5-qtsvg ', + dist_dir='./build') + else: + bdist_rpm_options = dict(requires='python3 ' + 'python3-beautifulsoup4 ' + 'python3-cryptography ' + 'python3-dateutil ' + 'python3-keyring ' + 'python3-lxml ' + 'python3-psutil ' + 'python3-pysocks ' + 'python3-pyqt6 ' + 'python3-requests ' + 'python3-requests-kerberos ' + 'python3-SecretStorage ' + 'qt6-qtmultimedia ' + 'qt6-qtsvg ', + dist_dir='./build') setup(name=NAME, version=VERSION, From 679540e83d6e35af2f9d2e3c0dfcf09501e9cd36 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 2 Jul 2023 17:58:17 +0200 Subject: [PATCH 624/884] experimenting with Wayland --- Nagstamon/Config.py | 4 +++- build/debian/changelog | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 2f433232f..23e5f4a3b 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -52,6 +52,8 @@ # simple Wayland detection if 'WAYLAND_DISPLAY' in os.environ or\ 'XDG_SESSION_TYPE' in os.environ and os.environ['XDG_SESSION_TYPE'] == 'wayland': + # dirty workaround to activate X11 support in Wayland environment - just a test + os.environ['QT_QPA_PLATFORM'] = 'xcb' DESKTOP_WAYLAND = True else: DESKTOP_WAYLAND = False @@ -121,7 +123,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.13-20230506' + VERSION = '3.13-20230702' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2023 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 4489fe494..1e69a6d6a 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.13-20230609) unstable; urgency=low +nagstamon (3.13-20230702) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Fri, June 09 2023 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Sun, July 02 2023 08:00:00 +0200 nagstamon (3.12.0) stable; urgency=low * New upstream From 990abedc1b742673bff9aac3a9c9ba3217330e81 Mon Sep 17 00:00:00 2001 From: Jan Kantert <jan-github2@kantert.net> Date: Mon, 3 Jul 2023 17:56:12 +0200 Subject: [PATCH 625/884] handle cornercase: icinga reports status 99 when check is pending initially this fixes a crash when a downtime is removed on a pending check --- Nagstamon/Servers/IcingaDBWebNotifications.py | 49 +++++++++++++------ 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWebNotifications.py b/Nagstamon/Servers/IcingaDBWebNotifications.py index 4ee787ea9..2c98987c1 100644 --- a/Nagstamon/Servers/IcingaDBWebNotifications.py +++ b/Nagstamon/Servers/IcingaDBWebNotifications.py @@ -94,8 +94,6 @@ def _update_new_host_content(self) -> Result: notifications = json.loads(result.result) for notification in reversed(notifications): - if notification["type"] not in ("problem", "recovery"): - continue if notification["object_type"] == "host": # host if not self.use_display_name_host: @@ -108,16 +106,25 @@ def _update_new_host_content(self) -> Result: host_name = notification['host']['display_name'] status_type = notification['host']["state_type"] + + if status_type == 'hard': + status_numeric = int(notification['host']['state']['hard_state']) + else: + status_numeric = int(notification['host']['state']['soft_state']) + + if status_numeric not in (1, 2): + try: + del self.new_hosts[host_name] + except KeyError: + pass + continue + self.new_hosts[host_name] = GenericHost() self.new_hosts[host_name].name = host_name self.new_hosts[host_name].server = self.name self.new_hosts[host_name].status_type = status_type - if status_type == 'hard': - self.new_hosts[host_name].status = self.STATES_MAPPING['hosts'][int(notification['host']['state']['hard_state'])] - else: - self.new_hosts[host_name].status = self.STATES_MAPPING['hosts'][int(notification['host']['state']['soft_state'])] - + self.new_hosts[host_name].status = self.STATES_MAPPING['hosts'][status_numeric] self.new_hosts[host_name].last_check = datetime.datetime.fromtimestamp(int(float(notification['host']['state']['last_update']))) self.new_hosts[host_name].attempt = "{}/{}".format(notification['host']['state']['check_attempt'],notification['host']['max_check_attempts']) self.new_hosts[host_name].status_information = BeautifulSoup(notification['host']['state']['output'].replace('\n', ' ').strip(), 'html.parser').text @@ -156,6 +163,23 @@ def _update_new_host_content(self) -> Result: # problems with that so here we go with extra display_name option host_name = notification['host']['display_name'] + status_type = notification['service']["state"]["state_type"] + service_name = notification['service']['display_name'] + + if status_type == 'hard': + status_numeric = int(notification['service']['state']['hard_state']) + else: + status_numeric = int(notification['service']['state']['soft_state']) + + if status_numeric not in (1, 2, 3): + try: + del self.new_hosts[host_name].services[service_name] + if not self.new_hosts[host_name].services: + del self.new_hosts[host_name] + except KeyError: + pass + continue + # host objects contain service objects if not host_name in self.new_hosts: self.new_hosts[host_name] = GenericHost() @@ -165,20 +189,15 @@ def _update_new_host_content(self) -> Result: # acknowledge needs host_description and no display name self.new_hosts[host_name].real_name = notification['host']['name'] - service_name = notification['service']['display_name'] - - status_type = notification['service']["state"]["state_type"] - # if a service does not exist create its object self.new_hosts[host_name].services[service_name] = GenericService() self.new_hosts[host_name].services[service_name].host = host_name self.new_hosts[host_name].services[service_name].name = service_name self.new_hosts[host_name].services[service_name].server = self.name self.new_hosts[host_name].services[service_name].status_type = status_type - if status_type == 'hard': - self.new_hosts[host_name].services[service_name].status = self.STATES_MAPPING['services'][int(notification['service']['state']['hard_state'])] - else: - self.new_hosts[host_name].services[service_name].status = self.STATES_MAPPING['services'][int(notification['service']['state']['soft_state'])] + + self.new_hosts[host_name].services[service_name].status = self.STATES_MAPPING['services'][status_numeric] + self.new_hosts[host_name].services[service_name].last_check = datetime.datetime.fromtimestamp(int(float(notification['service']['state']['last_update']))) self.new_hosts[host_name].services[service_name].status_information = BeautifulSoup(notification['service']['state']['output'].replace('\n', ' ').strip(), 'html.parser').text self.new_hosts[host_name].services[service_name].passiveonly = not int(notification['service'].get('active_checks_enabled') or '0') From 62db42a88bd692f283fd9a4705376e92a5460619 Mon Sep 17 00:00:00 2001 From: Jan Kantert <jan-github2@kantert.net> Date: Mon, 3 Jul 2023 18:01:13 +0200 Subject: [PATCH 626/884] fix crash when opening a check which just recovered (or got removed) --- Nagstamon/Servers/IcingaDBWeb.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index 3f9af0c92..b46ff038b 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -734,9 +734,14 @@ def get_start_end(self, host): def open_monitor(self, host, service=''): - ''' - open monitor from tablewidget context menu - ''' + """Open monitor from tablewidget context menu.""" + if host not in self.hosts: + print("Cannot find {}. Skipping!".format(host)) + return + if service and service not in self.hosts[host].services: + print("Cannot find {}::{}. Skipping!".format(host, service)) + return + # only type is important so do not care of service '' in case of host monitor if service == '': url = '{0}/icingadb/hosts?host.state.is_problem=y&sort=host.state.severity#!{1}/icingadb/host?{2}'.format(self.monitor_url, From b2badfc2b6cd80e01151dc4d33daa0cc138d5cf8 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Wed, 5 Jul 2023 19:32:44 +0200 Subject: [PATCH 627/884] fixed status information filter --- Nagstamon/Config.py | 2 +- Nagstamon/QUI/__init__.py | 12 +++++++----- build/debian/changelog | 4 ++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 23e5f4a3b..886fd81f8 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -123,7 +123,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.13-20230702' + VERSION = '3.13-20230705' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2023 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index d86e55a81..66611ed1a 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -3459,7 +3459,9 @@ def cell_clicked(self): # take data from model data_array miserable_host = self.model().data_array[lrow][0] miserable_service = self.model().data_array[lrow][2] - + miserable_duration = self.model().data_array[lrow][6] + miserable_attempt = self.model().data_array[lrow][7] + miserable_status_information = self.model().data_array[lrow][8] # check if clicked line is a service or host # if it is check if the action is targeted on hosts or services if miserable_service: @@ -3478,25 +3480,25 @@ def cell_clicked(self): item_visible_temporary = True # dito if action.re_status_information_enabled is True: - if is_found_by_re(miserable_service, + if is_found_by_re(miserable_status_information, action.re_status_information_pattern, action.re_status_information_reverse): item_visible_temporary = True # dito if action.re_duration_enabled is True: - if is_found_by_re(miserable_service, + if is_found_by_re(miserable_duration, action.re_duration_pattern, action.re_duration_reverse): item_visible_temporary = True # dito if action.re_attempt_enabled is True: - if is_found_by_re(miserable_service, + if is_found_by_re(miserable_attempt, action.re_attempt_pattern, action.re_attempt_reverse): item_visible_temporary = True - # dito + # dito - how is this supposed to work? if action.re_groups_enabled is True: if is_found_by_re(miserable_service, action.re_groups_pattern, diff --git a/build/debian/changelog b/build/debian/changelog index 1e69a6d6a..2e4fa8c8d 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.13-20230702) unstable; urgency=low +nagstamon (3.13-20230705) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Sun, July 02 2023 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Wed, July 05 2023 08:00:00 +0200 nagstamon (3.12.0) stable; urgency=low * New upstream From 607090e754e141d8142cb366675c23081dfc8d6b Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 6 Jul 2023 12:55:23 +0200 Subject: [PATCH 628/884] pyqt6==6.4.2 for macOS --- build/requirements/macos.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index 696e2320e..a9f4f4ee8 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -5,7 +5,7 @@ lxml psutil pyinstaller pyobjc-framework-ApplicationServices -pyqt6==6.5.1 +pyqt6==6.4.2 pysocks python-dateutil requests From 1985cb40c6b7c8bd2954a893945af734a51bbce7 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 6 Jul 2023 13:15:45 +0200 Subject: [PATCH 629/884] pyqt6==6.4.2 for macOS comment --- build/requirements/macos.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index a9f4f4ee8..d6fa8421e 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -5,6 +5,7 @@ lxml psutil pyinstaller pyobjc-framework-ApplicationServices +# 6.5.1 leads to crash on macOS pyqt6==6.4.2 pysocks python-dateutil From b54157ab0f57790cb362cbdac5760297dbeba4e7 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 6 Jul 2023 13:19:49 +0200 Subject: [PATCH 630/884] pyqt6==6.4.2 for Windows --- build/requirements/windows.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index 812c1de69..3a039225e 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -7,7 +7,7 @@ pip-system-certs psutil pyinstaller pypiwin32 -pyqt6==6.5.1 +pyqt6==6.4.2 pysocks python-dateutil requests From bb4ac6f4cd078fa005910e49837c0b9e694fb202 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 6 Jul 2023 14:18:36 +0200 Subject: [PATCH 631/884] pyqt6 6.5.1, Python 3.11.4 --- .github/workflows/build-release-latest.yml | 2 +- build/requirements/windows.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 55a51c7ef..3776821f3 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -8,7 +8,7 @@ on: - '!*.*.*' env: - python_win_version: 3.11.3 + python_win_version: 3.11.4 repo_dir: nagstamon-jekyll/docs/repo cr_image: ghcr.io/henriwahl/build-nagstamon # to be increased if new updates of build images are necessary diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index 3a039225e..812c1de69 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -7,7 +7,7 @@ pip-system-certs psutil pyinstaller pypiwin32 -pyqt6==6.4.2 +pyqt6==6.5.1 pysocks python-dateutil requests From a8aabc140b473ffcad74d688c176eab50c51f6ac Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Mon, 12 Jun 2023 23:52:01 +0200 Subject: [PATCH 632/884] Python 3.11.4 --- .github/workflows/build-release-latest.yml | 2 +- .github/workflows/build-release-stable.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 55a51c7ef..3776821f3 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -8,7 +8,7 @@ on: - '!*.*.*' env: - python_win_version: 3.11.3 + python_win_version: 3.11.4 repo_dir: nagstamon-jekyll/docs/repo cr_image: ghcr.io/henriwahl/build-nagstamon # to be increased if new updates of build images are necessary diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index 4ca88f805..567bebd95 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -4,7 +4,7 @@ on: tags: 'v*' env: - python_win_version: 3.11.3 + python_win_version: 3.11.4 repo_dir: nagstamon-jekyll/docs/repo cr_image: ghcr.io/henriwahl/build-nagstamon # to be increased if new updates of build images are necessary From cf656a392447b66d64dbcfef4809ef91509d1722 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 2 Jul 2023 17:58:17 +0200 Subject: [PATCH 633/884] experimenting with Wayland --- Nagstamon/Config.py | 4 +++- build/debian/changelog | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 2f433232f..23e5f4a3b 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -52,6 +52,8 @@ # simple Wayland detection if 'WAYLAND_DISPLAY' in os.environ or\ 'XDG_SESSION_TYPE' in os.environ and os.environ['XDG_SESSION_TYPE'] == 'wayland': + # dirty workaround to activate X11 support in Wayland environment - just a test + os.environ['QT_QPA_PLATFORM'] = 'xcb' DESKTOP_WAYLAND = True else: DESKTOP_WAYLAND = False @@ -121,7 +123,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.13-20230506' + VERSION = '3.13-20230702' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2023 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 4489fe494..1e69a6d6a 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.13-20230609) unstable; urgency=low +nagstamon (3.13-20230702) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Fri, June 09 2023 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Sun, July 02 2023 08:00:00 +0200 nagstamon (3.12.0) stable; urgency=low * New upstream From 6781c2e79615099e8ba1c69129be033e072baae6 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Wed, 5 Jul 2023 19:32:44 +0200 Subject: [PATCH 634/884] fixed status information filter --- Nagstamon/Config.py | 2 +- Nagstamon/QUI/__init__.py | 12 +++++++----- build/debian/changelog | 4 ++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 23e5f4a3b..886fd81f8 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -123,7 +123,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.13-20230702' + VERSION = '3.13-20230705' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2023 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index d86e55a81..66611ed1a 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -3459,7 +3459,9 @@ def cell_clicked(self): # take data from model data_array miserable_host = self.model().data_array[lrow][0] miserable_service = self.model().data_array[lrow][2] - + miserable_duration = self.model().data_array[lrow][6] + miserable_attempt = self.model().data_array[lrow][7] + miserable_status_information = self.model().data_array[lrow][8] # check if clicked line is a service or host # if it is check if the action is targeted on hosts or services if miserable_service: @@ -3478,25 +3480,25 @@ def cell_clicked(self): item_visible_temporary = True # dito if action.re_status_information_enabled is True: - if is_found_by_re(miserable_service, + if is_found_by_re(miserable_status_information, action.re_status_information_pattern, action.re_status_information_reverse): item_visible_temporary = True # dito if action.re_duration_enabled is True: - if is_found_by_re(miserable_service, + if is_found_by_re(miserable_duration, action.re_duration_pattern, action.re_duration_reverse): item_visible_temporary = True # dito if action.re_attempt_enabled is True: - if is_found_by_re(miserable_service, + if is_found_by_re(miserable_attempt, action.re_attempt_pattern, action.re_attempt_reverse): item_visible_temporary = True - # dito + # dito - how is this supposed to work? if action.re_groups_enabled is True: if is_found_by_re(miserable_service, action.re_groups_pattern, diff --git a/build/debian/changelog b/build/debian/changelog index 1e69a6d6a..2e4fa8c8d 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.13-20230702) unstable; urgency=low +nagstamon (3.13-20230705) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Sun, July 02 2023 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Wed, July 05 2023 08:00:00 +0200 nagstamon (3.12.0) stable; urgency=low * New upstream From 96a50b8d227bcaef70bb94e24b5568e077d7fef1 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 6 Jul 2023 12:55:23 +0200 Subject: [PATCH 635/884] pyqt6==6.4.2 for macOS --- build/requirements/macos.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index 696e2320e..a9f4f4ee8 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -5,7 +5,7 @@ lxml psutil pyinstaller pyobjc-framework-ApplicationServices -pyqt6==6.5.1 +pyqt6==6.4.2 pysocks python-dateutil requests From e32f4f7c8ed218156535a714c5a2d6a04db7c2a5 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 6 Jul 2023 13:15:45 +0200 Subject: [PATCH 636/884] pyqt6==6.4.2 for macOS comment --- build/requirements/macos.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index a9f4f4ee8..d6fa8421e 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -5,6 +5,7 @@ lxml psutil pyinstaller pyobjc-framework-ApplicationServices +# 6.5.1 leads to crash on macOS pyqt6==6.4.2 pysocks python-dateutil From 73b246c1baf54a6e90de50f38ff40d4920c8d1ea Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 6 Jul 2023 13:19:49 +0200 Subject: [PATCH 637/884] pyqt6==6.4.2 for Windows --- build/requirements/windows.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index 812c1de69..3a039225e 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -7,7 +7,7 @@ pip-system-certs psutil pyinstaller pypiwin32 -pyqt6==6.5.1 +pyqt6==6.4.2 pysocks python-dateutil requests From 5ab66f9d884d49309126c7cf02cfb3d0298d51ae Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 6 Jul 2023 14:18:36 +0200 Subject: [PATCH 638/884] pyqt6 6.5.1, Python 3.11.4 --- build/requirements/windows.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index 3a039225e..812c1de69 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -7,7 +7,7 @@ pip-system-certs psutil pyinstaller pypiwin32 -pyqt6==6.4.2 +pyqt6==6.5.1 pysocks python-dateutil requests From b6d2cd6918336849f686f4e4f1be30b8918f5221 Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Fri, 7 Jul 2023 18:22:33 +0200 Subject: [PATCH 639/884] comment pip-system-certs --- nagstamon.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/nagstamon.py b/nagstamon.py index aeb02d417..bdd6cbb4c 100755 --- a/nagstamon.py +++ b/nagstamon.py @@ -18,9 +18,6 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA -# according to https://gitlab.com/alelec/pip-system-certs/-/issues/7#note_1066992053 -#import pip_system_certs.wrapt_requests - import sys import socket @@ -32,6 +29,8 @@ from Nagstamon.Config import (conf, OS, OS_WINDOWS) + + # according to https://gitlab.com/alelec/pip-system-certs/-/issues/7#note_1066992053 if OS == OS_WINDOWS: import pip_system_certs.wrapt_requests From 279f563297e391395bd9b0f6c2d2121b423e9645 Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Fri, 7 Jul 2023 18:41:14 +0200 Subject: [PATCH 640/884] pinned pyqt6-qt6 pip package again --- build/requirements/linux.txt | 4 ++-- build/requirements/macos.txt | 1 + build/requirements/windows.txt | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/build/requirements/linux.txt b/build/requirements/linux.txt index 63b8646b2..e5b95c101 100644 --- a/build/requirements/linux.txt +++ b/build/requirements/linux.txt @@ -4,8 +4,8 @@ dbus-python keyring lxml psutil -pyqt6==6.4.2 -pyqt6-qt6==6.4.2 +pyqt6==6.5.1 +pyqt6-qt6==6.5.1 pysocks python-dateutil requests diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index d6fa8421e..cda41e2fc 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -7,6 +7,7 @@ pyinstaller pyobjc-framework-ApplicationServices # 6.5.1 leads to crash on macOS pyqt6==6.4.2 +pyqt6-qt6==6.4.2 pysocks python-dateutil requests diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index 812c1de69..d73e7b084 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -7,7 +7,8 @@ pip-system-certs psutil pyinstaller pypiwin32 -pyqt6==6.5.1 +pyqt6==6.4.2 +pyqt6-qt6==6.4.2 pysocks python-dateutil requests From 0e7445e337067f9e56775fea8fabac39a8dcac9a Mon Sep 17 00:00:00 2001 From: Jan Kantert <jan-github2@kantert.net> Date: Mon, 3 Jul 2023 17:56:12 +0200 Subject: [PATCH 641/884] handle cornercase: icinga reports status 99 when check is pending initially this fixes a crash when a downtime is removed on a pending check --- Nagstamon/Servers/IcingaDBWebNotifications.py | 49 +++++++++++++------ 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWebNotifications.py b/Nagstamon/Servers/IcingaDBWebNotifications.py index 4ee787ea9..2c98987c1 100644 --- a/Nagstamon/Servers/IcingaDBWebNotifications.py +++ b/Nagstamon/Servers/IcingaDBWebNotifications.py @@ -94,8 +94,6 @@ def _update_new_host_content(self) -> Result: notifications = json.loads(result.result) for notification in reversed(notifications): - if notification["type"] not in ("problem", "recovery"): - continue if notification["object_type"] == "host": # host if not self.use_display_name_host: @@ -108,16 +106,25 @@ def _update_new_host_content(self) -> Result: host_name = notification['host']['display_name'] status_type = notification['host']["state_type"] + + if status_type == 'hard': + status_numeric = int(notification['host']['state']['hard_state']) + else: + status_numeric = int(notification['host']['state']['soft_state']) + + if status_numeric not in (1, 2): + try: + del self.new_hosts[host_name] + except KeyError: + pass + continue + self.new_hosts[host_name] = GenericHost() self.new_hosts[host_name].name = host_name self.new_hosts[host_name].server = self.name self.new_hosts[host_name].status_type = status_type - if status_type == 'hard': - self.new_hosts[host_name].status = self.STATES_MAPPING['hosts'][int(notification['host']['state']['hard_state'])] - else: - self.new_hosts[host_name].status = self.STATES_MAPPING['hosts'][int(notification['host']['state']['soft_state'])] - + self.new_hosts[host_name].status = self.STATES_MAPPING['hosts'][status_numeric] self.new_hosts[host_name].last_check = datetime.datetime.fromtimestamp(int(float(notification['host']['state']['last_update']))) self.new_hosts[host_name].attempt = "{}/{}".format(notification['host']['state']['check_attempt'],notification['host']['max_check_attempts']) self.new_hosts[host_name].status_information = BeautifulSoup(notification['host']['state']['output'].replace('\n', ' ').strip(), 'html.parser').text @@ -156,6 +163,23 @@ def _update_new_host_content(self) -> Result: # problems with that so here we go with extra display_name option host_name = notification['host']['display_name'] + status_type = notification['service']["state"]["state_type"] + service_name = notification['service']['display_name'] + + if status_type == 'hard': + status_numeric = int(notification['service']['state']['hard_state']) + else: + status_numeric = int(notification['service']['state']['soft_state']) + + if status_numeric not in (1, 2, 3): + try: + del self.new_hosts[host_name].services[service_name] + if not self.new_hosts[host_name].services: + del self.new_hosts[host_name] + except KeyError: + pass + continue + # host objects contain service objects if not host_name in self.new_hosts: self.new_hosts[host_name] = GenericHost() @@ -165,20 +189,15 @@ def _update_new_host_content(self) -> Result: # acknowledge needs host_description and no display name self.new_hosts[host_name].real_name = notification['host']['name'] - service_name = notification['service']['display_name'] - - status_type = notification['service']["state"]["state_type"] - # if a service does not exist create its object self.new_hosts[host_name].services[service_name] = GenericService() self.new_hosts[host_name].services[service_name].host = host_name self.new_hosts[host_name].services[service_name].name = service_name self.new_hosts[host_name].services[service_name].server = self.name self.new_hosts[host_name].services[service_name].status_type = status_type - if status_type == 'hard': - self.new_hosts[host_name].services[service_name].status = self.STATES_MAPPING['services'][int(notification['service']['state']['hard_state'])] - else: - self.new_hosts[host_name].services[service_name].status = self.STATES_MAPPING['services'][int(notification['service']['state']['soft_state'])] + + self.new_hosts[host_name].services[service_name].status = self.STATES_MAPPING['services'][status_numeric] + self.new_hosts[host_name].services[service_name].last_check = datetime.datetime.fromtimestamp(int(float(notification['service']['state']['last_update']))) self.new_hosts[host_name].services[service_name].status_information = BeautifulSoup(notification['service']['state']['output'].replace('\n', ' ').strip(), 'html.parser').text self.new_hosts[host_name].services[service_name].passiveonly = not int(notification['service'].get('active_checks_enabled') or '0') From 6d8ffc5ae2df6b3c41933ce4e740cf52d4e1b2f9 Mon Sep 17 00:00:00 2001 From: Jan Kantert <jan-github2@kantert.net> Date: Mon, 3 Jul 2023 18:01:13 +0200 Subject: [PATCH 642/884] fix crash when opening a check which just recovered (or got removed) --- Nagstamon/Servers/IcingaDBWeb.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index 3f9af0c92..b46ff038b 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -734,9 +734,14 @@ def get_start_end(self, host): def open_monitor(self, host, service=''): - ''' - open monitor from tablewidget context menu - ''' + """Open monitor from tablewidget context menu.""" + if host not in self.hosts: + print("Cannot find {}. Skipping!".format(host)) + return + if service and service not in self.hosts[host].services: + print("Cannot find {}::{}. Skipping!".format(host, service)) + return + # only type is important so do not care of service '' in case of host monitor if service == '': url = '{0}/icingadb/hosts?host.state.is_problem=y&sort=host.state.severity#!{1}/icingadb/host?{2}'.format(self.monitor_url, From 15d20717ea9858064eda4ed9ada5fd60b8996b46 Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Mon, 12 Jun 2023 23:52:01 +0200 Subject: [PATCH 643/884] Python 3.11.4 --- .github/workflows/build-release-stable.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index 4ca88f805..567bebd95 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -4,7 +4,7 @@ on: tags: 'v*' env: - python_win_version: 3.11.3 + python_win_version: 3.11.4 repo_dir: nagstamon-jekyll/docs/repo cr_image: ghcr.io/henriwahl/build-nagstamon # to be increased if new updates of build images are necessary From 092227d6b719afa0263b48d86bbfe50ca533c93c Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Fri, 7 Jul 2023 18:22:33 +0200 Subject: [PATCH 644/884] comment pip-system-certs --- nagstamon.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/nagstamon.py b/nagstamon.py index aeb02d417..bdd6cbb4c 100755 --- a/nagstamon.py +++ b/nagstamon.py @@ -18,9 +18,6 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA -# according to https://gitlab.com/alelec/pip-system-certs/-/issues/7#note_1066992053 -#import pip_system_certs.wrapt_requests - import sys import socket @@ -32,6 +29,8 @@ from Nagstamon.Config import (conf, OS, OS_WINDOWS) + + # according to https://gitlab.com/alelec/pip-system-certs/-/issues/7#note_1066992053 if OS == OS_WINDOWS: import pip_system_certs.wrapt_requests From 7f2932ac0c19c8a97fedd3ff80f66ae9e0ed1d13 Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Fri, 7 Jul 2023 18:41:14 +0200 Subject: [PATCH 645/884] pinned pyqt6-qt6 pip package again --- build/requirements/linux.txt | 4 ++-- build/requirements/macos.txt | 1 + build/requirements/windows.txt | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/build/requirements/linux.txt b/build/requirements/linux.txt index 63b8646b2..e5b95c101 100644 --- a/build/requirements/linux.txt +++ b/build/requirements/linux.txt @@ -4,8 +4,8 @@ dbus-python keyring lxml psutil -pyqt6==6.4.2 -pyqt6-qt6==6.4.2 +pyqt6==6.5.1 +pyqt6-qt6==6.5.1 pysocks python-dateutil requests diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index d6fa8421e..cda41e2fc 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -7,6 +7,7 @@ pyinstaller pyobjc-framework-ApplicationServices # 6.5.1 leads to crash on macOS pyqt6==6.4.2 +pyqt6-qt6==6.4.2 pysocks python-dateutil requests diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index 812c1de69..d73e7b084 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -7,7 +7,8 @@ pip-system-certs psutil pyinstaller pypiwin32 -pyqt6==6.5.1 +pyqt6==6.4.2 +pyqt6-qt6==6.4.2 pysocks python-dateutil requests From b075334bdc7f346d520f6cffed982ee1a4c5e295 Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Fri, 7 Jul 2023 18:42:52 +0200 Subject: [PATCH 646/884] 3.13-20230707 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 886fd81f8..ca5a1f950 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -123,7 +123,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.13-20230705' + VERSION = '3.13-20230707' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2023 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 2e4fa8c8d..ac2d83a9b 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.13-20230705) unstable; urgency=low +nagstamon (3.13-20230707 unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Wed, July 05 2023 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Fri, July 07 2023 08:00:00 +0200 nagstamon (3.12.0) stable; urgency=low * New upstream From 366b59080cc29e9a6606421586964967b3506f82 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Fri, 7 Jul 2023 20:45:12 +0200 Subject: [PATCH 647/884] Update changelog --- build/debian/changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/debian/changelog b/build/debian/changelog index ac2d83a9b..aa8acb8bf 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.13-20230707 unstable; urgency=low +nagstamon (3.13-20230707) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Fri, July 07 2023 08:00:00 +0200 From c79764494136bd27d9dec65d6cb53d1db0eca5d9 Mon Sep 17 00:00:00 2001 From: Stonewall Jackson <stonewall@sacredheartsc.com> Date: Thu, 13 Jul 2023 17:25:30 -0400 Subject: [PATCH 648/884] fix 'NoneType is not subscriptable' error in Icinga2API When connecting to a certain Icinga instance, it seems that some checks do not have a value for last_check_result. This causes the following error in Nagstamon, with no results displayed: TypeError: 'NoneType' object is not subscriptable Enabling debug mode revealed that the error was thrown from the line modified in this commit. This commit wraps the assignment with a check for None to avoid this error. fixes #949 --- Nagstamon/Servers/Icinga2API.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Nagstamon/Servers/Icinga2API.py b/Nagstamon/Servers/Icinga2API.py index 4d1fd814d..1fa2ab339 100644 --- a/Nagstamon/Servers/Icinga2API.py +++ b/Nagstamon/Servers/Icinga2API.py @@ -161,9 +161,12 @@ def _get_status(self): new_service.attempt = "{}/{}".format( int(service['attrs']['check_attempt']), int(service['attrs']['max_check_attempts'])) + if service['attrs']['last_check_result'] is None: + new_service.status_information = 'UNKNOWN' + else: + new_service.status_information = service['attrs']['last_check_result']['output'] new_service.last_check = arrow.get(service['attrs']['last_check']).humanize() new_service.duration = arrow.get(service['attrs']['previous_state_change']).humanize() - new_service.status_information = service['attrs']['last_check_result']['output'] new_service.passiveonly = not(service['attrs']['enable_active_checks']) new_service.notifications_disabled = not(service['attrs']['enable_notifications']) new_service.flapping = service['attrs']['flapping'] From 83a971219eeee4d9ea8b8185b1f478761db2a42e Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Fri, 7 Jul 2023 18:42:52 +0200 Subject: [PATCH 649/884] 3.13-20230707 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 886fd81f8..ca5a1f950 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -123,7 +123,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.13-20230705' + VERSION = '3.13-20230707' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2023 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 2e4fa8c8d..ac2d83a9b 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.13-20230705) unstable; urgency=low +nagstamon (3.13-20230707 unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Wed, July 05 2023 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Fri, July 07 2023 08:00:00 +0200 nagstamon (3.12.0) stable; urgency=low * New upstream From 3aa0a4dceb5c9c557ed564e0c3275491d836d4c5 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Fri, 7 Jul 2023 20:45:12 +0200 Subject: [PATCH 650/884] Update changelog --- build/debian/changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/debian/changelog b/build/debian/changelog index ac2d83a9b..aa8acb8bf 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.13-20230707 unstable; urgency=low +nagstamon (3.13-20230707) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Fri, July 07 2023 08:00:00 +0200 From 3ed80fc8eafe5855806df91c9335fb62e0c4751e Mon Sep 17 00:00:00 2001 From: Jan Kantert <jan-github2@kantert.net> Date: Thu, 20 Jul 2023 23:00:45 +0200 Subject: [PATCH 651/884] add health check for icinga-db and icinga redis --- Nagstamon/Servers/IcingaDBWebNotifications.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWebNotifications.py b/Nagstamon/Servers/IcingaDBWebNotifications.py index 2c98987c1..3d1c356df 100644 --- a/Nagstamon/Servers/IcingaDBWebNotifications.py +++ b/Nagstamon/Servers/IcingaDBWebNotifications.py @@ -79,16 +79,26 @@ def _get_status(self) -> Result: def _update_new_host_content(self) -> Result: """Update self.new_host based on icinga notifications.""" - notification_url = "{}/icingadb/notifications?{}&history.event_time>{} ago&format=json".format( + notification_url = "{}/icingadb/notifications?{}&history.event_time>{} ago&format=json".format( self.monitor_cgi_url, self.notification_filter, self.notification_lookback) - + health_url = '{}/health?format=json'.format(self.monitor_cgi_url) result = self.FetchURL(notification_url, giveback='raw') - # check if any error occured + # check if any error occurred potential_error = self.check_for_error(result.result, result.error, result.status_code) if potential_error: return potential_error + # HEALTH CHECK + health_result = self.FetchURL(health_url, giveback='raw') + if health_result.status_code == 200: + # we already got check results so icinga is unlikely down. do not break it without need. + monitoring_health_results = json.loads(health_result.result) + if monitoring_health_results["status"] != "success": + errors = [e["message"] for e in monitoring_health_results["data"] if e["state"] != 0] + return Result(result="UNKNOWN", + error='Icinga2 not healthy: {}'.format("; ".join(errors))) + self.new_hosts = {} notifications = json.loads(result.result) From 28a0a7f5905ccce6d77d679e7433d97c78be9737 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 20 Jul 2023 20:52:47 +0200 Subject: [PATCH 652/884] use status_information for status hash too --- Nagstamon/Config.py | 5 +++-- Nagstamon/Objects.py | 2 +- build/debian/changelog | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index ca5a1f950..ffcbf3d5c 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -53,7 +53,8 @@ if 'WAYLAND_DISPLAY' in os.environ or\ 'XDG_SESSION_TYPE' in os.environ and os.environ['XDG_SESSION_TYPE'] == 'wayland': # dirty workaround to activate X11 support in Wayland environment - just a test - os.environ['QT_QPA_PLATFORM'] = 'xcb' + if not os.environ.get('QT_QPA_PLATFORM'): + os.environ['QT_QPA_PLATFORM'] = 'xcb' DESKTOP_WAYLAND = True else: DESKTOP_WAYLAND = False @@ -123,7 +124,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.13-20230707' + VERSION = '3.13-20230720' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2023 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/Objects.py b/Nagstamon/Objects.py index efc56678b..fe2b54f9d 100644 --- a/Nagstamon/Objects.py +++ b/Nagstamon/Objects.py @@ -165,7 +165,7 @@ def get_hash(self): """ return hash for event history tracking """ - return " ".join((self.server, self.site, self.host, self.name, self.status)) + return " ".join((self.server, self.site, self.host, self.name, self.status, self.status_information)) class Result(object): diff --git a/build/debian/changelog b/build/debian/changelog index aa8acb8bf..e8f9ad1e1 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.13-20230707) unstable; urgency=low +nagstamon (3.13-20230720) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Fri, July 07 2023 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Thu, July 20 2023 08:00:00 +0200 nagstamon (3.12.0) stable; urgency=low * New upstream From fa2767b86d39ca479ef9c58cc62b380a077fc0a9 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 20 Jul 2023 22:10:02 +0200 Subject: [PATCH 653/884] fix typos in comments --- Nagstamon/QUI/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 66611ed1a..a07266fac 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -4147,12 +4147,12 @@ def fill_data_array(self, sort_column, sort_order): if item.is_host(): if hash in self.server.events_history and \ self.server.events_history[hash] is True: - # second item in las data_array line is host flags + # second item in last data_array line is host flags self.data_array[-1][1] += 'N' else: if hash in self.server.events_history and \ self.server.events_history[hash] is True: - # fourth item in las data_array line is service flags + # fourth item in last data_array line is service flags self.data_array[-1][3] += 'N' # add text color as QBrush from status self.data_array[-1].append( From 6c2ce5179a190ca4927487cbc3e774b66fdc031c Mon Sep 17 00:00:00 2001 From: jr-timme <68227848+jr-timme@users.noreply.github.com> Date: Fri, 11 Aug 2023 13:54:27 +0200 Subject: [PATCH 654/884] don't filter out hosts/services with scheduled downtime Currently, `_get_status()` filters out all Hosts/Services with a scheduled downtime as there is already a filter available to filter this was removed --- Nagstamon/Servers/IcingaDBWeb.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index b46ff038b..a3edeab69 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -122,8 +122,8 @@ def _get_status(self): # define CGI URLs for hosts and services if self.cgiurl_hosts is None and self.cgiurl_services is None and self.cgiurl_monitoring_health is None: # services (unknown, warning or critical?) - self.cgiurl_services = {'hard': self.monitor_cgi_url + '/icingadb/services?service.state.is_problem=y&service.state.in_downtime=n&service.state.state_type=hard&columns=service.state.last_update,service.state.is_reachable&format=json', \ - 'soft': self.monitor_cgi_url + '/icingadb/services?service.state.is_problem=y&service.state.in_downtime=n&service.state.state_type=soft&columns=service.state.last_update,service.state.is_reachable&format=json'} + self.cgiurl_services = {'hard': self.monitor_cgi_url + '/icingadb/services?service.state.is_problem=y&service.state.state_type=hard&columns=service.state.last_update,service.state.is_reachable&format=json', \ + 'soft': self.monitor_cgi_url + '/icingadb/services?service.state.is_problem=y&service.state.state_type=soft&columns=service.state.last_update,service.state.is_reachable&format=json'} # hosts (up or down or unreachable) self.cgiurl_hosts = {'hard': self.monitor_cgi_url + '/icingadb/hosts?host.state.is_problem=y&host.state.state_type=hard&columns=host.state.last_update&format=json', \ 'soft': self.monitor_cgi_url + '/icingadb/hosts?host.state.is_problem=y&host.state.state_type=soft&columns=host.state.last_update&format=json'} From 5179f2a77e559999fd667553bc475fdbc9e83fc4 Mon Sep 17 00:00:00 2001 From: aleskxyz <39186039+aleskxyz@users.noreply.github.com> Date: Mon, 28 Aug 2023 11:18:55 +0200 Subject: [PATCH 655/884] Change inhibit query sting param to false --- Nagstamon/Servers/Alertmanager/alertmanagerserver.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Servers/Alertmanager/alertmanagerserver.py b/Nagstamon/Servers/Alertmanager/alertmanagerserver.py index 6ead148ed..cab61547e 100644 --- a/Nagstamon/Servers/Alertmanager/alertmanagerserver.py +++ b/Nagstamon/Servers/Alertmanager/alertmanagerserver.py @@ -37,9 +37,9 @@ class AlertmanagerServer(GenericServer): 'history': '$MONITOR$/#/alerts' } - API_PATH_ALERTS = "/api/v2/alerts" + API_PATH_ALERTS = "/api/v2/alerts?inhibited=false" API_PATH_SILENCES = "/api/v2/silences" - API_FILTERS = '?filter=' + API_FILTERS = '&filter=' # vars specific to alertmanager class map_to_hostname = '' From 5155c5d59df8f864b7e2a8327b615adf8111ff71 Mon Sep 17 00:00:00 2001 From: grzeg1 <grzeg1@users.noreply.github.com> Date: Wed, 6 Sep 2023 16:58:13 +0200 Subject: [PATCH 656/884] Zabbix requires downtime timestamps to be integers. --- Nagstamon/Servers/Zabbix.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Servers/Zabbix.py b/Nagstamon/Servers/Zabbix.py index a4bf338f2..6332be02c 100644 --- a/Nagstamon/Servers/Zabbix.py +++ b/Nagstamon/Servers/Zabbix.py @@ -559,10 +559,10 @@ def _set_downtime(self, hostname, service, author, comment, fixed, start_time, e hostids = [self.hosts[hostname].hostid] date = datetime.datetime.strptime(start_time, "%Y-%m-%d %H:%M") - stime = time.mktime(date.timetuple()) + stime = int(time.mktime(date.timetuple())) date = datetime.datetime.strptime(end_time, "%Y-%m-%d %H:%M") - etime = time.mktime(date.timetuple()) + etime = int(time.mktime(date.timetuple())) if conf.debug_mode is True: self.Debug(server=self.get_name(), From 59ba860f7f9ee02ae26f32931133ad79ab22b47b Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Wed, 20 Sep 2023 19:43:10 +0200 Subject: [PATCH 657/884] unhash status_information --- Nagstamon/Objects.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/Objects.py b/Nagstamon/Objects.py index fe2b54f9d..efc56678b 100644 --- a/Nagstamon/Objects.py +++ b/Nagstamon/Objects.py @@ -165,7 +165,7 @@ def get_hash(self): """ return hash for event history tracking """ - return " ".join((self.server, self.site, self.host, self.name, self.status, self.status_information)) + return " ".join((self.server, self.site, self.host, self.name, self.status)) class Result(object): From 93371aec41b471a2e7cf4e978ff326c75b38686e Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Wed, 20 Sep 2023 19:45:09 +0200 Subject: [PATCH 658/884] unhash status_information --- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index ffcbf3d5c..2e436e66a 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -124,7 +124,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.13-20230720' + VERSION = '3.13-20230921' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2023 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index e8f9ad1e1..0d927e0be 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.13-20230720) unstable; urgency=low +nagstamon (3.13-20230921) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Thu, July 20 2023 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Thu, Sept 21 2023 08:00:00 +0200 nagstamon (3.12.0) stable; urgency=low * New upstream From 98c9c1b0d6d7f261f26a58a7561b2f7b63fd6c1a Mon Sep 17 00:00:00 2001 From: Benoit Poulet <benoit.poulet@businessdecision.com> Date: Tue, 26 Sep 2023 17:14:18 +0200 Subject: [PATCH 659/884] Fixes for Nag. 3.13 and Centreon 23.04 --- Nagstamon/Servers/Centreon/CentreonAPI.py | 27 ++++++++++++++++------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/Nagstamon/Servers/Centreon/CentreonAPI.py b/Nagstamon/Servers/Centreon/CentreonAPI.py index ee50a635c..eee1343b6 100644 --- a/Nagstamon/Servers/Centreon/CentreonAPI.py +++ b/Nagstamon/Servers/Centreon/CentreonAPI.py @@ -78,7 +78,7 @@ def init_config(self): # check if any error occured errors_occured = self.check_for_error(data, error, status_code) - if errors_occured is not False: + if errors_occured is not None: return (errors_occured) self.centreon_version_major = int(data["web"]["major"]) @@ -95,8 +95,10 @@ def init_config(self): # RestAPI version if self.centreon_version_major == 21: self.restapi_version = "latest" - else: + elif self.centreon_version_major == 22: self.restapi_version = "v22.04" + else: + self.restapi_version = "v23.04" if conf.debug_mode is True: self.Debug(server='[' + self.get_name() + ']', debug='Centreon API version used : ' + self.restapi_version) @@ -160,7 +162,7 @@ def get_token(self): # check if any error occured errors_occured = self.check_for_error(data, error, status_code) - if errors_occured is not False: + if errors_occured is not None: return (errors_occured) token = data["security"]["token"] @@ -195,7 +197,7 @@ def GetHost(self, host): # check if any error occured errors_occured = self.check_for_error(data, error, status_code) - if errors_occured is not False: + if errors_occured is not None: return (errors_occured) fqdn = str(data["result"][0]["fqdn"]) @@ -230,7 +232,7 @@ def get_host_and_service_id(self, host, service=''): # check if any error occured errors_occured = self.check_for_error(data, error, status_code) - if errors_occured is not False: + if errors_occured is not None: return (errors_occured) host_id = data["result"][0]["id"] @@ -264,7 +266,7 @@ def get_host_and_service_id(self, host, service=''): # check if any error occured errors_occured = self.check_for_error(data, error, status_code) - if errors_occured is not False: + if errors_occured is not None: return (errors_occured) if host == "Meta_Services": @@ -334,7 +336,7 @@ def _get_status(self): # check if any error occured errors_occured = self.check_for_error(data, error, status_code) - if errors_occured is not False: + if errors_occured is not None: return (errors_occured) if data["meta"]["total"] == 0: @@ -391,7 +393,7 @@ def _get_status(self): # check if any error occured errors_occured = self.check_for_error(data, error, status_code) - if errors_occured is not False: + if errors_occured is not None: return (errors_occured) if data["meta"]["total"] == 0: @@ -549,6 +551,15 @@ def _set_recheck(self, host, service): ] } + # This new parameter was added in 23.04 + if self.restapi_version == "v23.04": + property_to_add = { + "check": { + "is_forced": True + } + } + rechecks.update(property_to_add) + try: # Host if service == '': From a92d5b3bb711550b355a72a8da93c89bf9fe9e9f Mon Sep 17 00:00:00 2001 From: "Andrew J. Hesford" <ajh@sideband.org> Date: Fri, 29 Sep 2023 15:46:19 -0400 Subject: [PATCH 660/884] Nagstamon.Helpers.get_distro: ignore lines missing an equal sign --- Nagstamon/Helpers.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Helpers.py b/Nagstamon/Helpers.py index 408ce4a9b..7b0de31d0 100644 --- a/Nagstamon/Helpers.py +++ b/Nagstamon/Helpers.py @@ -453,8 +453,9 @@ def get_distro(): os_release_dict = {} for line in os_release_file.read_text().splitlines(): if not line.startswith('#'): - key, value = line.split('=', 1) - os_release_dict[key] = value.strip('"').strip("'") + try: key, value = line.split('=', 1) + except ValueError: continue + else: os_release_dict[key] = value.strip('"').strip("'") # Since CentOS Linux got retired by Red Hat, there are various RHEL derivatives/clones; flow is: # CentOS Stream -> Red Hat Enterprise Linux -> (AlmaLinux, EuroLinux, Oracle Linux, Rocky Linux) # Goal of this hack is to rule them all as Red Hat Enterprise Linux, the baseline distribution. From 3e7dfc5a060a2e8e4701ce0857eeea32df6004b2 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Wed, 27 Sep 2023 08:32:46 +0200 Subject: [PATCH 661/884] 20230927 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 2e436e66a..29e0be2df 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -124,7 +124,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.13-20230921' + VERSION = '3.13-20230927' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2023 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 0d927e0be..ffa27aa14 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.13-20230921) unstable; urgency=low +nagstamon (3.13-20230927) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Thu, Sept 21 2023 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Wed, Sept 27 2023 08:00:00 +0200 nagstamon (3.12.0) stable; urgency=low * New upstream From a1826b1d255e917557aba95564bc3badb9a7a2d6 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 10 Oct 2023 20:16:03 +0200 Subject: [PATCH 662/884] 20231010 fedora39 qt6 6.5.2 --- .github/workflows/build-release-latest.yml | 12 ++++++------ Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- build/requirements/linux.txt | 4 ++-- build/requirements/macos.txt | 4 ++-- build/requirements/windows.txt | 4 ++-- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 3776821f3..a7da52b3f 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -8,7 +8,7 @@ on: - '!*.*.*' env: - python_win_version: 3.11.4 + python_win_version: 3.11.6 repo_dir: nagstamon-jekyll/docs/repo cr_image: ghcr.io/henriwahl/build-nagstamon # to be increased if new updates of build images are necessary @@ -69,7 +69,7 @@ jobs: retention-days: 1 if-no-files-found: error - fedora-35: + fedora-36: runs-on: ubuntu-latest needs: test steps: @@ -91,7 +91,7 @@ jobs: retention-days: 1 if-no-files-found: error - fedora-36: + fedora-37: runs-on: ubuntu-latest needs: test steps: @@ -113,7 +113,7 @@ jobs: retention-days: 1 if-no-files-found: error - fedora-37: + fedora-38: runs-on: ubuntu-latest needs: test steps: @@ -135,7 +135,7 @@ jobs: retention-days: 1 if-no-files-found: error - fedora-38: + fedora-39: runs-on: ubuntu-latest needs: test steps: @@ -277,7 +277,7 @@ jobs: repo-debian: runs-on: ubuntu-latest # try to avoid race condition and start uploading only after the last install package has been build - needs: [debian, fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, macos, windows-32, windows-64, windows-64-debug] + needs: [debian, fedora-36, fedora-37, fedora-38, fedora-39, rhel-9, macos, windows-32, windows-64, windows-64-debug] env: family: debian steps: diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 29e0be2df..3e86d896e 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -124,7 +124,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.13-20230927' + VERSION = '3.13-20231010' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2023 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index ffa27aa14..ba4ca1ff7 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.13-20230927) unstable; urgency=low +nagstamon (3.13-20231010) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Wed, Sept 27 2023 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Tue, Oct 10 2023 08:00:00 +0200 nagstamon (3.12.0) stable; urgency=low * New upstream diff --git a/build/requirements/linux.txt b/build/requirements/linux.txt index e5b95c101..9f4e3f992 100644 --- a/build/requirements/linux.txt +++ b/build/requirements/linux.txt @@ -4,8 +4,8 @@ dbus-python keyring lxml psutil -pyqt6==6.5.1 -pyqt6-qt6==6.5.1 +pyqt6==6.5.2 +pyqt6-qt6==6.5.2 pysocks python-dateutil requests diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index cda41e2fc..4e95f27aa 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -6,8 +6,8 @@ psutil pyinstaller pyobjc-framework-ApplicationServices # 6.5.1 leads to crash on macOS -pyqt6==6.4.2 -pyqt6-qt6==6.4.2 +pyqt6==6.5.2 +pyqt6-qt6==6.5.2 pysocks python-dateutil requests diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index d73e7b084..e6afea51d 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -7,8 +7,8 @@ pip-system-certs psutil pyinstaller pypiwin32 -pyqt6==6.4.2 -pyqt6-qt6==6.4.2 +pyqt6==6.5.2 +pyqt6-qt6==6.5.2 pysocks python-dateutil requests From 6ba0601dd29eb9a86ea2b8e6cf9e46a2b25f7055 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 10 Oct 2023 20:20:44 +0200 Subject: [PATCH 663/884] pyinstaller==5.13.2 --- build/requirements/macos.txt | 3 ++- build/requirements/windows.txt | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index 4e95f27aa..0e86b22e4 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -3,7 +3,8 @@ beautifulsoup4 keyring lxml psutil -pyinstaller +# last stable of the 5 series +pyinstaller==5.13.2 pyobjc-framework-ApplicationServices # 6.5.1 leads to crash on macOS pyqt6==6.5.2 diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index e6afea51d..c31f155f7 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -5,7 +5,8 @@ keyring lxml pip-system-certs psutil -pyinstaller +# last stable of the 5 series +pyinstaller==5.13.2 pypiwin32 pyqt6==6.5.2 pyqt6-qt6==6.5.2 From 29744339b668a1baa2d7009b5f7d36399a90c524 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 10 Oct 2023 20:27:39 +0200 Subject: [PATCH 664/884] Dockerfile-fedora-39 --- build/docker/Dockerfile-fedora-39 | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 build/docker/Dockerfile-fedora-39 diff --git a/build/docker/Dockerfile-fedora-39 b/build/docker/Dockerfile-fedora-39 new file mode 100644 index 000000000..8cf4f8e8a --- /dev/null +++ b/build/docker/Dockerfile-fedora-39 @@ -0,0 +1,25 @@ +FROM fedora:39 +LABEL maintainer=henri@nagstamon.de + +RUN dnf -y install createrepo_c \ + desktop-file-utils \ + git \ + python3 \ + python3-beautifulsoup4 \ + python3-cryptography \ + python3-dateutil \ + python3-devel \ + python3-keyring \ + python3-lxml \ + python3-psutil \ + python3-pyqt6 \ + python3-pyqt6-devel \ + python3-requests \ + python3-requests-kerberos \ + python3-SecretStorage \ + qt6-qtsvg \ + qt6-qtmultimedia \ + rpm-build + +CMD cd /nagstamon/build && \ + /usr/bin/python3 build.py From 04e884334ba7d7ebcb8c66f9bbd9836ccb68da2c Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 10 Oct 2023 21:10:15 +0200 Subject: [PATCH 665/884] KERBEROS_AVAILABLE --- Nagstamon/QUI/__init__.py | 26 +++++++++++++++++++++++++- Nagstamon/Servers/Generic.py | 10 +++++++--- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index a07266fac..f446bba8a 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -107,6 +107,28 @@ except ImportError: ECP_AVAILABLE = False +# flag to keep track of Kerberos availability +KERBEROS_AVAILABLE = False +if OS == OS_MACOS: + # requests_gssapi is newer but not available everywhere + try: + # extra imports needed to get it compiled on macOS + import numbers + import gssapi.raw.cython_converters + from requests_gssapi import HTTPSPNEGOAuth as HTTPSKerberos + KERBEROS_AVAILABLE = True + except ImportError: + print('No Kerberos available') +else: + # requests_gssapi is newer but not available everywhere + try: + # requests_gssapi needs installation of KfW - Kerberos for Windows + # requests_kerberoes doesn't + from requests_kerberos import HTTPKerberosAuth as HTTPSKerberos + KERBEROS_AVAILABLE = True + except ImportError: + print('No Kerberos available') + # since Qt6 HighDPI-awareness is default behaviour if QT_VERSION_MAJOR < 6: # enable HighDPI-awareness to avoid https://github.com/HenriWahl/Nagstamon/issues/618 @@ -5828,9 +5850,11 @@ def __init__(self, dialog): self.window.button_choose_custom_cert_ca_file.clicked.connect(self.choose_custom_cert_ca_file) # fill authentication combobox - self.window.input_combobox_authentication.addItems(['Basic', 'Digest', 'Kerberos', 'Bearer']) + self.window.input_combobox_authentication.addItems(['Basic', 'Digest', 'Bearer']) if ECP_AVAILABLE is True: self.window.input_combobox_authentication.addItems(['ECP']) + if KERBEROS_AVAILABLE is True: + self.window.input_combobox_authentication.addItems(['Kerberos']) # detect change of server type which leads to certain options shown or hidden self.window.input_combobox_type.activated.connect(self.toggle_type) diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index 0aca42590..cf3048513 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -61,6 +61,8 @@ OS_MACOS, RESOURCES) +# flag to keep track of Kerberos availability +KERBEROS_AVAILABLE = False if OS == OS_MACOS: # requests_gssapi is newer but not available everywhere try: @@ -68,16 +70,18 @@ import numbers import gssapi.raw.cython_converters from requests_gssapi import HTTPSPNEGOAuth as HTTPSKerberos + KERBEROS_AVAILABLE = True except ImportError: - from requests_kerberos import HTTPKerberosAuth as HTTPSKerberos + print('No Kerberos available') else: # requests_gssapi is newer but not available everywhere try: # requests_gssapi needs installation of KfW - Kerberos for Windows # requests_kerberoes doesn't from requests_kerberos import HTTPKerberosAuth as HTTPSKerberos + KERBEROS_AVAILABLE = True except ImportError: - from requests_gssapi import HTTPSPNEGOAuth as HTTPSKerberos + print('No Kerberos available') # disable annoying SubjectAltNameWarning warnings try: @@ -319,7 +323,7 @@ def create_session(self): session.auth = requests.auth.HTTPDigestAuth(self.username, self.password) elif self.authentication == 'ecp' and ECP_AVAILABLE: session.auth = HTTPECPAuth(self.idp_ecp_endpoint, username=self.username, password=self.password) - elif self.authentication == 'kerberos': + elif self.authentication == 'kerberos' and KERBEROS_AVAILABLE: session.auth = HTTPSKerberos() elif self.authentication == 'bearer': session.auth = BearerAuth(self.password) From 306a0296c2d176e2737e17e47362838b083069e5 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 10 Oct 2023 21:35:08 +0200 Subject: [PATCH 666/884] added comment https://github.com/pythongssapi/python-gssapi/issues/244#issuecomment-827404287 --- build/requirements/windows.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index c31f155f7..8013c89a4 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -14,6 +14,7 @@ pysocks python-dateutil requests requests-ecp +# maybe requests-gssapi might become usable with https://github.com/pythongssapi/python-gssapi/issues/244#issuecomment-827404287 requests-kerberos setuptools wheel From 17468a2a00a0884a846f8a7fbeb52f8c3c2ea463 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 10 Oct 2023 21:39:37 +0200 Subject: [PATCH 667/884] print(error) --- Nagstamon/QUI/__init__.py | 8 ++++---- Nagstamon/Servers/Generic.py | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index f446bba8a..6ee4a98fe 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -117,8 +117,8 @@ import gssapi.raw.cython_converters from requests_gssapi import HTTPSPNEGOAuth as HTTPSKerberos KERBEROS_AVAILABLE = True - except ImportError: - print('No Kerberos available') + except ImportError as error: + print(error) else: # requests_gssapi is newer but not available everywhere try: @@ -126,8 +126,8 @@ # requests_kerberoes doesn't from requests_kerberos import HTTPKerberosAuth as HTTPSKerberos KERBEROS_AVAILABLE = True - except ImportError: - print('No Kerberos available') + except ImportError as error: + print(error) # since Qt6 HighDPI-awareness is default behaviour if QT_VERSION_MAJOR < 6: diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index cf3048513..c541c1320 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -71,8 +71,8 @@ import gssapi.raw.cython_converters from requests_gssapi import HTTPSPNEGOAuth as HTTPSKerberos KERBEROS_AVAILABLE = True - except ImportError: - print('No Kerberos available') + except ImportError as error: + print(error) else: # requests_gssapi is newer but not available everywhere try: @@ -80,8 +80,8 @@ # requests_kerberoes doesn't from requests_kerberos import HTTPKerberosAuth as HTTPSKerberos KERBEROS_AVAILABLE = True - except ImportError: - print('No Kerberos available') + except ImportError as error: + print(error) # disable annoying SubjectAltNameWarning warnings try: From 72adabf3802807fb462400503a2b1bc284e90701 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 10 Oct 2023 21:59:48 +0200 Subject: [PATCH 668/884] trying hidden imports in pyinstaller --- build/build.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/build.py b/build/build.py index da833dd11..c8f1e6b0a 100644 --- a/build/build.py +++ b/build/build.py @@ -114,6 +114,8 @@ def winmain(): '--icon=..\\Nagstamon\\resources\\nagstamon.ico', '--name=Nagstamon', '--hidden-import=PyQt6.uic.plugins', + '--hidden-import=requests_kerberos', + '--hidden-import=spnego', '--hidden-import=win32timezone', GUI_MODE, '..\\nagstamon.py'], From 4537339b142f4972e38d81f50b89d2929912b697 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 10 Oct 2023 22:18:06 +0200 Subject: [PATCH 669/884] hidden imports in pyinstaller did not help --- build/build.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/build/build.py b/build/build.py index c8f1e6b0a..da833dd11 100644 --- a/build/build.py +++ b/build/build.py @@ -114,8 +114,6 @@ def winmain(): '--icon=..\\Nagstamon\\resources\\nagstamon.ico', '--name=Nagstamon', '--hidden-import=PyQt6.uic.plugins', - '--hidden-import=requests_kerberos', - '--hidden-import=spnego', '--hidden-import=win32timezone', GUI_MODE, '..\\nagstamon.py'], From 1f0f3970bbdda22a50c5677bd8ebe7e43c66269a Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Wed, 11 Oct 2023 01:50:46 +0200 Subject: [PATCH 670/884] harden version check --- Nagstamon/QUI/__init__.py | 6 +++++- Nagstamon/Servers/Multisite.py | 9 ++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 6ee4a98fe..5bf09653e 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -7010,7 +7010,11 @@ def check(self): giveback='raw', no_auth=True) # stop searching the available download URLs - if response.error == "" and not response.result.startswith('<'): + if response.error == "" and \ + not response.result.startswith('<') and \ + not '\n' in response.result and \ + len(response.result) > 5 and \ + response.result[0].isdigit(): latest_version = response.result.strip() break # ignore TLS error in case it was caused by requesting latest version - not important for monitoring diff --git a/Nagstamon/Servers/Multisite.py b/Nagstamon/Servers/Multisite.py index 79bf74a7b..1ac7ac650 100644 --- a/Nagstamon/Servers/Multisite.py +++ b/Nagstamon/Servers/Multisite.py @@ -179,18 +179,21 @@ def _get_url(self, url): # return '' # looks like cookieauth - elif content.startswith('<'): + elif content.startswith('<') or\ + '<!DOCTYPE html>' in content: self.CookieAuth = True # if first attempt login and then try to get data again if not self._is_auth_in_cookies(): self._get_cookie_login() result = self.FetchURL(url, 'raw') content, error = result.result, result.error - if content.startswith('<'): + if content.startswith('<') or\ + '<!DOCTYPE html>' in content: return '' # if finally still some <HTML> is sent this looks like a new login due to password change - if content.startswith('<'): + if content.startswith('<') or\ + '<!DOCTYPE html>' in content: self.refresh_authentication = True return '' From 11661386f6a673c6e0fe5ffb39e714b57ffe5c77 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 16 Oct 2023 08:43:21 +0200 Subject: [PATCH 671/884] test with pyqt 6.5.3 --- build/requirements/linux.txt | 4 ++-- build/requirements/macos.txt | 4 ++-- build/requirements/windows.txt | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build/requirements/linux.txt b/build/requirements/linux.txt index 9f4e3f992..8eb4fbc88 100644 --- a/build/requirements/linux.txt +++ b/build/requirements/linux.txt @@ -4,8 +4,8 @@ dbus-python keyring lxml psutil -pyqt6==6.5.2 -pyqt6-qt6==6.5.2 +pyqt6==6.5.3 +pyqt6-qt6==6.5.3 pysocks python-dateutil requests diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index 0e86b22e4..d66ca8055 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -7,8 +7,8 @@ psutil pyinstaller==5.13.2 pyobjc-framework-ApplicationServices # 6.5.1 leads to crash on macOS -pyqt6==6.5.2 -pyqt6-qt6==6.5.2 +pyqt6==6.5.3 +pyqt6-qt6==6.5.3 pysocks python-dateutil requests diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index 8013c89a4..13876c36c 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -8,8 +8,8 @@ psutil # last stable of the 5 series pyinstaller==5.13.2 pypiwin32 -pyqt6==6.5.2 -pyqt6-qt6==6.5.2 +pyqt6==6.5.3 +pyqt6-qt6==6.5.3 pysocks python-dateutil requests From ddf1f7b4189ecf321acb72b5b3efa38a252db0ff Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 16 Oct 2023 09:37:58 +0200 Subject: [PATCH 672/884] try latest Qt6 --- build/requirements/windows.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index 13876c36c..1b58efb72 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -8,6 +8,7 @@ psutil # last stable of the 5 series pyinstaller==5.13.2 pypiwin32 +# try latest Qt6 pyqt6==6.5.3 pyqt6-qt6==6.5.3 pysocks From 172b3306938355d8091c29ce30c8ccf6cdf741ea Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Mon, 16 Oct 2023 09:44:58 +0200 Subject: [PATCH 673/884] build new release --- build/requirements/windows.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index 1b58efb72..13876c36c 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -8,7 +8,6 @@ psutil # last stable of the 5 series pyinstaller==5.13.2 pypiwin32 -# try latest Qt6 pyqt6==6.5.3 pyqt6-qt6==6.5.3 pysocks From c13aaeb9433c4fb29f1cee417cf454744fe0e837 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Mon, 16 Oct 2023 09:51:47 +0200 Subject: [PATCH 674/884] build new release --- build/requirements/windows.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index 13876c36c..d456b3f81 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -8,6 +8,7 @@ psutil # last stable of the 5 series pyinstaller==5.13.2 pypiwin32 +# try newer version pyqt6==6.5.3 pyqt6-qt6==6.5.3 pysocks From fd2b0e1237103a5da0970ea1142ec5fa8b297ac2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20Fa=C3=9Fbender?= <robin@fassbender.io> Date: Mon, 30 Oct 2023 12:34:34 +0100 Subject: [PATCH 675/884] fixed issue with date string in new icingadb version fix should be backwards compatible fixed #962 --- Nagstamon/Servers/IcingaDBWeb.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index a3edeab69..589da4217 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -300,7 +300,12 @@ def _get_status(self): self.new_hosts[host_name].services[service_name].status = self.STATES_MAPPING['services'][int(s['state']['hard_state'])] else: self.new_hosts[host_name].services[service_name].status = self.STATES_MAPPING['services'][int(s['state']['soft_state'])] - self.new_hosts[host_name].services[service_name].last_check = datetime.datetime.fromtimestamp(int(float(s['state']['last_update']))) + + if s['state']['last_update'].isnumeric(): # new version of icingadb doesnt return unix timestamp + self.new_hosts[host_name].services[service_name].last_check = datetime.datetime.fromtimestamp(int(float(s['state']['last_update']))) + else: + self.new_hosts[host_name].services[service_name].last_check = datetime.datetime.fromisoformat(s['state']['last_update']) + self.new_hosts[host_name].services[service_name].attempt = "{}/{}".format(s['state']['check_attempt'],s['max_check_attempts']) self.new_hosts[host_name].services[service_name].status_information = BeautifulSoup(s['state']['output'].replace('\n', ' ').strip(), 'html.parser').text self.new_hosts[host_name].services[service_name].passiveonly = not int(s.get('active_checks_enabled') or '0') @@ -327,11 +332,17 @@ def _get_status(self): self.new_hosts[host_name].services[service_name].attempt = "HARD" # extra duration needed for calculation - if s['state']['last_state_change'] is not None and s['state']['last_state_change'] != 0: - duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(float(s['state']['last_state_change']))) - self.new_hosts[host_name].services[service_name].duration = strfdelta(duration, '{days}d {hours}h {minutes}m {seconds}s') - else: - self.new_hosts[host_name].services[service_name].duration = 'n/a' + if s['state']['last_state_change'] is not None: + if s['state']['last_update'].isnumeric(): # new version of icingadb doesnt return unix timestamp + duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(float(s['state']['last_state_change']))) + else: + last_state_change = datetime.datetime.fromisoformat(s['state']['last_state_change']) + duration = datetime.datetime.now().replace(tzinfo=last_state_change.tzinfo) - last_state_change + + if duration.total_seconds() > 0: + self.new_hosts[host_name].services[service_name].duration = strfdelta(duration, '{days}d {hours}h {minutes}m {seconds}s') + else: + self.new_hosts[host_name].services[service_name].duration = 'n/a' del s, host_name, service_name except: From 266b58e5769467950a5d7a7e74a2a07f25c146b4 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Mon, 30 Oct 2023 14:03:43 +0100 Subject: [PATCH 676/884] proxy / --- Nagstamon/Servers/Generic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index c541c1320..ca6933fa7 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -360,7 +360,7 @@ def proxify(self, requester): if self.proxy_username == self.proxy_password == '': user_pass = '' else: - user_pass = '{0}:{1}@'.format(self.proxy_username, self.proxy_password) + user_pass = '{0}:{1}'.format(self.proxy_username, self.proxy_password) # split and analyze proxy URL proxy_address_parts = self.proxy_address.split('//') @@ -370,7 +370,7 @@ def proxify(self, requester): # use only valid schemes if scheme.lower() in ('http:', 'https:', 'socks5:', 'socks5h:'): # merge proxy URL - proxy_url = '{0}//{1}{2}'.format(scheme, user_pass, host_port) + proxy_url = f'{scheme}//{user_pass}@{host_port}/' # fill session.proxies for both protocols requester.proxies = {'http': proxy_url, 'https': proxy_url} else: From 1d6e22e721bd5bfccc12bca749e8961b67202614 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Mon, 30 Oct 2023 14:09:27 +0100 Subject: [PATCH 677/884] 3.13-20231030 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 3e86d896e..0e5310777 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -124,7 +124,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.13-20231010' + VERSION = '3.13-20231030' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2023 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index ba4ca1ff7..aeb420fe9 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.13-20231010) unstable; urgency=low +nagstamon (3.13-20231030) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Tue, Oct 10 2023 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Tue, Oct 30 2023 08:00:00 +0200 nagstamon (3.12.0) stable; urgency=low * New upstream From b83b067402a22c2f9431a86e437bb309d517d699 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Mon, 30 Oct 2023 14:57:41 +0100 Subject: [PATCH 678/884] dciborow/action-github-releases@v1.0.1 --- .github/workflows/build-release-latest.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index a7da52b3f..3f56e445c 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -387,7 +387,8 @@ jobs: - uses: actions/download-artifact@v3 - run: cd artifact && md5sum *agstamon* > md5sums.txt - run: cd artifact && sha256sum *agstamon* > sha256sums.txt - - uses: marvinpinto/action-automatic-releases@latest + #- uses: marvinpinto/action-automatic-releases@latest + - uses: dciborow/action-github-releases@v1.0.1 with: repo_token: "${{ secrets.GITHUB_TOKEN }}" automatic_release_tag: "latest" From 585a89bdb65320ec0803d7cac860f88abbadcc60 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Mon, 30 Oct 2023 15:09:57 +0100 Subject: [PATCH 679/884] dciborow/action-github-releases@v1.0.1 outdated too --- .github/workflows/build-release-latest.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 3f56e445c..edb92662a 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -387,8 +387,9 @@ jobs: - uses: actions/download-artifact@v3 - run: cd artifact && md5sum *agstamon* > md5sums.txt - run: cd artifact && sha256sum *agstamon* > sha256sums.txt - #- uses: marvinpinto/action-automatic-releases@latest - - uses: dciborow/action-github-releases@v1.0.1 + - uses: marvinpinto/action-automatic-releases@latest + # the dciborow action is outdated as well :-( + #- uses: dciborow/action-github-releases@v1.0.1 with: repo_token: "${{ secrets.GITHUB_TOKEN }}" automatic_release_tag: "latest" From 9c304ade5f47473b3e01b78d6e72efd74f7a72a4 Mon Sep 17 00:00:00 2001 From: aleskxyz <39186039+aleskxyz@users.noreply.github.com> Date: Mon, 30 Oct 2023 15:53:52 +0100 Subject: [PATCH 680/884] Return value of check_for_error Fix return value of check_for_error function to send false instead of None --- Nagstamon/Servers/Generic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index c541c1320..c832cf451 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -1706,7 +1706,7 @@ def check_for_error(result, error, status_code) -> Optional[Result]: error=copy.deepcopy(error), status_code=copy.deepcopy(status_code)) else: - return None + return False def get_worst_status_current(self): """ From 6dffdf58cd0c132329a864051d657d12a072ccc6 Mon Sep 17 00:00:00 2001 From: Alireza Eskandari <alireza.eskandari@solvians.com> Date: Mon, 30 Oct 2023 16:33:42 +0100 Subject: [PATCH 681/884] refactor return value of check_for_error to None --- Nagstamon/Servers/Alertmanager/alertmanagerserver.py | 2 +- Nagstamon/Servers/Centreon/CentreonLegacy.py | 10 +++++----- Nagstamon/Servers/Generic.py | 6 +++--- Nagstamon/Servers/Icinga.py | 8 ++++---- Nagstamon/Servers/IcingaDBWeb.py | 2 +- Nagstamon/Servers/IcingaDBWebNotifications.py | 2 +- Nagstamon/Servers/Opsview.py | 2 +- Nagstamon/Servers/Prometheus.py | 2 +- Nagstamon/Servers/Thruk.py | 4 ++-- Nagstamon/Servers/op5Monitor.py | 4 ++-- 10 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Nagstamon/Servers/Alertmanager/alertmanagerserver.py b/Nagstamon/Servers/Alertmanager/alertmanagerserver.py index cab61547e..eb578fc6b 100644 --- a/Nagstamon/Servers/Alertmanager/alertmanagerserver.py +++ b/Nagstamon/Servers/Alertmanager/alertmanagerserver.py @@ -213,7 +213,7 @@ def _get_status(self): # check if any error occured errors_occured = self.check_for_error(data, error, status_code) - if errors_occured is not False: + if errors_occured is not None: return errors_occured for alert in data: diff --git a/Nagstamon/Servers/Centreon/CentreonLegacy.py b/Nagstamon/Servers/Centreon/CentreonLegacy.py index 0482ff014..76ad00859 100644 --- a/Nagstamon/Servers/Centreon/CentreonLegacy.py +++ b/Nagstamon/Servers/Centreon/CentreonLegacy.py @@ -604,7 +604,7 @@ def _get_status(self): errors_occured = self.check_for_error(xmlobj, error, status_code) # if there are errors return them - if errors_occured: + if errors_occured is not None: return(errors_occured) # Check if the result is not empty @@ -625,7 +625,7 @@ def _get_status(self): xmlobj, error, status_code = result.result, result.error, result.status_code errors_occured = self.check_for_error(xmlobj, error, status_code) # if there are errors return them - if errors_occured: + if errors_occured is not None: return(errors_occured) # a second time a bad session id should raise an error @@ -690,7 +690,7 @@ def _get_status(self): # check if any error occured errors_occured = self.check_for_error(xmlobj, error, status_code) # if there are errors return them - if errors_occured: + if errors_occured is not None: return(errors_occured) # Check if the result is not empty @@ -710,7 +710,7 @@ def _get_status(self): xmlobj, error, status_code = result.result, result.error, result.status_code errors_occured = self.check_for_error(xmlobj, error, status_code) # if there are errors return them - if errors_occured: + if errors_occured is not None: return(errors_occured) # a second time a bad session id should raise an error @@ -735,7 +735,7 @@ def _get_status(self): errors_occured = self.check_for_error(xmlobj_meta, error_meta, status_code_meta) # if there are errors return them - if errors_occured: + if errors_occured is not None: return(errors_occured) # a second time a bad session id should raise an error diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index c832cf451..d160ba69e 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -648,7 +648,7 @@ def _get_status(self): # check if any error occured errors_occured = self.check_for_error(htobj, error, status_code) # if there are errors return them - if errors_occured is not False: + if errors_occured is not None: return(errors_occured) # put a copy of a part of htobj into table to be able to delete htobj @@ -773,7 +773,7 @@ def _get_status(self): # check if any error occured errors_occured = self.check_for_error(htobj, error, status_code) # if there are errors return them - if errors_occured is not False: + if errors_occured is not None: return(errors_occured) # too much copy.deepcopy()s here give recursion crashs @@ -1706,7 +1706,7 @@ def check_for_error(result, error, status_code) -> Optional[Result]: error=copy.deepcopy(error), status_code=copy.deepcopy(status_code)) else: - return False + return None def get_worst_status_current(self): """ diff --git a/Nagstamon/Servers/Icinga.py b/Nagstamon/Servers/Icinga.py index ccbc0a3f4..81b2a4ae3 100644 --- a/Nagstamon/Servers/Icinga.py +++ b/Nagstamon/Servers/Icinga.py @@ -167,7 +167,7 @@ def _get_status_JSON(self): # check if any error occured errors_occured = self.check_for_error(jsonraw, error, status_code) # if there are errors return them - if errors_occured: + if errors_occured is not None: return(errors_occured) jsondict = json.loads(jsonraw) @@ -231,7 +231,7 @@ def _get_status_JSON(self): # check if any error occured errors_occured = self.check_for_error(jsonraw, error, status_code) # if there are errors return them - if errors_occured: + if errors_occured is not None: return(errors_occured) jsondict = json.loads(jsonraw) @@ -339,7 +339,7 @@ def _get_status_HTML(self): # check if any error occured errors_occured = self.check_for_error(htobj, error, status_code) # if there are errors return them - if errors_occured: + if errors_occured is not None: return(errors_occured) # put a copy of a part of htobj into table to be able to delete htobj @@ -468,7 +468,7 @@ def _get_status_HTML(self): # check if any error occured errors_occured = self.check_for_error(htobj, error, status_code) # if there are errors return them - if errors_occured: + if errors_occured is not None: return(errors_occured) table = htobj('table', {'class': 'status'})[0] diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index 589da4217..a98bb423d 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -161,7 +161,7 @@ def _get_status(self): # check if any error occured potential_error = self.check_for_error(jsonraw, error, status_code) - if potential_error: + if potential_error is not None: return potential_error # Check if the backend is running diff --git a/Nagstamon/Servers/IcingaDBWebNotifications.py b/Nagstamon/Servers/IcingaDBWebNotifications.py index 3d1c356df..41109df25 100644 --- a/Nagstamon/Servers/IcingaDBWebNotifications.py +++ b/Nagstamon/Servers/IcingaDBWebNotifications.py @@ -86,7 +86,7 @@ def _update_new_host_content(self) -> Result: # check if any error occurred potential_error = self.check_for_error(result.result, result.error, result.status_code) - if potential_error: + if potential_error is not None: return potential_error # HEALTH CHECK diff --git a/Nagstamon/Servers/Opsview.py b/Nagstamon/Servers/Opsview.py index fe47ea0ef..ea9577fec 100644 --- a/Nagstamon/Servers/Opsview.py +++ b/Nagstamon/Servers/Opsview.py @@ -244,7 +244,7 @@ def _get_status(self): # check if any error occured errors_occured = self.check_for_error(data, error, status_code) # if there are errors return them - if errors_occured: + if errors_occured is not None: return errors_occured if conf.debug_mode: diff --git a/Nagstamon/Servers/Prometheus.py b/Nagstamon/Servers/Prometheus.py index 53e8b777b..5b502dc16 100644 --- a/Nagstamon/Servers/Prometheus.py +++ b/Nagstamon/Servers/Prometheus.py @@ -155,7 +155,7 @@ def _get_status(self): # check if any error occured errors_occured = self.check_for_error(data, error, status_code) - if errors_occured is not False: + if errors_occured is not None: return(errors_occured) if conf.debug_mode: diff --git a/Nagstamon/Servers/Thruk.py b/Nagstamon/Servers/Thruk.py index 1d0b33959..209f16223 100644 --- a/Nagstamon/Servers/Thruk.py +++ b/Nagstamon/Servers/Thruk.py @@ -279,7 +279,7 @@ def _get_status(self): # check if any error occured errors_occured = self.check_for_error(jsonraw, error, status_code) # if there are errors return them - if errors_occured: + if errors_occured is not None: return(errors_occured) # in case basic auth did not work try form login cookie based login @@ -327,7 +327,7 @@ def _get_status(self): # check if any error occured errors_occured = self.check_for_error(jsonraw, error, status_code) # if there are errors return them - if errors_occured: + if errors_occured is not None: return(errors_occured) # in case basic auth did not work try form login cookie based login diff --git a/Nagstamon/Servers/op5Monitor.py b/Nagstamon/Servers/op5Monitor.py index 80f063c6e..9d520a353 100644 --- a/Nagstamon/Servers/op5Monitor.py +++ b/Nagstamon/Servers/op5Monitor.py @@ -151,7 +151,7 @@ def _get_status(self): # check if any error occured errors_occured = self.check_for_error(data, error, status_code) # if there are errors return them - if errors_occured: + if errors_occured is not None: return(errors_occured) if data['count']: @@ -212,7 +212,7 @@ def _get_status(self): # check if any error occured errors_occured = self.check_for_error(data, error, status_code) # if there are errors return them - if errors_occured: + if errors_occured is not None: return(errors_occured) if data['count']: From 4d3a8510eaab40a9cf3dfafc64d0bbfe5529546e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20Fa=C3=9Fbender?= <robin@fassbender.io> Date: Mon, 30 Oct 2023 21:45:05 +0100 Subject: [PATCH 682/884] fixed issue with date string in new icingadb version for host objects --- Nagstamon/Servers/IcingaDBWeb.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index 589da4217..a09441179 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -208,7 +208,11 @@ def _get_status(self): else: self.new_hosts[host_name].status = self.STATES_MAPPING['hosts'][int(h['state']['soft_state'])] - self.new_hosts[host_name].last_check = datetime.datetime.fromtimestamp(int(float(h['state']['last_update']))) + if h['state']['last_update'].isnumeric(): # new version of icingadb doesnt return unix timestamp + self.new_hosts[host_name].last_check = datetime.datetime.fromtimestamp(int(float(h['state']['last_update']))) + else: + self.new_hosts[host_name].last_check = datetime.datetime.fromisoformat(h['state']['last_update']) + self.new_hosts[host_name].attempt = "{}/{}".format(h['state']['check_attempt'],h['max_check_attempts']) self.new_hosts[host_name].status_information = BeautifulSoup(h['state']['output'].replace('\n', ' ').strip(), 'html.parser').text self.new_hosts[host_name].passiveonly = not int(h.get('active_checks_enabled') or '0') @@ -231,11 +235,17 @@ def _get_status(self): self.new_hosts[host_name].attempt = "HARD" # extra duration needed for calculation - if h['state']['last_state_change'] is not None and h['state']['last_state_change'] != 0: - duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(float(h['state']['last_state_change']))) - self.new_hosts[host_name].duration = strfdelta(duration,'{days}d {hours}h {minutes}m {seconds}s') - else: - self.new_hosts[host_name].duration = 'n/a' + self.new_hosts[host_name].duration = 'n/a' + if h['state']['last_state_change'] is not None: + if h['state']['last_state_change'].isnumeric(): # new version of icingadb doesnt return unix timestamp + duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(float(h['state']['last_state_change']))) + else: + last_state_change = datetime.datetime.fromisoformat(h['state']['last_state_change']) + duration = datetime.datetime.now().replace(tzinfo=last_state_change.tzinfo) - last_state_change + + if duration.total_seconds() > 0: + self.new_hosts[host_name].duration = strfdelta(duration,'{days}d {hours}h {minutes}m {seconds}s') + del h, host_name except: import traceback @@ -332,6 +342,7 @@ def _get_status(self): self.new_hosts[host_name].services[service_name].attempt = "HARD" # extra duration needed for calculation + self.new_hosts[host_name].services[service_name].duration = 'n/a' if s['state']['last_state_change'] is not None: if s['state']['last_update'].isnumeric(): # new version of icingadb doesnt return unix timestamp duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(float(s['state']['last_state_change']))) @@ -341,8 +352,6 @@ def _get_status(self): if duration.total_seconds() > 0: self.new_hosts[host_name].services[service_name].duration = strfdelta(duration, '{days}d {hours}h {minutes}m {seconds}s') - else: - self.new_hosts[host_name].services[service_name].duration = 'n/a' del s, host_name, service_name except: From e74445b0e79b0e47d9edf5fc943cf263138892c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20Fa=C3=9Fbender?= <robin@fassbender.io> Date: Mon, 30 Oct 2023 12:34:34 +0100 Subject: [PATCH 683/884] fixed issue with date string in new icingadb version fix should be backwards compatible fixed #962 --- Nagstamon/Servers/IcingaDBWeb.py | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index a3edeab69..589da4217 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -300,7 +300,12 @@ def _get_status(self): self.new_hosts[host_name].services[service_name].status = self.STATES_MAPPING['services'][int(s['state']['hard_state'])] else: self.new_hosts[host_name].services[service_name].status = self.STATES_MAPPING['services'][int(s['state']['soft_state'])] - self.new_hosts[host_name].services[service_name].last_check = datetime.datetime.fromtimestamp(int(float(s['state']['last_update']))) + + if s['state']['last_update'].isnumeric(): # new version of icingadb doesnt return unix timestamp + self.new_hosts[host_name].services[service_name].last_check = datetime.datetime.fromtimestamp(int(float(s['state']['last_update']))) + else: + self.new_hosts[host_name].services[service_name].last_check = datetime.datetime.fromisoformat(s['state']['last_update']) + self.new_hosts[host_name].services[service_name].attempt = "{}/{}".format(s['state']['check_attempt'],s['max_check_attempts']) self.new_hosts[host_name].services[service_name].status_information = BeautifulSoup(s['state']['output'].replace('\n', ' ').strip(), 'html.parser').text self.new_hosts[host_name].services[service_name].passiveonly = not int(s.get('active_checks_enabled') or '0') @@ -327,11 +332,17 @@ def _get_status(self): self.new_hosts[host_name].services[service_name].attempt = "HARD" # extra duration needed for calculation - if s['state']['last_state_change'] is not None and s['state']['last_state_change'] != 0: - duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(float(s['state']['last_state_change']))) - self.new_hosts[host_name].services[service_name].duration = strfdelta(duration, '{days}d {hours}h {minutes}m {seconds}s') - else: - self.new_hosts[host_name].services[service_name].duration = 'n/a' + if s['state']['last_state_change'] is not None: + if s['state']['last_update'].isnumeric(): # new version of icingadb doesnt return unix timestamp + duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(float(s['state']['last_state_change']))) + else: + last_state_change = datetime.datetime.fromisoformat(s['state']['last_state_change']) + duration = datetime.datetime.now().replace(tzinfo=last_state_change.tzinfo) - last_state_change + + if duration.total_seconds() > 0: + self.new_hosts[host_name].services[service_name].duration = strfdelta(duration, '{days}d {hours}h {minutes}m {seconds}s') + else: + self.new_hosts[host_name].services[service_name].duration = 'n/a' del s, host_name, service_name except: From b850e2e623245732da66158a6a5efc2771c89f6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20Fa=C3=9Fbender?= <robin@fassbender.io> Date: Mon, 30 Oct 2023 21:45:05 +0100 Subject: [PATCH 684/884] fixed issue with date string in new icingadb version for host objects --- Nagstamon/Servers/IcingaDBWeb.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index 589da4217..a09441179 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -208,7 +208,11 @@ def _get_status(self): else: self.new_hosts[host_name].status = self.STATES_MAPPING['hosts'][int(h['state']['soft_state'])] - self.new_hosts[host_name].last_check = datetime.datetime.fromtimestamp(int(float(h['state']['last_update']))) + if h['state']['last_update'].isnumeric(): # new version of icingadb doesnt return unix timestamp + self.new_hosts[host_name].last_check = datetime.datetime.fromtimestamp(int(float(h['state']['last_update']))) + else: + self.new_hosts[host_name].last_check = datetime.datetime.fromisoformat(h['state']['last_update']) + self.new_hosts[host_name].attempt = "{}/{}".format(h['state']['check_attempt'],h['max_check_attempts']) self.new_hosts[host_name].status_information = BeautifulSoup(h['state']['output'].replace('\n', ' ').strip(), 'html.parser').text self.new_hosts[host_name].passiveonly = not int(h.get('active_checks_enabled') or '0') @@ -231,11 +235,17 @@ def _get_status(self): self.new_hosts[host_name].attempt = "HARD" # extra duration needed for calculation - if h['state']['last_state_change'] is not None and h['state']['last_state_change'] != 0: - duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(float(h['state']['last_state_change']))) - self.new_hosts[host_name].duration = strfdelta(duration,'{days}d {hours}h {minutes}m {seconds}s') - else: - self.new_hosts[host_name].duration = 'n/a' + self.new_hosts[host_name].duration = 'n/a' + if h['state']['last_state_change'] is not None: + if h['state']['last_state_change'].isnumeric(): # new version of icingadb doesnt return unix timestamp + duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(float(h['state']['last_state_change']))) + else: + last_state_change = datetime.datetime.fromisoformat(h['state']['last_state_change']) + duration = datetime.datetime.now().replace(tzinfo=last_state_change.tzinfo) - last_state_change + + if duration.total_seconds() > 0: + self.new_hosts[host_name].duration = strfdelta(duration,'{days}d {hours}h {minutes}m {seconds}s') + del h, host_name except: import traceback @@ -332,6 +342,7 @@ def _get_status(self): self.new_hosts[host_name].services[service_name].attempt = "HARD" # extra duration needed for calculation + self.new_hosts[host_name].services[service_name].duration = 'n/a' if s['state']['last_state_change'] is not None: if s['state']['last_update'].isnumeric(): # new version of icingadb doesnt return unix timestamp duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(float(s['state']['last_state_change']))) @@ -341,8 +352,6 @@ def _get_status(self): if duration.total_seconds() > 0: self.new_hosts[host_name].services[service_name].duration = strfdelta(duration, '{days}d {hours}h {minutes}m {seconds}s') - else: - self.new_hosts[host_name].services[service_name].duration = 'n/a' del s, host_name, service_name except: From b62268c2db4f0df99ea3f36c4d2438ffaae804af Mon Sep 17 00:00:00 2001 From: aleskxyz <39186039+aleskxyz@users.noreply.github.com> Date: Mon, 30 Oct 2023 15:53:52 +0100 Subject: [PATCH 685/884] Return value of check_for_error Fix return value of check_for_error function to send false instead of None --- Nagstamon/Servers/Generic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index ca6933fa7..ee2bc7f47 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -1706,7 +1706,7 @@ def check_for_error(result, error, status_code) -> Optional[Result]: error=copy.deepcopy(error), status_code=copy.deepcopy(status_code)) else: - return None + return False def get_worst_status_current(self): """ From 83a277343b7ea22f6b457788b04d8cd0dc49fa11 Mon Sep 17 00:00:00 2001 From: Alireza Eskandari <alireza.eskandari@solvians.com> Date: Mon, 30 Oct 2023 16:33:42 +0100 Subject: [PATCH 686/884] refactor return value of check_for_error to None --- Nagstamon/Servers/Alertmanager/alertmanagerserver.py | 2 +- Nagstamon/Servers/Centreon/CentreonLegacy.py | 10 +++++----- Nagstamon/Servers/Generic.py | 6 +++--- Nagstamon/Servers/Icinga.py | 8 ++++---- Nagstamon/Servers/IcingaDBWeb.py | 2 +- Nagstamon/Servers/IcingaDBWebNotifications.py | 2 +- Nagstamon/Servers/Opsview.py | 2 +- Nagstamon/Servers/Prometheus.py | 2 +- Nagstamon/Servers/Thruk.py | 4 ++-- Nagstamon/Servers/op5Monitor.py | 4 ++-- 10 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Nagstamon/Servers/Alertmanager/alertmanagerserver.py b/Nagstamon/Servers/Alertmanager/alertmanagerserver.py index cab61547e..eb578fc6b 100644 --- a/Nagstamon/Servers/Alertmanager/alertmanagerserver.py +++ b/Nagstamon/Servers/Alertmanager/alertmanagerserver.py @@ -213,7 +213,7 @@ def _get_status(self): # check if any error occured errors_occured = self.check_for_error(data, error, status_code) - if errors_occured is not False: + if errors_occured is not None: return errors_occured for alert in data: diff --git a/Nagstamon/Servers/Centreon/CentreonLegacy.py b/Nagstamon/Servers/Centreon/CentreonLegacy.py index 0482ff014..76ad00859 100644 --- a/Nagstamon/Servers/Centreon/CentreonLegacy.py +++ b/Nagstamon/Servers/Centreon/CentreonLegacy.py @@ -604,7 +604,7 @@ def _get_status(self): errors_occured = self.check_for_error(xmlobj, error, status_code) # if there are errors return them - if errors_occured: + if errors_occured is not None: return(errors_occured) # Check if the result is not empty @@ -625,7 +625,7 @@ def _get_status(self): xmlobj, error, status_code = result.result, result.error, result.status_code errors_occured = self.check_for_error(xmlobj, error, status_code) # if there are errors return them - if errors_occured: + if errors_occured is not None: return(errors_occured) # a second time a bad session id should raise an error @@ -690,7 +690,7 @@ def _get_status(self): # check if any error occured errors_occured = self.check_for_error(xmlobj, error, status_code) # if there are errors return them - if errors_occured: + if errors_occured is not None: return(errors_occured) # Check if the result is not empty @@ -710,7 +710,7 @@ def _get_status(self): xmlobj, error, status_code = result.result, result.error, result.status_code errors_occured = self.check_for_error(xmlobj, error, status_code) # if there are errors return them - if errors_occured: + if errors_occured is not None: return(errors_occured) # a second time a bad session id should raise an error @@ -735,7 +735,7 @@ def _get_status(self): errors_occured = self.check_for_error(xmlobj_meta, error_meta, status_code_meta) # if there are errors return them - if errors_occured: + if errors_occured is not None: return(errors_occured) # a second time a bad session id should raise an error diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index ee2bc7f47..08a9a04df 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -648,7 +648,7 @@ def _get_status(self): # check if any error occured errors_occured = self.check_for_error(htobj, error, status_code) # if there are errors return them - if errors_occured is not False: + if errors_occured is not None: return(errors_occured) # put a copy of a part of htobj into table to be able to delete htobj @@ -773,7 +773,7 @@ def _get_status(self): # check if any error occured errors_occured = self.check_for_error(htobj, error, status_code) # if there are errors return them - if errors_occured is not False: + if errors_occured is not None: return(errors_occured) # too much copy.deepcopy()s here give recursion crashs @@ -1706,7 +1706,7 @@ def check_for_error(result, error, status_code) -> Optional[Result]: error=copy.deepcopy(error), status_code=copy.deepcopy(status_code)) else: - return False + return None def get_worst_status_current(self): """ diff --git a/Nagstamon/Servers/Icinga.py b/Nagstamon/Servers/Icinga.py index ccbc0a3f4..81b2a4ae3 100644 --- a/Nagstamon/Servers/Icinga.py +++ b/Nagstamon/Servers/Icinga.py @@ -167,7 +167,7 @@ def _get_status_JSON(self): # check if any error occured errors_occured = self.check_for_error(jsonraw, error, status_code) # if there are errors return them - if errors_occured: + if errors_occured is not None: return(errors_occured) jsondict = json.loads(jsonraw) @@ -231,7 +231,7 @@ def _get_status_JSON(self): # check if any error occured errors_occured = self.check_for_error(jsonraw, error, status_code) # if there are errors return them - if errors_occured: + if errors_occured is not None: return(errors_occured) jsondict = json.loads(jsonraw) @@ -339,7 +339,7 @@ def _get_status_HTML(self): # check if any error occured errors_occured = self.check_for_error(htobj, error, status_code) # if there are errors return them - if errors_occured: + if errors_occured is not None: return(errors_occured) # put a copy of a part of htobj into table to be able to delete htobj @@ -468,7 +468,7 @@ def _get_status_HTML(self): # check if any error occured errors_occured = self.check_for_error(htobj, error, status_code) # if there are errors return them - if errors_occured: + if errors_occured is not None: return(errors_occured) table = htobj('table', {'class': 'status'})[0] diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index a09441179..9370d3c2b 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -161,7 +161,7 @@ def _get_status(self): # check if any error occured potential_error = self.check_for_error(jsonraw, error, status_code) - if potential_error: + if potential_error is not None: return potential_error # Check if the backend is running diff --git a/Nagstamon/Servers/IcingaDBWebNotifications.py b/Nagstamon/Servers/IcingaDBWebNotifications.py index 3d1c356df..41109df25 100644 --- a/Nagstamon/Servers/IcingaDBWebNotifications.py +++ b/Nagstamon/Servers/IcingaDBWebNotifications.py @@ -86,7 +86,7 @@ def _update_new_host_content(self) -> Result: # check if any error occurred potential_error = self.check_for_error(result.result, result.error, result.status_code) - if potential_error: + if potential_error is not None: return potential_error # HEALTH CHECK diff --git a/Nagstamon/Servers/Opsview.py b/Nagstamon/Servers/Opsview.py index fe47ea0ef..ea9577fec 100644 --- a/Nagstamon/Servers/Opsview.py +++ b/Nagstamon/Servers/Opsview.py @@ -244,7 +244,7 @@ def _get_status(self): # check if any error occured errors_occured = self.check_for_error(data, error, status_code) # if there are errors return them - if errors_occured: + if errors_occured is not None: return errors_occured if conf.debug_mode: diff --git a/Nagstamon/Servers/Prometheus.py b/Nagstamon/Servers/Prometheus.py index 53e8b777b..5b502dc16 100644 --- a/Nagstamon/Servers/Prometheus.py +++ b/Nagstamon/Servers/Prometheus.py @@ -155,7 +155,7 @@ def _get_status(self): # check if any error occured errors_occured = self.check_for_error(data, error, status_code) - if errors_occured is not False: + if errors_occured is not None: return(errors_occured) if conf.debug_mode: diff --git a/Nagstamon/Servers/Thruk.py b/Nagstamon/Servers/Thruk.py index 1d0b33959..209f16223 100644 --- a/Nagstamon/Servers/Thruk.py +++ b/Nagstamon/Servers/Thruk.py @@ -279,7 +279,7 @@ def _get_status(self): # check if any error occured errors_occured = self.check_for_error(jsonraw, error, status_code) # if there are errors return them - if errors_occured: + if errors_occured is not None: return(errors_occured) # in case basic auth did not work try form login cookie based login @@ -327,7 +327,7 @@ def _get_status(self): # check if any error occured errors_occured = self.check_for_error(jsonraw, error, status_code) # if there are errors return them - if errors_occured: + if errors_occured is not None: return(errors_occured) # in case basic auth did not work try form login cookie based login diff --git a/Nagstamon/Servers/op5Monitor.py b/Nagstamon/Servers/op5Monitor.py index 80f063c6e..9d520a353 100644 --- a/Nagstamon/Servers/op5Monitor.py +++ b/Nagstamon/Servers/op5Monitor.py @@ -151,7 +151,7 @@ def _get_status(self): # check if any error occured errors_occured = self.check_for_error(data, error, status_code) # if there are errors return them - if errors_occured: + if errors_occured is not None: return(errors_occured) if data['count']: @@ -212,7 +212,7 @@ def _get_status(self): # check if any error occured errors_occured = self.check_for_error(data, error, status_code) # if there are errors return them - if errors_occured: + if errors_occured is not None: return(errors_occured) if data['count']: From 4b4e48c18737bcf358dc561a5f77f7f769808e75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20Fa=C3=9Fbender?= <robin@fassbender.io> Date: Mon, 30 Oct 2023 21:45:05 +0100 Subject: [PATCH 687/884] 3.13-20231031 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 0e5310777..40f5177a0 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -124,7 +124,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.13-20231030' + VERSION = '3.13-20231031' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2023 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index aeb420fe9..8b36f7cd9 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.13-20231030) unstable; urgency=low +nagstamon (3.13-20231031) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Tue, Oct 30 2023 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Tue, Oct 31 2023 08:00:00 +0200 nagstamon (3.12.0) stable; urgency=low * New upstream From 8049f94860a279664102dd26932dda55c3c6bb53 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Wed, 1 Nov 2023 09:52:30 +0100 Subject: [PATCH 688/884] macos qt 6.4.2 --- Nagstamon/Servers/Multisite.py | 4 ++-- build/requirements/macos.txt | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Nagstamon/Servers/Multisite.py b/Nagstamon/Servers/Multisite.py index 1ac7ac650..b7be265f9 100644 --- a/Nagstamon/Servers/Multisite.py +++ b/Nagstamon/Servers/Multisite.py @@ -124,8 +124,8 @@ def init_HTTP(self): # if no cookie yet login self._get_cookie_login() elif self.CookieAuth and self.refresh_authentication: - if self.session is None: - self.session = self.create_session() + #if self.session is None: + self.session = self.create_session() # force re-auth self._get_cookie_login() diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index d66ca8055..0142b3424 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -6,9 +6,9 @@ psutil # last stable of the 5 series pyinstaller==5.13.2 pyobjc-framework-ApplicationServices -# 6.5.1 leads to crash on macOS -pyqt6==6.5.3 -pyqt6-qt6==6.5.3 +# 6.5.3 leads to crash on macOS +pyqt6==6.4.2 +pyqt6-qt6==6.4.2 pysocks python-dateutil requests From 5ff59a0f92b3d7996b0a729a7c43f2b88d396de2 Mon Sep 17 00:00:00 2001 From: Yannick Charton <tontonitch-pro@yahoo.fr> Date: Mon, 13 Nov 2023 12:11:14 +0100 Subject: [PATCH 689/884] Fixing 4d3a851 that was using isnumeric on float --- Nagstamon/Servers/IcingaDBWeb.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index 9370d3c2b..a22089880 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -208,7 +208,7 @@ def _get_status(self): else: self.new_hosts[host_name].status = self.STATES_MAPPING['hosts'][int(h['state']['soft_state'])] - if h['state']['last_update'].isnumeric(): # new version of icingadb doesnt return unix timestamp + if h['state']['last_update'].replace(".", "").isnumeric(): # new version of icingadb doesnt return unix timestamp self.new_hosts[host_name].last_check = datetime.datetime.fromtimestamp(int(float(h['state']['last_update']))) else: self.new_hosts[host_name].last_check = datetime.datetime.fromisoformat(h['state']['last_update']) @@ -237,7 +237,7 @@ def _get_status(self): # extra duration needed for calculation self.new_hosts[host_name].duration = 'n/a' if h['state']['last_state_change'] is not None: - if h['state']['last_state_change'].isnumeric(): # new version of icingadb doesnt return unix timestamp + if h['state']['last_state_change'].replace(".", "").isnumeric(): # new version of icingadb doesnt return unix timestamp duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(float(h['state']['last_state_change']))) else: last_state_change = datetime.datetime.fromisoformat(h['state']['last_state_change']) @@ -311,7 +311,7 @@ def _get_status(self): else: self.new_hosts[host_name].services[service_name].status = self.STATES_MAPPING['services'][int(s['state']['soft_state'])] - if s['state']['last_update'].isnumeric(): # new version of icingadb doesnt return unix timestamp + if s['state']['last_update'].replace(".", "").isnumeric(): # new version of icingadb doesnt return unix timestamp self.new_hosts[host_name].services[service_name].last_check = datetime.datetime.fromtimestamp(int(float(s['state']['last_update']))) else: self.new_hosts[host_name].services[service_name].last_check = datetime.datetime.fromisoformat(s['state']['last_update']) @@ -344,7 +344,7 @@ def _get_status(self): # extra duration needed for calculation self.new_hosts[host_name].services[service_name].duration = 'n/a' if s['state']['last_state_change'] is not None: - if s['state']['last_update'].isnumeric(): # new version of icingadb doesnt return unix timestamp + if s['state']['last_update'].replace(".", "").isnumeric(): # new version of icingadb doesnt return unix timestamp duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(float(s['state']['last_state_change']))) else: last_state_change = datetime.datetime.fromisoformat(s['state']['last_state_change']) From f9d1a7c28d47089130989883c6cd37c9da6b8550 Mon Sep 17 00:00:00 2001 From: Benoit Poulet <benoit.poulet@businessdecision.com> Date: Tue, 14 Nov 2023 10:00:18 +0100 Subject: [PATCH 690/884] Centreon fixes for 23.10 --- Nagstamon/Servers/Centreon/CentreonAPI.py | 40 ++++++++++++++++------- 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/Nagstamon/Servers/Centreon/CentreonAPI.py b/Nagstamon/Servers/Centreon/CentreonAPI.py index eee1343b6..01ef0c7e9 100644 --- a/Nagstamon/Servers/Centreon/CentreonAPI.py +++ b/Nagstamon/Servers/Centreon/CentreonAPI.py @@ -97,8 +97,10 @@ def init_config(self): self.restapi_version = "latest" elif self.centreon_version_major == 22: self.restapi_version = "v22.04" - else: + elif self.centreon_version_major == 23 and self.centreon_version_minor == 4: self.restapi_version = "v23.04" + else: + self.restapi_version = "v23.10" if conf.debug_mode is True: self.Debug(server='[' + self.get_name() + ']', debug='Centreon API version used : ' + self.restapi_version) @@ -347,7 +349,7 @@ def _get_status(self): self.new_hosts[new_host] = GenericHost() self.new_hosts[new_host].name = alerts["name"] self.new_hosts[new_host].server = self.name - # API inconsistency, even by fixing exact version number + # API inconsistency, even by fixing exact version number, changed starting with 22.04 if self.centreon_version_major == 21 or (self.centreon_version_major == 22 and self.centreon_version_minor == 4): self.new_hosts[new_host].criticality = alerts["severity_level"] else: @@ -358,13 +360,20 @@ def _get_status(self): self.new_hosts[new_host].duration = alerts["duration"] self.new_hosts[new_host].attempt = alerts["tries"] self.new_hosts[new_host].status_information = alerts["information"] - self.new_hosts[new_host].passiveonly = alerts["passive_checks"] - self.new_hosts[new_host].notifications_disabled = not alerts["notification_enabled"] + # Change starting with 23.10 + if (self.centreon_version_major >= 23 and self.centreon_version_minor >= 10) or self.centreon_version_major > 23: + self.new_hosts[new_host].passiveonly = alerts["has_passive_checks_enabled"] + self.new_hosts[new_host].notifications_disabled = not alerts["is_notification_enabled"] + self.new_hosts[new_host].acknowledged = alerts["is_acknowledged"] + self.new_hosts[new_host].scheduled_downtime = alerts["is_in_downtime"] + else: + self.new_hosts[new_host].passiveonly = alerts["passive_checks"] + self.new_hosts[new_host].notifications_disabled = not alerts["notification_enabled"] + self.new_hosts[new_host].acknowledged = alerts["acknowledged"] + self.new_hosts[new_host].scheduled_downtime = alerts["in_downtime"] # avoid crash if flapping is not configured in Centreon # according to https://github.com/HenriWahl/Nagstamon/issues/866#issuecomment-1302257034 self.new_hosts[new_host].flapping = alerts.get("flapping", False) - self.new_hosts[new_host].acknowledged = alerts["acknowledged"] - self.new_hosts[new_host].scheduled_downtime = alerts["in_downtime"] if "(S)" in alerts["tries"]: self.new_hosts[new_host].status_type = self.HARD_SOFT['(S)'] else: @@ -425,18 +434,25 @@ def _get_status(self): self.new_hosts[new_host].services[new_service].duration = alerts["duration"] self.new_hosts[new_host].services[new_service].attempt = alerts["tries"] self.new_hosts[new_host].services[new_service].status_information = alerts["information"] - self.new_hosts[new_host].services[new_service].passiveonly = alerts["passive_checks"] - self.new_hosts[new_host].services[new_service].notifications_disabled = not alerts["notification_enabled"] + # Change starting with 23.10 + if (self.centreon_version_major >= 23 and self.centreon_version_minor >= 10) or self.centreon_version_major > 23: + self.new_hosts[new_host].services[new_service].passiveonly = alerts["has_passive_checks_enabled"] + self.new_hosts[new_host].services[new_service].notifications_disabled = not alerts["is_notification_enabled"] + self.new_hosts[new_host].services[new_service].acknowledged = alerts["is_acknowledged"] + self.new_hosts[new_host].services[new_service].scheduled_downtime = alerts["is_in_downtime"] + else: + self.new_hosts[new_host].services[new_service].passiveonly = alerts["passive_checks"] + self.new_hosts[new_host].services[new_service].notifications_disabled = not alerts["notification_enabled"] + self.new_hosts[new_host].services[new_service].acknowledged = alerts["acknowledged"] + self.new_hosts[new_host].services[new_service].scheduled_downtime = alerts["in_downtime"] # avoid crash if flapping is not configured in Centreon # according to https://github.com/HenriWahl/Nagstamon/issues/866#issuecomment-1302257034 self.new_hosts[new_host].services[new_service].flapping = alerts.get("flapping", False) - self.new_hosts[new_host].services[new_service].acknowledged = alerts["acknowledged"] - self.new_hosts[new_host].services[new_service].scheduled_downtime = alerts["in_downtime"] if "(S)" in alerts["tries"]: self.new_hosts[new_host].services[new_service].status_type = self.HARD_SOFT['(S)'] else: self.new_hosts[new_host].services[new_service].status_type = self.HARD_SOFT['(H)'] - # API inconsistency, even by fixing exact version number + # API inconsistency, even by fixing exact version number, changed starting with 22.04 if self.centreon_version_major == 21 or (self.centreon_version_major == 22 and self.centreon_version_minor == 4): self.new_hosts[new_host].services[new_service].criticality = alerts["severity_level"] else: @@ -552,7 +568,7 @@ def _set_recheck(self, host, service): } # This new parameter was added in 23.04 - if self.restapi_version == "v23.04": + if self.centreon_version_major >= 23: property_to_add = { "check": { "is_forced": True From ba361bf57e72507178683bdcd18d6d37db69da7a Mon Sep 17 00:00:00 2001 From: Yannick Charton <tontonitch-pro@yahoo.fr> Date: Tue, 14 Nov 2023 21:48:34 +0100 Subject: [PATCH 691/884] Fixing timezone issue when using latest version of icingadb Before that fix, the utc tz was applied to the tz unaware datetime.now(), leading to incorrect duration calculations. Now fixed by retrieving local tz for the date retrieved by now(). Fix for python 3.3+ for better compatibility. --- Nagstamon/Servers/IcingaDBWeb.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index a22089880..de7015c2f 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -36,6 +36,7 @@ import copy import json import datetime +from datetime import timezone import socket from bs4 import BeautifulSoup @@ -241,7 +242,7 @@ def _get_status(self): duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(float(h['state']['last_state_change']))) else: last_state_change = datetime.datetime.fromisoformat(h['state']['last_state_change']) - duration = datetime.datetime.now().replace(tzinfo=last_state_change.tzinfo) - last_state_change + duration = datetime.datetime.now(timezone.utc).astimezone() - last_state_change if duration.total_seconds() > 0: self.new_hosts[host_name].duration = strfdelta(duration,'{days}d {hours}h {minutes}m {seconds}s') @@ -348,7 +349,7 @@ def _get_status(self): duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(float(s['state']['last_state_change']))) else: last_state_change = datetime.datetime.fromisoformat(s['state']['last_state_change']) - duration = datetime.datetime.now().replace(tzinfo=last_state_change.tzinfo) - last_state_change + duration = datetime.datetime.now(timezone.utc).astimezone() - last_state_change if duration.total_seconds() > 0: self.new_hosts[host_name].services[service_name].duration = strfdelta(duration, '{days}d {hours}h {minutes}m {seconds}s') From 40f31ba0a2aeb50c8e93f1f2ac8a7d608d97bf2a Mon Sep 17 00:00:00 2001 From: tk422 <not@gmail.com> Date: Wed, 15 Nov 2023 18:01:59 -0500 Subject: [PATCH 692/884] Add map to down for Alertmanager --- Nagstamon/Config.py | 1 + Nagstamon/QUI/__init__.py | 2 ++ Nagstamon/Servers/Alertmanager/alertmanagerserver.py | 5 +++++ Nagstamon/resources/qui/settings_server.ui | 10 ++++++++++ 4 files changed, 18 insertions(+) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 3e86d896e..e780d5015 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -969,6 +969,7 @@ def __init__(self): self.alertmanager_filter = '' self.map_to_critical = 'critical,error' self.map_to_warning = 'warning,warn' + self.map_to_down = 'down' self.map_to_unknown = 'unknown' self.map_to_ok = 'ok' diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 5bf09653e..7eb881802 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -5815,6 +5815,8 @@ def __init__(self, dialog): self.window.input_lineedit_map_to_warning: ['Alertmanager'], self.window.label_map_to_critical: ['Alertmanager'], self.window.input_lineedit_map_to_critical: ['Alertmanager'], + self.window.label_map_to_down: ['Alertmanager'], + self.window.input_lineedit_map_to_down: ['Alertmanager'], self.window.input_lineedit_notification_filter: ['IcingaDBWebNotifications'], self.window.label_notification_filter: ['IcingaDBWebNotifications'], self.window.input_lineedit_notification_lookback: ['IcingaDBWebNotifications'], diff --git a/Nagstamon/Servers/Alertmanager/alertmanagerserver.py b/Nagstamon/Servers/Alertmanager/alertmanagerserver.py index eb578fc6b..f65c3400d 100644 --- a/Nagstamon/Servers/Alertmanager/alertmanagerserver.py +++ b/Nagstamon/Servers/Alertmanager/alertmanagerserver.py @@ -47,6 +47,7 @@ class AlertmanagerServer(GenericServer): map_to_status_information = '' map_to_critical = '' map_to_warning = '' + map_to_down = '' map_to_unknown = '' map_to_ok = '' name = '' @@ -96,6 +97,8 @@ def map_severity(self, the_severity): return "CRITICAL" if the_severity in self.map_to_warning.split(','): return "WARNING" + if the_severity in self.map_to_down.split(','): + return "DOWN" if the_severity in self.map_to_ok.split(','): return "OK" return the_severity.upper() @@ -183,6 +186,8 @@ def _get_status(self): self.map_to_critical) log.debug("severity config (map_to_warning): '%s'", self.map_to_warning) + log.debug("severity config (map_to_down): '%s'", + self.map_to_down) log.debug("severity config (map_to_ok): '%s'", self.map_to_ok) diff --git a/Nagstamon/resources/qui/settings_server.ui b/Nagstamon/resources/qui/settings_server.ui index 170f57e2c..eedf4119c 100644 --- a/Nagstamon/resources/qui/settings_server.ui +++ b/Nagstamon/resources/qui/settings_server.ui @@ -737,6 +737,9 @@ <widget class="QLineEdit" name="input_lineedit_map_to_critical"/> </item> <item row="19" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_map_to_down"/> + </item> + <item row="20" column="2" colspan="2"> <widget class="QLineEdit" name="input_lineedit_map_to_unknown"/> </item> <item row="16" column="1"> @@ -761,6 +764,13 @@ </widget> </item> <item row="19" column="1"> + <widget class="QLabel" name="label_map_to_down"> + <property name="text"> + <string>Map to DOWN</string> + </property> + </widget> + </item> + <item row="20" column="1"> <widget class="QLabel" name="label_map_to_unknown"> <property name="text"> <string>Map to UNKNOWN</string> From ba132fa55530c6854035e16ae84b529561a6fb88 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Mon, 16 Oct 2023 09:51:47 +0200 Subject: [PATCH 693/884] build new release --- build/requirements/windows.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index 13876c36c..d456b3f81 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -8,6 +8,7 @@ psutil # last stable of the 5 series pyinstaller==5.13.2 pypiwin32 +# try newer version pyqt6==6.5.3 pyqt6-qt6==6.5.3 pysocks From f525e9911fbbdbf2d108f91f154648982b686205 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Mon, 30 Oct 2023 14:03:43 +0100 Subject: [PATCH 694/884] proxy / --- Nagstamon/Servers/Generic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index d160ba69e..08a9a04df 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -360,7 +360,7 @@ def proxify(self, requester): if self.proxy_username == self.proxy_password == '': user_pass = '' else: - user_pass = '{0}:{1}@'.format(self.proxy_username, self.proxy_password) + user_pass = '{0}:{1}'.format(self.proxy_username, self.proxy_password) # split and analyze proxy URL proxy_address_parts = self.proxy_address.split('//') @@ -370,7 +370,7 @@ def proxify(self, requester): # use only valid schemes if scheme.lower() in ('http:', 'https:', 'socks5:', 'socks5h:'): # merge proxy URL - proxy_url = '{0}//{1}{2}'.format(scheme, user_pass, host_port) + proxy_url = f'{scheme}//{user_pass}@{host_port}/' # fill session.proxies for both protocols requester.proxies = {'http': proxy_url, 'https': proxy_url} else: From 1a27a9b1df9696a67dc9b893100c79777a38ebdc Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Mon, 30 Oct 2023 14:09:27 +0100 Subject: [PATCH 695/884] 3.13-20231030 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 3e86d896e..0e5310777 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -124,7 +124,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.13-20231010' + VERSION = '3.13-20231030' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2023 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index ba4ca1ff7..aeb420fe9 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.13-20231010) unstable; urgency=low +nagstamon (3.13-20231030) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Tue, Oct 10 2023 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Tue, Oct 30 2023 08:00:00 +0200 nagstamon (3.12.0) stable; urgency=low * New upstream From c784d0c87d1da2e88be78d0424df8a8825f1ba79 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Mon, 30 Oct 2023 14:57:41 +0100 Subject: [PATCH 696/884] dciborow/action-github-releases@v1.0.1 --- .github/workflows/build-release-latest.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index a7da52b3f..3f56e445c 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -387,7 +387,8 @@ jobs: - uses: actions/download-artifact@v3 - run: cd artifact && md5sum *agstamon* > md5sums.txt - run: cd artifact && sha256sum *agstamon* > sha256sums.txt - - uses: marvinpinto/action-automatic-releases@latest + #- uses: marvinpinto/action-automatic-releases@latest + - uses: dciborow/action-github-releases@v1.0.1 with: repo_token: "${{ secrets.GITHUB_TOKEN }}" automatic_release_tag: "latest" From 5d6f241d30b8635e29d1b2c8eddae78fe2c404e2 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Mon, 30 Oct 2023 15:09:57 +0100 Subject: [PATCH 697/884] dciborow/action-github-releases@v1.0.1 outdated too --- .github/workflows/build-release-latest.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 3f56e445c..edb92662a 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -387,8 +387,9 @@ jobs: - uses: actions/download-artifact@v3 - run: cd artifact && md5sum *agstamon* > md5sums.txt - run: cd artifact && sha256sum *agstamon* > sha256sums.txt - #- uses: marvinpinto/action-automatic-releases@latest - - uses: dciborow/action-github-releases@v1.0.1 + - uses: marvinpinto/action-automatic-releases@latest + # the dciborow action is outdated as well :-( + #- uses: dciborow/action-github-releases@v1.0.1 with: repo_token: "${{ secrets.GITHUB_TOKEN }}" automatic_release_tag: "latest" From 2d16bca427c34d3e3599b39c94fecdd4535c535f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20Fa=C3=9Fbender?= <robin@fassbender.io> Date: Mon, 30 Oct 2023 21:45:05 +0100 Subject: [PATCH 698/884] 3.13-20231031 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 0e5310777..40f5177a0 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -124,7 +124,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.13-20231030' + VERSION = '3.13-20231031' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2023 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index aeb420fe9..8b36f7cd9 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.13-20231030) unstable; urgency=low +nagstamon (3.13-20231031) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Tue, Oct 30 2023 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Tue, Oct 31 2023 08:00:00 +0200 nagstamon (3.12.0) stable; urgency=low * New upstream From 18a377decaf7079e440b163c5a2ee3deeb382c32 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Wed, 1 Nov 2023 09:52:30 +0100 Subject: [PATCH 699/884] macos qt 6.4.2 --- Nagstamon/Servers/Multisite.py | 4 ++-- build/requirements/macos.txt | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Nagstamon/Servers/Multisite.py b/Nagstamon/Servers/Multisite.py index 1ac7ac650..b7be265f9 100644 --- a/Nagstamon/Servers/Multisite.py +++ b/Nagstamon/Servers/Multisite.py @@ -124,8 +124,8 @@ def init_HTTP(self): # if no cookie yet login self._get_cookie_login() elif self.CookieAuth and self.refresh_authentication: - if self.session is None: - self.session = self.create_session() + #if self.session is None: + self.session = self.create_session() # force re-auth self._get_cookie_login() diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index d66ca8055..0142b3424 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -6,9 +6,9 @@ psutil # last stable of the 5 series pyinstaller==5.13.2 pyobjc-framework-ApplicationServices -# 6.5.1 leads to crash on macOS -pyqt6==6.5.3 -pyqt6-qt6==6.5.3 +# 6.5.3 leads to crash on macOS +pyqt6==6.4.2 +pyqt6-qt6==6.4.2 pysocks python-dateutil requests From bb79586d340a9c92f95fece50cf1ec34272a82a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20Fa=C3=9Fbender?= <robin@fassbender.io> Date: Mon, 30 Oct 2023 12:34:34 +0100 Subject: [PATCH 700/884] fixed issue with date string in new icingadb version fix should be backwards compatible fixed #962 --- Nagstamon/Servers/IcingaDBWeb.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index de7015c2f..33690beca 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -213,7 +213,7 @@ def _get_status(self): self.new_hosts[host_name].last_check = datetime.datetime.fromtimestamp(int(float(h['state']['last_update']))) else: self.new_hosts[host_name].last_check = datetime.datetime.fromisoformat(h['state']['last_update']) - + self.new_hosts[host_name].attempt = "{}/{}".format(h['state']['check_attempt'],h['max_check_attempts']) self.new_hosts[host_name].status_information = BeautifulSoup(h['state']['output'].replace('\n', ' ').strip(), 'html.parser').text self.new_hosts[host_name].passiveonly = not int(h.get('active_checks_enabled') or '0') @@ -243,7 +243,7 @@ def _get_status(self): else: last_state_change = datetime.datetime.fromisoformat(h['state']['last_state_change']) duration = datetime.datetime.now(timezone.utc).astimezone() - last_state_change - + if duration.total_seconds() > 0: self.new_hosts[host_name].duration = strfdelta(duration,'{days}d {hours}h {minutes}m {seconds}s') @@ -350,7 +350,7 @@ def _get_status(self): else: last_state_change = datetime.datetime.fromisoformat(s['state']['last_state_change']) duration = datetime.datetime.now(timezone.utc).astimezone() - last_state_change - + if duration.total_seconds() > 0: self.new_hosts[host_name].services[service_name].duration = strfdelta(duration, '{days}d {hours}h {minutes}m {seconds}s') From 3c49869ba2ad01c4a6fe8a8ad3598c8a0c710be3 Mon Sep 17 00:00:00 2001 From: aleskxyz <39186039+aleskxyz@users.noreply.github.com> Date: Mon, 30 Oct 2023 15:53:52 +0100 Subject: [PATCH 701/884] Return value of check_for_error Fix return value of check_for_error function to send false instead of None --- Nagstamon/Servers/Generic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index 08a9a04df..27fddc50c 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -1706,7 +1706,7 @@ def check_for_error(result, error, status_code) -> Optional[Result]: error=copy.deepcopy(error), status_code=copy.deepcopy(status_code)) else: - return None + return False def get_worst_status_current(self): """ From c49a1658e2ced4eb4456e10e54fba8a39e837bcb Mon Sep 17 00:00:00 2001 From: Alireza Eskandari <alireza.eskandari@solvians.com> Date: Mon, 30 Oct 2023 16:33:42 +0100 Subject: [PATCH 702/884] refactor return value of check_for_error to None --- Nagstamon/Servers/Generic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index 27fddc50c..08a9a04df 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -1706,7 +1706,7 @@ def check_for_error(result, error, status_code) -> Optional[Result]: error=copy.deepcopy(error), status_code=copy.deepcopy(status_code)) else: - return False + return None def get_worst_status_current(self): """ From 87f103784e70a62e79fdd6ece87d248dc12078a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20Fa=C3=9Fbender?= <robin@fassbender.io> Date: Mon, 30 Oct 2023 12:34:34 +0100 Subject: [PATCH 703/884] new release 3.13-20231117 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 40f5177a0..d82789d7f 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -124,7 +124,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.13-20231031' + VERSION = '3.13-20231117' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2023 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 8b36f7cd9..e0238d2b9 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.13-20231031) unstable; urgency=low +nagstamon (3.13-20231117) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Tue, Oct 31 2023 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Fri, Nov 17 2023 08:00:00 +0200 nagstamon (3.12.0) stable; urgency=low * New upstream From eafe457a02b14ade5d72d7017a8a1a4471c0cbee Mon Sep 17 00:00:00 2001 From: tk422 <not@gmail.com> Date: Wed, 22 Nov 2023 18:02:05 -0500 Subject: [PATCH 704/884] Fix Downtiming for Alertmanager --- .../Alertmanager/alertmanagerserver.py | 36 +++++++++---------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/Nagstamon/Servers/Alertmanager/alertmanagerserver.py b/Nagstamon/Servers/Alertmanager/alertmanagerserver.py index f65c3400d..ab07ccadb 100644 --- a/Nagstamon/Servers/Alertmanager/alertmanagerserver.py +++ b/Nagstamon/Servers/Alertmanager/alertmanagerserver.py @@ -276,31 +276,27 @@ def open_monitor(self, host, service=''): def _set_downtime(self, host, service, author, comment, fixed, start_time, end_time, hours, minutes): + alert = self.hosts[host].services[service] + # Convert local dates to UTC start_time_dt = convert_timestring_to_utc(start_time) end_time_dt = convert_timestring_to_utc(end_time) # API Spec: https://github.com/prometheus/alertmanager/blob/master/api/v2/openapi.yaml - silence_data = { - "matchers": [ - { - "name": "instance", - "value": host, - "isRegex": False, - "isEqual": False - }, - { - "name": "alertname", - "value": service, - "isRegex": False, - "isEqual": False - } - ], - "startsAt": start_time_dt, - "endsAt": end_time_dt, - "createdBy": author, - "comment": comment - } + silence_data = {} + silence_data["matchers"] = [] + for name, value in alert.labels.items(): + silence_data["matchers"].append({ + "name": name, + "value": value, + "isRegex": False, + "isEqual": True + }) + silence_data["startsAt"] = start_time_dt + silence_data["endsAt"] = end_time_dt + silence_data["comment"] = comment or "Nagstamon downtime" + silence_data["createdBy"] = author or "Nagstamon" + post = requests.post(self.monitor_url + self.API_PATH_SILENCES, json=silence_data) From 7ff29c0f05c231a5baa5271c09b5cf6517c47c84 Mon Sep 17 00:00:00 2001 From: tk422 <not@gmail.com> Date: Mon, 27 Nov 2023 13:10:26 -0500 Subject: [PATCH 705/884] Fix missing line --- Nagstamon/Servers/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/Servers/__init__.py b/Nagstamon/Servers/__init__.py index 51f57914e..f1f595a4c 100644 --- a/Nagstamon/Servers/__init__.py +++ b/Nagstamon/Servers/__init__.py @@ -220,7 +220,7 @@ def create_server(server=None): new_server.map_to_unknown = server.map_to_unknown new_server.map_to_warning = server.map_to_warning new_server.map_to_critical = server.map_to_critical - + new_server.map_to_down = server.map_to_down # server's individual preparations for HTTP connections (for example cookie creation) # is done in GetStatus() method of monitor From b43267447c5d30b20f6fc03cfd0f96fe4606520d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ferm=C3=ADn=20Olaiz?= <ferminolaiz@gmail.com> Date: Tue, 28 Nov 2023 13:07:37 -0300 Subject: [PATCH 706/884] Thruk: add disabled_backends field to filter out thruk backends via session cookies --- Nagstamon/Config.py | 3 +++ Nagstamon/QUI/__init__.py | 3 +++ Nagstamon/Servers/Generic.py | 3 +++ Nagstamon/Servers/Thruk.py | 3 +++ Nagstamon/Servers/__init__.py | 3 +++ Nagstamon/resources/qui/settings_server.ui | 24 ++++++++++++++++++++-- 6 files changed, 37 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index b9f8898eb..bd207b5f4 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -977,6 +977,9 @@ def __init__(self): self.notification_filter = "user.name=*" self.notification_lookback = "30 minutes" + # Thruk + self.disabled_backends = "" + class Action(object): """ class for custom actions, which whill be thrown into one config dictionary like the servers diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 7eb881802..ee770615a 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -5821,6 +5821,8 @@ def __init__(self, dialog): self.window.label_notification_filter: ['IcingaDBWebNotifications'], self.window.input_lineedit_notification_lookback: ['IcingaDBWebNotifications'], self.window.label_notification_lookback: ['IcingaDBWebNotifications'], + self.window.label_disabled_backends: ['Thruk'], + self.window.input_lineedit_disabled_backends: ['Thruk'], } # to be used when selecting authentication method Kerberos @@ -5932,6 +5934,7 @@ def decoration_function(self, **kwargs): # run through all input widgets and and apply defaults from config for widget in self.window.__dict__: + if widget.startswith('input_'): if widget.startswith('input_checkbox_'): setting = widget.split('input_checkbox_')[1] diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index 08a9a04df..f7ca97529 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -267,6 +267,9 @@ def __init__(self, **kwds): self.notification_filter = None self.notification_lookback = None + # Thruk + self.disabled_backends = None + def init_config(self): ''' set URLs for CGI - they are static and there is no need to set them with every cycle diff --git a/Nagstamon/Servers/Thruk.py b/Nagstamon/Servers/Thruk.py index 209f16223..72736fd73 100644 --- a/Nagstamon/Servers/Thruk.py +++ b/Nagstamon/Servers/Thruk.py @@ -135,6 +135,9 @@ def login(self): self.refresh_authentication = True return Result(result=None, error="Login failed") + if self.disabled_backends is not None: + self.session.cookies.set('thruk_backends', '&'.join((f"{v}=2" for v in self.disabled_backends.split(',')))) + print(self.session.cookies.get('thruk_backends')) def open_monitor(self, host, service=''): ''' diff --git a/Nagstamon/Servers/__init__.py b/Nagstamon/Servers/__init__.py index 51f57914e..bb34fd149 100644 --- a/Nagstamon/Servers/__init__.py +++ b/Nagstamon/Servers/__init__.py @@ -221,6 +221,9 @@ def create_server(server=None): new_server.map_to_warning = server.map_to_warning new_server.map_to_critical = server.map_to_critical + # Thruk + new_server.disabled_backends = server.disabled_backends + # server's individual preparations for HTTP connections (for example cookie creation) # is done in GetStatus() method of monitor diff --git a/Nagstamon/resources/qui/settings_server.ui b/Nagstamon/resources/qui/settings_server.ui index eedf4119c..3c80c2bac 100644 --- a/Nagstamon/resources/qui/settings_server.ui +++ b/Nagstamon/resources/qui/settings_server.ui @@ -54,6 +54,19 @@ </property> </widget> </item> + <item row="10" column="0"> + <widget class="QLabel" name="label_disabled_backends"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Disabled backends:</string> + </property> + </widget> + </item> <item row="4" column="2" colspan="2"> <widget class="QLineEdit" name="input_lineedit_monitor_url"> <property name="inputMask"> @@ -77,7 +90,7 @@ </property> </widget> </item> - <item row="6" column="2"> + <item row="7" column="2"> <widget class="QLineEdit" name="input_lineedit_username"> <property name="text"> <string>username</string> @@ -94,7 +107,7 @@ <item row="1" column="2"> <widget class="QComboBox" name="input_combobox_type"/> </item> - <item row="8" column="2"> + <item row="9" column="2"> <widget class="QLineEdit" name="input_lineedit_password"> <property name="text"> <string>1234567890</string> @@ -104,6 +117,13 @@ </property> </widget> </item> + <item row="11" column="2"> + <widget class="QLineEdit" name="input_lineedit_disabled_backends"> + <property name="text"> + <string>1234567890</string> + </property> + </widget> + </item> <item row="17" column="0"> <widget class="QCheckBox" name="input_checkbox_use_proxy"> <property name="text"> From 3b5c8c297b425ec54d64cbc33b12f5a2c2c91f65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ferm=C3=ADn=20Olaiz?= <ferminolaiz@gmail.com> Date: Tue, 28 Nov 2023 13:13:14 -0300 Subject: [PATCH 707/884] Remove line breaks --- Nagstamon/QUI/__init__.py | 1 - Nagstamon/Servers/__init__.py | 1 - 2 files changed, 2 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index ee770615a..2a20479cf 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -5934,7 +5934,6 @@ def decoration_function(self, **kwargs): # run through all input widgets and and apply defaults from config for widget in self.window.__dict__: - if widget.startswith('input_'): if widget.startswith('input_checkbox_'): setting = widget.split('input_checkbox_')[1] diff --git a/Nagstamon/Servers/__init__.py b/Nagstamon/Servers/__init__.py index bb34fd149..af019dcf4 100644 --- a/Nagstamon/Servers/__init__.py +++ b/Nagstamon/Servers/__init__.py @@ -224,7 +224,6 @@ def create_server(server=None): # Thruk new_server.disabled_backends = server.disabled_backends - # server's individual preparations for HTTP connections (for example cookie creation) # is done in GetStatus() method of monitor if server.enabled is True: From 0bb7ff493d55bb1db14fb30947d58f9d38c58d23 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 30 Nov 2023 14:45:54 +0100 Subject: [PATCH 708/884] 3.13-20231130 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index b9f8898eb..c34076140 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -124,7 +124,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.13-20231117' + VERSION = '3.13-20231130' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2023 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index e0238d2b9..803b32226 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.13-20231117) unstable; urgency=low +nagstamon (3.13-20231130) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Fri, Nov 17 2023 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Thu, Nov 30 2023 08:00:00 +0200 nagstamon (3.12.0) stable; urgency=low * New upstream From 655ae6f7a3e46ec2ffcbff8bb3a2c944b607b171 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 30 Nov 2023 14:45:54 +0100 Subject: [PATCH 709/884] 3.13-20231130 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index bd207b5f4..ee190c9b1 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -124,7 +124,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.13-20231117' + VERSION = '3.13-20231130' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2023 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index e0238d2b9..803b32226 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.13-20231117) unstable; urgency=low +nagstamon (3.13-20231130) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Fri, Nov 17 2023 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Thu, Nov 30 2023 08:00:00 +0200 nagstamon (3.12.0) stable; urgency=low * New upstream From a0b64f0dad24bff43ce718c98b8d1817e4d00138 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ferm=C3=ADn=20Olaiz?= <ferminolaiz@gmail.com> Date: Tue, 28 Nov 2023 13:07:37 -0300 Subject: [PATCH 710/884] Thruk: add disabled_backends field to filter out thruk backends via session cookies --- Nagstamon/QUI/__init__.py | 1 + Nagstamon/Servers/__init__.py | 1 + 2 files changed, 2 insertions(+) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 2a20479cf..ee770615a 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -5934,6 +5934,7 @@ def decoration_function(self, **kwargs): # run through all input widgets and and apply defaults from config for widget in self.window.__dict__: + if widget.startswith('input_'): if widget.startswith('input_checkbox_'): setting = widget.split('input_checkbox_')[1] diff --git a/Nagstamon/Servers/__init__.py b/Nagstamon/Servers/__init__.py index 4cae56d4d..8592c1d3f 100644 --- a/Nagstamon/Servers/__init__.py +++ b/Nagstamon/Servers/__init__.py @@ -225,6 +225,7 @@ def create_server(server=None): # Thruk new_server.disabled_backends = server.disabled_backends + # server's individual preparations for HTTP connections (for example cookie creation) # is done in GetStatus() method of monitor if server.enabled is True: From efbcdc2f6bac80f294c21390d21394188d3e19ac Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 30 Nov 2023 14:40:01 +0100 Subject: [PATCH 711/884] fixed settings --- Nagstamon/resources/qui/settings_server.ui | 983 +++++++++++---------- 1 file changed, 503 insertions(+), 480 deletions(-) diff --git a/Nagstamon/resources/qui/settings_server.ui b/Nagstamon/resources/qui/settings_server.ui index 3c80c2bac..79d5596dc 100644 --- a/Nagstamon/resources/qui/settings_server.ui +++ b/Nagstamon/resources/qui/settings_server.ui @@ -6,8 +6,8 @@ <rect> <x>0</x> <y>0</y> - <width>659</width> - <height>1231</height> + <width>639</width> + <height>1470</height> </rect> </property> <property name="sizePolicy"> @@ -41,21 +41,15 @@ <property name="verticalSpacing"> <number>2</number> </property> - <item row="8" column="0"> - <widget class="QLabel" name="label_password"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> + <item row="26" column="0" colspan="3"> + <widget class="QCheckBox" name="input_checkbox_show_options"> <property name="text"> - <string>Password:</string> + <string>Show more options</string> </property> </widget> </item> - <item row="10" column="0"> - <widget class="QLabel" name="label_disabled_backends"> + <item row="9" column="0"> + <widget class="QLabel" name="label_password"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> <horstretch>0</horstretch> @@ -63,22 +57,19 @@ </sizepolicy> </property> <property name="text"> - <string>Disabled backends:</string> + <string>Password:</string> </property> </widget> </item> - <item row="4" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_monitor_url"> - <property name="inputMask"> - <string/> - </property> + <item row="3" column="1" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_name"> <property name="text"> - <string>https://monitor-server</string> + <string>Monitor server</string> </property> </widget> </item> - <item row="3" column="0"> - <widget class="QLabel" name="label_name"> + <item row="7" column="0"> + <widget class="QLabel" name="label_username"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> <horstretch>0</horstretch> @@ -86,53 +77,32 @@ </sizepolicy> </property> <property name="text"> - <string>Monitor name:</string> + <string>Username:</string> </property> </widget> </item> - <item row="7" column="2"> + <item row="7" column="1"> <widget class="QLineEdit" name="input_lineedit_username"> <property name="text"> <string>username</string> </property> </widget> </item> - <item row="5" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_monitor_cgi_url"> - <property name="text"> - <string>https://monitor-server/monitor/cgi-bin</string> - </property> - </widget> - </item> - <item row="1" column="2"> - <widget class="QComboBox" name="input_combobox_type"/> - </item> - <item row="9" column="2"> - <widget class="QLineEdit" name="input_lineedit_password"> - <property name="text"> - <string>1234567890</string> - </property> - <property name="echoMode"> - <enum>QLineEdit::Password</enum> - </property> - </widget> - </item> - <item row="11" column="2"> - <widget class="QLineEdit" name="input_lineedit_disabled_backends"> - <property name="text"> - <string>1234567890</string> + <item row="5" column="0"> + <widget class="QLabel" name="label_monitor_cgi_url"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> </property> - </widget> - </item> - <item row="17" column="0"> - <widget class="QCheckBox" name="input_checkbox_use_proxy"> <property name="text"> - <string>Use proxy</string> + <string>Monitor CGI URL:</string> </property> </widget> </item> - <item row="18" column="0" colspan="4"> - <widget class="QGroupBox" name="groupbox_proxy"> + <item row="27" column="0" colspan="3"> + <widget class="QGroupBox" name="groupbox_options"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> <horstretch>0</horstretch> @@ -140,9 +110,9 @@ </sizepolicy> </property> <property name="title"> - <string>Proxy:</string> + <string>Options:</string> </property> - <layout class="QGridLayout" name="gridLayout_2"> + <layout class="QGridLayout" name="gridLayout_3"> <property name="leftMargin"> <number>10</number> </property> @@ -155,11 +125,14 @@ <property name="bottomMargin"> <number>10</number> </property> - <property name="spacing"> + <property name="horizontalSpacing"> + <number>10</number> + </property> + <property name="verticalSpacing"> <number>5</number> </property> - <item row="3" column="0"> - <widget class="QLabel" name="label_proxy_password"> + <item row="36" column="1"> + <widget class="QLabel" name="label_disabled_backends"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> <horstretch>0</horstretch> @@ -167,12 +140,22 @@ </sizepolicy> </property> <property name="text"> - <string>Proxy password:</string> + <string>Disabled backends:</string> </property> </widget> </item> - <item row="2" column="0"> - <widget class="QLabel" name="label_proxy_username"> + <item row="35" column="1"> + <widget class="QLabel" name="label_idp_ecp_endpoint"> + <property name="text"> + <string>IdP ECP endpoint URL</string> + </property> + </widget> + </item> + <item row="26" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_map_to_down"/> + </item> + <item row="12" column="1"> + <widget class="QLabel" name="label_host_filter"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> <horstretch>0</horstretch> @@ -180,26 +163,118 @@ </sizepolicy> </property> <property name="text"> - <string>Proxy username:</string> + <string>Host filter:</string> </property> </widget> </item> - <item row="2" column="1"> - <widget class="QLineEdit" name="input_lineedit_proxy_username"> + <item row="11" column="1"> + <widget class="QLabel" name="label_hashtag_filter"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> <property name="text"> - <string>proxyusername</string> + <string>Hashtag filter:</string> </property> </widget> </item> - <item row="1" column="1"> - <widget class="QLineEdit" name="input_lineedit_proxy_address"> + <item row="26" column="1"> + <widget class="QLabel" name="label_map_to_down"> <property name="text"> - <string>http://proxy:port/</string> + <string>Map to DOWN</string> </property> </widget> </item> - <item row="1" column="0"> - <widget class="QLabel" name="label_proxy_address"> + <item row="5" column="2"> + <widget class="QComboBox" name="input_combobox_authentication"/> + </item> + <item row="20" column="1"> + <widget class="QLabel" name="label_map_to_hostname"> + <property name="text"> + <string>Map to hostname:</string> + </property> + </widget> + </item> + <item row="6" column="3"> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="33" column="1" colspan="3"> + <widget class="QCheckBox" name="input_checkbox_force_authuser"> + <property name="text"> + <string>Only show permitted hosts for "see all" users (1.4.0i1 or newer)</string> + </property> + </widget> + </item> + <item row="34" column="1" colspan="3"> + <widget class="QGroupBox" name="groupbox_checkmk_views"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Views:</string> + </property> + <layout class="QGridLayout" name="gridLayout_4"> + <item row="0" column="0"> + <widget class="QLabel" name="label_checkmk_view_hosts"> + <property name="text"> + <string>Hosts:</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_checkmk_view_services"> + <property name="text"> + <string>Services:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="input_lineedit_checkmk_view_services"/> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="input_lineedit_checkmk_view_hosts"/> + </item> + <item row="0" column="2"> + <widget class="QPushButton" name="button_checkmk_view_hosts_reset"> + <property name="text"> + <string>Reset</string> + </property> + </widget> + </item> + <item row="1" column="2"> + <widget class="QPushButton" name="button_checkmk_view_services_reset"> + <property name="text"> + <string>Reset</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="23" column="1"> + <widget class="QLabel" name="label_map_to_ok"> + <property name="text"> + <string>Map to OK</string> + </property> + </widget> + </item> + <item row="8" column="1"> + <widget class="QLabel" name="label_monitor_site"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> <horstretch>0</horstretch> @@ -207,244 +282,148 @@ </sizepolicy> </property> <property name="text"> - <string>Proxy address:</string> + <string>Monitor Site:</string> </property> </widget> </item> - <item row="3" column="1"> - <widget class="QLineEdit" name="input_lineedit_proxy_password"> + <item row="32" column="1" colspan="3"> + <widget class="QCheckBox" name="input_checkbox_use_description_name_service"> <property name="text"> - <string>1234567890</string> + <string>Use description as service name</string> </property> - <property name="echoMode"> - <enum>QLineEdit::Password</enum> + </widget> + </item> + <item row="27" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_map_to_unknown"/> + </item> + <item row="7" column="1" colspan="3"> + <widget class="QCheckBox" name="input_checkbox_use_autologin"> + <property name="text"> + <string>Use autologin</string> </property> </widget> </item> - <item row="4" column="0" colspan="2"> - <widget class="QCheckBox" name="input_checkbox_use_proxy_from_os"> + <item row="9" column="1"> + <widget class="QLabel" name="label_autologin_key"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> <property name="text"> - <string>Use proxy from OS</string> + <string>Autologin Key:</string> </property> </widget> </item> - </layout> - </widget> - </item> - <item row="29" column="3"> - <widget class="QDialogButtonBox" name="button_box"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="standardButtons"> - <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> - </property> - </widget> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="label_server_type"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Monitor type:</string> - </property> - </widget> - </item> - <item row="6" column="0"> - <widget class="QLabel" name="label_username"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Username:</string> - </property> - </widget> - </item> - <item row="4" column="0"> - <widget class="QLabel" name="label_monitor_url"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Monitor URL:</string> - </property> - </widget> - </item> - <item row="3" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_name"> - <property name="text"> - <string>Monitor server</string> - </property> - </widget> - </item> - <item row="5" column="0"> - <widget class="QLabel" name="label_monitor_cgi_url"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Monitor CGI URL:</string> - </property> - </widget> - </item> - <item row="0" column="0" colspan="4"> - <widget class="QCheckBox" name="input_checkbox_enabled"> - <property name="text"> - <string>Enabled</string> - </property> - </widget> - </item> - <item row="8" column="3"> - <widget class="QCheckBox" name="input_checkbox_save_password"> - <property name="text"> - <string>Save password</string> - </property> - </widget> - </item> - <item row="26" column="0" colspan="4"> - <spacer name="verticalSpacer"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>0</width> - <height>0</height> - </size> - </property> - </spacer> - </item> - <item row="27" column="0" colspan="4"> - <widget class="QCheckBox" name="input_checkbox_show_options"> - <property name="text"> - <string>Show more options</string> - </property> - </widget> - </item> - <item row="28" column="0" colspan="4"> - <widget class="QGroupBox" name="groupbox_options"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="title"> - <string>Options:</string> - </property> - <layout class="QGridLayout" name="gridLayout_3"> - <property name="leftMargin"> - <number>10</number> - </property> - <property name="topMargin"> - <number>10</number> - </property> - <property name="rightMargin"> - <number>10</number> - </property> - <property name="bottomMargin"> - <number>10</number> - </property> - <property name="horizontalSpacing"> - <number>10</number> - </property> - <property name="verticalSpacing"> - <number>5</number> - </property> - <item row="2" column="2" colspan="2"> - <layout class="QHBoxLayout" name="horizontalLayout"> - <item> - <widget class="QLineEdit" name="input_lineedit_custom_cert_ca_file"/> - </item> - <item> - <widget class="QPushButton" name="button_choose_custom_cert_ca_file"> - <property name="text"> - <string>Choose file...</string> - </property> - </widget> - </item> - </layout> + <item row="22" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_map_to_status_information"/> </item> - <item row="7" column="1" colspan="3"> - <widget class="QCheckBox" name="input_checkbox_use_autologin"> + <item row="0" column="1" colspan="3"> + <widget class="QCheckBox" name="input_checkbox_ignore_cert"> <property name="text"> - <string>Use autologin</string> + <string>Ignore SSL/TLS certificate</string> </property> </widget> </item> - <item row="12" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_alertmanager_filter"/> - </item> - <item row="13" column="1"> - <widget class="QLabel" name="label_map_to_hostname"> + <item row="24" column="1"> + <widget class="QLabel" name="label_map_to_warning"> <property name="text"> - <string>Map to hostname:</string> + <string>Map to WARNING</string> </property> </widget> </item> - <item row="13" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_map_to_hostname"/> + <item row="11" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_hashtag_filter"> + <property name="text"> + <string/> + </property> + </widget> </item> - <item row="22" column="1" colspan="3"> + <item row="31" column="1" colspan="3"> <widget class="QCheckBox" name="input_checkbox_use_display_name_service"> <property name="text"> <string>Use display name as service name</string> </property> </widget> </item> - <item row="9" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_autologin_key"> + <item row="8" column="2"> + <widget class="QLineEdit" name="input_lineedit_monitor_site"> <property name="text"> - <string/> + <string>Site 1</string> </property> </widget> </item> - <item row="16" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_map_to_ok"/> + <item row="35" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_idp_ecp_endpoint"/> </item> - <item row="26" column="1"> - <widget class="QLabel" name="label_idp_ecp_endpoint"> + <item row="21" column="1"> + <widget class="QLabel" name="label_map_to_servicename"> <property name="text"> - <string>IdP ECP endpoint URL</string> + <string>Map to servicename:</string> </property> </widget> </item> - <item row="12" column="1"> + <item row="12" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_host_filter"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="14" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_notification_filter"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="16" column="1"> <widget class="QLabel" name="label_alertmanager_filter"> <property name="text"> <string>Filter:</string> </property> </widget> </item> - <item row="14" column="1"> - <widget class="QLabel" name="label_map_to_servicename"> + <item row="21" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_map_to_servicename"/> + </item> + <item row="16" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_alertmanager_filter"/> + </item> + <item row="6" column="1"> + <widget class="QLabel" name="label_timeout"> <property name="text"> - <string>Map to servicename:</string> + <string>Timeout:</string> </property> </widget> </item> - <item row="1" column="1" colspan="2"> - <widget class="QCheckBox" name="input_checkbox_custom_cert_use"> + <item row="6" column="2"> + <layout class="QHBoxLayout" name="horizontalLayout_timeout_seconds"> + <property name="spacing"> + <number>5</number> + </property> + <item> + <widget class="QSpinBox" name="input_spinbox_timeout"/> + </item> + <item> + <widget class="QLabel" name="label_timeout_sec"> + <property name="text"> + <string>seconds</string> + </property> + </widget> + </item> + </layout> + </item> + <item row="36" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_disabled_backends"> <property name="text"> - <string>Use custom CA file</string> + <string>1234567890</string> </property> </widget> </item> - <item row="6" column="3"> - <spacer name="horizontalSpacer"> + <item row="5" column="3"> + <spacer name="horizontalSpacer_2"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> @@ -456,53 +435,70 @@ </property> </spacer> </item> - <item row="11" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_service_filter"> + <item row="29" column="1" colspan="3"> + <widget class="QCheckBox" name="input_checkbox_no_cookie_auth"> <property name="text"> - <string/> + <string>Do not use cookie authentication</string> </property> </widget> </item> - <item row="11" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_notification_filter"> + <item row="2" column="1"> + <widget class="QLabel" name="label_custom_ca_file"> <property name="text"> - <string/> + <string>Custom CA file: </string> </property> </widget> </item> - <item row="12" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_notification_lookback"> + <item row="27" column="1"> + <widget class="QLabel" name="label_map_to_unknown"> <property name="text"> - <string/> + <string>Map to UNKNOWN</string> </property> </widget> </item> - <item row="6" column="1"> - <widget class="QLabel" name="label_timeout"> + <item row="2" column="2" colspan="2"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLineEdit" name="input_lineedit_custom_cert_ca_file"/> + </item> + <item> + <widget class="QPushButton" name="button_choose_custom_cert_ca_file"> + <property name="text"> + <string>Choose file...</string> + </property> + </widget> + </item> + </layout> + </item> + <item row="13" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_service_filter"> <property name="text"> - <string>Timeout:</string> + <string/> </property> </widget> </item> - <item row="24" column="1" colspan="3"> - <widget class="QCheckBox" name="input_checkbox_force_authuser"> + <item row="20" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_map_to_hostname"/> + </item> + <item row="1" column="1" colspan="2"> + <widget class="QCheckBox" name="input_checkbox_custom_cert_use"> <property name="text"> - <string>Only show permitted hosts for "see all" users (1.4.0i1 or newer)</string> + <string>Use custom CA file</string> </property> </widget> </item> - <item row="15" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_map_to_status_information"/> + <item row="24" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_map_to_warning"/> </item> - <item row="20" column="1" colspan="3"> - <widget class="QCheckBox" name="input_checkbox_no_cookie_auth"> + <item row="25" column="1"> + <widget class="QLabel" name="label_map_to_critical"> <property name="text"> - <string>Do not use cookie authentication</string> + <string>Map to CRITICAL</string> </property> </widget> </item> - <item row="9" column="1"> - <widget class="QLabel" name="label_autologin_key"> + <item row="13" column="1"> + <widget class="QLabel" name="label_service_filter"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> <horstretch>0</horstretch> @@ -510,47 +506,35 @@ </sizepolicy> </property> <property name="text"> - <string>Autologin Key:</string> + <string>Service filter:</string> </property> </widget> </item> - <item row="5" column="3"> - <spacer name="horizontalSpacer_2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> + <item row="5" column="1"> + <widget class="QLabel" name="label_auth_type"> + <property name="text"> + <string>Authentication:</string> </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item row="26" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_idp_ecp_endpoint"/> + </widget> </item> - <item row="5" column="1"> - <widget class="QLabel" name="label_auth_type"> + <item row="30" column="1" colspan="3"> + <widget class="QCheckBox" name="input_checkbox_use_display_name_host"> <property name="text"> - <string>Authentication:</string> + <string>Use display name as host name</string> </property> </widget> </item> - <item row="11" column="1"> - <widget class="QLabel" name="label_service_filter"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> + <item row="9" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_autologin_key"> <property name="text"> - <string>Service filter:</string> + <string/> </property> </widget> </item> - <item row="11" column="1"> + <item row="25" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_map_to_critical"/> + </item> + <item row="14" column="1"> <widget class="QLabel" name="label_notification_filter"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> @@ -563,38 +547,25 @@ </property> </widget> </item> - <item row="12" column="1"> - <widget class="QLabel" name="label_notification_lookback"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> + <item row="28" column="1" colspan="2"> + <widget class="QCheckBox" name="input_checkbox_can_change_only"> <property name="text"> - <string>Notification lookback horizon:</string> + <string>Only show services that the user can change or set downtimes on</string> </property> </widget> </item> - <item row="6" column="2"> - <layout class="QHBoxLayout" name="horizontalLayout_timeout_seconds"> - <property name="spacing"> - <number>5</number> + <item row="23" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_map_to_ok"/> + </item> + <item row="22" column="1"> + <widget class="QLabel" name="label_map_to_status_information"> + <property name="text"> + <string>Map to status_information:</string> </property> - <item> - <widget class="QSpinBox" name="input_spinbox_timeout"/> - </item> - <item> - <widget class="QLabel" name="label_timeout_sec"> - <property name="text"> - <string>seconds</string> - </property> - </widget> - </item> - </layout> + </widget> </item> - <item row="8" column="1"> - <widget class="QLabel" name="label_monitor_site"> + <item row="15" column="1"> + <widget class="QLabel" name="label_notification_lookback"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> <horstretch>0</horstretch> @@ -602,71 +573,112 @@ </sizepolicy> </property> <property name="text"> - <string>Monitor Site:</string> + <string>Notification lookback horizon:</string> </property> </widget> </item> - <item row="23" column="1" colspan="3"> - <widget class="QCheckBox" name="input_checkbox_use_description_name_service"> + <item row="15" column="2" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_notification_lookback"> <property name="text"> - <string>Use description as service name</string> - </property> - </widget> - </item> - <item row="5" column="2"> - <widget class="QComboBox" name="input_combobox_authentication"/> - </item> - <item row="25" column="1" colspan="3"> - <widget class="QGroupBox" name="groupbox_checkmk_views"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="title"> - <string>Views:</string> + <string/> </property> - <layout class="QGridLayout" name="gridLayout_4"> - <item row="0" column="0"> - <widget class="QLabel" name="label_checkmk_view_hosts"> - <property name="text"> - <string>Hosts:</string> - </property> - </widget> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="label_checkmk_view_services"> - <property name="text"> - <string>Services:</string> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QLineEdit" name="input_lineedit_checkmk_view_services"/> - </item> - <item row="0" column="1"> - <widget class="QLineEdit" name="input_lineedit_checkmk_view_hosts"/> - </item> - <item row="0" column="2"> - <widget class="QPushButton" name="button_checkmk_view_hosts_reset"> - <property name="text"> - <string>Reset</string> - </property> - </widget> - </item> - <item row="1" column="2"> - <widget class="QPushButton" name="button_checkmk_view_services_reset"> - <property name="text"> - <string>Reset</string> - </property> - </widget> - </item> - </layout> </widget> </item> - <item row="10" column="1"> - <widget class="QLabel" name="label_host_filter"> + </layout> + </widget> + </item> + <item row="17" column="0"> + <widget class="QCheckBox" name="input_checkbox_use_proxy"> + <property name="text"> + <string>Use proxy</string> + </property> + </widget> + </item> + <item row="5" column="1" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_monitor_cgi_url"> + <property name="text"> + <string>https://monitor-server/monitor/cgi-bin</string> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="label_monitor_url"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Monitor URL:</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_server_type"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Monitor type:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QComboBox" name="input_combobox_type"/> + </item> + <item row="0" column="0" colspan="3"> + <widget class="QCheckBox" name="input_checkbox_enabled"> + <property name="text"> + <string>Enabled</string> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="label_name"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Monitor name:</string> + </property> + </widget> + </item> + <item row="18" column="0" colspan="3"> + <widget class="QGroupBox" name="groupbox_proxy"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Proxy:</string> + </property> + <layout class="QGridLayout" name="gridLayout_2"> + <property name="leftMargin"> + <number>10</number> + </property> + <property name="topMargin"> + <number>10</number> + </property> + <property name="rightMargin"> + <number>10</number> + </property> + <property name="bottomMargin"> + <number>10</number> + </property> + <property name="spacing"> + <number>5</number> + </property> + <item row="3" column="0"> + <widget class="QLabel" name="label_proxy_password"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> <horstretch>0</horstretch> @@ -674,12 +686,12 @@ </sizepolicy> </property> <property name="text"> - <string>Host filter:</string> + <string>Proxy password:</string> </property> </widget> </item> - <item row="10" column="1"> - <widget class="QLabel" name="label_hashtag_filter"> + <item row="2" column="0"> + <widget class="QLabel" name="label_proxy_username"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> <horstretch>0</horstretch> @@ -687,119 +699,94 @@ </sizepolicy> </property> <property name="text"> - <string>Hashtag filter:</string> - </property> - </widget> - </item> - <item row="11" column="1" colspan="3"> - <widget class="QCheckBox" name="input_checkbox_can_change_only"> - <property name="text"> - <string>Only show services that the user can change or set downtimes on</string> - </property> - </widget> - </item> - <item row="0" column="1" colspan="3"> - <widget class="QCheckBox" name="input_checkbox_ignore_cert"> - <property name="text"> - <string>Ignore SSL/TLS certificate</string> - </property> - </widget> - </item> - <item row="14" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_map_to_servicename"/> - </item> - <item row="10" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_host_filter"> - <property name="text"> - <string/> - </property> - </widget> - </item> - <item row="10" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_hashtag_filter"> - <property name="text"> - <string/> + <string>Proxy username:</string> </property> </widget> </item> <item row="2" column="1"> - <widget class="QLabel" name="label_custom_ca_file"> - <property name="text"> - <string>Custom CA file: </string> - </property> - </widget> - </item> - <item row="15" column="1"> - <widget class="QLabel" name="label_map_to_status_information"> - <property name="text"> - <string>Map to status_information:</string> - </property> - </widget> - </item> - <item row="8" column="2"> - <widget class="QLineEdit" name="input_lineedit_monitor_site"> + <widget class="QLineEdit" name="input_lineedit_proxy_username"> <property name="text"> - <string>Site 1</string> + <string>proxyusername</string> </property> </widget> </item> - <item row="21" column="1" colspan="3"> - <widget class="QCheckBox" name="input_checkbox_use_display_name_host"> + <item row="1" column="1"> + <widget class="QLineEdit" name="input_lineedit_proxy_address"> <property name="text"> - <string>Use display name as host name</string> + <string>http://proxy:port/</string> </property> </widget> </item> - <item row="17" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_map_to_warning"/> - </item> - <item row="18" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_map_to_critical"/> - </item> - <item row="19" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_map_to_down"/> - </item> - <item row="20" column="2" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_map_to_unknown"/> - </item> - <item row="16" column="1"> - <widget class="QLabel" name="label_map_to_ok"> - <property name="text"> - <string>Map to OK</string> + <item row="1" column="0"> + <widget class="QLabel" name="label_proxy_address"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> </property> - </widget> - </item> - <item row="17" column="1"> - <widget class="QLabel" name="label_map_to_warning"> <property name="text"> - <string>Map to WARNING</string> + <string>Proxy address:</string> </property> </widget> </item> - <item row="18" column="1"> - <widget class="QLabel" name="label_map_to_critical"> + <item row="3" column="1"> + <widget class="QLineEdit" name="input_lineedit_proxy_password"> <property name="text"> - <string>Map to CRITICAL</string> + <string>1234567890</string> </property> - </widget> - </item> - <item row="19" column="1"> - <widget class="QLabel" name="label_map_to_down"> - <property name="text"> - <string>Map to DOWN</string> + <property name="echoMode"> + <enum>QLineEdit::Password</enum> </property> </widget> </item> - <item row="20" column="1"> - <widget class="QLabel" name="label_map_to_unknown"> + <item row="4" column="0" colspan="2"> + <widget class="QCheckBox" name="input_checkbox_use_proxy_from_os"> <property name="text"> - <string>Map to UNKNOWN</string> + <string>Use proxy from OS</string> </property> </widget> </item> </layout> </widget> </item> + <item row="9" column="1"> + <widget class="QLineEdit" name="input_lineedit_password"> + <property name="text"> + <string>1234567890</string> + </property> + <property name="echoMode"> + <enum>QLineEdit::Password</enum> + </property> + </widget> + </item> + <item row="9" column="2"> + <widget class="QCheckBox" name="input_checkbox_save_password"> + <property name="text"> + <string>Save password</string> + </property> + </widget> + </item> + <item row="28" column="2"> + <widget class="QDialogButtonBox" name="button_box"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + <item row="4" column="1" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_monitor_url"> + <property name="inputMask"> + <string/> + </property> + <property name="text"> + <string>https://monitor-server</string> + </property> + </widget> + </item> </layout> </widget> <tabstops> @@ -816,6 +803,42 @@ <tabstop>input_lineedit_proxy_username</tabstop> <tabstop>input_lineedit_proxy_password</tabstop> <tabstop>input_checkbox_use_proxy_from_os</tabstop> + <tabstop>input_checkbox_show_options</tabstop> + <tabstop>input_checkbox_ignore_cert</tabstop> + <tabstop>input_checkbox_custom_cert_use</tabstop> + <tabstop>input_lineedit_custom_cert_ca_file</tabstop> + <tabstop>button_choose_custom_cert_ca_file</tabstop> + <tabstop>input_combobox_authentication</tabstop> + <tabstop>input_spinbox_timeout</tabstop> + <tabstop>input_checkbox_use_autologin</tabstop> + <tabstop>input_lineedit_monitor_site</tabstop> + <tabstop>input_lineedit_autologin_key</tabstop> + <tabstop>input_lineedit_hashtag_filter</tabstop> + <tabstop>input_lineedit_host_filter</tabstop> + <tabstop>input_lineedit_service_filter</tabstop> + <tabstop>input_lineedit_notification_filter</tabstop> + <tabstop>input_lineedit_notification_lookback</tabstop> + <tabstop>input_lineedit_alertmanager_filter</tabstop> + <tabstop>input_lineedit_map_to_hostname</tabstop> + <tabstop>input_lineedit_map_to_servicename</tabstop> + <tabstop>input_lineedit_map_to_status_information</tabstop> + <tabstop>input_lineedit_map_to_ok</tabstop> + <tabstop>input_lineedit_map_to_warning</tabstop> + <tabstop>input_lineedit_map_to_critical</tabstop> + <tabstop>input_lineedit_map_to_down</tabstop> + <tabstop>input_lineedit_map_to_unknown</tabstop> + <tabstop>input_checkbox_can_change_only</tabstop> + <tabstop>input_checkbox_no_cookie_auth</tabstop> + <tabstop>input_checkbox_use_display_name_host</tabstop> + <tabstop>input_checkbox_use_display_name_service</tabstop> + <tabstop>input_checkbox_use_description_name_service</tabstop> + <tabstop>input_checkbox_force_authuser</tabstop> + <tabstop>input_lineedit_checkmk_view_hosts</tabstop> + <tabstop>button_checkmk_view_hosts_reset</tabstop> + <tabstop>input_lineedit_checkmk_view_services</tabstop> + <tabstop>button_checkmk_view_services_reset</tabstop> + <tabstop>input_lineedit_idp_ecp_endpoint</tabstop> + <tabstop>input_lineedit_disabled_backends</tabstop> </tabstops> <resources/> <connections/> From 39df467318c5294fdcd9325f70004b42eaf19c74 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 30 Nov 2023 14:49:12 +0100 Subject: [PATCH 712/884] 3.13-20231130 --- Nagstamon/Servers/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Nagstamon/Servers/__init__.py b/Nagstamon/Servers/__init__.py index 8592c1d3f..4cae56d4d 100644 --- a/Nagstamon/Servers/__init__.py +++ b/Nagstamon/Servers/__init__.py @@ -225,7 +225,6 @@ def create_server(server=None): # Thruk new_server.disabled_backends = server.disabled_backends - # server's individual preparations for HTTP connections (for example cookie creation) # is done in GetStatus() method of monitor if server.enabled is True: From 7a3a230da3ea4c4ecceeabdedd951cde4f758bc4 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Fri, 1 Dec 2023 09:08:18 +0100 Subject: [PATCH 713/884] cr_image_version: 2 --- .github/workflows/build-release-latest.yml | 2 +- .github/workflows/build-release-stable.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index edb92662a..972ac9bd6 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -12,7 +12,7 @@ env: repo_dir: nagstamon-jekyll/docs/repo cr_image: ghcr.io/henriwahl/build-nagstamon # to be increased if new updates of build images are necessary - cr_image_version: 1 + cr_image_version: 2 # release type this file is used for release: latest diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index 567bebd95..bde959403 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -8,7 +8,7 @@ env: repo_dir: nagstamon-jekyll/docs/repo cr_image: ghcr.io/henriwahl/build-nagstamon # to be increased if new updates of build images are necessary - cr_image_version: 1 + cr_image_version: 2 release: stable jobs: From 3a41b9b271842bbbfd2965bb9c31e5964b100382 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Fri, 1 Dec 2023 09:10:45 +0100 Subject: [PATCH 714/884] cr_image_version: 3 --- .github/workflows/build-release-latest.yml | 2 +- .github/workflows/build-release-stable.yml | 2 +- build/docker/Dockerfile-fedora-37 | 2 ++ build/docker/Dockerfile-fedora-38 | 2 ++ build/docker/Dockerfile-fedora-39 | 2 ++ build/docker/Dockerfile-rhel-9 | 2 ++ 6 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 972ac9bd6..e2cbee84e 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -12,7 +12,7 @@ env: repo_dir: nagstamon-jekyll/docs/repo cr_image: ghcr.io/henriwahl/build-nagstamon # to be increased if new updates of build images are necessary - cr_image_version: 2 + cr_image_version: 3 # release type this file is used for release: latest diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index bde959403..4810c38bb 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -8,7 +8,7 @@ env: repo_dir: nagstamon-jekyll/docs/repo cr_image: ghcr.io/henriwahl/build-nagstamon # to be increased if new updates of build images are necessary - cr_image_version: 2 + cr_image_version: 3 release: stable jobs: diff --git a/build/docker/Dockerfile-fedora-37 b/build/docker/Dockerfile-fedora-37 index c59d2d130..5a2f121b8 100644 --- a/build/docker/Dockerfile-fedora-37 +++ b/build/docker/Dockerfile-fedora-37 @@ -1,6 +1,8 @@ FROM fedora:37 LABEL maintainer=henri@nagstamon.de +RUN dnf -y upgrade + RUN dnf -y install createrepo_c \ desktop-file-utils \ git \ diff --git a/build/docker/Dockerfile-fedora-38 b/build/docker/Dockerfile-fedora-38 index bafd08e66..cafe383ed 100644 --- a/build/docker/Dockerfile-fedora-38 +++ b/build/docker/Dockerfile-fedora-38 @@ -1,6 +1,8 @@ FROM fedora:38 LABEL maintainer=henri@nagstamon.de +RUN dnf -y upgrade + RUN dnf -y install createrepo_c \ desktop-file-utils \ git \ diff --git a/build/docker/Dockerfile-fedora-39 b/build/docker/Dockerfile-fedora-39 index 8cf4f8e8a..ef5bf64a8 100644 --- a/build/docker/Dockerfile-fedora-39 +++ b/build/docker/Dockerfile-fedora-39 @@ -1,6 +1,8 @@ FROM fedora:39 LABEL maintainer=henri@nagstamon.de +RUN dnf -y upgrade + RUN dnf -y install createrepo_c \ desktop-file-utils \ git \ diff --git a/build/docker/Dockerfile-rhel-9 b/build/docker/Dockerfile-rhel-9 index f8ca54e87..a1e7997a8 100644 --- a/build/docker/Dockerfile-rhel-9 +++ b/build/docker/Dockerfile-rhel-9 @@ -1,6 +1,8 @@ FROM rockylinux:9 LABEL maintainer=henri@nagstamon.de +RUN dnf -y upgrade + RUN dnf -y install 'dnf-command(config-manager)' \ epel-release && \ crb enable && \ From 1003660f7fc6b09b75641d749c8d38feec40f3b8 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Fri, 1 Dec 2023 22:51:23 +0100 Subject: [PATCH 715/884] QT_QPA_PLATFORMTHEME gnome --- Nagstamon/Config.py | 17 ++++++++++++----- build/debian/changelog | 4 ++-- build/docker/Dockerfile-fedora-35 | 28 ---------------------------- build/docker/Dockerfile-fedora-36 | 2 ++ 4 files changed, 16 insertions(+), 35 deletions(-) delete mode 100644 build/docker/Dockerfile-fedora-35 diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index ee190c9b1..1c0f31f21 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -68,6 +68,13 @@ else: DESKTOP_NEEDS_FIX = False +# use QT platform plugins if not set otherwise on GNOME desktop +# based on QGnomePlatform +if not os.environ.get('QT_QPA_PLATFORMTHEME'): + if os.environ.get('XDG_SESSION_DESKTOP'): + if os.environ['XDG_SESSION_DESKTOP'].lower() == 'gnome': + os.environ['QT_QPA_PLATFORMTHEME'] = 'gnome' + # queue.Queue() needs threading module which might be not such a good idea to be used # because QThread is already in use # maybe not the most logical place here to be defined but at least all @@ -124,7 +131,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.13-20231130' + VERSION = '3.13-20231201' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2023 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' @@ -754,14 +761,14 @@ def _DefaultActions(self): """ if OS == OS_WINDOWS: defaultactions = {"RDP": Action(name="RDP", description="Connect via RDP.", - type="command", string="C:\windows\system32\mstsc.exe /v:$ADDRESS$"), + type="command", string="C:/windows/system32/mstsc.exe /v:$ADDRESS$"), "VNC": Action(name="VNC", description="Connect via VNC.", - type="command", string="C:\Program Files\TightVNC\vncviewer.exe $ADDRESS$"), + type="command", string="C:/Program Files/TightVNC/vncviewer.exe $ADDRESS$"), "Telnet": Action(name="Telnet", description="Connect via Telnet.", - type="command", string="C:\Windows\System32\Telnet.exe root@$ADDRESS$"), + type="command", string="C:/Windows/System32\telnet.exe root@$ADDRESS$"), "SSH": Action(name="SSH", description="Connect via SSH.", type="command", - string="C:\Program Files\PuTTY\putty.exe -l root $ADDRESS$")} + string="C:/Program Files/PuTTY/putty.exe -l root $ADDRESS$")} elif OS == OS_MACOS: defaultactions = {"RDP": Action(name="RDP", description="Connect via RDP.", type="command", string="open rdp://$ADDRESS$"), diff --git a/build/debian/changelog b/build/debian/changelog index 803b32226..f54afb621 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.13-20231130) unstable; urgency=low +nagstamon (3.13-20231201) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Thu, Nov 30 2023 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Fri, Dec 01 2023 08:00:00 +0200 nagstamon (3.12.0) stable; urgency=low * New upstream diff --git a/build/docker/Dockerfile-fedora-35 b/build/docker/Dockerfile-fedora-35 deleted file mode 100644 index a30ad8302..000000000 --- a/build/docker/Dockerfile-fedora-35 +++ /dev/null @@ -1,28 +0,0 @@ -FROM fedora:35 -LABEL maintainer=henri@nagstamon.de - -RUN dnf -y install createrepo_c \ - desktop-file-utils \ - git \ - python3 \ - python3-beautifulsoup4 \ - python3-cryptography \ - python3-dateutil \ - python3-devel \ - python3-keyring \ - python3-lxml \ - python3-psutil \ - python3-qt5 \ - python3-qt5-devel \ - python3-requests \ - python3-requests-kerberos \ - python3-SecretStorage \ - qt5-qtsvg \ - qt5-qtmultimedia \ - rpm-build - -# ugly workaround for legacy Qt5 on Fedora < 36 -CMD cd /nagstamon/build && \ - sed -i s/pyqt6/pyqt5/g redhat/nagstamon.spec && \ - sed -i s/qt6/qt5/g redhat/nagstamon.spec && \ - /usr/bin/python3 build.py diff --git a/build/docker/Dockerfile-fedora-36 b/build/docker/Dockerfile-fedora-36 index ecad4993c..4736dc7f2 100644 --- a/build/docker/Dockerfile-fedora-36 +++ b/build/docker/Dockerfile-fedora-36 @@ -1,6 +1,8 @@ FROM fedora:36 LABEL maintainer=henri@nagstamon.de +RUN dnf -y upgrade + RUN dnf -y install createrepo_c \ desktop-file-utils \ git \ From 844ed81a3a46cf03ff12378b0b2d5592073febd2 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Fri, 1 Dec 2023 23:30:50 +0100 Subject: [PATCH 716/884] QT_QPA_PLATFORMTHEME gnome --- Nagstamon/resources/qui/settings_server.ui | 390 ++++++++++----------- 1 file changed, 195 insertions(+), 195 deletions(-) diff --git a/Nagstamon/resources/qui/settings_server.ui b/Nagstamon/resources/qui/settings_server.ui index 79d5596dc..98908b16c 100644 --- a/Nagstamon/resources/qui/settings_server.ui +++ b/Nagstamon/resources/qui/settings_server.ui @@ -7,7 +7,7 @@ <x>0</x> <y>0</y> <width>639</width> - <height>1470</height> + <height>1527</height> </rect> </property> <property name="sizePolicy"> @@ -41,15 +41,15 @@ <property name="verticalSpacing"> <number>2</number> </property> - <item row="26" column="0" colspan="3"> - <widget class="QCheckBox" name="input_checkbox_show_options"> + <item row="5" column="1" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_monitor_cgi_url"> <property name="text"> - <string>Show more options</string> + <string>https://monitor-server/monitor/cgi-bin</string> </property> </widget> </item> - <item row="9" column="0"> - <widget class="QLabel" name="label_password"> + <item row="4" column="0"> + <widget class="QLabel" name="label_monitor_url"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> <horstretch>0</horstretch> @@ -57,7 +57,40 @@ </sizepolicy> </property> <property name="text"> - <string>Password:</string> + <string>Monitor URL:</string> + </property> + </widget> + </item> + <item row="5" column="0"> + <widget class="QLabel" name="label_monitor_cgi_url"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Monitor CGI URL:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QComboBox" name="input_combobox_type"/> + </item> + <item row="9" column="1"> + <widget class="QLineEdit" name="input_lineedit_password"> + <property name="text"> + <string>1234567890</string> + </property> + <property name="echoMode"> + <enum>QLineEdit::Password</enum> + </property> + </widget> + </item> + <item row="9" column="2"> + <widget class="QCheckBox" name="input_checkbox_save_password"> + <property name="text"> + <string>Save password</string> </property> </widget> </item> @@ -68,8 +101,18 @@ </property> </widget> </item> - <item row="7" column="0"> - <widget class="QLabel" name="label_username"> + <item row="4" column="1" colspan="2"> + <widget class="QLineEdit" name="input_lineedit_monitor_url"> + <property name="inputMask"> + <string/> + </property> + <property name="text"> + <string>https://monitor-server</string> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="label_name"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> <horstretch>0</horstretch> @@ -77,7 +120,7 @@ </sizepolicy> </property> <property name="text"> - <string>Username:</string> + <string>Monitor name:</string> </property> </widget> </item> @@ -88,8 +131,15 @@ </property> </widget> </item> - <item row="5" column="0"> - <widget class="QLabel" name="label_monitor_cgi_url"> + <item row="17" column="0"> + <widget class="QCheckBox" name="input_checkbox_use_proxy"> + <property name="text"> + <string>Use proxy</string> + </property> + </widget> + </item> + <item row="9" column="0"> + <widget class="QLabel" name="label_password"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> <horstretch>0</horstretch> @@ -97,7 +147,127 @@ </sizepolicy> </property> <property name="text"> - <string>Monitor CGI URL:</string> + <string>Password:</string> + </property> + </widget> + </item> + <item row="0" column="0" colspan="3"> + <widget class="QCheckBox" name="input_checkbox_enabled"> + <property name="text"> + <string>Enabled</string> + </property> + </widget> + </item> + <item row="18" column="0" colspan="3"> + <widget class="QGroupBox" name="groupbox_proxy"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Proxy:</string> + </property> + <layout class="QGridLayout" name="gridLayout_2"> + <property name="leftMargin"> + <number>10</number> + </property> + <property name="topMargin"> + <number>10</number> + </property> + <property name="rightMargin"> + <number>10</number> + </property> + <property name="bottomMargin"> + <number>10</number> + </property> + <property name="spacing"> + <number>5</number> + </property> + <item row="3" column="0"> + <widget class="QLabel" name="label_proxy_password"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Proxy password:</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_proxy_username"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Proxy username:</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLineEdit" name="input_lineedit_proxy_username"> + <property name="text"> + <string>proxyusername</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="input_lineedit_proxy_address"> + <property name="text"> + <string>http://proxy:port/</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_proxy_address"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Proxy address:</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QLineEdit" name="input_lineedit_proxy_password"> + <property name="text"> + <string>1234567890</string> + </property> + <property name="echoMode"> + <enum>QLineEdit::Password</enum> + </property> + </widget> + </item> + <item row="4" column="0" colspan="2"> + <widget class="QCheckBox" name="input_checkbox_use_proxy_from_os"> + <property name="text"> + <string>Use proxy from OS</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_server_type"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Monitor type:</string> </property> </widget> </item> @@ -587,58 +757,18 @@ </layout> </widget> </item> - <item row="17" column="0"> - <widget class="QCheckBox" name="input_checkbox_use_proxy"> - <property name="text"> - <string>Use proxy</string> - </property> - </widget> - </item> - <item row="5" column="1" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_monitor_cgi_url"> - <property name="text"> - <string>https://monitor-server/monitor/cgi-bin</string> - </property> - </widget> - </item> - <item row="4" column="0"> - <widget class="QLabel" name="label_monitor_url"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Monitor URL:</string> - </property> - </widget> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="label_server_type"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Monitor type:</string> + <item row="28" column="2"> + <widget class="QDialogButtonBox" name="button_box"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QComboBox" name="input_combobox_type"/> - </item> - <item row="0" column="0" colspan="3"> - <widget class="QCheckBox" name="input_checkbox_enabled"> - <property name="text"> - <string>Enabled</string> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> </property> </widget> </item> - <item row="3" column="0"> - <widget class="QLabel" name="label_name"> + <item row="7" column="0"> + <widget class="QLabel" name="label_username"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> <horstretch>0</horstretch> @@ -646,144 +776,14 @@ </sizepolicy> </property> <property name="text"> - <string>Monitor name:</string> - </property> - </widget> - </item> - <item row="18" column="0" colspan="3"> - <widget class="QGroupBox" name="groupbox_proxy"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="title"> - <string>Proxy:</string> - </property> - <layout class="QGridLayout" name="gridLayout_2"> - <property name="leftMargin"> - <number>10</number> - </property> - <property name="topMargin"> - <number>10</number> - </property> - <property name="rightMargin"> - <number>10</number> - </property> - <property name="bottomMargin"> - <number>10</number> - </property> - <property name="spacing"> - <number>5</number> - </property> - <item row="3" column="0"> - <widget class="QLabel" name="label_proxy_password"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Proxy password:</string> - </property> - </widget> - </item> - <item row="2" column="0"> - <widget class="QLabel" name="label_proxy_username"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Proxy username:</string> - </property> - </widget> - </item> - <item row="2" column="1"> - <widget class="QLineEdit" name="input_lineedit_proxy_username"> - <property name="text"> - <string>proxyusername</string> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QLineEdit" name="input_lineedit_proxy_address"> - <property name="text"> - <string>http://proxy:port/</string> - </property> - </widget> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="label_proxy_address"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Preferred"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="text"> - <string>Proxy address:</string> - </property> - </widget> - </item> - <item row="3" column="1"> - <widget class="QLineEdit" name="input_lineedit_proxy_password"> - <property name="text"> - <string>1234567890</string> - </property> - <property name="echoMode"> - <enum>QLineEdit::Password</enum> - </property> - </widget> - </item> - <item row="4" column="0" colspan="2"> - <widget class="QCheckBox" name="input_checkbox_use_proxy_from_os"> - <property name="text"> - <string>Use proxy from OS</string> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item row="9" column="1"> - <widget class="QLineEdit" name="input_lineedit_password"> - <property name="text"> - <string>1234567890</string> - </property> - <property name="echoMode"> - <enum>QLineEdit::Password</enum> - </property> - </widget> - </item> - <item row="9" column="2"> - <widget class="QCheckBox" name="input_checkbox_save_password"> - <property name="text"> - <string>Save password</string> - </property> - </widget> - </item> - <item row="28" column="2"> - <widget class="QDialogButtonBox" name="button_box"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="standardButtons"> - <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + <string>Username:</string> </property> </widget> </item> - <item row="4" column="1" colspan="2"> - <widget class="QLineEdit" name="input_lineedit_monitor_url"> - <property name="inputMask"> - <string/> - </property> + <item row="26" column="0" colspan="3"> + <widget class="QCheckBox" name="input_checkbox_show_options"> <property name="text"> - <string>https://monitor-server</string> + <string>Show more options</string> </property> </widget> </item> From 079897deb22716e84178f13ae89ce9351b717fdf Mon Sep 17 00:00:00 2001 From: tk422 <not@gmail.com> Date: Fri, 8 Dec 2023 11:10:07 -0500 Subject: [PATCH 717/884] harden creating a new alert when it already exists --- Nagstamon/Servers/Alertmanager/alertmanagerserver.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Nagstamon/Servers/Alertmanager/alertmanagerserver.py b/Nagstamon/Servers/Alertmanager/alertmanagerserver.py index ab07ccadb..24884371a 100644 --- a/Nagstamon/Servers/Alertmanager/alertmanagerserver.py +++ b/Nagstamon/Servers/Alertmanager/alertmanagerserver.py @@ -248,6 +248,12 @@ def _get_status(self): self.new_hosts[service.host].name = str(service.host) self.new_hosts[service.host].server = self.name self.new_hosts[service.host].services[service.name] = service + if service.name not in self.new_hosts[service.host].services: + self.new_hosts[service.host].services[service.name] = service + else: + # Compare the labels if they match do *not* create a new "instance" of alert + if not service.labels == self.new_hosts[service.host].services[service.name].labels: + self.new_hosts[service.host].services[service.name + service.fingerprint[0:4]] = service except Exception as the_exception: # set checking flag back to False From 1b443cabbff7d0a9bd68d981d7cb9d5cf47a22cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20Fa=C3=9Fbender?= <robin@fassbender.io> Date: Sat, 6 Jan 2024 14:39:56 +0100 Subject: [PATCH 718/884] Handle null JSON output in Icinga DB Web checks - Fixes issue where a check with no output in Icinga DB Web is marked as null in JSON, resulting in 'none' in code - Resolves processing error by casting the value to a string --- Nagstamon/Servers/IcingaDBWeb.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index 9370d3c2b..0ffdfcec3 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -214,7 +214,7 @@ def _get_status(self): self.new_hosts[host_name].last_check = datetime.datetime.fromisoformat(h['state']['last_update']) self.new_hosts[host_name].attempt = "{}/{}".format(h['state']['check_attempt'],h['max_check_attempts']) - self.new_hosts[host_name].status_information = BeautifulSoup(h['state']['output'].replace('\n', ' ').strip(), 'html.parser').text + self.new_hosts[host_name].status_information = BeautifulSoup(str(h['state']['output']).replace('\n', ' ').strip(), 'html.parser').text self.new_hosts[host_name].passiveonly = not int(h.get('active_checks_enabled') or '0') self.new_hosts[host_name].notifications_disabled = not int(h.get('notifications_enabled') or '0') self.new_hosts[host_name].flapping = bool(int(h['state']['is_flapping'] or 0)) @@ -317,7 +317,7 @@ def _get_status(self): self.new_hosts[host_name].services[service_name].last_check = datetime.datetime.fromisoformat(s['state']['last_update']) self.new_hosts[host_name].services[service_name].attempt = "{}/{}".format(s['state']['check_attempt'],s['max_check_attempts']) - self.new_hosts[host_name].services[service_name].status_information = BeautifulSoup(s['state']['output'].replace('\n', ' ').strip(), 'html.parser').text + self.new_hosts[host_name].services[service_name].status_information = BeautifulSoup(str(s['state']['output']).replace('\n', ' ').strip(), 'html.parser').text self.new_hosts[host_name].services[service_name].passiveonly = not int(s.get('active_checks_enabled') or '0') self.new_hosts[host_name].services[service_name].notifications_disabled = not int(s.get('notifications_enabled') or '0') self.new_hosts[host_name].services[service_name].flapping = bool(int(s['state']['is_flapping'] or 0)) From 9275a846a9589e128ac34a5397a4fcb26dd3508e Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Fri, 8 Dec 2023 07:45:02 +0100 Subject: [PATCH 719/884] 3.13-20231208 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 7e7824732..882ddb773 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -131,7 +131,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.13-20231130' + VERSION = '3.13-20231208' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2023 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index f54afb621..69a7ae9d6 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.13-20231201) unstable; urgency=low +nagstamon (3.13-20231208) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Fri, Dec 01 2023 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Thu, Dec 08 2023 08:00:00 +0200 nagstamon (3.12.0) stable; urgency=low * New upstream From c05c669318037699e13c5a96fbcba94bf83ab3cd Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 8 Jan 2024 22:27:51 +0100 Subject: [PATCH 720/884] 3.13-20240108 --- COPYRIGHT | 2 +- Nagstamon/Config.py | 6 +++--- Nagstamon/Helpers.py | 2 +- Nagstamon/Objects.py | 2 +- Nagstamon/QUI/__init__.py | 2 +- Nagstamon/QUI/qt.py | 2 +- Nagstamon/Servers/Alertmanager/LICENSE | 2 +- Nagstamon/Servers/Centreon/CentreonAPI.py | 2 +- Nagstamon/Servers/Centreon/CentreonLegacy.py | 2 +- Nagstamon/Servers/Centreon/__init__.py | 2 +- Nagstamon/Servers/Generic.py | 2 +- Nagstamon/Servers/Icinga.py | 2 +- Nagstamon/Servers/Icinga2API.py | 2 +- Nagstamon/Servers/IcingaDBWeb.py | 2 +- Nagstamon/Servers/IcingaDBWebNotifications.py | 2 +- Nagstamon/Servers/IcingaWeb2.py | 2 +- Nagstamon/Servers/Livestatus.py | 2 +- Nagstamon/Servers/Monitos3.py | 2 +- Nagstamon/Servers/Monitos4x.py | 2 +- Nagstamon/Servers/Multisite.py | 2 +- Nagstamon/Servers/Nagios.py | 2 +- Nagstamon/Servers/Opsview.py | 2 +- Nagstamon/Servers/Prometheus.py | 2 +- Nagstamon/Servers/SnagView3.py | 2 +- Nagstamon/Servers/Thruk.py | 2 +- Nagstamon/Servers/__init__.py | 2 +- Nagstamon/Servers/op5Monitor.py | 2 +- Nagstamon/__init__.py | 2 +- Nagstamon/resources/qui/dialog_about.ui | 2 +- Nagstamon/thirdparty/__init__.py | 2 +- build/build.py | 2 +- build/debian/changelog | 4 ++-- nagstamon.py | 2 +- setup.py | 2 +- 34 files changed, 37 insertions(+), 37 deletions(-) diff --git a/COPYRIGHT b/COPYRIGHT index 29bb3cea2..4208a096d 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -1 +1 @@ -Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. +Copyright (C) 2008-2024 Henri Wahl <henri@nagstamon.de> et al. diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 882ddb773..f57c5a501 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -2,7 +2,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2024 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -131,9 +131,9 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.13-20231208' + VERSION = '3.13-20240108' WEBSITE = 'https://nagstamon.de' - COPYRIGHT = '©2008-2023 Henri Wahl et al.' + COPYRIGHT = '©2008-2024 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' # dict of servers to offer for downloads if an update is available DOWNLOAD_SERVERS = {'nagstamon.de': 'https://github.com/HenriWahl/Nagstamon/releases'} diff --git a/Nagstamon/Helpers.py b/Nagstamon/Helpers.py index 7b0de31d0..2db28ad3e 100644 --- a/Nagstamon/Helpers.py +++ b/Nagstamon/Helpers.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2024 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Objects.py b/Nagstamon/Objects.py index efc56678b..09bfa2bce 100644 --- a/Nagstamon/Objects.py +++ b/Nagstamon/Objects.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2024 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index ee770615a..bc2bf27a6 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -1,6 +1,6 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2024 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/QUI/qt.py b/Nagstamon/QUI/qt.py index 64f0f3e46..3bc50c5bb 100644 --- a/Nagstamon/QUI/qt.py +++ b/Nagstamon/QUI/qt.py @@ -1,5 +1,5 @@ # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2024 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Alertmanager/LICENSE b/Nagstamon/Servers/Alertmanager/LICENSE index 43951f79b..82445400e 100644 --- a/Nagstamon/Servers/Alertmanager/LICENSE +++ b/Nagstamon/Servers/Alertmanager/LICENSE @@ -1,5 +1,5 @@ Nagstamon - Nagios status monitor for your desktop -Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. +Copyright (C) 2008-2024 Henri Wahl <henri@nagstamon.de> et al. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Centreon/CentreonAPI.py b/Nagstamon/Servers/Centreon/CentreonAPI.py index 01ef0c7e9..0ddea7a0c 100644 --- a/Nagstamon/Servers/Centreon/CentreonAPI.py +++ b/Nagstamon/Servers/Centreon/CentreonAPI.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2024 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Centreon/CentreonLegacy.py b/Nagstamon/Servers/Centreon/CentreonLegacy.py index 76ad00859..224a19954 100644 --- a/Nagstamon/Servers/Centreon/CentreonLegacy.py +++ b/Nagstamon/Servers/Centreon/CentreonLegacy.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2024 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Centreon/__init__.py b/Nagstamon/Servers/Centreon/__init__.py index 31eacf5f1..1aed1a7a4 100644 --- a/Nagstamon/Servers/Centreon/__init__.py +++ b/Nagstamon/Servers/Centreon/__init__.py @@ -1,5 +1,5 @@ # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2024 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index f7ca97529..cd0621112 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2024 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Icinga.py b/Nagstamon/Servers/Icinga.py index 81b2a4ae3..d67b47cf6 100644 --- a/Nagstamon/Servers/Icinga.py +++ b/Nagstamon/Servers/Icinga.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2024 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Icinga2API.py b/Nagstamon/Servers/Icinga2API.py index 1fa2ab339..aedd92f5a 100644 --- a/Nagstamon/Servers/Icinga2API.py +++ b/Nagstamon/Servers/Icinga2API.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2024 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index 0ffdfcec3..2d4d4a140 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2024 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/IcingaDBWebNotifications.py b/Nagstamon/Servers/IcingaDBWebNotifications.py index 41109df25..73e28acf8 100644 --- a/Nagstamon/Servers/IcingaDBWebNotifications.py +++ b/Nagstamon/Servers/IcingaDBWebNotifications.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2024 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/IcingaWeb2.py b/Nagstamon/Servers/IcingaWeb2.py index 369b96993..7f23d9046 100644 --- a/Nagstamon/Servers/IcingaWeb2.py +++ b/Nagstamon/Servers/IcingaWeb2.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2024 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Livestatus.py b/Nagstamon/Servers/Livestatus.py index d8e5edf7b..eafa32dc3 100644 --- a/Nagstamon/Servers/Livestatus.py +++ b/Nagstamon/Servers/Livestatus.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2024 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Monitos3.py b/Nagstamon/Servers/Monitos3.py index 9378ba5d0..1ef0cacd9 100644 --- a/Nagstamon/Servers/Monitos3.py +++ b/Nagstamon/Servers/Monitos3.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2024 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Monitos4x.py b/Nagstamon/Servers/Monitos4x.py index 7e139259d..78d8b059b 100644 --- a/Nagstamon/Servers/Monitos4x.py +++ b/Nagstamon/Servers/Monitos4x.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2024 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Multisite.py b/Nagstamon/Servers/Multisite.py index b7be265f9..3bfa453c9 100644 --- a/Nagstamon/Servers/Multisite.py +++ b/Nagstamon/Servers/Multisite.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2024 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Nagios.py b/Nagstamon/Servers/Nagios.py index 78e4a7df0..9a7214901 100644 --- a/Nagstamon/Servers/Nagios.py +++ b/Nagstamon/Servers/Nagios.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2024 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Opsview.py b/Nagstamon/Servers/Opsview.py index ea9577fec..eee2d053d 100644 --- a/Nagstamon/Servers/Opsview.py +++ b/Nagstamon/Servers/Opsview.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2024 Henri Wahl <henri@nagstamon.de> et al. # # Based on https://github.com/duncs/Nagstamon by @duncs # diff --git a/Nagstamon/Servers/Prometheus.py b/Nagstamon/Servers/Prometheus.py index 5b502dc16..ff90f5c14 100644 --- a/Nagstamon/Servers/Prometheus.py +++ b/Nagstamon/Servers/Prometheus.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2024 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/SnagView3.py b/Nagstamon/Servers/SnagView3.py index 4240ded4f..848af56fd 100644 --- a/Nagstamon/Servers/SnagView3.py +++ b/Nagstamon/Servers/SnagView3.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2024 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/Thruk.py b/Nagstamon/Servers/Thruk.py index 72736fd73..bf19543ed 100644 --- a/Nagstamon/Servers/Thruk.py +++ b/Nagstamon/Servers/Thruk.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2024 Henri Wahl <henri@nagstamon.de> et al. # Thruk additions copyright by dcec@Github # # This program is free software; you can redistribute it and/or modify diff --git a/Nagstamon/Servers/__init__.py b/Nagstamon/Servers/__init__.py index 4cae56d4d..d2657af4b 100644 --- a/Nagstamon/Servers/__init__.py +++ b/Nagstamon/Servers/__init__.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2024 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/Servers/op5Monitor.py b/Nagstamon/Servers/op5Monitor.py index 9d520a353..cf59d45e7 100644 --- a/Nagstamon/Servers/op5Monitor.py +++ b/Nagstamon/Servers/op5Monitor.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2024 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/__init__.py b/Nagstamon/__init__.py index 66f815d12..f556ba66c 100644 --- a/Nagstamon/__init__.py +++ b/Nagstamon/__init__.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2024 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/Nagstamon/resources/qui/dialog_about.ui b/Nagstamon/resources/qui/dialog_about.ui index a098f1a86..7b2bb6580 100644 --- a/Nagstamon/resources/qui/dialog_about.ui +++ b/Nagstamon/resources/qui/dialog_about.ui @@ -75,7 +75,7 @@ <item> <widget class="QLabel" name="label_copyright"> <property name="text"> - <string>©2008-2023 Henri Wahl et al.</string> + <string>©2008-2024 Henri Wahl et al.</string> </property> <property name="alignment"> <set>Qt::AlignCenter</set> diff --git a/Nagstamon/thirdparty/__init__.py b/Nagstamon/thirdparty/__init__.py index cb8df1192..acf9b0b43 100644 --- a/Nagstamon/thirdparty/__init__.py +++ b/Nagstamon/thirdparty/__init__.py @@ -1,7 +1,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2024 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/build/build.py b/build/build.py index da833dd11..a3c6baec2 100644 --- a/build/build.py +++ b/build/build.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2024 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/build/debian/changelog b/build/debian/changelog index 69a7ae9d6..4514f46dc 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.13-20231208) unstable; urgency=low +nagstamon (3.13-20240108) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Thu, Dec 08 2023 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Mon, Jan 08 2024 08:00:00 +0200 nagstamon (3.12.0) stable; urgency=low * New upstream diff --git a/nagstamon.py b/nagstamon.py index bdd6cbb4c..4c5db0b4d 100755 --- a/nagstamon.py +++ b/nagstamon.py @@ -2,7 +2,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2024 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/setup.py b/setup.py index 430e482f9..8c4b0be5c 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ # encoding: utf-8 # Nagstamon - Nagios status monitor for your desktop -# Copyright (C) 2008-2023 Henri Wahl <henri@nagstamon.de> et al. +# Copyright (C) 2008-2024 Henri Wahl <henri@nagstamon.de> et al. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by From 0ff24d25c493b00decc121274a37773f6d210582 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 8 Jan 2024 22:31:56 +0100 Subject: [PATCH 721/884] 3.13-20240108 pyqt6 6.6.1 --- build/requirements/linux.txt | 4 ++-- build/requirements/macos.txt | 4 ++-- build/requirements/windows.txt | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build/requirements/linux.txt b/build/requirements/linux.txt index 8eb4fbc88..bffc7660d 100644 --- a/build/requirements/linux.txt +++ b/build/requirements/linux.txt @@ -4,8 +4,8 @@ dbus-python keyring lxml psutil -pyqt6==6.5.3 -pyqt6-qt6==6.5.3 +pyqt6==6.6.1 +pyqt6-qt6==6.6.1 pysocks python-dateutil requests diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index 0142b3424..35d537d58 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -7,8 +7,8 @@ psutil pyinstaller==5.13.2 pyobjc-framework-ApplicationServices # 6.5.3 leads to crash on macOS -pyqt6==6.4.2 -pyqt6-qt6==6.4.2 +pyqt6==6.6.1 +pyqt6-qt6==6.6.1 pysocks python-dateutil requests diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index d456b3f81..f6ab32114 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -9,8 +9,8 @@ psutil pyinstaller==5.13.2 pypiwin32 # try newer version -pyqt6==6.5.3 -pyqt6-qt6==6.5.3 +pyqt6==6.6.1 +pyqt6-qt6==6.6.1 pysocks python-dateutil requests From 5dce842eb6af44410448392b3429c848f8481349 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 8 Jan 2024 23:10:45 +0100 Subject: [PATCH 722/884] 3.13-20240108 pyqt6 linux 6.5.3 --- build/requirements/linux.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/requirements/linux.txt b/build/requirements/linux.txt index bffc7660d..8eb4fbc88 100644 --- a/build/requirements/linux.txt +++ b/build/requirements/linux.txt @@ -4,8 +4,8 @@ dbus-python keyring lxml psutil -pyqt6==6.6.1 -pyqt6-qt6==6.6.1 +pyqt6==6.5.3 +pyqt6-qt6==6.5.3 pysocks python-dateutil requests From 4063e5daaf1cb192995cb262a671a77d8424f90d Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Tue, 9 Jan 2024 10:10:29 +0100 Subject: [PATCH 723/884] 3.13-20240108 pyqt6 macos 6.4.2 rollback --- build/requirements/macos.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index 35d537d58..8b52e0a2e 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -7,8 +7,9 @@ psutil pyinstaller==5.13.2 pyobjc-framework-ApplicationServices # 6.5.3 leads to crash on macOS -pyqt6==6.6.1 -pyqt6-qt6==6.6.1 +# 6.6.1 leads to crash on macOS +pyqt6==6.4.2 +pyqt6-qt6==6.4.2 pysocks python-dateutil requests From 35acb82fff2ec4bea9133197e027d289b9c0d5eb Mon Sep 17 00:00:00 2001 From: Yannick Charton <tontonitch-pro@yahoo.fr> Date: Mon, 15 Jan 2024 10:37:59 +0100 Subject: [PATCH 724/884] Fixing the use of isnumeric on float Fixing the use of isnumeric on float. I thought I requested that change, but seems that it is not in the code. --- Nagstamon/Servers/IcingaDBWeb.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index 0ffdfcec3..df357f9fb 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -208,7 +208,7 @@ def _get_status(self): else: self.new_hosts[host_name].status = self.STATES_MAPPING['hosts'][int(h['state']['soft_state'])] - if h['state']['last_update'].isnumeric(): # new version of icingadb doesnt return unix timestamp + if h['state']['last_update'].replace(".", "").isnumeric(): # new version of icingadb doesnt return unix timestamp self.new_hosts[host_name].last_check = datetime.datetime.fromtimestamp(int(float(h['state']['last_update']))) else: self.new_hosts[host_name].last_check = datetime.datetime.fromisoformat(h['state']['last_update']) @@ -237,7 +237,7 @@ def _get_status(self): # extra duration needed for calculation self.new_hosts[host_name].duration = 'n/a' if h['state']['last_state_change'] is not None: - if h['state']['last_state_change'].isnumeric(): # new version of icingadb doesnt return unix timestamp + if h['state']['last_state_change'].replace(".", "").isnumeric(): # new version of icingadb doesnt return unix timestamp duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(float(h['state']['last_state_change']))) else: last_state_change = datetime.datetime.fromisoformat(h['state']['last_state_change']) @@ -311,7 +311,7 @@ def _get_status(self): else: self.new_hosts[host_name].services[service_name].status = self.STATES_MAPPING['services'][int(s['state']['soft_state'])] - if s['state']['last_update'].isnumeric(): # new version of icingadb doesnt return unix timestamp + if s['state']['last_update'].replace(".", "").isnumeric(): # new version of icingadb doesnt return unix timestamp self.new_hosts[host_name].services[service_name].last_check = datetime.datetime.fromtimestamp(int(float(s['state']['last_update']))) else: self.new_hosts[host_name].services[service_name].last_check = datetime.datetime.fromisoformat(s['state']['last_update']) @@ -344,7 +344,7 @@ def _get_status(self): # extra duration needed for calculation self.new_hosts[host_name].services[service_name].duration = 'n/a' if s['state']['last_state_change'] is not None: - if s['state']['last_update'].isnumeric(): # new version of icingadb doesnt return unix timestamp + if s['state']['last_update'].replace(".", "").isnumeric(): # new version of icingadb doesnt return unix timestamp duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(float(s['state']['last_state_change']))) else: last_state_change = datetime.datetime.fromisoformat(s['state']['last_state_change']) From 142926171e2f6d9efc75cd9833021b0884742f81 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 28 Jan 2024 17:32:28 +0100 Subject: [PATCH 725/884] fix missing fixes --- Nagstamon/Servers/IcingaDBWeb.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index 1fcb0d3b2..58355b569 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -36,6 +36,7 @@ import copy import json import datetime +from datetime import timezone import socket from bs4 import BeautifulSoup @@ -241,7 +242,7 @@ def _get_status(self): duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(float(h['state']['last_state_change']))) else: last_state_change = datetime.datetime.fromisoformat(h['state']['last_state_change']) - duration = datetime.datetime.now().replace(tzinfo=last_state_change.tzinfo) - last_state_change + duration = datetime.datetime.now(timezone.utc).astimezone() - last_state_change if duration.total_seconds() > 0: self.new_hosts[host_name].duration = strfdelta(duration,'{days}d {hours}h {minutes}m {seconds}s') @@ -348,7 +349,7 @@ def _get_status(self): duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(int(float(s['state']['last_state_change']))) else: last_state_change = datetime.datetime.fromisoformat(s['state']['last_state_change']) - duration = datetime.datetime.now().replace(tzinfo=last_state_change.tzinfo) - last_state_change + duration = datetime.datetime.now(timezone.utc).astimezone() - last_state_change if duration.total_seconds() > 0: self.new_hosts[host_name].services[service_name].duration = strfdelta(duration, '{days}d {hours}h {minutes}m {seconds}s') From 65cf70cda650e4011eaeee2181289ddbeaa1c38b Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 20 Feb 2024 19:22:16 +0100 Subject: [PATCH 726/884] try along with IcingaWeb2 recheck --- Nagstamon/Servers/IcingaWeb2.py | 34 ++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/Nagstamon/Servers/IcingaWeb2.py b/Nagstamon/Servers/IcingaWeb2.py index 7f23d9046..fa4fff79f 100644 --- a/Nagstamon/Servers/IcingaWeb2.py +++ b/Nagstamon/Servers/IcingaWeb2.py @@ -356,7 +356,9 @@ def _set_recheck(self, host, service): if service == '': url = self.monitor_cgi_url + '/monitoring/host/show?host=' + self.hosts[host].real_name else: - url = self.monitor_cgi_url + '/monitoring/service/show?host=' + self.hosts[host].real_name + '&service=' + self.hosts[host].services[service].real_name + url = self.monitor_cgi_url + \ + '/monitoring/service/show?host=' + self.hosts[host].real_name + \ + '&service=' + urllib.parse.quote(self.hosts[host].services[service].real_name) result = self.FetchURL(url, giveback='raw') if result.error != '': @@ -367,20 +369,22 @@ def _set_recheck(self, host, service): pagesoup = BeautifulSoup(pageraw, 'html.parser') # Extract the relevant form element values - - formtag = pagesoup.find('form', {'name':'IcingaModuleMonitoringFormsCommandObjectCheckNowCommandForm'}) - CSRFToken = formtag.findNext('input', {'name':'CSRFToken'})['value'] - formUID = formtag.findNext('input', {'name':'formUID'})['value'] - btn_submit = formtag.findNext('button', {'name':'btn_submit'})['value'] - - # Pass these values to the same URL as cgi_data - cgi_data = {} - cgi_data['CSRFToken'] = CSRFToken - cgi_data['formUID'] = formUID - cgi_data['btn_submit'] = btn_submit - result = self.FetchURL(url, giveback='raw', cgi_data=cgi_data) - - + try: + formtag = pagesoup.find('form', {'name':'IcingaModuleMonitoringFormsCommandObjectCheckNowCommandForm'}) + CSRFToken = formtag.findNext('input', {'name':'CSRFToken'})['value'] + formUID = formtag.findNext('input', {'name':'formUID'})['value'] + btn_submit = formtag.findNext('button', {'name':'btn_submit'})['value'] + + # Pass these values to the same URL as cgi_data + cgi_data = {} + cgi_data['CSRFToken'] = CSRFToken + cgi_data['formUID'] = formUID + cgi_data['btn_submit'] = btn_submit + result = self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + except AttributeError: + if conf.debug_mode: + self.Debug(server=self.get_name(), host=host, service=service, + debug='No valid CSRFToken available') # Overwrite function from generic server to add expire_time value def set_acknowledge(self, info_dict): ''' From 4cbf671c2608f99baf544a2c744683a5acf592a8 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Wed, 21 Feb 2024 22:35:21 +0100 Subject: [PATCH 727/884] try along with IcingaWeb2 csrf token --- Nagstamon/Config.py | 2 +- Nagstamon/Servers/IcingaWeb2.py | 167 ++++++++++++++++++-------------- build/debian/changelog | 4 +- 3 files changed, 98 insertions(+), 75 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index f57c5a501..82e372158 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -131,7 +131,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.13-20240108' + VERSION = '3.13-20240221' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2024 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/Servers/IcingaWeb2.py b/Nagstamon/Servers/IcingaWeb2.py index fa4fff79f..9cc17f552 100644 --- a/Nagstamon/Servers/IcingaWeb2.py +++ b/Nagstamon/Servers/IcingaWeb2.py @@ -335,7 +335,6 @@ def _get_status(self): del s, host_name, service_name except: - import traceback traceback.print_exc(file=sys.stdout) @@ -356,6 +355,7 @@ def _set_recheck(self, host, service): if service == '': url = self.monitor_cgi_url + '/monitoring/host/show?host=' + self.hosts[host].real_name else: + # to make the request working even with %-characters in service name it has to be quoted url = self.monitor_cgi_url + \ '/monitoring/service/show?host=' + self.hosts[host].real_name + \ '&service=' + urllib.parse.quote(self.hosts[host].services[service].real_name) @@ -369,6 +369,7 @@ def _set_recheck(self, host, service): pagesoup = BeautifulSoup(pageraw, 'html.parser') # Extract the relevant form element values + # try-except needed in case the CSRFToken will not be found try: formtag = pagesoup.find('form', {'name':'IcingaModuleMonitoringFormsCommandObjectCheckNowCommandForm'}) CSRFToken = formtag.findNext('input', {'name':'CSRFToken'})['value'] @@ -380,11 +381,12 @@ def _set_recheck(self, host, service): cgi_data['CSRFToken'] = CSRFToken cgi_data['formUID'] = formUID cgi_data['btn_submit'] = btn_submit - result = self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + self.FetchURL(url, giveback='raw', cgi_data=cgi_data) except AttributeError: if conf.debug_mode: self.Debug(server=self.get_name(), host=host, service=service, debug='No valid CSRFToken available') + # Overwrite function from generic server to add expire_time value def set_acknowledge(self, info_dict): ''' @@ -413,14 +415,13 @@ def set_acknowledge(self, info_dict): def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=None, expire_time=None): # First retrieve the info page for this host/service if service == '': - # url = self.monitor_cgi_url + '/monitoring/host/acknowledge-problem?host=' + host url = '{0}/monitoring/host/acknowledge-problem?host={1}'.format(self.monitor_cgi_url, self.hosts[host].real_name) else: - # url = self.monitor_cgi_url + '/monitoring/service/acknowledge-problem?host=' + host + '&service=' + service + # to make the request working even with %-characters in service name it has to be quoted url = '{0}/monitoring/service/acknowledge-problem?host={1}&service={2}'.format(self.monitor_cgi_url, self.hosts[host].real_name, - self.hosts[host].services[service].real_name) + urllib.parse.quote(self.hosts[host].services[service].real_name)) result = self.FetchURL(url, giveback='raw') if result.error != '': @@ -431,28 +432,34 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi pagesoup = BeautifulSoup(pageraw, 'html.parser') # Extract the relevant form element values - formtag = pagesoup.find('form', {'name':'IcingaModuleMonitoringFormsCommandObjectAcknowledgeProblemCommandForm'}) + # try-except needed in case the CSRFToken will not be found + try: + formtag = pagesoup.find('form', {'name':'IcingaModuleMonitoringFormsCommandObjectAcknowledgeProblemCommandForm'}) - CSRFToken = formtag.findNext('input', {'name':'CSRFToken'})['value'] - formUID = formtag.findNext('input', {'name':'formUID'})['value'] - btn_submit = formtag.findNext('input', {'name':'btn_submit'})['value'] + CSRFToken = formtag.findNext('input', {'name':'CSRFToken'})['value'] + formUID = formtag.findNext('input', {'name':'formUID'})['value'] + btn_submit = formtag.findNext('input', {'name':'btn_submit'})['value'] - # Pass these values to the same URL as cgi_data - cgi_data = {} - cgi_data['CSRFToken'] = CSRFToken - cgi_data['formUID'] = formUID - cgi_data['btn_submit'] = btn_submit -# - cgi_data['comment'] = comment - cgi_data['persistent'] = int(persistent) - cgi_data['sticky'] = int(sticky) - cgi_data['notify'] = int(notify) - cgi_data['comment'] = comment - if expire_time: - cgi_data['expire'] = 1 - cgi_data['expire_time'] = expire_time + # Pass these values to the same URL as cgi_data + cgi_data = {} + cgi_data['CSRFToken'] = CSRFToken + cgi_data['formUID'] = formUID + cgi_data['btn_submit'] = btn_submit + cgi_data['comment'] = comment + cgi_data['persistent'] = int(persistent) + cgi_data['sticky'] = int(sticky) + cgi_data['notify'] = int(notify) + cgi_data['comment'] = comment + if expire_time: + cgi_data['expire'] = 1 + cgi_data['expire_time'] = expire_time - self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + + except AttributeError: + if conf.debug_mode: + self.Debug(server=self.get_name(), host=host, service=service, + debug='No valid CSRFToken available') if len(all_services) > 0: for s in all_services: @@ -466,7 +473,10 @@ def _set_submit_check_result(self, host, service, state, comment, check_output, url = self.monitor_cgi_url + '/monitoring/host/process-check-result?host=' + self.hosts[host].real_name status = self.STATES_MAPPING_REV['hosts'][state.upper()] else: - url = self.monitor_cgi_url + '/monitoring/service/process-check-result?host=' + self.hosts[host].real_name + '&service=' + self.hosts[host].services[service].real_name + # to make the request working even with %-characters in service name it has to be quoted + url = self.monitor_cgi_url + \ + '/monitoring/service/process-check-result?host=' + self.hosts[host].real_name + \ + '&service=' + urllib.parse.quote(self.hosts[host].services[service].real_name) status = self.STATES_MAPPING_REV['services'][state.upper()] result = self.FetchURL(url, giveback='raw') @@ -479,31 +489,38 @@ def _set_submit_check_result(self, host, service, state, comment, check_output, pagesoup = BeautifulSoup(pageraw, 'html.parser') # Extract the relevant form element values + # try-except needed in case the CSRFToken will not be found + try: + formtag = pagesoup.find('form', {'name':'IcingaModuleMonitoringFormsCommandObjectProcessCheckResultCommandForm'}) + CSRFToken = formtag.findNext('input', {'name':'CSRFToken'})['value'] + formUID = formtag.findNext('input', {'name':'formUID'})['value'] + btn_submit = formtag.findNext('input', {'name':'btn_submit'})['value'] - formtag = pagesoup.find('form', {'name':'IcingaModuleMonitoringFormsCommandObjectProcessCheckResultCommandForm'}) - CSRFToken = formtag.findNext('input', {'name':'CSRFToken'})['value'] - formUID = formtag.findNext('input', {'name':'formUID'})['value'] - btn_submit = formtag.findNext('input', {'name':'btn_submit'})['value'] - - # Pass these values to the same URL as cgi_data - cgi_data = {} - cgi_data['CSRFToken'] = CSRFToken - cgi_data['formUID'] = formUID - cgi_data['btn_submit'] = btn_submit + # Pass these values to the same URL as cgi_data + cgi_data = {} + cgi_data['CSRFToken'] = CSRFToken + cgi_data['formUID'] = formUID + cgi_data['btn_submit'] = btn_submit - cgi_data['status'] = status - cgi_data['output'] = check_output - cgi_data['perfdata'] = performance_data + cgi_data['status'] = status + cgi_data['output'] = check_output + cgi_data['perfdata'] = performance_data - self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + except AttributeError: + if conf.debug_mode: + self.Debug(server=self.get_name(), host=host, service=service, + debug='No valid CSRFToken available') def _set_downtime(self, host, service, author, comment, fixed, start_time, end_time, hours, minutes): # First retrieve the info page for this host/service if service == '': url = self.monitor_cgi_url + '/monitoring/host/schedule-downtime?host=' + self.hosts[host].real_name else: - url = self.monitor_cgi_url + '/monitoring/service/schedule-downtime?host=' + self.hosts[host].real_name + '&service=' + self.hosts[host].services[service].real_name + url = self.monitor_cgi_url + \ + '/monitoring/service/schedule-downtime?host=' + self.hosts[host].real_name + \ + '&service=' + urllib.parse.quote(self.hosts[host].services[service].real_name) result = self.FetchURL(url, giveback='raw') @@ -515,41 +532,47 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t pagesoup = BeautifulSoup(pageraw, 'html.parser') # Extract the relevant form element values - if service == '': - formtag = pagesoup.find('form', {'name':'IcingaModuleMonitoringFormsCommandObjectScheduleHostDowntimeCommandForm'}) - else: - formtag = pagesoup.find('form', {'name':'IcingaModuleMonitoringFormsCommandObjectScheduleServiceDowntimeCommandForm'}) - - CSRFToken = formtag.findNext('input', {'name':'CSRFToken'})['value'] - formUID = formtag.findNext('input', {'name':'formUID'})['value'] - btn_submit = formtag.findNext('input', {'name':'btn_submit'})['value'] - - # Pass these values to the same URL as cgi_data - cgi_data = {} - cgi_data['CSRFToken'] = CSRFToken - cgi_data['formUID'] = formUID - cgi_data['btn_submit'] = btn_submit - cgi_data['comment'] = comment - if fixed: - cgi_data['type'] = 'fixed' - else: - cgi_data['type'] = 'flexible' - cgi_data['hours'] = hours - cgi_data['minutes'] = minutes - if start_time == '' or start_time == 'n/a': - start = datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%S') - else: - start = start_time - if end_time == '' or end_time == 'n/a': - end = (datetime.datetime.now() + datetime.timedelta(hours=hours, minutes=minutes)).strftime('%Y-%m-%dT%H:%M:%S') - else: - end = end_time + # try-except needed in case the CSRFToken will not be found + try: + if service == '': + formtag = pagesoup.find('form', {'name':'IcingaModuleMonitoringFormsCommandObjectScheduleHostDowntimeCommandForm'}) + else: + formtag = pagesoup.find('form', {'name':'IcingaModuleMonitoringFormsCommandObjectScheduleServiceDowntimeCommandForm'}) + + CSRFToken = formtag.findNext('input', {'name':'CSRFToken'})['value'] + formUID = formtag.findNext('input', {'name':'formUID'})['value'] + btn_submit = formtag.findNext('input', {'name':'btn_submit'})['value'] + + # Pass these values to the same URL as cgi_data + cgi_data = {} + cgi_data['CSRFToken'] = CSRFToken + cgi_data['formUID'] = formUID + cgi_data['btn_submit'] = btn_submit + cgi_data['comment'] = comment + if fixed: + cgi_data['type'] = 'fixed' + else: + cgi_data['type'] = 'flexible' + cgi_data['hours'] = hours + cgi_data['minutes'] = minutes + if start_time == '' or start_time == 'n/a': + start = datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%S') + else: + start = start_time + if end_time == '' or end_time == 'n/a': + end = (datetime.datetime.now() + datetime.timedelta(hours=hours, minutes=minutes)).strftime('%Y-%m-%dT%H:%M:%S') + else: + end = end_time - cgi_data['start'] = start - cgi_data['end'] = end + cgi_data['start'] = start + cgi_data['end'] = end - self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + except AttributeError: + if conf.debug_mode: + self.Debug(server=self.get_name(), host=host, service=service, + debug='No valid CSRFToken available') def get_start_end(self, host): ''' diff --git a/build/debian/changelog b/build/debian/changelog index 4514f46dc..0fd93cb77 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.13-20240108) unstable; urgency=low +nagstamon (3.13-20240221) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Mon, Jan 08 2024 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Wed, Feb 21 2024 08:00:00 +0200 nagstamon (3.12.0) stable; urgency=low * New upstream From 6b0e619062b37b05c77364b18e84201a821875ff Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Wed, 21 Feb 2024 22:59:55 +0100 Subject: [PATCH 728/884] fix for numeric server name --- Nagstamon/Config.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 82e372158..c3b8e1363 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -444,6 +444,9 @@ def _LoadServersMultipleConfig(self): # time saving config try: for server in servers: + # server name needs to be a string at every cost + # see issue https://github.com/HenriWahl/Nagstamon/issues/1010 + servers[server].name = str(servers[server].name) # usernames for monitor server and proxy servers[server].username = self.DeObfuscate(servers[server].username) servers[server].proxy_username = self.DeObfuscate(servers[server].proxy_username) From 58fd17b46433c49402ab1df36c2db65fcd0d0d69 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 22 Feb 2024 13:17:28 +0100 Subject: [PATCH 729/884] prepare 3.14 --- ChangeLog | 15 +++++++++++++++ build/debian/changelog | 10 ++++++++++ 2 files changed, 25 insertions(+) diff --git a/ChangeLog b/ChangeLog index a64171fbb..2932a1ead 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +nagstamon (3.13-20240221) unstable; urgency=low + * New upstream + - improved Wayland support + - improved proxy support + - added Opsview hashtag filtering and can_change_only option + - fixes for Alertmanager + - fixes for Centreon + - fixes for Icinga + - fixes for Opsview + - fixes for Zabbix + - added support for registering version in Windows + - added support for using system certificates in Windows + + -- Henri Wahl <henri@nagstamon.de> Wed, Feb 21 2024 08:00:00 +0200 + nagstamon (3.12.0) stable; urgency=low * New upstream - added option to hide dock icon on macOS when using systray diff --git a/build/debian/changelog b/build/debian/changelog index 0fd93cb77..74651b326 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,5 +1,15 @@ nagstamon (3.13-20240221) unstable; urgency=low * New upstream + - improved Wayland support + - improved proxy support + - added Opsview hashtag filtering and can_change_only option + - fixes for Alertmanager + - fixes for Centreon + - fixes for Icinga + - fixes for Opsview + - fixes for Zabbix + - added support for registering version in Windows + - added support for using system certificates in Windows -- Henri Wahl <henri@nagstamon.de> Wed, Feb 21 2024 08:00:00 +0200 From c142319908d11c432c2542a316f31887043ffb5e Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 22 Feb 2024 13:26:12 +0100 Subject: [PATCH 730/884] prepare 3.14 --- .github/workflows/build-release-latest.yml | 32 +++---------------- .github/workflows/build-release-stable.yml | 36 ++++++---------------- 2 files changed, 14 insertions(+), 54 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index e2cbee84e..298d98932 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -8,7 +8,7 @@ on: - '!*.*.*' env: - python_win_version: 3.11.6 + python_win_version: 3.12.2 repo_dir: nagstamon-jekyll/docs/repo cr_image: ghcr.io/henriwahl/build-nagstamon # to be increased if new updates of build images are necessary @@ -69,28 +69,6 @@ jobs: retention-days: 1 if-no-files-found: error - fedora-36: - runs-on: ubuntu-latest - needs: test - steps: - - uses: actions/checkout@v3 - # docker login is needed for pushing the build image - - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed - - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . - - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - - uses: actions/upload-artifact@v3 - with: - path: build/*.rpm - retention-days: 1 - if-no-files-found: error - fedora-37: runs-on: ubuntu-latest needs: test @@ -277,7 +255,7 @@ jobs: repo-debian: runs-on: ubuntu-latest # try to avoid race condition and start uploading only after the last install package has been build - needs: [debian, fedora-36, fedora-37, fedora-38, fedora-39, rhel-9, macos, windows-32, windows-64, windows-64-debug] + needs: [debian, fedora-37, fedora-38, fedora-39, rhel-9, macos, windows-32, windows-64, windows-64-debug] env: family: debian steps: @@ -322,7 +300,7 @@ jobs: env: family: fedora # which image to use for packaging - cr_image_latest: 38 + cr_image_latest: 39 steps: # get binaries created by other jobs - uses: actions/download-artifact@v3 @@ -354,8 +332,8 @@ jobs: needs: [repo-rpm-fedora] env: family: rhel - # which image to use for packaging - cr_image_latest: 9 + # currently just one version available + version: 9 steps: # get binaries created by other jobs - uses: actions/download-artifact@v3 diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index 4810c38bb..744282148 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -4,7 +4,7 @@ on: tags: 'v*' env: - python_win_version: 3.11.4 + python_win_version: 3.12.2 repo_dir: nagstamon-jekyll/docs/repo cr_image: ghcr.io/henriwahl/build-nagstamon # to be increased if new updates of build images are necessary @@ -64,29 +64,7 @@ jobs: retention-days: 1 if-no-files-found: error - fedora-35: - runs-on: ubuntu-latest - needs: test - steps: - - uses: actions/checkout@v3 - # docker login is needed for pushing the build image - - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed - - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . - - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - - uses: actions/upload-artifact@v3 - with: - path: build/*.rpm - retention-days: 1 - if-no-files-found: error - - fedora-36: + fedora-37: runs-on: ubuntu-latest needs: test steps: @@ -108,7 +86,7 @@ jobs: retention-days: 1 if-no-files-found: error - fedora-37: + fedora-38: runs-on: ubuntu-latest needs: test steps: @@ -130,7 +108,7 @@ jobs: retention-days: 1 if-no-files-found: error - fedora-38: + fedora-39: runs-on: ubuntu-latest needs: test steps: @@ -272,7 +250,7 @@ jobs: repo-debian: runs-on: ubuntu-latest # try to avoid race condition and start uploading only after the last install package has been build - needs: [debian, fedora-35, fedora-36, fedora-37, fedora-38, rhel-9, macos, windows-32, windows-64, windows-64-debug] + needs: [debian, fedora-37, fedora-38, fedora-39, rhel-9, macos, windows-32, windows-64, windows-64-debug] env: family: debian steps: @@ -316,6 +294,8 @@ jobs: needs: [repo-debian] env: family: fedora + # which image to use for packaging + cr_image_latest: 39 steps: # get binaries created by other jobs - uses: actions/download-artifact@v3 @@ -388,6 +368,8 @@ jobs: - run: cd artifact && md5sum *agstamon* > md5sums.txt - run: cd artifact && sha256sum *agstamon* > sha256sums.txt - uses: marvinpinto/action-automatic-releases@latest + # the dciborow action is outdated as well :-( + #- uses: dciborow/action-github-releases@v1.0.1 with: repo_token: "${{ secrets.GITHUB_TOKEN }}" prerelease: false From 06279b303cd5c4a5c19ae8da9b84062d617c036f Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 22 Feb 2024 13:50:11 +0100 Subject: [PATCH 731/884] prepare 3.14 --- .github/workflows/build-release-latest.yml | 2 +- .github/workflows/build-release-stable.yml | 2 +- build/docker/Dockerfile-fedora-36 | 27 ---------------------- 3 files changed, 2 insertions(+), 29 deletions(-) delete mode 100644 build/docker/Dockerfile-fedora-36 diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 298d98932..b50e71bf8 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -351,7 +351,7 @@ jobs: mkdir -p mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${version} && \ cp -r artifact/*.${{ env.family }}* ${{ env.repo_dir }}/${{ env.family }}/${version} && \ docker run --rm -v ${PWD}/${{ env.repo_dir }}/${{ env.family }}/${version}:/repo \ - ${{ env.cr_image }}-${{ env.family }}-${{ env.cr_image_latest }}:${{ env.cr_image_version }} \ + ${{ env.cr_image }}-${{ env.family }}-${{ env.version }}:${{ env.cr_image_version }} \ /bin/bash -c "createrepo --verbose --workers 1 /repo" && \ ls -laR ${PWD}/${{ env.repo_dir }}/${{ env.family }}/${version} # commit and push new binaries to nagstamon-repo diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index 744282148..6aaf85639 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -295,7 +295,7 @@ jobs: env: family: fedora # which image to use for packaging - cr_image_latest: 39 + cr_image_latest: 38 steps: # get binaries created by other jobs - uses: actions/download-artifact@v3 diff --git a/build/docker/Dockerfile-fedora-36 b/build/docker/Dockerfile-fedora-36 deleted file mode 100644 index 4736dc7f2..000000000 --- a/build/docker/Dockerfile-fedora-36 +++ /dev/null @@ -1,27 +0,0 @@ -FROM fedora:36 -LABEL maintainer=henri@nagstamon.de - -RUN dnf -y upgrade - -RUN dnf -y install createrepo_c \ - desktop-file-utils \ - git \ - python3 \ - python3-beautifulsoup4 \ - python3-cryptography \ - python3-dateutil \ - python3-devel \ - python3-keyring \ - python3-lxml \ - python3-psutil \ - python3-pyqt6 \ - python3-pyqt6-devel \ - python3-requests \ - python3-requests-kerberos \ - python3-SecretStorage \ - qt6-qtsvg \ - qt6-qtmultimedia \ - rpm-build - -CMD cd /nagstamon/build && \ - /usr/bin/python3 build.py From e4eb12c06aa9da5e88e6f40210ee081a32ee72c4 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 22 Feb 2024 14:17:38 +0100 Subject: [PATCH 732/884] prepare 3.14 - win 2022 --- .github/workflows/build-release-latest.yml | 6 +++--- .github/workflows/build-release-stable.yml | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index b50e71bf8..e57db851d 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -174,7 +174,7 @@ jobs: windows-32: # better depend on stable build image - runs-on: windows-2019 + runs-on: windows-2022 needs: test steps: - uses: actions/checkout@v3 @@ -202,7 +202,7 @@ jobs: windows-64: # better depend on stable build image - runs-on: windows-2019 + runs-on: windows-2022 needs: test steps: - uses: actions/checkout@v3 @@ -228,7 +228,7 @@ jobs: windows-64-debug: # better depend on stable build image - runs-on: windows-2019 + runs-on: windows-2022 needs: test steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index 6aaf85639..f276a96a7 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -169,7 +169,7 @@ jobs: windows-32: # better depend on stable build image - runs-on: windows-2019 + runs-on: windows-2022 needs: test steps: - uses: actions/checkout@v3 @@ -197,7 +197,7 @@ jobs: windows-64: # better depend on stable build image - runs-on: windows-2019 + runs-on: windows-2022 needs: test steps: - uses: actions/checkout@v3 @@ -223,7 +223,7 @@ jobs: windows-64-debug: # better depend on stable build image - runs-on: windows-2019 + runs-on: windows-2022 needs: test steps: - uses: actions/checkout@v3 @@ -295,7 +295,7 @@ jobs: env: family: fedora # which image to use for packaging - cr_image_latest: 38 + cr_image_latest: 39 steps: # get binaries created by other jobs - uses: actions/download-artifact@v3 From b1286dc6de218a9869e1ea414f3628fe65b329f8 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 22 Feb 2024 14:28:16 +0100 Subject: [PATCH 733/884] prepare 3.14 - windows codesigning --- .github/workflows/build-release-stable.yml | 2 +- build/windows/code_signing.ps1 | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index f276a96a7..b2bdbe29b 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -4,7 +4,7 @@ on: tags: 'v*' env: - python_win_version: 3.12.2 + python_win_version: 3.11.8 repo_dir: nagstamon-jekyll/docs/repo cr_image: ghcr.io/henriwahl/build-nagstamon # to be increased if new updates of build images are necessary diff --git a/build/windows/code_signing.ps1 b/build/windows/code_signing.ps1 index 81f13d5d8..358eac005 100644 --- a/build/windows/code_signing.ps1 +++ b/build/windows/code_signing.ps1 @@ -8,4 +8,5 @@ $cert_buffer = [System.Convert]::FromBase64String($env:WIN_SIGNING_CERT_BASE64) $cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::New($cert_buffer, $env:WIN_SIGNING_PASSWORD) # finally sign the given file +Import-Module Microsoft.PowerShell.Security Set-AuthenticodeSignature -HashAlgorithm SHA256 -Certificate $cert -TimestampServer http://timestamp.sectigo.com -FilePath $file \ No newline at end of file From 33b4951d59e5fa224dfa14e76b0337be9a7199a4 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 22 Feb 2024 14:28:33 +0100 Subject: [PATCH 734/884] prepare 3.14 - windows codesigning --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index e57db851d..3c6d0075c 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -8,7 +8,7 @@ on: - '!*.*.*' env: - python_win_version: 3.12.2 + python_win_version: 3.11.8 repo_dir: nagstamon-jekyll/docs/repo cr_image: ghcr.io/henriwahl/build-nagstamon # to be increased if new updates of build images are necessary From 1de8bc508426839bf0690e85fd02ba279018b823 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 22 Feb 2024 14:37:33 +0100 Subject: [PATCH 735/884] prepare 3.14 - windows codesigning --- build/build.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/build.py b/build/build.py index a3c6baec2..fc08890b0 100644 --- a/build/build.py +++ b/build/build.py @@ -121,7 +121,7 @@ def winmain(): if SIGNING: # environment variables will be used by powershell script for signing - subprocess.call(['powershell', './windows/code_signing.ps1', 'build/Nagstamon/Nagstamon.exe']) + subprocess.run(['powershell', './windows/code_signing.ps1', 'build/Nagstamon/Nagstamon.exe']) # rename output os.rename(DIR_BUILD_EXE, DIR_BUILD_NAGSTAMON) @@ -160,7 +160,7 @@ def winmain(): if SIGNING: # environment variables will be used by powershell script for signing - subprocess.call(['powershell', '../windows/code_signing.ps1', '*.exe']) + subprocess.run(['powershell', '../windows/code_signing.ps1', '*.exe']) def macmain(): """ From 9bddc9b7f3461d395ba6c821bfda171ad8c0626f Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 22 Feb 2024 14:38:19 +0100 Subject: [PATCH 736/884] prepare 3.14 - windows codesigning --- build/windows/code_signing.ps1 | 1 - 1 file changed, 1 deletion(-) diff --git a/build/windows/code_signing.ps1 b/build/windows/code_signing.ps1 index 358eac005..81f13d5d8 100644 --- a/build/windows/code_signing.ps1 +++ b/build/windows/code_signing.ps1 @@ -8,5 +8,4 @@ $cert_buffer = [System.Convert]::FromBase64String($env:WIN_SIGNING_CERT_BASE64) $cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::New($cert_buffer, $env:WIN_SIGNING_PASSWORD) # finally sign the given file -Import-Module Microsoft.PowerShell.Security Set-AuthenticodeSignature -HashAlgorithm SHA256 -Certificate $cert -TimestampServer http://timestamp.sectigo.com -FilePath $file \ No newline at end of file From 367afbd38cfcbca8da5708b35ef897f210a2850b Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 22 Feb 2024 14:47:37 +0100 Subject: [PATCH 737/884] prepare 3.14 - windows codesigning --- build/build.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/build.py b/build/build.py index fc08890b0..6d726d62b 100644 --- a/build/build.py +++ b/build/build.py @@ -121,7 +121,7 @@ def winmain(): if SIGNING: # environment variables will be used by powershell script for signing - subprocess.run(['powershell', './windows/code_signing.ps1', 'build/Nagstamon/Nagstamon.exe']) + subprocess.run(['C:/Windows/System32/WindowsPowerShell/v1.0/powershell.exe', './windows/code_signing.ps1', 'build/Nagstamon/Nagstamon.exe']) # rename output os.rename(DIR_BUILD_EXE, DIR_BUILD_NAGSTAMON) @@ -160,7 +160,7 @@ def winmain(): if SIGNING: # environment variables will be used by powershell script for signing - subprocess.run(['powershell', '../windows/code_signing.ps1', '*.exe']) + subprocess.run(['C:/Windows/System32/WindowsPowerShell/v1.0/powershell.exe', '../windows/code_signing.ps1', '*.exe']) def macmain(): """ From 4bf9de486b5651e84a9a1d305c2f657b193411b3 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 22 Feb 2024 14:51:06 +0100 Subject: [PATCH 738/884] prepare 3.14 - windows codesigning pwsh --- build/build.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/build/build.py b/build/build.py index 6d726d62b..67580898c 100644 --- a/build/build.py +++ b/build/build.py @@ -121,7 +121,8 @@ def winmain(): if SIGNING: # environment variables will be used by powershell script for signing - subprocess.run(['C:/Windows/System32/WindowsPowerShell/v1.0/powershell.exe', './windows/code_signing.ps1', 'build/Nagstamon/Nagstamon.exe']) + #subprocess.run(['C:/Windows/System32/WindowsPowerShell/v1.0/powershell.exe', './windows/code_signing.ps1', 'build/Nagstamon/Nagstamon.exe']) + subprocess.run(['pwsh.exe', './windows/code_signing.ps1', 'build/Nagstamon/Nagstamon.exe']) # rename output os.rename(DIR_BUILD_EXE, DIR_BUILD_NAGSTAMON) @@ -160,7 +161,8 @@ def winmain(): if SIGNING: # environment variables will be used by powershell script for signing - subprocess.run(['C:/Windows/System32/WindowsPowerShell/v1.0/powershell.exe', '../windows/code_signing.ps1', '*.exe']) + #subprocess.run(['C:/Windows/System32/WindowsPowerShell/v1.0/powershell.exe', '../windows/code_signing.ps1', '*.exe']) + subprocess.run(['pwsh.exe', '../windows/code_signing.ps1', '*.exe']) def macmain(): """ From 44f6248180dbffc8b76f32d71576b69b12f00820 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 22 Feb 2024 14:55:37 +0100 Subject: [PATCH 739/884] prepare 3.14 - windows codesigning pwsh --- build/build.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/build/build.py b/build/build.py index 67580898c..20b1a7eda 100644 --- a/build/build.py +++ b/build/build.py @@ -121,7 +121,6 @@ def winmain(): if SIGNING: # environment variables will be used by powershell script for signing - #subprocess.run(['C:/Windows/System32/WindowsPowerShell/v1.0/powershell.exe', './windows/code_signing.ps1', 'build/Nagstamon/Nagstamon.exe']) subprocess.run(['pwsh.exe', './windows/code_signing.ps1', 'build/Nagstamon/Nagstamon.exe']) # rename output @@ -161,7 +160,6 @@ def winmain(): if SIGNING: # environment variables will be used by powershell script for signing - #subprocess.run(['C:/Windows/System32/WindowsPowerShell/v1.0/powershell.exe', '../windows/code_signing.ps1', '*.exe']) subprocess.run(['pwsh.exe', '../windows/code_signing.ps1', '*.exe']) def macmain(): From 92a343fd4cf670983475cbb4a12c7a6cc8170fdd Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 22 Feb 2024 15:04:57 +0100 Subject: [PATCH 740/884] Create FUNDING.yml --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..3c30dbf5b --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +custom: ['https://www.paypal.com/paypalme/nagstamon'] From 9fa776a6fecb1c4718977c4363d4a83f50e30e89 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 25 Feb 2024 00:45:43 +0100 Subject: [PATCH 741/884] prepare 3.14 - newer pyinstaller --- build/requirements/macos.txt | 9 +++------ build/requirements/windows.txt | 7 +++---- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index 8b52e0a2e..1e34e689e 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -3,13 +3,10 @@ beautifulsoup4 keyring lxml psutil -# last stable of the 5 series -pyinstaller==5.13.2 +pyinstaller pyobjc-framework-ApplicationServices -# 6.5.3 leads to crash on macOS -# 6.6.1 leads to crash on macOS -pyqt6==6.4.2 -pyqt6-qt6==6.4.2 +pyqt6 +pyqt6-qt6 pysocks python-dateutil requests diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index f6ab32114..5b1ae0712 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -5,12 +5,11 @@ keyring lxml pip-system-certs psutil -# last stable of the 5 series -pyinstaller==5.13.2 +pyinstaller pypiwin32 # try newer version -pyqt6==6.6.1 -pyqt6-qt6==6.6.1 +pyqt6 +pyqt6-qt6 pysocks python-dateutil requests From 70f277aad96341b3005e429028e14ee80701bd91 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 25 Feb 2024 01:04:44 +0100 Subject: [PATCH 742/884] prepare 3.14 - newer pyinstaller only for macOS --- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- build/requirements/windows.txt | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index c3b8e1363..4197b74d7 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -131,7 +131,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.13-20240221' + VERSION = '3.13-20240225' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2024 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 74651b326..dfd50ba04 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.13-20240221) unstable; urgency=low +nagstamon (3.13-20240225) unstable; urgency=low * New upstream - improved Wayland support - improved proxy support @@ -11,7 +11,7 @@ nagstamon (3.13-20240221) unstable; urgency=low - added support for registering version in Windows - added support for using system certificates in Windows - -- Henri Wahl <henri@nagstamon.de> Wed, Feb 21 2024 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Sun, Feb 25 2024 08:00:00 +0200 nagstamon (3.12.0) stable; urgency=low * New upstream diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index 5b1ae0712..8251444ac 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -5,9 +5,9 @@ keyring lxml pip-system-certs psutil -pyinstaller +# last stable of the 5 series +pyinstaller==5.13.2 pypiwin32 -# try newer version pyqt6 pyqt6-qt6 pysocks From 1014f146dfa4303bb60a2570bde9fcb0c2424d55 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 25 Feb 2024 01:09:36 +0100 Subject: [PATCH 743/884] prepare 3.14 - newer pyinstaller only for macOS --- build/requirements/windows.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index 8251444ac..406ee5adc 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -5,7 +5,7 @@ keyring lxml pip-system-certs psutil -# last stable of the 5 series +# last stable of the 5 series - InnoSetup does not work with Pyinstaller 6 pyinstaller==5.13.2 pypiwin32 pyqt6 From eea633607d6237686c0b3f6bf3a2bc21d15389ec Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 25 Feb 2024 01:19:56 +0100 Subject: [PATCH 744/884] prepare 3.14 - newer pyinstaller only for macOS, target_arch universal2 --- build/macos/nagstamon.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/macos/nagstamon.spec b/build/macos/nagstamon.spec index f897784fa..1a77723e1 100644 --- a/build/macos/nagstamon.spec +++ b/build/macos/nagstamon.spec @@ -34,7 +34,7 @@ exe = EXE(pyz, upx_exclude=[], runtime_tmpdir=None, console=False, - target_architecture='universal2', + target_arch='universal2', codesign_identity=None, entitlements_file=None, icon='../../Nagstamon/resources/nagstamon.icns') From 9f37d20a8406246fc20594b7e0f0f5c103905849 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 25 Feb 2024 11:25:05 +0100 Subject: [PATCH 745/884] prepare 3.14 - macos-13 runner --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 3c6d0075c..0bcf69b7c 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -158,7 +158,7 @@ jobs: if-no-files-found: error macos: - runs-on: macos-11 + runs-on: macos-13 needs: test steps: - uses: actions/checkout@v3 From 854c842a500d9fb06589fd5e65a042c5304bb7b0 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 25 Feb 2024 11:28:39 +0100 Subject: [PATCH 746/884] prepare 3.14 - macos-14 runner --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 0bcf69b7c..498c027a7 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -158,7 +158,7 @@ jobs: if-no-files-found: error macos: - runs-on: macos-13 + runs-on: macos-14 needs: test steps: - uses: actions/checkout@v3 From 71317c91a855686253ddc0dd8e90c0faaa9a3147 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 25 Feb 2024 11:31:20 +0100 Subject: [PATCH 747/884] prepare 3.14 - macos-12 runner --- .github/workflows/build-release-latest.yml | 2 +- build/macos/nagstamon.spec | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 498c027a7..ed51e510c 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -158,7 +158,7 @@ jobs: if-no-files-found: error macos: - runs-on: macos-14 + runs-on: macos-12 needs: test steps: - uses: actions/checkout@v3 diff --git a/build/macos/nagstamon.spec b/build/macos/nagstamon.spec index 1a77723e1..edda1c4b1 100644 --- a/build/macos/nagstamon.spec +++ b/build/macos/nagstamon.spec @@ -34,7 +34,6 @@ exe = EXE(pyz, upx_exclude=[], runtime_tmpdir=None, console=False, - target_arch='universal2', codesign_identity=None, entitlements_file=None, icon='../../Nagstamon/resources/nagstamon.icns') From 8840cf62b2e3addf5bef73d4ae46b995f6ad95f1 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 25 Feb 2024 11:31:48 +0100 Subject: [PATCH 748/884] prepare 3.14 - macos-12 runner --- .github/workflows/build-release-stable.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index b2bdbe29b..5096d4e27 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -153,7 +153,7 @@ jobs: if-no-files-found: error macos: - runs-on: macos-11 + runs-on: macos-12 needs: test steps: - uses: actions/checkout@v3 From 4f3a069ea635fd01716e05dad922fc9e1f3eeb1f Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 25 Feb 2024 21:32:55 +0100 Subject: [PATCH 749/884] prepare 3.14 --- ChangeLog | 4 ++-- Nagstamon/Config.py | 2 +- build/debian/changelog | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index 2932a1ead..21f549b89 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,4 @@ -nagstamon (3.13-20240221) unstable; urgency=low +nagstamon (3.14.0) stable; urgency=low * New upstream - improved Wayland support - improved proxy support @@ -11,7 +11,7 @@ nagstamon (3.13-20240221) unstable; urgency=low - added support for registering version in Windows - added support for using system certificates in Windows - -- Henri Wahl <henri@nagstamon.de> Wed, Feb 21 2024 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Sun, Feb 25 2024 08:00:00 +0200 nagstamon (3.12.0) stable; urgency=low * New upstream diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 4197b74d7..fd7839744 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -131,7 +131,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.13-20240225' + VERSION = '3.14.0' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2024 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index dfd50ba04..84010c2e8 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.13-20240225) unstable; urgency=low +nagstamon (3.14.0) stable; urgency=low * New upstream - improved Wayland support - improved proxy support From 1942497c5443a6316daac0de2ecb18e190f5a8b3 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Fri, 1 Mar 2024 15:01:50 +0100 Subject: [PATCH 750/884] Linux Qt6 6.6.2 --- build/requirements/linux.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/requirements/linux.txt b/build/requirements/linux.txt index 8eb4fbc88..b57ac3917 100644 --- a/build/requirements/linux.txt +++ b/build/requirements/linux.txt @@ -4,8 +4,8 @@ dbus-python keyring lxml psutil -pyqt6==6.5.3 -pyqt6-qt6==6.5.3 +pyqt6 +pyqt6-qt6 pysocks python-dateutil requests From 475ae223cfb15c897f11f8c05399652ff053722f Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 14 Mar 2024 10:56:00 +0100 Subject: [PATCH 751/884] Update build-release-latest.yml --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index ed51e510c..adf56d2e5 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.7, 3.9, 3.11] + python-version: [3.9, 3.11] steps: - uses: actions/checkout@v3 From 7eb2217151d90a44e07f0c4c6a8383e32cc20693 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Fri, 22 Mar 2024 22:30:15 +0100 Subject: [PATCH 752/884] ZabbixProblemBased.py problem base --- Nagstamon/Config.py | 2 +- Nagstamon/Servers/ZabbixProblemBased.py | 6 +++++- build/debian/changelog | 5 +++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index fd7839744..4d9ce3a81 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -131,7 +131,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.14.0' + VERSION = '3.15-20240322' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2024 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/Servers/ZabbixProblemBased.py b/Nagstamon/Servers/ZabbixProblemBased.py index ffb216a3f..319dce01c 100644 --- a/Nagstamon/Servers/ZabbixProblemBased.py +++ b/Nagstamon/Servers/ZabbixProblemBased.py @@ -98,7 +98,11 @@ def logged_in(self): def login(self, username, password): self.logger.debug("Login in as " + username) - self.zbx_auth = self.do_request('user.login', {'user': username, 'password': password}) + # see issue https://github.com/HenriWahl/Nagstamon/issues/1018 + if self.api_version() < '6.4': + self.zbx_auth = self.do_request('user.login', {'user': username, 'password': password}) + else: + self.zbx_auth = self.do_request('user.login', {'username': username, 'password': password}) class ZabbixLightApiException(Exception): pass diff --git a/build/debian/changelog b/build/debian/changelog index 84010c2e8..3912f8c91 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,3 +1,8 @@ +nagstamon (3.15-20240322) unstable; urgency=low + * New upstream + + -- Henri Wahl <henri@nagstamon.de> Fri, Mar 22 2024 08:00:00 +0200 + nagstamon (3.14.0) stable; urgency=low * New upstream - improved Wayland support From 7a3cd24613f2e1112901aad972c7f82ba602969b Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Fri, 22 Mar 2024 22:34:39 +0100 Subject: [PATCH 753/884] fedora40 --- .github/workflows/build-release-latest.yml | 24 ++++++++++++++++++- build/docker/Dockerfile-fedora-40 | 27 ++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 build/docker/Dockerfile-fedora-40 diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index adf56d2e5..b2d0dd354 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -135,6 +135,28 @@ jobs: retention-days: 1 if-no-files-found: error + fedora-40: + runs-on: ubuntu-latest + needs: test + steps: + - uses: actions/checkout@v3 + # docker login is needed for pushing the build image + - uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} + # building in precompiled image makes them way faster instead of creating the build environment every time from scratch + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} + - uses: actions/upload-artifact@v3 + with: + path: build/*.rpm + retention-days: 1 + if-no-files-found: error + rhel-9: runs-on: ubuntu-latest needs: test @@ -255,7 +277,7 @@ jobs: repo-debian: runs-on: ubuntu-latest # try to avoid race condition and start uploading only after the last install package has been build - needs: [debian, fedora-37, fedora-38, fedora-39, rhel-9, macos, windows-32, windows-64, windows-64-debug] + needs: [debian, fedora-37, fedora-38, fedora-39, fedora-40, rhel-9, macos, windows-32, windows-64, windows-64-debug] env: family: debian steps: diff --git a/build/docker/Dockerfile-fedora-40 b/build/docker/Dockerfile-fedora-40 new file mode 100644 index 000000000..9bcec9c1a --- /dev/null +++ b/build/docker/Dockerfile-fedora-40 @@ -0,0 +1,27 @@ +FROM fedora:40 +LABEL maintainer=henri@nagstamon.de + +RUN dnf -y upgrade + +RUN dnf -y install createrepo_c \ + desktop-file-utils \ + git \ + python3 \ + python3-beautifulsoup4 \ + python3-cryptography \ + python3-dateutil \ + python3-devel \ + python3-keyring \ + python3-lxml \ + python3-psutil \ + python3-pyqt6 \ + python3-pyqt6-devel \ + python3-requests \ + python3-requests-kerberos \ + python3-SecretStorage \ + qt6-qtsvg \ + qt6-qtmultimedia \ + rpm-build + +CMD cd /nagstamon/build && \ + /usr/bin/python3 build.py From 0a7a35870e087ece45f8f950ad730adb82c03bdd Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Fri, 22 Mar 2024 22:45:45 +0100 Subject: [PATCH 754/884] fedora40 --- .github/workflows/build-release-latest.yml | 2 +- build/docker/Dockerfile-fedora-40 | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index b2d0dd354..5c0db701b 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -12,7 +12,7 @@ env: repo_dir: nagstamon-jekyll/docs/repo cr_image: ghcr.io/henriwahl/build-nagstamon # to be increased if new updates of build images are necessary - cr_image_version: 3 + cr_image_version: 4 # release type this file is used for release: latest diff --git a/build/docker/Dockerfile-fedora-40 b/build/docker/Dockerfile-fedora-40 index 9bcec9c1a..013422320 100644 --- a/build/docker/Dockerfile-fedora-40 +++ b/build/docker/Dockerfile-fedora-40 @@ -19,6 +19,7 @@ RUN dnf -y install createrepo_c \ python3-requests \ python3-requests-kerberos \ python3-SecretStorage \ + python3-setuptools \ qt6-qtsvg \ qt6-qtmultimedia \ rpm-build From e505097305481d72583696238ba8e4d7f472d4c5 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 26 Mar 2024 21:35:14 +0100 Subject: [PATCH 755/884] added zabbix api_version() --- Nagstamon/Servers/ZabbixProblemBased.py | 4 ++++ Nagstamon/thirdparty/zabbix_api.py | 3 +-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Servers/ZabbixProblemBased.py b/Nagstamon/Servers/ZabbixProblemBased.py index 319dce01c..4cea0fc7a 100644 --- a/Nagstamon/Servers/ZabbixProblemBased.py +++ b/Nagstamon/Servers/ZabbixProblemBased.py @@ -85,6 +85,10 @@ def do_request(self, method, params={}, no_auth=False): return response_json['result'] + def api_version(self, **options): + obj = self.do_request('apiinfo.version', options, no_auth=True) + return obj['result'] + def logged_in(self): if self.zbx_auth is None: return False diff --git a/Nagstamon/thirdparty/zabbix_api.py b/Nagstamon/thirdparty/zabbix_api.py index 16bb94657..582e6391f 100644 --- a/Nagstamon/thirdparty/zabbix_api.py +++ b/Nagstamon/thirdparty/zabbix_api.py @@ -302,8 +302,7 @@ def logged_in(self): return False def api_version(self, **options): - # kicked out check auth to be able to check vesion before being logged in to use the correct username keyword - #self.__checkauth__() + # kicked out check auth to be able to check version before being logged in to use the correct username keyword obj = self.do_request(self.json_obj('apiinfo.version', options, auth=False)) return obj['result'] From ad0395b5c2b7ce71429d9fb8eca6ea5c5bd40a13 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 26 Mar 2024 21:37:42 +0100 Subject: [PATCH 756/884] added zabbix api_version() --- .github/workflows/build-release-latest.yml | 24 ++++++++++++++++++- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- build/docker/Dockerfile-fedora-41 | 28 ++++++++++++++++++++++ 4 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 build/docker/Dockerfile-fedora-41 diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 5c0db701b..c0aff6599 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -157,6 +157,28 @@ jobs: retention-days: 1 if-no-files-found: error + fedora-41: + runs-on: ubuntu-latest + needs: test + steps: + - uses: actions/checkout@v3 + # docker login is needed for pushing the build image + - uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} + # building in precompiled image makes them way faster instead of creating the build environment every time from scratch + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} + - uses: actions/upload-artifact@v3 + with: + path: build/*.rpm + retention-days: 1 + if-no-files-found: error + rhel-9: runs-on: ubuntu-latest needs: test @@ -277,7 +299,7 @@ jobs: repo-debian: runs-on: ubuntu-latest # try to avoid race condition and start uploading only after the last install package has been build - needs: [debian, fedora-37, fedora-38, fedora-39, fedora-40, rhel-9, macos, windows-32, windows-64, windows-64-debug] + needs: [debian, fedora-37, fedora-38, fedora-39, fedora-40, fedora-41, rhel-9, macos, windows-32, windows-64, windows-64-debug] env: family: debian steps: diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 4d9ce3a81..877025b5d 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -131,7 +131,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.15-20240322' + VERSION = '3.15-20240326' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2024 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 3912f8c91..596690dd6 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.15-20240322) unstable; urgency=low +nagstamon (3.15-20240326) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Fri, Mar 22 2024 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Tue, Mar 26 2024 08:00:00 +0200 nagstamon (3.14.0) stable; urgency=low * New upstream diff --git a/build/docker/Dockerfile-fedora-41 b/build/docker/Dockerfile-fedora-41 new file mode 100644 index 000000000..fc3a4cbdd --- /dev/null +++ b/build/docker/Dockerfile-fedora-41 @@ -0,0 +1,28 @@ +FROM fedora:41 +LABEL maintainer=henri@nagstamon.de + +RUN dnf -y upgrade + +RUN dnf -y install createrepo_c \ + desktop-file-utils \ + git \ + python3 \ + python3-beautifulsoup4 \ + python3-cryptography \ + python3-dateutil \ + python3-devel \ + python3-keyring \ + python3-lxml \ + python3-psutil \ + python3-pyqt6 \ + python3-pyqt6-devel \ + python3-requests \ + python3-requests-kerberos \ + python3-SecretStorage \ + python3-setuptools \ + qt6-qtsvg \ + qt6-qtmultimedia \ + rpm-build + +CMD cd /nagstamon/build && \ + /usr/bin/python3 build.py From 1d2ceafcd30257ff3be2946aa68d20282b196b3f Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Wed, 27 Mar 2024 09:41:30 +0100 Subject: [PATCH 757/884] take away ['result'] --- Nagstamon/Servers/ZabbixProblemBased.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/Servers/ZabbixProblemBased.py b/Nagstamon/Servers/ZabbixProblemBased.py index 4cea0fc7a..438e4d6eb 100644 --- a/Nagstamon/Servers/ZabbixProblemBased.py +++ b/Nagstamon/Servers/ZabbixProblemBased.py @@ -87,7 +87,7 @@ def do_request(self, method, params={}, no_auth=False): def api_version(self, **options): obj = self.do_request('apiinfo.version', options, no_auth=True) - return obj['result'] + return obj def logged_in(self): if self.zbx_auth is None: From 1a2a17602936c8629b658b2223e18b4bde3e8b65 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 13 Apr 2024 12:08:24 +0200 Subject: [PATCH 758/884] equalized nagstamon.svg dimensions for https://github.com/flathub/flathub/pull/5157 --- Nagstamon/resources/nagstamon.svg | 151 +++++++++++++++--------------- 1 file changed, 78 insertions(+), 73 deletions(-) diff --git a/Nagstamon/resources/nagstamon.svg b/Nagstamon/resources/nagstamon.svg index 15dce5b64..eddfb826a 100644 --- a/Nagstamon/resources/nagstamon.svg +++ b/Nagstamon/resources/nagstamon.svg @@ -2,22 +2,22 @@ <!-- Created with Inkscape (http://www.inkscape.org/) --> <svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="412.2182" - height="416.04276" + width="416" + height="416" id="svg2" sodipodi:version="0.32" - inkscape:version="0.48.4 r9939" + inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)" sodipodi:docname="nagstamon.svg" inkscape:output_extension="org.inkscape.output.svg.inkscape" - version="1.0"> + version="1.0" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:dc="http://purl.org/dc/elements/1.1/"> <defs id="defs4"> <linearGradient @@ -66,10 +66,10 @@ </linearGradient> <inkscape:perspective sodipodi:type="inkscape:persp3d" - inkscape:vp_x="0 : 526.18109 : 1" + inkscape:vp_x="0 : 526.13833 : 1" inkscape:vp_y="6.123234e-14 : 1000 : 0" - inkscape:vp_z="744.09448 : 526.18109 : 1" - inkscape:persp3d-origin="372.04724 : 350.78739 : 1" + inkscape:vp_z="744.09448 : 526.13833 : 1" + inkscape:persp3d-origin="372.04724 : 350.74463 : 1" id="perspective10" /> <linearGradient inkscape:collect="always" @@ -80,7 +80,7 @@ x2="550.94165" y2="599.38995" gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.115517,0,0,1.115517,-42.056997,-52.424134)" /> + gradientTransform="matrix(1.1243242,0,0,1.1243242,-43.738679,-54.972881)" /> <linearGradient inkscape:collect="always" xlink:href="#linearGradient3191" @@ -90,7 +90,7 @@ x2="210.94165" y2="309.38992" gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.115517,0,0,1.115517,-42.056997,-52.424134)" /> + gradientTransform="matrix(1.1243242,0,0,1.1243242,-43.738679,-54.972881)" /> <linearGradient inkscape:collect="always" xlink:href="#linearGradient3191" @@ -100,7 +100,7 @@ y1="309.38995" x2="540.94165" y2="589.38995" - gradientTransform="matrix(0.98030277,0,0,0.95615764,8.7756379,19.190415)" /> + gradientTransform="matrix(0.98804253,0,0,0.96370676,7.4952926,17.207084)" /> <linearGradient inkscape:collect="always" xlink:href="#linearGradient3343" @@ -110,7 +110,7 @@ x2="380.94165" y2="439.38992" gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.115517,0,0,1.115517,-42.056997,-52.424123)" /> + gradientTransform="matrix(1.1243242,0,0,1.1243242,-43.738679,-54.97287)" /> <radialGradient inkscape:collect="always" xlink:href="#linearGradient3371" @@ -120,7 +120,7 @@ fx="215" fy="381.04276" r="115.5" - gradientTransform="matrix(1,0,0,0.1341991,0,329.90715)" + gradientTransform="matrix(1.8038071,0,0,0.31476852,-8.8769011,510.36158)" gradientUnits="userSpaceOnUse" /> </defs> <sodipodi:namedview @@ -130,9 +130,9 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="1" - inkscape:cx="154.97244" - inkscape:cy="230.22276" + inkscape:zoom="2" + inkscape:cx="173.75" + inkscape:cy="245.5" inkscape:document-units="px" inkscape:current-layer="g3518" showgrid="true" @@ -149,17 +149,25 @@ guidetolerance="10" inkscape:snap-intersection-line-segments="true" inkscape:snap-center="true" - inkscape:window-width="991" - inkscape:window-height="890" - inkscape:window-x="4" - inkscape:window-y="831" + inkscape:window-width="1677" + inkscape:window-height="1330" + inkscape:window-x="26" + inkscape:window-y="23" gridtolerance="10" - inkscape:window-maximized="0"> + inkscape:window-maximized="0" + inkscape:showpageshadow="2" + inkscape:pagecheckerboard="0" + inkscape:deskcolor="#d1d1d1"> <inkscape:grid type="xygrid" id="grid2391" visible="true" - enabled="true" /> + enabled="true" + originx="0" + originy="0" + spacingy="1" + spacingx="1" + units="px" /> </sodipodi:namedview> <metadata id="metadata7"> @@ -183,79 +191,76 @@ id="g3518" inkscape:export-xdpi="34.611824" inkscape:export-ydpi="34.611824"> - <path - sodipodi:type="arc" - style="fill:url(#radialGradient3369);fill-opacity:1;stroke:none" + <ellipse + style="fill:url(#radialGradient3369);fill-opacity:1;stroke:none;stroke-width:2.05692" id="path3359" - sodipodi:cx="215" - sodipodi:cy="381.04276" - sodipodi:rx="115" - sodipodi:ry="15" - d="m 330,381.04276 a 115,15 0 1 1 -230,0 115,15 0 1 1 230,0 z" - transform="matrix(1.7896771,0,0,2.3271603,-7.4683057,-259.26501)" /> + cx="378.94159" + cy="630.30182" + rx="207.43782" + ry="35.18301" /> <rect y="270.3952" x="170.94165" - height="356.96542" - width="412.74127" + height="359.78375" + width="416" id="rect3470" - style="fill:#434343;fill-opacity:1;stroke:none" + style="fill:#434343;fill-opacity:1;stroke:none;stroke-width:1.0079" inkscape:export-xdpi="10.383548" inkscape:export-ydpi="10.383548" - ry="44.155521" /> + ry="44.504143" /> <rect - y="281.55038" - x="182.09679" - height="334.65515" - width="390.43091" + y="281.63846" + x="182.18486" + height="337.29736" + width="393.51346" id="rect3171" - style="fill:url(#linearGradient3181);fill-opacity:1;stroke:none" + style="fill:url(#linearGradient3181);fill-opacity:1;stroke:none;stroke-width:1.0079" inkscape:export-xdpi="10.383548" inkscape:export-ydpi="10.383548" - ry="41.395813" /> + ry="41.722641" /> <rect - y="292.70554" - x="193.25198" - height="312.34473" - width="368.12057" + y="292.88168" + x="193.42813" + height="314.81076" + width="371.02698" id="rect3173" - style="fill:url(#linearGradient3189);fill-opacity:1;stroke:none" + style="fill:url(#linearGradient3189);fill-opacity:1;stroke:none;stroke-width:1.0079" inkscape:export-xdpi="10.383548" inkscape:export-ydpi="10.383548" - ry="38.636082" /> + ry="38.941124" /> <rect - y="315.01593" - x="215.56232" - height="267.72412" - width="323.49994" + y="315.36823" + x="215.91461" + height="269.83789" + width="326.05405" id="rect3197" - style="fill:url(#linearGradient3199);fill-opacity:1;stroke:none" + style="fill:url(#linearGradient3199);fill-opacity:1;stroke:none;stroke-width:1.0079" inkscape:export-xdpi="10.383548" inkscape:export-ydpi="10.383548" - ry="33.116642" /> + ry="33.378109" /> <rect - y="326.17111" - x="226.7175" - height="245.4137" - width="301.18954" + y="326.61148" + x="227.15787" + height="247.35129" + width="303.5675" id="rect3201" - style="fill:#000000;fill-opacity:1;stroke:none" + style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.0079" inkscape:export-xdpi="10.383548" inkscape:export-ydpi="10.383548" - ry="30.356913" /> + ry="30.596588" /> <g id="g2417" - transform="matrix(-1.115517,0,0,-1.115517,796.68155,950.18)"> + transform="matrix(-1.1243242,0,0,-1.1243242,801.62194,955.54709)"> <path sodipodi:nodetypes="ccccccc" id="rect3244" - d="m 268.15497,339.38992 215.57336,0 c 15.07618,0 27.21332,12.13714 27.21332,27.21332 0,22.7867 0,32.7867 0,42.7867 l -270,0 0,-42.7867 c 0,-15.07618 12.13714,-27.21332 27.21332,-27.21332 z" + d="m 268.15497,339.38992 h 215.57336 c 15.07618,0 27.21332,12.13714 27.21332,27.21332 0,22.7867 0,32.7867 0,42.7867 h -270 v -42.7867 c 0,-15.07618 12.13714,-27.21332 27.21332,-27.21332 z" style="fill:#ffff00;fill-opacity:1;stroke:none" inkscape:connector-curvature="0" /> <path sodipodi:nodetypes="ccccccc" id="path3247" - d="m 483.72833,559.38994 -215.57336,0 c -15.07618,0 -27.21332,-12.13714 -27.21332,-27.21332 0,-22.7867 0,-42.7867 0,-52.7867 l 270,0 0,52.7867 c 0,15.07618 -12.13714,27.21332 -27.21332,27.21332 z" + d="M 483.72833,559.38994 H 268.15497 c -15.07618,0 -27.21332,-12.13714 -27.21332,-27.21332 0,-22.7867 0,-42.7867 0,-52.7867 h 270 v 52.7867 c 0,15.07618 -12.13714,27.21332 -27.21332,27.21332 z" style="fill:#ff0000;fill-opacity:1;stroke:none" inkscape:connector-curvature="0" /> <rect @@ -269,13 +274,13 @@ style="fill:#ff8500;fill-opacity:1;stroke:none" /> </g> <path - style="font-size:40px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans" - d="m 290.22788,348.48144 55.73979,0 70.3877,137.71741 0,-137.71741 47.31403,0 0,200.79304 -55.7398,0 -70.3877,-137.71743 0,137.71743 -47.31402,0 0,-200.79304" + style="font-style:normal;font-weight:normal;font-size:40px;font-family:'Bitstream Vera Sans';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.0079" + d="m 291.16969,349.09795 h 56.17986 l 70.94343,138.80472 V 349.09795 h 47.68757 V 551.47631 H 409.80069 L 338.85725,412.67156 V 551.47631 H 291.16969 V 349.09795" id="text3349" inkscape:connector-curvature="0" /> <path - style="fill:url(#linearGradient3341);fill-opacity:1;stroke:none" - d="m 257.07441,326.17109 240.44255,0 c 17.00391,0 30.41645,13.65191 30.39012,30.39784 0,11.62298 0,14.22286 0,14.22286 0,20.32038 -60.57334,-9.90271 -145.01721,55.77584 -84.44389,65.67856 -145.01721,66.93103 -156.17238,66.93103 l 0,-136.97066 c 0,-16.81773 13.53918,-30.35691 30.35692,-30.35691 z" + style="fill:url(#linearGradient3341);fill-opacity:1;stroke:none;stroke-width:1.0079" + d="m 257.75445,326.61146 h 242.34091 c 17.13816,0 30.6566,13.75969 30.63006,30.63783 0,11.71474 0,14.33515 0,14.33515 0,20.48083 -61.05158,-9.9809 -146.16217,56.21621 -85.11059,66.19711 -146.16215,67.45947 -157.40539,67.45947 V 357.20804 c 0,-16.95052 13.64606,-30.59658 30.59659,-30.59658 z" id="path3333" sodipodi:nodetypes="cccszccc" inkscape:connector-curvature="0" /> From 2ce6dca68fe7c55f1838eb4fdb91ab2f3dc5cb2e Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Tue, 16 Apr 2024 22:24:14 +0200 Subject: [PATCH 759/884] appdata.xml from flatpak --- Nagstamon/resources/nagstamon.appdata.xml | 100 +++++++++++++--------- 1 file changed, 58 insertions(+), 42 deletions(-) diff --git a/Nagstamon/resources/nagstamon.appdata.xml b/Nagstamon/resources/nagstamon.appdata.xml index 42d75394e..c12fa5448 100644 --- a/Nagstamon/resources/nagstamon.appdata.xml +++ b/Nagstamon/resources/nagstamon.appdata.xml @@ -1,43 +1,59 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- Copyright 2016 Henri Wahl <henri@nagstamon.de> --> -<application> - <id type="desktop">nagstamon.desktop</id> - <metadata_license>GFDL-1.3</metadata_license> - <project_license>GPL-2.0+ and LGPL-2.1</project_license> - <name>Nagstamon</name> - <summary>Nagios status monitor for your desktop</summary> - <description> - <p> - Nagstamon is a Nagios status monitor which takes place in systray, on desktop as floating statusbar or fullscreen - to inform in realtime about the status of your Nagios and its derivatives monitored network. It allows to connect - to multiple monitoring servers. - Nagstamon supports the following server types: - <ul> - <li>Nagios</li> - <li>Icinga</li> - <li>Checkmk Multisite</li> - <li>Thruk</li> - <li>Op5Monitor</li> - <li>Centreon</li> - <li>Opsview</li> - <li>Experimental: Zabbix</li> - <li>Experimental: Zenoss</li> - <li>Experimental: Livestatus</li> - <li>Experimental: Sensu</li> - </ul> - </p> - <p> - Events could be handled by instant access to failed hosts/services. - Nagstamon is very customizable by several types of event filters, notifications methods and actions. - </p> - </description> - <screenshots> - <screenshot type="default" width="624" height="351">https://nagstamon.de/files-nagstamon/appdata/nagstamon-appdata-01.png</screenshot> - <screenshot width="624" height="351">https://nagstamon.de/files-nagstamon/appdata/nagstamon-appdata-02.png</screenshot> - <screenshot width="624" height="351">https://nagstamon.de/files-nagstamon/appdata/nagstamon-appdata-03.png</screenshot> - <screenshot width="624" height="351">https://nagstamon.de/files-nagstamon/appdata/nagstamon-appdata-04.png</screenshot> - <screenshot width="624" height="351">https://nagstamon.de/files-nagstamon/appdata/nagstamon-appdata-05.png</screenshot> - </screenshots> - <url type="homepage">https://nagstamon.de</url> - <updatecontact>contact@nagstamon.de</updatecontact> -</application> +<component type="desktop-application"> + <id>de.nagstamon.nagstamon</id> + <replaces> + <id>de.ifw_dresden.nagstamon</id> + </replaces> + <metadata_license>CC0-1.0</metadata_license> + <project_license>GPL-2.0</project_license> + <name>Nagstamon</name> + <summary>The status monitor for the desktop</summary> + <description> + <p> + Nagstamon is a status monitor for the desktop. It connects to multiple Nagios, Icinga, Opsview, Centreon, + Op5 Monitor/Ninja, Check_MK Multisite and Thruk monitoring servers. It resides in systray, as a floating + statusbar or fullscreen at the desktop showing a brief summary of critical, warning, unknown, unreachable + and down hosts and services. It pops up a detailed status overview when being touched by the mouse pointer. + Connections to displayed hosts and services are easily established by context menu via SSH, RDP, VNC or + any self defined actions. Users can be notified by sound. Hosts and services can be filtered by category + and regular expressions. + </p> + </description> + <url type="homepage">https://nagstamon.de</url> + <url type="help">https://nagstamon.de/documentation</url> + <url type="bugtracker">https://github.com/HenriWahl/Nagstamon/issues</url> + <launchable type="desktop-id">de.nagstamon.nagstamon.desktop</launchable> + <branding> + <color type="primary" scheme_preference="light">#c9facc</color> + <color type="primary" scheme_preference="dark">#59b65f</color> + </branding> + <releases> + <release version="3.14.0" date="2024-02-24"> + <description> + <ul> + <li>improved Wayland support</li> + <li>improved proxy support</li> + <li>added Opsview hashtag filtering and can_change_only option</li> + <li>fixes for Alertmanager</li> + <li>fixes for Centreon</li> + <li>fixes for Icinga</li> + <li>fixes for Opsview</li> + <li>fixes for Zabbix</li> + <li>added support for registering version in Windows</li> + <li>added support for using system certificates in Windows</li> + </ul> + </description> + </release> + </releases> + <developer_name>Henri Wahl</developer_name> + <screenshots> + <screenshot type="default"> + <image type="source">https://nagstamon.de/assets/images/teaser_1.png</image> + </screenshot> + <screenshot> + <image type="source">https://nagstamon.de/assets/images/teaser_2.png</image> + </screenshot> + </screenshots> + <update_contact>flathub@nagstamon.de</update_contact> + <content_rating type="oars-1.1" /> +</component> From bb6baecf257cf67c14bae8fed3597d981f9a746e Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Wed, 17 Apr 2024 17:46:07 +0200 Subject: [PATCH 760/884] appdata.xml without releases --- Nagstamon/resources/nagstamon.appdata.xml | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/Nagstamon/resources/nagstamon.appdata.xml b/Nagstamon/resources/nagstamon.appdata.xml index c12fa5448..e88a0ed17 100644 --- a/Nagstamon/resources/nagstamon.appdata.xml +++ b/Nagstamon/resources/nagstamon.appdata.xml @@ -27,24 +27,6 @@ <color type="primary" scheme_preference="light">#c9facc</color> <color type="primary" scheme_preference="dark">#59b65f</color> </branding> - <releases> - <release version="3.14.0" date="2024-02-24"> - <description> - <ul> - <li>improved Wayland support</li> - <li>improved proxy support</li> - <li>added Opsview hashtag filtering and can_change_only option</li> - <li>fixes for Alertmanager</li> - <li>fixes for Centreon</li> - <li>fixes for Icinga</li> - <li>fixes for Opsview</li> - <li>fixes for Zabbix</li> - <li>added support for registering version in Windows</li> - <li>added support for using system certificates in Windows</li> - </ul> - </description> - </release> - </releases> <developer_name>Henri Wahl</developer_name> <screenshots> <screenshot type="default"> From 7e4008822c14965797f2a9a1e98eda944fbbda3d Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Wed, 17 Apr 2024 19:06:29 +0200 Subject: [PATCH 761/884] added release again to appdata.xml --- Nagstamon/resources/nagstamon.appdata.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Nagstamon/resources/nagstamon.appdata.xml b/Nagstamon/resources/nagstamon.appdata.xml index e88a0ed17..89ae34bd2 100644 --- a/Nagstamon/resources/nagstamon.appdata.xml +++ b/Nagstamon/resources/nagstamon.appdata.xml @@ -8,6 +8,10 @@ <project_license>GPL-2.0</project_license> <name>Nagstamon</name> <summary>The status monitor for the desktop</summary> +<releases> + <release version="3.14.0" date="2024-02-24"> + </release> + </releases> <description> <p> Nagstamon is a status monitor for the desktop. It connects to multiple Nagios, Icinga, Opsview, Centreon, From d3b54c35d1ca1b6c167b8892774b434c55139f20 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Wed, 17 Apr 2024 19:07:24 +0200 Subject: [PATCH 762/884] better indent --- Nagstamon/resources/nagstamon.appdata.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/resources/nagstamon.appdata.xml b/Nagstamon/resources/nagstamon.appdata.xml index 89ae34bd2..0e8573820 100644 --- a/Nagstamon/resources/nagstamon.appdata.xml +++ b/Nagstamon/resources/nagstamon.appdata.xml @@ -8,7 +8,7 @@ <project_license>GPL-2.0</project_license> <name>Nagstamon</name> <summary>The status monitor for the desktop</summary> -<releases> + <releases> <release version="3.14.0" date="2024-02-24"> </release> </releases> From 5d994ea42ad86bbbdc61669b8a45fecaa0183f14 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Wed, 17 Apr 2024 19:41:24 +0200 Subject: [PATCH 763/884] full release description again --- Nagstamon/resources/nagstamon.appdata.xml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Nagstamon/resources/nagstamon.appdata.xml b/Nagstamon/resources/nagstamon.appdata.xml index 0e8573820..7a8c524df 100644 --- a/Nagstamon/resources/nagstamon.appdata.xml +++ b/Nagstamon/resources/nagstamon.appdata.xml @@ -8,8 +8,22 @@ <project_license>GPL-2.0</project_license> <name>Nagstamon</name> <summary>The status monitor for the desktop</summary> - <releases> + <releases> <release version="3.14.0" date="2024-02-24"> + <description> + <ul> + <li>improved Wayland support</li> + <li>improved proxy support</li> + <li>added Opsview hashtag filtering and can_change_only option</li> + <li>fixes for Alertmanager</li> + <li>fixes for Centreon</li> + <li>fixes for Icinga</li> + <li>fixes for Opsview</li> + <li>fixes for Zabbix</li> + <li>added support for registering version in Windows</li> + <li>added support for using system certificates in Windows</li> + </ul> + </description> </release> </releases> <description> From c353c40d85f02ce600b6c5c45b3c5fb6faf3dc36 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 21 Apr 2024 00:23:21 +0200 Subject: [PATCH 764/884] Qt version bugfix (#1031) * fix un-integeresque Qt version string * move ] --------- --- Nagstamon/Config.py | 2 +- Nagstamon/QUI/qt.py | 4 ++-- build/debian/changelog | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 877025b5d..1bfcb6e35 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -131,7 +131,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.15-20240326' + VERSION = '3.15-20240417' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2024 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/QUI/qt.py b/Nagstamon/QUI/qt.py index 3bc50c5bb..3f40a2655 100644 --- a/Nagstamon/QUI/qt.py +++ b/Nagstamon/QUI/qt.py @@ -28,14 +28,14 @@ from PyQt6.QtCore import PYQT_VERSION_STR as QT_VERSION_STR # get int-ed version parts - QT_VERSION_MAJOR, QT_VERSION_MINOR, QT_VERSION_BUGFIX = [int(x) for x in QT_VERSION_STR.split('.')] + QT_VERSION_MAJOR, QT_VERSION_MINOR = [int(x) for x in QT_VERSION_STR.split('.')[0:2]] # for later decision which differences have to be considered QT_FLAVOR = 'PyQt6' except ImportError: try: from PyQt5.QtCore import PYQT_VERSION_STR as QT_VERSION_STR # get int-ed version parts - QT_VERSION_MAJOR, QT_VERSION_MINOR, QT_VERSION_BUGFIX = [int(x) for x in QT_VERSION_STR.split('.')] + QT_VERSION_MAJOR, QT_VERSION_MINOR = [int(x) for x in QT_VERSION_STR.split('.')[0:2]] # for later decision which differences have to be considered QT_FLAVOR = 'PyQt5' except ImportError: diff --git a/build/debian/changelog b/build/debian/changelog index 596690dd6..e47943acc 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.15-20240326) unstable; urgency=low +nagstamon (3.15-20240417) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Tue, Mar 26 2024 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Wed, Apr 17 2024 08:00:00 +0200 nagstamon (3.14.0) stable; urgency=low * New upstream From 52c43630a1800b18e36e8331fe6a0bcc70abcd9b Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 21 Apr 2024 00:27:35 +0200 Subject: [PATCH 765/884] 3.15 20240314 (#1032) * Update Thruk.py Fixing "Recheck" error for Thruk systems by adding the mandatory "Content-Type" header to fix #1013 * Update build-release-latest.yml --------- Co-authored-by: tabbitsp <111870089+tabbitsp@users.noreply.github.com> --- Nagstamon/Servers/Thruk.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Nagstamon/Servers/Thruk.py b/Nagstamon/Servers/Thruk.py index bf19543ed..00dce2501 100644 --- a/Nagstamon/Servers/Thruk.py +++ b/Nagstamon/Servers/Thruk.py @@ -201,6 +201,8 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi self.FetchURL(url, giveback='raw', cgi_data=cgi_data) def _set_recheck(self, host, service): + self.session.headers.update({'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}) + if service != '': if self.hosts[host].services[ service ].is_passive_only(): # Do not check passive only checks From bafe23f0af374eafee693c1cd81894fccd91b76a Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 21 Apr 2024 16:25:13 +0200 Subject: [PATCH 766/884] Centreon 24.04 (#1034) * Centreon, 24.04, Fix ack on host (#1030) Tested on 24.04 unstable. All the others actions are working on this version. * 3.15 20240314 (#1033) * Update Thruk.py Fixing "Recheck" error for Thruk systems by adding the mandatory "Content-Type" header to fix #1013 * Update build-release-latest.yml --------- Co-authored-by: tabbitsp <111870089+tabbitsp@users.noreply.github.com> * 3.15-20240420 * actions/checkout@v4 * actions/download-artifact@v4 * docker/login-action@v3 * actions/upload-artifact@v4 * name: ${{ github.job }} * actions/setup-python@v5 * actions/setup-python@v5 * actions/download-artifact ${{ github.job }} * artifact patterns * artifact patterns '' * added build-release-latest-test-artifacts.yml * no deps * no tests at all * path artifact * path artifact all distros * pattern: 'rhel*' * path: artifact --------- Co-authored-by: Benoit Poulet <benoit.poulet@businessdecision.com> Co-authored-by: tabbitsp <111870089+tabbitsp@users.noreply.github.com> --- ...ease-latest-test-artifacts.yml_deactivated | 293 ++++++++++++++++++ .github/workflows/build-release-latest.yml | 103 +++--- Nagstamon/Config.py | 2 +- Nagstamon/Servers/Centreon/CentreonAPI.py | 28 +- build/debian/changelog | 4 +- 5 files changed, 380 insertions(+), 50 deletions(-) create mode 100644 .github/workflows/build-release-latest-test-artifacts.yml_deactivated diff --git a/.github/workflows/build-release-latest-test-artifacts.yml_deactivated b/.github/workflows/build-release-latest-test-artifacts.yml_deactivated new file mode 100644 index 000000000..148a3d36c --- /dev/null +++ b/.github/workflows/build-release-latest-test-artifacts.yml_deactivated @@ -0,0 +1,293 @@ +name: build-release-latest +on: + push: + tags-ignore: 'v*' + branches: + - '**' + - '!master' + - '!*.*.*' + +env: + python_win_version: 3.11.8 + repo_dir: nagstamon-jekyll/docs/repo + cr_image: ghcr.io/henriwahl/build-nagstamon + # to be increased if new updates of build images are necessary + cr_image_version: 4 + # release type this file is used for + release: latest + +jobs: + debian: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + # docker login is needed for pushing the build image + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} + # building in precompiled image makes them way faster instead of creating the build environment every time from scratch + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon -e DEB_BUILD_OPTIONS=nocheck ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} + - uses: actions/upload-artifact@v4 + with: + path: build/*.deb + retention-days: 1 + if-no-files-found: error + name: ${{ github.job }} + + fedora-37: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + # docker login is needed for pushing the build image + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} + # building in precompiled image makes them way faster instead of creating the build environment every time from scratch + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} + - uses: actions/upload-artifact@v4 + with: + path: build/*.rpm + retention-days: 1 + if-no-files-found: error + name: ${{ github.job }} + + fedora-38: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + # docker login is needed for pushing the build image + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} + # building in precompiled image makes them way faster instead of creating the build environment every time from scratch + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} + - uses: actions/upload-artifact@v4 + with: + path: build/*.rpm + retention-days: 1 + if-no-files-found: error + name: ${{ github.job }} + + fedora-39: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + # docker login is needed for pushing the build image + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} + # building in precompiled image makes them way faster instead of creating the build environment every time from scratch + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} + - uses: actions/upload-artifact@v4 + with: + path: build/*.rpm + retention-days: 1 + if-no-files-found: error + name: ${{ github.job }} + + fedora-40: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + # docker login is needed for pushing the build image + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} + # building in precompiled image makes them way faster instead of creating the build environment every time from scratch + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} + - uses: actions/upload-artifact@v4 + with: + path: build/*.rpm + retention-days: 1 + if-no-files-found: error + name: ${{ github.job }} + + fedora-41: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + # docker login is needed for pushing the build image + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} + # building in precompiled image makes them way faster instead of creating the build environment every time from scratch + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} + - uses: actions/upload-artifact@v4 + with: + path: build/*.rpm + retention-days: 1 + if-no-files-found: error + name: ${{ github.job }} + + rhel-9: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + # docker login is needed for pushing the build image + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} + # building in precompiled image makes them way faster instead of creating the build environment every time from scratch + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} + - uses: actions/upload-artifact@v4 + with: + path: build/*.rpm + retention-days: 1 + if-no-files-found: error + name: ${{ github.job }} + +# borrowed from dhcpy6d + repo-debian: + runs-on: ubuntu-latest + # try to avoid race condition and start uploading only after the last install package has been build + needs: [debian, fedora-37, fedora-38, fedora-39, fedora-40, fedora-41, rhel-9] + env: + family: debian + steps: + - uses: actions/checkout@v4 + # get binaries created by other jobs + - uses: actions/download-artifact@v4 + with: + pattern: 'debian*' + path: artifact + merge-multiple: true + - run: pwd + - run: find . -name '*.deb' + # get secret signing key + - run: echo "${{ secrets.PACKAGE_SIGNING_KEY }}" > signing_key.asc + # organize SSH deploy key for nagstamon-jekyll repo + - run: mkdir ~/.ssh + - run: echo "${{ secrets.NAGSTAMON_REPO_KEY_WEB }}" > ~/.ssh/id_ed25519 + - run: chmod -R go-rwx ~/.ssh + # get and prepare nagstamon-jekyll + - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git + - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/${{ env.dist }}/${{ env.release }} + - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${{ env.dist }}/${{ env.release }} + # create deb repo via Debian build container + - run: | + /usr/bin/docker run --rm \ + -v ${{ github.workspace }}:/workspace \ + -v $PWD/${{ env.repo_dir }}/${{ env.family }}/${{ env.release }}:/repo \ + ${{ env.cr_image }}-${{ env.family }}:${{ env.cr_image_version }} \ + /bin/sh -c "cd /workspace && \ + gpg --import signing_key.asc && \ + cp -r artifact/*.deb nagstamon-jekyll/docs/repo/${{ env.family }}/${{ env.release }} && \ + cd nagstamon-jekyll/docs/repo/${{ env.family }}/${{ env.release }} + dpkg-scanpackages . > Packages && \ + gzip -k -f Packages && \ + apt-ftparchive release . > Release && \ + gpg -abs -o Release.gpg Release && \ + gpg --clearsign -o InRelease Release && \ + gpg --output key.gpg --armor --export" + # commit and push new binaries to nagstamon-jekyll + - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" + - run: cd ${{ env.repo_dir }} && git add . && git commit -am "new ${{ env.release }} repo ${{ env.family }}" && git push + + repo-rpm-fedora: + runs-on: ubuntu-latest + # if not all are ready there might be trouble when downloading artifacts + # maybe faster now with build containers + needs: [repo-debian] + env: + family: fedora + # which image to use for packaging + cr_image_latest: 39 + steps: + # get binaries created by other jobs + - uses: actions/download-artifact@v4 + with: + pattern: 'fedora*' + path: artifact + merge-multiple: true + # organize SSH deploy key for nagstamon-repo + - run: mkdir ~/.ssh + - run: echo "${{ secrets.NAGSTAMON_REPO_KEY_WEB }}" > ~/.ssh/id_ed25519 + - run: chmod -R go-rwx ~/.ssh + # get and prepare nagstamon-jekyll + - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git + - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/${{ env.release }} + - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${{ env.release }} + # copy *.rpm files into nagstamon-jekyll and create repodata + - run: | + version=${{ env.release }} && \ + mkdir -p mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${version} && \ + cp -r artifact/*.${{ env.family }}* ${{ env.repo_dir }}/${{ env.family }}/${version} && \ + docker run --rm -v ${PWD}/${{ env.repo_dir }}/${{ env.family }}/${version}:/repo \ + ${{ env.cr_image }}-${{ env.family }}-${{ env.cr_image_latest }}:${{ env.cr_image_version }} \ + /bin/bash -c "createrepo --verbose --workers 1 /repo" && \ + ls -laR ${PWD}/${{ env.repo_dir }}/${{ env.family }}/${version} + # commit and push new binaries to nagstamon-repo + - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" + - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push + + repo-rpm-rhel: + runs-on: ubuntu-latest + # if not all are ready there might be trouble when downloading artifacts + # maybe faster now with build containers + needs: [repo-rpm-fedora] + env: + family: rhel + # currently just one version available + version: 9 + steps: + # get binaries created by other jobs + - uses: actions/download-artifact@v4 + with: + pattern: 'rhel*' + path: artifact + merge-multiple: true + # organize SSH deploy key for nagstamon-repo + - run: mkdir ~/.ssh + - run: echo "${{ secrets.NAGSTAMON_REPO_KEY_WEB }}" > ~/.ssh/id_ed25519 + - run: chmod -R go-rwx ~/.ssh + # get and prepare nagstamon-jekyll + - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git + - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/${{ env.release }} + - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${{ env.release }} + # copy *.rpm files into nagstamon-jekyll and create repodata + - run: | + version=${{ env.release }} && \ + mkdir -p mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${version} && \ + cp -r artifact/*.${{ env.family }}* ${{ env.repo_dir }}/${{ env.family }}/${version} && \ + docker run --rm -v ${PWD}/${{ env.repo_dir }}/${{ env.family }}/${version}:/repo \ + ${{ env.cr_image }}-${{ env.family }}-${{ env.version }}:${{ env.cr_image_version }} \ + /bin/bash -c "createrepo --verbose --workers 1 /repo" && \ + ls -laR ${PWD}/${{ env.repo_dir }}/${{ env.family }}/${version} + # commit and push new binaries to nagstamon-repo + - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" + - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index c0aff6599..042c495b0 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -24,12 +24,12 @@ jobs: python-version: [3.9, 3.11] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # somehow weird way to get the hash over the requirements to be aware if they changed - id: requirements_hash run: echo "HASH=$(md5sum build/requirements/linux.txt | cut -d\ -f1)" >> $GITHUB_OUTPUT # docker login is needed for pushing the test image - - uses: docker/login-action@v2 + - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} @@ -51,9 +51,9 @@ jobs: runs-on: ubuntu-latest needs: test steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # docker login is needed for pushing the build image - - uses: docker/login-action@v2 + - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} @@ -63,19 +63,20 @@ jobs: - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon -e DEB_BUILD_OPTIONS=nocheck ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: path: build/*.deb retention-days: 1 if-no-files-found: error + name: ${{ github.job }} fedora-37: runs-on: ubuntu-latest needs: test steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # docker login is needed for pushing the build image - - uses: docker/login-action@v2 + - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} @@ -85,19 +86,20 @@ jobs: - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: path: build/*.rpm retention-days: 1 if-no-files-found: error + name: ${{ github.job }} fedora-38: runs-on: ubuntu-latest needs: test steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # docker login is needed for pushing the build image - - uses: docker/login-action@v2 + - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} @@ -107,19 +109,20 @@ jobs: - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: path: build/*.rpm retention-days: 1 if-no-files-found: error + name: ${{ github.job }} fedora-39: runs-on: ubuntu-latest needs: test steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # docker login is needed for pushing the build image - - uses: docker/login-action@v2 + - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} @@ -129,19 +132,20 @@ jobs: - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: path: build/*.rpm retention-days: 1 if-no-files-found: error + name: ${{ github.job }} fedora-40: runs-on: ubuntu-latest needs: test steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # docker login is needed for pushing the build image - - uses: docker/login-action@v2 + - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} @@ -151,19 +155,20 @@ jobs: - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: path: build/*.rpm retention-days: 1 if-no-files-found: error + name: ${{ github.job }} fedora-41: runs-on: ubuntu-latest needs: test steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # docker login is needed for pushing the build image - - uses: docker/login-action@v2 + - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} @@ -173,19 +178,20 @@ jobs: - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: path: build/*.rpm retention-days: 1 if-no-files-found: error + name: ${{ github.job }} rhel-9: runs-on: ubuntu-latest needs: test steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # docker login is needed for pushing the build image - - uses: docker/login-action@v2 + - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} @@ -195,34 +201,36 @@ jobs: - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: path: build/*.rpm retention-days: 1 if-no-files-found: error + name: ${{ github.job }} macos: runs-on: macos-12 needs: test steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: pip3 install --no-warn-script-location -r build/requirements/macos.txt - run: cd ${{ github.workspace }}/build; python3 build.py env: PYTHONPATH: ${{ github.workspace }} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: path: build/*.dmg retention-days: 1 if-no-files-found: error + name: ${{ github.job }} windows-32: # better depend on stable build image runs-on: windows-2022 needs: test steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: python-version: ${{ env.python_win_version }} architecture: x86 @@ -236,21 +244,22 @@ jobs: PYTHONPATH: ${{ github.workspace }} WIN_SIGNING_CERT_BASE64: ${{ secrets.SIGNING_CERT_BASE64 }} WIN_SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: path: | build/dist/*.zip build/dist/*.exe retention-days: 1 if-no-files-found: error + name: ${{ github.job }} windows-64: # better depend on stable build image runs-on: windows-2022 needs: test steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: python-version: ${{ env.python_win_version }} architecture: x64 @@ -262,21 +271,22 @@ jobs: PYTHONPATH: ${{ github.workspace }} WIN_SIGNING_CERT_BASE64: ${{ secrets.SIGNING_CERT_BASE64 }} WIN_SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: path: | build/dist/*.zip build/dist/*.exe retention-days: 1 if-no-files-found: error + name: ${{ github.job }} windows-64-debug: # better depend on stable build image runs-on: windows-2022 needs: test steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: python-version: ${{ env.python_win_version }} architecture: x64 @@ -288,12 +298,13 @@ jobs: PYTHONPATH: ${{ github.workspace }} WIN_SIGNING_CERT_BASE64: ${{ secrets.SIGNING_CERT_BASE64 }} WIN_SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: path: | build/dist/*.zip retention-days: 1 if-no-files-found: error + name: ${{ github.job }} # borrowed from dhcpy6d repo-debian: @@ -303,9 +314,13 @@ jobs: env: family: debian steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # get binaries created by other jobs - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 + with: + pattern: 'debian*' + path: artifact + merge-multiple: true # get secret signing key - run: echo "${{ secrets.PACKAGE_SIGNING_KEY }}" > signing_key.asc # organize SSH deploy key for nagstamon-jekyll repo @@ -347,7 +362,11 @@ jobs: cr_image_latest: 39 steps: # get binaries created by other jobs - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 + with: + pattern: 'fedora*' + path: artifact + merge-multiple: true # organize SSH deploy key for nagstamon-repo - run: mkdir ~/.ssh - run: echo "${{ secrets.NAGSTAMON_REPO_KEY_WEB }}" > ~/.ssh/id_ed25519 @@ -380,7 +399,11 @@ jobs: version: 9 steps: # get binaries created by other jobs - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 + with: + pattern: 'rhel*' + path: artifact + merge-multiple: true # organize SSH deploy key for nagstamon-repo - run: mkdir ~/.ssh - run: echo "${{ secrets.NAGSTAMON_REPO_KEY_WEB }}" > ~/.ssh/id_ed25519 @@ -406,7 +429,9 @@ jobs: runs-on: ubuntu-latest needs: [repo-rpm-rhel] steps: - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 + with: + pattern: '*' - run: cd artifact && md5sum *agstamon* > md5sums.txt - run: cd artifact && sha256sum *agstamon* > sha256sums.txt - uses: marvinpinto/action-automatic-releases@latest diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 1bfcb6e35..455c0a93f 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -131,7 +131,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.15-20240417' + VERSION = '3.15-20240420' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2024 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/Servers/Centreon/CentreonAPI.py b/Nagstamon/Servers/Centreon/CentreonAPI.py index 0ddea7a0c..abca00ea3 100644 --- a/Nagstamon/Servers/Centreon/CentreonAPI.py +++ b/Nagstamon/Servers/Centreon/CentreonAPI.py @@ -99,8 +99,12 @@ def init_config(self): self.restapi_version = "v22.04" elif self.centreon_version_major == 23 and self.centreon_version_minor == 4: self.restapi_version = "v23.04" - else: + elif self.centreon_version_major == 23 and self.centreon_version_minor == 10: self.restapi_version = "v23.10" + elif self.centreon_version_major == 24: + self.restapi_version = "v24.04" + else: + self.restapi_version = "v24.04" if conf.debug_mode is True: self.Debug(server='[' + self.get_name() + ']', debug='Centreon API version used : ' + self.restapi_version) @@ -486,14 +490,22 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi # host if service == '': host_id = self.get_host_and_service_id(host) - - new_resource = { - "type": "host", - "id": host_id, - "parent": { - "id": None + if self.centreon_version_major >= 24: + new_resource = { + "type": "host", + "id": host_id, + "parent": { + "id": host_id + } + } + else: + new_resource = { + "type": "host", + "id": host_id, + "parent": { + "id": None + } } - } acknowledgements["resources"].append(new_resource) diff --git a/build/debian/changelog b/build/debian/changelog index e47943acc..2c76d57b6 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.15-20240417) unstable; urgency=low +nagstamon (3.15-20240420) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Wed, Apr 17 2024 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Sat, Apr 20 2024 08:00:00 +0200 nagstamon (3.14.0) stable; urgency=low * New upstream From da6b4e788f91ba27691b0c43ff533e9e737043c9 Mon Sep 17 00:00:00 2001 From: ScHcSII <156169316+ScHcSII@users.noreply.github.com> Date: Fri, 3 May 2024 17:39:14 +0200 Subject: [PATCH 767/884] Attempt to fix the issue: (#1029) AttributeError with Icinga DB 1.2.0 / Icinga DB Web 1.1.2 #1028 Nagstamon Error: AttributeError: 'bool' object has no attribute 'replace' Co-authored-by: Topi6 <csabaschulmann@gmail.com> --- Nagstamon/Servers/IcingaDBWeb.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index 58355b569..bb40aa26a 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -220,7 +220,8 @@ def _get_status(self): self.new_hosts[host_name].notifications_disabled = not int(h.get('notifications_enabled') or '0') self.new_hosts[host_name].flapping = bool(int(h['state']['is_flapping'] or 0)) #s['state']['is_acknowledged'] can be null, 0, 1, or 'sticky' - self.new_hosts[host_name].acknowledged = bool(int(h['state']['is_acknowledged'].replace('sticky', '1') or 0)) + #self.new_hosts[host_name].acknowledged = bool(int(h['state']['is_acknowledged'].replace('sticky', '1') or 0)) + self.new_hosts[host_name].acknowledged = h['state']['is_acknowledged'] if isinstance(h['state']['is_acknowledged'], (bool)) else bool(int(h['state']['is_acknowledged'].replace('sticky', '1') or 0)) self.new_hosts[host_name].scheduled_downtime = bool(int(h['state']['in_downtime'] or 0)) # extra Icinga properties to solve https://github.com/HenriWahl/Nagstamon/issues/192 @@ -323,7 +324,8 @@ def _get_status(self): self.new_hosts[host_name].services[service_name].notifications_disabled = not int(s.get('notifications_enabled') or '0') self.new_hosts[host_name].services[service_name].flapping = bool(int(s['state']['is_flapping'] or 0)) #s['state']['is_acknowledged'] can be null, 0, 1, or 'sticky' - self.new_hosts[host_name].services[service_name].acknowledged = bool(int(s['state']['is_acknowledged'].replace('sticky', '1') or 0)) + #self.new_hosts[host_name].services[service_name].acknowledged = bool(int(s['state']['is_acknowledged'].replace('sticky', '1') or 0)) + self.new_hosts[host_name].services[service_name].acknowledged = s['state']['is_acknowledged'] if isinstance(s['state']['is_acknowledged'], (bool)) else bool(int(s['state']['is_acknowledged'].replace('sticky', '1') or 0)) self.new_hosts[host_name].services[service_name].scheduled_downtime = bool(int(s['state']['in_downtime'] or 0)) self.new_hosts[host_name].services[service_name].unreachable = not bool(int(s['state']['is_reachable'] or 0)) From 224e2d8a35331a326ef796f25f71af9ac106c8ca Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Fri, 3 May 2024 17:42:55 +0200 Subject: [PATCH 768/884] merged fix for IcingaDBWeb --- ...ease-latest-test-artifacts.yml_deactivated | 293 ------------------ Nagstamon/Config.py | 2 +- Nagstamon/Servers/IcingaDBWeb.py | 6 +- build/debian/changelog | 4 +- 4 files changed, 5 insertions(+), 300 deletions(-) delete mode 100644 .github/workflows/build-release-latest-test-artifacts.yml_deactivated diff --git a/.github/workflows/build-release-latest-test-artifacts.yml_deactivated b/.github/workflows/build-release-latest-test-artifacts.yml_deactivated deleted file mode 100644 index 148a3d36c..000000000 --- a/.github/workflows/build-release-latest-test-artifacts.yml_deactivated +++ /dev/null @@ -1,293 +0,0 @@ -name: build-release-latest -on: - push: - tags-ignore: 'v*' - branches: - - '**' - - '!master' - - '!*.*.*' - -env: - python_win_version: 3.11.8 - repo_dir: nagstamon-jekyll/docs/repo - cr_image: ghcr.io/henriwahl/build-nagstamon - # to be increased if new updates of build images are necessary - cr_image_version: 4 - # release type this file is used for - release: latest - -jobs: - debian: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - # docker login is needed for pushing the build image - - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed - - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . - - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon -e DEB_BUILD_OPTIONS=nocheck ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - - uses: actions/upload-artifact@v4 - with: - path: build/*.deb - retention-days: 1 - if-no-files-found: error - name: ${{ github.job }} - - fedora-37: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - # docker login is needed for pushing the build image - - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed - - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . - - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - - uses: actions/upload-artifact@v4 - with: - path: build/*.rpm - retention-days: 1 - if-no-files-found: error - name: ${{ github.job }} - - fedora-38: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - # docker login is needed for pushing the build image - - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed - - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . - - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - - uses: actions/upload-artifact@v4 - with: - path: build/*.rpm - retention-days: 1 - if-no-files-found: error - name: ${{ github.job }} - - fedora-39: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - # docker login is needed for pushing the build image - - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed - - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . - - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - - uses: actions/upload-artifact@v4 - with: - path: build/*.rpm - retention-days: 1 - if-no-files-found: error - name: ${{ github.job }} - - fedora-40: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - # docker login is needed for pushing the build image - - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed - - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . - - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - - uses: actions/upload-artifact@v4 - with: - path: build/*.rpm - retention-days: 1 - if-no-files-found: error - name: ${{ github.job }} - - fedora-41: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - # docker login is needed for pushing the build image - - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed - - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . - - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - - uses: actions/upload-artifact@v4 - with: - path: build/*.rpm - retention-days: 1 - if-no-files-found: error - name: ${{ github.job }} - - rhel-9: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - # docker login is needed for pushing the build image - - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed - - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . - - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - - uses: actions/upload-artifact@v4 - with: - path: build/*.rpm - retention-days: 1 - if-no-files-found: error - name: ${{ github.job }} - -# borrowed from dhcpy6d - repo-debian: - runs-on: ubuntu-latest - # try to avoid race condition and start uploading only after the last install package has been build - needs: [debian, fedora-37, fedora-38, fedora-39, fedora-40, fedora-41, rhel-9] - env: - family: debian - steps: - - uses: actions/checkout@v4 - # get binaries created by other jobs - - uses: actions/download-artifact@v4 - with: - pattern: 'debian*' - path: artifact - merge-multiple: true - - run: pwd - - run: find . -name '*.deb' - # get secret signing key - - run: echo "${{ secrets.PACKAGE_SIGNING_KEY }}" > signing_key.asc - # organize SSH deploy key for nagstamon-jekyll repo - - run: mkdir ~/.ssh - - run: echo "${{ secrets.NAGSTAMON_REPO_KEY_WEB }}" > ~/.ssh/id_ed25519 - - run: chmod -R go-rwx ~/.ssh - # get and prepare nagstamon-jekyll - - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/${{ env.dist }}/${{ env.release }} - - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${{ env.dist }}/${{ env.release }} - # create deb repo via Debian build container - - run: | - /usr/bin/docker run --rm \ - -v ${{ github.workspace }}:/workspace \ - -v $PWD/${{ env.repo_dir }}/${{ env.family }}/${{ env.release }}:/repo \ - ${{ env.cr_image }}-${{ env.family }}:${{ env.cr_image_version }} \ - /bin/sh -c "cd /workspace && \ - gpg --import signing_key.asc && \ - cp -r artifact/*.deb nagstamon-jekyll/docs/repo/${{ env.family }}/${{ env.release }} && \ - cd nagstamon-jekyll/docs/repo/${{ env.family }}/${{ env.release }} - dpkg-scanpackages . > Packages && \ - gzip -k -f Packages && \ - apt-ftparchive release . > Release && \ - gpg -abs -o Release.gpg Release && \ - gpg --clearsign -o InRelease Release && \ - gpg --output key.gpg --armor --export" - # commit and push new binaries to nagstamon-jekyll - - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - - run: cd ${{ env.repo_dir }} && git add . && git commit -am "new ${{ env.release }} repo ${{ env.family }}" && git push - - repo-rpm-fedora: - runs-on: ubuntu-latest - # if not all are ready there might be trouble when downloading artifacts - # maybe faster now with build containers - needs: [repo-debian] - env: - family: fedora - # which image to use for packaging - cr_image_latest: 39 - steps: - # get binaries created by other jobs - - uses: actions/download-artifact@v4 - with: - pattern: 'fedora*' - path: artifact - merge-multiple: true - # organize SSH deploy key for nagstamon-repo - - run: mkdir ~/.ssh - - run: echo "${{ secrets.NAGSTAMON_REPO_KEY_WEB }}" > ~/.ssh/id_ed25519 - - run: chmod -R go-rwx ~/.ssh - # get and prepare nagstamon-jekyll - - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/${{ env.release }} - - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${{ env.release }} - # copy *.rpm files into nagstamon-jekyll and create repodata - - run: | - version=${{ env.release }} && \ - mkdir -p mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${version} && \ - cp -r artifact/*.${{ env.family }}* ${{ env.repo_dir }}/${{ env.family }}/${version} && \ - docker run --rm -v ${PWD}/${{ env.repo_dir }}/${{ env.family }}/${version}:/repo \ - ${{ env.cr_image }}-${{ env.family }}-${{ env.cr_image_latest }}:${{ env.cr_image_version }} \ - /bin/bash -c "createrepo --verbose --workers 1 /repo" && \ - ls -laR ${PWD}/${{ env.repo_dir }}/${{ env.family }}/${version} - # commit and push new binaries to nagstamon-repo - - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push - - repo-rpm-rhel: - runs-on: ubuntu-latest - # if not all are ready there might be trouble when downloading artifacts - # maybe faster now with build containers - needs: [repo-rpm-fedora] - env: - family: rhel - # currently just one version available - version: 9 - steps: - # get binaries created by other jobs - - uses: actions/download-artifact@v4 - with: - pattern: 'rhel*' - path: artifact - merge-multiple: true - # organize SSH deploy key for nagstamon-repo - - run: mkdir ~/.ssh - - run: echo "${{ secrets.NAGSTAMON_REPO_KEY_WEB }}" > ~/.ssh/id_ed25519 - - run: chmod -R go-rwx ~/.ssh - # get and prepare nagstamon-jekyll - - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/${{ env.release }} - - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${{ env.release }} - # copy *.rpm files into nagstamon-jekyll and create repodata - - run: | - version=${{ env.release }} && \ - mkdir -p mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${version} && \ - cp -r artifact/*.${{ env.family }}* ${{ env.repo_dir }}/${{ env.family }}/${version} && \ - docker run --rm -v ${PWD}/${{ env.repo_dir }}/${{ env.family }}/${version}:/repo \ - ${{ env.cr_image }}-${{ env.family }}-${{ env.version }}:${{ env.cr_image_version }} \ - /bin/bash -c "createrepo --verbose --workers 1 /repo" && \ - ls -laR ${PWD}/${{ env.repo_dir }}/${{ env.family }}/${version} - # commit and push new binaries to nagstamon-repo - - run: git config --global user.email "repo@nagstamon.de" && git config --global user.name "Nagstamon Repository" - - run: cd ${{ env.repo_dir }} && git pull && git add . && git commit -am "new latest repo ${{ env.family }}" && git push diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 455c0a93f..7c2b36bcd 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -131,7 +131,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.15-20240420' + VERSION = '3.15-20240503' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2024 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index bb40aa26a..3d637c536 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -220,8 +220,7 @@ def _get_status(self): self.new_hosts[host_name].notifications_disabled = not int(h.get('notifications_enabled') or '0') self.new_hosts[host_name].flapping = bool(int(h['state']['is_flapping'] or 0)) #s['state']['is_acknowledged'] can be null, 0, 1, or 'sticky' - #self.new_hosts[host_name].acknowledged = bool(int(h['state']['is_acknowledged'].replace('sticky', '1') or 0)) - self.new_hosts[host_name].acknowledged = h['state']['is_acknowledged'] if isinstance(h['state']['is_acknowledged'], (bool)) else bool(int(h['state']['is_acknowledged'].replace('sticky', '1') or 0)) + self.new_hosts[host_name].acknowledged = h['state']['is_acknowledged'] if isinstance(h['state']['is_acknowledged'], bool) else bool(int(h['state']['is_acknowledged'].replace('sticky', '1') or 0)) self.new_hosts[host_name].scheduled_downtime = bool(int(h['state']['in_downtime'] or 0)) # extra Icinga properties to solve https://github.com/HenriWahl/Nagstamon/issues/192 @@ -324,8 +323,7 @@ def _get_status(self): self.new_hosts[host_name].services[service_name].notifications_disabled = not int(s.get('notifications_enabled') or '0') self.new_hosts[host_name].services[service_name].flapping = bool(int(s['state']['is_flapping'] or 0)) #s['state']['is_acknowledged'] can be null, 0, 1, or 'sticky' - #self.new_hosts[host_name].services[service_name].acknowledged = bool(int(s['state']['is_acknowledged'].replace('sticky', '1') or 0)) - self.new_hosts[host_name].services[service_name].acknowledged = s['state']['is_acknowledged'] if isinstance(s['state']['is_acknowledged'], (bool)) else bool(int(s['state']['is_acknowledged'].replace('sticky', '1') or 0)) + self.new_hosts[host_name].services[service_name].acknowledged = s['state']['is_acknowledged'] if isinstance(s['state']['is_acknowledged'], bool) else bool(int(s['state']['is_acknowledged'].replace('sticky', '1') or 0)) self.new_hosts[host_name].services[service_name].scheduled_downtime = bool(int(s['state']['in_downtime'] or 0)) self.new_hosts[host_name].services[service_name].unreachable = not bool(int(s['state']['is_reachable'] or 0)) diff --git a/build/debian/changelog b/build/debian/changelog index 2c76d57b6..1712846d9 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.15-20240420) unstable; urgency=low +nagstamon (3.15-20240503) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Sat, Apr 20 2024 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Fri, May 05 2024 08:00:00 +0200 nagstamon (3.14.0) stable; urgency=low * New upstream From 27afe5a9b770ac9e3c91bd3f0fa304e36320572d Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Fri, 3 May 2024 17:56:20 +0200 Subject: [PATCH 769/884] artifact github upload --- .github/workflows/build-release-latest.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 042c495b0..42d6d0539 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -432,6 +432,8 @@ jobs: - uses: actions/download-artifact@v4 with: pattern: '*' + path: artifact + merge-multiple: true - run: cd artifact && md5sum *agstamon* > md5sums.txt - run: cd artifact && sha256sum *agstamon* > sha256sums.txt - uses: marvinpinto/action-automatic-releases@latest From 3716c6fdb1a555cbf2f04bcc1dc045cdeda53574 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 4 May 2024 23:17:41 +0200 Subject: [PATCH 770/884] update build-release-stable.yml --- .github/workflows/build-release-stable.yml | 144 +++++++++++++++------ 1 file changed, 108 insertions(+), 36 deletions(-) diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index 5096d4e27..139d6907b 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -8,7 +8,8 @@ env: repo_dir: nagstamon-jekyll/docs/repo cr_image: ghcr.io/henriwahl/build-nagstamon # to be increased if new updates of build images are necessary - cr_image_version: 3 + cr_image_version: 4 + # release type this file is used for release: stable jobs: @@ -16,15 +17,15 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.7, 3.9, 3.11] + python-version: [3.9, 3.11] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # somehow weird way to get the hash over the requirements to be aware if they changed - id: requirements_hash run: echo "HASH=$(md5sum build/requirements/linux.txt | cut -d\ -f1)" >> $GITHUB_OUTPUT # docker login is needed for pushing the test image - - uses: docker/login-action@v2 + - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} @@ -46,9 +47,9 @@ jobs: runs-on: ubuntu-latest needs: test steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # docker login is needed for pushing the build image - - uses: docker/login-action@v2 + - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} @@ -58,19 +59,20 @@ jobs: - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon -e DEB_BUILD_OPTIONS=nocheck ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: path: build/*.deb retention-days: 1 if-no-files-found: error + name: ${{ github.job }} fedora-37: runs-on: ubuntu-latest needs: test steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # docker login is needed for pushing the build image - - uses: docker/login-action@v2 + - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} @@ -80,19 +82,20 @@ jobs: - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: path: build/*.rpm retention-days: 1 if-no-files-found: error + name: ${{ github.job }} fedora-38: runs-on: ubuntu-latest needs: test steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # docker login is needed for pushing the build image - - uses: docker/login-action@v2 + - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} @@ -102,19 +105,20 @@ jobs: - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: path: build/*.rpm retention-days: 1 if-no-files-found: error + name: ${{ github.job }} fedora-39: runs-on: ubuntu-latest needs: test steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # docker login is needed for pushing the build image - - uses: docker/login-action@v2 + - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} @@ -124,19 +128,66 @@ jobs: - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: path: build/*.rpm retention-days: 1 if-no-files-found: error + name: ${{ github.job }} + + fedora-40: + runs-on: ubuntu-latest + needs: test + steps: + - uses: actions/checkout@v4 + # docker login is needed for pushing the build image + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} + # building in precompiled image makes them way faster instead of creating the build environment every time from scratch + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} + - uses: actions/upload-artifact@v4 + with: + path: build/*.rpm + retention-days: 1 + if-no-files-found: error + name: ${{ github.job }} + + fedora-41: + runs-on: ubuntu-latest + needs: test + steps: + - uses: actions/checkout@v4 + # docker login is needed for pushing the build image + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} + # building in precompiled image makes them way faster instead of creating the build environment every time from scratch + - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} + - uses: actions/upload-artifact@v4 + with: + path: build/*.rpm + retention-days: 1 + if-no-files-found: error + name: ${{ github.job }} rhel-9: runs-on: ubuntu-latest needs: test steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # docker login is needed for pushing the build image - - uses: docker/login-action@v2 + - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} @@ -146,34 +197,36 @@ jobs: - run: docker push ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} # building in precompiled image makes them way faster instead of creating the build environment every time from scratch - run: /usr/bin/docker run -v ${{ github.workspace }}:/nagstamon ${{ env.cr_image }}-${{ github.job }}:${{ env.cr_image_version }} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: path: build/*.rpm retention-days: 1 if-no-files-found: error + name: ${{ github.job }} macos: runs-on: macos-12 needs: test steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: pip3 install --no-warn-script-location -r build/requirements/macos.txt - run: cd ${{ github.workspace }}/build; python3 build.py env: PYTHONPATH: ${{ github.workspace }} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: path: build/*.dmg retention-days: 1 if-no-files-found: error + name: ${{ github.job }} windows-32: # better depend on stable build image runs-on: windows-2022 needs: test steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: python-version: ${{ env.python_win_version }} architecture: x86 @@ -187,21 +240,22 @@ jobs: PYTHONPATH: ${{ github.workspace }} WIN_SIGNING_CERT_BASE64: ${{ secrets.SIGNING_CERT_BASE64 }} WIN_SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: path: | build/dist/*.zip build/dist/*.exe retention-days: 1 if-no-files-found: error + name: ${{ github.job }} windows-64: # better depend on stable build image runs-on: windows-2022 needs: test steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: python-version: ${{ env.python_win_version }} architecture: x64 @@ -213,21 +267,22 @@ jobs: PYTHONPATH: ${{ github.workspace }} WIN_SIGNING_CERT_BASE64: ${{ secrets.SIGNING_CERT_BASE64 }} WIN_SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: path: | build/dist/*.zip build/dist/*.exe retention-days: 1 if-no-files-found: error + name: ${{ github.job }} windows-64-debug: # better depend on stable build image runs-on: windows-2022 needs: test steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: python-version: ${{ env.python_win_version }} architecture: x64 @@ -239,24 +294,29 @@ jobs: PYTHONPATH: ${{ github.workspace }} WIN_SIGNING_CERT_BASE64: ${{ secrets.SIGNING_CERT_BASE64 }} WIN_SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: path: | build/dist/*.zip retention-days: 1 if-no-files-found: error + name: ${{ github.job }} # borrowed from dhcpy6d repo-debian: runs-on: ubuntu-latest # try to avoid race condition and start uploading only after the last install package has been build - needs: [debian, fedora-37, fedora-38, fedora-39, rhel-9, macos, windows-32, windows-64, windows-64-debug] + needs: [debian, fedora-37, fedora-38, fedora-39, fedora-40, fedora-41, rhel-9, macos, windows-32, windows-64, windows-64-debug] env: family: debian steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 # get binaries created by other jobs - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 + with: + pattern: 'debian*' + path: artifact + merge-multiple: true # get secret signing key - run: echo "${{ secrets.PACKAGE_SIGNING_KEY }}" > signing_key.asc # organize SSH deploy key for nagstamon-jekyll repo @@ -298,7 +358,11 @@ jobs: cr_image_latest: 39 steps: # get binaries created by other jobs - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 + with: + pattern: 'fedora*' + path: artifact + merge-multiple: true # organize SSH deploy key for nagstamon-repo - run: mkdir ~/.ssh - run: echo "${{ secrets.NAGSTAMON_REPO_KEY_WEB }}" > ~/.ssh/id_ed25519 @@ -333,7 +397,11 @@ jobs: version: 9 steps: # get binaries created by other jobs - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 + with: + pattern: 'rhel*' + path: artifact + merge-multiple: true # organize SSH deploy key for nagstamon-repo - run: mkdir ~/.ssh - run: echo "${{ secrets.NAGSTAMON_REPO_KEY_WEB }}" > ~/.ssh/id_ed25519 @@ -364,7 +432,11 @@ jobs: runs-on: ubuntu-latest needs: [repo-rpm-rhel] steps: - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 + with: + pattern: '*' + path: artifact + merge-multiple: true - run: cd artifact && md5sum *agstamon* > md5sums.txt - run: cd artifact && sha256sum *agstamon* > sha256sums.txt - uses: marvinpinto/action-automatic-releases@latest From 6111bff8a25aa6ca3bca9364ac210b44eb62f977 Mon Sep 17 00:00:00 2001 From: SimKim <github@simkim.de> Date: Tue, 11 Jun 2024 22:56:25 +0200 Subject: [PATCH 771/884] Fix Zabbix 6.4 auth Parameter (#1040) * Handle Zabbix Version while init * Add Bearer header instead of auth parameter, which is deprecated since 6.4. * Remove deprecated Zabbix Code * Undo Removal --------- Co-authored-by: Kimmig, Simon - D0242573 <simon.kimmig@dm.de> --- Nagstamon/Servers/Zabbix.py | 14 +------------- Nagstamon/thirdparty/zabbix_api.py | 13 +++++++++---- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/Nagstamon/Servers/Zabbix.py b/Nagstamon/Servers/Zabbix.py index 6332be02c..dbaca243f 100644 --- a/Nagstamon/Servers/Zabbix.py +++ b/Nagstamon/Servers/Zabbix.py @@ -134,17 +134,6 @@ def _get_status(self): services = [] services_in_maintenance = set() - try: - api_version = int(''.join(self.zapi.api_version().split('.')[:-1])) # Make API Version smaller - except ZabbixAPIException: - # FIXME Is there a cleaner way to handle this? I just borrowed - # this code from 80 lines ahead. -- AGV - # set checking flag back to False - self.isChecking = False - result, error = self.Error(sys.exc_info()) - print(sys.exc_info()) - return Result(result=result, error=error) - try: now_ts = int(datetime.datetime.utcnow().timestamp()) # only the maintenance object knows about services "in downtime" @@ -165,7 +154,6 @@ def _get_status(self): try: try: # Get a list of all issues (AKA tripped triggers) - # Zabbix 3+ returns array of objects services = self.zapi.trigger.get({'only_true': True, 'skipDependent': True, 'monitored': True, @@ -217,7 +205,7 @@ def _get_status(self): # Create Hostids for shorten Query try: hosts = [] - if api_version >= 54: # For Version 5.4 and higher + if self.zapi.api_version > '5.4': # For Version 5.4 and higher # Some performance improvement for 5.4 hostids = [] # get just involved Hosts. diff --git a/Nagstamon/thirdparty/zabbix_api.py b/Nagstamon/thirdparty/zabbix_api.py index 582e6391f..443a0f19c 100644 --- a/Nagstamon/thirdparty/zabbix_api.py +++ b/Nagstamon/thirdparty/zabbix_api.py @@ -119,6 +119,7 @@ class ZabbixAPI(object): httppasswd = None timeout = 10 validate_certs = None + api_version = '0.0' # sub-class instances. # Constructor Params: # server: Server to connect to @@ -149,6 +150,8 @@ def __init__(self, server='http://localhost/zabbix', user=httpuser, passwd=httpp self.r_query = deque([], maxlen=r_query_len) self.validate_certs = validate_certs self.debug(logging.INFO, "url: " + self.url) + self.api_version = self.get_api_version() + self.debug(logging.INFO, "Zabbix API version: " + self.api_version) def _setuplogging(self): self.logger = logging.getLogger("zabbix_api.%s" % self.__class__.__name__) @@ -176,10 +179,10 @@ def json_obj(self, method, params={}, auth=True): obj = {'jsonrpc': '2.0', 'method': method, 'params': params, - 'auth': self.auth, + 'auth': self.auth, # deprecated in 6.4 'id': self.id } - if not auth: + if not auth or self.api_version > '6.4': del obj['auth'] self.debug(logging.DEBUG, "json_obj: " + str(obj)) @@ -201,7 +204,7 @@ def login(self, user='', password='', save=True): raise ZabbixAPIException("No authentication information available.") # check version to use the correct keyword for username which changed since 6.4 - if self.api_version() < '6.4': + if self.api_version < '6.4': username_keyword = 'user' else: username_keyword = 'username' @@ -230,6 +233,8 @@ def do_request(self, json_obj): headers = {'Content-Type': 'application/json-rpc', 'User-Agent': 'python/zabbix_api'} + if self.api_version > '6.4': + headers['Authorization'] = 'Bearer ' + self.auth if self.httpuser: self.debug(logging.INFO, "HTTP Auth enabled") auth = 'Basic ' + string.strip(base64.encodestring(self.httpuser + ':' + self.httppasswd)) @@ -301,7 +306,7 @@ def logged_in(self): return True return False - def api_version(self, **options): + def get_api_version(self, **options): # kicked out check auth to be able to check version before being logged in to use the correct username keyword obj = self.do_request(self.json_obj('apiinfo.version', options, auth=False)) return obj['result'] From 0cafea0ea567e65ba26341878f9457d85d9c79f5 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 21 Jul 2024 15:44:17 +0200 Subject: [PATCH 772/884] try to fix checkmk host_in_downtime for service (#1049) --- Nagstamon/Config.py | 2 +- Nagstamon/Servers/Multisite.py | 5 ++++- build/debian/changelog | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 7c2b36bcd..c38d41fee 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -131,7 +131,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.15-20240503' + VERSION = '3.15-20240506' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2024 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/Servers/Multisite.py b/Nagstamon/Servers/Multisite.py index 3bfa453c9..4331dacc8 100644 --- a/Nagstamon/Servers/Multisite.py +++ b/Nagstamon/Servers/Multisite.py @@ -363,7 +363,7 @@ def _get_status(self): self.new_hosts[n['host']].services[new_service].address = n['address'] self.new_hosts[n['host']].services[new_service].command = n['command'] - # transistion to Checkmk 1.1.10p2 + # transition to Checkmk 1.1.10p2 if 'svc_in_downtime' in service: if service['svc_in_downtime'] == 'yes': self.new_hosts[n['host']].services[new_service].scheduled_downtime = True @@ -376,6 +376,9 @@ def _get_status(self): if 'svc_notifications_enabled' in service: if service['svc_notifications_enabled'] == 'no': self.new_hosts[n['host']].services[new_service].notifications_disabled = True + if 'host_in_downtime' in service: + if service['host_in_downtime'] == 'yes': + self.new_hosts[n['host']].scheduled_downtime = True # hard/soft state for later filter evaluation real_attempt, max_attempt = self.new_hosts[n['host']].services[new_service].attempt.split('/') diff --git a/build/debian/changelog b/build/debian/changelog index 1712846d9..6a66c9b53 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.15-20240503) unstable; urgency=low +nagstamon (3.15-20240506) unstable; urgency=low * New upstream -- Henri Wahl <henri@nagstamon.de> Fri, May 05 2024 08:00:00 +0200 From 9eb63ef622520f85dd866ea1e40e011cb114f9b6 Mon Sep 17 00:00:00 2001 From: Benjamin Renard <brenard@zionetrix.net> Date: Sun, 21 Jul 2024 16:00:54 +0200 Subject: [PATCH 773/884] Icinga2 API support improvements (#1043) * Remove icinga2api dependency * Implement menu actions in Icinga2API server --- Nagstamon/QUI/__init__.py | 14 +- Nagstamon/Servers/Icinga2API.py | 223 ++++++++++++++++++++++++-------- Nagstamon/Servers/__init__.py | 15 +-- build/requirements/windows.txt | 1 - 4 files changed, 177 insertions(+), 76 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index bc2bf27a6..5bc90c323 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -3354,7 +3354,7 @@ def get_real_height(self): height = 0 mddl = self.model() - + rwcnt = mddl.rowCount(self) # only count if there is anything to display - there is no use of the headers only @@ -5718,7 +5718,7 @@ def toggle_expire_time_widgets(self): use_expire_time = False for server in servers.values(): if server.enabled: - if server.type in ['IcingaWeb2', 'Alertmanager']: + if server.type in ['IcingaWeb2', 'Icinga2API', 'Alertmanager']: use_expire_time = True break if use_expire_time: @@ -6393,8 +6393,8 @@ def __init__(self, dialog): x.TYPE not in PROMETHEUS_OR_ALERTMANAGER] self.VOLATILE_WIDGETS = { - self.window.input_checkbox_use_expire_time: ['IcingaWeb2'], - self.window.input_datetime_expire_time: ['IcingaWeb2', 'Alertmanager'], + self.window.input_checkbox_use_expire_time: ['IcingaWeb2', 'Icinga2API'], + self.window.input_datetime_expire_time: ['IcingaWeb2', 'Icinga2API', 'Alertmanager'], self.window.input_checkbox_sticky_acknowledgement: NOT_PROMETHEUS_OR_ALERTMANAGER, self.window.input_checkbox_send_notification: NOT_PROMETHEUS_OR_ALERTMANAGER, self.window.input_checkbox_persistent_comment: NOT_PROMETHEUS_OR_ALERTMANAGER, @@ -6752,12 +6752,12 @@ def show_auth_dialog(self, server): if not statuswindow is None: statuswindow.hide_window() self.window.adjustSize() - + # the dock icon might be needed to be shown for a potential keyboard input self.show_macos_dock_icon_if_necessary() - + self.window.exec() - + # en reverse the dock icon might be hidden again after a potential keyboard input self.hide_macos_dock_icon_if_necessary() diff --git a/Nagstamon/Servers/Icinga2API.py b/Nagstamon/Servers/Icinga2API.py index aedd92f5a..16298646b 100644 --- a/Nagstamon/Servers/Icinga2API.py +++ b/Nagstamon/Servers/Icinga2API.py @@ -18,17 +18,19 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA import arrow +import copy +import datetime +import json import logging import sys -import urllib3 -from icinga2api.client import Client +import dateutil.parser +import urllib.parse from Nagstamon.Config import conf from Nagstamon.Servers.Generic import GenericServer from Nagstamon.Objects import (GenericHost, GenericService, Result) -log = logging.getLogger('Icinga2API.py') -urllib3.disable_warnings() +log = logging.getLogger(__name__) class Icinga2APIServer(GenericServer): @@ -38,13 +40,13 @@ class Icinga2APIServer(GenericServer): TYPE = 'Icinga2API' # ICINGA2API does not provide a web interface for humans - MENU_ACTIONS = [] + MENU_ACTIONS = ['Recheck', 'Acknowledge', 'Submit check result', 'Downtime'] + STATES_MAPPING = {'hosts' : {0 : 'UP', 1 : 'DOWN', 2 : 'UNREACHABLE'}, \ + 'services' : {0 : 'OK', 1 : 'WARNING', 2 : 'CRITICAL', 3 : 'UNKNOWN'}} + STATES_MAPPING_REV = {'hosts' : { 'UP': 0, 'DOWN': 1, 'UNREACHABLE': 2}, \ + 'services' : {'OK': 0, 'WARNING': 1, 'CRITICAL': 2, 'UNKNOWN': 3}} BROWSER_URLS = {} - # Helpers - iapi = None - SERVICE_SEVERITY_CODE_TEXT_MAP = dict() - HOST_SEVERITY_CODE_TEXT_MAP = dict() def __init__(self, **kwds): """ @@ -56,32 +58,6 @@ def __init__(self, **kwds): self.username = conf.servers[self.get_name()].username self.password = conf.servers[self.get_name()].password - self.SERVICE_SEVERITY_CODE_TEXT_MAP = { - 0: 'OK', - 1: 'WARNING', - 2: 'CRITICAL', - 3: 'UNKNOWN' - } - self.HOST_SEVERITY_CODE_TEXT_MAP = { - 0: 'UP', - 1: 'DOWN', - 2: 'UNREACHABLE' - } - - def _ilogin(self): - """ - Initialize HTTP connection to icinga api - """ - try: - self.iapi = Client(url=self.url, - username=self.username, - password=self.password, - ) - except Exception as e: - log.exception(e) - result, error = self.Error(sys.exc_info()) - return Result(result=result, error=error) - def _insert_service_to_hosts(self, service: GenericService): """ We want to create hosts for faulty services as GenericService requires @@ -106,6 +82,7 @@ def _get_status(self): try: # We ask icinga for hosts which are not doing well hosts = self._get_host_events() + assert isinstance(hosts, list), "Fail to list hosts" for host in hosts: host_name = host['attrs']['name'] if host_name not in self.new_hosts: @@ -113,7 +90,7 @@ def _get_status(self): self.new_hosts[host_name].name = host_name self.new_hosts[host_name].site = self.name try: - self.new_hosts[host_name].status = self.HOST_SEVERITY_CODE_TEXT_MAP.get(host['attrs']['state']) + self.new_hosts[host_name].status = self.STATES_MAPPING['hosts'].get(host['attrs']['state']) except KeyError: self.new_hosts[host_name].status = 'UNKNOWN' if int(host['attrs']['state_type']) > 0: # if state is not SOFT, icinga does not report attempts properly @@ -131,6 +108,7 @@ def _get_status(self): self.new_hosts[host_name].notifications_disabled = not(host['attrs']['enable_notifications']) self.new_hosts[host_name].flapping = host['attrs']['flapping'] self.new_hosts[host_name].acknowledged = host['attrs']['acknowledgement'] + self.new_hosts[host_name].scheduled_downtime = bool(host['attrs']['downtime_depth']) self.new_hosts[host_name].status_type = {0: "soft", 1: "hard"}[host['attrs']['state_type']] del host_name del hosts @@ -150,7 +128,7 @@ def _get_status(self): new_service.host = service['attrs']['host_name'] new_service.name = service['attrs']['name'] try: - new_service.status = self.SERVICE_SEVERITY_CODE_TEXT_MAP.get(service['attrs']['state']) + new_service.status = self.STATES_MAPPING['services'].get(service['attrs']['state']) except KeyError: new_service.status = 'UNKNOWN' if int(service['attrs']['state_type']) > 0: # if state is not SOFT, icinga does not report attempts properly @@ -171,6 +149,7 @@ def _get_status(self): new_service.notifications_disabled = not(service['attrs']['enable_notifications']) new_service.flapping = service['attrs']['flapping'] new_service.acknowledged = service['attrs']['acknowledgement'] + new_service.scheduled_downtime = bool(service['attrs']['downtime_depth']) new_service.status_type = {0: "soft", 1: "hard"}[service['attrs']['state_type']] self._insert_service_to_hosts(new_service) del services @@ -185,52 +164,186 @@ def _get_status(self): # dummy return in case all is OK return Result() + def _list_objects(self, object_type, filter): + """List objects""" + result = self.FetchURL( + f'{self.url}/objects/{object_type}?{urllib.parse.urlencode({"filter": filter})}', + giveback='raw' + ) + # purify JSON result of unnecessary control sequence \n + jsonraw, error, status_code = copy.deepcopy(result.result.replace('\n', '')),\ + copy.deepcopy(result.error),\ + result.status_code + + # check if any error occured + errors_occured = self.check_for_error(jsonraw, error, status_code) + # if there are errors return them + if errors_occured is not None: + return(errors_occured) + + jsondict = json.loads(jsonraw) + return jsondict.get('results', []) + def _get_service_events(self): """ Suck faulty service events from API """ - if self.iapi is None: - self._ilogin() - filters = 'service.state!=ServiceOK' - events = self.iapi.objects.list('Service', filters=filters) - return events + return self._list_objects('services', 'service.state!=ServiceOK') def _get_host_events(self): """ Suck faulty hosts from API """ - if self.iapi is None: - self._ilogin() - filters = 'host.state!=0' - events = self.iapi.objects.list('Host', filters=filters) - return events + return self._list_objects('hosts', 'host.state!=0') + + + def _trigger_action(self, action, **data): + """Trigger on action using Icinga2 API""" + action_data = {k: v for k, v in data.items() if v is not None} + self.Debug(server=self.get_name(), debug=f"Trigger action {action} with data={action_data}") + try: + response = self.session.post( + f'{self.url}/actions/{action}', + headers={'Accept': 'application/json'}, + json=action_data, + ) + self.Debug( + server=self.get_name(), + debug=f"API return on triggering action {action} (status={response.status_code}): " + f"{response.text}" + ) + if 200 <= response.status_code <= 299: + return True + self.Error(f"Fail to trigger action {action}: {response.json().get('status', 'Unknown error')}") + except IOError as err: + log.exception("Fail to trigger action %s with data %s", action, data) + self.Error(f"Fail to trigger action {action}: {err}") def _set_recheck(self, host, service): """ Please check again Icinga! """ - pass + self._trigger_action( + "reschedule-check", + type="Service" if service else "Host", + filter=( + 'host.name == host_name && service.name == service_name' + if service else 'host.name == host_name' + ), + filter_vars=( + {'host_name': host, 'service_name': service} + if service else {'host_name': host} + ), + ) + + # Overwrite function from generic server to add expire_time value + def set_acknowledge(self, info_dict): + ''' + different monitors might have different implementations of _set_acknowledge + ''' + if info_dict['acknowledge_all_services'] is True: + all_services = info_dict['all_services'] + else: + all_services = [] + + # Make sure expire_time is set + #if not info_dict['expire_time']: + # info_dict['expire_time'] = None + + self._set_acknowledge(info_dict['host'], + info_dict['service'], + info_dict['author'], + info_dict['comment'], + info_dict['sticky'], + info_dict['notify'], + info_dict['persistent'], + all_services, + info_dict['expire_time']) def _set_acknowledge(self, host, service, author, comment, sticky, - notify, persistent, all_services=None): + notify, persistent, all_services=None, expire_time=None): ''' Send acknowledge to monitor server ''' - pass + self._trigger_action( + "acknowledge-problem", + type="Service" if service else "Host", + filter=( + 'host.name == host_name && service.name == service_name' + if service else 'host.name == host_name' + ), + filter_vars=( + {'host_name': host, 'service_name': service} + if service else {'host_name': host} + ), + author=author, + comment=comment, + sticky=sticky, + notify=notify, + expiry=( + dateutil.parser.parse(expire_time).timestamp() + if expire_time else None + ), + persistent=persistent, + ) + + if len(all_services) > 0: + for s in all_services: + # cheap, recursive solution... + self._set_acknowledge(host, s, author, comment, sticky, notify, persistent, [], expire_time) def _set_submit_check_result(self, host, service, state, comment, check_output, performance_data): ''' Submit check results ''' - pass + self._trigger_action( + "process-check-result", + type="Service" if service else "Host", + filter=( + 'host.name == host_name && service.name == service_name' + if service else 'host.name == host_name' + ), + filter_vars=( + {'host_name': host, 'service_name': service} + if service else {'host_name': host} + ), + exit_status=self.STATES_MAPPING_REV['services' if service else 'hosts'][state.upper()], + plugin_output=check_output, + performance_data=performance_data, + ) def _set_downtime(self, host, service, author, comment, fixed, start_time, end_time, hours, minutes): """ Submit downtime """ - pass - - -0 + self._trigger_action( + "schedule-downtime", + type="Service" if service else "Host", + filter=( + 'host.name == host_name && service.name == service_name' + if service else 'host.name == host_name' + ), + filter_vars=( + {'host_name': host, 'service_name': service} + if service else {'host_name': host} + ), + author=author, + comment=comment, + start_time=( + datetime.datetime.now().timestamp() + if start_time == '' or start_time == 'n/a' + else dateutil.parser.parse(start_time).timestamp() + ), + end_time=( + (datetime.datetime.now() + datetime.timedelta(hours=hours, minutes=minutes)).timestamp() + if end_time == '' or end_time == 'n/a' + else dateutil.parser.parse(end_time).timestamp() + ), + fixed=fixed, + duration=( + (hours * 3600 + minutes * 60) + if not fixed else None + ), + ) diff --git a/Nagstamon/Servers/__init__.py b/Nagstamon/Servers/__init__.py index d2657af4b..587a1dab8 100644 --- a/Nagstamon/Servers/__init__.py +++ b/Nagstamon/Servers/__init__.py @@ -25,13 +25,6 @@ from collections import OrderedDict -# we need this for uncommon modules -try: - import icinga2api - icinga2api_is_available = True -except: - icinga2api_is_available = False - # load all existing server types from Nagstamon.Servers.Nagios import NagiosServer from Nagstamon.Servers.Centreon import CentreonServer @@ -39,6 +32,7 @@ from Nagstamon.Servers.IcingaWeb2 import IcingaWeb2Server from Nagstamon.Servers.IcingaDBWeb import IcingaDBWebServer from Nagstamon.Servers.IcingaDBWebNotifications import IcingaDBWebNotificationsServer +from Nagstamon.Servers.Icinga2API import Icinga2APIServer from Nagstamon.Servers.Multisite import MultisiteServer from Nagstamon.Servers.op5Monitor import Op5MonitorServer from Nagstamon.Servers.Opsview import OpsviewServer @@ -59,9 +53,6 @@ from Nagstamon.Helpers import STATES -# conditional load if module is available -if icinga2api_is_available is True: - from Nagstamon.Servers.Icinga2API import Icinga2APIServer # dictionary for servers servers = OrderedDict() @@ -247,6 +238,7 @@ def create_server(server=None): IcingaDBWebServer, IcingaDBWebNotificationsServer, IcingaWeb2Server, + Icinga2APIServer, LivestatusServer, Monitos3Server, Monitos4xServer, @@ -262,9 +254,6 @@ def create_server(server=None): ZabbixProblemBasedServer, ZabbixServer, ZenossServer] -# we use these servers conditionally if modules are available only -if icinga2api_is_available is True: - servers_list.append(Icinga2APIServer) for server in servers_list: register_server(server) diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index 406ee5adc..ba0b52d39 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -1,6 +1,5 @@ arrow beautifulsoup4 -icinga2api keyring lxml pip-system-certs From f9e54414196981bc566a4e66912024214c75d4c4 Mon Sep 17 00:00:00 2001 From: Jinho Kim <54465744+jkim2492@users.noreply.github.com> Date: Sat, 27 Jul 2024 04:49:37 -0400 Subject: [PATCH 774/884] Fix downtime and recheck functions for CheckMK 2.3 (#1047) * Add function _get_csrf_token to Multisite.py * Add function _omd_get_version to Multisite.py * Add function _omd_set_recheck to Multisite.py * Add function _omd_set_downtime to Multisite.py * Add function overrides for Checkmk 2.3+ --------- Co-authored-by: Jinho Kim <jkim@xelerance.com> --- Nagstamon/Servers/Multisite.py | 91 ++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/Nagstamon/Servers/Multisite.py b/Nagstamon/Servers/Multisite.py index 4331dacc8..ac6405c36 100644 --- a/Nagstamon/Servers/Multisite.py +++ b/Nagstamon/Servers/Multisite.py @@ -25,6 +25,8 @@ import time import copy import html +from datetime import datetime +from zoneinfo import ZoneInfo from Nagstamon.Objects import (GenericHost, GenericService, @@ -103,6 +105,10 @@ def init_HTTP(self): 'api_svcprob_act': self.monitor_url + '/view.py?_transid=-1&_do_actions=yes&_do_confirm=Yes!&view_name=svcproblems&filled_in=actions&lang=', 'human_events': self.monitor_url + '/index.py?%s' % urllib.parse.urlencode({'start_url': 'view.py?view_name=events'}), + 'omd_host_downtime': self.monitor_url + '/api/1.0/domain-types/downtime/collections/host', + 'omd_svc_downtime': self.monitor_url + '/api/1.0/domain-types/downtime/collections/service', + 'recheck': self.monitor_url + '/ajax_reschedule.py?_ajaxid=0', + 'omd_version': self.monitor_url + '/api/1.0/version', 'transid': self.monitor_url + '/view.py?actions=yes&filled_in=actions&host=$HOST$&service=$SERVICE$&view_name=service' } @@ -114,6 +120,12 @@ def init_HTTP(self): 'PEND': 'PENDING', } + # Function overrides for Checkmk 2.3+ + version = self._omd_get_version() + if version >= [2, 3]: + self._set_downtime = self._omd_set_downtime + self._set_recheck = self._omd_set_recheck + if self.CookieAuth and not self.refresh_authentication: # get cookie to access Checkmk web interface if 'cookies' in dir(self.session): @@ -526,6 +538,47 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t debug='Invalid start/end date/time given') + def _omd_set_downtime(self, host, service, author, comment, fixed, start_time, end_time, hours, minutes): + """ + _set_downtime function for Checkmk version 2.3+ + """ + try: + # Headers required for Checkmk API + headers = { + "Authorization": f"Bearer {self.username} {self.password}", + "Content-Type": "application/json", + "Accept": "application/json", + } + + # Only timezone aware dates are allowed + iso_start_time = datetime.strptime(start_time, "%Y-%m-%d %H:%M").replace(tzinfo=ZoneInfo('localtime')).isoformat() + iso_end_time = datetime.strptime(end_time, "%Y-%m-%d %H:%M").replace(tzinfo=ZoneInfo('localtime')).isoformat() + # Set parameters for host downtimes + url = self.urls["omd_host_downtime"] + params = { + "start_time": iso_start_time, + "end_time": iso_end_time, + "comment": author == self.username and comment or "%s: %s" % (author, comment), + "downtime_type": "host", + "host_name": host, + } + + # Downtime type is "flexible" if "duration" is set + if fixed == 0: + params["duration"] = hours * 60 + minutes + # Parameter overrides for service downtimes + if service: + url = self.urls["omd_svc_downtime"] + params["downtime_type"] = "service" + params["service_descriptions"] = [service] + + self.session.post(url, headers=headers, json=params) + except: + if conf.debug_mode: + self.Debug(server=self.get_name(), host=host, + debug='Invalid start/end date/time given') + + def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=None): p = { '_acknowledge': 'Acknowledge', @@ -550,6 +603,21 @@ def _set_recheck(self, host, service): self._action(self.hosts[host].site, host, service, p) + def _omd_set_recheck(self, host, service): + """ + _set_recheck function for Checkmk version 2.3+ + """ + csrf_token = self._get_csrf_token(host, service) + data = { + "site": self.hosts[host].site, + "host": host, + "service": service, + "wait_svc": service, + "csrf_token": csrf_token, + } + self.FetchURL(self.urls["recheck"], cgi_data=data) + + def recheck_all(self): """ special method for Checkmk as there is one URL for rescheduling all problems to be checked @@ -574,3 +642,26 @@ def _get_transid(self, host, service): transid = self.FetchURL(self.urls['transid'].replace('$HOST$', host).replace('$SERVICE$', service.replace(' ', '+')), 'obj').result.find(attrs={'name' : '_transid'})['value'] return transid + + + def _get_csrf_token(self, host, service): + """ + get csrf token for the session + """ + # since Checkmk 2.0 it seems to be a problem if service is empty so fill it with a definitively existing one + if not service: + service = "PING" + csrf_token = self.FetchURL(self.urls["transid"].replace("$HOST$", host).replace("$SERVICE$", service.replace(" ", "+")), "obj").result.find(attrs={"name": "csrf_token"})["value"] + return csrf_token + + + def _omd_get_version(self): + """ + get version of OMD Checkmk as [major_version, minor_version] + """ + try: + version = [int(v) for v in self.session.get(self.urls["omd_version"]).json()["versions"]["checkmk"].split(".")[:2]] + # If /version api is not supported, return the lowest non-negative pair + except: + version = [0, 0] + return version From 3ce38617038ca93b758ca10f185603563b65afca Mon Sep 17 00:00:00 2001 From: hw-iu <171809906+hw-iu@users.noreply.github.com> Date: Sat, 27 Jul 2024 10:54:16 +0200 Subject: [PATCH 775/884] checkmk updates --- Nagstamon/Config.py | 2 +- Nagstamon/resources/nagstamon.1.gz | Bin 783 -> 783 bytes build/debian/changelog | 4 ++-- build/requirements/windows.txt | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index c38d41fee..9adefce04 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -131,7 +131,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.15-20240506' + VERSION = '3.15-20240727' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2024 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/resources/nagstamon.1.gz b/Nagstamon/resources/nagstamon.1.gz index a3df1331af80cc25875c19fa74cf6c3dd49efa36..3b34db9a77fcffd2284b5ea7d3426ede80ee1ba4 100644 GIT binary patch delta 17 YcmeBY>u2MT@8;lGvcW8EBL_P(04p&Bpa1{> delta 17 YcmeBY>u2MT@8;mhWd4@Ck%OHX04WaybpQYW diff --git a/build/debian/changelog b/build/debian/changelog index 6a66c9b53..0954d41d8 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.15-20240506) unstable; urgency=low +nagstamon (3.15-20240727) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Fri, May 05 2024 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Sat, Jul 27 2024 08:00:00 +0200 nagstamon (3.14.0) stable; urgency=low * New upstream diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index ba0b52d39..3d2bcc3d6 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -5,7 +5,8 @@ lxml pip-system-certs psutil # last stable of the 5 series - InnoSetup does not work with Pyinstaller 6 -pyinstaller==5.13.2 +#pyinstaller==5.13.2 +pyinstaller pypiwin32 pyqt6 pyqt6-qt6 From fc666a3931069ff432d0570c8a75a6e9f9a76176 Mon Sep 17 00:00:00 2001 From: HenriWahl <171809906+hw-iu@users.noreply.github.com> Date: Mon, 29 Jul 2024 10:55:17 +0200 Subject: [PATCH 776/884] added python3-arrow --- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- build/debian/control | 2 +- build/redhat/nagstamon.spec | 1 + 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 9adefce04..59ea68aad 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -131,7 +131,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.15-20240727' + VERSION = '3.15-20240729' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2024 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 0954d41d8..9a44c1c2b 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.15-20240727) unstable; urgency=low +nagstamon (3.15-20240729) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Sat, Jul 27 2024 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Mon, Jul 29 2024 08:00:00 +0200 nagstamon (3.14.0) stable; urgency=low * New upstream diff --git a/build/debian/control b/build/debian/control index f00ef62ee..9ebadbda9 100644 --- a/build/debian/control +++ b/build/debian/control @@ -15,7 +15,7 @@ Architecture: all Depends: ${python3:Depends}, ${misc:Depends}, python3-pkg-resources, python3-bs4, python3-lxml, python3-pyqt5, python3-pyqt5.qtsvg, python3-pyqt5.qtmultimedia, libqt5multimedia5-plugins, python3-requests, python3-requests-kerberos, python3-psutil, python3-dbus.mainloop.pyqt5, - python3-keyring, python3-ewmh + python3-keyring, python3-ewmh, python3-arrow Recommends: python3-secretstorage Description: Nagios status monitor which takes place in systray or on desktop Nagstamon is a Nagios status monitor which takes place in systray or diff --git a/build/redhat/nagstamon.spec b/build/redhat/nagstamon.spec index fce111e0e..dd23d4c89 100644 --- a/build/redhat/nagstamon.spec +++ b/build/redhat/nagstamon.spec @@ -16,6 +16,7 @@ BuildRequires: python3-devel BuildRequires: python3-pyqt6-devel BuildRequires: desktop-file-utils Requires: python3 +Requires: python3-arrow Requires: python3-beautifulsoup4 Requires: python3-cryptography Requires: python3-dateutil From 719c67e0c1d94c6096171acaddf61f9d2dab26eb Mon Sep 17 00:00:00 2001 From: HenriWahl <171809906+hw-iu@users.noreply.github.com> Date: Mon, 29 Jul 2024 11:49:19 +0200 Subject: [PATCH 777/884] added python3-arrow --- setup.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup.py b/setup.py index 8c4b0be5c..312784319 100644 --- a/setup.py +++ b/setup.py @@ -93,6 +93,7 @@ if DIST.lower() == 'fedora' and int(DIST_VERSION) < 36 or \ DIST.lower() == 'rhel' and int(DIST_VERSION) <= 9: bdist_rpm_options = dict(requires='python3 ' + 'python3-arrow ' 'python3-beautifulsoup4 ' 'python3-cryptography ' 'python3-dateutil ' @@ -109,6 +110,7 @@ dist_dir='./build') else: bdist_rpm_options = dict(requires='python3 ' + 'python3-arrow ' 'python3-beautifulsoup4 ' 'python3-cryptography ' 'python3-dateutil ' From b50d3ecc31c6edc75a8989dc145a0e59462ab2c6 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 8 Aug 2024 12:52:48 +0200 Subject: [PATCH 778/884] Windows Python 3.11.9 --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 42d6d0539..56526c470 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -8,7 +8,7 @@ on: - '!*.*.*' env: - python_win_version: 3.11.8 + python_win_version: 3.11.9 repo_dir: nagstamon-jekyll/docs/repo cr_image: ghcr.io/henriwahl/build-nagstamon # to be increased if new updates of build images are necessary From cf8b623ac75c9be3f6e1ecb37ffc63e7b617a3bc Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 8 Aug 2024 14:16:42 +0200 Subject: [PATCH 779/884] Windows Python 3.11.9 debug --- build/build.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build/build.py b/build/build.py index 20b1a7eda..224f962ca 100644 --- a/build/build.py +++ b/build/build.py @@ -147,6 +147,10 @@ def winmain(): zip_archive.write('{0}{1}{2}'.format(root, os.sep, file)) if not DEBUG: + + # try to debug https://github.com/HenriWahl/Nagstamon/actions/runs/10300735350/job/28510780209#step:7:219 + print(r'/Dresources={0}{1}resources'.format(DIR_BUILD_NAGSTAMON, os.sep)) + # execute InnoSetup with many variables set by ISCC.EXE outside .iss file subprocess.call([ISCC, r'/Dsource={0}'.format(DIR_BUILD_NAGSTAMON), From bcb5c12c60af4aea58df85151aa14986c404fa21 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 8 Aug 2024 14:23:26 +0200 Subject: [PATCH 780/884] Windows Python 3.11.9 debug --- .github/workflows/build-release-latest.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 56526c470..2dd591df0 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -266,6 +266,7 @@ jobs: - run: python -m pip install --no-warn-script-location -r build/requirements/windows.txt # pretty hacky but no other idea to avoid gssapi being installed which breaks requests-kerberos - run: python -m pip uninstall -y gssapi requests-gssapi + - run: dir -a D:/a/Nagstamon/Nagstamon/build/dist - run: cd ${{ github.workspace }}/build; python build.py env: PYTHONPATH: ${{ github.workspace }} From f92a77d95a44c6bde58bf74a4905df5e007b42ee Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 8 Aug 2024 14:26:13 +0200 Subject: [PATCH 781/884] Windows Python 3.11.9 debug --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 2dd591df0..8a89a91ea 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -266,7 +266,7 @@ jobs: - run: python -m pip install --no-warn-script-location -r build/requirements/windows.txt # pretty hacky but no other idea to avoid gssapi being installed which breaks requests-kerberos - run: python -m pip uninstall -y gssapi requests-gssapi - - run: dir -a D:/a/Nagstamon/Nagstamon/build/dist + - run: dir -r D:/a/Nagstamon/Nagstamon/build/dist - run: cd ${{ github.workspace }}/build; python build.py env: PYTHONPATH: ${{ github.workspace }} From 15be9317969a64a3403de10c0cef61559c5b8d7a Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 8 Aug 2024 14:35:03 +0200 Subject: [PATCH 782/884] Windows Python 3.11.9 debug --- build/build.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/build.py b/build/build.py index 224f962ca..aa92c16c7 100644 --- a/build/build.py +++ b/build/build.py @@ -90,8 +90,6 @@ def winmain(): else: VERSION_IS = VERSION - print('VERSION_IS:', VERSION_IS) - # old-school formatstrings needed for old Debian build base distro jessie and its old python ISCC = r'{0}{1}Inno Setup 6{1}iscc.exe'.format(os.environ['PROGRAMFILES{0}'.format(ARCH_OPTS[ARCH][2])], os.sep) DIR_BUILD_EXE = '{0}{1}dist{1}Nagstamon'.format(CURRENT_DIR, os.sep, ARCH_OPTS[ARCH][0], PYTHON_VERSION) @@ -119,6 +117,8 @@ def winmain(): '..\\nagstamon.py'], shell=True) + print(subprocess) + if SIGNING: # environment variables will be used by powershell script for signing subprocess.run(['pwsh.exe', './windows/code_signing.ps1', 'build/Nagstamon/Nagstamon.exe']) From 04f4c1cf7e98352249d2fc6431900d8970ad716a Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 8 Aug 2024 14:46:28 +0200 Subject: [PATCH 783/884] Windows Python 3.11.9 debug --- build/build.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/build/build.py b/build/build.py index aa92c16c7..b6ae617b0 100644 --- a/build/build.py +++ b/build/build.py @@ -117,8 +117,6 @@ def winmain(): '..\\nagstamon.py'], shell=True) - print(subprocess) - if SIGNING: # environment variables will be used by powershell script for signing subprocess.run(['pwsh.exe', './windows/code_signing.ps1', 'build/Nagstamon/Nagstamon.exe']) @@ -152,7 +150,7 @@ def winmain(): print(r'/Dresources={0}{1}resources'.format(DIR_BUILD_NAGSTAMON, os.sep)) # execute InnoSetup with many variables set by ISCC.EXE outside .iss file - subprocess.call([ISCC, + result = subprocess.call([ISCC, r'/Dsource={0}'.format(DIR_BUILD_NAGSTAMON), r'/Dversion_is={0}'.format(VERSION_IS), r'/Dversion={0}'.format(VERSION), @@ -162,6 +160,9 @@ def winmain(): r'/O{0}{1}dist'.format(CURRENT_DIR, os.sep), r'{0}{1}windows{1}nagstamon.iss'.format(CURRENT_DIR, os.sep)], shell=True) + + print(result) + if SIGNING: # environment variables will be used by powershell script for signing subprocess.run(['pwsh.exe', '../windows/code_signing.ps1', '*.exe']) From b79f74aebdd63abd3ad86d4e4353ebf1854c36a6 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 8 Aug 2024 14:51:59 +0200 Subject: [PATCH 784/884] Windows Python 3.11.9 debug --- build/build.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/build/build.py b/build/build.py index b6ae617b0..9401cf778 100644 --- a/build/build.py +++ b/build/build.py @@ -158,10 +158,12 @@ def winmain(): r'/Darchs_allowed={0}'.format(ARCH_OPTS[ARCH][3]), r'/Dresources={0}{1}resources'.format(DIR_BUILD_NAGSTAMON, os.sep), r'/O{0}{1}dist'.format(CURRENT_DIR, os.sep), - r'{0}{1}windows{1}nagstamon.iss'.format(CURRENT_DIR, os.sep)], shell=True) + r'{0}{1}windows{1}nagstamon.iss'.format(CURRENT_DIR, os.sep)], + shell=True) - print(result) + if result > 0: + sys.exit(result) if SIGNING: # environment variables will be used by powershell script for signing From 5982f426ef858603a923a006fc276896b2703f6b Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 8 Aug 2024 15:03:04 +0200 Subject: [PATCH 785/884] Windows Python 3.11.9 debug --- build/build.py | 1 - build/windows/nagstamon.iss | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/build/build.py b/build/build.py index 9401cf778..5709a5dae 100644 --- a/build/build.py +++ b/build/build.py @@ -161,7 +161,6 @@ def winmain(): r'{0}{1}windows{1}nagstamon.iss'.format(CURRENT_DIR, os.sep)], shell=True) - if result > 0: sys.exit(result) diff --git a/build/windows/nagstamon.iss b/build/windows/nagstamon.iss index a6836e098..7d4e70e05 100644 --- a/build/windows/nagstamon.iss +++ b/build/windows/nagstamon.iss @@ -7,7 +7,7 @@ DefaultDirName={commonpf}\Nagstamon DefaultGroupName=Nagstamon AlwaysUsePersonalGroup=false ShowLanguageDialog=no -SetupIconFile={#resources}\nagstamon.ico +SetupIconFile=resources\nagstamon.ico UsePreviousGroup=false OutputBaseFilename=Nagstamon-{#version}-win{#arch}_setup UninstallDisplayIcon={app}\resources\nagstamon.ico From 3b640021428eddb09c435232f1b5520213920269 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 8 Aug 2024 15:08:09 +0200 Subject: [PATCH 786/884] Windows Python 3.11.9 debug --- .github/workflows/build-release-latest.yml | 2 +- build/build.py | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 8a89a91ea..28978d05d 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -266,7 +266,7 @@ jobs: - run: python -m pip install --no-warn-script-location -r build/requirements/windows.txt # pretty hacky but no other idea to avoid gssapi being installed which breaks requests-kerberos - run: python -m pip uninstall -y gssapi requests-gssapi - - run: dir -r D:/a/Nagstamon/Nagstamon/build/dist + - run: cd ${{ github.workspace }}; ls -r * - run: cd ${{ github.workspace }}/build; python build.py env: PYTHONPATH: ${{ github.workspace }} diff --git a/build/build.py b/build/build.py index 5709a5dae..ebd50b50a 100644 --- a/build/build.py +++ b/build/build.py @@ -145,10 +145,6 @@ def winmain(): zip_archive.write('{0}{1}{2}'.format(root, os.sep, file)) if not DEBUG: - - # try to debug https://github.com/HenriWahl/Nagstamon/actions/runs/10300735350/job/28510780209#step:7:219 - print(r'/Dresources={0}{1}resources'.format(DIR_BUILD_NAGSTAMON, os.sep)) - # execute InnoSetup with many variables set by ISCC.EXE outside .iss file result = subprocess.call([ISCC, r'/Dsource={0}'.format(DIR_BUILD_NAGSTAMON), @@ -160,7 +156,6 @@ def winmain(): r'/O{0}{1}dist'.format(CURRENT_DIR, os.sep), r'{0}{1}windows{1}nagstamon.iss'.format(CURRENT_DIR, os.sep)], shell=True) - if result > 0: sys.exit(result) From 7c5ecc917fa4e71694cb7726cb76fa315c97dcb4 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 8 Aug 2024 15:08:48 +0200 Subject: [PATCH 787/884] Windows Python 3.11.9 debug --- .github/workflows/build-release-latest.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 28978d05d..282967b9b 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -266,6 +266,7 @@ jobs: - run: python -m pip install --no-warn-script-location -r build/requirements/windows.txt # pretty hacky but no other idea to avoid gssapi being installed which breaks requests-kerberos - run: python -m pip uninstall -y gssapi requests-gssapi + - run: cd ${{ github.workspace }}; pwd - run: cd ${{ github.workspace }}; ls -r * - run: cd ${{ github.workspace }}/build; python build.py env: From 2240d9da919b7ddc8d066be5bf1b205f8b5b53c0 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 8 Aug 2024 15:13:41 +0200 Subject: [PATCH 788/884] Windows Python 3.11.9 debug --- build/windows/nagstamon.iss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/windows/nagstamon.iss b/build/windows/nagstamon.iss index 7d4e70e05..e41a5a6c6 100644 --- a/build/windows/nagstamon.iss +++ b/build/windows/nagstamon.iss @@ -7,7 +7,7 @@ DefaultDirName={commonpf}\Nagstamon DefaultGroupName=Nagstamon AlwaysUsePersonalGroup=false ShowLanguageDialog=no -SetupIconFile=resources\nagstamon.ico +SetupIconFile=..\..\Nagstamon\resources\nagstamon.ico UsePreviousGroup=false OutputBaseFilename=Nagstamon-{#version}-win{#arch}_setup UninstallDisplayIcon={app}\resources\nagstamon.ico From a9f95c6f7d16602f90738c6183d6c4ad1889a682 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 8 Aug 2024 15:22:19 +0200 Subject: [PATCH 789/884] Windows Python 3.11.9 debug --- build/build.py | 7 +++++++ build/windows/nagstamon.iss | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/build/build.py b/build/build.py index ebd50b50a..756887bea 100644 --- a/build/build.py +++ b/build/build.py @@ -32,6 +32,11 @@ NAGSTAMON_DIR = os.path.normpath('{0}{1}..{1}'.format(CURRENT_DIR, os.sep)) sys.path.insert(1, NAGSTAMON_DIR) +print(CURRENT_DIR) +print(NAGSTAMON_DIR) + +sys.exit(0) + SCRIPTS_DIR = '{0}{1}scripts-{2}.{3}'.format(CURRENT_DIR, os.sep, sys.version_info.major, sys.version_info.minor) # has to be imported here after NAGSTAMON_DIR was wadded to sys.path @@ -145,6 +150,8 @@ def winmain(): zip_archive.write('{0}{1}{2}'.format(root, os.sep, file)) if not DEBUG: + shutil.copyfile(f'{NAGSTAMON_DIR}{os.sep}Nagstamon{os.sep}resources{os.sep}nagstamon.ico', + f'{CURRENT_DIR}{os.sep}windows') # execute InnoSetup with many variables set by ISCC.EXE outside .iss file result = subprocess.call([ISCC, r'/Dsource={0}'.format(DIR_BUILD_NAGSTAMON), diff --git a/build/windows/nagstamon.iss b/build/windows/nagstamon.iss index e41a5a6c6..05ddb3f31 100644 --- a/build/windows/nagstamon.iss +++ b/build/windows/nagstamon.iss @@ -7,7 +7,7 @@ DefaultDirName={commonpf}\Nagstamon DefaultGroupName=Nagstamon AlwaysUsePersonalGroup=false ShowLanguageDialog=no -SetupIconFile=..\..\Nagstamon\resources\nagstamon.ico +SetupIconFile=nagstamon.ico UsePreviousGroup=false OutputBaseFilename=Nagstamon-{#version}-win{#arch}_setup UninstallDisplayIcon={app}\resources\nagstamon.ico From 07ae1b25ef76337304533ce561c07811e1737f18 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 8 Aug 2024 15:24:27 +0200 Subject: [PATCH 790/884] Windows Python 3.11.9 debug --- build/build.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/build/build.py b/build/build.py index 756887bea..b9b416f92 100644 --- a/build/build.py +++ b/build/build.py @@ -32,11 +32,6 @@ NAGSTAMON_DIR = os.path.normpath('{0}{1}..{1}'.format(CURRENT_DIR, os.sep)) sys.path.insert(1, NAGSTAMON_DIR) -print(CURRENT_DIR) -print(NAGSTAMON_DIR) - -sys.exit(0) - SCRIPTS_DIR = '{0}{1}scripts-{2}.{3}'.format(CURRENT_DIR, os.sep, sys.version_info.major, sys.version_info.minor) # has to be imported here after NAGSTAMON_DIR was wadded to sys.path From 01767d89bd5bf2d2235e0008e0ca857d55cec004 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 8 Aug 2024 15:28:08 +0200 Subject: [PATCH 791/884] Windows Python 3.11.9 debug --- build/build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/build.py b/build/build.py index b9b416f92..672c004fb 100644 --- a/build/build.py +++ b/build/build.py @@ -146,7 +146,7 @@ def winmain(): if not DEBUG: shutil.copyfile(f'{NAGSTAMON_DIR}{os.sep}Nagstamon{os.sep}resources{os.sep}nagstamon.ico', - f'{CURRENT_DIR}{os.sep}windows') + f'{CURRENT_DIR}{os.sep}windows{os.sep}nagstamon.ico') # execute InnoSetup with many variables set by ISCC.EXE outside .iss file result = subprocess.call([ISCC, r'/Dsource={0}'.format(DIR_BUILD_NAGSTAMON), From 7bb57a29f0fdf00a9735e1ca2362aa25389537d8 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 8 Aug 2024 15:35:56 +0200 Subject: [PATCH 792/884] Windows Python 3.11.9 debug --- .github/workflows/build-release-latest.yml | 1 + build/windows/nagstamon.iss | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 282967b9b..517870f2f 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -268,6 +268,7 @@ jobs: - run: python -m pip uninstall -y gssapi requests-gssapi - run: cd ${{ github.workspace }}; pwd - run: cd ${{ github.workspace }}; ls -r * + - run: cat D:\a\Nagstamon\Nagstamon\build\windows\nagstamon.iss - run: cd ${{ github.workspace }}/build; python build.py env: PYTHONPATH: ${{ github.workspace }} diff --git a/build/windows/nagstamon.iss b/build/windows/nagstamon.iss index 05ddb3f31..0f5ceb0a2 100644 --- a/build/windows/nagstamon.iss +++ b/build/windows/nagstamon.iss @@ -7,7 +7,7 @@ DefaultDirName={commonpf}\Nagstamon DefaultGroupName=Nagstamon AlwaysUsePersonalGroup=false ShowLanguageDialog=no -SetupIconFile=nagstamon.ico +SetupIconFile=windows\nagstamon.ico UsePreviousGroup=false OutputBaseFilename=Nagstamon-{#version}-win{#arch}_setup UninstallDisplayIcon={app}\resources\nagstamon.ico From 5f5891547c58f41f2e789d29caefae114427a02c Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 8 Aug 2024 15:50:18 +0200 Subject: [PATCH 793/884] Windows Python 3.11.9 debug --- build/build.py | 2 +- build/windows/nagstamon.iss | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/build.py b/build/build.py index 672c004fb..e62500b27 100644 --- a/build/build.py +++ b/build/build.py @@ -146,7 +146,7 @@ def winmain(): if not DEBUG: shutil.copyfile(f'{NAGSTAMON_DIR}{os.sep}Nagstamon{os.sep}resources{os.sep}nagstamon.ico', - f'{CURRENT_DIR}{os.sep}windows{os.sep}nagstamon.ico') + f'{CURRENT_DIR}{os.sep}nagstamon.ico') # execute InnoSetup with many variables set by ISCC.EXE outside .iss file result = subprocess.call([ISCC, r'/Dsource={0}'.format(DIR_BUILD_NAGSTAMON), diff --git a/build/windows/nagstamon.iss b/build/windows/nagstamon.iss index 0f5ceb0a2..05ddb3f31 100644 --- a/build/windows/nagstamon.iss +++ b/build/windows/nagstamon.iss @@ -7,7 +7,7 @@ DefaultDirName={commonpf}\Nagstamon DefaultGroupName=Nagstamon AlwaysUsePersonalGroup=false ShowLanguageDialog=no -SetupIconFile=windows\nagstamon.ico +SetupIconFile=nagstamon.ico UsePreviousGroup=false OutputBaseFilename=Nagstamon-{#version}-win{#arch}_setup UninstallDisplayIcon={app}\resources\nagstamon.ico From 58acccab99577b26b426afb1128bef4ed24c5bf8 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 8 Aug 2024 15:55:21 +0200 Subject: [PATCH 794/884] Windows Python 3.11.9 debug --- build/windows/nagstamon.iss | 1 + 1 file changed, 1 insertion(+) diff --git a/build/windows/nagstamon.iss b/build/windows/nagstamon.iss index 05ddb3f31..6a09b6396 100644 --- a/build/windows/nagstamon.iss +++ b/build/windows/nagstamon.iss @@ -25,6 +25,7 @@ SourceDir={#source} ArchitecturesAllowed={#archs_allowed} ArchitecturesInstallIn64BitMode=x64 CloseApplications=no +WizardStyle=modern [Icons] Name: {group}\Nagstamon; Filename: {app}\nagstamon.exe; WorkingDir: {app}; IconFilename: {app}\resources\nagstamon.ico; IconIndex: 0 Name: {commonstartup}\Nagstamon; Filename: {app}\nagstamon.exe; WorkingDir: {app}; IconFilename: {app}\resources\nagstamon.ico; IconIndex: 0 From 38ec27406f3d11311eba2a1a5a5f374a242e66e7 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 8 Aug 2024 16:14:06 +0200 Subject: [PATCH 795/884] Windows Python 3.11.9 debug --- build/build.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build/build.py b/build/build.py index e62500b27..8c44f0b88 100644 --- a/build/build.py +++ b/build/build.py @@ -145,8 +145,11 @@ def winmain(): zip_archive.write('{0}{1}{2}'.format(root, os.sep, file)) if not DEBUG: + print(NAGSTAMON_DIR) + print(CURRENT_DIR) shutil.copyfile(f'{NAGSTAMON_DIR}{os.sep}Nagstamon{os.sep}resources{os.sep}nagstamon.ico', f'{CURRENT_DIR}{os.sep}nagstamon.ico') + # execute InnoSetup with many variables set by ISCC.EXE outside .iss file result = subprocess.call([ISCC, r'/Dsource={0}'.format(DIR_BUILD_NAGSTAMON), From 945a3e93a46e875716ae245d21fae5afbdd2d880 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 8 Aug 2024 16:41:18 +0200 Subject: [PATCH 796/884] Windows Python 3.11.9 debug --- build/build.py | 5 ----- build/windows/nagstamon.iss | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/build/build.py b/build/build.py index 8c44f0b88..ebd50b50a 100644 --- a/build/build.py +++ b/build/build.py @@ -145,11 +145,6 @@ def winmain(): zip_archive.write('{0}{1}{2}'.format(root, os.sep, file)) if not DEBUG: - print(NAGSTAMON_DIR) - print(CURRENT_DIR) - shutil.copyfile(f'{NAGSTAMON_DIR}{os.sep}Nagstamon{os.sep}resources{os.sep}nagstamon.ico', - f'{CURRENT_DIR}{os.sep}nagstamon.ico') - # execute InnoSetup with many variables set by ISCC.EXE outside .iss file result = subprocess.call([ISCC, r'/Dsource={0}'.format(DIR_BUILD_NAGSTAMON), diff --git a/build/windows/nagstamon.iss b/build/windows/nagstamon.iss index 6a09b6396..7d23f5e7f 100644 --- a/build/windows/nagstamon.iss +++ b/build/windows/nagstamon.iss @@ -7,7 +7,7 @@ DefaultDirName={commonpf}\Nagstamon DefaultGroupName=Nagstamon AlwaysUsePersonalGroup=false ShowLanguageDialog=no -SetupIconFile=nagstamon.ico +SetupIconFile={#resources}\nagstamon.ico UsePreviousGroup=false OutputBaseFilename=Nagstamon-{#version}-win{#arch}_setup UninstallDisplayIcon={app}\resources\nagstamon.ico From d9c8b9c2b1ddfe673dad9287309ad3f4689d40ed Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 8 Aug 2024 16:49:21 +0200 Subject: [PATCH 797/884] Windows Python 3.11.9 debug --- build/build.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build/build.py b/build/build.py index ebd50b50a..d4ffc9301 100644 --- a/build/build.py +++ b/build/build.py @@ -145,6 +145,9 @@ def winmain(): zip_archive.write('{0}{1}{2}'.format(root, os.sep, file)) if not DEBUG: + shutil.copyfile(f'{NAGSTAMON_DIR}{os.sep}Nagstamon{os.sep}resources{os.sep}nagstamon.ico', + f'{DIR_BUILD_NAGSTAMON}{os.sep}nagstamon.ico') + # execute InnoSetup with many variables set by ISCC.EXE outside .iss file result = subprocess.call([ISCC, r'/Dsource={0}'.format(DIR_BUILD_NAGSTAMON), From e9d241edaf9168ae843f216787562c3be5b81560 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 8 Aug 2024 16:53:16 +0200 Subject: [PATCH 798/884] Windows Python 3.11.9 debug --- build/windows/nagstamon.iss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/windows/nagstamon.iss b/build/windows/nagstamon.iss index 7d23f5e7f..6a09b6396 100644 --- a/build/windows/nagstamon.iss +++ b/build/windows/nagstamon.iss @@ -7,7 +7,7 @@ DefaultDirName={commonpf}\Nagstamon DefaultGroupName=Nagstamon AlwaysUsePersonalGroup=false ShowLanguageDialog=no -SetupIconFile={#resources}\nagstamon.ico +SetupIconFile=nagstamon.ico UsePreviousGroup=false OutputBaseFilename=Nagstamon-{#version}-win{#arch}_setup UninstallDisplayIcon={app}\resources\nagstamon.ico From 3fc9b70376fc71f4116cc29e450177529f9cccb4 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 8 Aug 2024 16:58:55 +0200 Subject: [PATCH 799/884] Windows Python 3.11.9 debug --- .github/workflows/build-release-latest.yml | 3 --- build/build.py | 4 +++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 517870f2f..56526c470 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -266,9 +266,6 @@ jobs: - run: python -m pip install --no-warn-script-location -r build/requirements/windows.txt # pretty hacky but no other idea to avoid gssapi being installed which breaks requests-kerberos - run: python -m pip uninstall -y gssapi requests-gssapi - - run: cd ${{ github.workspace }}; pwd - - run: cd ${{ github.workspace }}; ls -r * - - run: cat D:\a\Nagstamon\Nagstamon\build\windows\nagstamon.iss - run: cd ${{ github.workspace }}/build; python build.py env: PYTHONPATH: ${{ github.workspace }} diff --git a/build/build.py b/build/build.py index d4ffc9301..01e162208 100644 --- a/build/build.py +++ b/build/build.py @@ -145,6 +145,9 @@ def winmain(): zip_archive.write('{0}{1}{2}'.format(root, os.sep, file)) if not DEBUG: + # for some reason out of nowhere the old path SetupIconFile={#resources}\nagstamon.ico + # does not work anymore, so the icon gets copied into the path referenced to + # as SourceDir in the ISS file shutil.copyfile(f'{NAGSTAMON_DIR}{os.sep}Nagstamon{os.sep}resources{os.sep}nagstamon.ico', f'{DIR_BUILD_NAGSTAMON}{os.sep}nagstamon.ico') @@ -155,7 +158,6 @@ def winmain(): r'/Dversion={0}'.format(VERSION), r'/Darch={0}'.format(ARCH), r'/Darchs_allowed={0}'.format(ARCH_OPTS[ARCH][3]), - r'/Dresources={0}{1}resources'.format(DIR_BUILD_NAGSTAMON, os.sep), r'/O{0}{1}dist'.format(CURRENT_DIR, os.sep), r'{0}{1}windows{1}nagstamon.iss'.format(CURRENT_DIR, os.sep)], shell=True) From ac986c689f201e69a6473df5a9ce02fedf222008 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 8 Aug 2024 17:07:23 +0200 Subject: [PATCH 800/884] dciborow/action-github-releases@ --- .github/workflows/build-release-latest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 56526c470..09ee948b7 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -436,9 +436,9 @@ jobs: merge-multiple: true - run: cd artifact && md5sum *agstamon* > md5sums.txt - run: cd artifact && sha256sum *agstamon* > sha256sums.txt - - uses: marvinpinto/action-automatic-releases@latest + #- uses: marvinpinto/action-automatic-releases@latest # the dciborow action is outdated as well :-( - #- uses: dciborow/action-github-releases@v1.0.1 + - uses: dciborow/action-github-releases@v1.0.1 with: repo_token: "${{ secrets.GITHUB_TOKEN }}" automatic_release_tag: "latest" From 7a50c55af873209f2d41f95e55f2c5f7a78b326b Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Wed, 14 Aug 2024 12:43:39 +0200 Subject: [PATCH 801/884] debug macos processor --- .github/workflows/build-release-latest.yml | 22 ++++++++++++++++++++-- build/build.py | 2 ++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 09ee948b7..d7eafe90c 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -208,11 +208,29 @@ jobs: if-no-files-found: error name: ${{ github.job }} - macos: + macos-intel: runs-on: macos-12 needs: test steps: - uses: actions/checkout@v4 + - run: python -c "import platform; print(platform.processor())" + - run: pip3 install --no-warn-script-location -r build/requirements/macos.txt + - run: cd ${{ github.workspace }}/build; python3 build.py + env: + PYTHONPATH: ${{ github.workspace }} + - uses: actions/upload-artifact@v4 + with: + path: build/*.dmg + retention-days: 1 + if-no-files-found: error + name: ${{ github.job }} + + macos-arm: + runs-on: macos-14 + needs: test + steps: + - uses: actions/checkout@v4 + - run: python -c "import platform; print(platform.processor())" - run: pip3 install --no-warn-script-location -r build/requirements/macos.txt - run: cd ${{ github.workspace }}/build; python3 build.py env: @@ -310,7 +328,7 @@ jobs: repo-debian: runs-on: ubuntu-latest # try to avoid race condition and start uploading only after the last install package has been build - needs: [debian, fedora-37, fedora-38, fedora-39, fedora-40, fedora-41, rhel-9, macos, windows-32, windows-64, windows-64-debug] + needs: [debian, fedora-37, fedora-38, fedora-39, fedora-40, fedora-41, rhel-9, macos-intel, macos-arm, windows-32, windows-64, windows-64-debug] env: family: debian steps: diff --git a/build/build.py b/build/build.py index 01e162208..75be8b7ee 100644 --- a/build/build.py +++ b/build/build.py @@ -47,6 +47,8 @@ DIST_NAME, DIST_VERSION, DIST_ID = get_distro() +#MACOS_PROCESSOR = platform.processor() + # depending on debug build or not a console window will be shown or not if len(sys.argv) > 1 and sys.argv[1] == 'debug': DEBUG = True From de93fd853dc4196a897a291b2b49a2aa5ee0e511 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Wed, 14 Aug 2024 12:51:11 +0200 Subject: [PATCH 802/884] Apple Silicon --- .github/workflows/build-release-latest.yml | 4 +--- build/build.py | 20 +++++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index d7eafe90c..54d2238f0 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -213,7 +213,6 @@ jobs: needs: test steps: - uses: actions/checkout@v4 - - run: python -c "import platform; print(platform.processor())" - run: pip3 install --no-warn-script-location -r build/requirements/macos.txt - run: cd ${{ github.workspace }}/build; python3 build.py env: @@ -230,8 +229,7 @@ jobs: needs: test steps: - uses: actions/checkout@v4 - - run: python -c "import platform; print(platform.processor())" - - run: pip3 install --no-warn-script-location -r build/requirements/macos.txt + - run: pip3 install --no-warn-script-location --break-system-packages -r build/requirements/macos.txt - run: cd ${{ github.workspace }}/build; python3 build.py env: PYTHONPATH: ${{ github.workspace }} diff --git a/build/build.py b/build/build.py index 75be8b7ee..c439813a4 100644 --- a/build/build.py +++ b/build/build.py @@ -39,15 +39,17 @@ from Nagstamon.Helpers import get_distro VERSION = AppInfo.VERSION -ARCH = platform.architecture()[0][0:2] -ARCH_OPTS = {'32': ('win32', 'win32', '', 'x86'), +ARCH_WINDOWS = platform.architecture()[0][0:2] +ARCH_WINDOWS_OPTS = {'32': ('win32', 'win32', '', 'x86'), '64': ('win-amd64', 'amd64', '(X86)', 'x64')} +ARCH_MACOS = platform.processor() +ARCH_MACOS_NAMES = {'i386': 'Intel', 'arm': 'Apple_Silicon'} + PYTHON_VERSION = '{0}.{1}'.format(sys.version_info[0], sys.version_info[1]) DIST_NAME, DIST_VERSION, DIST_ID = get_distro() -#MACOS_PROCESSOR = platform.processor() # depending on debug build or not a console window will be shown or not if len(sys.argv) > 1 and sys.argv[1] == 'debug': @@ -93,9 +95,9 @@ def winmain(): VERSION_IS = VERSION # old-school formatstrings needed for old Debian build base distro jessie and its old python - ISCC = r'{0}{1}Inno Setup 6{1}iscc.exe'.format(os.environ['PROGRAMFILES{0}'.format(ARCH_OPTS[ARCH][2])], os.sep) - DIR_BUILD_EXE = '{0}{1}dist{1}Nagstamon'.format(CURRENT_DIR, os.sep, ARCH_OPTS[ARCH][0], PYTHON_VERSION) - DIR_BUILD_NAGSTAMON = '{0}{1}dist{1}Nagstamon-{2}-win{3}{4}'.format(CURRENT_DIR, os.sep, VERSION, ARCH, + ISCC = r'{0}{1}Inno Setup 6{1}iscc.exe'.format(os.environ['PROGRAMFILES{0}'.format(ARCH_WINDOWS_OPTS[ARCH_WINDOWS][2])], os.sep) + DIR_BUILD_EXE = '{0}{1}dist{1}Nagstamon'.format(CURRENT_DIR, os.sep, ARCH_WINDOWS_OPTS[ARCH_WINDOWS][0], PYTHON_VERSION) + DIR_BUILD_NAGSTAMON = '{0}{1}dist{1}Nagstamon-{2}-win{3}{4}'.format(CURRENT_DIR, os.sep, VERSION, ARCH_WINDOWS, FILENAME_SUFFIX) FILE_ZIP = '{0}.zip'.format(DIR_BUILD_NAGSTAMON) @@ -158,8 +160,8 @@ def winmain(): r'/Dsource={0}'.format(DIR_BUILD_NAGSTAMON), r'/Dversion_is={0}'.format(VERSION_IS), r'/Dversion={0}'.format(VERSION), - r'/Darch={0}'.format(ARCH), - r'/Darchs_allowed={0}'.format(ARCH_OPTS[ARCH][3]), + r'/Darch={0}'.format(ARCH_WINDOWS), + r'/Darchs_allowed={0}'.format(ARCH_WINDOWS_OPTS[ARCH_WINDOWS][3]), r'/O{0}{1}dist'.format(CURRENT_DIR, os.sep), r'{0}{1}windows{1}nagstamon.iss'.format(CURRENT_DIR, os.sep)], shell=True) @@ -197,7 +199,7 @@ def macmain(): # Compress DMG subprocess.call(['hdiutil convert "Nagstamon {0} uncompressed".dmg ' - '-format UDZO -imagekey zlib-level=9 -o "Nagstamon {0}.dmg"'.format(VERSION)], shell=True) + f'-format UDZO -imagekey zlib-level=9 -o "Nagstamon {VERSION} {ARCH_MACOS_NAMES[ARCH_MACOS]}.dmg"'], shell=True) # Delete uncompressed DMG file as it is no longer needed os.unlink('Nagstamon {0} uncompressed.dmg'.format(VERSION)) From e43dfdf976e39cdfe0b37f8e7852ad5f0d997731 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Wed, 14 Aug 2024 12:58:03 +0200 Subject: [PATCH 803/884] Apple Silicon --- .github/workflows/build-release-latest.yml | 2 ++ build/build.py | 11 +++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 54d2238f0..d93e8d50e 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -213,6 +213,7 @@ jobs: needs: test steps: - uses: actions/checkout@v4 + - run: python -c "import platform; print(platform.machine())" - run: pip3 install --no-warn-script-location -r build/requirements/macos.txt - run: cd ${{ github.workspace }}/build; python3 build.py env: @@ -229,6 +230,7 @@ jobs: needs: test steps: - uses: actions/checkout@v4 + - run: python -c "import platform; print(platform.machine())" - run: pip3 install --no-warn-script-location --break-system-packages -r build/requirements/macos.txt - run: cd ${{ github.workspace }}/build; python3 build.py env: diff --git a/build/build.py b/build/build.py index c439813a4..25b8e7577 100644 --- a/build/build.py +++ b/build/build.py @@ -42,8 +42,11 @@ ARCH_WINDOWS = platform.architecture()[0][0:2] ARCH_WINDOWS_OPTS = {'32': ('win32', 'win32', '', 'x86'), '64': ('win-amd64', 'amd64', '(X86)', 'x64')} -ARCH_MACOS = platform.processor() -ARCH_MACOS_NAMES = {'i386': 'Intel', 'arm': 'Apple_Silicon'} +ARCH_MACOS = platform.machine() +ARCH_MACOS_NAMES = {'i386': 'Intel', + 'x86_64': 'Intel', + 'arm': 'Apple_Silicon', + 'arm64': 'Apple_Silicon'} PYTHON_VERSION = '{0}.{1}'.format(sys.version_info[0], sys.version_info[1]) @@ -198,8 +201,8 @@ def macmain(): '"Nagstamon {0} uncompressed.dmg"'.format(VERSION)], shell=True) # Compress DMG - subprocess.call(['hdiutil convert "Nagstamon {0} uncompressed".dmg ' - f'-format UDZO -imagekey zlib-level=9 -o "Nagstamon {VERSION} {ARCH_MACOS_NAMES[ARCH_MACOS]}.dmg"'], shell=True) + subprocess.call([f'hdiutil convert "Nagstamon {VERSION} uncompressed".dmg ' + f'-format UDZO -imagekey zlib-level=9 -o "Nagstamon {VERSION} {ARCH_MACOS_NAMES[ARCH_MACOS]}.dmg"'], shell=True) # Delete uncompressed DMG file as it is no longer needed os.unlink('Nagstamon {0} uncompressed.dmg'.format(VERSION)) From 65a63264c63dcc4d6f0c231967b8bbb37baf9852 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Wed, 14 Aug 2024 13:01:02 +0200 Subject: [PATCH 804/884] Apple Silicon build --- .github/workflows/build-release-latest.yml | 2 -- build/build.py | 6 ++---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index d93e8d50e..54d2238f0 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -213,7 +213,6 @@ jobs: needs: test steps: - uses: actions/checkout@v4 - - run: python -c "import platform; print(platform.machine())" - run: pip3 install --no-warn-script-location -r build/requirements/macos.txt - run: cd ${{ github.workspace }}/build; python3 build.py env: @@ -230,7 +229,6 @@ jobs: needs: test steps: - uses: actions/checkout@v4 - - run: python -c "import platform; print(platform.machine())" - run: pip3 install --no-warn-script-location --break-system-packages -r build/requirements/macos.txt - run: cd ${{ github.workspace }}/build; python3 build.py env: diff --git a/build/build.py b/build/build.py index 25b8e7577..25766f116 100644 --- a/build/build.py +++ b/build/build.py @@ -43,10 +43,8 @@ ARCH_WINDOWS_OPTS = {'32': ('win32', 'win32', '', 'x86'), '64': ('win-amd64', 'amd64', '(X86)', 'x64')} ARCH_MACOS = platform.machine() -ARCH_MACOS_NAMES = {'i386': 'Intel', - 'x86_64': 'Intel', - 'arm': 'Apple_Silicon', - 'arm64': 'Apple_Silicon'} +ARCH_MACOS_NAMES = {'x86_64': 'Intel', + 'arm64': 'ARM'} PYTHON_VERSION = '{0}.{1}'.format(sys.version_info[0], sys.version_info[1]) From d797198f1dd71a8a7509a1c695b788bf441c80fb Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Wed, 14 Aug 2024 13:25:14 +0200 Subject: [PATCH 805/884] Apple Silicon build --- build/build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/build.py b/build/build.py index 25766f116..f884ef197 100644 --- a/build/build.py +++ b/build/build.py @@ -203,7 +203,7 @@ def macmain(): f'-format UDZO -imagekey zlib-level=9 -o "Nagstamon {VERSION} {ARCH_MACOS_NAMES[ARCH_MACOS]}.dmg"'], shell=True) # Delete uncompressed DMG file as it is no longer needed - os.unlink('Nagstamon {0} uncompressed.dmg'.format(VERSION)) + os.unlink(f'Nagstamon {VERSION} uncompressed.dmg') def debmain(): From 1c4419220bcecb6bd6a3d5ae5ebd5201221e61f9 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Wed, 14 Aug 2024 13:33:02 +0200 Subject: [PATCH 806/884] Apple Silicon build cleanup --- build/build.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/build.py b/build/build.py index f884ef197..124b09d8e 100644 --- a/build/build.py +++ b/build/build.py @@ -194,9 +194,9 @@ def macmain(): os.unlink(dmg_file) # create DMG - subprocess.call(['hdiutil create -srcfolder "Nagstamon {0} Staging DMG" ' - '-volname "Nagstamon {0}" -fs HFS+ -format UDRW -size 100M ' - '"Nagstamon {0} uncompressed.dmg"'.format(VERSION)], shell=True) + subprocess.call([f'hdiutil create -srcfolder "Nagstamon {VERSION} Staging DMG" ' + f'-volname "Nagstamon {VERSION}" -fs HFS+ -format UDRW -size 100M ' + f'"Nagstamon {VERSION} uncompressed.dmg"'], shell=True) # Compress DMG subprocess.call([f'hdiutil convert "Nagstamon {VERSION} uncompressed".dmg ' From 53566e10d85e31e2164a70497ed6ec8022bbdb9d Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 2 Sep 2024 15:21:22 +0200 Subject: [PATCH 807/884] Apple Silicon build cleanup (#1054) Co-authored-by: Henri Wahl <henri.wahl@ukdd.de> --- build/build.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/build.py b/build/build.py index 124b09d8e..4ab90806f 100644 --- a/build/build.py +++ b/build/build.py @@ -39,9 +39,11 @@ from Nagstamon.Helpers import get_distro VERSION = AppInfo.VERSION + ARCH_WINDOWS = platform.architecture()[0][0:2] ARCH_WINDOWS_OPTS = {'32': ('win32', 'win32', '', 'x86'), '64': ('win-amd64', 'amd64', '(X86)', 'x64')} + ARCH_MACOS = platform.machine() ARCH_MACOS_NAMES = {'x86_64': 'Intel', 'arm64': 'ARM'} From 92fd4e422aa2a1cd96b712d1dda0dd98149e1806 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 29 Aug 2024 10:44:54 +0200 Subject: [PATCH 808/884] debug pyinstaller resources --- Nagstamon/Config.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 59ea68aad..0870afe31 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -1062,11 +1062,17 @@ def __init__(self, **kwds): executable_dir = os.path.join(os.sep.join(sys.executable.split(os.sep)[:-1])) if os.path.exists(os.path.normcase(os.sep.join((executable_dir, "resources")))): RESOURCES = os.path.normcase(os.sep.join((executable_dir, "resources"))) + + print('resources 1: ' + RESOURCES) + else: import pkg_resources RESOURCES = pkg_resources.resource_filename("Nagstamon", "resources") + print('resources 2: ' + RESOURCES) + + except Exception as err: # get resources directory from current directory - only if not being set before by pkg_resources # try-excepts necessary for platforms like Windows .EXE @@ -1096,6 +1102,10 @@ def __init__(self, **kwds): for path in paths_to_check: if os.path.exists(path): RESOURCES = path + print('resources 3: ' + RESOURCES, paths_to_check) + break else: RESOURCES = str(Path(__file__).parent.absolute().joinpath('resources')) + + print('resources 4: ' + RESOURCES) From 39c09fb9de318435e3f87f5768845fb8d8149f4f Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 29 Aug 2024 17:58:39 +0200 Subject: [PATCH 809/884] try to fix pyinstaller resources --- Nagstamon/Config.py | 5 +++++ build/requirements/windows.txt | 2 -- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 0870afe31..b416aa597 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -1109,3 +1109,8 @@ def __init__(self, **kwds): RESOURCES = str(Path(__file__).parent.absolute().joinpath('resources')) print('resources 4: ' + RESOURCES) + +# try to fix missing resources path for Windows +if OS == 'Windows': + if r'\_internal\Nagstamon\resources' in RESOURCES: + RESOURCES = RESOURCES.replace(r'\_internal\Nagstamon\resources', r'\_internal\resources') diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index 3d2bcc3d6..5a1becbd2 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -4,8 +4,6 @@ keyring lxml pip-system-certs psutil -# last stable of the 5 series - InnoSetup does not work with Pyinstaller 6 -#pyinstaller==5.13.2 pyinstaller pypiwin32 pyqt6 From b17ff73a12bafd0f26cf9f8c6366ea8c7cb821c6 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 29 Aug 2024 18:01:19 +0200 Subject: [PATCH 810/884] try to fix pyinstaller resources --- Nagstamon/Config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index b416aa597..00a654f8d 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -1111,6 +1111,6 @@ def __init__(self, **kwds): print('resources 4: ' + RESOURCES) # try to fix missing resources path for Windows -if OS == 'Windows': +if OS == OS_WINDOWS: if r'\_internal\Nagstamon\resources' in RESOURCES: RESOURCES = RESOURCES.replace(r'\_internal\Nagstamon\resources', r'\_internal\resources') From d0ae96ae33591854d8312d72cf6db495ef5d1d9b Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 29 Aug 2024 18:14:11 +0200 Subject: [PATCH 811/884] cleanup debugging --- Nagstamon/Config.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 00a654f8d..11d2c54e3 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -1062,17 +1062,11 @@ def __init__(self, **kwds): executable_dir = os.path.join(os.sep.join(sys.executable.split(os.sep)[:-1])) if os.path.exists(os.path.normcase(os.sep.join((executable_dir, "resources")))): RESOURCES = os.path.normcase(os.sep.join((executable_dir, "resources"))) - - print('resources 1: ' + RESOURCES) - else: import pkg_resources RESOURCES = pkg_resources.resource_filename("Nagstamon", "resources") - print('resources 2: ' + RESOURCES) - - except Exception as err: # get resources directory from current directory - only if not being set before by pkg_resources # try-excepts necessary for platforms like Windows .EXE @@ -1082,7 +1076,6 @@ def __init__(self, **kwds): # if resources dir is not available in CWD, try the # libs dir (site-packages) for the current Python from distutils.sysconfig import get_python_lib - paths_to_check.append(os.path.normcase(os.path.join(get_python_lib(), "Nagstamon", "resources"))) except Exception: pass @@ -1090,7 +1083,6 @@ def __init__(self, **kwds): # if we're still out of luck, maybe this was a user scheme install try: import site - site.getusersitepackages() # make sure USER_SITE is set paths_to_check.append(os.path.normcase(os.path.join(site.USER_SITE, "Nagstamon", "resources"))) except Exception: @@ -1103,13 +1095,10 @@ def __init__(self, **kwds): if os.path.exists(path): RESOURCES = path print('resources 3: ' + RESOURCES, paths_to_check) - break else: RESOURCES = str(Path(__file__).parent.absolute().joinpath('resources')) - print('resources 4: ' + RESOURCES) - # try to fix missing resources path for Windows if OS == OS_WINDOWS: if r'\_internal\Nagstamon\resources' in RESOURCES: From 811d4cf2ef5eae6ca80895923d8260c744a9e212 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Mon, 2 Sep 2024 17:12:45 +0200 Subject: [PATCH 812/884] minimal refactoring + checkmk api fetch_url --- Nagstamon/Objects.py | 2 +- Nagstamon/QUI/__init__.py | 42 ++--- .../Alertmanager/alertmanagerserver.py | 12 +- Nagstamon/Servers/Centreon/CentreonAPI.py | 120 ++++++------ Nagstamon/Servers/Centreon/CentreonLegacy.py | 154 +++++++-------- Nagstamon/Servers/Centreon/__init__.py | 10 +- Nagstamon/Servers/Generic.py | 160 ++++++++-------- Nagstamon/Servers/Icinga.py | 32 ++-- Nagstamon/Servers/Icinga2API.py | 14 +- Nagstamon/Servers/IcingaDBWeb.py | 177 ++++++++---------- Nagstamon/Servers/IcingaDBWebNotifications.py | 6 +- Nagstamon/Servers/IcingaWeb2.py | 54 +++--- Nagstamon/Servers/Monitos4x.py | 24 +-- Nagstamon/Servers/Multisite.py | 57 +++--- Nagstamon/Servers/Opsview.py | 46 ++--- Nagstamon/Servers/Prometheus.py | 10 +- Nagstamon/Servers/Sensu.py | 8 +- Nagstamon/Servers/SensuGo.py | 4 +- Nagstamon/Servers/SnagView3.py | 16 +- Nagstamon/Servers/Thruk.py | 28 +-- Nagstamon/Servers/Zabbix.py | 37 ++-- Nagstamon/Servers/ZabbixProblemBased.py | 2 +- Nagstamon/Servers/Zenoss.py | 4 +- Nagstamon/Servers/__init__.py | 2 +- Nagstamon/Servers/op5Monitor.py | 12 +- 25 files changed, 501 insertions(+), 532 deletions(-) diff --git a/Nagstamon/Objects.py b/Nagstamon/Objects.py index 09bfa2bce..ac16bcef8 100644 --- a/Nagstamon/Objects.py +++ b/Nagstamon/Objects.py @@ -171,7 +171,7 @@ def get_hash(self): class Result(object): """ - multi purpose result object, used in Servers.Generic.FetchURL() + multi purpose result object, used in Servers.Generic.fetch_url() """ result = '' error = '' diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 5bc90c323..fe3cef0a3 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -723,7 +723,7 @@ def open_url(self): url = url.replace('$MONITOR-CGI$', self.server.monitor_cgi_url) if conf.debug_mode: - self.server.Debug(server=self.server.get_name(), debug='Open {0} web page {1}'.format(self.url_type, url)) + self.server.debug(server=self.server.get_name(), debug='Open {0} web page {1}'.format(self.url_type, url)) # use Python method to open browser webbrowser_open(url) @@ -2074,7 +2074,7 @@ def refresh(self): for server in get_enabled_servers(): if conf.debug_mode: - server.Debug(server=server.name, debug='Refreshing all hosts and services') + server.debug(server=server.name, debug='Refreshing all hosts and services') # manipulate server thread counter so get_status loop will refresh when next looking # at thread counter @@ -2394,7 +2394,7 @@ def execute_action(self, server_name, custom_action_string): execute custom action """ if conf.debug_mode: - servers[server_name].Debug(debug='NOTIFICATION: ' + custom_action_string) + servers[server_name].debug(debug='NOTIFICATION: ' + custom_action_string) subprocess.Popen(custom_action_string, shell=True) def get_worst_notification_status(self): @@ -3628,7 +3628,7 @@ def action_menu_custom_response(self, action): # get data to send to action server = self.server.get_name() - address = self.server.GetHost(miserable_host).result + address = self.server.get_host(miserable_host).result monitor = self.server.monitor_url monitor_cgi = self.server.monitor_cgi_url username = self.server.username @@ -3792,7 +3792,7 @@ def action_archive_event(self): 'host': host, 'service': service, 'status-info': status, - 'address': self.server.GetHost(host).result, + 'address': self.server.get_host(host).result, 'monitor': self.server.monitor_url, 'monitor-cgi': self.server.monitor_cgi_url, 'username': self.server.username, @@ -4063,7 +4063,7 @@ def get_status(self): # reflect status retrieval attempt on server vbox label self.change_label_status.emit('Refreshing...', '') - status = self.server.GetStatus() + status = self.server.get_status() # all is OK if no error info came back if self.server.status_description == '' and \ @@ -4291,9 +4291,9 @@ def recheck(self, info_dict): if conf.debug_mode: # host if info_dict['service'] == '': - self.server.Debug(server=self.server.name, debug='Rechecking host {0}'.format(info_dict['host'])) + self.server.debug(server=self.server.name, debug='Rechecking host {0}'.format(info_dict['host'])) else: - self.server.Debug(server=self.server.name, + self.server.debug(server=self.server.name, debug='Rechecking service {0} on host {1}'.format(info_dict['service'], info_dict['host'])) @@ -4312,7 +4312,7 @@ def recheck_all(self): # change label of server vbox self.change_label_status.emit('Rechecking all...', '') if conf.debug_mode: - self.server.Debug(server=self.server.name, debug='Start rechecking all') + self.server.debug(server=self.server.name, debug='Start rechecking all') # special treatment for Checkmk Multisite because there is only one URL call necessary if self.server.type != 'Checkmk Multisite': # make a copy to preserve hosts/service to recheck - just in case something changes meanwhile @@ -4320,14 +4320,14 @@ def recheck_all(self): for status in nagitems_filtered['hosts'].items(): for host in status[1]: if conf.debug_mode: - self.server.Debug(server=self.server.name, + self.server.debug(server=self.server.name, debug='Rechecking host {0}'.format(host.name)) # call server recheck method self.server.set_recheck({'host': host.name, 'service': ''}) for status in nagitems_filtered['services'].items(): for service in status[1]: if conf.debug_mode: - self.server.Debug(server=self.server.name, + self.server.debug(server=self.server.name, debug='Rechecking service {0} on host {1}'.format(service.name, service.host)) # call server recheck method @@ -4342,7 +4342,7 @@ def recheck_all(self): self.restore_label_status.emit() else: if conf.debug_mode: - self.server.Debug(server=self.server.name, debug='Already rechecking all') + self.server.debug(server=self.server.name, debug='Already rechecking all') @Slot(str, str) def get_start_end(self, server_name, host): @@ -4409,13 +4409,13 @@ def execute_action(self, action, info): if action['type'] == 'browser': # debug if conf.debug_mode is True: - self.server.Debug(server=self.server.name, host=info['host'], service=info['service'], + self.server.debug(server=self.server.name, host=info['host'], service=info['service'], debug='ACTION: BROWSER ' + string) webbrowser_open(string) elif action['type'] == 'command': # debug if conf.debug_mode is True: - self.server.Debug(server=self.server.name, host=info['host'], service=info['service'], + self.server.debug(server=self.server.name, host=info['host'], service=info['service'], debug='ACTION: COMMAND ' + string) subprocess.Popen(string, shell=True) elif action['type'] == 'url': @@ -4428,18 +4428,18 @@ def execute_action(self, action, info): string = self._URLify(string) # debug if conf.debug_mode is True: - self.server.Debug(server=self.server.name, host=info['host'], service=info['service'], + self.server.debug(server=self.server.name, host=info['host'], service=info['service'], debug='ACTION: URL in background ' + string) - servers[info['server']].FetchURL(string) + servers[info['server']].fetch_url(string) # used for example by Op5Monitor.py elif action['type'] == 'url-post': # make string ready for URL string = self._URLify(string) # debug if conf.debug_mode is True: - self.server.Debug(server=self.server.name, host=info['host'], service=info['service'], + self.server.debug(server=self.server.name, host=info['host'], service=info['service'], debug='ACTION: URL-POST in background ' + string) - servers[info['server']].FetchURL(string, cgi_data=cgi_data, multipart=True) + servers[info['server']].fetch_url(string, cgi_data=cgi_data, multipart=True) if action['recheck']: self.recheck(info) @@ -7011,9 +7011,9 @@ def check(self): message = 'Cannot reach version check at <a href={0}>{0}</<a>.'.format( f'https://{download_server}{AppInfo.VERSION_PATH}') # retrieve VERSION_URL without auth information - response = server.FetchURL(f'https://{download_server}{AppInfo.VERSION_PATH}', - giveback='raw', - no_auth=True) + response = server.fetch_url(f'https://{download_server}{AppInfo.VERSION_PATH}', + giveback='raw', + no_auth=True) # stop searching the available download URLs if response.error == "" and \ not response.result.startswith('<') and \ diff --git a/Nagstamon/Servers/Alertmanager/alertmanagerserver.py b/Nagstamon/Servers/Alertmanager/alertmanagerserver.py index 24884371a..db2956973 100644 --- a/Nagstamon/Servers/Alertmanager/alertmanagerserver.py +++ b/Nagstamon/Servers/Alertmanager/alertmanagerserver.py @@ -194,11 +194,11 @@ def _get_status(self): # get all alerts from the API server try: if self.alertmanager_filter != '': - result = self.FetchURL(self.monitor_url + self.API_PATH_ALERTS + self.API_FILTERS + result = self.fetch_url(self.monitor_url + self.API_PATH_ALERTS + self.API_FILTERS + self.alertmanager_filter, giveback="raw") else: - result = self.FetchURL(self.monitor_url + self.API_PATH_ALERTS, - giveback="raw") + result = self.fetch_url(self.monitor_url + self.API_PATH_ALERTS, + giveback="raw") if result.status_code == 200: log.debug("received status code '%s' with this content in result.result: \n\ @@ -258,7 +258,7 @@ def _get_status(self): except Exception as the_exception: # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) log.exception(the_exception) return Result(result=result, error=error) @@ -353,6 +353,6 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi cgi_data["createdBy"] = author or "Nagstamon" cgi_data = json.dumps(cgi_data) - result = self.FetchURL(self.monitor_url + self.API_PATH_SILENCES, giveback="raw", - cgi_data=cgi_data) + result = self.fetch_url(self.monitor_url + self.API_PATH_SILENCES, giveback="raw", + cgi_data=cgi_data) return result diff --git a/Nagstamon/Servers/Centreon/CentreonAPI.py b/Nagstamon/Servers/Centreon/CentreonAPI.py index abca00ea3..17d373c02 100644 --- a/Nagstamon/Servers/Centreon/CentreonAPI.py +++ b/Nagstamon/Servers/Centreon/CentreonAPI.py @@ -70,7 +70,7 @@ def init_config(self): init_config, called at thread start ''' # Version check - result = self.FetchURL(f'{self.monitor_cgi_url}/api/latest/platform/versions', no_auth=True, giveback='raw') + result = self.fetch_url(f'{self.monitor_cgi_url}/api/latest/platform/versions', no_auth=True, giveback='raw') data = json.loads(result.result) error = result.error @@ -84,7 +84,7 @@ def init_config(self): self.centreon_version_major = int(data["web"]["major"]) self.centreon_version_minor = int(data["web"]["minor"]) if conf.debug_mode is True: - self.Debug(server='[' + self.get_name() + ']', debug='Centreon version detected : ' + str(self.centreon_version_major) + '.' + str(self.centreon_version_minor)) + self.debug(server='[' + self.get_name() + ']', debug='Centreon version detected : ' + str(self.centreon_version_major) + '.' + str(self.centreon_version_minor)) if self.centreon_version_major >= 21: # URLs for browser shortlinks/buttons on popup window @@ -106,11 +106,11 @@ def init_config(self): else: self.restapi_version = "v24.04" if conf.debug_mode is True: - self.Debug(server='[' + self.get_name() + ']', debug='Centreon API version used : ' + self.restapi_version) + self.debug(server='[' + self.get_name() + ']', debug='Centreon API version used : ' + self.restapi_version) else: if conf.debug_mode is True: - self.Debug(server='[' + self.get_name() + ']', debug='Unsupported Centreon version, must be >= 21') + self.debug(server='[' + self.get_name() + ']', debug='Unsupported Centreon version, must be >= 21') # Changed this because define_url was called 2 times if not self.tls_error and self.urls_centreon is None: @@ -156,14 +156,14 @@ def get_token(self): # Post json json_string = json.dumps(cgi_data) - result = self.FetchURL(self.urls_centreon['login'], cgi_data=json_string, giveback='raw') + result = self.fetch_url(self.urls_centreon['login'], cgi_data=json_string, giveback='raw') data = json.loads(result.result) error = result.error status_code = result.status_code if conf.debug_mode: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug="Fetched JSON: " + pprint.pformat(data)) # check if any error occured @@ -176,7 +176,7 @@ def get_token(self): user_id = data["contact"]["id"] if conf.debug_mode: - self.Debug(server='[' + self.get_name() + ']', + self.debug(server='[' + self.get_name() + ']', debug='API login : ' + self.username + ' / ' + self.password + ' > Token : ' + token + ' > User ID : ' + str( user_id)) @@ -186,16 +186,16 @@ def get_token(self): except: traceback.print_exc(file=sys.stdout) - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) - def GetHost(self, host): + def get_host(self, host): # https://demo.centreon.com/centreon/api/latest/monitoring/resources?page=1&limit=30&sort_by={"status_severity_code":"asc","last_status_change":"desc"}&types=["host"]&statuses=["WARNING","DOWN","CRITICAL","UNKNOWN"] url_hosts = self.urls_centreon['hosts'] + '?types=["host"]&search={"h.name":"' + host + '"}' try: # Get json - result = self.FetchURL(url_hosts, giveback='raw') + result = self.fetch_url(url_hosts, giveback='raw') data = json.loads(result.result) error = result.error @@ -209,7 +209,7 @@ def GetHost(self, host): fqdn = str(data["result"][0]["fqdn"]) if conf.debug_mode: - self.Debug(server='[' + self.get_name() + ']', + self.debug(server='[' + self.get_name() + ']', debug='Get Host FQDN or address : ' + host + " / " + fqdn) # Give back host or ip @@ -219,7 +219,7 @@ def GetHost(self, host): traceback.print_exc(file=sys.stdout) # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) def get_host_and_service_id(self, host, service=''): @@ -230,7 +230,7 @@ def get_host_and_service_id(self, host, service=''): try: # Get json - result = self.FetchURL(url_hosts, giveback='raw') + result = self.fetch_url(url_hosts, giveback='raw') data = json.loads(result.result) error = result.error @@ -244,14 +244,14 @@ def get_host_and_service_id(self, host, service=''): host_id = data["result"][0]["id"] if conf.debug_mode: - self.Debug(server='[' + self.get_name() + ']', debug='Get Host ID : ' + host + " / " + str(host_id)) + self.debug(server='[' + self.get_name() + ']', debug='Get Host ID : ' + host + " / " + str(host_id)) return host_id except: traceback.print_exc(file=sys.stdout) # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) else: # Host + Service @@ -264,7 +264,7 @@ def get_host_and_service_id(self, host, service=''): try: # Get json - result = self.FetchURL(url_service, giveback='raw') + result = self.fetch_url(url_service, giveback='raw') data = json.loads(result.result) error = result.error @@ -282,7 +282,7 @@ def get_host_and_service_id(self, host, service=''): service_id = data["result"][0]["id"] if conf.debug_mode: - self.Debug(server='[' + self.get_name() + ']', + self.debug(server='[' + self.get_name() + ']', debug='Get Host / Service ID : ' + str(host_id) + " / " + str(service_id)) return host_id, service_id @@ -290,7 +290,7 @@ def get_host_and_service_id(self, host, service=''): traceback.print_exc(file=sys.stdout) # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) def get_start_end(self, host): @@ -330,23 +330,19 @@ def _get_status(self): # Hosts try: # Get json - result = self.FetchURL(url_hosts, giveback='raw') + result = self.fetch_url(url_hosts, giveback='raw') data = json.loads(result.result) error = result.error status_code = result.status_code - # if conf.debug_mode: - # self.Debug(server=self.get_name(), - # debug="Get Hosts status Fetched JSON: " + pprint.pformat(data)) - # check if any error occured errors_occured = self.check_for_error(data, error, status_code) if errors_occured is not None: return (errors_occured) if data["meta"]["total"] == 0: - self.Debug(server='[' + self.get_name() + ']', debug='No host down') + self.debug(server='[' + self.get_name() + ']', debug='No host down') else: for alerts in data["result"]: new_host = alerts["name"] @@ -382,35 +378,31 @@ def _get_status(self): self.new_hosts[new_host].status_type = self.HARD_SOFT['(S)'] else: self.new_hosts[new_host].status_type = self.HARD_SOFT['(H)'] - self.Debug(server='[' + self.get_name() + ']', debug='Host indexed : ' + new_host) + self.debug(server='[' + self.get_name() + ']', debug='Host indexed : ' + new_host) except: traceback.print_exc(file=sys.stdout) # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) # Services try: # Get json - result = self.FetchURL(url_services, giveback='raw') + result = self.fetch_url(url_services, giveback='raw') data = json.loads(result.result) error = result.error status_code = result.status_code - # if conf.debug_mode: - # self.Debug(server=self.get_name(), - # debug="Get Services status Fetched JSON: " + pprint.pformat(data)) - # check if any error occured errors_occured = self.check_for_error(data, error, status_code) if errors_occured is not None: return (errors_occured) if data["meta"]["total"] == 0: - self.Debug(server='[' + self.get_name() + ']', debug='No service down') + self.debug(server='[' + self.get_name() + ']', debug='No service down') else: for alerts in data["result"]: if alerts["type"] == "metaservice": @@ -425,7 +417,7 @@ def _get_status(self): self.new_hosts[new_host].status = 'UP' self.new_hosts[new_host].services[new_service] = GenericService() # Attributs à remplir - self.Debug(server='[' + self.get_name() + ']', + self.debug(server='[' + self.get_name() + ']', debug='Service indexed : ' + new_host + ' / ' + new_service) self.new_hosts[new_host].services[new_service].server = self.name @@ -466,7 +458,7 @@ def _get_status(self): traceback.print_exc(file=sys.stdout) # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) # return True if all worked well @@ -512,14 +504,14 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi # Post json json_string = json.dumps(acknowledgements) # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/hosts/{host_id}/acknowledgements - result = self.FetchURL(self.urls_centreon['resources'] + '/acknowledge', cgi_data=json_string, - giveback='raw') + result = self.fetch_url(self.urls_centreon['resources'] + '/acknowledge', cgi_data=json_string, + giveback='raw') error = result.error status_code = result.status_code if conf.debug_mode: - self.Debug(server='[' + self.get_name() + ']', + self.debug(server='[' + self.get_name() + ']', debug="Set Ack on Host, status code : " + str(status_code)) # Service @@ -550,27 +542,27 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi acknowledgements["resources"].append(new_resource) if conf.debug_mode: - self.Debug(server='[' + self.get_name() + ']', + self.debug(server='[' + self.get_name() + ']', debug="Stack ack for Host (" + host + ") / Service (" + service + ")") # Post json json_string = json.dumps(acknowledgements) # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/services/acknowledgements - result = self.FetchURL(self.urls_centreon['resources'] + '/acknowledge', cgi_data=json_string, - giveback='raw') + result = self.fetch_url(self.urls_centreon['resources'] + '/acknowledge', cgi_data=json_string, + giveback='raw') error = result.error status_code = result.status_code if conf.debug_mode: - self.Debug(server='[' + self.get_name() + ']', + self.debug(server='[' + self.get_name() + ']', debug="Set Acks, status code : " + str(status_code)) except: traceback.print_exc(file=sys.stdout) # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) def _set_recheck(self, host, service): @@ -604,13 +596,13 @@ def _set_recheck(self, host, service): # Post json json_string = json.dumps(rechecks) # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/resources/check - result = self.FetchURL(self.urls_centreon['resources'] + '/check', cgi_data=json_string, giveback='raw') + result = self.fetch_url(self.urls_centreon['resources'] + '/check', cgi_data=json_string, giveback='raw') error = result.error status_code = result.status_code if conf.debug_mode: - self.Debug(server='[' + self.get_name() + ']', + self.debug(server='[' + self.get_name() + ']', debug="Recheck on Host : " + host + ", status code : " + str(status_code)) # Service @@ -638,13 +630,13 @@ def _set_recheck(self, host, service): # Post json json_string = json.dumps(rechecks) # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/resources/check - result = self.FetchURL(self.urls_centreon['resources'] + '/check', cgi_data=json_string, giveback='raw') + result = self.fetch_url(self.urls_centreon['resources'] + '/check', cgi_data=json_string, giveback='raw') error = result.error status_code = result.status_code if conf.debug_mode: - self.Debug(server='[' + self.get_name() + ']', + self.debug(server='[' + self.get_name() + ']', debug="Reckeck on Host (" + host + ") / Service (" + service + "), status code : " + str( status_code)) @@ -652,7 +644,7 @@ def _set_recheck(self, host, service): traceback.print_exc(file=sys.stdout) # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) def _set_downtime(self, host, service, author, comment, fixed, start_time, end_time, hours, minutes): @@ -701,14 +693,14 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t # Post json json_string = json.dumps(downtimes) # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/resources/downtime - result = self.FetchURL(self.urls_centreon['resources'] + '/downtime', cgi_data=json_string, - giveback='raw') + result = self.fetch_url(self.urls_centreon['resources'] + '/downtime', cgi_data=json_string, + giveback='raw') error = result.error status_code = result.status_code if conf.debug_mode: - self.Debug(server='[' + self.get_name() + ']', + self.debug(server='[' + self.get_name() + ']', debug="Downtime on Host : " + host + ", status code : " + str(status_code)) # Service @@ -738,14 +730,14 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t # Post json json_string = json.dumps(downtimes) # {protocol}://{server}:{port}/centreon/api/{version}/monitoring/resources/downtime - result = self.FetchURL(self.urls_centreon['resources'] + '/downtime', cgi_data=json_string, - giveback='raw') + result = self.fetch_url(self.urls_centreon['resources'] + '/downtime', cgi_data=json_string, + giveback='raw') error = result.error status_code = result.status_code if conf.debug_mode: - self.Debug(server='[' + self.get_name() + ']', + self.debug(server='[' + self.get_name() + ']', debug="Downtime on Host (" + host + ") / Service (" + service + "), status code : " + str( status_code)) @@ -754,24 +746,24 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t traceback.print_exc(file=sys.stdout) # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) def check_session(self): if conf.debug_mode: - self.Debug(server='[' + self.get_name() + ']', debug='Checking session status') + self.debug(server='[' + self.get_name() + ']', debug='Checking session status') try: if conf.debug_mode: - self.Debug(server='[' + self.get_name() + ']', + self.debug(server='[' + self.get_name() + ']', debug='Check-session, the token expire if not been used for more than one hour. Current Token = ' + str( self.token)) cgi_data = {'limit': '0'} # Get en empty service list, to check the status of the current token # This request must be done in a GET, so just encode the parameters and fetch - result = self.FetchURL(self.urls_centreon['resources'] + '?' + urllib.parse.urlencode(cgi_data), - giveback="raw") + result = self.fetch_url(self.urls_centreon['resources'] + '?' + urllib.parse.urlencode(cgi_data), + giveback="raw") # If we got an 403 or 401 (and 500 for version 21.), the token expired and must be renewed if self.centreon_version_major == 21: @@ -782,9 +774,9 @@ def check_session(self): if result.status_code in ressources_response_list: self.token = self.get_token().result if conf.debug_mode: - self.Debug(server='[' + self.get_name() + ']', debug='Check-session, session renewed') - result = self.FetchURL(self.urls_centreon['resources'] + '?' + urllib.parse.urlencode(cgi_data), - giveback="raw") + self.debug(server='[' + self.get_name() + ']', debug='Check-session, session renewed') + result = self.fetch_url(self.urls_centreon['resources'] + '?' + urllib.parse.urlencode(cgi_data), + giveback="raw") if not 'ConnectTimeoutError' in result.error and \ not 'NewConnectionError' in result.error: data = json.loads(result.result) @@ -795,12 +787,12 @@ def check_session(self): error='Connection error') if conf.debug_mode: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug="Check-session, Fetched JSON: " + pprint.pformat(data)) - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug="Check-session, Error : " + error + ", Status code : " + str(status_code)) except: traceback.print_exc(file=sys.stdout) - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) diff --git a/Nagstamon/Servers/Centreon/CentreonLegacy.py b/Nagstamon/Servers/Centreon/CentreonLegacy.py index 224a19954..fc79aa1b2 100644 --- a/Nagstamon/Servers/Centreon/CentreonLegacy.py +++ b/Nagstamon/Servers/Centreon/CentreonLegacy.py @@ -88,13 +88,13 @@ def init_HTTP(self): GenericServer.init_HTTP(self) if self.centreon_version is None: - result_versioncheck = self.FetchURL(self.monitor_cgi_url + '/index.php', giveback='raw') + result_versioncheck = self.fetch_url(self.monitor_cgi_url + '/index.php', giveback='raw') raw_versioncheck, error_versioncheck = result_versioncheck.result, result_versioncheck.error if error_versioncheck == '': if re.search('2\.2\.[0-9]', raw_versioncheck): self.centreon_version = 2.2 if conf.debug_mode is True: - self.Debug(server=self.get_name(), debug='Centreon version detected : 2.2') + self.debug(server=self.get_name(), debug='Centreon version detected : 2.2') # URLs for browser shortlinks/buttons on popup window self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?p=1', 'hosts': '$MONITOR$/main.php?p=20103&o=hpb', @@ -103,7 +103,7 @@ def init_HTTP(self): elif re.search('2\.[3-6]\.[0-5]', raw_versioncheck): self.centreon_version = 2.3456 if conf.debug_mode is True: - self.Debug(server=self.get_name(), debug='Centreon version detected : 2.3 <=> 2.6.5') + self.debug(server=self.get_name(), debug='Centreon version detected : 2.3 <=> 2.6.5') # URLs for browser shortlinks/buttons on popup window self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?p=1', 'hosts': '$MONITOR$/main.php?p=20103&o=hpb', @@ -112,7 +112,7 @@ def init_HTTP(self): elif re.search('2\.6\.[6-9]', raw_versioncheck): self.centreon_version = 2.66 if conf.debug_mode is True: - self.Debug(server=self.get_name(), debug='Centreon version detected : 2.6.6') + self.debug(server=self.get_name(), debug='Centreon version detected : 2.6.6') # URLs for browser shortlinks/buttons on popup window self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?p=1', 'hosts': '$MONITOR$/main.php?p=20103&o=hpb', @@ -122,7 +122,7 @@ def init_HTTP(self): # Centreon 2.7 only support C. Broker self.centreon_version = 2.7 if conf.debug_mode is True: - self.Debug(server=self.get_name(), debug='Centreon version detected : 2.7') + self.debug(server=self.get_name(), debug='Centreon version detected : 2.7') # URLs for browser shortlinks/buttons on popup window self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?', 'hosts': '$MONITOR$/main.php?p=20202&o=hpb', @@ -132,7 +132,7 @@ def init_HTTP(self): # Centreon 2.8 only support C. Broker self.centreon_version = 2.8 if conf.debug_mode is True: - self.Debug(server=self.get_name(), debug='Centreon version detected : 2.8') + self.debug(server=self.get_name(), debug='Centreon version detected : 2.8') # URLs for browser shortlinks/buttons on popup window self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?', 'hosts': '$MONITOR$/main.php?p=20202', @@ -141,7 +141,7 @@ def init_HTTP(self): elif re.search('18\.10\.[0-9]', raw_versioncheck): self.centreon_version = 18.10 if conf.debug_mode is True: - self.Debug(server=self.get_name(), debug='Centreon version detected : 18.10') + self.debug(server=self.get_name(), debug='Centreon version detected : 18.10') # URLs for browser shortlinks/buttons on popup window self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?', 'hosts': '$MONITOR$/main.php?p=20202', @@ -150,7 +150,7 @@ def init_HTTP(self): elif re.search('19\.(04|10)\.[0-9]', raw_versioncheck) or re.search('20\.(04|10)\.[0-9]', raw_versioncheck): self.centreon_version = 19.04 if conf.debug_mode is True: - self.Debug(server=self.get_name(), debug='Centreon version detected : 19.04 <=> 20.10') + self.debug(server=self.get_name(), debug='Centreon version detected : 19.04 <=> 20.10') # URLs for browser shortlinks/buttons on popup window self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?', 'hosts': '$MONITOR$/main.php?p=20202', @@ -160,7 +160,7 @@ def init_HTTP(self): # unsupported version or unable do determine self.centreon_version = 19.04 if conf.debug_mode is True: - self.Debug(server=self.get_name(), debug='Centreon version unknown : supposed to be >= 19.04') + self.debug(server=self.get_name(), debug='Centreon version unknown : supposed to be >= 19.04') # URLs for browser shortlinks/buttons on popup window self.BROWSER_URLS = {'monitor': '$MONITOR$/main.php?', 'hosts': '$MONITOR$/main.php?p=20202&o=hpb', @@ -168,7 +168,7 @@ def init_HTTP(self): 'history': '$MONITOR$/main.php?p=203'} else: if conf.debug_mode is True: - self.Debug(server=self.get_name(), debug='Error getting the home page : ' + error_versioncheck) + self.debug(server=self.get_name(), debug='Error getting the home page : ' + error_versioncheck) if self.first_login: self.SID = self._get_sid().result @@ -242,17 +242,17 @@ def _get_sid(self): 'hosts': self.BROWSER_URLS['hosts'] + auth,\ 'services': self.BROWSER_URLS['services'] + auth,\ 'history': self.BROWSER_URLS['history'] + auth} - raw = self.FetchURL(self.monitor_cgi_url + '/index.php?p=101&autologin=1&useralias=' + self.username + '&token=' + self.autologin_key, giveback='raw') + raw = self.fetch_url(self.monitor_cgi_url + '/index.php?p=101&autologin=1&useralias=' + self.username + '&token=' + self.autologin_key, giveback='raw') if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Autologin : ' + self.username + ' : ' + self.autologin_key) + self.debug(server=self.get_name(), debug='Autologin : ' + self.username + ' : ' + self.autologin_key) # Gathering of the token who will be used to interact with Centreon (start with 2.66) if self.centreon_version >= 2.66 and self.centreon_version < 19.04: - page = self.FetchURL(self.monitor_cgi_url + '/main.get.php') + page = self.fetch_url(self.monitor_cgi_url + '/main.get.php') self.centreon_token = page.result.find('input', {'name': "centreon_token"})['value'] # Password auth else: - login = self.FetchURL(self.monitor_cgi_url + '/index.php') + login = self.fetch_url(self.monitor_cgi_url + '/index.php') if login.error == '' and login.status_code == 200: # Centreon >= 2.6.6 implement a token if self.centreon_version >= 2.66 and self.centreon_version <= 19.04: @@ -265,18 +265,18 @@ def _get_sid(self): form_inputs['useralias'] = self.username form_inputs['password'] = self.password # fire up login button with all needed data - raw = self.FetchURL(self.monitor_cgi_url + '/index.php', cgi_data=form_inputs) + raw = self.fetch_url(self.monitor_cgi_url + '/index.php', cgi_data=form_inputs) else: login_data = {"useralias" : self.username, "password" : self.password, "submit" : "Login"} - raw = self.FetchURL(self.monitor_cgi_url + "/index.php",cgi_data=login_data, giveback="raw") + raw = self.fetch_url(self.monitor_cgi_url + "/index.php", cgi_data=login_data, giveback="raw") if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Password login : ' + self.username + ' : ' + self.password) + self.debug(server=self.get_name(), debug='Password login : ' + self.username + ' : ' + self.password) sid = self.session.cookies.get('PHPSESSID', '') if conf.debug_mode: - self.Debug(server=self.get_name(), debug='SID : ' + sid) + self.debug(server=self.get_name(), debug='SID : ' + sid) if self.centreon_version >= 2.66 and self.centreon_version < 19.04: - self.Debug(server=self.get_name(), debug='Centreon Token : ' + self.centreon_token) + self.debug(server=self.get_name(), debug='Centreon Token : ' + self.centreon_token) # those broker urls would not be changing too often so this check migth be done here if self.first_login: self._get_xml_path(sid) @@ -286,7 +286,7 @@ def _get_sid(self): except: import traceback traceback.print_exc(file=sys.stdout) - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) @@ -307,7 +307,7 @@ def get_start_end(self, host): elif self.centreon_version == 2.8: cgi_data['o'] = 'a' cgi_data['p'] = '210' - result = self.FetchURL(self.urls_centreon['main'], cgi_data = cgi_data, giveback='obj') + result = self.fetch_url(self.urls_centreon['main'], cgi_data = cgi_data, giveback='obj') html, error = result.result, result.error if error == '': @@ -327,11 +327,11 @@ def get_start_end(self, host): return start_time, end_time except: - self.Error(sys.exc_info()) + self.error(sys.exc_info()) return 'n/a', 'n/a' - def GetHost(self, host): + def get_host(self, host): ''' Centreonified way to get host ip - attribute 'a' in down hosts xml is of no use for up hosts so we need to get ip anyway from web page @@ -354,7 +354,7 @@ def GetHost(self, host): centreon_hosts = self.urls_centreon['xml_hosts'] + '?' + urllib.parse.urlencode(cgi_data) - result = self.FetchURL(centreon_hosts, giveback='xml') + result = self.fetch_url(centreon_hosts, giveback='xml') xmlobj, error, status_code = result.result, result.error, result.status_code # initialize ip string @@ -370,23 +370,23 @@ def GetHost(self, host): address = socket.gethostbyaddr(ip)[0] except: if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Unable to do a reverse DNS lookup on IP: ' + ip) + self.debug(server=self.get_name(), debug='Unable to do a reverse DNS lookup on IP: ' + ip) address = ip else: address = ip except: - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) else: - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(error=error) del xmlobj # print IP in debug mode if conf.debug_mode: - self.Debug(server=self.get_name(), debug='IP of %s:' % (host) + ' ' + address) + self.debug(server=self.get_name(), debug='IP of %s:' % (host) + ' ' + address) # give back host or ip return Result(result=address) @@ -403,29 +403,29 @@ def _get_xml_path(self, sid): if self.centreon_version <= 2.66: # 2.6 support NDO and C. Broker, we must check which one is used cgi_data = {'p':201, 'sid':sid} - result = self.FetchURL(self.monitor_cgi_url + '/main.php', cgi_data=cgi_data, giveback='raw') + result = self.fetch_url(self.monitor_cgi_url + '/main.php', cgi_data=cgi_data, giveback='raw') raw, error = result.result, result.error if error == '': if re.search('var _addrXML.*xml\/ndo\/host', raw): self.XML_PATH = 'xml/ndo' if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Detected broker : NDO') + self.debug(server=self.get_name(), debug='Detected broker : NDO') elif re.search('var _addrXML.*xml\/broker\/host', raw): self.XML_PATH = 'xml/broker' if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Detected broker : C. Broker') + self.debug(server=self.get_name(), debug='Detected broker : C. Broker') else: if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Could not detect the broker for Centeron 2.[3-6]. Using Centreon Broker') + self.debug(server=self.get_name(), debug='Could not detect the broker for Centeron 2.[3-6]. Using Centreon Broker') self.XML_PATH = 'xml/broker' del raw else: if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Unable to fetch the main page to detect the broker : ' + error) + self.debug(server=self.get_name(), debug='Unable to fetch the main page to detect the broker : ' + error) del result, error else: if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Only Centreon Broker is supported in Centeon >= 2.7 -> XML_PATH='+ self.XML_PATH) + self.debug(server=self.get_name(), debug='Only Centreon Broker is supported in Centeon >= 2.7 -> XML_PATH=' + self.XML_PATH) def _define_url(self): @@ -489,7 +489,7 @@ def _define_url(self): elif self.centreon_version >= 18.10: self.urls_centreon = urls_centreon_18_10 if conf.debug_mode: - self.Debug(server=self.get_name(), debug='URLs defined for Centreon %s' % (self.centreon_version)) + self.debug(server=self.get_name(), debug='URLs defined for Centreon %s' % (self.centreon_version)) def _get_host_id(self, host): @@ -503,7 +503,7 @@ def _get_host_id(self, host): url = self.urls_centreon['main'] + '?' + urllib.parse.urlencode(cgi_data) - result = self.FetchURL(url, giveback='raw') + result = self.fetch_url(url, giveback='raw') raw, error = result.result, result.error if error == '': @@ -511,7 +511,7 @@ def _get_host_id(self, host): del raw else: if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Host ID could not be retrieved.') + self.debug(server=self.get_name(), debug='Host ID could not be retrieved.') # some cleanup del result, error @@ -520,7 +520,7 @@ def _get_host_id(self, host): try: if int(host_id): if conf.debug_mode: - self.Debug(server=self.get_name(), host=host, debug='Host ID is ' + host_id) + self.debug(server=self.get_name(), host=host, debug='Host ID is ' + host_id) return host_id else: return '' @@ -538,7 +538,7 @@ def _get_host_and_service_id(self, host, service): 'o':'svcd'} # This request must be done in a GET, so just encode the parameters and fetch - result = self.FetchURL(self.urls_centreon['main'] + '?' + urllib.parse.urlencode(cgi_data), giveback="raw") + result = self.fetch_url(self.urls_centreon['main'] + '?' + urllib.parse.urlencode(cgi_data), giveback="raw") raw, error = result.result, result.error if error == '': @@ -546,10 +546,10 @@ def _get_host_and_service_id(self, host, service): svc_id = raw.partition("var svc_id = '")[2].partition("'")[0] del raw if conf.debug_mode: - self.Debug(server=self.get_name(), host=host, service=service, debug='- Get host/svc ID : ' + host_id + '/' + svc_id) + self.debug(server=self.get_name(), host=host, service=service, debug='- Get host/svc ID : ' + host_id + '/' + svc_id) else: if conf.debug_mode: - self.Debug(server=self.get_name(), host=host, service=service, debug='- IDs could not be retrieved.') + self.debug(server=self.get_name(), host=host, service=service, debug='- IDs could not be retrieved.') # some cleanup del result, error @@ -558,7 +558,7 @@ def _get_host_and_service_id(self, host, service): try: if int(host_id) and int(svc_id): if conf.debug_mode: - self.Debug(server=self.get_name(), host=host, service=service, debug='- Host & Service ID are valid (int)') + self.debug(server=self.get_name(), host=host, service=service, debug='- Host & Service ID are valid (int)') return host_id,svc_id else: return '','' @@ -597,7 +597,7 @@ def _get_status(self): # unfortunately the hosts status page has a different structure so # hosts must be analyzed separately try: - result = self.FetchURL(nagcgiurl_hosts, giveback='xml') + result = self.fetch_url(nagcgiurl_hosts, giveback='xml') xmlobj, error, status_code = result.result, result.error, result.status_code # check if any error occured @@ -610,18 +610,18 @@ def _get_status(self): # Check if the result is not empty if len(xmlobj) == 0: if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Empty host XML result') + self.debug(server=self.get_name(), debug='Empty host XML result') return Result(result=None, error="Empty host XML result") # in case there are no children session ID is expired if xmlobj.text.lower() == 'bad session id': del xmlobj if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Bad session ID, retrieving new one...') + self.debug(server=self.get_name(), debug='Bad session ID, retrieving new one...') # try again... self.SID = self._get_sid().result - result = self.FetchURL(nagcgiurl_hosts, giveback='xml') + result = self.fetch_url(nagcgiurl_hosts, giveback='xml') xmlobj, error, status_code = result.result, result.error, result.status_code errors_occured = self.check_for_error(xmlobj, error, status_code) # if there are errors return them @@ -631,7 +631,7 @@ def _get_status(self): # a second time a bad session id should raise an error if xmlobj.text.lower() == 'bad session id': if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Even after renewing session ID, unable to get the XML') + self.debug(server=self.get_name(), debug='Even after renewing session ID, unable to get the XML') return Result(result='ERROR', error='Bad session ID', status_code=status_code) @@ -669,7 +669,7 @@ def _get_status(self): traceback.print_exc(file=sys.stdout) # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) del xmlobj @@ -679,12 +679,12 @@ def _get_status(self): traceback.print_exc(file=sys.stdout) # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) # services try: - result = self.FetchURL(nagcgiurl_services, giveback='xml') + result = self.fetch_url(nagcgiurl_services, giveback='xml') xmlobj, error, status_code = result.result, result.error, result.status_code # check if any error occured @@ -696,17 +696,17 @@ def _get_status(self): # Check if the result is not empty if len(xmlobj) == 0: if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Empty service XML result') + self.debug(server=self.get_name(), debug='Empty service XML result') return Result(result=None, error="Empty service XML result") # in case there are no children session id is invalid if xmlobj.text.lower() == 'bad session id': # debug if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Bad session ID, retrieving new one...') + self.debug(server=self.get_name(), debug='Bad session ID, retrieving new one...') # try again... self.SID = self._get_sid().result - result = self.FetchURL(nagcgiurl_services, giveback='xml') + result = self.fetch_url(nagcgiurl_services, giveback='xml') xmlobj, error, status_code = result.result, result.error, result.status_code errors_occured = self.check_for_error(xmlobj, error, status_code) # if there are errors return them @@ -728,7 +728,7 @@ def _get_status(self): nagcgiurl_meta_services = self.urls_centreon['xml_meta'] + '?' + urllib.parse.urlencode({'num':0, 'limit':self.limit_services_number, 'o':'meta', 'sort_type':'status', 'sid':self.SID}) # retrive meta-services xml STATUS - result_meta = self.FetchURL(nagcgiurl_meta_services, giveback='xml') + result_meta = self.fetch_url(nagcgiurl_meta_services, giveback='xml') xmlobj_meta, error_meta, status_code_meta = result_meta.result, result_meta.error, result_meta.status_code # check if any error occured @@ -741,7 +741,7 @@ def _get_status(self): # a second time a bad session id should raise an error if xmlobj_meta.text.lower() == 'bad session id': if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Even after renewing session ID, unable to get the XML') + self.debug(server=self.get_name(), debug='Even after renewing session ID, unable to get the XML') return Result(result='ERROR', error='Bad session ID', @@ -755,7 +755,7 @@ def _get_status(self): traceback.print_exc(file=sys.stdout) # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) # do some cleanup del xmlobj_meta @@ -802,7 +802,7 @@ def _get_status(self): self.HARD_SOFT[self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_type] if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Parsing service XML (Host/Service/Status_type) ' + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].host + '/' + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].name + '/' + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_type) + self.debug(server=self.get_name(), debug='Parsing service XML (Host/Service/Status_type) ' + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].host + '/' + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].name + '/' + self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_type) self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].last_check = str(l.lc.text) self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].duration = str(l.d.text) self.new_hosts[str(l.hn.text)].services[str(l.sd.text)].status_information = str(l.po.text).replace('\n', ' ').strip() @@ -826,7 +826,7 @@ def _get_status(self): traceback.print_exc(file=sys.stdout) # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) # do some cleanup @@ -837,7 +837,7 @@ def _get_status(self): traceback.print_exc(file=sys.stdout) # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) # return True if all worked well @@ -868,7 +868,7 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi cgi_data['centreon_token'] = self.centreon_token # Post - raw = self.FetchURL(self.urls_centreon['main'], cgi_data=cgi_data, giveback='raw') + raw = self.fetch_url(self.urls_centreon['main'], cgi_data=cgi_data, giveback='raw') del raw # if host is acknowledged and all services should be to or if a service is acknowledged @@ -917,10 +917,10 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi cgi_data['service_description'] = rsd # POST, for some strange reason only working if giveback is 'raw' - raw = self.FetchURL(self.urls_centreon['main'], cgi_data=cgi_data, giveback='raw') + raw = self.fetch_url(self.urls_centreon['main'], cgi_data=cgi_data, giveback='raw') del raw except: - self.Error(sys.exc_info()) + self.error(sys.exc_info()) def _set_recheck(self, host, service): @@ -932,7 +932,7 @@ def _set_recheck(self, host, service): # Meta if host == '_Module_Meta': if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Recheck on a Meta service, more work to be done') + self.debug(server=self.get_name(), debug='Recheck on a Meta service, more work to be done') m = re.search(r'^.+ \((?P<rsd>.+)\)$', service) if m: rsd = m.group('rsd') @@ -972,15 +972,15 @@ def _set_recheck(self, host, service): if self.centreon_version < 19.04: # execute GET request - raw = self.FetchURL(url, giveback='raw') + raw = self.fetch_url(url, giveback='raw') del raw else: # running remote cgi command with POST method, for some strange reason only working if # giveback is 'raw' - raw = self.FetchURL(self.urls_centreon['xml_serviceSendCommand'], cgi_data=cgi_data, giveback='raw') + raw = self.fetch_url(self.urls_centreon['xml_serviceSendCommand'], cgi_data=cgi_data, giveback='raw') del raw except: - self.Error(sys.exc_info()) + self.error(sys.exc_info()) def _set_downtime(self, host, service, author, comment, fixed, start_time, end_time, hours, minutes): @@ -1065,51 +1065,51 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t if self.centreon_version < 19.04: # This request must be done in a GET, so just encode the parameters and fetch - raw = self.FetchURL(self.urls_centreon['external_cmd_cmdPopup'] + '?' + urllib.parse.urlencode(cgi_data), giveback="raw") + raw = self.fetch_url(self.urls_centreon['external_cmd_cmdPopup'] + '?' + urllib.parse.urlencode(cgi_data), giveback="raw") del raw # Starting from 19.04, must be POST else: # Do it in POST - raw = self.FetchURL(self.urls_centreon['external_cmd_cmdPopup'], cgi_data=cgi_data, giveback='raw') + raw = self.fetch_url(self.urls_centreon['external_cmd_cmdPopup'], cgi_data=cgi_data, giveback='raw') del raw except: - self.Error(sys.exc_info()) + self.error(sys.exc_info()) def _check_session(self): if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Checking session status') + self.debug(server=self.get_name(), debug='Checking session status') if 'url_centreon' not in self.__dict__: self.init_config() if self.centreon_version: try: if self.centreon_version >= 18.10: - result = self.FetchURL(self.urls_centreon['keepAlive'], giveback='raw') + result = self.fetch_url(self.urls_centreon['keepAlive'], giveback='raw') self.raw, self.error, self.status_code = result.result, result.error, result.status_code # Return 200 & null a session is open if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Session status : ' + self.raw + ', http code : ' + str(self.status_code)) + self.debug(server=self.get_name(), debug='Session status : ' + self.raw + ', http code : ' + str(self.status_code)) # 401 if no valid session is present if self.status_code == 401: self.SID = self._get_sid().result if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Session renewed') + self.debug(server=self.get_name(), debug='Session renewed') else: - result = self.FetchURL(self.urls_centreon['autologoutXMLresponse'], giveback='xml') + result = self.fetch_url(self.urls_centreon['autologoutXMLresponse'], giveback='xml') xmlobj, error, status_code = result.result, result.error, result.status_code self.session_state = xmlobj.find("state").text.lower() if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Session status : ' + self.session_state) + self.debug(server=self.get_name(), debug='Session status : ' + self.session_state) if self.session_state == "nok": self.SID = self._get_sid().result if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Session renewed') + self.debug(server=self.get_name(), debug='Session renewed') except: import traceback traceback.print_exc(file=sys.stdout) - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) else: return Result(result='ERROR', diff --git a/Nagstamon/Servers/Centreon/__init__.py b/Nagstamon/Servers/Centreon/__init__.py index 1aed1a7a4..360edb13e 100644 --- a/Nagstamon/Servers/Centreon/__init__.py +++ b/Nagstamon/Servers/Centreon/__init__.py @@ -37,21 +37,21 @@ def __init__(self, **kwds): self.ignore_cert = server_conf.ignore_cert self.custom_cert_use = server_conf.custom_cert_use # This URL exists on Centreon 22.x - if not accessible it must be legacy - versions_raw = self.FetchURL(f'{server_conf.monitor_cgi_url}/api/latest/platform/versions', no_auth=True, giveback='raw') - self.Debug(server='[' + self.get_name() + ']', debug='Status code %s' % (str(versions_raw.status_code))) + versions_raw = self.fetch_url(f'{server_conf.monitor_cgi_url}/api/latest/platform/versions', no_auth=True, giveback='raw') + self.debug(server='[' + self.get_name() + ']', debug='Status code %s' % (str(versions_raw.status_code))) if versions_raw.status_code == 200: data = json.loads(versions_raw.result) ver_major = int(data["web"]["major"]) ver_minor = int(data["web"]["minor"]) # API V2 is usable only after 21.04 (not tested), ressources endpoint is buggy in 20.10 if ver_major >= 21: - self.Debug(server='[' + self.get_name() + ']', debug='Loading class API, Centreon version : ' + str(ver_major) + '.' + str(ver_minor)) + self.debug(server='[' + self.get_name() + ']', debug='Loading class API, Centreon version : ' + str(ver_major) + '.' + str(ver_minor)) from .CentreonAPI import CentreonServer as CentreonServerReal else: - self.Debug(server='[' + self.get_name() + ']', debug='Loading class LEGACY, Centreon version : ' + str(ver_major) + '.' + str(ver_minor)) + self.debug(server='[' + self.get_name() + ']', debug='Loading class LEGACY, Centreon version : ' + str(ver_major) + '.' + str(ver_minor)) from .CentreonLegacy import CentreonServer as CentreonServerReal else: from .CentreonLegacy import CentreonServer as CentreonServerReal - self.Debug(server='[' + self.get_name() + ']', debug='Loading class LEGACY, Centreon version will be checked later') + self.debug(server='[' + self.get_name() + ']', debug='Loading class LEGACY, Centreon version will be checked later') # kind of mad but helps the Servers/__init__.py to detect if there is any other class to be used self.ClassServerReal = CentreonServerReal diff --git a/Nagstamon/Servers/Generic.py b/Nagstamon/Servers/Generic.py index cd0621112..6bc6cd2b4 100644 --- a/Nagstamon/Servers/Generic.py +++ b/Nagstamon/Servers/Generic.py @@ -1,5 +1,4 @@ # encoding: utf-8 - # Nagstamon - Nagios status monitor for your desktop # Copyright (C) 2008-2024 Henri Wahl <henri@nagstamon.de> et al. # @@ -20,6 +19,7 @@ from collections import OrderedDict import copy import datetime +import json from pathlib import Path import platform import socket @@ -419,7 +419,7 @@ def _set_recheck(self, host, service): return try: # get start time from Nagios as HTML to use same timezone setting like the locally installed Nagios - result = self.FetchURL( + result = self.fetch_url( self.monitor_cgi_url + '/cmd.cgi?' + urllib.parse.urlencode({'cmd_typ': '96', 'host': host})) self.start_time = dict(result.result.find(attrs={'name': 'start_time'}).attrs)['value'] # decision about host or service - they have different URLs @@ -438,7 +438,7 @@ def _set_recheck(self, host, service): ('force_check', 'on'), ('btnSubmit', 'Commit')]) # execute POST request - self.FetchURL(self.monitor_cgi_url + '/cmd.cgi', giveback='raw', cgi_data=cgi_data) + self.fetch_url(self.monitor_cgi_url + '/cmd.cgi', giveback='raw', cgi_data=cgi_data) except: traceback.print_exc(file=sys.stdout) @@ -497,14 +497,14 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi if sticky is True: cgi_data['sticky_ack'] = 'on' - self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + self.fetch_url(url, giveback='raw', cgi_data=cgi_data) # acknowledge all services on a host if all_services: for s in all_services: cgi_data['cmd_typ'] = '34' cgi_data['service'] = s - self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + self.fetch_url(url, giveback='raw', cgi_data=cgi_data) def set_downtime(self, info_dict): ''' @@ -548,7 +548,7 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t cgi_data['btnSubmit'] = 'Commit' # running remote cgi command - self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + self.fetch_url(url, giveback='raw', cgi_data=cgi_data) def set_submit_check_result(self, info_dict): """ @@ -574,7 +574,7 @@ def _set_submit_check_result(self, host, service, state, comment, check_output, ('plugin_state', {'up': '0', 'down': '1', 'unreachable': '2'}[state]), ('plugin_output', check_output), ('performance_data', performance_data), ('btnSubmit', 'Commit')]) - self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + self.fetch_url(url, giveback='raw', cgi_data=cgi_data) if service != '': # service @ host @@ -584,7 +584,7 @@ def _set_submit_check_result(self, host, service, state, comment, check_output, ('plugin_output', check_output), ('performance_data', performance_data), ('btnSubmit', 'Commit')]) # running remote cgi command - self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + self.fetch_url(url, giveback='raw', cgi_data=cgi_data) def get_start_end(self, host): ''' @@ -592,14 +592,14 @@ def get_start_end(self, host): directly from web interface ''' try: - result = self.FetchURL( + result = self.fetch_url( self.monitor_cgi_url + '/cmd.cgi?' + urllib.parse.urlencode({'cmd_typ': '55', 'host': host})) start_time = dict(result.result.find(attrs={'name': 'start_time'}).attrs)['value'] end_time = dict(result.result.find(attrs={'name': 'end_time'}).attrs)['value'] # give values back as tuple return start_time, end_time except Exception: - self.Error(sys.exc_info()) + self.error(sys.exc_info()) return 'n/a', 'n/a' def open_monitor(self, host, service=''): @@ -612,7 +612,7 @@ def open_monitor(self, host, service=''): else: typ = 2 if conf.debug_mode: - self.Debug(server=self.get_name(), host=host, service=service, + self.debug(server=self.get_name(), host=host, service=service, debug='Open host/service monitor web page ' + self.monitor_cgi_url + '/extinfo.cgi?' + urllib.parse.urlencode( {'type': typ, 'host': host, 'service': service})) webbrowser_open(self.monitor_cgi_url + '/extinfo.cgi?' + urllib.parse.urlencode( @@ -624,7 +624,7 @@ def open_monitor_webpage(self): ''' if conf.debug_mode: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug='Open monitor web page ' + self.monitor_cgi_url) webbrowser_open(self.monitor_url) @@ -645,7 +645,7 @@ def _get_status(self): # hosts must be analyzed separately try: for status_type in 'hard', 'soft': - result = self.FetchURL(self.cgiurl_hosts[status_type]) + result = self.fetch_url(self.cgiurl_hosts[status_type]) htobj, error, status_code = result.result, result.error, result.status_code # check if any error occured @@ -755,7 +755,7 @@ def _get_status(self): self.new_hosts[new_host].status_type = status_type del tds, n except Exception: - self.Error(sys.exc_info()) + self.error(sys.exc_info()) # do some cleanup htobj.decompose() @@ -764,13 +764,13 @@ def _get_status(self): except Exception: # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) # services try: for status_type in 'hard', 'soft': - result = self.FetchURL(self.cgiurl_services[status_type]) + result = self.fetch_url(self.cgiurl_services[status_type]) htobj, error, status_code = result.result, result.error, result.status_code # check if any error occured @@ -884,7 +884,7 @@ def _get_status(self): self.new_hosts[n['host']].services[new_service].status_type = status_type del tds, n except Exception: - self.Error(sys.exc_info()) + self.error(sys.exc_info()) # do some cleanup htobj.decompose() @@ -893,7 +893,7 @@ def _get_status(self): except Exception: # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) # some cleanup @@ -902,7 +902,7 @@ def _get_status(self): # dummy return in case all is OK return Result() - def GetStatus(self, output=None): + def get_status(self, output=None): ''' get nagios status information from cgiurl and give it back as dictionary @@ -1005,59 +1005,59 @@ def GetStatus(self, output=None): # Some generic filters if host.acknowledged is True and conf.filter_acknowledged_hosts_services is True: if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Filter: ACKNOWLEDGED ' + str(host.name)) + self.debug(server=self.get_name(), debug='Filter: ACKNOWLEDGED ' + str(host.name)) host.visible = False if host.notifications_disabled is True and\ conf.filter_hosts_services_disabled_notifications is True: if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Filter: NOTIFICATIONS ' + str(host.name)) + self.debug(server=self.get_name(), debug='Filter: NOTIFICATIONS ' + str(host.name)) host.visible = False if host.passiveonly is True and conf.filter_hosts_services_disabled_checks is True: if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Filter: PASSIVEONLY ' + str(host.name)) + self.debug(server=self.get_name(), debug='Filter: PASSIVEONLY ' + str(host.name)) host.visible = False if host.scheduled_downtime is True and conf.filter_hosts_services_maintenance is True: if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Filter: DOWNTIME ' + str(host.name)) + self.debug(server=self.get_name(), debug='Filter: DOWNTIME ' + str(host.name)) host.visible = False if host.flapping is True and conf.filter_all_flapping_hosts is True: if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Filter: FLAPPING HOST ' + str(host.name)) + self.debug(server=self.get_name(), debug='Filter: FLAPPING HOST ' + str(host.name)) host.visible = False # Checkmk and OP5 do not show the status_type so their host.status_type will be empty if host.status_type != '': if conf.filter_hosts_in_soft_state is True and host.status_type == 'soft': if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Filter: SOFT STATE ' + str(host.name)) + self.debug(server=self.get_name(), debug='Filter: SOFT STATE ' + str(host.name)) host.visible = False if host_is_filtered_out_by_re(host.name, conf) is True: if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Filter: REGEXP ' + str(host.name)) + self.debug(server=self.get_name(), debug='Filter: REGEXP ' + str(host.name)) host.visible = False if StatusInformationIsFilteredOutByRE(host.status_information, conf) is True: if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Filter: REGEXP ' + str(host.name)) + self.debug(server=self.get_name(), debug='Filter: REGEXP ' + str(host.name)) host.visible = False # The Criticality filter can be used only with centreon objects. Other objects don't have the criticality attribute. if self.type == 'Centreon': if CriticalityIsFilteredOutByRE(host.criticality, conf): if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Filter: REGEXP Criticality ' + str(host.name)) + self.debug(server=self.get_name(), debug='Filter: REGEXP Criticality ' + str(host.name)) host.visible = False # Finegrain for the specific state if host.status == 'DOWN': if conf.filter_all_down_hosts is True: if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Filter: DOWN ' + str(host.name)) + self.debug(server=self.get_name(), debug='Filter: DOWN ' + str(host.name)) host.visible = False if host.visible: @@ -1067,7 +1067,7 @@ def GetStatus(self, output=None): if host.status == 'UNREACHABLE': if conf.filter_all_unreachable_hosts is True: if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Filter: UNREACHABLE ' + str(host.name)) + self.debug(server=self.get_name(), debug='Filter: UNREACHABLE ' + str(host.name)) host.visible = False if host.visible: @@ -1090,66 +1090,66 @@ def GetStatus(self, output=None): # Some generic filtering if service.acknowledged is True and conf.filter_acknowledged_hosts_services is True: if conf.debug_mode: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug='Filter: ACKNOWLEDGED ' + str(host.name) + ';' + str(service.name)) service.visible = False if service.notifications_disabled is True and\ conf.filter_hosts_services_disabled_notifications is True: if conf.debug_mode: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug='Filter: NOTIFICATIONS ' + str(host.name) + ';' + str(service.name)) service.visible = False if service.passiveonly is True and conf.filter_hosts_services_disabled_checks is True: if conf.debug_mode: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug='Filter: PASSIVEONLY ' + str(host.name) + ';' + str(service.name)) service.visible = False if service.scheduled_downtime is True and conf.filter_hosts_services_maintenance is True: if conf.debug_mode: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug='Filter: DOWNTIME ' + str(host.name) + ';' + str(service.name)) service.visible = False if service.flapping is True and conf.filter_all_flapping_services is True: if conf.debug_mode: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug='Filter: FLAPPING SERVICE ' + str(host.name) + ';' + str(service.name)) service.visible = False if host.scheduled_downtime is True and conf.filter_services_on_hosts_in_maintenance is True: if conf.debug_mode: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug='Filter: Service on host in DOWNTIME ' + str(host.name) + ';' + str( service.name)) service.visible = False if host.acknowledged is True and conf.filter_services_on_acknowledged_hosts is True: if conf.debug_mode: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug='Filter: Service on acknowledged host' + str(host.name) + ';' + str( service.name)) service.visible = False if host.status == 'DOWN' and conf.filter_services_on_down_hosts is True: if conf.debug_mode: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug='Filter: Service on host in DOWN ' + str(host.name) + ';' + str(service.name)) service.visible = False if host.status == 'UNREACHABLE' and conf.filter_services_on_unreachable_hosts is True: if conf.debug_mode: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug='Filter: Service on host in UNREACHABLE ' + str(host.name) + ';' + str( service.name)) service.visible = False if conf.filter_all_unreachable_services is True and service.unreachable is True: if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Filter: UNREACHABLE ' + str(host.name) + ';' + str( + self.debug(server=self.get_name(), debug='Filter: UNREACHABLE ' + str(host.name) + ';' + str( service.name)) service.visible = False @@ -1157,7 +1157,7 @@ def GetStatus(self, output=None): if service.status_type != '': if conf.filter_services_in_soft_state is True and service.status_type == 'soft': if conf.debug_mode: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug='Filter: SOFT STATE ' + str(host.name) + ';' + str(service.name)) service.visible = False # fix for https://github.com/HenriWahl/Nagstamon/issues/654 @@ -1173,43 +1173,43 @@ def GetStatus(self, output=None): real_attempt, max_attempt = service.attempt.split('/') if real_attempt != max_attempt and conf.filter_services_in_soft_state is True: if conf.debug_mode: - self.Debug(server=self.get_name(), - debug='Filter: SOFT STATE ' + str(host.name) + ';' + str(service.name)) + self.debug(server=self.get_name(), + debug='Filter: SOFT STATE ' + str(host.name) + ';' + str(service.name)) service.visible = False if host_is_filtered_out_by_re(host.name, conf) is True: if conf.debug_mode: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug='Filter: REGEXP ' + str(host.name) + ';' + str(service.name)) service.visible = False if ServiceIsFilteredOutByRE(service.get_name(), conf) is True: if conf.debug_mode: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug='Filter: REGEXP ' + str(host.name) + ';' + str(service.name)) service.visible = False if StatusInformationIsFilteredOutByRE(service.status_information, conf) is True: if conf.debug_mode: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug='Filter: REGEXP ' + str(host.name) + ';' + str(service.name)) service.visible = False if DurationIsFilteredOutByRE(service.duration, conf) is True: if conf.debug_mode: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug='Filter: REGEXP ' + str(host.name) + ';' + str(service.name)) service.visible = False if AttemptIsFilteredOutByRE(service.attempt, conf) is True: if conf.debug_mode: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug='Filter: REGEXP ' + str(host.name) + ';' + str(service.name)) service.visible = False if GroupsIsFilteredOutByRE(service.groups, conf) is True: if conf.debug_mode: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug='Filter: REGEXP ' + str(host.name) + ';' + str(service.name)) service.visible = False @@ -1217,7 +1217,7 @@ def GetStatus(self, output=None): if self.type == 'Centreon': if CriticalityIsFilteredOutByRE(service.criticality, conf): if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Filter: REGEXP Criticality %s;%s %s' % ( + self.debug(server=self.get_name(), debug='Filter: REGEXP Criticality %s;%s %s' % ( (str(host.name), str(service.name), str(service.criticality)))) service.visible = False @@ -1226,7 +1226,7 @@ def GetStatus(self, output=None): if service.status == 'DISASTER': if conf.filter_all_disaster_services is True: if conf.debug_mode: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug='Filter: DISASTER ' + str(host.name) + ';' + str(service.name)) service.visible = False else: @@ -1236,7 +1236,7 @@ def GetStatus(self, output=None): if service.status == 'CRITICAL': if conf.filter_all_critical_services is True: if conf.debug_mode: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug='Filter: CRITICAL ' + str(host.name) + ';' + str(service.name)) service.visible = False else: @@ -1246,7 +1246,7 @@ def GetStatus(self, output=None): if service.status == 'HIGH': if conf.filter_all_high_services is True: if conf.debug_mode: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug='Filter: HIGH ' + str(host.name) + ';' + str(service.name)) service.visible = False else: @@ -1256,7 +1256,7 @@ def GetStatus(self, output=None): if service.status == 'AVERAGE': if conf.filter_all_average_services is True: if conf.debug_mode: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug='Filter: AVERAGE ' + str(host.name) + ';' + str(service.name)) service.visible = False else: @@ -1266,7 +1266,7 @@ def GetStatus(self, output=None): if service.status == 'WARNING': if conf.filter_all_warning_services is True: if conf.debug_mode: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug='Filter: WARNING ' + str(host.name) + ';' + str(service.name)) service.visible = False else: @@ -1276,7 +1276,7 @@ def GetStatus(self, output=None): if service.status == 'INFORMATION': if conf.filter_all_information_services is True: if conf.debug_mode: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug='Filter: INFORMATION ' + str(host.name) + ';' + str(service.name)) service.visible = False else: @@ -1286,7 +1286,7 @@ def GetStatus(self, output=None): if service.status == 'UNKNOWN': if conf.filter_all_unknown_services is True: if conf.debug_mode: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug='Filter: UNKNOWN ' + str(host.name) + ';' + str(service.name)) service.visible = False else: @@ -1437,12 +1437,13 @@ def GetStatus(self, output=None): # return True if all worked well return Result() - def FetchURL(self, url, giveback='obj', cgi_data=None, no_auth=False, multipart=False): + def fetch_url(self, url, giveback='obj', cgi_data=None, no_auth=False, multipart=False, headers=None): ''' get content of given url, cgi_data only used if present - 'obj' FetchURL gives back a dict full of miserable hosts/services, + 'obj' fetch_url() gives back a dict full of miserable hosts/services, 'xml' giving back as objectified xml 'raw' it gives back pure HTML - useful for finding out IP or new version + 'json' gives back JSON data existence of cgi_data forces urllib to use POST instead of GET requests NEW: gives back a list containing result and, if necessary, a more clear error description ''' @@ -1464,7 +1465,7 @@ def FetchURL(self, url, giveback='obj', cgi_data=None, no_auth=False, multipart= cgi_data_log[key] = '***************' else: cgi_data_log = cgi_data - self.Debug(server=self.get_name(), debug='FetchURL: ' + url + ' CGI Data: ' + str(cgi_data_log)) + self.debug(server=self.get_name(), debug='fetch_url: ' + url + ' CGI Data: ' + str(cgi_data_log)) if OS == OS_MACOS and self.cacert_path and not self.cacert_path.is_file(): # pyinstaller temp folder seems to be emptied completely after a while @@ -1494,9 +1495,9 @@ def FetchURL(self, url, giveback='obj', cgi_data=None, no_auth=False, multipart= # most requests come without multipart/form-data if multipart is False: if cgi_data is None: - response = self.session.get(url, timeout=self.timeout) + response = self.session.get(url, timeout=self.timeout, headers=headers) else: - response = self.session.post(url, data=cgi_data, timeout=self.timeout) + response = self.session.post(url, data=cgi_data, timeout=self.timeout, headers=headers) else: # Checkmk and Opsview need multipart/form-data encoding # http://stackoverflow.com/questions/23120974/python-requests-post-multipart-form-data-without-filename-in-http-request#23131823 @@ -1543,8 +1544,8 @@ def FetchURL(self, url, giveback='obj', cgi_data=None, no_auth=False, multipart= except Exception: if conf.debug_mode: - self.Error(sys.exc_info()) - result, error = self.Error(sys.exc_info()) + self.error(sys.exc_info()) + result, error = self.error(sys.exc_info()) if error.startswith('requests.exceptions.SSLError:'): self.tls_error = True else: @@ -1572,15 +1573,22 @@ def FetchURL(self, url, giveback='obj', cgi_data=None, no_auth=False, multipart= return Result(result=xmlobj, status_code=response.status_code) + # give back JSON giveback is 'raw' + if giveback == 'json': + # .text gives content in unicode + return Result(result=json.loads(response.text), + status_code=response.status_code) + + except Exception: - self.Error(sys.exc_info()) - result, error = self.Error(sys.exc_info()) + self.error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error, status_code=response.status_code) - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error, status_code=response.status_code) - def GetHost(self, host): + def get_host(self, host): ''' find out ip or hostname of given host to access hosts/devices which do not appear in DNS but have their ip saved in Nagios @@ -1597,7 +1605,7 @@ def GetHost(self, host): cgiurl_host = self.monitor_cgi_url + '/extinfo.cgi?type=1&host=' + host # get host info - result = self.FetchURL(cgiurl_host, giveback='obj') + result = self.fetch_url(cgiurl_host, giveback='obj') htobj = result.result try: @@ -1615,7 +1623,7 @@ def GetHost(self, host): # print IP in debug mode if conf.debug_mode is True: - self.Debug(server=self.get_name(), host=host, debug='IP of %s:' % (host) + ' ' + ip) + self.debug(server=self.get_name(), host=host, debug='IP of %s:' % (host) + ' ' + ip) # when connection by DNS is not configured do it by IP if conf.connect_by_dns is True: # try to get DNS name for ip, if not available use ip @@ -1626,7 +1634,7 @@ def GetHost(self, host): else: address = ip except Exception: - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) # do some cleanup @@ -1635,7 +1643,7 @@ def GetHost(self, host): # give back host or ip return Result(result=address) - def GetItemsGenerator(self): + def get_items_generator(self): ''' Generator for plain listing of all filtered items, used in QUI for tableview ''' @@ -1655,14 +1663,14 @@ def GetItemsGenerator(self): self.nagitems_filtered_count += 1 yield (service) - def Hook(self): + def hook(self): ''' allows to add some extra actions for a monitor server to be executed in RefreshLoop inspired by Centreon and its seemingly Alzheimer disease regarding session ID/Cookie/whatever ''' pass - def Error(self, error): + def error(self, error): ''' Handle errors somehow - print them or later log them into not yet existing log file ''' @@ -1670,11 +1678,11 @@ def Error(self, error): debug = '' for line in traceback.format_exception(error[0], error[1], error[2], 5): debug += line - self.Debug(server=self.get_name(), debug=debug, head='ERROR') + self.debug(server=self.get_name(), debug=debug, head='ERROR') return ['ERROR', traceback.format_exception_only(error[0], error[1])[0]] - def Debug(self, server='', host='', service='', debug='', head='DEBUG'): + def debug(self, server='', host='', service='', debug='', head='DEBUG'): ''' centralized debugging ''' diff --git a/Nagstamon/Servers/Icinga.py b/Nagstamon/Servers/Icinga.py index d67b47cf6..4b7253b2a 100644 --- a/Nagstamon/Servers/Icinga.py +++ b/Nagstamon/Servers/Icinga.py @@ -61,7 +61,7 @@ def get_server_version(self): """ Try to get Icinga version for different URLs and JSON capabilities """ - result = self.FetchURL('%s/tac.cgi?jsonoutput' % (self.monitor_cgi_url), giveback='raw') + result = self.fetch_url('%s/tac.cgi?jsonoutput' % (self.monitor_cgi_url), giveback='raw') if result.error != '': return result else: @@ -140,7 +140,7 @@ def _get_status(self): except: # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) # dummy return in case all is OK @@ -158,7 +158,7 @@ def _get_status_JSON(self): # now using JSON output from Icinga try: for status_type in 'hard', 'soft': - result = self.FetchURL(self.cgiurl_hosts[status_type], giveback='raw') + result = self.fetch_url(self.cgiurl_hosts[status_type], giveback='raw') # purify JSON result of unnecessary control sequence \n jsonraw, error, status_code = copy.deepcopy(result.result.replace('\n', '')),\ copy.deepcopy(result.error),\ @@ -216,13 +216,13 @@ def _get_status_JSON(self): except: # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) # services try: for status_type in 'hard', 'soft': - result = self.FetchURL(self.cgiurl_services[status_type], giveback='raw') + result = self.fetch_url(self.cgiurl_services[status_type], giveback='raw') # purify JSON result of unnecessary control sequence \n jsonraw, error, status_code = copy.deepcopy(result.result.replace('\n', '')),\ copy.deepcopy(result.error),\ @@ -303,7 +303,7 @@ def _get_status_JSON(self): except: # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) # some cleanup @@ -331,7 +331,7 @@ def _get_status_HTML(self): # hosts must be analyzed separately try: for status_type in 'hard', 'soft': - result = self.FetchURL(self.cgiurl_hosts[status_type]) + result = self.fetch_url(self.cgiurl_hosts[status_type]) htobj, error, status_code = result.result,\ result.error,\ result.status_code @@ -445,7 +445,7 @@ def _get_status_HTML(self): # some cleanup del tds, n except: - self.Error(sys.exc_info()) + self.error(sys.exc_info()) # do some cleanup htobj.decompose() @@ -454,13 +454,13 @@ def _get_status_HTML(self): except: # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) # services try: for status_type in 'hard', 'soft': - result = self.FetchURL(self.cgiurl_services[status_type]) + result = self.fetch_url(self.cgiurl_services[status_type]) htobj, error, status_code = result.result,\ result.error,\ result.status_code @@ -582,7 +582,7 @@ def _get_status_HTML(self): # some cleanup del tds, n except: - self.Error(sys.exc_info()) + self.error(sys.exc_info()) # do some cleanup htobj.decompose() @@ -591,7 +591,7 @@ def _get_status_HTML(self): except: # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) # some cleanup @@ -611,7 +611,7 @@ def _set_recheck(self, host, service): # Do not check passive only checks return # get start time from Nagios as HTML to use same timezone setting like the locally installed Nagios - result = self.FetchURL(self.monitor_cgi_url + '/cmd.cgi?' + urllib.parse.urlencode({'cmd_typ':'96', 'host':host})) + result = self.fetch_url(self.monitor_cgi_url + '/cmd.cgi?' + urllib.parse.urlencode({'cmd_typ': '96', 'host':host})) self.start_time = dict(result.result.find(attrs={'name':'start_time'}).attrs)['value'] # decision about host or service - they have different URLs @@ -631,7 +631,7 @@ def _set_recheck(self, host, service): ('com_data', 'Recheck by %s' % self.username), \ ('btnSubmit', 'Commit')]) # execute POST request - self.FetchURL(self.monitor_cgi_url + '/cmd.cgi', giveback='raw', cgi_data=cgi_data) + self.fetch_url(self.monitor_cgi_url + '/cmd.cgi', giveback='raw', cgi_data=cgi_data) def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=None): @@ -677,13 +677,13 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi if sticky: cgi_data['sticky_ack'] = '1' - self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + self.fetch_url(url, giveback='raw', cgi_data=cgi_data) # acknowledge all services on a host if all_services: for s in all_services: cgi_data['cmd_typ'] = '34' cgi_data['service'] = s - self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + self.fetch_url(url, giveback='raw', cgi_data=cgi_data) diff --git a/Nagstamon/Servers/Icinga2API.py b/Nagstamon/Servers/Icinga2API.py index 16298646b..1762fae73 100644 --- a/Nagstamon/Servers/Icinga2API.py +++ b/Nagstamon/Servers/Icinga2API.py @@ -116,7 +116,7 @@ def _get_status(self): except Exception as e: # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) log.exception(e) return Result(result=result, error=error) @@ -158,7 +158,7 @@ def _get_status(self): log.exception(e) # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) # dummy return in case all is OK @@ -166,7 +166,7 @@ def _get_status(self): def _list_objects(self, object_type, filter): """List objects""" - result = self.FetchURL( + result = self.fetch_url( f'{self.url}/objects/{object_type}?{urllib.parse.urlencode({"filter": filter})}', giveback='raw' ) @@ -200,24 +200,24 @@ def _get_host_events(self): def _trigger_action(self, action, **data): """Trigger on action using Icinga2 API""" action_data = {k: v for k, v in data.items() if v is not None} - self.Debug(server=self.get_name(), debug=f"Trigger action {action} with data={action_data}") + self.debug(server=self.get_name(), debug=f"Trigger action {action} with data={action_data}") try: response = self.session.post( f'{self.url}/actions/{action}', headers={'Accept': 'application/json'}, json=action_data, ) - self.Debug( + self.debug( server=self.get_name(), debug=f"API return on triggering action {action} (status={response.status_code}): " f"{response.text}" ) if 200 <= response.status_code <= 299: return True - self.Error(f"Fail to trigger action {action}: {response.json().get('status', 'Unknown error')}") + self.error(f"Fail to trigger action {action}: {response.json().get('status', 'Unknown error')}") except IOError as err: log.exception("Fail to trigger action %s with data %s", action, data) - self.Error(f"Fail to trigger action {action}: {err}") + self.error(f"Fail to trigger action {action}: {err}") def _set_recheck(self, host, service): """ diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index 3d637c536..3befe0023 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -100,7 +100,7 @@ def init_HTTP(self): if not self.no_cookie_auth: if 'cookies' not in dir(self.session) or len(self.session.cookies) == 0: # get login page, thus automatically a cookie - login = self.FetchURL('{0}/authentication/login'.format(self.monitor_url)) + login = self.fetch_url('{0}/authentication/login'.format(self.monitor_url)) if login.error == '' and login.status_code == 200: form = login.result.find('form') form_inputs = {} @@ -113,7 +113,7 @@ def init_HTTP(self): form_inputs['password'] = self.password # fire up login button with all needed data - self.FetchURL('{0}/authentication/login'.format(self.monitor_url), cgi_data=form_inputs) + self.fetch_url('{0}/authentication/login'.format(self.monitor_url), cgi_data=form_inputs) def _get_status(self): @@ -139,14 +139,14 @@ def _get_status(self): try: for status_type in 'hard', 'soft': # first attempt - result = self.FetchURL(self.cgiurl_hosts[status_type], giveback='raw') + result = self.fetch_url(self.cgiurl_hosts[status_type], giveback='raw') # authentication errors get a status code 200 too back because its # HTML works fine :-( if result.status_code < 400 and\ result.result.startswith('<'): # in case of auth error reset HTTP session and try again self.reset_HTTP() - result = self.FetchURL(self.cgiurl_hosts[status_type], giveback='raw') + result = self.fetch_url(self.cgiurl_hosts[status_type], giveback='raw') # if it does not work again tell GUI there is a problem if result.status_code < 400 and\ result.result.startswith('<'): @@ -172,7 +172,7 @@ def _get_status(self): pass # TODO: Health checks for IcingaDB and icinga-redis # try: - # result = self.FetchURL(self.cgiurl_monitoring_health, giveback='raw') + # result = self.fetch_url(self.cgiurl_monitoring_health, giveback='raw') # monitoring_health = json.loads(result.result)[0] # if (monitoring_health['is_currently_running'] == '0'): # return Result(result=monitoring_health, @@ -254,13 +254,13 @@ def _get_status(self): # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) # services try: for status_type in 'hard', 'soft': - result = self.FetchURL(self.cgiurl_services[status_type], giveback='raw') + result = self.fetch_url(self.cgiurl_services[status_type], giveback='raw') # purify JSON result of unnecessary control sequence \n jsonraw, error, status_code = copy.deepcopy(result.result.replace('\n', '')),\ copy.deepcopy(result.error),\ @@ -362,7 +362,7 @@ def _get_status(self): # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) # some cleanup @@ -385,7 +385,7 @@ def _set_recheck(self, host, service): self.hosts[host].services[service].real_name, self.hosts[host].real_name ) - result = self.FetchURL(url, giveback='raw') + result = self.fetch_url(url, giveback='raw') if result.error != '': return result @@ -394,22 +394,13 @@ def _set_recheck(self, host, service): pagesoup = BeautifulSoup(pageraw, 'html.parser') print(pagesoup.prettify()) - - ## Super debug - #if conf.debug_mode: - # self.Debug(server=self.get_name(), host=host, service=service, - # debug='[Recheck] Retrieve html from {0}: \n{1}'.format(url,pagesoup.prettify())) # Extract the relevant form element values - formtag = pagesoup.select_one('form[action*="check-now"]') - #print('-----------------') - #print(formtag.prettify()) - #print('-----------------') - + if conf.debug_mode: - self.Debug(server=self.get_name(), host=host, service=service, - debug='[Recheck] Retrieve html form from {0}: \n{1}'.format(url,formtag.prettify())) + self.debug(server=self.get_name(), host=host, service=service, + debug='[Recheck] Retrieve html form from {0}: \n{1}'.format(url,formtag.prettify())) btn_submit = formtag.findNext('button', {'name':'btn_submit'})['value'] CSRFToken = formtag.findNext('input', {'name':'CSRFToken'})['value'] @@ -427,23 +418,23 @@ def _set_recheck(self, host, service): self.hosts[host].real_name ) - response = self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + response = self.fetch_url(url, giveback='raw', cgi_data=cgi_data) # Some debug data data = response.result error = response.error status_code = response.status_code if conf.debug_mode: - self.Debug(server=self.get_name(), host=host, service=service, - debug='Recheck response') - self.Debug(server=self.get_name(), host=host, service=service, - debug="- response: {0}".format(response)) - self.Debug(server=self.get_name(), host=host, service=service, - debug="- data: {0}".format(data)) - self.Debug(server=self.get_name(), host=host, service=service, - debug="- error: {0}".format(error)) - self.Debug(server=self.get_name(), host=host, service=service, - debug="- status_code: {0}".format(status_code)) + self.debug(server=self.get_name(), host=host, service=service, + debug='Recheck response') + self.debug(server=self.get_name(), host=host, service=service, + debug="- response: {0}".format(response)) + self.debug(server=self.get_name(), host=host, service=service, + debug="- data: {0}".format(data)) + self.debug(server=self.get_name(), host=host, service=service, + debug="- error: {0}".format(error)) + self.debug(server=self.get_name(), host=host, service=service, + debug="- status_code: {0}".format(status_code)) # Overwrite function from generic server to add expire_time value @@ -473,7 +464,7 @@ def set_acknowledge(self, info_dict): except: import traceback traceback.print_exc(file=sys.stdout) - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) @@ -492,7 +483,7 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi self.hosts[host].real_name ) - result = self.FetchURL(url, giveback='raw') + result = self.fetch_url(url, giveback='raw') if result.error != '': return result @@ -501,21 +492,15 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi pagesoup = BeautifulSoup(pageraw, 'html.parser') - ## Super debug - #if conf.debug_mode: - # self.Debug(server=self.get_name(), host=host, service=service, - # debug='[Acknowledge] Retrieve html from {0}: {1}'.format(url,pagesoup.prettify())) - # Extract the relevant form element values - formtag = pagesoup.select_one('form[action*="acknowledge"]') #print('-----------------') #print(formtag.prettify()) #print('-----------------') if conf.debug_mode: - self.Debug(server=self.get_name(), host=host, service=service, - debug='[Acknowledge] Retrieve html form from {0}: \n{1}'.format(url,formtag.prettify())) + self.debug(server=self.get_name(), host=host, service=service, + debug='[Acknowledge] Retrieve html form from {0}: \n{1}'.format(url,formtag.prettify())) btn_submit = formtag.findNext('input', {'name':'btn_submit'})['value'] CSRFToken = formtag.findNext('input', {'name':'CSRFToken'})['value'] @@ -534,23 +519,23 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi else: cgi_data['expire'] = 'n' - response = self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + response = self.fetch_url(url, giveback='raw', cgi_data=cgi_data) # Some debug data data = response.result error = response.error status_code = response.status_code if conf.debug_mode: - self.Debug(server=self.get_name(), host=host, service=service, - debug='Achnowledgement response') - self.Debug(server=self.get_name(), host=host, service=service, - debug="- response: {0}".format(response)) - self.Debug(server=self.get_name(), host=host, service=service, - debug="- data: {0}".format(data)) - self.Debug(server=self.get_name(), host=host, service=service, - debug="- error: {0}".format(error)) - self.Debug(server=self.get_name(), host=host, service=service, - debug="- status_code: {0}".format(status_code)) + self.debug(server=self.get_name(), host=host, service=service, + debug='Achnowledgement response') + self.debug(server=self.get_name(), host=host, service=service, + debug="- response: {0}".format(response)) + self.debug(server=self.get_name(), host=host, service=service, + debug="- data: {0}".format(data)) + self.debug(server=self.get_name(), host=host, service=service, + debug="- error: {0}".format(error)) + self.debug(server=self.get_name(), host=host, service=service, + debug="- status_code: {0}".format(status_code)) if len(all_services) > 0: for s in all_services: @@ -575,7 +560,7 @@ def _set_submit_check_result(self, host, service, state, comment, check_output, ) status = self.STATES_MAPPING_REV['services'][state.upper()] - result = self.FetchURL(url, giveback='raw') + result = self.fetch_url(url, giveback='raw') if result.error != '': return result @@ -584,21 +569,15 @@ def _set_submit_check_result(self, host, service, state, comment, check_output, pagesoup = BeautifulSoup(pageraw, 'html.parser') - ## Super debug - #if conf.debug_mode: - # self.Debug(server=self.get_name(), host=host, service=service, - # debug='[Submit check result] Retrieve html from {0}: \n{1}'.format(url,pagesoup.prettify())) - # Extract the relevant form element values - formtag = pagesoup.select_one('form[action*="process-checkresult"]') #print('-----------------') #print(formtag.prettify()) #print('-----------------') if conf.debug_mode: - self.Debug(server=self.get_name(), host=host, service=service, - debug='[Submit check result] Retrieve html form from {0}: \n{1}'.format(url,formtag.prettify())) + self.debug(server=self.get_name(), host=host, service=service, + debug='[Submit check result] Retrieve html form from {0}: \n{1}'.format(url,formtag.prettify())) btn_submit = formtag.findNext('input', {'name':'btn_submit'})['value'] CSRFToken = formtag.findNext('input', {'name':'CSRFToken'})['value'] @@ -612,23 +591,23 @@ def _set_submit_check_result(self, host, service, state, comment, check_output, cgi_data['output'] = check_output cgi_data['perfdata'] = performance_data - response = self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + response = self.fetch_url(url, giveback='raw', cgi_data=cgi_data) # Some debug data data = response.result error = response.error status_code = response.status_code if conf.debug_mode: - self.Debug(server=self.get_name(), host=host, service=service, - debug='Achnowledgement response') - self.Debug(server=self.get_name(), host=host, service=service, - debug="- response: {0}".format(response)) - self.Debug(server=self.get_name(), host=host, service=service, - debug="- data: {0}".format(data)) - self.Debug(server=self.get_name(), host=host, service=service, - debug="- error: {0}".format(error)) - self.Debug(server=self.get_name(), host=host, service=service, - debug="- status_code: {0}".format(status_code)) + self.debug(server=self.get_name(), host=host, service=service, + debug='Achnowledgement response') + self.debug(server=self.get_name(), host=host, service=service, + debug="- response: {0}".format(response)) + self.debug(server=self.get_name(), host=host, service=service, + debug="- data: {0}".format(data)) + self.debug(server=self.get_name(), host=host, service=service, + debug="- error: {0}".format(error)) + self.debug(server=self.get_name(), host=host, service=service, + debug="- status_code: {0}".format(status_code)) def _set_downtime(self, host, service, author, comment, fixed, start_time, end_time, hours, minutes): @@ -646,7 +625,7 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t self.hosts[host].real_name ) - result = self.FetchURL(url, giveback='raw') + result = self.fetch_url(url, giveback='raw') if result.error != '': return result @@ -655,21 +634,15 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t pagesoup = BeautifulSoup(pageraw, 'html.parser') - ## Super debug - #if conf.debug_mode: - # self.Debug(server=self.get_name(), host=host, service=service, - # debug='[Set downtime] Retrieve html from {0}: \n{1}'.format(url,pagesoup.prettify())) - # Extract the relevant form element values - formtag = pagesoup.select_one('form[action*="schedule-downtime"]') #print('-----------------') #print(formtag.prettify()) #print('-----------------') if conf.debug_mode: - self.Debug(server=self.get_name(), host=host, service=service, - debug='[Set downtime] Retrieve html form from {0}: \n{1}'.format(url,formtag.prettify())) + self.debug(server=self.get_name(), host=host, service=service, + debug='[Set downtime] Retrieve html form from {0}: \n{1}'.format(url,formtag.prettify())) btn_submit = formtag.findNext('input', {'name':'btn_submit'})['value'] CSRFToken = formtag.findNext('input', {'name':'CSRFToken'})['value'] @@ -697,23 +670,23 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t cgi_data['start'] = start cgi_data['end'] = end - response = self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + response = self.fetch_url(url, giveback='raw', cgi_data=cgi_data) # Some debug data data = response.result error = response.error status_code = response.status_code if conf.debug_mode: - self.Debug(server=self.get_name(), host=host, service=service, - debug='Achnowledgement response') - self.Debug(server=self.get_name(), host=host, service=service, - debug="- response: {0}".format(response)) - self.Debug(server=self.get_name(), host=host, service=service, - debug="- data: {0}".format(data)) - self.Debug(server=self.get_name(), host=host, service=service, - debug="- error: {0}".format(error)) - self.Debug(server=self.get_name(), host=host, service=service, - debug="- status_code: {0}".format(status_code)) + self.debug(server=self.get_name(), host=host, service=service, + debug='Achnowledgement response') + self.debug(server=self.get_name(), host=host, service=service, + debug="- response: {0}".format(response)) + self.debug(server=self.get_name(), host=host, service=service, + debug="- data: {0}".format(data)) + self.debug(server=self.get_name(), host=host, service=service, + debug="- error: {0}".format(error)) + self.debug(server=self.get_name(), host=host, service=service, + debug="- status_code: {0}".format(status_code)) def get_start_end(self, host): @@ -727,7 +700,7 @@ def get_start_end(self, host): try: url = '{0}/icingadb/host/schedule-downtime?name={1}'.format(self.monitor_cgi_url, self.hosts[host].real_name) - downtime = self.FetchURL(url, giveback='raw') + downtime = self.fetch_url(url, giveback='raw') if downtime.error != '': return 'n/a', 'n/a' @@ -742,15 +715,15 @@ def get_start_end(self, host): # Super debug if conf.debug_mode: - self.Debug(server=self.get_name(), host=host, service='', - debug='[Get downtime start/end] Retrieve html from {0}: {1}'.format(url,pagesoup.prettify())) + self.debug(server=self.get_name(), host=host, service='', + debug='[Get downtime start/end] Retrieve html from {0}: {1}'.format(url,pagesoup.prettify())) start = pagesoup.find('input', {'name': 'start'})['value'] end = pagesoup.find('input', {'name': 'end'})['value'] # give values back as tuple return start, end except: - self.Error(sys.exc_info()) + self.error(sys.exc_info()) return 'n/a', 'n/a' @@ -776,11 +749,11 @@ def open_monitor(self, host, service=''): {'name': self.hosts[host].services[service].real_name, 'host.name': self.hosts[host].real_name}).replace('+', ' ')) if conf.debug_mode: - self.Debug(server=self.get_name(), host=host, service=service, + self.debug(server=self.get_name(), host=host, service=service, debug='[Open monitor] Open host/service monitor web page {0}'.format(url)) webbrowser_open(url) - def GetHost(self, host): + def get_host(self, host): ''' find out ip or hostname of given host to access hosts/devices which do not appear in DNS but have their ip saved in Icinga @@ -801,7 +774,7 @@ def GetHost(self, host): cgiurl_host = self.monitor_cgi_url + '/icingadb/hosts?name={0}&columns=host.address&format=json'.format(host) # get host info - hostobj = self.FetchURL(cgiurl_host, giveback='raw') + hostobj = self.fetch_url(cgiurl_host, giveback='raw') jsonhost = hostobj.result try: @@ -811,7 +784,7 @@ def GetHost(self, host): # print IP in debug mode if conf.debug_mode is True: - self.Debug(server=self.get_name(), host=host, debug='IP of %s:' % (host) + ' ' + ip) + self.debug(server=self.get_name(), host=host, debug='IP of %s:' % (host) + ' ' + ip) # when connection by DNS is not configured do it by IP if conf.connect_by_dns is True: @@ -823,7 +796,7 @@ def GetHost(self, host): else: address = ip except Exception: - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) # do some cleanup diff --git a/Nagstamon/Servers/IcingaDBWebNotifications.py b/Nagstamon/Servers/IcingaDBWebNotifications.py index 73e28acf8..be60ba10c 100644 --- a/Nagstamon/Servers/IcingaDBWebNotifications.py +++ b/Nagstamon/Servers/IcingaDBWebNotifications.py @@ -74,7 +74,7 @@ def _get_status(self) -> Result: # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) def _update_new_host_content(self) -> Result: @@ -82,7 +82,7 @@ def _update_new_host_content(self) -> Result: notification_url = "{}/icingadb/notifications?{}&history.event_time>{} ago&format=json".format( self.monitor_cgi_url, self.notification_filter, self.notification_lookback) health_url = '{}/health?format=json'.format(self.monitor_cgi_url) - result = self.FetchURL(notification_url, giveback='raw') + result = self.fetch_url(notification_url, giveback='raw') # check if any error occurred potential_error = self.check_for_error(result.result, result.error, result.status_code) @@ -90,7 +90,7 @@ def _update_new_host_content(self) -> Result: return potential_error # HEALTH CHECK - health_result = self.FetchURL(health_url, giveback='raw') + health_result = self.fetch_url(health_url, giveback='raw') if health_result.status_code == 200: # we already got check results so icinga is unlikely down. do not break it without need. monitoring_health_results = json.loads(health_result.result) diff --git a/Nagstamon/Servers/IcingaWeb2.py b/Nagstamon/Servers/IcingaWeb2.py index 9cc17f552..b2e59b862 100644 --- a/Nagstamon/Servers/IcingaWeb2.py +++ b/Nagstamon/Servers/IcingaWeb2.py @@ -99,7 +99,7 @@ def init_HTTP(self): if not self.no_cookie_auth: if 'cookies' not in dir(self.session) or len(self.session.cookies) == 0: # get login page, thus automatically a cookie - login = self.FetchURL('{0}/authentication/login'.format(self.monitor_url)) + login = self.fetch_url('{0}/authentication/login'.format(self.monitor_url)) if login.error == '' and login.status_code == 200: form = login.result.find('form') form_inputs = {} @@ -112,7 +112,7 @@ def init_HTTP(self): form_inputs['password'] = self.password # fire up login button with all needed data - self.FetchURL('{0}/authentication/login'.format(self.monitor_url), cgi_data=form_inputs) + self.fetch_url('{0}/authentication/login'.format(self.monitor_url), cgi_data=form_inputs) def _get_status(self): @@ -138,14 +138,14 @@ def _get_status(self): try: for status_type in 'hard', 'soft': # first attempt - result = self.FetchURL(self.cgiurl_hosts[status_type], giveback='raw') + result = self.fetch_url(self.cgiurl_hosts[status_type], giveback='raw') # authentication errors get a status code 200 too back because its # HTML works fine :-( if result.status_code < 400 and\ result.result.startswith('<'): # in case of auth error reset HTTP session and try again self.reset_HTTP() - result = self.FetchURL(self.cgiurl_hosts[status_type], giveback='raw') + result = self.fetch_url(self.cgiurl_hosts[status_type], giveback='raw') # if it does not work again tell GUI there is a problem if result.status_code < 400 and\ result.result.startswith('<'): @@ -172,7 +172,7 @@ def _get_status(self): # Unfortunately we need to make a extra request for this and only, if monitoring health is possible if self.cgiurl_monitoring_health: try: - result = self.FetchURL(self.cgiurl_monitoring_health, giveback='raw') + result = self.fetch_url(self.cgiurl_monitoring_health, giveback='raw') monitoring_health = json.loads(result.result)[0] if (monitoring_health['is_currently_running'] == '0'): return Result(result=monitoring_health, @@ -243,13 +243,13 @@ def _get_status(self): # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) # services try: for status_type in 'hard', 'soft': - result = self.FetchURL(self.cgiurl_services[status_type], giveback='raw') + result = self.fetch_url(self.cgiurl_services[status_type], giveback='raw') # purify JSON result of unnecessary control sequence \n jsonraw, error, status_code = copy.deepcopy(result.result.replace('\n', '')),\ copy.deepcopy(result.error),\ @@ -340,7 +340,7 @@ def _get_status(self): # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) # some cleanup @@ -359,7 +359,7 @@ def _set_recheck(self, host, service): url = self.monitor_cgi_url + \ '/monitoring/service/show?host=' + self.hosts[host].real_name + \ '&service=' + urllib.parse.quote(self.hosts[host].services[service].real_name) - result = self.FetchURL(url, giveback='raw') + result = self.fetch_url(url, giveback='raw') if result.error != '': return result @@ -381,10 +381,10 @@ def _set_recheck(self, host, service): cgi_data['CSRFToken'] = CSRFToken cgi_data['formUID'] = formUID cgi_data['btn_submit'] = btn_submit - self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + self.fetch_url(url, giveback='raw', cgi_data=cgi_data) except AttributeError: if conf.debug_mode: - self.Debug(server=self.get_name(), host=host, service=service, + self.debug(server=self.get_name(), host=host, service=service, debug='No valid CSRFToken available') # Overwrite function from generic server to add expire_time value @@ -422,7 +422,7 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi url = '{0}/monitoring/service/acknowledge-problem?host={1}&service={2}'.format(self.monitor_cgi_url, self.hosts[host].real_name, urllib.parse.quote(self.hosts[host].services[service].real_name)) - result = self.FetchURL(url, giveback='raw') + result = self.fetch_url(url, giveback='raw') if result.error != '': return result @@ -454,11 +454,11 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi cgi_data['expire'] = 1 cgi_data['expire_time'] = expire_time - self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + self.fetch_url(url, giveback='raw', cgi_data=cgi_data) except AttributeError: if conf.debug_mode: - self.Debug(server=self.get_name(), host=host, service=service, + self.debug(server=self.get_name(), host=host, service=service, debug='No valid CSRFToken available') if len(all_services) > 0: @@ -479,7 +479,7 @@ def _set_submit_check_result(self, host, service, state, comment, check_output, '&service=' + urllib.parse.quote(self.hosts[host].services[service].real_name) status = self.STATES_MAPPING_REV['services'][state.upper()] - result = self.FetchURL(url, giveback='raw') + result = self.fetch_url(url, giveback='raw') if result.error != '': return result @@ -506,11 +506,11 @@ def _set_submit_check_result(self, host, service, state, comment, check_output, cgi_data['output'] = check_output cgi_data['perfdata'] = performance_data - self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + self.fetch_url(url, giveback='raw', cgi_data=cgi_data) except AttributeError: if conf.debug_mode: - self.Debug(server=self.get_name(), host=host, service=service, + self.debug(server=self.get_name(), host=host, service=service, debug='No valid CSRFToken available') def _set_downtime(self, host, service, author, comment, fixed, start_time, end_time, hours, minutes): @@ -522,7 +522,7 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t '/monitoring/service/schedule-downtime?host=' + self.hosts[host].real_name + \ '&service=' + urllib.parse.quote(self.hosts[host].services[service].real_name) - result = self.FetchURL(url, giveback='raw') + result = self.fetch_url(url, giveback='raw') if result.error != '': return result @@ -567,11 +567,11 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t cgi_data['start'] = start cgi_data['end'] = end - self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + self.fetch_url(url, giveback='raw', cgi_data=cgi_data) except AttributeError: if conf.debug_mode: - self.Debug(server=self.get_name(), host=host, service=service, + self.debug(server=self.get_name(), host=host, service=service, debug='No valid CSRFToken available') def get_start_end(self, host): @@ -580,13 +580,13 @@ def get_start_end(self, host): directly from web interface ''' try: - downtime = self.FetchURL(self.monitor_cgi_url + '/monitoring/host/schedule-downtime?host=' + self.hosts[host].real_name) + downtime = self.fetch_url(self.monitor_cgi_url + '/monitoring/host/schedule-downtime?host=' + self.hosts[host].real_name) start = downtime.result.find('input', {'name': 'start'})['value'] end = downtime.result.find('input', {'name': 'end'})['value'] # give values back as tuple return start, end except: - self.Error(sys.exc_info()) + self.error(sys.exc_info()) return 'n/a', 'n/a' @@ -607,11 +607,11 @@ def open_monitor(self, host, service=''): {'host': self.hosts[host].real_name, 'service': self.hosts[host].services[service].real_name}).replace('+', ' ')) if conf.debug_mode: - self.Debug(server=self.get_name(), host=host, service=service, + self.debug(server=self.get_name(), host=host, service=service, debug='Open host/service monitor web page {0}'.format(url)) webbrowser_open(url) - def GetHost(self, host): + def get_host(self, host): ''' find out ip or hostname of given host to access hosts/devices which do not appear in DNS but have their ip saved in Icinga @@ -632,7 +632,7 @@ def GetHost(self, host): cgiurl_host = self.monitor_cgi_url + '/monitoring/list/hosts?host={0}&addColumns=host_address&format=json'.format(host) # get host info - hostobj = self.FetchURL(cgiurl_host, giveback='raw') + hostobj = self.fetch_url(cgiurl_host, giveback='raw') jsonhost = hostobj.result try: @@ -642,7 +642,7 @@ def GetHost(self, host): # print IP in debug mode if conf.debug_mode is True: - self.Debug(server=self.get_name(), host=host, debug='IP of %s:' % (host) + ' ' + ip) + self.debug(server=self.get_name(), host=host, debug='IP of %s:' % (host) + ' ' + ip) # when connection by DNS is not configured do it by IP if conf.connect_by_dns is True: @@ -654,7 +654,7 @@ def GetHost(self, host): else: address = ip except Exception: - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) # do some cleanup diff --git a/Nagstamon/Servers/Monitos4x.py b/Nagstamon/Servers/Monitos4x.py index 78d8b059b..d131c7bc9 100644 --- a/Nagstamon/Servers/Monitos4x.py +++ b/Nagstamon/Servers/Monitos4x.py @@ -128,9 +128,9 @@ def init_HTTP(self): form_inputs['_password'] = self.password # call login page to get temporary cookie - self.FetchURL('{0}/security/login'.format(self.monitor_url)) + self.fetch_url('{0}/security/login'.format(self.monitor_url)) # submit login form to retrieve authentication cookie - self.FetchURL( + self.fetch_url( '{0}/security/login_check'.format(self.monitor_url), cgi_data=form_inputs, multipart=True @@ -170,7 +170,7 @@ def _get_status(self): while True: cgiurl_hosts_page = self.cgiurl_hosts + '&page=' + str(page) - result = self.FetchURL( + result = self.fetch_url( cgiurl_hosts_page, giveback='raw', cgi_data=None) # authentication errors get a status code 200 too @@ -178,7 +178,7 @@ def _get_status(self): result.result.startswith('<'): # in case of auth error reset HTTP session and try again self.reset_HTTP() - result = self.FetchURL( + result = self.fetch_url( cgiurl_hosts_page, giveback='raw', cgi_data=None) if result.status_code < 400 and \ @@ -218,7 +218,7 @@ def _get_status(self): host_name = h['name'] if conf.debug_mode: - self.Debug(server=self.get_name(), debug=time.strftime('%a %H:%M:%S') + ' host_name is: ' + host_name) + self.debug(server=self.get_name(), debug=time.strftime('%a %H:%M:%S') + ' host_name is: ' + host_name) # If a host does not exist, create its object if host_name not in self.new_hosts: @@ -278,7 +278,7 @@ def _get_status(self): # extra duration needed for calculation if h['status']['lastStateChange'] is None: - self.Debug(server=self.get_name(), debug=time.strftime('%a %H:%M:%S') + 'Host has wrong lastStateChange - host_name is: ' + host_name) + self.debug(server=self.get_name(), debug=time.strftime('%a %H:%M:%S') + 'Host has wrong lastStateChange - host_name is: ' + host_name) else: duration = datetime.datetime.now( ) - datetime.datetime.fromtimestamp(int(h['status']['lastStateChange'])) @@ -293,7 +293,7 @@ def _get_status(self): # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) # services @@ -304,8 +304,8 @@ def _get_status(self): while True: cgiurl_services_page = self.cgiurl_services + '&page=' + str(page) - result = self.FetchURL(cgiurl_services_page, - giveback='raw', cgi_data=None) + result = self.fetch_url(cgiurl_services_page, + giveback='raw', cgi_data=None) # purify JSON result jsonraw = copy.deepcopy(result.result.replace('\n', '')) @@ -338,7 +338,7 @@ def _get_status(self): service_name = s['configuration']['serviceDescription'] if conf.debug_mode: - self.Debug(server=self.get_name(), debug=time.strftime('%a %H:%M:%S') + ' host_name is: ' + host_name + ' service_name is: ' + service_name) + self.debug(server=self.get_name(), debug=time.strftime('%a %H:%M:%S') + ' host_name is: ' + host_name + ' service_name is: ' + service_name) # If host not in problem list, create it if host_name not in self.new_hosts: @@ -407,7 +407,7 @@ def _get_status(self): # extra duration needed for calculation if s['status']['lastStateChange'] is None: - self.Debug(server=self.get_name(), debug=time.strftime('%a %H:%M:%S') + self.debug(server=self.get_name(), debug=time.strftime('%a %H:%M:%S') + 'Service has wrong lastStateChange - host_name is ' + host_name + ' service_name is: ' + service_name) else: duration = datetime.datetime.now( @@ -423,7 +423,7 @@ def _get_status(self): # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) del jsonraw, error, hosts diff --git a/Nagstamon/Servers/Multisite.py b/Nagstamon/Servers/Multisite.py index ac6405c36..60f51f81d 100644 --- a/Nagstamon/Servers/Multisite.py +++ b/Nagstamon/Servers/Multisite.py @@ -1,5 +1,5 @@ # encoding: utf-8 - +import json # Nagstamon - Nagios status monitor for your desktop # Copyright (C) 2008-2024 Henri Wahl <henri@nagstamon.de> et al. # @@ -161,7 +161,7 @@ def _is_auth_in_cookies(self): return False def _get_url(self, url): - result = self.FetchURL(url, 'raw') + result = self.fetch_url(url, 'raw') content, error, status_code = result.result, result.error, result.status_code if error != '' or status_code >= 400: @@ -173,7 +173,7 @@ def _get_url(self, url): c = content.split('\n') # Print non ERRORS to the log in debug mode - self.Debug(server=self.get_name(), debug=c[0]) + self.debug(server=self.get_name(), debug=c[0]) raise MultisiteError(False, Result(result='\n'.join(c[1:]), error=c[0], @@ -197,7 +197,7 @@ def _get_url(self, url): # if first attempt login and then try to get data again if not self._is_auth_in_cookies(): self._get_cookie_login() - result = self.FetchURL(url, 'raw') + result = self.fetch_url(url, 'raw') content, error = result.result, result.error if content.startswith('<') or\ '<!DOCTYPE html>' in content: @@ -224,9 +224,9 @@ def _get_cookie_login(self): # get cookie from login page via url retrieving as with other urls try: # login and get cookie - self.FetchURL(self.monitor_url + '/login.py', cgi_data=login_data, multipart=True) + self.fetch_url(self.monitor_url + '/login.py', cgi_data=login_data, multipart=True) except: - self.Error(sys.exc_info()) + self.error(sys.exc_info()) def _get_status(self): @@ -312,7 +312,7 @@ def _get_status(self): traceback.print_exc(file=sys.stdout) self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) # Add filters to the url which should only be applied to the service request @@ -407,7 +407,7 @@ def _get_status(self): # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=copy.deepcopy(result), error=copy.deepcopy(error)) del url_params @@ -426,11 +426,11 @@ def open_monitor(self, host, service=''): url = self.urls['human_service'] + urllib.parse.urlencode({'x': 'site='+self.hosts[host].site+'&host='+host+'&service='+service}).replace('x=', '%26') if conf.debug_mode: - self.Debug(server=self.get_name(), host=host, service=service, debug='Open host/service monitor web page ' + url) + self.debug(server=self.get_name(), host=host, service=service, debug='Open host/service monitor web page ' + url) webbrowser_open(url) - def GetHost(self, host): + def get_host(self, host): """ find out ip or hostname of given host to access hosts/devices which do not appear in DNS but have their ip saved in Nagios @@ -447,7 +447,7 @@ def GetHost(self, host): ip = self.hosts[host].address if conf.debug_mode: - self.Debug(server=self.get_name(), host=host, debug ='IP of %s:' % (host) + ' ' + ip) + self.debug(server=self.get_name(), host=host, debug ='IP of %s:' % (host) + ' ' + ip) if conf.connect_by_dns: try: @@ -457,7 +457,7 @@ def GetHost(self, host): else: address = ip except: - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) return Result(result=address) @@ -488,10 +488,10 @@ def _action(self, site, host, service, specific_params): url = url.replace('?_transid=-1&', '?_transid=%s&' % (transid)) if conf.debug_mode: - self.Debug(server=self.get_name(), host=host, debug ='Submitting action: ' + url + '&' + urllib.parse.urlencode(params)) + self.debug(server=self.get_name(), host=host, debug ='Submitting action: ' + url + '&' + urllib.parse.urlencode(params)) # apply action - self.FetchURL(url + '&' + urllib.parse.urlencode(params)) + self.fetch_url(url + '&' + urllib.parse.urlencode(params)) def _set_downtime(self, host, service, author, comment, fixed, start_time, end_time, hours, minutes): @@ -534,7 +534,7 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t self._action(self.hosts[host].site, host, service, params) except: if conf.debug_mode: - self.Debug(server=self.get_name(), host=host, + self.debug(server=self.get_name(), host=host, debug='Invalid start/end date/time given') @@ -565,17 +565,16 @@ def _omd_set_downtime(self, host, service, author, comment, fixed, start_time, e # Downtime type is "flexible" if "duration" is set if fixed == 0: - params["duration"] = hours * 60 + minutes + params['duration'] = hours * 60 + minutes # Parameter overrides for service downtimes if service: - url = self.urls["omd_svc_downtime"] - params["downtime_type"] = "service" - params["service_descriptions"] = [service] - - self.session.post(url, headers=headers, json=params) - except: + url = self.urls['omd_svc_downtime'] + params['downtime_type'] = 'service' + params['service_descriptions'] = [service] + self.fetch_url(url, headers=headers, cgi_data=json.dumps(params)) + except Exception as error: if conf.debug_mode: - self.Debug(server=self.get_name(), host=host, + self.debug(server=self.get_name(), host=host, debug='Invalid start/end date/time given') @@ -615,7 +614,7 @@ def _omd_set_recheck(self, host, service): "wait_svc": service, "csrf_token": csrf_token, } - self.FetchURL(self.urls["recheck"], cgi_data=data) + self.fetch_url(self.urls["recheck"], cgi_data=data) def recheck_all(self): @@ -627,9 +626,9 @@ def recheck_all(self): url = self.urls['api_svcprob_act'] if conf.debug_mode: - self.Debug(server=self.get_name(), debug ='Rechecking all action: ' + url + '&' + urllib.parse.urlencode(params)) + self.debug(server=self.get_name(), debug ='Rechecking all action: ' + url + '&' + urllib.parse.urlencode(params)) - result = self.FetchURL(url + '&' + urllib.parse.urlencode(params), giveback = 'raw') + result = self.fetch_url(url + '&' + urllib.parse.urlencode(params), giveback ='raw') def _get_transid(self, host, service): @@ -639,7 +638,7 @@ def _get_transid(self, host, service): # since Checkmk 2.0 it seems to be a problem if service is empty so fill it with a definitively existing one if not service: service = 'PING' - transid = self.FetchURL(self.urls['transid'].replace('$HOST$', host).replace('$SERVICE$', service.replace(' ', '+')), + transid = self.fetch_url(self.urls['transid'].replace('$HOST$', host).replace('$SERVICE$', service.replace(' ', '+')), 'obj').result.find(attrs={'name' : '_transid'})['value'] return transid @@ -651,7 +650,7 @@ def _get_csrf_token(self, host, service): # since Checkmk 2.0 it seems to be a problem if service is empty so fill it with a definitively existing one if not service: service = "PING" - csrf_token = self.FetchURL(self.urls["transid"].replace("$HOST$", host).replace("$SERVICE$", service.replace(" ", "+")), "obj").result.find(attrs={"name": "csrf_token"})["value"] + csrf_token = self.fetch_url(self.urls["transid"].replace("$HOST$", host).replace("$SERVICE$", service.replace(" ", "+")), "obj").result.find(attrs={"name": "csrf_token"})["value"] return csrf_token @@ -660,7 +659,7 @@ def _omd_get_version(self): get version of OMD Checkmk as [major_version, minor_version] """ try: - version = [int(v) for v in self.session.get(self.urls["omd_version"]).json()["versions"]["checkmk"].split(".")[:2]] + version = [int(x) for x in self.fetch_url(self.urls['omd_version'], 'json').result['versions']['checkmk'].split('.')[:2]] # If /version api is not supported, return the lowest non-negative pair except: version = [0, 0] diff --git a/Nagstamon/Servers/Opsview.py b/Nagstamon/Servers/Opsview.py index eee2d053d..390d13cdd 100644 --- a/Nagstamon/Servers/Opsview.py +++ b/Nagstamon/Servers/Opsview.py @@ -75,7 +75,7 @@ def init_HTTP(self): if len(self.session.cookies) == 0: if conf.debug_mode: - self.Debug(server=self.get_name(), debug="Fetching Login token") + self.debug(server=self.get_name(), debug="Fetching Login token") logindata = json.dumps({'username': self.get_username(), 'password': self.get_password()}) @@ -84,17 +84,17 @@ def init_HTTP(self): # get cookie from login page via url retrieving as with other urls try: # login and get cookie - resp = literal_eval(self.FetchURL(self.monitor_url + "/rest/login", - giveback='raw', - cgi_data=logindata).result) + resp = literal_eval(self.fetch_url(self.monitor_url + "/rest/login", + giveback='raw', + cgi_data=logindata).result) if conf.debug_mode: - self.Debug(server=self.get_name(), debug="Login Token: " + resp.get('token') ) + self.debug(server=self.get_name(), debug="Login Token: " + resp.get('token')) self.session.headers.update({'X-Opsview-Username': self.get_username(), 'X-Opsview-Token':resp.get('token')}) except: - self.Error(sys.exc_info()) + self.error(sys.exc_info()) def init_config(self): @@ -132,8 +132,8 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t cgi_data = urllib.parse.urlencode(data) - self.Debug(server=self.get_name(), debug="Downtime url: " + url) - self.FetchURL(url + cgi_data, giveback="raw", cgi_data=({ })) + self.debug(server=self.get_name(), debug="Downtime url: " + url) + self.fetch_url(url + cgi_data, giveback="raw", cgi_data=({ })) def _set_submit_check_result(self, host, service, state, comment, check_output, performance_data): @@ -155,8 +155,8 @@ def _set_submit_check_result(self, host, service, state, comment, check_output, cgi_data = urllib.parse.urlencode(data) - self.Debug(server=self.get_name(), debug="Submit result url: " + url) - self.FetchURL(url + cgi_data, giveback="raw", cgi_data=({ })) + self.debug(server=self.get_name(), debug="Submit result url: " + url) + self.fetch_url(url + cgi_data, giveback="raw", cgi_data=({ })) def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=None): @@ -176,8 +176,8 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi cgi_data = urllib.parse.urlencode(data) - self.Debug(server=self.get_name(), debug="ACK url: " + url) - self.FetchURL(url + cgi_data, giveback="raw", cgi_data=({ })) + self.debug(server=self.get_name(), debug="ACK url: " + url) + self.fetch_url(url + cgi_data, giveback="raw", cgi_data=({ })) def _set_recheck(self, host, service): @@ -194,8 +194,8 @@ def _set_recheck(self, host, service): cgi_data = urllib.parse.urlencode(data) - self.Debug(server=self.get_name(), debug="Recheck url: " + url) - self.FetchURL(url + cgi_data, giveback="raw", cgi_data=({ })) + self.debug(server=self.get_name(), debug="Recheck url: " + url) + self.fetch_url(url + cgi_data, giveback="raw", cgi_data=({ })) def _get_status(self): @@ -204,7 +204,7 @@ def _get_status(self): """ if self.can_change_only: if conf.debug_mode: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug="Showing only objects that the user can change or put in downtime") can_change = '&can_change=true' @@ -213,7 +213,7 @@ def _get_status(self): if self.hashtag_filter != '': if conf.debug_mode: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug="Raw hashtag filter string: " + self.hashtag_filter) @@ -221,14 +221,14 @@ def _get_status(self): list_of_non_empty_hashtags = [i for i in trimmed_hashtags if i] if conf.debug_mode: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug="List of trimmed hashtags" + pprint.pformat(list_of_non_empty_hashtags)) keywords = "&keyword=" + "&keyword=".join(list_of_non_empty_hashtags) if conf.debug_mode: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug="Keyword string" + pprint.pformat(keywords)) else: keywords = '' @@ -237,7 +237,7 @@ def _get_status(self): # because we filter them out later # the REST API gets all host and service info in one call try: - result = self.FetchURL(self.monitor_url + "/rest/status/service?state=1&state=2&state=3" + can_change + keywords, giveback="raw") + result = self.fetch_url(self.monitor_url + "/rest/status/service?state=1&state=2&state=3" + can_change + keywords, giveback="raw") data, error, status_code = json.loads(result.result), result.error, result.status_code @@ -248,7 +248,7 @@ def _get_status(self): return errors_occured if conf.debug_mode: - self.Debug(server=self.get_name(), debug="Fetched JSON: " + pprint.pformat(data)) + self.debug(server=self.get_name(), debug="Fetched JSON: " + pprint.pformat(data)) for host in data["list"]: self.new_hosts[host["name"]] = GenericHost() @@ -300,7 +300,7 @@ def _get_status(self): except: # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) #dummy return in case all is OK @@ -317,11 +317,11 @@ def open_monitor(self, host, service=''): quote_via=urllib.parse.quote) if service == '': if conf.debug_mode: - self.Debug(server=self.get_name(), host=host, service=service, + self.debug(server=self.get_name(), host=host, service=service, debug='Open host monitor web page ' + host_url) webbrowser_open(host_url) else: - self.Debug(server=self.get_name(), host=host, service=service, + self.debug(server=self.get_name(), host=host, service=service, debug='Open service monitor web page ' + service_url) webbrowser_open(service_url) diff --git a/Nagstamon/Servers/Prometheus.py b/Nagstamon/Servers/Prometheus.py index ff90f5c14..f27ca44e8 100644 --- a/Nagstamon/Servers/Prometheus.py +++ b/Nagstamon/Servers/Prometheus.py @@ -147,8 +147,8 @@ def _get_status(self): """ # get all alerts from the API server try: - result = self.FetchURL(self.monitor_url + self.API_PATH_ALERTS, - giveback="raw") + result = self.fetch_url(self.monitor_url + self.API_PATH_ALERTS, + giveback="raw") data = json.loads(result.result) error = result.error status_code = result.status_code @@ -159,12 +159,12 @@ def _get_status(self): return(errors_occured) if conf.debug_mode: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug="Fetched JSON: " + pprint.pformat(data)) for alert in data["data"]["alerts"]: if conf.debug_mode: - self.Debug( + self.debug( server=self.get_name(), debug="Processing Alert: " + pprint.pformat(alert) ) @@ -214,7 +214,7 @@ def _get_status(self): except: # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) # dummy return in case all is OK diff --git a/Nagstamon/Servers/Sensu.py b/Nagstamon/Servers/Sensu.py index 84c09063f..cc9ee81a8 100644 --- a/Nagstamon/Servers/Sensu.py +++ b/Nagstamon/Servers/Sensu.py @@ -97,7 +97,7 @@ def init_HTTP(self): verify=verify ) except SensuAPIException: - self.Error(sys.exc_info()) + self.error(sys.exc_info()) def _insert_service_to_hosts(self, service: GenericService): service_host = service.get_host_name() @@ -151,7 +151,7 @@ def _get_status(self): self._insert_service_to_hosts(new_service) except: self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) print(traceback.format_exc()) return Result(result=result, error=error) @@ -213,12 +213,12 @@ def _get_event_duration_string(self, start_seconds: int, end_seconds: int): def set_recheck(self, info_dict): if info_dict['service'] == 'keepalive': if conf.debug_mode: - self.Debug(server=self.name, debug='Keepalive results must come from the client running on host {0}, unable to recheck'.format(info_dict['host'])) + self.debug(server=self.name, debug='Keepalive results must come from the client running on host {0}, unable to recheck'.format(info_dict['host'])) else: standalone = self.sensu_api.get_event(info_dict['host'], info_dict['service'])['check']['standalone'] if standalone: if conf.debug_mode: - self.Debug(server=self.name, debug='Service {0} on host {1} is a standalone service, will not recheck'.format(info_dict['service'], info_dict['host'])) + self.debug(server=self.name, debug='Service {0} on host {1} is a standalone service, will not recheck'.format(info_dict['service'], info_dict['host'])) else: self.sensu_api.post_check_request( info_dict['service'], diff --git a/Nagstamon/Servers/SensuGo.py b/Nagstamon/Servers/SensuGo.py index f72b933be..b7c74480a 100644 --- a/Nagstamon/Servers/SensuGo.py +++ b/Nagstamon/Servers/SensuGo.py @@ -39,14 +39,14 @@ def _setup_sensugo_api(self): try: self._sensugo_api.auth(self.username, self.password, verify) except Exception: - self.Error(sys.exc_info()) + self.error(sys.exc_info()) def _get_status(self): try: response_code, events = self._sensugo_api.get_all_events() self._create_services(events) except Exception: - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) print(traceback.format_exc()) return Result(result=result, error=error) return Result() diff --git a/Nagstamon/Servers/SnagView3.py b/Nagstamon/Servers/SnagView3.py index 848af56fd..918e40d30 100644 --- a/Nagstamon/Servers/SnagView3.py +++ b/Nagstamon/Servers/SnagView3.py @@ -127,9 +127,9 @@ def init_HTTP(self): form_inputs['_password'] = self.password # call login page to get temporary cookie - self.FetchURL('{0}/security/login'.format(self.monitor_url)) + self.fetch_url('{0}/security/login'.format(self.monitor_url)) # submit login form to retrieve authentication cookie - self.FetchURL( + self.fetch_url( '{0}/security/login_check'.format(self.monitor_url), cgi_data=form_inputs, multipart=True @@ -162,7 +162,7 @@ def _get_status(self): # Get all hosts form_data['limit_length'] = 99999 - result = self.FetchURL( + result = self.fetch_url( self.cgiurl_hosts, giveback='raw', cgi_data=form_data) # authentication errors get a status code 200 too @@ -170,7 +170,7 @@ def _get_status(self): result.result.startswith('<'): # in case of auth error reset HTTP session and try again self.reset_HTTP() - result = self.FetchURL( + result = self.fetch_url( self.cgiurl_hosts, giveback='raw', cgi_data=form_data) if result.status_code < 400 and \ @@ -243,7 +243,7 @@ def _get_status(self): # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) # services @@ -258,8 +258,8 @@ def _get_status(self): # Get all services form_data['limit_length'] = 99999 - result = self.FetchURL(self.cgiurl_services, - giveback='raw', cgi_data=form_data) + result = self.fetch_url(self.cgiurl_services, + giveback='raw', cgi_data=form_data) # purify JSON result jsonraw = copy.deepcopy(result.result.replace('\n', '')) @@ -337,7 +337,7 @@ def _get_status(self): # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) del jsonraw, error, hosts diff --git a/Nagstamon/Servers/Thruk.py b/Nagstamon/Servers/Thruk.py index 00dce2501..6fc857d24 100644 --- a/Nagstamon/Servers/Thruk.py +++ b/Nagstamon/Servers/Thruk.py @@ -82,7 +82,7 @@ def init_HTTP(self): if self.session is None or self.session.cookies.get('thruk_auth') is None: self.login() except: - self.Error(sys.exc_info()) + self.error(sys.exc_info()) def init_config(self): @@ -109,7 +109,7 @@ def init_config(self): def login(self): """ - use pure session instead of FetchURL to get Thruk session + use pure session instead of fetch_url to get Thruk session """ if self.session is None: self.refresh_authentication = False @@ -118,7 +118,7 @@ def login(self): if self.use_autologin is True: req = self.session.post(self.monitor_cgi_url + '/user.cgi?', data={}, headers={'X-Thruk-Auth-Key':self.autologin_key.strip()}) if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Auto Login status: ' + req.url + ' http code : ' + str(req.status_code)) + self.debug(server=self.get_name(), debug='Auto Login status: ' + req.url + ' http code : ' + str(req.status_code)) if req.status_code != 200: self.refresh_authentication = True return Result(result=None, error="Login failed") @@ -130,7 +130,7 @@ def login(self): 'password': self.get_password(), 'submit': 'Login'}) if conf.debug_mode: - self.Debug(server=self.get_name(), debug='Login status: ' + req.url + ' http code : ' + str(req.status_code)) + self.debug(server=self.get_name(), debug='Login status: ' + req.url + ' http code : ' + str(req.status_code)) if req.status_code != 200: self.refresh_authentication = True return Result(result=None, error="Login failed") @@ -150,7 +150,7 @@ def open_monitor(self, host, service=''): url = self.monitor_cgi_url + '/extinfo.cgi?type=2&' + urllib.parse.urlencode( { 'host': host, 'service': self.hosts[host].services[ service ].real_name }) if conf.debug_mode: - self.Debug(server=self.get_name(), host=host, service=service, + self.debug(server=self.get_name(), host=host, service=service, debug='Open host/service monitor web page {0}'.format(url)) webbrowser_open(url) @@ -191,14 +191,14 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi if sticky is True: cgi_data['sticky_ack'] = 'on' - self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + self.fetch_url(url, giveback='raw', cgi_data=cgi_data) # acknowledge all services on a host if all_services: for s in all_services: cgi_data['cmd_typ'] = '34' cgi_data['service'] = self.hosts[host].services[ s ].real_name - self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + self.fetch_url(url, giveback='raw', cgi_data=cgi_data) def _set_recheck(self, host, service): self.session.headers.update({'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}) @@ -209,7 +209,7 @@ def _set_recheck(self, host, service): return try: # get start time from Nagios as HTML to use same timezone setting like the locally installed Nagios - result = self.FetchURL( + result = self.fetch_url( self.monitor_cgi_url + '/cmd.cgi?' + urllib.parse.urlencode({'cmd_typ': '96', 'host': host})) self.start_time = dict(result.result.find(attrs={'name': 'start_time'}).attrs)['value'] # decision about host or service - they have different URLs @@ -230,7 +230,7 @@ def _set_recheck(self, host, service): ('force_check', 'on'), ('btnSubmit', 'Commit')]) # execute POST request - self.FetchURL(self.monitor_cgi_url + '/cmd.cgi', giveback='raw', cgi_data=cgi_data) + self.fetch_url(self.monitor_cgi_url + '/cmd.cgi', giveback='raw', cgi_data=cgi_data) except: traceback.print_exc(file=sys.stdout) @@ -262,7 +262,7 @@ def _set_downtime(self, host, service, author, comment, fixed, start_time, end_t cgi_data['btnSubmit'] = 'Commit' # running remote cgi command - self.FetchURL(url, giveback='raw', cgi_data=cgi_data) + self.fetch_url(url, giveback='raw', cgi_data=cgi_data) def _get_status(self): """ @@ -276,7 +276,7 @@ def _get_status(self): # hosts must be analyzed separately try: # JSON experiments - result = self.FetchURL(self.cgiurl_hosts, giveback='raw') + result = self.fetch_url(self.cgiurl_hosts, giveback='raw') jsonraw, error, status_code = copy.deepcopy(result.result),\ copy.deepcopy(result.error),\ result.status_code @@ -318,13 +318,13 @@ def _get_status(self): traceback.print_exc(file=sys.stdout) # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) # services try: # JSON experiments - result = self.FetchURL(self.cgiurl_services, giveback="raw") + result = self.fetch_url(self.cgiurl_services, giveback="raw") jsonraw, error, status_code = copy.deepcopy(result.result),\ copy.deepcopy(result.error),\ result.status_code @@ -383,7 +383,7 @@ def _get_status(self): traceback.print_exc(file=sys.stdout) # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) # dummy return in case all is OK diff --git a/Nagstamon/Servers/Zabbix.py b/Nagstamon/Servers/Zabbix.py index dbaca243f..f73393453 100644 --- a/Nagstamon/Servers/Zabbix.py +++ b/Nagstamon/Servers/Zabbix.py @@ -92,7 +92,7 @@ def _login(self): if not self.zapi.logged_in(): self.zapi.login(self.username, self.password) except ZabbixAPIException: - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) def getLastApp(self, this_item): @@ -188,7 +188,7 @@ def _get_status(self): # this code from 80 lines ahead. -- AGV # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) print(sys.exc_info()) return Result(result=result, error=error) @@ -223,7 +223,7 @@ def _get_status(self): except (ZabbixError, ZabbixAPIException, APITimeout, Already_Exists): # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) else: try: @@ -240,7 +240,7 @@ def _get_status(self): except (ZabbixError, ZabbixAPIException, APITimeout, Already_Exists): # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) # get All Hosts. # 1. Store data in cache (to be used by events) @@ -339,7 +339,7 @@ def _get_status(self): except ZabbixError: self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) ### for service in services: @@ -348,9 +348,6 @@ def _get_status(self): # so I left it # print(service) status = self.statemap.get(service['lastEvent']['severity'], service['lastEvent']['severity']) - # self.Debug(server=self.get_name(), debug="SERVICE (" + service['application'] + ") STATUS: **" + - # status + "** PRIORITY: #" + service['priority']) self.Debug(server=self.get_name(), - # debug="-----======== SERVICE " + str(service)) if not status == 'OK': # if not service['description'].endswith('...'): # state = service['description'] @@ -413,7 +410,7 @@ def _get_status(self): print("================================") print("Host " + key + "Not found in host cache") if conf.debug_mode is True: - self.Debug(server=self.get_name(), debug="Host not found [" + key + "]") + self.debug(server=self.get_name(), debug="Host not found [" + key + "]") # if a service does not exist create its object new_service = n["triggerid"] @@ -440,13 +437,13 @@ def _get_status(self): self.new_hosts[key].services[new_service].eventid = n["eventid"] self.new_hosts[key].services[new_service].allow_manual_close = n["allow_manual_close"] if conf.debug_mode is True: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug="Adding new service[" + new_service + "] **" + n['service'] + "**") except (ZabbixError, ZabbixAPIException): # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) print(sys.exc_info()) return Result(result=result, error=error) @@ -456,7 +453,7 @@ def _open_browser(self, url): webbrowser_open(url) if conf.debug_mode is True: - self.Debug(server=self.get_name(), debug="Open web page " + url) + self.debug(server=self.get_name(), debug="Open web page " + url) def open_services(self): self._open_browser(self.urls['human_services']) @@ -476,7 +473,7 @@ def open_monitor(self, host, service=""): {'x': 'site=' + self.hosts[host].site + '&host=' + host + '&service=' + service}).replace('x=', '%26') if conf.debug_mode is True: - self.Debug(server=self.get_name(), host=host, service=service, + self.debug(server=self.get_name(), host=host, service=service, debug="Open host/service monitor web page " + url) webbrowser_open(url) @@ -486,7 +483,7 @@ def set_recheck(self, info_dict): def _set_acknowledge(self, host, service, author, comment, sticky, notify, persistent, all_services=None): if conf.debug_mode is True: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug="Set Acknowledge Host: " + host + " Service: " + service + " Sticky: " + str( sticky) + " persistent:" + str(persistent) + " All services: " + str(all_services)) self._login() @@ -522,7 +519,7 @@ def _set_acknowledge(self, host, service, author, comment, sticky, notify, persi if comment: actions |= 4 if conf.debug_mode: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug="Events to acknowledge: " + str(eventids) + " Close: " + str(actions)) # If some events are not closable, we need to make 2 requests, 1 for the closable and one for the not closable if sticky and unclosable_events: @@ -553,7 +550,7 @@ def _set_downtime(self, hostname, service, author, comment, fixed, start_time, e etime = int(time.mktime(date.timetuple())) if conf.debug_mode is True: - self.Debug(server=self.get_name(), + self.debug(server=self.get_name(), debug="Downtime for " + hostname + "[" + str(hostids) + "] stime:" + str( stime) + " etime:" + str(etime)) # print("Downtime for " + hostname + "[" + str(hostids) + "] stime:" + str(stime) + " etime:" + str(etime)) @@ -569,12 +566,12 @@ def _set_downtime(self, hostname, service, author, comment, fixed, start_time, e try: self.zapi.maintenance.create(body) except Already_Exists: - self.Debug(server=self.get_name(), debug=f"Maintanence with name {body['name']} already exists") + self.debug(server=self.get_name(), debug=f"Maintanence with name {body['name']} already exists") def get_start_end(self, host): return time.strftime("%Y-%m-%d %H:%M"), time.strftime("%Y-%m-%d %H:%M", time.localtime(time.time() + 7200)) - def GetHost(self, host): + def get_host(self, host): """ find out ip or hostname of given host to access hosts/devices which do not appear in DNS but have their ip saved in Nagios @@ -591,7 +588,7 @@ def GetHost(self, host): if host in self.hosts: ip = self.hosts[host].address if conf.debug_mode is True: - self.Debug(server=self.get_name(), host=host, debug="IP of %s:" % host + " " + ip) + self.debug(server=self.get_name(), host=host, debug="IP of %s:" % host + " " + ip) if conf.connect_by_dns is True: try: @@ -601,7 +598,7 @@ def GetHost(self, host): else: address = ip except ZabbixError: - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) return Result(result=address) diff --git a/Nagstamon/Servers/ZabbixProblemBased.py b/Nagstamon/Servers/ZabbixProblemBased.py index 438e4d6eb..b4db0de31 100644 --- a/Nagstamon/Servers/ZabbixProblemBased.py +++ b/Nagstamon/Servers/ZabbixProblemBased.py @@ -262,7 +262,7 @@ def _get_status(self): except ZabbixLightApiException: # set checking flag back to False self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) return Result() diff --git a/Nagstamon/Servers/Zenoss.py b/Nagstamon/Servers/Zenoss.py index 6197f1902..add848aa4 100644 --- a/Nagstamon/Servers/Zenoss.py +++ b/Nagstamon/Servers/Zenoss.py @@ -69,7 +69,7 @@ def _zlogin(self): try: self.zapi = ZenossAPI(Server=self.server) except Exception: - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error) def _get_status(self): @@ -139,7 +139,7 @@ def _get_status(self): except: self.isChecking = False - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) print(traceback.format_exc()) return Result(result=result, error=error) diff --git a/Nagstamon/Servers/__init__.py b/Nagstamon/Servers/__init__.py index 587a1dab8..3aec2d88e 100644 --- a/Nagstamon/Servers/__init__.py +++ b/Nagstamon/Servers/__init__.py @@ -226,7 +226,7 @@ def create_server(server=None): # debug if conf.debug_mode is True: - new_server.Debug(server=server.name, debug="Created server.") + new_server.debug(server=server.name, debug="Created server.") return new_server diff --git a/Nagstamon/Servers/op5Monitor.py b/Nagstamon/Servers/op5Monitor.py index cf59d45e7..561cf146b 100644 --- a/Nagstamon/Servers/op5Monitor.py +++ b/Nagstamon/Servers/op5Monitor.py @@ -143,7 +143,7 @@ def _get_status(self): api_default_host_query+='&format=json' api_default_host_query = api_default_host_query.replace(" ", "%20") - result = self.FetchURL(self.monitor_url + self.api_count + api_default_host_query, giveback="raw") + result = self.fetch_url(self.monitor_url + self.api_count + api_default_host_query, giveback="raw") data, error, status_code = json.loads(result.result),\ result.error,\ result.status_code @@ -161,7 +161,7 @@ def _get_status(self): api_default_host_query+='&format=json' api_default_host_query = api_default_host_query.replace(" ", "%20") - result = self.FetchURL(self.monitor_url + self.api_query + api_default_host_query + '&limit=' + str(count), giveback="raw") + result = self.fetch_url(self.monitor_url + self.api_query + api_default_host_query + '&limit=' + str(count), giveback="raw") data = json.loads(result.result) n = dict() for api in data: @@ -204,7 +204,7 @@ def _get_status(self): api_default_svc_query+='&format=json' api_default_svc_query = api_default_svc_query.replace(" ", "%20") - result = self.FetchURL(self.monitor_url + self.api_count + api_default_svc_query, giveback="raw") + result = self.fetch_url(self.monitor_url + self.api_count + api_default_svc_query, giveback="raw") data, error, status_code = json.loads(result.result),\ result.error,\ result.status_code @@ -222,7 +222,7 @@ def _get_status(self): api_default_svc_query+='&format=json' api_default_svc_query = api_default_svc_query.replace(" ", "%20") - result = self.FetchURL(self.monitor_url + self.api_query + api_default_svc_query + '&limit=' + str(count), giveback="raw") + result = self.fetch_url(self.monitor_url + self.api_query + api_default_svc_query + '&limit=' + str(count), giveback="raw") data = json.loads(result.result) for api in data: n = dict() @@ -280,7 +280,7 @@ def _get_status(self): # store status_code for returning result to tell GUI to reauthenticate status_code = result.status_code - result, error = self.Error(sys.exc_info()) + result, error = self.error(sys.exc_info()) return Result(result=result, error=error, status_code=status_code) return Result() @@ -300,7 +300,7 @@ def get_start_end(self, host): def send_command(self, command, params=False): url = self.monitor_url + self.api_cmd + '/' + command - self.FetchURL(url, cgi_data=params, giveback='raw') + self.fetch_url(url, cgi_data=params, giveback='raw') def _set_recheck(self, host, service): From 31c3cdfd17f16e94822ae510df37b477d06ff896 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Mon, 2 Sep 2024 17:13:54 +0200 Subject: [PATCH 813/884] 3.15-20240902 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 11d2c54e3..0966c5abf 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -131,7 +131,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.15-20240729' + VERSION = '3.15-20240902' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2024 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 9a44c1c2b..566dab836 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.15-20240729) unstable; urgency=low +nagstamon (3.15-20240902) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Mon, Jul 29 2024 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Mon, Sep 02 2024 08:00:00 +0200 nagstamon (3.14.0) stable; urgency=low * New upstream From 446c60fa0754e7bdc6c7ff024b707caf1ef1b6a0 Mon Sep 17 00:00:00 2001 From: HenriWahl <171809906+hw-iu@users.noreply.github.com> Date: Thu, 10 Oct 2024 20:56:49 +0200 Subject: [PATCH 814/884] updated for new release --- .github/workflows/build-release-latest.yml | 14 +++++++------- Nagstamon/Helpers.py | 2 +- Nagstamon/Servers/Livestatus.py | 2 +- Nagstamon/Servers/Monitos3.py | 2 +- Nagstamon/thirdparty/zabbix_api.py | 4 ++-- .../{Dockerfile-fedora-37 => Dockerfile-fedora-42} | 3 ++- 6 files changed, 14 insertions(+), 13 deletions(-) rename build/docker/{Dockerfile-fedora-37 => Dockerfile-fedora-42} (93%) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 54d2238f0..8aab9cdce 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -8,7 +8,7 @@ on: - '!*.*.*' env: - python_win_version: 3.11.9 + python_win_version: 3.12.7 repo_dir: nagstamon-jekyll/docs/repo cr_image: ghcr.io/henriwahl/build-nagstamon # to be increased if new updates of build images are necessary @@ -70,7 +70,7 @@ jobs: if-no-files-found: error name: ${{ github.job }} - fedora-37: + fedora-38: runs-on: ubuntu-latest needs: test steps: @@ -93,7 +93,7 @@ jobs: if-no-files-found: error name: ${{ github.job }} - fedora-38: + fedora-39: runs-on: ubuntu-latest needs: test steps: @@ -116,7 +116,7 @@ jobs: if-no-files-found: error name: ${{ github.job }} - fedora-39: + fedora-40: runs-on: ubuntu-latest needs: test steps: @@ -139,7 +139,7 @@ jobs: if-no-files-found: error name: ${{ github.job }} - fedora-40: + fedora-41: runs-on: ubuntu-latest needs: test steps: @@ -162,7 +162,7 @@ jobs: if-no-files-found: error name: ${{ github.job }} - fedora-41: + fedora-42: runs-on: ubuntu-latest needs: test steps: @@ -326,7 +326,7 @@ jobs: repo-debian: runs-on: ubuntu-latest # try to avoid race condition and start uploading only after the last install package has been build - needs: [debian, fedora-37, fedora-38, fedora-39, fedora-40, fedora-41, rhel-9, macos-intel, macos-arm, windows-32, windows-64, windows-64-debug] + needs: [debian, fedora-38, fedora-39, fedora-40, fedora-41, fedora-42, rhel-9, macos-intel, macos-arm, windows-32, windows-64, windows-64-debug] env: family: debian steps: diff --git a/Nagstamon/Helpers.py b/Nagstamon/Helpers.py index 2db28ad3e..6c16916a7 100644 --- a/Nagstamon/Helpers.py +++ b/Nagstamon/Helpers.py @@ -459,7 +459,7 @@ def get_distro(): # Since CentOS Linux got retired by Red Hat, there are various RHEL derivatives/clones; flow is: # CentOS Stream -> Red Hat Enterprise Linux -> (AlmaLinux, EuroLinux, Oracle Linux, Rocky Linux) # Goal of this hack is to rule them all as Red Hat Enterprise Linux, the baseline distribution. - if re.search('^platform:el\d+$', os_release_dict.get('PLATFORM_ID', 'unknown')): + if re.search(r'^platform:el\d+$', os_release_dict.get('PLATFORM_ID', 'unknown')): os_release_dict['ID'] = 'rhel' os_release_dict['VERSION_ID'] = os_release_dict.get('VERSION_ID', 'unknown').split('.', 1)[0] os_release_dict['NAME'] = 'Red Hat Enterprise Linux' diff --git a/Nagstamon/Servers/Livestatus.py b/Nagstamon/Servers/Livestatus.py index eafa32dc3..15a11ef68 100644 --- a/Nagstamon/Servers/Livestatus.py +++ b/Nagstamon/Servers/Livestatus.py @@ -68,7 +68,7 @@ def init_config(self): log.info(self.monitor_url) # we abuse the monitor_url for the connection information self.address = ('localhost', 6558) - m = re.match('.*?://([^:/]+?)(?::(\d+))?(?:/|$)', self.monitor_url) + m = re.match(r'.*?://([^:/]+?)(?::(\d+))?(?:/|$)', self.monitor_url) if m: host, port = m.groups() if not port: diff --git a/Nagstamon/Servers/Monitos3.py b/Nagstamon/Servers/Monitos3.py index 1ef0cacd9..b3adff409 100644 --- a/Nagstamon/Servers/Monitos3.py +++ b/Nagstamon/Servers/Monitos3.py @@ -87,7 +87,7 @@ def init_config(self): log.info(self.monitor_url) # we abuse the monitor_url for the connection information self.address = ('localhost', 6558) - m = re.match('.*?://([^:/]+?)(?::(\d+))?(?:/|$)', self.monitor_url) + m = re.match(r'.*?://([^:/]+?)(?::(\d+))?(?:/|$)', self.monitor_url) if m: host, port = m.groups() if not port: diff --git a/Nagstamon/thirdparty/zabbix_api.py b/Nagstamon/thirdparty/zabbix_api.py index 443a0f19c..c76e0958a 100644 --- a/Nagstamon/thirdparty/zabbix_api.py +++ b/Nagstamon/thirdparty/zabbix_api.py @@ -293,9 +293,9 @@ def do_request(self, json_obj): self.id += 1 if 'error' in jobj: # some exception - msg = "Error %s: %s, %s while sending %s" % (jobj['error']['code'], + msg = 'Error %s: %s, %s while sending %s' % (jobj['error']['code'], jobj['error']['message'], jobj['error']['data'], str(json_obj)) - if re.search(".*already\sexists.*", jobj["error"]["data"], re.I): # already exists + if re.search(r'.*already\sexists.*', jobj['error']['data'], re.I): # already exists raise Already_Exists(msg, jobj['error']['code']) else: raise ZabbixAPIException(msg, jobj['error']['code']) diff --git a/build/docker/Dockerfile-fedora-37 b/build/docker/Dockerfile-fedora-42 similarity index 93% rename from build/docker/Dockerfile-fedora-37 rename to build/docker/Dockerfile-fedora-42 index 5a2f121b8..897bd7df5 100644 --- a/build/docker/Dockerfile-fedora-37 +++ b/build/docker/Dockerfile-fedora-42 @@ -1,4 +1,4 @@ -FROM fedora:37 +FROM fedora:42 LABEL maintainer=henri@nagstamon.de RUN dnf -y upgrade @@ -19,6 +19,7 @@ RUN dnf -y install createrepo_c \ python3-requests \ python3-requests-kerberos \ python3-SecretStorage \ + python3-setuptools \ qt6-qtsvg \ qt6-qtmultimedia \ rpm-build From 0eeb6a56c1f2c69700d1009e1ba029b37d7266ce Mon Sep 17 00:00:00 2001 From: HenriWahl <171809906+hw-iu@users.noreply.github.com> Date: Thu, 10 Oct 2024 21:40:33 +0200 Subject: [PATCH 815/884] fix fedora-42 --- .github/workflows/build-release-latest.yml | 2 +- build/docker/Dockerfile-fedora-42 | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 8aab9cdce..f5b0008e4 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -12,7 +12,7 @@ env: repo_dir: nagstamon-jekyll/docs/repo cr_image: ghcr.io/henriwahl/build-nagstamon # to be increased if new updates of build images are necessary - cr_image_version: 4 + cr_image_version: 5 # release type this file is used for release: latest diff --git a/build/docker/Dockerfile-fedora-42 b/build/docker/Dockerfile-fedora-42 index 897bd7df5..97a57ca7f 100644 --- a/build/docker/Dockerfile-fedora-42 +++ b/build/docker/Dockerfile-fedora-42 @@ -1,6 +1,9 @@ FROM fedora:42 LABEL maintainer=henri@nagstamon.de +# avoid Cisco OpenH264 plugin causing upgrade failure +RUN dnf config-manager setopt fedora-cisco-openh264.enabled=false + RUN dnf -y upgrade RUN dnf -y install createrepo_c \ From ad8256e8d621d782c5ef2bee840b63b3155978d2 Mon Sep 17 00:00:00 2001 From: HenriWahl <171809906+hw-iu@users.noreply.github.com> Date: Thu, 10 Oct 2024 22:20:01 +0200 Subject: [PATCH 816/884] updated for new release --- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 11d2c54e3..e2b60a7a5 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -131,7 +131,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.15-20240729' + VERSION = '3.15-20241010' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2024 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 9a44c1c2b..5b201606d 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.15-20240729) unstable; urgency=low +nagstamon (3.15-20241010) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Mon, Jul 29 2024 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Thu, Oct 10 2024 08:00:00 +0200 nagstamon (3.14.0) stable; urgency=low * New upstream From 53b5ce92ecaa0d4445dfb7c959168b5f72fa7820 Mon Sep 17 00:00:00 2001 From: HenriWahl <171809906+hw-iu@users.noreply.github.com> Date: Thu, 10 Oct 2024 22:30:20 +0200 Subject: [PATCH 817/884] updated build-release-stable.yml --- .github/workflows/build-release-stable.yml | 38 +++++++++++++++------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index 139d6907b..a12309668 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -4,11 +4,11 @@ on: tags: 'v*' env: - python_win_version: 3.11.8 + python_win_version: 3.12.7 repo_dir: nagstamon-jekyll/docs/repo cr_image: ghcr.io/henriwahl/build-nagstamon # to be increased if new updates of build images are necessary - cr_image_version: 4 + cr_image_version: 5 # release type this file is used for release: stable @@ -66,7 +66,7 @@ jobs: if-no-files-found: error name: ${{ github.job }} - fedora-37: + fedora-38: runs-on: ubuntu-latest needs: test steps: @@ -89,7 +89,7 @@ jobs: if-no-files-found: error name: ${{ github.job }} - fedora-38: + fedora-39: runs-on: ubuntu-latest needs: test steps: @@ -112,7 +112,7 @@ jobs: if-no-files-found: error name: ${{ github.job }} - fedora-39: + fedora-40: runs-on: ubuntu-latest needs: test steps: @@ -135,7 +135,7 @@ jobs: if-no-files-found: error name: ${{ github.job }} - fedora-40: + fedora-41: runs-on: ubuntu-latest needs: test steps: @@ -158,7 +158,7 @@ jobs: if-no-files-found: error name: ${{ github.job }} - fedora-41: + fedora-42: runs-on: ubuntu-latest needs: test steps: @@ -204,7 +204,7 @@ jobs: if-no-files-found: error name: ${{ github.job }} - macos: + macos-intel: runs-on: macos-12 needs: test steps: @@ -220,6 +220,22 @@ jobs: if-no-files-found: error name: ${{ github.job }} + macos-arm: + runs-on: macos-14 + needs: test + steps: + - uses: actions/checkout@v4 + - run: pip3 install --no-warn-script-location --break-system-packages -r build/requirements/macos.txt + - run: cd ${{ github.workspace }}/build; python3 build.py + env: + PYTHONPATH: ${{ github.workspace }} + - uses: actions/upload-artifact@v4 + with: + path: build/*.dmg + retention-days: 1 + if-no-files-found: error + name: ${{ github.job }} + windows-32: # better depend on stable build image runs-on: windows-2022 @@ -306,7 +322,7 @@ jobs: repo-debian: runs-on: ubuntu-latest # try to avoid race condition and start uploading only after the last install package has been build - needs: [debian, fedora-37, fedora-38, fedora-39, fedora-40, fedora-41, rhel-9, macos, windows-32, windows-64, windows-64-debug] + needs: [debian, fedora-38, fedora-39, fedora-40, fedora-41, fedora-42, rhel-9, macos-intel, macos-arm, windows-32, windows-64, windows-64-debug] env: family: debian steps: @@ -439,9 +455,9 @@ jobs: merge-multiple: true - run: cd artifact && md5sum *agstamon* > md5sums.txt - run: cd artifact && sha256sum *agstamon* > sha256sums.txt - - uses: marvinpinto/action-automatic-releases@latest + #- uses: marvinpinto/action-automatic-releases@latest # the dciborow action is outdated as well :-( - #- uses: dciborow/action-github-releases@v1.0.1 + - uses: dciborow/action-github-releases@v1.0.1 with: repo_token: "${{ secrets.GITHUB_TOKEN }}" prerelease: false From 21101b9cc13143aff9497a82c21e280984c9597b Mon Sep 17 00:00:00 2001 From: HenriWahl <171809906+hw-iu@users.noreply.github.com> Date: Thu, 10 Oct 2024 22:46:04 +0200 Subject: [PATCH 818/884] updated re raw --- Nagstamon/Servers/Centreon/CentreonLegacy.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Nagstamon/Servers/Centreon/CentreonLegacy.py b/Nagstamon/Servers/Centreon/CentreonLegacy.py index fc79aa1b2..93d4dfc07 100644 --- a/Nagstamon/Servers/Centreon/CentreonLegacy.py +++ b/Nagstamon/Servers/Centreon/CentreonLegacy.py @@ -91,7 +91,7 @@ def init_HTTP(self): result_versioncheck = self.fetch_url(self.monitor_cgi_url + '/index.php', giveback='raw') raw_versioncheck, error_versioncheck = result_versioncheck.result, result_versioncheck.error if error_versioncheck == '': - if re.search('2\.2\.[0-9]', raw_versioncheck): + if re.search(r'2\.2\.[0-9]', raw_versioncheck): self.centreon_version = 2.2 if conf.debug_mode is True: self.debug(server=self.get_name(), debug='Centreon version detected : 2.2') @@ -100,7 +100,7 @@ def init_HTTP(self): 'hosts': '$MONITOR$/main.php?p=20103&o=hpb', 'services': '$MONITOR$/main.php?p=20202&o=svcpb', 'history': '$MONITOR$/main.php?p=203'} - elif re.search('2\.[3-6]\.[0-5]', raw_versioncheck): + elif re.search(r'2\.[3-6]\.[0-5]', raw_versioncheck): self.centreon_version = 2.3456 if conf.debug_mode is True: self.debug(server=self.get_name(), debug='Centreon version detected : 2.3 <=> 2.6.5') @@ -109,7 +109,7 @@ def init_HTTP(self): 'hosts': '$MONITOR$/main.php?p=20103&o=hpb', 'services': '$MONITOR$/main.php?p=20202&o=svcpb', 'history': '$MONITOR$/main.php?p=203'} - elif re.search('2\.6\.[6-9]', raw_versioncheck): + elif re.search(r'2\.6\.[6-9]', raw_versioncheck): self.centreon_version = 2.66 if conf.debug_mode is True: self.debug(server=self.get_name(), debug='Centreon version detected : 2.6.6') @@ -118,7 +118,7 @@ def init_HTTP(self): 'hosts': '$MONITOR$/main.php?p=20103&o=hpb', 'services': '$MONITOR$/main.php?p=20202&o=svcpb', 'history': '$MONITOR$/main.php?p=203'} - elif re.search('2\.7\.[0-9]', raw_versioncheck): + elif re.search(r'2\.7\.[0-9]', raw_versioncheck): # Centreon 2.7 only support C. Broker self.centreon_version = 2.7 if conf.debug_mode is True: @@ -128,7 +128,7 @@ def init_HTTP(self): 'hosts': '$MONITOR$/main.php?p=20202&o=hpb', 'services': '$MONITOR$/main.php?p=20201&o=svcpb', 'history': '$MONITOR$/main.php?p=203'} - elif re.search('2\.8\.[0-9]', raw_versioncheck): + elif re.search(r'2\.8\.[0-9]', raw_versioncheck): # Centreon 2.8 only support C. Broker self.centreon_version = 2.8 if conf.debug_mode is True: @@ -138,7 +138,7 @@ def init_HTTP(self): 'hosts': '$MONITOR$/main.php?p=20202', 'services': '$MONITOR$/main.php?p=20201', 'history': '$MONITOR$/main.php?p=203'} - elif re.search('18\.10\.[0-9]', raw_versioncheck): + elif re.search(r'18\.10\.[0-9]', raw_versioncheck): self.centreon_version = 18.10 if conf.debug_mode is True: self.debug(server=self.get_name(), debug='Centreon version detected : 18.10') @@ -147,7 +147,7 @@ def init_HTTP(self): 'hosts': '$MONITOR$/main.php?p=20202', 'services': '$MONITOR$/main.php?p=20201', 'history': '$MONITOR$/main.php?p=203'} - elif re.search('19\.(04|10)\.[0-9]', raw_versioncheck) or re.search('20\.(04|10)\.[0-9]', raw_versioncheck): + elif re.search(r'19\.(04|10)\.[0-9]', raw_versioncheck) or re.search(r'20\.(04|10)\.[0-9]', raw_versioncheck): self.centreon_version = 19.04 if conf.debug_mode is True: self.debug(server=self.get_name(), debug='Centreon version detected : 19.04 <=> 20.10') @@ -406,11 +406,11 @@ def _get_xml_path(self, sid): result = self.fetch_url(self.monitor_cgi_url + '/main.php', cgi_data=cgi_data, giveback='raw') raw, error = result.result, result.error if error == '': - if re.search('var _addrXML.*xml\/ndo\/host', raw): + if re.search(r'var _addrXML.*xml\/ndo\/host', raw): self.XML_PATH = 'xml/ndo' if conf.debug_mode: self.debug(server=self.get_name(), debug='Detected broker : NDO') - elif re.search('var _addrXML.*xml\/broker\/host', raw): + elif re.search(r'var _addrXML.*xml\/broker\/host', raw): self.XML_PATH = 'xml/broker' if conf.debug_mode: self.debug(server=self.get_name(), debug='Detected broker : C. Broker') From 72eac5e8a9fdab1f9162394e79bf0a283c9d2c3f Mon Sep 17 00:00:00 2001 From: HenriWahl <171809906+hw-iu@users.noreply.github.com> Date: Thu, 10 Oct 2024 22:51:42 +0200 Subject: [PATCH 819/884] iscc arch x64 --- build/build.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/build.py b/build/build.py index 4ab90806f..29f827e8a 100644 --- a/build/build.py +++ b/build/build.py @@ -42,7 +42,7 @@ ARCH_WINDOWS = platform.architecture()[0][0:2] ARCH_WINDOWS_OPTS = {'32': ('win32', 'win32', '', 'x86'), - '64': ('win-amd64', 'amd64', '(X86)', 'x64')} + '64': ('win-amd64', 'amd64', '(X86)', 'x64compatible')} ARCH_MACOS = platform.machine() ARCH_MACOS_NAMES = {'x86_64': 'Intel', @@ -126,7 +126,7 @@ def winmain(): if SIGNING: # environment variables will be used by powershell script for signing - subprocess.run(['pwsh.exe', './windows/code_signing.ps1', 'build/Nagstamon/Nagstamon.exe']) + subprocess.run(['pwsh.exe', './windows/code_signing.ps1', 'build/Nagstamon/*.exe']) # rename output os.rename(DIR_BUILD_EXE, DIR_BUILD_NAGSTAMON) From bd10ed13fc32dd892a163b9b2874cee6923324c8 Mon Sep 17 00:00:00 2001 From: HenriWahl <171809906+hw-iu@users.noreply.github.com> Date: Thu, 10 Oct 2024 22:58:15 +0200 Subject: [PATCH 820/884] debug sign --- build/build.py | 1 + 1 file changed, 1 insertion(+) diff --git a/build/build.py b/build/build.py index 29f827e8a..561f27c1a 100644 --- a/build/build.py +++ b/build/build.py @@ -126,6 +126,7 @@ def winmain(): if SIGNING: # environment variables will be used by powershell script for signing + subprocess.run(['pwsh.exe', 'ls', 'build/Nagstamon']) subprocess.run(['pwsh.exe', './windows/code_signing.ps1', 'build/Nagstamon/*.exe']) # rename output From 192d9a8dd031870124863b127378e717371d94c9 Mon Sep 17 00:00:00 2001 From: HenriWahl <171809906+hw-iu@users.noreply.github.com> Date: Thu, 10 Oct 2024 23:02:49 +0200 Subject: [PATCH 821/884] debug sign --- build/build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/build.py b/build/build.py index 561f27c1a..6173ad09f 100644 --- a/build/build.py +++ b/build/build.py @@ -126,7 +126,7 @@ def winmain(): if SIGNING: # environment variables will be used by powershell script for signing - subprocess.run(['pwsh.exe', 'ls', 'build/Nagstamon']) + subprocess.run(['ls', 'build/Nagstamon']) subprocess.run(['pwsh.exe', './windows/code_signing.ps1', 'build/Nagstamon/*.exe']) # rename output From a049afecbaad69a2409a7b70c323e301b08e3ab8 Mon Sep 17 00:00:00 2001 From: HenriWahl <171809906+hw-iu@users.noreply.github.com> Date: Thu, 10 Oct 2024 23:15:04 +0200 Subject: [PATCH 822/884] debug sign --- build/build.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/build.py b/build/build.py index 6173ad09f..8003e1c31 100644 --- a/build/build.py +++ b/build/build.py @@ -126,7 +126,6 @@ def winmain(): if SIGNING: # environment variables will be used by powershell script for signing - subprocess.run(['ls', 'build/Nagstamon']) subprocess.run(['pwsh.exe', './windows/code_signing.ps1', 'build/Nagstamon/*.exe']) # rename output @@ -173,6 +172,8 @@ def winmain(): sys.exit(result) if SIGNING: + subprocess.run(['pwsh.exe', './windows/code_signing.ps1', '*.zip']) + subprocess.run(['ls']) # environment variables will be used by powershell script for signing subprocess.run(['pwsh.exe', '../windows/code_signing.ps1', '*.exe']) From ae8d825c01d9c0e9822e8aa44f4a8846398b98ef Mon Sep 17 00:00:00 2001 From: HenriWahl <171809906+hw-iu@users.noreply.github.com> Date: Thu, 10 Oct 2024 23:20:12 +0200 Subject: [PATCH 823/884] debug sign --- build/build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/build.py b/build/build.py index 8003e1c31..9f272fe08 100644 --- a/build/build.py +++ b/build/build.py @@ -172,7 +172,7 @@ def winmain(): sys.exit(result) if SIGNING: - subprocess.run(['pwsh.exe', './windows/code_signing.ps1', '*.zip']) + subprocess.run(['pwsh.exe', '../windows/code_signing.ps1', '*.zip']) subprocess.run(['ls']) # environment variables will be used by powershell script for signing subprocess.run(['pwsh.exe', '../windows/code_signing.ps1', '*.exe']) From e18b6a3c6e5d10fa749ce5b8aa717912d3c7fee5 Mon Sep 17 00:00:00 2001 From: HenriWahl <171809906+hw-iu@users.noreply.github.com> Date: Thu, 10 Oct 2024 23:27:45 +0200 Subject: [PATCH 824/884] debug sign --- build/build.py | 1 + 1 file changed, 1 insertion(+) diff --git a/build/build.py b/build/build.py index 9f272fe08..72c781b24 100644 --- a/build/build.py +++ b/build/build.py @@ -150,6 +150,7 @@ def winmain(): for root, dirs, files in os.walk(os.path.basename(DIR_BUILD_NAGSTAMON)): for file in files: zip_archive.write('{0}{1}{2}'.format(root, os.sep, file)) + zip_archive.close() if not DEBUG: # for some reason out of nowhere the old path SetupIconFile={#resources}\nagstamon.ico From e933f2dc73805aacbd028a67d5b488eae87f3467 Mon Sep 17 00:00:00 2001 From: HenriWahl <171809906+hw-iu@users.noreply.github.com> Date: Thu, 10 Oct 2024 23:33:37 +0200 Subject: [PATCH 825/884] debug sign --- build/build.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/build/build.py b/build/build.py index 72c781b24..6e83a97fa 100644 --- a/build/build.py +++ b/build/build.py @@ -173,8 +173,6 @@ def winmain(): sys.exit(result) if SIGNING: - subprocess.run(['pwsh.exe', '../windows/code_signing.ps1', '*.zip']) - subprocess.run(['ls']) # environment variables will be used by powershell script for signing subprocess.run(['pwsh.exe', '../windows/code_signing.ps1', '*.exe']) From c8ba1d7f8c30551b2f80795770d5b63d833a2c2c Mon Sep 17 00:00:00 2001 From: HenriWahl <171809906+hw-iu@users.noreply.github.com> Date: Thu, 10 Oct 2024 23:36:44 +0200 Subject: [PATCH 826/884] removed duplicate changelog --- ChangeLog | 512 ------------------------------------------------------ 1 file changed, 512 deletions(-) delete mode 100644 ChangeLog diff --git a/ChangeLog b/ChangeLog deleted file mode 100644 index 21f549b89..000000000 --- a/ChangeLog +++ /dev/null @@ -1,512 +0,0 @@ -nagstamon (3.14.0) stable; urgency=low - * New upstream - - improved Wayland support - - improved proxy support - - added Opsview hashtag filtering and can_change_only option - - fixes for Alertmanager - - fixes for Centreon - - fixes for Icinga - - fixes for Opsview - - fixes for Zabbix - - added support for registering version in Windows - - added support for using system certificates in Windows - - -- Henri Wahl <henri@nagstamon.de> Sun, Feb 25 2024 08:00:00 +0200 - -nagstamon (3.12.0) stable; urgency=low - * New upstream - - added option to hide dock icon on macOS when using systray - - added fallback GSSAPI usage when no Kerberos is available - - added usage of local certificate store on Windows - - added codesigning for Windows builds - - added support for RHEL9 - - added Windows debugging version - - fixed Wayland support without using EWMH - - fixes for Icinga - - fixes for Zabbix - - fixes for Centreon - - fixes for Opsview - - fixes for Monitos - - improved build pipeline on GitHub Actions - - -- Henri Wahl <henri@nagstamon.de> Sat, May 06 2023 08:00:00 +0200 - -nagstamon (3.10.1) stable; urgency=low - * New upstream - - fixes Centreon flapping bug - - fixes Kubuntu 20.04 not starting bug - - -- Henri Wahl <henri@nagstamon.de> Fri, 04 Nov 2022 08:00:00 +0100 - -nagstamon (3.10.0) stable; urgency=low - * New upstream - - upgrade to Qt6 GUI framework - - updates for latest Centreon - - added bearer authentication - - added IcingaDB support - - extended Zabbix support - - fixes for GUI - - fixes for TLS - - fixes for Checkmk - - -- Henri Wahl <henri@nagstamon.de> Thu, 27 Oct 2022 08:00:00 +0100 - -nagstamon (3.8.0) stable; urgency=low - * New upstream - - added alertmanager acknownledgment - - added ECP authentication - - added Zabbix 5.4+ support - - Checkmk 2.0 fixes - - Thruk fixes - - Zabbix fixes - - dependency updates - - -- Henri Wahl <henri@nagstamon.de> Mon, 15 Nov 2021 19:00:00 +0100 - -nagstamon (3.6.0) stable; urgency=low - * New upstream - - added Prometheus support - - added SensuGo support - - added support for SOCKS5 proxy - - added display of macOS version - - added Thruk autologin - - update for Centreon 20.10 - - fixes for Zabbix - - fix for IcingaWeb2 - - fix for Nagios/Icinga login with invalid credentials - - fixes Thruk login - - fixes context menu bug - - fix for HighDPI - - fix for distro detection - - -- Henri Wahl <henri@nagstamon.de> Sun, 05 Apr 2021 17:00:00 +0100 - -nagstamon (3.4.1) stable; urgency=low - * New upstream - - Centreon fix for acknowledgement when using autologin key - - IcingaWeb2 fix support older setups - - fix popup in systray mode on Windows - - improved mode switching - - better support for high resolution displays - - build support for Python 3.8 - - improved Windows installer - - -- Henri Wahl <h.wahl@ifw-dresden.de> Mon, 27 Jan 2020 10:00:00 +0100 - -nagstamon (3.4) stable; urgency=low - * New upstream - - added multiple item selection and actions execution - - added notification for OK state - - added darkmode for macOS - - added option to copy service to clipboard - - added support for custom CA cert for Sensu/Uchiwa - - added support for IcingaWeb2 expire time - - added server encoding detection - - updated components - - fixed crash when selecting downtime on down host - - fixed settings filenames troubles by url-encoding them - - fixed pyinstaller onefile issues on macOS - - fixed Kerberos issues on macOS - - fixed Cinnamon systray/popup issue - - Centreon fixes - - Checkmk fixes - - IcingaWeb2 fixes - - Monitos4x fixes - - op5Monitor fixes - - Sensu fixes - --- Henri Wahl <h.wahl@ifw-dresden.de> Fri, 20 Dec 2019 10:00:00 +0100 - -nagstamon (3.2.1) stable; urgency=low - * New upstream - - fixed macOS system tray icon crash - --- Henri Wahl <h.wahl@ifw-dresden.de> Mon, 7 Jan 2019 10:00:00 +0100 - -nagstamon (3.2) stable; urgency=low - * New upstream - - new Zabbix backend - - added Sensu/Uchiwa changes - - fixed Opsview/Nagios duration counter - - fixed Centreon new versions - - fixed non-closing floating statusbar - - fixed certain custom notification action bugs - - fixed certain keyring bugs - - fixed macOS Retina support - - -- Henri Wahl <h.wahl@ifw-dresden.de> Wed, 19 Dec 2018 22:00:00 +0100 - -nagstamon (3.0.1) stable; urgency=low - * New upstream - - fixes for Zabbix - - fixed filters for Op5 - - fixes for update detection and self-signed certificates - - -- Henri Wahl <h.wahl@ifw-dresden.de> Tue, 19 Sep 2017 16:00:00 +0200 - -nagstamon (3.0) stable; urgency=low - * New upstream - - added Kerberos authentification - - added increased security by taking TLS seriously - - added window mode - - added large OK label for fullscreen and window mode - - added click-somewhere-to-close-statuswindow mode - - added custom views to Check_MK - - added monitor type Monitos3 - - added monitor type SNAG-View 3 - - added monitor type Sensu - - fixes statuswindow garbage when changing mode - - fixed filename bug - - fixed check for .Xauthority file - - fixes for Centreon - - fixes for Check_MK 1.4.0 - - fixes for Zabbix - - switched to pyinstaller - - -- Henri Wahl <h.wahl@ifw-dresden.de> Wed, 13 Sep 2017 16:00:00 +0200 - -nagstamon (2.0.1) stable; urgency=low - * New upstream - - Major Centreon bug making it useless - - Icinga version check fix - - Thruk login fix - - EWMH initialization change - - Systrayicon left mouse click without context menu - - DBus crash workaround - - -- Henri Wahl <h.wahl@ifw-dresden.de> Thu, 20 Oct 2016 08:00:00 +0200 - -nagstamon (2.0) stable; urgency=low - * New upstream - - Based on Qt 5 it now comes with a better integrated look-and-feel – especially remarkable on MacOS - - Partly simplified design - - Less clutter in setting dialogs - - Runs on latest Windows and MacOS - - Uses QT 5 multimedia which means native sound on Linux and MacOS - - Uses only SVG graphics – allows changing colors even in systray icon - - Customizable font and font size - - Adjust to dark or light desktop theme - - Action allowing to copy host/service information to clipboard - - Added ‘Archive Event’ action for Check_MK monitors - - Additionally supports IcingaWeb2 - - Updated Opsview and Centreon to support latest monitor server versions - - Experimental support for Livestatus and Zenoss - - New build script based on cx-Freeze for Windows and MacOS - - Native 64 bit version for Windows - - No or less memory leaks, especially in Windows - - Make sure only one instance per config is running - --- Henri Wahl <h.wahl@ifw-dresden.de> Mon, 29 Aug 2016 16:00:00 +0200 - -nagstamon (1.0.1) stable; urgency=low - - * New upstream - - added option to disable system keyring storage to prevent crashes - - reverted default sorting order to "Descending" - - fixed too narrow fullscreen display - - fixed vanishing Nagstamon submenu in Ubuntu Appindicator - - -- Henri Wahl <h.wahl@ifw-dresden.de> Mon, 22 Sep 2014 9:00:00 +0200 - -nagstamon (1.0) stable; urgency=low - - * New upstream - - added custom event notification with custom commands - - added highlighting of new events - - added storage of passwords in OS keyring - - added optional tooltip for full status information - - added support for applying custom actions to specific monitor only - - added copy buttons for servers and actions dialogs - - added stopping notification if event already vanished - - added support for Op5Monitor 6.3 instead of Ninja - - added experimental Zabbix support - - added automatic refreshing after acknowledging - - added permanent hamburger menu - - unified layout of dialogs - - various Check_MK improvements - - fixed old regression not-staying-on-top-bug - - fixed Check_MK-Recheck-DOS-bug - - fixed pop window size calculation on multiple screens - - fixed following popup window on multiple screens - - fixed hiding dialogs in MacOSX - - fixed ugly statusbar font in MacOSX - - fixed use of changed colors - - fixed non-ascending default sort order - - fixed Opsview downtime dialog - - fixed sometimes not working context menu - - fixed some GUI glitches - - fixed password saving bug - - fixed Centreon language inconsistencies - - fixed regression Umlaut bug - - -- Henri Wahl <h.wahl@ifw-dresden.de> Mon, 28 Jul 2014 09:30:00 +0200 - -nagstamon (1.0rc2) unstable; urgency=low - - * New upstream - - added automatic refreshing after acknowledging - - added permanent hamburger menu - - unified layout of dialogs - - fixed some GUI glitches - - fixed password saving bug - - fixed Centreon language inconsistencies - - fixed regression Umlaut bug - - -- Henri Wahl <h.wahl@ifw-dresden.de> Tue, 08 Jul 2014 11:00:00 +0200 - -nagstamon (1.0rc1) unstable; urgency=low - - * New upstream - - added custom event notification with custom commands - - added highlighting of new events - - added storage of passwords in OS keyring - - added optional tooltip for full status information - - added support for applying custom actions to specific monitor only - - added copy buttons for servers and actions dialogs - - added stopping notification if event already vanished - - added support for Op5Monitor 6.3 instead of Ninja - - added experimental Zabbix support - - fixed old regression not-staying-on-top-bug - - fixed Check_MK-Recheck-DOS-bug - - fixed pop window size calculation on multiple screens - - fixed following popup window on multiple screens - - fixed hiding dialogs in MacOSX - - fixed ugly statusbar font in MacOSX - - fixed use of changed colors - - fixed non-ascending default sort order - - fixed Opsview downtime dialog - - fixed sometimes not working context menu - - various Check_MK improvements - - -- Henri Wahl <h.wahl@ifw-dresden.de> Tue, 24 Jun 2014 11:00:00 +0200 - -nagstamon (0.9.11) stable; urgency=low - - * New upstream - - added Ubuntu AppIndicator support - - added libnotify desktop notification support - - added Centreon criticality support - - fixed broken authentication dialog - - fixed wrong OK state for Nagios and Icinga - - fixed Correct-Statusbar-Position-O-Matic - - fixed some Thruk issues - - fixed popup resizing artefact - - fixed some server edit dialog bugs - - fixed missing auth field in Icinga when credentials are wrong - - fixed quoting URLs for browser actions - - -- Henri Wahl <h.wahl@ifw-dresden.de> Wed, 11 Sep 2013 09:00:00 +0200 - -nagstamon (0.9.11rc1) unstable; urgency=low - - * New upstream - - added Ubuntu AppIndicator support - - added libnotify desktop notification support - - added Centreon criticality support - - fixed broken authentication dialog - - fixed wrong OK state for Nagios and Icinga - - fixed Correct-Statusbar-Position-O-Matic - - fixed some Thruk issues - - fixed popup resizing artefact - - fixed some server edit dialog bugs - - fixed missing auth field in Icinga when credentials are wrong - - -- Henri Wahl <h.wahl@ifw-dresden.de> Mon, 29 Jul 2013 10:35:00 +0200 - -nagstamon (0.9.10) stable; urgency=low - - * New upstream - - added fullscreen option - - added Thruk support - - added Check_MK cookie-based auth - - added new Centreon autologin option - - added configurable default sort order - - added filter for hosts in hard/soft state for Nagios, Icinga, Opsview and Centreon - - added $STATUS-INFO$ variable for custom actions - - added audio alarms also in fullscreen mode - - improved update interval set in seconds instead minutes - - improved Icinga JSON support - - improved Centreon 2.4 xml/broker support - - improved Nagios 3.4 pagination support - - improved nicer GTK theme Murrine on MacOSX - - fixed security bug - - fixed some memory leaks - - fixed superfluous passive icon for Check_MK - - fixed blocking of shutdown/reboot on MacOSX - - fixed saving converted pre 0.9.9 config immediately - - fixed statusbar position when offscreen - - fixed some GUI issues - - fixed update detection - - -- Henri Wahl <h.wahl@ifw-dresden.de> Wed, 11 Jul 2013 11:07:13 +0200 - -nagstamon (0.9.10rc2) unstable; urgency=low - - * New upstream - - audio alarms also in fullscreen mode - - adjust x0 y0 position of statusbar when offscreen - - save converted pre 0.9.9 config immediately - - -- Henri Wahl <h.wahl@ifw-dresden.de> Tue, 09 Jul 2013 14:25:00 +0200 - -nagstamon (0.9.10rc1) unstable; urgency=low - - * New upstream - - added fullscreen option - - added Thruk support - - added Check_MK cookie-based auth - - added new Centreon autologin option - - added configurable default sort order - - added filter for hosts in hard/soft state for Nagios, Icinga, Opsview and Centreon - - added $STATUS-INFO$ variable for custom actions - - update interval set in seconds instead minutes - - improved Icinga JSON support - - improved Centreon 2.4 xml/broker support - - improved Nagios 3.4 pagination support - - uses nicer GTK theme Murrine on MacOSX - - fixed some memory leaks - - fixed superfluous passive icon for Check_MK - - fixed blocking of shutdown/reboot on MacOSX - - fixed some GUI issues - - fixed update detection - - -- Henri Wahl <h.wahl@ifw-dresden.de> Wed, 03 Jul 2013 10:25:00 +0200 - -nagstamon (0.9.9.1-1) stable; urgency=low - - * New upstream - - added custom actions in context menu - - added reauthentication in case of authenticaton problems - - changed configuration file to configuration directory (default: - ~/.nagstamon) - - added filter for flapping hosts and services - - added history button for monitors - - added shortcut to filter settings in popup window - - improved keyboard usage in acknowledge/downtime/submit dialogs - - fixed bug in Icinga acknowledgement - - fixed bug in Check_MK Multisite sorting - - fixed some Check_MK Multisite UTF trouble - - fixed some GUI artefacts when resizing popup window - - -- Henri Wahl <h.wahl@ifw-dresden.de> Fri, 13 Apr 2012 11:25:00 +0200 - -nagstamon (0.9.8.1-1) stable; urgency=low - - * New upstream - - added customizable acknowledge/downtime/submit-result defaults - - added regexp filter for status information column - - added option to connect to hosts via its monitor hostname without HTTP overhead - - added ability to keep status detail popup open despite hovering away - - added option to change offset between popup window and systray icon to avoid partly hidden popup - - fixed some popup artefacts - - fixed various bugs with acknowledgement flags (persistent/sticky/notification), now they are actually working - - fixed some issues when running on MacOS X - - -- Henri Wahl <h.wahl@ifw-dresden.de> Wed, 10 Oct 2011 14:49:00 +0200 - -nagstamon (0.9.7.1-1) stable; urgency=low - - * New upstream - - hot fix for broken Centreon support - sf.net bug 3309166 - - -- Henri Wahl <h.wahl@ifw-dresden.de> Fri, 30 May 2011 12:01:00 +0200 - -nagstamon (0.9.7-1) stable; urgency=low - - * New upstream - - on some servers now context menu allows submitting check results for hosts and services - - added filter for services on acknowledged hosts - - added icons for "passiveonly" and "flapping" hosts and services - - fix for uneditable text entry fields in settings dialog - sf.net bug 3300873 - - fix for not working filter "services on hosts in maintenance" - sf.net bug 3299790 - - fix for soft state detection in Centreon - sf.net bug 3303861 - - fix for not filtered services which should have been filtered - sf.net bug 3308008 - - -- Henri Wahl <h.wahl@ifw-dresden.de> Fri, 27 May 2011 16:01:00 +0200 - -nagstamon (0.9.6.1-1) stable; urgency=low - - * fix for sf.net bug 3298321 - displaying error when all is OK - - -- Henri Wahl <h.wahl@ifw-dresden.de> Fri, 06 May 2011 16:01:00 +0200 - -nagstamon (0.9.6-1) stable; urgency=low - - * New upstreeam release - - improved, full Ninja support - - rewritten filtering mechanism allows new features - - displaying icons in status overview popup indicating states "acknowledged" and "scheduled downtime" - - added option to play notification sounds more than once - - small UI improvements - - uses BeautifulSoup instead of lxml - - uses GTK UI Builder instead of glade - - as always: bugfixes - - -- Henri Wahl <h.wahl@ifw-dresden.de> Fri, 06 May 2011 16:01:00 +0200 - -nagstamon (0.9.5-1) stable; urgency=low - - * New upstream release - - added op5 Ninja support - - added Check_MK Multisite support - - improved Icinga support (compatibility with Icinga 1.3) - - improved Centreon support (compatible with Centreon 2.1) - - added sortable columns in status overview - - added customizable colors - - better debugging and error messages - - password must not be stored in config file - - major memory leak closed, various bugs fixed - - -- Henri Wahl <h.wahl@ifw-dresden.de> Tue, 05 Apr 2011 13:23:00 +0200 - -nagstamon (0.9.4-1) stable; urgency=low - - * New upstream release (Closes: #582977) - * removed debian/manpages - * renamed debian/nagstamon.install to debian/install - * debian/patches - - removed settings_glade - - removed setup_patch - - -- Carl Chenet <chaica@ohmytux.com> Sat, 19 Jun 2010 12:46:42 +0200 - -nagstamon (0.9.3-2) stable; urgency=low - - * debian/patches/default_search - - disable the default search for newer versions (Closes: #585928) - * debian/patches/series - - added default_search - - -- Carl Chenet <chaica@ohmytux.com> Tue, 15 Jun 2010 00:41:22 +0200 - -nagstamon (0.9.3-1) stable; urgency=low - - * New upstream release - * Switching to 3.0 source format - * debian/patches - - added settings_glade patch to close #2998035 upstream bug - - added setup_patch to remove an absolute link for the upstream - manpage - * debian/control - - added quilt in Build-Depends - - added the support of Opsview servers in the long description - * debian/nagstamon.manpages - - switched to the file provided by the upstream - * debian/nagstamon.desktop - - commenting the OnlyShowIn directive - - -- Carl Chenet <chaica@ohmytux.com> Sun, 23 May 2010 12:47:11 +0200 - -nagstamon (0.9.2-2) stable; urgency=low - - * debian/control - - Added a mandatory runtime missing dependency python-pkg-resources. - - Fixed a typo in the long message. - - -- Carl Chenet <chaica@ohmytux.com> Wed, 24 Mar 2010 23:18:21 +0100 - -nagstamon (0.9.2-1) stable; urgency=low - - * Initial release. (Closes: #534842) - - -- Carl Chenet <chaica@ohmytux.com> Mon, 22 Feb 2010 14:16:44 +0100 From 563c471b7734c3990b2a71e33691e91ad4bc9538 Mon Sep 17 00:00:00 2001 From: HenriWahl <171809906+hw-iu@users.noreply.github.com> Date: Thu, 10 Oct 2024 23:37:42 +0200 Subject: [PATCH 827/884] removed duplicate changelog --- build/debian/changelog | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/build/debian/changelog b/build/debian/changelog index 5b201606d..1e240909d 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,5 +1,13 @@ nagstamon (3.15-20241010) unstable; urgency=low * New upstream + - Apple Silicon native support + - fixes for Centreon + - fixes for Checkmk + - fixes for Icinga2 + - fixes for IcingaDBWeb + - fixes for Zabbix + - fixes for pyinstaller + - updated Qt6 -- Henri Wahl <henri@nagstamon.de> Thu, Oct 10 2024 08:00:00 +0200 From 77c528decbb28d622aaea188fc4b872ca95ba007 Mon Sep 17 00:00:00 2001 From: HenriWahl <171809906+hw-iu@users.noreply.github.com> Date: Thu, 10 Oct 2024 23:39:13 +0200 Subject: [PATCH 828/884] prepare 3.16.0 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index e2b60a7a5..7e668d71a 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -131,7 +131,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.15-20241010' + VERSION = '3.16.0' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2024 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 1e240909d..26c9e7ea4 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.15-20241010) unstable; urgency=low +nagstamon (3.16.0) unstable; urgency=low * New upstream - Apple Silicon native support - fixes for Centreon From 0d38767985ad1b487b022f0cbc2d9485704558a3 Mon Sep 17 00:00:00 2001 From: HenriWahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 14 Oct 2024 21:31:22 +0200 Subject: [PATCH 829/884] appdata 3.16.0 --- Nagstamon/resources/nagstamon.appdata.xml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/Nagstamon/resources/nagstamon.appdata.xml b/Nagstamon/resources/nagstamon.appdata.xml index 7a8c524df..6757f1f76 100644 --- a/Nagstamon/resources/nagstamon.appdata.xml +++ b/Nagstamon/resources/nagstamon.appdata.xml @@ -9,19 +9,17 @@ <name>Nagstamon</name> <summary>The status monitor for the desktop</summary> <releases> - <release version="3.14.0" date="2024-02-24"> + <release version="3.16.0" date="2024-10-10"> <description> <ul> - <li>improved Wayland support</li> - <li>improved proxy support</li> - <li>added Opsview hashtag filtering and can_change_only option</li> - <li>fixes for Alertmanager</li> + <li>Apple Silicon native support</li> <li>fixes for Centreon</li> - <li>fixes for Icinga</li> - <li>fixes for Opsview</li> + <li>fixes for Checkmk</li> + <li>fixes for Icinga2</li> + <li>fixes for IcingaDBWeb</li> <li>fixes for Zabbix</li> - <li>added support for registering version in Windows</li> - <li>added support for using system certificates in Windows</li> + <li>fixes for pyinstaller</li> + <li>updated Qt6</li> </ul> </description> </release> From 3058b72313397beefbeaf76961f706c617572f70 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 17 Oct 2024 14:12:40 +0200 Subject: [PATCH 830/884] 3.17-20241017 --- Nagstamon/QUI/__init__.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index fe3cef0a3..63a4be599 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -2937,6 +2937,8 @@ def __init__(self, server, parent=None): # start ignoring TLS trouble when button clicked self.button_fix_tls_error.clicked.connect(self.fix_tls_error) + print('highlight color', self.table.palette().highlight().color().setRgb(0,0,0,255)) + self.addWidget(self.table, 1) # as default do not show anything @@ -3237,7 +3239,10 @@ def __init__(self, columncount, rowcount, sort_column, sort_order, server, paren QTreeView::item:hover {margin: 0px; color: white; background-color: dimgrey;} - ''') + QTreeView::item:selected {margin: 0px; + color: white; + background-color: blue;} + ''') # set application font self.set_font() @@ -3324,6 +3329,8 @@ def __init__(self, columncount, rowcount, sort_column, sort_order, server, paren ## display mode - all or only header to display error #self.is_shown = False + + @Slot() def set_font(self): """ From 548bc427e74b09fef19b24eb7ccbba0ff7bd3cc7 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 17 Oct 2024 14:16:13 +0200 Subject: [PATCH 831/884] 3.17-20241017 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 7e668d71a..9380688fb 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -131,7 +131,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.16.0' + VERSION = '3.17-20241017' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2024 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 26c9e7ea4..f815464bc 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,3 +1,8 @@ +nagstamon (3.17-20241017) unstable; urgency=low + * New upstream + + -- Henri Wahl <henri@nagstamon.de> Thu, Oct 17 2024 08:00:00 +0200 + nagstamon (3.16.0) unstable; urgency=low * New upstream - Apple Silicon native support From be6fa5c04ac991def4121c8a984af0785e9929e8 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 17 Oct 2024 15:06:33 +0200 Subject: [PATCH 832/884] background color selected --- Nagstamon/QUI/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 63a4be599..53a3fd150 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -3241,7 +3241,7 @@ def __init__(self, columncount, rowcount, sort_column, sort_order, server, paren background-color: dimgrey;} QTreeView::item:selected {margin: 0px; color: white; - background-color: blue;} + background-color: dimgrey;} ''') # set application font From 6709338aed745e629810559185cdf56df12ad15d Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Thu, 17 Oct 2024 15:10:31 +0200 Subject: [PATCH 833/884] background color selected grey --- Nagstamon/QUI/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 53a3fd150..c70a432c5 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -3241,7 +3241,7 @@ def __init__(self, columncount, rowcount, sort_column, sort_order, server, paren background-color: dimgrey;} QTreeView::item:selected {margin: 0px; color: white; - background-color: dimgrey;} + background-color: grey;} ''') # set application font From 18d0437d033f0cd014fb8399601a9cabf8d5d015 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 17 Oct 2024 21:51:51 +0200 Subject: [PATCH 834/884] force fusion style for even uglier Windows 11 style --- Nagstamon/QUI/__init__.py | 6 ++++-- build/debian/rules | 0 nagstamon.py | 0 3 files changed, 4 insertions(+), 2 deletions(-) mode change 100755 => 100644 build/debian/rules mode change 100755 => 100644 nagstamon.py diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index c70a432c5..62e8a2c26 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -141,6 +141,10 @@ # global application instance APP = QApplication(sys.argv) +# as long as Windows 11 + Qt6 looks that ugly it's better to choose another app style +if OS_WINDOWS and platform.release() >= '11': + APP.setStyle('fusion') + # fixed shortened and lowered color names for cells, also used by statusbar label snippets COLORS = OrderedDict([('DOWN', 'color_down_'), ('UNREACHABLE', 'color_unreachable_'), @@ -2937,8 +2941,6 @@ def __init__(self, server, parent=None): # start ignoring TLS trouble when button clicked self.button_fix_tls_error.clicked.connect(self.fix_tls_error) - print('highlight color', self.table.palette().highlight().color().setRgb(0,0,0,255)) - self.addWidget(self.table, 1) # as default do not show anything diff --git a/build/debian/rules b/build/debian/rules old mode 100755 new mode 100644 diff --git a/nagstamon.py b/nagstamon.py old mode 100755 new mode 100644 From 0cbf76e55ec003fc70d44b0cff22a18e36b9d5a9 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 17 Oct 2024 22:11:34 +0200 Subject: [PATCH 835/884] make build.py more robust with pyinstaller --- build/build.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/build/build.py b/build/build.py index 6e83a97fa..8d6e58a15 100644 --- a/build/build.py +++ b/build/build.py @@ -112,8 +112,11 @@ def winmain(): except: os.remove(file) - # now with pyinstaller - dev version is able to run with Python 3.6 - subprocess.call(['{0}\\Scripts\\pyinstaller'.format(sys.base_prefix), + # pyinstaller seems also to be installed not in \Scripts folder - if so, try without path + pyinstaller_path = f'{sys.base_prefix}\\Scripts\\pyinstaller' + if not Path(pyinstaller_path).exists(): + pyinstaller_path = 'pyinstaller' + subprocess.call([pyinstaller_path, '--noconfirm', '--add-data=..\\Nagstamon/resources;resources', '--icon=..\\Nagstamon\\resources\\nagstamon.ico', From c489a4c8bce4a3e9cbc4f6e014226b455811273a Mon Sep 17 00:00:00 2001 From: HenriWahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 17 Oct 2024 22:20:50 +0200 Subject: [PATCH 836/884] removed Nagstamon.spec --- build/Nagstamon.spec | 51 -------------------------------------------- 1 file changed, 51 deletions(-) delete mode 100644 build/Nagstamon.spec diff --git a/build/Nagstamon.spec b/build/Nagstamon.spec deleted file mode 100644 index 3a535bcad..000000000 --- a/build/Nagstamon.spec +++ /dev/null @@ -1,51 +0,0 @@ -# -*- mode: python ; coding: utf-8 -*- - - -block_cipher = None - - -a = Analysis( - ['..\\nagstamon.py'], - pathex=[], - binaries=[], - datas=[('..\\Nagstamon/resources', 'resources')], - hiddenimports=['PyQt6.uic.plugins', 'win32timezone'], - hookspath=[], - hooksconfig={}, - runtime_hooks=[], - excludes=[], - win_no_prefer_redirects=False, - win_private_assemblies=False, - cipher=block_cipher, - noarchive=False, -) -pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) - -exe = EXE( - pyz, - a.scripts, - [], - exclude_binaries=True, - name='Nagstamon', - debug=False, - bootloader_ignore_signals=False, - strip=False, - upx=True, - console=False, - disable_windowed_traceback=False, - argv_emulation=False, - target_arch=None, - codesign_identity=None, - entitlements_file=None, - icon=['..\\Nagstamon\\resources\\nagstamon.ico'], -) -coll = COLLECT( - exe, - a.binaries, - a.zipfiles, - a.datas, - strip=False, - upx=True, - upx_exclude=[], - name='Nagstamon', -) From 93a00808ce3b52dd328fc8450f9a556c0debe737 Mon Sep 17 00:00:00 2001 From: HenriWahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 17 Oct 2024 22:47:46 +0200 Subject: [PATCH 837/884] debian rules file --- build/debian/rules | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 build/debian/rules diff --git a/build/debian/rules b/build/debian/rules old mode 100644 new mode 100755 From 62706182068efa114b690c3e206585e6013e85c5 Mon Sep 17 00:00:00 2001 From: HenriWahl <2835065+HenriWahl@users.noreply.github.com> Date: Fri, 18 Oct 2024 20:09:31 +0200 Subject: [PATCH 838/884] fix OS check --- Nagstamon/QUI/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index 62e8a2c26..f0051b514 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -142,7 +142,7 @@ APP = QApplication(sys.argv) # as long as Windows 11 + Qt6 looks that ugly it's better to choose another app style -if OS_WINDOWS and platform.release() >= '11': +if OS == OS_WINDOWS and platform.release() >= '11': APP.setStyle('fusion') # fixed shortened and lowered color names for cells, also used by statusbar label snippets From 436696eaa33ff3aa1a76d6639b9c86e28569bff1 Mon Sep 17 00:00:00 2001 From: HenriWahl <2835065+HenriWahl@users.noreply.github.com> Date: Fri, 18 Oct 2024 21:22:45 +0200 Subject: [PATCH 839/884] fix Zabbix all information bug --- Nagstamon/QUI/__init__.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index f0051b514..d8c041726 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -3932,11 +3932,20 @@ def action_clipboard_action_all(self): # item to access all properties of host/service object # defaults to host item = self.server.hosts[host] - text += 'Host: {0}\n'.format(host) + text += f'Host: {host}\n' # if it is a service switch to service object - if service != '' and item.services.get(service): - item = item.services[service] - text += 'Service: {0}\n'.format(service) + if service != '': + if item.services.get(service): + item = item.services[service] + text += f'Service: {service}\n' + # finally solve https://github.com/HenriWahl/Nagstamon/issues/1024 + elif self.server.TYPE == 'Zabbix': + for service_item in item.services.values(): + if service_item.name == service: + item = service_item + text += f'Service: {service}\n' + break + # the other properties belong to both hosts and services text += 'Status: {0}\n'.format(item.status) text += 'Last check: {0}\n'.format(item.last_check) From 7d4dc7b201213717b88be5eda8b8795c0d97d459 Mon Sep 17 00:00:00 2001 From: HenriWahl <2835065+HenriWahl@users.noreply.github.com> Date: Fri, 18 Oct 2024 21:32:37 +0200 Subject: [PATCH 840/884] 3.17-20241018 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 9380688fb..befe6b94b 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -131,7 +131,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.17-20241017' + VERSION = '3.17-20241018' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2024 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index f815464bc..d36d479cc 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,7 +1,7 @@ -nagstamon (3.17-20241017) unstable; urgency=low +nagstamon (3.17-20241018) unstable; urgency=low * New upstream - -- Henri Wahl <henri@nagstamon.de> Thu, Oct 17 2024 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Fri, Oct 18 2024 08:00:00 +0200 nagstamon (3.16.0) unstable; urgency=low * New upstream From 3d43fd7161385c6e9ac5505e88ee994a4223e9c9 Mon Sep 17 00:00:00 2001 From: HenriWahl <2835065+HenriWahl@users.noreply.github.com> Date: Sat, 19 Oct 2024 22:08:24 +0200 Subject: [PATCH 841/884] prepare 3.16.1 --- Nagstamon/Config.py | 2 +- Nagstamon/resources/nagstamon.appdata.xml | 12 +++--------- build/debian/changelog | 8 +++++--- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index befe6b94b..939a63349 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -131,7 +131,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.17-20241018' + VERSION = '3.16.1' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2024 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/resources/nagstamon.appdata.xml b/Nagstamon/resources/nagstamon.appdata.xml index 6757f1f76..31d08a43b 100644 --- a/Nagstamon/resources/nagstamon.appdata.xml +++ b/Nagstamon/resources/nagstamon.appdata.xml @@ -9,17 +9,11 @@ <name>Nagstamon</name> <summary>The status monitor for the desktop</summary> <releases> - <release version="3.16.0" date="2024-10-10"> + <release version="3.16.1" date="2024-10-20"> <description> <ul> - <li>Apple Silicon native support</li> - <li>fixes for Centreon</li> - <li>fixes for Checkmk</li> - <li>fixes for Icinga2</li> - <li>fixes for IcingaDBWeb</li> - <li>fixes for Zabbix</li> - <li>fixes for pyinstaller</li> - <li>updated Qt6</li> + <li>fix ridiculous Windows 11 appearance</li> + <li>fix Zabbix all information copy action</li> </ul> </description> </release> diff --git a/build/debian/changelog b/build/debian/changelog index d36d479cc..2a7a5ec80 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,9 +1,11 @@ -nagstamon (3.17-20241018) unstable; urgency=low +nagstamon (3.16.1) stable; urgency=low * New upstream + - fix ridiculous Windows 11 appearance + - fix Zabbix all information copy action - -- Henri Wahl <henri@nagstamon.de> Fri, Oct 18 2024 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Sun, Oct 20 2024 08:00:00 +0200 -nagstamon (3.16.0) unstable; urgency=low +nagstamon (3.16.0) stable; urgency=low * New upstream - Apple Silicon native support - fixes for Centreon From e67623e648317288410114509559665b49009b6d Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 20 Oct 2024 00:03:42 +0200 Subject: [PATCH 842/884] Update Config.py 3.17-20241018 --- Nagstamon/Config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 939a63349..befe6b94b 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -131,7 +131,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.16.1' + VERSION = '3.17-20241018' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2024 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' From 099f2de6f49fcdf926c18d9221bf08489ec51490 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 20 Oct 2024 00:04:16 +0200 Subject: [PATCH 843/884] Update changelog 3.17-20241018 --- build/debian/changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/debian/changelog b/build/debian/changelog index 2a7a5ec80..b41199c48 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.16.1) stable; urgency=low +nagstamon (3.17-20241018) unstable; urgency=low * New upstream - fix ridiculous Windows 11 appearance - fix Zabbix all information copy action From 3f2f8dfb72efef21d226495fb358966a0b4429d2 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 20 Oct 2024 23:51:51 +0200 Subject: [PATCH 844/884] nagstamon.iss icon path _internal --- build/windows/nagstamon.iss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/windows/nagstamon.iss b/build/windows/nagstamon.iss index 6a09b6396..3cb9c0884 100644 --- a/build/windows/nagstamon.iss +++ b/build/windows/nagstamon.iss @@ -27,8 +27,8 @@ ArchitecturesInstallIn64BitMode=x64 CloseApplications=no WizardStyle=modern [Icons] -Name: {group}\Nagstamon; Filename: {app}\nagstamon.exe; WorkingDir: {app}; IconFilename: {app}\resources\nagstamon.ico; IconIndex: 0 -Name: {commonstartup}\Nagstamon; Filename: {app}\nagstamon.exe; WorkingDir: {app}; IconFilename: {app}\resources\nagstamon.ico; IconIndex: 0 +Name: {group}\Nagstamon; Filename: {app}\nagstamon.exe; WorkingDir: {app}; IconFilename: {app}\_internal\resources\nagstamon.ico; IconIndex: 0 +Name: {commonstartup}\Nagstamon; Filename: {app}\nagstamon.exe; WorkingDir: {app}; IconFilename: {app}\_internal\resources\nagstamon.ico; IconIndex: 0 [Files] Source: "*"; DestDir: {app}; Flags: recursesubdirs createallsubdirs ignoreversion; BeforeInstall: KillRunningNagstamon() [Tasks] From 3e2ddc2a43a9bf30acb92732f726ee175090d87c Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Mon, 21 Oct 2024 00:07:50 +0200 Subject: [PATCH 845/884] fix for Windows icon --- build/windows/nagstamon.iss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/windows/nagstamon.iss b/build/windows/nagstamon.iss index 6a09b6396..3cb9c0884 100644 --- a/build/windows/nagstamon.iss +++ b/build/windows/nagstamon.iss @@ -27,8 +27,8 @@ ArchitecturesInstallIn64BitMode=x64 CloseApplications=no WizardStyle=modern [Icons] -Name: {group}\Nagstamon; Filename: {app}\nagstamon.exe; WorkingDir: {app}; IconFilename: {app}\resources\nagstamon.ico; IconIndex: 0 -Name: {commonstartup}\Nagstamon; Filename: {app}\nagstamon.exe; WorkingDir: {app}; IconFilename: {app}\resources\nagstamon.ico; IconIndex: 0 +Name: {group}\Nagstamon; Filename: {app}\nagstamon.exe; WorkingDir: {app}; IconFilename: {app}\_internal\resources\nagstamon.ico; IconIndex: 0 +Name: {commonstartup}\Nagstamon; Filename: {app}\nagstamon.exe; WorkingDir: {app}; IconFilename: {app}\_internal\resources\nagstamon.ico; IconIndex: 0 [Files] Source: "*"; DestDir: {app}; Flags: recursesubdirs createallsubdirs ignoreversion; BeforeInstall: KillRunningNagstamon() [Tasks] From ac337ff6251ed7f130821a1ac5306da1f506f404 Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Mon, 21 Oct 2024 00:08:44 +0200 Subject: [PATCH 846/884] fix for Windows icon --- build/windows/nagstamon.iss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/windows/nagstamon.iss b/build/windows/nagstamon.iss index 6a09b6396..3cb9c0884 100644 --- a/build/windows/nagstamon.iss +++ b/build/windows/nagstamon.iss @@ -27,8 +27,8 @@ ArchitecturesInstallIn64BitMode=x64 CloseApplications=no WizardStyle=modern [Icons] -Name: {group}\Nagstamon; Filename: {app}\nagstamon.exe; WorkingDir: {app}; IconFilename: {app}\resources\nagstamon.ico; IconIndex: 0 -Name: {commonstartup}\Nagstamon; Filename: {app}\nagstamon.exe; WorkingDir: {app}; IconFilename: {app}\resources\nagstamon.ico; IconIndex: 0 +Name: {group}\Nagstamon; Filename: {app}\nagstamon.exe; WorkingDir: {app}; IconFilename: {app}\_internal\resources\nagstamon.ico; IconIndex: 0 +Name: {commonstartup}\Nagstamon; Filename: {app}\nagstamon.exe; WorkingDir: {app}; IconFilename: {app}\_internal\resources\nagstamon.ico; IconIndex: 0 [Files] Source: "*"; DestDir: {app}; Flags: recursesubdirs createallsubdirs ignoreversion; BeforeInstall: KillRunningNagstamon() [Tasks] From 9d5e1583f9381c3f39bbde972a1e7e990d32a545 Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Mon, 21 Oct 2024 00:29:19 +0200 Subject: [PATCH 847/884] fix for Windows icon uninstaller --- build/windows/nagstamon.iss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/windows/nagstamon.iss b/build/windows/nagstamon.iss index 3cb9c0884..96b8f4a4a 100644 --- a/build/windows/nagstamon.iss +++ b/build/windows/nagstamon.iss @@ -10,7 +10,7 @@ ShowLanguageDialog=no SetupIconFile=nagstamon.ico UsePreviousGroup=false OutputBaseFilename=Nagstamon-{#version}-win{#arch}_setup -UninstallDisplayIcon={app}\resources\nagstamon.ico +UninstallDisplayIcon={app}\_internal\resources\nagstamon.ico UsePreviousAppDir=false AppID={{44F7CFFB-4776-4DA4-9930-A07178069517} UninstallRestartComputer=false From c9cb241b60d077800000ec434647de27bfae4bc3 Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Mon, 21 Oct 2024 00:30:30 +0200 Subject: [PATCH 848/884] fix for Windows icon uninstaller --- build/windows/nagstamon.iss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/windows/nagstamon.iss b/build/windows/nagstamon.iss index 3cb9c0884..96b8f4a4a 100644 --- a/build/windows/nagstamon.iss +++ b/build/windows/nagstamon.iss @@ -10,7 +10,7 @@ ShowLanguageDialog=no SetupIconFile=nagstamon.ico UsePreviousGroup=false OutputBaseFilename=Nagstamon-{#version}-win{#arch}_setup -UninstallDisplayIcon={app}\resources\nagstamon.ico +UninstallDisplayIcon={app}\_internal\resources\nagstamon.ico UsePreviousAppDir=false AppID={{44F7CFFB-4776-4DA4-9930-A07178069517} UninstallRestartComputer=false From 8e7fb5ff2bd8c3f3c7c0ce16a701935f4b24e344 Mon Sep 17 00:00:00 2001 From: Henri <henri@nagstamon.de> Date: Mon, 21 Oct 2024 00:31:13 +0200 Subject: [PATCH 849/884] fix for Windows icon uninstaller --- build/windows/nagstamon.iss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/windows/nagstamon.iss b/build/windows/nagstamon.iss index 3cb9c0884..96b8f4a4a 100644 --- a/build/windows/nagstamon.iss +++ b/build/windows/nagstamon.iss @@ -10,7 +10,7 @@ ShowLanguageDialog=no SetupIconFile=nagstamon.ico UsePreviousGroup=false OutputBaseFilename=Nagstamon-{#version}-win{#arch}_setup -UninstallDisplayIcon={app}\resources\nagstamon.ico +UninstallDisplayIcon={app}\_internal\resources\nagstamon.ico UsePreviousAppDir=false AppID={{44F7CFFB-4776-4DA4-9930-A07178069517} UninstallRestartComputer=false From 90c9d8c67436871f6f2d2568f8effbd4f80f0625 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Mon, 21 Oct 2024 16:48:31 +0200 Subject: [PATCH 850/884] play with Qt 6.5.3 --- Nagstamon/QUI/__init__.py | 5 +++-- build/requirements/windows.txt | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index d8c041726..81c51c3f0 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -142,8 +142,9 @@ APP = QApplication(sys.argv) # as long as Windows 11 + Qt6 looks that ugly it's better to choose another app style -if OS == OS_WINDOWS and platform.release() >= '11': - APP.setStyle('fusion') +# might be mitigated with Qt 6.5.3, so commented out now +#if OS == OS_WINDOWS and platform.release() >= '11': +# APP.setStyle('fusion') # fixed shortened and lowered color names for cells, also used by statusbar label snippets COLORS = OrderedDict([('DOWN', 'color_down_'), diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index 5a1becbd2..033189ac0 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -6,8 +6,9 @@ pip-system-certs psutil pyinstaller pypiwin32 -pyqt6 -pyqt6-qt6 +# since 6.6.0 no sound is available!?! +pyqt6==6.5.3 +pyqt6-qt6==6.5.3 pysocks python-dateutil requests From 297bf8bbb3dd85655de8b765b7c7460b4b33d29b Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Mon, 21 Oct 2024 17:04:27 +0200 Subject: [PATCH 851/884] fix sound problem --- build/debian/changelog | 6 ++++++ build/requirements/macos.txt | 5 +++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/build/debian/changelog b/build/debian/changelog index 2a7a5ec80..0cccdf057 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,3 +1,9 @@ +nagstamon (3.17-20241021) unstable; urgency=low + * New upstream + - fix sound problem + + -- Henri Wahl <henri@nagstamon.de> Mon, Oct 21 2024 08:00:00 +0200 + nagstamon (3.16.1) stable; urgency=low * New upstream - fix ridiculous Windows 11 appearance diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index 1e34e689e..e9842b4f2 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -5,8 +5,9 @@ lxml psutil pyinstaller pyobjc-framework-ApplicationServices -pyqt6 -pyqt6-qt6 +# since 6.6.0 no sound is available!?! +pyqt6==6.5.3 +pyqt6-qt6==6.5.3 pysocks python-dateutil requests From 61bc87e8c51ca70fe81cdad39d78a7ecb3927ec4 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Mon, 21 Oct 2024 17:04:27 +0200 Subject: [PATCH 852/884] fix sound problem (cherry picked from commit 297bf8bbb3dd85655de8b765b7c7460b4b33d29b) --- build/debian/changelog | 8 +++++++- build/requirements/macos.txt | 5 +++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/build/debian/changelog b/build/debian/changelog index b41199c48..0cccdf057 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,10 @@ -nagstamon (3.17-20241018) unstable; urgency=low +nagstamon (3.17-20241021) unstable; urgency=low + * New upstream + - fix sound problem + + -- Henri Wahl <henri@nagstamon.de> Mon, Oct 21 2024 08:00:00 +0200 + +nagstamon (3.16.1) stable; urgency=low * New upstream - fix ridiculous Windows 11 appearance - fix Zabbix all information copy action diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index 1e34e689e..e9842b4f2 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -5,8 +5,9 @@ lxml psutil pyinstaller pyobjc-framework-ApplicationServices -pyqt6 -pyqt6-qt6 +# since 6.6.0 no sound is available!?! +pyqt6==6.5.3 +pyqt6-qt6==6.5.3 pysocks python-dateutil requests From c0010abe698295edc76d5e3af9b0253d071b3750 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Mon, 21 Oct 2024 17:04:27 +0200 Subject: [PATCH 853/884] fix sound problem (cherry picked from commit 297bf8bbb3dd85655de8b765b7c7460b4b33d29b) --- build/debian/changelog | 6 ++++++ build/requirements/macos.txt | 5 +++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/build/debian/changelog b/build/debian/changelog index 2a7a5ec80..0cccdf057 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,3 +1,9 @@ +nagstamon (3.17-20241021) unstable; urgency=low + * New upstream + - fix sound problem + + -- Henri Wahl <henri@nagstamon.de> Mon, Oct 21 2024 08:00:00 +0200 + nagstamon (3.16.1) stable; urgency=low * New upstream - fix ridiculous Windows 11 appearance diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index 1e34e689e..e9842b4f2 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -5,8 +5,9 @@ lxml psutil pyinstaller pyobjc-framework-ApplicationServices -pyqt6 -pyqt6-qt6 +# since 6.6.0 no sound is available!?! +pyqt6==6.5.3 +pyqt6-qt6==6.5.3 pysocks python-dateutil requests From 4b592a3003c4e742dd415129b00360b9ac7aaaa1 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Mon, 21 Oct 2024 17:07:08 +0200 Subject: [PATCH 854/884] fix sound problem --- build/debian/changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/debian/changelog b/build/debian/changelog index 0cccdf057..d634d769e 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.17-20241021) unstable; urgency=low +nagstamon (3.16.2) stable; urgency=low * New upstream - fix sound problem From dd3ca396a2a1bd9e9115eb56cb564cddcf00c644 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Wed, 23 Oct 2024 17:53:24 +0200 Subject: [PATCH 855/884] try to fix sound problem --- Nagstamon/Config.py | 2 +- build/debian/changelog | 2 +- build/requirements/windows.txt | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index befe6b94b..86a5b8beb 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -131,7 +131,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.17-20241018' + VERSION = '3.17-20241023' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2024 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index 0cccdf057..3b33385f4 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.17-20241021) unstable; urgency=low +nagstamon (3.17-20241023) unstable; urgency=low * New upstream - fix sound problem diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index 033189ac0..0894ec2a2 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -7,8 +7,8 @@ psutil pyinstaller pypiwin32 # since 6.6.0 no sound is available!?! -pyqt6==6.5.3 -pyqt6-qt6==6.5.3 +pyqt6==6.6.1 +pyqt6-qt6==6.6.1 pysocks python-dateutil requests From be4283d636b968716caf4b0a55dfa6786d0198ed Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Mon, 21 Oct 2024 16:48:31 +0200 Subject: [PATCH 856/884] play with Qt 6.5.3 --- Nagstamon/QUI/__init__.py | 5 +++-- build/requirements/windows.txt | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Nagstamon/QUI/__init__.py b/Nagstamon/QUI/__init__.py index d8c041726..81c51c3f0 100644 --- a/Nagstamon/QUI/__init__.py +++ b/Nagstamon/QUI/__init__.py @@ -142,8 +142,9 @@ APP = QApplication(sys.argv) # as long as Windows 11 + Qt6 looks that ugly it's better to choose another app style -if OS == OS_WINDOWS and platform.release() >= '11': - APP.setStyle('fusion') +# might be mitigated with Qt 6.5.3, so commented out now +#if OS == OS_WINDOWS and platform.release() >= '11': +# APP.setStyle('fusion') # fixed shortened and lowered color names for cells, also used by statusbar label snippets COLORS = OrderedDict([('DOWN', 'color_down_'), diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index 5a1becbd2..033189ac0 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -6,8 +6,9 @@ pip-system-certs psutil pyinstaller pypiwin32 -pyqt6 -pyqt6-qt6 +# since 6.6.0 no sound is available!?! +pyqt6==6.5.3 +pyqt6-qt6==6.5.3 pysocks python-dateutil requests From 195e48b2bc78684c48076582c6132c8148fec4f0 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Wed, 23 Oct 2024 17:53:24 +0200 Subject: [PATCH 857/884] try to fix sound problem --- Nagstamon/Config.py | 2 +- build/requirements/windows.txt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 939a63349..0aaa0c7c9 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -131,7 +131,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.16.1' + VERSION = '3.16.2' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2024 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index 033189ac0..0894ec2a2 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -7,8 +7,8 @@ psutil pyinstaller pypiwin32 # since 6.6.0 no sound is available!?! -pyqt6==6.5.3 -pyqt6-qt6==6.5.3 +pyqt6==6.6.1 +pyqt6-qt6==6.6.1 pysocks python-dateutil requests From 78788bcfc08614b39bd63f0fcbf1ea8d96086dff Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Wed, 23 Oct 2024 19:27:33 +0200 Subject: [PATCH 858/884] try to fix sound problem --- build/requirements/macos.txt | 6 +++--- build/requirements/windows.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index e9842b4f2..8136b3a50 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -5,9 +5,9 @@ lxml psutil pyinstaller pyobjc-framework-ApplicationServices -# since 6.6.0 no sound is available!?! -pyqt6==6.5.3 -pyqt6-qt6==6.5.3 +# 6.7.3 has no sound +pyqt6==6.6.1 +pyqt6-qt6==6.6.1 pysocks python-dateutil requests diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index 0894ec2a2..4c56fd130 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -6,7 +6,7 @@ pip-system-certs psutil pyinstaller pypiwin32 -# since 6.6.0 no sound is available!?! +# 6.7.3 has no sound pyqt6==6.6.1 pyqt6-qt6==6.6.1 pysocks From 9b408463fa1d202ff927a584da4534aa3f799e1a Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Wed, 23 Oct 2024 19:27:33 +0200 Subject: [PATCH 859/884] try to fix sound problem --- build/requirements/macos.txt | 6 +++--- build/requirements/windows.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index e9842b4f2..8136b3a50 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -5,9 +5,9 @@ lxml psutil pyinstaller pyobjc-framework-ApplicationServices -# since 6.6.0 no sound is available!?! -pyqt6==6.5.3 -pyqt6-qt6==6.5.3 +# 6.7.3 has no sound +pyqt6==6.6.1 +pyqt6-qt6==6.6.1 pysocks python-dateutil requests diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index 0894ec2a2..4c56fd130 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -6,7 +6,7 @@ pip-system-certs psutil pyinstaller pypiwin32 -# since 6.6.0 no sound is available!?! +# 6.7.3 has no sound pyqt6==6.6.1 pyqt6-qt6==6.6.1 pysocks From 70764f74b9952e5d324044bc5f0d9bcf11e1c604 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Mon, 21 Oct 2024 17:04:27 +0200 Subject: [PATCH 860/884] fix sound problem (cherry picked from commit 297bf8bbb3dd85655de8b765b7c7460b4b33d29b) --- build/requirements/macos.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index 8136b3a50..e9842b4f2 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -5,9 +5,9 @@ lxml psutil pyinstaller pyobjc-framework-ApplicationServices -# 6.7.3 has no sound -pyqt6==6.6.1 -pyqt6-qt6==6.6.1 +# since 6.6.0 no sound is available!?! +pyqt6==6.5.3 +pyqt6-qt6==6.5.3 pysocks python-dateutil requests From c1140b75f2748e170c19c3a797db29dc9b5d3595 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Wed, 23 Oct 2024 17:53:24 +0200 Subject: [PATCH 861/884] try to fix sound problem --- build/requirements/windows.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index 4c56fd130..0894ec2a2 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -6,7 +6,7 @@ pip-system-certs psutil pyinstaller pypiwin32 -# 6.7.3 has no sound +# since 6.6.0 no sound is available!?! pyqt6==6.6.1 pyqt6-qt6==6.6.1 pysocks From c924ff8f980f8aee5ee8548f277b5c788297fc22 Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Wed, 23 Oct 2024 19:27:33 +0200 Subject: [PATCH 862/884] try to fix sound problem --- build/requirements/macos.txt | 6 +++--- build/requirements/windows.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/requirements/macos.txt b/build/requirements/macos.txt index e9842b4f2..8136b3a50 100644 --- a/build/requirements/macos.txt +++ b/build/requirements/macos.txt @@ -5,9 +5,9 @@ lxml psutil pyinstaller pyobjc-framework-ApplicationServices -# since 6.6.0 no sound is available!?! -pyqt6==6.5.3 -pyqt6-qt6==6.5.3 +# 6.7.3 has no sound +pyqt6==6.6.1 +pyqt6-qt6==6.6.1 pysocks python-dateutil requests diff --git a/build/requirements/windows.txt b/build/requirements/windows.txt index 0894ec2a2..4c56fd130 100644 --- a/build/requirements/windows.txt +++ b/build/requirements/windows.txt @@ -6,7 +6,7 @@ pip-system-certs psutil pyinstaller pypiwin32 -# since 6.6.0 no sound is available!?! +# 6.7.3 has no sound pyqt6==6.6.1 pyqt6-qt6==6.6.1 pysocks From a16af223313e54ce3e9c509f2075e6c6b6f1e96b Mon Sep 17 00:00:00 2001 From: Henri Wahl <henri.wahl@ukdd.de> Date: Wed, 23 Oct 2024 19:50:15 +0200 Subject: [PATCH 863/884] prepare 3.16.2 --- build/debian/changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/debian/changelog b/build/debian/changelog index d634d769e..a6d50a57b 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -2,7 +2,7 @@ nagstamon (3.16.2) stable; urgency=low * New upstream - fix sound problem - -- Henri Wahl <henri@nagstamon.de> Mon, Oct 21 2024 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Wed, Oct 23 2024 08:00:00 +0200 nagstamon (3.16.1) stable; urgency=low * New upstream From b586f976490a47bf9db290ec0bdfd8af7513e235 Mon Sep 17 00:00:00 2001 From: Dregaringo <46315689+Dregaringo@users.noreply.github.com> Date: Thu, 24 Oct 2024 23:00:26 +0300 Subject: [PATCH 864/884] Fix double URL encoding on macOS for IcingaDBWeb (#1071) * fix for Windows icon * fix for Windows icon uninstaller * fix sound problem * fix: resolve double URL encoding issue on macOS --------- Co-authored-by: Henri <henri@nagstamon.de> Co-authored-by: Henri Wahl <henri.wahl@ukdd.de> Co-authored-by: Andrii Ivanov <andrii.ivanov@namecheap.com> Co-authored-by: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> --- Nagstamon/Servers/IcingaDBWeb.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index 3befe0023..ed4ed023e 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -736,18 +736,26 @@ def open_monitor(self, host, service=''): print("Cannot find {}::{}. Skipping!".format(host, service)) return - # only type is important so do not care of service '' in case of host monitor + # Generate the base path for the URL + base_path = urllib.parse.urlparse(self.monitor_url).path + + # Handle URL for host monitoring if service == '': - url = '{0}/icingadb/hosts?host.state.is_problem=y&sort=host.state.severity#!{1}/icingadb/host?{2}'.format(self.monitor_url, - (urllib.parse.urlparse(self.monitor_url).path), - urllib.parse.urlencode( - {'name': self.hosts[host].real_name}).replace('+', ' ')) + url = '{0}/icingadb/hosts?host.state.is_problem=y&sort=host.state.severity#!{1}/icingadb/host?{2}'.format( + self.monitor_url, + base_path, + urllib.parse.urlencode({'name': self.hosts[host].real_name}, quote_via=quote) + ) else: - url = '{0}/icingadb/services?service.state.is_problem=y&sort=service.state.severity%20desc#!{1}/icingadb/service?{2}'.format(self.monitor_url, - (urllib.parse.urlparse(self.monitor_url).path), - urllib.parse.urlencode( - {'name': self.hosts[host].services[service].real_name, - 'host.name': self.hosts[host].real_name}).replace('+', ' ')) + # Handle URL for service monitoring + url = '{0}/icingadb/services?service.state.is_problem=y&sort=service.state.severity%20desc#!{1}/icingadb/service?{2}'.format( + self.monitor_url, + base_path, + urllib.parse.urlencode({ + 'name': self.hosts[host].services[service].real_name, + 'host.name': self.hosts[host].real_name + }, quote_via=urllib.parse.quote) + ) if conf.debug_mode: self.debug(server=self.get_name(), host=host, service=service, debug='[Open monitor] Open host/service monitor web page {0}'.format(url)) From d601ecfbdd589de4c9b8fec3f04b7441ce3377ca Mon Sep 17 00:00:00 2001 From: HenriWahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 24 Oct 2024 22:02:34 +0200 Subject: [PATCH 865/884] prepare 3.16.2 --- build/debian/changelog | 1 + 1 file changed, 1 insertion(+) diff --git a/build/debian/changelog b/build/debian/changelog index 3b33385f4..e499e501a 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,6 +1,7 @@ nagstamon (3.17-20241023) unstable; urgency=low * New upstream - fix sound problem + - fix IncingaDBWeb -- Henri Wahl <henri@nagstamon.de> Mon, Oct 21 2024 08:00:00 +0200 From 7db649edc5c3aef54f81972d1f66a017b8075816 Mon Sep 17 00:00:00 2001 From: HenriWahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 24 Oct 2024 22:07:39 +0200 Subject: [PATCH 866/884] cr_image_latest: 40 --- .github/workflows/build-release-latest.yml | 2 +- .github/workflows/build-release-stable.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index f5b0008e4..0d46f7e15 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -375,7 +375,7 @@ jobs: env: family: fedora # which image to use for packaging - cr_image_latest: 39 + cr_image_latest: 40 steps: # get binaries created by other jobs - uses: actions/download-artifact@v4 diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index a12309668..057cfe08f 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -371,7 +371,7 @@ jobs: env: family: fedora # which image to use for packaging - cr_image_latest: 39 + cr_image_latest: 40 steps: # get binaries created by other jobs - uses: actions/download-artifact@v4 From ce4aa5f4e40896d451798edaaf4a49a41b8d8851 Mon Sep 17 00:00:00 2001 From: HenriWahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 24 Oct 2024 22:21:14 +0200 Subject: [PATCH 867/884] docker images --- .github/workflows/build-release-latest.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 0d46f7e15..ee9250f9f 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -347,6 +347,9 @@ jobs: - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/${{ env.dist }}/${{ env.release }} - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${{ env.dist }}/${{ env.release }} + # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed + - run: docker pull${{ env.cr_image }}-${{ env.family }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ env.family }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ env.family }}:${{ env.cr_image_version }} # create deb repo via Debian build container - run: | /usr/bin/docker run --rm \ @@ -391,6 +394,9 @@ jobs: - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/${{ env.release }} - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${{ env.release }} + # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ env.family }}-${{ env.cr_image_latest }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ env.family }}-${{ env.cr_image_latest }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ env.family }}-${{ env.cr_image_latest }}:${{ env.cr_image_version }} # copy *.rpm files into nagstamon-jekyll and create repodata - run: | version=${{ env.release }} && \ From 4f77bb28176cc55809449002610137bd55a096c2 Mon Sep 17 00:00:00 2001 From: HenriWahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 24 Oct 2024 22:23:55 +0200 Subject: [PATCH 868/884] docker images --- .github/workflows/build-release-latest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index ee9250f9f..7c3b226dc 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -348,7 +348,7 @@ jobs: - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/${{ env.dist }}/${{ env.release }} - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${{ env.dist }}/${{ env.release }} # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed - - run: docker pull${{ env.cr_image }}-${{ env.family }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ env.family }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker pull ${{ env.cr_image }}-${{ env.family }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ env.family }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . - run: docker push ${{ env.cr_image }}-${{ env.family }}:${{ env.cr_image_version }} # create deb repo via Debian build container - run: | From 4fef7da63da40092b2cd98ac334f715890a39ee0 Mon Sep 17 00:00:00 2001 From: HenriWahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 24 Oct 2024 22:30:08 +0200 Subject: [PATCH 869/884] docker images --- .github/workflows/build-release-latest.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 7c3b226dc..f379f67d4 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -337,6 +337,12 @@ jobs: pattern: 'debian*' path: artifact merge-multiple: true + # docker login is needed for pushing the build image + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} # get secret signing key - run: echo "${{ secrets.PACKAGE_SIGNING_KEY }}" > signing_key.asc # organize SSH deploy key for nagstamon-jekyll repo @@ -386,6 +392,12 @@ jobs: pattern: 'fedora*' path: artifact merge-multiple: true + # docker login is needed for pushing the build image + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} # organize SSH deploy key for nagstamon-repo - run: mkdir ~/.ssh - run: echo "${{ secrets.NAGSTAMON_REPO_KEY_WEB }}" > ~/.ssh/id_ed25519 From 78eb43c3cda740e756c440dd9c7ab9b2173ea75d Mon Sep 17 00:00:00 2001 From: HenriWahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 24 Oct 2024 22:38:24 +0200 Subject: [PATCH 870/884] docker images --- .github/workflows/build-release-latest.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index f379f67d4..b8af3d5bb 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -438,6 +438,12 @@ jobs: pattern: 'rhel*' path: artifact merge-multiple: true + # docker login is needed for pushing the build image + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} # organize SSH deploy key for nagstamon-repo - run: mkdir ~/.ssh - run: echo "${{ secrets.NAGSTAMON_REPO_KEY_WEB }}" > ~/.ssh/id_ed25519 @@ -446,6 +452,9 @@ jobs: - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/${{ env.release }} - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${{ env.release }} + # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ env.family }}-${{ env.version }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ env.family }}-${{ env.version }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ env.family }}-${{ env.version }}:${{ env.cr_image_version }} # copy *.rpm files into nagstamon-jekyll and create repodata - run: | version=${{ env.release }} && \ From ddfd7b0e12bbabf8e98f5bd4ac2ef0ef632deafd Mon Sep 17 00:00:00 2001 From: HenriWahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 24 Oct 2024 22:47:36 +0200 Subject: [PATCH 871/884] docker images --- .github/workflows/build-release-latest.yml | 27 ---------------------- 1 file changed, 27 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index b8af3d5bb..0d46f7e15 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -337,12 +337,6 @@ jobs: pattern: 'debian*' path: artifact merge-multiple: true - # docker login is needed for pushing the build image - - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} # get secret signing key - run: echo "${{ secrets.PACKAGE_SIGNING_KEY }}" > signing_key.asc # organize SSH deploy key for nagstamon-jekyll repo @@ -353,9 +347,6 @@ jobs: - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/${{ env.dist }}/${{ env.release }} - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${{ env.dist }}/${{ env.release }} - # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed - - run: docker pull ${{ env.cr_image }}-${{ env.family }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ env.family }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . - - run: docker push ${{ env.cr_image }}-${{ env.family }}:${{ env.cr_image_version }} # create deb repo via Debian build container - run: | /usr/bin/docker run --rm \ @@ -392,12 +383,6 @@ jobs: pattern: 'fedora*' path: artifact merge-multiple: true - # docker login is needed for pushing the build image - - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} # organize SSH deploy key for nagstamon-repo - run: mkdir ~/.ssh - run: echo "${{ secrets.NAGSTAMON_REPO_KEY_WEB }}" > ~/.ssh/id_ed25519 @@ -406,9 +391,6 @@ jobs: - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/${{ env.release }} - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${{ env.release }} - # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed - - run: docker pull ${{ env.cr_image }}-${{ env.family }}-${{ env.cr_image_latest }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ env.family }}-${{ env.cr_image_latest }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . - - run: docker push ${{ env.cr_image }}-${{ env.family }}-${{ env.cr_image_latest }}:${{ env.cr_image_version }} # copy *.rpm files into nagstamon-jekyll and create repodata - run: | version=${{ env.release }} && \ @@ -438,12 +420,6 @@ jobs: pattern: 'rhel*' path: artifact merge-multiple: true - # docker login is needed for pushing the build image - - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} # organize SSH deploy key for nagstamon-repo - run: mkdir ~/.ssh - run: echo "${{ secrets.NAGSTAMON_REPO_KEY_WEB }}" > ~/.ssh/id_ed25519 @@ -452,9 +428,6 @@ jobs: - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/${{ env.release }} - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${{ env.release }} - # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed - - run: docker pull ${{ env.cr_image }}-${{ env.family }}-${{ env.version }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ env.family }}-${{ env.version }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . - - run: docker push ${{ env.cr_image }}-${{ env.family }}-${{ env.version }}:${{ env.cr_image_version }} # copy *.rpm files into nagstamon-jekyll and create repodata - run: | version=${{ env.release }} && \ From daa5e216df8d6f3ec375055052eb953df47a7f5f Mon Sep 17 00:00:00 2001 From: HenriWahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 24 Oct 2024 22:51:14 +0200 Subject: [PATCH 872/884] prepare 3.16.2 --- Nagstamon/Config.py | 2 +- build/debian/changelog | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 86a5b8beb..0aaa0c7c9 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -131,7 +131,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.17-20241023' + VERSION = '3.16.2' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2024 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/build/debian/changelog b/build/debian/changelog index e499e501a..ba53a1b0e 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,4 +1,4 @@ -nagstamon (3.17-20241023) unstable; urgency=low +nagstamon (3.16.2) unstable; urgency=low * New upstream - fix sound problem - fix IncingaDBWeb From cbe2d9110d358db8d71cd72931b2a1315cd9ac20 Mon Sep 17 00:00:00 2001 From: HenriWahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 24 Oct 2024 22:56:39 +0200 Subject: [PATCH 873/884] prepare 3.16.2 --- .github/workflows/build-release-latest.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 0d46f7e15..62d037be3 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -383,6 +383,12 @@ jobs: pattern: 'fedora*' path: artifact merge-multiple: true + # docker login is needed for pushing the build image + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} # organize SSH deploy key for nagstamon-repo - run: mkdir ~/.ssh - run: echo "${{ secrets.NAGSTAMON_REPO_KEY_WEB }}" > ~/.ssh/id_ed25519 @@ -391,6 +397,9 @@ jobs: - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/${{ env.release }} - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${{ env.release }} + # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ env.family }}-${{ env.cr_image_latest }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ env.family }}-${{ env.cr_image_latest }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ env.family }}-${{ env.cr_image_latest }}:${{ env.cr_image_version }} # copy *.rpm files into nagstamon-jekyll and create repodata - run: | version=${{ env.release }} && \ From 2c0c470d1c6863b0c7ddf0831d7311774962bec9 Mon Sep 17 00:00:00 2001 From: HenriWahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 24 Oct 2024 22:57:53 +0200 Subject: [PATCH 874/884] prepare 3.16.2 --- .github/workflows/build-release-latest.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 62d037be3..c7d5eb76f 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -397,9 +397,9 @@ jobs: - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/${{ env.release }} - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${{ env.release }} - # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed - - run: docker pull ${{ env.cr_image }}-${{ env.family }}-${{ env.cr_image_latest }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ env.family }}-${{ env.cr_image_latest }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . - - run: docker push ${{ env.cr_image }}-${{ env.family }}-${{ env.cr_image_latest }}:${{ env.cr_image_version }} + # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ env.family }}-${{ env.cr_image_latest }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ env.family }}-${{ env.cr_image_latest }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ env.family }}-${{ env.cr_image_latest }}:${{ env.cr_image_version }} # copy *.rpm files into nagstamon-jekyll and create repodata - run: | version=${{ env.release }} && \ From 898daa50884a8516e209e69eb89216460aadd5fe Mon Sep 17 00:00:00 2001 From: HenriWahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 24 Oct 2024 22:59:07 +0200 Subject: [PATCH 875/884] prepare 3.16.2 --- .github/workflows/build-release-latest.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index c7d5eb76f..d5c5251f6 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -398,6 +398,7 @@ jobs: - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/${{ env.release }} - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${{ env.release }} # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed + # only needed for fedora - run: docker pull ${{ env.cr_image }}-${{ env.family }}-${{ env.cr_image_latest }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ env.family }}-${{ env.cr_image_latest }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . - run: docker push ${{ env.cr_image }}-${{ env.family }}-${{ env.cr_image_latest }}:${{ env.cr_image_version }} # copy *.rpm files into nagstamon-jekyll and create repodata From d7e7960bc16353834b9776e64700fb2f68ea713f Mon Sep 17 00:00:00 2001 From: HenriWahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 24 Oct 2024 22:59:07 +0200 Subject: [PATCH 876/884] prepare 3.16.2 --- .github/workflows/build-release-latest.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/build-release-latest.yml b/.github/workflows/build-release-latest.yml index 0d46f7e15..c7d5eb76f 100644 --- a/.github/workflows/build-release-latest.yml +++ b/.github/workflows/build-release-latest.yml @@ -383,6 +383,12 @@ jobs: pattern: 'fedora*' path: artifact merge-multiple: true + # docker login is needed for pushing the build image + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} # organize SSH deploy key for nagstamon-repo - run: mkdir ~/.ssh - run: echo "${{ secrets.NAGSTAMON_REPO_KEY_WEB }}" > ~/.ssh/id_ed25519 @@ -391,6 +397,9 @@ jobs: - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/${{ env.release }} - run: mkdir -p ${{ env.repo_dir }}/${{ env.family }}/${{ env.release }} + # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ env.family }}-${{ env.cr_image_latest }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ env.family }}-${{ env.cr_image_latest }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ env.family }}-${{ env.cr_image_latest }}:${{ env.cr_image_version }} # copy *.rpm files into nagstamon-jekyll and create repodata - run: | version=${{ env.release }} && \ From 4a53e98b9cf69cbdb3b3836ae1ad67fb04d9bb76 Mon Sep 17 00:00:00 2001 From: HenriWahl <2835065+HenriWahl@users.noreply.github.com> Date: Thu, 24 Oct 2024 23:14:10 +0200 Subject: [PATCH 877/884] prepare 3.16.2 --- .github/workflows/build-release-stable.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/build-release-stable.yml b/.github/workflows/build-release-stable.yml index 057cfe08f..a1410047b 100644 --- a/.github/workflows/build-release-stable.yml +++ b/.github/workflows/build-release-stable.yml @@ -379,6 +379,12 @@ jobs: pattern: 'fedora*' path: artifact merge-multiple: true + # docker login is needed for pushing the build image + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} # organize SSH deploy key for nagstamon-repo - run: mkdir ~/.ssh - run: echo "${{ secrets.NAGSTAMON_REPO_KEY_WEB }}" > ~/.ssh/id_ed25519 @@ -386,6 +392,9 @@ jobs: # get and prepare nagstamon-jekyll - run: git clone git@github.com:HenriWahl/nagstamon-jekyll.git - run: rm -rf ${{ env.repo_dir }}/${{ env.family }}/?? + # if image defined by variable cr_image_version is not pullable aka does not exist it will be created and pushed + - run: docker pull ${{ env.cr_image }}-${{ env.family }}-${{ env.cr_image_latest }}:${{ env.cr_image_version }} || /usr/bin/docker build -t ${{ env.cr_image }}-${{ env.family }}-${{ env.cr_image_latest }}:${{ env.cr_image_version }} -f build/docker/Dockerfile-${{ github.job }} . + - run: docker push ${{ env.cr_image }}-${{ env.family }}-${{ env.cr_image_latest }}:${{ env.cr_image_version }} # copy *.rpm files into nagstamon-jekyll and create repodata - run: | for noarch_rpm in artifact/*${{ env.family }}*.noarch.rpm; \ From 78653d53447f14e19fc11471f358ae7ee76bf8a1 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Sun, 27 Oct 2024 22:17:09 +0100 Subject: [PATCH 878/884] Update nagstamon.appdata.xml Release 3.16.2 --- Nagstamon/resources/nagstamon.appdata.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Nagstamon/resources/nagstamon.appdata.xml b/Nagstamon/resources/nagstamon.appdata.xml index 31d08a43b..9c3884296 100644 --- a/Nagstamon/resources/nagstamon.appdata.xml +++ b/Nagstamon/resources/nagstamon.appdata.xml @@ -9,11 +9,11 @@ <name>Nagstamon</name> <summary>The status monitor for the desktop</summary> <releases> - <release version="3.16.1" date="2024-10-20"> + <release version="3.16.2" date="2024-10-25"> <description> <ul> - <li>fix ridiculous Windows 11 appearance</li> - <li>fix Zabbix all information copy action</li> + <li>fix sound problem</li> + <li>fix IncingaDBWeb</li> </ul> </description> </release> From 5a37b6c0e23f71012565de0d8ec4db3d710620df Mon Sep 17 00:00:00 2001 From: Dregaringo <46315689+Dregaringo@users.noreply.github.com> Date: Mon, 28 Oct 2024 08:05:21 +0200 Subject: [PATCH 879/884] =?UTF-8?q?Fix=20incorrect=20definition=20of=20?= =?UTF-8?q?=E2=80=9Cquote=E2=80=9D=20for=20host=20link=20(#1074)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: resolve double URL encoding issue on macOS * fix: resolve double URL encoding issue on macOS --------- Co-authored-by: Andrii Ivanov <andrii.ivanov@namecheap.com> Co-authored-by: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> --- Nagstamon/Servers/IcingaDBWeb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index ed4ed023e..8340d7c4e 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -744,7 +744,7 @@ def open_monitor(self, host, service=''): url = '{0}/icingadb/hosts?host.state.is_problem=y&sort=host.state.severity#!{1}/icingadb/host?{2}'.format( self.monitor_url, base_path, - urllib.parse.urlencode({'name': self.hosts[host].real_name}, quote_via=quote) + urllib.parse.urlencode({'name': self.hosts[host].real_name}, quote_via=urllib.parse.quote) ) else: # Handle URL for service monitoring From b5f5664584c11fd496ce7aee666261ad7e5be284 Mon Sep 17 00:00:00 2001 From: Hagen Grube <github@fvbor.de> Date: Mon, 18 Nov 2024 15:33:43 +0100 Subject: [PATCH 880/884] Update nagstamon.iss (#1079) Warning: Architecture identifier "x64" is deprecated. Substituting "x64os", but note that "x64compatible" is preferred in most cases. See the "Architecture Identifiers" topic in help file for more information. https://jrsoftware.org/ishelp/index.php?topic=archidentifiers --- build/windows/nagstamon.iss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/windows/nagstamon.iss b/build/windows/nagstamon.iss index 96b8f4a4a..80440df90 100644 --- a/build/windows/nagstamon.iss +++ b/build/windows/nagstamon.iss @@ -23,7 +23,7 @@ Compression=lzma SolidCompression=true SourceDir={#source} ArchitecturesAllowed={#archs_allowed} -ArchitecturesInstallIn64BitMode=x64 +ArchitecturesInstallIn64BitMode=x64compatible CloseApplications=no WizardStyle=modern [Icons] From 2fe0aa19e515a1387289bc623d98bfe51b5285d0 Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 18 Nov 2024 15:45:23 +0100 Subject: [PATCH 881/884] 3.16.x back to master (#1080) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update nagstamon.appdata.xml Release 3.16.2 (cherry picked from commit 78653d53447f14e19fc11471f358ae7ee76bf8a1) * Fix incorrect definition of “quote” for host link (#1074) * fix: resolve double URL encoding issue on macOS * fix: resolve double URL encoding issue on macOS --------- Co-authored-by: Andrii Ivanov <andrii.ivanov@namecheap.com> Co-authored-by: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> (cherry picked from commit 5a37b6c0e23f71012565de0d8ec4db3d710620df) --------- Co-authored-by: Dregaringo <46315689+Dregaringo@users.noreply.github.com> From eb1cdaf39f845049c96787ece0cf741298627faa Mon Sep 17 00:00:00 2001 From: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> Date: Mon, 18 Nov 2024 15:47:35 +0100 Subject: [PATCH 882/884] Fixes 3 16 0 (#1081) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update nagstamon.appdata.xml Release 3.16.2 (cherry picked from commit 78653d53447f14e19fc11471f358ae7ee76bf8a1) * Fix incorrect definition of “quote” for host link (#1074) * fix: resolve double URL encoding issue on macOS * fix: resolve double URL encoding issue on macOS --------- Co-authored-by: Andrii Ivanov <andrii.ivanov@namecheap.com> Co-authored-by: Henri Wahl <2835065+HenriWahl@users.noreply.github.com> (cherry picked from commit 5a37b6c0e23f71012565de0d8ec4db3d710620df) * 3.17-20241028 * Incorrect display of last_check field (#1075) Incorrect display of last_check field (#1075) --------- Co-authored-by: Dregaringo <46315689+Dregaringo@users.noreply.github.com> Co-authored-by: wigor2206 <wigor2206@gmail.com> --- Nagstamon/Config.py | 2 +- Nagstamon/Servers/IcingaDBWeb.py | 18 ++++++++++++++---- build/debian/changelog | 4 ++-- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/Nagstamon/Config.py b/Nagstamon/Config.py index 0aaa0c7c9..33db90478 100644 --- a/Nagstamon/Config.py +++ b/Nagstamon/Config.py @@ -131,7 +131,7 @@ class AppInfo(object): contains app information previously located in GUI.py """ NAME = 'Nagstamon' - VERSION = '3.16.2' + VERSION = '3.17-20241102' WEBSITE = 'https://nagstamon.de' COPYRIGHT = '©2008-2024 Henri Wahl et al.' COMMENTS = 'Nagios status monitor for your desktop' diff --git a/Nagstamon/Servers/IcingaDBWeb.py b/Nagstamon/Servers/IcingaDBWeb.py index 8340d7c4e..fc1691eb4 100644 --- a/Nagstamon/Servers/IcingaDBWeb.py +++ b/Nagstamon/Servers/IcingaDBWeb.py @@ -210,9 +210,14 @@ def _get_status(self): self.new_hosts[host_name].status = self.STATES_MAPPING['hosts'][int(h['state']['soft_state'])] if h['state']['last_update'].replace(".", "").isnumeric(): # new version of icingadb doesnt return unix timestamp - self.new_hosts[host_name].last_check = datetime.datetime.fromtimestamp(int(float(h['state']['last_update']))) + #self.new_hosts[host_name].last_check = datetime.datetime.fromtimestamp(int(float(h['state']['last_update']))) + utc_time = datetime.datetime.fromtimestamp(int(float(h['state']['last_update'])), tz=timezone.utc) else: - self.new_hosts[host_name].last_check = datetime.datetime.fromisoformat(h['state']['last_update']) + #self.new_hosts[host_name].last_check = datetime.datetime.fromisoformat(h['state']['last_update']) + utc_time = datetime.datetime.fromisoformat(h['state']['last_update']) + + local_time = utc_time.astimezone() + self.new_hosts[host_name].last_check = local_time.strftime("%Y-%m-%d %H:%M:%S") # format without microseconds and tz self.new_hosts[host_name].attempt = "{}/{}".format(h['state']['check_attempt'],h['max_check_attempts']) self.new_hosts[host_name].status_information = BeautifulSoup(str(h['state']['output']).replace('\n', ' ').strip(), 'html.parser').text @@ -313,9 +318,14 @@ def _get_status(self): self.new_hosts[host_name].services[service_name].status = self.STATES_MAPPING['services'][int(s['state']['soft_state'])] if s['state']['last_update'].replace(".", "").isnumeric(): # new version of icingadb doesnt return unix timestamp - self.new_hosts[host_name].services[service_name].last_check = datetime.datetime.fromtimestamp(int(float(s['state']['last_update']))) + #self.new_hosts[host_name].services[service_name].last_check = datetime.datetime.fromtimestamp(int(float(s['state']['last_update']))) + utc_time = datetime.datetime.fromtimestamp(int(float(s['state']['last_update'])), tz=timezone.utc) else: - self.new_hosts[host_name].services[service_name].last_check = datetime.datetime.fromisoformat(s['state']['last_update']) + #self.new_hosts[host_name].services[service_name].last_check = datetime.datetime.fromisoformat(s['state']['last_update']) + utc_time = datetime.datetime.fromisoformat(s['state']['last_update']) + + local_time = utc_time.astimezone() + self.new_hosts[host_name].services[service_name].last_check = local_time.strftime("%Y-%m-%d %H:%M:%S") # format without microseconds and tz self.new_hosts[host_name].services[service_name].attempt = "{}/{}".format(s['state']['check_attempt'],s['max_check_attempts']) self.new_hosts[host_name].services[service_name].status_information = BeautifulSoup(str(s['state']['output']).replace('\n', ' ').strip(), 'html.parser').text diff --git a/build/debian/changelog b/build/debian/changelog index ba53a1b0e..7a9266326 100644 --- a/build/debian/changelog +++ b/build/debian/changelog @@ -1,9 +1,9 @@ -nagstamon (3.16.2) unstable; urgency=low +nagstamon (3.17-20241102) unstable; urgency=low * New upstream - fix sound problem - fix IncingaDBWeb - -- Henri Wahl <henri@nagstamon.de> Mon, Oct 21 2024 08:00:00 +0200 + -- Henri Wahl <henri@nagstamon.de> Sat, Nov 02 2024 08:00:00 +0200 nagstamon (3.16.1) stable; urgency=low * New upstream From 2bbc6d254a7cc6140b10a525cae1cae1436b662c Mon Sep 17 00:00:00 2001 From: Marco Kamner <marco@kamner.de> Date: Mon, 18 Nov 2024 16:03:38 +0100 Subject: [PATCH 883/884] Handle flexible downtimes for Zabbix (#1078) --- Nagstamon/Servers/Zabbix.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Nagstamon/Servers/Zabbix.py b/Nagstamon/Servers/Zabbix.py index f73393453..069271fce 100644 --- a/Nagstamon/Servers/Zabbix.py +++ b/Nagstamon/Servers/Zabbix.py @@ -543,11 +543,15 @@ def _set_downtime(self, hostname, service, author, comment, fixed, start_time, e hostids = [self.hosts[hostname].hostid] - date = datetime.datetime.strptime(start_time, "%Y-%m-%d %H:%M") - stime = int(time.mktime(date.timetuple())) + if fixed == 1: + start_date = datetime.datetime.strptime(start_time, "%Y-%m-%d %H:%M") + end_date = datetime.datetime.strptime(end_time, "%Y-%m-%d %H:%M") + else: + start_date = datetime.datetime.now() + end_date = start_date + datetime.timedelta(hours=hours, minutes=minutes) - date = datetime.datetime.strptime(end_time, "%Y-%m-%d %H:%M") - etime = int(time.mktime(date.timetuple())) + stime = int(time.mktime(start_date.timetuple())) + etime = int(time.mktime(end_date.timetuple())) if conf.debug_mode is True: self.debug(server=self.get_name(), From 29a421ea767acd64ca26d1376ff9f6065f44f4c7 Mon Sep 17 00:00:00 2001 From: Marco Kamner <marco@kamner.de> Date: Mon, 18 Nov 2024 16:33:18 +0100 Subject: [PATCH 884/884] Enable monitoring menu action for Zabbix (#1082) It opens the "problems" view inside Zabbix, filtered to problems of the given host. --- Nagstamon/Servers/Zabbix.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Nagstamon/Servers/Zabbix.py b/Nagstamon/Servers/Zabbix.py index 069271fce..30392f9bf 100644 --- a/Nagstamon/Servers/Zabbix.py +++ b/Nagstamon/Servers/Zabbix.py @@ -65,7 +65,7 @@ def __init__(self, **kwds): '5': 'DISASTER'} # Entries for monitor default actions in context menu - self.MENU_ACTIONS = ["Acknowledge", "Downtime"] + self.MENU_ACTIONS = ["Monitor", "Acknowledge", "Downtime"] # URLs for browser shortlinks/buttons on popup window self.BROWSER_URLS = {'monitor': '$MONITOR$', 'hosts': '$MONITOR-CGI$/hosts.php?ddreset=1', @@ -465,12 +465,8 @@ def open_monitor(self, host, service=""): """ open monitor from treeview context menu """ - if service == "": - url = self.urls['human_host'] + urllib.parse.urlencode( - {'x': 'site=' + self.hosts[host].site + '&host=' + host}).replace('x=', '%26') - else: - url = self.urls['human_service'] + urllib.parse.urlencode( - {'x': 'site=' + self.hosts[host].site + '&host=' + host + '&service=' + service}).replace('x=', '%26') + host_id = self.hosts[host].hostid + url = f"{self.monitor_url}/zabbix.php?action=problem.view&hostids%5B%5D={host_id}&filter_set=1&show_suppressed=1" if conf.debug_mode is True: self.debug(server=self.get_name(), host=host, service=service,