mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-24 16:03:43 +08:00
Merge pull request #20023 from zexi/container-save-vm-image
feat(container): save volume_mount to image
This commit is contained in:
@@ -41,6 +41,7 @@ func init() {
|
||||
cmd.BatchPerform("stop", new(options.ContainerStopOptions))
|
||||
cmd.BatchPerform("start", new(options.ContainerStartOptions))
|
||||
cmd.BatchPerform("syncstatus", new(options.ContainerIdsOptions))
|
||||
cmd.Perform("save-volume-mount-image", new(options.ContainerSaveVolumeMountImage))
|
||||
|
||||
type UpdateSpecOptions struct {
|
||||
ID string `help:"ID or name of server" json:"-"`
|
||||
|
||||
@@ -47,8 +47,8 @@ const (
|
||||
CONTAINER_STATUS_PULLED_IMAGE = "pulled_image"
|
||||
CONTAINER_STATUS_CREATING = "creating"
|
||||
CONTAINER_STATUS_CREATE_FAILED = "create_failed"
|
||||
CONTAINER_STATUS_CACHING_IMAGE = "caching_image"
|
||||
CONTAINER_STATUS_CACHE_IMAGE_FAILED = "cache_image_failed"
|
||||
CONTAINER_STATUS_SAVING_IMAGE = "saving_image"
|
||||
CONTAINER_STATUS_SAVE_IMAGE_FAILED = "save_image_failed"
|
||||
CONTAINER_STATUS_STARTING = "starting"
|
||||
CONTAINER_STATUS_START_FAILED = "start_failed"
|
||||
CONTAINER_STATUS_STOPPING = "stopping"
|
||||
@@ -134,3 +134,10 @@ type ContainerDevice struct {
|
||||
IsolatedDevice *ContainerIsolatedDevice `json:"isolated_device"`
|
||||
Host *ContainerHostDevice `json:"host"`
|
||||
}
|
||||
|
||||
type ContainerSaveVolumeMountToImageInput struct {
|
||||
Name string `json:"name"`
|
||||
GenerateName string `json:"generate_name"`
|
||||
Notes string `json:"notes"`
|
||||
Index int `json:"index"`
|
||||
}
|
||||
|
||||
@@ -102,3 +102,10 @@ type ContainerDesc struct {
|
||||
Name string `json:"name"`
|
||||
Spec *ContainerSpec `json:"spec"`
|
||||
}
|
||||
|
||||
type ContainerSaveVolumeMountToImageInput struct {
|
||||
ImageId string `json:"image_id"`
|
||||
|
||||
VolumeMountIndex int `json:"volume_mount_index"`
|
||||
VolumeMount *ContainerVolumeMount `json:"volume_mount"`
|
||||
}
|
||||
|
||||
@@ -477,3 +477,7 @@ func (p *SPodDriver) IsSupportFloppy(guest *models.SGuest) (bool, error) {
|
||||
func (p *SPodDriver) GetChangeConfigStatus(guest *models.SGuest) ([]string, error) {
|
||||
return []string{api.VM_READY}, nil
|
||||
}
|
||||
|
||||
func (p *SPodDriver) RequestSaveVolumeMountImage(ctx context.Context, userCred mcclient.TokenCredential, task models.IContainerTask) error {
|
||||
return p.performContainerAction(ctx, userCred, task, "save-volume-mount-to-image", task.GetParams())
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/apis"
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
hostapi "yunion.io/x/onecloud/pkg/apis/host"
|
||||
imageapi "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/httperrors"
|
||||
@@ -463,3 +464,65 @@ func (c *SContainer) GetJsonDescAtHost() (*hostapi.ContainerDesc, error) {
|
||||
Spec: spec,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *SContainer) PrepareSaveImage(ctx context.Context, userCred mcclient.TokenCredential, input *api.ContainerSaveVolumeMountToImageInput) (string, error) {
|
||||
imageInput := &CreateGlanceImageInput{
|
||||
Name: input.Name,
|
||||
GenerateName: input.GenerateName,
|
||||
DiskFormat: imageapi.IMAGE_DISK_FORMAT_TGZ,
|
||||
Properties: map[string]string{
|
||||
"notes": input.Notes,
|
||||
},
|
||||
// inherit the ownership of disk
|
||||
ProjectId: c.ProjectId,
|
||||
}
|
||||
// check class metadata
|
||||
cm, err := c.GetAllClassMetadata()
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "unable to GetAllClassMetadata")
|
||||
}
|
||||
imageInput.ClassMetadata = cm
|
||||
return DiskManager.CreateGlanceImage(ctx, userCred, imageInput)
|
||||
}
|
||||
|
||||
func (c *SContainer) PerformSaveVolumeMountImage(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.ContainerSaveVolumeMountToImageInput) (*hostapi.ContainerSaveVolumeMountToImageInput, error) {
|
||||
if c.GetStatus() != api.CONTAINER_STATUS_EXITED {
|
||||
return nil, httperrors.NewInvalidStatusError("Can't save volume disk of container in status %s", c.Status)
|
||||
}
|
||||
if c.GetPod().GetStatus() != api.VM_READY {
|
||||
return nil, httperrors.NewInvalidStatusError("Can't save volume disk of pod in status %s", c.GetPod().GetStatus())
|
||||
}
|
||||
vols := c.GetVolumeMounts()
|
||||
if input.Index < 0 || input.Index >= len(vols) {
|
||||
return nil, httperrors.NewInputParameterError("Only %d volume_mounts", len(vols))
|
||||
}
|
||||
|
||||
imageId, err := c.PrepareSaveImage(ctx, userCred, input)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "prepare to save image")
|
||||
}
|
||||
vrs, err := c.GetVolumeMountRelations()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetVolumeMountRelations")
|
||||
}
|
||||
hvm, err := vrs[input.Index].ToHostMount()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "ToHostMount")
|
||||
}
|
||||
hostInput := &hostapi.ContainerSaveVolumeMountToImageInput{
|
||||
ImageId: imageId,
|
||||
VolumeMountIndex: input.Index,
|
||||
VolumeMount: hvm,
|
||||
}
|
||||
|
||||
return hostInput, c.StartSaveVolumeMountImage(ctx, userCred, hostInput, "")
|
||||
}
|
||||
|
||||
func (c *SContainer) StartSaveVolumeMountImage(ctx context.Context, userCred mcclient.TokenCredential, input *hostapi.ContainerSaveVolumeMountToImageInput, parentTaskId string) error {
|
||||
c.SetStatus(ctx, userCred, api.CONTAINER_STATUS_SAVING_IMAGE, "")
|
||||
task, err := taskman.TaskManager.NewTask(ctx, "ContainerSaveVolumeMountImageTask", c, userCred, jsonutils.Marshal(input).(*jsonutils.JSONDict), parentTaskId, "", nil)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "NewTask")
|
||||
}
|
||||
return task.ScheduleRun(nil)
|
||||
}
|
||||
|
||||
+61
-49
@@ -1149,34 +1149,67 @@ func (self *SDisk) GetZone() (*SZone, error) {
|
||||
return storage.getZone()
|
||||
}
|
||||
|
||||
func (m *SDiskManager) CheckGlanceImage(ctx context.Context, userCred mcclient.TokenCredential, name string, generateName string) error {
|
||||
if len(generateName) == 0 {
|
||||
s := auth.GetAdminSession(ctx, options.Options.Region)
|
||||
imageList, err := image.Images.List(s, jsonutils.Marshal(map[string]string{"name": name, "admin": "true"}))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if imageList.Total > 0 {
|
||||
return httperrors.NewConflictError("Duplicate image name %s", name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type CreateGlanceImageInput struct {
|
||||
Name string
|
||||
GenerateName string
|
||||
VirtualSize int
|
||||
DiskFormat string
|
||||
OsArch string
|
||||
Properties map[string]string
|
||||
ProjectId string
|
||||
EncryptKeyId string
|
||||
ClassMetadata map[string]string
|
||||
}
|
||||
|
||||
func (m *SDiskManager) CreateGlanceImage(ctx context.Context, userCred mcclient.TokenCredential, input *CreateGlanceImageInput) (string, error) {
|
||||
if err := DiskManager.CheckGlanceImage(ctx, userCred, input.Name, input.GenerateName); err != nil {
|
||||
return "", err
|
||||
}
|
||||
/*
|
||||
no need to check quota anymore
|
||||
session := auth.GetSession(userCred, options.Options.Region, "v2")
|
||||
quota := image_models.SQuota{Image: 1}
|
||||
if _, err := image.ImageQuotas.DoQuotaCheck(session, jsonutils.Marshal("a)); err != nil {
|
||||
return "", err
|
||||
}*/
|
||||
us := auth.GetSession(ctx, userCred, options.Options.Region)
|
||||
result, err := image.Images.Create(us, jsonutils.Marshal(input))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
imageId, err := result.GetString("id")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(input.ClassMetadata) > 0 {
|
||||
_, err = image.Images.PerformAction(us, imageId, "set-class-metadata", jsonutils.Marshal(input.ClassMetadata))
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "unable to SetClassMetadata for image %s", imageId)
|
||||
}
|
||||
}
|
||||
return imageId, nil
|
||||
}
|
||||
|
||||
func (self *SDisk) PrepareSaveImage(ctx context.Context, userCred mcclient.TokenCredential, input api.ServerSaveImageInput) (string, error) {
|
||||
zone, _ := self.GetZone()
|
||||
if zone == nil {
|
||||
return "", httperrors.NewResourceNotFoundError("No zone for this disk")
|
||||
}
|
||||
if len(input.GenerateName) == 0 {
|
||||
s := auth.GetAdminSession(ctx, options.Options.Region)
|
||||
imageList, err := image.Images.List(s, jsonutils.Marshal(map[string]string{"name": input.Name, "admin": "true"}))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if imageList.Total > 0 {
|
||||
return "", httperrors.NewConflictError("Duplicate image name %s", input.Name)
|
||||
}
|
||||
}
|
||||
|
||||
opts := struct {
|
||||
Name string
|
||||
GenerateName string
|
||||
VirtualSize int
|
||||
DiskFormat string
|
||||
OsArch string
|
||||
Properties map[string]string
|
||||
|
||||
ProjectId string
|
||||
|
||||
EncryptKeyId string
|
||||
}{
|
||||
imageInput := &CreateGlanceImageInput{
|
||||
Name: input.Name,
|
||||
GenerateName: input.GenerateName,
|
||||
VirtualSize: self.DiskSize,
|
||||
@@ -1191,43 +1224,21 @@ func (self *SDisk) PrepareSaveImage(ctx context.Context, userCred mcclient.Token
|
||||
// inherit the ownership of disk
|
||||
ProjectId: self.ProjectId,
|
||||
}
|
||||
|
||||
if self.IsEncrypted() {
|
||||
encKey, err := self.GetEncryptInfo(ctx, userCred)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "GetEncryptInfo")
|
||||
}
|
||||
opts.EncryptKeyId = encKey.Id
|
||||
}
|
||||
|
||||
/*
|
||||
no need to check quota anymore
|
||||
session := auth.GetSession(userCred, options.Options.Region, "v2")
|
||||
quota := image_models.SQuota{Image: 1}
|
||||
if _, err := image.ImageQuotas.DoQuotaCheck(session, jsonutils.Marshal("a)); err != nil {
|
||||
return "", err
|
||||
}*/
|
||||
us := auth.GetSession(ctx, userCred, options.Options.Region)
|
||||
result, err := image.Images.Create(us, jsonutils.Marshal(opts))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
imageId, err := result.GetString("id")
|
||||
if err != nil {
|
||||
return "", err
|
||||
imageInput.EncryptKeyId = encKey.Id
|
||||
}
|
||||
// check class metadata
|
||||
cm, err := self.GetAllClassMetadata()
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "unable to GetAllClassMetadata")
|
||||
}
|
||||
if len(cm) > 0 {
|
||||
_, err = image.Images.PerformAction(us, imageId, "set-class-metadata", jsonutils.Marshal(cm))
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "unable to SetClassMetadata for image %s", imageId)
|
||||
}
|
||||
}
|
||||
return imageId, nil
|
||||
imageInput.ClassMetadata = cm
|
||||
|
||||
return DiskManager.CreateGlanceImage(ctx, userCred, imageInput)
|
||||
}
|
||||
|
||||
func (self *SDisk) PerformSave(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input api.DiskSaveInput) (jsonutils.JSONObject, error) {
|
||||
@@ -1249,6 +1260,7 @@ func (self *SDisk) PerformSave(ctx context.Context, userCred mcclient.TokenCrede
|
||||
opts := api.ServerSaveImageInput{
|
||||
Name: input.Name,
|
||||
}
|
||||
|
||||
input.ImageId, err = self.PrepareSaveImage(ctx, userCred, opts)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "PrepareSaveImage")
|
||||
|
||||
@@ -23,4 +23,5 @@ type IPodDriver interface {
|
||||
RequestDeleteContainer(ctx context.Context, userCred mcclient.TokenCredential, task IContainerTask) error
|
||||
RequestSyncContainerStatus(ctx context.Context, userCred mcclient.TokenCredential, task IContainerTask) error
|
||||
RequestPullContainerImage(ctx context.Context, userCred mcclient.TokenCredential, task IContainerTask) error
|
||||
RequestSaveVolumeMountImage(ctx context.Context, userCred mcclient.TokenCredential, task IContainerTask) error
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package tasks
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db"
|
||||
"yunion.io/x/onecloud/pkg/cloudcommon/db/taskman"
|
||||
"yunion.io/x/onecloud/pkg/compute/models"
|
||||
)
|
||||
|
||||
func init() {
|
||||
taskman.RegisterTask(ContainerSaveVolumeMountImageTask{})
|
||||
}
|
||||
|
||||
type ContainerSaveVolumeMountImageTask struct {
|
||||
ContainerBaseTask
|
||||
}
|
||||
|
||||
func (t *ContainerSaveVolumeMountImageTask) OnInit(ctx context.Context, obj db.IStandaloneModel, body jsonutils.JSONObject) {
|
||||
t.requestSaveImage(ctx, obj.(*models.SContainer))
|
||||
}
|
||||
|
||||
func (t *ContainerSaveVolumeMountImageTask) requestSaveImage(ctx context.Context, container *models.SContainer) {
|
||||
t.SetStage("OnImageSaved", nil)
|
||||
if err := t.GetPodDriver().RequestSaveVolumeMountImage(ctx, t.GetUserCred(), t); err != nil {
|
||||
t.OnImageSavedFailed(ctx, container, jsonutils.NewString(err.Error()))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (t *ContainerSaveVolumeMountImageTask) OnImageSaved(ctx context.Context, container *models.SContainer, data jsonutils.JSONObject) {
|
||||
t.SetStage("OnSyncStatus", nil)
|
||||
container.StartSyncStatusTask(ctx, t.GetUserCred(), t.GetTaskId())
|
||||
}
|
||||
|
||||
func (t *ContainerSaveVolumeMountImageTask) OnImageSavedFailed(ctx context.Context, container *models.SContainer, reason jsonutils.JSONObject) {
|
||||
container.SetStatus(ctx, t.GetUserCred(), api.CONTAINER_STATUS_SAVE_IMAGE_FAILED, reason.String())
|
||||
t.SetStageFailed(ctx, reason)
|
||||
}
|
||||
|
||||
func (t *ContainerSaveVolumeMountImageTask) OnSyncStatus(ctx context.Context, container *models.SContainer, data jsonutils.JSONObject) {
|
||||
t.SetStageComplete(ctx, nil)
|
||||
}
|
||||
func (t *ContainerSaveVolumeMountImageTask) OnSyncStatusFailed(ctx context.Context, container *models.SContainer, reason jsonutils.JSONObject) {
|
||||
t.SetStageFailed(ctx, reason)
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -46,9 +47,11 @@ import (
|
||||
"yunion.io/x/onecloud/pkg/mcclient"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
computemod "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/modules/image"
|
||||
"yunion.io/x/onecloud/pkg/util/fileutils2"
|
||||
"yunion.io/x/onecloud/pkg/util/netutils2/getport"
|
||||
"yunion.io/x/onecloud/pkg/util/pod"
|
||||
"yunion.io/x/onecloud/pkg/util/procutils"
|
||||
)
|
||||
|
||||
type PodInstance interface {
|
||||
@@ -60,6 +63,7 @@ type PodInstance interface {
|
||||
SyncContainerStatus(ctx context.Context, cred mcclient.TokenCredential, ctrId string) (jsonutils.JSONObject, error)
|
||||
StopContainer(ctx context.Context, userCred mcclient.TokenCredential, ctrId string, body jsonutils.JSONObject) (jsonutils.JSONObject, error)
|
||||
PullImage(ctx context.Context, userCred mcclient.TokenCredential, ctrId string, input *hostapi.ContainerPullImageInput) (jsonutils.JSONObject, error)
|
||||
SaveVolumeMountToImage(ctx context.Context, userCred mcclient.TokenCredential, input *hostapi.ContainerSaveVolumeMountToImageInput, ctrId string) (jsonutils.JSONObject, error)
|
||||
}
|
||||
|
||||
type sContainer struct {
|
||||
@@ -1133,3 +1137,70 @@ func (s *sPodGuestInstance) PullImage(ctx context.Context, userCred mcclient.Tok
|
||||
}
|
||||
return jsonutils.Marshal(resp), nil
|
||||
}
|
||||
|
||||
func (s *sPodGuestInstance) SaveVolumeMountToImage(ctx context.Context, userCred mcclient.TokenCredential, input *hostapi.ContainerSaveVolumeMountToImageInput, ctrId string) (jsonutils.JSONObject, error) {
|
||||
vol := input.VolumeMount
|
||||
drv := volume_mount.GetDriver(vol.Type)
|
||||
if err := drv.Mount(s, ctrId, vol); err != nil {
|
||||
return nil, errors.Wrapf(err, "mount volume %s, ctrId %s", jsonutils.Marshal(vol), ctrId)
|
||||
}
|
||||
defer func() {
|
||||
if err := drv.Unmount(s, ctrId, vol); err != nil {
|
||||
log.Warningf("unmount volume %s: %v", jsonutils.Marshal(vol), err)
|
||||
}
|
||||
}()
|
||||
|
||||
hostPath, err := drv.GetRuntimeMountHostPath(s, ctrId, vol)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "get runtime host mount path of %s", jsonutils.Marshal(vol))
|
||||
}
|
||||
// 1. tar hostPath to tgz
|
||||
imgPath, err := s.tarGzDir(input, ctrId, hostPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "tar and zip directory %s", hostPath)
|
||||
}
|
||||
defer func() {
|
||||
out, err := procutils.NewRemoteCommandAsFarAsPossible("rm", "-f", imgPath).Output()
|
||||
if err != nil {
|
||||
log.Warningf("rm -f %s: %s", imgPath, out)
|
||||
}
|
||||
}()
|
||||
|
||||
// 2. upload target tgz to glance
|
||||
if err := s.saveTarGzToGlance(ctx, input, imgPath); err != nil {
|
||||
return nil, errors.Wrapf(err, "saveTarGzToGlance: %s", imgPath)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *sPodGuestInstance) tarGzDir(input *hostapi.ContainerSaveVolumeMountToImageInput, ctrId string, hostPath string) (string, error) {
|
||||
fp := fmt.Sprintf("volimg-%s-ctr-%s-%d.tar.gz", input.ImageId, ctrId, input.VolumeMountIndex)
|
||||
outputFp := filepath.Join(s.GetVolumesDir(), fp)
|
||||
cmd := fmt.Sprintf("tar -czf %s -C %s .", outputFp, hostPath)
|
||||
if out, err := procutils.NewRemoteCommandAsFarAsPossible("sh", "-c", cmd).Output(); err != nil {
|
||||
return "", errors.Wrapf(err, "%s: %s", cmd, out)
|
||||
}
|
||||
return outputFp, nil
|
||||
}
|
||||
|
||||
func (s *sPodGuestInstance) saveTarGzToGlance(ctx context.Context, input *hostapi.ContainerSaveVolumeMountToImageInput, imgPath string) error {
|
||||
f, err := os.Open(imgPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
finfo, err := f.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
size := finfo.Size()
|
||||
|
||||
var params = jsonutils.NewDict()
|
||||
params.Set("image_id", jsonutils.NewString(input.ImageId))
|
||||
|
||||
if _, err := image.Images.Upload(hostutils.GetImageSession(ctx), params, f, size); err != nil {
|
||||
return errors.Wrap(err, "upload image")
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -64,12 +64,13 @@ func containerActionHandler(cf containerActionFunc) appsrv.FilterHandler {
|
||||
|
||||
func AddPodHandlers(prefix string, app *appsrv.Application) {
|
||||
ctrHandlers := map[string]containerActionFunc{
|
||||
"create": createContainer,
|
||||
"start": startContainer,
|
||||
"stop": stopContainer,
|
||||
"delete": deleteContainer,
|
||||
"sync-status": syncContainerStatus,
|
||||
"pull-image": pullImage,
|
||||
"create": createContainer,
|
||||
"start": startContainer,
|
||||
"stop": stopContainer,
|
||||
"delete": deleteContainer,
|
||||
"sync-status": syncContainerStatus,
|
||||
"pull-image": pullImage,
|
||||
"save-volume-mount-to-image": saveVolumeMountToImage,
|
||||
}
|
||||
for action, f := range ctrHandlers {
|
||||
app.AddHandler("POST",
|
||||
@@ -113,3 +114,11 @@ func deleteContainer(ctx context.Context, userCred mcclient.TokenCredential, pod
|
||||
func syncContainerStatus(ctx context.Context, userCred mcclient.TokenCredential, pod guestman.PodInstance, id string, body jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
return pod.SyncContainerStatus(ctx, userCred, id)
|
||||
}
|
||||
|
||||
func saveVolumeMountToImage(ctx context.Context, userCred mcclient.TokenCredential, pod guestman.PodInstance, ctrId string, body jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
input := new(hostapi.ContainerSaveVolumeMountToImageInput)
|
||||
if err := body.Unmarshal(input); err != nil {
|
||||
return nil, errors.Wrap(err, "unmarshal to input")
|
||||
}
|
||||
return pod.SaveVolumeMountToImage(ctx, userCred, input, ctrId)
|
||||
}
|
||||
|
||||
@@ -246,3 +246,20 @@ func (o *ContainerStopOptions) Params() (jsonutils.JSONObject, error) {
|
||||
type ContainerStartOptions struct {
|
||||
ContainerIdsOptions
|
||||
}
|
||||
|
||||
type ContainerSaveVolumeMountImage struct {
|
||||
options.ResourceIdOptions
|
||||
IMAGENAME string `help:"Image name"`
|
||||
INDEX int `help:"Index of volume mount"`
|
||||
GenerateName string `help:"Generate image name automatically"`
|
||||
Notes string `help:"Extra notes of the image"`
|
||||
}
|
||||
|
||||
func (o ContainerSaveVolumeMountImage) Params() (jsonutils.JSONObject, error) {
|
||||
return jsonutils.Marshal(&computeapi.ContainerSaveVolumeMountToImageInput{
|
||||
Name: o.IMAGENAME,
|
||||
GenerateName: o.GenerateName,
|
||||
Notes: o.Notes,
|
||||
Index: o.INDEX,
|
||||
}), nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user