]>
git.proxmox.com Git - librados2-perl.git/blob - PVE/RADOS.pm
15 use PVE
::RPCEnvironment
;
19 my $rados_default_timeout = 5;
20 my $ceph_default_conf = '/etc/pve/ceph.conf';
21 my $ceph_default_user = 'admin';
27 XSLoader
::load
('PVE::RADOS', $VERSION);
30 my ($fh, $cmd, $data) = @_;
32 local $SIG{PIPE
} = 'IGNORE';
34 my $bin = pack "a L/a*", $cmd, $data || '';
35 my $res = syswrite $fh, $bin;
37 die "write data failed - $!\n" if !defined($res);
41 my ($fh, $allow_eof) = @_;
45 local $SIG{PIPE
} = 'IGNORE';
47 while (length($head) < 5) {
48 last if !sysread $fh, $head, 5 - length($head), length($head);
50 return undef if $allow_eof && length($head) == 0;
52 die "partial read\n" if length($head) < 5;
54 my ($cmd, $len) = unpack "a L", $head;
57 while (length($data) < $len) {
58 last if !sysread $fh, $data, $len - length($data), length($data);
60 die "partial data read\n" if length($data) < $len;
62 return wantarray ?
($cmd, $data) : $data;
65 my $kill_worker = sub {
68 return if !$self->{cpid
};
69 return if $self->{__already_killed
};
71 $self->{__already_killed
} = 1;
73 close($self->{child
}) if defined($self->{child
});
75 # only kill if we created the process
76 return if $self->{pid
} != $$;
78 kill(9, $self->{cpid
});
79 waitpid($self->{cpid
}, 0);
83 my ($self, $cmd, $data, $expect_tag) = @_;
85 $expect_tag = '>' if !$expect_tag;
87 die "detected forked connection\n" if $self->{pid
} != $$;
91 &$writedata($self->{child
}, $cmd, $data) if $expect_tag ne 'S';
92 ($restag, $raw) = &$readdata($self->{child
});
94 eval { PVE
::Tools
::run_with_timeout
($self->{timeout
}, $code); };
101 die "unknown error\n";
104 die "got unexpected result\n" if $restag ne $expect_tag;
110 my ($self, $parent, $timeout, %params) = @_;
114 my $ceph_user = delete $params{userid
} || $ceph_default_user;
115 $conn = pve_rados_create
($ceph_user) ||
116 die "unable to create RADOS object\n";
118 if (defined($params{ceph_conf
}) && (!-e
$params{ceph_conf
})) {
119 die "Supplied ceph config doesn't exist, $params{ceph_conf}\n";
122 my $ceph_conf = delete $params{ceph_conf
} || $ceph_default_conf;
125 pve_rados_conf_read_file
($conn, $ceph_conf);
128 pve_rados_conf_set
($conn, 'client_mount_timeout', $timeout);
130 foreach my $k (keys %params) {
131 pve_rados_conf_set
($conn, $k, $params{$k});
134 pve_rados_connect
($conn);
137 &$writedata($parent, 'E', $err);
140 &$writedata($parent, 'S');
142 $self->{conn
} = $conn;
145 my ($cmd, $data) = &$readdata($parent, 1);
147 last if !$cmd || $cmd eq 'Q';
151 if ($cmd eq 'M') { # rados monitor commands
152 $res = encode_json
(pve_rados_mon_command
($self->{conn
}, [ $data ]));
153 } elsif ($cmd eq 'C') { # class methods
154 my $aref = decode_json
($data);
155 my $method = shift @$aref;
156 $res = encode_json
($self->$method(@$aref));
158 die "invalid command\n";
162 &$writedata($parent, 'E', $err);
165 &$writedata($parent, '>', $res);
170 my ($class, %params) = @_;
172 my $rpcenv = PVE
::RPCEnvironment
::get
();
174 socketpair(my $child, my $parent, AF_UNIX
, SOCK_STREAM
, PF_UNSPEC
)
175 || die "socketpair: $!\n";
179 die "unable to fork - $!\n" if !defined($cpid);
181 my $self = bless {}, $class;
183 my $timeout = delete $params{timeout
} || $rados_default_timeout;
185 $self->{timeout
} = $timeout;
188 if ($cpid) { # parent
191 $self->{cpid
} = $cpid;
192 $self->{child
} = $child;
194 &$sendcmd($self, undef, undef, 'S'); # wait for sync
200 PVE
::INotify
::inotify_close
();
202 if (my $atfork = $rpcenv->{atfork
}) {
206 # override signal handlers inherited from the parent
207 local $SIG{HUP
} = $SIG{INT
} = $SIG{QUIT
} = $SIG{TERM
} = sub {
208 pve_rados_shutdown
($self->{conn
}) if $self->{conn
};
216 $self->pve_rados_work($parent, $timeout, %params);
221 pve_rados_shutdown
($self->{conn
}) if $self->{conn
};
222 POSIX
::_exit
($err ?
1 : 0);
229 my ($self, $new_timeout) = @_;
231 $self->{timeout
} = $new_timeout if $new_timeout;
233 return $self->{timeout
};
240 #print "$$: DESTROY WAIT0\n";
241 &$kill_worker($self);
242 #print "$$: DESTROY WAIT\n";
247 my ($self, @args) = @_;
250 my $data = encode_json
(['cluster_stat', @args]);
251 my $raw = &$sendcmd($self, 'C', $data);
252 return decode_json
($raw);
254 return pve_rados_cluster_stat
($self->{conn
});
258 # example1: { prefix => 'get_command_descriptions'})
259 # example2: { prefix => 'mon dump', format => 'json' }
261 my ($self, $cmd, $no_result_catch) = @_;
263 $cmd->{format
} = 'json' if !$cmd->{format
};
265 my $json = encode_json
($cmd);
267 my $ret = $sendcmd->($self, 'M', $json);
268 die "error with '$cmd->{prefix}': $@" if $@;
270 my $raw = decode_json
($ret);
272 die "error with '$cmd->{prefix}': mon_cmd failed - $raw->{status_message}\n"
273 if !$no_result_catch && $raw->{return_code
} < 0;
276 if ($cmd->{format
} && $cmd->{format
} eq 'json') {
277 $data = length($raw->{data
}) ? decode_json
($raw->{data
}) : undef;
279 $data = $raw->{data
};
282 return_code
=> $raw->{return_code
},
283 status_message
=> $raw->{status_message
},
288 # for backward compatibillity or if one just doesn't care about actual RADOS error details
290 my ($self, $cmd) = @_;
292 my $res = mon_cmd
($self, $cmd);
302 PVE::RADOS - Perl bindings for librados
308 my $rados = PVE::RADOS->new();
309 my $stat = $rados->cluster_stat();
310 my $res = $rados->mon_command({ prefix => 'mon dump', format => 'json' });
314 Perl bindings for librados.
322 Dietmar Maurer, E<lt>dietmar@proxmox.com<gt>
323 Thomas Lamprecht E<lt>t.lamprecht@proxmox.com<gt>
324 Wolfgang Bumiller E<lt>w.bumiller@proxmox.com<gt>