]>
Commit | Line | Data |
---|---|---|
714a4016 DM |
1 | package PVE::HA::Env::PVE2; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
76737af5 DM |
5 | use POSIX qw(:errno_h :fcntl_h); |
6 | use IO::File; | |
714a4016 DM |
7 | |
8 | use PVE::SafeSyslog; | |
9 | use PVE::Tools; | |
abc920b4 | 10 | use PVE::Cluster qw(cfs_register_file cfs_read_file cfs_lock_file); |
714a4016 DM |
11 | |
12 | use PVE::HA::Tools; | |
13 | use PVE::HA::Env; | |
abc920b4 | 14 | use PVE::HA::Groups; |
714a4016 | 15 | |
007fcc8b DM |
16 | my $lockdir = "/etc/pve/priv/lock"; |
17 | ||
714a4016 | 18 | my $manager_status_filename = "/etc/pve/manager_status"; |
abc920b4 DM |
19 | my $ha_groups_config = "ha/groups.cfg"; |
20 | ||
6cbcb5f7 DM |
21 | #cfs_register_file($ha_groups_config, |
22 | # sub { PVE::HA::Groups->parse_config(@_); }, | |
23 | # sub { PVE::HA::Groups->write_config(@_); }); | |
714a4016 DM |
24 | |
25 | sub new { | |
26 | my ($this, $nodename) = @_; | |
27 | ||
28 | die "missing nodename" if !$nodename; | |
29 | ||
30 | my $class = ref($this) || $this; | |
31 | ||
32 | my $self = bless {}, $class; | |
33 | ||
34 | $self->{nodename} = $nodename; | |
35 | ||
36 | return $self; | |
37 | } | |
38 | ||
39 | sub nodename { | |
40 | my ($self) = @_; | |
41 | ||
42 | return $self->{nodename}; | |
43 | } | |
44 | ||
45 | sub read_manager_status { | |
46 | my ($self) = @_; | |
47 | ||
48 | my $filename = $manager_status_filename; | |
49 | ||
50 | return PVE::HA::Tools::read_json_from_file($filename, {}); | |
51 | } | |
52 | ||
53 | sub write_manager_status { | |
54 | my ($self, $status_obj) = @_; | |
55 | ||
56 | my $filename = $manager_status_filename; | |
57 | ||
58 | PVE::HA::Tools::write_json_to_file($filename, $status_obj); | |
59 | } | |
60 | ||
c4a221bc DM |
61 | sub read_lrm_status { |
62 | my ($self, $node) = @_; | |
63 | ||
64 | $node = $self->{nodename} if !defined($node); | |
65 | ||
66 | my $filename = "/etc/pve/nodes/$node/lrm_status"; | |
67 | ||
68 | return PVE::HA::Tools::read_json_from_file($filename, {}); | |
69 | } | |
70 | ||
71 | sub write_lrm_status { | |
72 | my ($self, $status_obj) = @_; | |
73 | ||
6cbcb5f7 | 74 | my $node = $self->{nodename}; |
c4a221bc DM |
75 | |
76 | my $filename = "/etc/pve/nodes/$node/lrm_status"; | |
77 | ||
78 | PVE::HA::Tools::write_json_to_file($filename, $status_obj); | |
79 | } | |
80 | ||
714a4016 DM |
81 | sub manager_status_exists { |
82 | my ($self) = @_; | |
83 | ||
84 | return -f $manager_status_filename ? 1 : 0; | |
85 | } | |
86 | ||
87 | sub read_service_config { | |
88 | my ($self) = @_; | |
89 | ||
90 | die "implement me"; | |
91 | } | |
92 | ||
8456bde2 DM |
93 | sub change_service_location { |
94 | my ($self, $sid, $node) = @_; | |
95 | ||
96 | die "implement me"; | |
97 | } | |
98 | ||
abc920b4 DM |
99 | sub read_group_config { |
100 | my ($self) = @_; | |
101 | ||
102 | return cfs_read_file($ha_groups_config); | |
103 | } | |
104 | ||
3b996922 DM |
105 | sub queue_crm_commands { |
106 | my ($self, $cmd) = @_; | |
107 | ||
108 | die "implement me"; | |
109 | } | |
110 | ||
111 | sub read_crm_commands { | |
112 | my ($self) = @_; | |
113 | ||
114 | die "implement me"; | |
115 | } | |
116 | ||
714a4016 DM |
117 | # this should return a hash containing info |
118 | # what nodes are members and online. | |
119 | sub get_node_info { | |
120 | my ($self) = @_; | |
121 | ||
122 | die "implement me"; | |
123 | } | |
124 | ||
125 | sub log { | |
126 | my ($self, $level, $msg) = @_; | |
127 | ||
128 | chomp $msg; | |
129 | ||
130 | syslog($level, $msg); | |
131 | } | |
132 | ||
007fcc8b DM |
133 | my $last_lock_status = {}; |
134 | ||
135 | sub get_pve_lock { | |
136 | my ($self, $lockid) = @_; | |
714a4016 | 137 | |
007fcc8b | 138 | my $got_lock = 0; |
4d24e7db | 139 | |
4d24e7db DM |
140 | my $filename = "$lockdir/$lockid"; |
141 | ||
007fcc8b DM |
142 | my $last = $last_lock_status->{$lockid} || 0; |
143 | ||
144 | my $ctime = time(); | |
4d24e7db DM |
145 | |
146 | eval { | |
147 | ||
148 | mkdir $lockdir; | |
149 | ||
007fcc8b DM |
150 | # pve cluster filesystem not online |
151 | die "can't create '$lockdir' (pmxcfs not mounted?)\n" if ! -d $lockdir; | |
152 | ||
153 | if ($last && (($ctime - $last) < 100)) { # fixme: what timeout | |
154 | utime(0, $ctime, $filename) || # cfs lock update request | |
155 | die "cfs lock update failed - $!\n"; | |
156 | } else { | |
157 | ||
158 | # fixme: wait some time? | |
159 | if (!(mkdir $filename)) { | |
160 | utime 0, 0, $filename; # cfs unlock request | |
161 | die "can't get cfs lock\n"; | |
162 | } | |
163 | } | |
4d24e7db | 164 | |
007fcc8b | 165 | $got_lock = 1; |
4d24e7db DM |
166 | }; |
167 | ||
007fcc8b DM |
168 | my $err = $@; |
169 | ||
170 | $last_lock_status->{$lockid} = $got_lock ? $ctime : 0; | |
171 | ||
172 | if ($got_lock != $last) { | |
173 | if ($got_lock) { | |
174 | $self->log('info', "successfully aquired lock '$lockid'"); | |
175 | } else { | |
176 | my $msg = "lost lock '$lockid"; | |
177 | $msg .= " - $err" if $err; | |
178 | $self->log('err', $msg); | |
179 | } | |
180 | } | |
181 | ||
182 | return $got_lock; | |
183 | } | |
184 | ||
185 | sub get_ha_manager_lock { | |
186 | my ($self) = @_; | |
187 | ||
188 | my $lockid = "ha_manager_lock"; | |
189 | ||
190 | my $filename = "$lockdir/$lockid"; | |
191 | ||
192 | return $self->get_pve_lock("ha_manager_lock"); | |
714a4016 DM |
193 | } |
194 | ||
195 | sub get_ha_agent_lock { | |
196 | my ($self) = @_; | |
007fcc8b DM |
197 | |
198 | my $node = $self->nodename(); | |
714a4016 | 199 | |
007fcc8b | 200 | return $self->get_pve_lock("ha_agent_${node}_lock"); |
714a4016 DM |
201 | } |
202 | ||
203 | sub test_ha_agent_lock { | |
204 | my ($self, $node) = @_; | |
007fcc8b DM |
205 | |
206 | my $lockid = "ha_agent_${node}_lock"; | |
207 | my $filename = "$lockdir/$lockid"; | |
208 | my $res = $self->get_pve_lock($lockid); | |
209 | rmdir $filename if $res; # cfs unlock | |
714a4016 | 210 | |
007fcc8b | 211 | return $res; |
714a4016 DM |
212 | } |
213 | ||
214 | sub quorate { | |
215 | my ($self) = @_; | |
216 | ||
4d24e7db DM |
217 | my $quorate = 0; |
218 | eval { | |
219 | $quorate = PVE::Cluster::check_cfs_quorum(); | |
220 | }; | |
221 | ||
222 | return $quorate; | |
714a4016 DM |
223 | } |
224 | ||
225 | sub get_time { | |
226 | my ($self) = @_; | |
227 | ||
228 | return time(); | |
229 | } | |
230 | ||
231 | sub sleep { | |
232 | my ($self, $delay) = @_; | |
233 | ||
234 | CORE::sleep($delay); | |
235 | } | |
236 | ||
237 | sub sleep_until { | |
238 | my ($self, $end_time) = @_; | |
239 | ||
240 | for (;;) { | |
241 | my $cur_time = time(); | |
242 | ||
243 | last if $cur_time >= $end_time; | |
244 | ||
245 | $self->sleep(1); | |
246 | } | |
247 | } | |
248 | ||
249 | sub loop_start_hook { | |
250 | my ($self) = @_; | |
251 | ||
4d24e7db DM |
252 | PVE::Cluster::cfs_update(); |
253 | ||
714a4016 DM |
254 | $self->{loop_start} = $self->get_time(); |
255 | } | |
256 | ||
257 | sub loop_end_hook { | |
258 | my ($self) = @_; | |
259 | ||
260 | my $delay = $self->get_time() - $self->{loop_start}; | |
261 | ||
262 | warn "loop take too long ($delay seconds)\n" if $delay > 30; | |
263 | } | |
264 | ||
76737af5 DM |
265 | my $watchdog_fh; |
266 | ||
267 | my $WDIOC_GETSUPPORT = 0x80285700; | |
268 | my $WDIOC_KEEPALIVE = 0x80045705; | |
269 | my $WDIOC_SETTIMEOUT = 0xc0045706; | |
270 | my $WDIOC_GETTIMEOUT = 0x80045707; | |
271 | ||
714a4016 DM |
272 | sub watchdog_open { |
273 | my ($self) = @_; | |
274 | ||
76737af5 | 275 | system("modprobe -q softdog soft_noboot=1") if ! -e "/dev/watchdog"; |
007fcc8b | 276 | |
76737af5 DM |
277 | die "watchdog already open\n" if defined($watchdog_fh); |
278 | ||
279 | $watchdog_fh = IO::File->new(">/dev/watchdog") || | |
280 | die "unable to open watchdog device - $!\n"; | |
281 | ||
282 | eval { | |
283 | my $timeoutbuf = pack('I', 100); | |
284 | my $res = ioctl($watchdog_fh, $WDIOC_SETTIMEOUT, $timeoutbuf) || | |
285 | die "unable to set watchdog timeout - $!\n"; | |
286 | my $timeout = unpack("I", $timeoutbuf); | |
287 | die "got wrong watchdog timeout '$timeout'\n" if $timeout != 100; | |
714a4016 | 288 | |
76737af5 DM |
289 | my $wdinfo = "\x00" x 40; |
290 | $res = ioctl($watchdog_fh, $WDIOC_GETSUPPORT, $wdinfo) || | |
291 | die "unable to get watchdog info - $!\n"; | |
007fcc8b | 292 | |
76737af5 DM |
293 | my ($options, $firmware_version, $indentity) = unpack("lla32", $wdinfo); |
294 | die "watchdog does not support magic close\n" if !($options & 0x0100); | |
007fcc8b | 295 | |
76737af5 DM |
296 | }; |
297 | if (my $err = $@) { | |
298 | $self->watchdog_close(); | |
299 | die $err; | |
300 | } | |
301 | ||
302 | # fixme: use ioctl to setup watchdog timeout (requires C interface) | |
303 | ||
304 | $self->log('info', "watchdog active"); | |
714a4016 DM |
305 | } |
306 | ||
307 | sub watchdog_update { | |
308 | my ($self, $wfh) = @_; | |
309 | ||
76737af5 DM |
310 | my $res = $watchdog_fh->syswrite("\0", 1); |
311 | if (!defined($res)) { | |
312 | $self->log('err', "watchdog update failed - $!\n"); | |
313 | return 0; | |
314 | } | |
315 | if ($res != 1) { | |
316 | $self->log('err', "watchdog update failed - write $res bytes\n"); | |
317 | return 0; | |
318 | } | |
319 | ||
320 | return 1; | |
714a4016 DM |
321 | } |
322 | ||
323 | sub watchdog_close { | |
324 | my ($self, $wfh) = @_; | |
325 | ||
76737af5 DM |
326 | $watchdog_fh->syswrite("V", 1); # magic watchdog close |
327 | if (!$watchdog_fh->close()) { | |
328 | $self->log('err', "watchdog close failed - $!"); | |
329 | } else { | |
330 | $watchdog_fh = undef; | |
331 | $self->log('info', "watchdog closed (disabled)"); | |
332 | } | |
714a4016 DM |
333 | } |
334 | ||
c4a221bc DM |
335 | sub exec_resource_agent { |
336 | my ($self, $sid, $cmd, @params) = @_; | |
337 | ||
338 | die "implement me"; | |
339 | } | |
340 | ||
714a4016 | 341 | 1; |