-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(zfs): Add local ZFS CLI parsing
Preparing for removal of go-zfs dependency.
- Loading branch information
Showing
3 changed files
with
203 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package zfs | ||
|
||
import ( | ||
"strings" | ||
) | ||
|
||
// DatasetKind enum of supported dataset types | ||
type DatasetKind string | ||
|
||
const ( | ||
// DatasetFilesystem enum entry | ||
DatasetFilesystem DatasetKind = `filesystem` | ||
// DatasetVolume enum entry | ||
DatasetVolume DatasetKind = `volume` | ||
// DatasetSnapshot enum entry | ||
DatasetSnapshot DatasetKind = `snapshot` | ||
) | ||
|
||
// Dataset holds the properties for an individual dataset | ||
type Dataset struct { | ||
Pool string | ||
Name string | ||
Properties map[string]string | ||
} | ||
|
||
// DatasetProperties returns the requested properties for all datasets in the given pool | ||
func DatasetProperties(pool string, kind DatasetKind, properties ...string) ([]Dataset, error) { | ||
handler := newDatasetHandler() | ||
if err := execute(pool, handler, `zfs`, `get`, `-Hprt`, string(kind), `-o`, `name,property,value`, strings.Join(properties, `,`)); err != nil { | ||
return nil, err | ||
} | ||
return handler.datasets(), nil | ||
} | ||
|
||
// datasetHandler handles parsing of the data returned from the CLI into Dataset structs | ||
type datasetHandler struct { | ||
store map[string]Dataset | ||
} | ||
|
||
// processLine implements the handler interface | ||
func (h *datasetHandler) processLine(pool string, line []string) error { | ||
if len(line) != 3 { | ||
return ErrInvalidOutput | ||
} | ||
if _, ok := h.store[line[0]]; !ok { | ||
h.store[line[0]] = newDataset(pool, line[0]) | ||
} | ||
h.store[line[0]].Properties[line[1]] = line[2] | ||
return nil | ||
} | ||
|
||
func (h *datasetHandler) datasets() []Dataset { | ||
result := make([]Dataset, len(h.store)) | ||
i := 0 | ||
for _, dataset := range h.store { | ||
result[i] = dataset | ||
i++ | ||
} | ||
return result | ||
} | ||
|
||
func newDataset(pool string, name string) Dataset { | ||
return Dataset{ | ||
Pool: pool, | ||
Name: name, | ||
Properties: make(map[string]string), | ||
} | ||
} | ||
|
||
func newDatasetHandler() *datasetHandler { | ||
return &datasetHandler{store: make(map[string]Dataset)} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package zfs | ||
|
||
import ( | ||
"bufio" | ||
"os/exec" | ||
"strings" | ||
) | ||
|
||
// PoolStatus enum contains status text | ||
type PoolStatus string | ||
|
||
const ( | ||
// PoolOnline enum entry | ||
PoolOnline PoolStatus = `ONLINE` | ||
// PoolDegraded enum entry | ||
PoolDegraded PoolStatus = `DEGRADED` | ||
// PoolFaulted enum entry | ||
PoolFaulted PoolStatus = `FAULTED` | ||
// PoolOffline enum entry | ||
PoolOffline PoolStatus = `OFFLINE` | ||
// PoolUnavail enum entry | ||
PoolUnavail PoolStatus = `UNAVAIL` | ||
// PoolRemoved enum entry | ||
PoolRemoved PoolStatus = `REMOVED` | ||
) | ||
|
||
// Pool holds the properties for an individual pool | ||
type Pool struct { | ||
Name string | ||
Properties map[string]string | ||
} | ||
|
||
// processLine implements the handler interface | ||
func (p Pool) processLine(pool string, line []string) error { | ||
if len(line) != 3 || line[0] != pool { | ||
return ErrInvalidOutput | ||
} | ||
p.Properties[line[1]] = line[2] | ||
|
||
return nil | ||
} | ||
|
||
// PoolNames returns a list of available pool names | ||
func PoolNames() ([]string, error) { | ||
pools := make([]string, 0) | ||
cmd := exec.Command(`zpool`, `list`, `-Ho`, `name`) | ||
out, err := cmd.StdoutPipe() | ||
if err != nil { | ||
return nil, err | ||
} | ||
scanner := bufio.NewScanner(out) | ||
|
||
if err = cmd.Start(); err != nil { | ||
return nil, err | ||
} | ||
|
||
for scanner.Scan() { | ||
pools = append(pools, scanner.Text()) | ||
} | ||
if err = cmd.Wait(); err != nil { | ||
return nil, err | ||
} | ||
|
||
return pools, nil | ||
} | ||
|
||
// PoolProperties returns the requested properties for the given pool | ||
func PoolProperties(pool string, properties ...string) (Pool, error) { | ||
handler := newPool(pool) | ||
if err := execute(pool, handler, `zpool`, `get`, `-Hpo`, `name,property,value`, strings.Join(properties, `,`)); err != nil { | ||
return handler, err | ||
} | ||
return handler, nil | ||
} | ||
|
||
func newPool(name string) Pool { | ||
return Pool{ | ||
Name: name, | ||
Properties: make(map[string]string), | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package zfs | ||
|
||
import ( | ||
"encoding/csv" | ||
"errors" | ||
"io" | ||
"os/exec" | ||
) | ||
|
||
var ( | ||
// ErrInvalidOutput is returned on unparseable CLI output | ||
ErrInvalidOutput = errors.New(`Invalid output executing command`) | ||
) | ||
|
||
type handler interface { | ||
processLine(pool string, line []string) error | ||
} | ||
|
||
func execute(pool string, h handler, cmd string, args ...string) error { | ||
c := exec.Command(cmd, append(args, pool)...) | ||
out, err := c.StdoutPipe() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
r := csv.NewReader(out) | ||
r.Comma = '\t' | ||
r.LazyQuotes = true | ||
r.ReuseRecord = true | ||
r.FieldsPerRecord = 3 | ||
|
||
if err = c.Start(); err != nil { | ||
return err | ||
} | ||
|
||
for { | ||
line, err := r.Read() | ||
if err == io.EOF { | ||
break | ||
} | ||
if err != nil { | ||
return err | ||
} | ||
if err = h.processLine(pool, line); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return c.Wait() | ||
} |