]> git.proxmox.com Git - pve-ha-manager.git/blame - src/PVE/HA/Env/PVE2.pm
change include order to register resource plugin correctly
[pve-ha-manager.git] / src / PVE / HA / Env / PVE2.pm
CommitLineData
714a4016
DM
1package PVE::HA::Env::PVE2;
2
3use strict;
4use warnings;
76737af5
DM
5use POSIX qw(:errno_h :fcntl_h);
6use IO::File;
115805fd 7use IO::Socket::UNIX;
714a4016
DM
8
9use PVE::SafeSyslog;
10use PVE::Tools;
119656b9 11use PVE::Cluster qw(cfs_register_file cfs_read_file cfs_write_file cfs_lock_file);
022e4e79
DM
12use PVE::INotify;
13use PVE::RPCEnvironment;
714a4016 14
a89ff919 15use PVE::HA::Tools ':exit_codes';
714a4016 16use PVE::HA::Env;
ce216792 17use PVE::HA::Config;
9e5ea8f7
DM
18use PVE::HA::Resources;
19use PVE::HA::Resources::PVEVM;
20use PVE::HA::Resources::PVECT;
714a4016 21
9e5ea8f7
DM
22PVE::HA::Resources::PVEVM->register();
23PVE::HA::Resources::PVECT->register();
24
25PVE::HA::Resources->init();
022e4e79 26
007fcc8b
DM
27my $lockdir = "/etc/pve/priv/lock";
28
714a4016
DM
29sub new {
30 my ($this, $nodename) = @_;
31
32 die "missing nodename" if !$nodename;
33
34 my $class = ref($this) || $this;
35
36 my $self = bless {}, $class;
37
38 $self->{nodename} = $nodename;
39
40 return $self;
41}
42
43sub nodename {
44 my ($self) = @_;
45
46 return $self->{nodename};
47}
48
dd9c0c9d
TL
49sub hardware {
50 my ($self) = @_;
51
52 die "hardware is for testing and simulation only";
53}
54
714a4016
DM
55sub read_manager_status {
56 my ($self) = @_;
714a4016 57
139a9b90 58 return PVE::HA::Config::read_manager_status();
714a4016
DM
59}
60
61sub write_manager_status {
62 my ($self, $status_obj) = @_;
63f6a08c 63
139a9b90 64 PVE::HA::Config::write_manager_status($status_obj);
714a4016
DM
65}
66
c4a221bc
DM
67sub read_lrm_status {
68 my ($self, $node) = @_;
69
70 $node = $self->{nodename} if !defined($node);
71
139a9b90 72 return PVE::HA::Config::read_lrm_status($node);
c4a221bc
DM
73}
74
75sub write_lrm_status {
76 my ($self, $status_obj) = @_;
77
6cbcb5f7 78 my $node = $self->{nodename};
63f6a08c 79
139a9b90
DM
80 PVE::HA::Config::write_lrm_status($node, $status_obj);
81}
c4a221bc 82
cde77779 83sub is_node_shutdown {
d42219a3
TL
84 my ($self) = @_;
85
cde77779 86 my $shutdown = 0;
d42219a3
TL
87
88 my $code = sub {
89 my $line = shift;
90
cde77779 91 $shutdown = 1 if ($line =~ m/shutdown\.target/);
d42219a3
TL
92 };
93
94 my $cmd = ['/bin/systemctl', 'list-jobs'];
95 eval { PVE::Tools::run_command($cmd, outfunc => $code, noerr => 1); };
96
cde77779 97 return $shutdown;
d42219a3
TL
98}
99
139a9b90
DM
100sub queue_crm_commands {
101 my ($self, $cmd) = @_;
c4a221bc 102
139a9b90
DM
103 return PVE::HA::Config::queue_crm_commands($cmd);
104}
105
106sub read_crm_commands {
107 my ($self) = @_;
108
109 return PVE::HA::Config::read_crm_commands();
c4a221bc
DM
110}
111
b83b4ae8 112sub service_config_exists {
714a4016 113 my ($self) = @_;
63f6a08c 114
b83b4ae8
DM
115 return PVE::HA::Config::resources_config_exists();
116}
714a4016 117
b83b4ae8
DM
118sub read_service_config {
119 my ($self) = @_;
ce216792 120
b83b4ae8 121 my $res = PVE::HA::Config::read_resources_config();
63f6a08c 122
ce216792
DM
123 my $vmlist = PVE::Cluster::get_vmlist();
124 my $conf = {};
125
126 foreach my $sid (keys %{$res->{ids}}) {
127 my $d = $res->{ids}->{$sid};
6ca2edcd 128 my (undef, undef, $name) = PVE::HA::Tools::parse_sid($sid);
7a19642e 129 $d->{state} = 'enabled' if !defined($d->{state});
ea4443cc
TL
130 $d->{max_restart} = 1 if !defined($d->{max_restart});
131 $d->{max_relocate} = 1 if !defined($d->{max_relocate});
303a08aa 132 if (PVE::HA::Resources->lookup($d->{type})) {
b47a7a1b 133 if (my $vmd = $vmlist->{ids}->{$name}) {
ce216792 134 if (!$vmd) {
b47a7a1b 135 warn "no such VM '$name'\n";
ce216792
DM
136 } else {
137 $d->{node} = $vmd->{node};
138 $conf->{$sid} = $d;
139 }
140 } else {
141 if (defined($d->{node})) {
142 $conf->{$sid} = $d;
143 } else {
144 warn "service '$sid' without node\n";
145 }
146 }
147 }
148 }
63f6a08c 149
ce216792 150 return $conf;
714a4016
DM
151}
152
8456bde2 153sub change_service_location {
6da27e23 154 my ($self, $sid, $current_node, $new_node) = @_;
8456bde2 155
6ca2edcd 156 my (undef, $type, $name) = PVE::HA::Tools::parse_sid($sid);
6da27e23 157
303a08aa
TL
158 if(my $plugin = PVE::HA::Resources->lookup($type)) {
159 my $old = $plugin->config_file($name, $current_node);
160 my $new = $plugin->config_file($name, $new_node);
6da27e23
DM
161 rename($old, $new) ||
162 die "rename '$old' to '$new' failed - $!\n";
163 } else {
164 die "implement me";
165 }
8456bde2
DM
166}
167
abc920b4
DM
168sub read_group_config {
169 my ($self) = @_;
170
139a9b90 171 return PVE::HA::Config::read_group_config();
3b996922
DM
172}
173
714a4016
DM
174# this should return a hash containing info
175# what nodes are members and online.
176sub get_node_info {
177 my ($self) = @_;
178
d706ef8b 179 my ($node_info, $quorate) = ({}, 0);
63f6a08c 180
d706ef8b
DM
181 my $nodename = $self->{nodename};
182
183 $quorate = PVE::Cluster::check_cfs_quorum(1) || 0;
184
185 my $members = PVE::Cluster::get_members();
186
187 foreach my $node (keys %$members) {
188 my $d = $members->{$node};
63f6a08c 189 $node_info->{$node}->{online} = $d->{online};
d706ef8b 190 }
63f6a08c 191
d706ef8b 192 $node_info->{$nodename}->{online} = 1; # local node is always up
63f6a08c 193
d706ef8b 194 return ($node_info, $quorate);
714a4016
DM
195}
196
197sub log {
198 my ($self, $level, $msg) = @_;
199
200 chomp $msg;
201
202 syslog($level, $msg);
203}
204
007fcc8b
DM
205my $last_lock_status = {};
206
207sub get_pve_lock {
208 my ($self, $lockid) = @_;
714a4016 209
007fcc8b 210 my $got_lock = 0;
4d24e7db 211
4d24e7db
DM
212 my $filename = "$lockdir/$lockid";
213
007fcc8b
DM
214 my $last = $last_lock_status->{$lockid} || 0;
215
216 my $ctime = time();
4d24e7db 217
75aca181
DM
218 my $retry = 0;
219 my $retry_timeout = 100; # fixme: what timeout
63f6a08c 220
4d24e7db
DM
221 eval {
222
223 mkdir $lockdir;
224
007fcc8b
DM
225 # pve cluster filesystem not online
226 die "can't create '$lockdir' (pmxcfs not mounted?)\n" if ! -d $lockdir;
227
75aca181
DM
228 if ($last && (($ctime - $last) < $retry_timeout)) {
229 # send cfs lock update request (utime)
230 if (!utime(0, $ctime, $filename)) {
231 $retry = 1;
007fcc8b 232 die "cfs lock update failed - $!\n";
75aca181 233 }
007fcc8b
DM
234 } else {
235
236 # fixme: wait some time?
237 if (!(mkdir $filename)) {
238 utime 0, 0, $filename; # cfs unlock request
239 die "can't get cfs lock\n";
240 }
241 }
4d24e7db 242
007fcc8b 243 $got_lock = 1;
4d24e7db
DM
244 };
245
007fcc8b
DM
246 my $err = $@;
247
75aca181
DM
248 if ($retry) {
249 # $self->log('err', $err) if $err; # for debugging
250 return 0;
251 }
63f6a08c 252
007fcc8b
DM
253 $last_lock_status->{$lockid} = $got_lock ? $ctime : 0;
254
17e90af6 255 if (!!$got_lock != !!$last) {
007fcc8b 256 if ($got_lock) {
63f6a08c 257 $self->log('info', "successfully acquired lock '$lockid'");
007fcc8b
DM
258 } else {
259 my $msg = "lost lock '$lockid";
63f6a08c 260 $msg .= " - $err" if $err;
007fcc8b
DM
261 $self->log('err', $msg);
262 }
75aca181
DM
263 } else {
264 # $self->log('err', $err) if $err; # for debugging
007fcc8b
DM
265 }
266
267 return $got_lock;
268}
269
270sub get_ha_manager_lock {
271 my ($self) = @_;
272
007fcc8b 273 return $self->get_pve_lock("ha_manager_lock");
714a4016
DM
274}
275
de002253
TL
276# release the cluster wide manager lock.
277# when released another CRM may step up and get the lock, thus this should only
278# get called when shutting down/deactivating the current master
279sub release_ha_manager_lock {
280 my ($self) = @_;
281
282 return rmdir("$lockdir/ha_manager_lock");
283}
284
714a4016 285sub get_ha_agent_lock {
714a4016 286 my ($self, $node) = @_;
63f6a08c 287
f5c29173 288 $node = $self->nodename() if !defined($node);
714a4016 289
f5c29173 290 return $self->get_pve_lock("ha_agent_${node}_lock");
714a4016
DM
291}
292
ff165cd8
TL
293# release the respective node agent lock.
294# this should only get called if the nodes LRM gracefully shuts down with
295# all services already cleanly stopped!
296sub release_ha_agent_lock {
297 my ($self) = @_;
298
299 my $node = $self->nodename();
300
301 return rmdir("$lockdir/ha_agent_${node}_lock");
302}
303
714a4016
DM
304sub quorate {
305 my ($self) = @_;
306
4d24e7db 307 my $quorate = 0;
63f6a08c
TL
308 eval {
309 $quorate = PVE::Cluster::check_cfs_quorum();
4d24e7db 310 };
63f6a08c 311
4d24e7db 312 return $quorate;
714a4016
DM
313}
314
315sub get_time {
316 my ($self) = @_;
317
318 return time();
319}
320
321sub sleep {
322 my ($self, $delay) = @_;
323
324 CORE::sleep($delay);
325}
326
327sub sleep_until {
328 my ($self, $end_time) = @_;
329
330 for (;;) {
331 my $cur_time = time();
332
333 last if $cur_time >= $end_time;
334
335 $self->sleep(1);
336 }
337}
338
339sub loop_start_hook {
340 my ($self) = @_;
341
4d24e7db 342 PVE::Cluster::cfs_update();
63f6a08c 343
714a4016
DM
344 $self->{loop_start} = $self->get_time();
345}
346
347sub loop_end_hook {
348 my ($self) = @_;
349
350 my $delay = $self->get_time() - $self->{loop_start};
63f6a08c 351
714a4016
DM
352 warn "loop take too long ($delay seconds)\n" if $delay > 30;
353}
354
76737af5
DM
355my $watchdog_fh;
356
714a4016
DM
357sub watchdog_open {
358 my ($self) = @_;
359
76737af5
DM
360 die "watchdog already open\n" if defined($watchdog_fh);
361
115805fd
DM
362 $watchdog_fh = IO::Socket::UNIX->new(
363 Type => SOCK_STREAM(),
364 Peer => "/run/watchdog-mux.sock") ||
365 die "unable to open watchdog socket - $!\n";
63f6a08c 366
76737af5 367 $self->log('info', "watchdog active");
714a4016
DM
368}
369
370sub watchdog_update {
371 my ($self, $wfh) = @_;
372
76737af5
DM
373 my $res = $watchdog_fh->syswrite("\0", 1);
374 if (!defined($res)) {
375 $self->log('err', "watchdog update failed - $!\n");
376 return 0;
377 }
378 if ($res != 1) {
379 $self->log('err', "watchdog update failed - write $res bytes\n");
380 return 0;
381 }
382
383 return 1;
714a4016
DM
384}
385
386sub watchdog_close {
387 my ($self, $wfh) = @_;
388
76737af5
DM
389 $watchdog_fh->syswrite("V", 1); # magic watchdog close
390 if (!$watchdog_fh->close()) {
391 $self->log('err', "watchdog close failed - $!");
392 } else {
393 $watchdog_fh = undef;
394 $self->log('info', "watchdog closed (disabled)");
395 }
714a4016
DM
396}
397
022e4e79
DM
398sub upid_wait {
399 my ($self, $upid) = @_;
400
401 my $task = PVE::Tools::upid_decode($upid);
402
403 CORE::sleep(1);
404 while (PVE::ProcFSTools::check_process_running($task->{pid}, $task->{pstart})) {
405 $self->log('debug', "Task still active, waiting");
406 CORE::sleep(1);
407 }
408}
409
0d1d32fb
DM
410sub can_fork {
411 my ($self) = @_;
412
413 return 1;
414}
415
714a4016 4161;