Add support for zpool user properties
Usage:
zpool set org.freebsd:comment="this is my pool" poolname
Tests are based on zfs_set's user property tests.
Also stop truncating property values at MAXNAMELEN, use ZFS_MAXPROPLEN.
Reviewed by: markj
Approved by: markj
Co-authored-by: Mateusz Piotrowski <0mp@FreeBSD.org>
Obtained from: OpenZFS 8eae2d214c
Add support for zpool user properties
Sponsored by: Beckhoff Automation GmbH & Co. KG.
Sponsored by: Klara Inc.
Differential Revision: https://reviews.freebsd.org/D39657
This commit is contained in:
parent
820ac12679
commit
c98ecfceb3
@ -6069,11 +6069,14 @@ print_pool(zpool_handle_t *zhp, list_cbdata_t *cb)
|
||||
zpool_prop_get_feature(zhp, pl->pl_user_prop, property,
|
||||
sizeof (property)) == 0) {
|
||||
propstr = property;
|
||||
} else if (zfs_prop_user(pl->pl_user_prop) &&
|
||||
zpool_get_userprop(zhp, pl->pl_user_prop, property,
|
||||
sizeof (property), NULL) == 0) {
|
||||
propstr = property;
|
||||
} else {
|
||||
propstr = "-";
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* If this is being called in scripted mode, or if this is the
|
||||
* last column and it is left-justified, don't include a width
|
||||
@ -10011,7 +10014,7 @@ static int
|
||||
get_callback(zpool_handle_t *zhp, void *data)
|
||||
{
|
||||
zprop_get_cbdata_t *cbp = (zprop_get_cbdata_t *)data;
|
||||
char value[MAXNAMELEN];
|
||||
char value[ZFS_MAXPROPLEN];
|
||||
zprop_source_t srctype;
|
||||
zprop_list_t *pl;
|
||||
int vid;
|
||||
@ -10047,6 +10050,17 @@ get_callback(zpool_handle_t *zhp, void *data)
|
||||
continue;
|
||||
|
||||
if (pl->pl_prop == ZPROP_INVAL &&
|
||||
zfs_prop_user(pl->pl_user_prop)) {
|
||||
srctype = ZPROP_SRC_LOCAL;
|
||||
|
||||
if (zpool_get_userprop(zhp, pl->pl_user_prop,
|
||||
value, sizeof (value), &srctype) != 0)
|
||||
continue;
|
||||
|
||||
zprop_print_one_property(zpool_get_name(zhp),
|
||||
cbp, pl->pl_user_prop, value, srctype,
|
||||
NULL, NULL);
|
||||
} else if (pl->pl_prop == ZPROP_INVAL &&
|
||||
(zpool_prop_feature(pl->pl_user_prop) ||
|
||||
zpool_prop_unsupported(pl->pl_user_prop))) {
|
||||
srctype = ZPROP_SRC_LOCAL;
|
||||
|
@ -333,6 +333,8 @@ _LIBZFS_H const char *zpool_get_state_str(zpool_handle_t *);
|
||||
_LIBZFS_H int zpool_set_prop(zpool_handle_t *, const char *, const char *);
|
||||
_LIBZFS_H int zpool_get_prop(zpool_handle_t *, zpool_prop_t, char *,
|
||||
size_t proplen, zprop_source_t *, boolean_t literal);
|
||||
_LIBZFS_H int zpool_get_userprop(zpool_handle_t *, const char *, char *,
|
||||
size_t proplen, zprop_source_t *);
|
||||
_LIBZFS_H uint64_t zpool_get_prop_int(zpool_handle_t *, zpool_prop_t,
|
||||
zprop_source_t *);
|
||||
_LIBZFS_H int zpool_props_refresh(zpool_handle_t *);
|
||||
|
@ -484,6 +484,7 @@
|
||||
<elf-symbol name='zpool_get_state' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||
<elf-symbol name='zpool_get_state_str' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||
<elf-symbol name='zpool_get_status' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||
<elf-symbol name='zpool_get_userprop' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||
<elf-symbol name='zpool_get_vdev_prop' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||
<elf-symbol name='zpool_get_vdev_prop_value' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||
<elf-symbol name='zpool_history_unpack' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
|
||||
@ -6108,6 +6109,14 @@
|
||||
<parameter type-id='4c81de99' name='zhp'/>
|
||||
<return type-id='80f4b756'/>
|
||||
</function-decl>
|
||||
<function-decl name='zpool_get_userprop' mangled-name='zpool_get_userprop' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='zpool_get_userprop'>
|
||||
<parameter type-id='4c81de99' name='zhp'/>
|
||||
<parameter type-id='80f4b756' name='propname'/>
|
||||
<parameter type-id='26a90f95' name='buf'/>
|
||||
<parameter type-id='b59d7dce' name='len'/>
|
||||
<parameter type-id='debc6aa3' name='srctype'/>
|
||||
<return type-id='95e97e5e'/>
|
||||
</function-decl>
|
||||
<function-decl name='zpool_set_prop' mangled-name='zpool_set_prop' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='zpool_set_prop'>
|
||||
<parameter type-id='4c81de99' name='zhp'/>
|
||||
<parameter type-id='80f4b756' name='propname'/>
|
||||
|
@ -426,6 +426,37 @@ zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf,
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a zpool property value for 'propname' and return the value in
|
||||
* a pre-allocated buffer.
|
||||
*/
|
||||
int
|
||||
zpool_get_userprop(zpool_handle_t *zhp, const char *propname, char *buf,
|
||||
size_t len, zprop_source_t *srctype)
|
||||
{
|
||||
nvlist_t *nv, *nvl;
|
||||
uint64_t ival;
|
||||
const char *value;
|
||||
zprop_source_t source = ZPROP_SRC_LOCAL;
|
||||
|
||||
nvl = zhp->zpool_props;
|
||||
if (nvlist_lookup_nvlist(nvl, propname, &nv) == 0) {
|
||||
if (nvlist_lookup_uint64(nv, ZPROP_SOURCE, &ival) == 0)
|
||||
source = ival;
|
||||
verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) == 0);
|
||||
} else {
|
||||
source = ZPROP_SRC_DEFAULT;
|
||||
value = "-";
|
||||
}
|
||||
|
||||
if (srctype)
|
||||
*srctype = source;
|
||||
|
||||
(void) strlcpy(buf, value, len);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the bootfs name has the same pool name as it is set to.
|
||||
* Assuming bootfs is a valid dataset name.
|
||||
@ -549,6 +580,44 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
|
||||
(void) no_memory(hdl);
|
||||
goto error;
|
||||
}
|
||||
continue;
|
||||
} else if (prop == ZPOOL_PROP_INVAL &&
|
||||
zfs_prop_user(propname)) {
|
||||
/*
|
||||
* This is a user property: make sure it's a
|
||||
* string, and that it's less than ZAP_MAXNAMELEN.
|
||||
*/
|
||||
if (nvpair_type(elem) != DATA_TYPE_STRING) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"'%s' must be a string"), propname);
|
||||
(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"property name '%s' is too long"),
|
||||
propname);
|
||||
(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
|
||||
goto error;
|
||||
}
|
||||
|
||||
(void) nvpair_value_string(elem, &strval);
|
||||
|
||||
if (strlen(strval) >= ZFS_MAXPROPLEN) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"property value '%s' is too long"),
|
||||
strval);
|
||||
(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (nvlist_add_string(retprops, propname,
|
||||
strval) != 0) {
|
||||
(void) no_memory(hdl);
|
||||
goto error;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -855,9 +924,30 @@ zpool_expand_proplist(zpool_handle_t *zhp, zprop_list_t **plp,
|
||||
features = zpool_get_features(zhp);
|
||||
|
||||
if ((*plp)->pl_all && firstexpand) {
|
||||
/* Handle userprops in the all properties case */
|
||||
if (zhp->zpool_props == NULL && zpool_props_refresh(zhp))
|
||||
return (-1);
|
||||
|
||||
nvp = NULL;
|
||||
while ((nvp = nvlist_next_nvpair(zhp->zpool_props, nvp)) !=
|
||||
NULL) {
|
||||
const char *propname = nvpair_name(nvp);
|
||||
|
||||
if (!zfs_prop_user(propname))
|
||||
continue;
|
||||
|
||||
entry = zfs_alloc(hdl, sizeof (zprop_list_t));
|
||||
entry->pl_prop = ZPROP_USERPROP;
|
||||
entry->pl_user_prop = zfs_strdup(hdl, propname);
|
||||
entry->pl_width = strlen(entry->pl_user_prop);
|
||||
entry->pl_all = B_TRUE;
|
||||
|
||||
*last = entry;
|
||||
last = &entry->pl_next;
|
||||
}
|
||||
|
||||
for (i = 0; i < SPA_FEATURES; i++) {
|
||||
zprop_list_t *entry = zfs_alloc(hdl,
|
||||
sizeof (zprop_list_t));
|
||||
entry = zfs_alloc(hdl, sizeof (zprop_list_t));
|
||||
entry->pl_prop = ZPROP_USERPROP;
|
||||
entry->pl_user_prop = zfs_asprintf(hdl, "feature@%s",
|
||||
spa_feature_table[i].fi_uname);
|
||||
@ -874,7 +964,6 @@ zpool_expand_proplist(zpool_handle_t *zhp, zprop_list_t **plp,
|
||||
nvp != NULL; nvp = nvlist_next_nvpair(features, nvp)) {
|
||||
char *propname;
|
||||
boolean_t found;
|
||||
zprop_list_t *entry;
|
||||
|
||||
if (zfeature_is_supported(nvpair_name(nvp)))
|
||||
continue;
|
||||
@ -920,6 +1009,12 @@ zpool_expand_proplist(zpool_handle_t *zhp, zprop_list_t **plp,
|
||||
NULL, literal) == 0) {
|
||||
if (strlen(buf) > entry->pl_width)
|
||||
entry->pl_width = strlen(buf);
|
||||
} else if (entry->pl_prop == ZPROP_INVAL &&
|
||||
zfs_prop_user(entry->pl_user_prop) &&
|
||||
zpool_get_userprop(zhp, entry->pl_user_prop, buf,
|
||||
sizeof (buf), NULL) == 0) {
|
||||
if (strlen(buf) > entry->pl_width)
|
||||
entry->pl_width = strlen(buf);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1774,6 +1774,7 @@ addlist(libzfs_handle_t *hdl, const char *propname, zprop_list_t **listp,
|
||||
* a user-defined property.
|
||||
*/
|
||||
if (prop == ZPROP_USERPROP && ((type == ZFS_TYPE_POOL &&
|
||||
!zfs_prop_user(propname) &&
|
||||
!zpool_prop_feature(propname) &&
|
||||
!zpool_prop_unsupported(propname)) ||
|
||||
((type == ZFS_TYPE_DATASET) && !zfs_prop_user(propname) &&
|
||||
|
@ -26,8 +26,9 @@
|
||||
.\" Copyright 2017 Nexenta Systems, Inc.
|
||||
.\" Copyright (c) 2017 Open-E, Inc. All Rights Reserved.
|
||||
.\" Copyright (c) 2021, Colm Buckley <colm@tuatha.org>
|
||||
.\" Copyright (c) 2023, Klara Inc.
|
||||
.\"
|
||||
.Dd May 27, 2021
|
||||
.Dd April 18, 2023
|
||||
.Dt ZPOOLPROPS 7
|
||||
.Os
|
||||
.
|
||||
@ -40,6 +41,12 @@ Each pool has several properties associated with it.
|
||||
Some properties are read-only statistics while others are configurable and
|
||||
change the behavior of the pool.
|
||||
.Pp
|
||||
User properties have no effect on ZFS behavior.
|
||||
Use them to annotate pools in a way that is meaningful in your environment.
|
||||
For more information about user properties, see the
|
||||
.Sx User Properties
|
||||
section.
|
||||
.Pp
|
||||
The following are read-only properties:
|
||||
.Bl -tag -width "unsupported@guid"
|
||||
.It Sy allocated
|
||||
@ -431,3 +438,49 @@ backwards compatibility.
|
||||
Once feature flags are enabled on a pool this property will no longer have a
|
||||
value.
|
||||
.El
|
||||
.
|
||||
.Ss User Properties
|
||||
In addition to the standard native properties, ZFS supports arbitrary user
|
||||
properties.
|
||||
User properties have no effect on ZFS behavior, but applications or
|
||||
administrators can use them to annotate pools.
|
||||
.Pp
|
||||
User property names must contain a colon
|
||||
.Pq Qq Sy \&:
|
||||
character to distinguish them from native properties.
|
||||
They may contain lowercase letters, numbers, and the following punctuation
|
||||
characters: colon
|
||||
.Pq Qq Sy \&: ,
|
||||
dash
|
||||
.Pq Qq Sy - ,
|
||||
period
|
||||
.Pq Qq Sy \&. ,
|
||||
and underscore
|
||||
.Pq Qq Sy _ .
|
||||
The expected convention is that the property name is divided into two portions
|
||||
such as
|
||||
.Ar module : Ns Ar property ,
|
||||
but this namespace is not enforced by ZFS.
|
||||
User property names can be at most 256 characters, and cannot begin with a dash
|
||||
.Pq Qq Sy - .
|
||||
.Pp
|
||||
When making programmatic use of user properties, it is strongly suggested to use
|
||||
a reversed DNS domain name for the
|
||||
.Ar module
|
||||
component of property names to reduce the chance that two
|
||||
independently-developed packages use the same property name for different
|
||||
purposes.
|
||||
.Pp
|
||||
The values of user properties are arbitrary strings and
|
||||
are never validated.
|
||||
All of the commands that operate on properties
|
||||
.Po Nm zpool Cm list ,
|
||||
.Nm zpool Cm get ,
|
||||
.Nm zpool Cm set ,
|
||||
and so forth
|
||||
.Pc
|
||||
can be used to manipulate both native properties and user properties.
|
||||
Use
|
||||
.Nm zpool Cm set Ar name Ns =
|
||||
to clear a user property.
|
||||
Property values are limited to 8192 bytes.
|
||||
|
@ -296,6 +296,22 @@ spa_prop_add_list(nvlist_t *nvl, zpool_prop_t prop, const char *strval,
|
||||
nvlist_free(propval);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a user property (source=src, propname=propval) to an nvlist.
|
||||
*/
|
||||
static void
|
||||
spa_prop_add_user(nvlist_t *nvl, const char *propname, char *strval,
|
||||
zprop_source_t src)
|
||||
{
|
||||
nvlist_t *propval;
|
||||
|
||||
VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0);
|
||||
VERIFY(nvlist_add_uint64(propval, ZPROP_SOURCE, src) == 0);
|
||||
VERIFY(nvlist_add_string(propval, ZPROP_VALUE, strval) == 0);
|
||||
VERIFY(nvlist_add_nvlist(nvl, propname, propval) == 0);
|
||||
nvlist_free(propval);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get property values from the spa configuration.
|
||||
*/
|
||||
@ -471,7 +487,8 @@ spa_prop_get(spa_t *spa, nvlist_t **nvp)
|
||||
zprop_source_t src = ZPROP_SRC_DEFAULT;
|
||||
zpool_prop_t prop;
|
||||
|
||||
if ((prop = zpool_name_to_prop(za.za_name)) == ZPOOL_PROP_INVAL)
|
||||
if ((prop = zpool_name_to_prop(za.za_name)) ==
|
||||
ZPOOL_PROP_INVAL && !zfs_prop_user(za.za_name))
|
||||
continue;
|
||||
|
||||
switch (za.za_integer_length) {
|
||||
@ -514,7 +531,13 @@ spa_prop_get(spa_t *spa, nvlist_t **nvp)
|
||||
kmem_free(strval, za.za_num_integers);
|
||||
break;
|
||||
}
|
||||
if (prop != ZPOOL_PROP_INVAL) {
|
||||
spa_prop_add_list(*nvp, prop, strval, 0, src);
|
||||
} else {
|
||||
src = ZPROP_SRC_LOCAL;
|
||||
spa_prop_add_user(*nvp, za.za_name, strval,
|
||||
src);
|
||||
}
|
||||
kmem_free(strval, za.za_num_integers);
|
||||
break;
|
||||
|
||||
@ -556,14 +579,21 @@ spa_prop_validate(spa_t *spa, nvlist_t *props)
|
||||
|
||||
switch (prop) {
|
||||
case ZPOOL_PROP_INVAL:
|
||||
if (!zpool_prop_feature(propname)) {
|
||||
error = SET_ERROR(EINVAL);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sanitize the input.
|
||||
*/
|
||||
if (zfs_prop_user(propname)) {
|
||||
if (strlen(propname) >= ZAP_MAXNAMELEN) {
|
||||
error = SET_ERROR(ENAMETOOLONG);
|
||||
break;
|
||||
}
|
||||
|
||||
if (strlen(fnvpair_value_string(elem)) >=
|
||||
ZAP_MAXVALUELEN) {
|
||||
error = SET_ERROR(E2BIG);
|
||||
break;
|
||||
}
|
||||
} else if (zpool_prop_feature(propname)) {
|
||||
if (nvpair_type(elem) != DATA_TYPE_UINT64) {
|
||||
error = SET_ERROR(EINVAL);
|
||||
break;
|
||||
@ -586,6 +616,10 @@ spa_prop_validate(spa_t *spa, nvlist_t *props)
|
||||
}
|
||||
|
||||
has_feature = B_TRUE;
|
||||
} else {
|
||||
error = SET_ERROR(EINVAL);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case ZPOOL_PROP_VERSION:
|
||||
@ -792,6 +826,12 @@ spa_prop_set(spa_t *spa, nvlist_t *nvp)
|
||||
prop == ZPOOL_PROP_READONLY)
|
||||
continue;
|
||||
|
||||
if (prop == ZPOOL_PROP_INVAL &&
|
||||
zfs_prop_user(nvpair_name(elem))) {
|
||||
need_sync = B_TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (prop == ZPOOL_PROP_VERSION || prop == ZPOOL_PROP_INVAL) {
|
||||
uint64_t ver = 0;
|
||||
|
||||
@ -8788,24 +8828,11 @@ spa_sync_props(void *arg, dmu_tx_t *tx)
|
||||
const char *strval, *fname;
|
||||
zpool_prop_t prop;
|
||||
const char *propname;
|
||||
const char *elemname = nvpair_name(elem);
|
||||
zprop_type_t proptype;
|
||||
spa_feature_t fid;
|
||||
|
||||
switch (prop = zpool_name_to_prop(nvpair_name(elem))) {
|
||||
case ZPOOL_PROP_INVAL:
|
||||
/*
|
||||
* We checked this earlier in spa_prop_validate().
|
||||
*/
|
||||
ASSERT(zpool_prop_feature(nvpair_name(elem)));
|
||||
|
||||
fname = strchr(nvpair_name(elem), '@') + 1;
|
||||
VERIFY0(zfeature_lookup_name(fname, &fid));
|
||||
|
||||
spa_feature_enable(spa, fid, tx);
|
||||
spa_history_log_internal(spa, "set", tx,
|
||||
"%s=enabled", nvpair_name(elem));
|
||||
break;
|
||||
|
||||
switch (prop = zpool_name_to_prop(elemname)) {
|
||||
case ZPOOL_PROP_VERSION:
|
||||
intval = fnvpair_value_uint64(elem);
|
||||
/*
|
||||
@ -8848,7 +8875,7 @@ spa_sync_props(void *arg, dmu_tx_t *tx)
|
||||
spa_async_request(spa, SPA_ASYNC_CONFIG_UPDATE);
|
||||
}
|
||||
spa_history_log_internal(spa, "set", tx,
|
||||
"%s=%s", nvpair_name(elem), strval);
|
||||
"%s=%s", elemname, strval);
|
||||
break;
|
||||
case ZPOOL_PROP_COMPATIBILITY:
|
||||
strval = fnvpair_value_string(elem);
|
||||
@ -8867,6 +8894,20 @@ spa_sync_props(void *arg, dmu_tx_t *tx)
|
||||
"%s=%s", nvpair_name(elem), strval);
|
||||
break;
|
||||
|
||||
case ZPOOL_PROP_INVAL:
|
||||
if (zpool_prop_feature(elemname)) {
|
||||
fname = strchr(elemname, '@') + 1;
|
||||
VERIFY0(zfeature_lookup_name(fname, &fid));
|
||||
|
||||
spa_feature_enable(spa, fid, tx);
|
||||
spa_history_log_internal(spa, "set", tx,
|
||||
"%s=enabled", elemname);
|
||||
break;
|
||||
} else if (!zfs_prop_user(elemname)) {
|
||||
ASSERT(zpool_prop_feature(elemname));
|
||||
break;
|
||||
}
|
||||
zfs_fallthrough;
|
||||
default:
|
||||
/*
|
||||
* Set pool property values in the poolprops mos object.
|
||||
@ -8881,6 +8922,11 @@ spa_sync_props(void *arg, dmu_tx_t *tx)
|
||||
/* normalize the property name */
|
||||
propname = zpool_prop_to_name(prop);
|
||||
proptype = zpool_prop_get_type(prop);
|
||||
if (prop == ZPOOL_PROP_INVAL &&
|
||||
zfs_prop_user(elemname)) {
|
||||
propname = elemname;
|
||||
proptype = PROP_TYPE_STRING;
|
||||
}
|
||||
|
||||
if (nvpair_type(elem) == DATA_TYPE_STRING) {
|
||||
ASSERT(proptype == PROP_TYPE_STRING);
|
||||
@ -8889,7 +8935,7 @@ spa_sync_props(void *arg, dmu_tx_t *tx)
|
||||
spa->spa_pool_props_object, propname,
|
||||
1, strlen(strval) + 1, strval, tx));
|
||||
spa_history_log_internal(spa, "set", tx,
|
||||
"%s=%s", nvpair_name(elem), strval);
|
||||
"%s=%s", elemname, strval);
|
||||
} else if (nvpair_type(elem) == DATA_TYPE_UINT64) {
|
||||
intval = fnvpair_value_uint64(elem);
|
||||
|
||||
@ -8902,7 +8948,7 @@ spa_sync_props(void *arg, dmu_tx_t *tx)
|
||||
spa->spa_pool_props_object, propname,
|
||||
8, 1, &intval, tx));
|
||||
spa_history_log_internal(spa, "set", tx,
|
||||
"%s=%lld", nvpair_name(elem),
|
||||
"%s=%lld", elemname,
|
||||
(longlong_t)intval);
|
||||
|
||||
switch (prop) {
|
||||
|
@ -481,7 +481,8 @@ tags = ['functional', 'cli_root', 'zpool_scrub']
|
||||
|
||||
[tests/functional/cli_root/zpool_set]
|
||||
tests = ['zpool_set_001_pos', 'zpool_set_002_neg', 'zpool_set_003_neg',
|
||||
'zpool_set_ashift', 'zpool_set_features']
|
||||
'zpool_set_ashift', 'zpool_set_features',
|
||||
'user_property_001_pos', 'user_property_002_neg']
|
||||
tags = ['functional', 'cli_root', 'zpool_set']
|
||||
|
||||
[tests/functional/cli_root/zpool_split]
|
||||
|
@ -1146,10 +1146,13 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
|
||||
functional/cli_root/zpool_set/cleanup.ksh \
|
||||
functional/cli_root/zpool_set/setup.ksh \
|
||||
functional/cli_root/zpool/setup.ksh \
|
||||
functional/cli_root/zpool_set/zpool_set_common.kshlib \
|
||||
functional/cli_root/zpool_set/zpool_set_001_pos.ksh \
|
||||
functional/cli_root/zpool_set/zpool_set_002_neg.ksh \
|
||||
functional/cli_root/zpool_set/zpool_set_003_neg.ksh \
|
||||
functional/cli_root/zpool_set/zpool_set_ashift.ksh \
|
||||
functional/cli_root/zpool_set/user_property_001_pos.ksh \
|
||||
functional/cli_root/zpool_set/user_property_002_neg.ksh \
|
||||
functional/cli_root/zpool_set/zpool_set_features.ksh \
|
||||
functional/cli_root/zpool_split/cleanup.ksh \
|
||||
functional/cli_root/zpool_split/setup.ksh \
|
||||
|
@ -0,0 +1,89 @@
|
||||
#!/bin/ksh -p
|
||||
#
|
||||
# CDDL HEADER START
|
||||
#
|
||||
# The contents of this file are subject to the terms of the
|
||||
# Common Development and Distribution License (the "License").
|
||||
# You may not use this file except in compliance with the License.
|
||||
#
|
||||
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
# or https://opensource.org/licenses/CDDL-1.0.
|
||||
# See the License for the specific language governing permissions
|
||||
# and limitations under the License.
|
||||
#
|
||||
# When distributing Covered Code, include this CDDL HEADER in each
|
||||
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
# If applicable, add the following below this CDDL HEADER, with the
|
||||
# fields enclosed by brackets "[]" replaced with your own identifying
|
||||
# information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
#
|
||||
# CDDL HEADER END
|
||||
#
|
||||
|
||||
#
|
||||
# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
|
||||
# Use is subject to license terms.
|
||||
#
|
||||
|
||||
#
|
||||
# Copyright (c) 2016 by Delphix. All rights reserved.
|
||||
# Copyright (c) 2023 by Klara Inc.
|
||||
#
|
||||
|
||||
. $STF_SUITE/tests/functional/cli_root/zpool_set/zpool_set_common.kshlib
|
||||
|
||||
#
|
||||
# DESCRIPTION:
|
||||
# ZFS can set any valid user-defined pool property.
|
||||
#
|
||||
# STRATEGY:
|
||||
# 1. Combine all kind of valid characters into a valid user-defined
|
||||
# property name.
|
||||
# 2. Random get a string as the value.
|
||||
# 3. Verify all the valid user-defined pool properties can be set to a
|
||||
# pool.
|
||||
#
|
||||
|
||||
verify_runnable "both"
|
||||
|
||||
log_assert "ZFS can set any valid user-defined pool property."
|
||||
log_onexit cleanup_user_prop $TESTPOOL
|
||||
|
||||
typeset -a names=()
|
||||
typeset -a values=()
|
||||
|
||||
# Longest property name (255 bytes, which is the 256-byte limit minus 1 byte
|
||||
# for the null byte)
|
||||
names+=("$(awk 'BEGIN { printf "x:"; while (c++ < (256 - 2 - 1)) printf "a" }')")
|
||||
values+=("long-property-name")
|
||||
# Longest property value (the limits are 1024 on FreeBSD and 4096 on Linux, so
|
||||
# pick the right one; the longest value can use limit minus 1 bytes for the
|
||||
# null byte)
|
||||
if is_linux; then
|
||||
typeset ZFS_MAXPROPLEN=4096
|
||||
else
|
||||
typeset ZFS_MAXPROPLEN=1024
|
||||
fi
|
||||
names+=("long:property:value")
|
||||
values+=("$(awk -v max="$ZFS_MAXPROPLEN" 'BEGIN { while (c++ < (max - 1)) printf "A" }')")
|
||||
# Valid property names
|
||||
for i in {1..10}; do
|
||||
typeset -i len
|
||||
((len = RANDOM % 32))
|
||||
names+=("$(valid_user_property $len)")
|
||||
((len = RANDOM % 512))
|
||||
values+=("$(user_property_value $len)")
|
||||
done
|
||||
|
||||
typeset -i i=0
|
||||
while ((i < ${#names[@]})); do
|
||||
typeset name="${names[$i]}"
|
||||
typeset value="${values[$i]}"
|
||||
|
||||
log_must eval "zpool set $name='$value' $TESTPOOL"
|
||||
log_must eval "check_user_prop $TESTPOOL $name '$value'"
|
||||
|
||||
((i += 1))
|
||||
done
|
||||
|
||||
log_pass "ZFS can set any valid user-defined pool property passed."
|
@ -0,0 +1,88 @@
|
||||
#!/bin/ksh -p
|
||||
#
|
||||
# CDDL HEADER START
|
||||
#
|
||||
# The contents of this file are subject to the terms of the
|
||||
# Common Development and Distribution License (the "License").
|
||||
# You may not use this file except in compliance with the License.
|
||||
#
|
||||
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
# or https://opensource.org/licenses/CDDL-1.0.
|
||||
# See the License for the specific language governing permissions
|
||||
# and limitations under the License.
|
||||
#
|
||||
# When distributing Covered Code, include this CDDL HEADER in each
|
||||
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
# If applicable, add the following below this CDDL HEADER, with the
|
||||
# fields enclosed by brackets "[]" replaced with your own identifying
|
||||
# information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
#
|
||||
# CDDL HEADER END
|
||||
#
|
||||
|
||||
#
|
||||
# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
|
||||
# Use is subject to license terms.
|
||||
#
|
||||
|
||||
#
|
||||
# Copyright (c) 2016 by Delphix. All rights reserved.
|
||||
# Copyright (c) 2023 by Klara Inc.
|
||||
#
|
||||
|
||||
. $STF_SUITE/tests/functional/cli_root/zpool_set/zpool_set_common.kshlib
|
||||
|
||||
#
|
||||
# DESCRIPTION:
|
||||
# ZFS can handle any invalid user-defined pool property.
|
||||
#
|
||||
# STRATEGY:
|
||||
# 1. Combine all kind of invalid user pool property names.
|
||||
# 2. Random get a string as the value.
|
||||
# 3. Verify all the invalid user-defined pool properties can not be set
|
||||
# to the pool.
|
||||
#
|
||||
|
||||
verify_runnable "both"
|
||||
|
||||
log_assert "ZFS can handle any invalid user pool property."
|
||||
log_onexit cleanup_user_prop $TESTPOOL
|
||||
|
||||
typeset -a names=()
|
||||
typeset -a values=()
|
||||
|
||||
# Too long property name (256 bytes, which is the 256-byte limit minus 1 byte
|
||||
# for the null byte plus 1 byte to reach back over the limit)
|
||||
names+=("$(awk 'BEGIN { printf "x:"; while (c++ < (256 - 2 - 1 + 1)) printf "a" }')")
|
||||
values+=("too-long-property-name")
|
||||
# Too long property value (the limits are 1024 on FreeBSD and 4096 on Linux, so
|
||||
# pick the right one; the too long value is, e.g., the limit minus 1 bytes for the
|
||||
# null byte plus 1 byte to reach back over the limit)
|
||||
if is_linux; then
|
||||
typeset ZFS_MAXPROPLEN=4096
|
||||
else
|
||||
typeset ZFS_MAXPROPLEN=1024
|
||||
fi
|
||||
names+=("too:long:property:value")
|
||||
values+=("$(awk -v max="$ZFS_MAXPROPLEN" 'BEGIN { while (c++ < (max - 1 + 1)) printf "A" }')")
|
||||
# Invalid property names
|
||||
for i in {1..10}; do
|
||||
typeset -i len
|
||||
((len = RANDOM % 32))
|
||||
names+=("$(invalid_user_property $len)")
|
||||
((len = RANDOM % 512))
|
||||
values+=("$(user_property_value $len)")
|
||||
done
|
||||
|
||||
typeset -i i=0
|
||||
while ((i < ${#names[@]})); do
|
||||
typeset name="${names[$i]}"
|
||||
typeset value="${values[$i]}"
|
||||
|
||||
log_mustnot zpool set $name=$value $TESTPOOL
|
||||
log_mustnot check_user_prop $TESTPOOL \"$name\" \"$value\"
|
||||
|
||||
((i += 1))
|
||||
done
|
||||
|
||||
log_pass "ZFS can handle invalid user pool property passed."
|
@ -0,0 +1,178 @@
|
||||
#
|
||||
# CDDL HEADER START
|
||||
#
|
||||
# The contents of this file are subject to the terms of the
|
||||
# Common Development and Distribution License (the "License").
|
||||
# You may not use this file except in compliance with the License.
|
||||
#
|
||||
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
# or https://opensource.org/licenses/CDDL-1.0.
|
||||
# See the License for the specific language governing permissions
|
||||
# and limitations under the License.
|
||||
#
|
||||
# When distributing Covered Code, include this CDDL HEADER in each
|
||||
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
# If applicable, add the following below this CDDL HEADER, with the
|
||||
# fields enclosed by brackets "[]" replaced with your own identifying
|
||||
# information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
#
|
||||
# CDDL HEADER END
|
||||
#
|
||||
|
||||
#
|
||||
# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
|
||||
# Use is subject to license terms.
|
||||
#
|
||||
|
||||
#
|
||||
# Copyright (c) 2014, 2016 by Delphix. All rights reserved.
|
||||
# Copyright (c) 2023 by Klara Inc.
|
||||
#
|
||||
|
||||
. $STF_SUITE/include/libtest.shlib
|
||||
|
||||
set -A VALID_NAME_CHAR a b c d e f g h i j k l m n o p q r s t u v w x y z \
|
||||
0 1 2 3 4 5 6 7 8 9 ':' '-' '.' '_'
|
||||
set -A INVALID_NAME_CHAR A B C D E F G H I J K L M N O P Q R S T U V W X Y Z \
|
||||
'`' '~' '!' '@' '#' '$' '%' '^' '&' '(' ')' '+' '=' '|' "\\" '{' '[' ']' \
|
||||
'}' ';' '"' '<' ',' '>' '?' '/' ' '
|
||||
set -A ALL_CHAR ${VALID_NAME_CHAR[*]} ${INVALID_NAME_CHAR[*]}
|
||||
|
||||
#
|
||||
# Cleanup all the user properties of the pool.
|
||||
#
|
||||
# $1 pool name
|
||||
#
|
||||
function cleanup_user_prop
|
||||
{
|
||||
typeset pool=$1
|
||||
|
||||
typeset user_prop
|
||||
user_prop=$(zpool get -H -o property all $pool | grep ":")
|
||||
|
||||
typeset prop
|
||||
for prop in $user_prop; do
|
||||
zpool set $prop="" $pool ||
|
||||
log_must zpool set $prop="" $pool
|
||||
done
|
||||
}
|
||||
|
||||
#
|
||||
# Random select character from the specified character set and combine into a
|
||||
# random string
|
||||
#
|
||||
# $1 character set name
|
||||
# $2 String length
|
||||
#
|
||||
function random_string
|
||||
{
|
||||
typeset char_set=${1:-VALID_NAME_CHAR}
|
||||
typeset -i len=${2:-5}
|
||||
|
||||
eval typeset -i count=\${#$char_set[@]}
|
||||
|
||||
# No consumers want an empty string.
|
||||
((len == 0)) && len=3
|
||||
|
||||
typeset str
|
||||
typeset -i i=0
|
||||
while ((i < len)); do
|
||||
typeset -i ind
|
||||
((ind = RANDOM % count))
|
||||
eval str=\${str}\${$char_set[\$ind]}
|
||||
|
||||
((i += 1))
|
||||
done
|
||||
|
||||
echo "$str"
|
||||
}
|
||||
|
||||
#
|
||||
# Get valid user-defined property name
|
||||
#
|
||||
# $1 user-defined property name length
|
||||
#
|
||||
function valid_user_property
|
||||
{
|
||||
typeset -i sumlen=${1:-10}
|
||||
((sumlen < 2 )) && sumlen=2
|
||||
typeset -i len
|
||||
((len = RANDOM % sumlen))
|
||||
typeset part1 part2
|
||||
|
||||
while true; do
|
||||
part1="$(random_string VALID_NAME_CHAR $len)"
|
||||
if [[ "$part1" == "-"* ]]; then
|
||||
continue
|
||||
fi
|
||||
break
|
||||
done
|
||||
((len = sumlen - (len + 1)))
|
||||
|
||||
while true; do
|
||||
part2="$(random_string VALID_NAME_CHAR $len)"
|
||||
if [[ -z $part1 && -z $part2 ]]; then
|
||||
continue
|
||||
fi
|
||||
break
|
||||
done
|
||||
|
||||
echo "${part1}:${part2}"
|
||||
}
|
||||
|
||||
#
|
||||
# Get invalid user-defined property name
|
||||
#
|
||||
# $1 user-defined property name length
|
||||
#
|
||||
function invalid_user_property
|
||||
{
|
||||
typeset -i sumlen=${1:-10}
|
||||
((sumlen == 0)) && sumlen=1
|
||||
typeset -i len
|
||||
((len = RANDOM % sumlen))
|
||||
|
||||
typeset part1 part2
|
||||
while true; do
|
||||
part1="$(random_string VALID_NAME_CHAR $len)"
|
||||
((len = sumlen - len))
|
||||
part2="$(random_string INVALID_NAME_CHAR $len)"
|
||||
|
||||
# Avoid $part1 is *:* and $part2 is "=*"
|
||||
if [[ "$part1" == *":"* && "$part2" == "="* ]]; then
|
||||
continue
|
||||
fi
|
||||
break
|
||||
done
|
||||
|
||||
echo "${part1}${part2}"
|
||||
}
|
||||
|
||||
#
|
||||
# Get user-defined property value
|
||||
#
|
||||
# $1 user-defined property name length
|
||||
#
|
||||
function user_property_value
|
||||
{
|
||||
typeset -i len=${1:-100}
|
||||
|
||||
random_string ALL_CHAR $len
|
||||
}
|
||||
|
||||
#
|
||||
# Check if the user-defined property is identical to the expected value.
|
||||
#
|
||||
# $1 pool
|
||||
# $2 user property
|
||||
# $3 expected value
|
||||
#
|
||||
function check_user_prop
|
||||
{
|
||||
typeset pool=$1
|
||||
typeset user_prop="$2"
|
||||
typeset expect_value="$3"
|
||||
typeset value=$(zpool get -p -H -o value "$user_prop" $pool 2>&1)
|
||||
|
||||
[ "$expect_value" = "$value" ]
|
||||
}
|
Loading…
Reference in New Issue
Block a user