Add filter option, update Readme
This commit is contained in:
parent
eceb108f2c
commit
fd0cb27798
66
README.md
66
README.md
@ -1,5 +1,65 @@
|
|||||||
# gocage
|
# gocage
|
||||||
|
|
||||||
Jail management tool for FreeBSD, written in Go.
|
Jail management tool for FreeBSD, written in Go.
|
||||||
Support iocage jails, so they can coexist.
|
Support iocage jails, so they can coexist.
|
||||||
Gocage is meant to be a complete jail management tool with network, snapshots, jail cloning support and a web interface.
|
Gocage is meant to be a complete jail management tool with network, snapshots, jail cloning support and a web interface.
|
||||||
|
|
||||||
|
# List jails
|
||||||
|
Nothing fancy, use
|
||||||
|
gocage list
|
||||||
|
|
||||||
|
## Specify fields to display
|
||||||
|
Use -o to specify which fields you want to display:
|
||||||
|
gocage list -o JID,Name,Running,Config.Boot,Config.Comment
|
||||||
|
+=====+==========+=========+=============+================+
|
||||||
|
| JID | Name | Running | Config.Boot | Config.Comment |
|
||||||
|
+=====+==========+=========+=============+================+
|
||||||
|
| 183 | test | true | 1 | none |
|
||||||
|
+-----+----------+---------+-------------+----------------+
|
||||||
|
| 29 | srv-irc | true | 1 | |
|
||||||
|
+-----+----------+---------+-------------+----------------+
|
||||||
|
| | srv-web | false | 0 | |
|
||||||
|
+-----+----------+---------+-------------+----------------+
|
||||||
|
| 22 | srv-dns1 | true | 1 | |
|
||||||
|
+-----+----------+---------+-------------+----------------+
|
||||||
|
|
||||||
|
## Filter jails
|
||||||
|
### By name
|
||||||
|
Just add name on gocage list command :
|
||||||
|
gocage list srv-bdd srv-web
|
||||||
|
+=====+=========+=================+=======================+=========+
|
||||||
|
| JID | Name | Config.Release | Config.Ip4_addr | Running |
|
||||||
|
+=====+=========+=================+=======================+=========+
|
||||||
|
| 98 | srv-db | 13.0-RELEASE-p5 | vnet0|192.168.1.56/24 | true |
|
||||||
|
+-----+---------+-----------------+-----------------------+---------+
|
||||||
|
| 41 | srv-web | 13.0-RELEASE-p4 | vnet0|192.168.1.26/24 | true |
|
||||||
|
+-----+---------+-----------------+-----------------------+---------+
|
||||||
|
|
||||||
|
### By field value
|
||||||
|
You can filter jails with -f option, followed by key=value. Suppose you want to see only active at boot jails:
|
||||||
|
gocage list -f Config.Boot=1 -o JID,Name,Running,Config.Boot,Config.Comment
|
||||||
|
+=====+==========+=========+=============+================+
|
||||||
|
| JID | Name | Running | Config.Boot | Config.Comment |
|
||||||
|
+=====+==========+=========+=============+================+
|
||||||
|
| 183 | test | true | 1 | none |
|
||||||
|
+-----+----------+---------+-------------+----------------+
|
||||||
|
| 29 | srv-irc | true | 1 | |
|
||||||
|
+-----+----------+---------+-------------+----------------+
|
||||||
|
| | srv-db | false | 1 | none |
|
||||||
|
+-----+----------+---------+-------------+----------------+
|
||||||
|
| 22 | srv-dns1 | true | 1 | |
|
||||||
|
+-----+----------+---------+-------------+----------------+
|
||||||
|
|
||||||
|
Now, only active at boot and running :
|
||||||
|
gocage list -f Config.Boot=1,Running=true -o JID,Name,Running,Config.Boot
|
||||||
|
+=====+==========+=========+=============+
|
||||||
|
| JID | Name | Running | Config.Boot |
|
||||||
|
+=====+==========+=========+=============+
|
||||||
|
| 183 | test | true | 1 |
|
||||||
|
+-----+----------+---------+-------------+
|
||||||
|
| 29 | srv-irc | true | 1 |
|
||||||
|
+-----+----------+---------+-------------+
|
||||||
|
| 22 | srv-dns1 | true | 1 |
|
||||||
|
+-----+----------+---------+-------------+
|
||||||
|
|
||||||
|
|
||||||
|
111
cmd/list.go
111
cmd/list.go
@ -4,6 +4,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"errors"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"io/ioutil"
|
"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.
|
// Recurse into structure, returning reflect.Value of wanted field.
|
||||||
// Nested fields are named with a dot (ex "MyStruct.MyField")
|
// Nested fields are named with a dot (ex "MyStruct.MyField")
|
||||||
func getStructField(parentStruct interface{}, fieldName string) (reflect.Value, string) {
|
func getStructField(parentStruct interface{}, fieldName string) (reflect.Value, string) {
|
||||||
@ -101,6 +142,11 @@ func displayStructFields(jails []Jail, valsToDisplay []string) {
|
|||||||
out = append(out, line)
|
out = append(out, line)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(out) == 0 {
|
||||||
|
fmt.Printf("Nothing to see here!\n")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Get real maximum length
|
// Get real maximum length
|
||||||
maxlen := make([]int, len(valsToDisplay))
|
maxlen := make([]int, len(valsToDisplay))
|
||||||
for i := 0; i< len(valsToDisplay); i++ {
|
for i := 0; i< len(valsToDisplay); i++ {
|
||||||
@ -173,6 +219,7 @@ func displayStructFields(jails []Jail, valsToDisplay []string) {
|
|||||||
// Then display data
|
// Then display data
|
||||||
// Loop through lines
|
// Loop through lines
|
||||||
for _, l := range out {
|
for _, l := range out {
|
||||||
|
|
||||||
// Loop through fields
|
// Loop through fields
|
||||||
// In case we need to add a line for a 2nd IP, or whatever object
|
// In case we need to add a line for a 2nd IP, or whatever object
|
||||||
var supplines = make(map[string]string)
|
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 */
|
/* Get Jails from datastores. Store config and running metadata into gJails global var */
|
||||||
func ListJails(args []string, display bool) {
|
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, ",")
|
fields := strings.Split(gDisplayColumns, ",")
|
||||||
|
|
||||||
for _, d := range viper.GetStringSlice("datastore") {
|
for _, d := range viper.GetStringSlice("datastore") {
|
||||||
listJailsFromDatastore(d)
|
listJailsFromDatastore(d)
|
||||||
}
|
}
|
||||||
|
|
||||||
if display {
|
// This is the structure we will filter, then display
|
||||||
if len(args) > 0 {
|
var jails []Jail
|
||||||
var js []Jail
|
|
||||||
for _, a := range args {
|
/************************************************************************************
|
||||||
for _, j := range gJails {
|
/ Filter jails with "filter" options
|
||||||
if j.Name == a {
|
/***********************************************************************************/
|
||||||
js = append(js, j)
|
if len(gFilterJails) > 0 && gFilterJails != "none" {
|
||||||
break
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
12
cmd/root.go
12
cmd/root.go
@ -20,6 +20,8 @@ var (
|
|||||||
|
|
||||||
gConfigFile string
|
gConfigFile string
|
||||||
gDisplayColumns string
|
gDisplayColumns string
|
||||||
|
gFilterJails string
|
||||||
|
gSortFields string
|
||||||
gNoLineSep bool
|
gNoLineSep bool
|
||||||
|
|
||||||
|
|
||||||
@ -73,11 +75,13 @@ func init() {
|
|||||||
|
|
||||||
// Global switches
|
// Global switches
|
||||||
rootCmd.PersistentFlags().StringVarP(&gConfigFile, "config", "c", "/usr/local/etc/gocage.conf.yml", "GoCage configuration file")
|
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
|
// Command dependant switches
|
||||||
listCmd.PersistentFlags().StringVarP(&gDisplayColumns, "outcol", "o", "JID,Name,Config.Release,Config.Ip4_addr,Running", "Show these columns in output")
|
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().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
|
// Now declare commands
|
||||||
rootCmd.AddCommand(versionCmd)
|
rootCmd.AddCommand(versionCmd)
|
||||||
@ -114,6 +118,12 @@ func initConfig() {
|
|||||||
if listCmd.Flags().Lookup("nolinesep") != nil && false == listCmd.Flags().Lookup("nolinesep").Changed {
|
if listCmd.Flags().Lookup("nolinesep") != nil && false == listCmd.Flags().Lookup("nolinesep").Changed {
|
||||||
gNoLineSep = viper.GetBool("nolinesep")
|
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() {
|
func Execute() {
|
||||||
|
Loading…
Reference in New Issue
Block a user