Commit f98ab0e5 authored by Mike Hibler's avatar Mike Hibler

Initial support for ephemeral RW clones of persistent blockstores.

Using "set-rwclone" ala:

    set $bsobj [$ns blockstore]
    $bsobj set-lease "emulab-ops/bar"
    $bsobj set-node $node
    $bsobj set-rwclone 1
    ...

in your NS file will create a clone of the indicated persistent blockstore.

Somewhat limited in utility since you can only have one clone of a
particular blockstore per experiment.
parent dae72111
......@@ -3564,7 +3564,7 @@ sub getstorageconfig($;$) {
'CLASS' => '(SAN|local)',
'HOSTID' => '[-\w\.]+',
'MOUNTPOINT' => '\/[-\w\/\.]+',
'PERMS' => '(RO|RW)',
'PERMS' => '(RO|RW|CLONE)',
'PERSIST' => '(0|1)',
'PROTO' => '(iSCSI|local|SCSI|SAS|SATA|PATA|IDE)',
'UUID' => '[-\w\.:]+',
......@@ -3597,9 +3597,13 @@ sub getstorageconfig($;$) {
#
# Validate the info and untaint.
#
# Ignore unknown keywords (for compat), fail on unknown values.
# XXX we could also ignore unknown values, but that might leave
# us with an undefined/unexpected/undesirable default value.
#
if (!exists($fields{$key})) {
warn("*** WARNING: invalid keyword in storageinfo: '$key'\n");
return -1;
warn("*** WARNING: invalid keyword '$key' in storageinfo ignored\n");
next;
}
if ($val !~ /^$fields{$key}$/) {
warn("*** WARNING: invalid value for $key in storageinfo: '$val'\n");
......
......@@ -706,17 +706,23 @@ sub exportSlice($$$$) {
my $iqn = lc($1);
my $isro = "false";
if (exists($sconf->{'PERMS'}) && $sconf->{'PERMS'} eq "RO") {
$isro = "true";
my $isclone = "false";
if (exists($sconf->{'PERMS'})) {
if ($sconf->{'PERMS'} eq "RO") {
$isclone = "true";
$isro = "true";
}
elsif ($sconf->{'PERMS'} eq "CLONE") {
$isclone = "true";
}
}
#
# XXX hack temporary support for RO sharing of persistent blockstores.
#
# If the mapping to a persistent store is RO, then we will create
# an ephemeral clone for each such mapping.
# If the mapping to a persistent store is RO or CLONE, then we will
# create a read-only (RO) or read-write (CLONE) ephemeral clone for
# each such mapping.
#
if ($volume =~ /^lease-\d+$/ && $isro eq "true") {
if ($volume =~ /^lease-\d+$/ && $isclone eq "true") {
#
# If no snapshot exists, create one. VolumeClone must have
# a snapshot to hang the clone on. If a snapshot already exists
......@@ -735,7 +741,7 @@ sub exportSlice($$$$) {
$tstamp = time();
if (freenasVolumeSnapshot($pool, $volume, $tstamp)) {
warn("*** ERROR: blockstore_exportSlice: $volname: ".
"Could not create snapshot for RO mapping");
"Could not create snapshot for RO/Clone mapping");
return -1;
}
} else {
......@@ -751,7 +757,7 @@ sub exportSlice($$$$) {
#
if (freenasVolumeClone($pool, $volume, $vnode_id)) {
warn("*** ERROR: blockstore_exportSlice: $volname: ".
"Could not create clone for RO mapping");
"Could not create clone for RO/Clone mapping");
if ($tstamp) {
freenasVolumeDesnapshot($pool, $volume, $tstamp);
}
......@@ -886,7 +892,7 @@ sub exportSlice($$$$) {
return -1;
# Check requested perms.
if ($isro eq "false") {
if ($isclone eq "false") {
warn("*** ERROR: blockstore_exportSlice: $volname: ".
"Cannot re-export in-use dataset as RW!");
return -1;
......
......@@ -759,9 +759,17 @@ sub IsReadOnly($) {
return $self->HowUsed()->{'readonly'};
}
#
# Is this reservation a RW clone?
#
sub IsRWClone($) {
my ($self) = @_;
return $self->HowUsed()->{'rwclone'};
}
#
# How is the associated blockstore used in this reservation?
# Currently the only thing returned in the hash is the "readonly" flag.
#
sub HowUsed($) {
my ($self) = @_;
......@@ -769,6 +777,7 @@ sub HowUsed($) {
my $rethash = {
'readonly' => 0,
'rwclone' => 0,
};
my $virtexpt = VirtExperiment->Lookup(Experiment->Lookup($self->exptidx()));
......@@ -778,9 +787,15 @@ sub HowUsed($) {
}
my @attrs = ($self->vname(), "readonly");
my $rorow = $virtexpt->Find("virt_blockstore_attributes", @attrs);
if ($rorow) {
$rethash->{'readonly'} = int($rorow->attrvalue());
my $row = $virtexpt->Find("virt_blockstore_attributes", @attrs);
if ($row) {
$rethash->{'readonly'} = int($row->attrvalue());
}
@attrs = ($self->vname(), "rwclone");
$row = $virtexpt->Find("virt_blockstore_attributes", @attrs);
if ($row) {
$rethash->{'rwclone'} = int($row->attrvalue());
}
return $rethash;
......
#!/usr/bin/perl -w
#
# Copyright (c) 2005-2015 University of Utah and the Flux Group.
# Copyright (c) 2005-2016 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -1621,15 +1621,20 @@ sub LoadVirtNodes($)
}
#
# Make a quick pass of the virt_blockstores looking for RO attributes.
# Make a quick pass of the virt_blockstores looking for RO and RW clone
# attributes.
# We do this before we make the main pass below so that we have this
# info available when we lookup a lease.
#
my %roleases = ();
my %cloneleases = ();
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();
}
elsif ($virt_bs_attr->attrkey() eq "rwclone") {
$cloneleases{$virt_bs_attr->vname()} = $virt_bs_attr->attrvalue();
}
}
foreach my $virt_bs_attr ($self->virt_blockstore_attributes()->Rows()) {
......@@ -1694,19 +1699,21 @@ sub LoadVirtNodes($)
}
# Do sanity/permission checks based on requested mode (RW/RO)
if (exists($roleases{$vname}) && $roleases{$vname} == 1) {
# Does user have RO rights to this lease?
if (exists($roleases{$vname}) && $roleases{$vname} == 1 ||
exists($cloneleases{$vname}) && $cloneleases{$vname} == 1) {
# Does user have read rights to this lease?
if (!$lease->AccessCheck($self->realuser(),
LEASE_ACCESS_READ())) {
tberror("Not allowed to use dataset $lease in RO mode\n");
tberror("Not allowed to use dataset $lease in ".
"RO or RW-clone mode\n");
return -1;
}
# For shared use, a snapshot must exist
if (!$exclusive) {
if (!$snapshot) {
tberror("Dataset $lease has no snapshot ".
"and cannot be mapped RO at this time.\n");
tberror("Dataset $lease has no snapshot and cannot ".
"be mapped RO or cloned at this time.\n");
return -1;
}
}
......
......@@ -1626,15 +1626,20 @@ sub LoadVirtNodes($)
}
#
# Make a quick pass of the virt_blockstores looking for RO attributes.
# Make a quick pass of the virt_blockstores looking for RO and RW clone
# attributes.
# We do this before we make the main pass below so that we have this
# info available when we lookup a lease.
#
my %roleases = ();
my %cloneleases = ();
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();
}
elsif ($virt_bs_attr->attrkey() eq "rwclone") {
$cloneleases{$virt_bs_attr->vname()} = $virt_bs_attr->attrvalue();
}
}
foreach my $virt_bs_attr ($self->virt_blockstore_attributes()->Rows()) {
......@@ -1699,19 +1704,21 @@ sub LoadVirtNodes($)
}
# Do sanity/permission checks based on requested mode (RW/RO)
if (exists($roleases{$vname}) && $roleases{$vname} == 1) {
# Does user have RO rights to this lease?
if (exists($roleases{$vname}) && $roleases{$vname} == 1 ||
exists($cloneleases{$vname}) && $cloneleases{$vname} == 1) {
# Does user have read rights to this lease?
if (!$lease->AccessCheck($self->realuser(),
LEASE_ACCESS_READ())) {
tberror("Not allowed to use dataset $lease in RO mode\n");
tberror("Not allowed to use dataset $lease in ".
"RO or RW-clone mode\n");
return -1;
}
# For shared use, a snapshot must exist
if (!$exclusive) {
if (!$snapshot) {
tberror("Dataset $lease has no snapshot ".
"and cannot be mapped RO at this time.\n");
tberror("Dataset $lease has no snapshot and cannot ".
"be mapped RO or cloned at this time.\n");
return -1;
}
}
......
......@@ -207,10 +207,31 @@ Blockstore instproc set-readonly {roflag} {
set roflag 1
}
if {$roflag &&
[info exists attributes(rwclone)] && $attributes(rwclone) != 0} {
perror "\[set-readonly] cannot set both readonly and rwclone"
return
}
set attributes(readonly) $roflag
return
}
Blockstore instproc set-rwclone {flag} {
$self instvar attributes
if {$flag != 0} {
set flag 1
}
if {$flag &&
[info exists attributes(readonly)] && $attributes(readonly) != 0} {
perror "\[set-rwclone] cannot set both rwclone and readonly"
return
}
set attributes(rwclone) $flag
return
}
#
# Alias for procedure below
#
......@@ -299,6 +320,18 @@ Blockstore instproc finalize {} {
}
set attributes(readonly) $ro
# Check RW clone status
set rwclone 0
if {[info exists attributes(rwclone)]} {
set rwclone $attributes(rwclone)
# RW clone of anon blockstore is equally dumb
if {$leasename == {} && $rwclone} {
puts stderr "*** WARNING: marking ephemeral blockstore $self as RW-clone is useless, ignoring rwclone setting"
set rwclone 0
}
}
set attributes(rwclone) $rwclone
# If the blockstore is associated with a lease, disallow/override certain
# explicitly-specified values
if {$leasename != {}} {
......@@ -312,7 +345,7 @@ Blockstore instproc finalize {} {
perror "Cannot explicitly set size/type/class/protocol of lease-associated blockstore $self"
return -1
}
if {$ro == 0 &&
if {$ro == 0 && $rwclone == 0 &&
[info exists dataset_readonly($leasename)] &&
$dataset_readonly($leasename) != 0} {
perror "Cannot RW access RO lease-associated blockstore $self"
......
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