Permalink
Browse files

OS-1684 fix vmadm display of delegated dataset when mountpoint has be…

…en changed

OS-1633 add support for 'snapshots' listing in VM objects in VM.js
smartos-live#133 fixed issue deleting KVM VMs with snapshots (thanks calmh)
  • Loading branch information...
1 parent 41b9446 commit 3ff74a3cee5cd5d7e5f2f1075b89b3ee6c5aa371 @joshwilsdon joshwilsdon committed Nov 9, 2012
Showing with 515 additions and 33 deletions.
  1. +118 −33 src/vm/node_modules/VM.js
  2. +48 −0 src/vm/tests/test-blocksizes.js
  3. +1 −0 src/vm/tests/test-defaults.js
  4. +348 −0 src/vm/tests/test-snapshots.js
@@ -58,6 +58,7 @@
// Ensure we're using the platform's node
require('/usr/node/node_modules/platform_node_version').assert();
+var assert = require('assert');
var async = require('/usr/node/node_modules/async');
var bunyan = require('/usr/node/node_modules/bunyan');
var cp = require('child_process');
@@ -1531,8 +1532,9 @@ function loadDatasetInfo(m, callback)
{
var args;
- args = ['list', '-H', '-p', '-t', 'volume,filesystem', '-o',
- 'name,quota,volsize,mountpoint,type,compression,recsize,volblocksize'];
+ args = ['list', '-H', '-p', '-t', 'volume,filesystem,snapshot', '-o',
+ 'name,quota,volsize,mountpoint,type,compression,recsize,'
+ + 'volblocksize,zoned'];
if (m && m.hasOwnProperty('zonepath')) {
args.push(m.zonepath);
@@ -1544,20 +1546,24 @@ function loadDatasetInfo(m, callback)
var dataset;
var dsinfo = {};
var fields;
+ var infokey;
var line;
var lines = fds.stdout.split('\n');
var mountpoint;
var quota;
var recsize;
+ var snapname;
+ var snapparts;
var type;
var volblocksize;
var volsize;
+ var zoned;
for (line in lines) {
line = trim(lines[line]);
fields = line.split(/\s+/);
- if (fields.length === 8) {
+ if (fields.length === 9) {
dataset = fields[0];
quota = fields[1];
if (quota !== '-') {
@@ -1578,26 +1584,68 @@ function loadDatasetInfo(m, callback)
if (volblocksize !== '-') {
volblocksize = Number(volblocksize);
}
+ zoned = fields[8];
+ if (zoned === 'on') {
+ zoned = true;
+ } else {
+ zoned = false;
+ }
if (type === 'volume') {
mountpoint = '/dev/zvol/rdsk/' + dataset;
+ } else if (type == 'snapshot') {
+ // The only thing we do for snapshots is store the snapshot
+ // name keyed on the dataset name. So we can include the
+ // list of snapshots for a dataset on the VM.
+ snapparts = dataset.split('@');
+ assert.equal(snapparts.length, 2);
+ snapname = dataset;
+ dataset = snapparts[0];
+ if (!dsinfo.hasOwnProperty('snapshots')) {
+ dsinfo.snapshots = {};
+ }
+ if (!dsinfo.snapshots.hasOwnProperty(dataset)) {
+ dsinfo.snapshots[dataset] = [];
+ }
+ dsinfo.snapshots[dataset].push({'name': snapname});
+ continue;
} else if (mountpoint === '-' || mountpoint === 'legacy') {
mountpoint = '/' + dataset;
}
- // We use mountpoint as the key as zonepath comes from
- // zoneadm's "cheap" info.
- dsinfo[mountpoint] = {
+ /*
+ *
+ * For non-zoned filesystem datasets (these should be the
+ * zoneroot datasets), we use mountpoint as the key as zonepath
+ * comes from zoneadm's "cheap" info so we can match them up.
+ *
+ * For volumes (KVM VM's disks) we also use mountpoint as we've
+ * set that to the block device path and that's available from
+ * the devices section of the zoneconfig.
+ *
+ * For zoned filesystems (delegated datasets) we use the
+ * dataset name as the mountpoint can be changed from within the
+ * zone. When a delegated dataset is assigned but the zone's
+ * not been booted, the dataset will not have the 'zoned'
+ * property. So we also check if the name ends in /data.
+ *
+ */
+ infokey = mountpoint;
+ if (zoned || (dataset.split('/')[2] === 'data')) {
+ // delegated
+ infokey = dataset;
+ }
+ dsinfo[infokey] = {
'quota': quota,
'volsize': volsize,
'dataset': dataset,
'compression': compression
};
if (type === 'volume' && volblocksize !== '-') {
- dsinfo[mountpoint].volblocksize = volblocksize;
+ dsinfo[infokey].volblocksize = volblocksize;
} else if (recsize !== '-') {
- dsinfo[mountpoint].recsize = recsize;
+ dsinfo[infokey].recsize = recsize;
}
} else if (trim(line).length > 0) {
VM.log.debug('loadDatasetQuota(): ignoring line: ' + line);
@@ -1983,8 +2031,10 @@ function loadVM(uuid, data, callback)
var disk;
var disk_info;
var ds;
- var k;
+ var dsinfo;
var filesys;
+ var k;
+ var snapshots = [];
// when zone_state is 'incomplete' we could be deleting it in which
// case metadata may already be gone, ignore failure to load mdata
@@ -2000,43 +2050,65 @@ function loadVM(uuid, data, callback)
}
}
- if (data.dsinfo.hasOwnProperty(vmobj.zonepath)) {
- vmobj.quota = data.dsinfo[vmobj.zonepath].quota;
+ // local alias
+ dsinfo = data.dsinfo;
+
+ if (dsinfo.hasOwnProperty(vmobj.zonepath)) {
+ vmobj.quota = dsinfo[vmobj.zonepath].quota;
VM.log.debug('found quota "' + vmobj.quota + '" for '
+ vmobj.uuid);
- if (data.dsinfo[vmobj.zonepath].hasOwnProperty('compression')
- && (data.dsinfo[vmobj.zonepath].compression !== 'off')) {
+ if (dsinfo[vmobj.zonepath].hasOwnProperty('compression')
+ && (dsinfo[vmobj.zonepath].compression !== 'off')) {
vmobj.zfs_root_compression =
- data.dsinfo[vmobj.zonepath].compression;
+ dsinfo[vmobj.zonepath].compression;
}
- if (data.dsinfo[vmobj.zonepath].hasOwnProperty('recsize')) {
+ if (dsinfo[vmobj.zonepath].hasOwnProperty('recsize')) {
vmobj.zfs_root_recsize =
- data.dsinfo[vmobj.zonepath].recsize;
+ dsinfo[vmobj.zonepath].recsize;
}
- vmobj.zfs_filesystem = data.dsinfo[vmobj.zonepath].dataset;
+ vmobj.zfs_filesystem = dsinfo[vmobj.zonepath].dataset;
+
+ // If there are snapshots for this dataset, add them to the list
+ if (dsinfo.hasOwnProperty('snapshots')
+ && dsinfo.snapshots.hasOwnProperty(vmobj.zfs_filesystem)) {
+
+ snapshots = snapshots.concat(
+ dsinfo.snapshots[vmobj.zfs_filesystem]);
+ }
+
VM.log.debug('found dataset "' + vmobj.zfs_filesystem
+ '" for ' + vmobj.uuid);
} else {
VM.log.debug('no dsinfo for ' + vmobj.uuid);
}
- delegated = vmobj.zonepath + '/data';
- if (data.dsinfo.hasOwnProperty(delegated)) {
- if (data.dsinfo[delegated].hasOwnProperty('compression')
- && (data.dsinfo[delegated].compression !== 'off')) {
+ // delegated datasets are keyed on the dataset name instead of
+ // mountpoint, since mountpoint can change in a zone.
+ if (vmobj.hasOwnProperty('zfs_filesystem')) {
+ delegated = vmobj.zfs_filesystem + '/data';
+ if (dsinfo.hasOwnProperty(delegated)) {
+ if (dsinfo[delegated].hasOwnProperty('compression')
+ && (dsinfo[delegated].compression !== 'off')) {
- vmobj.zfs_data_compression =
- data.dsinfo[delegated].compression;
- }
- if (data.dsinfo[delegated].hasOwnProperty('recsize')) {
- vmobj.zfs_data_recsize = data.dsinfo[delegated].recsize;
+ vmobj.zfs_data_compression =
+ dsinfo[delegated].compression;
+ }
+ if (dsinfo[delegated].hasOwnProperty('recsize')) {
+ vmobj.zfs_data_recsize = dsinfo[delegated].recsize;
+ }
+
+ // If there are snapshots for this dataset, add them
+ if (dsinfo.hasOwnProperty('snapshots')
+ && dsinfo.snapshots.hasOwnProperty(delegated)) {
+
+ snapshots =
+ snapshots.concat(dsinfo.snapshots[delegated]);
+ }
+ } else {
+ VM.log.debug('no dsinfo for ' + delegated);
}
- } else {
- VM.log.debug('no dsinfo for ' + vmobj.uuid);
- }
- if (vmobj.hasOwnProperty('zfs_filesystem')) {
vmobj.zpool =
vmobj.zfs_filesystem.split('/')[0];
}
@@ -2045,9 +2117,9 @@ function loadVM(uuid, data, callback)
for (d in vmobj.disks) {
d = vmobj.disks[d];
if (d.hasOwnProperty('path')
- && data.dsinfo.hasOwnProperty(d.path)) {
+ && dsinfo.hasOwnProperty(d.path)) {
- disk_info = data.dsinfo[d.path];
+ disk_info = dsinfo[d.path];
if (disk_info.hasOwnProperty('volsize')) {
d.size = disk_info.volsize;
VM.log.debug('found size=' + d.size + ' for '
@@ -2059,6 +2131,16 @@ function loadVM(uuid, data, callback)
if (disk_info.hasOwnProperty('volblocksize')) {
d.block_size = disk_info.volblocksize;
}
+
+ // If there are snapshots for this dataset, add them to
+ // the list.
+ if (dsinfo.hasOwnProperty('snapshots')
+ && dsinfo.snapshots.hasOwnProperty(
+ d.zfs_filesystem)) {
+
+ snapshots = snapshots.concat(snapshots,
+ dsinfo.snapshots[d.zfs_filesystem]);
+ }
} else if (d.hasOwnProperty('path')) {
d.missing = true;
} else {
@@ -2083,6 +2165,8 @@ function loadVM(uuid, data, callback)
vmobj.tags = {};
}
+ vmobj.snapshots = snapshots;
+
if (vmobj.state === 'receiving') {
vmobj.missing = { 'datasets': [], 'disks': [],
'filesystems': [] };
@@ -6072,7 +6156,8 @@ exports.create = function (payload, callback)
// delete a zvol
function deleteVolume(volume, callback)
{
- var args = ['destroy', '-F', volume.zfs_filesystem];
+ // use recursive delete to handle possible snapshots on volume
+ var args = ['destroy', '-rF', volume.zfs_filesystem];
if (volume.missing) {
// this volume doesn't actually exist, so skip trying to delete.
@@ -75,6 +75,12 @@ function testInvalidRootRecsize(t, size, vmobj, callback)
};
var prev_size = vmobj.zfs_root_recsize;
+ if (abort) {
+ t.ok(false, 'skipping update as test run is aborted.');
+ callback();
+ return;
+ }
+
VM.update(vmobj.uuid, payload, function (e) {
// we expect this to fail
if (e) {
@@ -104,6 +110,12 @@ function testValidRootRecsize(t, size, vmobj, callback)
};
var prev_size = vmobj.zfs_root_recsize;
+ if (abort) {
+ t.ok(false, 'skipping update as test run is aborted.');
+ callback();
+ return;
+ }
+
VM.update(vmobj.uuid, payload, function (e) {
// we expect this to succeed
if (e) {
@@ -281,6 +293,12 @@ function testInvalidDataRecsize(t, size, vmobj, callback)
};
var prev_size = vmobj.zfs_data_recsize;
+ if (abort) {
+ t.ok(false, 'skipping send as test run is aborted.');
+ callback();
+ return;
+ }
+
VM.update(vmobj.uuid, payload, function (e) {
// we expect this to fail
if (e) {
@@ -310,6 +328,12 @@ function testValidDataRecsize(t, size, vmobj, callback)
};
var prev_size = vmobj.zfs_data_recsize;
+ if (abort) {
+ t.ok(false, 'skipping send as test run is aborted.');
+ callback();
+ return;
+ }
+
VM.update(vmobj.uuid, payload, function (e) {
// we expect this to succeed
if (e) {
@@ -483,6 +507,12 @@ test('update zone with compression off', {'timeout': 240000}, function(t) {
'zfs_root_compression': 'off'
};
+ if (abort) {
+ t.ok(false, 'skipping send as test run is aborted.');
+ t.end();
+ return;
+ }
+
VM.update(vmobj.uuid, payload, function (e) {
if (e) {
t.ok(false, 'failed to update: ' + e.message);
@@ -512,6 +542,12 @@ test('update zone with compression gzip-2 for data', {'timeout': 240000}, functi
'zfs_data_compression': 'gzip-2'
};
+ if (abort) {
+ t.ok(false, 'skipping send as test run is aborted.');
+ t.end();
+ return;
+ }
+
VM.update(vmobj.uuid, payload, function (e) {
if (e) {
t.ok(false, 'failed to update: ' + e.message);
@@ -607,6 +643,12 @@ function testAddDiskInvalidBlockSize(t, size, vmobj, callback)
};
var prev_count = vmobj.disks.length;
+ if (abort) {
+ t.ok(false, 'skipping send as test run is aborted.');
+ callback();
+ return;
+ }
+
VM.update(vmobj.uuid, payload, function (e) {
// we expect this to fail
if (e) {
@@ -740,6 +782,12 @@ test('update kvm with compression off', {'timeout': 240000}, function(t) {
]
};
+ if (abort) {
+ t.ok(false, 'skipping send as test run is aborted.');
+ t.end();
+ return;
+ }
+
VM.update(vmobj.uuid, payload, function (e) {
if (e) {
t.ok(false, 'failed to update: ' + e.message);
@@ -35,6 +35,7 @@ var zone_defaults = {
'image_uuid': [image_uuid],
'zfs_filesystem': ['uuid', prefix_zones],
'zfs_root_recsize': [131072],
+ 'snapshots': ['<EMPTY-ARRAY>'],
'owner_uuid': ['00000000-0000-0000-0000-000000000000'],
'uuid': ['uuid', state_property],
'dns_domain': ['local'],
Oops, something went wrong.

0 comments on commit 3ff74a3

Please sign in to comment.