- local disk snapshot create and delete

- auto snapshot everyday
- rewrite cornman
- make dep and some fix
- fix conflict
This commit is contained in:
wanyaoqi
2018-09-06 16:22:36 +08:00
parent 6d29c36abb
commit 4f2b50a869
21 changed files with 1014 additions and 67 deletions
+12 -4
View File
@@ -120,10 +120,11 @@ func init() {
})
type DiskUpdateOptions struct {
ID string `help:"ID or name of disk"`
Name string `help:"New name of disk"`
Desc string `help:"Description" metavar:"DESCRIPTION"`
AutoDelete string `help:"enable/disable auto delete of disk" choices:"enable|disable"`
ID string `help:"ID or name of disk"`
Name string `help:"New name of disk"`
Desc string `help:"Description" metavar:"DESCRIPTION"`
AutoDelete string `help:"enable/disable auto delete of disk" choices:"enable|disable"`
AutoSnapshot string `help:"enable/disable auto snapshot of disk" choices:"enable|disable"`
}
R(&DiskUpdateOptions{}, "disk-update", "Update property of a virtual disk", func(s *mcclient.ClientSession, args *DiskUpdateOptions) error {
params := jsonutils.NewDict()
@@ -140,6 +141,13 @@ func init() {
params.Add(jsonutils.JSONFalse, "auto_delete")
}
}
if len(args.AutoSnapshot) > 0 {
if args.AutoSnapshot == "enable" {
params.Add(jsonutils.JSONTrue, "auto_snapshot")
} else {
params.Add(jsonutils.JSONFalse, "auto_snapshot")
}
}
if params.Size() == 0 {
return InvalidUpdateError()
}
+17
View File
@@ -352,6 +352,23 @@ func init() {
return nil
})
type ServerDiskSnapshotOptions struct {
SERVER string `help:"server ID or Name"`
DISK string `help:"create snapshot disk id"`
SNAPSHOTNAME string `help:"Snapshot name"`
}
R(&ServerDiskSnapshotOptions{}, "server-disk-create-snapshot", "Task server disk snapshot", func(s *mcclient.ClientSession, args *ServerDiskSnapshotOptions) error {
params := jsonutils.NewDict()
params.Set("disk_id", jsonutils.NewString(args.DISK))
params.Set("name", jsonutils.NewString(args.SNAPSHOTNAME))
srv, err := modules.Servers.PerformAction(s, args.SERVER, "disk-snapshot", params)
if err != nil {
return err
}
printObject(srv)
return nil
})
type ServerInsertISOOptions struct {
ID string `help:"server ID or Name"`
ISO string `help:"Glance image ID of the ISO"`
+40
View File
@@ -0,0 +1,40 @@
package shell
import (
"yunion.io/x/jsonutils"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/modules"
"yunion.io/x/onecloud/pkg/mcclient/options"
)
func init() {
type SnapshotsListOptions struct {
options.BaseListOptions
Disk string `help:"Disk snapshots"`
}
R(&SnapshotsListOptions{}, "snapshot-list", "Show snapshots", func(s *mcclient.ClientSession, args *SnapshotsListOptions) error {
params, err := args.BaseListOptions.Params()
if err != nil {
return err
}
params.Add(jsonutils.NewString(args.Disk), "disk_id")
result, err := modules.Snapshots.List(s, params)
if err != nil {
return err
}
printList(result, modules.Snapshots.GetColumns(s))
return nil
})
type SnapshotDeleteOptions struct {
ID string `help:"Delete snapshot id"`
}
R(&SnapshotDeleteOptions{}, "snapshot-delete", "Delete snapshots", func(s *mcclient.ClientSession, args *SnapshotDeleteOptions) error {
result, err := modules.Snapshots.Delete(s, args.ID, nil)
if err != nil {
return err
}
printObject(result)
return nil
})
}
+10 -3
View File
@@ -205,7 +205,6 @@ func performClassActionHandler(ctx context.Context, w http.ResponseWriter, r *ht
var data jsonutils.JSONObject
if body != nil {
data, _ = body.Get(manager.KeywordPlural())
// about string ??
if data == nil {
data = body.(*jsonutils.JSONDict)
}
@@ -217,13 +216,21 @@ func performClassActionHandler(ctx context.Context, w http.ResponseWriter, r *ht
httperrors.GeneralServerError(w, err)
return
}
if results == nil {
results = jsonutils.NewDict()
}
appsrv.SendJSON(w, wrapBody(results, manager.KeywordPlural()))
}
func performActionHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
manager, params, query, body := fetchEnv(ctx, w, r)
data, _ := body.Get(manager.Keyword())
if data == nil {
var data jsonutils.JSONObject
if body != nil {
data, _ = body.Get(manager.Keyword())
if data == nil {
data = body.(*jsonutils.JSONDict)
}
} else {
data = jsonutils.NewDict()
}
result, err := manager.PerformAction(ctx, params["<resid>"], params["<action>"], query, data)
+148 -52
View File
@@ -1,96 +1,192 @@
package cronman
import (
"container/heap"
"context"
"reflect"
"runtime"
"runtime/debug"
"time"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/appctx"
"yunion.io/x/onecloud/pkg/mcclient"
"yunion.io/x/onecloud/pkg/mcclient/auth"
)
const (
DEFAULT_CRON_INTERVAL = 60 * time.Second // default resolution is 1 monutes
)
var manager *SCronJobManager
type SCronJobManager struct {
checkInterval time.Duration
timer *time.Timer
jobs []SCronJob
func init() {
manager = &SCronJobManager{
jobs: make([]*SCronJob, 0),
}
}
type ICronTimer interface {
Next(time.Time) time.Time
}
type Timer1 struct {
dur time.Duration
}
func (t *Timer1) Next(now time.Time) time.Time {
return now.Add(t.dur)
}
type Timer2 struct {
day, hour, min, sec int
}
func (t *Timer2) Next(now time.Time) time.Time {
next := now.Add(time.Hour * time.Duration(t.day) * 24)
return time.Date(next.Year(), next.Month(), next.Day(), t.hour, t.min, t.sec, 0, next.Location())
}
type SCronJob struct {
name string
runInterval time.Duration
job func(ctx context.Context, userCred mcclient.TokenCredential)
lastRun time.Time
Name string
job func(ctx context.Context, userCred mcclient.TokenCredential)
Timer ICronTimer
Next time.Time
}
func NewCronJobManager(interval time.Duration) *SCronJobManager {
if interval == 0 {
interval = DEFAULT_CRON_INTERVAL
type CronJobTimerHeap []*SCronJob
func (cjth CronJobTimerHeap) Len() int {
return len(cjth)
}
func (cjth CronJobTimerHeap) Swap(i, j int) {
cjth[i], cjth[j] = cjth[j], cjth[i]
}
func (cjth CronJobTimerHeap) Less(i, j int) bool {
if cjth[i].Next.IsZero() {
return false
}
if cjth[j].Next.IsZero() {
return true
}
return cjth[i].Next.Before(cjth[j].Next)
}
func (cjth *CronJobTimerHeap) Push(x interface{}) {
*cjth = append(*cjth, x.(*SCronJob))
}
func (cjth *CronJobTimerHeap) Pop() interface{} {
old := *cjth
n := old.Len()
x := old[n-1]
*cjth = old[0 : n-1]
return x
}
type SCronJobManager struct {
jobs CronJobTimerHeap
add chan *SCronJob
stop chan struct{}
running bool
}
func GetCronJobManager() *SCronJobManager {
return manager
}
func (self *SCronJobManager) AddJob1(name string, interval time.Duration, jobFunc func(ctx context.Context, userCred mcclient.TokenCredential)) {
t := Timer1{
dur: interval,
}
job := SCronJob{
Name: name,
job: jobFunc,
Timer: &t,
}
if !self.running {
self.jobs = append(self.jobs, &job)
} else {
self.add <- &job
}
}
func (self *SCronJobManager) AddJob2(name string, day, hour, min, sec int, jobFunc func(ctx context.Context, userCred mcclient.TokenCredential)) {
t := Timer2{
day: day,
hour: hour,
min: min,
sec: sec,
}
job := SCronJob{
Name: name,
job: jobFunc,
Timer: &t,
}
if !self.running {
self.jobs = append(self.jobs, &job)
} else {
self.add <- &job
}
}
func (self *SCronJobManager) Next(now time.Time) {
for _, job := range self.jobs {
job.Next = job.Timer.Next(now)
}
cron := SCronJobManager{checkInterval: interval, jobs: make([]SCronJob, 0)}
return &cron
}
func (self *SCronJobManager) Start() {
if self.timer != nil {
if self.running {
return
}
self.timer = time.AfterFunc(self.checkInterval, self.runCronJobs)
self.running = true
go self.run()
}
func (self *SCronJobManager) Stop() {
if self.timer != nil {
self.timer.Stop()
self.timer = nil
}
close(self.stop)
}
func getFunctionName(i interface{}) string {
return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
}
func (self *SCronJobManager) AddJob(name string, interval time.Duration, jobFunc func(ctx context.Context, userCred mcclient.TokenCredential)) {
// name := getFunctionName(jobFunc)
log.Debugf("Add cronjob %s", name)
job := SCronJob{name: name, job: jobFunc, runInterval: interval}
self.jobs = append(self.jobs, job)
}
func (self *SCronJobManager) runCronJobs() {
func (self *SCronJobManager) run() {
now := time.Now()
for i := 0; i < len(self.jobs); i += 1 {
self.jobs[i].run(now)
}
self.timer = nil
self.Start() // schedule next run
}
func (self *SCronJob) run(now time.Time) {
if self.lastRun.IsZero() || now.Sub(self.lastRun) >= self.runInterval {
log.Debugf("Run cronjob %s", self.name)
go runJob(self.name, self.job)
self.lastRun = now
self.Next(now)
heap.Init(&self.jobs)
for {
var timer *time.Timer
if len(self.jobs) == 0 || self.jobs[0].Next.IsZero() {
timer = time.NewTimer(100000 * time.Hour)
} else {
timer = time.NewTimer(self.jobs[0].Next.Sub(now))
}
select {
case now = <-timer.C:
for i, job := range self.jobs {
if job.Next.After(now) || job.Next.IsZero() {
break
}
go job.runJob()
job.Next = job.Timer.Next(now)
heap.Fix(&self.jobs, i)
}
case newJob := <-self.add:
now = time.Now()
newJob.Next = newJob.Timer.Next(now)
heap.Push(&self.jobs, newJob)
case <-self.stop:
timer.Stop()
return
}
}
}
func runJob(name string, job func(ctx context.Context, userCred mcclient.TokenCredential)) {
func (job *SCronJob) runJob() {
defer func() {
if r := recover(); r != nil {
log.Errorf("CronJob task %s run error: %s", name, r)
log.Errorf("CronJob task %s run error: %s", job.Name, r)
debug.PrintStack()
}
}()
log.Debugf("Cron job: %s started", job.Name)
ctx := context.Background()
ctx = context.WithValue(ctx, appctx.APP_CONTEXT_KEY_APPNAME, "Region-Corn-Service")
userCred := auth.AdminCredential()
job(ctx, userCred)
job.job(ctx, userCred)
}
+16
View File
@@ -129,3 +129,19 @@ func (self *SBaseGuestDriver) RequestGuestHotAddIso(ctx context.Context, guest *
func (self *SBaseGuestDriver) RequestRebuildRootDisk(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
return fmt.Errorf("Not Implement")
}
func (self *SBaseGuestDriver) StartGuestDiskSnapshotTask(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, params *jsonutils.JSONDict) error {
return fmt.Errorf("Not Implement")
}
func (self *SBaseGuestDriver) RequestDiskSnapshot(ctx context.Context, guest *models.SGuest, task taskman.ITask, snapshotId, diskId string) error {
return fmt.Errorf("Not Implement")
}
func (self *SBaseGuestDriver) RequestDeleteSnapshot(ctx context.Context, guest *models.SGuest, task taskman.ITask, params *jsonutils.JSONDict) error {
return fmt.Errorf("Not Implement")
}
func (self *SBaseGuestDriver) RequestReloadDiskSnapshot(ctx context.Context, guest *models.SGuest, task taskman.ITask, params *jsonutils.JSONDict) error {
return fmt.Errorf("Not Implement")
}
+42
View File
@@ -47,6 +47,48 @@ func (self *SKVMGuestDriver) DoGuestCreateDisksTask(ctx context.Context, guest *
return nil
}
func (self *SKVMGuestDriver) StartGuestDiskSnapshotTask(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, params *jsonutils.JSONDict) error {
task, err := taskman.TaskManager.NewTask(ctx, "GuestDiskSnapshotTask", guest, userCred, params, "", "", nil)
if err != nil {
return err
}
task.ScheduleRun(nil)
return nil
}
func (self *SKVMGuestDriver) RequestDiskSnapshot(ctx context.Context, guest *models.SGuest, task taskman.ITask, snapshotId, diskId string) error {
url := fmt.Sprintf("/servers/%s/snapshot", guest.Id)
body := jsonutils.NewDict()
body.Set("disk_id", jsonutils.NewString(diskId))
body.Set("snapshot_id", jsonutils.NewString(snapshotId))
header := http.Header{}
header.Set("X-Task-Id", task.GetTaskId())
header.Set("X-Region-Version", "v2")
host := guest.GetHost()
_, err := host.Request(task.GetUserCred(), "POST", url, header, body)
return err
}
func (self *SKVMGuestDriver) RequestDeleteSnapshot(ctx context.Context, guest *models.SGuest, task taskman.ITask, params *jsonutils.JSONDict) error {
url := fmt.Sprintf("/servers/%s/delete-snapshot", guest.Id)
header := http.Header{}
header.Set("X-Task-Id", task.GetTaskId())
header.Set("X-Region-Version", "v2")
host := guest.GetHost()
_, err := host.Request(task.GetUserCred(), "POST", url, header, params)
return err
}
func (self *SKVMGuestDriver) RequestReloadDiskSnapshot(ctx context.Context, guest *models.SGuest, task taskman.ITask, params *jsonutils.JSONDict) error {
url := fmt.Sprintf("/servers/%s/reload-disk-snapshot", guest.Id)
header := http.Header{}
header.Set("X-Task-Id", task.GetTaskId())
header.Set("X-Region-Version", "v2")
host := guest.GetHost()
_, err := host.Request(task.GetUserCred(), "POST", url, header, params)
return err
}
func findVNCPort(results string) int {
reg := regexp.MustCompile(`(\d+\.\d+\.\d+\.\d+):([\d]+)`)
finds := reg.FindStringSubmatch(results)
+1
View File
@@ -57,6 +57,7 @@ func InitHandlers(app *appsrv.Application) {
models.VCenterManager,
models.DnsRecordManager,
models.ElasticipManager,
models.SnapshotManager,
} {
db.RegisterModelManager(manager)
handler := db.NewModelHandler(manager)
+1
View File
@@ -13,6 +13,7 @@ import (
)
type SAliyunHostDriver struct {
SBaseHostDriver
}
func init() {
+16
View File
@@ -0,0 +1,16 @@
package hostdrivers
import (
"context"
"fmt"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/compute/models"
)
type SBaseHostDriver struct {
}
func (self *SBaseHostDriver) RequestDeleteSnapshotWithStorage(ctx context.Context, host *models.SHost, snapshot *models.SSnapshot, task taskman.ITask) error {
return fmt.Errorf("Not Implement")
}
+12
View File
@@ -13,6 +13,7 @@ import (
)
type SKVMHostDriver struct {
SBaseHostDriver
}
func init() {
@@ -147,3 +148,14 @@ func (self *SKVMHostDriver) RequestSaveUploadImageOnHost(ctx context.Context, ho
_, err := host.Request(task.GetUserCred(), "POST", url, header, body)
return err
}
func (self *SKVMHostDriver) RequestDeleteSnapshotWithStorage(ctx context.Context, host *models.SHost, snapshot *models.SSnapshot, task taskman.ITask) error {
url := fmt.Sprintf("/storages/%s/delete-snapshots", snapshot.StorageId)
body := jsonutils.NewDict()
body.Set("disk_id", jsonutils.NewString(snapshot.DiskId))
header := http.Header{}
header.Add("X-Task-Id", task.GetTaskId())
header.Add("X-Region-Version", "v2")
_, err := host.Request(task.GetUserCred(), "POST", url, header, body)
return err
}
+77 -2
View File
@@ -53,6 +53,9 @@ const (
DISK_POST_MIGRATE = "post_migrate"
DISK_MIGRATING = "migrating"
DISK_START_SNAPSHOT = "start_snapshot"
DISK_SNAPSHOTING = "snapshoting"
DISK_TYPE_SYS = "sys"
DISK_TYPE_SWAP = "swap"
DISK_TYPE_DATA = "data"
@@ -89,6 +92,7 @@ type SDisk struct {
DiskType string `width:"32" charset:"ascii" nullable:"true" list:"user"` // Column(VARCHAR(32, charset='ascii'), nullable=True)
// # is persistent
Nonpersistent bool `default:"false" list:"user"` // Column(Boolean, default=False)
AutoSnapshot bool `default:"false" nullable:"true" get:"user" update:"user"`
}
func (manager *SDiskManager) GetContextManager() []db.IModelManager {
@@ -299,6 +303,13 @@ func (self *SDisk) StartDiskCreateTask(ctx context.Context, userCred mcclient.To
return nil
}
func (self *SDisk) GetSnapshotCount() int {
q := SnapshotManager.Query()
count := q.Filter(sqlchemy.AND(sqlchemy.Equals(q.Field("disk_id"), self.Id),
sqlchemy.Equals(q.Field("out_of_chain"), false))).Count()
return count
}
func (self *SDisk) StartAllocate(ctx context.Context, host *SHost, storage *SStorage, taskId string, userCred mcclient.TokenCredential, rebuild bool, snapshot string, task taskman.ITask) error {
log.Infof("Allocating disk on host %s ...", host.GetName())
@@ -331,6 +342,30 @@ func (self *SDisk) StartAllocate(ctx context.Context, host *SHost, storage *SSto
return host.GetHostDriver().RequestAllocateDiskOnStorage(ctx, host, storage, self, task, content)
}
func (self *SDisk) AllowGetDetailsConvertSnapshot(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
return self.IsOwner(userCred)
}
func (self *SDisk) GetDetailsConvertSnapshot(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
deleteSnapshot := SnapshotManager.GetDiskFirstSnapshot(self.Id)
if deleteSnapshot == nil {
return nil, httperrors.NewNotFoundError("Can not get disk snapshot")
}
convertSnapshot, err := SnapshotManager.GetConvertSnapshot(deleteSnapshot)
if err != nil {
return nil, httperrors.NewBadRequestError("Get convert snapshot failed: %s", err.Error())
}
var pendingDelete bool
if deleteSnapshot.CreatedBy == MANUAL && !deleteSnapshot.PendingDeleted {
pendingDelete = true
}
ret := jsonutils.NewDict()
ret.Set("delete_snapshot", jsonutils.NewString(deleteSnapshot.Id))
ret.Set("convert_snapshot", jsonutils.NewString(convertSnapshot.Id))
ret.Set("pending_delete", jsonutils.NewBool(pendingDelete))
return ret, nil
}
func (self *SDisk) AllowPerformResize(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return self.IsOwner(userCred)
}
@@ -1047,7 +1082,7 @@ func (self *SDisk) isInit() bool {
return self.Status == DISK_INIT
}
func (model *SDisk) AllowPerformCancelDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
func (self *SDisk) AllowPerformCancelDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return userCred.IsSystemAdmin()
}
@@ -1060,7 +1095,7 @@ func (self *SDisk) PerformCancelDelete(ctx context.Context, userCred mcclient.To
}
func (manager *SDiskManager) getExpiredPendingDeleteDisks() []SDisk {
deadline := time.Now().Add(time.Duration(options.Options.PendingDeleteExpireSeconds) * time.Second)
deadline := time.Now().Add(time.Duration(options.Options.PendingDeleteExpireSeconds*-1) * time.Second)
q := manager.Query()
q = q.IsTrue("pending_deleted").LT("pending_deleted_at", deadline).Limit(options.Options.PendingDeleteMaxCleanBatchSize)
@@ -1084,3 +1119,43 @@ func (manager *SDiskManager) CleanPendingDeleteDisks(ctx context.Context, userCr
disks[i].StartDiskDeleteTask(ctx, userCred, "", false)
}
}
func (manager *SDiskManager) getAutoSnapshotDisks() []SDisk {
q := manager.Query().SubQuery()
dest := make([]SDisk, 0)
err := q.Query().Filter(sqlchemy.Equals(q.Field("auto_snapshot"), true)).All(&dest)
if err != nil {
return nil
}
return dest
}
func (manager *SDiskManager) AutoDiskSnapshot(ctx context.Context, userCred mcclient.TokenCredential) {
disks := manager.getAutoSnapshotDisks()
if disks == nil {
return
}
for _, disk := range disks {
snapCount := disk.GetSnapshotCount()
if snapCount >= DISK_MAX_SNAPSHOT {
continue
}
guests := disk.GetGuests()
if guests == nil || len(guests) > 1 {
log.Errorln("Disk %s not attach or attached more than one guest", disk.Id)
continue
}
// if !utils.IsInStringArray(guests[0].Status, []string{VM_RUNNING, VM_READY}) {
// log.Errorln("Guest(%s) in status(%s) cannot do snapshot action", guests[0].Id, guests[0].Status)
// continue
// }
// name
name := disk.Name + time.Now().Format("2006-01-02#15:04:05")
snap, err := SnapshotManager.CreateSnapshot(ctx, userCred, AUTO, disk.Id, guests[0].Id, "", name)
if err != nil {
log.Errorln(err)
continue
}
guests[0].StartDiskSnapshot(ctx, userCred, disk.Id, snap.Id)
}
}
+5
View File
@@ -93,6 +93,11 @@ type IGuestDriver interface {
RequestGuestHotAddIso(ctx context.Context, guest *SGuest, path string, task taskman.ITask) error
RequestRebuildRootDisk(ctx context.Context, guest *SGuest, task taskman.ITask) error
StartGuestDiskSnapshotTask(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, params *jsonutils.JSONDict) error
RequestDiskSnapshot(ctx context.Context, guest *SGuest, task taskman.ITask, snapshotId, diskId string) error
RequestDeleteSnapshot(ctx context.Context, guest *SGuest, task taskman.ITask, params *jsonutils.JSONDict) error
RequestReloadDiskSnapshot(ctx context.Context, guest *SGuest, task taskman.ITask, params *jsonutils.JSONDict) error
}
var guestDrivers map[string]IGuestDriver
+48 -2
View File
@@ -75,7 +75,7 @@ const (
VM_CHANGE_FLAVOR = "change_flavor"
VM_REBUILD_ROOT = "rebuild_root"
VM_REBUILD_ROOT_FAIL = "rebld_root_fail"
VM_REBUILD_ROOT_FAIL = "rebuild_root_fail"
VM_START_SNAPSHOT = "snapshot_start"
VM_SNAPSHOT = "snapshot"
@@ -3406,6 +3406,52 @@ func (self *SGuest) PerformReset(ctx context.Context, userCred mcclient.TokenCre
return nil, httperrors.NewInvalidStatusError("Cannot reset VM in status %s", self.Status)
}
func (self *SGuest) AllowPerformDiskSnapshot(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return self.IsOwner(userCred)
}
func (self *SGuest) PerformDiskSnapshot(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
if !utils.IsInStringArray(self.Status, []string{VM_RUNNING, VM_READY}) {
return nil, httperrors.NewInvalidStatusError("Cannot do snapshot when VM in status %s", self.Status)
}
diskId, err := data.GetString("disk_id")
if err != nil {
return nil, err
}
name, err := data.GetString("name")
if err != nil {
return nil, err
}
if self.GetGuestDisk(diskId) == nil {
return nil, httperrors.NewNotFoundError("Guest disk %s not found", diskId)
}
snapshots := SnapshotManager.GetDiskSnapshotsByCreate(diskId, MANUAL)
if snapshots != nil {
if len(snapshots) >= DISK_MAX_MANUAL_SNAPSHOT {
return nil, httperrors.NewBadRequestError("Disk %s snapshot full, cannot take any more", diskId)
}
for _, snapshot := range snapshots {
if snapshot.Name == name {
return nil, httperrors.NewBadRequestError("Name Conflict")
}
}
}
snapshot, err := SnapshotManager.CreateSnapshot(ctx, userCred, MANUAL, diskId, self.Id, "", name)
if err != nil {
return nil, err
}
err = self.StartDiskSnapshot(ctx, userCred, diskId, snapshot.Id)
return nil, err
}
func (self *SGuest) StartDiskSnapshot(ctx context.Context, userCred mcclient.TokenCredential, diskId, snapshotId string) error {
self.SetStatus(userCred, VM_START_SNAPSHOT, "StartDiskSnapshot")
params := jsonutils.NewDict()
params.Set("disk_id", jsonutils.NewString(diskId))
params.Set("snapshot_id", jsonutils.NewString(snapshotId))
return self.GetDriver().StartGuestDiskSnapshotTask(ctx, userCred, self, params)
}
func (self *SGuest) AllowPerformStop(ctx context.Context,
userCred mcclient.TokenCredential,
query jsonutils.JSONObject,
@@ -3556,7 +3602,7 @@ func (manager *SGuestManager) getIpsByExit(ips []string, isExitOnly bool) []stri
}
func (manager *SGuestManager) getExpiredPendingDeleteGuests() []SGuest {
deadline := time.Now().Add(time.Duration(options.Options.PendingDeleteExpireSeconds) * time.Second)
deadline := time.Now().Add(time.Duration(options.Options.PendingDeleteExpireSeconds*-1) * time.Second)
q := manager.Query()
q = q.IsTrue("pending_deleted").LT("pending_deleted_at", deadline).In("hypervisor", []string{"aliyun"}).Limit(options.Options.PendingDeleteMaxCleanBatchSize)
+1
View File
@@ -18,6 +18,7 @@ type IHostDriver interface {
RequestDeallocateDiskOnHost(host *SHost, storage *SStorage, disk *SDisk, task taskman.ITask) error
RequestResizeDiskOnHostOnline(host *SHost, storage *SStorage, disk *SDisk, size int64, task taskman.ITask) error
RequestResizeDiskOnHost(host *SHost, storage *SStorage, disk *SDisk, size int64, task taskman.ITask) error
RequestDeleteSnapshotWithStorage(ctx context.Context, host *SHost, snapshot *SSnapshot, task taskman.ITask) error
}
var hostDrivers map[string]IHostDriver
+278
View File
@@ -0,0 +1,278 @@
package models
import (
"context"
"fmt"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/sqlchemy"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/httperrors"
"yunion.io/x/onecloud/pkg/mcclient"
)
const (
DISK_MAX_SNAPSHOT = 9
DISK_MAX_MANUAL_SNAPSHOT = 2
// create by
MANUAL = "manual"
AUTO = "auto"
SNAPSHOT_FAILED = "create_failed"
SNAPSHOT_READY = "ready"
)
type SSnapshotManager struct {
db.SVirtualResourceBaseManager
}
type SSnapshot struct {
db.SVirtualResourceBase
DiskId string `width:"36" charset:"ascii" nullable:"false" create:"required" key_index:"true" list:"user"`
StorageId string `width:"36" charset:"ascii" nullable:"true" list:"admin"`
CreatedBy string `width:"36" charset:"ascii" nullable:"false" default:"manual" list:"admin"`
Location string `charset:"ascii" nullable:"false" list:"admin"`
Size int `nullable:"false" list:"user"` // MB
OutOfChain bool `nullable:"false" default:"false" index:"true" get:"admin"`
}
var SnapshotManager *SSnapshotManager
func init() {
SnapshotManager = &SSnapshotManager{SVirtualResourceBaseManager: db.NewVirtualResourceBaseManager(SSnapshot{}, "snapshots_tbl", "snapshot", "snapshots")}
}
func (self *SSnapshot) AllowListItems(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
return self.IsOwner(userCred)
}
func (self *SSnapshot) AllowCreateItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return false
}
func (self *SSnapshot) AllowGetDetails(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
return false
}
func (self *SSnapshot) AllowUpdateItem(ctx context.Context, userCred mcclient.TokenCredential) bool {
return false
}
func (self *SSnapshot) CustomizeCreate(ctx context.Context, userCred mcclient.TokenCredential, ownerProjId string, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
return self.SVirtualResourceBase.CustomizeCreate(ctx, userCred, ownerProjId, query, data)
}
func (self *SSnapshot) GetGuest() (*SGuest, error) {
iDisk, err := DiskManager.FetchById(self.DiskId)
if err != nil {
return nil, err
}
disk := iDisk.(*SDisk)
guests := disk.GetGuests()
if len(guests) > 1 {
return nil, fmt.Errorf("Snapshot disk attach mutil guest")
} else if len(guests) == 1 {
return &guests[0], nil
} else {
return nil, nil
}
}
func (self *SSnapshot) GetHost() *SHost {
iStorage, err := StorageManager.FetchById(self.StorageId)
if err != nil {
log.Errorln(err)
return nil
}
storage := iStorage.(*SStorage)
return storage.GetMasterHost()
}
func (self *SSnapshotManager) GetDiskSnapshotsByCreate(diskId, createdBy string) []SSnapshot {
dest := make([]SSnapshot, 0)
q := self.Query().SubQuery()
err := q.Query().Filter(sqlchemy.AND(sqlchemy.Equals(q.Field("disk_id"), diskId),
sqlchemy.Equals(q.Field("created_by"), createdBy),
sqlchemy.Equals(q.Field("pending_deleted"), false))).All(&dest)
if err != nil {
log.Errorf("GetDiskSnapshots error: %s", err)
return nil
}
for i := 0; i < len(dest); i++ {
dest[i].SetModelManager(self)
}
return dest
}
func (self *SSnapshotManager) GetDiskSnapshots(diskId string) []SSnapshot {
dest := make([]SSnapshot, 0)
q := self.Query().SubQuery()
err := q.Query().Filter(sqlchemy.AND(sqlchemy.Equals(q.Field("disk_id"), diskId))).All(&dest)
if err != nil {
log.Errorf("GetDiskSnapshots error: %s", err)
return nil
}
for i := 0; i < len(dest); i++ {
dest[i].SetModelManager(self)
}
return dest
}
func (self *SSnapshotManager) GetDiskFirstSnapshot(diskId string) *SSnapshot {
dest := &SSnapshot{}
q := self.Query().SubQuery()
err := q.Query().Filter(sqlchemy.AND(sqlchemy.Equals(q.Field("disk_id"), diskId),
sqlchemy.Equals(q.Field("status"), SNAPSHOT_READY),
sqlchemy.Equals(q.Field("out_of_chain"), false))).Asc("created_at").First(dest)
if err != nil {
log.Errorf("Get Disk First snapshot error: %s", err.Error())
return nil
}
dest.SetModelManager(self)
return dest
}
func (self *SSnapshotManager) GetDiskSnapshotCount(diskId string) int {
q := self.Query().SubQuery()
return q.Query().Filter(sqlchemy.AND(sqlchemy.Equals(q.Field("disk_id"), diskId),
sqlchemy.Equals(q.Field("pending_deleted"), false))).Count()
}
func (self *SSnapshotManager) CreateSnapshot(ctx context.Context, userCred mcclient.TokenCredential, createdBy, diskId, guestId, location, name string) (*SSnapshot, error) {
iDisk, err := DiskManager.FetchById(diskId)
if err != nil {
return nil, err
}
disk := iDisk.(*SDisk)
snapshot := &SSnapshot{}
snapshot.SetModelManager(self)
snapshot.ProjectId = userCred.GetProjectId()
snapshot.DiskId = disk.Id
snapshot.StorageId = disk.StorageId
snapshot.Size = disk.DiskSize
snapshot.Location = location
snapshot.CreatedBy = createdBy
snapshot.Name = name
err = SnapshotManager.TableSpec().Insert(snapshot)
if err != nil {
return nil, err
}
return snapshot, nil
}
func (self *SSnapshot) AllowDeleteItem(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return self.IsOwner(userCred)
}
func (self *SSnapshot) StartSnapshotDeleteTask(ctx context.Context, userCred mcclient.TokenCredential, reloadDisk bool, parentTaskId string) error {
params := jsonutils.NewDict()
params.Set("reload_disk", jsonutils.NewBool(reloadDisk))
task, err := taskman.TaskManager.NewTask(ctx, "SnapshotDeleteTask", self, userCred, params, parentTaskId, "", nil)
if err != nil {
log.Errorf(err.Error())
return err
} else {
task.ScheduleRun(nil)
}
return nil
}
func (self *SSnapshot) ValidateDeleteCondition(ctx context.Context) error {
return nil
}
func (self *SSnapshot) CustomizeDelete(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) error {
if self.CreatedBy == MANUAL {
if !self.PendingDeleted {
return self.SVirtualResourceBase.PendingDelete()
} else {
return self.StartSnapshotDeleteTask(ctx, userCred, false, "")
}
} else {
return httperrors.NewBadRequestError("Cannot delete snapshot created by %s", self.CreatedBy)
}
}
func (self *SSnapshot) AllowPerformDeleted(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return self.IsOwner(userCred)
}
func (self *SSnapshot) PerformDeleted(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
self.GetModelManager().TableSpec().Update(self, func() error {
self.OutOfChain = true
return nil
})
err := self.StartSnapshotDeleteTask(ctx, userCred, true, "")
return nil, err
}
func (self *SSnapshotManager) AllowGetPropertyMaxCount(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) bool {
return true
}
func (self *SSnapshotManager) GetPropertyMaxCount(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject) (jsonutils.JSONObject, error) {
ret := jsonutils.NewDict()
ret.Set("max_count", jsonutils.NewInt(int64(DISK_MAX_SNAPSHOT)))
return ret, nil
}
func (self *SSnapshotManager) GetConvertSnapshot(deleteSnapshot *SSnapshot) (*SSnapshot, error) {
dest := make([]SSnapshot, 0)
q := self.Query().SubQuery()
err := q.Query().Filter(sqlchemy.AND(sqlchemy.Equals(q.Field("disk_id"), deleteSnapshot.DiskId),
sqlchemy.Equals(q.Field("status"), SNAPSHOT_READY),
sqlchemy.Equals(q.Field("out_of_chain"), false))).
Asc("created_at").Limit(2).All(&dest)
if err != nil {
return nil, err
}
if len(dest) == 2 && dest[0].Id == deleteSnapshot.Id {
dest[1].SetModelManager(self)
return &dest[1], nil
}
return nil, fmt.Errorf("Snapshot %s cannot convert", deleteSnapshot.Id)
}
func (self *SSnapshotManager) AllowPerformDeleteDiskSnapshots(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) bool {
return userCred.IsAdmin()
}
func (self *SSnapshotManager) PerformDeleteDiskSnapshots(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
diskId, err := data.GetString("disk_id")
if err != nil {
return nil, err
}
disk, err := DiskManager.FetchById(diskId)
if disk != nil {
return nil, httperrors.NewBadRequestError("Cannot Delete disk %s snapshots, disk exist", diskId)
}
snapshots := self.GetDiskSnapshots(diskId)
if snapshots == nil || len(snapshots) == 0 {
return nil, httperrors.NewNotFoundError("Disk %s dose not have snapshot", diskId)
}
err = snapshots[0].StartSnapshotsDeleteTask(ctx, userCred, "")
return nil, err
}
func (self *SSnapshot) StartSnapshotsDeleteTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
task, err := taskman.TaskManager.NewTask(ctx, "BatchSnapshostDeleteTask", self, userCred, nil, parentTaskId, "", nil)
if err != nil {
log.Errorf(err.Error())
return err
} else {
task.ScheduleRun(nil)
}
return nil
}
func (self *SSnapshot) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
return db.DeleteModel(ctx, userCred, self)
}
func (self *SSnapshot) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
return nil
}
+3
View File
@@ -49,6 +49,9 @@ type ComputeOptions struct {
BaremetalPreparePackageUrl string `help:"Baremetal online register package"`
AutoSnapshotDay int `default:"1" help:"Days auto snapshot disks, default 1 day"`
AutoSnapshotHour int `default:"2" help:"What hour take sanpshot, default 02:00"`
cloudcommon.DBOptions
}
+4 -3
View File
@@ -52,10 +52,11 @@ func StartService() {
if db.CheckSync(options.Options.AutoSyncTable) {
err := models.InitDB()
if err == nil {
cron := cronman.GetCronJobManager()
cron.AddJob1("CleanPendingDeleteServers", time.Duration(options.Options.PendingDeleteCheckSeconds)*time.Second, models.GuestManager.CleanPendingDeleteServers)
cron.AddJob1("CleanPendingDeleteDisks", time.Duration(options.Options.PendingDeleteCheckSeconds)*time.Second, models.DiskManager.CleanPendingDeleteDisks)
cron.AddJob2("AutoDiskSnapshot", options.Options.AutoSnapshotDay, options.Options.AutoSnapshotHour, 0, 0, models.DiskManager.AutoDiskSnapshot)
cron := cronman.NewCronJobManager(0)
cron.AddJob("CleanPendingDeleteServers", time.Duration(options.Options.PendingDeleteCheckSeconds)*time.Second, models.GuestManager.CleanPendingDeleteServers)
cron.AddJob("CleanPendingDeleteDisks", time.Duration(options.Options.PendingDeleteCheckSeconds)*time.Second, models.DiskManager.CleanPendingDeleteDisks)
cron.Start()
defer cron.Stop()
@@ -0,0 +1,267 @@
package tasks
import (
"context"
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
"yunion.io/x/onecloud/pkg/compute/models"
)
type GuestDiskSnapshotTask struct {
SGuestBaseTask
}
func init() {
taskman.RegisterTask(GuestDiskSnapshotTask{})
taskman.RegisterTask(SnapshotDeleteTask{})
taskman.RegisterTask(BatchSnapshostDeleteTask{})
}
func (self *GuestDiskSnapshotTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
guest := obj.(*models.SGuest)
self.DoDiskSnapshot(ctx, guest)
}
func (self *GuestDiskSnapshotTask) DoDiskSnapshot(ctx context.Context, guest *models.SGuest) {
diskId, err := self.Params.GetString("disk_id")
if err != nil {
self.TaskFailed(ctx, guest, err.Error())
return
}
snapshotId, err := self.Params.GetString("snapshot_id")
if err != nil {
self.TaskFailed(ctx, guest, err.Error())
return
}
self.SetStage("OnDiskSnapshotComplete", nil)
guest.SetStatus(self.UserCred, models.VM_SNAPSHOT, "")
err = guest.GetDriver().RequestDiskSnapshot(ctx, guest, self, snapshotId, diskId)
if err != nil {
self.TaskFailed(ctx, guest, err.Error())
return
}
}
func (self *GuestDiskSnapshotTask) OnDiskSnapshotComplete(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) {
res := data.(*jsonutils.JSONDict)
location, err := res.GetString("location")
if err != nil {
log.Infof("OnDiskSnapshotComplete called with data no location")
return
}
snapshotId, _ := self.Params.GetString("snapshot_id")
iSnapshot, _ := models.SnapshotManager.FetchById(snapshotId)
snapshot := iSnapshot.(*models.SSnapshot)
models.SnapshotManager.TableSpec().Update(snapshot, func() error {
snapshot.Location = location
snapshot.Status = models.SNAPSHOT_READY
return nil
})
guest.SetStatus(self.UserCred, models.VM_SNAPSHOT_SUCC, "")
self.TaskComplete(ctx, guest, nil)
}
func (self *GuestDiskSnapshotTask) OnDiskSnapshotCompleteFailed(ctx context.Context, guest *models.SGuest, err jsonutils.JSONObject) {
self.TaskFailed(ctx, guest, err.String())
}
func (self *GuestDiskSnapshotTask) OnAutoDeleteSnapshot(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) {
self.TaskComplete(ctx, guest, data)
}
func (self *GuestDiskSnapshotTask) OnAutoDeleteSnapshotFailed(ctx context.Context, guest *models.SGuest, err jsonutils.JSONObject) {
log.Errorf("Auto Delete Snapshot Failed %s", err.String())
self.TaskComplete(ctx, guest, err)
}
func (self *GuestDiskSnapshotTask) TaskComplete(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) {
guest.StartSyncstatus(ctx, self.UserCred, self.GetTaskId())
self.SetStage("OnSyncStatus", nil)
}
func (self *GuestDiskSnapshotTask) OnSyncStatus(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) {
self.SetStageComplete(ctx, nil)
}
func (self *GuestDiskSnapshotTask) TaskFailed(ctx context.Context, guest *models.SGuest, reason string) {
snapshotId, _ := self.Params.GetString("snapshot_id")
iSnapshot, _ := models.SnapshotManager.FetchById(snapshotId)
snapshot := iSnapshot.(*models.SSnapshot)
models.SnapshotManager.TableSpec().Update(snapshot, func() error {
snapshot.Status = models.SNAPSHOT_FAILED
return nil
})
self.SetStageFailed(ctx, reason)
guest.SetStatus(self.UserCred, models.VM_SNAPSHOT_FAILED, reason)
}
/***************************** Snapshot Delete Task *****************************/
type SnapshotDeleteTask struct {
taskman.STask
}
func (self *SnapshotDeleteTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
snapshot := obj.(*models.SSnapshot)
guest, err := snapshot.GetGuest()
if err != nil {
self.SetStageFailed(ctx, err.Error())
return
}
if guest == nil {
self.SetStageFailed(ctx, "Cannot delete without guest")
return
}
if jsonutils.QueryBoolean(self.Params, "reload_disk", false) && snapshot.OutOfChain {
self.StartReloadDisk(ctx, snapshot, guest)
} else {
self.StartDeleteSnapshot(ctx, snapshot, guest)
}
}
func (self *SnapshotDeleteTask) StartReloadDisk(ctx context.Context, snapshot *models.SSnapshot, guest *models.SGuest) {
self.SetStage("OnReloadDiskSnapshot", nil)
guest.SetStatus(self.UserCred, models.VM_SNAPSHOT, "Start Reload Snapshot")
params := jsonutils.NewDict()
params.Set("disk_id", jsonutils.NewString(snapshot.DiskId))
err := guest.GetDriver().RequestReloadDiskSnapshot(ctx, guest, self, params)
if err != nil {
self.TaskFailed(ctx, snapshot, err.Error())
}
}
func (self *SnapshotDeleteTask) StartDeleteSnapshot(ctx context.Context, snapshot *models.SSnapshot, guest *models.SGuest) {
convertSnapshot, err := models.SnapshotManager.GetConvertSnapshot(snapshot)
if err != nil {
self.TaskFailed(ctx, snapshot, err.Error())
return
}
params := jsonutils.NewDict()
params.Set("delete_snapshot", jsonutils.NewString(snapshot.Id))
params.Set("disk_id", jsonutils.NewString(snapshot.DiskId))
if !snapshot.OutOfChain {
params.Set("convert_snapshot", jsonutils.NewString(convertSnapshot.Id))
var pendingDelete = jsonutils.JSONFalse
if snapshot.CreatedBy == models.MANUAL && snapshot.PendingDeleted == false {
pendingDelete = jsonutils.JSONTrue
}
params.Set("pending_delete", pendingDelete)
} else {
params.Set("auto_deleted", jsonutils.JSONTrue)
}
self.SetStage("OnDeleteSnapshot", nil)
guest.SetStatus(self.UserCred, models.VM_SNAPSHOT, "Start Delete Snapshot")
err = guest.GetDriver().RequestDeleteSnapshot(ctx, guest, self, params)
if err != nil {
self.TaskFailed(ctx, snapshot, err.Error())
}
}
func (self *SnapshotDeleteTask) OnDeleteSnapshot(ctx context.Context, snapshot *models.SSnapshot, data jsonutils.JSONObject) {
if !jsonutils.QueryBoolean(data, "deleted", false) {
log.Infof("OnDeleteSnapshot with no deleted")
return
}
if snapshot.OutOfChain {
snapshot.RealDelete(ctx, self.UserCred)
self.TaskComplete(ctx, snapshot, nil)
} else {
guest, err := snapshot.GetGuest()
if err != nil {
self.SetStageFailed(ctx, err.Error())
return
}
var pendingDelete = false
if snapshot.CreatedBy == models.MANUAL && snapshot.PendingDeleted == false &&
!jsonutils.QueryBoolean(self.Params, "force_delete", false) {
pendingDelete = true
}
if pendingDelete {
snapshot.PendingDelete()
} else {
snapshot.RealDelete(ctx, self.UserCred)
}
self.SetStage("TaskComplete", nil)
guest.StartSyncstatus(ctx, self.UserCred, "")
}
}
func (self *SnapshotDeleteTask) OnDeleteSnapshotFailed(ctx context.Context, snapshot *models.SSnapshot, data jsonutils.JSONObject) {
self.SetStageFailed(ctx, data.String())
}
func (self *SnapshotDeleteTask) OnReloadDiskSnapshot(ctx context.Context, snapshot *models.SSnapshot, data jsonutils.JSONObject) {
if !jsonutils.QueryBoolean(data, "reopen", false) {
log.Infof("OnDeleteSnapshot with no reopen")
return
}
if snapshot.CreatedBy == models.AUTO {
err := snapshot.RealDelete(ctx, self.UserCred)
if err != nil {
self.TaskFailed(ctx, snapshot, err.Error())
return
}
} else {
err := snapshot.SVirtualResourceBase.PendingDelete()
if err != nil {
self.TaskFailed(ctx, snapshot, err.Error())
return
}
}
self.SetStage("TaskComplete", nil)
guest, err := snapshot.GetGuest()
if err != nil {
self.SetStageFailed(ctx, err.Error())
return
}
guest.StartSyncstatus(ctx, self.UserCred, self.GetTaskId())
}
func (self *SnapshotDeleteTask) TaskComplete(ctx context.Context, snapshot *models.SSnapshot, data jsonutils.JSONObject) {
self.SetStageComplete(ctx, nil)
}
func (self *SnapshotDeleteTask) TaskFailed(ctx context.Context, snapshot *models.SSnapshot, reason string) {
self.SetStageFailed(ctx, reason)
guest, err := snapshot.GetGuest()
if err != nil {
log.Errorln(err.Error())
return
}
guest.StartSyncstatus(ctx, self.UserCred, "")
}
type BatchSnapshostDeleteTask struct {
taskman.STask
}
func (self *BatchSnapshostDeleteTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
snapshot := obj.(*models.SSnapshot)
self.StartStorageDeleteSnapshot(ctx, snapshot)
}
func (self *BatchSnapshostDeleteTask) StartStorageDeleteSnapshot(ctx context.Context, snapshot *models.SSnapshot) {
host := snapshot.GetHost()
if host == nil {
self.SetStageFailed(ctx, "Cannot found snapshot host")
return
}
self.SetStage("OnStorageDeleteSnapshot", nil)
err := host.GetHostDriver().RequestDeleteSnapshotWithStorage(ctx, host, snapshot, self)
if err != nil {
self.SetStageFailed(ctx, err.Error())
}
}
func (self *BatchSnapshostDeleteTask) OnStorageDeleteSnapshot(ctx context.Context, snapshot *models.SSnapshot, data jsonutils.JSONObject) {
snapshots := models.SnapshotManager.GetDiskSnapshots(snapshot.DiskId)
for i := 0; i < len(snapshots); i++ {
snapshots[i].RealDelete(ctx, self.UserCred)
}
self.SetStageComplete(ctx, nil)
}
+14
View File
@@ -0,0 +1,14 @@
package modules
var (
Snapshots ResourceManager
)
func init() {
Snapshots = NewComputeManager("snapshot", "snapshots",
[]string{"ID", "Name", "Size", "Status",
"Disk_id", "Guest_id", "Created_at"},
[]string{"Storage_id", "Create_by", "Location"})
registerCompute(&Snapshots)
}
+2 -1
View File
@@ -16,6 +16,7 @@ import (
"yunion.io/x/jsonutils"
"yunion.io/x/log"
"yunion.io/x/pkg/gotypes"
"yunion.io/x/pkg/trace"
"yunion.io/x/onecloud/pkg/appctx"
@@ -154,7 +155,7 @@ func Request(client *http.Client, ctx context.Context, method string, urlStr str
func JSONRequest(client *http.Client, ctx context.Context, method string, urlStr string, header http.Header, body jsonutils.JSONObject, debug bool) (http.Header, jsonutils.JSONObject, error) {
bodystr := ""
if body != nil {
if !gotypes.IsNil(body) {
bodystr = body.String()
}
jbody := strings.NewReader(bodystr)