Add datastore to snapshots, force Datastore display when jail exist on multi datastores

This commit is contained in:
yo 2022-06-18 18:24:09 +02:00
parent 1c04f62ed8
commit 9218ffafe1
6 changed files with 145 additions and 92 deletions

View File

@ -3,7 +3,6 @@ package cmd
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/spf13/viper"
"gocage/jail" "gocage/jail"
"io/ioutil" "io/ioutil"
"log" "log"
@ -40,12 +39,12 @@ func ListJailsProps(args []string) {
* into gJails global var * into gJails global var
*******************************************************************************/ *******************************************************************************/
func ListJails(args []string, display bool) { func ListJails(args []string, display bool) {
fields := strings.Split(gDisplayJColumns, ",") for _, ds := range gDatastores {
listJailsFromDatastore(ds)
for _, d := range viper.GetStringSlice("datastore") {
listJailsFromDatastore(d)
} }
fields := strings.Split(gDisplayJColumns, ",")
// This is the structure we will filter, then display // This is the structure we will filter, then display
var jails []Jail var jails []Jail
@ -91,7 +90,6 @@ func ListJails(args []string, display bool) {
for _, j := range jails { for _, j := range jails {
if j.Name == a { if j.Name == a {
js = append(js, j) js = append(js, j)
break
} }
} }
} }
@ -150,17 +148,17 @@ func ListJails(args []string, display bool) {
} }
} }
func listJailsFromDatastore(datastore string) { func listJailsFromDatastore(ds Datastore) {
fileInfo, err := os.Stat(datastore) fileInfo, err := os.Stat(ds.Mountpoint)
if err != nil { if err != nil {
log.Fatalln(fmt.Sprintf("Unable to access %s, check path and/or rights", datastore)) log.Fatalln(fmt.Sprintf("Unable to access %s, check path and/or rights", ds.Mountpoint))
} }
if fileInfo.IsDir() == false { if fileInfo.IsDir() == false {
log.Fatalln(fmt.Sprintf("%s is not a directory", datastore)) log.Fatalln(fmt.Sprintf("%s is not a directory", ds.Mountpoint))
} }
// A datastore have to contain a "jails" directory // A datastore have to contain a "jails" directory
jailsDir := fmt.Sprintf("%s/jails", datastore) jailsDir := fmt.Sprintf("%s/jails", ds.Mountpoint)
fileInfo, err = os.Stat(jailsDir) fileInfo, err = os.Stat(jailsDir)
if err != nil { if err != nil {
log.Fatalln(fmt.Sprintf("Unable to access %s, check path and/or rights", jailsDir)) log.Fatalln(fmt.Sprintf("Unable to access %s, check path and/or rights", jailsDir))
@ -169,10 +167,10 @@ func listJailsFromDatastore(datastore string) {
log.Fatalln(fmt.Sprintf("%s is not a directory", jailsDir)) log.Fatalln(fmt.Sprintf("%s is not a directory", jailsDir))
} }
listJailsFromDirectory(jailsDir) listJailsFromDirectory(jailsDir, ds.Name)
} }
func listJailsFromDirectory(dir string) []Jail { func listJailsFromDirectory(dir string, dsname string) []Jail {
files, err := ioutil.ReadDir(dir) files, err := ioutil.ReadDir(dir)
if err != nil { if err != nil {
log.Fatalln(fmt.Sprintf("Unable to browse %s, check path and/or rights", dir)) log.Fatalln(fmt.Sprintf("Unable to browse %s, check path and/or rights", dir))
@ -194,6 +192,7 @@ func listJailsFromDirectory(dir string) []Jail {
Name: jailConf.Host_hostname, Name: jailConf.Host_hostname,
Config: jailConf, Config: jailConf,
ConfigPath: jailConfPath, ConfigPath: jailConfPath,
Datastore: dsname,
RootPath: jailRootPath, RootPath: jailRootPath,
Running: false, Running: false,
} }
@ -223,9 +222,12 @@ func listJailsFromDirectory(dir string) []Jail {
// Check if jail with the same name already exist on another DS // Check if jail with the same name already exist on another DS
for _, jj := range gJails { for _, jj := range gJails {
if strings.EqualFold(jj.Name, j.Name) { if strings.EqualFold(jj.Name, j.Name) {
fmt.Printf("ERROR: A jail with name %s already exist on datastore %s!\n", j.Name, jj.Zpool) fmt.Printf(" ---------------------------------------------- \n")
fmt.Printf("Jail %s on datastore %s wont be handled\n", j.Name, j.Zpool) fmt.Printf("Warning: A jail with name %s already exist on datastore %s!\n", j.Name, jj.Datastore)
return gJails fmt.Printf(" ---------------------------------------------- \n")
// Add Datastore to avoid confusion
gDisplayJColumns += ",Datastore"
gDisplaySColumns += ",Datastore"
} }
} }

View File

@ -18,7 +18,6 @@ const (
*******************************************************************************/ *******************************************************************************/
func MigrateJail(args []string) { func MigrateJail(args []string) {
var jailNames []string var jailNames []string
var destDS Datastore
if len(args) > 0 { if len(args) > 0 {
for _, a := range args { for _, a := range args {
@ -27,26 +26,24 @@ func MigrateJail(args []string) {
} }
for _, jn := range jailNames { for _, jn := range jailNames {
// Check if destination datastore exist
found := false
for _, ds := range gDatastores {
if strings.EqualFold(gMigrateDestDatastore, ds.Name) {
found = true
destDS = ds
break
}
}
if false == found {
fmt.Printf("Unkown datastore: %s\n", gMigrateDestDatastore)
return
}
cj, err := getJailFromArray(jn, gJails) cj, err := getJailFromArray(jn, gJails)
if cj == nil { if cj == nil {
fmt.Printf("Error getting jail %s: Not found\n", jn) fmt.Printf("Error getting jail %s: Not found\n", jn)
return return
} }
// Check if destination datastore exist & get current DS
destDS, err := getDatastoreFromArray(gMigrateDestDatastore, gDatastores)
if err != nil {
fmt.Printf("Error getting datastore \"%s\": %v\n", gMigrateDestDatastore, err)
return
}
curDS, err := getDatastoreFromArray(cj.Datastore, gDatastores)
if err != nil {
fmt.Printf("Error getting datastore \"%s\": %v\n", gMigrateDestDatastore, err)
return
}
if cj.Running == true && gYesToAll == false { if cj.Running == true && gYesToAll == false {
fmt.Printf("WARNING: Jail %s is running\n", cj.Name) fmt.Printf("WARNING: Jail %s is running\n", cj.Name)
fmt.Printf("Migration will stop it for data sync before starting on the new pool. You will be prompted for shutdown.\n") fmt.Printf("Migration will stop it for data sync before starting on the new pool. You will be prompted for shutdown.\n")
@ -74,7 +71,7 @@ func MigrateJail(args []string) {
*/ */
// Snapshot config // Snapshot config
dsconf := strings.Join([]string{cj.Zpool, "iocage", "jails", jn}, "/") dsconf := strings.Join([]string{curDS.ZFSDataset, "jails", jn}, "/")
fmt.Printf("Snapshot %s: ", dsconf) fmt.Printf("Snapshot %s: ", dsconf)
if err = zfsSnapshot(dsconf, "gocage_mig_init"); err != nil { if err = zfsSnapshot(dsconf, "gocage_mig_init"); err != nil {
fmt.Printf("Error: %v\n", err) fmt.Printf("Error: %v\n", err)
@ -83,7 +80,7 @@ func MigrateJail(args []string) {
fmt.Printf("Done\n") fmt.Printf("Done\n")
// Snapshot filesystem // Snapshot filesystem
dsdata := strings.Join([]string{cj.Zpool, "iocage", "jails", jn, "root"}, "/") dsdata := strings.Join([]string{curDS.ZFSDataset, "iocage", "jails", jn, "root"}, "/")
fmt.Printf("Snapshot %s: ", dsdata) fmt.Printf("Snapshot %s: ", dsdata)
if err := zfsSnapshot(dsdata, "gocage_mig_init"); err != nil { if err := zfsSnapshot(dsdata, "gocage_mig_init"); err != nil {
fmt.Printf("Error: %v\n", err) fmt.Printf("Error: %v\n", err)
@ -178,11 +175,14 @@ func CleanMigrateMess(args []string) error {
for _, jn := range jailNames { for _, jn := range jailNames {
cj, err := getJailFromArray(jn, gJails) cj, err := getJailFromArray(jn, gJails)
if cj == nil { if cj == nil {
fmt.Printf("Error getting jail %s: Not found\n", jn)
return errors.New(fmt.Sprintf("Error getting jail %s: Not found\n", jn)) return errors.New(fmt.Sprintf("Error getting jail %s: Not found\n", jn))
} }
curDS, err := getDatastoreFromArray(cj.Datastore, gDatastores)
if err != nil {
return errors.New(fmt.Sprintf("Error getting datastore \"%s\": %v\n", cj.Datastore, err))
}
cmd := fmt.Sprintf("zfs destroy %s@gocage_mig_init", strings.Join([]string{cj.Zpool, "iocage", "jails", jn}, "/")) cmd := fmt.Sprintf("zfs destroy %s@gocage_mig_init", strings.Join([]string{curDS.ZFSDataset, "jails", jn}, "/"))
out, err := executeCommand(cmd) out, err := executeCommand(cmd)
if err != nil { if err != nil {
if false == strings.HasSuffix(out, "could not find any snapshots to destroy; check snapshot names.\n") { if false == strings.HasSuffix(out, "could not find any snapshots to destroy; check snapshot names.\n") {
@ -190,7 +190,7 @@ func CleanMigrateMess(args []string) error {
return errors.New(fmt.Sprintf("Error executing command %s: %v; command returned: %s\n", cmd, err, out)) return errors.New(fmt.Sprintf("Error executing command %s: %v; command returned: %s\n", cmd, err, out))
} }
} }
cmd = fmt.Sprintf("zfs destroy %s@gocage_mig_init", strings.Join([]string{cj.Zpool, "iocage", "jails", jn, "root"}, "/")) cmd = fmt.Sprintf("zfs destroy %s@gocage_mig_init", strings.Join([]string{curDS.ZFSDataset, "jails", jn, "root"}, "/"))
out, err = executeCommand(cmd) out, err = executeCommand(cmd)
if err != nil { if err != nil {
if false == strings.HasSuffix(out, "could not find any snapshots to destroy; check snapshot names.\n") { if false == strings.HasSuffix(out, "could not find any snapshots to destroy; check snapshot names.\n") {
@ -198,7 +198,7 @@ func CleanMigrateMess(args []string) error {
return errors.New(fmt.Sprintf("Error executing command %s: %v; command returned: %s\n", cmd, err, out)) return errors.New(fmt.Sprintf("Error executing command %s: %v; command returned: %s\n", cmd, err, out))
} }
} }
cmd = fmt.Sprintf("zfs destroy %s@gocage_mig_last_sync", strings.Join([]string{cj.Zpool, "iocage", "jails", jn}, "/")) cmd = fmt.Sprintf("zfs destroy %s@gocage_mig_last_sync", strings.Join([]string{curDS.ZFSDataset, "jails", jn}, "/"))
out, err = executeCommand(cmd) out, err = executeCommand(cmd)
if err != nil { if err != nil {
if false == strings.HasSuffix(out, "could not find any snapshots to destroy; check snapshot names.\n") { if false == strings.HasSuffix(out, "could not find any snapshots to destroy; check snapshot names.\n") {
@ -206,7 +206,7 @@ func CleanMigrateMess(args []string) error {
return errors.New(fmt.Sprintf("Error executing command %s: %v; command returned: %s\n", cmd, err, out)) return errors.New(fmt.Sprintf("Error executing command %s: %v; command returned: %s\n", cmd, err, out))
} }
} }
cmd = fmt.Sprintf("zfs destroy %s@gocage_mig_last_sync", strings.Join([]string{cj.Zpool, "iocage", "jails", jn, "root"}, "/")) cmd = fmt.Sprintf("zfs destroy %s@gocage_mig_last_sync", strings.Join([]string{curDS.ZFSDataset, "jails", jn, "root"}, "/"))
out, err = executeCommand(cmd) out, err = executeCommand(cmd)
if err != nil { if err != nil {
if false == strings.HasSuffix(out, "could not find any snapshots to destroy; check snapshot names.\n") { if false == strings.HasSuffix(out, "could not find any snapshots to destroy; check snapshot names.\n") {

View File

@ -225,7 +225,10 @@ You can specify multiple jails.`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
// Load inventory // Load inventory
ListJails(args, false) ListJails(args, false)
CleanMigrateMess(args) err := CleanMigrateMess(args)
if err != nil {
fmt.Printf("%v", err)
}
}, },
} }
@ -262,8 +265,8 @@ func init() {
// Command dependant switches // Command dependant switches
// We reuse these in "gocage snapshot list myjail" and 'gocage datastore list" commands (TODO) // We reuse these flags in "gocage snapshot list myjail" and 'gocage datastore list" commands
listCmd.Flags().StringVarP(&gDisplayJColumns, "outcol", "o", "JID,Name,Config.Release,Config.Ip4_addr,Running", "Show these columns in output") listCmd.Flags().StringVarP(&gDisplayJColumns, "outcol", "o", "JID,Name,Datastore,Config.Release,Config.Ip4_addr,Running", "Show these columns in output")
listCmd.Flags().BoolVarP(&gNoJailLineSep, "nolinesep", "l", false, "Do not display line separator between jails") listCmd.Flags().BoolVarP(&gNoJailLineSep, "nolinesep", "l", false, "Do not display line separator between jails")
listCmd.Flags().StringVarP(&gFilterJails, "filter", "f", "none", "Only display jails with these values. Ex: \"gocage list -f Config.Boot=1\" will only list started on boot jails") listCmd.Flags().StringVarP(&gFilterJails, "filter", "f", "none", "Only display jails with these values. Ex: \"gocage list -f Config.Boot=1\" will only list started on boot jails")
listCmd.Flags().StringVarP(&gSortJailFields, "sort", "s", "none", "Display jails sorted by field values. Ex: \"gocage list -s +Name,-Config.Priority\" will sort jails by their decreasing name, then increasing start priority. 3 critera max supported.") listCmd.Flags().StringVarP(&gSortJailFields, "sort", "s", "none", "Display jails sorted by field values. Ex: \"gocage list -s +Name,-Config.Priority\" will sort jails by their decreasing name, then increasing start priority. 3 critera max supported.")

View File

@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"os" "os"
"regexp" "regexp"
"strconv"
"strings" "strings"
"reflect" "reflect"
"time" "time"
@ -23,6 +24,10 @@ func ListJailsSnapshots(args []string) {
/**************************************************************/ /**************************************************************/
if len(args) > 0 { if len(args) > 0 {
for _, a := range args { for _, a := range args {
/*if countOfJailsWithThisName(a) > 1 {
fmt.Printf("Nope")
return
}*/
jailNames = append(jailNames, a) jailNames = append(jailNames, a)
} }
} }
@ -99,23 +104,27 @@ func listJailSnapshots(jail Jail) []Snapshot {
// 1. List all datasets // 1. List all datasets
// TODO : Include mounted filesystems? // TODO : Include mounted filesystems?
rs := strings.Split(jail.RootPath, "/")
rootDataset := fmt.Sprintf("%s%s", jail.Zpool, strings.Join(rs[:len(rs)-1], "/")) curDS, err := getDatastoreFromArray(jail.Datastore, gDatastores)
cmd := fmt.Sprintf("zfs list -r -H -o name,mountpoint,used,referenced,creation -t snapshot %s", rootDataset) if err != nil {
fmt.Printf("Error getting datastore \"%s\": %v\n", jail.Datastore, err)
return snapshots
}
rootDataset := fmt.Sprintf("%s/%s/%s", curDS.ZFSDataset, "jails", jail.Name)
cmd := fmt.Sprintf("zfs list -p -r -H -o name,mountpoint,used,referenced,creation -t snapshot %s", rootDataset)
out, err := executeCommand(cmd) out, err := executeCommand(cmd)
if err != nil { if err != nil {
fmt.Printf("Error: %s\n", err.Error()) fmt.Printf("Error: %s\n", err.Error())
return snapshots return snapshots
} }
dateLayout := "Mon Jan _2 15:04 2006"
loc, _ := time.LoadLocation(gTimeZone)
for _, line := range strings.Split(out, "\n") { for _, line := range strings.Split(out, "\n") {
if len(line) > 0 { if len(line) > 0 {
ls := strings.Split(line, "\t") ls := strings.Split(line, "\t")
// Parse creation date so we can use it to sort snapshots // Parse creation date so we can use it to sort snapshots
creationts, err := time.ParseInLocation(dateLayout, ls[4], loc) //creationts, err := time.ParseInLocation(dateLayout, ls[4], loc)
creationts, err := strconv.ParseInt(ls[4], 10, 64)
if err != nil { if err != nil {
fmt.Println("Error while parsing date %s:", ls[4], err) fmt.Println("Error while parsing date %s:", ls[4], err)
return snapshots return snapshots
@ -123,13 +132,15 @@ func listJailSnapshots(jail Jail) []Snapshot {
// Get subdir to append to snapshot name // Get subdir to append to snapshot name
subdir := strings.Replace(strings.Split(ls[0], "@")[0], rootDataset, "", 1) subdir := strings.Replace(strings.Split(ls[0], "@")[0], rootDataset, "", 1)
snapshots = append(snapshots, Snapshot{Dsname: ls[0], u, _ := strconv.ParseUint(ls[2], 10, 64)
r, _ := strconv.ParseUint(ls[3], 10, 64)
snapshots = append(snapshots, Snapshot{Datastore: curDS.Name,
Name: fmt.Sprintf("%s%s", strings.Split(ls[0], "@")[1], subdir), Name: fmt.Sprintf("%s%s", strings.Split(ls[0], "@")[1], subdir),
Jailname: jail.Name, Jailname: jail.Name,
Mountpoint: ls[1], Mountpoint: ls[1],
Used: ls[2], Used: u,
Referenced: ls[3], Referenced: r,
Creation: creationts}) Creation: time.Unix(creationts, 0)})
} }
} }
@ -165,8 +176,8 @@ func CreateJailSnapshot(args []string) {
* Create snapshot for a jail * Create snapshot for a jail
*******************************************************************************/ *******************************************************************************/
func createJailSnapshot(jail Jail) error { func createJailSnapshot(jail Jail) error {
rs := strings.Split(jail.RootPath, "/") curDS, _ := getDatastoreFromArray(jail.Datastore, gDatastores)
rootDataset := fmt.Sprintf("%s%s", jail.Zpool, strings.Join(rs[:len(rs)-1], "/")) rootDataset := fmt.Sprintf("%s/%s/%s", curDS.ZFSDataset, "jails", jail.Name)
cmd := fmt.Sprintf("zfs snapshot -r %s@%s", rootDataset, gSnapshotName) cmd := fmt.Sprintf("zfs snapshot -r %s@%s", rootDataset, gSnapshotName)
_, err := executeCommand(cmd) _, err := executeCommand(cmd)
@ -207,8 +218,8 @@ func deleteJailSnapshot(jail Jail) error {
var snaptodel []string var snaptodel []string
// Get all recursive snapshots // Get all recursive snapshots
rs := strings.Split(jail.RootPath, "/") curDS, _ := getDatastoreFromArray(jail.Datastore, gDatastores)
rootDataset := fmt.Sprintf("%s%s", jail.Zpool, strings.Join(rs[:len(rs)-1], "/")) rootDataset := fmt.Sprintf("%s/%s/%s", curDS.ZFSDataset, "jails", jail.Name)
cmd := fmt.Sprintf("zfs list -r -H -o name -t snapshot %s", rootDataset) cmd := fmt.Sprintf("zfs list -r -H -o name -t snapshot %s", rootDataset)
out, err := executeCommand(cmd) out, err := executeCommand(cmd)
if err != nil { if err != nil {
@ -284,8 +295,8 @@ func rollbackJailSnapshot(jail Jail) error {
// We need to rollback parent and childs // We need to rollback parent and childs
// Get all recursive snapshots // Get all recursive snapshots
rs := strings.Split(jail.RootPath, "/") curDS, _ := getDatastoreFromArray(jail.Datastore, gDatastores)
rootDataset := fmt.Sprintf("%s%s", jail.Zpool, strings.Join(rs[:len(rs)-1], "/")) rootDataset := fmt.Sprintf("%s/%s/%s", curDS.ZFSDataset, "jails", jail.Name)
cmd := fmt.Sprintf("zfs list -r -H -o name -t snapshot %s", rootDataset) cmd := fmt.Sprintf("zfs list -r -H -o name -t snapshot %s", rootDataset)
out, err := executeCommand(cmd) out, err := executeCommand(cmd)
if err != nil { if err != nil {

View File

@ -28,6 +28,7 @@ type Jail struct {
// No need, Config.Release always represent what is running (plus it know release for non-running jails) // No need, Config.Release always represent what is running (plus it know release for non-running jails)
//Release string //Release string
Zpool string Zpool string
Datastore string
} }
// iocage struct as stored in config.json // iocage struct as stored in config.json
@ -191,11 +192,11 @@ type Mount struct {
type Snapshot struct { type Snapshot struct {
// snapshot name is stored after '@' in dataset name // snapshot name is stored after '@' in dataset name
Name string Name string
Dsname string Datastore string
Jailname string Jailname string
Mountpoint string Mountpoint string
Used string Used uint64
Referenced string Referenced uint64
Creation time.Time Creation time.Time
} }
@ -215,6 +216,8 @@ type JailSort struct {
ConfigPathDec jailLessFunc ConfigPathDec jailLessFunc
RunningInc jailLessFunc RunningInc jailLessFunc
RunningDec jailLessFunc RunningDec jailLessFunc
DatastoreInc jailLessFunc
DatastoreDec jailLessFunc
ZpoolInc jailLessFunc ZpoolInc jailLessFunc
ZpoolDec jailLessFunc ZpoolDec jailLessFunc
Config JailConfigSort Config JailConfigSort
@ -498,8 +501,8 @@ type JailConfigSort struct {
type SnapshotSort struct { type SnapshotSort struct {
NameInc snapshotLessFunc NameInc snapshotLessFunc
NameDec snapshotLessFunc NameDec snapshotLessFunc
DsnameInc snapshotLessFunc DatastoreInc snapshotLessFunc
DsnameDec snapshotLessFunc DatastoreDec snapshotLessFunc
JailnameInc snapshotLessFunc JailnameInc snapshotLessFunc
JailnameDec snapshotLessFunc JailnameDec snapshotLessFunc
MountpointInc snapshotLessFunc MountpointInc snapshotLessFunc

View File

@ -391,7 +391,7 @@ func getJailFromArray(name string, jarray []Jail) (*Jail, error) {
return &Jail{}, errors.New("Jail not found") return &Jail{}, errors.New("Jail not found")
} }
/***************************************************************************** /******************************************************************************
* *
* Generic utilities * Generic utilities
* *
@ -405,6 +405,28 @@ func isStringInArray(strarr []string, searched string) bool {
return false return false
} }
func getDatastoreFromArray(name string, dsa []Datastore) (*Datastore, error) {
for _, d := range dsa {
if name == d.Name {
return &d, nil
}
}
return &Datastore{}, errors.New("Datastore not found")
}
/******************************************************************************
* Return the quantity of jails with the name passed as parameter
*****************************************************************************/
func countOfJailsWithThisName(name string) int {
count := 0
for _, j := range gJails {
if strings.EqualFold(j.Name, name) {
count++
}
}
return count
}
/******************************************************************************** /********************************************************************************
* Recurse into structure, returning reflect.Kind of named field. * Recurse into structure, returning reflect.Kind of named field.
* Nested fields are named with a dot (ex "MyStruct.MyField") * Nested fields are named with a dot (ex "MyStruct.MyField")
@ -836,7 +858,7 @@ func displayJailsFields(jails []Jail, valsToDisplay []string) {
/******************************************************************************** /********************************************************************************
* Pretty display of snapshots field * Pretty display of snapshots field
* Fields to show are given in a string array parameter * Fields to show are given in a string array parameter
* Ex. : displaySnapshotsFields(snapshots, ["Name", "Dsname", "Used"]) * Ex. : displaySnapshotsFields(snapshots, ["Name", "Datastore", "Used"])
*******************************************************************************/ *******************************************************************************/
func displaySnapshotsFields(snaps []Snapshot, valsToDisplay []string) { func displaySnapshotsFields(snaps []Snapshot, valsToDisplay []string) {
/* A line is defined by : /* A line is defined by :
@ -877,9 +899,16 @@ func displaySnapshotsFields(snaps []Snapshot, valsToDisplay []string) {
if reflect.TypeOf(a.FieldByName(f).Interface()).Kind() == reflect.String { if reflect.TypeOf(a.FieldByName(f).Interface()).Kind() == reflect.String {
itnr := len(strings.Split(string(a.FieldByName(f).Interface().(string)), ",")) itnr := len(strings.Split(string(a.FieldByName(f).Interface().(string)), ","))
field.MaxLen = len(fmt.Sprintf("%v", a.FieldByName(f).Interface())) / itnr field.MaxLen = len(fmt.Sprintf("%v", a.FieldByName(f).Interface())) / itnr
} else {
// Special case of disk size : We will print human readable values
if field.Name == "Used" || field.Name == "Referenced" || field.Name == "Available" {
var v datasize.ByteSize
v.UnmarshalText([]byte(fmt.Sprintf("%v", a.FieldByName(f).Interface())))
field.MaxLen = len(fmt.Sprintf("%v", v.HumanReadable()))
} else { } else {
field.MaxLen = len(fmt.Sprintf("%v", a.FieldByName(f).Interface())) field.MaxLen = len(fmt.Sprintf("%v", a.FieldByName(f).Interface()))
} }
}
field.Value = fmt.Sprintf("%v", a.FieldByName(f).Interface()) field.Value = fmt.Sprintf("%v", a.FieldByName(f).Interface())
} else { } else {
fmt.Printf("Invalid field name: %s\n", f) fmt.Printf("Invalid field name: %s\n", f)
@ -983,26 +1012,25 @@ func displaySnapshotsFields(snaps []Snapshot, valsToDisplay []string) {
fmt.Printf("|") fmt.Printf("|")
} }
// Special cases of value displaying // Special cases of value displaying
/* if f.Name == "JID" && f.Value == "0" { // Pretty print of sizes
if f.Name == "Used" || f.Name == "Referenced" || f.Name == "Available" {
var v datasize.ByteSize
err := v.UnmarshalText([]byte(f.Value))
if err != nil {
return
}
fmt.Printf(" %s", v.HumanReadable())
// Complete with spaces to the max length
for i := len(v.HumanReadable()) + 1; i < f.MaxLen+1; i++ {
fmt.Printf(" ") fmt.Printf(" ")
} else if f.Name == "Ip4_addr" { }
ia := strings.Split(f.Value, ",")
// If we have more than 1 value we need to finish this line, and store value for writing at the end of line loop
for i, inter := range ia {
if i > 0 {
supplines[f.Name] = inter
} else { } else {
fmt.Printf(" %s", inter)
}
}
//fmt.Printf(" %s", strings.Split(strings.Split(f.Value, "|")[1], "/")[0])
} else {*/
fmt.Printf(" %s", f.Value) fmt.Printf(" %s", f.Value)
/*}*/
// Complete with spaces to the max length // Complete with spaces to the max length
for i := len(f.Value) + 1; i < f.MaxLen+1; i++ { for i := len(f.Value) + 1; i < f.MaxLen+1; i++ {
fmt.Printf(" ") fmt.Printf(" ")
} }
}
fmt.Printf(" |") fmt.Printf(" |")
} }
// Draw supplementary lines // Draw supplementary lines
@ -2125,6 +2153,12 @@ func initJailSortStruct() JailSort {
ConfigPathDec: func(j1, j2 *Jail) bool { ConfigPathDec: func(j1, j2 *Jail) bool {
return j1.ConfigPath > j2.ConfigPath return j1.ConfigPath > j2.ConfigPath
}, },
DatastoreInc: func(j1, j2 *Jail) bool {
return j1.Datastore < j2.Datastore
},
DatastoreDec: func(j1, j2 *Jail) bool {
return j1.Datastore > j2.Datastore
},
InternalNameInc: func(j1, j2 *Jail) bool { InternalNameInc: func(j1, j2 *Jail) bool {
return j1.InternalName < j2.InternalName return j1.InternalName < j2.InternalName
}, },
@ -2241,11 +2275,11 @@ func initSnapshotSortStruct() SnapshotSort {
NameDec: func(s1, s2 *Snapshot) bool { NameDec: func(s1, s2 *Snapshot) bool {
return s1.Name > s2.Name return s1.Name > s2.Name
}, },
DsnameInc: func(s1, s2 *Snapshot) bool { DatastoreInc: func(s1, s2 *Snapshot) bool {
return s1.Dsname < s2.Dsname return s1.Datastore < s2.Datastore
}, },
DsnameDec: func(s1, s2 *Snapshot) bool { DatastoreDec: func(s1, s2 *Snapshot) bool {
return s1.Dsname > s2.Dsname return s1.Datastore > s2.Datastore
}, },
JailnameInc: func(s1, s2 *Snapshot) bool { JailnameInc: func(s1, s2 *Snapshot) bool {
return s1.Jailname < s2.Jailname return s1.Jailname < s2.Jailname