line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package MogileFS::Worker::Query; |
2
|
|
|
|
|
|
|
# responds to queries from Mogile clients |
3
|
|
|
|
|
|
|
|
4
|
21
|
|
|
21
|
|
123
|
use strict; |
|
21
|
|
|
|
|
41
|
|
|
21
|
|
|
|
|
2104
|
|
5
|
21
|
|
|
21
|
|
145
|
use warnings; |
|
21
|
|
|
|
|
36
|
|
|
21
|
|
|
|
|
925
|
|
6
|
|
|
|
|
|
|
|
7
|
21
|
|
|
21
|
|
117
|
use base 'MogileFS::Worker'; |
|
21
|
|
|
|
|
37
|
|
|
21
|
|
|
|
|
13188
|
|
8
|
21
|
|
|
21
|
|
174
|
use fields qw(querystarttime reqid callid); |
|
21
|
|
|
|
|
45
|
|
|
21
|
|
|
|
|
135
|
|
9
|
21
|
|
|
|
|
2316
|
use MogileFS::Util qw(error error_code first weighted_list |
10
|
21
|
|
|
21
|
|
2530
|
device_state eurl decode_url_args); |
|
21
|
|
|
|
|
45
|
|
11
|
21
|
|
|
21
|
|
17858
|
use MogileFS::HTTPFile; |
|
21
|
|
|
|
|
76
|
|
|
21
|
|
|
|
|
719
|
|
12
|
21
|
|
|
21
|
|
20998
|
use MogileFS::Rebalance; |
|
21
|
|
|
|
|
91
|
|
|
21
|
|
|
|
|
898
|
|
13
|
21
|
|
|
21
|
|
217
|
use MogileFS::Config; |
|
21
|
|
|
|
|
53
|
|
|
21
|
|
|
|
|
4939
|
|
14
|
21
|
|
|
21
|
|
1035
|
use MogileFS::Server; |
|
21
|
|
|
|
|
48
|
|
|
21
|
|
|
|
|
32738
|
|
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
sub new { |
17
|
0
|
|
|
0
|
0
|
|
my ($class, $psock) = @_; |
18
|
0
|
|
|
|
|
|
my $self = fields::new($class); |
19
|
0
|
|
|
|
|
|
$self->SUPER::new($psock); |
20
|
|
|
|
|
|
|
|
21
|
0
|
|
|
|
|
|
$self->{querystarttime} = undef; |
22
|
0
|
|
|
|
|
|
$self->{reqid} = undef; |
23
|
0
|
|
|
|
|
|
$self->{callid} = undef; |
24
|
0
|
|
|
|
|
|
return $self; |
25
|
|
|
|
|
|
|
} |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
# no query should take 30 seconds, and we check in every 5 seconds. |
28
|
0
|
|
|
0
|
0
|
|
sub watchdog_timeout { 30 } |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
# called by plugins to register a command in the namespace |
31
|
|
|
|
|
|
|
sub register_command { |
32
|
0
|
|
|
0
|
0
|
|
my ($cmd, $sub) = @_; |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
# validate the command, then convert it to the actual thing the user |
35
|
|
|
|
|
|
|
# will be calling |
36
|
0
|
0
|
|
|
|
|
return 0 unless $cmd =~ /^[\w\d]+$/; |
37
|
0
|
|
|
|
|
|
$cmd = "plugin_$cmd"; |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
# register in namespace with 'cmd_' which we will automatically find |
40
|
21
|
|
|
21
|
|
207
|
no strict 'refs'; |
|
21
|
|
|
|
|
53
|
|
|
21
|
|
|
|
|
14682
|
|
41
|
0
|
|
|
|
|
|
*{"cmd_$cmd"} = $sub; |
|
0
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
# all's well |
44
|
0
|
|
|
|
|
|
return 1; |
45
|
|
|
|
|
|
|
} |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
sub work { |
48
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
49
|
0
|
|
|
|
|
|
my $psock = $self->{psock}; |
50
|
0
|
|
|
|
|
|
my $rin = ''; |
51
|
0
|
|
|
|
|
|
vec($rin, fileno($psock), 1) = 1; |
52
|
0
|
|
|
|
|
|
my $buf; |
53
|
|
|
|
|
|
|
|
54
|
0
|
|
|
|
|
|
while (1) { |
55
|
0
|
|
|
|
|
|
my $rout; |
56
|
0
|
0
|
|
|
|
|
unless (select($rout=$rin, undef, undef, 5.0)) { |
57
|
0
|
|
|
|
|
|
$self->still_alive; |
58
|
0
|
|
|
|
|
|
next; |
59
|
|
|
|
|
|
|
} |
60
|
|
|
|
|
|
|
|
61
|
0
|
|
|
|
|
|
my $newread; |
62
|
0
|
|
|
|
|
|
my $rv = sysread($psock, $newread, Mgd::UNIX_RCVBUF_SIZE()); |
63
|
0
|
0
|
|
|
|
|
if (!$rv) { |
64
|
0
|
0
|
|
|
|
|
if (defined $rv) { |
65
|
0
|
|
|
|
|
|
die "While reading pipe from parent, got EOF. Parent's gone. Quitting.\n"; |
66
|
|
|
|
|
|
|
} else { |
67
|
0
|
|
|
|
|
|
die "Error reading pipe from parent: $!\n"; |
68
|
|
|
|
|
|
|
} |
69
|
|
|
|
|
|
|
} |
70
|
0
|
|
|
|
|
|
$buf .= $newread; |
71
|
|
|
|
|
|
|
|
72
|
0
|
|
|
|
|
|
while ($buf =~ s/^(.+?)\r?\n//) { |
73
|
0
|
|
|
|
|
|
my $line = $1; |
74
|
0
|
0
|
|
|
|
|
if ($self->process_generic_command(\$line)) { |
75
|
0
|
|
|
|
|
|
$self->still_alive; # no-op for watchdog |
76
|
|
|
|
|
|
|
} else { |
77
|
0
|
|
|
|
|
|
$self->validate_dbh; |
78
|
0
|
|
|
|
|
|
$self->process_line(\$line); |
79
|
|
|
|
|
|
|
} |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
} |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
sub process_line { |
85
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
86
|
0
|
|
|
|
|
|
my $lineref = shift; |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
# see what kind of command this is |
89
|
0
|
0
|
|
|
|
|
return $self->err_line('unknown_command') |
90
|
|
|
|
|
|
|
unless $$lineref =~ /^(\d+-\d+)?\s*(\S+)\s*(.*)/; |
91
|
|
|
|
|
|
|
|
92
|
0
|
|
0
|
|
|
|
$self->{reqid} = $1 || undef; |
93
|
0
|
|
|
|
|
|
my ($client_ip, $line) = ($2, $3); |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
# set global variables for zone determination |
96
|
0
|
|
|
|
|
|
local $MogileFS::REQ_client_ip = $client_ip; |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
# Use as array here, otherwise we get a string which breaks usage of |
99
|
|
|
|
|
|
|
# Time::HiRes::tv_interval further on. |
100
|
0
|
|
|
|
|
|
$self->{querystarttime} = [ Time::HiRes::gettimeofday() ]; |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
# fallback to normal command handling |
103
|
0
|
0
|
|
|
|
|
if ($line =~ /^(\w+)\s*(.*)/) { |
104
|
0
|
|
|
|
|
|
my ($cmd, $orig_args) = ($1, $2); |
105
|
0
|
|
|
|
|
|
$cmd = lc($cmd); |
106
|
|
|
|
|
|
|
|
107
|
21
|
|
|
21
|
|
159
|
no strict 'refs'; |
|
21
|
|
|
|
|
46
|
|
|
21
|
|
|
|
|
376427
|
|
108
|
0
|
|
|
|
|
|
my $cmd_handler = *{"cmd_$cmd"}{CODE}; |
|
0
|
|
|
|
|
|
|
109
|
0
|
|
|
|
|
|
my $args = decode_url_args(\$orig_args); |
110
|
0
|
|
|
|
|
|
$self->{callid} = $args->{callid}; |
111
|
0
|
0
|
|
|
|
|
if ($cmd_handler) { |
112
|
0
|
|
0
|
|
|
|
local $MogileFS::REQ_altzone = ($args->{zone} && $args->{zone} eq 'alt'); |
113
|
0
|
|
|
|
|
|
eval { |
114
|
0
|
|
|
|
|
|
$cmd_handler->($self, $args); |
115
|
|
|
|
|
|
|
}; |
116
|
0
|
0
|
|
|
|
|
if ($@) { |
117
|
0
|
|
|
|
|
|
my $errc = error_code($@); |
118
|
0
|
0
|
|
|
|
|
if ($errc eq "dup") { |
119
|
0
|
|
|
|
|
|
return $self->err_line("dup"); |
120
|
|
|
|
|
|
|
} else { |
121
|
0
|
|
|
|
|
|
warn "Error: $@\n"; |
122
|
0
|
|
|
|
|
|
error("Error running command '$cmd': $@"); |
123
|
0
|
|
|
|
|
|
return $self->err_line("failure"); |
124
|
|
|
|
|
|
|
} |
125
|
|
|
|
|
|
|
} |
126
|
0
|
|
|
|
|
|
return; |
127
|
|
|
|
|
|
|
} |
128
|
|
|
|
|
|
|
} |
129
|
|
|
|
|
|
|
|
130
|
0
|
|
|
|
|
|
return $self->err_line('unknown_command'); |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
# this is a half-finished command. in particular, errors tend to |
134
|
|
|
|
|
|
|
# crash the parent or child or something. it's a quick hack for a quick |
135
|
|
|
|
|
|
|
# ops task that needs done. note in particular how it reaches across |
136
|
|
|
|
|
|
|
# package boundaries into an API that the Replicator probably doesn't |
137
|
|
|
|
|
|
|
# want exposed. |
138
|
|
|
|
|
|
|
sub cmd_httpcopy { |
139
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
140
|
0
|
|
|
|
|
|
my $args = shift; |
141
|
0
|
|
|
|
|
|
my $sdevid = $args->{sdevid}; |
142
|
0
|
|
|
|
|
|
my $ddevid = $args->{ddevid}; |
143
|
0
|
|
|
|
|
|
my $fid = $args->{fid}; |
144
|
|
|
|
|
|
|
|
145
|
0
|
|
|
|
|
|
my $err; |
146
|
0
|
|
|
|
|
|
my $rv = MogileFS::Worker::Replicate::http_copy(sdevid => $sdevid, |
147
|
|
|
|
|
|
|
ddevid => $ddevid, |
148
|
|
|
|
|
|
|
fid => $fid, |
149
|
|
|
|
|
|
|
errref => \$err); |
150
|
0
|
0
|
|
|
|
|
if ($rv) { |
151
|
0
|
|
|
|
|
|
my $dfid = MogileFS::DevFID->new($ddevid, $fid); |
152
|
0
|
0
|
|
|
|
|
$dfid->add_to_db |
153
|
|
|
|
|
|
|
or return $self->err_line("copy_err", "failed to add link to database"); |
154
|
0
|
|
|
|
|
|
return $self->ok_line; |
155
|
|
|
|
|
|
|
} else { |
156
|
0
|
|
|
|
|
|
return $self->err_line("copy_err", $err); |
157
|
|
|
|
|
|
|
} |
158
|
|
|
|
|
|
|
} |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
# returns 0 on error, or dmid of domain |
161
|
|
|
|
|
|
|
sub check_domain { |
162
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
163
|
0
|
|
|
|
|
|
my $args = shift; |
164
|
|
|
|
|
|
|
|
165
|
0
|
|
|
|
|
|
my $domain = $args->{domain}; |
166
|
|
|
|
|
|
|
|
167
|
0
|
0
|
0
|
|
|
|
return $self->err_line("no_domain") unless defined $domain && length $domain; |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
# validate domain |
170
|
0
|
0
|
|
|
|
|
my $dmid = eval { Mgd::domain_factory()->get_by_name($domain)->id } or |
|
0
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
return $self->err_line("unreg_domain"); |
172
|
|
|
|
|
|
|
|
173
|
0
|
|
|
|
|
|
return $dmid; |
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
sub cmd_sleep { |
177
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
178
|
0
|
|
|
|
|
|
my $args = shift; |
179
|
0
|
|
0
|
|
|
|
sleep($args->{duration} || 10); |
180
|
0
|
|
|
|
|
|
return $self->ok_line; |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
sub cmd_test { |
184
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
185
|
0
|
|
|
|
|
|
my $args = shift; |
186
|
0
|
0
|
|
|
|
|
die "Crashed on purpose" if $args->{crash}; |
187
|
0
|
|
|
|
|
|
return $self->ok_line; |
188
|
|
|
|
|
|
|
} |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
sub cmd_clear_cache { |
191
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
192
|
|
|
|
|
|
|
|
193
|
0
|
|
|
|
|
|
$self->forget_that_monitor_has_run; |
194
|
0
|
|
|
|
|
|
$self->send_to_parent(":refresh_monitor"); |
195
|
0
|
|
|
|
|
|
$self->wait_for_monitor; |
196
|
|
|
|
|
|
|
|
197
|
0
|
|
|
|
|
|
return $self->ok_line(@_); |
198
|
|
|
|
|
|
|
} |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
sub cmd_create_open { |
201
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
202
|
0
|
|
|
|
|
|
my $args = shift; |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
# has to be filled out for some plugins |
205
|
0
|
0
|
|
|
|
|
$args->{dmid} = $self->check_domain($args) or return; |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
# first, pass this to a hook to do any manipulations needed |
208
|
0
|
|
|
|
|
|
eval {MogileFS::run_global_hook('cmd_create_open', $args)}; |
|
0
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
|
210
|
0
|
0
|
|
|
|
|
return $self->err_line("plugin_aborted", "$@") |
211
|
|
|
|
|
|
|
if $@; |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
# validate parameters |
214
|
0
|
|
|
|
|
|
my $dmid = $args->{dmid}; |
215
|
0
|
|
0
|
|
|
|
my $key = $args->{key} || ""; |
216
|
0
|
0
|
|
|
|
|
my $multi = $args->{multi_dest} ? 1 : 0; |
217
|
0
|
|
0
|
|
|
|
my $size = $args->{size} || undef; # Size is optional at create time, |
218
|
|
|
|
|
|
|
# but used to grep devices if available |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
# optional profiling of stages, if $args->{debug_profile} |
221
|
0
|
|
|
|
|
|
my @profpoints; # array of [point,hires-starttime] |
222
|
|
|
|
|
|
|
my $profstart = sub { |
223
|
0
|
|
|
0
|
|
|
my $pt = shift; |
224
|
0
|
|
|
|
|
|
push @profpoints, [$pt, Time::HiRes::time()]; |
225
|
0
|
|
|
|
|
|
}; |
226
|
0
|
0
|
|
0
|
|
|
$profstart = sub {} unless $args->{debug_profile}; |
|
0
|
|
|
|
|
|
|
227
|
0
|
|
|
|
|
|
$profstart->("begin"); |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
# we want it to be undef if not explicit, else force to numeric |
230
|
0
|
0
|
|
|
|
|
my $exp_fidid = $args->{fid} ? int($args->{fid}) : undef; |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
# get DB handle |
233
|
0
|
|
|
|
|
|
my $sto = Mgd::get_store(); |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
# figure out what classid this file is for |
236
|
0
|
|
0
|
|
|
|
my $class = $args->{class} || ""; |
237
|
0
|
|
|
|
|
|
my $classid = 0; |
238
|
0
|
0
|
|
|
|
|
if (length($class)) { |
239
|
0
|
0
|
|
|
|
|
$classid = eval { Mgd::class_factory()->get_by_name($dmid, $class)->id } |
|
0
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
or return $self->err_line("unreg_class"); |
241
|
|
|
|
|
|
|
} |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
# if we haven't heard from the monitoring job yet, we need to chill a bit |
244
|
|
|
|
|
|
|
# to prevent a race where we tell a user that we can't create a file when |
245
|
|
|
|
|
|
|
# in fact we've just not heard from the monitor |
246
|
0
|
|
|
|
|
|
$profstart->("wait_monitor"); |
247
|
0
|
|
|
|
|
|
$self->wait_for_monitor; |
248
|
|
|
|
|
|
|
|
249
|
0
|
|
|
|
|
|
$profstart->("find_deviceid"); |
250
|
|
|
|
|
|
|
|
251
|
0
|
|
|
|
|
|
my @devices = Mgd::device_factory()->get_all; |
252
|
0
|
0
|
|
|
|
|
if ($size) { |
253
|
|
|
|
|
|
|
# We first ignore all the devices with an unknown space free. |
254
|
0
|
0
|
|
|
|
|
@devices = grep { length($_->mb_free) && ($_->mb_free * 1024*1024) > $size } @devices; |
|
0
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
# If we didn't find any, try all the devices with an unknown space free. |
257
|
|
|
|
|
|
|
# This may happen if mogstored isn't running. |
258
|
0
|
0
|
|
|
|
|
if (!@devices) { |
259
|
0
|
|
|
|
|
|
@devices = grep { !length($_->mb_free) } Mgd::device_factory()->get_all; |
|
0
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
} |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
|
263
|
0
|
0
|
|
|
|
|
unless (MogileFS::run_global_hook('cmd_create_open_order_devices', [ @devices ], \@devices)) { |
264
|
0
|
|
|
|
|
|
@devices = sort_devs_by_freespace(@devices); |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
# find suitable device(s) to put this file on. |
268
|
0
|
|
|
|
|
|
my @dests; # MogileFS::Device objects which are suitable |
269
|
|
|
|
|
|
|
|
270
|
0
|
0
|
|
|
|
|
while (scalar(@dests) < ($multi ? 3 : 1)) { |
271
|
0
|
|
|
|
|
|
my $ddev = shift @devices; |
272
|
|
|
|
|
|
|
|
273
|
0
|
0
|
|
|
|
|
last unless $ddev; |
274
|
0
|
0
|
|
|
|
|
next unless $ddev->not_on_hosts(map { $_->host } @dests); |
|
0
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
|
276
|
0
|
|
|
|
|
|
push @dests, $ddev; |
277
|
|
|
|
|
|
|
} |
278
|
0
|
0
|
|
|
|
|
return $self->err_line("no_devices") unless @dests; |
279
|
|
|
|
|
|
|
|
280
|
0
|
|
|
|
|
|
my $fidid = eval { |
281
|
0
|
|
|
|
|
|
$sto->register_tempfile( |
282
|
|
|
|
|
|
|
fid => $exp_fidid, # may be undef/NULL to mean auto-increment |
283
|
|
|
|
|
|
|
dmid => $dmid, |
284
|
|
|
|
|
|
|
key => $key, |
285
|
|
|
|
|
|
|
classid => $classid, |
286
|
0
|
|
|
|
|
|
devids => join(',', map { $_->id } @dests), |
287
|
|
|
|
|
|
|
); |
288
|
|
|
|
|
|
|
}; |
289
|
0
|
0
|
|
|
|
|
unless ($fidid) { |
290
|
0
|
|
|
|
|
|
my $errc = error_code($@); |
291
|
0
|
0
|
|
|
|
|
return $self->err_line("fid_in_use") if $errc eq "dup"; |
292
|
0
|
|
|
|
|
|
warn "Error registering tempfile: $@\n"; |
293
|
0
|
|
|
|
|
|
return $self->err_line("db"); |
294
|
|
|
|
|
|
|
} |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
# make sure directories exist for client to be able to PUT into |
297
|
0
|
|
|
|
|
|
my %dir_done; |
298
|
0
|
|
|
|
|
|
$profstart->("vivify_dir_on_all_devs"); |
299
|
|
|
|
|
|
|
|
300
|
0
|
|
|
|
|
|
my $t0 = Time::HiRes::time(); |
301
|
0
|
|
|
|
|
|
foreach my $dev (@dests) { |
302
|
0
|
|
|
|
|
|
my $dfid = MogileFS::DevFID->new($dev, $fidid); |
303
|
|
|
|
|
|
|
$dfid->vivify_directories(sub { |
304
|
0
|
|
|
0
|
|
|
$dir_done{$dfid->devid} = Time::HiRes::time() - $t0; |
305
|
0
|
|
|
|
|
|
}); |
306
|
|
|
|
|
|
|
} |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
# don't start the event loop if results are all cached |
309
|
0
|
0
|
|
|
|
|
if (scalar keys %dir_done != scalar @dests) { |
310
|
0
|
|
|
0
|
|
|
Danga::Socket->SetPostLoopCallback(sub { scalar keys %dir_done != scalar @dests }); |
|
0
|
|
|
|
|
|
|
311
|
0
|
|
|
|
|
|
Danga::Socket->EventLoop; |
312
|
|
|
|
|
|
|
} |
313
|
0
|
|
|
|
|
|
$profstart->("end"); |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
# common reply variables |
316
|
0
|
|
|
|
|
|
my $res = { |
317
|
|
|
|
|
|
|
fid => $fidid, |
318
|
|
|
|
|
|
|
}; |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
# add profiling data |
321
|
0
|
0
|
|
|
|
|
if (@profpoints) { |
322
|
0
|
|
|
|
|
|
$res->{profpoints} = 0; |
323
|
0
|
|
|
|
|
|
for (my $i=0; $i<$#profpoints; $i++) { |
324
|
0
|
|
|
|
|
|
my $ptnum = ++$res->{profpoints}; |
325
|
0
|
|
|
|
|
|
$res->{"prof_${ptnum}_name"} = $profpoints[$i]->[0]; |
326
|
0
|
|
|
|
|
|
$res->{"prof_${ptnum}_time"} = |
327
|
|
|
|
|
|
|
sprintf("%0.03f", |
328
|
|
|
|
|
|
|
$profpoints[$i+1]->[1] - $profpoints[$i]->[1]); |
329
|
|
|
|
|
|
|
} |
330
|
0
|
|
|
|
|
|
while (my ($devid, $time) = each %dir_done) { |
331
|
0
|
|
|
|
|
|
my $ptnum = ++$res->{profpoints}; |
332
|
0
|
|
|
|
|
|
$res->{"prof_${ptnum}_name"} = "vivify_dir_on_dev$devid"; |
333
|
0
|
|
|
|
|
|
$res->{"prof_${ptnum}_time"} = sprintf("%0.03f", $time); |
334
|
|
|
|
|
|
|
} |
335
|
|
|
|
|
|
|
} |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
# add path info |
338
|
0
|
0
|
|
|
|
|
if ($multi) { |
339
|
0
|
|
|
|
|
|
my $ct = 0; |
340
|
0
|
|
|
|
|
|
foreach my $dev (@dests) { |
341
|
0
|
|
|
|
|
|
$ct++; |
342
|
0
|
|
|
|
|
|
$res->{"devid_$ct"} = $dev->id; |
343
|
0
|
|
|
|
|
|
$res->{"path_$ct"} = MogileFS::DevFID->new($dev, $fidid)->url; |
344
|
|
|
|
|
|
|
} |
345
|
0
|
|
|
|
|
|
$res->{dev_count} = $ct; |
346
|
|
|
|
|
|
|
} else { |
347
|
0
|
|
|
|
|
|
$res->{devid} = $dests[0]->id; |
348
|
0
|
|
|
|
|
|
$res->{path} = MogileFS::DevFID->new($dests[0], $fidid)->url; |
349
|
|
|
|
|
|
|
} |
350
|
|
|
|
|
|
|
|
351
|
0
|
|
|
|
|
|
return $self->ok_line($res); |
352
|
|
|
|
|
|
|
} |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
sub sort_devs_by_freespace { |
355
|
0
|
|
|
|
|
|
my @devices_with_weights = map { |
356
|
0
|
|
|
|
|
|
[$_, 100 * $_->percent_free] |
357
|
|
|
|
|
|
|
} sort { |
358
|
0
|
|
|
|
|
|
$b->percent_free <=> $a->percent_free; |
359
|
|
|
|
|
|
|
} grep { |
360
|
0
|
|
|
0
|
0
|
|
$_->should_get_new_files; |
361
|
|
|
|
|
|
|
} @_; |
362
|
|
|
|
|
|
|
|
363
|
0
|
|
|
|
|
|
my @list = |
364
|
|
|
|
|
|
|
MogileFS::Util::weighted_list(splice(@devices_with_weights, 0, 20)); |
365
|
|
|
|
|
|
|
|
366
|
0
|
|
|
|
|
|
return @list; |
367
|
|
|
|
|
|
|
} |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
sub valid_key { |
370
|
0
|
|
|
0
|
0
|
|
my ($key) = @_; |
371
|
|
|
|
|
|
|
|
372
|
0
|
|
0
|
|
|
|
return defined($key) && length($key); |
373
|
|
|
|
|
|
|
} |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
sub cmd_create_close { |
376
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
377
|
0
|
|
|
|
|
|
my $args = shift; |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
# has to be filled out for some plugins |
380
|
0
|
0
|
|
|
|
|
$args->{dmid} = $self->check_domain($args) or return; |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
# call out to a hook that might modify the arguments for us |
383
|
0
|
|
|
|
|
|
MogileFS::run_global_hook('cmd_create_close', $args); |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
# late validation of parameters |
386
|
0
|
|
|
|
|
|
my $dmid = $args->{dmid}; |
387
|
0
|
|
|
|
|
|
my $key = $args->{key}; |
388
|
0
|
0
|
|
|
|
|
my $fidid = $args->{fid} or return $self->err_line("no_fid"); |
389
|
0
|
0
|
|
|
|
|
my $devid = $args->{devid} or return $self->err_line("no_devid"); |
390
|
0
|
0
|
|
|
|
|
my $path = $args->{path} or return $self->err_line("no_path"); |
391
|
0
|
|
|
|
|
|
my $checksum = $args->{checksum}; |
392
|
|
|
|
|
|
|
|
393
|
0
|
0
|
|
|
|
|
if ($checksum) { |
394
|
0
|
|
|
|
|
|
$checksum = eval { MogileFS::Checksum->from_string($fidid, $checksum) }; |
|
0
|
|
|
|
|
|
|
395
|
0
|
0
|
|
|
|
|
return $self->err_line("invalid_checksum_format") if $@; |
396
|
|
|
|
|
|
|
} |
397
|
|
|
|
|
|
|
|
398
|
0
|
|
|
|
|
|
my $fid = MogileFS::FID->new($fidid); |
399
|
0
|
|
|
|
|
|
my $dfid = MogileFS::DevFID->new($devid, $fid); |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
# is the provided path what we'd expect for this fid/devid? |
402
|
0
|
0
|
|
|
|
|
return $self->err_line("bogus_args") |
403
|
|
|
|
|
|
|
unless $path eq $dfid->url; |
404
|
|
|
|
|
|
|
|
405
|
0
|
|
|
|
|
|
my $sto = Mgd::get_store(); |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
# find the temp file we're closing and making real. If another worker |
408
|
|
|
|
|
|
|
# already has it, bail out---the client closed it twice. |
409
|
|
|
|
|
|
|
# this is racy, but the only expected use case is a client retrying. |
410
|
|
|
|
|
|
|
# should still be fixed better once more scalable locking is available. |
411
|
0
|
0
|
|
|
|
|
my $trow = $sto->delete_and_return_tempfile_row($fidid) or |
412
|
|
|
|
|
|
|
return $self->err_line("no_temp_file"); |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
# Protect against leaving orphaned uploads. |
415
|
|
|
|
|
|
|
my $failed = sub { |
416
|
0
|
|
|
0
|
|
|
$dfid->add_to_db; |
417
|
0
|
|
|
|
|
|
$fid->delete; |
418
|
0
|
|
|
|
|
|
}; |
419
|
|
|
|
|
|
|
|
420
|
0
|
0
|
|
|
|
|
unless ($trow->{devids} =~ m/\b$devid\b/) { |
421
|
0
|
|
|
|
|
|
$failed->(); |
422
|
0
|
|
|
|
|
|
return $self->err_line("invalid_destdev", "File uploaded to invalid dest $devid. Valid devices were: " . $trow->{devids}); |
423
|
|
|
|
|
|
|
} |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
# if a temp file is closed without a provided-key, that means to |
426
|
|
|
|
|
|
|
# delete it. |
427
|
0
|
0
|
|
|
|
|
unless (valid_key($key)) { |
428
|
0
|
|
|
|
|
|
$failed->(); |
429
|
0
|
|
|
|
|
|
return $self->ok_line; |
430
|
|
|
|
|
|
|
} |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
# get size of file and verify that it matches what we were given, if anything |
433
|
0
|
|
|
|
|
|
my $httpfile = MogileFS::HTTPFile->at($path); |
434
|
0
|
|
|
|
|
|
my $size = $httpfile->size; |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
# size check is optional? Needs to support zero byte files. |
437
|
0
|
0
|
|
|
|
|
$args->{size} = -1 unless $args->{size}; |
438
|
0
|
0
|
0
|
|
|
|
if (!defined($size) || $size == MogileFS::HTTPFile::FILE_MISSING) { |
439
|
|
|
|
|
|
|
# storage node is unreachable or the file is missing |
440
|
0
|
0
|
|
|
|
|
my $type = defined $size ? "missing" : "cantreach"; |
441
|
0
|
|
|
|
|
|
my $lasterr = MogileFS::Util::last_error(); |
442
|
0
|
|
|
|
|
|
$failed->(); |
443
|
0
|
|
|
|
|
|
return $self->err_line("size_verify_error", "Expected: $args->{size}; actual: 0 ($type); path: $path; error: $lasterr") |
444
|
|
|
|
|
|
|
} |
445
|
|
|
|
|
|
|
|
446
|
0
|
0
|
0
|
|
|
|
if ($args->{size} > -1 && ($args->{size} != $size)) { |
447
|
0
|
|
|
|
|
|
$failed->(); |
448
|
0
|
|
|
|
|
|
return $self->err_line("size_mismatch", "Expected: $args->{size}; actual: $size; path: $path") |
449
|
|
|
|
|
|
|
} |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
# checksum validation is optional as it can be very expensive |
452
|
|
|
|
|
|
|
# However, we /always/ verify it if the client wants us to, even |
453
|
|
|
|
|
|
|
# if the class does not enforce or store it. |
454
|
0
|
0
|
0
|
|
|
|
if ($checksum && $args->{checksumverify}) { |
455
|
0
|
|
|
|
|
|
my $alg = $checksum->hashname; |
456
|
0
|
|
|
0
|
|
|
my $actual = $httpfile->digest($alg, sub { $self->still_alive }); |
|
0
|
|
|
|
|
|
|
457
|
0
|
0
|
|
|
|
|
if ($actual ne $checksum->{checksum}) { |
458
|
0
|
|
|
|
|
|
$failed->(); |
459
|
0
|
|
|
|
|
|
$actual = "$alg:" . unpack("H*", $actual); |
460
|
0
|
|
|
|
|
|
return $self->err_line("checksum_mismatch", |
461
|
|
|
|
|
|
|
"Expected: $checksum; actual: $actual; path: $path"); |
462
|
|
|
|
|
|
|
} |
463
|
|
|
|
|
|
|
} |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
# see if we have a fid for this key already |
466
|
0
|
|
|
|
|
|
my $old_fid = MogileFS::FID->new_from_dmid_and_key($dmid, $key); |
467
|
0
|
0
|
|
|
|
|
if ($old_fid) { |
468
|
|
|
|
|
|
|
# Fail if a file already exists for this fid. Should never |
469
|
|
|
|
|
|
|
# happen, as it should not be possible to close a file twice. |
470
|
0
|
0
|
|
|
|
|
return $self->err_line("fid_exists") |
471
|
|
|
|
|
|
|
unless $old_fid->{fidid} != $fidid; |
472
|
|
|
|
|
|
|
|
473
|
0
|
|
|
|
|
|
$old_fid->delete; |
474
|
|
|
|
|
|
|
} |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
# TODO: check for EIO? |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
# insert file_on row |
479
|
0
|
|
|
|
|
|
$dfid->add_to_db; |
480
|
|
|
|
|
|
|
|
481
|
0
|
0
|
|
|
|
|
$checksum->maybe_save($dmid, $trow->{classid}) if $checksum; |
482
|
|
|
|
|
|
|
|
483
|
0
|
|
|
|
|
|
$sto->replace_into_file( |
484
|
|
|
|
|
|
|
fidid => $fidid, |
485
|
|
|
|
|
|
|
dmid => $dmid, |
486
|
|
|
|
|
|
|
key => $key, |
487
|
|
|
|
|
|
|
length => $size, |
488
|
|
|
|
|
|
|
classid => $trow->{classid}, |
489
|
|
|
|
|
|
|
devcount => 1, |
490
|
|
|
|
|
|
|
); |
491
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
# mark it as needing replicating: |
493
|
0
|
|
|
|
|
|
$fid->enqueue_for_replication(); |
494
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
# call the hook - if this fails, we need to back the file out |
496
|
0
|
|
|
|
|
|
my $rv = MogileFS::run_global_hook('file_stored', $args); |
497
|
0
|
0
|
0
|
|
|
|
if (defined $rv && ! $rv) { # undef = no hooks, 1 = success, 0 = failure |
498
|
0
|
|
|
|
|
|
$fid->delete; |
499
|
0
|
|
|
|
|
|
return $self->err_line("plugin_aborted"); |
500
|
|
|
|
|
|
|
} |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
# all went well, we would've hit condthrow on DB errors |
503
|
0
|
|
|
|
|
|
return $self->ok_line; |
504
|
|
|
|
|
|
|
} |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
sub cmd_updateclass { |
507
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
508
|
0
|
|
|
|
|
|
my $args = shift; |
509
|
|
|
|
|
|
|
|
510
|
0
|
0
|
|
|
|
|
$args->{dmid} = $self->check_domain($args) or return; |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
# call out to a hook that might modify the arguments for us, abort if it tells us to |
513
|
0
|
|
|
|
|
|
my $rv = MogileFS::run_global_hook('cmd_updateclass', $args); |
514
|
0
|
0
|
0
|
|
|
|
return $self->err_line('plugin_aborted') if defined $rv && ! $rv; |
515
|
|
|
|
|
|
|
|
516
|
0
|
|
|
|
|
|
my $dmid = $args->{dmid}; |
517
|
0
|
|
|
|
|
|
my $key = $args->{key}; |
518
|
0
|
0
|
|
|
|
|
valid_key($key) or return $self->err_line("no_key"); |
519
|
0
|
0
|
|
|
|
|
my $class = $args->{class} or return $self->err_line("no_class"); |
520
|
|
|
|
|
|
|
|
521
|
0
|
0
|
|
|
|
|
my $classobj = Mgd::class_factory()->get_by_name($dmid, $class) |
522
|
|
|
|
|
|
|
or return $self->err_line('class_not_found'); |
523
|
0
|
|
|
|
|
|
my $classid = $classobj->id; |
524
|
|
|
|
|
|
|
|
525
|
0
|
0
|
|
|
|
|
my $fid = MogileFS::FID->new_from_dmid_and_key($dmid, $key) |
526
|
|
|
|
|
|
|
or return $self->err_line('invalid_key'); |
527
|
|
|
|
|
|
|
|
528
|
0
|
|
|
|
|
|
my @devids = $fid->devids; |
529
|
0
|
0
|
|
|
|
|
return $self->err_line("no_devices") unless @devids; |
530
|
|
|
|
|
|
|
|
531
|
0
|
0
|
|
|
|
|
if ($fid->classid != $classid) { |
532
|
0
|
|
|
|
|
|
$fid->update_class(classid => $classid); |
533
|
0
|
|
|
|
|
|
$fid->enqueue_for_replication(); |
534
|
|
|
|
|
|
|
} |
535
|
|
|
|
|
|
|
|
536
|
0
|
|
|
|
|
|
return $self->ok_line; |
537
|
|
|
|
|
|
|
} |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
sub cmd_delete { |
540
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
541
|
0
|
|
|
|
|
|
my $args = shift; |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
# validate domain for plugins |
544
|
0
|
0
|
|
|
|
|
$args->{dmid} = $self->check_domain($args) or return; |
545
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
# now invoke the plugin, abort if it tells us to |
547
|
0
|
|
|
|
|
|
my $rv = MogileFS::run_global_hook('cmd_delete', $args); |
548
|
0
|
0
|
0
|
|
|
|
return $self->err_line('plugin_aborted') |
549
|
|
|
|
|
|
|
if defined $rv && ! $rv; |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
# validate parameters |
552
|
0
|
|
|
|
|
|
my $dmid = $args->{dmid}; |
553
|
0
|
|
|
|
|
|
my $key = $args->{key}; |
554
|
|
|
|
|
|
|
|
555
|
0
|
0
|
|
|
|
|
valid_key($key) or return $self->err_line("no_key"); |
556
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
# is this fid still owned by this key? |
558
|
0
|
0
|
|
|
|
|
my $fid = MogileFS::FID->new_from_dmid_and_key($dmid, $key) |
559
|
|
|
|
|
|
|
or return $self->err_line("unknown_key"); |
560
|
|
|
|
|
|
|
|
561
|
0
|
|
|
|
|
|
$fid->delete; |
562
|
|
|
|
|
|
|
|
563
|
0
|
|
|
|
|
|
return $self->ok_line; |
564
|
|
|
|
|
|
|
} |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
# Takes either domain/dkey or fid and tries to return as much as possible. |
567
|
|
|
|
|
|
|
sub cmd_file_debug { |
568
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
569
|
0
|
|
|
|
|
|
my $args = shift; |
570
|
|
|
|
|
|
|
# Talk to the master since this is "debug mode" |
571
|
0
|
|
|
|
|
|
my $sto = Mgd::get_store(); |
572
|
0
|
|
|
|
|
|
my $ret = {}; |
573
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
# If a FID is provided, just use that. |
575
|
0
|
|
|
|
|
|
my $fid; |
576
|
|
|
|
|
|
|
my $fidid; |
577
|
0
|
0
|
|
|
|
|
if ($args->{fid}) { |
578
|
0
|
|
|
|
|
|
$fidid = $args->{fid}+0; |
579
|
|
|
|
|
|
|
# It's not fatal if we don't find the row here. |
580
|
0
|
|
|
|
|
|
$fid = $sto->file_row_from_fidid($args->{fid}+0); |
581
|
|
|
|
|
|
|
} else { |
582
|
|
|
|
|
|
|
# If not, require dmid/dkey and pick up the fid from there. |
583
|
0
|
0
|
|
|
|
|
$args->{dmid} = $self->check_domain($args) or return; |
584
|
0
|
0
|
|
|
|
|
return $self->err_line("no_key") unless valid_key($args->{key}); |
585
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
# now invoke the plugin, abort if it tells us to |
587
|
0
|
|
|
|
|
|
my $rv = MogileFS::run_global_hook('cmd_file_debug', $args); |
588
|
0
|
0
|
0
|
|
|
|
return $self->err_line('plugin_aborted') |
589
|
|
|
|
|
|
|
if defined $rv && ! $rv; |
590
|
|
|
|
|
|
|
|
591
|
0
|
|
|
|
|
|
$fid = $sto->file_row_from_dmid_key($args->{dmid}, $args->{key}); |
592
|
0
|
0
|
|
|
|
|
return $self->err_line("unknown_key") unless $fid; |
593
|
0
|
|
|
|
|
|
$fidid = $fid->{fid}; |
594
|
|
|
|
|
|
|
} |
595
|
|
|
|
|
|
|
|
596
|
0
|
0
|
|
|
|
|
if ($fid) { |
597
|
0
|
|
|
|
|
|
$fid->{domain} = Mgd::domain_factory()->get_by_id($fid->{dmid})->name; |
598
|
0
|
|
|
|
|
|
$fid->{class} = Mgd::class_factory()->get_by_id($fid->{dmid}, |
599
|
|
|
|
|
|
|
$fid->{classid})->name; |
600
|
|
|
|
|
|
|
} |
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
# Fetch all of the queue data. |
603
|
0
|
|
|
|
|
|
my $tfile = $sto->tempfile_row_from_fid($fidid); |
604
|
0
|
|
|
|
|
|
my $repl = $sto->find_fid_from_file_to_replicate($fidid); |
605
|
0
|
|
|
|
|
|
my $del = $sto->find_fid_from_file_to_delete2($fidid); |
606
|
0
|
|
|
|
|
|
my $reb = $sto->find_fid_from_file_to_queue($fidid, REBAL_QUEUE); |
607
|
0
|
|
|
|
|
|
my $fsck = $sto->find_fid_from_file_to_queue($fidid, FSCK_QUEUE); |
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
# Fetch file_on rows, and turn into paths. |
610
|
0
|
|
|
|
|
|
my @devids = $sto->fid_devids($fidid); |
611
|
0
|
|
|
|
|
|
for my $devid (@devids) { |
612
|
|
|
|
|
|
|
# Won't matter if we can't make the path (dev is dead/deleted/etc) |
613
|
0
|
|
|
|
|
|
eval { |
614
|
0
|
|
|
|
|
|
my $dfid = MogileFS::DevFID->new($devid, $fidid); |
615
|
0
|
|
|
|
|
|
my $path = $dfid->get_url; |
616
|
0
|
|
|
|
|
|
$ret->{'devpath_' . $devid} = $path; |
617
|
|
|
|
|
|
|
}; |
618
|
|
|
|
|
|
|
} |
619
|
0
|
0
|
|
|
|
|
$ret->{devids} = join(',', @devids) if @devids; |
620
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
# Always look for a checksum |
622
|
0
|
|
|
|
|
|
my $checksum = Mgd::get_store()->get_checksum($fidid); |
623
|
0
|
0
|
|
|
|
|
if ($checksum) { |
624
|
0
|
|
|
|
|
|
$checksum = MogileFS::Checksum->new($checksum); |
625
|
0
|
|
|
|
|
|
$ret->{checksum} = $checksum->info; |
626
|
|
|
|
|
|
|
} else { |
627
|
0
|
|
|
|
|
|
$ret->{checksum} = 'NONE'; |
628
|
|
|
|
|
|
|
} |
629
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
# Return file row (if found) and all other data. |
631
|
0
|
|
|
|
|
|
my %toret = (fid => $fid, tempfile => $tfile, replqueue => $repl, |
632
|
|
|
|
|
|
|
delqueue => $del, rebqueue => $reb, fsckqueue => $fsck); |
633
|
0
|
|
|
|
|
|
while (my ($key, $hash) = each %toret) { |
634
|
0
|
|
|
|
|
|
while (my ($name, $val) = each %$hash) { |
635
|
0
|
|
|
|
|
|
$ret->{$key . '_' . $name} = $val; |
636
|
|
|
|
|
|
|
} |
637
|
|
|
|
|
|
|
} |
638
|
|
|
|
|
|
|
|
639
|
0
|
0
|
|
|
|
|
return $self->err_line("unknown_fid") unless keys %$ret; |
640
|
0
|
|
|
|
|
|
return $self->ok_line($ret); |
641
|
|
|
|
|
|
|
} |
642
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
sub cmd_file_info { |
644
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
645
|
0
|
|
|
|
|
|
my $args = shift; |
646
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
# validate domain for plugins |
648
|
0
|
0
|
|
|
|
|
$args->{dmid} = $self->check_domain($args) or return; |
649
|
|
|
|
|
|
|
|
650
|
|
|
|
|
|
|
# now invoke the plugin, abort if it tells us to |
651
|
0
|
|
|
|
|
|
my $rv = MogileFS::run_global_hook('cmd_file_info', $args); |
652
|
0
|
0
|
0
|
|
|
|
return $self->err_line('plugin_aborted') |
653
|
|
|
|
|
|
|
if defined $rv && ! $rv; |
654
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
# validate parameters |
656
|
0
|
|
|
|
|
|
my $dmid = $args->{dmid}; |
657
|
0
|
|
|
|
|
|
my $key = $args->{key}; |
658
|
|
|
|
|
|
|
|
659
|
0
|
0
|
|
|
|
|
valid_key($key) or return $self->err_line("no_key"); |
660
|
|
|
|
|
|
|
|
661
|
0
|
|
|
|
|
|
my $fid; |
662
|
|
|
|
|
|
|
Mgd::get_store()->slaves_ok(sub { |
663
|
0
|
|
|
0
|
|
|
$fid = MogileFS::FID->new_from_dmid_and_key($dmid, $key); |
664
|
0
|
|
|
|
|
|
}); |
665
|
0
|
0
|
|
|
|
|
$fid or return $self->err_line("unknown_key"); |
666
|
|
|
|
|
|
|
|
667
|
0
|
|
|
|
|
|
my $ret = {}; |
668
|
0
|
|
|
|
|
|
$ret->{fid} = $fid->id; |
669
|
0
|
|
|
|
|
|
$ret->{domain} = Mgd::domain_factory()->get_by_id($fid->dmid)->name; |
670
|
0
|
|
|
|
|
|
my $class = Mgd::class_factory()->get_by_id($fid->dmid, $fid->classid); |
671
|
0
|
|
|
|
|
|
$ret->{class} = $class->name; |
672
|
0
|
0
|
|
|
|
|
if ($class->{hashtype}) { |
673
|
0
|
|
|
|
|
|
my $checksum = Mgd::get_store()->get_checksum($fid->id); |
674
|
0
|
0
|
|
|
|
|
if ($checksum) { |
675
|
0
|
|
|
|
|
|
$checksum = MogileFS::Checksum->new($checksum); |
676
|
0
|
|
|
|
|
|
$ret->{checksum} = $checksum->info; |
677
|
|
|
|
|
|
|
} else { |
678
|
0
|
|
|
|
|
|
$ret->{checksum} = "MISSING"; |
679
|
|
|
|
|
|
|
} |
680
|
|
|
|
|
|
|
} |
681
|
0
|
|
|
|
|
|
$ret->{key} = $key; |
682
|
0
|
|
|
|
|
|
$ret->{'length'} = $fid->length; |
683
|
0
|
|
|
|
|
|
$ret->{devcount} = $fid->devcount; |
684
|
|
|
|
|
|
|
# Only if requested, also return the raw devids. |
685
|
|
|
|
|
|
|
# Caller should use get_paths if they intend to fetch the file. |
686
|
0
|
0
|
|
|
|
|
if ($args->{devices}) { |
687
|
0
|
|
|
|
|
|
$ret->{devids} = join(',', $fid->devids); |
688
|
|
|
|
|
|
|
} |
689
|
|
|
|
|
|
|
|
690
|
0
|
|
|
|
|
|
return $self->ok_line($ret); |
691
|
|
|
|
|
|
|
} |
692
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
sub cmd_list_fids { |
694
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
695
|
0
|
|
|
|
|
|
my $args = shift; |
696
|
|
|
|
|
|
|
|
697
|
|
|
|
|
|
|
# validate parameters |
698
|
0
|
|
0
|
|
|
|
my $fromfid = ($args->{from} || 0)+0; |
699
|
0
|
|
0
|
|
|
|
my $count = ($args->{to} || 0)+0; |
700
|
0
|
|
0
|
|
|
|
$count ||= 100; |
701
|
0
|
0
|
0
|
|
|
|
$count = 500 if $count > 500 || $count < 0; |
702
|
|
|
|
|
|
|
|
703
|
0
|
|
|
|
|
|
my $rows = Mgd::get_store()->file_row_from_fidid_range($fromfid, $count); |
704
|
0
|
0
|
|
|
|
|
return $self->err_line('failure') unless $rows; |
705
|
0
|
0
|
|
|
|
|
return $self->ok_line({ fid_count => 0 }) unless @$rows; |
706
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
# setup temporary storage of class/host |
708
|
0
|
|
|
|
|
|
my (%domains, %classes); |
709
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
# now iterate over our data rows and construct result |
711
|
0
|
|
|
|
|
|
my $ct = 0; |
712
|
0
|
|
|
|
|
|
my $ret = {}; |
713
|
0
|
|
|
|
|
|
foreach my $r (@$rows) { |
714
|
0
|
|
|
|
|
|
$ct++; |
715
|
0
|
|
|
|
|
|
my $fid = $r->{fid}; |
716
|
0
|
|
|
|
|
|
$ret->{"fid_${ct}_fid"} = $fid; |
717
|
0
|
|
0
|
|
|
|
$ret->{"fid_${ct}_domain"} = ($domains{$r->{dmid}} ||= |
718
|
|
|
|
|
|
|
Mgd::domain_factory()->get_by_id($r->{dmid})->name); |
719
|
0
|
|
0
|
|
|
|
$ret->{"fid_${ct}_class"} = ($classes{$r->{dmid}}{$r->{classid}} ||= |
720
|
|
|
|
|
|
|
Mgd::class_factory()->get_by_id($r->{dmid}, $r->{classid})->name); |
721
|
0
|
|
|
|
|
|
$ret->{"fid_${ct}_key"} = $r->{dkey}; |
722
|
0
|
|
|
|
|
|
$ret->{"fid_${ct}_length"} = $r->{length}; |
723
|
0
|
|
|
|
|
|
$ret->{"fid_${ct}_devcount"} = $r->{devcount}; |
724
|
|
|
|
|
|
|
} |
725
|
0
|
|
|
|
|
|
$ret->{fid_count} = $ct; |
726
|
0
|
|
|
|
|
|
return $self->ok_line($ret); |
727
|
|
|
|
|
|
|
} |
728
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
sub cmd_list_keys { |
730
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
731
|
0
|
|
|
|
|
|
my $args = shift; |
732
|
|
|
|
|
|
|
|
733
|
|
|
|
|
|
|
# validate parameters |
734
|
0
|
0
|
|
|
|
|
my $dmid = $self->check_domain($args) or return; |
735
|
0
|
|
|
|
|
|
my ($prefix, $after, $limit) = ($args->{prefix}, $args->{after}, $args->{limit}); |
736
|
|
|
|
|
|
|
|
737
|
0
|
0
|
0
|
|
|
|
if (defined $prefix and $prefix ne '') { |
738
|
|
|
|
|
|
|
# now validate that after matches prefix |
739
|
0
|
0
|
0
|
|
|
|
return $self->err_line('after_mismatch') |
740
|
|
|
|
|
|
|
if $after && $after !~ /^$prefix/; |
741
|
|
|
|
|
|
|
} |
742
|
|
|
|
|
|
|
|
743
|
0
|
|
0
|
|
|
|
$limit ||= 1000; |
744
|
0
|
|
|
|
|
|
$limit += 0; |
745
|
0
|
0
|
|
|
|
|
$limit = 1000 if $limit > 1000; |
746
|
|
|
|
|
|
|
|
747
|
0
|
|
|
|
|
|
my $keys = Mgd::get_store()->get_keys_like($dmid, $prefix, $after, $limit); |
748
|
|
|
|
|
|
|
|
749
|
|
|
|
|
|
|
# if we got nothing, say so |
750
|
0
|
0
|
0
|
|
|
|
return $self->err_line('none_match') unless $keys && @$keys; |
751
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
# construct the output and send |
753
|
0
|
|
|
|
|
|
my $ret = { key_count => 0, next_after => '' }; |
754
|
0
|
|
|
|
|
|
foreach my $key (@$keys) { |
755
|
0
|
|
|
|
|
|
$ret->{key_count}++; |
756
|
0
|
0
|
|
|
|
|
$ret->{next_after} = $key |
757
|
|
|
|
|
|
|
if $key gt $ret->{next_after}; |
758
|
0
|
|
|
|
|
|
$ret->{"key_$ret->{key_count}"} = $key; |
759
|
|
|
|
|
|
|
} |
760
|
0
|
|
|
|
|
|
return $self->ok_line($ret); |
761
|
|
|
|
|
|
|
} |
762
|
|
|
|
|
|
|
|
763
|
|
|
|
|
|
|
sub cmd_rename { |
764
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
765
|
0
|
|
|
|
|
|
my $args = shift; |
766
|
|
|
|
|
|
|
|
767
|
|
|
|
|
|
|
# validate parameters |
768
|
0
|
0
|
|
|
|
|
my $dmid = $self->check_domain($args) or return; |
769
|
0
|
|
|
|
|
|
my ($fkey, $tkey) = ($args->{from_key}, $args->{to_key}); |
770
|
0
|
0
|
0
|
|
|
|
unless (valid_key($fkey) && valid_key($tkey)) { |
771
|
0
|
|
|
|
|
|
return $self->err_line("no_key"); |
772
|
|
|
|
|
|
|
} |
773
|
|
|
|
|
|
|
|
774
|
0
|
0
|
|
|
|
|
my $fid = MogileFS::FID->new_from_dmid_and_key($dmid, $fkey) |
775
|
|
|
|
|
|
|
or return $self->err_line("unknown_key"); |
776
|
|
|
|
|
|
|
|
777
|
0
|
0
|
|
|
|
|
$fid->rename($tkey) or |
778
|
|
|
|
|
|
|
$self->err_line("key_exists"); |
779
|
|
|
|
|
|
|
|
780
|
0
|
|
|
|
|
|
return $self->ok_line; |
781
|
|
|
|
|
|
|
} |
782
|
|
|
|
|
|
|
|
783
|
|
|
|
|
|
|
sub cmd_get_hosts { |
784
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
785
|
0
|
|
|
|
|
|
my $args = shift; |
786
|
|
|
|
|
|
|
|
787
|
0
|
|
|
|
|
|
my $ret = { hosts => 0 }; |
788
|
0
|
|
|
|
|
|
for my $host (Mgd::host_factory()->get_all) { |
789
|
0
|
0
|
0
|
|
|
|
next if defined $args->{hostid} && $host->id != $args->{hostid}; |
790
|
0
|
|
|
|
|
|
my $n = ++$ret->{hosts}; |
791
|
0
|
|
|
|
|
|
my $fields = $host->fields(qw(hostid status hostname hostip http_port |
792
|
|
|
|
|
|
|
http_get_port altip altmask)); |
793
|
0
|
|
|
|
|
|
while (my ($key, $val) = each %$fields) { |
794
|
|
|
|
|
|
|
# must be regular data so copy it in |
795
|
0
|
|
|
|
|
|
$ret->{"host${n}_$key"} = $val; |
796
|
|
|
|
|
|
|
} |
797
|
|
|
|
|
|
|
} |
798
|
|
|
|
|
|
|
|
799
|
0
|
|
|
|
|
|
return $self->ok_line($ret); |
800
|
|
|
|
|
|
|
} |
801
|
|
|
|
|
|
|
|
802
|
|
|
|
|
|
|
sub cmd_get_devices { |
803
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
804
|
0
|
|
|
|
|
|
my $args = shift; |
805
|
|
|
|
|
|
|
|
806
|
0
|
|
|
|
|
|
my $ret = { devices => 0 }; |
807
|
0
|
|
|
|
|
|
for my $dev (Mgd::device_factory()->get_all) { |
808
|
0
|
0
|
0
|
|
|
|
next if defined $args->{devid} && $dev->id != $args->{devid}; |
809
|
0
|
|
|
|
|
|
my $n = ++$ret->{devices}; |
810
|
|
|
|
|
|
|
|
811
|
0
|
|
|
|
|
|
my $sum = $dev->fields; |
812
|
0
|
|
|
|
|
|
while (my ($key, $val) = each %$sum) { |
813
|
0
|
|
|
|
|
|
$ret->{"dev${n}_$key"} = $val; |
814
|
|
|
|
|
|
|
} |
815
|
|
|
|
|
|
|
} |
816
|
|
|
|
|
|
|
|
817
|
0
|
|
|
|
|
|
return $self->ok_line($ret); |
818
|
|
|
|
|
|
|
} |
819
|
|
|
|
|
|
|
|
820
|
|
|
|
|
|
|
sub cmd_create_device { |
821
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
822
|
0
|
|
|
|
|
|
my $args = shift; |
823
|
|
|
|
|
|
|
|
824
|
0
|
|
0
|
|
|
|
my $status = $args->{state} || "alive"; |
825
|
0
|
0
|
|
|
|
|
return $self->err_line("invalid_state") unless |
826
|
|
|
|
|
|
|
device_state($status); |
827
|
|
|
|
|
|
|
|
828
|
0
|
|
|
|
|
|
my $devid = $args->{devid}; |
829
|
0
|
0
|
0
|
|
|
|
return $self->err_line("invalid_devid") unless $devid && $devid =~ /^\d+$/; |
830
|
|
|
|
|
|
|
|
831
|
0
|
|
|
|
|
|
my $hostid; |
832
|
|
|
|
|
|
|
|
833
|
0
|
|
|
|
|
|
my $sto = Mgd::get_store(); |
834
|
0
|
0
|
0
|
|
|
|
if ($args->{hostid} && $args->{hostid} =~ /^\d+$/) { |
|
|
0
|
|
|
|
|
|
835
|
0
|
|
|
|
|
|
$hostid = $sto->get_hostid_by_id($args->{hostid}); |
836
|
0
|
0
|
|
|
|
|
return $self->err_line("unknown_hostid") unless $hostid; |
837
|
|
|
|
|
|
|
} elsif (my $hname = $args->{hostname}) { |
838
|
0
|
|
|
|
|
|
$hostid = $sto->get_hostid_by_name($hname); |
839
|
0
|
0
|
|
|
|
|
return $self->err_line("unknown_host") unless $hostid; |
840
|
|
|
|
|
|
|
} else { |
841
|
0
|
|
|
|
|
|
return $self->err_line("bad_args", "No hostid/hostname parameter"); |
842
|
|
|
|
|
|
|
} |
843
|
|
|
|
|
|
|
|
844
|
0
|
0
|
|
|
|
|
if (eval { $sto->create_device($devid, $hostid, $status) }) { |
|
0
|
|
|
|
|
|
|
845
|
0
|
|
|
|
|
|
return $self->cmd_clear_cache; |
846
|
|
|
|
|
|
|
} |
847
|
|
|
|
|
|
|
|
848
|
0
|
|
|
|
|
|
my $errc = error_code($@); |
849
|
0
|
0
|
|
|
|
|
return $self->err_line("existing_devid") if $errc; |
850
|
0
|
|
|
|
|
|
die $@; # rethrow; |
851
|
|
|
|
|
|
|
} |
852
|
|
|
|
|
|
|
|
853
|
|
|
|
|
|
|
sub cmd_create_domain { |
854
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
855
|
0
|
|
|
|
|
|
my $args = shift; |
856
|
|
|
|
|
|
|
|
857
|
0
|
0
|
|
|
|
|
my $domain = $args->{domain} or |
858
|
|
|
|
|
|
|
return $self->err_line('no_domain'); |
859
|
|
|
|
|
|
|
|
860
|
0
|
|
|
|
|
|
my $dom = eval { Mgd::get_store()->create_domain($domain); }; |
|
0
|
|
|
|
|
|
|
861
|
0
|
0
|
|
|
|
|
if ($@) { |
862
|
0
|
0
|
|
|
|
|
if (error_code($@) eq "dup") { |
863
|
0
|
|
|
|
|
|
return $self->err_line('domain_exists'); |
864
|
|
|
|
|
|
|
} |
865
|
0
|
|
|
|
|
|
return $self->err_line('failure', "$@"); |
866
|
|
|
|
|
|
|
} |
867
|
|
|
|
|
|
|
|
868
|
0
|
|
|
|
|
|
return $self->cmd_clear_cache({ domain => $domain }); |
869
|
|
|
|
|
|
|
} |
870
|
|
|
|
|
|
|
|
871
|
|
|
|
|
|
|
sub cmd_delete_domain { |
872
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
873
|
0
|
|
|
|
|
|
my $args = shift; |
874
|
|
|
|
|
|
|
|
875
|
0
|
0
|
|
|
|
|
my $domain = $args->{domain} or |
876
|
|
|
|
|
|
|
return $self->err_line('no_domain'); |
877
|
|
|
|
|
|
|
|
878
|
0
|
|
|
|
|
|
my $sto = Mgd::get_store(); |
879
|
0
|
0
|
|
|
|
|
my $dmid = $sto->get_domainid_by_name($domain) or |
880
|
|
|
|
|
|
|
return $self->err_line('domain_not_found'); |
881
|
|
|
|
|
|
|
|
882
|
0
|
0
|
|
|
|
|
if (eval { $sto->delete_domain($dmid) }) { |
|
0
|
|
|
|
|
|
|
883
|
0
|
|
|
|
|
|
return $self->cmd_clear_cache({ domain => $domain }); |
884
|
|
|
|
|
|
|
} |
885
|
|
|
|
|
|
|
|
886
|
0
|
|
|
|
|
|
my $err = error_code($@); |
887
|
0
|
0
|
|
|
|
|
return $self->err_line('domain_has_files') if $err eq "has_files"; |
888
|
0
|
0
|
|
|
|
|
return $self->err_line('domain_has_classes') if $err eq "has_classes"; |
889
|
0
|
|
|
|
|
|
return $self->err_line("failure"); |
890
|
|
|
|
|
|
|
} |
891
|
|
|
|
|
|
|
|
892
|
|
|
|
|
|
|
sub cmd_create_class { |
893
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
894
|
0
|
|
|
|
|
|
my $args = shift; |
895
|
|
|
|
|
|
|
|
896
|
0
|
|
|
|
|
|
my $domain = $args->{domain}; |
897
|
0
|
0
|
|
|
|
|
return $self->err_line('no_domain') unless length $domain; |
898
|
|
|
|
|
|
|
|
899
|
0
|
|
|
|
|
|
my $class = $args->{class}; |
900
|
0
|
0
|
|
|
|
|
return $self->err_line('no_class') unless length $class; |
901
|
|
|
|
|
|
|
|
902
|
0
|
|
|
|
|
|
my $mindevcount = $args->{mindevcount}+0; |
903
|
0
|
0
|
|
|
|
|
return $self->err_line('invalid_mindevcount') unless $mindevcount > 0; |
904
|
|
|
|
|
|
|
|
905
|
0
|
|
0
|
|
|
|
my $replpolicy = $args->{replpolicy} || ''; |
906
|
0
|
0
|
|
|
|
|
if ($replpolicy) { |
907
|
0
|
|
|
|
|
|
eval { |
908
|
0
|
|
|
|
|
|
MogileFS::ReplicationPolicy->new_from_policy_string($replpolicy); |
909
|
|
|
|
|
|
|
}; |
910
|
0
|
0
|
|
|
|
|
return $self->err_line('invalid_replpolicy', $@) if $@; |
911
|
|
|
|
|
|
|
} |
912
|
|
|
|
|
|
|
|
913
|
0
|
|
|
|
|
|
my $hashtype = $args->{hashtype}; |
914
|
0
|
0
|
0
|
|
|
|
if ($hashtype && $hashtype ne 'NONE') { |
915
|
0
|
|
|
|
|
|
my $tmp = $MogileFS::Checksum::NAME2TYPE{$hashtype}; |
916
|
0
|
0
|
|
|
|
|
return $self->err_line('invalid_hashtype') unless $tmp; |
917
|
0
|
|
|
|
|
|
$hashtype = $tmp; |
918
|
|
|
|
|
|
|
} |
919
|
|
|
|
|
|
|
|
920
|
0
|
|
|
|
|
|
my $sto = Mgd::get_store(); |
921
|
0
|
0
|
|
|
|
|
my $dmid = $sto->get_domainid_by_name($domain) or |
922
|
|
|
|
|
|
|
return $self->err_line('domain_not_found'); |
923
|
|
|
|
|
|
|
|
924
|
0
|
|
|
|
|
|
my $clsid = $sto->get_classid_by_name($dmid, $class); |
925
|
0
|
0
|
0
|
|
|
|
if (!defined $clsid && $args->{update} && $class eq 'default') { |
|
|
|
0
|
|
|
|
|
926
|
0
|
|
|
|
|
|
$args->{update} = 0; |
927
|
|
|
|
|
|
|
} |
928
|
0
|
0
|
|
|
|
|
if ($args->{update}) { |
929
|
0
|
0
|
|
|
|
|
return $self->err_line('class_not_found') if ! defined $clsid; |
930
|
0
|
|
|
|
|
|
$sto->update_class_name(dmid => $dmid, classid => $clsid, |
931
|
|
|
|
|
|
|
classname => $class); |
932
|
|
|
|
|
|
|
} else { |
933
|
0
|
|
|
|
|
|
$clsid = eval { $sto->create_class($dmid, $class); }; |
|
0
|
|
|
|
|
|
|
934
|
0
|
0
|
|
|
|
|
if ($@) { |
935
|
0
|
0
|
|
|
|
|
if (error_code($@) eq "dup") { |
936
|
0
|
|
|
|
|
|
return $self->err_line('class_exists'); |
937
|
|
|
|
|
|
|
} |
938
|
0
|
|
|
|
|
|
return $self->err_line('failure', "$@"); |
939
|
|
|
|
|
|
|
} |
940
|
|
|
|
|
|
|
} |
941
|
0
|
|
|
|
|
|
$sto->update_class_mindevcount(dmid => $dmid, classid => $clsid, |
942
|
|
|
|
|
|
|
mindevcount => $mindevcount); |
943
|
|
|
|
|
|
|
# don't erase an existing replpolicy if we're not setting a new one. |
944
|
0
|
0
|
|
|
|
|
$sto->update_class_replpolicy(dmid => $dmid, classid => $clsid, |
945
|
|
|
|
|
|
|
replpolicy => $replpolicy) if $replpolicy; |
946
|
0
|
0
|
|
|
|
|
if ($hashtype) { |
947
|
0
|
0
|
|
|
|
|
$sto->update_class_hashtype(dmid => $dmid, classid => $clsid, |
948
|
|
|
|
|
|
|
hashtype => $hashtype eq 'NONE' ? undef : $hashtype); |
949
|
|
|
|
|
|
|
} |
950
|
|
|
|
|
|
|
|
951
|
|
|
|
|
|
|
# return success |
952
|
0
|
|
|
|
|
|
return $self->cmd_clear_cache({ class => $class, mindevcount => $mindevcount, domain => $domain }); |
953
|
|
|
|
|
|
|
} |
954
|
|
|
|
|
|
|
|
955
|
|
|
|
|
|
|
sub cmd_update_class { |
956
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
957
|
0
|
|
|
|
|
|
my $args = shift; |
958
|
|
|
|
|
|
|
|
959
|
|
|
|
|
|
|
# simply passes through to create_class with update set |
960
|
0
|
|
|
|
|
|
$self->cmd_create_class({ %$args, update => 1 }); |
961
|
|
|
|
|
|
|
} |
962
|
|
|
|
|
|
|
|
963
|
|
|
|
|
|
|
sub cmd_delete_class { |
964
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
965
|
0
|
|
|
|
|
|
my $args = shift; |
966
|
|
|
|
|
|
|
|
967
|
0
|
|
|
|
|
|
my $domain = $args->{domain}; |
968
|
0
|
0
|
|
|
|
|
return $self->err_line('no_domain') unless length $domain; |
969
|
0
|
|
|
|
|
|
my $class = $args->{class}; |
970
|
0
|
0
|
|
|
|
|
return $self->err_line('no_class') unless length $domain; |
971
|
|
|
|
|
|
|
|
972
|
0
|
0
|
|
|
|
|
return $self->err_line('nodel_default_class') if $class eq 'default'; |
973
|
|
|
|
|
|
|
|
974
|
0
|
|
|
|
|
|
my $sto = Mgd::get_store(); |
975
|
0
|
0
|
|
|
|
|
my $dmid = $sto->get_domainid_by_name($domain) or |
976
|
|
|
|
|
|
|
return $self->err_line('domain_not_found'); |
977
|
0
|
|
|
|
|
|
my $clsid = $sto->get_classid_by_name($dmid, $class); |
978
|
0
|
0
|
|
|
|
|
return $self->err_line('class_not_found') unless defined $clsid; |
979
|
|
|
|
|
|
|
|
980
|
0
|
0
|
|
|
|
|
if (eval { Mgd::get_store()->delete_class($dmid, $clsid) }) { |
|
0
|
|
|
|
|
|
|
981
|
0
|
|
|
|
|
|
return $self->cmd_clear_cache({ domain => $domain, class => $class }); |
982
|
|
|
|
|
|
|
} |
983
|
|
|
|
|
|
|
|
984
|
0
|
|
|
|
|
|
my $errc = error_code($@); |
985
|
0
|
0
|
|
|
|
|
return $self->err_line('class_has_files') if $errc eq "has_files"; |
986
|
0
|
|
|
|
|
|
return $self->err_line('failure'); |
987
|
|
|
|
|
|
|
} |
988
|
|
|
|
|
|
|
|
989
|
|
|
|
|
|
|
sub cmd_create_host { |
990
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
991
|
0
|
|
|
|
|
|
my $args = shift; |
992
|
|
|
|
|
|
|
|
993
|
0
|
0
|
|
|
|
|
my $hostname = $args->{host} or |
994
|
|
|
|
|
|
|
return $self->err_line('no_host'); |
995
|
|
|
|
|
|
|
|
996
|
0
|
|
|
|
|
|
my $sto = Mgd::get_store(); |
997
|
0
|
|
|
|
|
|
my $hostid = $sto->get_hostid_by_name($hostname); |
998
|
|
|
|
|
|
|
|
999
|
|
|
|
|
|
|
# if we're creating a new host, require ip/port, and default to |
1000
|
|
|
|
|
|
|
# host being down if client didn't specify |
1001
|
0
|
0
|
|
|
|
|
if ($args->{update}) { |
1002
|
0
|
0
|
|
|
|
|
return $self->err_line('host_not_found') unless $hostid; |
1003
|
|
|
|
|
|
|
} else { |
1004
|
0
|
0
|
|
|
|
|
return $self->err_line('host_exists') if $hostid; |
1005
|
0
|
0
|
|
|
|
|
return $self->err_line('no_ip') unless $args->{ip}; |
1006
|
0
|
0
|
|
|
|
|
return $self->err_line('no_port') unless $args->{port}; |
1007
|
0
|
|
0
|
|
|
|
$args->{status} ||= 'down'; |
1008
|
|
|
|
|
|
|
} |
1009
|
|
|
|
|
|
|
|
1010
|
0
|
0
|
|
|
|
|
if ($args->{status}) { |
1011
|
0
|
0
|
|
|
|
|
return $self->err_line('unknown_state') |
1012
|
|
|
|
|
|
|
unless MogileFS::Host->valid_state($args->{status}); |
1013
|
|
|
|
|
|
|
} |
1014
|
|
|
|
|
|
|
|
1015
|
|
|
|
|
|
|
# arguments all good, let's do it. |
1016
|
|
|
|
|
|
|
|
1017
|
0
|
|
0
|
|
|
|
$hostid ||= $sto->create_host($hostname, $args->{ip}); |
1018
|
|
|
|
|
|
|
|
1019
|
|
|
|
|
|
|
# Protocol mismatch data fixup. |
1020
|
0
|
0
|
|
|
|
|
$args->{hostip} = delete $args->{ip} if exists $args->{ip}; |
1021
|
0
|
0
|
|
|
|
|
$args->{http_port} = delete $args->{port} if exists $args->{port}; |
1022
|
0
|
0
|
|
|
|
|
$args->{http_get_port} = delete $args->{getport} if exists $args->{getport}; |
1023
|
0
|
|
|
|
|
|
my @toupdate = grep { exists $args->{$_} } qw(status hostip http_port |
|
0
|
|
|
|
|
|
|
1024
|
|
|
|
|
|
|
http_get_port altip altmask); |
1025
|
0
|
|
|
|
|
|
$sto->update_host($hostid, { map { $_ => $args->{$_} } @toupdate }); |
|
0
|
|
|
|
|
|
|
1026
|
|
|
|
|
|
|
|
1027
|
|
|
|
|
|
|
# return success |
1028
|
0
|
|
|
|
|
|
return $self->cmd_clear_cache({ hostid => $hostid, hostname => $hostname }); |
1029
|
|
|
|
|
|
|
} |
1030
|
|
|
|
|
|
|
|
1031
|
|
|
|
|
|
|
sub cmd_update_host { |
1032
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
1033
|
0
|
|
|
|
|
|
my $args = shift; |
1034
|
|
|
|
|
|
|
|
1035
|
|
|
|
|
|
|
# simply passes through to create_host with update set |
1036
|
0
|
|
|
|
|
|
$self->cmd_create_host({ %$args, update => 1 }); |
1037
|
|
|
|
|
|
|
} |
1038
|
|
|
|
|
|
|
|
1039
|
|
|
|
|
|
|
sub cmd_delete_host { |
1040
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
1041
|
0
|
|
|
|
|
|
my $args = shift; |
1042
|
|
|
|
|
|
|
|
1043
|
0
|
|
|
|
|
|
my $sto = Mgd::get_store(); |
1044
|
0
|
0
|
|
|
|
|
my $hostid = $sto->get_hostid_by_name($args->{host}) |
1045
|
|
|
|
|
|
|
or return $self->err_line('unknown_host'); |
1046
|
|
|
|
|
|
|
|
1047
|
|
|
|
|
|
|
# TODO: $sto->delete_host should have a "has_devices" test internally |
1048
|
0
|
|
|
|
|
|
for my $dev ($sto->get_all_devices) { |
1049
|
0
|
0
|
|
|
|
|
return $self->err_line('host_not_empty') |
1050
|
|
|
|
|
|
|
if $dev->{hostid} == $hostid; |
1051
|
|
|
|
|
|
|
} |
1052
|
|
|
|
|
|
|
|
1053
|
0
|
|
|
|
|
|
$sto->delete_host($hostid); |
1054
|
|
|
|
|
|
|
|
1055
|
0
|
|
|
|
|
|
return $self->cmd_clear_cache; |
1056
|
|
|
|
|
|
|
} |
1057
|
|
|
|
|
|
|
|
1058
|
|
|
|
|
|
|
sub cmd_get_domains { |
1059
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
1060
|
0
|
|
|
|
|
|
my $args = shift; |
1061
|
|
|
|
|
|
|
|
1062
|
0
|
|
|
|
|
|
my $ret = {}; |
1063
|
0
|
|
|
|
|
|
my $dm_n = 0; |
1064
|
0
|
|
|
|
|
|
for my $dom (Mgd::domain_factory()->get_all) { |
1065
|
0
|
|
|
|
|
|
$dm_n++; |
1066
|
0
|
|
|
|
|
|
$ret->{"domain${dm_n}"} = $dom->name; |
1067
|
0
|
|
|
|
|
|
my $cl_n = 0; |
1068
|
0
|
|
|
|
|
|
foreach my $cl ($dom->classes) { |
1069
|
0
|
|
|
|
|
|
$cl_n++; |
1070
|
0
|
|
|
|
|
|
$ret->{"domain${dm_n}class${cl_n}name"} = $cl->name; |
1071
|
0
|
|
|
|
|
|
$ret->{"domain${dm_n}class${cl_n}mindevcount"} = $cl->mindevcount; |
1072
|
0
|
|
|
|
|
|
$ret->{"domain${dm_n}class${cl_n}replpolicy"} = |
1073
|
|
|
|
|
|
|
$cl->repl_policy_string; |
1074
|
0
|
|
|
|
|
|
$ret->{"domain${dm_n}class${cl_n}hashtype"} = $cl->hashtype_string; |
1075
|
|
|
|
|
|
|
} |
1076
|
0
|
|
|
|
|
|
$ret->{"domain${dm_n}classes"} = $cl_n; |
1077
|
|
|
|
|
|
|
} |
1078
|
0
|
|
|
|
|
|
$ret->{"domains"} = $dm_n; |
1079
|
|
|
|
|
|
|
|
1080
|
0
|
|
|
|
|
|
return $self->ok_line($ret); |
1081
|
|
|
|
|
|
|
} |
1082
|
|
|
|
|
|
|
|
1083
|
|
|
|
|
|
|
sub cmd_get_paths { |
1084
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
1085
|
0
|
|
|
|
|
|
my $args = shift; |
1086
|
|
|
|
|
|
|
|
1087
|
|
|
|
|
|
|
# memcache mappings are as follows: |
1088
|
|
|
|
|
|
|
# mogfid:: -> fidid |
1089
|
|
|
|
|
|
|
# mogdevids: -> \@devids (and TODO: invalidate when deletion is run!) |
1090
|
|
|
|
|
|
|
|
1091
|
|
|
|
|
|
|
# if you specify 'noverify', that means a correct answer isn't needed and memcache can |
1092
|
|
|
|
|
|
|
# be used. |
1093
|
0
|
|
|
|
|
|
my $memc = MogileFS::Config->memcache_client; |
1094
|
0
|
|
0
|
|
|
|
my $get_from_memc = $memc && $args->{noverify}; |
1095
|
0
|
|
0
|
|
|
|
my $memcache_ttl = MogileFS::Config->server_setting_cached("memcache_ttl") || 3600; |
1096
|
|
|
|
|
|
|
|
1097
|
|
|
|
|
|
|
# validate domain for plugins |
1098
|
0
|
0
|
|
|
|
|
$args->{dmid} = $self->check_domain($args) or return; |
1099
|
|
|
|
|
|
|
|
1100
|
|
|
|
|
|
|
# now invoke the plugin, abort if it tells us to |
1101
|
0
|
|
|
|
|
|
my $rv = MogileFS::run_global_hook('cmd_get_paths', $args); |
1102
|
0
|
0
|
0
|
|
|
|
return $self->err_line('plugin_aborted') |
1103
|
|
|
|
|
|
|
if defined $rv && ! $rv; |
1104
|
|
|
|
|
|
|
|
1105
|
|
|
|
|
|
|
# validate parameters |
1106
|
0
|
|
|
|
|
|
my $dmid = $args->{dmid}; |
1107
|
0
|
|
|
|
|
|
my $key = $args->{key}; |
1108
|
|
|
|
|
|
|
|
1109
|
0
|
0
|
|
|
|
|
valid_key($key) or return $self->err_line("no_key"); |
1110
|
|
|
|
|
|
|
|
1111
|
|
|
|
|
|
|
# We default to returning two possible paths. |
1112
|
|
|
|
|
|
|
# but the client may ask for more if they want. |
1113
|
0
|
|
0
|
|
|
|
my $pathcount = $args->{pathcount} || 2; |
1114
|
0
|
0
|
|
|
|
|
$pathcount = 2 if $pathcount < 2; |
1115
|
|
|
|
|
|
|
|
1116
|
|
|
|
|
|
|
# get DB handle |
1117
|
0
|
|
|
|
|
|
my $fid; |
1118
|
0
|
|
|
|
|
|
my $need_fid_in_memcache = 0; |
1119
|
0
|
|
|
|
|
|
my $mogfid_memkey = "mogfid:$args->{dmid}:$key"; |
1120
|
0
|
0
|
|
|
|
|
if ($get_from_memc) { |
1121
|
0
|
0
|
|
|
|
|
if (my $fidid = $memc->get($mogfid_memkey)) { |
1122
|
0
|
|
|
|
|
|
$fid = MogileFS::FID->new($fidid); |
1123
|
|
|
|
|
|
|
} else { |
1124
|
0
|
|
|
|
|
|
$need_fid_in_memcache = 1; |
1125
|
|
|
|
|
|
|
} |
1126
|
|
|
|
|
|
|
} |
1127
|
0
|
0
|
|
|
|
|
unless ($fid) { |
1128
|
|
|
|
|
|
|
Mgd::get_store()->slaves_ok(sub { |
1129
|
0
|
|
|
0
|
|
|
$fid = MogileFS::FID->new_from_dmid_and_key($dmid, $key); |
1130
|
0
|
|
|
|
|
|
}); |
1131
|
0
|
0
|
|
|
|
|
$fid or return $self->err_line("unknown_key"); |
1132
|
|
|
|
|
|
|
} |
1133
|
|
|
|
|
|
|
|
1134
|
|
|
|
|
|
|
# add to memcache, if needed. for an hour. |
1135
|
0
|
0
|
0
|
|
|
|
$memc->set($mogfid_memkey, $fid->id, $memcache_ttl ) if $need_fid_in_memcache || ($memc && !$get_from_memc); |
|
|
|
0
|
|
|
|
|
1136
|
|
|
|
|
|
|
|
1137
|
0
|
|
|
|
|
|
my $dmap = Mgd::device_factory()->map_by_id; |
1138
|
|
|
|
|
|
|
|
1139
|
0
|
|
|
|
|
|
my $ret = { |
1140
|
|
|
|
|
|
|
paths => 0, |
1141
|
|
|
|
|
|
|
}; |
1142
|
|
|
|
|
|
|
|
1143
|
|
|
|
|
|
|
# find devids that FID is on in memcache or db. |
1144
|
0
|
|
|
|
|
|
my @fid_devids; |
1145
|
0
|
|
|
|
|
|
my $need_devids_in_memcache = 0; |
1146
|
0
|
|
|
|
|
|
my $devid_memkey = "mogdevids:" . $fid->id; |
1147
|
0
|
0
|
|
|
|
|
if ($get_from_memc) { |
1148
|
0
|
0
|
|
|
|
|
if (my $list = $memc->get($devid_memkey)) { |
1149
|
0
|
|
|
|
|
|
@fid_devids = @$list; |
1150
|
|
|
|
|
|
|
} else { |
1151
|
0
|
|
|
|
|
|
$need_devids_in_memcache = 1; |
1152
|
|
|
|
|
|
|
} |
1153
|
|
|
|
|
|
|
} |
1154
|
0
|
0
|
|
|
|
|
unless (@fid_devids) { |
1155
|
|
|
|
|
|
|
Mgd::get_store()->slaves_ok(sub { |
1156
|
0
|
|
|
0
|
|
|
@fid_devids = $fid->devids; |
1157
|
0
|
|
|
|
|
|
}); |
1158
|
0
|
0
|
0
|
|
|
|
$memc->set($devid_memkey, \@fid_devids, $memcache_ttl ) if $need_devids_in_memcache || ($memc && !$get_from_memc); |
|
|
|
0
|
|
|
|
|
1159
|
|
|
|
|
|
|
} |
1160
|
|
|
|
|
|
|
|
1161
|
0
|
|
|
|
|
|
my @devices = map { $dmap->{$_} } @fid_devids; |
|
0
|
|
|
|
|
|
|
1162
|
|
|
|
|
|
|
|
1163
|
0
|
|
|
|
|
|
my @sorted_devs; |
1164
|
0
|
0
|
|
|
|
|
unless (MogileFS::run_global_hook('cmd_get_paths_order_devices', \@devices, \@sorted_devs)) { |
1165
|
0
|
|
|
|
|
|
@sorted_devs = sort_devs_by_utilization(@devices); |
1166
|
|
|
|
|
|
|
} |
1167
|
|
|
|
|
|
|
|
1168
|
|
|
|
|
|
|
# keep one partially-bogus path around just in case we have nothing else to send. |
1169
|
0
|
|
|
|
|
|
my $backup_path; |
1170
|
|
|
|
|
|
|
|
1171
|
|
|
|
|
|
|
# files on devices set for drain may disappear soon. |
1172
|
|
|
|
|
|
|
my @drain_paths; |
1173
|
|
|
|
|
|
|
|
1174
|
|
|
|
|
|
|
# construct result paths |
1175
|
0
|
|
|
|
|
|
foreach my $dev (@sorted_devs) { |
1176
|
0
|
0
|
0
|
|
|
|
next unless $dev && $dev->host; |
1177
|
|
|
|
|
|
|
|
1178
|
0
|
|
|
|
|
|
my $dfid = MogileFS::DevFID->new($dev, $fid); |
1179
|
0
|
|
|
|
|
|
my $path = $dfid->get_url; |
1180
|
0
|
|
|
|
|
|
my $currently_up = $dev->should_read_from; |
1181
|
|
|
|
|
|
|
|
1182
|
0
|
0
|
|
|
|
|
if (! $currently_up) { |
1183
|
0
|
|
|
|
|
|
$backup_path = $path; |
1184
|
0
|
|
|
|
|
|
next; |
1185
|
|
|
|
|
|
|
} |
1186
|
|
|
|
|
|
|
|
1187
|
|
|
|
|
|
|
# only verify size one first one, and never verify if they've asked not to |
1188
|
|
|
|
|
|
|
next unless |
1189
|
0
|
0
|
0
|
|
|
|
$ret->{paths} || |
|
|
|
0
|
|
|
|
|
1190
|
|
|
|
|
|
|
$args->{noverify} || |
1191
|
|
|
|
|
|
|
$dfid->size_matches; |
1192
|
|
|
|
|
|
|
|
1193
|
0
|
0
|
|
|
|
|
if ($dev->dstate->should_drain) { |
1194
|
0
|
|
|
|
|
|
push @drain_paths, $path; |
1195
|
0
|
|
|
|
|
|
next; |
1196
|
|
|
|
|
|
|
} |
1197
|
|
|
|
|
|
|
|
1198
|
0
|
|
|
|
|
|
my $n = ++$ret->{paths}; |
1199
|
0
|
|
|
|
|
|
$ret->{"path$n"} = $path; |
1200
|
0
|
0
|
|
|
|
|
last if $n == $pathcount; # one verified, one likely seems enough for now. time will tell. |
1201
|
|
|
|
|
|
|
} |
1202
|
|
|
|
|
|
|
|
1203
|
|
|
|
|
|
|
# deprioritize devices set for drain, they could disappear soon... |
1204
|
|
|
|
|
|
|
# Clients /should/ try to use lower-numbered paths first to avoid this. |
1205
|
0
|
0
|
0
|
|
|
|
if ($ret->{paths} < $pathcount && @drain_paths) { |
1206
|
0
|
|
|
|
|
|
foreach my $path (@drain_paths) { |
1207
|
0
|
|
|
|
|
|
my $n = ++$ret->{paths}; |
1208
|
0
|
|
|
|
|
|
$ret->{"path$n"} = $path; |
1209
|
0
|
0
|
|
|
|
|
last if $n == $pathcount; |
1210
|
|
|
|
|
|
|
} |
1211
|
|
|
|
|
|
|
} |
1212
|
|
|
|
|
|
|
|
1213
|
|
|
|
|
|
|
# use our backup path if all else fails |
1214
|
0
|
0
|
0
|
|
|
|
if ($backup_path && ! $ret->{paths}) { |
1215
|
0
|
|
|
|
|
|
$ret->{paths} = 1; |
1216
|
0
|
|
|
|
|
|
$ret->{path1} = $backup_path; |
1217
|
|
|
|
|
|
|
} |
1218
|
|
|
|
|
|
|
|
1219
|
0
|
|
|
|
|
|
return $self->ok_line($ret); |
1220
|
|
|
|
|
|
|
} |
1221
|
|
|
|
|
|
|
|
1222
|
|
|
|
|
|
|
sub sort_devs_by_utilization { |
1223
|
0
|
|
|
0
|
0
|
|
my @devices_with_weights; |
1224
|
|
|
|
|
|
|
|
1225
|
|
|
|
|
|
|
# is this fid still owned by this key? |
1226
|
0
|
|
|
|
|
|
foreach my $dev (@_) { |
1227
|
0
|
|
|
|
|
|
my $weight; |
1228
|
0
|
|
|
|
|
|
my $util = $dev->observed_utilization; |
1229
|
|
|
|
|
|
|
|
1230
|
0
|
0
|
0
|
|
|
|
if (defined($util) and $util =~ /\A\d+\Z/) { |
1231
|
0
|
|
|
|
|
|
$weight = 102 - $util; |
1232
|
0
|
|
0
|
|
|
|
$weight ||= 100; |
1233
|
|
|
|
|
|
|
} else { |
1234
|
0
|
|
|
|
|
|
$weight = $dev->weight; |
1235
|
0
|
|
0
|
|
|
|
$weight ||= 100; |
1236
|
|
|
|
|
|
|
} |
1237
|
0
|
|
|
|
|
|
push @devices_with_weights, [$dev, $weight]; |
1238
|
|
|
|
|
|
|
} |
1239
|
|
|
|
|
|
|
|
1240
|
|
|
|
|
|
|
# randomly weight the devices |
1241
|
0
|
|
|
|
|
|
my @list = MogileFS::Util::weighted_list(@devices_with_weights); |
1242
|
|
|
|
|
|
|
|
1243
|
0
|
|
|
|
|
|
return @list; |
1244
|
|
|
|
|
|
|
} |
1245
|
|
|
|
|
|
|
|
1246
|
|
|
|
|
|
|
# ------------------------------------------------------------ |
1247
|
|
|
|
|
|
|
# |
1248
|
|
|
|
|
|
|
# NOTE: cmd_edit_file is EXPERIMENTAL. Please see the documentation |
1249
|
|
|
|
|
|
|
# for edit_file in L. |
1250
|
|
|
|
|
|
|
# It is not recommended to use cmd_edit_file on production systems. |
1251
|
|
|
|
|
|
|
# |
1252
|
|
|
|
|
|
|
# cmd_edit_file is similar to cmd_get_paths, except we: |
1253
|
|
|
|
|
|
|
# - take the device of the first path we would have returned |
1254
|
|
|
|
|
|
|
# - get a tempfile with a new fid (pointing to nothing) on the same device |
1255
|
|
|
|
|
|
|
# the tempfile has the same key, so will replace the old contents on |
1256
|
|
|
|
|
|
|
# create_close |
1257
|
|
|
|
|
|
|
# - detach the old fid from that device (leaving the file in place) |
1258
|
|
|
|
|
|
|
# - attach the new fid to that device |
1259
|
|
|
|
|
|
|
# - returns only the first path to the old fid and a path to new fid |
1260
|
|
|
|
|
|
|
# (the client then DAV-renames the old path to the new path) |
1261
|
|
|
|
|
|
|
# |
1262
|
|
|
|
|
|
|
# TODO - what to do about situations where we would be reducing the |
1263
|
|
|
|
|
|
|
# replica count to zero? |
1264
|
|
|
|
|
|
|
# TODO - what to do about pending replications where we remove the source? |
1265
|
|
|
|
|
|
|
# TODO - the current implementation of cmd_edit_file is based on a copy |
1266
|
|
|
|
|
|
|
# of cmd_get_paths. Once proven mature, consider factoring out common |
1267
|
|
|
|
|
|
|
# code from the two functions. |
1268
|
|
|
|
|
|
|
# ------------------------------------------------------------ |
1269
|
|
|
|
|
|
|
sub cmd_edit_file { |
1270
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
1271
|
0
|
|
|
|
|
|
my $args = shift; |
1272
|
|
|
|
|
|
|
|
1273
|
0
|
|
|
|
|
|
my $memc = MogileFS::Config->memcache_client; |
1274
|
|
|
|
|
|
|
|
1275
|
|
|
|
|
|
|
# validate domain for plugins |
1276
|
0
|
0
|
|
|
|
|
$args->{dmid} = $self->check_domain($args) or return; |
1277
|
|
|
|
|
|
|
|
1278
|
|
|
|
|
|
|
# now invoke the plugin, abort if it tells us to |
1279
|
0
|
|
|
|
|
|
my $rv = MogileFS::run_global_hook('cmd_get_paths', $args); |
1280
|
0
|
0
|
0
|
|
|
|
return $self->err_line('plugin_aborted') |
1281
|
|
|
|
|
|
|
if defined $rv && ! $rv; |
1282
|
|
|
|
|
|
|
|
1283
|
|
|
|
|
|
|
# validate parameters |
1284
|
0
|
|
|
|
|
|
my $dmid = $args->{dmid}; |
1285
|
0
|
|
|
|
|
|
my $key = $args->{key}; |
1286
|
|
|
|
|
|
|
|
1287
|
0
|
0
|
|
|
|
|
valid_key($key) or return $self->err_line("no_key"); |
1288
|
|
|
|
|
|
|
|
1289
|
|
|
|
|
|
|
# get DB handle |
1290
|
0
|
|
|
|
|
|
my $fid; |
1291
|
0
|
|
|
|
|
|
my $need_fid_in_memcache = 0; |
1292
|
0
|
|
|
|
|
|
my $mogfid_memkey = "mogfid:$args->{dmid}:$key"; |
1293
|
0
|
0
|
|
|
|
|
if (my $fidid = $memc->get($mogfid_memkey)) { |
1294
|
0
|
|
|
|
|
|
$fid = MogileFS::FID->new($fidid); |
1295
|
|
|
|
|
|
|
} else { |
1296
|
0
|
|
|
|
|
|
$need_fid_in_memcache = 1; |
1297
|
|
|
|
|
|
|
} |
1298
|
0
|
0
|
|
|
|
|
unless ($fid) { |
1299
|
|
|
|
|
|
|
Mgd::get_store()->slaves_ok(sub { |
1300
|
0
|
|
|
0
|
|
|
$fid = MogileFS::FID->new_from_dmid_and_key($dmid, $key); |
1301
|
0
|
|
|
|
|
|
}); |
1302
|
0
|
0
|
|
|
|
|
$fid or return $self->err_line("unknown_key"); |
1303
|
|
|
|
|
|
|
} |
1304
|
|
|
|
|
|
|
|
1305
|
|
|
|
|
|
|
# add to memcache, if needed. for an hour. |
1306
|
0
|
0
|
|
|
|
|
$memc->add($mogfid_memkey, $fid->id, 3600) if $need_fid_in_memcache; |
1307
|
|
|
|
|
|
|
|
1308
|
0
|
|
|
|
|
|
my $dmap = Mgd::device_factory()->map_by_id; |
1309
|
|
|
|
|
|
|
|
1310
|
0
|
|
|
|
|
|
my @devices_with_weights; |
1311
|
|
|
|
|
|
|
|
1312
|
|
|
|
|
|
|
# find devids that FID is on in memcache or db. |
1313
|
|
|
|
|
|
|
my @fid_devids; |
1314
|
0
|
|
|
|
|
|
my $need_devids_in_memcache = 0; |
1315
|
0
|
|
|
|
|
|
my $devid_memkey = "mogdevids:" . $fid->id; |
1316
|
0
|
0
|
|
|
|
|
if (my $list = $memc->get($devid_memkey)) { |
1317
|
0
|
|
|
|
|
|
@fid_devids = @$list; |
1318
|
|
|
|
|
|
|
} else { |
1319
|
0
|
|
|
|
|
|
$need_devids_in_memcache = 1; |
1320
|
|
|
|
|
|
|
} |
1321
|
0
|
0
|
|
|
|
|
unless (@fid_devids) { |
1322
|
|
|
|
|
|
|
Mgd::get_store()->slaves_ok(sub { |
1323
|
0
|
|
|
0
|
|
|
@fid_devids = $fid->devids; |
1324
|
0
|
|
|
|
|
|
}); |
1325
|
0
|
0
|
|
|
|
|
$memc->add($devid_memkey, \@fid_devids, 3600) if $need_devids_in_memcache; |
1326
|
|
|
|
|
|
|
} |
1327
|
|
|
|
|
|
|
|
1328
|
|
|
|
|
|
|
# is this fid still owned by this key? |
1329
|
0
|
|
|
|
|
|
foreach my $devid (@fid_devids) { |
1330
|
0
|
|
|
|
|
|
my $weight; |
1331
|
0
|
|
|
|
|
|
my $dev = $dmap->{$devid}; |
1332
|
0
|
|
|
|
|
|
my $util = $dev->observed_utilization; |
1333
|
|
|
|
|
|
|
|
1334
|
0
|
0
|
0
|
|
|
|
if (defined($util) and $util =~ /\A\d+\Z/) { |
1335
|
0
|
|
|
|
|
|
$weight = 102 - $util; |
1336
|
0
|
|
0
|
|
|
|
$weight ||= 100; |
1337
|
|
|
|
|
|
|
} else { |
1338
|
0
|
|
|
|
|
|
$weight = $dev->weight; |
1339
|
0
|
|
0
|
|
|
|
$weight ||= 100; |
1340
|
|
|
|
|
|
|
} |
1341
|
0
|
|
|
|
|
|
push @devices_with_weights, [$devid, $weight]; |
1342
|
|
|
|
|
|
|
} |
1343
|
|
|
|
|
|
|
|
1344
|
|
|
|
|
|
|
# randomly weight the devices |
1345
|
|
|
|
|
|
|
# TODO - should we reverse the order, to leave the best |
1346
|
|
|
|
|
|
|
# one there for get_paths? |
1347
|
0
|
|
|
|
|
|
my @list = MogileFS::Util::weighted_list(@devices_with_weights); |
1348
|
|
|
|
|
|
|
|
1349
|
|
|
|
|
|
|
# Filter out bad devs |
1350
|
0
|
|
|
|
|
|
@list = grep { |
1351
|
0
|
|
|
|
|
|
my $devid = $_; |
1352
|
0
|
|
|
|
|
|
my $dev = $dmap->{$devid}; |
1353
|
|
|
|
|
|
|
|
1354
|
0
|
0
|
|
|
|
|
$dev && $dev->should_read_from; |
1355
|
|
|
|
|
|
|
} @list; |
1356
|
|
|
|
|
|
|
|
1357
|
|
|
|
|
|
|
# Take first remaining device from list |
1358
|
0
|
|
|
|
|
|
my $devid = $list[0]; |
1359
|
|
|
|
|
|
|
|
1360
|
0
|
|
|
|
|
|
my $classid = $fid->classid; |
1361
|
0
|
|
|
|
|
|
my $newfid = eval { |
1362
|
0
|
|
|
|
|
|
Mgd::get_store()->register_tempfile( |
1363
|
|
|
|
|
|
|
fid => undef, # undef => let the store pick a fid |
1364
|
|
|
|
|
|
|
dmid => $dmid, |
1365
|
|
|
|
|
|
|
key => $key, # This tempfile will ultimately become this key |
1366
|
|
|
|
|
|
|
classid => $classid, |
1367
|
|
|
|
|
|
|
devids => $devid, |
1368
|
|
|
|
|
|
|
); |
1369
|
|
|
|
|
|
|
}; |
1370
|
0
|
0
|
|
|
|
|
unless ($newfid) { |
1371
|
0
|
|
|
|
|
|
my $errc = error_code($@); |
1372
|
0
|
0
|
|
|
|
|
return $self->err_line("fid_in_use") if $errc eq "dup"; |
1373
|
0
|
|
|
|
|
|
warn "Error registering tempfile: $@\n"; |
1374
|
0
|
|
|
|
|
|
return $self->err_line("db"); |
1375
|
|
|
|
|
|
|
} |
1376
|
0
|
0
|
|
|
|
|
unless (Mgd::get_store()->remove_fidid_from_devid($fid->id, $devid)) { |
1377
|
0
|
|
|
|
|
|
warn "Error removing fidid from devid"; |
1378
|
0
|
|
|
|
|
|
return $self->err_line("db"); |
1379
|
|
|
|
|
|
|
} |
1380
|
0
|
0
|
|
|
|
|
unless (Mgd::get_store()->add_fidid_to_devid($newfid, $devid)) { |
1381
|
0
|
|
|
|
|
|
warn "Error removing fidid from devid"; |
1382
|
0
|
|
|
|
|
|
return $self->err_line("db"); |
1383
|
|
|
|
|
|
|
} |
1384
|
|
|
|
|
|
|
|
1385
|
0
|
|
|
|
|
|
my @paths = map { |
1386
|
0
|
|
|
|
|
|
my $dfid = MogileFS::DevFID->new($devid, $_); |
1387
|
0
|
|
|
|
|
|
my $path = $dfid->get_url; |
1388
|
|
|
|
|
|
|
} ($fid, $newfid); |
1389
|
0
|
|
|
|
|
|
my $ret; |
1390
|
0
|
|
|
|
|
|
$ret->{oldpath} = $paths[0]; |
1391
|
0
|
|
|
|
|
|
$ret->{newpath} = $paths[1]; |
1392
|
0
|
|
|
|
|
|
$ret->{fid} = $newfid; |
1393
|
0
|
|
|
|
|
|
$ret->{devid} = $devid; |
1394
|
0
|
|
|
|
|
|
$ret->{class} = $classid; |
1395
|
0
|
|
|
|
|
|
return $self->ok_line($ret); |
1396
|
|
|
|
|
|
|
} |
1397
|
|
|
|
|
|
|
|
1398
|
|
|
|
|
|
|
sub cmd_set_weight { |
1399
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
1400
|
0
|
|
|
|
|
|
my $args = shift; |
1401
|
|
|
|
|
|
|
|
1402
|
|
|
|
|
|
|
# figure out what they want to do |
1403
|
0
|
|
|
|
|
|
my ($hostname, $devid, $weight) = ($args->{host}, $args->{device}+0, $args->{weight}+0); |
1404
|
0
|
0
|
0
|
|
|
|
return $self->err_line('bad_params') |
|
|
|
0
|
|
|
|
|
1405
|
|
|
|
|
|
|
unless $hostname && $devid && $weight >= 0; |
1406
|
|
|
|
|
|
|
|
1407
|
0
|
|
|
|
|
|
my $dev = Mgd::device_factory()->get_by_id($devid); |
1408
|
0
|
0
|
|
|
|
|
return $self->err_line('no_device') unless $dev; |
1409
|
0
|
0
|
|
|
|
|
return $self->err_line('host_mismatch') |
1410
|
|
|
|
|
|
|
unless $dev->host->hostname eq $hostname; |
1411
|
|
|
|
|
|
|
|
1412
|
0
|
|
|
|
|
|
Mgd::get_store()->set_device_weight($dev->id, $weight); |
1413
|
|
|
|
|
|
|
|
1414
|
0
|
|
|
|
|
|
return $self->cmd_clear_cache; |
1415
|
|
|
|
|
|
|
} |
1416
|
|
|
|
|
|
|
|
1417
|
|
|
|
|
|
|
sub cmd_set_state { |
1418
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
1419
|
0
|
|
|
|
|
|
my $args = shift; |
1420
|
|
|
|
|
|
|
|
1421
|
|
|
|
|
|
|
# figure out what they want to do |
1422
|
0
|
|
|
|
|
|
my ($hostname, $devid, $state) = ($args->{host}, $args->{device}+0, $args->{state}); |
1423
|
|
|
|
|
|
|
|
1424
|
0
|
|
|
|
|
|
my $dstate = device_state($state); |
1425
|
0
|
0
|
0
|
|
|
|
return $self->err_line('bad_params') |
|
|
|
0
|
|
|
|
|
1426
|
|
|
|
|
|
|
unless $hostname && $devid && $dstate; |
1427
|
|
|
|
|
|
|
|
1428
|
0
|
|
|
|
|
|
my $dev = Mgd::device_factory()->get_by_id($devid); |
1429
|
0
|
0
|
|
|
|
|
return $self->err_line('no_device') unless $dev; |
1430
|
0
|
0
|
|
|
|
|
return $self->err_line('host_mismatch') |
1431
|
|
|
|
|
|
|
unless $dev->host->hostname eq $hostname; |
1432
|
|
|
|
|
|
|
|
1433
|
|
|
|
|
|
|
# make sure the destination state isn't too high |
1434
|
0
|
0
|
|
|
|
|
return $self->err_line('state_too_high') |
1435
|
|
|
|
|
|
|
unless $dev->can_change_to_state($state); |
1436
|
|
|
|
|
|
|
|
1437
|
0
|
|
|
|
|
|
Mgd::get_store()->set_device_state($dev->id, $state); |
1438
|
0
|
|
|
|
|
|
return $self->cmd_clear_cache; |
1439
|
|
|
|
|
|
|
} |
1440
|
|
|
|
|
|
|
|
1441
|
|
|
|
|
|
|
sub cmd_noop { |
1442
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
1443
|
0
|
|
|
|
|
|
my $args = shift; |
1444
|
0
|
|
|
|
|
|
return $self->ok_line; |
1445
|
|
|
|
|
|
|
} |
1446
|
|
|
|
|
|
|
|
1447
|
|
|
|
|
|
|
sub cmd_replicate_now { |
1448
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
1449
|
|
|
|
|
|
|
|
1450
|
0
|
|
|
|
|
|
my $rv = Mgd::get_store()->replicate_now; |
1451
|
0
|
|
|
|
|
|
return $self->ok_line({ count => int($rv) }); |
1452
|
|
|
|
|
|
|
} |
1453
|
|
|
|
|
|
|
|
1454
|
|
|
|
|
|
|
sub cmd_set_server_setting { |
1455
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
1456
|
0
|
|
|
|
|
|
my $args = shift; |
1457
|
0
|
0
|
|
|
|
|
my $key = $args->{key} or |
1458
|
|
|
|
|
|
|
return $self->err_line("bad_params"); |
1459
|
0
|
|
|
|
|
|
my $val = $args->{value}; |
1460
|
|
|
|
|
|
|
|
1461
|
0
|
0
|
|
|
|
|
my $chk = MogileFS::Config->server_setting_is_writable($key) or |
1462
|
|
|
|
|
|
|
return $self->err_line("not_writable"); |
1463
|
|
|
|
|
|
|
|
1464
|
0
|
|
|
|
|
|
my $cleanval = eval { $chk->($val); }; |
|
0
|
|
|
|
|
|
|
1465
|
0
|
0
|
|
|
|
|
return $self->err_line("invalid_format", $@) if $@; |
1466
|
|
|
|
|
|
|
|
1467
|
0
|
|
|
|
|
|
MogileFS::Config->set_server_setting($key, $cleanval); |
1468
|
|
|
|
|
|
|
|
1469
|
|
|
|
|
|
|
# GROSS HACK: slave settings are managed directly by MogileFS::Client, but |
1470
|
|
|
|
|
|
|
# I need to add a version key, so we check and inject that code here. |
1471
|
|
|
|
|
|
|
# FIXME: Move this when slave keys are managed by query worker commands! |
1472
|
0
|
0
|
|
|
|
|
if ($key =~ /^slave_/) { |
1473
|
0
|
|
|
|
|
|
Mgd::get_store()->incr_server_setting('slave_version', 1); |
1474
|
|
|
|
|
|
|
} |
1475
|
|
|
|
|
|
|
|
1476
|
0
|
|
|
|
|
|
return $self->ok_line; |
1477
|
|
|
|
|
|
|
} |
1478
|
|
|
|
|
|
|
|
1479
|
|
|
|
|
|
|
sub cmd_server_setting { |
1480
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
1481
|
0
|
|
|
|
|
|
my $args = shift; |
1482
|
0
|
|
|
|
|
|
my $key = $args->{key}; |
1483
|
0
|
0
|
|
|
|
|
return $self->err_line("bad_params") unless $key; |
1484
|
0
|
|
|
|
|
|
my $value = MogileFS::Config->server_setting($key); |
1485
|
0
|
|
|
|
|
|
return $self->ok_line({key => $key, value => $value}); |
1486
|
|
|
|
|
|
|
} |
1487
|
|
|
|
|
|
|
|
1488
|
|
|
|
|
|
|
sub cmd_server_settings { |
1489
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
1490
|
0
|
|
|
|
|
|
my $ss = Mgd::get_store()->server_settings; |
1491
|
0
|
|
|
|
|
|
my $ret = {}; |
1492
|
0
|
|
|
|
|
|
my $n = 0; |
1493
|
0
|
|
|
|
|
|
while (my ($k, $v) = each %$ss) { |
1494
|
0
|
0
|
|
|
|
|
next unless MogileFS::Config->server_setting_is_readable($k); |
1495
|
0
|
|
|
|
|
|
$ret->{"key_count"} = ++$n; |
1496
|
0
|
|
|
|
|
|
$ret->{"key_$n"} = $k; |
1497
|
0
|
|
|
|
|
|
$ret->{"value_$n"} = $v; |
1498
|
|
|
|
|
|
|
} |
1499
|
0
|
|
|
|
|
|
return $self->ok_line($ret); |
1500
|
|
|
|
|
|
|
} |
1501
|
|
|
|
|
|
|
|
1502
|
|
|
|
|
|
|
sub cmd_do_monitor_round { |
1503
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
1504
|
0
|
|
|
|
|
|
my $args = shift; |
1505
|
0
|
|
|
|
|
|
$self->forget_that_monitor_has_run; |
1506
|
0
|
|
|
|
|
|
$self->wait_for_monitor; |
1507
|
0
|
|
|
|
|
|
return $self->ok_line; |
1508
|
|
|
|
|
|
|
} |
1509
|
|
|
|
|
|
|
|
1510
|
|
|
|
|
|
|
sub cmd_fsck_start { |
1511
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
1512
|
0
|
|
|
|
|
|
my $sto = Mgd::get_store(); |
1513
|
|
|
|
|
|
|
|
1514
|
0
|
|
|
|
|
|
my $fsck_host = MogileFS::Config->server_setting("fsck_host"); |
1515
|
0
|
|
|
|
|
|
my $rebal_host = MogileFS::Config->server_setting("rebal_host"); |
1516
|
|
|
|
|
|
|
|
1517
|
0
|
0
|
|
|
|
|
return $self->err_line("fsck_running", "fsck is already running") if $fsck_host; |
1518
|
0
|
0
|
|
|
|
|
return $self->err_line("rebal_running", "rebalance running; cannot run fsck at same time") if $rebal_host; |
1519
|
|
|
|
|
|
|
|
1520
|
|
|
|
|
|
|
# reset position, if a previous fsck was already completed. |
1521
|
0
|
0
|
|
0
|
|
|
my $intss = sub { MogileFS::Config->server_setting($_[0]) || 0 }; |
|
0
|
|
|
|
|
|
|
1522
|
0
|
|
|
|
|
|
my $checked_fid = $intss->("fsck_highest_fid_checked"); |
1523
|
0
|
|
|
|
|
|
my $final_fid = $intss->("fsck_fid_at_end"); |
1524
|
0
|
0
|
0
|
|
|
|
if (($checked_fid && $final_fid && $checked_fid >= $final_fid) || |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
1525
|
|
|
|
|
|
|
(!$final_fid && !$checked_fid)) { |
1526
|
0
|
0
|
|
|
|
|
$self->_do_fsck_reset or return $self->err_line("db"); |
1527
|
|
|
|
|
|
|
} |
1528
|
|
|
|
|
|
|
|
1529
|
|
|
|
|
|
|
# set params for stats: |
1530
|
0
|
|
|
|
|
|
$sto->set_server_setting("fsck_start_time", $sto->get_db_unixtime); |
1531
|
0
|
|
|
|
|
|
$sto->set_server_setting("fsck_stop_time", undef); |
1532
|
0
|
|
|
|
|
|
$sto->set_server_setting("fsck_fids_checked", 0); |
1533
|
0
|
|
0
|
|
|
|
my $start_fid = |
1534
|
|
|
|
|
|
|
MogileFS::Config->server_setting('fsck_highest_fid_checked') || 0; |
1535
|
0
|
|
|
|
|
|
$sto->set_server_setting("fsck_start_fid", $start_fid); |
1536
|
|
|
|
|
|
|
|
1537
|
|
|
|
|
|
|
# and start it: |
1538
|
0
|
|
|
|
|
|
$sto->set_server_setting("fsck_host", MogileFS::Config->hostname); |
1539
|
0
|
|
|
|
|
|
MogileFS::ProcManager->wake_a("fsck"); |
1540
|
|
|
|
|
|
|
|
1541
|
0
|
|
|
|
|
|
return $self->ok_line; |
1542
|
|
|
|
|
|
|
} |
1543
|
|
|
|
|
|
|
|
1544
|
|
|
|
|
|
|
sub cmd_fsck_stop { |
1545
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
1546
|
0
|
|
|
|
|
|
my $sto = Mgd::get_store(); |
1547
|
0
|
|
|
|
|
|
$sto->set_server_setting("fsck_host", undef); |
1548
|
0
|
|
|
|
|
|
$sto->set_server_setting("fsck_stop_time", $sto->get_db_unixtime); |
1549
|
0
|
|
|
|
|
|
return $self->ok_line; |
1550
|
|
|
|
|
|
|
} |
1551
|
|
|
|
|
|
|
|
1552
|
|
|
|
|
|
|
sub cmd_fsck_reset { |
1553
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
1554
|
0
|
|
|
|
|
|
my $args = shift; |
1555
|
|
|
|
|
|
|
|
1556
|
0
|
|
|
|
|
|
my $sto = Mgd::get_store(); |
1557
|
0
|
0
|
|
|
|
|
$sto->set_server_setting("fsck_opt_policy_only", |
1558
|
|
|
|
|
|
|
($args->{policy_only} ? "1" : undef)); |
1559
|
0
|
0
|
|
|
|
|
$sto->set_server_setting("fsck_highest_fid_checked", |
1560
|
|
|
|
|
|
|
($args->{startpos} ? $args->{startpos} : "0")); |
1561
|
|
|
|
|
|
|
|
1562
|
0
|
0
|
|
|
|
|
$self->_do_fsck_reset or return $self->err_line("db"); |
1563
|
0
|
|
|
|
|
|
return $self->ok_line; |
1564
|
|
|
|
|
|
|
} |
1565
|
|
|
|
|
|
|
|
1566
|
|
|
|
|
|
|
sub _do_fsck_reset { |
1567
|
0
|
|
|
0
|
|
|
my MogileFS::Worker::Query $self = shift; |
1568
|
0
|
|
|
|
|
|
eval { |
1569
|
0
|
|
|
|
|
|
my $sto = Mgd::get_store(); |
1570
|
0
|
|
|
|
|
|
$sto->set_server_setting("fsck_start_time", undef); |
1571
|
0
|
|
|
|
|
|
$sto->set_server_setting("fsck_stop_time", undef); |
1572
|
0
|
|
|
|
|
|
$sto->set_server_setting("fsck_fids_checked", 0); |
1573
|
0
|
|
|
|
|
|
$sto->set_server_setting("fsck_fid_at_end", $sto->max_fidid); |
1574
|
|
|
|
|
|
|
|
1575
|
|
|
|
|
|
|
# clear existing event counts summaries. |
1576
|
0
|
|
|
|
|
|
my $ss = $sto->server_settings; |
1577
|
0
|
|
|
|
|
|
foreach my $k (keys %$ss) { |
1578
|
0
|
0
|
|
|
|
|
next unless $k =~ /^fsck_sum_evcount_/; |
1579
|
0
|
|
|
|
|
|
$sto->set_server_setting($k, undef); |
1580
|
|
|
|
|
|
|
} |
1581
|
0
|
|
|
|
|
|
my $logid = $sto->max_fsck_logid; |
1582
|
0
|
|
|
|
|
|
$sto->set_server_setting("fsck_start_maxlogid", $logid); |
1583
|
0
|
|
|
|
|
|
$sto->set_server_setting("fsck_logid_processed", $logid); |
1584
|
|
|
|
|
|
|
}; |
1585
|
0
|
0
|
|
|
|
|
if ($@) { |
1586
|
0
|
|
|
|
|
|
error("DB error in _do_fsck_reset: $@"); |
1587
|
0
|
|
|
|
|
|
return 0; |
1588
|
|
|
|
|
|
|
} |
1589
|
0
|
|
|
|
|
|
return 1; |
1590
|
|
|
|
|
|
|
} |
1591
|
|
|
|
|
|
|
|
1592
|
|
|
|
|
|
|
sub cmd_fsck_clearlog { |
1593
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
1594
|
0
|
|
|
|
|
|
my $sto = Mgd::get_store(); |
1595
|
0
|
|
|
|
|
|
$sto->clear_fsck_log; |
1596
|
0
|
|
|
|
|
|
return $self->ok_line; |
1597
|
|
|
|
|
|
|
} |
1598
|
|
|
|
|
|
|
|
1599
|
|
|
|
|
|
|
sub cmd_fsck_getlog { |
1600
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
1601
|
0
|
|
|
|
|
|
my $args = shift; |
1602
|
|
|
|
|
|
|
|
1603
|
0
|
|
|
|
|
|
my $sto = Mgd::get_store(); |
1604
|
0
|
|
|
|
|
|
my @rows = $sto->fsck_log_rows($args->{after_logid}, 100); |
1605
|
0
|
|
|
|
|
|
my $ret; |
1606
|
0
|
|
|
|
|
|
my $n = 0; |
1607
|
0
|
|
|
|
|
|
foreach my $row (@rows) { |
1608
|
0
|
|
|
|
|
|
$n++; |
1609
|
0
|
|
|
|
|
|
foreach my $k (keys %$row) { |
1610
|
0
|
0
|
|
|
|
|
$ret->{"row_${n}_$k"} = $row->{$k} if defined $row->{$k}; |
1611
|
|
|
|
|
|
|
} |
1612
|
|
|
|
|
|
|
} |
1613
|
0
|
|
|
|
|
|
$ret->{row_count} = $n; |
1614
|
0
|
|
|
|
|
|
return $self->ok_line($ret); |
1615
|
|
|
|
|
|
|
} |
1616
|
|
|
|
|
|
|
|
1617
|
|
|
|
|
|
|
sub cmd_fsck_status { |
1618
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
1619
|
|
|
|
|
|
|
|
1620
|
0
|
|
|
|
|
|
my $sto = Mgd::get_store(); |
1621
|
|
|
|
|
|
|
# Kick up the summary before we read the values |
1622
|
0
|
|
|
|
|
|
$sto->fsck_log_summarize; |
1623
|
0
|
|
|
|
|
|
my $fsck_host = MogileFS::Config->server_setting('fsck_host'); |
1624
|
0
|
0
|
|
0
|
|
|
my $intss = sub { MogileFS::Config->server_setting($_[0]) || 0 }; |
|
0
|
|
|
|
|
|
|
1625
|
0
|
0
|
|
|
|
|
my $ret = { |
1626
|
|
|
|
|
|
|
running => ($fsck_host ? 1 : 0), |
1627
|
|
|
|
|
|
|
host => $fsck_host, |
1628
|
|
|
|
|
|
|
max_fid_checked => $intss->('fsck_highest_fid_checked'), |
1629
|
|
|
|
|
|
|
policy_only => $intss->('fsck_opt_policy_only'), |
1630
|
|
|
|
|
|
|
end_fid => $intss->('fsck_fid_at_end'), |
1631
|
|
|
|
|
|
|
start_time => $intss->('fsck_start_time'), |
1632
|
|
|
|
|
|
|
stop_time => $intss->('fsck_stop_time'), |
1633
|
|
|
|
|
|
|
current_time => $sto->get_db_unixtime, |
1634
|
|
|
|
|
|
|
max_logid => $sto->max_fsck_logid, |
1635
|
|
|
|
|
|
|
}; |
1636
|
|
|
|
|
|
|
|
1637
|
|
|
|
|
|
|
# throw some stats in. |
1638
|
0
|
|
|
|
|
|
my $ss = $sto->server_settings; |
1639
|
0
|
|
|
|
|
|
foreach my $k (keys %$ss) { |
1640
|
0
|
0
|
|
|
|
|
next unless $k =~ /^fsck_sum_evcount_(.+)/; |
1641
|
0
|
|
|
|
|
|
$ret->{"num_$1"} += $ss->{$k}; |
1642
|
|
|
|
|
|
|
} |
1643
|
|
|
|
|
|
|
|
1644
|
0
|
|
|
|
|
|
return $self->ok_line($ret); |
1645
|
|
|
|
|
|
|
} |
1646
|
|
|
|
|
|
|
|
1647
|
|
|
|
|
|
|
sub cmd_rebalance_status { |
1648
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
1649
|
|
|
|
|
|
|
|
1650
|
0
|
|
|
|
|
|
my $sto = Mgd::get_store(); |
1651
|
|
|
|
|
|
|
|
1652
|
0
|
|
|
|
|
|
my $rebal_state = MogileFS::Config->server_setting('rebal_state'); |
1653
|
0
|
0
|
|
|
|
|
return $self->err_line('no_rebal_state') unless $rebal_state; |
1654
|
0
|
|
|
|
|
|
return $self->ok_line({ state => $rebal_state }); |
1655
|
|
|
|
|
|
|
} |
1656
|
|
|
|
|
|
|
|
1657
|
|
|
|
|
|
|
sub cmd_rebalance_start { |
1658
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
1659
|
|
|
|
|
|
|
|
1660
|
0
|
|
|
|
|
|
my $rebal_host = MogileFS::Config->server_setting("rebal_host"); |
1661
|
0
|
|
|
|
|
|
my $fsck_host = MogileFS::Config->server_setting("fsck_host"); |
1662
|
|
|
|
|
|
|
|
1663
|
0
|
0
|
|
|
|
|
return $self->err_line("rebal_running", "rebalance is already running") if $rebal_host; |
1664
|
0
|
0
|
|
|
|
|
return $self->err_line("fsck_running", "fsck running; cannot run rebalance at same time") if $fsck_host; |
1665
|
|
|
|
|
|
|
|
1666
|
0
|
|
|
|
|
|
my $rebal_state = MogileFS::Config->server_setting('rebal_state'); |
1667
|
0
|
0
|
|
|
|
|
unless ($rebal_state) { |
1668
|
0
|
|
|
|
|
|
my $rebal_pol = MogileFS::Config->server_setting('rebal_policy'); |
1669
|
0
|
0
|
|
|
|
|
return $self->err_line('no_rebal_policy') unless $rebal_pol; |
1670
|
|
|
|
|
|
|
|
1671
|
0
|
|
|
|
|
|
my $rebal = MogileFS::Rebalance->new; |
1672
|
0
|
|
|
|
|
|
$rebal->policy($rebal_pol); |
1673
|
0
|
|
|
|
|
|
my @devs = Mgd::device_factory()->get_all; |
1674
|
0
|
|
|
|
|
|
$rebal->init(\@devs); |
1675
|
0
|
|
|
|
|
|
my $sdevs = $rebal->source_devices; |
1676
|
|
|
|
|
|
|
|
1677
|
0
|
|
|
|
|
|
$rebal_state = $rebal->save_state; |
1678
|
0
|
|
|
|
|
|
MogileFS::Config->set_server_setting('rebal_state', $rebal_state); |
1679
|
|
|
|
|
|
|
} |
1680
|
|
|
|
|
|
|
# TODO: register start time somewhere. |
1681
|
0
|
|
|
|
|
|
MogileFS::Config->set_server_setting('rebal_host', MogileFS::Config->hostname); |
1682
|
0
|
|
|
|
|
|
return $self->ok_line({ state => $rebal_state }); |
1683
|
|
|
|
|
|
|
} |
1684
|
|
|
|
|
|
|
|
1685
|
|
|
|
|
|
|
sub cmd_rebalance_test { |
1686
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
1687
|
0
|
|
|
|
|
|
my $rebal_pol = MogileFS::Config->server_setting('rebal_policy'); |
1688
|
0
|
|
|
|
|
|
my $rebal_state = MogileFS::Config->server_setting('rebal_state'); |
1689
|
0
|
0
|
|
|
|
|
return $self->err_line('no_rebal_policy') unless $rebal_pol; |
1690
|
|
|
|
|
|
|
|
1691
|
0
|
|
|
|
|
|
my $rebal = MogileFS::Rebalance->new; |
1692
|
0
|
|
|
|
|
|
my @devs = Mgd::device_factory()->get_all; |
1693
|
0
|
|
|
|
|
|
$rebal->policy($rebal_pol); |
1694
|
0
|
|
|
|
|
|
$rebal->init(\@devs); |
1695
|
|
|
|
|
|
|
|
1696
|
|
|
|
|
|
|
# client should display list of source, destination devices. |
1697
|
|
|
|
|
|
|
# FIXME: can probably avoid calling this twice by pulling state? |
1698
|
|
|
|
|
|
|
# *or* not running init. |
1699
|
0
|
|
|
|
|
|
my $sdevs = $rebal->filter_source_devices(\@devs); |
1700
|
0
|
|
|
|
|
|
my $ddevs = $rebal->filter_dest_devices(\@devs); |
1701
|
0
|
|
|
|
|
|
my $ret = {}; |
1702
|
0
|
|
|
|
|
|
$ret->{sdevs} = join(',', @$sdevs); |
1703
|
0
|
|
|
|
|
|
$ret->{ddevs} = join(',', @$ddevs); |
1704
|
|
|
|
|
|
|
|
1705
|
0
|
|
|
|
|
|
return $self->ok_line($ret); |
1706
|
|
|
|
|
|
|
} |
1707
|
|
|
|
|
|
|
|
1708
|
|
|
|
|
|
|
sub cmd_rebalance_reset { |
1709
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
1710
|
0
|
|
|
|
|
|
my $host = MogileFS::Config->server_setting('rebal_host'); |
1711
|
0
|
0
|
|
|
|
|
if ($host) { |
1712
|
0
|
0
|
|
|
|
|
return $self->err_line("rebal_running", "rebalance is running") if $host; |
1713
|
|
|
|
|
|
|
} |
1714
|
0
|
|
|
|
|
|
MogileFS::Config->set_server_setting('rebal_state', undef); |
1715
|
0
|
|
|
|
|
|
return $self->ok_line; |
1716
|
|
|
|
|
|
|
} |
1717
|
|
|
|
|
|
|
|
1718
|
|
|
|
|
|
|
sub cmd_rebalance_stop { |
1719
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
1720
|
0
|
|
|
|
|
|
my $host = MogileFS::Config->server_setting('rebal_host'); |
1721
|
0
|
0
|
|
|
|
|
unless ($host) { |
1722
|
0
|
|
|
|
|
|
return $self->err_line('rebal_not_started'); |
1723
|
|
|
|
|
|
|
} |
1724
|
0
|
|
|
|
|
|
MogileFS::Config->set_server_setting('rebal_signal', 'stop'); |
1725
|
0
|
|
|
|
|
|
return $self->ok_line; |
1726
|
|
|
|
|
|
|
} |
1727
|
|
|
|
|
|
|
|
1728
|
|
|
|
|
|
|
sub cmd_rebalance_set_policy { |
1729
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
1730
|
0
|
|
|
|
|
|
my $args = shift; |
1731
|
|
|
|
|
|
|
|
1732
|
0
|
|
|
|
|
|
my $rebal_host = MogileFS::Config->server_setting("rebal_host"); |
1733
|
0
|
0
|
|
|
|
|
return $self->err_line("no_set_rebal", "cannot change rebalance policy while rebalance is running") if $rebal_host; |
1734
|
|
|
|
|
|
|
|
1735
|
|
|
|
|
|
|
# load policy object, test policy, set policy. |
1736
|
0
|
|
|
|
|
|
my $rebal = MogileFS::Rebalance->new; |
1737
|
0
|
|
|
|
|
|
eval { |
1738
|
0
|
|
|
|
|
|
$rebal->policy($args->{policy}); |
1739
|
|
|
|
|
|
|
}; |
1740
|
0
|
0
|
|
|
|
|
if ($@) { |
1741
|
0
|
|
|
|
|
|
return $self->err_line("bad_rebal_pol", $@); |
1742
|
|
|
|
|
|
|
} |
1743
|
|
|
|
|
|
|
|
1744
|
0
|
|
|
|
|
|
MogileFS::Config->set_server_setting('rebal_policy', $args->{policy}); |
1745
|
0
|
|
|
|
|
|
MogileFS::Config->set_server_setting('rebal_state', undef); |
1746
|
0
|
|
|
|
|
|
return $self->ok_line; |
1747
|
|
|
|
|
|
|
} |
1748
|
|
|
|
|
|
|
|
1749
|
|
|
|
|
|
|
sub ok_line { |
1750
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
1751
|
|
|
|
|
|
|
|
1752
|
0
|
|
|
|
|
|
my $delay = ''; |
1753
|
0
|
0
|
|
|
|
|
if ($self->{querystarttime}) { |
1754
|
0
|
|
|
|
|
|
$delay = sprintf("%.4f ", Time::HiRes::tv_interval( $self->{querystarttime} )); |
1755
|
0
|
|
|
|
|
|
$self->{querystarttime} = undef; |
1756
|
|
|
|
|
|
|
} |
1757
|
|
|
|
|
|
|
|
1758
|
0
|
0
|
|
|
|
|
my $id = defined $self->{reqid} ? "$self->{reqid} " : ''; |
1759
|
|
|
|
|
|
|
|
1760
|
0
|
|
0
|
|
|
|
my $args = shift || {}; |
1761
|
0
|
0
|
|
|
|
|
$args->{callid} = $self->{callid} if defined $self->{callid}; |
1762
|
0
|
|
|
|
|
|
my $argline = join('&', map { eurl($_) . "=" . eurl($args->{$_}) } keys %$args); |
|
0
|
|
|
|
|
|
|
1763
|
0
|
|
|
|
|
|
$self->send_to_parent("${id}${delay}OK $argline"); |
1764
|
0
|
|
|
|
|
|
return 1; |
1765
|
|
|
|
|
|
|
} |
1766
|
|
|
|
|
|
|
|
1767
|
|
|
|
|
|
|
# first argument: error code. |
1768
|
|
|
|
|
|
|
# second argument: optional error text. text will be taken from code if no text provided. |
1769
|
|
|
|
|
|
|
sub err_line { |
1770
|
0
|
|
|
0
|
0
|
|
my MogileFS::Worker::Query $self = shift; |
1771
|
|
|
|
|
|
|
|
1772
|
0
|
|
|
|
|
|
my $err_code = shift; |
1773
|
0
|
|
0
|
|
|
|
my $err_text = shift || { |
1774
|
|
|
|
|
|
|
'dup' => "Duplicate name/number used.", |
1775
|
|
|
|
|
|
|
'after_mismatch' => "Pattern does not match the after-value?", |
1776
|
|
|
|
|
|
|
'bad_params' => "Invalid parameters to command; please see documentation", |
1777
|
|
|
|
|
|
|
'class_exists' => "That class already exists in that domain", |
1778
|
|
|
|
|
|
|
'class_has_files' => "Class still has files, unable to delete", |
1779
|
|
|
|
|
|
|
'class_not_found' => "Class not found", |
1780
|
|
|
|
|
|
|
'db' => "Database error", |
1781
|
|
|
|
|
|
|
'domain_has_files' => "Domain still has files, unable to delete", |
1782
|
|
|
|
|
|
|
'domain_exists' => "That domain already exists", |
1783
|
|
|
|
|
|
|
'domain_not_empty' => "Domain still has classes, unable to delete", |
1784
|
|
|
|
|
|
|
'domain_not_found' => "Domain not found", |
1785
|
|
|
|
|
|
|
'failure' => "Operation failed", |
1786
|
|
|
|
|
|
|
'host_exists' => "That host already exists", |
1787
|
|
|
|
|
|
|
'host_mismatch' => "The device specified doesn't belong to the host specified", |
1788
|
|
|
|
|
|
|
'host_not_empty' => "Unable to delete host; it contains devices still", |
1789
|
|
|
|
|
|
|
'host_not_found' => "Host not found", |
1790
|
|
|
|
|
|
|
'invalid_checker_level' => "Checker level invalid. Please see documentation on this command.", |
1791
|
|
|
|
|
|
|
'invalid_mindevcount' => "The mindevcount must be at least 1", |
1792
|
|
|
|
|
|
|
'key_exists' => "Target key name already exists; can't overwrite.", |
1793
|
|
|
|
|
|
|
'no_class' => "No class provided", |
1794
|
|
|
|
|
|
|
'no_devices' => "No devices found to store file", |
1795
|
|
|
|
|
|
|
'no_device' => "Device not found", |
1796
|
|
|
|
|
|
|
'no_domain' => "No domain provided", |
1797
|
|
|
|
|
|
|
'no_host' => "No host provided", |
1798
|
|
|
|
|
|
|
'no_ip' => "IP required to create host", |
1799
|
|
|
|
|
|
|
'no_port' => "Port required to create host", |
1800
|
|
|
|
|
|
|
'no_temp_file' => "No tempfile or file already closed", |
1801
|
|
|
|
|
|
|
'none_match' => "No keys match that pattern and after-value (if any).", |
1802
|
|
|
|
|
|
|
'plugin_aborted' => "Action aborted by plugin", |
1803
|
|
|
|
|
|
|
'state_too_high' => "Status cannot go from dead to alive; must use down", |
1804
|
|
|
|
|
|
|
'unknown_command' => "Unknown server command", |
1805
|
|
|
|
|
|
|
'unknown_host' => "Host not found", |
1806
|
|
|
|
|
|
|
'unknown_state' => "Invalid/unknown state", |
1807
|
|
|
|
|
|
|
'unreg_domain' => "Domain name invalid/not found", |
1808
|
|
|
|
|
|
|
'rebal_not_started' => "Rebalance not running", |
1809
|
|
|
|
|
|
|
'no_rebal_state' => "No available rebalance status", |
1810
|
|
|
|
|
|
|
'no_rebal_policy' => "No rebalance policy available", |
1811
|
|
|
|
|
|
|
'nodel_default_class' => "Cannot delete the default class", |
1812
|
|
|
|
|
|
|
}->{$err_code} || $err_code; |
1813
|
|
|
|
|
|
|
|
1814
|
0
|
|
|
|
|
|
my $delay = ''; |
1815
|
0
|
0
|
|
|
|
|
if ($self->{querystarttime}) { |
1816
|
0
|
|
|
|
|
|
$delay = sprintf("%.4f ", Time::HiRes::tv_interval($self->{querystarttime})); |
1817
|
0
|
|
|
|
|
|
$self->{querystarttime} = undef; |
1818
|
|
|
|
|
|
|
} else { |
1819
|
|
|
|
|
|
|
# don't send another ERR line if we already sent one |
1820
|
0
|
|
|
|
|
|
error("err_line called redundantly with $err_code ( " . eurl($err_text) . ")"); |
1821
|
0
|
|
|
|
|
|
return 0; |
1822
|
|
|
|
|
|
|
} |
1823
|
|
|
|
|
|
|
|
1824
|
0
|
0
|
|
|
|
|
my $id = defined $self->{reqid} ? "$self->{reqid} " : ''; |
1825
|
0
|
0
|
|
|
|
|
my $callid = defined $self->{callid} ? ' ' . eurl($self->{callid}) : ''; |
1826
|
|
|
|
|
|
|
|
1827
|
0
|
|
|
|
|
|
$self->send_to_parent("${id}${delay}ERR $err_code " . eurl($err_text) . $callid); |
1828
|
0
|
|
|
|
|
|
return 0; |
1829
|
|
|
|
|
|
|
} |
1830
|
|
|
|
|
|
|
|
1831
|
|
|
|
|
|
|
1; |
1832
|
|
|
|
|
|
|
|
1833
|
|
|
|
|
|
|
# Local Variables: |
1834
|
|
|
|
|
|
|
# mode: perl |
1835
|
|
|
|
|
|
|
# c-basic-indent: 4 |
1836
|
|
|
|
|
|
|
# indent-tabs-mode: nil |
1837
|
|
|
|
|
|
|
# End: |
1838
|
|
|
|
|
|
|
|
1839
|
|
|
|
|
|
|
__END__ |