Passed
Push — master ( 1e13fc...1b17d5 )
by Vitalii
11:05 queued 14s
created

show_status_xml()   B

Complexity

Conditions 6
Paths 24

Size

Total Lines 46
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 6
eloc 39
c 1
b 1
f 0
nc 24
nop 1
dl 0
loc 46
rs 8.6737
1
<?php
2
// This file is part of BOINC.
3
// http://boinc.berkeley.edu
4
// Copyright (C) 2014 University of California
5
//
6
// BOINC is free software; you can redistribute it and/or modify it
7
// under the terms of the GNU Lesser General Public License
8
// as published by the Free Software Foundation,
9
// either version 3 of the License, or (at your option) any later version.
10
//
11
// BOINC is distributed in the hope that it will be useful,
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
// See the GNU Lesser General Public License for more details.
15
//
16
// You should have received a copy of the GNU Lesser General Public License
17
// along with BOINC.  If not, see <http://www.gnu.org/licenses/>.
18
19
// Get server status.
20
//
21
// default: show as web page
22
// ?xml=1:  show as XML
23
// ?counts=1: show only overall job counts, w/o caching
24
//      (for remote job submission systems)
25
// Sources of data:
26
// - daemons on this host: use "ps" to see if each is running
27
//   (this could be made more efficient using a single "ps",
28
//   or it could be cached)
29
// - daemons on other hosts: get from a cached file generated periodically
30
//   by ops/remote_server_status.php
31
//   (we can't do this ourselves because apache can't generally ssh)
32
// - apps and job counts: get from a cached file that we generate ourselves
33
34
require_once("../inc/cache.inc");
35
require_once("../inc/util.inc");
36
require_once("../inc/xml.inc");
37
require_once("../inc/boinc_db.inc");
38
require_once("../inc/server_version.inc");
39
40
if (!defined('STATUS_PAGE_TTL')) {
41
    define('STATUS_PAGE_TTL', 3600);
42
}
43
44
// trim a daemon command for display.
45
// For now, remove the cmdline args, but show the app if any
46
//
47
function command_display($cmd) {
48
    $x = explode(" -", $cmd);
49
    $prog = $x[0];
50
    $x = strpos($cmd, "-app ");
51
    if ($x) {
52
        $y = substr($cmd, $x);
53
        $y = explode(" ", $y);
54
        $app = $y[1];
55
        $prog .= " ($app)";
56
    }
57
    return $prog;
58
}
59
60
function daemon_html($d) {
61
    switch ($d->status) {
62
    case 0:
63
        $s = tra("Not Running");
64
        $c = "bg-danger";
65
        break;
66
    case 1:
67
        $s = tra("Running");
68
        $c = "bg-success";
69
        break;
70
    default:
0 ignored issues
show
Coding Style introduced by
DEFAULT keyword must be indented 4 spaces from SWITCH keyword
Loading history...
71
        $s = tra("Disabled");
72
        $c = "bg-warning";
73
        break;
74
    }
75
    echo "<tr>
76
        <td>".command_display($d->cmd)."</td>
77
        <td>$d->host</td>
78
        <td class=\"$c\"><nobr>$s</nobr></td>
79
        </tr>
80
    ";
81
}
82
83
function daemon_xml($d) {
84
    switch ($d->status) {
85
    case 0: $s = "not running"; break;
86
    case 1: $s = "running"; break;
87
    default: $s = "disabled";
0 ignored issues
show
Coding Style introduced by
DEFAULT keyword must be indented 4 spaces from SWITCH keyword
Loading history...
Coding Style introduced by
DEFAULT case must have a breaking statement
Loading history...
88
    }
89
    echo "  <daemon>
90
        <host>$d->host</host>
91
        <command>".command_display($d->cmd)."</command>
92
        <status>$s</status>
93
    </daemon>
94
";
95
}
96
97
function item_xml($name, $val) {
98
    if (!$val) $val = 0;
99
    echo "   <$name>$val</$name>\n";
100
}
101
102
function item_html($name, $val) {
103
    $name = tra($name);
104
    echo "<tr><td>$name</td><td>$val</td></tr>\n";
105
    //echo "<tr><td align=right>$name</td><td align=right>$val</td></tr>\n";
106
}
107
108
function show_status_html($x) {
109
    global $server_version, $server_version_str;
110
111
    page_head(tra("Project status"));
112
    $j = $x->jobs;
113
    $daemons = $x->daemons;
114
    start_table();
115
    echo "<tr><td>\n";
116
    echo "
117
         <h3>".tra("Server status")."</h3>
118
    ";
119
    start_table('table-striped');
120
    table_header(tra("Program"), tra("Host"), tra("Status"));
121
    foreach ($daemons->local_daemons as $d) {
122
        daemon_html($d);
123
    }
124
    foreach ($daemons->remote_daemons as $d) {
125
        daemon_html($d);
126
    }
127
    foreach ($daemons->disabled_daemons as $d) {
128
        daemon_html($d);
129
    }
130
    end_table();
131
132
    if ($daemons->cached_time) {
133
        echo "<br>Remote daemon status as of ", time_str($daemons->cached_time);
134
    }
135
    if ($daemons->missing_remote_status) {
136
        echo "<br>Status of remote daemons is missing\n";
137
    }
138
    if (function_exists('server_status_project_info')) {
139
        echo "<br>";
140
        server_status_project_info();
141
    }
142
    echo "</td><td>\n";
143
    echo "<h3>".tra("Computing status")."</h3>\n";
144
    echo "<h4>".tra("Work")."</h4>\n";
145
    start_table('table-striped');
146
    item_html("Tasks ready to send", $j->results_ready_to_send);
147
    item_html("Tasks in progress", $j->results_in_progress);
148
    item_html("Workunits waiting for validation", $j->wus_need_validate);
149
    item_html("Workunits waiting for assimilation", $j->wus_need_assimilate);
150
    item_html("Workunits waiting for file deletion", $j->wus_need_file_delete);
151
    item_html("Tasks waiting for file deletion", $j->results_need_file_delete);
152
    item_html("Transitioner backlog (hours)", number_format($j->transitioner_backlog, 2));
153
    end_table();
154
    echo "<h4>".tra("Users")."</h4>\n";
155
    start_table('table-striped');
156
    item_html("With credit", $j->users_with_credit);
157
    item_html("With recent credit", $j->users_with_recent_credit);
158
    item_html("Registered in past 24 hours", $j->users_past_24_hours);
159
    end_table();
160
    echo "<h4>".tra("Computers")."</h4>\n";
161
    start_table('table-striped');
162
    item_html("With credit", $j->hosts_with_credit);
163
    item_html("With recent credit", $j->hosts_with_recent_credit);
164
    item_html("Registered in past 24 hours", $j->hosts_past_24_hours);
165
    item_html("Current GigaFLOPS", round($j->flops, 2));
166
    end_table();
167
    echo "</td></tr>\n";
168
    end_table();
169
    echo "<h3>".tra("Tasks by application")."</h3>\n";
170
    start_table('table-striped');
171
    table_header(
172
        tra("Application"),
173
        tra("Unsent"),
174
        tra("In progress"),
175
        tra("Runtime of recent tasks in hours: average, min, max"),
176
        tra("Users in last 24 hours")
177
    );
178
    foreach ($j->apps as $app) {
179
        if ($app->info) {
180
            $avg = round($app->info->avg, 2);
181
            $min = round($app->info->min, 2);
182
            $max = round($app->info->max, 2);
183
            $x = $max?"$avg ($min - $max)":"---";
184
            $u = $app->info->users;
185
        } else {
186
            $x = '---';
187
            $u = '---';
188
        }
189
        echo "<tr>
190
            <td>$app->user_friendly_name</td>
191
            <td>$app->unsent</td>
192
            <td>$app->in_progress</td>
193
            <td>$x</td>
194
            <td>$u</td>
195
            </tr>
196
        ";
197
    }
198
    end_table();
199
200
    // show server software version.
201
    // If it's a release (minor# is even) link to github branch
202
    //
203
    echo "Server software version: $server_version_str";
204
    if ($server_version[1]%2 == 0) {
205
        $url = sprintf("%s/%d/%d.%d",
206
            "https://github.com/BOINC/boinc/tree/server_release",
207
            $server_version[0],
208
            $server_version[0],
209
            $server_version[1]
210
        );
211
        echo " <a href=\"$url\">View source on Github</a>.";
212
    }
213
    echo "<br>\n";
214
215
    if ($j->db_revision) {
216
        echo tra("Database schema version: "), $j->db_revision;
217
    }
218
    echo "<p>Task data as of ".time_str($j->cached_time);
219
    page_tail();
220
}
221
222
function show_status_xml($x) {
223
    xml_header();
224
    echo "<server_status>\n<daemon_status>\n";
225
226
    $daemons = $x->daemons;
227
    foreach ($daemons->local_daemons as $d) {
228
        daemon_xml($d);
229
    }
230
    foreach ($daemons->remote_daemons as $d) {
231
        daemon_xml($d);
232
    }
233
    foreach ($daemons->disabled_daemons as $d) {
234
        daemon_xml($d);
235
    }
236
    echo "</daemon_status>\n<database_file_states>\n";
237
    $j = $x->jobs;
238
    item_xml("results_ready_to_send", $j->results_ready_to_send);
239
    item_xml("results_in_progress", $j->results_in_progress);
240
    item_xml("workunits_waiting_for_validation", $j->wus_need_validate);
241
    item_xml("workunits_waiting_for_assimilation", $j->wus_need_assimilate);
242
    item_xml("workunits_waiting_for_deletion", $j->wus_need_file_delete);
243
    item_xml("results_waiting_for_deletion", $j->results_need_file_delete);
244
    item_xml("transitioner_backlog_hours", $j->transitioner_backlog);
245
    item_xml("users_with_recent_credit", $j->users_with_recent_credit);
246
    item_xml("users_with_credit", $j->users_with_credit);
247
    item_xml("users_registered_in_past_24_hours", $j->users_past_24_hours);
248
    item_xml("hosts_with_recent_credit", $j->hosts_with_recent_credit);
249
    item_xml("hosts_with_credit", $j->hosts_with_credit);
250
    item_xml("hosts_registered_in_past_24_hours", $j->hosts_past_24_hours);
251
    item_xml("current_floating_point_speed", $j->flops);
252
    echo "<tasks_by_app>\n";
253
    foreach ($j->apps as $app) {
254
        echo "<app>\n";
255
        item_xml("id", $app->id);
256
        item_xml("name", $app->name);
257
        item_xml("unsent", $app->unsent);
258
        item_xml("in_progress", $app->in_progress);
259
        if ($app->info) {
260
            item_xml("avg_runtime", $app->info->avg);
261
            item_xml("min_runtime", $app->info->min);
262
            item_xml("max_runtime", $app->info->max);
263
            item_xml("users", $app->info->users);
264
        }
265
        echo "</app>\n";
266
    }
267
    echo "</tasks_by_app>
268
</database_file_states>
269
</server_status>
270
";
271
}
272
273
function local_daemon_running($cmd, $pidname, $host) {
274
    if (!$pidname) {
275
        $cmd = trim($cmd);
276
        $x = explode(" ", $cmd);
277
        $prog = $x[0];
278
        $pidname = $prog . '.pid';
279
    }
280
    $path = "../../pid_$host/$pidname";
281
    if (is_file($path)) {
282
        $pid = file_get_contents($path);
283
        if ($pid) {
284
            $pid = trim($pid);
285
            $out = Array();
0 ignored issues
show
Coding Style introduced by
Array keyword should be lower case; expected "array" but found "Array"
Loading history...
Coding Style introduced by
As per coding-style, PHP keywords should be in lowercase; expected array, but found Array.
Loading history...
286
            exec("ps -ww $pid", $out);
287
            foreach ($out as $y) {
288
                if (strstr($y, (string)$pid)) return 1;
289
            }
290
        }
291
    }
292
    return 0;
293
}
294
295
// returns a data structure of the form
296
// local_daemons: array of
297
//   cmd, status
298
// remote_daemons: array of
299
//   cmd, host, status
300
// disabled_daemons: array of
301
//   cmd, host
302
//
303
function get_daemon_status() {
304
    $c = simplexml_load_file("../../config.xml");
305
    if (!$c) {
0 ignored issues
show
introduced by
$c is of type SimpleXMLElement, thus it always evaluated to true.
Loading history...
306
        die("can't parse config file\n");
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
307
    }
308
    $daemons = $c->daemons;
309
    $config = $c->config;
310
    $main_host = trim((string)$config->host);
311
    $master_url = trim((string)$config->master_url);
312
    $u = parse_url($master_url);
313
    if (!array_key_exists("host", $u)) {
314
        print_r($u);
315
        die("can't parse URL $master_url");
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
316
    }
317
    $master_host = $u["host"];
318
    if ($config->www_host) {
319
        $web_host = trim((string) $config->www_host);
320
    } else {
321
        $web_host = $main_host;
322
    }
323
    if ($config->sched_host) {
324
        $sched_host = trim((string) $config->sched_host);
325
    } else {
326
        $sched_host = $main_host;
327
    }
328
    $have_remote = false;
329
    $local_daemons = array();
330
    $disabled_daemons = array();
331
332
    // the upload and download servers are sort of daemons too
333
    //
334
    $url = trim((string) $config->download_url);
335
    $u = parse_url($url);
336
    $h = $u["host"];
337
    if ($h == $master_host) {
338
        $y = new StdClass;
339
        $y->cmd = "Download server";
340
        $y->host = $h;
341
        $y->status = 1;
342
        $local_daemons[] = $y;
343
    } else {
344
        $have_remote = true;
345
    }
346
    $url = trim((string) $config->upload_url);
347
    $u = parse_url($url);
348
    $h = $u["host"];
349
    if ($h == $master_host) {
350
        $y = new StdClass;
351
        $y->cmd = "Upload server";
352
        $y->host = $h;
353
        $y->status = !file_exists("../../stop_upload");;
354
        $local_daemons[] = $y;
355
    } else {
356
        $have_remote = true;
357
    }
358
359
    // Scheduler is a daemon too
360
    //
361
    if ($sched_host == $main_host) {
362
        $y = new StdClass;
363
        $y->cmd = "Scheduler";
364
        $y->host = $sched_host;
365
        $y->status = !file_exists("../../stop_sched");;
366
        $local_daemons[] = $y;
367
    } else {
368
        $have_remote = true;
369
    }
370
371
    foreach ($daemons->daemon as $d) {
372
        if ((int)$d->disabled != 0) {
373
            $x = new StdClass;
374
            $x->cmd = (string)$d->cmd;
375
            $x->host = (string)$d->host;
376
            if (!$x->host) $x->host = $main_host;
377
            $x->status = -1;
378
            $disabled_daemons[] = $x;
379
            continue;
380
        }
381
        $host = $d->host?(string)$d->host:$main_host;
382
        if ($host != $web_host) {
383
            $have_remote = true;
384
            continue;
385
        }
386
        $x = new StdClass;
387
        $x->cmd = (string)$d->cmd;
388
        $x->status = local_daemon_running($x->cmd, trim($d->pid_file), $web_host);
389
        $x->host = $web_host;
390
        $local_daemons[] = $x;
391
392
    }
393
394
    $x = new StdClass;
395
    $x->local_daemons = $local_daemons;
396
    $x->disabled_daemons = $disabled_daemons;
397
    $x->missing_remote_status = false;
398
    $x->cached_time = 0;
399
    $x->remote_daemons = array();
400
    if ($have_remote) {
401
        $f = @file_get_contents("../cache/remote_server_status");
402
        if ($f) {
403
            $x->remote_daemons = unserialize($f);
404
            $x->cached_time = filemtime("../cache/remote_server_status");
405
        } else {
406
            $x->missing_remote_status = true;
407
        }
408
    }
409
    return $x;
410
}
411
412
function get_job_status() {
413
    $s = unserialize(get_cached_data(STATUS_PAGE_TTL, "job_status"));
0 ignored issues
show
Bug introduced by
It seems like get_cached_data(STATUS_PAGE_TTL, 'job_status') can also be of type false and null; however, parameter $data of unserialize() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

413
    $s = unserialize(/** @scrutinizer ignore-type */ get_cached_data(STATUS_PAGE_TTL, "job_status"));
Loading history...
414
    if ($s) {
415
        return $s;
416
    }
417
418
    $s = new StdClass;
419
    $apps = BoincApp::enum("deprecated=0");
420
    foreach ($apps as $app) {
421
        $info = BoincDB::get()->lookup_fields("result", "stdClass",
422
            "ceil(avg(elapsed_time)/3600*100)/100 as avg,
423
            ceil(min(elapsed_time)/3600*100)/100 as min,
424
            ceil(max(elapsed_time)/3600*100)/100 as max,
425
            count(distinct userid) as users",
426
            "appid = $app->id
427
            AND validate_state=1
428
            AND received_time > (unix_timestamp()-86400)
429
            "
430
        );
431
        // $info fields will be null if app has no results
432
        if ($info->avg) {
433
            $app->info = $info;
434
        } else {
435
            $app->info = null;
436
        }
437
        $app->unsent = BoincResult::count("appid=$app->id and server_state=2");
438
        $app->in_progress = BoincResult::count("appid=$app->id and server_state=4");
439
    }
440
    $s->apps = $apps;
441
    $s->results_ready_to_send = BoincResult::count("server_state=2");
442
    $s->results_in_progress = BoincResult::count("server_state=4");
443
    $s->results_need_file_delete = BoincResult::count("file_delete_state=1");
444
    $s->wus_need_validate = BoincWorkunit::count("need_validate=1");
445
    $s->wus_need_assimilate = BoincWorkunit::count("assimilate_state=1");
446
    $s->wus_need_file_delete = BoincWorkunit::count("file_delete_state=1");
447
    $x = BoincDB::get()->lookup_fields("workunit", "stdClass", "MIN(transition_time) as min", "TRUE");
448
    $gap = (time() - $x->min)/3600;
449
    if (($gap < 0) || ($x->min == 0)) {
450
        $gap = 0;
451
    }
452
    $s->transitioner_backlog = $gap;
453
    $s->users_with_recent_credit = BoincUser::count("expavg_credit>1");
454
    $s->users_with_credit = BoincUser::count("total_credit>1");
455
    $s->users_past_24_hours = BoincUser::count("create_time > (unix_timestamp() - 86400)");
456
    $s->hosts_with_recent_credit = BoincHost::count("expavg_credit>1");
457
    $s->hosts_with_credit = BoincHost::count("total_credit>1");
458
    $s->hosts_past_24_hours = BoincHost::count("create_time > (unix_timestamp() - 86400)");
459
    $s->flops = BoincUser::sum("expavg_credit")/200;
460
461
    $s->db_revision = null;
462
    if (file_exists("../../db_revision")) {
463
        $s->db_revision = trim(file_get_contents("../../db_revision"));
464
    }
465
466
    $s->cached_time = time();
467
    $e = set_cached_data(STATUS_PAGE_TTL, serialize($s), "job_status");
468
    if ($e) echo "set_cached_data(): $e\n";
469
    return $s;
470
}
471
472
function show_counts_xml() {
473
    xml_header();
474
    echo "<job_counts>\n";
475
    item_xml('results_ready_to_send', BoincResult::count("server_state=2"));
476
    item_xml('results_in_progress', BoincResult::count("server_state=4"));
477
    item_xml('results_need_file_delete', BoincResult::count("file_delete_state=1"));
478
    item_xml('wus_need_validate', BoincWorkunit::count("need_validate=1"));
479
    item_xml('wus_need_assimilate', BoincWorkunit::count("assimilate_state=1"));
480
    item_xml('wus_need_file_delete', BoincWorkunit::count("file_delete_state=1"));
481
    echo "</job_counts>\n";
482
}
483
484
function main() {
485
    if (get_int('counts', true)) {
486
        show_counts_xml();
487
    } else {
488
        $x = new StdClass;
489
        $x->daemons = get_daemon_status();
490
        $x->jobs = get_job_status();
491
        if (get_int('xml', true)) {
492
            show_status_xml($x);
493
        } else {
494
            show_status_html($x);
495
        }
496
    }
497
}
498
499
main();
500
?>
501