From da831eaf87e28ddb805f042c3be9469fed37aba9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=83=91=E9=9B=A8?= Date: Thu, 31 Oct 2019 15:34:11 +0800 Subject: [PATCH] fix: Add operational restrictions and fix some bugs for guest image 1. Support generate_name for guest image 2. Only allow subimage of guest image to update description 3. Add support for creating vm from guest image in fetchDiskInfo(disk.go) 4. Fix the bug vm can't start automatically after build guest image 5. Fix the bug that project of guest image is incorrect 6. Fix the bug about updating guest image --- cmd/climc/shell/servers.go | 4 ++-- pkg/apis/image/consts.go | 2 ++ pkg/compute/guestdrivers/virtualization.go | 2 +- pkg/compute/models/disks.go | 7 ++++++- pkg/compute/models/guest_actions.go | 4 ++-- pkg/compute/tasks/guest_save_instance_image_task.go | 3 ++- pkg/image/models/images.go | 8 +++----- pkg/image/tasks/guest_image_update_task.go | 9 +++++++++ pkg/mcclient/options/servers.go | 6 ++++++ 9 files changed, 33 insertions(+), 12 deletions(-) diff --git a/cmd/climc/shell/servers.go b/cmd/climc/shell/servers.go index 0c25e409a0..cb4af1b473 100644 --- a/cmd/climc/shell/servers.go +++ b/cmd/climc/shell/servers.go @@ -556,9 +556,9 @@ func init() { return nil }) - R(&options.ServerSaveImageOptions{}, "server-save-guest-image", + R(&options.ServerSaveGuestImageOptions{}, "server-save-guest-image", "save root disk and data disks to new images and upload to glance.", func(s *mcclient.ClientSession, - opts *options.ServerSaveImageOptions) error { + opts *options.ServerSaveGuestImageOptions) error { params, err := options.StructToParams(opts) if err != nil { diff --git a/pkg/apis/image/consts.go b/pkg/apis/image/consts.go index 1b7210fdac..447a4c5436 100644 --- a/pkg/apis/image/consts.go +++ b/pkg/apis/image/consts.go @@ -47,6 +47,8 @@ const ( IMAGE_IS_READONLY = "is_readonly" IMAGE_PARTITION_TYPE = "partition_type" IMAGE_INSTALLED_CLOUDINIT = "installed_cloud_init" + + IMAGE_STATUS_UPDATING = "updating" ) var ( diff --git a/pkg/compute/guestdrivers/virtualization.go b/pkg/compute/guestdrivers/virtualization.go index d4e8f26639..5edecaf1ac 100644 --- a/pkg/compute/guestdrivers/virtualization.go +++ b/pkg/compute/guestdrivers/virtualization.go @@ -300,7 +300,7 @@ func (self *SVirtualizedGuestDriver) StartGuestSaveImage(ctx context.Context, us func (self *SVirtualizedGuestDriver) StartGuestSaveGuestImage(ctx context.Context, userCred mcclient.TokenCredential, guest *models.SGuest, params *jsonutils.JSONDict, parentTaskId string) error { - + guest.SetStatus(userCred, api.VM_START_SAVE_DISK, "") if task, err := taskman.TaskManager.NewTask(ctx, "GuestSaveGuestImageTask", guest, userCred, params, parentTaskId, "", nil); err != nil { return err diff --git a/pkg/compute/models/disks.go b/pkg/compute/models/disks.go index 4e0ded79ac..8eb1cad439 100644 --- a/pkg/compute/models/disks.go +++ b/pkg/compute/models/disks.go @@ -1511,7 +1511,12 @@ func parseIsoInfo(ctx context.Context, userCred mcclient.TokenCredential, imageI func (self *SDisk) fetchDiskInfo(diskConfig *api.DiskConfig) { if len(diskConfig.ImageId) > 0 { self.TemplateId = diskConfig.ImageId - self.DiskType = api.DISK_TYPE_SYS + // support for create vm from guest image + if len(diskConfig.DiskType) == 0 { + self.DiskType = api.DISK_TYPE_SYS + } else { + self.DiskType = diskConfig.DiskType + } } else if len(diskConfig.SnapshotId) > 0 { self.SnapshotId = diskConfig.SnapshotId self.DiskType = diskConfig.DiskType diff --git a/pkg/compute/models/guest_actions.go b/pkg/compute/models/guest_actions.go index 7e029bbcec..fda6945ab7 100644 --- a/pkg/compute/models/guest_actions.go +++ b/pkg/compute/models/guest_actions.go @@ -198,7 +198,7 @@ func (self *SGuest) PerformSaveGuestImage(ctx context.Context, userCred mcclient if !utils.IsInStringArray(self.Status, []string{api.VM_READY}) { return nil, httperrors.NewBadRequestError("Cannot save image in status %s", self.Status) } - if !data.Contains("name") { + if !data.Contains("name") && !data.Contains("generate_name") { return nil, httperrors.NewMissingParameterError("Image name is required") } if self.Hypervisor != api.HYPERVISOR_KVM { @@ -237,7 +237,7 @@ func (self *SGuest) PerformSaveGuestImage(ctx context.Context, userCred mcclient kwargs.Add(images, "images") - s := auth.GetAdminSession(ctx, options.Options.Region, "") + s := auth.GetSession(ctx, userCred, options.Options.Region, "") ret, err := modules.GuestImages.Create(s, kwargs) if err != nil { return nil, err diff --git a/pkg/compute/tasks/guest_save_instance_image_task.go b/pkg/compute/tasks/guest_save_instance_image_task.go index 6fc090e16d..79feb82fdd 100644 --- a/pkg/compute/tasks/guest_save_instance_image_task.go +++ b/pkg/compute/tasks/guest_save_instance_image_task.go @@ -66,10 +66,11 @@ func (self *GuestSaveGuestImageTask) OnSaveRootImageComplete(ctx context.Context self.taskFailed(ctx, guest, "subtask failed") } - if restart, _ := self.GetParams().Bool("restart"); restart { + if restart, _ := self.GetParams().Bool("auto_start"); restart { self.SetStage("on_start_server_complete", nil) guest.StartGueststartTask(ctx, self.GetUserCred(), nil, self.GetTaskId()) } else { + guest.SetStatus(self.UserCred, api.VM_READY, "") self.taskSuc(ctx, guest) } } diff --git a/pkg/image/models/images.go b/pkg/image/models/images.go index 1aa234dedc..6c53509971 100644 --- a/pkg/image/models/images.go +++ b/pkg/image/models/images.go @@ -554,7 +554,7 @@ func (self *SImage) ImageProbeAndCustomization( func (self *SImage) ValidateUpdateData(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data *jsonutils.JSONDict) (*jsonutils.JSONDict, error) { if self.Status != api.IMAGE_STATUS_QUEUED { - if self.IsGuestImage.IsTrue() && !self.CanUpdate(data) { + if !self.CanUpdate(data) { return nil, httperrors.NewForbiddenError("image is the part of guest imgae") } appParams := appsrv.AppContextGetParams(ctx) @@ -1263,8 +1263,6 @@ func (self *SImage) PerformUpdateTorrentStatus(ctx context.Context, userCred mcc func (self *SImage) CanUpdate(data jsonutils.JSONObject) bool { dict := data.(*jsonutils.JSONDict) - if dict.Length() == 1 && !dict.Contains("description") { - return false - } - return true + // Only allow update description for now when Image is part of guest image + return self.IsGuestImage.IsFalse() || (dict.Length() == 1 && dict.Contains("description")) } diff --git a/pkg/image/tasks/guest_image_update_task.go b/pkg/image/tasks/guest_image_update_task.go index c7d4291444..a6dc3e8ba1 100644 --- a/pkg/image/tasks/guest_image_update_task.go +++ b/pkg/image/tasks/guest_image_update_task.go @@ -21,6 +21,7 @@ import ( "yunion.io/x/jsonutils" "yunion.io/x/sqlchemy" + "yunion.io/x/onecloud/pkg/apis/image" "yunion.io/x/onecloud/pkg/cloudcommon/db" "yunion.io/x/onecloud/pkg/cloudcommon/db/taskman" "yunion.io/x/onecloud/pkg/image/models" @@ -37,6 +38,10 @@ func init() { func (self *GuestImageUpdateTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { guestImage := obj.(*models.SGuestImage) + + self.Params.Add(jsonutils.NewString(guestImage.Status), "old_status") + guestImage.SetStatus(self.UserCred, image.IMAGE_STATUS_UPDATING, "") + subImages, err := models.GuestImageJointManager.GetImagesByFilter(guestImage.GetId(), func(q *sqlchemy.SQuery) *sqlchemy.SQuery { return q.Asc("name") }) @@ -75,10 +80,14 @@ func (self *GuestImageUpdateTask) OnInit(ctx context.Context, obj db.IStandalone return } } + oldStatus, _ := self.Params.GetString("old_status") + guestImage.SetStatus(self.UserCred, oldStatus, "") self.SetStageComplete(ctx, nil) } func (self *GuestImageUpdateTask) taskFailed(ctx context.Context, guestImage *models.SGuestImage, reason string) { + oldStatus, _ := self.Params.GetString("old_status") + guestImage.SetStatus(self.UserCred, oldStatus, "") db.OpsLog.LogEvent(guestImage, db.ACT_SUBIMAGE_UPDATE_FAIL, reason, self.UserCred) logclient.AddActionLogWithContext(ctx, guestImage, logclient.ACT_SUBIMAGE_UPDATE, reason, self.UserCred, false) self.SetStageFailed(ctx, reason) diff --git a/pkg/mcclient/options/servers.go b/pkg/mcclient/options/servers.go index 7268e260d0..29d2755e43 100644 --- a/pkg/mcclient/options/servers.go +++ b/pkg/mcclient/options/servers.go @@ -538,6 +538,12 @@ type ServerSaveImageOptions struct { AutoStart *bool `help:"Auto start server after image saved"` } +type ServerSaveGuestImageOptions struct { + ID string `help:"ID or name of server" json:"-"` + IMAGE string `help:"Image name" json:"name"` + AutoStart *bool `help:"Auto start server after image saved"` +} + type ServerRebuildRootOptions struct { ID string `help:"Server to rebuild root" json:"-"` ImageId string `help:"New root Image template ID" json:"image_id" token:"image"`