]>
git.proxmox.com Git - pve-manager.git/blob - PVE/API2/Ceph.pm
1 package PVE
::API2
::Ceph
;
11 use PVE
::Ceph
::Services
;
12 use PVE
::Cluster
qw(cfs_read_file cfs_write_file);
13 use PVE
::JSONSchema
qw(get_standard_option);
17 use PVE
::RPCEnvironment
;
19 use PVE
::Tools
qw(run_command file_get_contents file_set_contents);
21 use PVE
::API2
::Ceph
::OSD
;
22 use PVE
::API2
::Ceph
::FS
;
23 use PVE
::API2
::Ceph
::MDS
;
24 use PVE
::API2
::Storage
::Config
;
26 use base
qw(PVE::RESTHandler);
28 my $pve_osd_default_journal_size = 1024*5;
30 __PACKAGE__-
>register_method ({
31 subclass
=> "PVE::API2::Ceph::OSD",
35 __PACKAGE__-
>register_method ({
36 subclass
=> "PVE::API2::Ceph::MDS",
40 __PACKAGE__-
>register_method ({
41 subclass
=> "PVE::API2::Ceph::FS",
45 __PACKAGE__-
>register_method ({
49 description
=> "Directory index.",
50 permissions
=> { user
=> 'all' },
52 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
55 additionalProperties
=> 0,
57 node
=> get_standard_option
('pve-node'),
66 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
92 __PACKAGE__-
>register_method ({
96 description
=> "List local disks.",
100 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
103 additionalProperties
=> 0,
105 node
=> get_standard_option
('pve-node'),
107 description
=> "Only list specific types of disks.",
109 enum
=> ['unused', 'journal_disks'],
119 dev
=> { type
=> 'string' },
120 used
=> { type
=> 'string', optional
=> 1 },
121 gpt
=> { type
=> 'boolean' },
122 size
=> { type
=> 'integer' },
123 osdid
=> { type
=> 'integer' },
124 vendor
=> { type
=> 'string', optional
=> 1 },
125 model
=> { type
=> 'string', optional
=> 1 },
126 serial
=> { type
=> 'string', optional
=> 1 },
129 # links => [ { rel => 'child', href => "{}" } ],
134 PVE
::Ceph
::Tools
::check_ceph_inited
();
136 my $disks = PVE
::Diskmanage
::get_disks
(undef, 1);
139 foreach my $dev (keys %$disks) {
140 my $d = $disks->{$dev};
141 if ($param->{type
}) {
142 if ($param->{type
} eq 'journal_disks') {
143 next if $d->{osdid
} >= 0;
145 } elsif ($param->{type
} eq 'unused') {
148 die "internal error"; # should not happen
152 $d->{dev
} = "/dev/$dev";
159 __PACKAGE__-
>register_method ({
164 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
166 description
=> "Get Ceph configuration.",
168 additionalProperties
=> 0,
170 node
=> get_standard_option
('pve-node'),
173 returns
=> { type
=> 'string' },
177 PVE
::Ceph
::Tools
::check_ceph_inited
();
179 my $path = PVE
::Ceph
::Tools
::get_config
('pve_ceph_cfgpath');
180 return file_get_contents
($path);
184 my $add_storage = sub {
185 my ($pool, $storeid) = @_;
187 my $storage_params = {
192 content
=> 'rootdir,images',
195 PVE
::API2
::Storage
::Config-
>create($storage_params);
198 my $get_storages = sub {
201 my $cfg = PVE
::Storage
::config
();
203 my $storages = $cfg->{ids
};
205 foreach my $storeid (keys %$storages) {
206 my $curr = $storages->{$storeid};
207 $res->{$storeid} = $storages->{$storeid}
208 if $curr->{type
} eq 'rbd' && $pool eq $curr->{pool
};
214 __PACKAGE__-
>register_method ({
218 description
=> "Get Ceph monitor list.",
222 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
225 additionalProperties
=> 0,
227 node
=> get_standard_option
('pve-node'),
235 name
=> { type
=> 'string' },
236 addr
=> { type
=> 'string' },
239 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
244 PVE
::Ceph
::Tools
::check_ceph_inited
();
248 my $cfg = cfs_read_file
('ceph.conf');
251 foreach my $section (keys %$cfg) {
252 my $d = $cfg->{$section};
253 if ($section =~ m/^mon\.(\S+)$/) {
255 if ($d->{'mon addr'} && $d->{'host'}) {
256 $monhash->{$monid} = {
257 addr
=> $d->{'mon addr'},
258 host
=> $d->{'host'},
266 my $rados = PVE
::RADOS-
>new();
267 my $monstat = $rados->mon_command({ prefix
=> 'mon_status' });
268 my $mons = $monstat->{monmap
}->{mons
};
269 foreach my $d (@$mons) {
270 next if !defined($d->{name
});
271 $monhash->{$d->{name
}}->{rank
} = $d->{rank
};
272 $monhash->{$d->{name
}}->{addr
} = $d->{addr
};
273 if (grep { $_ eq $d->{rank
} } @{$monstat->{quorum
}}) {
274 $monhash->{$d->{name
}}->{quorum
} = 1;
280 return PVE
::RESTHandler
::hash_to_array
($monhash, 'name');
283 __PACKAGE__-
>register_method ({
287 description
=> "Create initial ceph default configuration and setup symlinks.",
291 check
=> ['perm', '/', [ 'Sys.Modify' ]],
294 additionalProperties
=> 0,
296 node
=> get_standard_option
('pve-node'),
298 description
=> "Use specific network for all ceph related traffic",
299 type
=> 'string', format
=> 'CIDR',
303 'cluster-network' => {
304 description
=> "Declare a separate cluster network, OSDs will route" .
305 "heartbeat, object replication and recovery traffic over it",
306 type
=> 'string', format
=> 'CIDR',
307 requires
=> 'network',
312 description
=> 'Targeted number of replicas per object',
320 description
=> 'Minimum number of available replicas per object to allow I/O',
328 description
=> "Placement group bits, used to specify the " .
329 "default number of placement groups.\n\nNOTE: 'osd pool " .
330 "default pg num' does not work for default pools.",
338 description
=> "Disable cephx authentification.\n\n" .
339 "WARNING: cephx is a security feature protecting against " .
340 "man-in-the-middle attacks. Only consider disabling cephx ".
341 "if your network is private!",
348 returns
=> { type
=> 'null' },
352 my $version = PVE
::Ceph
::Tools
::get_local_version
(1);
354 if (!$version || $version < 12) {
355 die "Ceph Luminous required - please run 'pveceph install'\n";
357 PVE
::Ceph
::Tools
::check_ceph_installed
('ceph_bin');
360 # simply load old config if it already exists
361 my $cfg = cfs_read_file
('ceph.conf');
363 if (!$cfg->{global
}) {
368 UUID
::generate
($uuid);
369 UUID
::unparse
($uuid, $fsid);
371 my $auth = $param->{disable_cephx
} ?
'none' : 'cephx';
375 'auth cluster required' => $auth,
376 'auth service required' => $auth,
377 'auth client required' => $auth,
378 'osd journal size' => $pve_osd_default_journal_size,
379 'osd pool default size' => $param->{size
} // 3,
380 'osd pool default min size' => $param->{min_size
} // 2,
381 'mon allow pool delete' => 'true',
384 # this does not work for default pools
385 #'osd pool default pg num' => $pg_num,
386 #'osd pool default pgp num' => $pg_num,
389 $cfg->{global
}->{keyring
} = '/etc/pve/priv/$cluster.$name.keyring';
390 $cfg->{osd
}->{keyring
} = '/var/lib/ceph/osd/ceph-$id/keyring';
392 if ($param->{pg_bits
}) {
393 $cfg->{global
}->{'osd pg bits'} = $param->{pg_bits
};
394 $cfg->{global
}->{'osd pgp bits'} = $param->{pg_bits
};
397 if ($param->{network
}) {
398 $cfg->{global
}->{'public network'} = $param->{network
};
399 $cfg->{global
}->{'cluster network'} = $param->{network
};
402 if ($param->{'cluster-network'}) {
403 $cfg->{global
}->{'cluster network'} = $param->{'cluster-network'};
406 cfs_write_file
('ceph.conf', $cfg);
408 PVE
::Ceph
::Tools
::setup_pve_symlinks
();
413 my $find_mon_ip = sub {
414 my ($pubnet, $node, $overwrite_ip) = @_;
417 return $overwrite_ip // PVE
::Cluster
::remote_node_ip
($node);
420 my $allowed_ips = PVE
::Network
::get_local_ip_from_cidr
($pubnet);
421 die "No IP configured and up from ceph public network '$pubnet'\n"
422 if scalar(@$allowed_ips) < 1;
424 if (!$overwrite_ip) {
425 if (scalar(@$allowed_ips) == 1) {
426 return $allowed_ips->[0];
428 die "Multiple IPs for ceph public network '$pubnet' detected on $node:\n".
429 join("\n", @$allowed_ips) ."\nuse 'mon-address' to specify one of them.\n";
431 if (grep { $_ eq $overwrite_ip } @$allowed_ips) {
432 return $overwrite_ip;
434 die "Monitor IP '$overwrite_ip' not in ceph public network '$pubnet'\n"
435 if !PVE
::Network
::is_ip_in_cidr
($overwrite_ip, $pubnet);
437 die "Specified monitor IP '$overwrite_ip' not configured or up on $node!\n";
441 __PACKAGE__-
>register_method ({
445 description
=> "Create Ceph Monitor and Manager",
449 check
=> ['perm', '/', [ 'Sys.Modify' ]],
452 additionalProperties
=> 0,
454 node
=> get_standard_option
('pve-node'),
458 pattern
=> '[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?',
459 description
=> "The ID for the monitor, when omitted the same as the nodename",
461 'exclude-manager' => {
465 description
=> "When set, only a monitor will be created.",
468 description
=> 'Overwrites autodetected monitor IP address. ' .
469 'Must be in the public network of ceph.',
470 type
=> 'string', format
=> 'ip',
475 returns
=> { type
=> 'string' },
479 PVE
::Ceph
::Tools
::check_ceph_installed
('ceph_mon');
481 PVE
::Ceph
::Tools
::check_ceph_installed
('ceph_mgr')
482 if (!$param->{'exclude-manager'});
484 PVE
::Ceph
::Tools
::check_ceph_inited
();
486 PVE
::Ceph
::Tools
::setup_pve_symlinks
();
488 my $rpcenv = PVE
::RPCEnvironment
::get
();
490 my $authuser = $rpcenv->get_user();
492 my $cfg = cfs_read_file
('ceph.conf');
496 my $monaddrhash = {};
498 my $systemd_managed = PVE
::Ceph
::Tools
::systemd_managed
();
500 foreach my $section (keys %$cfg) {
501 next if $section eq 'global';
502 my $d = $cfg->{$section};
503 if ($section =~ m/^mon\./) {
505 if ($d->{'mon addr'}) {
506 $monaddrhash->{$d->{'mon addr'}} = $section;
511 my $monid = $param->{id
} // $param->{node
};
513 my $monsection = "mon.$monid";
514 my $pubnet = $cfg->{global
}->{'public network'};
515 my $ip = $find_mon_ip->($pubnet, $param->{node
}, $param->{'mon-address'});
517 my $monaddr = Net
::IP
::ip_is_ipv6
($ip) ?
"[$ip]:6789" : "$ip:6789";
518 my $monname = $param->{node
};
520 die "monitor '$monsection' already exists\n" if $cfg->{$monsection};
521 die "monitor address '$monaddr' already in use by '$monaddrhash->{$monaddr}'\n"
522 if $monaddrhash->{$monaddr};
527 my $pve_ckeyring_path = PVE
::Ceph
::Tools
::get_config
('pve_ckeyring_path');
529 if (! -f
$pve_ckeyring_path) {
530 run_command
("ceph-authtool $pve_ckeyring_path --create-keyring " .
531 "--gen-key -n client.admin");
534 my $pve_mon_key_path = PVE
::Ceph
::Tools
::get_config
('pve_mon_key_path');
535 if (! -f
$pve_mon_key_path) {
536 run_command
("cp $pve_ckeyring_path $pve_mon_key_path.tmp");
537 run_command
("ceph-authtool $pve_mon_key_path.tmp -n client.admin --set-uid=0 " .
538 "--cap mds 'allow' " .
539 "--cap osd 'allow *' " .
540 "--cap mgr 'allow *' " .
541 "--cap mon 'allow *'");
542 run_command
("cp $pve_mon_key_path.tmp /etc/ceph/ceph.client.admin.keyring") if $systemd_managed;
543 run_command
("chown ceph:ceph /etc/ceph/ceph.client.admin.keyring") if $systemd_managed;
544 run_command
("ceph-authtool $pve_mon_key_path.tmp --gen-key -n mon. --cap mon 'allow *'");
545 run_command
("mv $pve_mon_key_path.tmp $pve_mon_key_path");
548 my $ccname = PVE
::Ceph
::Tools
::get_config
('ccname');
550 my $mondir = "/var/lib/ceph/mon/$ccname-$monid";
551 -d
$mondir && die "monitor filesystem '$mondir' already exist\n";
553 my $monmap = "/tmp/monmap";
558 run_command
("chown ceph:ceph $mondir") if $systemd_managed;
561 my $rados = PVE
::RADOS-
>new(timeout
=> PVE
::Ceph
::Tools
::get_config
('long_rados_timeout'));
562 my $mapdata = $rados->mon_command({ prefix
=> 'mon getmap', format
=> 'plain' });
563 file_set_contents
($monmap, $mapdata);
565 run_command
("monmaptool --create --clobber --add $monid $monaddr --print $monmap");
568 run_command
("ceph-mon --mkfs -i $monid --monmap $monmap --keyring $pve_mon_key_path");
569 run_command
("chown ceph:ceph -R $mondir") if $systemd_managed;
574 File
::Path
::remove_tree
($mondir);
578 $cfg->{$monsection} = {
580 'mon addr' => $monaddr,
583 cfs_write_file
('ceph.conf', $cfg);
585 my $create_keys_pid = fork();
586 if (!defined($create_keys_pid)) {
587 die "Could not spawn ceph-create-keys to create bootstrap keys\n";
588 } elsif ($create_keys_pid == 0) {
589 exit PVE
::Tools
::run_command
(['ceph-create-keys', '-i', $monid]);
591 PVE
::Ceph
::Services
::ceph_service_cmd
('start', $monsection);
593 if ($systemd_managed) {
594 #to ensure we have the correct startup order.
595 eval { PVE
::Tools
::run_command
(['/bin/systemctl', 'enable', "ceph-mon\@${monid}.service"]); };
596 warn "Enable ceph-mon\@${monid}.service manually"if $@;
598 waitpid($create_keys_pid, 0);
602 if (!$param->{'exclude-manager'}) {
603 my $rados = PVE
::RADOS-
>new(timeout
=> PVE
::Ceph
::Tools
::get_config
('long_rados_timeout'));
604 PVE
::Ceph
::Services
::create_mgr
($monid, $rados);
608 return $rpcenv->fork_worker('cephcreatemon', $monsection, $authuser, $worker);
611 __PACKAGE__-
>register_method ({
612 name
=> 'destroymon',
613 path
=> 'mon/{monid}',
615 description
=> "Destroy Ceph Monitor and Manager.",
619 check
=> ['perm', '/', [ 'Sys.Modify' ]],
622 additionalProperties
=> 0,
624 node
=> get_standard_option
('pve-node'),
626 description
=> 'Monitor ID',
628 pattern
=> '[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?',
630 'exclude-manager' => {
634 description
=> "When set, removes only the monitor, not the manager"
638 returns
=> { type
=> 'string' },
642 my $rpcenv = PVE
::RPCEnvironment
::get
();
644 my $authuser = $rpcenv->get_user();
646 PVE
::Ceph
::Tools
::check_ceph_inited
();
648 my $cfg = cfs_read_file
('ceph.conf');
650 my $monid = $param->{monid
};
651 my $monsection = "mon.$monid";
653 my $rados = PVE
::RADOS-
>new();
654 my $monstat = $rados->mon_command({ prefix
=> 'mon_status' });
655 my $monlist = $monstat->{monmap
}->{mons
};
657 die "no such monitor id '$monid'\n"
658 if !defined($cfg->{$monsection});
660 my $ccname = PVE
::Ceph
::Tools
::get_config
('ccname');
662 my $mondir = "/var/lib/ceph/mon/$ccname-$monid";
663 -d
$mondir || die "monitor filesystem '$mondir' does not exist on this node\n";
665 die "can't remove last monitor\n" if scalar(@$monlist) <= 1;
670 # reopen with longer timeout
671 $rados = PVE
::RADOS-
>new(timeout
=> PVE
::Ceph
::Tools
::get_config
('long_rados_timeout'));
673 $rados->mon_command({ prefix
=> "mon remove", name
=> $monid, format
=> 'plain' });
675 eval { PVE
::Ceph
::Services
::ceph_service_cmd
('stop', $monsection); };
678 delete $cfg->{$monsection};
679 cfs_write_file
('ceph.conf', $cfg);
680 File
::Path
::remove_tree
($mondir);
683 if (!$param->{'exclude-manager'}) {
684 eval { PVE
::Ceph
::Services
::destroy_mgr
($mgrid) };
689 return $rpcenv->fork_worker('cephdestroymon', $monsection, $authuser, $worker);
692 __PACKAGE__-
>register_method ({
696 description
=> "Create Ceph Manager",
700 check
=> ['perm', '/', [ 'Sys.Modify' ]],
703 additionalProperties
=> 0,
705 node
=> get_standard_option
('pve-node'),
709 pattern
=> '[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?',
710 description
=> "The ID for the manager, when omitted the same as the nodename",
714 returns
=> { type
=> 'string' },
718 PVE
::Ceph
::Tools
::check_ceph_installed
('ceph_mgr');
720 PVE
::Ceph
::Tools
::check_ceph_inited
();
722 my $rpcenv = PVE
::RPCEnvironment
::get
();
724 my $authuser = $rpcenv->get_user();
726 my $mgrid = $param->{id
} // $param->{node
};
731 my $rados = PVE
::RADOS-
>new(timeout
=> PVE
::Ceph
::Tools
::get_config
('long_rados_timeout'));
733 PVE
::Ceph
::Services
::create_mgr
($mgrid, $rados);
736 return $rpcenv->fork_worker('cephcreatemgr', "mgr.$mgrid", $authuser, $worker);
739 __PACKAGE__-
>register_method ({
740 name
=> 'destroymgr',
743 description
=> "Destroy Ceph Manager.",
747 check
=> ['perm', '/', [ 'Sys.Modify' ]],
750 additionalProperties
=> 0,
752 node
=> get_standard_option
('pve-node'),
754 description
=> 'The ID of the manager',
756 pattern
=> '[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?',
760 returns
=> { type
=> 'string' },
764 my $rpcenv = PVE
::RPCEnvironment
::get
();
766 my $authuser = $rpcenv->get_user();
768 PVE
::Ceph
::Tools
::check_ceph_inited
();
770 my $mgrid = $param->{id
};
775 PVE
::Ceph
::Services
::destroy_mgr
($mgrid);
778 return $rpcenv->fork_worker('cephdestroymgr', "mgr.$mgrid", $authuser, $worker);
781 __PACKAGE__-
>register_method ({
785 description
=> "Stop ceph services.",
789 check
=> ['perm', '/', [ 'Sys.Modify' ]],
792 additionalProperties
=> 0,
794 node
=> get_standard_option
('pve-node'),
796 description
=> 'Ceph service name.',
799 default => 'ceph.target',
800 pattern
=> '(mon|mds|osd|mgr)\.[A-Za-z0-9\-]{1,32}',
804 returns
=> { type
=> 'string' },
808 my $rpcenv = PVE
::RPCEnvironment
::get
();
810 my $authuser = $rpcenv->get_user();
812 PVE
::Ceph
::Tools
::check_ceph_inited
();
814 my $cfg = cfs_read_file
('ceph.conf');
815 scalar(keys %$cfg) || die "no configuration\n";
821 if ($param->{service
}) {
822 push @$cmd, $param->{service
};
825 PVE
::Ceph
::Services
::ceph_service_cmd
(@$cmd);
828 return $rpcenv->fork_worker('srvstop', $param->{service
} || 'ceph',
832 __PACKAGE__-
>register_method ({
836 description
=> "Start ceph services.",
840 check
=> ['perm', '/', [ 'Sys.Modify' ]],
843 additionalProperties
=> 0,
845 node
=> get_standard_option
('pve-node'),
847 description
=> 'Ceph service name.',
850 default => 'ceph.target',
851 pattern
=> '(mon|mds|osd|mgr)\.[A-Za-z0-9\-]{1,32}',
855 returns
=> { type
=> 'string' },
859 my $rpcenv = PVE
::RPCEnvironment
::get
();
861 my $authuser = $rpcenv->get_user();
863 PVE
::Ceph
::Tools
::check_ceph_inited
();
865 my $cfg = cfs_read_file
('ceph.conf');
866 scalar(keys %$cfg) || die "no configuration\n";
872 if ($param->{service
}) {
873 push @$cmd, $param->{service
};
876 PVE
::Ceph
::Services
::ceph_service_cmd
(@$cmd);
879 return $rpcenv->fork_worker('srvstart', $param->{service
} || 'ceph',
883 __PACKAGE__-
>register_method ({
887 description
=> "Restart ceph services.",
891 check
=> ['perm', '/', [ 'Sys.Modify' ]],
894 additionalProperties
=> 0,
896 node
=> get_standard_option
('pve-node'),
898 description
=> 'Ceph service name.',
901 default => 'ceph.target',
902 pattern
=> '(mon|mds|osd|mgr)\.[A-Za-z0-9\-]{1,32}',
906 returns
=> { type
=> 'string' },
910 my $rpcenv = PVE
::RPCEnvironment
::get
();
912 my $authuser = $rpcenv->get_user();
914 PVE
::Ceph
::Tools
::check_ceph_inited
();
916 my $cfg = cfs_read_file
('ceph.conf');
917 scalar(keys %$cfg) || die "no configuration\n";
922 my $cmd = ['restart'];
923 if ($param->{service
}) {
924 push @$cmd, $param->{service
};
927 PVE
::Ceph
::Services
::ceph_service_cmd
(@$cmd);
930 return $rpcenv->fork_worker('srvrestart', $param->{service
} || 'ceph',
934 __PACKAGE__-
>register_method ({
938 description
=> "Get ceph status.",
942 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
945 additionalProperties
=> 0,
947 node
=> get_standard_option
('pve-node'),
950 returns
=> { type
=> 'object' },
954 PVE
::Ceph
::Tools
::check_ceph_enabled
();
956 my $rados = PVE
::RADOS-
>new();
957 my $status = $rados->mon_command({ prefix
=> 'status' });
958 $status->{health
} = $rados->mon_command({ prefix
=> 'health', detail
=> 'detail' });
962 __PACKAGE__-
>register_method ({
966 description
=> "List all pools.",
970 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
973 additionalProperties
=> 0,
975 node
=> get_standard_option
('pve-node'),
983 pool
=> { type
=> 'integer' },
984 pool_name
=> { type
=> 'string' },
985 size
=> { type
=> 'integer' },
988 links
=> [ { rel
=> 'child', href
=> "{pool_name}" } ],
993 PVE
::Ceph
::Tools
::check_ceph_inited
();
995 my $rados = PVE
::RADOS-
>new();
998 my $res = $rados->mon_command({ prefix
=> 'df' });
1000 foreach my $d (@{$res->{pools
}}) {
1001 next if !$d->{stats
};
1002 next if !defined($d->{id
});
1003 $stats->{$d->{id
}} = $d->{stats
};
1006 $res = $rados->mon_command({ prefix
=> 'osd dump' });
1007 my $rulestmp = $rados->mon_command({ prefix
=> 'osd crush rule dump'});
1010 for my $rule (@$rulestmp) {
1011 $rules->{$rule->{rule_id
}} = $rule->{rule_name
};
1015 foreach my $e (@{$res->{pools
}}) {
1017 foreach my $attr (qw(pool pool_name size min_size pg_num crush_rule)) {
1018 $d->{$attr} = $e->{$attr} if defined($e->{$attr});
1021 if (defined($d->{crush_rule
}) && defined($rules->{$d->{crush_rule
}})) {
1022 $d->{crush_rule_name
} = $rules->{$d->{crush_rule
}};
1025 if (my $s = $stats->{$d->{pool
}}) {
1026 $d->{bytes_used
} = $s->{bytes_used
};
1027 $d->{percent_used
} = $s->{percent_used
};
1036 __PACKAGE__-
>register_method ({
1037 name
=> 'createpool',
1040 description
=> "Create POOL",
1044 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1047 additionalProperties
=> 0,
1049 node
=> get_standard_option
('pve-node'),
1051 description
=> "The name of the pool. It must be unique.",
1055 description
=> 'Number of replicas per object',
1063 description
=> 'Minimum number of replicas per object',
1071 description
=> "Number of placement groups.",
1079 description
=> "The rule to use for mapping object placement in the cluster.",
1084 description
=> "The application of the pool, 'rbd' by default.",
1086 enum
=> ['rbd', 'cephfs', 'rgw'],
1090 description
=> "Configure VM and CT storage using the new pool.",
1096 returns
=> { type
=> 'string' },
1100 PVE
::Cluster
::check_cfs_quorum
();
1101 PVE
::Ceph
::Tools
::check_ceph_inited
();
1103 my $pve_ckeyring_path = PVE
::Ceph
::Tools
::get_config
('pve_ckeyring_path');
1105 die "not fully configured - missing '$pve_ckeyring_path'\n"
1106 if ! -f
$pve_ckeyring_path;
1108 my $pool = $param->{name
};
1109 my $rpcenv = PVE
::RPCEnvironment
::get
();
1110 my $user = $rpcenv->get_user();
1112 if ($param->{add_storages
}) {
1113 $rpcenv->check($user, '/storage', ['Datastore.Allocate']);
1114 die "pool name contains characters which are illegal for storage naming\n"
1115 if !PVE
::JSONSchema
::parse_storage_id
($pool);
1118 my $pg_num = $param->{pg_num
} || 128;
1119 my $size = $param->{size
} || 3;
1120 my $min_size = $param->{min_size
} || 2;
1121 my $application = $param->{application
} // 'rbd';
1125 PVE
::Ceph
::Tools
::create_pool
($pool, $param);
1127 if ($param->{add_storages
}) {
1129 eval { $add_storage->($pool, "${pool}"); };
1131 warn "failed to add storage: $@";
1134 die "adding storage for pool '$pool' failed, check log and add manually!\n"
1139 return $rpcenv->fork_worker('cephcreatepool', $pool, $user, $worker);
1142 __PACKAGE__-
>register_method ({
1143 name
=> 'get_flags',
1146 description
=> "get all set ceph flags",
1150 check
=> ['perm', '/', [ 'Sys.Audit' ]],
1153 additionalProperties
=> 0,
1155 node
=> get_standard_option
('pve-node'),
1158 returns
=> { type
=> 'string' },
1162 PVE
::Ceph
::Tools
::check_ceph_inited
();
1164 my $pve_ckeyring_path = PVE
::Ceph
::Tools
::get_config
('pve_ckeyring_path');
1166 die "not fully configured - missing '$pve_ckeyring_path'\n"
1167 if ! -f
$pve_ckeyring_path;
1169 my $rados = PVE
::RADOS-
>new();
1171 my $stat = $rados->mon_command({ prefix
=> 'osd dump' });
1173 return $stat->{flags
} // '';
1176 __PACKAGE__-
>register_method ({
1178 path
=> 'flags/{flag}',
1180 description
=> "Set a ceph flag",
1184 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1187 additionalProperties
=> 0,
1189 node
=> get_standard_option
('pve-node'),
1191 description
=> 'The ceph flag to set/unset',
1193 enum
=> [ 'full', 'pause', 'noup', 'nodown', 'noout', 'noin', 'nobackfill', 'norebalance', 'norecover', 'noscrub', 'nodeep-scrub', 'notieragent'],
1197 returns
=> { type
=> 'null' },
1201 PVE
::Ceph
::Tools
::check_ceph_inited
();
1203 my $pve_ckeyring_path = PVE
::Ceph
::Tools
::get_config
('pve_ckeyring_path');
1205 die "not fully configured - missing '$pve_ckeyring_path'\n"
1206 if ! -f
$pve_ckeyring_path;
1208 my $set = $param->{set
} // !$param->{unset
};
1209 my $rados = PVE
::RADOS-
>new();
1211 $rados->mon_command({
1212 prefix
=> "osd set",
1213 key
=> $param->{flag
},
1219 __PACKAGE__-
>register_method ({
1220 name
=> 'unset_flag',
1221 path
=> 'flags/{flag}',
1223 description
=> "Unset a ceph flag",
1227 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1230 additionalProperties
=> 0,
1232 node
=> get_standard_option
('pve-node'),
1234 description
=> 'The ceph flag to set/unset',
1236 enum
=> [ 'full', 'pause', 'noup', 'nodown', 'noout', 'noin', 'nobackfill', 'norebalance', 'norecover', 'noscrub', 'nodeep-scrub', 'notieragent'],
1240 returns
=> { type
=> 'null' },
1244 PVE
::Ceph
::Tools
::check_ceph_inited
();
1246 my $pve_ckeyring_path = PVE
::Ceph
::Tools
::get_config
('pve_ckeyring_path');
1248 die "not fully configured - missing '$pve_ckeyring_path'\n"
1249 if ! -f
$pve_ckeyring_path;
1251 my $set = $param->{set
} // !$param->{unset
};
1252 my $rados = PVE
::RADOS-
>new();
1254 $rados->mon_command({
1255 prefix
=> "osd unset",
1256 key
=> $param->{flag
},
1262 __PACKAGE__-
>register_method ({
1263 name
=> 'destroypool',
1264 path
=> 'pools/{name}',
1266 description
=> "Destroy pool",
1270 check
=> ['perm', '/', [ 'Sys.Modify' ]],
1273 additionalProperties
=> 0,
1275 node
=> get_standard_option
('pve-node'),
1277 description
=> "The name of the pool. It must be unique.",
1281 description
=> "If true, destroys pool even if in use",
1286 remove_storages
=> {
1287 description
=> "Remove all pveceph-managed storages configured for this pool",
1294 returns
=> { type
=> 'string' },
1298 PVE
::Ceph
::Tools
::check_ceph_inited
();
1300 my $rpcenv = PVE
::RPCEnvironment
::get
();
1301 my $user = $rpcenv->get_user();
1302 $rpcenv->check($user, '/storage', ['Datastore.Allocate'])
1303 if $param->{remove_storages
};
1305 my $pool = $param->{name
};
1308 my $storages = $get_storages->($pool);
1310 # if not forced, destroy ceph pool only when no
1311 # vm disks are on it anymore
1312 if (!$param->{force
}) {
1313 my $storagecfg = PVE
::Storage
::config
();
1314 foreach my $storeid (keys %$storages) {
1315 my $storage = $storages->{$storeid};
1317 # check if any vm disks are on the pool
1318 print "checking storage '$storeid' for RBD images..\n";
1319 my $res = PVE
::Storage
::vdisk_list
($storagecfg, $storeid);
1320 die "ceph pool '$pool' still in use by storage '$storeid'\n"
1321 if @{$res->{$storeid}} != 0;
1325 PVE
::Ceph
::Tools
::destroy_pool
($pool);
1327 if ($param->{remove_storages
}) {
1329 foreach my $storeid (keys %$storages) {
1330 # skip external clusters, not managed by pveceph
1331 next if $storages->{$storeid}->{monhost
};
1332 eval { PVE
::API2
::Storage
::Config-
>delete({storage
=> $storeid}) };
1334 warn "failed to remove storage '$storeid': $@\n";
1338 die "failed to remove (some) storages - check log and remove manually!\n"
1342 return $rpcenv->fork_worker('cephdestroypool', $pool, $user, $worker);
1346 __PACKAGE__-
>register_method ({
1350 description
=> "Get OSD crush map",
1354 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
1357 additionalProperties
=> 0,
1359 node
=> get_standard_option
('pve-node'),
1362 returns
=> { type
=> 'string' },
1366 PVE
::Ceph
::Tools
::check_ceph_inited
();
1368 # this produces JSON (difficult to read for the user)
1369 # my $txt = &$run_ceph_cmd_text(['osd', 'crush', 'dump'], quiet => 1);
1373 my $mapfile = "/var/tmp/ceph-crush.map.$$";
1374 my $mapdata = "/var/tmp/ceph-crush.txt.$$";
1376 my $rados = PVE
::RADOS-
>new();
1379 my $bindata = $rados->mon_command({ prefix
=> 'osd getcrushmap', format
=> 'plain' });
1380 file_set_contents
($mapfile, $bindata);
1381 run_command
(['crushtool', '-d', $mapfile, '-o', $mapdata]);
1382 $txt = file_get_contents
($mapdata);
1394 __PACKAGE__-
>register_method({
1398 description
=> "Read ceph log",
1401 check
=> ['perm', '/nodes/{node}', [ 'Sys.Syslog' ]],
1405 additionalProperties
=> 0,
1407 node
=> get_standard_option
('pve-node'),
1426 description
=> "Line number",
1430 description
=> "Line text",
1439 PVE
::Ceph
::Tools
::check_ceph_inited
();
1441 my $rpcenv = PVE
::RPCEnvironment
::get
();
1442 my $user = $rpcenv->get_user();
1443 my $node = $param->{node
};
1445 my $logfile = "/var/log/ceph/ceph.log";
1446 my ($count, $lines) = PVE
::Tools
::dump_logfile
($logfile, $param->{start
}, $param->{limit
});
1448 $rpcenv->set_result_attrib('total', $count);
1453 __PACKAGE__-
>register_method ({
1457 description
=> "List ceph rules.",
1461 check
=> ['perm', '/', [ 'Sys.Audit', 'Datastore.Audit' ], any
=> 1],
1464 additionalProperties
=> 0,
1466 node
=> get_standard_option
('pve-node'),
1475 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
1480 PVE
::Ceph
::Tools
::check_ceph_inited
();
1482 my $rados = PVE
::RADOS-
>new();
1484 my $rules = $rados->mon_command({ prefix
=> 'osd crush rule ls' });
1488 foreach my $rule (@$rules) {
1489 push @$res, { name
=> $rule };