1
+# -*- coding: utf-8 -*-
2
+# Description: BOINC netdata python.d module
3
+# Author: Austin S. Hemmelgarn (Ferroin)
4
+# SPDX-License-Identifier: GPL-3.0+
5
+
6
+import platform
7
+import socket
8
+
9
+from copy import deepcopy
10
+
11
+from bases.FrameworkServices.SimpleService import SimpleService
12
+
13
+from third_party import boinc_client
14
+
15
+
16
+ORDER = ['tasks', 'states', 'sched_states', 'process_states']
17
+
18
+CHARTS = {
19
+ 'tasks': {
20
+ 'options': [None, 'Overall Tasks', 'tasks', 'boinc', 'boinc.tasks', 'line'],
21
+ 'lines': [
22
+ ['total', 'Total', 'absolute', 1, 1],
23
+ ['active', 'Active', 'absolute', 1, 1]
24
+ ]
25
+ },
26
+ 'states': {
27
+ 'options': [None, 'Tasks per State', 'tasks', 'boinc', 'boinc.states', 'line'],
28
+ 'lines': [
29
+ ['new', 'New', 'absolute', 1, 1],
30
+ ['downloading', 'Downloading', 'absolute', 1, 1],
31
+ ['downloaded', 'Ready to Run', 'absolute', 1, 1],
32
+ ['comperror', 'Compute Errors', 'absolute', 1, 1],
33
+ ['uploading', 'Uploading', 'absolute', 1, 1],
34
+ ['uploaded', 'Uploaded', 'absolute', 1, 1],
35
+ ['aborted', 'Aborted', 'absolute', 1, 1],
36
+ ['upload_failed', 'Failed Uploads', 'absolute', 1, 1]
37
+ ]
38
+ },
39
+ 'sched_states': {
40
+ 'options': [None, 'Tasks per Scheduler State', 'tasks', 'boinc', 'boinc.sched', 'line'],
41
+ 'lines': [
42
+ ['uninit_sched', 'Uninitialized', 'absolute', 1, 1],
43
+ ['preempted', 'Preempted', 'absolute', 1, 1],
44
+ ['scheduled', 'Scheduled', 'absolute', 1, 1]
45
+ ]
46
+ },
47
+ 'process_states': {
48
+ 'options': [None, 'Tasks per Process State', 'tasks', 'boinc', 'boinc.process', 'line'],
49
+ 'lines': [
50
+ ['uninit_proc', 'Uninitialized', 'absolute', 1, 1],
51
+ ['executing', 'Executing', 'absolute', 1, 1],
52
+ ['suspended', 'Suspended', 'absolute', 1, 1],
53
+ ['aborting', 'Aborted', 'absolute', 1, 1],
54
+ ['quit', 'Quit', 'absolute', 1, 1],
55
+ ['copy_pending', 'Copy Pending', 'absolute', 1, 1]
56
+ ]
57
+ }
58
+}
59
+
60
+# A simple template used for pre-loading the return dictionary to make
61
+# the _get_data() method simpler.
62
+_DATA_TEMPLATE = {
63
+ 'total': 0,
64
+ 'active': 0,
65
+ 'new': 0,
66
+ 'downloading': 0,
67
+ 'downloaded': 0,
68
+ 'comperror': 0,
69
+ 'uploading': 0,
70
+ 'uploaded': 0,
71
+ 'aborted': 0,
72
+ 'upload_failed': 0,
73
+ 'uninit_sched': 0,
74
+ 'preempted': 0,
75
+ 'scheduled': 0,
76
+ 'uninit_proc': 0,
77
+ 'executing': 0,
78
+ 'suspended': 0,
79
+ 'aborting': 0,
80
+ 'quit': 0,
81
+ 'copy_pending': 0
82
+}
83
+
84
+# Map task states to dimensions
85
+_TASK_MAP = {
86
+ boinc_client.ResultState.NEW: 'new',
87
+ boinc_client.ResultState.FILES_DOWNLOADING: 'downloading',
88
+ boinc_client.ResultState.FILES_DOWNLOADED: 'downloaded',
89
+ boinc_client.ResultState.COMPUTE_ERROR: 'comperror',
90
+ boinc_client.ResultState.FILES_UPLOADING: 'uploading',
91
+ boinc_client.ResultState.FILES_UPLOADED: 'uploaded',
92
+ boinc_client.ResultState.ABORTED: 'aborted',
93
+ boinc_client.ResultState.UPLOAD_FAILED: 'upload_failed'
94
+}
95
+
96
+# Map scheduler states to dimensions
97
+_SCHED_MAP = {
98
+ boinc_client.CpuSched.UNINITIALIZED: 'uninit_sched',
99
+ boinc_client.CpuSched.PREEMPTED: 'preempted',
100
+ boinc_client.CpuSched.SCHEDULED: 'scheduled',
101
+}
102
+
103
+# Maps process states to dimensions
104
+_PROC_MAP = {
105
+ boinc_client.Process.UNINITIALIZED: 'uninit_proc',
106
+ boinc_client.Process.EXECUTING: 'executing',
107
+ boinc_client.Process.SUSPENDED: 'suspended',
108
+ boinc_client.Process.ABORT_PENDING: 'aborted',
109
+ boinc_client.Process.QUIT_PENDING: 'quit',
110
+ boinc_client.Process.COPY_PENDING: 'copy_pending'
111
+}
112
+
113
+
114
+class Service(SimpleService):
115
+ def __init__(self, configuration=None, name=None):
116
+ SimpleService.__init__(self, configuration=configuration, name=name)
117
+ self.order = ORDER
118
+ self.definitions = CHARTS
119
+ self.host = self.configuration.get('host', 'localhost')
120
+ self.port = self.configuration.get('port', 0)
121
+ self.password = self.configuration.get('password', '')
122
+ self.client = boinc_client.BoincClient(host=self.host, port=self.port, passwd=self.password)
123
+ self.alive = False
124
+
125
+ def check(self):
126
+ if platform.system() != 'Linux':
127
+ self.error('Only supported on Linux.')
128
+ return False
129
+ self.connect()
130
+ if self.client.connected and self.client.authorized:
131
+ self.alive = True
132
+ else:
133
+ self.alive = False
134
+ return self.alive
135
+
136
+ def connect(self):
137
+ self.client.connect()
138
+
139
+ def reconnect(self):
140
+ try:
141
+ self.client.disconnect()
142
+ except socket.error:
143
+ pass
144
+ self.client.connect()
145
+ if self.client.connected and self.client.authorized:
146
+ self.alive = True
147
+ else:
148
+ self.alive = False
149
+ return self.alive
150
+
151
+ def is_alive(self):
152
+ if (not self.alive) or \
153
+ self.client.rpc.sock.getsockopt(socket.IPPROTO_TCP, socket.TCP_INFO, 0) != 1:
154
+ return self.reconnect()
155
+ return True
156
+
157
+ def _get_data(self):
158
+ if not self.is_alive():
159
+ return None
160
+ data = deepcopy(_DATA_TEMPLATE)
161
+ results = []
162
+ try:
163
+ results = self.client.get_tasks()
164
+ except socket.error:
165
+ self.error('Connection is dead')
166
+ self.alive = False
167
+ return None
168
+ for task in results:
169
+ data['total'] += 1
170
+ data[_TASK_MAP[task.state]] += 1
171
+ try:
172
+ if task.active_task:
173
+ data['active'] += 1
174
+ data[_SCHED_MAP[task.scheduler_state]] += 1
175
+ data[_PROC_MAP[task.active_task_state]] += 1
176
+ except AttributeError:
177
+ pass
178
+ return data