metrics: provide release metrics ingestion and dashboard.
This commit is contained in:
4
dist/package/openSUSE-release-tools.spec
vendored
4
dist/package/openSUSE-release-tools.spec
vendored
@@ -373,6 +373,7 @@ fi
|
||||
%exclude %{_datadir}/%{source_dir}/manager_42.py
|
||||
%exclude %{_datadir}/%{source_dir}/metrics
|
||||
%exclude %{_datadir}/%{source_dir}/metrics.py
|
||||
%exclude %{_datadir}/%{source_dir}/metrics_release.py
|
||||
%exclude %{_datadir}/%{source_dir}/pkglistgen.py
|
||||
%exclude %{_datadir}/%{source_dir}/repo_checker.pl
|
||||
%exclude %{_datadir}/%{source_dir}/repo_checker.py
|
||||
@@ -446,12 +447,15 @@ fi
|
||||
%{_bindir}/osrt-metrics
|
||||
%{_datadir}/%{source_dir}/metrics
|
||||
%{_datadir}/%{source_dir}/metrics.py
|
||||
%{_datadir}/%{source_dir}/metrics_release.py
|
||||
# To avoid adding grafana as BuildRequires since it does not live in same repo.
|
||||
%dir %attr(0750, grafana, grafana) %{_localstatedir}/lib/grafana
|
||||
%dir %{_localstatedir}/lib/grafana/dashboards
|
||||
%{_localstatedir}/lib/grafana/dashboards/%{name}
|
||||
%{_unitdir}/osrt-metrics@.service
|
||||
%{_unitdir}/osrt-metrics@.timer
|
||||
%{_unitdir}/osrt-metrics-release@.service
|
||||
%{_unitdir}/osrt-metrics-release@.timer
|
||||
|
||||
%files repo-checker
|
||||
%defattr(-,root,root,-)
|
||||
|
@@ -11,6 +11,7 @@ import subprocess
|
||||
import sys
|
||||
import yaml
|
||||
|
||||
import metrics_release
|
||||
import osc.conf
|
||||
import osc.core
|
||||
import osclib.conf
|
||||
@@ -350,6 +351,10 @@ def main(args):
|
||||
osc.conf.get_config(override_apiurl=args.apiurl)
|
||||
osc.conf.config['debug'] = args.debug
|
||||
|
||||
metrics_release.ingest(client)
|
||||
if args.release_only:
|
||||
return
|
||||
|
||||
# Use separate cache since it is persistent.
|
||||
Cache.CACHE_DIR = os.path.expanduser('~/.cache/osc-plugin-factory-metrics')
|
||||
if args.wipe_cache:
|
||||
@@ -384,6 +389,7 @@ if __name__ == '__main__':
|
||||
parser.add_argument('--user', default='root', help='InfluxDB user')
|
||||
parser.add_argument('--password', default='root', help='InfluxDB password')
|
||||
parser.add_argument('--wipe-cache', action='store_true', help='wipe GET request cache before executing')
|
||||
parser.add_argument('--release-only', action='store_true', help='ingest release metrics only')
|
||||
args = parser.parse_args()
|
||||
|
||||
sys.exit(main(args))
|
||||
|
654
metrics/grafana/release.json
Normal file
654
metrics/grafana/release.json
Normal file
@@ -0,0 +1,654 @@
|
||||
{
|
||||
"__inputs": [
|
||||
{
|
||||
"name": "DS_TUMBLEWEED_SNAPSHOTS - AWS",
|
||||
"label": "Tumbleweed Snapshots - AWS",
|
||||
"description": "",
|
||||
"type": "datasource",
|
||||
"pluginId": "cloudwatch",
|
||||
"pluginName": "CloudWatch"
|
||||
}
|
||||
],
|
||||
"__requires": [
|
||||
{
|
||||
"type": "datasource",
|
||||
"id": "cloudwatch",
|
||||
"name": "CloudWatch",
|
||||
"version": "5.0.0"
|
||||
},
|
||||
{
|
||||
"type": "grafana",
|
||||
"id": "grafana",
|
||||
"name": "Grafana",
|
||||
"version": "5.0.0"
|
||||
},
|
||||
{
|
||||
"type": "panel",
|
||||
"id": "graph",
|
||||
"name": "Graph",
|
||||
"version": "5.0.0"
|
||||
}
|
||||
],
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": "-- Grafana --",
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "Metrics pertaining to release feedback.",
|
||||
"editable": true,
|
||||
"gnetId": null,
|
||||
"graphTooltip": 0,
|
||||
"id": null,
|
||||
"iteration": 1520567435112,
|
||||
"links": [],
|
||||
"panels": [
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "$project",
|
||||
"description": "Estimation of release stability based on feedback.",
|
||||
"fill": 1,
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 4,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"show": false,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"links": [],
|
||||
"nullPointMode": "null",
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"alias": "$col",
|
||||
"groupBy": [],
|
||||
"measurement": "release_score",
|
||||
"orderByTime": "ASC",
|
||||
"policy": "default",
|
||||
"refId": "A",
|
||||
"resultFormat": "time_series",
|
||||
"select": [
|
||||
[
|
||||
{
|
||||
"params": [
|
||||
"score"
|
||||
],
|
||||
"type": "field"
|
||||
}
|
||||
]
|
||||
],
|
||||
"tags": []
|
||||
}
|
||||
],
|
||||
"thresholds": [
|
||||
{
|
||||
"colorMode": "critical",
|
||||
"fill": false,
|
||||
"line": true,
|
||||
"op": "lt",
|
||||
"value": 70
|
||||
},
|
||||
{
|
||||
"colorMode": "warning",
|
||||
"fill": false,
|
||||
"line": true,
|
||||
"op": "lt",
|
||||
"value": 90
|
||||
}
|
||||
],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Stability Score",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "$project",
|
||||
"description": "Mailing list references to releases.",
|
||||
"fill": 1,
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 9
|
||||
},
|
||||
"id": 6,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"links": [],
|
||||
"nullPointMode": "null",
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"alias": "$col",
|
||||
"groupBy": [],
|
||||
"measurement": "release_mail",
|
||||
"orderByTime": "ASC",
|
||||
"policy": "default",
|
||||
"refId": "A",
|
||||
"resultFormat": "time_series",
|
||||
"select": [
|
||||
[
|
||||
{
|
||||
"params": [
|
||||
"reference_count"
|
||||
],
|
||||
"type": "field"
|
||||
},
|
||||
{
|
||||
"params": [
|
||||
"reference count"
|
||||
],
|
||||
"type": "alias"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"params": [
|
||||
"thread_count"
|
||||
],
|
||||
"type": "field"
|
||||
},
|
||||
{
|
||||
"params": [
|
||||
"thread count"
|
||||
],
|
||||
"type": "alias"
|
||||
}
|
||||
]
|
||||
],
|
||||
"tags": []
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Mail",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "$project",
|
||||
"description": "Number of bugs filed after a release.",
|
||||
"fill": 1,
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 18
|
||||
},
|
||||
"id": 8,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"links": [],
|
||||
"nullPointMode": "null",
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"alias": "$col",
|
||||
"groupBy": [],
|
||||
"measurement": "release_bug",
|
||||
"orderByTime": "ASC",
|
||||
"policy": "default",
|
||||
"refId": "A",
|
||||
"resultFormat": "time_series",
|
||||
"select": [
|
||||
[
|
||||
{
|
||||
"params": [
|
||||
"bug_count"
|
||||
],
|
||||
"type": "field"
|
||||
},
|
||||
{
|
||||
"params": [
|
||||
"bug count"
|
||||
],
|
||||
"type": "alias"
|
||||
}
|
||||
]
|
||||
],
|
||||
"tags": []
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Bug",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "$project",
|
||||
"description": "Total count of binaries and the number changed in a release.",
|
||||
"fill": 1,
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 27
|
||||
},
|
||||
"id": 10,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"links": [],
|
||||
"nullPointMode": "null",
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"alias": "$col",
|
||||
"groupBy": [],
|
||||
"measurement": "release_snapshot",
|
||||
"orderByTime": "ASC",
|
||||
"policy": "default",
|
||||
"refId": "A",
|
||||
"resultFormat": "time_series",
|
||||
"select": [
|
||||
[
|
||||
{
|
||||
"params": [
|
||||
"binary_count"
|
||||
],
|
||||
"type": "field"
|
||||
},
|
||||
{
|
||||
"params": [
|
||||
"binary count"
|
||||
],
|
||||
"type": "alias"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"params": [
|
||||
"binary_unique_count"
|
||||
],
|
||||
"type": "field"
|
||||
},
|
||||
{
|
||||
"params": [
|
||||
"binary unique count"
|
||||
],
|
||||
"type": "alias"
|
||||
}
|
||||
]
|
||||
],
|
||||
"tags": []
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Binary",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TUMBLEWEED_SNAPSHOTS - AWS}",
|
||||
"description": "The combined storage used by all Tumbleweed Snapshots stored as part of download.tumbleweed.boombatower.com.",
|
||||
"fill": 1,
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 36
|
||||
},
|
||||
"id": 2,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"links": [],
|
||||
"nullPointMode": "connected",
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [
|
||||
{
|
||||
"alias": "count",
|
||||
"yaxis": 2
|
||||
}
|
||||
],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"alias": "size",
|
||||
"dimensions": {
|
||||
"BucketName": "download.tumbleweed.boombatower.com",
|
||||
"StorageType": "StandardStorage"
|
||||
},
|
||||
"metricName": "BucketSizeBytes",
|
||||
"namespace": "AWS/S3",
|
||||
"period": "",
|
||||
"refId": "A",
|
||||
"region": "default",
|
||||
"statistics": [
|
||||
"Average"
|
||||
]
|
||||
},
|
||||
{
|
||||
"alias": "count",
|
||||
"dimensions": {
|
||||
"BucketName": "download.tumbleweed.boombatower.com",
|
||||
"StorageType": "AllStorageTypes"
|
||||
},
|
||||
"metricName": "NumberOfObjects",
|
||||
"namespace": "AWS/S3",
|
||||
"period": "",
|
||||
"refId": "B",
|
||||
"region": "default",
|
||||
"statistics": [
|
||||
"Average"
|
||||
]
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "S3 Storage Consumption",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "decbytes",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"schemaVersion": 16,
|
||||
"style": "dark",
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": [
|
||||
{
|
||||
"current": {
|
||||
"text": "openSUSE:Factory",
|
||||
"value": "openSUSE:Factory"
|
||||
},
|
||||
"hide": 0,
|
||||
"label": null,
|
||||
"name": "project",
|
||||
"options": [],
|
||||
"query": "influxdb",
|
||||
"refresh": 1,
|
||||
"regex": "/openSUSE:.*/",
|
||||
"type": "datasource"
|
||||
}
|
||||
]
|
||||
},
|
||||
"time": {
|
||||
"from": "now-90d",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {
|
||||
"refresh_intervals": [
|
||||
"5s",
|
||||
"10s",
|
||||
"30s",
|
||||
"1m",
|
||||
"5m",
|
||||
"15m",
|
||||
"30m",
|
||||
"1h",
|
||||
"2h",
|
||||
"1d"
|
||||
],
|
||||
"time_options": [
|
||||
"5m",
|
||||
"15m",
|
||||
"1h",
|
||||
"6h",
|
||||
"12h",
|
||||
"24h",
|
||||
"2d",
|
||||
"7d",
|
||||
"30d"
|
||||
]
|
||||
},
|
||||
"timezone": "",
|
||||
"title": "OSRT: Release",
|
||||
"uid": "3Hg0iSgiz",
|
||||
"version": 14
|
||||
}
|
55
metrics_release.py
Executable file
55
metrics_release.py
Executable file
@@ -0,0 +1,55 @@
|
||||
from dateutil.parser import parse as date_parse
|
||||
from metrics import timestamp
|
||||
import requests
|
||||
from urlparse import urljoin
|
||||
import yaml
|
||||
|
||||
BASEURL = 'http://review.tumbleweed.boombatower.com/data/'
|
||||
|
||||
def data_load(name):
|
||||
response = requests.get(urljoin(BASEURL, '{}.yaml'.format(name)))
|
||||
return yaml.safe_load(response.text)
|
||||
|
||||
def data_write(client, measurement, points):
|
||||
client.drop_measurement(measurement)
|
||||
client.write_points(points, 's')
|
||||
|
||||
def ingest_data(client, name):
|
||||
data = data_load(name)
|
||||
|
||||
measurement = 'release_{}'.format(name)
|
||||
map_func = globals()['map_{}'.format(name)]
|
||||
points = []
|
||||
for release, details in data.items():
|
||||
points.append({
|
||||
'measurement': measurement,
|
||||
'fields': map_func(details),
|
||||
'time': timestamp(date_parse(release)),
|
||||
})
|
||||
|
||||
data_write(client, measurement, points)
|
||||
print('wrote {} for {}'.format(len(points), name))
|
||||
|
||||
def map_bug(bugs):
|
||||
return {
|
||||
'bug_count': len(bugs),
|
||||
}
|
||||
|
||||
def map_mail(details):
|
||||
return {
|
||||
'reference_count': details['reference_count'],
|
||||
'thread_count': details['thread_count'],
|
||||
}
|
||||
|
||||
def map_score(details):
|
||||
return details
|
||||
|
||||
def map_snapshot(details):
|
||||
return {
|
||||
'binary_count': details['binary_count'],
|
||||
'binary_unique_count': details['binary_unique_count'],
|
||||
}
|
||||
|
||||
def ingest(client):
|
||||
for name in ['bug', 'mail', 'score', 'snapshot']:
|
||||
ingest_data(client, name)
|
10
systemd/osrt-metrics-release@.service
Normal file
10
systemd/osrt-metrics-release@.service
Normal file
@@ -0,0 +1,10 @@
|
||||
[Unit]
|
||||
Description=openSUSE Release Tools: metrics release for %i
|
||||
|
||||
[Service]
|
||||
User=osrt-metrics
|
||||
SyslogIdentifier=osrt-metrics
|
||||
ExecStart=/usr/bin/osrt-metrics --debug --release-only -p "%i"
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
10
systemd/osrt-metrics-release@.timer
Normal file
10
systemd/osrt-metrics-release@.timer
Normal file
@@ -0,0 +1,10 @@
|
||||
[Unit]
|
||||
Description=openSUSE Release Tools: metrics release for %i
|
||||
|
||||
[Timer]
|
||||
OnBootSec=120
|
||||
OnCalendar=daily
|
||||
Unit=osrt-metrics-release@%i.service
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
Reference in New Issue
Block a user