summaryrefslogtreecommitdiff
path: root/vendor/github.com/mistifyio/go-zfs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/mistifyio/go-zfs')
-rw-r--r--vendor/github.com/mistifyio/go-zfs/LICENSE201
-rw-r--r--vendor/github.com/mistifyio/go-zfs/README.md54
-rw-r--r--vendor/github.com/mistifyio/go-zfs/error.go18
-rw-r--r--vendor/github.com/mistifyio/go-zfs/utils.go320
-rw-r--r--vendor/github.com/mistifyio/go-zfs/zfs.go382
-rw-r--r--vendor/github.com/mistifyio/go-zfs/zpool.go105
6 files changed, 1080 insertions, 0 deletions
diff --git a/vendor/github.com/mistifyio/go-zfs/LICENSE b/vendor/github.com/mistifyio/go-zfs/LICENSE
new file mode 100644
index 000000000..f4c265cfe
--- /dev/null
+++ b/vendor/github.com/mistifyio/go-zfs/LICENSE
@@ -0,0 +1,201 @@
+Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright (c) 2014, OmniTI Computer Consulting, Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License. \ No newline at end of file
diff --git a/vendor/github.com/mistifyio/go-zfs/README.md b/vendor/github.com/mistifyio/go-zfs/README.md
new file mode 100644
index 000000000..2515e588e
--- /dev/null
+++ b/vendor/github.com/mistifyio/go-zfs/README.md
@@ -0,0 +1,54 @@
+# Go Wrapper for ZFS #
+
+Simple wrappers for ZFS command line tools.
+
+[![GoDoc](https://godoc.org/github.com/mistifyio/go-zfs?status.svg)](https://godoc.org/github.com/mistifyio/go-zfs)
+
+## Requirements ##
+
+You need a working ZFS setup. To use on Ubuntu 14.04, setup ZFS:
+
+ sudo apt-get install python-software-properties
+ sudo apt-add-repository ppa:zfs-native/stable
+ sudo apt-get update
+ sudo apt-get install ubuntu-zfs libzfs-dev
+
+Developed using Go 1.3, but currently there isn't anything 1.3 specific. Don't use Ubuntu packages for Go, use http://golang.org/doc/install
+
+Generally you need root privileges to use anything zfs related.
+
+## Status ##
+
+This has been only been tested on Ubuntu 14.04
+
+In the future, we hope to work directly with libzfs.
+
+# Hacking #
+
+The tests have decent examples for most functions.
+
+```go
+//assuming a zpool named test
+//error handling ommitted
+
+
+f, err := zfs.CreateFilesystem("test/snapshot-test", nil)
+ok(t, err)
+
+s, err := f.Snapshot("test", nil)
+ok(t, err)
+
+// snapshot is named "test/snapshot-test@test"
+
+c, err := s.Clone("test/clone-test", nil)
+
+err := c.Destroy()
+err := s.Destroy()
+err := f.Destroy()
+
+```
+
+# Contributing #
+
+See the [contributing guidelines](./CONTRIBUTING.md)
+
diff --git a/vendor/github.com/mistifyio/go-zfs/error.go b/vendor/github.com/mistifyio/go-zfs/error.go
new file mode 100644
index 000000000..5408ccdb5
--- /dev/null
+++ b/vendor/github.com/mistifyio/go-zfs/error.go
@@ -0,0 +1,18 @@
+package zfs
+
+import (
+ "fmt"
+)
+
+// Error is an error which is returned when the `zfs` or `zpool` shell
+// commands return with a non-zero exit code.
+type Error struct {
+ Err error
+ Debug string
+ Stderr string
+}
+
+// Error returns the string representation of an Error.
+func (e Error) Error() string {
+ return fmt.Sprintf("%s: %q => %s", e.Err, e.Debug, e.Stderr)
+}
diff --git a/vendor/github.com/mistifyio/go-zfs/utils.go b/vendor/github.com/mistifyio/go-zfs/utils.go
new file mode 100644
index 000000000..d5b735349
--- /dev/null
+++ b/vendor/github.com/mistifyio/go-zfs/utils.go
@@ -0,0 +1,320 @@
+package zfs
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "os/exec"
+ "regexp"
+ "strconv"
+ "strings"
+)
+
+type command struct {
+ Command string
+ Stdin io.Reader
+ Stdout io.Writer
+}
+
+func (c *command) Run(arg ...string) ([][]string, error) {
+
+ cmd := exec.Command(c.Command, arg...)
+
+ var stdout, stderr bytes.Buffer
+
+ if c.Stdout == nil {
+ cmd.Stdout = &stdout
+ } else {
+ cmd.Stdout = c.Stdout
+ }
+
+ if c.Stdin != nil {
+ cmd.Stdin = c.Stdin
+
+ }
+ cmd.Stderr = &stderr
+
+ debug := strings.Join([]string{cmd.Path, strings.Join(cmd.Args, " ")}, " ")
+ if logger != nil {
+ logger.Log(cmd.Args)
+ }
+ err := cmd.Run()
+
+ if err != nil {
+ return nil, &Error{
+ Err: err,
+ Debug: debug,
+ Stderr: stderr.String(),
+ }
+ }
+
+ // assume if you passed in something for stdout, that you know what to do with it
+ if c.Stdout != nil {
+ return nil, nil
+ }
+
+ lines := strings.Split(stdout.String(), "\n")
+
+ //last line is always blank
+ lines = lines[0 : len(lines)-1]
+ output := make([][]string, len(lines))
+
+ for i, l := range lines {
+ output[i] = strings.Fields(l)
+ }
+
+ return output, nil
+}
+
+func setString(field *string, value string) {
+ v := ""
+ if value != "-" {
+ v = value
+ }
+ *field = v
+}
+
+func setUint(field *uint64, value string) error {
+ var v uint64
+ if value != "-" {
+ var err error
+ v, err = strconv.ParseUint(value, 10, 64)
+ if err != nil {
+ return err
+ }
+ }
+ *field = v
+ return nil
+}
+
+func (ds *Dataset) parseLine(line []string) error {
+ prop := line[1]
+ val := line[2]
+
+ var err error
+
+ switch prop {
+ case "available":
+ err = setUint(&ds.Avail, val)
+ case "compression":
+ setString(&ds.Compression, val)
+ case "mountpoint":
+ setString(&ds.Mountpoint, val)
+ case "quota":
+ err = setUint(&ds.Quota, val)
+ case "type":
+ setString(&ds.Type, val)
+ case "origin":
+ setString(&ds.Origin, val)
+ case "used":
+ err = setUint(&ds.Used, val)
+ case "volsize":
+ err = setUint(&ds.Volsize, val)
+ case "written":
+ err = setUint(&ds.Written, val)
+ case "logicalused":
+ err = setUint(&ds.Logicalused, val)
+ }
+ return err
+}
+
+/*
+ * from zfs diff`s escape function:
+ *
+ * Prints a file name out a character at a time. If the character is
+ * not in the range of what we consider "printable" ASCII, display it
+ * as an escaped 3-digit octal value. ASCII values less than a space
+ * are all control characters and we declare the upper end as the
+ * DELete character. This also is the last 7-bit ASCII character.
+ * We choose to treat all 8-bit ASCII as not printable for this
+ * application.
+ */
+func unescapeFilepath(path string) (string, error) {
+ buf := make([]byte, 0, len(path))
+ llen := len(path)
+ for i := 0; i < llen; {
+ if path[i] == '\\' {
+ if llen < i+4 {
+ return "", fmt.Errorf("Invalid octal code: too short")
+ }
+ octalCode := path[(i + 1):(i + 4)]
+ val, err := strconv.ParseUint(octalCode, 8, 8)
+ if err != nil {
+ return "", fmt.Errorf("Invalid octal code: %v", err)
+ }
+ buf = append(buf, byte(val))
+ i += 4
+ } else {
+ buf = append(buf, path[i])
+ i++
+ }
+ }
+ return string(buf), nil
+}
+
+var changeTypeMap = map[string]ChangeType{
+ "-": Removed,
+ "+": Created,
+ "M": Modified,
+ "R": Renamed,
+}
+var inodeTypeMap = map[string]InodeType{
+ "B": BlockDevice,
+ "C": CharacterDevice,
+ "/": Directory,
+ ">": Door,
+ "|": NamedPipe,
+ "@": SymbolicLink,
+ "P": EventPort,
+ "=": Socket,
+ "F": File,
+}
+
+// matches (+1) or (-1)
+var referenceCountRegex = regexp.MustCompile("\\(([+-]\\d+?)\\)")
+
+func parseReferenceCount(field string) (int, error) {
+ matches := referenceCountRegex.FindStringSubmatch(field)
+ if matches == nil {
+ return 0, fmt.Errorf("Regexp does not match")
+ }
+ return strconv.Atoi(matches[1])
+}
+
+func parseInodeChange(line []string) (*InodeChange, error) {
+ llen := len(line)
+ if llen < 1 {
+ return nil, fmt.Errorf("Empty line passed")
+ }
+
+ changeType := changeTypeMap[line[0]]
+ if changeType == 0 {
+ return nil, fmt.Errorf("Unknown change type '%s'", line[0])
+ }
+
+ switch changeType {
+ case Renamed:
+ if llen != 4 {
+ return nil, fmt.Errorf("Mismatching number of fields: expect 4, got: %d", llen)
+ }
+ case Modified:
+ if llen != 4 && llen != 3 {
+ return nil, fmt.Errorf("Mismatching number of fields: expect 3..4, got: %d", llen)
+ }
+ default:
+ if llen != 3 {
+ return nil, fmt.Errorf("Mismatching number of fields: expect 3, got: %d", llen)
+ }
+ }
+
+ inodeType := inodeTypeMap[line[1]]
+ if inodeType == 0 {
+ return nil, fmt.Errorf("Unknown inode type '%s'", line[1])
+ }
+
+ path, err := unescapeFilepath(line[2])
+ if err != nil {
+ return nil, fmt.Errorf("Failed to parse filename: %v", err)
+ }
+
+ var newPath string
+ var referenceCount int
+ switch changeType {
+ case Renamed:
+ newPath, err = unescapeFilepath(line[3])
+ if err != nil {
+ return nil, fmt.Errorf("Failed to parse filename: %v", err)
+ }
+ case Modified:
+ if llen == 4 {
+ referenceCount, err = parseReferenceCount(line[3])
+ if err != nil {
+ return nil, fmt.Errorf("Failed to parse reference count: %v", err)
+ }
+ }
+ default:
+ newPath = ""
+ }
+
+ return &InodeChange{
+ Change: changeType,
+ Type: inodeType,
+ Path: path,
+ NewPath: newPath,
+ ReferenceCountChange: referenceCount,
+ }, nil
+}
+
+// example input
+//M / /testpool/bar/
+//+ F /testpool/bar/hello.txt
+//M / /testpool/bar/hello.txt (+1)
+//M / /testpool/bar/hello-hardlink
+func parseInodeChanges(lines [][]string) ([]*InodeChange, error) {
+ changes := make([]*InodeChange, len(lines))
+
+ for i, line := range lines {
+ c, err := parseInodeChange(line)
+ if err != nil {
+ return nil, fmt.Errorf("Failed to parse line %d of zfs diff: %v, got: '%s'", i, err, line)
+ }
+ changes[i] = c
+ }
+ return changes, nil
+}
+
+func listByType(t, filter string) ([]*Dataset, error) {
+ args := []string{"get", "-rHp", "-t", t, "all"}
+ if filter != "" {
+ args = append(args, filter)
+ }
+ out, err := zfs(args...)
+ if err != nil {
+ return nil, err
+ }
+
+ var datasets []*Dataset
+
+ name := ""
+ var ds *Dataset
+ for _, line := range out {
+ if name != line[0] {
+ name = line[0]
+ ds = &Dataset{Name: name}
+ datasets = append(datasets, ds)
+ }
+ if err := ds.parseLine(line); err != nil {
+ return nil, err
+ }
+ }
+
+ return datasets, nil
+}
+
+func propsSlice(properties map[string]string) []string {
+ args := make([]string, 0, len(properties)*3)
+ for k, v := range properties {
+ args = append(args, "-o")
+ args = append(args, fmt.Sprintf("%s=%s", k, v))
+ }
+ return args
+}
+
+func (z *Zpool) parseLine(line []string) error {
+ prop := line[1]
+ val := line[2]
+
+ var err error
+
+ switch prop {
+ case "health":
+ setString(&z.Health, val)
+ case "allocated":
+ err = setUint(&z.Allocated, val)
+ case "size":
+ err = setUint(&z.Size, val)
+ case "free":
+ err = setUint(&z.Free, val)
+ }
+ return err
+}
diff --git a/vendor/github.com/mistifyio/go-zfs/zfs.go b/vendor/github.com/mistifyio/go-zfs/zfs.go
new file mode 100644
index 000000000..a1d740e07
--- /dev/null
+++ b/vendor/github.com/mistifyio/go-zfs/zfs.go
@@ -0,0 +1,382 @@
+// Package zfs provides wrappers around the ZFS command line tools.
+package zfs
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "strconv"
+ "strings"
+)
+
+// ZFS dataset types, which can indicate if a dataset is a filesystem,
+// snapshot, or volume.
+const (
+ DatasetFilesystem = "filesystem"
+ DatasetSnapshot = "snapshot"
+ DatasetVolume = "volume"
+)
+
+// Dataset is a ZFS dataset. A dataset could be a clone, filesystem, snapshot,
+// or volume. The Type struct member can be used to determine a dataset's type.
+//
+// The field definitions can be found in the ZFS manual:
+// http://www.freebsd.org/cgi/man.cgi?zfs(8).
+type Dataset struct {
+ Name string
+ Origin string
+ Used uint64
+ Avail uint64
+ Mountpoint string
+ Compression string
+ Type string
+ Written uint64
+ Volsize uint64
+ Usedbydataset uint64
+ Logicalused uint64
+ Quota uint64
+}
+
+// InodeType is the type of inode as reported by Diff
+type InodeType int
+
+// Types of Inodes
+const (
+ _ = iota // 0 == unknown type
+ BlockDevice InodeType = iota
+ CharacterDevice
+ Directory
+ Door
+ NamedPipe
+ SymbolicLink
+ EventPort
+ Socket
+ File
+)
+
+// ChangeType is the type of inode change as reported by Diff
+type ChangeType int
+
+// Types of Changes
+const (
+ _ = iota // 0 == unknown type
+ Removed ChangeType = iota
+ Created
+ Modified
+ Renamed
+)
+
+// DestroyFlag is the options flag passed to Destroy
+type DestroyFlag int
+
+// Valid destroy options
+const (
+ DestroyDefault DestroyFlag = 1 << iota
+ DestroyRecursive = 1 << iota
+ DestroyRecursiveClones = 1 << iota
+ DestroyDeferDeletion = 1 << iota
+ DestroyForceUmount = 1 << iota
+)
+
+// InodeChange represents a change as reported by Diff
+type InodeChange struct {
+ Change ChangeType
+ Type InodeType
+ Path string
+ NewPath string
+ ReferenceCountChange int
+}
+
+// Logger can be used to log commands/actions
+type Logger interface {
+ Log(cmd []string)
+}
+
+var logger Logger
+
+// SetLogger set a log handler to log all commands including arguments before
+// they are executed
+func SetLogger(l Logger) {
+ logger = l
+}
+
+// zfs is a helper function to wrap typical calls to zfs.
+func zfs(arg ...string) ([][]string, error) {
+ c := command{Command: "zfs"}
+ return c.Run(arg...)
+}
+
+// Datasets returns a slice of ZFS datasets, regardless of type.
+// A filter argument may be passed to select a dataset with the matching name,
+// or empty string ("") may be used to select all datasets.
+func Datasets(filter string) ([]*Dataset, error) {
+ return listByType("all", filter)
+}
+
+// Snapshots returns a slice of ZFS snapshots.
+// A filter argument may be passed to select a snapshot with the matching name,
+// or empty string ("") may be used to select all snapshots.
+func Snapshots(filter string) ([]*Dataset, error) {
+ return listByType(DatasetSnapshot, filter)
+}
+
+// Filesystems returns a slice of ZFS filesystems.
+// A filter argument may be passed to select a filesystem with the matching name,
+// or empty string ("") may be used to select all filesystems.
+func Filesystems(filter string) ([]*Dataset, error) {
+ return listByType(DatasetFilesystem, filter)
+}
+
+// Volumes returns a slice of ZFS volumes.
+// A filter argument may be passed to select a volume with the matching name,
+// or empty string ("") may be used to select all volumes.
+func Volumes(filter string) ([]*Dataset, error) {
+ return listByType(DatasetVolume, filter)
+}
+
+// GetDataset retrieves a single ZFS dataset by name. This dataset could be
+// any valid ZFS dataset type, such as a clone, filesystem, snapshot, or volume.
+func GetDataset(name string) (*Dataset, error) {
+ out, err := zfs("get", "-Hp", "all", name)
+ if err != nil {
+ return nil, err
+ }
+
+ ds := &Dataset{Name: name}
+ for _, line := range out {
+ if err := ds.parseLine(line); err != nil {
+ return nil, err
+ }
+ }
+
+ return ds, nil
+}
+
+// Clone clones a ZFS snapshot and returns a clone dataset.
+// An error will be returned if the input dataset is not of snapshot type.
+func (d *Dataset) Clone(dest string, properties map[string]string) (*Dataset, error) {
+ if d.Type != DatasetSnapshot {
+ return nil, errors.New("can only clone snapshots")
+ }
+ args := make([]string, 2, 4)
+ args[0] = "clone"
+ args[1] = "-p"
+ if properties != nil {
+ args = append(args, propsSlice(properties)...)
+ }
+ args = append(args, []string{d.Name, dest}...)
+ _, err := zfs(args...)
+ if err != nil {
+ return nil, err
+ }
+ return GetDataset(dest)
+}
+
+// ReceiveSnapshot receives a ZFS stream from the input io.Reader, creates a
+// new snapshot with the specified name, and streams the input data into the
+// newly-created snapshot.
+func ReceiveSnapshot(input io.Reader, name string) (*Dataset, error) {
+ c := command{Command: "zfs", Stdin: input}
+ _, err := c.Run("receive", name)
+ if err != nil {
+ return nil, err
+ }
+ return GetDataset(name)
+}
+
+// SendSnapshot sends a ZFS stream of a snapshot to the input io.Writer.
+// An error will be returned if the input dataset is not of snapshot type.
+func (d *Dataset) SendSnapshot(output io.Writer) error {
+ if d.Type != DatasetSnapshot {
+ return errors.New("can only send snapshots")
+ }
+
+ c := command{Command: "zfs", Stdout: output}
+ _, err := c.Run("send", d.Name)
+ return err
+}
+
+// CreateVolume creates a new ZFS volume with the specified name, size, and
+// properties.
+// A full list of available ZFS properties may be found here:
+// https://www.freebsd.org/cgi/man.cgi?zfs(8).
+func CreateVolume(name string, size uint64, properties map[string]string) (*Dataset, error) {
+ args := make([]string, 4, 5)
+ args[0] = "create"
+ args[1] = "-p"
+ args[2] = "-V"
+ args[3] = strconv.FormatUint(size, 10)
+ if properties != nil {
+ args = append(args, propsSlice(properties)...)
+ }
+ args = append(args, name)
+ _, err := zfs(args...)
+ if err != nil {
+ return nil, err
+ }
+ return GetDataset(name)
+}
+
+// Destroy destroys a ZFS dataset. If the destroy bit flag is set, any
+// descendents of the dataset will be recursively destroyed, including snapshots.
+// If the deferred bit flag is set, the snapshot is marked for deferred
+// deletion.
+func (d *Dataset) Destroy(flags DestroyFlag) error {
+ args := make([]string, 1, 3)
+ args[0] = "destroy"
+ if flags&DestroyRecursive != 0 {
+ args = append(args, "-r")
+ }
+
+ if flags&DestroyRecursiveClones != 0 {
+ args = append(args, "-R")
+ }
+
+ if flags&DestroyDeferDeletion != 0 {
+ args = append(args, "-d")
+ }
+
+ if flags&DestroyForceUmount != 0 {
+ args = append(args, "-f")
+ }
+
+ args = append(args, d.Name)
+ _, err := zfs(args...)
+ return err
+}
+
+// SetProperty sets a ZFS property on the receiving dataset.
+// A full list of available ZFS properties may be found here:
+// https://www.freebsd.org/cgi/man.cgi?zfs(8).
+func (d *Dataset) SetProperty(key, val string) error {
+ prop := strings.Join([]string{key, val}, "=")
+ _, err := zfs("set", prop, d.Name)
+ return err
+}
+
+// GetProperty returns the current value of a ZFS property from the
+// receiving dataset.
+// A full list of available ZFS properties may be found here:
+// https://www.freebsd.org/cgi/man.cgi?zfs(8).
+func (d *Dataset) GetProperty(key string) (string, error) {
+ out, err := zfs("get", key, d.Name)
+ if err != nil {
+ return "", err
+ }
+
+ return out[0][2], nil
+}
+
+// Snapshots returns a slice of all ZFS snapshots of a given dataset.
+func (d *Dataset) Snapshots() ([]*Dataset, error) {
+ return Snapshots(d.Name)
+}
+
+// CreateFilesystem creates a new ZFS filesystem with the specified name and
+// properties.
+// A full list of available ZFS properties may be found here:
+// https://www.freebsd.org/cgi/man.cgi?zfs(8).
+func CreateFilesystem(name string, properties map[string]string) (*Dataset, error) {
+ args := make([]string, 1, 4)
+ args[0] = "create"
+
+ if properties != nil {
+ args = append(args, propsSlice(properties)...)
+ }
+
+ args = append(args, name)
+ _, err := zfs(args...)
+ if err != nil {
+ return nil, err
+ }
+ return GetDataset(name)
+}
+
+// Snapshot creates a new ZFS snapshot of the receiving dataset, using the
+// specified name. Optionally, the snapshot can be taken recursively, creating
+// snapshots of all descendent filesystems in a single, atomic operation.
+func (d *Dataset) Snapshot(name string, recursive bool) (*Dataset, error) {
+ args := make([]string, 1, 4)
+ args[0] = "snapshot"
+ if recursive {
+ args = append(args, "-r")
+ }
+ snapName := fmt.Sprintf("%s@%s", d.Name, name)
+ args = append(args, snapName)
+ _, err := zfs(args...)
+ if err != nil {
+ return nil, err
+ }
+ return GetDataset(snapName)
+}
+
+// Rollback rolls back the receiving ZFS dataset to a previous snapshot.
+// Optionally, intermediate snapshots can be destroyed. A ZFS snapshot
+// rollback cannot be completed without this option, if more recent
+// snapshots exist.
+// An error will be returned if the input dataset is not of snapshot type.
+func (d *Dataset) Rollback(destroyMoreRecent bool) error {
+ if d.Type != DatasetSnapshot {
+ return errors.New("can only rollback snapshots")
+ }
+
+ args := make([]string, 1, 3)
+ args[0] = "rollback"
+ if destroyMoreRecent {
+ args = append(args, "-r")
+ }
+ args = append(args, d.Name)
+
+ _, err := zfs(args...)
+ return err
+}
+
+// Children returns a slice of children of the receiving ZFS dataset.
+// A recursion depth may be specified, or a depth of 0 allows unlimited
+// recursion.
+func (d *Dataset) Children(depth uint64) ([]*Dataset, error) {
+ args := []string{"get", "-t", "all", "-Hp", "all"}
+ if depth > 0 {
+ args = append(args, "-d")
+ args = append(args, strconv.FormatUint(depth, 10))
+ } else {
+ args = append(args, "-r")
+ }
+ args = append(args, d.Name)
+
+ out, err := zfs(args...)
+ if err != nil {
+ return nil, err
+ }
+
+ var datasets []*Dataset
+ name := ""
+ var ds *Dataset
+ for _, line := range out {
+ if name != line[0] {
+ name = line[0]
+ ds = &Dataset{Name: name}
+ datasets = append(datasets, ds)
+ }
+ if err := ds.parseLine(line); err != nil {
+ return nil, err
+ }
+ }
+ return datasets[1:], nil
+}
+
+// Diff returns changes between a snapshot and the given ZFS dataset.
+// The snapshot name must include the filesystem part as it is possible to
+// compare clones with their origin snapshots.
+func (d *Dataset) Diff(snapshot string) ([]*InodeChange, error) {
+ args := []string{"diff", "-FH", snapshot, d.Name}[:]
+ out, err := zfs(args...)
+ if err != nil {
+ return nil, err
+ }
+ inodeChanges, err := parseInodeChanges(out)
+ if err != nil {
+ return nil, err
+ }
+ return inodeChanges, nil
+}
diff --git a/vendor/github.com/mistifyio/go-zfs/zpool.go b/vendor/github.com/mistifyio/go-zfs/zpool.go
new file mode 100644
index 000000000..6ba52d30c
--- /dev/null
+++ b/vendor/github.com/mistifyio/go-zfs/zpool.go
@@ -0,0 +1,105 @@
+package zfs
+
+// ZFS zpool states, which can indicate if a pool is online, offline,
+// degraded, etc. More information regarding zpool states can be found here:
+// https://docs.oracle.com/cd/E19253-01/819-5461/gamno/index.html.
+const (
+ ZpoolOnline = "ONLINE"
+ ZpoolDegraded = "DEGRADED"
+ ZpoolFaulted = "FAULTED"
+ ZpoolOffline = "OFFLINE"
+ ZpoolUnavail = "UNAVAIL"
+ ZpoolRemoved = "REMOVED"
+)
+
+// Zpool is a ZFS zpool. A pool is a top-level structure in ZFS, and can
+// contain many descendent datasets.
+type Zpool struct {
+ Name string
+ Health string
+ Allocated uint64
+ Size uint64
+ Free uint64
+}
+
+// zpool is a helper function to wrap typical calls to zpool.
+func zpool(arg ...string) ([][]string, error) {
+ c := command{Command: "zpool"}
+ return c.Run(arg...)
+}
+
+// GetZpool retrieves a single ZFS zpool by name.
+func GetZpool(name string) (*Zpool, error) {
+ out, err := zpool("get", "all", "-p", name)
+ if err != nil {
+ return nil, err
+ }
+
+ // there is no -H
+ out = out[1:]
+
+ z := &Zpool{Name: name}
+ for _, line := range out {
+ if err := z.parseLine(line); err != nil {
+ return nil, err
+ }
+ }
+
+ return z, nil
+}
+
+// Datasets returns a slice of all ZFS datasets in a zpool.
+func (z *Zpool) Datasets() ([]*Dataset, error) {
+ return Datasets(z.Name)
+}
+
+// Snapshots returns a slice of all ZFS snapshots in a zpool.
+func (z *Zpool) Snapshots() ([]*Dataset, error) {
+ return Snapshots(z.Name)
+}
+
+// CreateZpool creates a new ZFS zpool with the specified name, properties,
+// and optional arguments.
+// A full list of available ZFS properties and command-line arguments may be
+// found here: https://www.freebsd.org/cgi/man.cgi?zfs(8).
+func CreateZpool(name string, properties map[string]string, args ...string) (*Zpool, error) {
+ cli := make([]string, 1, 4)
+ cli[0] = "create"
+ if properties != nil {
+ cli = append(cli, propsSlice(properties)...)
+ }
+ cli = append(cli, name)
+ cli = append(cli, args...)
+ _, err := zpool(cli...)
+ if err != nil {
+ return nil, err
+ }
+
+ return &Zpool{Name: name}, nil
+}
+
+// Destroy destroys a ZFS zpool by name.
+func (z *Zpool) Destroy() error {
+ _, err := zpool("destroy", z.Name)
+ return err
+}
+
+// ListZpools list all ZFS zpools accessible on the current system.
+func ListZpools() ([]*Zpool, error) {
+ args := []string{"list", "-Ho", "name"}
+ out, err := zpool(args...)
+ if err != nil {
+ return nil, err
+ }
+
+ var pools []*Zpool
+
+ for _, line := range out {
+ z, err := GetZpool(line[0])
+ if err != nil {
+ return nil, err
+ }
+ pools = append(pools, z)
+ }
+ return pools, nil
+}