]>
git.proxmox.com Git - pve-manager.git/blob - PVE/API2/Ceph.pm
1 package PVE
::API2
::CephOSD
;
9 use PVE
::Tools
qw(extract_param run_command file_get_contents file_read_firstline dir_glob_regex dir_glob_foreach);
10 use PVE
::Exception
qw(raise raise_param_exc);
12 use PVE
::Cluster
qw(cfs_lock_file cfs_read_file cfs_write_file);
13 use PVE
::AccessControl
;
16 use PVE
::RPCEnvironment
;
17 use PVE
::JSONSchema
qw(get_standard_option);
22 use base
qw(PVE::RESTHandler);
24 use Data
::Dumper
; # fixme: remove
26 my $get_osd_status = sub {
27 my ($rados, $osdid) = @_;
29 my $stat = $rados->mon_command({ prefix
=> 'osd dump' });
31 my $osdlist = $stat->{osds
} || [];
33 my $flags = $stat->{flags
} || undef;
36 foreach my $d (@$osdlist) {
37 $osdstat->{$d->{osd
}} = $d if defined($d->{osd
});
39 if (defined($osdid)) {
40 die "no such OSD '$osdid'\n" if !$osdstat->{$osdid};
41 return $osdstat->{$osdid};
44 return wantarray?
($osdstat, $flags):$osdstat;
47 my $get_osd_usage = sub {
50 my $osdlist = $rados->mon_command({ prefix
=> 'pg dump',
51 dumpcontents
=> [ 'osds' ]}) || [];
54 foreach my $d (@$osdlist) {
55 $osdstat->{$d->{osd
}} = $d if defined($d->{osd
});
61 __PACKAGE__-
>register_method ({
65 description
=> "Get Ceph osd list/tree.",
69 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
72 additionalProperties
=> 0,
74 node
=> get_standard_option
('pve-node'),
77 # fixme: return a list instead of extjs tree format ?
84 PVE
::CephTools
::check_ceph_inited
();
86 my $rados = PVE
::RADOS-
>new();
87 my $res = $rados->mon_command({ prefix
=> 'osd tree' });
89 die "no tree nodes found\n" if !($res && $res->{nodes
});
91 my ($osdhash, $flags) = &$get_osd_status($rados);
93 my $usagehash = &$get_osd_usage($rados);
97 foreach my $e (@{$res->{nodes
}}) {
98 $nodes->{$e->{id
}} = $e;
106 foreach my $opt (qw(status crush_weight reweight)) {
107 $new->{$opt} = $e->{$opt} if defined($e->{$opt});
110 if (my $stat = $osdhash->{$e->{id
}}) {
111 $new->{in} = $stat->{in} if defined($stat->{in});
114 if (my $stat = $usagehash->{$e->{id
}}) {
115 $new->{total_space
} = ($stat->{kb
} || 1) * 1024;
116 $new->{bytes_used
} = ($stat->{kb_used
} || 0) * 1024;
117 $new->{percent_used
} = ($new->{bytes_used
}*100)/$new->{total_space
};
118 if (my $d = $stat->{fs_perf_stat
}) {
119 $new->{commit_latency_ms
} = $d->{commit_latency_ms
};
120 $new->{apply_latency_ms
} = $d->{apply_latency_ms
};
124 $newnodes->{$e->{id
}} = $new;
127 foreach my $e (@{$res->{nodes
}}) {
128 my $new = $newnodes->{$e->{id
}};
129 if ($e->{children
} && scalar(@{$e->{children
}})) {
130 $new->{children
} = [];
132 foreach my $cid (@{$e->{children
}}) {
133 $nodes->{$cid}->{parent
} = $e->{id
};
134 if ($nodes->{$cid}->{type
} eq 'osd' &&
135 $e->{type
} eq 'host') {
136 $newnodes->{$cid}->{host
} = $e->{name
};
138 push @{$new->{children
}}, $newnodes->{$cid};
141 $new->{leaf
} = ($e->{id
} >= 0) ?
1 : 0;
146 foreach my $e (@{$res->{nodes
}}) {
147 if (!$nodes->{$e->{id
}}->{parent
}) {
148 push @$roots, $newnodes->{$e->{id
}};
152 die "no root node\n" if !@$roots;
154 my $data = { root
=> { leaf
=> 0, children
=> $roots } };
156 # we want this for the noout flag
157 $data->{flags
} = $flags if $flags;
162 __PACKAGE__-
>register_method ({
166 description
=> "Create OSD",
170 additionalProperties
=> 0,
172 node
=> get_standard_option
('pve-node'),
174 description
=> "Block device name.",
178 description
=> "Block device name for journal.",
183 description
=> "File system type.",
185 enum
=> ['xfs', 'ext4', 'btrfs'],
191 returns
=> { type
=> 'string' },
195 my $rpcenv = PVE
::RPCEnvironment
::get
();
197 my $authuser = $rpcenv->get_user();
199 PVE
::CephTools
::check_ceph_inited
();
201 PVE
::CephTools
::setup_pve_symlinks
();
205 if ($param->{journal_dev
} && ($param->{journal_dev
} ne $param->{dev
})) {
206 $journal_dev = PVE
::Diskmanage
::verify_blockdev_path
($param->{journal_dev
});
209 $param->{dev
} = PVE
::Diskmanage
::verify_blockdev_path
($param->{dev
});
211 my $devname = $param->{dev
};
212 $devname =~ s
|/dev/||;
214 my $disklist = PVE
::Diskmanage
::get_disks
($devname, 1);
216 my $diskinfo = $disklist->{$devname};
217 die "unable to get device info for '$devname'\n"
220 die "device '$param->{dev}' is in use\n"
221 if $diskinfo->{used
};
223 my $devpath = $diskinfo->{devpath
};
224 my $rados = PVE
::RADOS-
>new();
225 my $monstat = $rados->mon_command({ prefix
=> 'mon_status' });
226 die "unable to get fsid\n" if !$monstat->{monmap
} || !$monstat->{monmap
}->{fsid
};
228 my $fsid = $monstat->{monmap
}->{fsid
};
229 $fsid = $1 if $fsid =~ m/^([0-9a-f\-]+)$/;
231 my $ceph_bootstrap_osd_keyring = PVE
::CephTools
::get_config
('ceph_bootstrap_osd_keyring');
233 if (! -f
$ceph_bootstrap_osd_keyring) {
234 my $bindata = $rados->mon_command({ prefix
=> 'auth get', entity
=> 'client.bootstrap-osd', format
=> 'plain' });
235 PVE
::Tools
::file_set_contents
($ceph_bootstrap_osd_keyring, $bindata);
241 my $fstype = $param->{fstype
} || 'xfs';
243 print "create OSD on $devpath ($fstype)\n";
245 my $ccname = PVE
::CephTools
::get_config
('ccname');
247 my $cmd = ['ceph-disk', 'prepare', '--zap-disk', '--fs-type', $fstype,
248 '--cluster', $ccname, '--cluster-uuid', $fsid ];
251 print "using device '$journal_dev' for journal\n";
252 push @$cmd, '--journal-dev', $devpath, $journal_dev;
254 push @$cmd, $devpath;
260 return $rpcenv->fork_worker('cephcreateosd', $devname, $authuser, $worker);
263 __PACKAGE__-
>register_method ({
264 name
=> 'destroyosd',
267 description
=> "Destroy OSD",
271 additionalProperties
=> 0,
273 node
=> get_standard_option
('pve-node'),
275 description
=> 'OSD ID',
279 description
=> "If set, we remove partition table entries.",
286 returns
=> { type
=> 'string' },
290 my $rpcenv = PVE
::RPCEnvironment
::get
();
292 my $authuser = $rpcenv->get_user();
294 PVE
::CephTools
::check_ceph_inited
();
296 my $osdid = $param->{osdid
};
298 my $rados = PVE
::RADOS-
>new();
299 my $osdstat = &$get_osd_status($rados, $osdid);
301 die "osd is in use (in == 1)\n" if $osdstat->{in};
302 #&$run_ceph_cmd(['osd', 'out', $osdid]);
304 die "osd is still runnung (up == 1)\n" if $osdstat->{up
};
306 my $osdsection = "osd.$osdid";
311 # reopen with longer timeout
312 $rados = PVE
::RADOS-
>new(timeout
=> PVE
::CephTools
::get_config
('long_rados_timeout'));
314 print "destroy OSD $osdsection\n";
316 eval { PVE
::CephTools
::ceph_service_cmd
('stop', $osdsection); };
319 print "Remove $osdsection from the CRUSH map\n";
320 $rados->mon_command({ prefix
=> "osd crush remove", name
=> $osdsection, format
=> 'plain' });
322 print "Remove the $osdsection authentication key.\n";
323 $rados->mon_command({ prefix
=> "auth del", entity
=> $osdsection, format
=> 'plain' });
325 print "Remove OSD $osdsection\n";
326 $rados->mon_command({ prefix
=> "osd rm", ids
=> [ $osdsection ], format
=> 'plain' });
328 # try to unmount from standard mount point
329 my $mountpoint = "/var/lib/ceph/osd/ceph-$osdid";
331 my $remove_partition = sub {
332 my ($disklist, $part) = @_;
334 return if !$part || (! -b
$part );
336 foreach my $real_dev (keys %$disklist) {
337 my $diskinfo = $disklist->{$real_dev};
338 my $devpath = $diskinfo->{devpath
};
339 next if !$diskinfo->{gpt
};
340 if ($part =~ m
|^${devpath
}(\d
+)$|) {
342 print "remove partition $part (disk '${devpath}', partnum $partnum)\n";
343 eval { run_command
(['/sbin/sgdisk', '-d', $partnum, "${devpath}"]); };
353 if ($param->{cleanup
}) {
354 my $jpath = "$mountpoint/journal";
355 $journal_part = abs_path
($jpath);
357 if (my $fd = IO
::File-
>new("/proc/mounts", "r")) {
358 while (defined(my $line = <$fd>)) {
359 my ($dev, $path, $fstype) = split(/\s+/, $line);
360 next if !($dev && $path && $fstype);
361 next if $dev !~ m
|^/dev/|;
362 if ($path eq $mountpoint) {
363 $data_part = abs_path
($dev);
371 print "Unmount OSD $osdsection from $mountpoint\n";
372 eval { run_command
(['umount', $mountpoint]); };
375 } elsif ($param->{cleanup
}) {
376 my $disklist = PVE
::Diskmanage
::get_disks
(undef, 1);
377 &$remove_partition($disklist, $journal_part);
378 &$remove_partition($disklist, $data_part);
382 return $rpcenv->fork_worker('cephdestroyosd', $osdsection, $authuser, $worker);
385 __PACKAGE__-
>register_method ({
387 path
=> '{osdid}/in',
389 description
=> "ceph osd in",
393 check
=> ['perm', '/', [ 'Sys.Modify' ]],
396 additionalProperties
=> 0,
398 node
=> get_standard_option
('pve-node'),
400 description
=> 'OSD ID',
405 returns
=> { type
=> "null" },
409 PVE
::CephTools
::check_ceph_inited
();
411 my $osdid = $param->{osdid
};
413 my $rados = PVE
::RADOS-
>new();
415 my $osdstat = &$get_osd_status($rados, $osdid); # osd exists?
417 my $osdsection = "osd.$osdid";
419 $rados->mon_command({ prefix
=> "osd in", ids
=> [ $osdsection ], format
=> 'plain' });
424 __PACKAGE__-
>register_method ({
426 path
=> '{osdid}/out',
428 description
=> "ceph osd out",
432 check
=> ['perm', '/', [ 'Sys.Modify' ]],
435 additionalProperties
=> 0,
437 node
=> get_standard_option
('pve-node'),
439 description
=> 'OSD ID',
444 returns
=> { type
=> "null" },
448 PVE
::CephTools
::check_ceph_inited
();
450 my $osdid = $param->{osdid
};
452 my $rados = PVE
::RADOS-
>new();
454 my $osdstat = &$get_osd_status($rados, $osdid); # osd exists?
456 my $osdsection = "osd.$osdid";
458 $rados->mon_command({ prefix
=> "osd out", ids
=> [ $osdsection ], format
=> 'plain' });
463 package PVE
::API2
::Ceph
;
469 use POSIX qw
(LONG_MAX
);
470 use Cwd
qw(abs_path);
476 use PVE
::Tools
qw(extract_param run_command file_get_contents file_read_firstline dir_glob_regex dir_glob_foreach);
477 use PVE
::Exception
qw(raise raise_param_exc);
479 use PVE
::Cluster
qw(cfs_lock_file cfs_read_file cfs_write_file);
480 use PVE
::AccessControl
;
482 use PVE
::RESTHandler
;
483 use PVE
::RPCEnvironment
;
484 use PVE
::JSONSchema
qw(get_standard_option);
489 use base
qw(PVE::RESTHandler);
491 use Data
::Dumper
; # fixme: remove
493 my $pve_osd_default_journal_size = 1024*5;
495 __PACKAGE__-
>register_method ({
496 subclass
=> "PVE::API2::CephOSD",
500 __PACKAGE__-
>register_method ({
504 description
=> "Directory index.",
505 permissions
=> { user
=> 'all' },
507 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
510 additionalProperties
=> 0,
512 node
=> get_standard_option
('pve-node'),
521 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
533 { name
=> 'status' },
535 { name
=> 'config' },
544 __PACKAGE__-
>register_method ({
548 description
=> "List local disks.",
552 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
555 additionalProperties
=> 0,
557 node
=> get_standard_option
('pve-node'),
559 description
=> "Only list specific types of disks.",
561 enum
=> ['unused', 'journal_disks'],
571 dev
=> { type
=> 'string' },
572 used
=> { type
=> 'string', optional
=> 1 },
573 gpt
=> { type
=> 'boolean' },
574 size
=> { type
=> 'integer' },
575 osdid
=> { type
=> 'integer' },
576 vendor
=> { type
=> 'string', optional
=> 1 },
577 model
=> { type
=> 'string', optional
=> 1 },
578 serial
=> { type
=> 'string', optional
=> 1 },
581 # links => [ { rel => 'child', href => "{}" } ],
586 PVE
::CephTools
::check_ceph_inited
();
588 my $disks = PVE
::Diskmanage
::get_disks
(undef, 1);
591 foreach my $dev (keys %$disks) {
592 my $d = $disks->{$dev};
593 if ($param->{type
}) {
594 if ($param->{type
} eq 'journal_disks') {
595 next if $d->{osdid
} >= 0;
597 } elsif ($param->{type
} eq 'unused') {
600 die "internal error"; # should not happen
604 $d->{dev
} = "/dev/$dev";
611 __PACKAGE__-
>register_method ({
616 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
618 description
=> "Get Ceph configuration.",
620 additionalProperties
=> 0,
622 node
=> get_standard_option
('pve-node'),
625 returns
=> { type
=> 'string' },
629 PVE
::CephTools
::check_ceph_inited
();
631 my $path = PVE
::CephTools
::get_config
('pve_ceph_cfgpath');
632 return PVE
::Tools
::file_get_contents
($path);
636 __PACKAGE__-
>register_method ({
640 description
=> "Get Ceph monitor list.",
644 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
647 additionalProperties
=> 0,
649 node
=> get_standard_option
('pve-node'),
657 name
=> { type
=> 'string' },
658 addr
=> { type
=> 'string' },
661 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
666 PVE
::CephTools
::check_ceph_inited
();
670 my $cfg = PVE
::CephTools
::parse_ceph_config
();
673 foreach my $section (keys %$cfg) {
674 my $d = $cfg->{$section};
675 if ($section =~ m/^mon\.(\S+)$/) {
677 if ($d->{'mon addr'} && $d->{'host'}) {
678 $monhash->{$monid} = {
679 addr
=> $d->{'mon addr'},
680 host
=> $d->{'host'},
688 my $rados = PVE
::RADOS-
>new();
689 my $monstat = $rados->mon_command({ prefix
=> 'mon_status' });
690 my $mons = $monstat->{monmap
}->{mons
};
691 foreach my $d (@$mons) {
692 next if !defined($d->{name
});
693 $monhash->{$d->{name
}}->{rank
} = $d->{rank
};
694 $monhash->{$d->{name
}}->{addr
} = $d->{addr
};
695 if (grep { $_ eq $d->{rank
} } @{$monstat->{quorum
}}) {
696 $monhash->{$d->{name
}}->{quorum
} = 1;
702 return PVE
::RESTHandler
::hash_to_array
($monhash, 'name');
705 __PACKAGE__-
>register_method ({
709 description
=> "Create initial ceph default configuration and setup symlinks.",
713 check
=> ['perm', '/', [ 'Sys.Modify' ]],
716 additionalProperties
=> 0,
718 node
=> get_standard_option
('pve-node'),
720 description
=> "Use specific network for all ceph related traffic",
721 type
=> 'string', format
=> 'CIDR',
726 description
=> 'Number of replicas per object',
734 description
=> "Placement group bits, used to specify the " .
735 "default number of placement groups.\n\nNOTE: 'osd pool " .
736 "default pg num' does not work for default pools.",
745 returns
=> { type
=> 'null' },
749 PVE
::CephTools
::check_ceph_installed
();
751 # simply load old config if it already exists
752 my $cfg = PVE
::CephTools
::parse_ceph_config
();
754 if (!$cfg->{global
}) {
759 UUID
::generate
($uuid);
760 UUID
::unparse
($uuid, $fsid);
764 'auth cluster required' => 'cephx',
765 'auth service required' => 'cephx',
766 'auth client required' => 'cephx',
767 'filestore xattr use omap' => 'true',
768 'osd journal size' => $pve_osd_default_journal_size,
769 'osd pool default min size' => 1,
772 # this does not work for default pools
773 #'osd pool default pg num' => $pg_num,
774 #'osd pool default pgp num' => $pg_num,
777 $cfg->{global
}->{keyring
} = '/etc/pve/priv/$cluster.$name.keyring';
778 $cfg->{osd
}->{keyring
} = '/var/lib/ceph/osd/ceph-$id/keyring';
780 $cfg->{global
}->{'osd pool default size'} = $param->{size
} if $param->{size
};
782 if ($param->{pg_bits
}) {
783 $cfg->{global
}->{'osd pg bits'} = $param->{pg_bits
};
784 $cfg->{global
}->{'osd pgp bits'} = $param->{pg_bits
};
787 if ($param->{network
}) {
788 $cfg->{global
}->{'public network'} = $param->{network
};
789 $cfg->{global
}->{'cluster network'} = $param->{network
};
792 PVE
::CephTools
::write_ceph_config
($cfg);
794 PVE
::CephTools
::setup_pve_symlinks
();
799 my $find_node_ip = sub {
802 my $net = Net
::IP-
>new($cidr) || die Net
::IP
::Error
() . "\n";
803 my $id = $net->version == 6 ?
'address6' : 'address';
805 my $config = PVE
::INotify
::read_file
('interfaces');
806 my $ifaces = $config->{ifaces
};
808 foreach my $iface (keys %$ifaces) {
809 my $d = $ifaces->{$iface};
811 my $a = Net
::IP-
>new($d->{$id});
813 return $d->{$id} if $net->overlaps($a);
816 die "unable to find local address within network '$cidr'\n";
819 __PACKAGE__-
>register_method ({
823 description
=> "Create Ceph Monitor",
827 check
=> ['perm', '/', [ 'Sys.Modify' ]],
830 additionalProperties
=> 0,
832 node
=> get_standard_option
('pve-node'),
835 returns
=> { type
=> 'string' },
839 PVE
::CephTools
::check_ceph_inited
();
841 PVE
::CephTools
::setup_pve_symlinks
();
843 my $rpcenv = PVE
::RPCEnvironment
::get
();
845 my $authuser = $rpcenv->get_user();
847 my $cfg = PVE
::CephTools
::parse_ceph_config
();
851 my $monaddrhash = {};
853 foreach my $section (keys %$cfg) {
854 next if $section eq 'global';
855 my $d = $cfg->{$section};
856 if ($section =~ m/^mon\./) {
858 if ($d->{'mon addr'}) {
859 $monaddrhash->{$d->{'mon addr'}} = $section;
865 for (my $i = 0; $i < 7; $i++) {
866 if (!$cfg->{"mon.$i"}) {
871 die "unable to find usable monitor id\n" if !defined($monid);
873 my $monsection = "mon.$monid";
875 if (my $pubnet = $cfg->{global
}->{'public network'}) {
876 $ip = &$find_node_ip($pubnet);
878 $ip = PVE
::Cluster
::remote_node_ip
($param->{node
});
881 my $monaddr = Net
::IP
::ip_is_ipv6
($ip) ?
"[$ip]:6789" : "$ip:6789";
882 my $monname = $param->{node
};
884 die "monitor '$monsection' already exists\n" if $cfg->{$monsection};
885 die "monitor address '$monaddr' already in use by '$monaddrhash->{$monaddr}'\n"
886 if $monaddrhash->{$monaddr};
891 my $pve_ckeyring_path = PVE
::CephTools
::get_config
('pve_ckeyring_path');
893 if (! -f
$pve_ckeyring_path) {
894 run_command
("ceph-authtool $pve_ckeyring_path --create-keyring " .
895 "--gen-key -n client.admin");
898 my $pve_mon_key_path = PVE
::CephTools
::get_config
('pve_mon_key_path');
899 if (! -f
$pve_mon_key_path) {
900 run_command
("cp $pve_ckeyring_path $pve_mon_key_path.tmp");
901 run_command
("ceph-authtool $pve_mon_key_path.tmp -n client.admin --set-uid=0 " .
902 "--cap mds 'allow' " .
903 "--cap osd 'allow *' " .
904 "--cap mon 'allow *'");
905 run_command
("ceph-authtool $pve_mon_key_path.tmp --gen-key -n mon. --cap mon 'allow *'");
906 run_command
("mv $pve_mon_key_path.tmp $pve_mon_key_path");
909 my $ccname = PVE
::CephTools
::get_config
('ccname');
911 my $mondir = "/var/lib/ceph/mon/$ccname-$monid";
912 -d
$mondir && die "monitor filesystem '$mondir' already exist\n";
914 my $monmap = "/tmp/monmap";
920 my $rados = PVE
::RADOS-
>new(timeout
=> PVE
::CephTools
::get_config
('long_rados_timeout'));
921 my $mapdata = $rados->mon_command({ prefix
=> 'mon getmap', format
=> 'plain' });
922 PVE
::Tools
::file_set_contents
($monmap, $mapdata);
924 run_command
("monmaptool --create --clobber --add $monid $monaddr --print $monmap");
927 run_command
("ceph-mon --mkfs -i $monid --monmap $monmap --keyring $pve_mon_key_path");
932 File
::Path
::remove_tree
($mondir);
936 $cfg->{$monsection} = {
938 'mon addr' => $monaddr,
941 PVE
::CephTools
::write_ceph_config
($cfg);
943 PVE
::CephTools
::ceph_service_cmd
('start', $monsection);
946 return $rpcenv->fork_worker('cephcreatemon', $monsection, $authuser, $worker);
949 __PACKAGE__-
>register_method ({
950 name
=> 'destroymon',
951 path
=> 'mon/{monid}',
953 description
=> "Destroy Ceph monitor.",
957 check
=> ['perm', '/', [ 'Sys.Modify' ]],
960 additionalProperties
=> 0,
962 node
=> get_standard_option
('pve-node'),
964 description
=> 'Monitor ID',
969 returns
=> { type
=> 'string' },
973 my $rpcenv = PVE
::RPCEnvironment
::get
();
975 my $authuser = $rpcenv->get_user();
977 PVE
::CephTools
::check_ceph_inited
();
979 my $cfg = PVE
::CephTools
::parse_ceph_config
();
981 my $monid = $param->{monid
};
982 my $monsection = "mon.$monid";
984 my $rados = PVE
::RADOS-
>new();
985 my $monstat = $rados->mon_command({ prefix
=> 'mon_status' });
986 my $monlist = $monstat->{monmap
}->{mons
};
988 die "no such monitor id '$monid'\n"
989 if !defined($cfg->{$monsection});
991 my $ccname = PVE
::CephTools
::get_config
('ccname');
993 my $mondir = "/var/lib/ceph/mon/$ccname-$monid";
994 -d
$mondir || die "monitor filesystem '$mondir' does not exist on this node\n";
996 die "can't remove last monitor\n" if scalar(@$monlist) <= 1;
1001 # reopen with longer timeout
1002 $rados = PVE
::RADOS-
>new(timeout
=> PVE
::CephTools
::get_config
('long_rados_timeout'));
1004 $rados->mon_command({ prefix
=> "mon remove", name
=> $monid, format
=> 'plain' });
1006 eval { PVE
::CephTools
::ceph_service_cmd
('stop', $monsection); };
1009 delete $cfg->{$monsection};
1010 PVE
::CephTools
::write_ceph_config
($cfg);
1011 File
::Path
::remove_tree
($mondir);
1014 return $rpcenv->fork_worker('cephdestroymon', $monsection, $authuser, $worker);
1017 __PACKAGE__-
>register_method ({
1021 description
=> "Stop ceph services.",
1025 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1028 additionalProperties
=> 0,
1030 node
=> get_standard_option
('pve-node'),
1032 description
=> 'Ceph service name.',
1035 pattern
=> '(mon|mds|osd)\.[A-Za-z0-9]{1,32}',
1039 returns
=> { type
=> 'string' },
1043 my $rpcenv = PVE
::RPCEnvironment
::get
();
1045 my $authuser = $rpcenv->get_user();
1047 PVE
::CephTools
::check_ceph_inited
();
1049 my $cfg = PVE
::CephTools
::parse_ceph_config
();
1050 scalar(keys %$cfg) || die "no configuration\n";
1056 if ($param->{service
}) {
1057 push @$cmd, $param->{service
};
1060 PVE
::CephTools
::ceph_service_cmd
(@$cmd);
1063 return $rpcenv->fork_worker('srvstop', $param->{service
} || 'ceph',
1064 $authuser, $worker);
1067 __PACKAGE__-
>register_method ({
1071 description
=> "Start ceph services.",
1075 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1078 additionalProperties
=> 0,
1080 node
=> get_standard_option
('pve-node'),
1082 description
=> 'Ceph service name.',
1085 pattern
=> '(mon|mds|osd)\.[A-Za-z0-9]{1,32}',
1089 returns
=> { type
=> 'string' },
1093 my $rpcenv = PVE
::RPCEnvironment
::get
();
1095 my $authuser = $rpcenv->get_user();
1097 PVE
::CephTools
::check_ceph_inited
();
1099 my $cfg = PVE
::CephTools
::parse_ceph_config
();
1100 scalar(keys %$cfg) || die "no configuration\n";
1105 my $cmd = ['start'];
1106 if ($param->{service
}) {
1107 push @$cmd, $param->{service
};
1110 PVE
::CephTools
::ceph_service_cmd
(@$cmd);
1113 return $rpcenv->fork_worker('srvstart', $param->{service
} || 'ceph',
1114 $authuser, $worker);
1117 __PACKAGE__-
>register_method ({
1121 description
=> "Get ceph status.",
1125 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
1128 additionalProperties
=> 0,
1130 node
=> get_standard_option
('pve-node'),
1133 returns
=> { type
=> 'object' },
1137 PVE
::CephTools
::check_ceph_enabled
();
1139 my $rados = PVE
::RADOS-
>new();
1140 return $rados->mon_command({ prefix
=> 'status' });
1143 __PACKAGE__-
>register_method ({
1147 description
=> "List all pools.",
1151 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
1154 additionalProperties
=> 0,
1156 node
=> get_standard_option
('pve-node'),
1164 pool
=> { type
=> 'integer' },
1165 pool_name
=> { type
=> 'string' },
1166 size
=> { type
=> 'integer' },
1169 links
=> [ { rel
=> 'child', href
=> "{pool_name}" } ],
1174 PVE
::CephTools
::check_ceph_inited
();
1176 my $rados = PVE
::RADOS-
>new();
1179 my $res = $rados->mon_command({ prefix
=> 'df' });
1180 my $total = $res->{stats
}->{total_avail_bytes
} || 0;
1182 foreach my $d (@{$res->{pools
}}) {
1183 next if !$d->{stats
};
1184 next if !defined($d->{id
});
1185 $stats->{$d->{id
}} = $d->{stats
};
1188 $res = $rados->mon_command({ prefix
=> 'osd dump' });
1191 foreach my $e (@{$res->{pools
}}) {
1193 foreach my $attr (qw(pool pool_name size min_size pg_num crush_ruleset)) {
1194 $d->{$attr} = $e->{$attr} if defined($e->{$attr});
1196 if (my $s = $stats->{$d->{pool
}}) {
1197 $d->{bytes_used
} = $s->{bytes_used
};
1198 $d->{percent_used
} = ($s->{bytes_used
} / $total)*100
1199 if $s->{max_avail
} && $total;
1208 __PACKAGE__-
>register_method ({
1209 name
=> 'createpool',
1212 description
=> "Create POOL",
1216 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1219 additionalProperties
=> 0,
1221 node
=> get_standard_option
('pve-node'),
1223 description
=> "The name of the pool. It must be unique.",
1227 description
=> 'Number of replicas per object',
1235 description
=> 'Minimum number of replicas per object',
1243 description
=> "Number of placement groups.",
1251 description
=> "The ruleset to use for mapping object placement in the cluster.",
1260 returns
=> { type
=> 'null' },
1264 PVE
::CephTools
::check_ceph_inited
();
1266 my $pve_ckeyring_path = PVE
::CephTools
::get_config
('pve_ckeyring_path');
1268 die "not fully configured - missing '$pve_ckeyring_path'\n"
1269 if ! -f
$pve_ckeyring_path;
1271 my $pg_num = $param->{pg_num
} || 64;
1272 my $size = $param->{size
} || 2;
1273 my $min_size = $param->{min_size
} || 1;
1274 my $ruleset = $param->{crush_ruleset
} || 0;
1275 my $rados = PVE
::RADOS-
>new();
1277 $rados->mon_command({
1278 prefix
=> "osd pool create",
1279 pool
=> $param->{name
},
1280 pg_num
=> int($pg_num),
1281 # this does not work for unknown reason
1282 # properties => ["size=$size", "min_size=$min_size", "crush_ruleset=$ruleset"],
1286 $rados->mon_command({
1287 prefix
=> "osd pool set",
1288 pool
=> $param->{name
},
1294 $rados->mon_command({
1295 prefix
=> "osd pool set",
1296 pool
=> $param->{name
},
1302 if (defined($param->{crush_ruleset
})) {
1303 $rados->mon_command({
1304 prefix
=> "osd pool set",
1305 pool
=> $param->{name
},
1306 var
=> 'crush_ruleset',
1307 val
=> $param->{crush_ruleset
},
1315 __PACKAGE__-
>register_method ({
1316 name
=> 'get_flags',
1319 description
=> "get all set ceph flags",
1323 check
=> ['perm', '/', [ 'Sys.Audit' ]],
1326 additionalProperties
=> 0,
1328 node
=> get_standard_option
('pve-node'),
1331 returns
=> { type
=> 'string' },
1335 PVE
::CephTools
::check_ceph_inited
();
1337 my $pve_ckeyring_path = PVE
::CephTools
::get_config
('pve_ckeyring_path');
1339 die "not fully configured - missing '$pve_ckeyring_path'\n"
1340 if ! -f
$pve_ckeyring_path;
1342 my $rados = PVE
::RADOS-
>new();
1344 my $stat = $rados->mon_command({ prefix
=> 'osd dump' });
1346 return $stat->{flags
} // '';
1349 __PACKAGE__-
>register_method ({
1351 path
=> 'flags/{flag}',
1353 description
=> "Set a ceph flag",
1357 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1360 additionalProperties
=> 0,
1362 node
=> get_standard_option
('pve-node'),
1364 description
=> 'The ceph flag to set/unset',
1366 enum
=> [ 'full', 'pause', 'noup', 'nodown', 'noout', 'noin', 'nobackfill', 'norebalance', 'norecover', 'noscrub', 'nodeep-scrub', 'notieragent'],
1370 returns
=> { type
=> 'null' },
1374 PVE
::CephTools
::check_ceph_inited
();
1376 my $pve_ckeyring_path = PVE
::CephTools
::get_config
('pve_ckeyring_path');
1378 die "not fully configured - missing '$pve_ckeyring_path'\n"
1379 if ! -f
$pve_ckeyring_path;
1381 my $set = $param->{set
} // !$param->{unset
};
1382 my $rados = PVE
::RADOS-
>new();
1384 $rados->mon_command({
1385 prefix
=> "osd set",
1386 key
=> $param->{flag
},
1392 __PACKAGE__-
>register_method ({
1393 name
=> 'unset_flag',
1394 path
=> 'flags/{flag}',
1396 description
=> "Unset a ceph flag",
1400 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1403 additionalProperties
=> 0,
1405 node
=> get_standard_option
('pve-node'),
1407 description
=> 'The ceph flag to set/unset',
1409 enum
=> [ 'full', 'pause', 'noup', 'nodown', 'noout', 'noin', 'nobackfill', 'norebalance', 'norecover', 'noscrub', 'nodeep-scrub', 'notieragent'],
1413 returns
=> { type
=> 'null' },
1417 PVE
::CephTools
::check_ceph_inited
();
1419 my $pve_ckeyring_path = PVE
::CephTools
::get_config
('pve_ckeyring_path');
1421 die "not fully configured - missing '$pve_ckeyring_path'\n"
1422 if ! -f
$pve_ckeyring_path;
1424 my $set = $param->{set
} // !$param->{unset
};
1425 my $rados = PVE
::RADOS-
>new();
1427 $rados->mon_command({
1428 prefix
=> "osd unset",
1429 key
=> $param->{flag
},
1435 __PACKAGE__-
>register_method ({
1436 name
=> 'destroypool',
1437 path
=> 'pools/{name}',
1439 description
=> "Destroy pool",
1443 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1446 additionalProperties
=> 0,
1448 node
=> get_standard_option
('pve-node'),
1450 description
=> "The name of the pool. It must be unique.",
1454 description
=> "If true, destroys pool even if in use",
1461 returns
=> { type
=> 'null' },
1465 PVE
::CephTools
::check_ceph_inited
();
1467 # if not forced, destroy ceph pool only when no
1468 # vm disks are on it anymore
1469 if (!$param->{force
}) {
1470 my $storagecfg = PVE
::Storage
::config
();
1471 foreach my $storageid (keys %{$storagecfg->{ids
}}) {
1472 my $storage = $storagecfg->{ids
}->{$storageid};
1473 next if $storage->{type
} ne 'rbd';
1474 next if $storage->{pool
} ne $param->{name
};
1476 # check if any vm disks are on the pool
1477 my $res = PVE
::Storage
::vdisk_list
($storagecfg, $storageid);
1478 die "ceph pool '$param->{name}' still in use by storage '$storageid'\n"
1479 if @{$res->{$storageid}} != 0;
1483 my $rados = PVE
::RADOS-
>new();
1484 # fixme: '--yes-i-really-really-mean-it'
1485 $rados->mon_command({
1486 prefix
=> "osd pool delete",
1487 pool
=> $param->{name
},
1488 pool2
=> $param->{name
},
1489 sure
=> '--yes-i-really-really-mean-it',
1497 __PACKAGE__-
>register_method ({
1501 description
=> "Get OSD crush map",
1505 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
1508 additionalProperties
=> 0,
1510 node
=> get_standard_option
('pve-node'),
1513 returns
=> { type
=> 'string' },
1517 PVE
::CephTools
::check_ceph_inited
();
1519 # this produces JSON (difficult to read for the user)
1520 # my $txt = &$run_ceph_cmd_text(['osd', 'crush', 'dump'], quiet => 1);
1524 my $mapfile = "/var/tmp/ceph-crush.map.$$";
1525 my $mapdata = "/var/tmp/ceph-crush.txt.$$";
1527 my $rados = PVE
::RADOS-
>new();
1530 my $bindata = $rados->mon_command({ prefix
=> 'osd getcrushmap', format
=> 'plain' });
1531 PVE
::Tools
::file_set_contents
($mapfile, $bindata);
1532 run_command
(['crushtool', '-d', $mapfile, '-o', $mapdata]);
1533 $txt = PVE
::Tools
::file_get_contents
($mapdata);
1545 __PACKAGE__-
>register_method({
1549 description
=> "Read ceph log",
1552 check
=> ['perm', '/nodes/{node}', [ 'Sys.Syslog' ]],
1556 additionalProperties
=> 0,
1558 node
=> get_standard_option
('pve-node'),
1577 description
=> "Line number",
1581 description
=> "Line text",
1590 my $rpcenv = PVE
::RPCEnvironment
::get
();
1591 my $user = $rpcenv->get_user();
1592 my $node = $param->{node
};
1594 my $logfile = "/var/log/ceph/ceph.log";
1595 my ($count, $lines) = PVE
::Tools
::dump_logfile
($logfile, $param->{start
}, $param->{limit
});
1597 $rpcenv->set_result_attrib('total', $count);