Add filter option, update Readme

This commit is contained in:
yo
2021-12-19 16:49:07 +01:00
parent eceb108f2c
commit fd0cb27798
3 changed files with 172 additions and 17 deletions

View File

@ -4,6 +4,7 @@ import (
"os"
"fmt"
"log"
"errors"
"reflect"
"strings"
"io/ioutil"
@ -13,6 +14,46 @@ import (
)
// Recurse into structure, returning reflect.Value of wanted field.
// Nested fields are named with a dot (ex "MyStruct.MyField")
func getStructFieldValue(parentStruct interface{}, fieldName string) (reflect.Value, string, error) {
v := reflect.ValueOf(parentStruct)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
if false {
for i := 0 ; i < v.NumField(); i++ {
f := v.Field(i)
if f.Kind() == reflect.String {
fmt.Printf("%v=%v\n", v.Type().Field(i).Name, f.Interface())
}
}
}
if strings.Contains(fieldName, ".") {
fs := strings.Split(fieldName, ".")
f := v.FieldByName(fs[0])
if f.Kind() == reflect.Struct {
return getStructFieldValue(f.Interface(), strings.Join(fs[1:], "."))
} else {
log.Fatalln(fmt.Sprintf("%s is not a struct: %s\n", fs[0], f.Kind().String()))
}
} else {
f := v.FieldByName(fieldName)
if f.IsValid() {
return f, fieldName, nil
} else {
return v, fieldName, errors.New(fmt.Sprintf("Field not found: %s", fieldName))
}
}
return v, fieldName, nil
}
// TODO : Replace by getStructFieldValue
// Recurse into structure, returning reflect.Value of wanted field.
// Nested fields are named with a dot (ex "MyStruct.MyField")
func getStructField(parentStruct interface{}, fieldName string) (reflect.Value, string) {
@ -101,6 +142,11 @@ func displayStructFields(jails []Jail, valsToDisplay []string) {
out = append(out, line)
}
if len(out) == 0 {
fmt.Printf("Nothing to see here!\n")
return
}
// Get real maximum length
maxlen := make([]int, len(valsToDisplay))
for i := 0; i< len(valsToDisplay); i++ {
@ -173,6 +219,7 @@ func displayStructFields(jails []Jail, valsToDisplay []string) {
// Then display data
// Loop through lines
for _, l := range out {
// Loop through fields
// In case we need to add a line for a 2nd IP, or whatever object
var supplines = make(map[string]string)
@ -251,29 +298,67 @@ func displayStructFields(jails []Jail, valsToDisplay []string) {
/* Get Jails from datastores. Store config and running metadata into gJails global var */
func ListJails(args []string, display bool) {
//fields := []string{"JID", "Name", "Config.Release", "Config.Ip4_addr", "RootPath", "Running", "Config.Jail_zfs", "Config.Jail_zfs_dataset", "Zpool"}
//fields := []string{"JID", "Name", "Config.Release", "Config.Ip4_addr", "Running", "Zpool"}
fields := strings.Split(gDisplayColumns, ",")
for _, d := range viper.GetStringSlice("datastore") {
listJailsFromDatastore(d)
}
if display {
if len(args) > 0 {
var js []Jail
for _, a := range args {
for _, j := range gJails {
if j.Name == a {
js = append(js, j)
break
// This is the structure we will filter, then display
var jails []Jail
/************************************************************************************
/ Filter jails with "filter" options
/***********************************************************************************/
if len(gFilterJails) > 0 && gFilterJails != "none" {
flts := make(map[string]string)
for _, flt := range strings.Split(gFilterJails, ",") {
sa := strings.Split(flt, "=")
if len(sa) != 2 {
fmt.Printf("Invalid format for filter %s\n", gFilterJails)
return
}
flts[sa[0]] = sa[1]
}
if len(flts) > 0 {
// Browse global gJails array to get filtered results
for _, j := range gJails {
for key, val := range flts {
v, _, err := getStructFieldValue(&j, key)
if err != nil {
fmt.Printf("ERROR: Field %s not found\n", key)
return
}
if strings.EqualFold(fmt.Sprintf("%v", v.Interface()), val) {
jails = append(jails, j)
}
}
}
displayStructFields(js, fields)
} else {
displayStructFields(gJails, fields)
}
} else {
jails = gJails
}
/************************************************************************************
/ Filter jails by names given on command line
/***********************************************************************************/
if len(args) > 0 {
var js []Jail
for _, a := range args {
for _, j := range jails {
if j.Name == a {
js = append(js, j)
break
}
}
}
jails = js
}
if display {
displayStructFields(jails, fields)
}
}

View File

@ -20,6 +20,8 @@ var (
gConfigFile string
gDisplayColumns string
gFilterJails string
gSortFields string
gNoLineSep bool
@ -73,11 +75,13 @@ func init() {
// Global switches
rootCmd.PersistentFlags().StringVarP(&gConfigFile, "config", "c", "/usr/local/etc/gocage.conf.yml", "GoCage configuration file")
rootCmd.PersistentFlags().BoolVarP(&gUseSudo, "sudo", "s", false, "Use sudo to run commands")
rootCmd.PersistentFlags().BoolVarP(&gUseSudo, "sudo", "u", false, "Use sudo to run commands")
// Command dependant switches
listCmd.PersistentFlags().StringVarP(&gDisplayColumns, "outcol", "o", "JID,Name,Config.Release,Config.Ip4_addr,Running", "Show these columns in output")
listCmd.PersistentFlags().BoolVarP(&gNoLineSep, "nolinesep", "l", false, "Do not display line separator between jails")
listCmd.PersistentFlags().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.PersistentFlags().StringVarP(&gSortFields, "sort", "s", "none", "Display jails sorted by field values. Ex: \"gocage list -s Config.Priority\" will sort jails by their start priority. NOT IMPLEMENTED YET")
// Now declare commands
rootCmd.AddCommand(versionCmd)
@ -114,6 +118,12 @@ func initConfig() {
if listCmd.Flags().Lookup("nolinesep") != nil && false == listCmd.Flags().Lookup("nolinesep").Changed {
gNoLineSep = viper.GetBool("nolinesep")
}
if listCmd.Flags().Lookup("filter") != nil && false == listCmd.Flags().Lookup("filter").Changed {
gFilterJails = viper.GetString("filter")
}
if listCmd.Flags().Lookup("sort") != nil && false == listCmd.Flags().Lookup("sort").Changed {
gSortFields = viper.GetString("sort")
}
}
func Execute() {