]> git.immae.eu Git - perso/Immae/Config/Nix.git/commitdiff
Add Eban monitoring
authorIsmaël Bouya <ismael.bouya@normalesup.org>
Tue, 29 Dec 2020 21:06:59 +0000 (22:06 +0100)
committerIsmaël Bouya <ismael.bouya@normalesup.org>
Tue, 29 Dec 2020 21:06:59 +0000 (22:06 +0100)
12 files changed:
modules/private/environment.nix
modules/private/monitoring/default.nix
modules/private/monitoring/objects_common.nix
modules/private/monitoring/objects_eban.nix [new file with mode: 0644]
modules/private/monitoring/objects_phare.nix
modules/private/monitoring/objects_tiboqorl-fr.nix
modules/private/monitoring/objects_ulminfo-fr.nix
modules/private/monitoring/plugins/notify_eban_url [new file with mode: 0755]
modules/private/monitoring/status.nix
modules/private/monitoring/status/app.py
modules/private/system.nix
nixops/secrets

index e79feec573e2273e3471cc3d2f0b362f397173ae..490a4051bf3604b5bd4591d7562ed0395e5e743b 100644 (file)
@@ -558,6 +558,14 @@ in
               };
             };
           };
+          eban = mkOption {
+            description = "Eban credentials for webhook";
+            type = submodule {
+              options = {
+                password = mkOption { type = str; description = "Password"; };
+              };
+            };
+          };
           nrdp_tokens = mkOption { type = listOf str; description = "Tokens allowed to push status update"; };
           slack_url = mkOption { type = str; description = "Slack webhook url to push status update"; };
           slack_channel = mkOption { type = str; description = "Slack channel to push status update"; };
index a298f9246965aecd54b085193b950db6ee826cc3..73e427554bcc5ce6d3372f419c63bd41daabbdfa 100644 (file)
@@ -78,6 +78,9 @@ let
     wrapProgram $out/notify_by_slack --prefix PATH : ${lib.makeBinPath [
       pkgs.curl pkgs.jq
     ]}
+    wrapProgram $out/notify_eban_url --prefix PATH : ${lib.makeBinPath [
+      pkgs.curl
+    ]}
     wrapProgram $out/check_ovh_sms --prefix PATH : ${lib.makeBinPath [
       (pkgs.python3.withPackages (ps: [ps.ovh]))
     ]}
@@ -157,7 +160,7 @@ let
     };
   otherObjects = map
     (n: (pkgs.callPackage (./. + "/objects_" + n + ".nix") { inherit emailCheck; }))
-    [ "ulminfo-fr" "phare" ];
+    [ "ulminfo-fr" "phare" "eban" ];
   masterObjects = pkgs.callPackage ./objects_master.nix { inherit config; };
   commonObjects = pkgs.callPackage ./objects_common.nix ({
     master = cfg.master;
@@ -326,6 +329,7 @@ in
           config.myEnv.monitoring.ovh_sms.consumer_key
           config.myEnv.monitoring.ovh_sms.account
         ]}
+        $USER210$=${config.myEnv.monitoring.eban.password}
       '';
       objectDefs = toObjects commonObjects
         + toObjects hostObjects
index 7f553a0044b734caf3975036133ddbf6e4f68604..df378b5243adfa6a9dd0aa6cf83b1f8a1bb6767a 100644 (file)
@@ -34,6 +34,7 @@ in
       use = "linux-server";
       hostgroups = "webstatus-hosts";
       _webstatus_name = hostName;
