Commit 01777b31 authored by Dietmar Maurer's avatar Dietmar Maurer

ceph: add API and buttons for osd in/out

And move OSD API into separate package PVE::API2::CephOSD
parent 33c1150f
This diff is collapsed.
...@@ -6,8 +6,9 @@ use File::Basename; ...@@ -6,8 +6,9 @@ use File::Basename;
use File::Path; use File::Path;
use POSIX qw (LONG_MAX); use POSIX qw (LONG_MAX);
use Cwd qw(abs_path); use Cwd qw(abs_path);
use IO::Dir;
use PVE::Tools; use PVE::Tools qw(extract_param run_command file_get_contents file_read_firstline dir_glob_regex dir_glob_foreach);
my $ccname = 'ceph'; # ceph cluster name my $ccname = 'ceph'; # ceph cluster name
my $ceph_cfgdir = "/etc/ceph"; my $ceph_cfgdir = "/etc/ceph";
...@@ -28,6 +29,7 @@ my $config_hash = { ...@@ -28,6 +29,7 @@ my $config_hash = {
pve_ckeyring_path => $pve_ckeyring_path, pve_ckeyring_path => $pve_ckeyring_path,
ceph_bootstrap_osd_keyring => $ceph_bootstrap_osd_keyring, ceph_bootstrap_osd_keyring => $ceph_bootstrap_osd_keyring,
ceph_bootstrap_mds_keyring => $ceph_bootstrap_mds_keyring, ceph_bootstrap_mds_keyring => $ceph_bootstrap_mds_keyring,
long_rados_timeout => 60,
}; };
sub get_config { sub get_config {
...@@ -187,4 +189,147 @@ sub ceph_service_cmd { ...@@ -187,4 +189,147 @@ sub ceph_service_cmd {
PVE::Tools::run_command(['service', 'ceph', '-c', $pve_ceph_cfgpath, @_]); PVE::Tools::run_command(['service', 'ceph', '-c', $pve_ceph_cfgpath, @_]);
} }
sub list_disks {
my $disklist = {};
my $fd = IO::File->new("/proc/mounts", "r") ||
die "unable to open /proc/mounts - $!\n";
my $mounted = {};
while (defined(my $line = <$fd>)) {
my ($dev, $path, $fstype) = split(/\s+/, $line);
next if !($dev && $path && $fstype);
next if $dev !~ m|^/dev/|;
my $real_dev = abs_path($dev);
$mounted->{$real_dev} = $path;
}
close($fd);
my $dev_is_mounted = sub {
my ($dev) = @_;
return $mounted->{$dev};
};
my $dir_is_epmty = sub {
my ($dir) = @_;
my $dh = IO::Dir->new ($dir);
return 1 if !$dh;
while (defined(my $tmp = $dh->read)) {
next if $tmp eq '.' || $tmp eq '..';
$dh->close;
return 0;
}
$dh->close;
return 1;
};
my $journal_uuid = '45b0969e-9b03-4f30-b4c6-b4b80ceff106';
my $journalhash = {};
dir_glob_foreach('/dev/disk/by-parttypeuuid', "$journal_uuid\..+", sub {
my ($entry) = @_;
my $real_dev = abs_path("/dev/disk/by-parttypeuuid/$entry");
$journalhash->{$real_dev} = 1;
});
dir_glob_foreach('/sys/block', '.*', sub {
my ($dev) = @_;
return if $dev eq '.';
return if $dev eq '..';
return if $dev =~ m|^ram\d+$|; # skip ram devices
return if $dev =~ m|^loop\d+$|; # skip loop devices
return if $dev =~ m|^md\d+$|; # skip md devices
return if $dev =~ m|^dm-.*$|; # skip dm related things
return if $dev =~ m|^fd\d+$|; # skip Floppy
return if $dev =~ m|^sr\d+$|; # skip CDs
my $devdir = "/sys/block/$dev/device";
return if ! -d $devdir;
my $size = file_read_firstline("/sys/block/$dev/size");
return if !$size;
$size = $size * 512;
my $info = `udevadm info --path /sys/block/$dev --query all`;
return if !$info;
return if $info !~ m/^E: DEVTYPE=disk$/m;
return if $info =~ m/^E: ID_CDROM/m;
my $serial = 'unknown';
if ($info =~ m/^E: ID_SERIAL_SHORT=(\S+)$/m) {
$serial = $1;
}
my $gpt = 0;
if ($info =~ m/^E: ID_PART_TABLE_TYPE=gpt$/m) {
$gpt = 1;
}
# detect SSD (fixme - currently only works for ATA disks)
my $rpm = 7200; # default guess
if ($info =~ m/^E: ID_ATA_ROTATION_RATE_RPM=(\d+)$/m) {
$rpm = $1;
}
my $vendor = file_read_firstline("$devdir/vendor") || 'unknown';
my $model = file_read_firstline("$devdir/model") || 'unknown';
my $used;
$used = 'LVM' if !&$dir_is_epmty("/sys/block/$dev/holders");
$used = 'mounted' if &$dev_is_mounted("/dev/$dev");
$disklist->{$dev} = {
vendor => $vendor,
model => $model,
size => $size,
serial => $serial,
gpt => $gpt,
rmp => $rpm,
};
my $osdid = -1;
my $journal_count = 0;
my $found_partitions;
my $found_lvm;
my $found_mountpoints;
dir_glob_foreach("/sys/block/$dev", "$dev.+", sub {
my ($part) = @_;
$found_partitions = 1;
if (my $mp = &$dev_is_mounted("/dev/$part")) {
$found_mountpoints = 1;
if ($mp =~ m|^/var/lib/ceph/osd/ceph-(\d+)$|) {
$osdid = $1;
}
}
if (!&$dir_is_epmty("/sys/block/$dev/$part/holders")) {
$found_lvm = 1;
}
$journal_count++ if $journalhash->{"/dev/$part"};
});
$used = 'mounted' if $found_mountpoints && !$used;
$used = 'LVM' if $found_lvm && !$used;
$used = 'partitions' if $found_partitions && !$used;
$disklist->{$dev}->{used} = $used if $used;
$disklist->{$dev}->{osdid} = $osdid;
$disklist->{$dev}->{journals} = $journal_count;
});
return $disklist;
}
1; 1;
...@@ -124,8 +124,8 @@ my $cmddef = { ...@@ -124,8 +124,8 @@ my $cmddef = {
}], }],
createpool => [ 'PVE::API2::Ceph', 'createpool', ['name'], { node => $nodename }], createpool => [ 'PVE::API2::Ceph', 'createpool', ['name'], { node => $nodename }],
destroypool => [ 'PVE::API2::Ceph', 'destroypool', ['name'], { node => $nodename } ], destroypool => [ 'PVE::API2::Ceph', 'destroypool', ['name'], { node => $nodename } ],
createosd => [ 'PVE::API2::Ceph', 'createosd', ['dev'], { node => $nodename }, $upid_exit], createosd => [ 'PVE::API2::CephOSD', 'createosd', ['dev'], { node => $nodename }, $upid_exit],
destroyosd => [ 'PVE::API2::Ceph', 'destroyosd', ['osdid'], { node => $nodename }, $upid_exit], destroyosd => [ 'PVE::API2::CephOSD', 'destroyosd', ['osdid'], { node => $nodename }, $upid_exit],
createmon => [ 'PVE::API2::Ceph', 'createmon', [], { node => $nodename }, $upid_exit], createmon => [ 'PVE::API2::Ceph', 'createmon', [], { node => $nodename }, $upid_exit],
destroymon => [ 'PVE::API2::Ceph', 'destroymon', ['monid'], { node => $nodename }, $upid_exit], destroymon => [ 'PVE::API2::Ceph', 'destroymon', ['monid'], { node => $nodename }, $upid_exit],
start => [ 'PVE::API2::Ceph', 'start', ['service'], { node => $nodename }, $upid_exit], start => [ 'PVE::API2::Ceph', 'start', ['service'], { node => $nodename }, $upid_exit],
......
...@@ -292,6 +292,42 @@ Ext.define('PVE.node.CephOsdTree', { ...@@ -292,6 +292,42 @@ Ext.define('PVE.node.CephOsdTree', {
var sm = Ext.create('Ext.selection.TreeModel', {}); var sm = Ext.create('Ext.selection.TreeModel', {});
var set_button_status; // defined later
var reload = function() {
PVE.Utils.API2Request({
url: "/nodes/" + nodename + "/ceph/osd",
waitMsgTarget: me,
method: 'GET',
failure: function(response, opts) {
PVE.Utils.setErrorMask(me, response.htmlStatus);
},
success: function(response, opts) {
sm.deselectAll();
me.setRootNode(response.result.data.root);
me.expandAll();
set_button_status();
}
});
};
var osd_cmd = function(cmd) {
var rec = sm.getSelection()[0];
if (!(rec && (rec.data.id >= 0) && rec.data.host)) {
return;
}
PVE.Utils.API2Request({
url: "/nodes/" + rec.data.host + "/ceph/osd/" +
rec.data.id + '/' + cmd,
waitMsgTarget: me,
method: 'POST',
success: reload,
failure: function(response, opts) {
Ext.Msg.alert(gettext('Error'), response.htmlStatus);
}
});
};
var service_cmd = function(cmd) { var service_cmd = function(cmd) {
var rec = sm.getSelection()[0]; var rec = sm.getSelection()[0];
if (!(rec && rec.data.name && rec.data.host)) { if (!(rec && rec.data.name && rec.data.host)) {
...@@ -302,6 +338,7 @@ Ext.define('PVE.node.CephOsdTree', { ...@@ -302,6 +338,7 @@ Ext.define('PVE.node.CephOsdTree', {
params: { service: rec.data.name }, params: { service: rec.data.name },
waitMsgTarget: me, waitMsgTarget: me,
method: 'POST', method: 'POST',
success: reload,
failure: function(response, opts) { failure: function(response, opts) {
Ext.Msg.alert(gettext('Error'), response.htmlStatus); Ext.Msg.alert(gettext('Error'), response.htmlStatus);
} }
...@@ -320,6 +357,18 @@ Ext.define('PVE.node.CephOsdTree', { ...@@ -320,6 +357,18 @@ Ext.define('PVE.node.CephOsdTree', {
handler: function(){ service_cmd('stop'); } handler: function(){ service_cmd('stop'); }
}); });
var osd_out_btn = new Ext.Button({
text: 'Out',
disabled: true,
handler: function(){ osd_cmd('out'); }
});
var osd_in_btn = new Ext.Button({
text: 'In',
disabled: true,
handler: function(){ osd_cmd('in'); }
});
var remove_btn = new Ext.Button({ var remove_btn = new Ext.Button({
text: gettext('Remove'), text: gettext('Remove'),
disabled: true, disabled: true,
...@@ -334,16 +383,19 @@ Ext.define('PVE.node.CephOsdTree', { ...@@ -334,16 +383,19 @@ Ext.define('PVE.node.CephOsdTree', {
osdid: rec.data.id osdid: rec.data.id
}); });
win.show(); win.show();
me.mon(win, 'close', reload, me);
} }
}); });
var set_button_status = function() { set_button_status = function() {
var rec = sm.getSelection()[0]; var rec = sm.getSelection()[0];
if (!rec) { if (!rec) {
start_btn.setDisabled(true); start_btn.setDisabled(true);
stop_btn.setDisabled(true); stop_btn.setDisabled(true);
remove_btn.setDisabled(true); remove_btn.setDisabled(true);
osd_out_btn.setDisabled(true);
osd_in_btn.setDisabled(true);
return; return;
} }
...@@ -352,37 +404,23 @@ Ext.define('PVE.node.CephOsdTree', { ...@@ -352,37 +404,23 @@ Ext.define('PVE.node.CephOsdTree', {
start_btn.setDisabled(!(isOsd && (rec.data.status !== 'up'))); start_btn.setDisabled(!(isOsd && (rec.data.status !== 'up')));
stop_btn.setDisabled(!(isOsd && (rec.data.status !== 'down'))); stop_btn.setDisabled(!(isOsd && (rec.data.status !== 'down')));
remove_btn.setDisabled(!(isOsd && (rec.data.status === 'down'))); remove_btn.setDisabled(!(isOsd && (rec.data.status === 'down')));
osd_out_btn.setDisabled(!(isOsd && rec.data['in']));
osd_in_btn.setDisabled(!(isOsd && !rec.data['in']));
}; };
sm.on('selectionchange', set_button_status); sm.on('selectionchange', set_button_status);
var reload = function() {
PVE.Utils.API2Request({
url: "/nodes/" + nodename + "/ceph/osd",
waitMsgTarget: me,
method: 'GET',
failure: function(response, opts) {
PVE.Utils.setErrorMask(me, response.htmlStatus);
},
success: function(response, opts) {
sm.deselectAll();
me.setRootNode(response.result.data.root);
me.expandAll();
set_button_status();
}
});
};
var reload_btn = new Ext.Button({ var reload_btn = new Ext.Button({
text: gettext('Reload'), text: gettext('Reload'),
handler: reload handler: reload
}); });
Ext.apply(me, { Ext.apply(me, {
tbar: [ reload_btn, start_btn, stop_btn, remove_btn ], tbar: [ reload_btn, start_btn, stop_btn, osd_out_btn, osd_in_btn, remove_btn ],
rootVisible: false, rootVisible: false,
fields: ['name', 'type', 'status', 'host', fields: ['name', 'type', 'status', 'host', 'in',
{ type: 'integre', name: 'id' }, { type: 'integer', name: 'id' },
{ type: 'number', name: 'reweight' }, { type: 'number', name: 'reweight' },
{ type: 'number', name: 'crush_weight' }], { type: 'number', name: 'crush_weight' }],
stateful: false, stateful: false,
...@@ -416,6 +454,13 @@ Ext.define('PVE.node.CephOsdTree', { ...@@ -416,6 +454,13 @@ Ext.define('PVE.node.CephOsdTree', {
text: 'Status', text: 'Status',
dataIndex: 'status', dataIndex: 'status',
align: 'right', align: 'right',
renderer: function(value, metaData, rec) {
if (!value) {
return value;
}
var data = rec.data;
return value + '/' + (data['in'] ? 'in' : 'out');
},
width: 100 width: 100
}, },
{ {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment