Commit 72fb6763 authored by Mike Hibler's avatar Mike Hibler

Machinery for supporting multiple RO/RW clones of a dataset in one experiment.

Mostly ptopgen/libvtop changes to get things through assign.

Added a new virt_blockstore_attribute, 'prereserve' that can be applied to
a RW clone to pre-allocate the full amount of space allocated to the volume
being cloned. This is instead of the default "sparse" clone which could run
out of space at an inopportune time if the containing pool runs out of space.
But it doesn't work yet.

Everything is there in the front end to do the necessary capacity checks and
allocations of space, but then I discovered that ZFS doesn't readily support
a non-sparse clone! You can do this, I think, by tweaking the "refreserved"
attribute of the volume after it is created but that would have to be done
behind the back of FreeNAS and I would have to do some more testing before I
am willing to go here.

So for now, all clones are sparse and no one is charged for their usage.
parent bf41a4ce
......@@ -1641,6 +1641,7 @@ sub LoadVirtNodes($)
#
my %roleases = ();
my %cloneleases = ();
my %prereserves = ();
foreach my $virt_bs_attr ($self->virt_blockstore_attributes()->Rows()) {
if ($virt_bs_attr->attrkey() eq "readonly") {
$roleases{$virt_bs_attr->vname()} = $virt_bs_attr->attrvalue();
......@@ -1648,6 +1649,9 @@ sub LoadVirtNodes($)
elsif ($virt_bs_attr->attrkey() eq "rwclone") {
$cloneleases{$virt_bs_attr->vname()} = $virt_bs_attr->attrvalue();
}
elsif ($virt_bs_attr->attrkey() eq "prereserve") {
$prereserves{$virt_bs_attr->vname()} = $virt_bs_attr->attrvalue();
}
}
foreach my $virt_bs_attr ($self->virt_blockstore_attributes()->Rows()) {
......@@ -1685,8 +1689,8 @@ sub LoadVirtNodes($)
# upon termination of any RW mapping (see Blockstore->Release).
# Snapshot creation may be explicit in the future.
#
# A RO mapping of a dataset always gets the most recent snapshot
# of the dataset. This is true whether the dataset is currently
# A RW clone or a RO mapping of a dataset always gets the most recent
# snapshot of the dataset. This is true whether the dataset is currently
# in use or not. If the dataset does not have a snapshot, it is
# an error.
#
......@@ -1730,6 +1734,12 @@ sub LoadVirtNodes($)
return -1;
}
}
#
# XXX we want the attribute to get listed as "bs-clone-<lease>"
# rather than bs-lease-<lease> in the vtop file.
#
$attrkey = "clone";
} else {
# Does user have RW rights?
if (!$lease->AccessCheck($self->realuser(),
......@@ -1797,6 +1807,19 @@ sub LoadVirtNodes($)
my $fixnode = $self->blockstores()->{$vname};
$fixnode->_blockstore_attributes()->{$attrkey} = $attrval;
# Make it easier to figure out our perms later on
if (exists($roleases{$vname}) && $roleases{$vname} == 1) {
$fixnode->_bsperms("RO");
} elsif (exists($cloneleases{$vname}) && $cloneleases{$vname} == 1) {
if (exists($prereserves{$vname}) && $prereserves{$vname} == 1) {
$fixnode->_bsperms("CLONE-PRERES");
} else {
$fixnode->_bsperms("CLONE");
}
} else {
$fixnode->_bsperms("RW");
}
}
return 0;
}
......@@ -2336,9 +2359,34 @@ sub GenVirtNodes($)
if (defined($vnode->_blockstore())) {
my $virt_bs = $vnode->_blockstore();
my $bs_vname = $virt_bs->vname();
my $size = $virt_bs->size();
#
# RO clones have no cost in capacity.
#
# RW clones come in two flavors: best-effort (zero bytes) or
# guaranteed (full size of cloned dataset). The former is
# signaled by CLONE, the latter by CLONE-PRERES.
#
if ($vnode->_bsperms() eq "RO") {
$size = 0;
}
elsif ($vnode->_bsperms() eq "CLONE") {
$size = 0;
}
# XXX "zfs clone" or the FreeNAS API does not have an interface
# for creating a pre-allocated clone. You can do it (I think!)
# by using "zfs set refreserved=..." after the clone call, but
# I am not ready to go there yet, so we ignore the "prereserve"
# attribute for now.
#
elsif ($vnode->_bsperms() eq "CLONE-PRERES") {
$size = 0;
}
$vnode->_bsallocsize($size);
# This is an additive desire.
$desires->{"bs-capacity"} = ['+', $virt_bs->size()];
$desires->{"bs-capacity"} = ['+', $size];
# The rest come from the attributes.
foreach my $attrkey (keys(%{ $vnode->_blockstore_attributes() })) {
......@@ -9582,10 +9630,25 @@ sub UploadBlockstores($)
my $vvnode = $self->solution_v2v()->{$vname};
my $vpnode = $virtnode->_pnode();
my $pnode = $vpnode->phys_nodeid();
my $size = $virtnode->_blockstore()->size();
#
# If this is a blockstore clone, then bstore will be the storage
# pool that the clone is allocating space from. We need this to
# charge resources to. However, what we record in the DB is the
# blockstore that we are cloning since that is what is needed later
# in tmcd for passing to the storage server.
#
my $pool = $bstore;
if ($virtnode->_bsperms() ne "RW") {
my $bsattr = $virtnode->_blockstore_attributes();
if ($bsattr && exists($bsattr->{'clone'})) {
$bstore = "lease-" . $bsattr->{'clone'};
}
}
my $size = $virtnode->_bsallocsize();
$self->printdb("BlockStore: ".
"$vname, $bstore, $size, $vvnode, $vpnode, $pnode\n");
"$vname, $bstore ($pool), $size, $vvnode, $vpnode, $pnode\n");
my $blockstore = Blockstore->Lookup($pnode, $bstore);
if (!defined($blockstore)) {
......
......@@ -232,6 +232,17 @@ Blockstore instproc set-rwclone {flag} {
return
}
Blockstore instproc set-prereserve {flag} {
$self instvar attributes
if {$flag != 0} {
set flag 1
}
set attributes(prereserve) $flag
return
}
#
# Alias for procedure below
#
......@@ -332,6 +343,18 @@ Blockstore instproc finalize {} {
}
set attributes(rwclone) $rwclone
# Check prereserve
set prereserve 0
if {[info exists attributes(prereserve)]} {
set prereserve $attributes(prereserve)
# Prereserve only applies to RW clones
if {$rwclone == 0 && $prereserve} {
puts stderr "*** WARNING: space pre-reservation only applies to RW-clones, ignoring setting on $self"
set prereserve 0
}
}
set attributes(prereserve) $prereserve
# If the blockstore is associated with a lease, disallow/override certain
# explicitly-specified values
if {$leasename != {}} {
......
......@@ -1590,15 +1590,42 @@ foreach $node (@nodenames) {
#
# Print out all blockstores
#
# XXX we should try to narrow down the list of lease blockstores to only
# those accessible by this user/project.
#
if ($useblockstore) {
$result = DBQueryFatal("select b.node_id, b.bs_id, b.lease_idx, b.type, ".
"s.remaining_capacity ".
"s.remaining_capacity, b2.bs_id as pool ".
"from blockstores as b ".
"left join blockstore_state as s ".
"on b.bsidx=s.bsidx ".
"left join blockstore_trees as t ".
"on b.bsidx=t.bsidx ".
"left join blockstores as b2 ".
"on t.aggidx=b2.bsidx ".
"where s.ready=1");
#
# Pass 1: associate leases with pools
#
my @rows = ();
my %bspools = ();
while (my ($nodeId, $bsId, $leaseIdx, $typeName,
$capacity) = $result->fetchrow_array) {
$capacity, $pool) = $result->fetchrow_array) {
if ($pool) {
my $key = "$nodeId/$pool";
if (!exists($bspools{$key})) {
$bspools{$key} = [];
}
push(@{$bspools{$key}}, $leaseIdx);
}
push(@rows, [ $nodeId, $bsId, $leaseIdx, $typeName, $capacity ]);
}
#
# Pass 2: output the blockstores
#
foreach my $row (@rows) {
my ($nodeId, $bsId, $leaseIdx, $typeName, $capacity) = @$row;
my $type = BlockstoreType->Lookup($typeName);
if (defined($type)) {
my @features = ();
......@@ -1627,6 +1654,16 @@ if ($useblockstore) {
if ($leaseIdx > 0) {
push(@features, "bs-lease-$leaseIdx:1.0");
}
#
# If it is not a lease, we need to see if it is the home
# of any leases. If so we output a unique feature to say
# that this pool supports clones of the indicated lease.
#
elsif (exists($bspools{"$nodeId/$bsId"})) {
foreach my $lidx (@{$bspools{"$nodeId/$bsId"}}) {
push(@features, "bs-clone-$lidx:0.5");
}
}
push(@features, "+bs-capacity:$capacity");
print_node("$nodeId:$bsId", ["blockstore:$slots"], \@features,
......
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