+      _webstatus_vhost = "status.immae.eu";
     };
   };
   service = [
@@ -155,6 +156,8 @@ in
     # $OVE is to force naemon to run via shell instead of execve which fails here
     notify-service-by-email = "ADMINEMAIL=\"$ADMINEMAIL$\" SERVICENOTIFICATIONID=\"$SERVICENOTIFICATIONID$\" SERVICEDESC=\"$SERVICEDESC$\" SERVICESTATE=\"$SERVICESTATE$\" SERVICEOUTPUT=\"$SERVICEOUTPUT$\" $USER2$/notify_by_email service \"$NOTIFICATIONTYPE$\" \"$HOSTALIAS$\" \"$LONGDATETIME$\" \"$CONTACTEMAIL$\" $OVE";
     notify-by-slack = "HOST=\"$HOSTALIAS$\" SERVICESTATE=\"$SERVICESTATE$\" SERVICEDESC=\"$SERVICEDESC$\" SERVICEOUTPUT=\"$SERVICEOUTPUT$\" $USER2$/notify_by_slack \"$ARG1$\" \"$ARG2$\"";
+    notify-host-eban-url = "STATUS_NAME=\"Server\" PASSWORD=\"$USER210$\" HOSTSTATE=\"$HOSTSTATE$\" $USER2$/notify_eban_url";
+    notify-service-eban-url = "STATUS_NAME=\"$_SERVICEWEBSTATUS_NAME$\" PASSWORD=\"$USER210$\" SERVICESTATE=\"$SERVICESTATE$\" $USER2$/notify_eban_url";
 
     notify-master = "$USER2$/send_nrdp.sh -u \"$USER200$\" -t \"$USER201$\" -H \"$HOSTADDRESS$\" -s \"$SERVICEDESC$\" -S \"$SERVICESTATEID$\" -o \"$SERVICEOUTPUT$ | $SERVICEPERFDATA$\"";
   };
diff --git a/modules/private/monitoring/objects_eban.nix b/modules/private/monitoring/objects_eban.nix
new file mode 100644 (file)
index 0000000..659b0ec
--- /dev/null
@@ -0,0 +1,60 @@
+{ ... }:
+let
+  serviceTemplate = rest: {
+    host_name = "eban.bzh";
+    use = "external-web-service";
+    contacts = "eban";
+    contact_groups = "null";
+    check_interval = "15";
+
+    servicegroups = "webstatus-resources";
+  } // rest;
+in
+{
+  contact = {
+    eban = {
+      use = "generic-contact";
+      host_notification_commands = "notify-host-eban-url";
+      service_notification_commands = "notify-service-eban-url";
+    };
+  };
+  host = {
+    "eban.bzh" = {
+      alias = "eban.bzh";
+      address = "eban.bzh";
+      use = "linux-server";
+      hostgroups = "webstatus-hosts";
+      contacts = "eban";
+      contact_groups = "null";
+      _webstatus_name = "Eban";
+      _webstatus_vhost = "status.eban.bzh";
+    };
+  };
+  service = [
+    (serviceTemplate {
+      service_description = "Eban website is up and running";
+      check_command = ["check_https" "eban.bzh" "/" "<title>"];
+      _webstatus_name = "Main Website";
+      _webstatus_url = "https://eban.bzh/";
+    })
+    (serviceTemplate {
+      service_description = "Eban blog is up and running";
+      check_command = ["check_https" "blog.eban.bzh" "/" "<title>"];
+      _webstatus_name = "Blog";
+      _webstatus_url = "https://blog.eban.bzh/";
+    })
+    (serviceTemplate {
+      service_description = "Eban gitea is up and running";
+      check_command = ["check_https" "git.eban.bzh" "/" "<title>"];
+      _webstatus_name = "Git";
+      _webstatus_url = "https://git.eban.bzh/";
+    })
+    (serviceTemplate {
+      service_description = "Eban Cloud is up and running";
+      check_command = ["check_https" "cloud.eban.bzh" "/" "<title>"];
+
+      _webstatus_name = "Cloud";
+      _webstatus_url = "https://cloud.eban.bzh/";
+    })
+  ];
+}
index a00e5e84c86074a7cefe5ae7a17805248cfdb9b5..a61b46ea24fd18a0a2b0fc8a6af9f29a51dac288 100644 (file)
@@ -7,6 +7,7 @@
       use = "linux-server";
       hostgroups = "webstatus-hosts";
       _webstatus_name = "phare";
