331 lines
9.3 KiB
Go
331 lines
9.3 KiB
Go
package cmd
|
|
|
|
import (
|
|
"bufio"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"reflect"
|
|
"time"
|
|
)
|
|
|
|
/********************************************************************************
|
|
* List all snapshots jails have
|
|
*******************************************************************************/
|
|
func ListJailsSnapshots(args []string) {
|
|
var jailNames []string
|
|
var snapshots []Snapshot
|
|
|
|
/***************************************************************
|
|
/ Filter snapshots by jailname
|
|
/**************************************************************/
|
|
if len(args) > 0 {
|
|
for _, a := range args {
|
|
/*if countOfJailsWithThisName(a) > 1 {
|
|
fmt.Printf("Nope")
|
|
return
|
|
}*/
|
|
jailNames = append(jailNames, a)
|
|
}
|
|
}
|
|
|
|
if len(jailNames) == 0 || len(args) == 0 {
|
|
for _, j := range gJails {
|
|
snapshots = append(snapshots, listJailSnapshots(j)...)
|
|
}
|
|
} else {
|
|
for _, cj := range gJails {
|
|
for _, jn := range jailNames {
|
|
if strings.EqualFold(cj.Name, jn) {
|
|
snapshots = append(snapshots, listJailSnapshots(cj)...)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fields := strings.Split(gDisplaySColumns, ",")
|
|
|
|
/***************************************************************
|
|
/ Sort snapshots
|
|
/ We support 3 sort criteria max
|
|
/**************************************************************/
|
|
if len(gSortSnapFields) > 0 && gSortSnapFields != "none" {
|
|
ss := initSnapshotSortStruct()
|
|
|
|
// The way we manage criteria quantity is not very elegant...
|
|
var fct1, fct2, fct3 *reflect.Value
|
|
for i, c := range strings.Split(gSortSnapFields, ",") {
|
|
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(ss, fctName)
|
|
|
|
if err != nil {
|
|
fieldName := strings.Replace(strings.Replace(c, "-", "", 1), "+", "", 1)
|
|
fmt.Printf("ERROR getting SnapshotSort 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(gSortSnapFields, ",")) {
|
|
case 1:
|
|
SnapshotsOrderedBy(fct1.Interface().(snapshotLessFunc)).Sort(snapshots)
|
|
case 2:
|
|
SnapshotsOrderedBy(fct1.Interface().(snapshotLessFunc), fct2.Interface().(snapshotLessFunc)).Sort(snapshots)
|
|
case 3:
|
|
SnapshotsOrderedBy(fct1.Interface().(snapshotLessFunc), fct2.Interface().(snapshotLessFunc), fct3.Interface().(snapshotLessFunc)).Sort(snapshots)
|
|
}
|
|
}
|
|
|
|
displaySnapshotsFields(snapshots, fields)
|
|
}
|
|
|
|
/********************************************************************************
|
|
* List all snapshots a jail have
|
|
*******************************************************************************/
|
|
func listJailSnapshots(jail Jail) []Snapshot {
|
|
var snapshots []Snapshot
|
|
|
|
// 1. List all datasets
|
|
// TODO : Include mounted filesystems?
|
|
|
|
curDS, err := getDatastoreFromArray(jail.Datastore, gDatastores)
|
|
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)
|
|
if err != nil {
|
|
fmt.Printf("Error: %s\n", err.Error())
|
|
return snapshots
|
|
}
|
|
|
|
for _, line := range strings.Split(out, "\n") {
|
|
if len(line) > 0 {
|
|
ls := strings.Split(line, "\t")
|
|
// Parse creation date so we can use it to sort snapshots
|
|
//creationts, err := time.ParseInLocation(dateLayout, ls[4], loc)
|
|
creationts, err := strconv.ParseInt(ls[4], 10, 64)
|
|
if err != nil {
|
|
fmt.Println("Error while parsing date %s:", ls[4], err)
|
|
return snapshots
|
|
}
|
|
// Get subdir to append to snapshot name
|
|
subdir := strings.Replace(strings.Split(ls[0], "@")[0], rootDataset, "", 1)
|
|
|
|
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),
|
|
Jailname: jail.Name,
|
|
Mountpoint: ls[1],
|
|
Used: u,
|
|
Referenced: r,
|
|
Creation: time.Unix(creationts, 0)})
|
|
}
|
|
}
|
|
|
|
// Sort snapshots by creation date
|
|
ss := initSnapshotSortStruct()
|
|
SnapshotsOrderedBy(ss.CreationInc).Sort(snapshots)
|
|
|
|
return snapshots
|
|
}
|
|
|
|
/********************************************************************************
|
|
* Create snapshot for jail(s)
|
|
*******************************************************************************/
|
|
func CreateJailSnapshot(args []string) {
|
|
var jailNames []string
|
|
|
|
if len(args) > 0 {
|
|
for _, a := range args {
|
|
jailNames = append(jailNames, a)
|
|
}
|
|
}
|
|
|
|
for _, cj := range gJails {
|
|
for _, jn := range jailNames {
|
|
if strings.EqualFold(cj.Name, jn) {
|
|
createJailSnapshot(cj)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/********************************************************************************
|
|
* Create snapshot for a jail
|
|
*******************************************************************************/
|
|
func createJailSnapshot(jail Jail) error {
|
|
curDS, _ := getDatastoreFromArray(jail.Datastore, gDatastores)
|
|
rootDataset := fmt.Sprintf("%s/%s/%s", curDS.ZFSDataset, "jails", jail.Name)
|
|
cmd := fmt.Sprintf("zfs snapshot -r %s@%s", rootDataset, gSnapshotName)
|
|
|
|
_, err := executeCommand(cmd)
|
|
if err != nil {
|
|
fmt.Printf("Error creating snapshot %s@%s: %s\n", rootDataset, gSnapshotName, err.Error())
|
|
return err
|
|
}
|
|
fmt.Printf("Snapshot %s@%s created\n", rootDataset, gSnapshotName)
|
|
|
|
return nil
|
|
}
|
|
|
|
/********************************************************************************
|
|
* Delete snapshot for jail(s)
|
|
*******************************************************************************/
|
|
func DeleteJailSnapshot(args []string) {
|
|
var jailNames []string
|
|
|
|
if len(args) > 0 {
|
|
for _, a := range args {
|
|
jailNames = append(jailNames, a)
|
|
}
|
|
}
|
|
|
|
for _, cj := range gJails {
|
|
for _, jn := range jailNames {
|
|
if strings.EqualFold(cj.Name, jn) {
|
|
deleteJailSnapshot(cj)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/********************************************************************************
|
|
* Delete snapshot for a jail
|
|
*******************************************************************************/
|
|
func deleteJailSnapshot(jail Jail) error {
|
|
var snaptodel []string
|
|
|
|
// Get all recursive snapshots
|
|
curDS, _ := getDatastoreFromArray(jail.Datastore, gDatastores)
|
|
rootDataset := fmt.Sprintf("%s/%s/%s", curDS.ZFSDataset, "jails", jail.Name)
|
|
cmd := fmt.Sprintf("zfs list -r -H -o name -t snapshot %s", rootDataset)
|
|
out, err := executeCommand(cmd)
|
|
if err != nil {
|
|
fmt.Printf("Error: listing snapshots: %s\n", err.Error())
|
|
return nil
|
|
}
|
|
|
|
for _, line := range strings.Split(out, "\n") {
|
|
if len(line) > 0 {
|
|
ls := strings.Split(line, "@")
|
|
for _, sn := range strings.Split(gSnapshotName, ",") {
|
|
matched, _ := regexp.Match(fmt.Sprintf("^%s(\\/.*)?$", sn), []byte(ls[1]))
|
|
if matched {
|
|
snaptodel = append(snaptodel, strings.Join(ls, "@"))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, s := range snaptodel {
|
|
cmd := fmt.Sprintf("zfs destroy %s", s)
|
|
_, err := executeCommand(cmd)
|
|
if err != nil {
|
|
fmt.Printf("Error deleting snapshot %s: %s\n", s, err.Error())
|
|
return nil
|
|
}
|
|
fmt.Printf("Snapshot %s deleted\n", s)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func RollbackJailSnapshot(args []string) error {
|
|
var jailNames []string
|
|
|
|
if len(args) > 0 {
|
|
for _, a := range args {
|
|
jailNames = append(jailNames, a)
|
|
}
|
|
}
|
|
|
|
for _, cj := range gJails {
|
|
for _, jn := range jailNames {
|
|
if strings.EqualFold(cj.Name, jn) {
|
|
rollbackJailSnapshot(cj)
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
/********************************************************************************
|
|
* rollback jail to snapshot gSnapshotName, destroy this snapshots and more
|
|
* recents snapshots and bookmarks
|
|
*******************************************************************************/
|
|
func rollbackJailSnapshot(jail Jail) error {
|
|
var snaptorb []string
|
|
|
|
if jail.Running {
|
|
fmt.Printf("Jail should be stoped to rollback, should we stop and rollback? (y/n)\n")
|
|
scanr := bufio.NewScanner(os.Stdin)
|
|
if scanr.Scan() {
|
|
if !strings.EqualFold(scanr.Text(), "y") {
|
|
return errors.New("Jail is running")
|
|
} else {
|
|
err := stopJail(&jail)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// We need to rollback parent and childs
|
|
// Get all recursive snapshots
|
|
curDS, _ := getDatastoreFromArray(jail.Datastore, gDatastores)
|
|
rootDataset := fmt.Sprintf("%s/%s/%s", curDS.ZFSDataset, "jails", jail.Name)
|
|
cmd := fmt.Sprintf("zfs list -r -H -o name -t snapshot %s", rootDataset)
|
|
out, err := executeCommand(cmd)
|
|
if err != nil {
|
|
fmt.Printf("Error: listing snapshots: %s\n", err.Error())
|
|
return err
|
|
}
|
|
|
|
for _, line := range strings.Split(out, "\n") {
|
|
if len(line) > 0 {
|
|
ls := strings.Split(line, "@")
|
|
|
|
matched, _ := regexp.Match(fmt.Sprintf("^%s(\\/.*)?$", gSnapshotName), []byte(ls[1]))
|
|
if matched {
|
|
snaptorb = append(snaptorb, strings.Join(ls, "@"))
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, s := range snaptorb {
|
|
cmd := fmt.Sprintf("zfs rollback -r %s", s)
|
|
_, err := executeCommand(cmd)
|
|
if err != nil {
|
|
fmt.Printf("Error rolling back snapshot %s: %s\n", s, err.Error())
|
|
return err
|
|
}
|
|
}
|
|
fmt.Printf("Jail is back to %s\n", gSnapshotName)
|
|
|
|
return nil
|
|
}
|