Skip to content

Commit

Permalink
Merge pull request #1971 from phillxnet/1700_Implement_a_delete_missi…
Browse files Browse the repository at this point in the history
…ng_disk_in_pool_UI

Implement a delete missing disk in pool UI #1700
  • Loading branch information
schakrava authored Oct 5, 2018
2 parents e79e17d + 127dab1 commit ccbd952
Show file tree
Hide file tree
Showing 13 changed files with 233 additions and 58 deletions.
25 changes: 19 additions & 6 deletions src/rockstor/fs/btrfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ def get_pool_info(disk, root_pool=False):
o, e, rc = run_command(cmd)
pool_info = {'disks': [], 'hasMissingDev': False, 'fullDevCount': 0,
'missingDevCount': 0}
# TODO: Move 'disks' to dictionary with (devid, size, used) tuple values
full_dev_count = 0 # Number of devices in non degraded state.
attached_dev_count = 0 # Number of currently attached devices.
for l in o:
Expand Down Expand Up @@ -321,7 +322,7 @@ def resize_pool(pool, dev_list_byid, add=True):
(when adding) or are already members of the pool (when deleting).
If any device in the supplied dev_list fails this test then no command is
executed and None is returned.
:param pool: btrfs pool name
:param pool: btrfs pool object
:param dev_list_byid: list of devices to add/delete in by-id (without
path).
:param add: when true (default) or not specified then attempt to add
Expand All @@ -330,23 +331,35 @@ def resize_pool(pool, dev_list_byid, add=True):
:return: Tuple of results from run_command(generated command) or None if
the device member/pool sanity check fails.
"""
dev_list_byid = [get_device_path(d) for d in dev_list_byid]
if pool.has_missing_dev and not add:
if dev_list_byid == []:
dev_list_byid = ['missing']
else:
# list has at least a single element
# substiture 'missing' for any member matching 'detached-'
dev_list_byid = [
'missing' if re.match('detached-', dev) is not None else
get_device_path(dev) for dev in dev_list_byid]
else:
dev_list_byid = [get_device_path(dev) for dev in dev_list_byid]
root_mnt_pt = mount_root(pool)
cur_dev = cur_devices(root_mnt_pt)
resize_flag = 'add'
if (not add):
resize_flag = 'delete'
resize_cmd = [BTRFS, 'device', resize_flag, ]
# Until we verify that all devices are or are not already members of the
# given pools depending on if we are adding (default) or removing
# (add=False) those devices we set our resize flag to false.
# given pool, depending on if we are adding (default) or removing
# (add=False), we set our resize flag to false.
resize = False
for d in dev_list_byid:
if (((resize_flag == 'add' and (d not in cur_dev)) or
(resize_flag == 'delete' and (d in cur_dev)))):
if (resize_flag == 'add' and (d not in cur_dev)) or \
(resize_flag == 'delete' and ((d in cur_dev) or
d == 'missing')):
resize = True # Basic disk member of pool sanity check passed.
resize_cmd.append(d)
if (not resize):
logger.debug('Note: resize_pool() taking no action.')
return None
resize_cmd.append(root_mnt_pt)
return run_command(resize_cmd)
Expand Down
4 changes: 2 additions & 2 deletions src/rockstor/storageadmin/models/disk.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,14 @@ def apm_level(self, *args, **kwargs):
def io_error_stats(self, *args, **kwargs):
# json charfield format
try:
return get_dev_io_error_stats(str(self.name))
return get_dev_io_error_stats(str(self.target_name))
except:
return None

@property
def temp_name(self, *args, **kwargs):
try:
return get_dev_temp_name(str(self.name))
return get_dev_temp_name(str(self.target_name))
except:
return None

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
{{/if}}
</td>
<td>{{humanReadableSize this.size}}</td>
<td><input type="checkbox" name="diskname" id="{{this.name}}" value="{{this.name}}" class="diskname"></td>
<td><input type="checkbox" name="diskid" id="{{this.id}}" value='{"id": {{this.id}}, "name": "{{this.name}}"}' class="diskid"></td>
{{/each}}
</tbody>
</table>
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,13 @@
<tr>
<td><a href="#disks/{{this.id}}"><i class="glyphicon glyphicon-hdd"></i>&nbsp;{{this.name}}</a>
{{#if this.offline}}
<a href="#" class="delete" data-disk-id="{{this.id}}" title="Disk is unusable because it is detached.
Click to delete it from the system if it is not to be reattached." rel="tooltip"><i class="glyphicon glyphicon-trash"></i></a>
{{#if this.pool_name}}
<i class="glyphicon glyphicon-map-marker" title="Drive is a detached member of a Rockstor managed Pool." rel="tooltip"></i>
<a href="#pools/{{this.pool}}"><i class="glyphicon glyphicon-exclamation-sign" title="Use linked Pool page Resize / ReRaid 'Remove disks' option if no reattachment is planned." rel="tooltip"></i></a>
{{else}}
<a href="#" class="delete" data-disk-id="{{this.id}}" title="Disk is unusable because it is detached.
Click to delete it from the system if it is not to be reattached." rel="tooltip"><i class="glyphicon glyphicon-trash"></i></a>
{{/if}}
{{else if (isRootDevice this.role)}}
<i class="glyphicon glyphicon-registration-mark" title="Rockstor System Drive." rel="tooltip"></i>
{{else if (isLuksContainer this.role)}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
<br>
<h4>Select an operation to perform on {{display_poolName}}</h4>
<ol>
<li><h4><a href="#" id="change-raid">Modify RAID level</a></h4></li>
<li><h4><a href="#" id="add-disks">Add disks</a></h4></li>
<li><h4><a href="#" id="change-raid">Modify RAID level only</a></h4></li>
<li><h4><a href="#" id="add-disks">Add disks (optional RAID change)</a></h4></li>
<li><h4><a href="#" id="remove-disks">Remove disks</a></h4></li>
<li><h4>Replace existing disk (planned option)</h4></li>
</ol>

Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
</div>
<br>
<div class="alert alert-success">
<h4>Resize is initiated. A balance process is kicked off to redistribute data. It could take a while. You can check the status in the Balances tab. Its finish marks the success of resize.</h4>
<h4>Resize initiated - disk addition or raid change entails a subsequent Rockstor visible balance which may take several hours. Check status in the Balances tab. Disk delete progress is currently unmonitored.</h4>
</div>

Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,32 @@
{{/unless}}
</h3>

{{#if pool.has_missing_dev}}
<h4><u>Maintenance required</u></h4>
{{#unless (isDegradedRw pool.mount_status)}}
Missing disk removal requires <strong>degraded,rw</strong> mount options.
{{/unless}}
{{#if pool.is_mounted}}
{{#if (isWritable pool.mount_status)}}
{{#if (isDegradedRw pool.mount_status)}}
<a href="#" class="js-delete-missing" data-pool-id="{{pool.id}}" title="If detached members listed use - Resize/ReRaid 'Remove disks' - instead." rel="tooltip">
<i class="glyphicon glyphicon-erase"></i> Delete a missing disk if pool has no detached members.</a><br>
<strong>Header "Pool Degraded Alert" persists during delete process (can take several hours).</strong><br>
{{else}}
Consider <strong>degraded,ro</strong> to refresh backups first.<br>
{{/if}}
{{else}}
Pool is read only (<strong>ro</strong>).<br> Refresh backups before using <strong>degraded,rw</strong>.
{{/if}}
Active 'degraded' option is sticky: once unset a reboot is required to deactivate it.<br>
{{else}}
Pool is currently (<strong>unmounted</strong>).<br>
Consider <strong>degraded,ro</strong> to refresh backups first.<br>
{{/if}}
Reload page to refresh active mount options.
<br><br>
{{/if}}

<i>Cumulative pool errors per device - 'btrfs dev stats -z /mnt2/{{pool.name}}'
to reset.</i>

Expand All @@ -15,6 +41,7 @@
<thead>
<tr>
<th scope="col" abbr="Name">Name</th>
<th scope="col" abbr="Temp Name">Temp Name</th>
<th scope="col" abbr="Capacity">Capacity</th>
<th scope="col" abbr="write_io_errs">Write I/O errors</th>
<th scope="col" abbr="read_io_errs">Read I/O errors</th>
Expand All @@ -36,6 +63,9 @@
<i class="glyphicon glyphicon-eye-open"></i></a>
{{/if}}
</td>
<td>
{{this.temp_name}}
</td>
<td>{{humanReadableSize this.size}}</td>
{{ioErrorStatsTableData this.io_error_stats}}
</tr>
Expand All @@ -58,6 +88,9 @@
<i class="glyphicon glyphicon-eye-open"></i></a>
{{/if}}
</td>
<td>
{{this.temp_name}}
</td>
<td>{{humanReadableSize this.size}}</td>
{{ioErrorStatsTableData this.io_error_stats}}
</tr>
Expand All @@ -67,4 +100,5 @@
</table>

<a id="js-resize-pool" class="btn btn-primary" href="#">
<i class="glyphicon glyphicon-edit "></i> Resize Pool</a></br>
<i class="glyphicon glyphicon-edit "></i> Resize/ReRaid Pool</a></br>

Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ PoolAddDisks = RockstorWizardPage.extend({

events: {
'click #checkAll': 'selectAllCheckboxes',
'click [class="diskname"]': 'clickCheckbox'
'click [class="diskid"]': 'clickCheckbox'
},

initialize: function () {
Expand Down Expand Up @@ -95,12 +95,12 @@ PoolAddDisks = RockstorWizardPage.extend({
return $.Deferred().reject();
}
var _this = this;
var checked = this.$('.diskname:checked').length;
var diskNames = [];
this.$('.diskname:checked').each(function (i) {
diskNames.push($(this).val());
var checked = this.$('.diskid:checked').length;
var diskIds = [];
this.$('.diskid:checked').each(function (i) {
diskIds.push($(this).val());
});
this.model.set('diskNames', diskNames);
this.model.set('diskIds', diskIds);
if (this.model.get('raidChange')) {
this.model.set('raidLevel', this.$('#raid-level').val());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
PoolRemoveDisks = RockstorWizardPage.extend({
events: {
'click #checkAll': 'selectAllCheckboxes',
'click [class="diskname"]': 'clickCheckbox'
'click [class="diskid"]': 'clickCheckbox'
},

initialize: function() {
Expand Down Expand Up @@ -74,12 +74,12 @@ PoolRemoveDisks = RockstorWizardPage.extend({

save: function() {
var _this = this;
var checked = this.$('.diskname:checked').length;
var diskNames = [];
this.$('.diskname:checked').each(function(i) {
diskNames.push($(this).val());
var checked = this.$('.diskid:checked').length;
var diskIds = [];
this.$('.diskid:checked').each(function(i) {
diskIds.push($(this).val());
});
this.model.set('diskNames', diskNames);
this.model.set('diskIds', diskIds);
return $.Deferred().resolve();
},

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,41 @@ PoolResizeSummary = RockstorWizardPage.extend({
this.template = window.JST.pool_resize_summary;
var choice = this.model.get('choice');
var raidLevel = null;
var poolDisks = _.map(this.model.get('pool').get('disks'), function(disk) {
return disk.name;
var diskIdtoNameMap = new Map();
// Extract the selected disks ids and names.
var selectedDiskIds = [];
if (!_.isUndefined(this.model.get('diskIds'))) {
this.model.get('diskIds').forEach(function (diskInfo) {
var diskInfoAsObj = JSON.parse(diskInfo);
diskIdtoNameMap.set(diskInfoAsObj.id, diskInfoAsObj.name);
selectedDiskIds.push(diskInfoAsObj.id);
});
}
this.model.set('selectedDiskIds', selectedDiskIds);
var poolDiskIds = _.map(this.model.get('pool').get('disks'), function(disk) {
diskIdtoNameMap.set(disk.id, disk.name);
return disk.id;
});
this.model.set('diskIdtoNameMap', diskIdtoNameMap)
if (choice == 'add') {
this.newRaidLevel = this.model.get('raidChange') ? this.model.get('raidLevel') :
this.model.get('pool').get('raid');
this.newDisks = _.union(poolDisks, this.model.get('diskNames'));
this.proposedDiskIds = _.union(poolDiskIds, selectedDiskIds);
} else if (choice == 'remove') {
this.newRaidLevel = this.model.get('pool').get('raid');
this.newDisks = _.difference(poolDisks, this.model.get('diskNames'));
this.proposedDiskIds = _.difference(poolDiskIds, selectedDiskIds);
} else if (choice == 'raid') {
this.newRaidLevel = this.model.get('raidLevel');
this.newDisks = _.union(poolDisks, this.model.get('diskNames'));
this.proposedDiskIds = _.union(poolDiskIds, selectedDiskIds);
}
// Retrieve proposed disk names from proposedDiskIds
var proposedDisksByName = [];
this.proposedDiskIds.forEach(function(diskId) {
proposedDisksByName.push(diskIdtoNameMap.get(diskId))
});

this.newDisks = proposedDisksByName;

RockstorWizardPage.prototype.initialize.apply(this, arguments);
this.initHandlebarHelpers();
},
Expand Down Expand Up @@ -74,7 +95,7 @@ PoolResizeSummary = RockstorWizardPage.extend({
dataType: 'json',
contentType: 'application/json',
data: JSON.stringify({
'disks': this.model.get('diskNames'),
'disks': this.model.get('selectedDiskIds'),
'raid_level': raidLevel
}),
success: function() {
Expand All @@ -89,7 +110,7 @@ PoolResizeSummary = RockstorWizardPage.extend({
dataType: 'json',
contentType: 'application/json',
data: JSON.stringify({
'disks': this.model.get('diskNames'),
'disks': this.model.get('selectedDiskIds'),
'raid_level': this.model.get('pool').get('raid')
}),
success: function() {
Expand All @@ -104,7 +125,7 @@ PoolResizeSummary = RockstorWizardPage.extend({
dataType: 'json',
contentType: 'application/json',
data: JSON.stringify({
'disks': this.model.get('diskNames'),
'disks': this.model.get('selectedDiskIds'),
'raid_level': this.model.get('raidLevel')
}),
success: function() {
Expand All @@ -124,6 +145,8 @@ PoolResizeSummary = RockstorWizardPage.extend({
return new Handlebars.SafeString(html);
});

// potentially a display_diskSet_proposed handlebars helper

Handlebars.registerHelper('display_breadCrumbs', function() {
var html = '';
if (this.model.get('choice') == 'add') {
Expand Down
Loading

0 comments on commit ccbd952

Please sign in to comment.