diff --git a/lxd/storage/drivers/driver_zfs.go b/lxd/storage/drivers/driver_zfs.go index 761832529aee..c9b06be28e61 100644 --- a/lxd/storage/drivers/driver_zfs.go +++ b/lxd/storage/drivers/driver_zfs.go @@ -144,19 +144,26 @@ func (d zfs) ensureInitialDatasets(warnOnExistingPolicyApplyError bool) error { args = append(args, fmt.Sprintf("%s=%s", k, v)) } - err := d.setDatasetProperties(d.config["zfs.pool_name"], args...) + args, err := d.filterRedundantOptions(d.config["zfs.pool_name"], args...) if err != nil { - if warnOnExistingPolicyApplyError { + return err + } + + if len(args) > 0 { + err := d.setDatasetProperties(d.config["zfs.pool_name"], args...) + if err != nil { + if !warnOnExistingPolicyApplyError { + return fmt.Errorf("Failed applying policy to existing dataset %q: %w", d.config["zfs.pool_name"], err) + } + d.logger.Warn("Failed applying policy to existing dataset", logger.Ctx{"dataset": d.config["zfs.pool_name"], "err": err}) - } else { - return fmt.Errorf("Failed applying policy to existing dataset %q: %w", d.config["zfs.pool_name"], err) } } for _, dataset := range d.initialDatasets() { - properties := []string{"mountpoint=legacy"} + args := []string{"mountpoint=legacy"} if shared.ValueInSlice(dataset, []string{"virtual-machines", "deleted/virtual-machines"}) { - properties = append(properties, "volmode=none") + args = append(args, "volmode=none") } datasetPath := filepath.Join(d.config["zfs.pool_name"], dataset) @@ -166,16 +173,23 @@ func (d zfs) ensureInitialDatasets(warnOnExistingPolicyApplyError bool) error { } if exists { - err = d.setDatasetProperties(datasetPath, properties...) + args, err = d.filterRedundantOptions(datasetPath, args...) if err != nil { - if warnOnExistingPolicyApplyError { + return err + } + + if len(args) > 0 { + err = d.setDatasetProperties(datasetPath, args...) + if err != nil { + if !warnOnExistingPolicyApplyError { + return fmt.Errorf("Failed applying policy to existing dataset %q: %w", datasetPath, err) + } + d.logger.Warn("Failed applying policy to existing dataset", logger.Ctx{"dataset": datasetPath, "err": err}) - } else { - return fmt.Errorf("Failed applying policy to existing dataset %q: %w", datasetPath, err) } } } else { - err = d.createDataset(datasetPath, properties...) + err = d.createDataset(datasetPath, args...) if err != nil { return fmt.Errorf("Failed creating dataset %q: %w", datasetPath, err) } diff --git a/lxd/storage/drivers/driver_zfs_utils.go b/lxd/storage/drivers/driver_zfs_utils.go index 7bf8b183b7d7..3dea19da1869 100644 --- a/lxd/storage/drivers/driver_zfs_utils.go +++ b/lxd/storage/drivers/driver_zfs_utils.go @@ -194,6 +194,44 @@ func (d *zfs) getDatasets(dataset string, types string) ([]string, error) { return children, nil } +// filterRedundantOptions filters out options for setting dataset properties that match with the values already set. +func (d *zfs) filterRedundantOptions(dataset string, options ...string) ([]string, error) { + var keys, values []string + + // Extract keys and values from options. + for _, option := range options { + property := strings.Split(option, "=") + + if len(property) != 2 { + return nil, fmt.Errorf("Wrongly formatted option %q", option) + } + + keys = append(keys, property[0]) + values = append(values, property[1]) + } + + // Get current values for the keys. + currentProperties, err := d.getDatasetProperties(dataset, keys...) + if err != nil { + return nil, err + } + + var resultantOptions []string + + // Change property values that are different from the current value. + for propertyIndex := range keys { + if currentProperties[keys[propertyIndex]] == "posix" && values[propertyIndex] == "posixacl" { // "posixacl" is an alias for "posix" + continue + } + + if currentProperties[keys[propertyIndex]] != values[propertyIndex] { + resultantOptions = append(resultantOptions, options[propertyIndex]) + } + } + + return resultantOptions, nil +} + func (d *zfs) setDatasetProperties(dataset string, options ...string) error { args := []string{"set"} args = append(args, options...)