]>
Commit | Line | Data |
---|---|---|
3e8560ac DM |
1 | package PVE::CLI::pveceph; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
5 | ||
6 | use Fcntl ':flock'; | |
7 | use File::Path; | |
8 | use IO::File; | |
9 | use JSON; | |
10 | use Data::Dumper; | |
11 | use LWP::UserAgent; | |
12 | ||
13 | use PVE::SafeSyslog; | |
14 | use PVE::Cluster; | |
15 | use PVE::INotify; | |
16 | use PVE::RPCEnvironment; | |
17 | use PVE::Storage; | |
18 | use PVE::Tools qw(run_command); | |
19 | use PVE::JSONSchema qw(get_standard_option); | |
6fb08cb9 | 20 | use PVE::Ceph::Tools; |
91dfa228 | 21 | use PVE::Ceph::Services; |
3e8560ac | 22 | use PVE::API2::Ceph; |
7e1a9d25 | 23 | use PVE::API2::Ceph::FS; |
b82649cc | 24 | use PVE::API2::Ceph::MDS; |
4fec2764 | 25 | use PVE::API2::Ceph::MGR; |
98fe93ae | 26 | use PVE::API2::Ceph::MON; |
79fa41a2 | 27 | use PVE::API2::Ceph::OSD; |
3e8560ac DM |
28 | |
29 | use PVE::CLIHandler; | |
30 | ||
31 | use base qw(PVE::CLIHandler); | |
32 | ||
33 | my $nodename = PVE::INotify::nodename(); | |
34 | ||
35 | my $upid_exit = sub { | |
36 | my $upid = shift; | |
37 | my $status = PVE::Tools::upid_read_status($upid); | |
38 | exit($status eq 'OK' ? 0 : -1); | |
39 | }; | |
40 | ||
7e017024 DM |
41 | sub setup_environment { |
42 | PVE::RPCEnvironment->setup_default_cli_env(); | |
43 | } | |
44 | ||
3e8560ac DM |
45 | __PACKAGE__->register_method ({ |
46 | name => 'purge', | |
47 | path => 'purge', | |
48 | method => 'POST', | |
49 | description => "Destroy ceph related data and configuration files.", | |
50 | parameters => { | |
51 | additionalProperties => 0, | |
52 | properties => { | |
91dfa228 AA |
53 | logs => { |
54 | description => 'Additionally purge Ceph logs, /var/log/ceph.', | |
55 | type => 'boolean', | |
56 | optional => 1, | |
57 | }, | |
58 | crash => { | |
59 | description => 'Additionally purge Ceph crash logs, /var/lib/ceph/crash.', | |
60 | type => 'boolean', | |
61 | optional => 1, | |
62 | }, | |
3e8560ac DM |
63 | }, |
64 | }, | |
65 | returns => { type => 'null' }, | |
66 | code => sub { | |
67 | my ($param) = @_; | |
68 | ||
91dfa228 AA |
69 | my $message; |
70 | my $pools = []; | |
71 | my $monstat = {}; | |
72 | my $mdsstat = {}; | |
73 | my $osdstat = []; | |
3e8560ac DM |
74 | |
75 | eval { | |
76 | my $rados = PVE::RADOS->new(); | |
91dfa228 AA |
77 | $pools = PVE::Ceph::Tools::ls_pools(undef, $rados); |
78 | $monstat = PVE::Ceph::Services::get_services_info('mon', undef, $rados); | |
79 | $mdsstat = PVE::Ceph::Services::get_services_info('mds', undef, $rados); | |
80 | $osdstat = $rados->mon_command({ prefix => 'osd metadata' }); | |
3e8560ac | 81 | }; |
c3a3f3ab | 82 | warn "Error gathering ceph info, already purged? Message: $@" if $@; |
3e8560ac | 83 | |
91dfa228 AA |
84 | my $osd = grep { $_->{hostname} eq $nodename } @$osdstat; |
85 | my $mds = grep { $mdsstat->{$_}->{host} eq $nodename } keys %$mdsstat; | |
86 | my $mon = grep { $monstat->{$_}->{host} eq $nodename } keys %$monstat; | |
3e8560ac | 87 | |
91dfa228 AA |
88 | # no pools = no data |
89 | $message .= "- remove pools, this will !!DESTROY DATA!!\n" if @$pools; | |
90 | $message .= "- remove active OSD on $nodename\n" if $osd; | |
91 | $message .= "- remove active MDS on $nodename\n" if $mds; | |
92 | $message .= "- remove other MONs, $nodename is not the last MON\n" | |
93 | if scalar(keys %$monstat) > 1 && $mon; | |
94 | ||
95 | # display all steps at once | |
96 | die "Unable to purge Ceph!\n\nTo continue:\n$message" if $message; | |
97 | ||
98 | my $services = PVE::Ceph::Services::get_local_services(); | |
99 | $services->{mon} = $monstat if $mon; | |
100 | $services->{crash}->{$nodename} = { direxists => 1 } if $param->{crash}; | |
101 | $services->{logs}->{$nodename} = { direxists => 1 } if $param->{logs}; | |
102 | ||
103 | PVE::Ceph::Tools::purge_all_ceph_services($services); | |
104 | PVE::Ceph::Tools::purge_all_ceph_files($services); | |
3e8560ac DM |
105 | |
106 | return undef; | |
107 | }}); | |
108 | ||
109 | __PACKAGE__->register_method ({ | |
110 | name => 'install', | |
111 | path => 'install', | |
112 | method => 'POST', | |
113 | description => "Install ceph related packages.", | |
114 | parameters => { | |
115 | additionalProperties => 0, | |
116 | properties => { | |
117 | version => { | |
118 | type => 'string', | |
c3d9c698 | 119 | # for buster, luminous kept for testing/upgrade purposes only! - FIXME: remove with 6.2? |
61819403 | 120 | enum => ['luminous', 'nautilus', 'octopus'], |
c3d9c698 TL |
121 | default => 'nautilus', |
122 | description => "Ceph version to install.", | |
3e8560ac | 123 | optional => 1, |
61819403 TL |
124 | }, |
125 | 'allow-experimental' => { | |
126 | type => 'boolean', | |
127 | default => 0, | |
128 | optional => 1, | |
129 | description => "Allow experimental versions. Use with care!", | |
130 | }, | |
3e8560ac DM |
131 | }, |
132 | }, | |
133 | returns => { type => 'null' }, | |
134 | code => sub { | |
135 | my ($param) = @_; | |
136 | ||
c3d9c698 TL |
137 | my $default_vers = 'nautilus'; |
138 | my $cephver = $param->{version} || $default_vers; | |
3e8560ac | 139 | |
c3d9c698 TL |
140 | my $repolist; |
141 | if ($cephver eq 'nautilus') { | |
61819403 | 142 | $repolist = "deb http://download.proxmox.com/debian/ceph-nautilus buster main\n"; |
c3d9c698 | 143 | } elsif ($cephver eq 'luminous') { |
dea23f32 | 144 | die "Not allowed to select version '$cephver'\n" if !$param->{'allow-experimental'}; |
61819403 TL |
145 | $repolist = "deb http://download.proxmox.com/debian/ceph-luminous buster main\n"; |
146 | } elsif ($cephver eq 'octopus') { | |
147 | die "Not allowed to select version '$cephver'\n" if !$param->{'allow-experimental'}; | |
45221aae TL |
148 | $repolist = "deb http://download.proxmox.com/debian/ceph-octopus buster test\n"; |
149 | # FIXME: use production component once available | |
61819403 | 150 | #$repolist = "deb http://download.proxmox.com/debian/ceph-octopus buster main\n"; |
caf37855 | 151 | } else { |
36d5ee12 | 152 | die "not implemented ceph version: $cephver"; |
caf37855 | 153 | } |
c3d9c698 TL |
154 | PVE::Tools::file_set_contents("/etc/apt/sources.list.d/ceph.list", $repolist); |
155 | ||
156 | warn "WARNING: installing non-default ceph release '$cephver'!\n\n" if $cephver ne $default_vers; | |
3e8560ac | 157 | |
87166f36 | 158 | local $ENV{DEBIAN_FRONTEND} = 'noninteractive'; |
3e8560ac | 159 | print "update available package list\n"; |
19137ed6 | 160 | eval { run_command(['apt', '-q', 'update'], outfunc => sub {}, errfunc => sub { print STDERR "$_[0]\n" }) }; |
3e8560ac | 161 | |
19137ed6 | 162 | my @apt_install = qw(apt --no-install-recommends -o Dpkg::Options::=--force-confnew install --); |
2ca75379 TL |
163 | my @ceph_packages = qw( |
164 | ceph | |
165 | ceph-common | |
166 | ceph-mds | |
167 | ceph-fuse | |
168 | gdisk | |
169 | ); | |
170 | ||
55ab726e | 171 | print "start installation\n"; |
2ca75379 TL |
172 | if (system(@apt_install, @ceph_packages) != 0) { |
173 | die "apt failed during ceph installation ($?)\n"; | |
174 | } | |
55ab726e TL |
175 | |
176 | print "\ninstalled ceph $cephver successfully\n"; | |
3e8560ac DM |
177 | |
178 | return undef; | |
179 | }}); | |
180 | ||
181 | our $cmddef = { | |
182 | init => [ 'PVE::API2::Ceph', 'init', [], { node => $nodename } ], | |
8c476782 TL |
183 | pool => { |
184 | ls => [ 'PVE::API2::Ceph', 'lspools', [], { node => $nodename }, sub { | |
d4dba076 AA |
185 | my ($data, $schema, $options) = @_; |
186 | PVE::CLIFormatter::print_api_result($data, $schema, | |
187 | [ | |
188 | 'pool_name', | |
189 | 'size', | |
190 | 'min_size', | |
191 | 'pg_num', | |
192 | 'pg_autoscale_mode', | |
193 | 'crush_rule_name', | |
194 | 'percent_used', | |
195 | 'bytes_used', | |
196 | ], | |
197 | $options); | |
198 | }, $PVE::RESTHandler::standard_output_options], | |
8c476782 TL |
199 | create => [ 'PVE::API2::Ceph', 'createpool', ['name'], { node => $nodename }], |
200 | destroy => [ 'PVE::API2::Ceph', 'destroypool', ['name'], { node => $nodename } ], | |
201 | }, | |
202 | lspools => { alias => 'pool ls' }, | |
203 | createpool => { alias => 'pool create' }, | |
204 | destroypool => { alias => 'pool destroy' }, | |
205 | fs => { | |
206 | create => [ 'PVE::API2::Ceph::FS', 'createfs', [], { node => $nodename }], | |
207 | }, | |
208 | osd => { | |
79fa41a2 DC |
209 | create => [ 'PVE::API2::Ceph::OSD', 'createosd', ['dev'], { node => $nodename }, $upid_exit], |
210 | destroy => [ 'PVE::API2::Ceph::OSD', 'destroyosd', ['osdid'], { node => $nodename }, $upid_exit], | |
8c476782 TL |
211 | }, |
212 | createosd => { alias => 'osd create' }, | |
213 | destroyosd => { alias => 'osd destroy' }, | |
214 | mon => { | |
98fe93ae DC |
215 | create => [ 'PVE::API2::Ceph::MON', 'createmon', [], { node => $nodename }, $upid_exit], |
216 | destroy => [ 'PVE::API2::Ceph::MON', 'destroymon', ['monid'], { node => $nodename }, $upid_exit], | |
8c476782 TL |
217 | }, |
218 | createmon => { alias => 'mon create' }, | |
219 | destroymon => { alias => 'mon destroy' }, | |
220 | mgr => { | |
4fec2764 DC |
221 | create => [ 'PVE::API2::Ceph::MGR', 'createmgr', [], { node => $nodename }, $upid_exit], |
222 | destroy => [ 'PVE::API2::Ceph::MGR', 'destroymgr', ['id'], { node => $nodename }, $upid_exit], | |
8c476782 TL |
223 | }, |
224 | createmgr => { alias => 'mgr create' }, | |
225 | destroymgr => { alias => 'mgr destroy' }, | |
226 | mds => { | |
227 | create => [ 'PVE::API2::Ceph::MDS', 'createmds', [], { node => $nodename }, $upid_exit], | |
f16bb531 | 228 | destroy => [ 'PVE::API2::Ceph::MDS', 'destroymds', ['name'], { node => $nodename }, $upid_exit], |
8c476782 | 229 | }, |
7da6ff26 DJ |
230 | start => [ 'PVE::API2::Ceph', 'start', [], { node => $nodename }, $upid_exit], |
231 | stop => [ 'PVE::API2::Ceph', 'stop', [], { node => $nodename }, $upid_exit], | |
3e8560ac DM |
232 | install => [ __PACKAGE__, 'install', [] ], |
233 | purge => [ __PACKAGE__, 'purge', [] ], | |
234 | status => [ 'PVE::API2::Ceph', 'status', [], { node => $nodename }, sub { | |
235 | my $res = shift; | |
236 | my $json = JSON->new->allow_nonref; | |
237 | print $json->pretty->encode($res) . "\n"; | |
238 | }], | |
239 | }; | |
240 | ||
241 | 1; |