mirror of
https://github.com/netdata/netdata.git
synced 2025-04-29 07:00:01 +00:00
remove python.d/boinc (#18397)
This commit is contained in:
parent
96fc4d5c16
commit
dde8e19a7a
7 changed files with 0 additions and 1188 deletions
|
@ -2831,7 +2831,6 @@ install(FILES src/collectors/python.d.plugin/python.d.conf
|
|||
install(FILES
|
||||
src/collectors/python.d.plugin/am2320/am2320.conf
|
||||
src/collectors/python.d.plugin/anomalies/anomalies.conf
|
||||
src/collectors/python.d.plugin/boinc/boinc.conf
|
||||
src/collectors/python.d.plugin/ceph/ceph.conf
|
||||
src/collectors/python.d.plugin/go_expvar/go_expvar.conf
|
||||
src/collectors/python.d.plugin/haproxy/haproxy.conf
|
||||
|
@ -2850,7 +2849,6 @@ install(FILES
|
|||
install(FILES
|
||||
src/collectors/python.d.plugin/am2320/am2320.chart.py
|
||||
src/collectors/python.d.plugin/anomalies/anomalies.chart.py
|
||||
src/collectors/python.d.plugin/boinc/boinc.chart.py
|
||||
src/collectors/python.d.plugin/ceph/ceph.chart.py
|
||||
src/collectors/python.d.plugin/go_expvar/go_expvar.chart.py
|
||||
src/collectors/python.d.plugin/haproxy/haproxy.chart.py
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
integrations/boinc.md
|
|
@ -1,168 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Description: BOINC netdata python.d module
|
||||
# Author: Austin S. Hemmelgarn (Ferroin)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import socket
|
||||
|
||||
from bases.FrameworkServices.SimpleService import SimpleService
|
||||
from third_party import boinc_client
|
||||
|
||||
ORDER = [
|
||||
'tasks',
|
||||
'states',
|
||||
'sched_states',
|
||||
'process_states',
|
||||
]
|
||||
|
||||
CHARTS = {
|
||||
'tasks': {
|
||||
'options': [None, 'Overall Tasks', 'tasks', 'boinc', 'boinc.tasks', 'line'],
|
||||
'lines': [
|
||||
['total', 'Total', 'absolute', 1, 1],
|
||||
['active', 'Active', 'absolute', 1, 1]
|
||||
]
|
||||
},
|
||||
'states': {
|
||||
'options': [None, 'Tasks per State', 'tasks', 'boinc', 'boinc.states', 'line'],
|
||||
'lines': [
|
||||
['new', 'New', 'absolute', 1, 1],
|
||||
['downloading', 'Downloading', 'absolute', 1, 1],
|
||||
['downloaded', 'Ready to Run', 'absolute', 1, 1],
|
||||
['comperror', 'Compute Errors', 'absolute', 1, 1],
|
||||
['uploading', 'Uploading', 'absolute', 1, 1],
|
||||
['uploaded', 'Uploaded', 'absolute', 1, 1],
|
||||
['aborted', 'Aborted', 'absolute', 1, 1],
|
||||
['upload_failed', 'Failed Uploads', 'absolute', 1, 1]
|
||||
]
|
||||
},
|
||||
'sched_states': {
|
||||
'options': [None, 'Tasks per Scheduler State', 'tasks', 'boinc', 'boinc.sched', 'line'],
|
||||
'lines': [
|
||||
['uninit_sched', 'Uninitialized', 'absolute', 1, 1],
|
||||
['preempted', 'Preempted', 'absolute', 1, 1],
|
||||
['scheduled', 'Scheduled', 'absolute', 1, 1]
|
||||
]
|
||||
},
|
||||
'process_states': {
|
||||
'options': [None, 'Tasks per Process State', 'tasks', 'boinc', 'boinc.process', 'line'],
|
||||
'lines': [
|
||||
['uninit_proc', 'Uninitialized', 'absolute', 1, 1],
|
||||
['executing', 'Executing', 'absolute', 1, 1],
|
||||
['suspended', 'Suspended', 'absolute', 1, 1],
|
||||
['aborting', 'Aborted', 'absolute', 1, 1],
|
||||
['quit', 'Quit', 'absolute', 1, 1],
|
||||
['copy_pending', 'Copy Pending', 'absolute', 1, 1]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
# A simple template used for pre-loading the return dictionary to make
|
||||
# the _get_data() method simpler.
|
||||
_DATA_TEMPLATE = {
|
||||
'total': 0,
|
||||
'active': 0,
|
||||
'new': 0,
|
||||
'downloading': 0,
|
||||
'downloaded': 0,
|
||||
'comperror': 0,
|
||||
'uploading': 0,
|
||||
'uploaded': 0,
|
||||
'aborted': 0,
|
||||
'upload_failed': 0,
|
||||
'uninit_sched': 0,
|
||||
'preempted': 0,
|
||||
'scheduled': 0,
|
||||
'uninit_proc': 0,
|
||||
'executing': 0,
|
||||
'suspended': 0,
|
||||
'aborting': 0,
|
||||
'quit': 0,
|
||||
'copy_pending': 0
|
||||
}
|
||||
|
||||
# Map task states to dimensions
|
||||
_TASK_MAP = {
|
||||
boinc_client.ResultState.NEW: 'new',
|
||||
boinc_client.ResultState.FILES_DOWNLOADING: 'downloading',
|
||||
boinc_client.ResultState.FILES_DOWNLOADED: 'downloaded',
|
||||
boinc_client.ResultState.COMPUTE_ERROR: 'comperror',
|
||||
boinc_client.ResultState.FILES_UPLOADING: 'uploading',
|
||||
boinc_client.ResultState.FILES_UPLOADED: 'uploaded',
|
||||
boinc_client.ResultState.ABORTED: 'aborted',
|
||||
boinc_client.ResultState.UPLOAD_FAILED: 'upload_failed'
|
||||
}
|
||||
|
||||
# Map scheduler states to dimensions
|
||||
_SCHED_MAP = {
|
||||
boinc_client.CpuSched.UNINITIALIZED: 'uninit_sched',
|
||||
boinc_client.CpuSched.PREEMPTED: 'preempted',
|
||||
boinc_client.CpuSched.SCHEDULED: 'scheduled',
|
||||
}
|
||||
|
||||
# Maps process states to dimensions
|
||||
_PROC_MAP = {
|
||||
boinc_client.Process.UNINITIALIZED: 'uninit_proc',
|
||||
boinc_client.Process.EXECUTING: 'executing',
|
||||
boinc_client.Process.SUSPENDED: 'suspended',
|
||||
boinc_client.Process.ABORT_PENDING: 'aborted',
|
||||
boinc_client.Process.QUIT_PENDING: 'quit',
|
||||
boinc_client.Process.COPY_PENDING: 'copy_pending'
|
||||
}
|
||||
|
||||
|
||||
class Service(SimpleService):
|
||||
def __init__(self, configuration=None, name=None):
|
||||
SimpleService.__init__(self, configuration=configuration, name=name)
|
||||
self.order = ORDER
|
||||
self.definitions = CHARTS
|
||||
self.host = self.configuration.get('host', 'localhost')
|
||||
self.port = self.configuration.get('port', 0)
|
||||
self.password = self.configuration.get('password', '')
|
||||
self.client = boinc_client.BoincClient(host=self.host, port=self.port, passwd=self.password)
|
||||
self.alive = False
|
||||
|
||||
def check(self):
|
||||
return self.connect()
|
||||
|
||||
def connect(self):
|
||||
self.client.connect()
|
||||
self.alive = self.client.connected and self.client.authorized
|
||||
return self.alive
|
||||
|
||||
def reconnect(self):
|
||||
# The client class itself actually disconnects existing
|
||||
# connections when it is told to connect, so we don't need to
|
||||
# explicitly disconnect when we're just trying to reconnect.
|
||||
return self.connect()
|
||||
|
||||
def is_alive(self):
|
||||
if not self.alive:
|
||||
return self.reconnect()
|
||||
return True
|
||||
|
||||
def _get_data(self):
|
||||
if not self.is_alive():
|
||||
return None
|
||||
|
||||
data = dict(_DATA_TEMPLATE)
|
||||
|
||||
try:
|
||||
results = self.client.get_tasks()
|
||||
except socket.error:
|
||||
self.error('Connection is dead')
|
||||
self.alive = False
|
||||
return None
|
||||
|
||||
for task in results:
|
||||
data['total'] += 1
|
||||
data[_TASK_MAP[task.state]] += 1
|
||||
try:
|
||||
if task.active_task:
|
||||
data['active'] += 1
|
||||
data[_SCHED_MAP[task.scheduler_state]] += 1
|
||||
data[_PROC_MAP[task.active_task_state]] += 1
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
return data or None
|
|
@ -1,66 +0,0 @@
|
|||
# netdata python.d.plugin configuration for boinc
|
||||
#
|
||||
# This file is in YaML format. Generally the format is:
|
||||
#
|
||||
# name: value
|
||||
#
|
||||
# There are 2 sections:
|
||||
# - global variables
|
||||
# - one or more JOBS
|
||||
#
|
||||
# JOBS allow you to collect values from multiple sources.
|
||||
# Each source will have its own set of charts.
|
||||
#
|
||||
# JOB parameters have to be indented (using spaces only, example below).
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Global Variables
|
||||
# These variables set the defaults for all JOBs, however each JOB
|
||||
# may define its own, overriding the defaults.
|
||||
|
||||
# update_every sets the default data collection frequency.
|
||||
# If unset, the python.d.plugin default is used.
|
||||
# update_every: 1
|
||||
|
||||
# priority controls the order of charts at the netdata dashboard.
|
||||
# Lower numbers move the charts towards the top of the page.
|
||||
# If unset, the default for python.d.plugin is used.
|
||||
# priority: 60000
|
||||
|
||||
# penalty indicates whether to apply penalty to update_every in case of failures.
|
||||
# Penalty will increase every 5 failed updates in a row. Maximum penalty is 10 minutes.
|
||||
# penalty: yes
|
||||
|
||||
# autodetection_retry sets the job re-check interval in seconds.
|
||||
# The job is not deleted if check fails.
|
||||
# Attempts to start the job are made once every autodetection_retry.
|
||||
# This feature is disabled by default.
|
||||
# autodetection_retry: 0
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# JOBS (data collection sources)
|
||||
#
|
||||
# The default JOBS share the same *name*. JOBS with the same name
|
||||
# are mutually exclusive. Only one of them will be allowed running at
|
||||
# any time. This allows autodetection to try several alternatives and
|
||||
# pick the one that works.
|
||||
#
|
||||
# Any number of jobs is supported.
|
||||
#
|
||||
# All python.d.plugin JOBS (for all its modules) support a set of
|
||||
# predefined parameters. These are:
|
||||
#
|
||||
# job_name:
|
||||
# name: myname # the JOB's name as it will appear at the
|
||||
# # dashboard (by default is the job_name)
|
||||
# # JOBs sharing a name are mutually exclusive
|
||||
# update_every: 1 # the JOB's data collection frequency
|
||||
# priority: 60000 # the JOB's order on the dashboard
|
||||
# penalty: yes # the JOB's penalty
|
||||
# autodetection_retry: 0 # the JOB's re-check interval in seconds
|
||||
#
|
||||
# Additionally to the above, boinc also supports the following:
|
||||
#
|
||||
# hostname: localhost # The host running the BOINC client
|
||||
# port: 31416 # The remote GUI RPC port for BOINC
|
||||
# password: '' # The remote GUI RPC password
|
|
@ -1,238 +0,0 @@
|
|||
<!--startmeta
|
||||
custom_edit_url: "https://github.com/netdata/netdata/edit/master/src/collectors/python.d.plugin/boinc/README.md"
|
||||
meta_yaml: "https://github.com/netdata/netdata/edit/master/src/collectors/python.d.plugin/boinc/metadata.yaml"
|
||||
sidebar_label: "BOINC"
|
||||
learn_status: "Published"
|
||||
learn_rel_path: "Collecting Metrics/Distributed Computing Systems"
|
||||
most_popular: False
|
||||
message: "DO NOT EDIT THIS FILE DIRECTLY, IT IS GENERATED BY THE COLLECTOR'S metadata.yaml FILE"
|
||||
endmeta-->
|
||||
|
||||
# BOINC
|
||||
|
||||
|
||||
<img src="https://netdata.cloud/img/bolt.svg" width="150"/>
|
||||
|
||||
|
||||
Plugin: python.d.plugin
|
||||
Module: boinc
|
||||
|
||||
<img src="https://img.shields.io/badge/maintained%20by-Netdata-%2300ab44" />
|
||||
|
||||
## Overview
|
||||
|
||||
This collector monitors task counts for the Berkeley Open Infrastructure Networking Computing (BOINC) distributed computing client.
|
||||
|
||||
It uses the same RPC interface that the BOINC monitoring GUI does.
|
||||
|
||||
This collector is supported on all platforms.
|
||||
|
||||
This collector supports collecting metrics from multiple instances of this integration, including remote instances.
|
||||
|
||||
|
||||
### Default Behavior
|
||||
|
||||
#### Auto-Detection
|
||||
|
||||
By default, the module will try to auto-detect the password to the RPC interface by looking in `/var/lib/boinc` for this file (this is the location most Linux distributions use for a system-wide BOINC installation), so things may just work without needing configuration for a local system.
|
||||
|
||||
#### Limits
|
||||
|
||||
The default configuration for this integration does not impose any limits on data collection.
|
||||
|
||||
#### Performance Impact
|
||||
|
||||
The default configuration for this integration is not expected to impose a significant performance impact on the system.
|
||||
|
||||
|
||||
## Metrics
|
||||
|
||||
Metrics grouped by *scope*.
|
||||
|
||||
The scope defines the instance that the metric belongs to. An instance is uniquely identified by a set of labels.
|
||||
|
||||
|
||||
|
||||
### Per BOINC instance
|
||||
|
||||
These metrics refer to the entire monitored application.
|
||||
|
||||
This scope has no labels.
|
||||
|
||||
Metrics:
|
||||
|
||||
| Metric | Dimensions | Unit |
|
||||
|:------|:----------|:----|
|
||||
| boinc.tasks | Total, Active | tasks |
|
||||
| boinc.states | New, Downloading, Ready to Run, Compute Errors, Uploading, Uploaded, Aborted, Failed Uploads | tasks |
|
||||
| boinc.sched | Uninitialized, Preempted, Scheduled | tasks |
|
||||
| boinc.process | Uninitialized, Executing, Suspended, Aborted, Quit, Copy Pending | tasks |
|
||||
|
||||
|
||||
|
||||
## Alerts
|
||||
|
||||
|
||||
The following alerts are available:
|
||||
|
||||
| Alert name | On metric | Description |
|
||||
|:------------|:----------|:------------|
|
||||
| [ boinc_total_tasks ](https://github.com/netdata/netdata/blob/master/src/health/health.d/boinc.conf) | boinc.tasks | average number of total tasks over the last 10 minutes |
|
||||
| [ boinc_active_tasks ](https://github.com/netdata/netdata/blob/master/src/health/health.d/boinc.conf) | boinc.tasks | average number of active tasks over the last 10 minutes |
|
||||
| [ boinc_compute_errors ](https://github.com/netdata/netdata/blob/master/src/health/health.d/boinc.conf) | boinc.states | average number of compute errors over the last 10 minutes |
|
||||
| [ boinc_upload_errors ](https://github.com/netdata/netdata/blob/master/src/health/health.d/boinc.conf) | boinc.states | average number of failed uploads over the last 10 minutes |
|
||||
|
||||
|
||||
## Setup
|
||||
|
||||
### Prerequisites
|
||||
|
||||
#### Boinc RPC interface
|
||||
|
||||
BOINC requires use of a password to access it's RPC interface. You can find this password in the `gui_rpc_auth.cfg` file in your BOINC directory.
|
||||
|
||||
|
||||
### Configuration
|
||||
|
||||
#### File
|
||||
|
||||
The configuration file name for this integration is `python.d/boinc.conf`.
|
||||
|
||||
|
||||
You can edit the configuration file using the `edit-config` script from the
|
||||
Netdata [config directory](/docs/netdata-agent/configuration/README.md#the-netdata-config-directory).
|
||||
|
||||
```bash
|
||||
cd /etc/netdata 2>/dev/null || cd /opt/netdata/etc/netdata
|
||||
sudo ./edit-config python.d/boinc.conf
|
||||
```
|
||||
#### Options
|
||||
|
||||
There are 2 sections:
|
||||
|
||||
* Global variables
|
||||
* One or more JOBS that can define multiple different instances to monitor.
|
||||
|
||||
The following options can be defined globally: priority, penalty, autodetection_retry, update_every, but can also be defined per JOB to override the global values.
|
||||
|
||||
Additionally, the following collapsed table contains all the options that can be configured inside a JOB definition.
|
||||
|
||||
Every configuration JOB starts with a `job_name` value which will appear in the dashboard, unless a `name` parameter is specified.
|
||||
|
||||
|
||||
<details open><summary>Config options</summary>
|
||||
|
||||
| Name | Description | Default | Required |
|
||||
|:----|:-----------|:-------|:--------:|
|
||||
| update_every | Sets the default data collection frequency. | 5 | no |
|
||||
| priority | Controls the order of charts at the netdata dashboard. | 60000 | no |
|
||||
| autodetection_retry | Sets the job re-check interval in seconds. | 0 | no |
|
||||
| penalty | Indicates whether to apply penalty to update_every in case of failures. | yes | no |
|
||||
| name | Job name. This value will overwrite the `job_name` value. JOBS with the same name are mutually exclusive. Only one of them will be allowed running at any time. This allows autodetection to try several alternatives and pick the one that works. | | no |
|
||||
| hostname | Define a hostname where boinc is running. | localhost | no |
|
||||
| port | The port of boinc RPC interface. | | no |
|
||||
| password | Provide a password to connect to a boinc RPC interface. | | no |
|
||||
|
||||
</details>
|
||||
|
||||
#### Examples
|
||||
|
||||
##### Configuration of a remote boinc instance
|
||||
|
||||
A basic JOB configuration for a remote boinc instance
|
||||
|
||||
```yaml
|
||||
remote:
|
||||
hostname: '1.2.3.4'
|
||||
port: 1234
|
||||
password: 'some-password'
|
||||
|
||||
```
|
||||
##### Multi-instance
|
||||
|
||||
> **Note**: When you define multiple jobs, their names must be unique.
|
||||
|
||||
Collecting metrics from local and remote instances.
|
||||
|
||||
|
||||
<details open><summary>Config</summary>
|
||||
|
||||
```yaml
|
||||
localhost:
|
||||
name: 'local'
|
||||
host: '127.0.0.1'
|
||||
port: 1234
|
||||
password: 'some-password'
|
||||
|
||||
remote_job:
|
||||
name: 'remote'
|
||||
host: '192.0.2.1'
|
||||
port: 1234
|
||||
password: some-other-password
|
||||
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Debug Mode
|
||||
|
||||
|
||||
To troubleshoot issues with the `boinc` collector, run the `python.d.plugin` with the debug option enabled. The output
|
||||
should give you clues as to why the collector isn't working.
|
||||
|
||||
- Navigate to the `plugins.d` directory, usually at `/usr/libexec/netdata/plugins.d/`. If that's not the case on
|
||||
your system, open `netdata.conf` and look for the `plugins` setting under `[directories]`.
|
||||
|
||||
```bash
|
||||
cd /usr/libexec/netdata/plugins.d/
|
||||
```
|
||||
|
||||
- Switch to the `netdata` user.
|
||||
|
||||
```bash
|
||||
sudo -u netdata -s
|
||||
```
|
||||
|
||||
- Run the `python.d.plugin` to debug the collector:
|
||||
|
||||
```bash
|
||||
./python.d.plugin boinc debug trace
|
||||
```
|
||||
|
||||
### Getting Logs
|
||||
|
||||
If you're encountering problems with the `boinc` collector, follow these steps to retrieve logs and identify potential issues:
|
||||
|
||||
- **Run the command** specific to your system (systemd, non-systemd, or Docker container).
|
||||
- **Examine the output** for any warnings or error messages that might indicate issues. These messages should provide clues about the root cause of the problem.
|
||||
|
||||
#### System with systemd
|
||||
|
||||
Use the following command to view logs generated since the last Netdata service restart:
|
||||
|
||||
```bash
|
||||
journalctl _SYSTEMD_INVOCATION_ID="$(systemctl show --value --property=InvocationID netdata)" --namespace=netdata --grep boinc
|
||||
```
|
||||
|
||||
#### System without systemd
|
||||
|
||||
Locate the collector log file, typically at `/var/log/netdata/collector.log`, and use `grep` to filter for collector's name:
|
||||
|
||||
```bash
|
||||
grep boinc /var/log/netdata/collector.log
|
||||
```
|
||||
|
||||
**Note**: This method shows logs from all restarts. Focus on the **latest entries** for troubleshooting current issues.
|
||||
|
||||
#### Docker Container
|
||||
|
||||
If your Netdata runs in a Docker container named "netdata" (replace if different), use this command:
|
||||
|
||||
```bash
|
||||
docker logs netdata 2>&1 | grep boinc
|
||||
```
|
||||
|
||||
|
|
@ -1,198 +0,0 @@
|
|||
plugin_name: python.d.plugin
|
||||
modules:
|
||||
- meta:
|
||||
plugin_name: python.d.plugin
|
||||
module_name: boinc
|
||||
monitored_instance:
|
||||
name: BOINC
|
||||
link: "https://boinc.berkeley.edu/"
|
||||
categories:
|
||||
- data-collection.distributed-computing-systems
|
||||
icon_filename: "bolt.svg"
|
||||
related_resources:
|
||||
integrations:
|
||||
list: []
|
||||
info_provided_to_referring_integrations:
|
||||
description: ""
|
||||
keywords:
|
||||
- boinc
|
||||
- distributed
|
||||
most_popular: false
|
||||
overview:
|
||||
data_collection:
|
||||
metrics_description: "This collector monitors task counts for the Berkeley Open Infrastructure Networking Computing (BOINC) distributed computing client."
|
||||
method_description: "It uses the same RPC interface that the BOINC monitoring GUI does."
|
||||
supported_platforms:
|
||||
include: []
|
||||
exclude: []
|
||||
multi_instance: true
|
||||
additional_permissions:
|
||||
description: ""
|
||||
default_behavior:
|
||||
auto_detection:
|
||||
description: "By default, the module will try to auto-detect the password to the RPC interface by looking in `/var/lib/boinc` for this file (this is the location most Linux distributions use for a system-wide BOINC installation), so things may just work without needing configuration for a local system."
|
||||
limits:
|
||||
description: ""
|
||||
performance_impact:
|
||||
description: ""
|
||||
setup:
|
||||
prerequisites:
|
||||
list:
|
||||
- title: "Boinc RPC interface"
|
||||
description: BOINC requires use of a password to access it's RPC interface. You can find this password in the `gui_rpc_auth.cfg` file in your BOINC directory.
|
||||
configuration:
|
||||
file:
|
||||
name: python.d/boinc.conf
|
||||
options:
|
||||
description: |
|
||||
There are 2 sections:
|
||||
|
||||
* Global variables
|
||||
* One or more JOBS that can define multiple different instances to monitor.
|
||||
|
||||
The following options can be defined globally: priority, penalty, autodetection_retry, update_every, but can also be defined per JOB to override the global values.
|
||||
|
||||
Additionally, the following collapsed table contains all the options that can be configured inside a JOB definition.
|
||||
|
||||
Every configuration JOB starts with a `job_name` value which will appear in the dashboard, unless a `name` parameter is specified.
|
||||
folding:
|
||||
title: "Config options"
|
||||
enabled: true
|
||||
list:
|
||||
- name: update_every
|
||||
description: Sets the default data collection frequency.
|
||||
default_value: 5
|
||||
required: false
|
||||
- name: priority
|
||||
description: Controls the order of charts at the netdata dashboard.
|
||||
default_value: 60000
|
||||
required: false
|
||||
- name: autodetection_retry
|
||||
description: Sets the job re-check interval in seconds.
|
||||
default_value: 0
|
||||
required: false
|
||||
- name: penalty
|
||||
description: Indicates whether to apply penalty to update_every in case of failures.
|
||||
default_value: yes
|
||||
required: false
|
||||
- name: name
|
||||
description: Job name. This value will overwrite the `job_name` value. JOBS with the same name are mutually exclusive. Only one of them will be allowed running at any time. This allows autodetection to try several alternatives and pick the one that works.
|
||||
default_value: ""
|
||||
required: false
|
||||
- name: hostname
|
||||
description: Define a hostname where boinc is running.
|
||||
default_value: "localhost"
|
||||
required: false
|
||||
- name: port
|
||||
description: The port of boinc RPC interface.
|
||||
default_value: ""
|
||||
required: false
|
||||
- name: password
|
||||
description: Provide a password to connect to a boinc RPC interface.
|
||||
default_value: ""
|
||||
required: false
|
||||
examples:
|
||||
folding:
|
||||
enabled: true
|
||||
title: "Config"
|
||||
list:
|
||||
- name: Configuration of a remote boinc instance
|
||||
description: A basic JOB configuration for a remote boinc instance
|
||||
folding:
|
||||
enabled: false
|
||||
config: |
|
||||
remote:
|
||||
hostname: '1.2.3.4'
|
||||
port: 1234
|
||||
password: 'some-password'
|
||||
- name: Multi-instance
|
||||
description: |
|
||||
> **Note**: When you define multiple jobs, their names must be unique.
|
||||
|
||||
Collecting metrics from local and remote instances.
|
||||
config: |
|
||||
localhost:
|
||||
name: 'local'
|
||||
host: '127.0.0.1'
|
||||
port: 1234
|
||||
password: 'some-password'
|
||||
|
||||
remote_job:
|
||||
name: 'remote'
|
||||
host: '192.0.2.1'
|
||||
port: 1234
|
||||
password: some-other-password
|
||||
troubleshooting:
|
||||
problems:
|
||||
list: []
|
||||
alerts:
|
||||
- name: boinc_total_tasks
|
||||
link: https://github.com/netdata/netdata/blob/master/src/health/health.d/boinc.conf
|
||||
metric: boinc.tasks
|
||||
info: average number of total tasks over the last 10 minutes
|
||||
os: "*"
|
||||
- name: boinc_active_tasks
|
||||
link: https://github.com/netdata/netdata/blob/master/src/health/health.d/boinc.conf
|
||||
metric: boinc.tasks
|
||||
info: average number of active tasks over the last 10 minutes
|
||||
os: "*"
|
||||
- name: boinc_compute_errors
|
||||
link: https://github.com/netdata/netdata/blob/master/src/health/health.d/boinc.conf
|
||||
metric: boinc.states
|
||||
info: average number of compute errors over the last 10 minutes
|
||||
os: "*"
|
||||
- name: boinc_upload_errors
|
||||
link: https://github.com/netdata/netdata/blob/master/src/health/health.d/boinc.conf
|
||||
metric: boinc.states
|
||||
info: average number of failed uploads over the last 10 minutes
|
||||
os: "*"
|
||||
metrics:
|
||||
folding:
|
||||
title: Metrics
|
||||
enabled: false
|
||||
description: ""
|
||||
availability: []
|
||||
scopes:
|
||||
- name: global
|
||||
description: "These metrics refer to the entire monitored application."
|
||||
labels: []
|
||||
metrics:
|
||||
- name: boinc.tasks
|
||||
description: Overall Tasks
|
||||
unit: "tasks"
|
||||
chart_type: line
|
||||
dimensions:
|
||||
- name: Total
|
||||
- name: Active
|
||||
- name: boinc.states
|
||||
description: Tasks per State
|
||||
unit: "tasks"
|
||||
chart_type: line
|
||||
dimensions:
|
||||
- name: New
|
||||
- name: Downloading
|
||||
- name: Ready to Run
|
||||
- name: Compute Errors
|
||||
- name: Uploading
|
||||
- name: Uploaded
|
||||
- name: Aborted
|
||||
- name: Failed Uploads
|
||||
- name: boinc.sched
|
||||
description: Tasks per Scheduler State
|
||||
unit: "tasks"
|
||||
chart_type: line
|
||||
dimensions:
|
||||
- name: Uninitialized
|
||||
- name: Preempted
|
||||
- name: Scheduled
|
||||
- name: boinc.process
|
||||
description: Tasks per Process State
|
||||
unit: "tasks"
|
||||
chart_type: line
|
||||
dimensions:
|
||||
- name: Uninitialized
|
||||
- name: Executing
|
||||
- name: Suspended
|
||||
- name: Aborted
|
||||
- name: Quit
|
||||
- name: Copy Pending
|
|
@ -1,515 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# client.py - Somewhat higher-level GUI_RPC API for BOINC core client
|
||||
#
|
||||
# Copyright (C) 2013 Rodrigo Silva (MestreLion) <linux@rodrigosilva.com>
|
||||
# Copyright (C) 2017 Austin S. Hemmelgarn
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0
|
||||
|
||||
# Based on client/boinc_cmd.cpp
|
||||
|
||||
import hashlib
|
||||
import socket
|
||||
import sys
|
||||
import time
|
||||
from functools import total_ordering
|
||||
from xml.etree import ElementTree
|
||||
|
||||
GUI_RPC_PASSWD_FILE = "/var/lib/boinc/gui_rpc_auth.cfg"
|
||||
|
||||
GUI_RPC_HOSTNAME = None # localhost
|
||||
GUI_RPC_PORT = 31416
|
||||
GUI_RPC_TIMEOUT = 1
|
||||
|
||||
class Rpc(object):
|
||||
''' Class to perform GUI RPC calls to a BOINC core client.
|
||||
Usage in a context manager ('with' block) is recommended to ensure
|
||||
disconnect() is called. Using the same instance for all calls is also
|
||||
recommended so it reuses the same socket connection
|
||||
'''
|
||||
def __init__(self, hostname="", port=0, timeout=0, text_output=False):
|
||||
self.hostname = hostname
|
||||
self.port = port
|
||||
self.timeout = timeout
|
||||
self.sock = None
|
||||
self.text_output = text_output
|
||||
|
||||
@property
|
||||
def sockargs(self):
|
||||
return (self.hostname, self.port, self.timeout)
|
||||
|
||||
def __enter__(self): self.connect(*self.sockargs); return self
|
||||
def __exit__(self, *args): self.disconnect()
|
||||
|
||||
def connect(self, hostname="", port=0, timeout=0):
|
||||
''' Connect to (hostname, port) with timeout in seconds.
|
||||
Hostname defaults to None (localhost), and port to 31416
|
||||
Calling multiple times will disconnect previous connection (if any),
|
||||
and (re-)connect to host.
|
||||
'''
|
||||
if self.sock:
|
||||
self.disconnect()
|
||||
|
||||
self.hostname = hostname or GUI_RPC_HOSTNAME
|
||||
self.port = port or GUI_RPC_PORT
|
||||
self.timeout = timeout or GUI_RPC_TIMEOUT
|
||||
|
||||
self.sock = socket.create_connection(self.sockargs[0:2], self.sockargs[2])
|
||||
|
||||
def disconnect(self):
|
||||
''' Disconnect from host. Calling multiple times is OK (idempotent)
|
||||
'''
|
||||
if self.sock:
|
||||
self.sock.close()
|
||||
self.sock = None
|
||||
|
||||
def call(self, request, text_output=None):
|
||||
''' Do an RPC call. Pack and send the XML request and return the
|
||||
unpacked reply. request can be either plain XML text or a
|
||||
xml.etree.ElementTree.Element object. Return ElementTree.Element
|
||||
or XML text according to text_output flag.
|
||||
Will auto-connect if not connected.
|
||||
'''
|
||||
if text_output is None:
|
||||
text_output = self.text_output
|
||||
|
||||
if not self.sock:
|
||||
self.connect(*self.sockargs)
|
||||
|
||||
if not isinstance(request, ElementTree.Element):
|
||||
request = ElementTree.fromstring(request)
|
||||
|
||||
# pack request
|
||||
end = '\003'
|
||||
if sys.version_info[0] < 3:
|
||||
req = "<boinc_gui_rpc_request>\n{0}\n</boinc_gui_rpc_request>\n{1}".format(ElementTree.tostring(request).replace(' />', '/>'), end)
|
||||
else:
|
||||
req = "<boinc_gui_rpc_request>\n{0}\n</boinc_gui_rpc_request>\n{1}".format(ElementTree.tostring(request, encoding='unicode').replace(' />', '/>'), end).encode()
|
||||
|
||||
try:
|
||||
self.sock.sendall(req)
|
||||
except (socket.error, socket.herror, socket.gaierror, socket.timeout):
|
||||
raise
|
||||
|
||||
req = ""
|
||||
while True:
|
||||
try:
|
||||
buf = self.sock.recv(8192)
|
||||
if not buf:
|
||||
raise socket.error("No data from socket")
|
||||
if sys.version_info[0] >= 3:
|
||||
buf = buf.decode()
|
||||
except socket.error:
|
||||
raise
|
||||
n = buf.find(end)
|
||||
if not n == -1: break
|
||||
req += buf
|
||||
req += buf[:n]
|
||||
|
||||
# unpack reply (remove root tag, ie: first and last lines)
|
||||
req = '\n'.join(req.strip().rsplit('\n')[1:-1])
|
||||
|
||||
if text_output:
|
||||
return req
|
||||
else:
|
||||
return ElementTree.fromstring(req)
|
||||
|
||||
def setattrs_from_xml(obj, xml, attrfuncdict={}):
|
||||
''' Helper to set values for attributes of a class instance by mapping
|
||||
matching tags from a XML file.
|
||||
attrfuncdict is a dict of functions to customize value data type of
|
||||
each attribute. It falls back to simple int/float/bool/str detection
|
||||
based on values defined in __init__(). This would not be needed if
|
||||
Boinc used standard RPC protocol, which includes data type in XML.
|
||||
'''
|
||||
if not isinstance(xml, ElementTree.Element):
|
||||
xml = ElementTree.fromstring(xml)
|
||||
for e in list(xml):
|
||||
if hasattr(obj, e.tag):
|
||||
attr = getattr(obj, e.tag)
|
||||
attrfunc = attrfuncdict.get(e.tag, None)
|
||||
if attrfunc is None:
|
||||
if isinstance(attr, bool): attrfunc = parse_bool
|
||||
elif isinstance(attr, int): attrfunc = parse_int
|
||||
elif isinstance(attr, float): attrfunc = parse_float
|
||||
elif isinstance(attr, str): attrfunc = parse_str
|
||||
elif isinstance(attr, list): attrfunc = parse_list
|
||||
else: attrfunc = lambda x: x
|
||||
setattr(obj, e.tag, attrfunc(e))
|
||||
else:
|
||||
pass
|
||||
#print "class missing attribute '%s': %r" % (e.tag, obj)
|
||||
return obj
|
||||
|
||||
|
||||
def parse_bool(e):
|
||||
''' Helper to convert ElementTree.Element.text to boolean.
|
||||
Treat '<foo/>' (and '<foo>[[:blank:]]</foo>') as True
|
||||
Treat '0' and 'false' as False
|
||||
'''
|
||||
if e.text is None:
|
||||
return True
|
||||
else:
|
||||
return bool(e.text) and not e.text.strip().lower() in ('0', 'false')
|
||||
|
||||
|
||||
def parse_int(e):
|
||||
''' Helper to convert ElementTree.Element.text to integer.
|
||||
Treat '<foo/>' (and '<foo></foo>') as 0
|
||||
'''
|
||||
# int(float()) allows casting to int a value expressed as float in XML
|
||||
return 0 if e.text is None else int(float(e.text.strip()))
|
||||
|
||||
|
||||
def parse_float(e):
|
||||
''' Helper to convert ElementTree.Element.text to float. '''
|
||||
return 0.0 if e.text is None else float(e.text.strip())
|
||||
|
||||
|
||||
def parse_str(e):
|
||||
''' Helper to convert ElementTree.Element.text to string. '''
|
||||
return "" if e.text is None else e.text.strip()
|
||||
|
||||
|
||||
def parse_list(e):
|
||||
''' Helper to convert ElementTree.Element to list. For now, simply return
|
||||
the list of root element's children
|
||||
'''
|
||||
return list(e)
|
||||
|
||||
|
||||
class Enum(object):
|
||||
UNKNOWN = -1 # Not in original API
|
||||
|
||||
@classmethod
|
||||
def name(cls, value):
|
||||
''' Quick-and-dirty fallback for getting the "name" of an enum item '''
|
||||
|
||||
# value as string, if it matches an enum attribute.
|
||||
# Allows short usage as Enum.name("VALUE") besides Enum.name(Enum.VALUE)
|
||||
if hasattr(cls, str(value)):
|
||||
return cls.name(getattr(cls, value, None))
|
||||
|
||||
# value not handled in subclass name()
|
||||
for k, v in cls.__dict__.items():
|
||||
if v == value:
|
||||
return k.lower().replace('_', ' ')
|
||||
|
||||
# value not found
|
||||
return cls.name(Enum.UNKNOWN)
|
||||
|
||||
|
||||
class CpuSched(Enum):
|
||||
''' values of ACTIVE_TASK::scheduler_state and ACTIVE_TASK::next_scheduler_state
|
||||
"SCHEDULED" is synonymous with "executing" except when CPU throttling
|
||||
is in use.
|
||||
'''
|
||||
UNINITIALIZED = 0
|
||||
PREEMPTED = 1
|
||||
SCHEDULED = 2
|
||||
|
||||
|
||||
class ResultState(Enum):
|
||||
''' Values of RESULT::state in client.
|
||||
THESE MUST BE IN NUMERICAL ORDER
|
||||
(because of the > comparison in RESULT::computing_done())
|
||||
see html/inc/common_defs.inc
|
||||
'''
|
||||
NEW = 0
|
||||
#// New result
|
||||
FILES_DOWNLOADING = 1
|
||||
#// Input files for result (WU, app version) are being downloaded
|
||||
FILES_DOWNLOADED = 2
|
||||
#// Files are downloaded, result can be (or is being) computed
|
||||
COMPUTE_ERROR = 3
|
||||
#// computation failed; no file upload
|
||||
FILES_UPLOADING = 4
|
||||
#// Output files for result are being uploaded
|
||||
FILES_UPLOADED = 5
|
||||
#// Files are uploaded, notify scheduling server at some point
|
||||
ABORTED = 6
|
||||
#// result was aborted
|
||||
UPLOAD_FAILED = 7
|
||||
#// some output file permanent failure
|
||||
|
||||
|
||||
class Process(Enum):
|
||||
''' values of ACTIVE_TASK::task_state '''
|
||||
UNINITIALIZED = 0
|
||||
#// process doesn't exist yet
|
||||
EXECUTING = 1
|
||||
#// process is running, as far as we know
|
||||
SUSPENDED = 9
|
||||
#// we've sent it a "suspend" message
|
||||
ABORT_PENDING = 5
|
||||
#// process exceeded limits; send "abort" message, waiting to exit
|
||||
QUIT_PENDING = 8
|
||||
#// we've sent it a "quit" message, waiting to exit
|
||||
COPY_PENDING = 10
|
||||
#// waiting for async file copies to finish
|
||||
|
||||
|
||||
class _Struct(object):
|
||||
''' base helper class with common methods for all classes derived from
|
||||
BOINC's C++ structs
|
||||
'''
|
||||
@classmethod
|
||||
def parse(cls, xml):
|
||||
return setattrs_from_xml(cls(), xml)
|
||||
|
||||
def __str__(self, indent=0):
|
||||
buf = '{0}{1}:\n'.format('\t' * indent, self.__class__.__name__)
|
||||
for attr in self.__dict__:
|
||||
value = getattr(self, attr)
|
||||
if isinstance(value, list):
|
||||
buf += '{0}\t{1} [\n'.format('\t' * indent, attr)
|
||||
for v in value: buf += '\t\t{0}\t\t,\n'.format(v)
|
||||
buf += '\t]\n'
|
||||
else:
|
||||
buf += '{0}\t{1}\t{2}\n'.format('\t' * indent,
|
||||
attr,
|
||||
value.__str__(indent+2)
|
||||
if isinstance(value, _Struct)
|
||||
else repr(value))
|
||||
return buf
|
||||
|
||||
|
||||
@total_ordering
|
||||
class VersionInfo(_Struct):
|
||||
def __init__(self, major=0, minor=0, release=0):
|
||||
self.major = major
|
||||
self.minor = minor
|
||||
self.release = release
|
||||
|
||||
@property
|
||||
def _tuple(self):
|
||||
return (self.major, self.minor, self.release)
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, self.__class__) and self._tuple == other._tuple
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
|
||||
def __gt__(self, other):
|
||||
if not isinstance(other, self.__class__):
|
||||
return NotImplemented
|
||||
return self._tuple > other._tuple
|
||||
|
||||
def __str__(self):
|
||||
return "{0}.{1}.{2}".format(self.major, self.minor, self.release)
|
||||
|
||||
def __repr__(self):
|
||||
return "{0}{1}".format(self.__class__.__name__, self._tuple)
|
||||
|
||||
|
||||
class Result(_Struct):
|
||||
''' Also called "task" in some contexts '''
|
||||
def __init__(self):
|
||||
# Names and values follow lib/gui_rpc_client.h @ RESULT
|
||||
# Order too, except when grouping contradicts client/result.cpp
|
||||
# RESULT::write_gui(), then XML order is used.
|
||||
|
||||
self.name = ""
|
||||
self.wu_name = ""
|
||||
self.version_num = 0
|
||||
#// identifies the app used
|
||||
self.plan_class = ""
|
||||
self.project_url = "" # from PROJECT.master_url
|
||||
self.report_deadline = 0.0 # seconds since epoch
|
||||
self.received_time = 0.0 # seconds since epoch
|
||||
#// when we got this from server
|
||||
self.ready_to_report = False
|
||||
#// we're ready to report this result to the server;
|
||||
#// either computation is done and all the files have been uploaded
|
||||
#// or there was an error
|
||||
self.got_server_ack = False
|
||||
#// we've received the ack for this result from the server
|
||||
self.final_cpu_time = 0.0
|
||||
self.final_elapsed_time = 0.0
|
||||
self.state = ResultState.NEW
|
||||
self.estimated_cpu_time_remaining = 0.0
|
||||
#// actually, estimated elapsed time remaining
|
||||
self.exit_status = 0
|
||||
#// return value from the application
|
||||
self.suspended_via_gui = False
|
||||
self.project_suspended_via_gui = False
|
||||
self.edf_scheduled = False
|
||||
#// temporary used to tell GUI that this result is deadline-scheduled
|
||||
self.coproc_missing = False
|
||||
#// a coproc needed by this job is missing
|
||||
#// (e.g. because user removed their GPU board).
|
||||
self.scheduler_wait = False
|
||||
self.scheduler_wait_reason = ""
|
||||
self.network_wait = False
|
||||
self.resources = ""
|
||||
#// textual description of resources used
|
||||
|
||||
#// the following defined if active
|
||||
# XML is generated in client/app.cpp ACTIVE_TASK::write_gui()
|
||||
self.active_task = False
|
||||
self.active_task_state = Process.UNINITIALIZED
|
||||
self.app_version_num = 0
|
||||
self.slot = -1
|
||||
self.pid = 0
|
||||
self.scheduler_state = CpuSched.UNINITIALIZED
|
||||
self.checkpoint_cpu_time = 0.0
|
||||
self.current_cpu_time = 0.0
|
||||
self.fraction_done = 0.0
|
||||
self.elapsed_time = 0.0
|
||||
self.swap_size = 0
|
||||
self.working_set_size_smoothed = 0.0
|
||||
self.too_large = False
|
||||
self.needs_shmem = False
|
||||
self.graphics_exec_path = ""
|
||||
self.web_graphics_url = ""
|
||||
self.remote_desktop_addr = ""
|
||||
self.slot_path = ""
|
||||
#// only present if graphics_exec_path is
|
||||
|
||||
# The following are not in original API, but are present in RPC XML reply
|
||||
self.completed_time = 0.0
|
||||
#// time when ready_to_report was set
|
||||
self.report_immediately = False
|
||||
self.working_set_size = 0
|
||||
self.page_fault_rate = 0.0
|
||||
#// derived by higher-level code
|
||||
|
||||
# The following are in API, but are NEVER in RPC XML reply. Go figure
|
||||
self.signal = 0
|
||||
|
||||
self.app = None # APP*
|
||||
self.wup = None # WORKUNIT*
|
||||
self.project = None # PROJECT*
|
||||
self.avp = None # APP_VERSION*
|
||||
|
||||
@classmethod
|
||||
def parse(cls, xml):
|
||||
if not isinstance(xml, ElementTree.Element):
|
||||
xml = ElementTree.fromstring(xml)
|
||||
|
||||
# parse main XML
|
||||
result = super(Result, cls).parse(xml)
|
||||
|
||||
# parse '<active_task>' children
|
||||
active_task = xml.find('active_task')
|
||||
if active_task is None:
|
||||
result.active_task = False # already the default after __init__()
|
||||
else:
|
||||
result.active_task = True # already the default after main parse
|
||||
result = setattrs_from_xml(result, active_task)
|
||||
|
||||
#// if CPU time is nonzero but elapsed time is zero,
|
||||
#// we must be talking to an old client.
|
||||
#// Set elapsed = CPU
|
||||
#// (easier to deal with this here than in the manager)
|
||||
if result.current_cpu_time != 0 and result.elapsed_time == 0:
|
||||
result.elapsed_time = result.current_cpu_time
|
||||
|
||||
if result.final_cpu_time != 0 and result.final_elapsed_time == 0:
|
||||
result.final_elapsed_time = result.final_cpu_time
|
||||
|
||||
return result
|
||||
|
||||
def __str__(self):
|
||||
buf = '{0}:\n'.format(self.__class__.__name__)
|
||||
for attr in self.__dict__:
|
||||
value = getattr(self, attr)
|
||||
if attr in ['received_time', 'report_deadline']:
|
||||
value = time.ctime(value)
|
||||
buf += '\t{0}\t{1}\n'.format(attr, value)
|
||||
return buf
|
||||
|
||||
|
||||
class BoincClient(object):
|
||||
|
||||
def __init__(self, host="", port=0, passwd=None):
|
||||
self.hostname = host
|
||||
self.port = port
|
||||
self.passwd = passwd
|
||||
self.rpc = Rpc(text_output=False)
|
||||
self.version = None
|
||||
self.authorized = False
|
||||
|
||||
# Informative, not authoritative. Records status of *last* RPC call,
|
||||
# but does not infer success about the *next* one.
|
||||
# Thus, it should be read *after* an RPC call, not prior to one
|
||||
self.connected = False
|
||||
|
||||
def __enter__(self): self.connect(); return self
|
||||
def __exit__(self, *args): self.disconnect()
|
||||
|
||||
def connect(self):
|
||||
try:
|
||||
self.rpc.connect(self.hostname, self.port)
|
||||
self.connected = True
|
||||
except socket.error:
|
||||
self.connected = False
|
||||
return
|
||||
self.authorized = self.authorize(self.passwd)
|
||||
self.version = self.exchange_versions()
|
||||
|
||||
def disconnect(self):
|
||||
self.rpc.disconnect()
|
||||
|
||||
def authorize(self, password):
|
||||
''' Request authorization. If password is None and we are connecting
|
||||
to localhost, try to read password from the local config file
|
||||
GUI_RPC_PASSWD_FILE. If file can't be read (not found or no
|
||||
permission to read), try to authorize with a blank password.
|
||||
If authorization is requested and fails, all subsequent calls
|
||||
will be refused with socket.error 'Connection reset by peer' (104).
|
||||
Since most local calls do no require authorization, do not attempt
|
||||
it if you're not sure about the password.
|
||||
'''
|
||||
if password is None and not self.hostname:
|
||||
password = read_gui_rpc_password() or ""
|
||||
nonce = self.rpc.call('<auth1/>').text
|
||||
authhash = hashlib.md5('{0}{1}'.format(nonce, password).encode()).hexdigest().lower()
|
||||
reply = self.rpc.call('<auth2><nonce_hash>{0}</nonce_hash></auth2>'.format(authhash))
|
||||
|
||||
if reply.tag == 'authorized':
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def exchange_versions(self):
|
||||
''' Return VersionInfo instance with core client version info '''
|
||||
return VersionInfo.parse(self.rpc.call('<exchange_versions/>'))
|
||||
|
||||
def get_tasks(self):
|
||||
''' Same as get_results(active_only=False) '''
|
||||
return self.get_results(False)
|
||||
|
||||
def get_results(self, active_only=False):
|
||||
''' Get a list of results.
|
||||
Those that are in progress will have information such as CPU time
|
||||
and fraction done. Each result includes a name;
|
||||
Use CC_STATE::lookup_result() to find this result in the current static state;
|
||||
if it's not there, call get_state() again.
|
||||
'''
|
||||
reply = self.rpc.call("<get_results><active_only>{0}</active_only></get_results>".format(1 if active_only else 0))
|
||||
if not reply.tag == 'results':
|
||||
return []
|
||||
|
||||
results = []
|
||||
for item in list(reply):
|
||||
results.append(Result.parse(item))
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def read_gui_rpc_password():
|
||||
''' Read password string from GUI_RPC_PASSWD_FILE file, trim the last CR
|
||||
(if any), and return it
|
||||
'''
|
||||
try:
|
||||
with open(GUI_RPC_PASSWD_FILE, 'r') as f:
|
||||
buf = f.read()
|
||||
if buf.endswith('\n'): return buf[:-1] # trim last CR
|
||||
else: return buf
|
||||
except IOError:
|
||||
# Permission denied or File not found.
|
||||
pass
|
Loading…
Add table
Add a link
Reference in a new issue