Compare commits

...

142 Commits

Author SHA1 Message Date
Benjamin Drieu 07534326e8 Update 2023-11-08 11:38:30 +01:00
Benjamin Drieu a42ce2283d Update 2023-11-08 11:37:39 +01:00
Benjamin Drieu 0d8113e52b Do not use fixed string 2023-11-08 11:37:09 +01:00
Benjamin Drieu d57264db3e Implement proxy 2023-07-03 16:41:56 +02:00
Benjamin Drieu 9ae23885de Lower log noise 2023-07-03 16:41:13 +02:00
Benjamin Drieu 6b38c5374e Update screenshot 2023-07-03 15:09:07 +02:00
Benjamin Drieu 09d7b3386b New file 2023-03-29 08:29:30 +02:00
Benjamin Drieu 61964a83a4 Tentative popup menu 2023-03-29 08:29:21 +02:00
Benjamin Drieu 65771792d3 Improve columns display 2023-03-29 08:29:00 +02:00
Benjamin Drieu 6e83fc331b Port to GTK4 & Gnome-Shell 42 2023-03-28 21:21:49 +02:00
Benjamin Drieu d987193ac6 Columns improvements 2023-01-06 17:40:30 +01:00
Benjamin Drieu b4e3b8f0c7 Fix the buttons data (which is ugly, to tell the truth) 2022-09-14 15:44:45 +02:00
Benjamin Drieu 07e0112ad6 Merge branch 'master' of ssh://forge.chapril.org:222/Monito/monito 2022-09-09 19:55:38 +02:00
Benjamin Drieu 348f2a593d Actually let user change columns 2022-09-09 19:22:46 +02:00
Benjamin Drieu 00cdfc4a81 Actually let user change columns 2022-09-09 19:22:46 +02:00
Benjamin Drieu 6b7ba7c0f1 Import of that file 2022-09-09 18:11:22 +02:00
Benjamin Drieu ededa75d94 Import of that file 2022-09-09 18:11:22 +02:00
Benjamin Drieu 9bc3175aee Start to implement column configuration 2022-09-09 11:41:32 +02:00
Benjamin Drieu c8d0b98348 Start to implement column configuration 2022-09-09 11:41:32 +02:00
Benjamin Drieu 0fe40e0ab9 Horrible fix to prevent variable from being modified later 2022-09-09 11:40:55 +02:00
Benjamin Drieu 07a62e5280 Horrible fix to prevent variable from being modified later 2022-09-09 11:40:55 +02:00
Benjamin Drieu 548d9cde09 Apply eslinst recommandations 2022-05-30 17:16:27 +02:00
Benjamin Drieu d51f470428 Apply eslinst recommandations 2022-05-30 17:16:27 +02:00
Benjamin Drieu 42d1d4f548 Implement show all feature 2022-05-30 11:27:27 +02:00
Benjamin Drieu d7263f491b Implement show all feature 2022-05-30 11:27:27 +02:00
Benjamin Drieu 7795bd3d51 Implement GTK+3/4 compatibility 2022-05-30 11:27:11 +02:00
Benjamin Drieu 4e21919d2e Implement GTK+3/4 compatibility 2022-05-30 11:27:11 +02:00
Benjamin Drieu 6484e4f386 Port to Gtk4 2022-05-28 22:34:01 +02:00
Benjamin Drieu af32fbefd2 Port to Gtk4 2022-05-28 22:34:01 +02:00
Benjamin Drieu 3be7b5240d Fix URL stuff 2022-05-20 16:32:36 +02:00
Benjamin Drieu 66f1deed14 Fix URL stuff 2022-05-20 16:32:36 +02:00
Benjamin Drieu f0e5d0ba3f Start of implementation of custom columns 2022-05-20 16:32:20 +02:00
Benjamin Drieu acc8512800 Start of implementation of custom columns 2022-05-20 16:32:20 +02:00
Benjamin Drieu a5c37ef38a Update todo 2022-05-11 12:11:11 +02:00
Benjamin Drieu b8eba20b7c Update todo 2022-05-11 12:11:11 +02:00
Benjamin Drieu c046b12723 - Implement Hover
- Implement services lines fold/unfold
- Implement new action for services lines : launche browser
- Some refactoring
2022-05-11 12:08:41 +02:00
Benjamin Drieu b468ca0326 - Implement Hover
- Implement services lines fold/unfold
- Implement new action for services lines : launche browser
- Some refactoring
2022-05-11 12:08:41 +02:00
Benjamin Drieu 31120af615 Be case insenstitive 2022-04-22 11:22:38 +02:00
Benjamin Drieu 5aee2cb9f2 Be case insenstitive 2022-04-22 11:22:38 +02:00
Benjamin Drieu ec0de6f81e Make specific recheck work again 2022-04-22 11:22:25 +02:00
Benjamin Drieu 51bfedd349 Make specific recheck work again 2022-04-22 11:22:25 +02:00
Benjamin Drieu 09337b905f Merge branch 'master' of ssh://forge.chapril.org:222/Monito/monito 2022-03-22 22:20:15 +01:00
Benjamin Drieu 146ffe7b09 Merge branch 'master' of ssh://forge.chapril.org:222/Monito/monito 2022-03-22 22:20:15 +01:00
Benjamin Drieu 24067018e8 Updates 2022-03-22 22:19:07 +01:00
Benjamin Drieu 5e8e660c7a Updates 2022-03-22 22:19:07 +01:00
Benjamin Drieu 4adb15ae7e Be less strict on SSL: add an option instead? 2022-03-21 12:10:05 +01:00
Benjamin Drieu e52a346369 Be less strict on SSL: add an option instead? 2022-03-21 12:10:05 +01:00
Benjamin Drieu 4b3200542a Merge branch 'master' of https://forge.chapril.org/Monito/monito 2022-02-11 21:20:38 +01:00
Benjamin Drieu 4d2d0132a3 Merge branch 'master' of https://forge.chapril.org/Monito/monito 2022-02-11 21:20:38 +01:00
Benjamin Drieu 9db7823797 Upadte for newer gnome-shell 2022-02-11 21:20:36 +01:00
Benjamin Drieu 191c1bcb21 Upadte for newer gnome-shell 2022-02-11 21:20:36 +01:00
Benjamin Drieu 405742b70f Add pending status + trim status info after first line 2021-12-17 15:18:02 +01:00
Benjamin Drieu d5f975ce44 Add pending status + trim status info after first line 2021-12-17 15:18:02 +01:00
Benjamin Drieu 85293000a5 Better handling of spinners 2021-12-06 11:45:02 +01:00
Benjamin Drieu 44d2c57dc4 Better handling of spinners 2021-12-06 11:45:02 +01:00
Benjamin Drieu 5cfb752943 Implement natural date + fixes 2021-12-06 11:34:55 +01:00
Benjamin Drieu bba8b0a69c Implement natural date + fixes 2021-12-06 11:34:55 +01:00
Benjamin Drieu f18d8bbd61 Implement spinners 2021-12-03 16:35:50 +01:00
Benjamin Drieu 1e25177673 Implement spinners 2021-12-03 16:35:50 +01:00
Benjamin Drieu c344c31d4c Implement acked monitoring + filter out 2021-12-02 19:05:44 +01:00
Benjamin Drieu 8e952badff Implement acked monitoring + filter out 2021-12-02 19:05:44 +01:00
Benjamin Drieu 4ebc7b9681 Fix replacement bugs + implement correctly text-based filters 2021-12-02 17:52:08 +01:00
Benjamin Drieu d7408fdf0a Fix replacement bugs + implement correctly text-based filters 2021-12-02 17:52:08 +01:00
Benjamin Drieu c50753c07e Vertical align, at last :-) 2021-12-02 00:28:51 +01:00
Benjamin Drieu 5a4ebbf486 Vertical align, at last :-) 2021-12-02 00:28:51 +01:00
Benjamin Drieu 3bc50dc398 Tyop 2021-12-02 00:12:33 +01:00
Benjamin Drieu 4b5881ffae Tyop 2021-12-02 00:12:33 +01:00
Benjamin Drieu 4b3eca677b Remove annoying notification 2021-12-01 16:24:18 +01:00
Benjamin Drieu 68e394508c Remove annoying notification 2021-12-01 16:24:18 +01:00
Benjamin Drieu 73a89920ff Update roadmap 2021-12-01 16:24:07 +01:00
Benjamin Drieu e194321288 Update roadmap 2021-12-01 16:24:07 +01:00
Benjamin Drieu 5260757d21 Wording 2021-12-01 16:22:16 +01:00
Benjamin Drieu 5628b8efe5 Wording 2021-12-01 16:22:16 +01:00
Benjamin Drieu 489a59b31f Better multi-server implementation 2021-12-01 16:18:38 +01:00
Benjamin Drieu c4245fe199 Better multi-server implementation 2021-12-01 16:18:38 +01:00
Benjamin Drieu 46dd36c653 Implement recheck 2021-12-01 16:18:30 +01:00
Benjamin Drieu 42d4537b23 Implement recheck 2021-12-01 16:18:30 +01:00
Benjamin Drieu 367bee14e2 Add Icinga2 API server 2021-12-01 15:28:19 +01:00
Benjamin Drieu 07dc49fd13 Add Icinga2 API server 2021-12-01 15:28:19 +01:00
Benjamin Drieu dd8f153f96 Quiet the logs + add some bulletproof checks 2021-12-01 15:28:05 +01:00
Benjamin Drieu c4a9402e4e Quiet the logs + add some bulletproof checks 2021-12-01 15:28:05 +01:00
Benjamin Drieu fac786de7b Add Icinga2 API server 2021-12-01 15:27:37 +01:00
Benjamin Drieu ffebd4d6a0 Add Icinga2 API server 2021-12-01 15:27:37 +01:00
Benjamin Drieu fd04906622 Some cleanup + add Icinga2 API server 2021-12-01 15:27:25 +01:00
Benjamin Drieu 93d4e485ae Some cleanup + add Icinga2 API server 2021-12-01 15:27:25 +01:00
Benjamin Drieu 41c8def12c Improve color display of services 2021-11-29 23:04:17 +01:00
Benjamin Drieu 64806075b6 Improve color display of services 2021-11-29 23:04:17 +01:00
Benjamin Drieu 28a2710e9e Add dep on Makefile 2021-11-29 18:19:58 +01:00
Benjamin Drieu bd99ca0850 Add dep on Makefile 2021-11-29 18:19:58 +01:00
Benjamin Drieu 1944123372 Typo 2021-11-29 18:15:50 +01:00
Benjamin Drieu 13cabc55d0 Typo 2021-11-29 18:15:50 +01:00
Benjamin Drieu 848de2568b Display OK lines in case no errors (so as to allow recheck and friends 2021-11-29 18:15:18 +01:00
Benjamin Drieu b207dd2fc7 Display OK lines in case no errors (so as to allow recheck and friends 2021-11-29 18:15:18 +01:00
Benjamin Drieu 208e901566 Refactoring 2021-11-29 16:07:29 +01:00
Benjamin Drieu 83c27bf84a Refactoring 2021-11-29 16:07:29 +01:00
Benjamin Drieu e556871713 Remove transient refresh bug 2021-11-25 14:45:42 +01:00
Benjamin Drieu 747778571a Remove transient refresh bug 2021-11-25 14:45:42 +01:00
Benjamin Drieu 8812be23a6 Refactor command sending 2021-11-24 20:58:07 +01:00
Benjamin Drieu b9d8ef5727 Refactor command sending 2021-11-24 20:58:07 +01:00
Benjamin Drieu 2eeb9f9655 Update prefs 2021-11-24 17:04:47 +01:00
Benjamin Drieu 5f70bdea9f Update prefs 2021-11-24 17:04:47 +01:00
Benjamin Drieu dce089494f Add more loging + error control 2021-11-22 22:12:49 +01:00
Benjamin Drieu d533f08e44 Add more loging + error control 2021-11-22 22:12:49 +01:00
Benjamin Drieu aec2df8b7a Some refactoring 2021-11-22 17:36:55 +01:00
Benjamin Drieu 76319c11d5 Some refactoring 2021-11-22 17:36:55 +01:00
Benjamin Drieu 54dced6925 IHM tweaks 2021-11-19 23:07:45 +01:00
Benjamin Drieu 886501cba2 IHM tweaks 2021-11-19 23:07:45 +01:00
Benjamin Drieu 838f4012a1 Add button + change defaults 2021-11-19 22:16:45 +01:00
Benjamin Drieu 775f721009 Add button + change defaults 2021-11-19 22:16:45 +01:00
Benjamin Drieu 2e493e6fe7 Implement column ordering + variable columns (need conf tool in the prfs window) 2021-11-19 17:46:36 +01:00
Benjamin Drieu 4a66c46042 Implement column ordering + variable columns (need conf tool in the prfs window) 2021-11-19 17:46:36 +01:00
Benjamin Drieu 4046f01f2b Implement better table view for services 2021-11-19 11:21:20 +01:00
Benjamin Drieu 0ab8b79ae1 Implement better table view for services 2021-11-19 11:21:20 +01:00
Benjamin Drieu b4ca71c158 Add schema for columns ordering and configuration 2021-11-19 11:20:59 +01:00
Benjamin Drieu 378154a3b4 Add schema for columns ordering and configuration 2021-11-19 11:20:59 +01:00
Benjamin Drieu 4ce284d782 Better error handling 2021-11-18 23:16:59 +01:00
Benjamin Drieu 25a3f7a6e8 Better error handling 2021-11-18 23:16:59 +01:00
Benjamin Drieu 6230b03cff Implement real multi-indicators extension + some cleanup 2021-11-18 23:08:49 +01:00
Benjamin Drieu 49f4cbebb4 Implement real multi-indicators extension + some cleanup 2021-11-18 23:08:49 +01:00
Benjamin Drieu 2a36fc113c Some refactoring 2021-11-18 01:32:48 +01:00
Benjamin Drieu 33522a08f9 Some refactoring 2021-11-18 01:32:48 +01:00
Benjamin Drieu 9317c4cb7a Merge branch 'master' of ssh://forge.chapril.org:222/Monito/monito 2021-11-17 21:01:54 +01:00
Benjamin Drieu 31b39b8eb7 Merge branch 'master' of ssh://forge.chapril.org:222/Monito/monito 2021-11-17 21:01:54 +01:00
Benjamin Drieu 9fc2cdf975 Cosmetic changes 2021-11-17 21:01:51 +01:00
Benjamin Drieu 3f50ae0016 Cosmetic changes 2021-11-17 21:01:51 +01:00
Benjamin Drieu c88df6c499 Some UI refinements 2021-11-17 20:16:53 +01:00
Benjamin Drieu 168b509b22 Some UI refinements 2021-11-17 20:16:53 +01:00
Benjamin Drieu f6fc59e309 Update README 2021-11-17 17:51:46 +01:00
Benjamin Drieu 859cdc40b9 Update README 2021-11-17 17:51:46 +01:00
Benjamin Drieu 159c52c218 Merge branch 'master' of ssh://forge.chapril.org:222/Monito/monito 2021-11-17 17:44:51 +01:00
Benjamin Drieu 56a1494ddf Merge branch 'master' of ssh://forge.chapril.org:222/Monito/monito 2021-11-17 17:41:13 +01:00
Benjamin Drieu e0b1983ac6 New README 2021-11-17 17:32:33 +01:00
Benjamin Drieu 7d0c8d4a76 Update rules 2021-11-17 17:18:53 +01:00
Benjamin Drieu dae2afd39d Make server add/remove working 2021-11-17 17:15:21 +01:00
Benjamin Drieu ff5280f729 Working prefs 2021-11-17 15:10:47 +01:00
Benjamin Drieu 57e67ef312 Update preferences to handle colors 2021-11-17 14:44:04 +01:00
Benjamin Drieu 1f95af7137 Kinda implement Icinga2 ... but why the f... is the last_checked value not present in the json ??? 2021-11-15 16:09:48 +01:00
Benjamin Drieu 332e51b1bf Implement Icinga/Icinga2 logic 2021-11-12 18:26:31 +01:00
Benjamin Drieu c6c7c48319 Unfuck 2021-11-12 12:11:20 +01:00
Benjamin Drieu 0e978af3d5 More layout 2021-11-10 17:21:03 +01:00
Benjamin Drieu 4e27aa28cf Working multi-servers 2021-11-10 16:59:24 +01:00
Benjamin Drieu 10242f32a5 Working prefs 2021-11-10 15:52:40 +01:00
16 changed files with 2810 additions and 253 deletions

252
.eslintrc.yml Normal file
View File

@ -0,0 +1,252 @@
---
# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
# SPDX-FileCopyrightText: 2018 Claudio André <claudioandre.br@gmail.com>
extends: 'eslint:recommended'
rules:
array-bracket-newline:
- error
- consistent
array-bracket-spacing:
- error
- never
array-callback-return: error
arrow-parens:
- error
- as-needed
arrow-spacing: error
block-scoped-var: error
block-spacing: error
brace-style: error
# Waiting for this to have matured a bit in eslint
# camelcase:
# - error
# - properties: never
# allow: [^vfunc_, ^on_, _instance_init]
comma-dangle:
- error
- arrays: always-multiline
objects: always-multiline
functions: never
comma-spacing:
- error
- before: false
after: true
comma-style:
- error
- last
computed-property-spacing: error
curly:
- error
- multi-or-nest
- consistent
dot-location:
- error
- property
eol-last: error
eqeqeq: error
func-call-spacing: error
func-name-matching: error
func-style:
- error
- declaration
- allowArrowFunctions: true
indent:
- error
- 4
- ignoredNodes:
# Allow not indenting the body of GObject.registerClass, since in the
# future it's intended to be a decorator
- 'CallExpression[callee.object.name=GObject][callee.property.name=registerClass] > ClassExpression:first-child'
# Allow dedenting chained member expressions
MemberExpression: 'off'
key-spacing:
- error
- beforeColon: false
afterColon: true
keyword-spacing:
- error
- before: true
after: true
linebreak-style:
- error
- unix
lines-between-class-members:
- error
- always
- exceptAfterSingleLine: true
max-nested-callbacks: error
max-statements-per-line: error
new-parens: error
no-array-constructor: error
no-await-in-loop: error
no-caller: error
no-constant-condition:
- error
- checkLoops: false
no-div-regex: error
no-empty:
- error
- allowEmptyCatch: true
no-extra-bind: error
no-extra-parens:
- error
- all
- conditionalAssign: false
nestedBinaryExpressions: false
returnAssign: false
no-implicit-coercion:
- error
- allow:
- '!!'
no-invalid-this: error
no-iterator: error
no-label-var: error
no-lonely-if: error
no-loop-func: error
no-nested-ternary: error
no-new-object: error
no-new-wrappers: error
no-octal-escape: error
no-proto: error
no-prototype-builtins: 'off'
no-restricted-globals: [error, window]
no-restricted-properties:
- error
- object: imports
property: format
message: Use template strings
- object: pkg
property: initFormat
message: Use template strings
- object: Lang
property: copyProperties
message: Use Object.assign()
- object: Lang
property: bind
message: Use arrow notation or Function.prototype.bind()
- object: Lang
property: Class
message: Use ES6 classes
no-restricted-syntax:
- error
- selector: >-
MethodDefinition[key.name="_init"] >
FunctionExpression[params.length=1] >
BlockStatement[body.length=1]
CallExpression[arguments.length=1][callee.object.type="Super"][callee.property.name="_init"] >
Identifier:first-child
message: _init() that only calls super._init() is unnecessary
- selector: >-
MethodDefinition[key.name="_init"] >
FunctionExpression[params.length=0] >
BlockStatement[body.length=1]
CallExpression[arguments.length=0][callee.object.type="Super"][callee.property.name="_init"]
message: _init() that only calls super._init() is unnecessary
- selector: BinaryExpression[operator="instanceof"][right.name="Array"]
message: Use Array.isArray()
no-return-assign: error
no-return-await: error
no-self-compare: error
no-shadow: error
no-shadow-restricted-names: error
no-spaced-func: error
no-tabs: error
no-template-curly-in-string: error
no-throw-literal: error
no-trailing-spaces: error
no-undef-init: error
no-unneeded-ternary: error
no-unused-expressions: error
no-unused-vars:
- error
# Vars use a suffix _ instead of a prefix because of file-scope private vars
- varsIgnorePattern: (^unused|_$)
argsIgnorePattern: ^(unused|_)
no-useless-call: error
no-useless-computed-key: error
no-useless-concat: error
no-useless-constructor: error
no-useless-rename: error
no-useless-return: error
no-whitespace-before-property: error
no-with: error
nonblock-statement-body-position:
- error
- below
object-curly-newline:
- error
- consistent: true
multiline: true
object-curly-spacing: error
object-shorthand: error
operator-assignment: error
operator-linebreak: error
padded-blocks:
- error
- never
# These may be a bit controversial, we can try them out and enable them later
# prefer-const: error
# prefer-destructuring: error
prefer-numeric-literals: error
prefer-promise-reject-errors: error
prefer-rest-params: error
prefer-spread: error
prefer-template: error
quotes:
- error
- single
- avoidEscape: true
require-await: error
rest-spread-spacing: error
semi:
- error
- always
semi-spacing:
- error
- before: false
after: true
semi-style: error
space-before-blocks: error
space-before-function-paren:
- error
- named: never
# for `function ()` and `async () =>`, preserve space around keywords
anonymous: always
asyncArrow: always
space-in-parens: error
space-infix-ops:
- error
- int32Hint: false
space-unary-ops: error
spaced-comment: error
switch-colon-spacing: error
symbol-description: error
template-curly-spacing: error
template-tag-spacing: error
unicode-bom: error
wrap-iife:
- error
- inside
yield-star-spacing: error
yoda: error
globals:
ARGV: readonly
Debugger: readonly
GIRepositoryGType: readonly
globalThis: readonly
imports: readonly
Intl: readonly
log: readonly
logError: readonly
print: readonly
printerr: readonly
window: readonly
TextEncoder: readonly
TextDecoder: readonly
console: readonly
setTimeout: readonly
setInterval: readonly
clearTimeout: readonly
clearInterval: readonly
parserOptions:
ecmaVersion: 2021

View File

@ -1,8 +1,10 @@
reload: schema
busctl --user call org.gnome.Shell /org/gnome/Shell org.gnome.Shell Eval s 'Meta.restart("Restarting…")'
schema:
schema: schemas/gschemas.compiled
schemas/gschemas.compiled: schemas/org.gnome.shell.extensions.monito@drieu.org.gschema.xml
glib-compile-schemas --strict schemas/
pref: reload
sleep 1 && gnome-extensions prefs monito@drieu.org
pref:
gnome-extensions prefs monito@drieu.org

39
README.md Normal file
View File

@ -0,0 +1,39 @@
# Monito multi-monitoring checker
Monito is a simple tool to query monitoring servers and display
results in the Gnome-Shell panel.
![Screenshot](img/screenshot.png "Screenshot")
## Manual Installation
If you install from sources/scratch, put the sources into the
<kbd>~/.local/share/gnome-shell/extensions/monito@drieu.org</kbd>
directory. Then build the schema using either <kbd>make schema</kbd>
or <kbd>glib-compile-schemas --strict schemas/</kbd>.
Once this is done, reload gnome-shell (Alt-F2) and execute
gnome-tweaks to enable the extension or alternatively the following
command <kbd>gnome-extensions enable monito@drieu.org</kbd>.
And voilà!
## About
This extension has been developed for my own needs. I will maintain
it and add features but it may be very crude, compared to the
excellent *nagstamon* package, which only missed the ability to be put
inside of the gnome-shell panel (this could not be developed).
Things I plan to add at some point or another:
* Buttons to operate on services à la nagstamon (connect via SSH, etc.)
* Filters to hide services (handled, scheduled downtime, host down, ...)
* Support for other free (as in free speech) monitoring servers
Things I will be unlikely to add unless a patch is provided:
* Notification for new alerts
* Display list of down hosts (I am not interested in host monitoring but services)

View File

@ -24,58 +24,106 @@
const GETTEXT_DOMAIN = 'monito';
let _httpSession;
let _status;
let _ok_text;
let _warning_text;
let _critical_text;
const Gettext = imports.gettext.domain(GETTEXT_DOMAIN);
const ExtensionUtils = imports.misc.extensionUtils; // eslint-disable-line no-undef
const Lang = imports.lang; // eslint-disable-line no-undef
const Main = imports.ui.main; // eslint-disable-line no-undef
const Mainloop = imports.mainloop; // eslint-disable-line no-undef
const Me = ExtensionUtils.getCurrentExtension(); // eslint-disable-line no-undef
const PanelMenu = imports.ui.panelMenu; // eslint-disable-line no-undef
const PopupMenu = imports.ui.popupMenu; // eslint-disable-line no-undef
const Gettext = imports.gettext.domain(GETTEXT_DOMAIN); // eslint-disable-line no-undef
const _ = Gettext.gettext;
const Lang = imports.lang;
const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const { GObject, St, Clutter, Gio, GLib, Pango } = imports.gi; // eslint-disable-line no-undef
const { GObject, St, Soup, Clutter } = imports.gi;
const SETTINGS_SCHEMA = "org.gnome.shell.extensions.monito";
const SETTINGS_SCHEMA = "org.gnome.shell.extensions.monito@drieu.org";
const Convenience = Me.imports.convenience;
const Icinga = Me.imports.servers.icinga.Icinga;
const Icinga2 = Me.imports.servers.icinga2.Icinga2;
const Icinga2API = Me.imports.servers.icinga2api.Icinga2API;
const Preferences = Me.imports.prefs;
let settings = Convenience.getSettings(SETTINGS_SCHEMA);
let account_settings = [ ];
//const column_definitions = {
// status: { label: _('Status'), width: 50, expand: false, },
// host_name: { label: _('Host name'), width: 300, expand: false, },
// service_display_name: { label: _('Service'), width: 300, expand: false, },
// has_been_acknowledged: { label: _('Ack'), width: 50, expand: false, align: Clutter.ActorAlign.CENTER, style: 'font-weight:bold;' },
// last_check: { label: _('Last check'), width: 200, expand: false, type: 'date' },
// attempts: { label: _('Attempts'), width: 50, expand: false, },
// status_information: { label: _('Information'), width: 600, expand: false, },
// actions: { label: 'Actions', width: 50, expand: false, special: 'actions' },
//};
const Indicator = GObject.registerClass(
class Indicator extends PanelMenu.Button {
_init() {
_init(server) {
super._init(0.0, _('Monito Checker'));
let box = new St.BoxLayout ( { } );
this.add_child(box);
this.server = server;
this.initStatus ( );
// Main Box
/*
let ok_box = new St.BoxLayout({ style_class: 'monito-ok-box monito-box' });
_ok_text = new St.Label({ text: String(_status['OK']) })
ok_box.add_child(_ok_text);
box.add_child(ok_box);
*/
let warning_box = new St.BoxLayout({ style_class: 'monito-warning-box monito-box' });
_warning_text = new St.Label({ text: String(_status['WARNING']) })
warning_box.add_child(_warning_text);
box.add_child(warning_box);
this.namesBoxes = { };
this.boxes = { };
this.sortIcons = { };
this.showAll = false;
let critical_box = new St.BoxLayout({ style_class: 'monito-critical-box monito-box' });
_critical_text = new St.Label({ text: String(_status['CRITICAL']) })
critical_box.add_child(_critical_text);
box.add_child(critical_box);
account_settings [ server ] = Preferences.getAccountSettings ( this.server );
this.account_settings = account_settings [ server ];
let type = this.account_settings.get_string ( "type" );
if ( type == 'Icinga' )
this.serverLogic = new Icinga ( this.server, this );
else if ( type == 'Icinga2' )
this.serverLogic = new Icinga2 ( this.server, this );
else if ( type == 'Icinga2API' )
this.serverLogic = new Icinga2API ( this.server, this );
this.initUI ( );
}
initUI ( ) {
let box = new St.BoxLayout ( { } );
this.add_child(box);
monitoLog ( '> Server ' + this.server );
let serverBox = new St.BoxLayout ( { style_class: 'monito-serverbox' } );
box.add_child(serverBox);
let name_box = new St.BoxLayout( { style_class: 'monito-namebox' } );
this.namesBoxes [ this.server ] = new St.Label ( { text: this.account_settings.get_string ( 'name' ),
y_align: Clutter.ActorAlign.CENTER, } );
name_box.add_child ( this.namesBoxes [ this.server ] );
serverBox.add_child(name_box);
this.account_settings.bind ( 'name', this.namesBoxes [ this.server ], 'text', Gio.SettingsBindFlags.GET );
this.namesBoxes [ this.server ].connect ( 'button-press-event', Lang.bind ( this, function ( ) { this.setMenu ( this.menu ); } ) );
for ( var boxName of [ 'ok', 'warning', 'critical', 'unknown' ] )
{
let _box = new St.BoxLayout( { style_class: 'monito-%s-box monito-box'.format(boxName), visible: false } );
_box.set_style ( this.getStyleForRow ( boxName ) );
this.account_settings.connect("changed::%s-color".format(boxName), Lang.bind ( { widget: _box }, setColor ) );
this.account_settings.connect("changed::%s-fg".format(boxName), Lang.bind ( { widget: _box }, setColor ) );
this.boxes [ boxName ] = new St.Label ( { text: '' } );
_box.add_child ( this.boxes [ boxName ] );
serverBox.add_child(_box);
}
box.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM));
this.menu_new = new PopupMenu.PopupMenu(this, Clutter.ActorAlign.START, St.Side.TOP, 0);
// Menu
this._buttonMenu = new PopupMenu.PopupBaseMenuItem({
@ -84,23 +132,59 @@ class Indicator extends PanelMenu.Button {
});
this.menu.addMenuItem(this._buttonMenu);
let _path = Me.path + '/img/monito.png';
let _icon = new St.Icon({
gicon: Gio.icon_new_for_string(_path),
});
let _iconBin = new St.Bin();
_iconBin.child = _icon;
this._buttonMenu.actor.add_actor(_iconBin);
// let item = new PopupMenu.PopupMenuItem(_('Reload'));
// item.connect('activate', () => {
// this.updateStatus ( );
// });
// this.menu.addMenuItem(item);
this._mainLabel = new St.Label({ style_class: 'monito-title', text: 'Monito Checker', x_expand: true });
this._mainLabel = new St.Label ( { style_class: 'monito-title', text: _('Monito Checker'), x_expand: true } );
this._buttonMenu.actor.add_actor(this._mainLabel);
this._searchField = new St.Entry ( {
x_align: Clutter.ActorAlign.END,
y_align: Clutter.ActorAlign.CENTER,
reactive: true,
can_focus: true,
track_hover: true,
style_class: 'entry',
secondary_icon: new St.Icon ( {
style_class: 'monito-button-icon',
icon_name: 'edit-find-symbolic',
icon_size: 24,
width: 24,
height: 24,
} ),
});
this._searchField.connect('secondary-icon-clicked', Lang.bind(this, this._onSearchFieldActivate ) );
this._searchField.clutter_text.connect('activate', Lang.bind(this, this._onSearchFieldActivate ) );
this._searchField.clutter_text.connect('text-changed', Lang.bind(this, this._onSearchFieldActivate ) );
this._buttonMenu.actor.add_child (this._searchField);
this._prefsButton = this._createButton ( 'preferences-system-symbolic', 'Preferences', this._onPreferencesActivate );
this._showAllButton = this._createButton ( 'big', 'view-reveal-symbolic', _('Show all'), this.changeShowAll, true );
this._buttonMenu.actor.add_child (this._showAllButton );
this._prefsButton = this._createButton ( 'big', 'preferences-system-symbolic', _('Preferences'), this._onPreferencesActivate );
this._buttonMenu.actor.add_child (this._prefsButton);
this._reloadButton = this._createButton ( 'view-refresh-symbolic', 'Reload', this.updateStatus );
this._reloadButton = this._createButton ( 'big', 'view-refresh-symbolic', _('Reload view'), this.updateStatus );
this._buttonMenu.actor.add_child (this._reloadButton );
if ( this.serverLogic && this.serverLogic.canRecheck )
{
let multiButtonBox = new St.BoxLayout( { style_class: 'monito-multibutton' } );
this._recheckButton = this._createButton ( 'big', 'mail-send-receive-symbolic', _('Recheck all'), this.recheckAll );
this._recheckSpecialButton = this._createPopupButton ( this.recheckSpecial );
multiButtonBox.add_child ( this._recheckButton );
multiButtonBox.add_child ( this._recheckSpecialButton );
this._buttonMenu.actor.add_child (multiButtonBox);
const sourceActor = new St.Widget();
this._menu = new PopupMenu.PopupMenu(sourceActor, 0.0, St.Side.TOP);
const menuItem1 = this._menu.addAction('Item 1', () => console.log('activated'));
}
let _intermediate = new PopupMenu.PopupBaseMenuItem ( {
style_class: 'monito-services',
@ -114,16 +198,23 @@ class Indicator extends PanelMenu.Button {
x_expand: true
});
_intermediate.actor.add_actor(this._box);
this.menu_orig = this.menu;
// let _bin = new St.Bin();
// _intermediate.actor.add_actor(_bin);
// _bin.set_child(this._services_table);
// this._list = new FlatList()
// _bin.set_child(this._list);
this.updateStatus ( );
this.setupTimeout ( );
}
cancelTimeout ( )
{
if (this.timeout)
Mainloop.source_remove(this.timeout);
}
setupTimeout ( )
{
monitoLog ( 'Setting up timeout of ' + settings.get_int ( "poll-delay" ) + ' secs' );
this.cancelTimeout ( );
this.timeout = Mainloop.timeout_add ( settings.get_int ( "poll-delay" ) * 1000, Lang.bind(this, this.updateStatus ) );
}
initStatus ( ) {
@ -133,115 +224,352 @@ class Indicator extends PanelMenu.Button {
'UNKNOWN': 0 };
}
updateStatus ( ) {
const SETTINGS_SCHEMA_ACCOUNT = "org.gnome.shell.extensions.monito@drieu.org.account";
const Convenience = Me.imports.convenience;
let account_settings = Convenience.getSettings(SETTINGS_SCHEMA_ACCOUNT, '/org/gnome/shell/extensions/monito@drieu/org/account/0');
let username = account_settings.get_string("username");
let password = account_settings.get_string("password");
let urlcgi = account_settings.get_string("urlcgi");
if ( ! urlcgi )
{
log ( 'Not updating monito because no URL configured' );
return;
}
urlcgi = urlcgi.replace ( /^(https?:\/\/)/, '$1' + username + ':' + password + '@' );
// log ( 'monito >>> ' + urlcgi );
this.load_data_async ( urlcgi, { 'jsonoutput': '' }, function(res) { Main.notify(res); } )
recheckAll ( )
{
this.spinChildOf ( this._recheckButton );
this.serverLogic.recheckAll ( this._recheckButton );
}
createBin(status, text, col) {
let _widths = [ 300, 300, 200, 50, 600 ];
recheckSpecial ( )
{
this._menu.open();
}
changeShowAll ( )
{
this.showAll = ! this.showAll;
this.refreshUI ( );
}
updateStatus ( )
{
this.spinChildOf ( this._reloadButton );
this.serverLogic.refresh ( );
this.setupTimeout ( );
}
spinChildOf ( widget )
{
if ( ! widget )
return;
let _size = widget.get_child().width;
let _child = new St.Icon({
style_class: 'monito-button-icon',
icon_name: 'process-working-symbolic',
icon_size: _size,
width: _size,
height: _size,
});
let _transition = new Clutter.PropertyTransition ( { property_name: 'rotation_angle_z',
repeat_count: -1,
duration: 1000 } );
_transition.set_from ( 0 );
_transition.set_to ( 360 );
widget.set_child ( _child );
_child.set_pivot_point ( .5, .5 );
_child.add_transition ( 'rotation', _transition );
}
stopChildSpin ( widget )
{
let _size = widget.get_child().width;
if ( widget.get_child() && widget.get_child().get_transition('rotation') )
widget.get_child().get_transition('rotation').stop();
widget.child = new St.Icon({
style_class: 'monito-button-icon',
icon_name: widget.prevIcon,
icon_size: _size,
width: _size,
height: _size,
});
}
createHeaderBin ( colName, colSize = 50 ) {
let col = Preferences.column_definitions [ colName ];
if ( ! col )
return new St.Bin({
style_class: 'monito-service',
width: colSize,
x_expand: false
});
let _box = new St.BoxLayout ( { vertical: false,
x_expand: true } );
_box.add_child ( new St.Label ( { text: col.label,
x_expand: true,
x_align: Clutter.ActorAlign.START } ) );
let _iconBin = new St.Bin ( { x_align: Clutter.ActorAlign.END });
_box.add_child ( _iconBin );
let _iconName = '';
let _sortOrder = Preferences.getSortOrder ( this.server );
if ( _sortOrder.indexOf ( colName + '+' ) >= 0 )
_iconName = 'view-sort-descending-symbolic';
else if ( _sortOrder.indexOf ( colName + '-' ) >= 0 )
_iconName = 'view-sort-ascending-symbolic';
this.sortIcons [ colName ] = new St.Icon ( {
style_class: 'monito-button-icon',
icon_name: _iconName,
icon_size: 16,
} );
_iconBin.child = this.sortIcons [ colName ];
let _button = new St.Button ( {
x_align: Clutter.ActorAlign.FILL,
y_align: Clutter.ActorAlign.CENTER,
width: colSize,
reactive: true,
can_focus: true,
track_hover: true,
accessible_name: col.label,
style_class: 'button',
} );
_button.column = colName;
_button.add_actor ( _box );
_button.connect ( 'clicked', Lang.bind ( this, this._onSortColumnClick ) );
let _bin = new St.Bin({
style_class: 'monito-service-' + status,
width: _widths[col],
x_expand: ( col == 4 ? true : false ),
child: new St.Label({ style_class: 'monito-label', text: text })
style_class: 'monito-service',
width: colSize,
x_expand: col.expand,
child: _button,
});
return _bin;
}
createBin ( status, text, colSize = 50, col ) {
let _child;
if ( ! col )
return new St.Bin({
track_hover: true,
width: colSize,
x_expand: false
})
if ( text === undefined )
text = '…';
if ( ! col [ 'special' ] )
{
_child = new St.Label ( { style_class: 'monito-label',
reactive: true,
can_focus: true,
track_hover: true,
width: colSize,
text: text.toString().replace(/\n.*/s, ''),
x_align: ( col.align ? col.align : Clutter.ActorAlign.START ),
style: ( col.style ? col.style : '' ) } );
_child.original_text = text.toString();
_child.connect('button-press-event', Lang.bind(this, this._onExpandLabel ) );
// _child.connect('notify::hover', Lang.bind(this, this._onEnterEvent ) );
}
else if ( col.special == 'actions' )
{
_child = new St.BoxLayout ( { x_expand: true,
vertical: false,
width: colSize, } );
if ( this.serverLogic.canRecheck )
{
_child.add_child ( this._createButton ( 'small', 'mail-send-receive-symbolic', text, this._onRecheckButtonClick ) );
}
_child.add_child ( this._createButton ( 'small', 'web-browser-symbolic', text, this._onOpenInBrowser ) );
}
let _bin = new St.Bin({
track_hover: true,
width: colSize,
x_expand: col.expand,
child: _child,
});
return _bin;
}
load_data_async(url, params, fun) {
if (_httpSession === undefined) {
_httpSession = new Soup.Session();
_httpSession.user_agent = Me.metadata.uuid;
} else {
// abort previous requests.
_httpSession.abort();
getStyleForRow ( boxName, row = 0 )
{
let bgColor = this.account_settings.get_string ( boxName + '-color' );
let fgColor = this.account_settings.get_string ( boxName + '-fg' );
if ( row % 2 )
{
bgColor = this.lightenColor(bgColor, 12);
fgColor = this.lightenColor(fgColor, 12);
}
else
{
bgColor = this.lightenColor(bgColor, -12);
fgColor = this.lightenColor(fgColor, -12);
}
let message = Soup.form_request_new_from_hash('GET', url, params);
return 'background-color: %s; color: %s' . format ( bgColor, fgColor );
}
lightenColor ( col, amt ) {
if ( col.substring(0,1) == '#' )
col = col.substring ( 1 );
col = parseInt(col, 16);
return '#%06x'.format (Math.max(Math.min((col & 0x0000FF) + amt,0x0000FF),0) |
(Math.max(Math.min((((col >> 8) & 0x00FF) + amt),0x0000FF),0) << 8) |
(Math.max(Math.min(((col >> 16) + amt),0x0000FF),0) << 16));
}
_httpSession.queue_message(message, Lang.bind(this, function(_httpSession, message) {
//Main.notify(message.response_body.data);
try {
// log ( message.response_body.data );
let json = JSON.parse(message.response_body.data);
refreshUI ( ) {
try {
this.initStatus ( );
this.initStatus ( );
this._box.remove_all_children();
this._box.remove_all_children();
for ( let i = 0 ; i < json.status.service_status.length ; i ++ )
{
_status [ json.status.service_status[i].status ] ++;
if ( json.status.service_status[i].status != 'OK' )
{
let infoBox = new St.BoxLayout({
style_class: 'monito-service-line monito-service-line-' + json.status.service_status[i].status,
hover: true,
x_expand: true
});
this._box.add_child(infoBox);
infoBox.add_child ( this.createBin ( json.status.service_status[i].status,
json.status.service_status[i].host_name,
0 ) );
infoBox.add_child ( this.createBin ( json.status.service_status[i].status,
json.status.service_status[i].service_display_name,
1 ) );
infoBox.add_child ( this.createBin ( json.status.service_status[i].status,
json.status.service_status[i].last_check,
2) );
infoBox.add_child ( this.createBin ( json.status.service_status[i].status,
json.status.service_status[i].attempts,
3 ) );
infoBox.add_child ( this.createBin ( json.status.service_status[i].status,
json.status.service_status[i].status_information,
4 ) );
// let url = 'https://xxx:xxx@host/cgi-bin/icinga/extinfo.cgi?type=2&host='+json.status.service_status[i].host_name+"&service="+json.status.service_status[i].service_description;
// activeLabel.connect('button-press-event', () => {
// Main.notify(url);
// Gtk.show_uri(null, url, global.get_current_time());
// } );
}
}
// _ok_text.set_text ( String(_status.OK) );
_warning_text.set_text ( String(_status.WARNING) );
_critical_text.set_text ( String(_status.CRITICAL) );
} catch (e) {
Main.notify(_('Zbeu!'));
_warning_text.set_text ( '…' );
_critical_text.set_text ( '…' );
log(e);
if ( this.serverLogic.error || ! this.serverLogic.status )
{
this._box.add_child ( new St.Label ( { style_class: 'monito-network-error', text: this.serverLogic.error } ) );
this.boxes['ok'].set_text ( '…' );
this.boxes['warning'].set_text ( '…' );
this.boxes['critical'].set_text ( '…' );
this.boxes['unknown'].set_text ( '…' );
this.stopChildSpin ( this._reloadButton );
return;
}
}));
let headerBox = new St.BoxLayout({
track_hover: true,
x_expand: true
});
this._box.add_child(headerBox);
for ( let _col of Preferences.getColumns ( this.server ) )
headerBox.add_child ( this.createHeaderBin ( _col.name, _col.size ) );
let scrollBox = new St.ScrollView ( { hscrollbar_policy: St.PolicyType.NEVER,
enable_mouse_scrolling: true, } );
this._box.add_child(scrollBox);
let tableBox = new St.BoxLayout({
track_hover: true,
vertical: true,
x_expand: true,
});
scrollBox.add_actor(tableBox);
let processedStatus = this.serverLogic.getProcessedStatus ( )
for ( let entryCount of processedStatus )
_status [ entryCount.status ] ++;
let _row = 0;
for ( let entry of processedStatus )
{
if ( this._searchString &&
! ( entry [ 'host_name' ].toLowerCase().includes ( this._searchString ) ||
entry [ 'service_display_name' ].toLowerCase().includes ( this._searchString ) ||
entry [ 'status_information' ].toLowerCase().includes ( this._searchString ) ) )
continue;
if ( this.showAll ||
( ! _status [ 'WARNING' ] && ! _status [ 'CRITICAL' ] && ! _status [ 'UNKNOWN' ] && entry.status == 'OK' ) ||
( ( _status [ 'WARNING' ] || _status [ 'CRITICAL' ] || _status [ 'UNKNOWN' ] ) && entry.status != 'OK' ) )
{
let _style = this.getStyleForRow ( entry.status.toLowerCase(), _row );
let infoBox = new St.BoxLayout({
style_class: 'monito-service-line',
style: _style,
x_expand: true,
reactive: true,
can_focus: true,
track_hover: true,
});
tableBox.add_child(infoBox);
for ( let _col of Preferences.getColumns ( this.server ) )
{
entry [ 'real_' + _col.name ] = entry [ _col.name ];
if ( _col.name == 'host_name' && this.account_settings.get_string ( 'host-match' ) )
entry [ _col.name ] = entry [ _col.name ] . replace ( new RegExp ( this.account_settings.get_string ( 'host-match' ), 'i' ),
this.account_settings.get_string ( 'host-replace' ) );
else if ( _col.name == 'service_display_name' && this.account_settings.get_string ( 'service-match' ) )
entry [ _col.name ] = entry [ _col.name ] . replace ( new RegExp ( this.account_settings.get_string ( 'service-match' ), 'i' ),
this.account_settings.get_string ( 'service-replace' ) );
else if ( _col.name == 'status_information' && this.account_settings.get_string ( 'status-info-match' ) )
entry [ _col.name ] = entry [ _col.name ] . replace ( new RegExp ( this.account_settings.get_string ( 'status-info-match' ), 'i' ),
this.account_settings.get_string ( 'status-info-replace' ) ) . replace ( new RegExp ( "\\n.*" ), "" );
else if ( _col.name == 'has_been_acknowledged' )
{
if ( entry [ _col.name ] )
entry [ _col.name ] = '✔';
else
entry [ _col.name ] = '';
}
infoBox.add_child ( this.createBin ( entry.status, ( _col.name == 'actions' ? entry : entry [ _col.name ] ), _col.size, Preferences.column_definitions [ _col.name ] ) );
}
_row ++;
}
}
if ( _status.WARNING == 0 && _status.CRITICAL == 0 )
{
this.boxes['ok'].set_text ( String(_status.OK) );
this.boxes['ok'].get_parent().show ( );
this.boxes['warning'].get_parent().hide ( );
this.boxes['critical'].get_parent().hide ( );
this.boxes['unknown'].get_parent().hide ( );
}
else
{
this.boxes['warning'].set_text ( String(_status.WARNING) );
this.boxes['critical'].set_text ( String(_status.CRITICAL) );
this.boxes['ok'].get_parent().hide ( );
this.boxes['warning'].get_parent().show ( );
this.boxes['critical'].get_parent().show ( );
}
if ( _status.UNKNOWN != 0 )
{
this.boxes['unknown'].set_text ( String(_status.UNKNOWN) );
this.boxes['unknown'].get_parent().show ( );
}
else
{
this.boxes['unknown'].get_parent().hide ( );
}
}
catch ( e )
{
monitoLog ( 'RefreshUI error: ' + e );
monitoLog ( e.stack );
}
this.stopChildSpin ( this._reloadButton );
monitoLog ( 'RefreshUI done' );
return;
}
_onPreferencesActivate() {
_onSearchFieldActivate ( e ) {
monitoLog ( 'Search: ' + e.text );
this._searchString = e.text.toLowerCase();
this.refreshUI ( );
return;
}
_onPreferencesActivate ( ) {
this.menu.actor.hide();
if (typeof ExtensionUtils.openPrefs === 'function') {
ExtensionUtils.openPrefs();
} else {
Util.spawn([
ExtensionUtils.spawn([
"gnome-shell-extension-prefs",
Me.uuid
]);
@ -249,23 +577,129 @@ class Indicator extends PanelMenu.Button {
return 0;
}
_createButton ( icon, text, callback ) {
_onSortColumnClick ( button ) {
let _sortOrder = Preferences.getSortOrder ( this.server );
let _indexPlus = _sortOrder.indexOf ( button.column + '+' );
let _indexMinus = _sortOrder.indexOf ( button.column + '-' );
if ( _indexPlus >= 0 )
_sortOrder [ _indexPlus ] = button.column + '-';
else if ( _indexMinus >= 0 )
_sortOrder.splice ( _indexMinus, 1 );
else
_sortOrder.unshift ( button.column + '+' );
Preferences.setSortOrder ( this.server, _sortOrder );
this.refreshUI ( );
}
_onRecheckButtonClick ( e )
{
this.spinChildOf ( e );
// monitoLog ( JSON.stringify ( e ) );
e.service.button = e;
this.serverLogic.recheck ( e.service );
}
_onExpandLabel ( e )
{
monitoLog ( "Monito: Click label " + e.original_text );
let temp = e.text;
e.text = e.original_text;
e.original_text = temp;
if ( e.clutter_text.line_wrap )
{
e.clutter_text.line_wrap = false;
}
else
{
e.clutter_text.line_wrap = true;
e.clutter_text.line_wrap_mode = Pango.WrapMode.WORD;
e.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
}
}
// _onEnterEvent ( e )
// {
// log ( "Monito: enter");
// }
_onOpenInBrowser ( e )
{
this.spinChildOf ( e );
e.service.button = e;
let loop = GLib.MainLoop.new(null, false);
try {
let _cmd = "%s %s".format(settings.get_string ( "web-browser" ), this.serverLogic.getUrlForService ( e.service ) );
let proc = Gio.Subprocess.new (
_cmd.split ( ' ' ),
Gio.SubprocessFlags.STDOUT_PIPE | Gio.SubprocessFlags.STDERR_PIPE
);
let cancellable = new Gio.Cancellable();
proc.wait_async(cancellable, (proc, result) => {
try {
proc.wait_finish(result);
if ( ! proc.get_successful()) {
monitoLog ( 'Monito: the process failed' );
}
} catch (e) {
monitoLog(e);
} finally {
loop.quit();
}
});
} catch ( e ) {
monitoLog ( 'Monito err: ' + e.message );
Main.notifyError ( _('Unable to execute command: %s').format ( e.message ) );
}
this.stopChildSpin ( e.service.button );
}
_createButton ( sizeName, icon, data, callback, radiobutton = false ) {
let size = 24;
if ( sizeName == 'small' )
size = 24;
else if ( sizeName == 'big' )
size = 32;
let _text = '';
if ( ! ( data instanceof Object ) )
_text = data;
let button = new St.Button({
x_align: Clutter.ActorAlign.END,
y_align: Clutter.ActorAlign.CENTER,
reactive: true,
can_focus: true,
track_hover: true,
accessible_name: text,
style_class: 'button'
accessible_name: _text,
style_class: 'button %s-button'.format(sizeName),
rotation_angle_x: 0.0,
// width: size,
// height: size
});
button.prevIcon = icon;
if ( radiobutton )
{
button.toggle_mode = true;
button.set_checked ( true );
}
if ( data instanceof Object )
button.service = data;
button.child = new St.Icon({
style_class: 'monito-button-icon',
icon_name: icon,
icon_size: 24,
width: 24,
height: 24,
icon_size: size - 4,
width: size - 4,
height: size - 4
});
button.connect('clicked', Lang.bind(this, callback ) );
@ -273,31 +707,89 @@ class Indicator extends PanelMenu.Button {
return button;
}
_createPopupButton ( callback ) {
let size = 48;
let sizeName = 'big';
let button = new St.Button({
x_align: Clutter.ActorAlign.END,
y_align: Clutter.ActorAlign.CENTER,
reactive: true,
can_focus: true,
track_hover: true,
style_class: 'button %s-button'.format(sizeName),
rotation_angle_x: 0.0,
width: 32,
height: size
});
button.child = PopupMenu.arrowIcon(St.Side.BOTTOM);
button.connect('clicked', Lang.bind(this, callback ) );
return button;
}
});
class Extension {
constructor(uuid) {
this._uuid = uuid;
this._connectId = settings.connect("changed::servers", Lang.bind ( this, function() {
this.disable ( );
this.enable ( );
} ) );
ExtensionUtils.initTranslations(GETTEXT_DOMAIN);
}
enable() {
this._indicator = new Indicator();
Main.panel.addToStatusArea(this._uuid, this._indicator);
this._indicators = { };
for ( let _server of Preferences.getServersList() )
{
let _pref = Preferences.getAccountSettings ( _server );
if ( _pref.get_boolean ( 'active' ) )
{
this._indicators [ _server ] = new Indicator(_server);
Main.panel.addToStatusArea ( '%s-%d'.format ( this._uuid, _server), this._indicators [ _server ] );
}
}
}
disable() {
this._indicator.destroy();
this._indicator = null;
for ( var i of Object.keys(this._indicators) )
{
this._indicators[i].cancelTimeout();
this._indicators[i].destroy();
}
if ( this._connectId )
{
settings.disconnect( this._connectId );
this._connectId = null;
}
if (_httpSession !== undefined)
_httpSession.abort();
_httpSession = undefined;
this._indicators = { };
}
}
function init(meta) {
function init(meta) { // eslint-disable-line no-unused-vars
return new Extension(meta.uuid);
}
function monitoLog ( msg )
{
log ( 'Monito: ' + msg ); // eslint-disable-line no-undef
}
function setColor (stgs, key) {
// monitoLog ( '> %s color %s'.format ( key, stgs.get_string(key) ) );
// monitoLog ( '> style %s'.format ( style ) );
let style = this.widget.get_style ( );
if ( key.match ( /-fg$/ ) )
this.widget.set_style ( style + ';color: ' + stgs.get_string(key) );
else
this.widget.set_style ( style + ';background-color: ' + stgs.get_string(key) );
}

BIN
img/monito.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

431
img/monito.svg Normal file
View File

@ -0,0 +1,431 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
id="svg2"
sodipodi:docname="monitoring.svg"
inkscape:export-filename="monitoring.png"
viewBox="0 0 741.58 741.58003"
inkscape:export-xdpi="8.29"
version="1.1"
inkscape:export-ydpi="8.29"
inkscape:version="1.1.1 (1:1.1+202109281949+c3084ef5ed)"
width="741.58002"
height="741.58002"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
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">
<filter
id="filter3801"
height="1.0242089"
width="3.7811639"
color-interpolation-filters="sRGB"
y="-0.012104463"
x="-1.3905819"
inkscape:collect="always">
<feGaussianBlur
id="feGaussianBlur3803"
stdDeviation="3.0676239"
inkscape:collect="always" />
</filter>
<filter
id="filter3845"
inkscape:collect="always"
color-interpolation-filters="sRGB"
x="-0.0055384827"
y="-0.0065454037"
width="1.011077"
height="1.0130908">
<feGaussianBlur
id="feGaussianBlur3847"
stdDeviation="1.4424979"
inkscape:collect="always" />
</filter>
<linearGradient
id="linearGradient3861"
y2="31.312"
gradientUnits="userSpaceOnUse"
x2="326.09"
y1="595.31"
x1="324.69"
inkscape:collect="always">
<stop
id="stop3855"
style="stop-color:#0000ff;stop-opacity:0"
offset="0" />
<stop
id="stop3863"
style="stop-color:#0000ff"
offset=".5" />
<stop
id="stop3857"
style="stop-color:#0000ff;stop-opacity:0"
offset="1" />
</linearGradient>
<linearGradient
id="linearGradient3883"
y2="506.79001"
gradientUnits="userSpaceOnUse"
x2="548.53003"
y1="-77.213997"
x1="-111.75"
inkscape:collect="always">
<stop
id="stop3879"
style="stop-color:#ffffff"
offset="0" />
<stop
id="stop3881"
style="stop-color:#ffffff;stop-opacity:0"
offset="1" />
</linearGradient>
<filter
id="filter3885"
inkscape:collect="always"
color-interpolation-filters="sRGB"
x="-0.028768596"
y="-0.048094022"
width="1.0575372"
height="1.096188">
<feGaussianBlur
id="feGaussianBlur3887"
stdDeviation="7.1951454"
inkscape:collect="always" />
</filter>
<filter
id="filter3947"
inkscape:collect="always"
color-interpolation-filters="sRGB"
x="-0.042500001"
y="-0.068807402"
width="1.085"
height="1.1363132">
<feGaussianBlur
id="feGaussianBlur3949"
stdDeviation="8.8000002"
inkscape:collect="always" />
</filter>
<filter
id="filter3951"
inkscape:collect="always"
color-interpolation-filters="sRGB"
x="-0.019352941"
y="-0.031322734"
width="1.0387059"
height="1.0561378">
<feGaussianBlur
id="feGaussianBlur3953"
stdDeviation="0.22"
inkscape:collect="always" />
</filter>
<linearGradient
id="linearGradient3969"
y2="626.10999"
gradientUnits="userSpaceOnUse"
x2="353.78"
y1="2.3622"
x1="353.79001"
inkscape:collect="always">
<stop
id="stop3779"
style="stop-color:#999999"
offset="0" />
<stop
id="stop3791"
style="stop-color:#f0f0f0"
offset=".019117" />
<stop
id="stop3789"
style="stop-color:#c8c8c8"
offset=".033653" />
<stop
id="stop3785"
style="stop-color:#787878"
offset=".95616" />
<stop
id="stop3787"
style="stop-color:#505050"
offset=".97508" />
<stop
id="stop3793"
style="stop-color:#787878"
offset=".98702" />
<stop
id="stop3781"
style="stop-color:#999999"
offset="1" />
</linearGradient>
<filter
id="filter3971"
inkscape:collect="always"
color-interpolation-filters="sRGB"
x="-0.011920366"
y="-0.01369327"
width="1.0238407"
height="1.0273865">
<feGaussianBlur
id="feGaussianBlur3973"
stdDeviation="3.3535715"
inkscape:collect="always" />
</filter>
</defs>
<sodipodi:namedview
id="base"
fit-margin-left="0"
inkscape:zoom="0.70710678"
borderopacity="1.0"
inkscape:current-layer="layer3"
inkscape:cx="289.20667"
inkscape:window-y="28"
inkscape:window-maximized="1"
showgrid="false"
fit-margin-right="0"
showguides="false"
bordercolor="#666666"
inkscape:window-x="0"
inkscape:guide-bbox="true"
fit-margin-bottom="0"
inkscape:window-width="1920"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
pagecolor="#ffffff"
inkscape:cy="277.18586"
inkscape:document-units="px"
inkscape:window-height="1015"
fit-margin-top="0"
inkscape:pagecheckerboard="0"
width="741.58002px">
<sodipodi:guide
id="guide2997"
position="6.2197715,-407.95883"
orientation="1,0" />
<sodipodi:guide
id="guide2999"
position="756.21977,-407.95883"
orientation="1,0" />
<sodipodi:guide
id="guide3001"
position="6.2197715,-407.95883"
orientation="0,1" />
<sodipodi:guide
id="guide3003"
position="6.2197715,642.04117"
orientation="0,1" />
</sodipodi:namedview>
<g
id="layer2"
inkscape:label="Capa"
inkscape:groupmode="layer"
transform="translate(6.2198,3.3933)" />
<g
id="layer3"
inkscape:label="Capa#1"
transform="translate(6.2198,3.3933)"
inkscape:groupmode="layer">
<g
id="g933"
transform="translate(2.2543189e-4,46.889546)">
<rect
id="rect3955"
style="fill:#000000;stroke:#000000;filter:url(#filter3971)"
transform="matrix(1.01,0,0,1.01,-1.6257,-1.1851)"
height="624.28998"
width="717.14001"
y="6.3621998"
x="4" />
<rect
id="rect3775"
style="fill:url(#linearGradient3969);stroke:#000000"
height="624.28998"
width="717.14001"
y="2.3622"
x="0" />
<rect
id="rect3795"
style="opacity:0.66531;fill:#ffffff;filter:url(#filter3801)"
transform="matrix(0.74929,0,0,0.9773,-0.5423,0.068731)"
height="608.22998"
width="5.2944002"
y="10.39"
x="5.1992998" />
<rect
id="rect3805"
style="opacity:0.66531;fill:#ffffff;filter:url(#filter3801)"
transform="matrix(0.74929,0,0,0.9773,703.46,0.068731)"
height="608.22998"
width="5.2944002"
y="10.39"
x="5.1992998" />
<rect
id="rect3808"
style="fill:#505050;filter:url(#filter3845)"
height="528.91998"
width="625.08002"
y="46.855999"
x="43.841" />
<path
id="path3838"
style="fill:#000000"
inkscape:connector-curvature="0"
d="m 46.688,49.688 v 523.25 h 619.41 V 49.688 Z" />
<path
id="path3851"
style="fill:url(#linearGradient3861)"
sodipodi:type="inkscape:offset"
d="m 50.6875,53.6875 v 515.25 h 611.40625 v -515.25 z"
inkscape:original="M 46.6875 49.6875 L 46.6875 572.9375 L 666.09375 572.9375 L 666.09375 49.6875 L 46.6875 49.6875 z "
inkscape:radius="-4" />
<path
id="path3875"
style="opacity:0.60816;fill:url(#linearGradient3883);filter:url(#filter3885)"
sodipodi:type="inkscape:offset"
d="M 70.6875,59.265625 A 14.423647,14.423647 0 0 0 56.265625,73.6875 v 329.71875 a 14.423647,14.423647 0 0 0 13.714844,14.4043 C 160.79548,422.26869 260.57246,398.2393 351.6582,314.16016 445.1963,227.8173 548.4538,198.85429 642.40039,196.85742 A 14.423647,14.423647 0 0 0 656.51562,182.4375 V 73.6875 A 14.423647,14.423647 0 0 0 642.09375,59.265625 Z"
inkscape:original="M 70.6875 73.6875 L 70.6875 403.40625 C 158.5204 407.718 253.60229 385.045 341.875 303.5625 C 438.13844 214.70394 545.37247 184.49335 642.09375 182.4375 L 642.09375 73.6875 L 70.6875 73.6875 z "
inkscape:radius="14.422205" />
<path
id="path3889"
style="fill:none;stroke:#c8c8dc;stroke-width:2;stroke-linecap:round;stroke-dasharray:4, 2"
inkscape:connector-curvature="0"
d="m 58,107.36 602,2" />
<path
id="path3891"
d="m 58,175.36 602,2"
style="fill:none;stroke:#c8c8dc;stroke-width:2;stroke-linecap:round;stroke-dasharray:4, 2"
inkscape:connector-curvature="0" />
<path
id="path3893"
style="fill:none;stroke:#c8c8dc;stroke-width:2;stroke-linecap:round;stroke-dasharray:4, 2"
inkscape:connector-curvature="0"
d="m 58,243.36 602,2" />
<path
id="path3895"
d="m 58,313.36 602,2"
style="fill:none;stroke:#c8c8dc;stroke-width:2;stroke-linecap:round;stroke-dasharray:4, 2"
inkscape:connector-curvature="0" />
<path
id="path3897"
style="fill:none;stroke:#c8c8dc;stroke-width:2;stroke-linecap:round;stroke-dasharray:4, 2"
inkscape:connector-curvature="0"
d="m 58,383.36 602,2" />
<path
id="path3899"
d="m 58,451.36 602,2"
style="fill:none;stroke:#c8c8dc;stroke-width:2;stroke-linecap:round"
inkscape:connector-curvature="0" />
<path
id="path3901"
style="fill:none;stroke:#c8c8dc;stroke-width:2;stroke-linecap:round;stroke-dasharray:4, 2"
inkscape:connector-curvature="0"
d="m 58,521.36 602,2" />
<path
id="path3903"
d="M 75.941,567.42 78.059,63.3"
style="fill:none;stroke:#c8c8dc;stroke-width:1.883;stroke-linecap:round;stroke-dasharray:3.76596, 1.88298"
inkscape:connector-curvature="0" />
<path
id="path3905"
style="fill:none;stroke:#c8c8dc;stroke-width:1.883;stroke-linecap:round;stroke-dasharray:3.76596, 1.88298"
inkscape:connector-curvature="0"
d="M 145.94,567.42 148.06,63.3" />
<path
id="path3907"
d="M 213.94,567.42 216.06,63.3"
style="fill:none;stroke:#c8c8dc;stroke-width:1.883;stroke-linecap:round;stroke-dasharray:3.76596, 1.88298"
inkscape:connector-curvature="0" />
<path
id="path3909"
style="fill:none;stroke:#c8c8dc;stroke-width:1.883;stroke-linecap:round;stroke-dasharray:3.76596, 1.88298"
inkscape:connector-curvature="0"
d="M 281.94,567.42 284.06,63.3" />
<path
id="path3911"
d="M 351.94,567.42 354.06,63.3"
style="fill:none;stroke:#c8c8dc;stroke-width:1.883;stroke-linecap:round;stroke-dasharray:3.76596, 1.88298"
inkscape:connector-curvature="0" />
<path
id="path3913"
style="fill:none;stroke:#c8c8dc;stroke-width:1.883;stroke-linecap:round;stroke-dasharray:3.76596, 1.88298"
inkscape:connector-curvature="0"
d="M 421.94,567.42 424.06,63.3" />
<path
id="path3915"
d="M 489.94,567.42 492.06,63.3"
style="fill:none;stroke:#c8c8dc;stroke-width:1.883;stroke-linecap:round"
inkscape:connector-curvature="0" />
<path
id="path3917"
style="fill:none;stroke:#c8c8dc;stroke-width:1.883;stroke-linecap:round;stroke-dasharray:3.76596, 1.88298"
inkscape:connector-curvature="0"
d="M 559.94,567.42 562.06,63.3" />
<path
id="path3919"
d="M 627.94,567.42 630.06,63.3"
style="fill:none;stroke:#c8c8dc;stroke-width:1.883;stroke-linecap:round;stroke-dasharray:3.76596, 1.88298"
inkscape:connector-curvature="0" />
<path
id="path3933"
d="m 62,355.36 122,2 c 0,0 28,-2 28,-46 0,-44 -6,20 16,32 22,12 48,14 48,26 0,12 6,26 6,26 1.5884,-77.994 2.8294,-154.48 20,-300 30.009,89.991 24.713,215.29 24,336 0.0644,0.0515 22.112,-102.55 34,-78 18.831,38.888 52,27.525 52,22 0,-0.11141 2,-8.1014 16,-38 22.019,-47.023 24,-44 24,-44 0,0 10,-6 28,38 18,44 26,24 30,28 4,4 12,14 22,-12 10,-26 36,2 36,2 0,0 8,26 38,12"
sodipodi:nodetypes="ccssscccscscssscc"
style="fill:none;stroke:#f0f0ff;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;filter:url(#filter3947)"
inkscape:connector-curvature="0" />
<path
id="path3923"
sodipodi:nodetypes="ccssscccscscssscc"
style="fill:none;stroke:#e6e6f5;stroke-width:20;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;filter:url(#filter3951)"
inkscape:connector-curvature="0"
d="m 62,355.36 122,2 c 0,0 28,-2 28,-46 0,-44 -6,20 16,32 22,12 48,14 48,26 0,12 6,26 6,26 1.5884,-77.994 2.8294,-154.48 20,-300 30.009,89.991 24.713,215.29 24,336 0.0644,0.0515 22.112,-102.55 34,-78 18.831,38.888 52,27.525 52,22 0,-0.11141 2,-8.1014 16,-38 22.019,-47.023 24,-44 24,-44 0,0 10,-6 28,38 18,44 26,24 30,28 4,4 12,14 22,-12 10,-26 36,2 36,2 0,0 8,26 38,12" />
</g>
</g>
<metadata
id="metadata63">
<rdf:RDF>
<cc:Work>
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<cc:license
rdf:resource="http://creativecommons.org/publicdomain/zero/1.0/" />
<dc:publisher>
<cc:Agent
rdf:about="http://openclipart.org/">
<dc:title>Openclipart</dc:title>
</cc:Agent>
</dc:publisher>
<dc:title>monitoring</dc:title>
<dc:date>2011-02-16T19:15:54</dc:date>
<dc:description>Iconos para monitoreo de proyectos</dc:description>
<dc:source>https://openclipart.org/detail/119695/monitoring-by-jabernal</dc:source>
<dc:creator>
<cc:Agent>
<dc:title>jabernal</dc:title>
</cc:Agent>
</dc:creator>
<dc:subject>
<rdf:Bag>
<rdf:li>búsqueda</rdf:li>
<rdf:li>monitoreo</rdf:li>
<rdf:li>monitoring</rdf:li>
<rdf:li>project</rdf:li>
<rdf:li>proyecto</rdf:li>
<rdf:li>search</rdf:li>
</rdf:Bag>
</dc:subject>
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/publicdomain/zero/1.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
</cc:License>
</rdf:RDF>
</metadata>
</svg>

After

Width:  |  Height:  |  Size: 15 KiB

BIN
img/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

View File

@ -1,8 +1,8 @@
{
"name": "Monito",
"description": "Checks various monitoring websites",
"description": "Checks for various monitoring servers (Icinga & Icinga2 at the moment)",
"uuid": "monito@drieu.org",
"shell-version": [
"3.38"
"42"
]
}

15
package.json Normal file
View File

@ -0,0 +1,15 @@
{
"name": "monito",
"version": "1.0.0",
"description": "Monito extension for gnome-shell",
"main": "extension.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "ssh://gitea@forge.chapril.org:222/Monito/monito.git"
},
"author": "Benjamin Drieu",
"license": "GPL-2.0-or-later"
}

643
prefs.js
View File

@ -23,12 +23,15 @@
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Gdk = imports.gi.Gdk;
const GObject = imports.gi.GObject;
// It's common practice to keep GNOME API and JS imports in separate blocks
const Lang = imports.lang;
const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
const Convenience = Me.imports.convenience;
const Mainloop = imports.mainloop;
const Gettext = imports.gettext.domain('monito');
const _ = Gettext.gettext;
@ -36,21 +39,69 @@ const N_ = function (e) {
return e;
};
const SETTINGS_SCHEMA = "org.gnome.shell.extensions.monito@drieu.org";
const SETTINGS_SCHEMA_ACCOUNT = "org.gnome.shell.extensions.monito@drieu.org.account";
const SETTINGS_SCHEMA_ACCOUNT_PATH = "/org/gnome/shell/extensions/monito@drieu.org/account";
const SETTINGS_SCHEMA = "org.gnome.shell.extensions.monito";
const SETTINGS_SCHEMA_ACCOUNT = "org.gnome.shell.extensions.monito.account";
const SETTINGS_SCHEMA_ACCOUNT_PATH = "/org/gnome/shell/extensions/monito/account";
const prefs = [ { type: Gtk.Entry, label: _('Name'), key: 'name' },
{ type: Gtk.ComboBoxText, label: _('Type'), key: 'type' },
{ type: Gtk.Entry, label: _('Username'), key: 'username' },
{ type: Gtk.Entry, label: _('Password'), key: 'password' },
{ type: Gtk.Entry, label: _('URL CGI'), key: 'urlcgi' },
];
const column_definitions = {
status: { label: _('Status'), width: 50, expand: false, },
host_name: { label: _('Host name'), width: 300, expand: false, },
service_display_name: { label: _('Service'), width: 300, expand: false, },
has_been_acknowledged: { label: _('Ack'), width: 50, expand: false },
last_check: { label: _('Last check'), width: 200, expand: false, type: 'date' },
next_check: { label: _('Next check'), width: 200, expand: false, type: 'date' },
last_state_change: { label: _('Last state changed'), width: 200, expand: false, type: 'date' },
attempts: { label: _('Attempts'), width: 50, expand: false, },
status_information: { label: _('Test result'), width: 600, expand: false, },
output: { label: _('Output'), width: 600, expand: false, },
actions: { label: 'Actions', width: 50, expand: false, special: 'actions' },
};
const prefs = [
{ type: Gtk.Switch, category: 'Settings', label: _('Active (restart for effect)'), key: 'active', align: Gtk.Align.START },
{ type: Gtk.Entry, category: 'Settings', label: _('Name'), key: 'name' },
{ type: Gtk.ComboBoxText, category: 'Settings', label: _('Type'), key: 'type' },
{ type: Gtk.Entry, category: 'Settings', label: _('Username'), key: 'username' },
{ type: Gtk.Entry, category: 'Settings', label: _('Password'), key: 'password' },
{ type: Gtk.Entry, category: 'Settings', label: _('Web URL'), key: 'url' },
{ type: Gtk.Entry, category: 'Settings', label: _('CGI / API URL'), key: 'urlcgi' },
{ type: Gtk.Entry, category: 'Settings', label: _('Proxy URL'), key: 'proxy' },
{ type: Gtk.Switch, category: 'Settings', label: _('Check SSL certificate'), key: 'strict-ssl', align: Gtk.Align.START },
{ type: Gtk.ColorButton, category: 'Colors', label: _('OK background color'), key: 'ok-color', align: Gtk.Align.START },
{ type: Gtk.ColorButton, category: 'Colors', label: _('Warning background color'), key: 'warning-color', align: Gtk.Align.START },
{ type: Gtk.ColorButton, category: 'Colors', label: _('Critical background color'), key: 'critical-color', align: Gtk.Align.START },
{ type: Gtk.ColorButton, category: 'Colors', label: _('Unknown background color'), key: 'unknown-color', align: Gtk.Align.START },
{ type: Gtk.ColorButton, category: 'Colors', label: _('Pending background color'), key: 'pending-color', align: Gtk.Align.START },
{ type: Gtk.ColorButton, category: 'Colors', label: _('OK color'), key: 'ok-fg', align: Gtk.Align.START },
{ type: Gtk.ColorButton, category: 'Colors', label: _('Warning color'), key: 'warning-fg', align: Gtk.Align.START },
{ type: Gtk.ColorButton, category: 'Colors', label: _('Critical color'), key: 'critical-fg', align: Gtk.Align.START },
{ type: Gtk.ColorButton, category: 'Colors', label: _('Unknown color'), key: 'unknown-fg', align: Gtk.Align.START },
{ type: Gtk.ColorButton, category: 'Colors', label: _('Pending color'), key: 'pending-fg', align: Gtk.Align.START },
{ type: Gtk.Switch, category: 'Filters', label: _('Do <b>not</b> display acknowledged services'), key: 'acknowledged-filter-out', align: Gtk.Align.START },
{ type: Gtk.Entry, category: 'Filters', label: _('Only display hosts matching'), key: 'host-grep' },
{ type: Gtk.Entry, category: 'Filters', label: _('Only display services matching'), key: 'service-grep' },
{ type: Gtk.Entry, category: 'Filters', label: _('Only display status info matching'), key: 'status-info-grep' },
{ type: Gtk.Entry, category: 'Filters', label: _('Do <b>not</b> display hosts matching'), key: 'host-filter-out' },
{ type: Gtk.Entry, category: 'Filters', label: _('Do <b>not</b> display services matching'), key: 'service-filter-out' },
{ type: Gtk.Entry, category: 'Filters', label: _('Do <b>not</b> display status info matching'), key: 'status-info-filter-out' },
{ type: Gtk.Entry, category: 'Replacements', label: _('Host regexp ...'), key: 'host-match' },
{ type: Gtk.Entry, category: 'Replacements', label: _('... to replace with'), key: 'host-replace' },
{ type: Gtk.Entry, category: 'Replacements', label: _('Service regexp ...'), key: 'service-match' },
{ type: Gtk.Entry, category: 'Replacements', label: _('... to replace with'), key: 'service-replace' },
{ type: Gtk.Entry, category: 'Replacements', label: _('Status info regexp ...'), key: 'status-info-match' },
{ type: Gtk.Entry, category: 'Replacements', label: _('... to replace with'), key: 'status-info-replace' },
];
// Like 'extension.js' this is used for any one-time setup like translations.
function init() {
// log('initializing ${Me.metadata.name} Preferences');
monitoLog('initializing ${Me.metadata.name} Preferences');
this.settings = ExtensionUtils.getSettings(SETTINGS_SCHEMA);
this.gtkVersion = Gtk.get_major_version();
// monitoLog ( 'GTK version is ' + this.gtkVersion );
}
@ -61,123 +112,236 @@ function buildPrefsWidget() {
// Copy the same GSettings code from `extension.js`
this.settings = ExtensionUtils.getSettings(SETTINGS_SCHEMA);
this._columnsStores = { };
let mainVbox = new Gtk.Box( { orientation: Gtk.Orientation.VERTICAL } );
let mainWidget = new Gtk.Notebook( { } );
let prefsWidget = new Gtk.Grid({
margin: 18,
column_spacing: 12,
row_spacing: 12,
column_homogeneous: false,
});
if ( this.gtkVersion < 4 )
prefsWidget.margin = 18;
this.prefWidgets = { };
// Add a simple title and add it to the prefsWidget
let title = new Gtk.Label({
label: `<b>${Me.metadata.name} Preferences</b>`,
label: '<b>' + _('Monito Preferences') + '</b>',
halign: Gtk.Align.START,
use_markup: true,
});
prefsWidget.attach(title, 0, 0, 2, 1);
let _label = new Gtk.Label({
label: _('Poll delay in seconds'),
visible: true,
halign: Gtk.Align.START,
});
prefsWidget.attach ( _label, 0, 1, 1, 1 );
let _entry = new Gtk.SpinButton ({
numeric: true,
halign: Gtk.Align.FILL,
visible: true,
hexpand: true,
can_focus: true,
});
_entry.set_range ( 1, 1000 );
_entry.set_increments ( 1, 5 );
prefsWidget.attach ( _entry, 1, 1, 1, 1 );
this.settings.bind ( 'poll-delay', _entry, 'value', Gio.SettingsBindFlags.DEFAULT );
_label = new Gtk.Label({
label: _('Web browser'),
visible: true,
halign: Gtk.Align.START,
});
prefsWidget.attach ( _label, 0, 2, 1, 1 );
_entry = new Gtk.Entry ({
halign: Gtk.Align.FILL,
visible: true,
hexpand: true,
can_focus: true,
});
prefsWidget.attach ( _entry, 1, 2, 1, 1 );
this.settings.bind ( 'web-browser', _entry, 'text', Gio.SettingsBindFlags.DEFAULT );
// Misc Settings (TBD)
mainWidget.append_page ( prefsWidget,
new Gtk.Label ( { label: 'General', } ) );
new Gtk.Label ( { label: _('General'), } ) );
// Accounts
let accountsWidgetContainer = new Gtk.Box( { orientation: Gtk.Orientation.HORIZONTAL,
homogeneous: false, } );
mainWidget.append_page ( accountsWidgetContainer,
new Gtk.Label ( { label: 'Servers', } ) );
this._accountsWidgetContainer = new Gtk.Box( { orientation: Gtk.Orientation.HORIZONTAL,
homogeneous: false, } );
mainWidget.append_page ( this._accountsWidgetContainer,
new Gtk.Label ( { label: _('Servers'), } ) );
let accountsChooserContainer = new Gtk.Box( { orientation: Gtk.Orientation.VERTICAL,
homogeneous: false, } );
accountsWidgetContainer.pack_start(accountsChooserContainer, true, true, 0);
if ( this.gtkVersion == 4 )
this._accountsWidgetContainer.append(accountsChooserContainer, true, true, 0);
else
this._accountsWidgetContainer.add(accountsChooserContainer);
let accountsChooser = new Gtk.ListBox ( { valign: Gtk.Align.FILL,
this.accountsChooser = new Gtk.ListBox ( { valign: Gtk.Align.FILL,
hexpand: true,
vexpand: true } );
accountsChooserContainer.pack_start(accountsChooser, true, true, 0);
if ( this.gtkVersion == 4 )
accountsChooserContainer.append(this.accountsChooser, true, true, 0);
else
accountsChooserContainer.add(this.accountsChooser);
// Account list
for ( var server of this.settings.get_string ( 'servers' ) . split ( ',' ) )
{
let _account_settings = getAccountSettings ( server );
let row = new Gtk.ListBoxRow ( { hexpand: true,
halign: Gtk.Align.FILL } );
row.server= server
let _label = new Gtk.Label ( { label: _account_settings.get_string('name'),
hexpand: true,
margin: 5,
halign: Gtk.Align.START,
expand: true } );
row.add ( _label );
_label.set_data ( 'server', GLib.strdup(server) );
accountsChooser.add ( row );
}
accountsChooser.connect('row-activated', Lang.bind(this, function () {
let _row = accountsChooser.get_selected_row();
log('Active:' + _row.server);
let _account_settings = getAccountSettings ( _row.server );
for ( var server_id of this.getServersList ( ) )
this.addAccountLine ( server_id );
this.accountsChooser.connect ( 'row-activated', Lang.bind ( this, this.activateAccountRow ) );
for ( var prefEntry of prefs )
{
if ( prefEntry.type == Gtk.Entry )
{
// How to unbind previous one?
_account_settings.bind (
prefEntry.key,
this.prefWidgets[prefEntry.key],
'text',
Gio.SettingsBindFlags.DEFAULT
);
}
else if ( prefEntry.type == Gtk.ComboBoxText )
{
this.prefWidgets[prefEntry.key].set_active(_account_settings.get_enum('type'));
this.prefWidgets[prefEntry.key].connect('changed', Lang.bind(this, function () {
log('Active:' + this.prefWidgets[prefEntry.key].get_active());
_account_settings.set_enum('type', this.prefWidgets[prefEntry.key].get_active());
}));
}
}
// Action Bar
let accountsChooserActionBar = new Gtk.ActionBar ( { valign: Gtk.Align.END } );
this.addToContainer ( accountsChooserContainer, accountsChooserActionBar );
let accountCreateButton = Gtk.Button.new_from_icon_name ( 'list-add',
Gtk.IconSize.BUTTON );
accountsChooserActionBar.pack_start(accountCreateButton, true);
//this.addToContainer ( accountsChooserActionBar, accountCreateButton );
accountCreateButton.connect ( 'clicked', Lang.bind ( this, this.createAccount ) );
let accountRemoveButton = Gtk.Button.new_from_icon_name ( 'list-remove',
Gtk.IconSize.BUTTON );
accountsChooserActionBar.pack_start(accountRemoveButton, true);
// this.addToContainer ( accountsChooserActionBar, accountRemoveButton );
accountRemoveButton.connect ( 'clicked', Lang.bind ( this, this.removeAccount ) );
this.createAccountWidgets ( false ) ;
this.addToContainer ( mainVbox, mainWidget );
if ( this.gtkVersion == 4 )
mainVbox.set_visible(true);
else
mainVbox.show_all();
return mainVbox;
}
function getServersList ( )
{
if ( ! this.settings )
this.settings = ExtensionUtils.getSettings(SETTINGS_SCHEMA);
return this.settings.get_string ( 'servers' ) . split ( ',' );
}
function getAccountSettings ( id )
{
let _path = SETTINGS_SCHEMA_ACCOUNT_PATH + '/' + id + '/';
return Convenience.getSettings(SETTINGS_SCHEMA_ACCOUNT, _path);
}
function getSortOrder ( server )
{
return this.getAccountSettings ( server ) . get_strv ( 'columns-order' );
}
function setSortOrder ( server, sort_order )
{
return this.getAccountSettings ( server ) . set_strv ( 'columns-order', sort_order );
}
function getColumns ( server )
{
let _columns = this.getAccountSettings ( server ) . get_strv ( 'columns' );
let _columnsSizes = this.getAccountSettings ( server ) . get_strv ( 'columns-size' );
const zip = (a1, a2) => a1.map((x, i) => { return { name: x, size: a2[i] } } );
let _result = zip ( _columns, _columnsSizes );
return _result;
}
function setColumns ( server, columns )
{
return this.getAccountSettings ( server ) . set_strv ( 'columns', columns );
}
function createAccountWidgets ( isActive )
{
this.prefWidgets = { };
this._accountsWidget = new Gtk.Notebook( { } );
if ( this.gtkVersion == 4 )
this._accountsWidgetContainer.append(this._accountsWidget, true, true, 0);
else
this._accountsWidgetContainer.add(this._accountsWidget);
for ( var _tab of [ 'Settings', 'Columns', 'Colors', 'Filters', 'Replacements' ] )
{
if ( _tab != 'Columns' )
this.createPrefWidgets ( _accountsWidget, _tab, isActive );
else
this.createColumnsPrefTab ( _accountsWidget, _tab, isActive );
}
// Settings
this.prefWidgets['name'].connect('changed', Lang.bind(this, function () {
let _row = this.accountsChooser.get_selected_row();
_row.get_child().label = this.prefWidgets['name'].text;
} ) );
if ( this.gtkVersion == 4 )
this._accountsWidget.set_visible(true);
else
this._accountsWidget.show_all();
}
let accountsChooserActionBar = new Gtk.ActionBar ( { visible: true,
valign: Gtk.Align.END,
vexpand: false} );
accountsChooserContainer.pack_start(accountsChooserActionBar, true, true, 0);
let accountsWidget = new Gtk.Grid ( {
function createPrefWidgets ( noteBook, type, isActive )
{
let grid = new Gtk.Grid ( {
halign: Gtk.Align.FILL,
margin: 18,
// margin: 18,
column_spacing: 12,
row_spacing: 12,
visible: true,
column_homogeneous: false,
});
accountsWidgetContainer.pack_start(accountsWidget, true, true, 0);
if ( this.gtkVersion < 4 )
grid.margin = 18;
noteBook.append_page ( grid, new Gtk.Label ( { label: _(type), } ) );
let y = 1;
let y = 0;
for ( var prefEntry of prefs )
{
if ( prefEntry.category != type )
continue;
let _label = new Gtk.Label({
label: prefEntry.label + ':',
visible: true,
halign: Gtk.Align.START,
use_markup: true,
});
accountsWidget.attach ( _label, 0, y, 1, 1 );
grid.attach ( _label, 0, y, 1, 1 );
this.prefWidgets[prefEntry.key] = new prefEntry.type({
halign: Gtk.Align.FILL,
this.prefWidgets[prefEntry.key] = new prefEntry.type ( {
halign: ( prefEntry.align ? prefEntry.align : Gtk.Align.FILL ),
visible: true,
hexpand: true,
});
can_focus: isActive,
} );
let boundValue = 'text';
if ( prefEntry.key == 'password' )
@ -187,12 +351,14 @@ function buildPrefsWidget() {
}
else if ( prefEntry.key == 'type' )
{
[ 'Icinga', 'Icinga2' ].forEach((item) => {
this.prefWidgets[prefEntry.key].append_text(item);
[ { name: 'Icinga', value: 'Icinga server' },
{ name: 'Icinga2API', value: 'Icinga2 server (using API, prefered)' },
{ name: 'Icinga2', value: 'Icinga2 server (using Icingaweb2, limited)' } ].forEach((item) => {
this.prefWidgets[prefEntry.key].insert ( -1, item.name, item.value );
} );
}
accountsWidget.attach(this.prefWidgets[prefEntry.key], 1, y, 1, 1);
grid.attach(this.prefWidgets[prefEntry.key], 1, y, 1, 1);
if ( prefEntry.type != Gtk.ComboBoxText )
{
@ -205,16 +371,327 @@ function buildPrefsWidget() {
}
y++;
}
mainVbox.pack_start(mainWidget, true, true, 0);
mainVbox.show_all();
return mainVbox;
}
function getAccountSettings ( id )
function createColumnsPrefTab ( noteBook, type, isActive )
{
let _path = SETTINGS_SCHEMA_ACCOUNT_PATH + '/' + id;
return Convenience.getSettings(SETTINGS_SCHEMA_ACCOUNT, _path);
let grid = new Gtk.Grid ( {
halign: Gtk.Align.FILL,
// margin: 18,
column_spacing: 12,
row_spacing: 12,
visible: true,
column_homogeneous: false,
});
if ( this.gtkVersion < 4 )
grid.margin = 18;
noteBook.append_page ( grid, new Gtk.Label ( { label: _(type), } ) );
this.treeView = new Gtk.TreeView ( { headers_visible: true,
reorderable: true,
hexpand: true,
vexpand: true });
let columnNumbers = new Gtk.TreeViewColumn ( { title: _("Column") } );
this.ColumnNameRenderer = new Gtk.CellRendererCombo ( { editable: true,
has_entry: false,
text_column: 0 } );
this.ColumnNameRenderer.connect("edited", onComboChanged)
this.ColumnNameRenderer.col = 0;
columnNumbers.pack_start(this.ColumnNameRenderer, true);
columnNumbers.add_attribute(this.ColumnNameRenderer, 'text', 0);
this.treeView.append_column(columnNumbers);
this.ColumnNameRenderer.connect('edited', Lang.bind ( this, this.editColumn ) );
let _colSize = new Gtk.TreeViewColumn ( { title: _("Size") } );
let _colRenderer = new Gtk.CellRendererSpin ( { adjustment: new Gtk.Adjustment ( { lower: 0, upper: 999, step_increment: 1, page_increment: 10 } ),
editable: true } );
// if ( this.gtkVersion == 4 )
// _colSize.append(_colRenderer, true);
// else
_colSize.pack_start(_colRenderer, true);
_colSize.add_attribute(_colRenderer, 'text', 1);
this.treeView.append_column(_colSize);
_colRenderer.col = 1;
_colRenderer.connect('edited', Lang.bind ( this, this.editColumn ) );
// _treeView.connect('row-activated', this._editPath.bind(this));
grid.attach ( this.treeView, 0, 1, 1, 1 );
// Action Bar
let rowsChooserActionBar = new Gtk.ActionBar ( { valign: Gtk.Align.END } );
let rowCreateButton = Gtk.Button.new_from_icon_name ( 'list-add',
Gtk.IconSize.BUTTON );
rowsChooserActionBar.pack_start(rowCreateButton, true);
// rowsChooserActionBar.add ( rowCreateButton );
rowCreateButton.connect ( 'clicked', Lang.bind ( this, this.createColumnRow ) );
let rowRemoveButton = Gtk.Button.new_from_icon_name ( 'list-remove',
Gtk.IconSize.BUTTON );
rowsChooserActionBar.pack_start(rowRemoveButton, true);
// rowsChooserActionBar.add ( rowRemoveButton );
rowRemoveButton.connect ( 'clicked', Lang.bind ( this, this.removeColumnRow ) );
grid.attach ( rowsChooserActionBar, 0, 2, 1, 1 );
}
function onComboChanged(widget, path, text)
{
monitoLog ( widget );
monitoLog ( path );
monitoLog ( text );
}
function activateAccountRow ( ) {
if ( this._accountsWidget )
this._accountsWidgetContainer.remove ( this._accountsWidget );
this.createAccountWidgets ( true );
this.store = new Gtk.ListStore();
this.store.set_column_types([GObject.TYPE_STRING, GObject.TYPE_INT]);
this.store.filter_new(null);
this.treeView.model = this.store;
let _row = this.accountsChooser.get_selected_row();
let _account_settings = getAccountSettings ( _row.server );
this.columnsModel = new Gtk.ListStore ( );
this.columnsModel.set_column_types([GObject.TYPE_STRING,GObject.TYPE_STRING]);
for ( let [ _colName, _colDef ] of Object.entries(column_definitions) )
{
let _iter = this.columnsModel.append();
this.columnsModel.set_value(_iter, 1, _colName );
this.columnsModel.set_value(_iter, 0, _colDef.label );
}
this.ColumnNameRenderer.model = this.columnsModel;
if ( ! _account_settings )
return;
let _columns = _account_settings . get_strv ( 'columns' );
let _columnsSizes = _account_settings . get_strv ( 'columns-size' );
for ( var i in _columns )
{
let _iter = this.store.append();
this.store.set_value(_iter, 0, getEntryLabel ( _columns [ i ] ) );
this.store.set_value(_iter, 1, parseInt(_columnsSizes [ i ]) );
}
for ( var prefEntry of prefs )
{
if ( prefEntry.type == Gtk.Entry )
{
_account_settings.bind (
prefEntry.key,
this.prefWidgets[prefEntry.key],
'text',
Gio.SettingsBindFlags.DEFAULT
);
}
else if ( prefEntry.type == Gtk.Switch )
{
_account_settings.bind (
prefEntry.key,
this.prefWidgets[prefEntry.key],
'active',
Gio.SettingsBindFlags.DEFAULT
);
}
else if ( prefEntry.type == Gtk.ColorButton )
{
let _color = new Gdk.RGBA ( );
_color.parse (_account_settings.get_string(prefEntry.key) );
this.prefWidgets[prefEntry.key].set_use_alpha ( false );
this.prefWidgets[prefEntry.key].set_rgba ( _color );
this.prefWidgets[prefEntry.key].connect('color-set', Lang.bind ( { key: prefEntry.key, settings: _account_settings }, setColor ) );
}
else if ( prefEntry.type == Gtk.ComboBoxText )
{
this.prefWidgets[prefEntry.key].set_active(_account_settings.get_enum('type'));
this.prefWidgets[prefEntry.key].connect('changed', Lang.bind(this, function (e) {
let _account_settings = getAccountSettings ( _row.server );
_account_settings.set_enum('type', this.prefWidgets['type'].get_active());
}));
}
}
}
function addAccountLine ( server_id )
{
monitoLog ( '> Add line ' + server_id );
let _account_settings = getAccountSettings ( server_id );
let row = new Gtk.ListBoxRow ( { hexpand: true,
halign: Gtk.Align.FILL } );
row.server = server_id;
let _label = new Gtk.Label ( { label: _account_settings.get_string('name'),
hexpand: true,
// margin: 5,
halign: Gtk.Align.START } );
if ( this.gtkVersion == 4 )
{
row.set_child ( _label );
this.accountsChooser.append ( row );
}
else
{
_label.margin = 5;
row.add ( _label );
this.accountsChooser.add ( row );
if ( this.gtkVersion >= 4 )
this.accountsChooser.set_visible(true);
else
this.accountsChooser.show_all();
}
}
function createAccount ( ) {
monitoLog ( '> Create Account' );
let _servers = this.getServersList ( );
let _max = Math.max ( ..._servers );
let _server_id = _max + 1;
_servers.push ( _server_id );
let _account_settings = getAccountSettings ( _server_id );
_account_settings.set_string ( 'name', _('New server #') + _server_id );
this.addAccountLine ( _max + 1 );
this.settings.set_string ( 'servers', _servers.join(',') );
}
function removeAccount ( ) {
let _row = this.accountsChooser.get_selected_row();
monitoLog('Active:' + _row.server);
let _servers = this.getServersList ( );
_servers.splice ( _servers.indexOf ( _row.server ), 1 );
monitoLog ( _servers );
if ( ! _row )
return;
_row.destroy();
this.settings.set_string ( 'servers', _servers . join ( ',' ) );
}
function setColor ( color )
{
monitoLog ( 'Color ' + + ': ' + color );
let _output = '#%02x%02x%02x'.format(
255 * color.get_rgba().red,
255 * color.get_rgba().green,
255 * color.get_rgba().blue );
this.settings.set_string('' + this.key, _output);
}
function createColumnRow ( ) {
let _item = this.store.append();
this.store.set_value(_item, 0, 'status' );
this.store.set_value(_item, 1, 300 );
let _row = this.accountsChooser.get_selected_row();
let _account_settings = getAccountSettings ( _row.server );
let prefKey;
let _defs = _account_settings . get_strv ( 'columns' );
_defs.push ( 'status' );
_account_settings . set_strv ( 'columns', _defs );
_defs = _account_settings . get_strv ( 'columns-size' );
_defs.push ( '300' );
_account_settings . set_strv ( 'columns-size', _defs );
}
function removeColumnRow ( widget ) {
let [any, model, _iter] = this.treeView.get_selection().get_selected ( );
let _row = this.accountsChooser.get_selected_row();
let _account_settings = getAccountSettings ( _row.server );
let prefKey;
for ( var foo of this.treeView.get_selection().get_selected_rows()[0] )
{
let _defs = _account_settings . get_strv ( 'columns' );
_defs.splice ( foo.get_indices ( ), 1 );
_account_settings . set_strv ( 'columns', _defs );
_defs = _account_settings . get_strv ( 'columns-size' );
_defs.splice ( foo.get_indices ( ), 1 );
_account_settings . set_strv ( 'columns-size', _defs );
}
this.store.remove ( _iter );
}
function getEntryName ( text )
{
for ( let [ _colName, _colDef ] of Object.entries(column_definitions) )
if ( _colDef.label == text )
return _colName;
return 'N/A';
}
function getEntryLabel ( text )
{
return column_definitions[text].label;
}
function editColumn ( widget, path, text )
{
let _row = this.accountsChooser.get_selected_row();
let _account_settings = getAccountSettings ( _row.server );
let prefKey;
let _iter = this.store.get_iter ( Gtk.TreePath.new_from_string ( path ) );
if ( widget.col == 1 )
{
this.store.set_value(_iter[1], widget.col, parseInt(text) );
prefKey = 'columns-size';
}
else
{
this.store.set_value ( _iter[1], widget.col, text );
text = getEntryName ( text );
prefKey = 'columns';
}
let _defs = _account_settings . get_strv ( prefKey );
_defs [ parseInt(path) ] = text;
_account_settings . set_strv ( prefKey, _defs );
return true;
}
function addToContainer ( parent, child )
{
if ( this.gtkVersion == 4 )
parent.append(child, true, true, 0);
else
parent.add(child);
}
function monitoLog ( msg )
{
log ( 'Monito: ' + msg );
}

View File

@ -20,39 +20,52 @@
-->
<schemalist >
<enum id="org.gnome.shell.extensions.monito@drieu.org.MonitoringType">
<enum id="org.gnome.shell.extensions.monito.MonitoringType">
<value value="0" nick="Icinga"/>
<value value="1" nick="Icinga2"/>
<value value="1" nick="Icinga2API"/>
<value value="2" nick="Icinga2"/>
</enum>
<enum id="org.gnome.shell.extensions.monito@drieu.org.DisplayType">
<enum id="org.gnome.shell.extensions.monito.DisplayType">
<value value="0" nick="Short"/>
<value value="1" nick="Long"/>
</enum>
<!-- Main Schema -->
<schema id="org.gnome.shell.extensions.monito@drieu.org" path="/org/gnome/shell/extensions/monito/">
<schema id="org.gnome.shell.extensions.monito" path="/org/gnome/shell/extensions/monito/">
<child name="account" schema="org.gnome.shell.extensions.monito@drieu.org.account"/>
<child name="account" schema="org.gnome.shell.extensions.monito.account"/>
<key name="servers" type="s">
<default>'0'</default>
</key>
<key name="display-type" enum="org.gnome.shell.extensions.monito@drieu.org.DisplayType">
<key name="display-type" enum="org.gnome.shell.extensions.monito.DisplayType">
<default>"Short"</default>
</key>
<key name="poll-delay" type="i">
<default>300</default>
</key>
<key name="web-browser" type="s">
<default>"/usr/bin/firefox"</default>
</key>
</schema>
<!-- Account list -->
<schema id="org.gnome.shell.extensions.monito@drieu.org.account">
<schema id="org.gnome.shell.extensions.monito.account">
<key name="active" type="b">
<default>true</default>
</key>
<key name="icon" type="s">
<default>''</default>
</key>
<key name="type" enum="org.gnome.shell.extensions.monito@drieu.org.MonitoringType">
<key name="type" enum="org.gnome.shell.extensions.monito.MonitoringType">
<default>"Icinga"</default>
</key>
@ -68,13 +81,126 @@
<default>''</default>
</key>
<key name="url" type="s">
<default>''</default>
</key>
<key name="urlcgi" type="s">
<default>''</default>
</key>
<key name="timeoutinterval" type="i">
<default>1</default>
<key name="proxy" type="s">
<default>''</default>
</key>
<key name="strict-ssl" type="b">
<default>true</default>
</key>
<key name="ok-color" type="s">
<default>'#00cc33'</default>
</key>
<key name="warning-color" type="s">
<default>'#ffa500'</default>
</key>
<key name="critical-color" type="s">
<default>'#ff3300'</default>
</key>
<key name="unknown-color" type="s">
<default>'#e496f5'</default>
</key>
<key name="pending-color" type="s">
<default>'#aaaaaa'</default>
</key>
<key name="ok-fg" type="s">
<default>'#ffffff'</default>
</key>
<key name="warning-fg" type="s">
<default>'#ffffff'</default>
</key>
<key name="critical-fg" type="s">
<default>'#ffffff'</default>
</key>
<key name="unknown-fg" type="s">
<default>'#ffffff'</default>
</key>
<key name="pending-fg" type="s">
<default>'#ffffff'</default>
</key>
<key name="columns" type="as">
<default>['status','host_name','service_display_name','has_been_acknowledged','last_check','attempts','status_information','actions']</default>
</key>
<key name="columns-size" type="as">
<default>[ '50', '300', '300', '50', '200', '50', '600', '50' ]</default>
</key>
<key name="columns-order" type="as">
<default>['status+', 'host_name+','service_display_name+']</default>
</key>
<key name="host-grep" type="s">
<default>''</default>
</key>
<key name="service-grep" type="s">
<default>''</default>
</key>
<key name="status-info-grep" type="s">
<default>''</default>
</key>
<key name="acknowledged-filter-out" type="b">
<default>false</default>
</key>
<key name="host-filter-out" type="s">
<default>''</default>
</key>
<key name="service-filter-out" type="s">
<default>''</default>
</key>
<key name="status-info-filter-out" type="s">
<default>''</default>
</key>
<key name="host-match" type="s">
<default>''</default>
</key>
<key name="host-replace" type="s">
<default>''</default>
</key>
<key name="service-match" type="s">
<default>''</default>
</key>
<key name="service-replace" type="s">
<default>''</default>
</key>
<key name="status-info-match" type="s">
<default>''</default>
</key>
<key name="status-info-replace" type="s">
<default>''</default>
</key>
</schema>
</schemalist>

349
servers/genericserver.js Normal file
View File

@ -0,0 +1,349 @@
/* -*- mode: js; js-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
Monito Gnome-Shell extension
Copyright (C) 2021 Benjamin Drieu
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.
SPDX-License-Identifier: GPL-2.0-or-later
*/
const { Soup, Gio } = imports.gi;
const ExtensionUtils = imports.misc.extensionUtils;
const Lang = imports.lang;
const Main = imports.ui.main;
const Me = ExtensionUtils.getCurrentExtension();
const Preferences = Me.imports.prefs;
var GenericServer = class {
constructor ( _server, _extension, _serverType = 'Generic' )
{
// this.monitoLog ( '>>> New %s server #%s'.format ( _serverType, _server ) );
this._server = _server;
this._settings = Preferences.getAccountSettings ( this._server );
this._httpSession = null;
this._url = null;
this.extension = _extension;
this.canRecheck = true;
}
getServer ( )
{
return this._server;
}
buildURL ( )
{
// if ( ! this._settings )
// this.monitoLog ( 'monito build URL' );
this._settings = Preferences.getAccountSettings ( this._server );
if ( this.type != this._settings.get_string ( "type" ) ||
this.username != this._settings.get_string ( "username" ) ||
this.strict_ssl != this._settings.get_boolean ( "strict-ssl" ) ||
this.name != this._settings.get_string ( "name" ) ||
this.password != this._settings.get_string ( "password" ) ||
this.urlcgi != this._settings.get_string ( "urlcgi" ) ||
this.proxy != this._settings.get_string ( "proxy" ) )
{
this.type = this._settings.get_string ( "type" );
this.username = this._settings.get_string ( "username" );
this.strict_ssl = this._settings.get_boolean ( "strict-ssl" );
this.name = this._settings.get_string ( "name" );
this.password = this._settings.get_string ( "password" );
this.urlcgi = this._settings.get_string ( "urlcgi" );
this.proxy = this._settings.get_string ( "proxy" );
this._httpSession = null;
this.monitoLog ( 'Refreshing URL parameters' );
}
// this.monitoLog ( 'monito server >>> ' + this._server );
// this.monitoLog ( 'monito name >>> ' + this.name );
// this.monitoLog ( 'monito type >>> ' + this.type );
// this.monitoLog ( 'monito username >>> ' + this.username );
// this.monitoLog ( 'monito urlcgi >>> ' + this.urlcgi );
}
prepareHttp ( )
{
if ( this._httpSession == null ) {
this.monitoLog ( 'Preparing new HTTP with strict SSL ' + this.strict_ssl );
this._httpSession = new Soup.SessionSync();
this._httpSession.timeout = 5;
if ( this.proxy )
{
this.monitoLog ( 'monito Proxy ' + this.proxy + ' for ' + this.name );
let proxy = new Gio.SimpleProxyResolver ( { default_proxy: this.proxy } );
//let proxy = new Gio.SimpleProxyResolver ( { default_proxy: 'socks://127.0.0.1:3128' } );
this._httpSession.proxy_resolver = proxy;
}
// Soup.Session.prototype.add_feature.call(this._httpSession, new Soup.SimpleProxyResolver());
this._httpSession.ssl_strict = this.strict_ssl;
this._httpSession.user_agent = Me.metadata.uuid;
}
}
authenticateAndSend ( message, callback = this.handleMessage )
{
let auth = new Soup.AuthBasic()
auth.authenticate ( this.username, this.password );
message.request_headers.append ( "Authorization", auth.get_authorization ( message ) );
// this.monitoLog ( 'Sending message' );
this._httpSession.queue_message ( message, Lang.bind (this, callback ) );
}
handleMessage ( _httpSession, message )
{
this.status = { };
this.status.service_status = [ ];
this.error = null;
// this.monitoLog ( message.status_code );
// this.monitoLog ( message.response_body );
<<<<<<< HEAD
message.response_headers.foreach ((name, val) => {
// this.monitoLog (name, val);
});
=======
// message.response_headers.foreach ((name, val) => {
// this.monitoLog (name, val);
// });
>>>>>>> 0d8113e52b9d6e05fe8ad6a0c7dab045291ac987
// this.monitoLog ( message.response_body.data );
if ( message.status_code != Soup.Status.OK )
{
this.monitoLog ( '>>> Error: ' + message.reason_phrase );
//this.monitoLog ( '>>> Data: ' + message.data );
// TODO: add pref for that
// Main.notifyError ( 'Monito: ' + this.name,
// 'URL: ' + this.urlcgi + "\n" +
// message.status_code + ': ' + message.reason_phrase );
this.error = message.reason_phrase;
return null;
}
else
{
return message.response_body.data;
}
}
handleCMDMessage ( _httpSession, message )
{
let _data;
try {
_data = this.handleMessage ( _httpSession, message );
if ( this.error )
this.monitoLog ( 'Parent error ' + this.error );
else
{
// if error, grep for class='errorMessage'
// else grep for class='successBox'
// this.monitoLog ( 'Cmd output ' + _data );
}
}
catch ( e )
{
this.monitoLog ( e );
this.monitoLog ( _data );
}
if ( message.button )
this.extension.stopChildSpin ( message.button );
}
getProcessedStatus ( )
{
let status = this.status.service_status;
this.columns = Preferences.getColumns ( this._server );
this.sortOrder = Preferences.getSortOrder ( this._server );
status = this.filterStatus ( status );
status = status.sort ( Lang.bind ( this, this.compareServices ) );
return status;
}
filterStatus ( status )
{
let filters = [ { prefKey: 'service-grep',
entryKey: 'service_display_name',
positive: true },
{ prefKey: 'service-filter-out',
entryKey: 'service_display_name',
positive: false },
{ prefKey: 'host-grep',
entryKey: 'host_name',
positive: true },
{ prefKey: 'host-filter-out',
entryKey: 'host_name',
positive: false },
{ prefKey: 'status-info-grep',
entryKey: 'status_information',
positive: true },
{ prefKey: 'status-info-filter-out',
entryKey: 'status_information',
positive: false } ];
for ( var _filter of filters )
_filter.value = this._settings.get_string ( _filter.prefKey );
entries:
for ( var i = 0 ; i < status.length ; i ++ )
{
if ( status[i]['has_been_acknowledged'] && this._settings.get_boolean ( 'acknowledged-filter-out' ) )
{
// this.monitoLog ( '> ACKED:%s ' . format ( status[i]['service_display_name'] ) );
status.splice ( i, 1 );
i --; // This has been removed, so get back one step.
continue entries;
}
for ( var _filter of filters )
{
if ( _filter['value'] &&
( status[i][_filter['entryKey']].match ( new RegExp ( _filter['value'], 'i' ) ) <= 0 ) == _filter['positive'] )
{
status.splice ( i, 1 );
i --; // This has been removed, so get back one step.
continue entries;
}
}
}
return status;
}
compareServices ( a, b )
{
for ( let _comparison of this.sortOrder )
{
let _name = _comparison.substring ( 0, _comparison.length - 1 );
let _order = _comparison.substring ( _comparison.length - 1, _comparison.length );
if ( _name && _order && _name in a && _name in b )
{
if ( ! a [ _name ] )
return 1;
let _result = a [ _name ] . localeCompare ( b [ _name ] );
if ( _result != 0 )
{
if ( _order == '-' )
return - _result;
else
return _result;
}
}
}
return 0;
}
formatDate ( date )
{
if ( typeof date == 'string' || date instanceof String )
date = Date.parse ( date ) / 1000;
return this.timeAgo ( new Date ( date * 1000 ) );
}
getFormattedDate(date, prefomattedDate = false, hideYear = false)
{
const MONTH_NAMES = [
'Jan.', 'Feb.', 'Mar.', 'Apr.', 'May', 'Jun.',
'Jul.', 'Aug.', 'Sep.', 'Oct.', 'Nov.', 'Dec.'
];
const day = date.getDate();
const month = MONTH_NAMES[date.getMonth()];
const year = date.getFullYear();
const hours = date.getHours();
let minutes = date.getMinutes();
if (minutes < 10) {
// Adding leading zero to minutes
minutes = _(`0${ minutes }`);
}
if (prefomattedDate) {
// Today at 10:20
// Yesterday at 10:20
return _(`${ prefomattedDate } at ${ hours }:${ minutes }`);
}
if (hideYear) {
// 10. January at 10:20
return _(`${ month } ${ day } at ${ hours }:${ minutes }`);
}
// 10. January 2017. at 10:20
return _(`${ year } ${ month } ${ day }`);
}
timeAgo ( dateParam )
{
if (!dateParam) {
return null;
}
const date = typeof dateParam === 'object' ? dateParam : new Date(dateParam);
const DAY_IN_MS = 86400000; // 24 * 60 * 60 * 1000
const today = new Date();
const yesterday = new Date(today - DAY_IN_MS);
const seconds = Math.round((today - date) / 1000);
const minutes = Math.round(seconds / 60);
const isToday = today.toDateString() === date.toDateString();
const isYesterday = yesterday.toDateString() === date.toDateString();
const isThisYear = today.getFullYear() === date.getFullYear();
if (seconds < 5) {
return 'now';
} else if (seconds < 60) {
return `${ seconds } seconds ago`;
} else if (seconds < 90) {
return 'a minute ago';
} else if (minutes < 60) {
return `${ minutes } minutes ago`;
} else if (isToday) {
return this.getFormattedDate(date, 'today'); // Today at 10:20
} else if (isYesterday) {
return this.getFormattedDate(date, 'yesterday'); // Yesterday at 10:20
} else if (isThisYear) {
return this.getFormattedDate(date, false, true); // 10. January at 10:20
}
return this.getFormattedDate(date); // 10. January 2017. at 10:20
}
monitoLog ( msg )
{
log ( 'Monito: ' + msg ); // eslint-disable-line no-undef
}
}

118
servers/icinga.js Normal file
View File

@ -0,0 +1,118 @@
/* -*- mode: js; js-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
Monito Gnome-Shell extension
Copyright (C) 2021 Benjamin Drieu
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.
SPDX-License-Identifier: GPL-2.0-or-later
*/
const { Soup } = imports.gi;
const ExtensionUtils = imports.misc.extensionUtils;
const Lang = imports.lang;
const Main = imports.ui.main;
const Me = ExtensionUtils.getCurrentExtension();
const Preferences = Me.imports.prefs;
const GenericServer = Me.imports.servers.genericserver.GenericServer;
let _httpSession;
var Icinga = class extends GenericServer {
constructor ( _server, extension ) {
super(_server, extension, 'Icinga');
}
refresh ( ) {
this.buildURL ( );
this.prepareHttp ( );
}
prepareHttp ( )
{
super.prepareHttp ( );
let message = Soup.form_request_new_from_hash ( 'GET', this.urlcgi, { 'jsonoutput': '' } );
if ( message )
{
message.request_headers.append ( 'Accept', 'application/json' );
this.authenticateAndSend ( message );
}
}
recheck ( entry )
{
// TODO: perhaps use a better idea, like if the urlcgi param
// is a directory, then appendinstead of changing... use a method for that?
let cmdcgi = this.urlcgi.replace ( /status.cgi/, 'cmd.cgi' );
let d = new Date ( Date.now() );
var datestring = '%04d-%02d-%02d+%02d:%02d:%02d'.format ( d.getFullYear(), d.getMonth() + 1, d.getDate(),
d.getHours(), d.getMinutes(), d.getSeconds() );
// We have to do this manually since the default encoding of
// Soup.form_request_new_from_hash is not understood by Icinga ... a shame!
let params = 'cmd_typ=7&cmd_mod=2&host=%s&service=%s&start_time=%s&force_check=on&com_data=Recheck+by+Monito&btnSubmit=Commit' .
format ( encodeURI ( entry.real_host_name ),
encodeURI ( entry.real_service_display_name ),
encodeURI ( datestring ) );
let message = Soup.form_request_new_from_hash ( 'POST', cmdcgi, { } );
message.request_body.truncate();
message.request_body.append ( params );
message.request_body.flatten();
message.button = entry.button;
this.authenticateAndSend ( message, this.handleCMDMessage );
}
handleMessage ( _httpSession, message )
{
let _data;
try {
_data = super.handleMessage ( _httpSession, message );
if ( this.error )
log ( 'Parent error ' + this.error );
else
{
let json = JSON.parse ( _data );
this.status = json.status;
log ( this.status );
for ( var entry of this.status.service_status )
entry.last_check = this.formatDate ( entry.last_check );
}
this.extension.refreshUI ( this );
return ! this.error;
}
catch ( e )
{
log ( e );
log ( _data );
}
}
getUrlForService ( service )
{
return '%s?type=2&host=%s&service=%s'.format ( this._settings.get_string ( 'urlcgi' ).replace ( /status.cgi/, 'extinfo.cgi' ),
encodeURI ( service.real_host_name ),
encodeURI ( service.service_display_name ) );
}
}

91
servers/icinga2.js Normal file
View File

@ -0,0 +1,91 @@
/* -*- mode: js; js-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
Monito Gnome-Shell extension
Copyright (C) 2021 Benjamin Drieu
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.
SPDX-License-Identifier: GPL-2.0-or-later
*/
const { Soup } = imports.gi;
const ExtensionUtils = imports.misc.extensionUtils;
const Lang = imports.lang;
const Main = imports.ui.main;
const Me = ExtensionUtils.getCurrentExtension();
const Preferences = Me.imports.prefs;
const GenericServer = Me.imports.servers.genericserver.GenericServer;
var Icinga2 = class extends GenericServer {
constructor ( _server, extension ) {
super(_server, extension, 'Icinga2');
this.canRecheck = false;
}
refresh ( ) {
this.buildURL ( );
this.prepareHttp ( );
}
prepareHttp ( )
{
super.prepareHttp ( );
let message = Soup.form_request_new_from_hash ( 'GET', this.urlcgi, { 'format': 'json' } );
message.request_headers.append ( 'Accept', 'application/json' );
this.authenticateAndSend ( message );
}
handleMessage ( _httpSession, message )
{
let _data = super.handleMessage ( _httpSession, message );
if ( this.error )
this.monitoLog ( 'Parent error ' + this.error );
else
{
let json = JSON.parse ( _data );
let _statuses = [ 'OK', 'WARNING', 'CRITICAL', 'UNKNOWN' ];
for ( var entry of json )
{
this.status.service_status.push ( {
status: _statuses [ entry.service_state ],
host_name: entry.host_name,
service_display_name: entry.service_display_name,
attempts: entry.service_attempt,
has_been_acknowledged: parseInt(entry.service_acknowledged),
status_information: entry.service_output,
last_check: this.formatDate(parseInt(entry.service_last_state_change)),
} );
}
}
this.extension.refreshUI ( this );
return ! this.error;
}
getUrlForService ( service )
{
return '%s/monitoring/service/show?host=%s&service=%s'.format ( this._settings.get_string ( 'url' ),
service.real_host_name,
service.service_display_name );
}
}

137
servers/icinga2api.js Normal file
View File

@ -0,0 +1,137 @@
/* -*- mode: js; js-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
Monito Gnome-Shell extension
Copyright (C) 2021 Benjamin Drieu
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.
SPDX-License-Identifier: GPL-2.0-or-later
*/
const { Soup } = imports.gi;
const ExtensionUtils = imports.misc.extensionUtils;
const Lang = imports.lang;
const Main = imports.ui.main;
const Me = ExtensionUtils.getCurrentExtension();
const Preferences = Me.imports.prefs;
const GenericServer = Me.imports.servers.genericserver.GenericServer;
var Icinga2API = class extends GenericServer {
constructor ( _server, extension ) {
super(_server, extension, 'Icinga2 API');
}
refresh ( ) {
this.buildURL ( );
this.prepareHttp ( );
}
prepareHttp ( )
{
super.prepareHttp ( );
let message = Soup.form_request_new_from_hash ( 'GET', this.urlcgi + '/objects/services', { } );
message.request_headers.append ( 'Accept', 'application/json' );
this.authenticateAndSend ( message, this.handlePollMessage );
}
recheck ( entry )
{
let message = Soup.form_request_new_from_hash ( 'POST', this.urlcgi + '/actions/reschedule-check', { } );
let params = '{ "type": "Service", "filter": "host.name==\\"%s\\" && service.name==\\"%s\\"", "force": true, "pretty": true }' . format ( entry.real_host_name, entry.real_service_display_name );
message.request_body.truncate();
message.request_body.append ( params );
message.request_body.flatten();
message.button = entry.button;
message.request_headers.append ( 'Accept', 'application/json' );
this.authenticateAndSend ( message, this.handleCMDMessage );
}
recheckAll ( button )
{
let message = Soup.form_request_new_from_hash ( 'POST', this.urlcgi + '/actions/reschedule-check', { } );
let params = '{ "type": "Service", "force": true, "pretty": true }';
message.request_body.truncate();
message.request_body.append ( params );
message.request_body.flatten();
message.button = button;
message.request_headers.append ( 'Accept', 'application/json' );
this.authenticateAndSend ( message, this.handleCMDMessage );
}
handlePollMessage ( _httpSession, message )
{
// this.monitoLog ( 'handlePollMessage' );
let _data = super.handleMessage ( _httpSession, message );
try
{
if ( this.error )
this.monitoLog ( 'Parent error ' + this.error );
else
{
let json = JSON.parse ( _data );
let _statuses = [ 'OK', 'WARNING', 'CRITICAL', 'UNKNOWN', 'PENDING' ];
for ( var entry of json.results )
{
// this.monitoLog ( JSON.stringify(entry) );
var _attrs = entry.attrs;
_attrs [ 'real_host_name' ] = entry.attrs.host_name.repeat ( 1 );
_attrs [ 'status' ] = _statuses [ entry.attrs.state ];
_attrs [ 'service_display_name' ] = entry.attrs.display_name.repeat ( 1 );
_attrs [ 'has_been_acknowledged' ] = parseInt ( entry.attrs.acknowledgement );
_attrs [ 'attempts' ] = '%d/%d'.format ( entry.attrs.check_attempt, entry.attrs.max_check_attempts );
_attrs [ 'status_information' ] = ( entry.attrs.last_check_result ? entry.attrs.last_check_result.output : _statuses[entry.attrs.state] );
_attrs [ 'real_last_state_change' ] = entry.attrs.last_state_change;
_attrs [ 'last_state_change' ] = ( entry.attrs.last_state_change ? this.formatDate(parseInt(entry.attrs.last_state_change)) : '' );
_attrs [ 'real_last_check' ] = entry.attrs.last_check;
_attrs [ 'last_check' ] = ( entry.attrs.last_check ? this.formatDate(parseInt(entry.attrs.last_check)) : '' );
_attrs [ 'real_next_check' ] = entry.attrs.next_check;
_attrs [ 'next_check' ] = ( entry.attrs.next_check ? this.formatDate(parseInt(entry.attrs.next_check)) : '' );
this.status.service_status.push ( _attrs );
}
}
}
catch ( e )
{
this.monitoLog ( '> ERROR: ' + e );
this.monitoLog ( '> DATA: ' + _data );
}
this.extension.refreshUI ( this );
return ! this.error;
}
getUrlForService ( service )
{
return '%s/monitoring/service/show?host=%s&service=%s'.format ( this._settings.get_string ( 'url' ),
encodeURI ( service.real_host_name ),
encodeURI ( service.service_display_name ) );
}
}

View File

@ -29,6 +29,10 @@
margin: 0px; */
}
.monito-serverbox {
}
.monito-box {
color: white;
border: 1px solid white;
@ -38,8 +42,12 @@
line-height: 20px;
}
.monito-namebox {
margin-right: .5em;
}
.monito-critical-box, .monito-service-CRITICAL, .monito-service-line-CRITICAL {
background-color: #FF3300;
background-color: #ff3300;
}
.monito-warning-box, .monito-service-WARNING, .monito-service-line-WARNING {
@ -47,11 +55,11 @@
}
.monito-ok-box, .monito-service-OK, .monito-service-line-OK {
background-color: #00CC33;
background-color: #00cc33;
}
.monito-unknown-box, .monito-service-UNKNOWN, .monito-service-line-UNKNOWN {
background-color: purple;
background-color: #e496f5;
}
.monito-service-line {
@ -64,6 +72,10 @@
line-height: 20px;
}
.monito-service-line:hover {
font-weight: bold;
}
.monito-title {
margin: 2px;
font-size: 200%;
@ -73,11 +85,27 @@
margin: 2px;
}
.monito-network-error {
font-size: 150%;
font-weight: bold;
}
.monito-button-icon {
padding: 0px;
margin: 0px;
}
.button {
.entry {
margin-right: 0px;
min-width: 256px;
padding: 12px !important;
}
.big-button {
margin-left: 0px;
padding: 12px !important;
}
.small-button {
padding: 3px !important;
}