diff --git a/cicd_playbook.yml b/cicd_playbook.yml index 6a76a9617daa7fade1d2e2e3245c9e27e6181e93..6d561146d6731a168b3a96767ad9193843e83f2b 100644 --- a/cicd_playbook.yml +++ b/cicd_playbook.yml @@ -3,6 +3,7 @@ connection: local gather_facts: no vars: + DEBMIRROR: http://debcache.mpifr-bonn.mpg.de/ubuntu/ register_as_pipeline: False docker_registry: eddinfra0 docker_registry_port: 5000 diff --git a/playbooks/basic_configuration.yml b/playbooks/basic_configuration.yml index 864b87f9ac3974ab6b4a5e26190beb507d647e76..a67bce1d41b4bb2e65299d59c6e7b0a655fdf3e9 100644 --- a/playbooks/basic_configuration.yml +++ b/playbooks/basic_configuration.yml @@ -157,3 +157,15 @@ - redis_sidecar roles: - pipeline_builder + + +- name: integrate to monitoring service + hosts: monitor_service + gather_facts: no + + roles: + - monitoring_integration + tags: + - monitoring_integration + + diff --git a/roles/ansible_interface/tasks/main.yml b/roles/ansible_interface/tasks/main.yml index f43e6e98f46c2dc4bf5c2a4d7b71d39d894a98f5..48029ec057cb5e0b7ca3295dbfad2dee1d8ed802 100644 --- a/roles/ansible_interface/tasks/main.yml +++ b/roles/ansible_interface/tasks/main.yml @@ -63,7 +63,8 @@ network_mode: host # host as need access to network for introspection pull: yes privileged: yes - auto_remove: yes + auto_remove: no + restart_policy: "always" force_kill: yes # avoid lag between rm and restart volumes: - "/var/run/docker.sock:/var/run/docker.sock" diff --git a/roles/edd_base/defaults/main.yml b/roles/edd_base/defaults/main.yml index daef02617cb1122e4a4119d68f267f35b3cc3e43..5a6f9586af394b970051b89b77b4ed8f6cadd0ca 100644 --- a/roles/edd_base/defaults/main.yml +++ b/roles/edd_base/defaults/main.yml @@ -7,7 +7,7 @@ gcc_architecture: "native" # Architecture to use for GCC optimizations. Set baseimage: "nvidia/cuda:11.0.3-devel-ubuntu{{UBUNTU_VERSION}}" baseimage_gpuless: "ubuntu:{{UBUNTU_VERSION}}" psrdada_version: bf756866898686065562ac537376cf9d7d1b54ee -spead2_tag: v1.8.0 -mkrecv_tag: f55b8d40fee730a091266ee7d52d3d74b4c32f0a -mksend_tag: 648e7d3242b097dca82ec0ef4851deec17edbdfc -psrdadacpp_tag: a2e4755fd88c2d2711543b5efa199e57644e4945 +spead2_tag: v3.11.1 +mkrecv_tag: 65a520d21bd7354deac01d7c667b4b49564152ab +mksend_tag: 47c064942d3b7f90eb336d6e5701f6b8bd09ee57 +psrdadacpp_tag: 5fc008fb271647d227a2d5d2164151dcacf0a6ed diff --git a/roles/edd_base/files/mkrecv_fix_issue_12.diff b/roles/edd_base/files/mkrecv_fix_issue_12.diff deleted file mode 100644 index 24a028965abd05e161774c4e5372215760797c97..0000000000000000000000000000000000000000 --- a/roles/edd_base/files/mkrecv_fix_issue_12.diff +++ /dev/null @@ -1,39 +0,0 @@ -diff --git a/mkrecv_options.cpp b/mkrecv_options.cpp -index bc35998..77ca04f 100644 ---- a/mkrecv_options.cpp -+++ b/mkrecv_options.cpp -@@ -159,7 +159,7 @@ namespace mkrecv - time_t start_utc; - struct tm *nowtm; - char tbuf[64]; -- char utc_string[64]; -+ char utc_string[128]; - - tv.tv_sec = integral; - tv.tv_usec = (int) (fractional*1e6); -diff --git a/mkrecv_rnt.cpp b/mkrecv_rnt.cpp -index c5c5d5d..6014004 100644 ---- a/mkrecv_rnt.cpp -+++ b/mkrecv_rnt.cpp -@@ -444,7 +444,7 @@ namespace mkrecv - time_t start_utc; - struct tm *nowtm; - char tbuf[64]; -- char utc_string[64]; -+ char utc_string[128]; - - tv.tv_sec = integral; - tv.tv_usec = (int) (fractional*1e6); -diff --git a/mkrecv_v4.cpp b/mkrecv_v4.cpp -index 858272f..a1c68c2 100644 ---- a/mkrecv_v4.cpp -+++ b/mkrecv_v4.cpp -@@ -450,7 +450,7 @@ void signal_handler(int v); - uint64_t start_ps; - struct tm *nowtm; - char tbuf[64]; -- char utc_string[64]; -+ char utc_string[128]; - char ps_string[64]; - - if (floor(sample_clock) == ceil(sample_clock)) { diff --git a/roles/edd_base/files/mksend_fix_issue_9.diff b/roles/edd_base/files/mksend_fix_issue_9.diff deleted file mode 100644 index dbc3fc2332d80672acf7496a37c12ef3890b4478..0000000000000000000000000000000000000000 --- a/roles/edd_base/files/mksend_fix_issue_9.diff +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/mksend_options.cpp b/mksend_options.cpp -index f2ada4f..4d37df3 100644 ---- a/mksend_options.cpp -+++ b/mksend_options.cpp -@@ -159,7 +159,7 @@ namespace mksend - time_t start_utc; - struct tm *nowtm; - char tbuf[64]; -- char utc_string[64]; -+ char utc_string[128]; - - tv.tv_sec = integral; - tv.tv_usec = (int) (fractional*1e6); diff --git a/roles/edd_base/templates/Dockerfile b/roles/edd_base/templates/Dockerfile index 22c6bf436d7675f71c67040575ee9eccce13672f..e41c36253e6756c97727309542448f57c40fd5e2 100644 --- a/roles/edd_base/templates/Dockerfile +++ b/roles/edd_base/templates/Dockerfile @@ -72,7 +72,7 @@ RUN mkdir /src && \ ### PSRDADA RUN cd /src && \ - git clone git://git.code.sf.net/p/psrdada/code psrdada && \ + git clone https://git.code.sf.net/p/psrdada/code psrdada && \ cd psrdada && \ git checkout {{ psrdada_version }}&& \ # Version from Wed Feb 12 07:46:37 2020 +0200 \ @@ -87,100 +87,85 @@ RUN cd /src && \ RUN cd /src && \ git clone https://gitlab.mpcdf.mpg.de/mpifr-bdg/psrdada_cpp.git && \ cd psrdada_cpp/ &&\ - ### Could be lib for lib layout in VLBI mode \ git checkout {{ psrdadacpp_tag }}&& \ - git submodule init &&\ - git submodule update &&\ - mkdir build/ &&\ - cd build/ &&\ - cmake -DENABLE_CUDA=True -DARCH={{gcc_architecture}} -DPSRDADA_INCLUDE_DIR=/usr/local/include/psrdada .. &&\ - make -j 8 &&\ - make test;\ - make install + cmake -S . -B build/ -DENABLE_CUDA=True -DARCH={{gcc_architecture}} -DPSRDADA_INCLUDE_DIR=/usr/local/include/psrdada .. &&\ + make -C build/ -j8 && make -C build/ install + +### SPEAD 2 dependencies +RUN cd /src && \ + apt-get --no-install-recommends -y install \ + python3-jinja2 \ + python3-pycparser \ + python3-packaging \ + libdivide-dev ### SPEAD 2 \ RUN cd /src && \ git clone https://github.com/ska-sa/spead2.git && \ cd spead2 && \ git checkout {{ spead2_tag }} && \ - # v1.8.0 or later is required by mksend \ - # v1.9.2 does not work with the ibverbs version MLNX_OFED-4.1-1.0.2.0 used above \ ./bootstrap.sh --no-python && \ ./configure && \ make -j8 &&\ make install -COPY mkrecv_fix_issue_12.diff /tmp - ### MKRECV \ RUN cd /src && \ git clone https://gitlab.mpifr-bonn.mpg.de/mhein/mkrecv.git && \ cd mkrecv &&\ git checkout {{ mkrecv_tag }} && \ - git apply /tmp/mkrecv_fix_issue_12.diff && \ - mkdir build &&\ - cd build && \ - # TODO: AVOID LOCAL PATHS - cmake .. -DENABLE_CUDA=TRUE -DPSRDADA_CPP_INCLUDE_DIR=/src/psrdada_cpp/ -DPSRDADA_CPP_LIBRARIES=/src/psrdada_cpp/build/psrdada_cpp/libpsrdada_cpp.a -DPSRDADA_INCLUDE_DIR=/usr/local/include/psrdada -DARCH={{gcc_architecture}}&& \ - make -j8 &&\ - make install - -COPY mksend_fix_issue_9.diff /tmp + cmake -S . -B build/ -DENABLE_CUDA=TRUE -DPSRDADA_CPP_INCLUDE_DIR=/src/psrdada_cpp/ -DPSRDADA_CPP_LIBRARIES=/src/psrdada_cpp/build/psrdada_cpp/libpsrdada_cpp.a -DPSRDADA_INCLUDE_DIR=/usr/local/include/psrdada -DARCH={{gcc_architecture}}&& \ + make -C build/ -j8 && make -C build/ install ### MKSEND \ RUN cd /src && \ git clone https://gitlab.mpifr-bonn.mpg.de/mhein/mksend.git && \ cd mksend && \ git checkout {{ mksend_tag }} && \ - git apply /tmp/mksend_fix_issue_9.diff && \ - mkdir build &&\ - cd build && \ - cmake .. -DENABLE_CUDA=TRUE -DPSRDADA_CPP_INCLUDE_DIR=/src/psrdada_cpp/ -DPSRDADA_CPP_LIBRARIES=/src/psrdada_cpp/build/psrdada_cpp/libpsrdada_cpp.a -DPSRDADA_INCLUDE_DIR=/usr/local/include/psrdada -DARCH={{gcc_architecture}} && \ - make -j8 &&\ - make install + cmake -S . -B build/ -DENABLE_CUDA=TRUE -DPSRDADA_CPP_INCLUDE_DIR=/src/psrdada_cpp/ -DPSRDADA_CPP_LIBRARIES=/src/psrdada_cpp/build/psrdada_cpp/libpsrdada_cpp.a -DPSRDADA_INCLUDE_DIR=/usr/local/include/psrdada -DARCH={{gcc_architecture}} && \ + make -C build/ -j8 && make -C build/ install ### MPIKAT dependencies\ RUN cd /src && \ apt-get --no-install-recommends -y install \ - libhdf5-dev \ - python3-h5py \ - python3-setuptools \ - python3-setuptools-git \ - python3-setuptools-scm \ - python3-pip \ - python3-pil \ - python3-jinja2 \ - python3-cmd2 \ - libpng-dev \ - pkg-config \ - libfreetype6-dev \ - python3-scipy \ - python3-pytest \ - python3-posix-ipc \ - python3-redis \ - python3-numpy \ - python3-netaddr \ - python3-astropy \ - python3-future \ - python3-git \ - python3-coloredlogs \ - python3-yaml \ - &&\ - \ - pip3 install --no-deps \ - omnijson==0.1.2 \ - ujson==2.0.3 \ - katpoint==0.9 \ - katcp==0.9.1 \ - kiwisolver==1.1.0 \ - nvector==0.7.4 \ - ephem==3.7.7.0 \ - spead2==1.8.0 \ - tornado==4.5.3 \ - tomli==1.0.0 \ - typing-extensions==3.7.4.3\ - setuptools_scm==7.0.5 \ - && \ + libhdf5-dev \ + python3-h5py \ + python3-setuptools \ + python3-setuptools-git \ + python3-setuptools-scm \ + python3-pip \ + python3-pil \ + python3-cmd2 \ + libpng-dev \ + pkg-config \ + libfreetype6-dev \ + python3-scipy \ + python3-pytest \ + python3-posix-ipc \ + python3-redis \ + python3-numpy \ + python3-netaddr \ + python3-astropy \ + python3-future \ + python3-git \ + python3-coloredlogs \ + python3-yaml \ + &&\ + \ + pip3 install --no-deps \ + omnijson==0.1.2 \ + ujson==2.0.3 \ + katpoint==0.9 \ + katcp==0.9.1 \ + kiwisolver==1.1.0 \ + nvector==0.7.4 \ + ephem==3.7.7.0 \ + spead2==3.11.1 \ + tornado==4.5.3 \ + tomli==1.0.0 \ + typing-extensions==3.7.4.3\ + setuptools_scm==7.0.5 \ + && \ echo 'DONE!' WORKDIR /root diff --git a/roles/edd_base/templates/Dockerfile_gpuless b/roles/edd_base/templates/Dockerfile_gpuless index 52de2f788851898849a3bc3cdf6c691e4c1201a1..bdd0e4e23f5a8e8a3d28d78a4724d620c3c6c581 100644 --- a/roles/edd_base/templates/Dockerfile_gpuless +++ b/roles/edd_base/templates/Dockerfile_gpuless @@ -66,6 +66,14 @@ RUN mkdir /src && \ gnupg +### SPEAD 2 dependencies +RUN cd /src && \ + apt-get --no-install-recommends -y install \ + python3-jinja2 \ + python3-pycparser \ + python3-packaging \ + libdivide-dev + ### SPEAD 2 \ RUN cd /src && \ git clone https://github.com/ska-sa/spead2.git && \ @@ -89,7 +97,6 @@ RUN cd /src && \ python3-setuptools-scm \ python3-pip \ python3-pil \ - python3-jinja2 \ python3-cmd2 \ libpng-dev \ pkg-config \ @@ -115,7 +122,7 @@ RUN cd /src && \ kiwisolver==1.1.0 \ nvector==0.7.4 \ ephem==3.7.7.0 \ - spead2==1.8.0 \ + spead2==3.11.1 \ tornado==4.5.3 \ tomli==2.0.1 \ typing-extensions==3.7.4.3 \ diff --git a/roles/gs_plotter/tasks/main.yml b/roles/gs_plotter/tasks/main.yml new file mode 100644 index 0000000000000000000000000000000000000000..b4cbe382bcae37e3acb2aac5b2dc05aad7b489ad --- /dev/null +++ b/roles/gs_plotter/tasks/main.yml @@ -0,0 +1,17 @@ +--- +- name: Plotter for Gated Spectrometer + include_role: + name: EDD.core.common + vars: + image_name: gs_plotter + default_container_name: gs_plotter + container_cmd: "python /src/mpikat/mpikat/pipelines/gs_plotter.py --host={{ edd_subnet }} --port={{ bind_port }}" + measurement: "gated_spectrometer" + dashboard_is_live: true + dashboard_refresh: "30s" + dashboard_panels: + - "spectrum_plotter.json" + dashboard_vars: + - "spectrum_plotter_vars.json" + tags: + - always diff --git a/roles/gs_plotter/templates/Dockerfile b/roles/gs_plotter/templates/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..717a656324953b5701d9208428ccfa90eabcc0fd --- /dev/null +++ b/roles/gs_plotter/templates/Dockerfile @@ -0,0 +1,17 @@ +FROM {{docker_registry}}:{{docker_registry_port}}/eddbase_gpuless:{{version_tag}} +MAINTAINER Niclas Esser "nesser@mpifr-bonn.mpg.de" + +# Suppress debconf warnings +ENV DEBIAN_FRONTEND noninteractive + +# Switch account to root and adding user accounts and password +USER root + +RUN mkdir -p /src && \ + cd /src && \ + git clone {{ mpikat_repository }} mpikat &&\ + cd mpikat && \ + git checkout {{ mpikat_branch }}&& \ + pip3 install . + +WORKDIR /root diff --git a/roles/gs_plotter/templates/spectrum_plotter.json b/roles/gs_plotter/templates/spectrum_plotter.json new file mode 100644 index 0000000000000000000000000000000000000000..a4b50f343e0415e3315e7b0d7537021318898d39 --- /dev/null +++ b/roles/gs_plotter/templates/spectrum_plotter.json @@ -0,0 +1,120 @@ +{ + "datasource": { + "type": "redis-datasource", + "uid": "redisdatastreams" + }, + "description": "Plot of the latest spectrum of $data_streams", + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 6 + }, + "id": 4, + "maxPerRow": 4, + "options": { + "config": "", + "data": "", + "layout": { + "autosize": true, + "margin": { + "b": 20, + "l": 40, + "r": 0, + "t": 0 + }, + "xaxis": { + "autorange": true, + "range": [ + 349500000, + 350496096 + ], + "type": "linear" + } + }, + "onclick": "", + "script": "result = {}\ndata.series[0].fields.forEach((object) => {\n result[object[\"name\"]] = object[\"values\"].buffer[0]\n});\n\nconst uint8Array = new Uint8Array(atob(result[\"data\"]).split('').map(char => char.charCodeAt(0)));\n// Create a DataView on the Uint8Array\nconst dataView = new DataView(uint8Array.buffer);\n// Get the number of elements in the array (1024 in this example)\nconst numElements = dataView.byteLength / 4;\n// Create a new Float32Array with the same number of elements\nconst x = new Float32Array(numElements);\n\nconst v_min = result[\"f_min\"];\nconst v_max = result[\"f_max\"];\nconst bw = v_max - v_min \nconst res = bw / numElements;\n\n\nconst y = new Float32Array(numElements);\n\nfor (let i = 0; i < numElements; i++) {\n y[i] = dataView.getFloat32(i * 4, true); // true specifies little-endian byte order\n x[i] = v_min + i * res\n}\nvar trace = {\n y: Array.from(y),\n x: Array.from(x)\n};\n\nreturn {data:[trace]};\n" + }, + "pluginVersion": "9.3.2", + "repeat": "data_streams", + "repeatDirection": "h", + "targets": [ + { + "command": "json.get", + "datasource": { + "type": "redis-datasource", + "uid": "redisdatastreams" + }, + "hide": false, + "keyName": "$data_streams", + "path": "$", + "query": "", + "refId": "A", + "streaming": true, + "streamingCapacity": 1000, + "streamingDataType": "DataFrame", + "streamingInterval": 1000, + "type": "json" + } + ], + "title": "Current Spectrum of $data_streams", + "type": "ae3e-plotly-panel" + }, + { + "datasource": { + "type": "redis-datasource", + "uid": "redisdatastreams" + }, + "description": "Waterfall plot of $data_streams", + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 38 + }, + "id": 7, + "maxPerRow": 4, + "options": { + "config": "", + "data": "", + "layout": { + "autosize": true, + "margin": { + "b": 20, + "l": 100, + "r": 0, + "t": 0 + }, + "xaxis": { + "autorange": true, + "range": [ + 348501945.52529186, + 349501945.5252918 + ], + "type": "linear" + } + }, + "onclick": "window.updateVariables({query:{'var-project':'test'}, partial: true})", + "script": "// FUNCTIONS\nfunction parse_data(series)\n{\n result = {}\n series.fields.forEach((object) => {\n result[object[\"name\"]] = object[\"values\"].buffer[0]\n });\n return result;\n}\n\nfunction remove_old_items(name, max_length)\n{\n var keys = Object.keys(window.sessionStorage)\n .filter(key => key.includes(name))\n .sort();\n for(let i = 0; i < keys.length - max_length; i++)\n {\n window.sessionStorage.removeItem(keys[i]);\n }\n}\n\nfunction initialize_array(n_spectra, nfft)\n{\n var data_array = new Array(n_spectra);\n for(var i = 0; i < n_spectra; i++)\n {\n data_array[i] = new Array(nfft);\n }\n return data_array;\n}\n\nfunction seconds2timestamp(seconds) {\n return new Date(parseInt(seconds)).toISOString();\n}\n\nfunction compute(n_spectra, f_min, f_max, name)\n{\n var sorted = Object.keys(window.sessionStorage).sort();\n var encoded = window.sessionStorage.getItem(sorted[0]);\n var uint8Array = new Uint8Array(atob(encoded).split('').map(char => char.charCodeAt(0)));\n var dataView = new DataView(uint8Array.buffer);\n var nfft = dataView.byteLength / 4;\n var bw = f_max - f_min\n var res = bw / nfft;\n var x = new Array(nfft);\n var y = new Array(nfft);\n var z = initialize_array(n_spectra, nfft);\n for (var i = 0; i < nfft; i++) {\n x[i] = f_min + i * res;\n z[0][i] = dataView.getFloat32(i * 4, true); // true specifies little-endian byte order\n }\n var cnt = 0;\n for(var i = 1; i < sorted.length; i++)\n {\n if(sorted[i].includes(name))\n {\n cnt = cnt + 1;\n if(cnt >= n_spectra){\n continue;\n }\n encoded = window.sessionStorage.getItem(sorted[i]);\n uint8Array = new Uint8Array(atob(encoded).split('').map(char => char.charCodeAt(0)));\n dataView = new DataView(uint8Array.buffer);\n //y[i] = seconds2timestamp(sorted[i]);\n y[cnt] = sorted[i].replace(name + \"/\",\"\").split(\".\")[0].replace(\".\");\n for (var ii = 0; ii < nfft; ii++) {\n z[cnt][ii] = dataView.getFloat32(ii * 4, true); // true specifies little-endian byte order\n }\n }\n }\n return {\"x\":x, \"y\":y, \"z\":z};\n}\n// VARIBALES\nvar n_spectra = parseInt(document.getElementById(\"var-n_spectra\").value);\nvar stream_name = data.request.scopedVars.data_streams.value.split(\":\")[1];\nvar res_dict = parse_data(data.series[0]);\nwindow.sessionStorage.setItem(\n stream_name + \"/\" + res_dict[\"timestamp\"], res_dict[\"data\"]\n);\nremove_old_items(stream_name, n_spectra);\nvar result = compute(n_spectra, res_dict[\"f_min\"], res_dict[\"f_max\"], stream_name);\n\nvar trace = [{\n x: result[\"x\"],\n y: result[\"y\"],\n z: result[\"z\"],\n type: 'heatmap'\n}];\n\nreturn {data:trace};" + }, + "repeat": "data_streams", + "repeatDirection": "h", + "targets": [ + { + "command": "json.get", + "datasource": { + "type": "redis-datasource", + "uid": "redisdatastreams" + }, + "keyName": "$data_streams", + "path": "$", + "query": "", + "refId": "A", + "streaming": true, + "streamingDataType": "DataFrame", + "type": "json" + } + ], + "title": "Waterfall plot of $data_streams", + "type": "ae3e-plotly-panel" + } \ No newline at end of file diff --git a/roles/gs_plotter/templates/spectrum_plotter_vars.json b/roles/gs_plotter/templates/spectrum_plotter_vars.json new file mode 100644 index 0000000000000000000000000000000000000000..dc4232f2bb332b4e9c3b25fc4d42fdc523bef1b4 --- /dev/null +++ b/roles/gs_plotter/templates/spectrum_plotter_vars.json @@ -0,0 +1,96 @@ +{ + "current": { + "selected": false, + "text": "32", + "value": "32" + }, + "description": "The number of spectra displayed in the waterfall plot", + "hide": 0, + "label": "Number of Spectra", + "name": "n_spectra", + "options": [ + { + "selected": false, + "text": "128", + "value": "128" + } + ], + "query": "32", + "skipUrlSync": false, + "type": "textbox" +}, +{ + "current": { + "selected": false, + "text": [ + "(empty)" + ], + "value": [ + "(empty)" + ] + }, + "datasource": { + "type": "redis-datasource", + "uid": "redisdatastreams" + }, + "definition": "KEYS {{ container_name }}*_stream", + "description": "Name of the data stream to subscribe to", + "hide": 0, + "includeAll": false, + "label": "Data stream names", + "multi": true, + "name": "data_streams", + "options": [], + "query": "KEYS {{ container_name }}*_stream", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 1, + "type": "query" +}, +{ + "auto": false, + "auto_count": 30, + "auto_min": "10s", + "current": { + "selected": false, + "text": "2000", + "value": "2000" + }, + "description": "The interval of updating the plot.", + "hide": 0, + "label": "Update in ms", + "name": "update_ms", + "options": [ + { + "selected": false, + "text": "1000", + "value": "1000" + }, + { + "selected": true, + "text": "2000", + "value": "2000" + }, + { + "selected": false, + "text": "3000", + "value": "3000" + }, + { + "selected": false, + "text": "4000", + "value": "4000" + }, + { + "selected": false, + "text": "5000", + "value": "5000" + } + ], + "query": "1000,2000,3000,4000,5000", + "queryValue": "", + "refresh": 2, + "skipUrlSync": false, + "type": "interval" +} \ No newline at end of file diff --git a/roles/influx_sidecar/defaults/main.yml b/roles/influx_sidecar/defaults/main.yml index e3d7577f99a5a917ecf9298a81f8822dffbe9a1c..43c102f1ec8e182b66c164d3e8fb3c9808948866 100644 --- a/roles/influx_sidecar/defaults/main.yml +++ b/roles/influx_sidecar/defaults/main.yml @@ -1,2 +1,2 @@ -interval: 60 +interval: 5 strategy: "event-rate" diff --git a/roles/mock_fits_writer/tasks/main.yml b/roles/mock_fits_writer/tasks/main.yml index a6ce9d3126eb06691b0328b217550f236d60c1b6..ecf3a1e0d1343a33a39880220b6be6d2798ebddd 100644 --- a/roles/mock_fits_writer/tasks/main.yml +++ b/roles/mock_fits_writer/tasks/main.yml @@ -5,6 +5,6 @@ vars: image_name: mock_fits_writer container_name: mock_fits_writer - container_cmd: "/usr/bin/python3 /src/mpikat/mpikat/pipelines/mock_fits_writer.py --host={{ edd_subnet }} --port={{ bind_port }} --fits_interface_host={{ interface_host }}" + container_cmd: "/usr/bin/python3 /src/mpikat/mpikat/pipelines/mock_fits_writer.py --host={{ edd_subnet }} --port={{ bind_port }}" tags: - always diff --git a/roles/monitoring_integration/files/check_grafana_alerts b/roles/monitoring_integration/files/check_grafana_alerts new file mode 100755 index 0000000000000000000000000000000000000000..bdeaad101eeb31d706c2d9d06f36ed4f415330e7 --- /dev/null +++ b/roles/monitoring_integration/files/check_grafana_alerts @@ -0,0 +1,108 @@ +#!/usr/bin/python3 +""" +Check grafana for existing alerts and update according services as passive checks. Create services if needed. +""" +import json +import argparse +import requests + +def get_username_and_password(ffile): + """ + looks up username and passord of the ApiUser from the grafana config + """ + + username = None + password = None + for line in ffile: + if line.startswith('//'): + continue + if 'ApiUser' in line: + username = line.split()[2].strip("'").strip('"') + elif 'password' in line: + password = line.split('=')[1].strip().strip("'").strip('"') + return username, password + +def get_grafana_alerts(uri): + """Return list of configured grafana alerts + """ + + r = requests.get("{uri}/api/prometheus/grafana/api/v1/rules".format(uri=uri)) + + if not r.status_code == 200: + raise RuntimeError('ERROR querying grafana alerts. ({}, {})'.format(response.status, response.reason)) + + data = r.json() + + alerts = [] + + for group in data['data']['groups']: + for alert in group['rules']: + alerts.append(alert) + + #print(alert['name'], alert['lastEvaluation'], alert['state']) + return alerts + + + +def create_service(uri, alert, host, username, password): + """ + Create a new service from the alert on the given host. + """ + name = 'grafana_' + alert['name'].replace(' ', '_') + print('Creating new service: {name}'.format(name=name)) + res = requests.put('{uri}/objects/services/{host}!{name}'.format(uri=uri, name=name, host=host), headers={'Accept': 'application/json'}, auth=requests.auth.HTTPBasicAuth(username, password), data=json.dumps({'templates': ['generic-service'], + "attrs": { "check_command": "passive", + "enable_passive_checks": True, + "enable_active_checks": False, + "display_name": alert['name'], + "groups": [ "EDD_services" ] + } }), verify=False) + print(json.dumps(res.json(), indent=4)) + if res.status_code != 200: + raise RuntimeError('Cannot create service {name}:{res}'.format(name=name, res=res)) + + +def post_check_result(uri, alert, host, username, password): + name = 'grafana_' + alert['name'].replace(' ', '_') + print('Posting check result for {name}'.format(name=name)) + if 'state' not in alert: + status = 3 + elif alert['state'] == 'firing': + status = 2 + else: + status = 0 + + message = alert['annotations'].get('message', 'Unknown reason') + res = requests.post('{uri}/actions/process-check-result'.format(uri=uri), headers={'Accept': 'application/json'}, + auth=requests.auth.HTTPBasicAuth(username, password), json={'type': 'Service', + 'filter': "host.name==\"{host}\" && service.name==\"{name}\"".format(host=host, name=name), + "exit_status": status, + "plugin_output": message}, verify=False) + return res.json() + + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('grafana_uri', type=str, + help='grafana instance http://HOST:PORT to access') + + parser.add_argument('icinga_uri', type=str, + help='icinga instance http://HOST:PORT to access') + + parser.add_argument('host', type=str, help='name of the icinga host to create the services on') + parser.add_argument('--icinga_file', default='/etc/icinga2/features-enabled/api.conf', help='API config file used to get user account and password') + args = parser.parse_args() + + alerts = get_grafana_alerts(args.grafana_uri) + + with open(args.icinga_file) as f: + username, password = get_username_and_password(f) + + for alert in alerts: + res = post_check_result(args.icinga_uri, alert, args.host, username, password) + if not res['results']: + print('Cannot post result. Creating service') + create_service(args.icinga_uri, alert, args.host, username, password) + + diff --git a/roles/monitoring_integration/files/check_katcp_sensor b/roles/monitoring_integration/files/check_katcp_sensor new file mode 100755 index 0000000000000000000000000000000000000000..1cf195df8da39e6b7b028666210f923b0086d3e4 --- /dev/null +++ b/roles/monitoring_integration/files/check_katcp_sensor @@ -0,0 +1,72 @@ +#!/usr/bin/python3 +""" +Check the status of katcp sensor and eventually raise an icinga warning. +""" + +import argparse +import socket +import sys + + + + +if __name__ == "__main__": + comparators = {'equal': lambda a,b: a == b, 'min': lambda a,b: a <= b, 'max': lambda a,b: a >= b } + + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('-H', '--host', dest='host', type=str, default='0.0.0.0', + help='Host interface to bind to') + parser.add_argument('-p', '--port', dest='port', type=int, default=1235, + help='Port number to bind to') + parser.add_argument('-s', '--sensor', dest='sensor', + help='Sensor to query') + parser.add_argument('-r', '--reference', dest='reference', + help='Reference sensor value') + + parser.add_argument('-c', '--comparator', dest='comparator', choices=list(comparators.keys()), + help='Comparator for value', required=True) + args = parser.parse_args() + + + # get value from + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.settimeout(10) + sock.connect((args.host, args.port)) + sock.send("?sensor-value {}\n".format(args.sensor).encode('ascii')) + + resp = "" + + print("Checking katcp sensor: {} of {}:{}".format(args.sensor, args.host, args.port)) + #print(f"Checking katcp sensor: {args.sensor} of {args.host}:{args.port}") + while True: + resp += sock.recv(4096).decode('ascii') + + if '\n' not in resp: + continue + + messages = resp.split('\n') + if not resp.endswith('\n'): + resp = messages.pop() + else: + resp = '' + + for message in messages: + resp_split = message.split() + + if message.startswith("!sensor-value"): + if resp_split[-2] == 'fail': + print("Sensor response: {}".format(message)) + #print(f"Sensor response: {message}") + sys.exit(2) + + elif message.startswith("#sensor-value"): + print("Sensor response: {}".format(message)) + #print(f"Sensor response: {message}") + if comparators[args.comparator](args.reference, resp_split[5]): + print('Check failed - {} {} {}'.format(args.reference, args.comparator, resp_split[5])) + #print(f'Check failed - {args.reference} {args.comparator} {resp_split[5]}') + sys.exit(2) + else: + print('Check passed - fail on {} {} {}'.format(args.reference, args.comparator, resp_split[5])) + #print(f'Check passed - fail on {args.reference} {args.comparator} {resp_split[5]}') + sys.exit(0) diff --git a/roles/monitoring_integration/templates/commands.conf b/roles/monitoring_integration/templates/commands.conf new file mode 100644 index 0000000000000000000000000000000000000000..a39aa5bb84a5025002acc7f4df8fc2dd7099dece --- /dev/null +++ b/roles/monitoring_integration/templates/commands.conf @@ -0,0 +1,22 @@ +object CheckCommand "katcp_sensor" { + command = [ SysconfDir + "/icinga2/edd.d/check_katcp_sensor" ] + arguments = { + "-H" = "$katcp_host$" + "-p" = "$katcp_port$" + "-s" = "$katcp_sensor$" + "-r" = "$katcp_reference$" + "-c" = "$katcp_comparator$" + } + vars.katcp_host = "" + vars.katcp_port = "1235" + vars.katcp_sensor = "" + vars.katcp_reference = "" + vars.katcp_comparator = "" +} + +object CheckCommand "grafana_alerts" { + command = [ SysconfDir + "/icinga2/edd.d/check_grafana_alerts", "$grafana_uri$", "$icinga_uri$", "$host$" ] + vars.host = "" + vars.grafana_uri= "" + vars.icinga_uri = "" +} diff --git a/roles/monitoring_integration/templates/groups.conf b/roles/monitoring_integration/templates/groups.conf new file mode 100644 index 0000000000000000000000000000000000000000..8ebb3536a3c4097fc86d4b935b562dc063fb0185 --- /dev/null +++ b/roles/monitoring_integration/templates/groups.conf @@ -0,0 +1,3 @@ +object ServiceGroup "EDD_services" { + display_name = "EDD Services" +} diff --git a/roles/monitoring_integration/templates/services.conf b/roles/monitoring_integration/templates/services.conf new file mode 100644 index 0000000000000000000000000000000000000000..3a46f9e7154f3ce0cdab4c926a8522a9eef67301 --- /dev/null +++ b/roles/monitoring_integration/templates/services.conf @@ -0,0 +1,110 @@ +object Service "EDD_master_controler_katcp" { + + import "generic-service" + host_name = "{{ interface_host }}." + DomainName + + display_name = "master-controller katcp server" + + check_command = "tcp" + vars.tcp_port = {{ master_controller_port }} + vars.tcp_expect = "katcp" + + groups = [ "EDD_services" ] +} + +object Service "EDD_master_controler_status" { + + import "generic-service" + host_name = "{{ interface_host }}." + DomainName + + display_name = "EDD master controller status" + + check_command = "katcp_sensor" + vars.katcp_host = "{{ interface_host }}" + vars.katcp_port = {{ master_controller_port }} + vars.katcp_sensor = "pipeline-status" + vars.katcp_comparator = "equal" + vars.katcp_reference = "error" + groups = [ "EDD_services" ] +} + +object Service "edd_grafana" { + + import "generic-service" + host_name = "{{ grafana_host }}." + DomainName + + display_name = "EDD grafana" + + check_command = "http" + vars.http_string = "Grafana" + vars.http_port = 3000 + + groups = [ "EDD_services" ] +} + +object Service "grafana_alerts" { + + import "generic-service" + host_name = "{{ grafana_host }}." + DomainName + + display_name = "EDD grafana alerts" + + check_command = "grafana_alerts" + vars.grafana_uri= "http://{{ grafana_host }}:3000" + vars.icinga_uri = "https://localhost:5665/v1" + vars.host = "{{ grafana_host }}." + DomainName + + groups = [ "EDD_services" ] +} + +object Service "EDD_influx" { + + import "generic-service" + host_name = "{{ influx_host }}." + DomainName + + display_name = "EDD influx server" + + check_command = "tcp" + vars.tcp_port = {{ influxdb_port }} + + groups = [ "EDD_services" ] +} + +object Service "EDD_Loki" { + + import "generic-service" + host_name = "{{ loki_host }}." + DomainName + + display_name = "EDD loki server" + + check_command = "tcp" + vars.tcp_port = {{ loki_port }} + + groups = [ "EDD_services" ] +} + +object Service "EDD_REDIS" { + + import "generic-service" + host_name = "{{ redis_host }}." + DomainName + + display_name = "EDD redis server" + + check_command = "tcp" + vars.tcp_port = {{ redis_port }} + + groups = [ "EDD_services" ] +} + +object Service "EDD_DOCKER" { + + import "generic-service" + host_name = "{{ docker_registry }}." + DomainName + + display_name = "EDD docker registry" + + check_command = "tcp" + vars.tcp_port = {{ docker_registry_port }} + + groups = [ "EDD_services" ] +} diff --git a/roles/packetizer/templates/packer-boot b/roles/packetizer/templates/packer-boot new file mode 100644 index 0000000000000000000000000000000000000000..ee2d2142a8a2a121aa45391c37bc9727128dae5d --- /dev/null +++ b/roles/packetizer/templates/packer-boot @@ -0,0 +1,3 @@ +### packer options +PACKETIZER_OPTS="--acceptInvalidFreq" + diff --git a/roles/packetizer/templates/packer-boot.service b/roles/packetizer/templates/packer-boot.service new file mode 100644 index 0000000000000000000000000000000000000000..9c1e19e5cef209b68e771357f5e61c195c8a4aa9 --- /dev/null +++ b/roles/packetizer/templates/packer-boot.service @@ -0,0 +1,16 @@ +[Unit] +Description=Packer with program option after Reboot +After=network-online.target +Wants=network-online.target +Before=packer.service + +[Service] +Type=oneshot +RemainAfterExit=yes +User=root +EnvironmentFile=-/etc/default/packer-boot +ExecStart=/usr/local/bin/packer $PACKETIZER_OPTS +TimeoutStartSec=10 + +[Install] +WantedBy=multi-user.target diff --git a/roles/pulsar_pipeline/tasks/main.yml b/roles/pulsar_pipeline/tasks/main.yml index 38e0b5e83d5ea202d83a297201876c03dd725b7c..279e25271298b4d015db9d7830afd5b05d0f8647 100644 --- a/roles/pulsar_pipeline/tasks/main.yml +++ b/roles/pulsar_pipeline/tasks/main.yml @@ -52,14 +52,20 @@ register: additional_build_files check_mode: false + - name: Touch token file to create dummy if not exist + ansible.builtin.file: + path: "{{ inventory_dir }}/data/git_token" + state: touch + - name: Copy git token copy: src: "{{ inventory_dir }}/data/git_token" dest: "{{ additional_build_files.path }}/git_token" - mode: go-rwx decrypt: yes + mode: go-rwx when: inventory_dir is defined + - name: Pulsar Pipeline include_role: name: common diff --git a/roles/ska_proto_status_server/tasks/main.yml b/roles/ska_proto_status_server/tasks/main.yml index 526c3a29663ca4eddd57eca40c17f911a1a8002d..50f2cae0c9f4f1c5182e7826bc46ff75bc143bff 100644 --- a/roles/ska_proto_status_server/tasks/main.yml +++ b/roles/ska_proto_status_server/tasks/main.yml @@ -43,6 +43,39 @@ tags: - dashboard + + +- name: Setup processing data source + uri: + url: "http://{{ grafana_host }}:{{ grafana_port }}/api/datasources" + method: POST + headers: + Content-Type: "application/json;charset=UTF-8" + Accept: "application/json" + body: "{{ lookup('template', 'processing_db.json' ) }}" + body_format: json + status_code: + - 200 + - 409 + tags: + - datasource + +- name: Setup bucardo data source + uri: + url: "http://{{ grafana_host }}:{{ grafana_port }}/api/datasources" + method: POST + headers: + Content-Type: "application/json;charset=UTF-8" + Accept: "application/json" + body: "{{ lookup('template', 'processing_db.json' ) }}" + body_format: json + status_code: + - 200 + - 409 + tags: + - datasource + + - name: Setup Process-Bot-Dashboard uri: url: "http://{{ grafana_host }}:{{ grafana_port }}/api/dashboards/db" diff --git a/roles/ska_proto_status_server/templates/bucardo_db.json b/roles/ska_proto_status_server/templates/bucardo_db.json new file mode 100644 index 0000000000000000000000000000000000000000..3fd137547546515c021a286136dd3ffa2d56d7b1 --- /dev/null +++ b/roles/ska_proto_status_server/templates/bucardo_db.json @@ -0,0 +1,39 @@ + { + "id": 10, + "uid": "IG15EBVSk", + "orgId": 1, + "name": "PostgreSQL", + "type": "postgres", + "typeName": "PostgreSQL", + "typeLogoUrl": "public/app/plugins/datasource/postgres/img/postgresql_logo.svg", + "access": "proxy", + "url": "srx02:5432", + "user": "skaprotoro", + "database": "processing", + "basicAuth": false, + "isDefault": false, + "jsonData": { + "maxOpenConns": 2, + "sslmode": "disable" + }, + "readOnly": false + }, + { + "id": 11, + "uid": "_PS92SFSk", + "orgId": 1, + "name": "PostgreSQL-BUCARDO", + "type": "postgres", + "typeName": "PostgreSQL", + "typeLogoUrl": "public/app/plugins/datasource/postgres/img/postgresql_logo.svg", + "access": "proxy", + "url": "srx02", + "user": "bucardo", + "database": "bucardo", + "basicAuth": false, + "isDefault": false, + "jsonData": { + "sslmode": "disable" + }, + "readOnly": false + } diff --git a/roles/ska_proto_status_server/templates/processing_db.json b/roles/ska_proto_status_server/templates/processing_db.json new file mode 100644 index 0000000000000000000000000000000000000000..14ef3bbc186974ba8f1b5c78bc304910082f16a0 --- /dev/null +++ b/roles/ska_proto_status_server/templates/processing_db.json @@ -0,0 +1,19 @@ +{ +"name": "Processing DB", +"type": "postgres", +"typeName": "PostgreSQL", +"typeLogoUrl": "public/app/plugins/datasource/postgres/img/postgresql_logo.svg", +"access": "proxy", +"url": "srx02:5432", +"user": "skaprotoro", +"password": "skaproto", +"database": "processing", +"basicAuth": false, +"isDefault": false, +"jsonData": { + "maxOpenConns": 2, + "sslmode": "disable" +}, +"readOnly": false +} + diff --git a/roles/ska_proto_status_server/templates/transfer_bot_dashboard.json b/roles/ska_proto_status_server/templates/transfer_bot_dashboard.json new file mode 100644 index 0000000000000000000000000000000000000000..98eafe1ea4c31df5ba50477c5586ee2c6852f558 --- /dev/null +++ b/roles/ska_proto_status_server/templates/transfer_bot_dashboard.json @@ -0,0 +1,838 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 2432, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "postgres", + "uid": "_PS92SFSk" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 10800, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "#EAB839", + "value": 3600 + }, + { + "color": "red", + "value": 7200 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true + }, + "pluginVersion": "9.3.2", + "targets": [ + { + "datasource": { + "type": "postgres", + "uid": "_PS92SFSk" + }, + "editorMode": "code", + "format": "table", + "rawQuery": true, + "rawSql": "SELECT $__time(started), EXTRACT(EPOCH FROM(now() - ended)) as duration FROM syncrun ORDER BY started DESC LIMIT 1", + "refId": "A", + "sql": { + "columns": [ + { + "parameters": [], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + } + } + ], + "title": "Time since last DB sync", + "type": "gauge" + }, + { + "datasource": { + "type": "postgres", + "uid": "JeO7-SKIk" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "bars", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "duration" + }, + "properties": [ + { + "id": "custom.axisPlacement", + "value": "right" + }, + { + "id": "custom.drawStyle", + "value": "line" + }, + { + "id": "unit", + "value": "d" + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 8, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.3.2", + "targets": [ + { + "datasource": { + "type": "postgres", + "uid": "JeO7-SKIk" + }, + "editorMode": "code", + "format": "time_series", + "hide": false, + "rawQuery": true, + "rawSql": "SELECT $__timeGroup(requested_at, 1d, 0), \nsum(case when state = 0 then 1 else 0 end) as pending,\nsum(case when state = 1 then 1 else 0 end) as staged,\nsum(case when state = 2 then 1 else 0 end) as queued,\nsum(case when state = 3 then 1 else 0 end) as progress,\nsum(case when state = 4 then 1 else 0 end) as done\n FROM transferrequest WHERE $__timeFilter(requested_at) AND destination_id = 3 GROUP BY time ORDER BY time\n", + "refId": "A", + "sql": { + "columns": [ + { + "alias": "\"time\"", + "parameters": [ + { + "name": "requested_at", + "type": "functionParameter" + } + ], + "type": "function" + }, + { + "name": "COUNT", + "parameters": [ + { + "name": "state", + "type": "functionParameter" + } + ], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "name": "requested_at", + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50, + "whereJsonTree": { + "children1": [ + { + "id": "9ab89b89-0123-4456-b89a-b18cca7924c7", + "properties": { + "value": [], + "valueSrc": [] + }, + "type": "rule" + } + ], + "id": "9b8baa8a-89ab-4cde-b012-318cc9c79e96", + "type": "group" + } + }, + "table": "transferrequest" + }, + { + "datasource": { + "type": "postgres", + "uid": "JeO7-SKIk" + }, + "editorMode": "code", + "format": "table", + "hide": false, + "rawQuery": true, + "rawSql": "SELECT $__timeGroup(requested_at, 1d, 0), \nAVG(EXTRACT (EPOCH FROM(modified_at - requested_at))) / (3600 * 24) AS DURATION\n FROM transferrequest WHERE $__timeFilter(requested_at) AND destination_id = 3 GROUP BY time ORDER BY time\n", + "refId": "B", + "sql": { + "columns": [ + { + "parameters": [], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + } + } + ], + "title": "Transfer to BONN", + "transformations": [], + "type": "timeseries" + }, + { + "datasource": { + "type": "postgres", + "uid": "_PS92SFSk" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "log": 2, + "type": "symlog" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 8 + }, + "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "postgres", + "uid": "_PS92SFSk" + }, + "editorMode": "code", + "format": "table", + "rawQuery": true, + "rawSql": "SELECT $__timeGroupAlias(started, '5m'), sum(inserts) as inserts FROM syncrun WHERE $__timeFilter(started) GROUP BY time ORDER BY time", + "refId": "A", + "sql": { + "columns": [ + { + "parameters": [], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + } + }, + { + "datasource": { + "type": "postgres", + "uid": "_PS92SFSk" + }, + "editorMode": "code", + "format": "table", + "hide": false, + "rawQuery": true, + "rawSql": "SELECT $__timeGroupAlias(started, '5m'), sum(deletes) as deletes FROM syncrun WHERE $__timeFilter(started) GROUP BY time ORDER BY time", + "refId": "B", + "sql": { + "columns": [ + { + "parameters": [], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + } + }, + { + "datasource": { + "type": "postgres", + "uid": "_PS92SFSk" + }, + "editorMode": "code", + "format": "table", + "hide": false, + "rawQuery": true, + "rawSql": "SELECT $__timeGroupAlias(started, '5m'), sum(conflicts) as conflicts FROM syncrun WHERE $__timeFilter(started) GROUP BY time ORDER BY time", + "refId": "C", + "sql": { + "columns": [ + { + "parameters": [], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + } + } + ], + "title": "Sync Status", + "type": "timeseries" + }, + { + "datasource": { + "type": "postgres", + "uid": "JeO7-SKIk" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "bars", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "duration" + }, + "properties": [ + { + "id": "custom.axisPlacement", + "value": "right" + }, + { + "id": "custom.drawStyle", + "value": "line" + }, + { + "id": "unit", + "value": "d" + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 8 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "9.3.2", + "targets": [ + { + "datasource": { + "type": "postgres", + "uid": "JeO7-SKIk" + }, + "editorMode": "code", + "format": "time_series", + "hide": false, + "rawQuery": true, + "rawSql": "SELECT $__timeGroup(requested_at, 1d, 0), \nsum(case when state = 0 then 1 else 0 end) as pending,\nsum(case when state = 1 then 1 else 0 end) as staged,\nsum(case when state = 2 then 1 else 0 end) as queued,\nsum(case when state = 3 then 1 else 0 end) as progress,\nsum(case when state = 4 then 1 else 0 end) as done\n FROM transferrequest WHERE $__timeFilter(requested_at) AND destination_id = 2 GROUP BY time ORDER BY time\n", + "refId": "A", + "sql": { + "columns": [ + { + "alias": "\"time\"", + "parameters": [ + { + "name": "requested_at", + "type": "functionParameter" + } + ], + "type": "function" + }, + { + "name": "COUNT", + "parameters": [ + { + "name": "state", + "type": "functionParameter" + } + ], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "name": "requested_at", + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50, + "whereJsonTree": { + "children1": [ + { + "id": "9ab89b89-0123-4456-b89a-b18cca7924c7", + "properties": { + "value": [], + "valueSrc": [] + }, + "type": "rule" + } + ], + "id": "9b8baa8a-89ab-4cde-b012-318cc9c79e96", + "type": "group" + } + }, + "table": "transferrequest" + }, + { + "datasource": { + "type": "postgres", + "uid": "JeO7-SKIk" + }, + "editorMode": "code", + "format": "table", + "hide": false, + "rawQuery": true, + "rawSql": "SELECT $__timeGroup(requested_at, 1d, 0), \nAVG(EXTRACT (EPOCH FROM(modified_at - requested_at))) / (3600 * 24) AS DURATION\n FROM transferrequest WHERE $__timeFilter(requested_at) AND destination_id = 2 GROUP BY time ORDER BY time\n", + "refId": "B", + "sql": { + "columns": [ + { + "parameters": [], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + } + } + ], + "title": "Transfer to GLOW", + "transformations": [], + "type": "timeseries" + }, + { + "datasource": { + "type": "postgres", + "uid": "JeO7-SKIk" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "bars", + "fillOpacity": 35, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 16 + }, + "id": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "postgres", + "uid": "JeO7-SKIk" + }, + "editorMode": "code", + "format": "table", + "rawQuery": true, + "rawSql": "SELECT $__timeGroup(\"modified_at\", 1d, 0), COUNT(modified_at) as archive FROM transferrequest WHERE $__timeFilter(\"modified_at\") AND state=4 AND DESTINATION_ID=3 GROUP BY time ORDER BY time", + "refId": "A", + "sql": { + "columns": [ + { + "parameters": [ + { + "name": "requested_at", + "type": "functionParameter" + } + ], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + }, + "table": "transferrequest" + }, + { + "datasource": { + "type": "postgres", + "uid": "JeO7-SKIk" + }, + "editorMode": "code", + "format": "table", + "hide": false, + "rawQuery": true, + "rawSql": "SELECT $__timeGroup(\"modified_at\", 1d, 0), COUNT(modified_at) as glow FROM transferrequest WHERE $__timeFilter(\"modified_at\") AND state=4 AND DESTINATION_ID=2 GROUP BY time ORDER BY time", + "refId": "B", + "sql": { + "columns": [ + { + "parameters": [], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + } + } + ], + "title": "Finished Transfers", + "type": "timeseries" + } + ], + "refresh": false, + "schemaVersion": 37, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "2023-05-10T15:44:21.594Z", + "to": "2023-12-23T22:11:09.362Z" + }, + "timepicker": {}, + "timezone": "", + "title": "Transferbot", + "uid": "yHP6hIKSk", + "version": 12, + "weekStart": "" +}