]> git.proxmox.com Git - pve-ha-manager.git/blame - src/PVE/HA/Env/PVE2.pm
use new PVE::HA:Config
[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;
abc920b4 11use PVE::Cluster qw(cfs_register_file cfs_read_file cfs_lock_file);
714a4016
DM
12
13use PVE::HA::Tools;
14use PVE::HA::Env;
ce216792 15use PVE::HA::Config;
714a4016 16
007fcc8b
DM
17my $lockdir = "/etc/pve/priv/lock";
18
ce216792 19my $manager_status_filename = "/etc/pve/ha/manager_status";
abc920b4 20my $ha_groups_config = "ha/groups.cfg";
ce216792 21my $ha_resources_config = "ha/resources.cfg";
abc920b4 22
6cbcb5f7
DM
23#cfs_register_file($ha_groups_config,
24# sub { PVE::HA::Groups->parse_config(@_); },
25# sub { PVE::HA::Groups->write_config(@_); });
714a4016
DM
26
27sub new {
28 my ($this, $nodename) = @_;
29
30 die "missing nodename" if !$nodename;
31
32 my $class = ref($this) || $this;
33
34 my $self = bless {}, $class;
35
36 $self->{nodename} = $nodename;
37
38 return $self;
39}
40
41sub nodename {
42 my ($self) = @_;
43
44 return $self->{nodename};
45}
46
47sub read_manager_status {
48 my ($self) = @_;
49
50 my $filename = $manager_status_filename;
51
52 return PVE::HA::Tools::read_json_from_file($filename, {});
53}
54
55sub write_manager_status {
56 my ($self, $status_obj) = @_;
57
58 my $filename = $manager_status_filename;
59
60 PVE::HA::Tools::write_json_to_file($filename, $status_obj);
61}
62
c4a221bc
DM
63sub read_lrm_status {
64 my ($self, $node) = @_;
65
66 $node = $self->{nodename} if !defined($node);
67
68 my $filename = "/etc/pve/nodes/$node/lrm_status";
69
70 return PVE::HA::Tools::read_json_from_file($filename, {});
71}
72
73sub write_lrm_status {
74 my ($self, $status_obj) = @_;
75
6cbcb5f7 76 my $node = $self->{nodename};
c4a221bc
DM
77
78 my $filename = "/etc/pve/nodes/$node/lrm_status";
79
80 PVE::HA::Tools::write_json_to_file($filename, $status_obj);
81}
82
714a4016
DM
83sub manager_status_exists {
84 my ($self) = @_;
85
86 return -f $manager_status_filename ? 1 : 0;
87}
88
89sub read_service_config {
90 my ($self) = @_;
91
ce216792
DM
92 # fixme: use cfs_read_file
93
94 my $raw = '';
95
96 $raw = PVE::Tools::file_get_contents($ha_resources_config)
97 if -f $ha_resources_config;
98
99 my $res = PVE::HA::Config::parse_resources_config($ha_resources_config, $raw);
100
101 my $vmlist = PVE::Cluster::get_vmlist();
102 my $conf = {};
103
104 foreach my $sid (keys %{$res->{ids}}) {
105 my $d = $res->{ids}->{$sid};
106 if ($d->{type} eq 'pvevm') {
107 if (my $vmd = $vmlist->{ids}->{$d->{name}}) {
108 if (!$vmd) {
109 warn "no such VM '$d->{name}'\n";
110 } else {
111 $d->{node} = $vmd->{node};
112 $conf->{$sid} = $d;
113 }
114 } else {
115 if (defined($d->{node})) {
116 $conf->{$sid} = $d;
117 } else {
118 warn "service '$sid' without node\n";
119 }
120 }
121 }
122 }
123
124 return $conf;
714a4016
DM
125}
126
8456bde2
DM
127sub change_service_location {
128 my ($self, $sid, $node) = @_;
129
130 die "implement me";
131}
132
abc920b4
DM
133sub read_group_config {
134 my ($self) = @_;
135
ce216792
DM
136 # fixme: use cfs_read_file
137
138 my $raw = '';
139
140 $raw = PVE::Tools::file_get_contents($ha_groups_config)
141 if -f $ha_groups_config;
142
143 return PVE::HA::Config::parse_groups_config($ha_groups_config, $raw);
abc920b4
DM
144}
145
3b996922
DM
146sub queue_crm_commands {
147 my ($self, $cmd) = @_;
148
ce216792
DM
149 chomp $cmd;
150
151 my $code = sub {
152 my $data = '';
153 my $filename = "/etc/pve/ha/crm_commands";
154 if (-f $filename) {
155 $data = PVE::Tools::file_get_contents($filename);
156 }
157 $data .= "$cmd\n";
158 PVE::Tools::file_set_contents($filename, $data);
159 };
160
161 # fixme: do not use cfs_lock_storage (replace with cfs_lock_ha)
162 my $res = PVE::Cluster::cfs_lock_storage("_ha_crm_commands", undef, $code);
163 die $@ if $@;
164 return $res;
3b996922
DM
165}
166
167sub read_crm_commands {
168 my ($self) = @_;
169
ce216792
DM
170 my $code = sub {
171 my $data = '';
172
173 my $filename = "/etc/pve/ha/crm_commands";
174 if (-f $filename) {
175 $data = PVE::Tools::file_get_contents($filename);
176 PVE::Tools::file_set_contents($filename, '');
177 }
178
179 return $data;
180 };
181
182 # fixme: do not use cfs_lock_storage (replace with cfs_lock_ha)
183 my $res = PVE::Cluster::cfs_lock_storage("_ha_crm_commands", undef, $code);
184 die $@ if $@;
185 return $res;
3b996922
DM
186}
187
714a4016
DM
188# this should return a hash containing info
189# what nodes are members and online.
190sub get_node_info {
191 my ($self) = @_;
192
d706ef8b
DM
193 my ($node_info, $quorate) = ({}, 0);
194
195 my $nodename = $self->{nodename};
196
197 $quorate = PVE::Cluster::check_cfs_quorum(1) || 0;
198
199 my $members = PVE::Cluster::get_members();
200
201 foreach my $node (keys %$members) {
202 my $d = $members->{$node};
203 $node_info->{$node}->{online} = $d->{online};
204 }
205
206 $node_info->{$nodename}->{online} = 1; # local node is always up
207
208 return ($node_info, $quorate);
714a4016
DM
209}
210
211sub log {
212 my ($self, $level, $msg) = @_;
213
214 chomp $msg;
215
216 syslog($level, $msg);
217}
218
007fcc8b
DM
219my $last_lock_status = {};
220
221sub get_pve_lock {
222 my ($self, $lockid) = @_;
714a4016 223
007fcc8b 224 my $got_lock = 0;
4d24e7db 225
4d24e7db
DM
226 my $filename = "$lockdir/$lockid";
227
007fcc8b
DM
228 my $last = $last_lock_status->{$lockid} || 0;
229
230 my $ctime = time();
4d24e7db
DM
231
232 eval {
233
234 mkdir $lockdir;
235
007fcc8b
DM
236 # pve cluster filesystem not online
237 die "can't create '$lockdir' (pmxcfs not mounted?)\n" if ! -d $lockdir;
238
239 if ($last && (($ctime - $last) < 100)) { # fixme: what timeout
240 utime(0, $ctime, $filename) || # cfs lock update request
241 die "cfs lock update failed - $!\n";
242 } else {
243
244 # fixme: wait some time?
245 if (!(mkdir $filename)) {
246 utime 0, 0, $filename; # cfs unlock request
247 die "can't get cfs lock\n";
248 }
249 }
4d24e7db 250
007fcc8b 251 $got_lock = 1;
4d24e7db
DM
252 };
253
007fcc8b
DM
254 my $err = $@;
255
256 $last_lock_status->{$lockid} = $got_lock ? $ctime : 0;
257
258 if ($got_lock != $last) {
259 if ($got_lock) {
260 $self->log('info', "successfully aquired lock '$lockid'");
261 } else {
262 my $msg = "lost lock '$lockid";
263 $msg .= " - $err" if $err;
264 $self->log('err', $msg);
265 }
266 }
267
268 return $got_lock;
269}
270
271sub get_ha_manager_lock {
272 my ($self) = @_;
273
007fcc8b 274 return $self->get_pve_lock("ha_manager_lock");
714a4016
DM
275}
276
277sub get_ha_agent_lock {
278 my ($self) = @_;
007fcc8b
DM
279
280 my $node = $self->nodename();
714a4016 281
007fcc8b 282 return $self->get_pve_lock("ha_agent_${node}_lock");
714a4016
DM
283}
284
285sub test_ha_agent_lock {
286 my ($self, $node) = @_;
007fcc8b
DM
287
288 my $lockid = "ha_agent_${node}_lock";
289 my $filename = "$lockdir/$lockid";
290 my $res = $self->get_pve_lock($lockid);
291 rmdir $filename if $res; # cfs unlock
714a4016 292
007fcc8b 293 return $res;
714a4016
DM
294}
295
296sub quorate {
297 my ($self) = @_;
298
4d24e7db
DM
299 my $quorate = 0;
300 eval {
301 $quorate = PVE::Cluster::check_cfs_quorum();
302 };
303
304 return $quorate;
714a4016
DM
305}
306
307sub get_time {
308 my ($self) = @_;
309
310 return time();
311}
312
313sub sleep {
314 my ($self, $delay) = @_;
315
316 CORE::sleep($delay);
317}
318
319sub sleep_until {
320 my ($self, $end_time) = @_;
321
322 for (;;) {
323 my $cur_time = time();
324
325 last if $cur_time >= $end_time;
326
327 $self->sleep(1);
328 }
329}
330
331sub loop_start_hook {
332 my ($self) = @_;
333
4d24e7db
DM
334 PVE::Cluster::cfs_update();
335
714a4016
DM
336 $self->{loop_start} = $self->get_time();
337}
338
339sub loop_end_hook {
340 my ($self) = @_;
341
342 my $delay = $self->get_time() - $self->{loop_start};
343
344 warn "loop take too long ($delay seconds)\n" if $delay > 30;
345}
346
76737af5
DM
347my $watchdog_fh;
348
714a4016
DM
349sub watchdog_open {
350 my ($self) = @_;
351
76737af5
DM
352 die "watchdog already open\n" if defined($watchdog_fh);
353
115805fd
DM
354 $watchdog_fh = IO::Socket::UNIX->new(
355 Type => SOCK_STREAM(),
356 Peer => "/run/watchdog-mux.sock") ||
357 die "unable to open watchdog socket - $!\n";
358
76737af5 359 $self->log('info', "watchdog active");
714a4016
DM
360}
361
362sub watchdog_update {
363 my ($self, $wfh) = @_;
364
76737af5
DM
365 my $res = $watchdog_fh->syswrite("\0", 1);
366 if (!defined($res)) {
367 $self->log('err', "watchdog update failed - $!\n");
368 return 0;
369 }
370 if ($res != 1) {
371 $self->log('err', "watchdog update failed - write $res bytes\n");
372 return 0;
373 }
374
375 return 1;
714a4016
DM
376}
377
378sub watchdog_close {
379 my ($self, $wfh) = @_;
380
76737af5
DM
381 $watchdog_fh->syswrite("V", 1); # magic watchdog close
382 if (!$watchdog_fh->close()) {
383 $self->log('err', "watchdog close failed - $!");
384 } else {
385 $watchdog_fh = undef;
386 $self->log('info', "watchdog closed (disabled)");
387 }
714a4016
DM
388}
389
c4a221bc
DM
390sub exec_resource_agent {
391 my ($self, $sid, $cmd, @params) = @_;
392
393 die "implement me";
394}
395
714a4016 3961;