]> git.proxmox.com Git - pve-manager.git/blame - PVE/Service/pvestatd.pm
ui: node/config: use simply boolean expression for powermngt check
[pve-manager.git] / PVE / Service / pvestatd.pm
CommitLineData
efd04666
DM
1package PVE::Service::pvestatd;
2
3use strict;
4use warnings;
5
6use PVE::SafeSyslog;
7use PVE::Daemon;
8
fea39196
DC
9use JSON;
10
efd04666
DM
11use Time::HiRes qw (gettimeofday);
12use PVE::Tools qw(dir_glob_foreach file_read_firstline);
13use PVE::ProcFSTools;
41db757b 14use PVE::CpuSet;
efd04666
DM
15use Filesys::Df;
16use PVE::INotify;
0fcced16 17use PVE::Network;
efd04666
DM
18use PVE::Cluster qw(cfs_read_file);
19use PVE::Storage;
20use PVE::QemuServer;
7a108020 21use PVE::QemuServer::Monitor;
efd04666 22use PVE::LXC;
41db757b 23use PVE::LXC::Config;
efd04666
DM
24use PVE::RPCEnvironment;
25use PVE::API2::Subscription;
26use PVE::AutoBalloon;
5ea29d13 27use PVE::AccessControl;
fea39196
DC
28use PVE::Ceph::Services;
29use PVE::Ceph::Tools;
efd04666 30
f1f4bfef 31use PVE::ExtMetric;
efd04666 32use PVE::Status::Plugin;
efd04666
DM
33
34use base qw(PVE::Daemon);
35
a36565ba
AD
36my $have_sdn;
37eval {
74058057 38 require PVE::Network::SDN;
a36565ba
AD
39 $have_sdn = 1;
40};
41
efd04666
DM
42my $opt_debug;
43my $restart_request;
44
45my $nodename = PVE::INotify::nodename();
46
47my $cmdline = [$0, @ARGV];
48
49my %daemon_options = (restart_on_error => 5, stop_wait_time => 5);
50my $daemon = __PACKAGE__->new('pvestatd', $cmdline, %daemon_options);
51
52sub init {
53 my ($self) = @_;
54
55 $opt_debug = $self->{debug};
56
57 PVE::Cluster::cfs_update();
58}
59
60sub shutdown {
61 my ($self) = @_;
62
63 syslog('info' , "server closing");
64
65 # wait for children
66 1 while (waitpid(-1, POSIX::WNOHANG()) > 0);
67
68 $self->exit_daemon(0);
69}
70
71sub hup {
72 my ($self) = @_;
73
74 $restart_request = 1;
75}
76
00b58c8c
SR
77my $cached_kvm_version = '';
78my $next_flag_update_time;
79my $failed_flag_update_delay_sec = 120;
80
81sub update_supported_cpuflags {
82 my $kvm_version = PVE::QemuServer::kvm_user_version();
83
84 # only update when QEMU/KVM version has changed, as that is the only reason
85 # why flags could change without restarting pvestatd
86 return if $cached_kvm_version && $cached_kvm_version eq $kvm_version;
87
88 if ($next_flag_update_time && $next_flag_update_time > time()) {
89 return;
90 }
91 $next_flag_update_time = 0;
92
93 my $supported_cpuflags = eval { PVE::QemuServer::query_supported_cpu_flags() };
94 warn $@ if $@;
95
96 if (!$supported_cpuflags ||
97 (!$supported_cpuflags->{tcg} && !$supported_cpuflags->{kvm})) {
98 # something went wrong, clear broadcast flags and set try-again delay
99 warn "CPU flag detection failed, will try again after delay\n";
100 $next_flag_update_time = time() + $failed_flag_update_delay_sec;
101
102 $supported_cpuflags = {};
103 } else {
104 # only set cached version if there's actually something to braodcast
105 $cached_kvm_version = $kvm_version;
106 }
107
108 for my $accel ("tcg", "kvm") {
109 if ($supported_cpuflags->{$accel}) {
110 PVE::Cluster::broadcast_node_kv("cpuflags-$accel", join(' ', @{$supported_cpuflags->{$accel}}));
111 } else {
112 # clear potentially invalid data
113 PVE::Cluster::broadcast_node_kv("cpuflags-$accel", '');
114 }
115 }
116}
117
50786956
DM
118my $generate_rrd_string = sub {
119 my ($data) = @_;
120
121 return join(':', map { $_ // 'U' } @$data);
122};
123
efd04666
DM
124sub update_node_status {
125 my ($status_cfg) = @_;
126
efd04666
DM
127 my ($uptime) = PVE::ProcFSTools::read_proc_uptime();
128
78873100
TL
129 my ($avg1, $avg5, $avg15) = PVE::ProcFSTools::read_loadavg();
130 my $stat = PVE::ProcFSTools::read_proc_stat();
efd04666 131 my $cpuinfo = PVE::ProcFSTools::read_cpuinfo();
78873100 132 my $maxcpu = $cpuinfo->{cpus};
efd04666 133
00b58c8c
SR
134 update_supported_cpuflags();
135
efd04666
DM
136 my $subinfo = PVE::INotify::read_file('subscription');
137 my $sublevel = $subinfo->{level} || '';
138
78873100 139 my $netdev = PVE::ProcFSTools::read_proc_net_dev();
efd04666 140 # traffic from/to physical interface cards
78873100
TL
141 my ($netin, $netout) = (0, 0);
142 for my $dev (grep { /^$PVE::Network::PHYSICAL_NIC_RE$/ } keys %$netdev) {
efd04666
DM
143 $netin += $netdev->{$dev}->{receive};
144 $netout += $netdev->{$dev}->{transmit};
145 }
78873100 146
efd04666
DM
147 my $meminfo = PVE::ProcFSTools::read_meminfo();
148
149 my $dinfo = df('/', 1); # output is bytes
efd04666
DM
150 # everything not free is considered to be used
151 my $dused = $dinfo->{blocks} - $dinfo->{bfree};
152
78873100
TL
153 my $ctime = time();
154
50786956
DM
155 my $data = $generate_rrd_string->(
156 [$uptime, $sublevel, $ctime, $avg1, $maxcpu, $stat->{cpu}, $stat->{wait},
157 $meminfo->{memtotal}, $meminfo->{memused},
158 $meminfo->{swaptotal}, $meminfo->{swapused},
78873100
TL
159 $dinfo->{blocks}, $dused, $netin, $netout]
160 );
efd04666
DM
161 PVE::Cluster::broadcast_rrd("pve2-node/$nodename", $data);
162
5e82aaac
TL
163 my $node_metric = {
164 uptime => $uptime,
165 cpustat => $stat,
166 memory => $meminfo,
167 blockstat => $dinfo,
168 nics => $netdev,
169 };
170 $node_metric->{cpustat}->@{qw(avg1 avg5 avg15)} = ($avg1, $avg5, $avg15);
171 $node_metric->{cpustat}->{cpus} = $maxcpu;
172
87be2c19
TL
173 my $transactions = PVE::ExtMetric::transactions_start($status_cfg);
174 PVE::ExtMetric::update_all($transactions, 'node', $nodename, $node_metric, $ctime);
175 PVE::ExtMetric::transactions_finish($transactions);
efd04666
DM
176}
177
178sub auto_balloning {
179 my ($vmstatus) = @_;
180
0dd73a7f 181 my $log = sub { $opt_debug and printf @_ };
efd04666
DM
182
183 my $hostmeminfo = PVE::ProcFSTools::read_meminfo();
0dd73a7f 184 # NOTE: to debug, run 'pvestatd -d' and set memtotal here
efd04666 185 #$hostmeminfo->{memtotal} = int(2*1024*1024*1024/0.8); # you can set this to test
efd04666
DM
186 my $hostfreemem = $hostmeminfo->{memtotal} - $hostmeminfo->{memused};
187
0dd73a7f
TL
188 # try to use ~80% host memory; goal is the change amount required to achieve that
189 my $goal = int($hostmeminfo->{memtotal} * 0.8 - $hostmeminfo->{memused});
190 $log->("host goal: $goal free: $hostfreemem total: $hostmeminfo->{memtotal}\n");
efd04666
DM
191
192 my $maxchange = 100*1024*1024;
193 my $res = PVE::AutoBalloon::compute_alg1($vmstatus, $goal, $maxchange);
efd04666 194
0dd73a7f
TL
195 for my $vmid (sort keys %$res) {
196 my $target = int($res->{$vmid});
197 my $current = int($vmstatus->{$vmid}->{balloon});
198 next if $target == $current; # no need to change
199
200 $log->("BALLOON $vmid to $target (%d)\n", $target - $current);
e2509f4e 201 eval { PVE::QemuServer::Monitor::mon_cmd($vmid, "balloon", value => int($target)) };
0dd73a7f 202 warn $@ if $@;
efd04666
DM
203 }
204}
205
206sub update_qemu_status {
207 my ($status_cfg) = @_;
208
209 my $ctime = time();
efd04666
DM
210 my $vmstatus = PVE::QemuServer::vmstatus(undef, 1);
211
212 eval { auto_balloning($vmstatus); };
213 syslog('err', "auto ballooning error: $@") if $@;
214
87be2c19 215 my $transactions = PVE::ExtMetric::transactions_start($status_cfg);
efd04666
DM
216 foreach my $vmid (keys %$vmstatus) {
217 my $d = $vmstatus->{$vmid};
218 my $data;
219 my $status = $d->{qmpstatus} || $d->{status} || 'stopped';
220 my $template = $d->{template} ? $d->{template} : "0";
221 if ($d->{pid}) { # running
50786956
DM
222 $data = $generate_rrd_string->(
223 [$d->{uptime}, $d->{name}, $status, $template, $ctime, $d->{cpus}, $d->{cpu},
224 $d->{maxmem}, $d->{mem}, $d->{maxdisk}, $d->{disk},
225 $d->{netin}, $d->{netout}, $d->{diskread}, $d->{diskwrite}]);
efd04666 226 } else {
50786956
DM
227 $data = $generate_rrd_string->(
228 [0, $d->{name}, $status, $template, $ctime, $d->{cpus}, undef,
229 $d->{maxmem}, undef, $d->{maxdisk}, $d->{disk}, undef, undef, undef, undef]);
efd04666
DM
230 }
231 PVE::Cluster::broadcast_rrd("pve2.3-vm/$vmid", $data);
232
87be2c19 233 PVE::ExtMetric::update_all($transactions, 'qemu', $vmid, $d, $ctime, $nodename);
efd04666 234 }
87be2c19
TL
235
236 PVE::ExtMetric::transactions_finish($transactions);
efd04666
DM
237}
238
239sub remove_stale_lxc_consoles {
240
241 my $vmstatus = PVE::LXC::vmstatus();
242 my $pidhash = PVE::LXC::find_lxc_console_pids();
243
244 foreach my $vmid (keys %$pidhash) {
245 next if defined($vmstatus->{$vmid});
246 syslog('info', "remove stale lxc-console for CT $vmid");
247 foreach my $pid (@{$pidhash->{$vmid}}) {
248 kill(9, $pid);
249 }
250 }
251}
252
b3f1adb2
DM
253my $rebalance_error_count = {};
254
41db757b 255sub rebalance_lxc_containers {
41db757b
DM
256
257 return if !-d '/sys/fs/cgroup/cpuset/lxc'; # nothing to do...
258
259 my $all_cpus = PVE::CpuSet->new_from_cgroup('lxc', 'effective_cpus');
260 my @allowed_cpus = $all_cpus->members();
261 my $cpucount = scalar(@allowed_cpus);
127470f4 262 my $max_cpuid = $allowed_cpus[-1];
41db757b 263
127470f4 264 my @cpu_ctcount = (0) x ($max_cpuid+1);
41db757b
DM
265 my @balanced_cts;
266
0b959507
DM
267 my $modify_cpuset = sub {
268 my ($vmid, $cpuset, $newset) = @_;
269
b3f1adb2
DM
270 if (!$rebalance_error_count->{$vmid}) {
271 syslog('info', "modified cpu set for lxc/$vmid: " .
272 $newset->short_string());
273 }
274
0b959507 275 eval {
cbce367d
DM
276
277 if (-d "/sys/fs/cgroup/cpuset/lxc/$vmid/ns") {
278 # allow all, so that we can set new cpuset in /ns
279 $all_cpus->write_to_cgroup("lxc/$vmid");
280 eval {
281 $newset->write_to_cgroup("lxc/$vmid/ns");
282 };
283 if (my $err = $@) {
284 warn $err if !$rebalance_error_count->{$vmid}++;
285 # restore original
286 $cpuset->write_to_cgroup("lxc/$vmid");
287 } else {
288 # also apply to container root cgroup
289 $newset->write_to_cgroup("lxc/$vmid");
290 $rebalance_error_count->{$vmid} = 0;
291 }
0b959507 292 } else {
cbce367d 293 # old style container
0b959507 294 $newset->write_to_cgroup("lxc/$vmid");
b3f1adb2 295 $rebalance_error_count->{$vmid} = 0;
0b959507
DM
296 }
297 };
b3f1adb2
DM
298 if (my $err = $@) {
299 warn $err if !$rebalance_error_count->{$vmid}++;
300 }
0b959507
DM
301 };
302
e0dc09ad
DM
303 my $ctlist = PVE::LXC::config_list();
304
305 foreach my $vmid (sort keys %$ctlist) {
306 next if ! -d "/sys/fs/cgroup/cpuset/lxc/$vmid";
41db757b
DM
307
308 my ($conf, $cpuset);
309 eval {
310
311 $conf = PVE::LXC::Config->load_config($vmid);
312
313 $cpuset = PVE::CpuSet->new_from_cgroup("lxc/$vmid");
314 };
315 if (my $err = $@) {
316 warn $err;
317 next;
318 }
319
320 my @cpuset_members = $cpuset->members();
321
8b750abc 322 if (!PVE::LXC::Config->has_lxc_entry($conf, 'lxc.cgroup.cpuset.cpus')) {
2499255b 323
8b750abc
DM
324 my $cores = $conf->{cores} || $cpucount;
325 $cores = $cpucount if $cores > $cpucount;
41db757b 326
2499255b
DM
327 # see if the number of cores was hot-reduced or
328 # hasn't been enacted at all yet
329 my $newset = PVE::CpuSet->new();
330 if ($cores < scalar(@cpuset_members)) {
331 for (my $i = 0; $i < $cores; $i++) {
332 $newset->insert($cpuset_members[$i]);
333 }
334 } elsif ($cores > scalar(@cpuset_members)) {
335 my $count = $newset->insert(@cpuset_members);
336 foreach my $cpu (@allowed_cpus) {
337 $count += $newset->insert($cpu);
338 last if $count >= $cores;
339 }
340 } else {
341 $newset->insert(@cpuset_members);
342 }
07f9595f 343
2499255b
DM
344 # Apply hot-plugged changes if any:
345 if (!$newset->is_equal($cpuset)) {
346 @cpuset_members = $newset->members();
0b959507 347 $modify_cpuset->($vmid, $cpuset, $newset);
2499255b 348 }
07f9595f 349
2499255b
DM
350 # Note: no need to rebalance if we already use all cores
351 push @balanced_cts, [$vmid, $cores, $newset]
8b750abc 352 if defined($conf->{cores}) && ($cores != $cpucount);
2499255b 353 }
07f9595f 354
2499255b 355 foreach my $cpu (@cpuset_members) {
ccfff920 356 $cpu_ctcount[$cpu]++ if $cpu <= $max_cpuid;
07f9595f 357 }
2499255b 358 }
07f9595f 359
2499255b
DM
360 my $find_best_cpu = sub {
361 my ($cpulist, $cpu) = @_;
07f9595f 362
2499255b
DM
363 my $cur_cost = $cpu_ctcount[$cpu];
364 my $cur_cpu = $cpu;
41db757b 365
2499255b
DM
366 foreach my $candidate (@$cpulist) {
367 my $cost = $cpu_ctcount[$candidate];
368 if ($cost < ($cur_cost -1)) {
369 $cur_cost = $cost;
370 $cur_cpu = $candidate;
371 }
07f9595f
DM
372 }
373
2499255b
DM
374 return $cur_cpu;
375 };
376
377 foreach my $bct (@balanced_cts) {
378 my ($vmid, $cores, $cpuset) = @$bct;
41db757b
DM
379
380 my $newset = PVE::CpuSet->new();
381
2499255b
DM
382 my $rest = [];
383 foreach my $cpu (@allowed_cpus) {
384 next if $cpuset->has($cpu);
385 push @$rest, $cpu;
386 }
387
388 my @members = $cpuset->members();
389 foreach my $cpu (@members) {
390 my $best = &$find_best_cpu($rest, $cpu);
391 if ($best != $cpu) {
392 $cpu_ctcount[$best]++;
393 $cpu_ctcount[$cpu]--;
394 }
395 $newset->insert($best);
41db757b
DM
396 }
397
398 if (!$newset->is_equal($cpuset)) {
0b959507 399 $modify_cpuset->($vmid, $cpuset, $newset);
41db757b
DM
400 }
401 }
402}
403
efd04666
DM
404sub update_lxc_status {
405 my ($status_cfg) = @_;
406
407 my $ctime = time();
efd04666
DM
408 my $vmstatus = PVE::LXC::vmstatus();
409
87be2c19
TL
410 my $transactions = PVE::ExtMetric::transactions_start($status_cfg);
411
efd04666
DM
412 foreach my $vmid (keys %$vmstatus) {
413 my $d = $vmstatus->{$vmid};
414 my $template = $d->{template} ? $d->{template} : "0";
415 my $data;
416 if ($d->{status} eq 'running') { # running
50786956
DM
417 $data = $generate_rrd_string->(
418 [$d->{uptime}, $d->{name}, $d->{status}, $template,
419 $ctime, $d->{cpus}, $d->{cpu},
420 $d->{maxmem}, $d->{mem},
421 $d->{maxdisk}, $d->{disk},
422 $d->{netin}, $d->{netout},
423 $d->{diskread}, $d->{diskwrite}]);
efd04666 424 } else {
50786956
DM
425 $data = $generate_rrd_string->(
426 [0, $d->{name}, $d->{status}, $template, $ctime, $d->{cpus}, undef,
427 $d->{maxmem}, undef, $d->{maxdisk}, $d->{disk}, undef, undef, undef, undef]);
efd04666
DM
428 }
429 PVE::Cluster::broadcast_rrd("pve2.3-vm/$vmid", $data);
430
87be2c19 431 PVE::ExtMetric::update_all($transactions, 'lxc', $vmid, $d, $ctime, $nodename);
efd04666 432 }
87be2c19 433 PVE::ExtMetric::transactions_finish($transactions);
efd04666
DM
434}
435
436sub update_storage_status {
437 my ($status_cfg) = @_;
438
bbcfdc08 439 my $cfg = PVE::Storage::config();
efd04666 440 my $ctime = time();
efd04666
DM
441 my $info = PVE::Storage::storage_info($cfg);
442
87be2c19
TL
443 my $transactions = PVE::ExtMetric::transactions_start($status_cfg);
444
efd04666
DM
445 foreach my $storeid (keys %$info) {
446 my $d = $info->{$storeid};
447 next if !$d->{active};
448
50786956 449 my $data = $generate_rrd_string->([$ctime, $d->{total}, $d->{used}]);
efd04666
DM
450
451 my $key = "pve2-storage/${nodename}/$storeid";
452 PVE::Cluster::broadcast_rrd($key, $data);
453
87be2c19 454 PVE::ExtMetric::update_all($transactions, 'storage', $nodename, $storeid, $d, $ctime);
efd04666 455 }
87be2c19 456 PVE::ExtMetric::transactions_finish($transactions);
efd04666
DM
457}
458
5ea29d13
FG
459sub rotate_authkeys {
460 PVE::AccessControl::rotate_authkey() if !PVE::AccessControl::check_authkey(1);
461}
462
a6dff455
TL
463sub update_ceph_metadata {
464 return if !PVE::Ceph::Tools::check_ceph_inited(1); # nothing to do
465
466 PVE::Ceph::Services::broadcast_ceph_services();
a78fd21f 467
0496138e 468 PVE::Ceph::Services::broadcast_ceph_versions();
fea39196
DC
469}
470
a36565ba
AD
471sub update_sdn_status {
472
473 if($have_sdn) {
474 my ($transport_status, $vnet_status) = PVE::Network::SDN::status();
475
476 my $status = $transport_status ? encode_json($transport_status) : undef;
477 PVE::Cluster::broadcast_node_kv("sdn", $status);
478 }
479}
480
efd04666
DM
481sub update_status {
482
483 # update worker list. This is not really required and
484 # we just call this to make sure that we have a correct
485 # list in case of an unexpected crash.
8a9bf777
DM
486 my $rpcenv = PVE::RPCEnvironment::get();
487
efd04666 488 eval {
8a9bf777 489 my $tlist = $rpcenv->active_workers();
efd04666
DM
490 PVE::Cluster::broadcast_tasklist($tlist);
491 };
492 my $err = $@;
493 syslog('err', $err) if $err;
494
495 my $status_cfg = PVE::Cluster::cfs_read_file('status.cfg');
496
497 eval {
498 update_node_status($status_cfg);
499 };
500 $err = $@;
501 syslog('err', "node status update error: $err") if $err;
502
503 eval {
504 update_qemu_status($status_cfg);
505 };
506 $err = $@;
507 syslog('err', "qemu status update error: $err") if $err;
508
509 eval {
510 update_lxc_status($status_cfg);
511 };
512 $err = $@;
513 syslog('err', "lxc status update error: $err") if $err;
514
e0dc09ad
DM
515 eval {
516 rebalance_lxc_containers();
517 };
518 $err = $@;
519 syslog('err', "lxc cpuset rebalance error: $err") if $err;
520
efd04666
DM
521 eval {
522 update_storage_status($status_cfg);
523 };
524 $err = $@;
525 syslog('err', "storage status update error: $err") if $err;
526
527 eval {
528 remove_stale_lxc_consoles();
529 };
530 $err = $@;
531 syslog('err', "lxc console cleanup error: $err") if $err;
5ea29d13
FG
532
533 eval {
534 rotate_authkeys();
535 };
536 $err = $@;
537 syslog('err', "authkey rotation error: $err") if $err;
538
fea39196 539 eval {
a6dff455 540 update_ceph_metadata();
fea39196
DC
541 };
542 $err = $@;
2a8e5149 543 syslog('err', "ceph metadata update error: $err") if $err;
fea39196 544
a36565ba
AD
545 eval {
546 update_sdn_status();
547 };
548 $err = $@;
549 syslog('err', "sdn status update error: $err") if $err;
550
efd04666
DM
551}
552
553my $next_update = 0;
554
555# do not update directly after startup, because install scripts
556# have a problem with that
557my $cycle = 0;
558my $updatetime = 10;
559
560my $initial_memory_usage;
561
562sub run {
563 my ($self) = @_;
564
565 for (;;) { # forever
566
567 $next_update = time() + $updatetime;
568
569 if ($cycle) {
570 my ($ccsec, $cusec) = gettimeofday ();
571 eval {
572 # syslog('info', "start status update");
573 PVE::Cluster::cfs_update();
574 update_status();
575 };
576 my $err = $@;
577
578 if ($err) {
579 syslog('err', "status update error: $err");
580 }
581
582 my ($ccsec_end, $cusec_end) = gettimeofday ();
583 my $cptime = ($ccsec_end-$ccsec) + ($cusec_end - $cusec)/1000000;
584
585 syslog('info', sprintf("status update time (%.3f seconds)", $cptime))
586 if ($cptime > 5);
587 }
588
589 $cycle++;
590
591 my $mem = PVE::ProcFSTools::read_memory_usage();
cc3d280b 592 my $resident_kb = $mem->{resident} / 1024;
efd04666
DM
593
594 if (!defined($initial_memory_usage) || ($cycle < 10)) {
cc3d280b 595 $initial_memory_usage = $resident_kb;
efd04666 596 } else {
cc3d280b 597 my $diff = $resident_kb - $initial_memory_usage;
2112d310 598 if ($diff > 15 * 1024) {
efd04666 599 syslog ('info', "restarting server after $cycle cycles to " .
cc3d280b 600 "reduce memory usage (free $resident_kb ($diff) KB)");
efd04666
DM
601 $self->restart_daemon();
602 }
603 }
604
605 my $wcount = 0;
606 while ((time() < $next_update) &&
607 ($wcount < $updatetime) && # protect against time wrap
608 !$restart_request) { $wcount++; sleep (1); };
609
610 $self->restart_daemon() if $restart_request;
611 }
612}
613
614$daemon->register_start_command();
615$daemon->register_restart_command(1);
616$daemon->register_stop_command();
617$daemon->register_status_command();
618
619our $cmddef = {
620 start => [ __PACKAGE__, 'start', []],
621 restart => [ __PACKAGE__, 'restart', []],
622 stop => [ __PACKAGE__, 'stop', []],
623 status => [ __PACKAGE__, 'status', [], undef, sub { print shift . "\n";} ],
624};
625
efd04666
DM
6261;
627
efd04666
DM
628
629
630
631