231 lines
6.7 KiB
Go
231 lines
6.7 KiB
Go
package cmd
|
|
|
|
import (
|
|
"os"
|
|
"fmt"
|
|
"log"
|
|
"reflect"
|
|
"strings"
|
|
"io/ioutil"
|
|
"gocage/jail"
|
|
"encoding/json"
|
|
"github.com/spf13/viper"
|
|
)
|
|
|
|
/********************************************************************************
|
|
* List all properties a jail have, with their internal name
|
|
* Only print properties name. To get name & values, use GetJailProperties()
|
|
*******************************************************************************/
|
|
func ListJailsProps(args []string) {
|
|
var conf Jail
|
|
var result []string
|
|
// Mandatory constructor to init default values
|
|
jailconf, err := NewJailConfig()
|
|
if err != nil {
|
|
fmt.Printf("Error allocating JailConfig: %s\n", err.Error())
|
|
return
|
|
}
|
|
|
|
conf.Config = jailconf
|
|
|
|
result = getStructFieldNames(conf, result, "")
|
|
|
|
for _, f := range result {
|
|
fmt.Printf("%s\n", f)
|
|
}
|
|
}
|
|
|
|
/********************************************************************************
|
|
* Get Jails from datastores. Store config and running metadata
|
|
* into gJails global var
|
|
*******************************************************************************/
|
|
func ListJails(args []string, display bool) {
|
|
fields := strings.Split(gDisplayColumns, ",")
|
|
|
|
for _, d := range viper.GetStringSlice("datastore") {
|
|
listJailsFromDatastore(d)
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} 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
|
|
}
|
|
|
|
/***************************************************************
|
|
/ Sort jails
|
|
/ We support 3 sort criteria max
|
|
/**************************************************************/
|
|
if len(gSortFields) > 0 && gSortFields != "none" {
|
|
js := initJailSortStruct()
|
|
|
|
// The way we manage criteria quantity is not very elegant...
|
|
var fct1, fct2, fct3 *reflect.Value
|
|
for i, c := range strings.Split(gSortFields, ",") {
|
|
var fctName string
|
|
if strings.HasPrefix(c, "-") {
|
|
fctName = fmt.Sprintf("%sDec", strings.Replace(c, "-", "", 1))
|
|
} else { // Par defaut (pas de prefix +/-) on considere un tri incremental
|
|
fctName = fmt.Sprintf("%sInc", strings.Replace(c, "+", "", 1))
|
|
}
|
|
|
|
// Get function by its name
|
|
fct, _, err := getStructFieldValue(js, fctName)
|
|
if err != nil {
|
|
fieldName := strings.Replace(strings.Replace(c, "-", "", 1), "+", "", 1)
|
|
fmt.Printf("ERROR getting JailSort struct field %s. Please check the field name: %s\n", fctName, fieldName)
|
|
return
|
|
}
|
|
switch i+1 {
|
|
case 1: fct1 = fct
|
|
case 2: fct2 = fct
|
|
case 3: fct3 = fct
|
|
}
|
|
}
|
|
|
|
switch len(strings.Split(gSortFields, ",")) {
|
|
case 1:
|
|
JailsOrderedBy(fct1.Interface().(jailLessFunc)).Sort(jails)
|
|
case 2:
|
|
JailsOrderedBy(fct1.Interface().(jailLessFunc), fct2.Interface().(jailLessFunc)).Sort(jails)
|
|
case 3:
|
|
JailsOrderedBy(fct1.Interface().(jailLessFunc), fct2.Interface().(jailLessFunc), fct3.Interface().(jailLessFunc)).Sort(jails)
|
|
}
|
|
}
|
|
|
|
/***************************************************************
|
|
/ Finally, display jails
|
|
/**************************************************************/
|
|
if display {
|
|
displayJailsFields(jails, fields)
|
|
}
|
|
}
|
|
|
|
|
|
func listJailsFromDatastore(datastore string) {
|
|
fileInfo, err := os.Stat(datastore)
|
|
if err != nil { log.Fatalln(fmt.Sprintf("Unable to access %s, check path and/or rights", datastore)) }
|
|
if fileInfo.IsDir() == false { log.Fatalln(fmt.Sprintf("%s is not a directory", datastore)) }
|
|
|
|
// A datastore have to contain a "jails" directory
|
|
jailsDir := fmt.Sprintf("%s/jails", datastore)
|
|
fileInfo, err = os.Stat(jailsDir)
|
|
if err != nil { log.Fatalln(fmt.Sprintf("Unable to access %s, check path and/or rights", jailsDir)) }
|
|
if fileInfo.IsDir() == false { log.Fatalln(fmt.Sprintf("%s is not a directory", jailsDir)) }
|
|
|
|
listJailsFromDirectory(jailsDir)
|
|
}
|
|
|
|
|
|
func listJailsFromDirectory(dir string) ([]Jail) {
|
|
files, err := ioutil.ReadDir(dir)
|
|
if err != nil { log.Fatalln(fmt.Sprintf("Unable to browse %s, check path and/or rights", dir)) }
|
|
|
|
for _, fi := range files {
|
|
if fi.IsDir() == true {
|
|
|
|
// 1. Get conf from config.json
|
|
jailConfPath := fmt.Sprintf("%s/%s/%s", dir, fi.Name(), "config.json")
|
|
jailConf, err := getJailConfig(jailConfPath)
|
|
if err != nil { log.Println("ERROR reading jail config for %s", jailConfPath) }
|
|
|
|
// 2. Build jail object from config
|
|
jailRootPath := fmt.Sprintf("%s/%s/%s", dir, fi.Name(), "root")
|
|
j := Jail{
|
|
Name: jailConf.Host_hostname,
|
|
Config: jailConf,
|
|
ConfigPath: jailConfPath,
|
|
RootPath: jailRootPath,
|
|
Running: false,
|
|
}
|
|
|
|
// 3. Add current running informations
|
|
rjails, err := jail.GetJails()
|
|
if err != nil { log.Fatalln("Unable to list running jails") }
|
|
for _, rj := range rjails {
|
|
if rj.Path == j.RootPath {
|
|
j.JID = rj.Jid
|
|
j.Running = true
|
|
j.InternalName = rj.Name
|
|
break
|
|
}
|
|
}
|
|
|
|
/* This op take some 600ms for ~40 jails :^( */
|
|
out, err := executeCommand(fmt.Sprintf("zfs list -H -o name %s", j.RootPath))
|
|
if err != nil {
|
|
fmt.Printf("ERROR getting dataset from %s: %s\n", j.RootPath, err.Error())
|
|
} else {
|
|
j.Zpool = strings.Split(strings.TrimSuffix(out, "\n"), "/")[0]
|
|
}
|
|
|
|
gJails = append(gJails, j)
|
|
}
|
|
}
|
|
|
|
return gJails
|
|
}
|
|
|
|
|
|
func getJailConfig(jailConfigPath string) (JailConfig, error) {
|
|
content, err := ioutil.ReadFile(jailConfigPath)
|
|
if err != nil { log.Fatalln(fmt.Sprintf("Unable to read %s, check path and/or rights", jailConfigPath)) }
|
|
|
|
// Mandatory constructor to init default values
|
|
jc, err := NewJailConfig()
|
|
if err != nil {
|
|
return jc, err
|
|
}
|
|
err = json.Unmarshal([]byte(content), &jc)
|
|
if err != nil { log.Fatalln(fmt.Sprintf("Error occured during unmarshaling %s: %s", jailConfigPath, err.Error())) }
|
|
|
|
return jc, err
|
|
}
|