mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-24 16:03:43 +08:00
Merge pull request #3411 from Mjoycarry/feature/guest_image_second
Feature & Fix: Guest Image
This commit is contained in:
@@ -247,6 +247,8 @@ const (
|
||||
ACT_RESTORE = "restore"
|
||||
ACT_CHANGE_CONFIG = "change_config"
|
||||
ACT_RESET_PASSWORD = "reset_password"
|
||||
|
||||
ACT_SUBIMAGE_UPDATE_FAIL = "guest_image_subimages_update_fail"
|
||||
)
|
||||
|
||||
type SOpsLogManager struct {
|
||||
|
||||
@@ -332,6 +332,18 @@ var (
|
||||
Action: PolicyActionGet,
|
||||
Result: rbacutils.Allow,
|
||||
},
|
||||
{
|
||||
Service: "image",
|
||||
Resource: "guestimages",
|
||||
Action: PolicyActionList,
|
||||
Result: rbacutils.Allow,
|
||||
},
|
||||
{
|
||||
Service: "image",
|
||||
Resource: "guestimages",
|
||||
Action: PolicyActionGet,
|
||||
Result: rbacutils.Allow,
|
||||
},
|
||||
{
|
||||
Service: "log",
|
||||
Resource: "actions",
|
||||
|
||||
@@ -4725,7 +4725,11 @@ func (self *SGuest) GetDiskSnapshotsNotInInstanceSnapshots() ([]SSnapshot, error
|
||||
}
|
||||
|
||||
func (self *SGuestManager) checkGuestImage(ctx context.Context, input *api.ServerCreateInput) error {
|
||||
// that data disks has image id show that these image is part of guest image.
|
||||
// There is no need to check the availability of guest imag if input.Disks is empty
|
||||
if len(input.Disks) == 0 {
|
||||
return nil
|
||||
}
|
||||
// That data disks has image id show that these image is part of guest image
|
||||
for _, config := range input.Disks[1:] {
|
||||
if len(config.ImageId) != 0 && len(input.GuestImageID) == 0 {
|
||||
return httperrors.NewMissingParameterError("guest_image_id")
|
||||
|
||||
@@ -110,7 +110,6 @@ func (gi *SGuestImage) PostCreate(ctx context.Context, userCred mcclient.TokenCr
|
||||
// HACK
|
||||
appParams := appsrv.AppContextGetParams(ctx)
|
||||
appParams.Request.ContentLength = 0
|
||||
kwargs.Add(jsonutils.NewString("此镜像为主机镜像子镜像,不可操作"), "description")
|
||||
|
||||
for i := 0; i < len(images); i++ {
|
||||
params := jsonutils.DeepCopy(kwargs).(*jsonutils.JSONDict)
|
||||
@@ -291,6 +290,7 @@ func (self *SGuestImage) getMoreDetails(ctx context.Context, userCred mcclient.T
|
||||
if !image.IsData.IsTrue() {
|
||||
rootImage = sPair{image.Id, images[i].Name, image.MinDiskMB, image.DiskFormat}
|
||||
extra.Add(jsonutils.NewInt(int64(image.MinRamMB)), "min_ram_mb")
|
||||
extra.Add(jsonutils.NewString(image.DiskFormat), "disk_format")
|
||||
continue
|
||||
}
|
||||
dataImages = append(dataImages, sPair{image.Id, image.Name, image.MinDiskMB, image.DiskFormat})
|
||||
@@ -338,6 +338,23 @@ func (self *SGuestImage) GetExtraDetails(ctx context.Context, userCred mcclient.
|
||||
return self.getMoreDetails(ctx, userCred, query, extra), nil
|
||||
}
|
||||
|
||||
func (self *SGuestImage) PostUpdate(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject,
|
||||
data jsonutils.JSONObject) {
|
||||
|
||||
if !data.Contains("name") && !data.Contains("properties") {
|
||||
return
|
||||
}
|
||||
params := data.(*jsonutils.JSONDict)
|
||||
if task, err := taskman.TaskManager.NewTask(ctx, "GuestImageUpdateTask", self, userCred, params, "", "",
|
||||
nil); err != nil {
|
||||
|
||||
log.Errorf("GusetImage %s fail to start GuestImageUpdateTask", self.Id)
|
||||
return
|
||||
} else {
|
||||
task.ScheduleRun(nil)
|
||||
}
|
||||
}
|
||||
|
||||
var checkStatus = map[string]int{
|
||||
api.IMAGE_STATUS_ACTIVE: 1,
|
||||
api.IMAGE_STATUS_QUEUED: 2,
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"context"
|
||||
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
@@ -82,7 +83,9 @@ func (gm *SGuestImageJointManager) GetGuestImageByImageId(imageId string) (*SGue
|
||||
return model.(*SGuestImage), nil
|
||||
}
|
||||
|
||||
func (gm *SGuestImageJointManager) GetImagesByGuestImageId(guestImageId string) ([]SImage, error) {
|
||||
func (gm *SGuestImageJointManager) GetImagesByFilter(guestImageId string,
|
||||
filter func(q *sqlchemy.SQuery) *sqlchemy.SQuery) ([]SImage, error) {
|
||||
|
||||
giJoints, err := gm.GetByGuestImageId(guestImageId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get joints of guest and image failed")
|
||||
@@ -95,6 +98,7 @@ func (gm *SGuestImageJointManager) GetImagesByGuestImageId(guestImageId string)
|
||||
imageIds[i] = giJoints[i].ImageId
|
||||
}
|
||||
q := ImageManager.Query().In("id", imageIds)
|
||||
q = filter(q)
|
||||
images := make([]SImage, 0, len(imageIds))
|
||||
err = db.FetchModelObjects(ImageManager, q, &images)
|
||||
if err != nil {
|
||||
@@ -103,6 +107,12 @@ func (gm *SGuestImageJointManager) GetImagesByGuestImageId(guestImageId string)
|
||||
return images, nil
|
||||
}
|
||||
|
||||
func (gm *SGuestImageJointManager) GetImagesByGuestImageId(guestImageId string) ([]SImage, error) {
|
||||
return gm.GetImagesByFilter(guestImageId, func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
|
||||
return q
|
||||
})
|
||||
}
|
||||
|
||||
func (gt *SGuestImageJoint) RealDelete(ctx context.Context, userCred mcclient.TokenCredential) error {
|
||||
return db.DeleteModel(ctx, userCred, gt)
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
if self.IsGuestImage.IsTrue() && !self.CanUpdate(data) {
|
||||
return nil, httperrors.NewForbiddenError("image is the part of guest imgae")
|
||||
}
|
||||
appParams := appsrv.AppContextGetParams(ctx)
|
||||
@@ -757,7 +757,7 @@ func (manager *SImageManager) getExpiredPendingDeleteDisks() []SImage {
|
||||
q := manager.Query()
|
||||
// those images part of guest image will be clean in GuestImageManager.CleanPendingDeleteImages
|
||||
q = q.IsTrue("pending_deleted").LT("pending_deleted_at",
|
||||
deadline).Limit(options.Options.PendingDeleteMaxCleanBatchSize).IsFalse("belong_guest_image")
|
||||
deadline).Limit(options.Options.PendingDeleteMaxCleanBatchSize).IsFalse("is_guest_image")
|
||||
|
||||
disks := make([]SImage, 0)
|
||||
err := db.FetchModelObjects(ImageManager, q, &disks)
|
||||
@@ -1260,3 +1260,11 @@ func (self *SImage) PerformUpdateTorrentStatus(ctx context.Context, userCred mcc
|
||||
subimg.SetStatusSeeding(true)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (self *SImage) CanUpdate(data jsonutils.JSONObject) bool {
|
||||
dict := data.(*jsonutils.JSONDict)
|
||||
if dict.Length() == 1 && !dict.Contains("description") {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tasks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/sqlchemy"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/image/models"
|
||||
"yunion.io/x/onecloud/pkg/util/logclient"
|
||||
)
|
||||
|
||||
type GuestImageUpdateTask struct {
|
||||
taskman.STask
|
||||
}
|
||||
|
||||
func init() {
|
||||
taskman.RegisterTask(GuestImageUpdateTask{})
|
||||
}
|
||||
|
||||
func (self *GuestImageUpdateTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
|
||||
guestImage := obj.(*models.SGuestImage)
|
||||
subImages, err := models.GuestImageJointManager.GetImagesByFilter(guestImage.GetId(), func(q *sqlchemy.SQuery) *sqlchemy.SQuery {
|
||||
return q.Asc("name")
|
||||
})
|
||||
if err != nil {
|
||||
self.taskFailed(ctx, guestImage, err.Error())
|
||||
}
|
||||
if self.Params.Contains("name") {
|
||||
name, _ := self.Params.GetString("name")
|
||||
for i := range subImages[:len(subImages)-1] {
|
||||
sub := &subImages[i]
|
||||
_, err := db.Update(sub, func() error {
|
||||
sub.Name = fmt.Sprintf("%s-%s-%d", name, "data", i)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
self.taskFailed(ctx, guestImage, fmt.Sprintf("modify subimage's name failed: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
}
|
||||
root := &subImages[len(subImages)-1]
|
||||
_, err := db.Update(root, func() error {
|
||||
root.Name = fmt.Sprintf("%s-%s", name, "root")
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
self.taskFailed(ctx, guestImage, fmt.Sprintf("modify subimage's name failed: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
}
|
||||
if self.Params.Contains("properties") {
|
||||
rootImageId := subImages[len(subImages)-1].GetId()
|
||||
props, _ := self.Params.Get("properties")
|
||||
err := models.ImagePropertyManager.SaveProperties(ctx, self.UserCred, rootImageId, props)
|
||||
if err != nil {
|
||||
self.taskFailed(ctx, guestImage, fmt.Sprintf("save properties error: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
}
|
||||
self.SetStageComplete(ctx, nil)
|
||||
}
|
||||
|
||||
func (self *GuestImageUpdateTask) taskFailed(ctx context.Context, guestImage *models.SGuestImage, reason string) {
|
||||
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)
|
||||
}
|
||||
@@ -169,4 +169,6 @@ const (
|
||||
ACT_VM_DISSOCIATE = "解绑虚拟机"
|
||||
ACT_NATGATEWAY_DISSOCIATE = "解绑NAT网关"
|
||||
ACT_LOADBALANCER_DISSOCIATE = "解绑负载均衡"
|
||||
|
||||
ACT_SUBIMAGE_UPDATE = "更新子镜像"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user