267 lines
7.2 KiB
Go
267 lines
7.2 KiB
Go
package cmd
|
|
|
|
import (
|
|
"bufio"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
/********************************************************************************
|
|
* List all snapshots jails have
|
|
*******************************************************************************/
|
|
func ListJailsSnapshots(args []string) {
|
|
var jailNames []string
|
|
var snapshots []Snapshot
|
|
|
|
if len(args) > 0 {
|
|
for _, a := range args {
|
|
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)...)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
displaySnapshotsFields(snapshots, []string{"Jailname", "Name", "Creation", "Referenced", "Used"})
|
|
}
|
|
|
|
/********************************************************************************
|
|
* List all snapshots a jail have
|
|
*******************************************************************************/
|
|
func listJailSnapshots(jail Jail) []Snapshot {
|
|
var snapshots []Snapshot
|
|
|
|
// 1. List all datasets
|
|
// TODO : Include mounted filesystems?
|
|
rs := strings.Split(jail.RootPath, "/")
|
|
rootDataset := fmt.Sprintf("%s%s", jail.Zpool, strings.Join(rs[:len(rs)-1], "/"))
|
|
cmd := fmt.Sprintf("zfs list -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
|
|
}
|
|
|
|
dateLayout := "Mon Jan _2 15:04 2006"
|
|
loc, _ := time.LoadLocation(gTimeZone)
|
|
|
|
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)
|
|
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)
|
|
|
|
snapshots = append(snapshots, Snapshot{Dsname: ls[0],
|
|
Name: fmt.Sprintf("%s%s", strings.Split(ls[0], "@")[1], subdir),
|
|
Jailname: jail.Name,
|
|
Mountpoint: ls[1],
|
|
Used: ls[2],
|
|
Referenced: ls[3],
|
|
Creation: creationts})
|
|
}
|
|
}
|
|
|
|
// 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 {
|
|
rs := strings.Split(jail.RootPath, "/")
|
|
rootDataset := fmt.Sprintf("%s%s", jail.Zpool, strings.Join(rs[:len(rs)-1], "/"))
|
|
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
|
|
rs := strings.Split(jail.RootPath, "/")
|
|
rootDataset := fmt.Sprintf("%s%s", jail.Zpool, strings.Join(rs[:len(rs)-1], "/"))
|
|
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, "@")
|
|
|
|
matched, _ := regexp.Match(fmt.Sprintf("^%s(\\/.*)?$", gSnapshotName), []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
|
|
rs := strings.Split(jail.RootPath, "/")
|
|
rootDataset := fmt.Sprintf("%s%s", jail.Zpool, strings.Join(rs[:len(rs)-1], "/"))
|
|
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
|
|
}
|