]> git.proxmox.com Git - pve-ha-manager.git/blob - PVE/HA/Sim/Env.pm
fa244705682f06eb5fa72e017cb53d3389a21070
[pve-ha-manager.git] / PVE / HA / Sim / Env.pm
1 package PVE::HA::Sim::Env;
2
3 use strict;
4 use warnings;
5 use POSIX qw(strftime EINTR);
6 use Data::Dumper;
7 use JSON;
8 use IO::File;
9 use Fcntl qw(:DEFAULT :flock);
10
11 use PVE::HA::Tools;
12 use PVE::HA::Env;
13
14 sub new {
15 my ($this, $nodename, $hardware, $log_id) = @_;
16
17 die "missing nodename" if !$nodename;
18 die "missing log_id" if !$log_id;
19
20 my $class = ref($this) || $this;
21
22 my $self = bless {}, $class;
23
24 $self->{statusdir} = $hardware->statusdir();
25 $self->{nodename} = $nodename;
26
27 $self->{hardware} = $hardware;
28 $self->{lock_timeout} = 120;
29
30 $self->{log_id} = $log_id;
31
32 return $self;
33 }
34
35 sub nodename {
36 my ($self) = @_;
37
38 return $self->{nodename};
39 }
40
41 sub sim_get_lock {
42 my ($self, $lock_name, $unlock) = @_;
43
44 return 0 if !$self->quorate();
45
46 my $filename = "$self->{statusdir}/cluster_locks";
47
48 my $code = sub {
49
50 my $data = PVE::HA::Tools::read_json_from_file($filename, {});
51
52 my $res;
53
54 my $nodename = $self->nodename();
55 my $ctime = $self->get_time();
56
57 if ($unlock) {
58
59 if (my $d = $data->{$lock_name}) {
60 my $tdiff = $ctime - $d->{time};
61
62 if ($tdiff > $self->{lock_timeout}) {
63 $res = 1;
64 } elsif (($tdiff <= $self->{lock_timeout}) && ($d->{node} eq $nodename)) {
65 delete $data->{$lock_name};
66 $res = 1;
67 } else {
68 $res = 0;
69 }
70 }
71
72 } else {
73
74 if (my $d = $data->{$lock_name}) {
75
76 my $tdiff = $ctime - $d->{time};
77
78 if ($tdiff <= $self->{lock_timeout}) {
79 if ($d->{node} eq $nodename) {
80 $d->{time} = $ctime;
81 $res = 1;
82 } else {
83 $res = 0;
84 }
85 } else {
86 $self->log('info', "got lock '$lock_name'");
87 $d->{node} = $nodename;
88 $d->{time} = $ctime;
89 $res = 1;
90 }
91
92 } else {
93 $data->{$lock_name} = {
94 time => $ctime,
95 node => $nodename,
96 };
97 $self->log('info', "got lock '$lock_name'");
98 $res = 1;
99 }
100 }
101
102 PVE::HA::Tools::write_json_to_file($filename, $data);
103
104 return $res;
105 };
106
107 return $self->{hardware}->global_lock($code);
108 }
109
110 sub read_manager_status {
111 my ($self) = @_;
112
113 my $filename = "$self->{statusdir}/manager_status";
114
115 return PVE::HA::Tools::read_json_from_file($filename, {});
116 }
117
118 sub write_manager_status {
119 my ($self, $status_obj) = @_;
120
121 my $filename = "$self->{statusdir}/manager_status";
122
123 PVE::HA::Tools::write_json_to_file($filename, $status_obj);
124 }
125
126 sub manager_status_exists {
127 my ($self) = @_;
128
129 my $filename = "$self->{statusdir}/manager_status";
130
131 return -f $filename ? 1 : 0;
132 }
133
134 sub read_lrm_status {
135 my ($self, $node) = @_;
136
137 $node = $self->{nodename} if !defined($node);
138
139 return $self->{hardware}->read_lrm_status($node);
140 }
141
142 sub write_lrm_status {
143 my ($self, $status_obj) = @_;
144
145 my $node = $self->{nodename};
146
147 return $self->{hardware}->write_lrm_status($node, $status_obj);
148 }
149
150 sub read_service_config {
151 my ($self) = @_;
152
153 return $self->{hardware}->read_service_config();
154 }
155
156 sub read_group_config {
157 my ($self) = @_;
158
159 return $self->{hardware}->read_group_config();
160 }
161
162 sub change_service_location {
163 my ($self, $sid, $node) = @_;
164
165 return $self->{hardware}->change_service_location($sid, $node);
166 }
167
168 sub queue_crm_commands {
169 my ($self, $cmd) = @_;
170
171 return $self->{hardware}->queue_crm_commands($cmd);
172 }
173
174 sub read_crm_commands {
175 my ($self) = @_;
176
177 return $self->{hardware}->read_crm_commands();
178 }
179
180 sub log {
181 my ($self, $level, $msg) = @_;
182
183 chomp $msg;
184
185 my $time = $self->get_time();
186
187 printf("%-5s %5d %12s: $msg\n", $level, $time, "$self->{nodename}/$self->{log_id}");
188 }
189
190 sub get_time {
191 my ($self) = @_;
192
193 die "implement in subclass";
194 }
195
196 sub sleep {
197 my ($self, $delay) = @_;
198
199 die "implement in subclass";
200 }
201
202 sub sleep_until {
203 my ($self, $end_time) = @_;
204
205 die "implement in subclass";
206 }
207
208 sub get_ha_manager_lock {
209 my ($self) = @_;
210
211 return $self->sim_get_lock('ha_manager_lock');
212 }
213
214 sub get_ha_agent_lock_name {
215 my ($self, $node) = @_;
216
217 $node = $self->nodename() if !$node;
218
219 return "ha_agent_${node}_lock";
220 }
221
222 sub get_ha_agent_lock {
223 my ($self) = @_;
224
225 my $lck = $self->get_ha_agent_lock_name();
226 return $self->sim_get_lock($lck);
227 }
228
229 sub test_ha_agent_lock {
230 my ($self, $node) = @_;
231
232 my $lck = $self->get_ha_agent_lock_name($node);
233 my $res = $self->sim_get_lock($lck);
234 $self->sim_get_lock($lck, 1) if $res; # unlock
235 return $res;
236 }
237
238 # return true when cluster is quorate
239 sub quorate {
240 my ($self) = @_;
241
242 my ($node_info, $quorate) = $self->{hardware}->get_node_info();
243 my $node = $self->nodename();
244 return 0 if !$node_info->{$node}->{online};
245 return $quorate;
246 }
247
248 sub get_node_info {
249 my ($self) = @_;
250
251 return $self->{hardware}->get_node_info();
252 }
253
254 sub loop_start_hook {
255 my ($self, $starttime) = @_;
256
257 # do nothing, overwrite in subclass
258 }
259
260 sub loop_end_hook {
261 my ($self) = @_;
262
263 # do nothing, overwrite in subclass
264 }
265
266 sub watchdog_open {
267 my ($self) = @_;
268
269 my $node = $self->nodename();
270
271 return $self->{hardware}->watchdog_open($node);
272 }
273
274 sub watchdog_update {
275 my ($self, $wfh) = @_;
276
277 return $self->{hardware}->watchdog_update($wfh);
278 }
279
280 sub watchdog_close {
281 my ($self, $wfh) = @_;
282
283 return $self->{hardware}->watchdog_close($wfh);
284 }
285
286 sub exec_resource_agent {
287 my ($self, $sid, $cmd, @params) = @_;
288
289 die "implement me";
290 }
291
292 1;