+      _webstatus_vhost = "status.immae.eu";
     };
   };
   service = [
index c3851b52a0994e72a14dd96603f1da3402bda70c..77403ba4c8521379386a3e907fa97290e7c1226c 100644 (file)
@@ -22,6 +22,7 @@ in
       contact_groups = "tiboqorl";
       hostgroups = "webstatus-hosts";
       _webstatus_name = "tiboqorl";
+      _webstatus_vhost = "status.immae.eu";
     };
   };
   service = [
index b970ecda266ef86e4fcb0a175f65fa2f2b63f60c..574e0e35d08c245499bf442107088ed6c9d99d39 100644 (file)
@@ -7,6 +7,7 @@
       use = "linux-server";
       hostgroups = "webstatus-hosts";
       _webstatus_name = "ulminfo";
+      _webstatus_vhost = "status.immae.eu";
     };
   };
   service = [
diff --git a/modules/private/monitoring/plugins/notify_eban_url b/modules/private/monitoring/plugins/notify_eban_url
new file mode 100755 (executable)
index 0000000..431e320
--- /dev/null
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+
+PASS=$(echo "$PASSWORD" | base64 -d)
+
+if [ "$SERVICESTATE" = "CRITICAL" -o "$SERVICESTATE" = "UNKNOWN" -o "$HOSTSTATE" = "DOWN" -o "$HOSTSTATE" = "UNREACHABLE" ]; then
+  action=downAlert
+elif [ "$SERVICESTATE" = "OK" -o "$HOSTSTATE" = "UP" ]; then
+  action=upAlert
+fi
+
+if [ -n "$action" ]; then
+  curl -X GET -G --data-urlencode "service=$STATUS_NAME" --data-urlencode "mdp=$PASS" https://infra.eban.bzh/$action
+fi
index 4ca0327e80e88d7a6650fb1ba2a7a5d3c34e4a7a..73f474926f5de1f147d99ecc382c6cfaec0f5cbf 100644 (file)
       upstreams."netdata".extraConfig = ''
         keepalive 64;
         '';
+      virtualHosts."status.eban.bzh" = {
+        acmeRoot = config.myServices.certificates.webroot;
+        useACMEHost = name;
+        forceSSL = true;
+        locations."/".proxyPass = "http://unix:/run/naemon-status/socket.sock:/";
+      };
       virtualHosts."status.immae.eu" = {
         acmeRoot = config.myServices.certificates.webroot;
         useACMEHost = name;
@@ -60,6 +66,7 @@
     };
     security.acme.certs."${name}" = {
       extraDomains."status.immae.eu" = null;
+      extraDomains."status.eban.bzh" = null;
       user = config.services.nginx.user;
       group = config.services.nginx.group;
     };
index b1d419cc1cc88909a265dca4d293a1337a235b9e..ff928914c6d681f15769ea81dd2cfbd95b59b662 100755 (executable)
@@ -55,33 +55,34 @@ def get_lq(request):
     return b"".join(chunks).decode()
 
 class Host:
-    def __init__(self, name, alias, status, webname):
+    def __init__(self, name, alias, status, webname, vhost):
         self.name = name
         self.alias = alias
         self.webname = webname or alias
+        self.vhost = vhost
         self.status = status
         self.services = []
 
     @classmethod
-    def parse_hosts(cls, payload):
-        parsed = [cls.parse(p) for p in json.loads(payload)]
+    def parse_hosts(cls, payload, vhost):
+        parsed = filter(lambda x: x.vhost == vhost, [cls.parse(p) for p in json.loads(payload)])
         return {p.name: p for p in parsed}
 
     @classmethod
     def parse(cls, payload):
-        return cls(payload[0], payload[1], HOST_STATUS[payload[2]], payload[3].get("WEBSTATUS_NAME"))
+        return cls(payload[0], payload[1], HOST_STATUS[payload[2]], payload[3].get("WEBSTATUS_NAME"), payload[3].get("WEBSTATUS_VHOST"))
 
     def __repr__(self):
         return "Host {}: {} ({})".format(self.name, self.alias, self.webname)
 
     @classmethod
-    def query(cls):
+    def query(cls, vhost):
         answer = get_lq("""GET hosts
 Filter: groups >= webstatus-hosts
 Columns: name alias state custom_variables
 OutputFormat: json
 """)
-        return cls.parse_hosts(answer)
+        return cls.parse_hosts(answer, vhost)
 
     def fill_services(self, services):
         self.services = [service for service in services if service.host == self.name]
@@ -110,8 +111,8 @@ OutputFormat: json
 """)
         return cls.parse_groups(answer)
 
-    def fill_services(self, services):
-        self.services = [service for service in services if any([group == self.name for group in service.groups])]
+    def fill_services(self, services, hosts):
+        self.services = [service for service in services if any([group == self.name for group in service.groups]) and service.host in hosts]
 
     def __repr__(self):
         return "ServiceGroup {}: {}".format(self.name, self.alias)
@@ -158,15 +159,15 @@ OutputFormat: json
     def __repr__(self):
         return "Service {}: {}".format(self.name, self.webname)
 
-def get_infos():
-    hosts = Host.query()
+def get_infos(vhost):
+    hosts = Host.query(vhost)
     servicegroups = ServiceGroup.query()
     services = Service.query()
 
     for host in hosts:
         hosts[host].fill_services(services)
     for group in servicegroups:
-        servicegroups[group].fill_services(services)
+        servicegroups[group].fill_services(services, hosts)
     return (hosts, servicegroups, services)
 
 TEMPLATE='''<?xml version="1.0" encoding="UTF-8"?>
@@ -254,12 +255,14 @@ TEMPLATE='''<?xml version="1.0" encoding="UTF-8"?>
             {% endfor %}
         {%- endfor %}
 
+        {%- for group in servicegroups.values() if group.services and group.name != "webstatus-resources" %}
+        {%- if loop.first %}
         <h2>Services</h2>
         <div id="services">
-        {%- for group in servicegroups.values() if group.services and group.name != "webstatus-resources" %}
+        {%- endif %}
             <div class="servicegroup">
             <h3 class="servicegroup_title">{{ group.alias }}</h3>
-            {%- for service in group.services -%}
+            {%- for service in group.services if service.host in hosts -%}
                 {%- if loop.first %}
                 <ul class="services">
                 {% endif %}
@@ -281,8 +284,10 @@ TEMPLATE='''<?xml version="1.0" encoding="UTF-8"?>
                 {% endif %}
             {%- endfor -%}
             </div>
-        {%- endfor %}
+        {%- if loop.last %}
         </div>
+        {% endif %}
+        {%- endfor %}
     </body>
 </html>
 '''
@@ -307,7 +312,7 @@ def live():
 
 @app.route("/", methods=["GET"])
 def get():
-    (hosts, servicegroups, services) = get_infos()
+    (hosts, servicegroups, services) = get_infos(request.host)
     resp = make_response(render_template_string(TEMPLATE, hosts=hosts, servicegroups=servicegroups))
     resp.content_type = "text/html"
     return resp
index 71a49e36fea61e371420fa52cb1b26d26671341a..0e72d9962fec977563a3607002aa44910cc42f89 100644 (file)
@@ -26,7 +26,8 @@
     ];
 
     services.journald.extraConfig = ''
-      MaxLevelStore=warning
+      #Should be "warning" but disabled for now, it prevents anything from being stored
+      MaxLevelStore=info
       MaxRetentionSec=1year
       '';
 
index 3d83a120a6247493e7cdac8d670ea85ebbfb711c..00f59f49098d79f0839541fd379e8933e3ddf9d7 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 3d83a120a6247493e7cdac8d670ea85ebbfb711c
+Subproject commit 00f59f49098d79f0839541fd379e8933e3ddf9d7