mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-09-24 16:03:43 +08:00
Merge pull request #19926 from zexi/save-container-image
feat(glance,region,host,climc): support using tar.gz image as overlayer
This commit is contained in:
@@ -34,7 +34,7 @@ import (
|
||||
)
|
||||
|
||||
type ImageOptionalOptions struct {
|
||||
Format string `help:"Image format" choices:"raw|qcow2|iso|vmdk|docker|vhd"`
|
||||
Format string `help:"Image format" choices:"raw|qcow2|iso|vmdk|docker|vhd|tgz"`
|
||||
Protected bool `help:"Prevent image from being deleted"`
|
||||
Unprotected bool `help:"Allow image to be deleted"`
|
||||
Standard bool `help:"Mark image as a standard image"`
|
||||
|
||||
@@ -47,6 +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_STARTING = "starting"
|
||||
CONTAINER_STATUS_START_FAILED = "start_failed"
|
||||
CONTAINER_STATUS_STOPPING = "stopping"
|
||||
@@ -67,9 +69,9 @@ const (
|
||||
|
||||
type ContainerSpec struct {
|
||||
apis.ContainerSpec
|
||||
// Mounts for the container.
|
||||
// Mounts []*ContainerMount `json:"mounts"`
|
||||
Devices []*ContainerDevice `json:"devices"`
|
||||
// Volume mounts
|
||||
VolumeMounts []*apis.ContainerVolumeMount `json:"volume_mounts"`
|
||||
Devices []*ContainerDevice `json:"devices"`
|
||||
}
|
||||
|
||||
func (c *ContainerSpec) String() string {
|
||||
@@ -110,12 +112,6 @@ type ContainerSyncStatusResponse struct {
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type ContainerDesc struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Spec *ContainerSpec `json:"spec"`
|
||||
}
|
||||
|
||||
type ContainerHostDevice struct {
|
||||
// Path of the device within the container.
|
||||
ContainerPath string `json:"container_path"`
|
||||
|
||||
@@ -284,6 +284,7 @@ type DiskAllocateInput struct {
|
||||
Format string
|
||||
DiskSizeMb int
|
||||
ImageId string
|
||||
ImageFormat string
|
||||
FsFormat string
|
||||
Rebuild bool
|
||||
BackingDiskId string
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis"
|
||||
"yunion.io/x/onecloud/pkg/apis/billing"
|
||||
"yunion.io/x/onecloud/pkg/apis/host"
|
||||
imageapi "yunion.io/x/onecloud/pkg/apis/image"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
)
|
||||
@@ -906,8 +907,8 @@ type GuestJsonDesc struct {
|
||||
|
||||
LightMode bool `json:"light_mode"`
|
||||
|
||||
Hypervisor string `json:"hypervisor"`
|
||||
Containers []*ContainerDesc `json:"containers"`
|
||||
Hypervisor string `json:"hypervisor"`
|
||||
Containers []*host.ContainerDesc `json:"containers"`
|
||||
}
|
||||
|
||||
type ServerSetBootIndexInput struct {
|
||||
|
||||
+38
-11
@@ -14,7 +14,10 @@
|
||||
|
||||
package apis
|
||||
|
||||
import "yunion.io/x/pkg/util/sets"
|
||||
import (
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/sets"
|
||||
)
|
||||
|
||||
type ContainerKeyValue struct {
|
||||
Key string `json:"key"`
|
||||
@@ -54,14 +57,12 @@ type ContainerSpec struct {
|
||||
// List of environment variable to set in the container.
|
||||
Envs []*ContainerKeyValue `json:"envs"`
|
||||
// Enable lxcfs
|
||||
EnableLxcfs bool `json:"enable_lxcfs"`
|
||||
// Volume mounts
|
||||
VolumeMounts []*ContainerVolumeMount `json:"volume_mounts"`
|
||||
Capabilities *ContainerCapability `json:"capabilities"`
|
||||
Privileged bool `json:"privileged"`
|
||||
Lifecyle *ContainerLifecyle `json:"lifecyle"`
|
||||
CgroupDevicesAllow []string `json:"cgroup_devices_allow"`
|
||||
SimulateCpu bool `json:"simulate_cpu"`
|
||||
EnableLxcfs bool `json:"enable_lxcfs"`
|
||||
Capabilities *ContainerCapability `json:"capabilities"`
|
||||
Privileged bool `json:"privileged"`
|
||||
Lifecyle *ContainerLifecyle `json:"lifecyle"`
|
||||
CgroupDevicesAllow []string `json:"cgroup_devices_allow"`
|
||||
SimulateCpu bool `json:"simulate_cpu"`
|
||||
}
|
||||
|
||||
type ContainerCapability struct {
|
||||
@@ -122,10 +123,36 @@ type ContainerVolumeMount struct {
|
||||
Propagation ContainerMountPropagation `json:"propagation,omitempty"`
|
||||
}
|
||||
|
||||
type ContainerOverlayDiskImage struct {
|
||||
DiskId string `json:"disk_id"`
|
||||
ImageId string `json:"image_id"`
|
||||
}
|
||||
|
||||
type ContainerDiskOverlayType string
|
||||
|
||||
const (
|
||||
CONTAINER_DISK_OVERLAY_TYPE_DIRECTORY ContainerDiskOverlayType = "directory"
|
||||
CONTAINER_DISK_OVERLAY_TYPE_UNKNOWN ContainerDiskOverlayType = "unknown"
|
||||
)
|
||||
|
||||
type ContainerVolumeMountDiskOverlay struct {
|
||||
LowerDir []string `json:"lower_dir"`
|
||||
}
|
||||
|
||||
func (o ContainerVolumeMountDiskOverlay) GetType() ContainerDiskOverlayType {
|
||||
if len(o.LowerDir) != 0 {
|
||||
return CONTAINER_DISK_OVERLAY_TYPE_DIRECTORY
|
||||
}
|
||||
return CONTAINER_DISK_OVERLAY_TYPE_UNKNOWN
|
||||
}
|
||||
|
||||
func (o ContainerVolumeMountDiskOverlay) IsValid() error {
|
||||
if o.GetType() == CONTAINER_DISK_OVERLAY_TYPE_UNKNOWN {
|
||||
return errors.ErrNotSupported
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ContainerVolumeMountDisk struct {
|
||||
Index *int `json:"index,omitempty"`
|
||||
Id string `json:"id"`
|
||||
@@ -137,8 +164,8 @@ type ContainerVolumeMountDisk struct {
|
||||
type ContainerVolumeMountHostPathType string
|
||||
|
||||
const (
|
||||
ContainerVolumeMountHostPathTypeDirectory ContainerVolumeMountHostPathType = "directory"
|
||||
ContainerVolumeMountHostPathTypeFile ContainerVolumeMountHostPathType = "file"
|
||||
CONTAINER_VOLUME_MOUNT_HOST_PATH_TYPE_DIRECTORY ContainerVolumeMountHostPathType = "directory"
|
||||
CONTAINER_VOLUME_MOUNT_HOST_PATH_TYPE_FILE ContainerVolumeMountHostPathType = "file"
|
||||
)
|
||||
|
||||
type ContainerVolumeMountHostPath struct {
|
||||
|
||||
@@ -14,11 +14,38 @@
|
||||
|
||||
package host
|
||||
|
||||
import "yunion.io/x/onecloud/pkg/apis"
|
||||
import (
|
||||
"yunion.io/x/onecloud/pkg/apis"
|
||||
)
|
||||
|
||||
type ContainerVolumeMountDisk struct {
|
||||
Index *int `json:"index,omitempty"`
|
||||
Id string `json:"id"`
|
||||
TemplateId string `json:"template_id"`
|
||||
SubDirectory string `json:"sub_directory"`
|
||||
StorageSizeFile string `json:"storage_size_file"`
|
||||
Overlay *apis.ContainerVolumeMountDiskOverlay `json:"overlay"`
|
||||
}
|
||||
|
||||
type ContainerVolumeMount struct {
|
||||
Type apis.ContainerVolumeMountType `json:"type"`
|
||||
Disk *ContainerVolumeMountDisk `json:"disk"`
|
||||
HostPath *apis.ContainerVolumeMountHostPath `json:"host_path"`
|
||||
// Mounted read-only if true, read-write otherwise (false or unspecified).
|
||||
ReadOnly bool `json:"read_only"`
|
||||
// Path within the container at which the volume should be mounted. Must
|
||||
// not contain ':'.
|
||||
MountPath string `json:"mount_path"`
|
||||
// If set, the mount needs SELinux relabeling.
|
||||
SelinuxRelabel bool `json:"selinux_relabel,omitempty"`
|
||||
// Requested propagation mode.
|
||||
Propagation apis.ContainerMountPropagation `json:"propagation,omitempty"`
|
||||
}
|
||||
|
||||
type ContainerSpec struct {
|
||||
apis.ContainerSpec
|
||||
Devices []*ContainerDevice `json:"devices"`
|
||||
VolumeMounts []*ContainerVolumeMount `json:"volume_mounts"`
|
||||
Devices []*ContainerDevice `json:"devices"`
|
||||
}
|
||||
|
||||
type ContainerDevice struct {
|
||||
@@ -69,3 +96,9 @@ type ContainerPullImageInput struct {
|
||||
PullPolicy apis.ImagePullPolicy `json:"pull_policy"`
|
||||
Auth *ContainerPullImageAuthConfig `json:"auth"`
|
||||
}
|
||||
|
||||
type ContainerDesc struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Spec *ContainerSpec `json:"spec"`
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ const (
|
||||
|
||||
ImageTypeTemplate = TImageType("image")
|
||||
ImageTypeISO = TImageType("iso")
|
||||
ImageTypeTarGzip = TImageType("tgz")
|
||||
|
||||
LocalFilePrefix = "file://"
|
||||
S3Prefix = "s3://"
|
||||
@@ -72,3 +73,13 @@ const (
|
||||
var (
|
||||
ImageDeadStatus = []string{IMAGE_STATUS_DEACTIVATED, IMAGE_STATUS_KILLED, IMAGE_STATUS_DELETED, IMAGE_STATUS_PENDING_DELETE}
|
||||
)
|
||||
|
||||
const (
|
||||
IMAGE_DISK_FORMAT_RAW = "raw"
|
||||
IMAGE_DISK_FORMAT_QCOW2 = "qcow2"
|
||||
IMAGE_DISK_FORMAT_ISO = "iso"
|
||||
IMAGE_DISK_FORMAT_VMDK = "vmdk"
|
||||
IMAGE_DISK_FORMAT_DOCKER = "docker"
|
||||
IMAGE_DISK_FORMAT_VHD = "vhd"
|
||||
IMAGE_DISK_FORMAT_TGZ = "tgz"
|
||||
)
|
||||
|
||||
@@ -16,10 +16,20 @@ func init() {
|
||||
models.RegisterContainerVolumeMountDriver(newDisk())
|
||||
}
|
||||
|
||||
type disk struct{}
|
||||
type iDiskOverlay interface {
|
||||
validateCreateData(ctx context.Context, userCred mcclient.TokenCredential, input *apis.ContainerVolumeMountDiskOverlay) error
|
||||
}
|
||||
|
||||
type disk struct {
|
||||
overlayDrivers map[apis.ContainerDiskOverlayType]iDiskOverlay
|
||||
}
|
||||
|
||||
func newDisk() models.IContainerVolumeMountDriver {
|
||||
return &disk{}
|
||||
return &disk{
|
||||
overlayDrivers: map[apis.ContainerDiskOverlayType]iDiskOverlay{
|
||||
apis.CONTAINER_DISK_OVERLAY_TYPE_DIRECTORY: newDiskOverlayDir(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (d disk) GetType() apis.ContainerVolumeMountType {
|
||||
@@ -56,9 +66,15 @@ func (d disk) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCre
|
||||
if diskIndex >= len(disks) {
|
||||
return nil, httperrors.NewInputParameterError("disk.index %d is large than disk size %d", diskIndex, len(disks))
|
||||
}
|
||||
vm.Disk.Id = disks[diskIndex].GetId()
|
||||
diskObj := disks[diskIndex]
|
||||
vm.Disk.Id = diskObj.GetId()
|
||||
// remove index
|
||||
vm.Disk.Index = nil
|
||||
if diskObj.TemplateId != "" {
|
||||
if vm.Disk.SubDirectory == "" {
|
||||
return nil, httperrors.NewInputParameterError("sub_directory is required when disk has template_id %s", diskObj.TemplateId)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if disk.Id == "" {
|
||||
return nil, httperrors.NewNotEmptyError("disk.id is empty")
|
||||
@@ -67,6 +83,11 @@ func (d disk) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCre
|
||||
for _, d := range disks {
|
||||
if d.GetId() == disk.Id || d.GetName() == disk.Id {
|
||||
disk.Id = d.GetId()
|
||||
if d.TemplateId != "" {
|
||||
if vm.Disk.SubDirectory == "" {
|
||||
return nil, httperrors.NewInputParameterError("sub_directory is required when disk has template_id %s", d.TemplateId)
|
||||
}
|
||||
}
|
||||
foundDisk = true
|
||||
break
|
||||
}
|
||||
@@ -75,7 +96,7 @@ func (d disk) ValidateCreateData(ctx context.Context, userCred mcclient.TokenCre
|
||||
return nil, httperrors.NewNotFoundError("not found pod disk by %s", disk.Id)
|
||||
}
|
||||
}
|
||||
if err := d.validateOverlay(vm); err != nil {
|
||||
if err := d.validateOverlay(ctx, userCred, vm); err != nil {
|
||||
return nil, errors.Wrapf(err, "validate overlay")
|
||||
}
|
||||
return vm, nil
|
||||
@@ -100,18 +121,44 @@ func (d disk) ValidatePodCreateData(ctx context.Context, userCred mcclient.Token
|
||||
if diskIndex >= len(disks) {
|
||||
return httperrors.NewInputParameterError("disk.index %d is large than disk size %d", diskIndex, len(disks))
|
||||
}
|
||||
inputDisk := disks[diskIndex]
|
||||
if inputDisk.ImageId != "" {
|
||||
if disk.SubDirectory == "" {
|
||||
return httperrors.NewInputParameterError("sub_directory is required when disk has image_id %s", inputDisk.ImageId)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d disk) validateOverlay(vm *apis.ContainerVolumeMount) error {
|
||||
func (d disk) getOverlayDriver(ov *apis.ContainerVolumeMountDiskOverlay) iDiskOverlay {
|
||||
return d.overlayDrivers[ov.GetType()]
|
||||
}
|
||||
|
||||
func (d disk) validateOverlay(ctx context.Context, userCred mcclient.TokenCredential, vm *apis.ContainerVolumeMount) error {
|
||||
if vm.Disk.Overlay == nil {
|
||||
return nil
|
||||
}
|
||||
ov := vm.Disk.Overlay
|
||||
if len(ov.LowerDir) == 0 {
|
||||
if err := ov.IsValid(); err != nil {
|
||||
return httperrors.NewInputParameterError("invalid overlay input: %v", err)
|
||||
}
|
||||
if err := d.getOverlayDriver(ov).validateCreateData(ctx, userCred, ov); err != nil {
|
||||
return errors.Wrapf(err, "validate overlay %s", ov.GetType())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type diskOverlayDir struct{}
|
||||
|
||||
func newDiskOverlayDir() iDiskOverlay {
|
||||
return &diskOverlayDir{}
|
||||
}
|
||||
|
||||
func (d diskOverlayDir) validateCreateData(ctx context.Context, userCred mcclient.TokenCredential, input *apis.ContainerVolumeMountDiskOverlay) error {
|
||||
if len(input.LowerDir) == 0 {
|
||||
return httperrors.NewNotEmptyError("lower_dir is required")
|
||||
}
|
||||
for idx, ld := range ov.LowerDir {
|
||||
for idx, ld := range input.LowerDir {
|
||||
if ld == "" {
|
||||
return httperrors.NewNotEmptyError("empty %d dir", idx)
|
||||
}
|
||||
|
||||
@@ -36,11 +36,11 @@ func (h hostLocal) ValidatePodCreateData(ctx context.Context, userCred mcclient.
|
||||
return httperrors.NewNotEmptyError("host_path is nil")
|
||||
}
|
||||
if hp.Type == "" {
|
||||
hp.Type = apis.ContainerVolumeMountHostPathTypeFile
|
||||
hp.Type = apis.CONTAINER_VOLUME_MOUNT_HOST_PATH_TYPE_FILE
|
||||
}
|
||||
if !sets.NewString(
|
||||
string(apis.ContainerVolumeMountHostPathTypeFile),
|
||||
string(apis.ContainerVolumeMountHostPathTypeDirectory)).Has(string(hp.Type)) {
|
||||
string(apis.CONTAINER_VOLUME_MOUNT_HOST_PATH_TYPE_FILE),
|
||||
string(apis.CONTAINER_VOLUME_MOUNT_HOST_PATH_TYPE_DIRECTORY)).Has(string(hp.Type)) {
|
||||
return httperrors.NewInputParameterError("unsupported type %s", hp.Type)
|
||||
}
|
||||
if hp.Path == "" {
|
||||
|
||||
@@ -338,9 +338,13 @@ func (p *SPodDriver) GetJsonDescAtHost(ctx context.Context, userCred mcclient.To
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetContainersByPod")
|
||||
}
|
||||
ctrDescs := make([]*api.ContainerDesc, len(ctrs))
|
||||
ctrDescs := make([]*hostapi.ContainerDesc, len(ctrs))
|
||||
for idx, ctr := range ctrs {
|
||||
ctrDescs[idx] = ctr.GetJsonDescAtHost()
|
||||
desc, err := ctr.GetJsonDescAtHost()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "GetJsonDescAtHost of container %s", ctr.GetId())
|
||||
}
|
||||
ctrDescs[idx] = desc
|
||||
}
|
||||
desc.Containers = ctrDescs
|
||||
return jsonutils.Marshal(desc), nil
|
||||
@@ -398,7 +402,7 @@ func (p *SPodDriver) performContainerAction(ctx context.Context, userCred mcclie
|
||||
}
|
||||
|
||||
func (p *SPodDriver) getContainerCreateInput(ctx context.Context, userCred mcclient.TokenCredential, ctr *models.SContainer) (*hostapi.ContainerCreateInput, error) {
|
||||
spec, err := ctr.ToHostContainerSpec(ctx, userCred)
|
||||
spec, err := ctr.ToHostContainerSpec()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "ToHostContainerSpec")
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ func (m *SContainerManager) ValidateSpecDevice(ctx context.Context, userCred mcc
|
||||
}
|
||||
|
||||
func (m *SContainerManager) ValidateSpecVolumeMounts(ctx context.Context, userCred mcclient.TokenCredential, pod *SGuest, spec *api.ContainerSpec) error {
|
||||
relation, err := m.GetVolumeMountRelations(ctx, userCred, pod, spec)
|
||||
relation, err := m.GetVolumeMountRelations(pod, spec)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "GetVolumeMountRelations")
|
||||
}
|
||||
@@ -288,11 +288,39 @@ type ContainerVolumeMountRelation struct {
|
||||
pod *SGuest
|
||||
}
|
||||
|
||||
func (vm *ContainerVolumeMountRelation) ToHostMount() (*apis.ContainerVolumeMount, error) {
|
||||
return vm.VolumeMount, nil
|
||||
func (vm *ContainerVolumeMountRelation) toHostDiskMount(disk *apis.ContainerVolumeMountDisk) (*hostapi.ContainerVolumeMountDisk, error) {
|
||||
diskObj := DiskManager.FetchDiskById(disk.Id)
|
||||
if diskObj == nil {
|
||||
return nil, errors.Errorf("fetch disk by id %s", disk.Id)
|
||||
}
|
||||
ret := &hostapi.ContainerVolumeMountDisk{
|
||||
Index: disk.Index,
|
||||
Id: disk.Id,
|
||||
TemplateId: diskObj.TemplateId,
|
||||
SubDirectory: disk.SubDirectory,
|
||||
StorageSizeFile: disk.StorageSizeFile,
|
||||
Overlay: disk.Overlay,
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (m *SContainerManager) GetVolumeMountRelations(ctx context.Context, userCred mcclient.TokenCredential, pod *SGuest, spec *api.ContainerSpec) ([]*ContainerVolumeMountRelation, error) {
|
||||
func (vm *ContainerVolumeMountRelation) ToHostMount() (*hostapi.ContainerVolumeMount, error) {
|
||||
disk, err := vm.toHostDiskMount(vm.VolumeMount.Disk)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "toHostDiskMount")
|
||||
}
|
||||
return &hostapi.ContainerVolumeMount{
|
||||
Type: vm.VolumeMount.Type,
|
||||
Disk: disk,
|
||||
HostPath: vm.VolumeMount.HostPath,
|
||||
ReadOnly: vm.VolumeMount.ReadOnly,
|
||||
MountPath: vm.VolumeMount.MountPath,
|
||||
SelinuxRelabel: vm.VolumeMount.SelinuxRelabel,
|
||||
Propagation: vm.VolumeMount.Propagation,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *SContainerManager) GetVolumeMountRelations(pod *SGuest, spec *api.ContainerSpec) ([]*ContainerVolumeMountRelation, error) {
|
||||
relation := make([]*ContainerVolumeMountRelation, len(spec.VolumeMounts))
|
||||
for idx, vm := range spec.VolumeMounts {
|
||||
tmpVm := vm
|
||||
@@ -304,8 +332,8 @@ func (m *SContainerManager) GetVolumeMountRelations(ctx context.Context, userCre
|
||||
return relation, nil
|
||||
}
|
||||
|
||||
func (c *SContainer) GetVolumeMountRelations(ctx context.Context, userCred mcclient.TokenCredential) ([]*ContainerVolumeMountRelation, error) {
|
||||
return GetContainerManager().GetVolumeMountRelations(ctx, userCred, c.GetPod(), c.Spec)
|
||||
func (c *SContainer) GetVolumeMountRelations() ([]*ContainerVolumeMountRelation, error) {
|
||||
return GetContainerManager().GetVolumeMountRelations(c.GetPod(), c.Spec)
|
||||
}
|
||||
|
||||
func (c *SContainer) PerformStart(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) {
|
||||
@@ -379,8 +407,8 @@ func (c *SContainer) RealDelete(ctx context.Context, userCred mcclient.TokenCred
|
||||
return c.SVirtualResourceBase.Delete(ctx, userCred)
|
||||
}
|
||||
|
||||
func (m *SContainerManager) ConvertVolumeMountRelationToSpec(relation []*ContainerVolumeMountRelation) ([]*apis.ContainerVolumeMount, error) {
|
||||
mounts := make([]*apis.ContainerVolumeMount, 0)
|
||||
func (m *SContainerManager) ConvertVolumeMountRelationToSpec(relation []*ContainerVolumeMountRelation) ([]*hostapi.ContainerVolumeMount, error) {
|
||||
mounts := make([]*hostapi.ContainerVolumeMount, 0)
|
||||
for _, r := range relation {
|
||||
mount, err := r.ToHostMount()
|
||||
if err != nil {
|
||||
@@ -393,8 +421,8 @@ func (m *SContainerManager) ConvertVolumeMountRelationToSpec(relation []*Contain
|
||||
return mounts, nil
|
||||
}
|
||||
|
||||
func (c *SContainer) ToHostContainerSpec(ctx context.Context, userCred mcclient.TokenCredential) (*hostapi.ContainerSpec, error) {
|
||||
vmRelation, err := c.GetVolumeMountRelations(ctx, userCred)
|
||||
func (c *SContainer) ToHostContainerSpec() (*hostapi.ContainerSpec, error) {
|
||||
vmRelation, err := c.GetVolumeMountRelations()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetVolumeMountRelations")
|
||||
}
|
||||
@@ -402,12 +430,6 @@ func (c *SContainer) ToHostContainerSpec(ctx context.Context, userCred mcclient.
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "ConvertVolumeRelationToSpec")
|
||||
}
|
||||
|
||||
spec := c.Spec.ContainerSpec
|
||||
spec.VolumeMounts = mounts
|
||||
hSpec := &hostapi.ContainerSpec{
|
||||
ContainerSpec: spec,
|
||||
}
|
||||
ctrDevs := make([]*hostapi.ContainerDevice, 0)
|
||||
for _, dev := range c.Spec.Devices {
|
||||
ctrDev, err := GetContainerDeviceDriver(dev.Type).ToHostDevice(dev)
|
||||
@@ -416,14 +438,24 @@ func (c *SContainer) ToHostContainerSpec(ctx context.Context, userCred mcclient.
|
||||
}
|
||||
ctrDevs = append(ctrDevs, ctrDev)
|
||||
}
|
||||
hSpec.Devices = ctrDevs
|
||||
|
||||
spec := c.Spec.ContainerSpec
|
||||
hSpec := &hostapi.ContainerSpec{
|
||||
ContainerSpec: spec,
|
||||
VolumeMounts: mounts,
|
||||
Devices: ctrDevs,
|
||||
}
|
||||
return hSpec, nil
|
||||
}
|
||||
|
||||
func (c *SContainer) GetJsonDescAtHost() *api.ContainerDesc {
|
||||
return &api.ContainerDesc{
|
||||
func (c *SContainer) GetJsonDescAtHost() (*hostapi.ContainerDesc, error) {
|
||||
spec, err := c.ToHostContainerSpec()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "ToHostContainerSpec")
|
||||
}
|
||||
return &hostapi.ContainerDesc{
|
||||
Id: c.GetId(),
|
||||
Name: c.GetName(),
|
||||
Spec: c.Spec,
|
||||
}
|
||||
Spec: spec,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -828,6 +828,12 @@ func (self *SDisk) StartAllocate(ctx context.Context, host *SHost, storage *SSto
|
||||
}
|
||||
} else if len(templateId) > 0 {
|
||||
input.ImageId = templateId
|
||||
s := auth.GetAdminSession(ctx, options.Options.Region)
|
||||
img, err := image.Images.Get(s, templateId, nil)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "get image details from glance")
|
||||
}
|
||||
input.ImageFormat, _ = img.GetString("disk_format")
|
||||
}
|
||||
if len(fsFormat) > 0 {
|
||||
input.FsFormat = fsFormat
|
||||
@@ -1204,7 +1210,7 @@ func (self *SDisk) validateDeleteCondition(ctx context.Context, isPurge bool) er
|
||||
if !isPurge {
|
||||
storage, _ := self.GetStorage()
|
||||
if storage == nil {
|
||||
// storage is empty, a dirty data, allow delete
|
||||
// storage is empty, a dirty data, allow to delete
|
||||
return nil
|
||||
}
|
||||
host, _ := storage.GetMasterHost()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package volume_mount
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -8,6 +9,8 @@ import (
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis"
|
||||
computeapi "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
hostapi "yunion.io/x/onecloud/pkg/apis/host"
|
||||
"yunion.io/x/onecloud/pkg/hostman/container/storage"
|
||||
container_storage "yunion.io/x/onecloud/pkg/hostman/container/storage"
|
||||
"yunion.io/x/onecloud/pkg/hostman/guestman/desc"
|
||||
@@ -20,17 +23,28 @@ func init() {
|
||||
RegisterDriver(newDisk())
|
||||
}
|
||||
|
||||
type disk struct{}
|
||||
type iDiskOverlay interface {
|
||||
mount(d disk, pod IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount) error
|
||||
unmount(d disk, pod IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount) error
|
||||
}
|
||||
|
||||
type disk struct {
|
||||
overlayDrivers map[apis.ContainerDiskOverlayType]iDiskOverlay
|
||||
}
|
||||
|
||||
func newDisk() IVolumeMount {
|
||||
return &disk{}
|
||||
return &disk{
|
||||
overlayDrivers: map[apis.ContainerDiskOverlayType]iDiskOverlay{
|
||||
apis.CONTAINER_DISK_OVERLAY_TYPE_DIRECTORY: newDiskOverlayDir(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (d disk) GetType() apis.ContainerVolumeMountType {
|
||||
return apis.CONTAINER_VOLUME_MOUNT_TYPE_DISK
|
||||
}
|
||||
|
||||
func (d disk) getRuntimeMountHostPath(pod IPodInfo, vm *apis.ContainerVolumeMount) (string, error) {
|
||||
func (d disk) getRuntimeMountHostPath(pod IPodInfo, vm *hostapi.ContainerVolumeMount) (string, error) {
|
||||
diskInput := vm.Disk
|
||||
if diskInput == nil {
|
||||
return "", httperrors.NewNotEmptyError("disk is nil")
|
||||
@@ -45,19 +59,19 @@ func (d disk) getRuntimeMountHostPath(pod IPodInfo, vm *apis.ContainerVolumeMoun
|
||||
return hostPath, nil
|
||||
}
|
||||
|
||||
func (d disk) GetRuntimeMountHostPath(pod IPodInfo, ctrId string, vm *apis.ContainerVolumeMount) (string, error) {
|
||||
func (d disk) GetRuntimeMountHostPath(pod IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount) (string, error) {
|
||||
hostPath, err := d.getRuntimeMountHostPath(pod, vm)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "get runtime mount host_path")
|
||||
}
|
||||
overlay := vm.Disk.Overlay
|
||||
if overlay == nil {
|
||||
if overlay == nil && vm.Disk.TemplateId == "" {
|
||||
return hostPath, nil
|
||||
}
|
||||
return d.getOverlayMergedDir(pod, ctrId, vm, hostPath), nil
|
||||
}
|
||||
|
||||
func (d disk) getPodDisk(pod IPodInfo, vm *apis.ContainerVolumeMount) (storageman.IDisk, *desc.SGuestDisk, error) {
|
||||
func (d disk) getPodDisk(pod IPodInfo, vm *hostapi.ContainerVolumeMount) (storageman.IDisk, *desc.SGuestDisk, error) {
|
||||
var disk *desc.SGuestDisk = nil
|
||||
disks := pod.GetDisks()
|
||||
volDisk := vm.Disk
|
||||
@@ -82,7 +96,7 @@ func (d disk) getPodDisk(pod IPodInfo, vm *apis.ContainerVolumeMount) (storagema
|
||||
return iDisk, disk, nil
|
||||
}
|
||||
|
||||
func (d disk) getDiskStorageDriver(pod IPodInfo, vm *apis.ContainerVolumeMount) (storage.IContainerStorage, error) {
|
||||
func (d disk) getDiskStorageDriver(pod IPodInfo, vm *hostapi.ContainerVolumeMount) (storage.IContainerStorage, error) {
|
||||
iDisk, _, err := d.getPodDisk(pod, vm)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get pod disk interface")
|
||||
@@ -94,7 +108,7 @@ func (d disk) getDiskStorageDriver(pod IPodInfo, vm *apis.ContainerVolumeMount)
|
||||
return drv, nil
|
||||
}
|
||||
|
||||
func (d disk) getOverlayDir(pod IPodInfo, ctrId string, vm *apis.ContainerVolumeMount, upperDir string, suffix string) string {
|
||||
func (d disk) getOverlayDir(pod IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount, upperDir string, suffix string) string {
|
||||
return filepath.Join(pod.GetVolumesOverlayDir(), vm.Disk.Id, ctrId, fmt.Sprintf("%s-%s", filepath.Base(upperDir), suffix))
|
||||
}
|
||||
|
||||
@@ -102,11 +116,11 @@ func (d disk) getOverlayWorkDir(upperDir string) string {
|
||||
return fmt.Sprintf("%s-work", upperDir)
|
||||
}
|
||||
|
||||
func (d disk) getOverlayMergedDir(pod IPodInfo, ctrId string, vm *apis.ContainerVolumeMount, upperDir string) string {
|
||||
func (d disk) getOverlayMergedDir(pod IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount, upperDir string) string {
|
||||
return d.getOverlayDir(pod, ctrId, vm, upperDir, "merged")
|
||||
}
|
||||
|
||||
func (d disk) Mount(pod IPodInfo, ctrId string, vm *apis.ContainerVolumeMount) error {
|
||||
func (d disk) Mount(pod IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount) error {
|
||||
iDisk, gd, err := d.getPodDisk(pod, vm)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "get pod disk interface")
|
||||
@@ -146,10 +160,15 @@ func (d disk) Mount(pod IPodInfo, ctrId string, vm *apis.ContainerVolumeMount) e
|
||||
return errors.Wrapf(err, "mount container %s overlay dir: %#v", ctrId, vmDisk.Overlay)
|
||||
}
|
||||
}
|
||||
if vmDisk.TemplateId != "" {
|
||||
if err := d.mountTemplateOverlay(context.Background(), pod, ctrId, vm); err != nil {
|
||||
return errors.Wrapf(err, "mount container %s template overlay: %#v", ctrId, vmDisk.TemplateId)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d disk) createStorageSizeFile(iDisk storageman.IDisk, mntPoint string, input *apis.ContainerVolumeMountDisk) error {
|
||||
func (d disk) createStorageSizeFile(iDisk storageman.IDisk, mntPoint string, input *hostapi.ContainerVolumeMountDisk) error {
|
||||
desc := iDisk.GetDiskDesc()
|
||||
diskSizeMB, err := desc.Int("disk_size")
|
||||
if err != nil {
|
||||
@@ -164,7 +183,7 @@ func (d disk) createStorageSizeFile(iDisk storageman.IDisk, mntPoint string, inp
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d disk) Unmount(pod IPodInfo, ctrId string, vm *apis.ContainerVolumeMount) error {
|
||||
func (d disk) Unmount(pod IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount) error {
|
||||
iDisk, _, err := d.getPodDisk(pod, vm)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "get pod disk interface")
|
||||
@@ -178,6 +197,11 @@ func (d disk) Unmount(pod IPodInfo, ctrId string, vm *apis.ContainerVolumeMount)
|
||||
return errors.Wrapf(err, "umount overlay")
|
||||
}
|
||||
}
|
||||
if vm.Disk.TemplateId != "" {
|
||||
if err := d.unmountTemplateOverlay(context.Background(), pod, ctrId, vm); err != nil {
|
||||
return errors.Wrapf(err, "unmount container %s template overlay: %#v", ctrId, vm.Disk.TemplateId)
|
||||
}
|
||||
}
|
||||
mntPoint := pod.GetDiskMountPoint(iDisk)
|
||||
if err := container_storage.Unmount(mntPoint); err != nil {
|
||||
return errors.Wrapf(err, "unmount %s", mntPoint)
|
||||
@@ -194,16 +218,71 @@ func (d disk) Unmount(pod IPodInfo, ctrId string, vm *apis.ContainerVolumeMount)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d disk) unmoutOverlay(pod IPodInfo, ctrId string, vm *apis.ContainerVolumeMount) error {
|
||||
upperDir, err := d.getRuntimeMountHostPath(pod, vm)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "getRuntimeMountHostPath")
|
||||
}
|
||||
overlayDir := d.getOverlayMergedDir(pod, ctrId, vm, upperDir)
|
||||
return container_storage.Unmount(overlayDir)
|
||||
func (d disk) getOverlayDriver(ov *apis.ContainerVolumeMountDiskOverlay) iDiskOverlay {
|
||||
return d.overlayDrivers[ov.GetType()]
|
||||
}
|
||||
|
||||
func (d disk) mountOverlay(pod IPodInfo, ctrId string, vm *apis.ContainerVolumeMount) error {
|
||||
func (d disk) unmoutOverlay(pod IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount) error {
|
||||
return d.getOverlayDriver(vm.Disk.Overlay).unmount(d, pod, ctrId, vm)
|
||||
}
|
||||
|
||||
func (d disk) mountOverlay(pod IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount) error {
|
||||
return d.getOverlayDriver(vm.Disk.Overlay).mount(d, pod, ctrId, vm)
|
||||
}
|
||||
|
||||
func (d disk) doTemplateOverlayAction(
|
||||
ctx context.Context,
|
||||
pod IPodInfo, ctrId string,
|
||||
vm *hostapi.ContainerVolumeMount,
|
||||
ovAction func(d disk, pod IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount) error) error {
|
||||
templateId := vm.Disk.TemplateId
|
||||
input := computeapi.CacheImageInput{
|
||||
ImageId: templateId,
|
||||
}
|
||||
cachedImgMan := storageman.GetManager().LocalStorageImagecacheManager
|
||||
cachedImg, err := cachedImgMan.AcquireImage(ctx, input, nil)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "Get cache image %s", templateId)
|
||||
}
|
||||
defer cachedImgMan.ReleaseImage(ctx, templateId)
|
||||
cachedImageDir, err := cachedImg.GetAccessDirectory()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "GetAccessDirectory of cached image %s", cachedImg.GetPath())
|
||||
}
|
||||
vm.Disk.Overlay = &apis.ContainerVolumeMountDiskOverlay{
|
||||
LowerDir: []string{cachedImageDir},
|
||||
}
|
||||
if err := ovAction(d, pod, ctrId, vm); err != nil {
|
||||
return errors.Wrapf(err, "overlay dir %s", cachedImageDir)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d disk) mountTemplateOverlay(
|
||||
ctx context.Context,
|
||||
pod IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount) error {
|
||||
if err := d.doTemplateOverlayAction(ctx, pod, ctrId, vm, newDiskOverlayDir().mount); err != nil {
|
||||
return errors.Wrapf(err, "mount template overlay")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d disk) unmountTemplateOverlay(
|
||||
ctx context.Context,
|
||||
pod IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount) error {
|
||||
if err := d.doTemplateOverlayAction(ctx, pod, ctrId, vm, newDiskOverlayDir().unmount); err != nil {
|
||||
return errors.Wrapf(err, "unmount template overlay")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type diskOverlayDir struct{}
|
||||
|
||||
func newDiskOverlayDir() iDiskOverlay {
|
||||
return &diskOverlayDir{}
|
||||
}
|
||||
|
||||
func (dod diskOverlayDir) mount(d disk, pod IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount) error {
|
||||
vmDisk := vm.Disk
|
||||
lowerDir := vmDisk.Overlay.LowerDir
|
||||
upperDir, err := d.getRuntimeMountHostPath(pod, vm)
|
||||
@@ -226,3 +305,12 @@ func (d disk) mountOverlay(pod IPodInfo, ctrId string, vm *apis.ContainerVolumeM
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dod diskOverlayDir) unmount(d disk, pod IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount) error {
|
||||
upperDir, err := d.getRuntimeMountHostPath(pod, vm)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "getRuntimeMountHostPath")
|
||||
}
|
||||
overlayDir := d.getOverlayMergedDir(pod, ctrId, vm, upperDir)
|
||||
return container_storage.Unmount(overlayDir)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"yunion.io/x/pkg/errors"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis"
|
||||
hostapi "yunion.io/x/onecloud/pkg/apis/host"
|
||||
"yunion.io/x/onecloud/pkg/httperrors"
|
||||
"yunion.io/x/onecloud/pkg/util/procutils"
|
||||
)
|
||||
@@ -14,11 +15,11 @@ func init() {
|
||||
|
||||
type hostLocal struct{}
|
||||
|
||||
func (h hostLocal) Mount(pod IPodInfo, ctrId string, vm *apis.ContainerVolumeMount) error {
|
||||
func (h hostLocal) Mount(pod IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h hostLocal) Unmount(pod IPodInfo, ctrId string, vm *apis.ContainerVolumeMount) error {
|
||||
func (h hostLocal) Unmount(pod IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -30,22 +31,22 @@ func (h hostLocal) GetType() apis.ContainerVolumeMountType {
|
||||
return apis.CONTAINER_VOLUME_MOUNT_TYPE_HOST_PATH
|
||||
}
|
||||
|
||||
func (h hostLocal) GetRuntimeMountHostPath(pod IPodInfo, ctrId string, vm *apis.ContainerVolumeMount) (string, error) {
|
||||
func (h hostLocal) GetRuntimeMountHostPath(pod IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount) (string, error) {
|
||||
host := vm.HostPath
|
||||
if host == nil {
|
||||
return "", httperrors.NewNotEmptyError("host_local is nil")
|
||||
}
|
||||
switch host.Type {
|
||||
case "", apis.ContainerVolumeMountHostPathTypeFile:
|
||||
case "", apis.CONTAINER_VOLUME_MOUNT_HOST_PATH_TYPE_FILE:
|
||||
return host.Path, nil
|
||||
case apis.ContainerVolumeMountHostPathTypeDirectory:
|
||||
case apis.CONTAINER_VOLUME_MOUNT_HOST_PATH_TYPE_DIRECTORY:
|
||||
return h.getDirectoryPath(host)
|
||||
}
|
||||
return "", httperrors.NewInputParameterError("unsupported type %q", host.Type)
|
||||
}
|
||||
|
||||
func (h hostLocal) getDirectoryPath(input *apis.ContainerVolumeMountHostPath) (string, error) {
|
||||
if input.Type != apis.ContainerVolumeMountHostPathTypeDirectory {
|
||||
if input.Type != apis.CONTAINER_VOLUME_MOUNT_HOST_PATH_TYPE_DIRECTORY {
|
||||
return "", httperrors.NewInputParameterError("unsupported type %q", input.Type)
|
||||
}
|
||||
dirPath := input.Path
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis"
|
||||
hostapi "yunion.io/x/onecloud/pkg/apis/host"
|
||||
"yunion.io/x/onecloud/pkg/hostman/guestman/desc"
|
||||
"yunion.io/x/onecloud/pkg/hostman/storageman"
|
||||
)
|
||||
@@ -35,9 +36,9 @@ type IPodInfo interface {
|
||||
|
||||
type IVolumeMount interface {
|
||||
GetType() apis.ContainerVolumeMountType
|
||||
GetRuntimeMountHostPath(pod IPodInfo, ctrId string, vm *apis.ContainerVolumeMount) (string, error)
|
||||
Mount(pod IPodInfo, ctrId string, vm *apis.ContainerVolumeMount) error
|
||||
Unmount(pod IPodInfo, ctrId string, vm *apis.ContainerVolumeMount) error
|
||||
GetRuntimeMountHostPath(pod IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount) (string, error)
|
||||
Mount(pod IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount) error
|
||||
Unmount(pod IPodInfo, ctrId string, vm *hostapi.ContainerVolumeMount) error
|
||||
}
|
||||
|
||||
func GetRuntimeVolumeMountPropagation(input apis.ContainerMountPropagation) runtimeapi.MountPropagation {
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"yunion.io/x/jsonutils"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
"yunion.io/x/onecloud/pkg/apis/host"
|
||||
)
|
||||
|
||||
type SGuestCpu struct {
|
||||
@@ -381,7 +382,7 @@ type SGuestMetaDesc struct {
|
||||
}
|
||||
|
||||
type SGuestContainerDesc struct {
|
||||
Containers []*api.ContainerDesc
|
||||
Containers []*host.ContainerDesc
|
||||
}
|
||||
|
||||
type SGuestDesc struct {
|
||||
|
||||
@@ -223,12 +223,12 @@ func (s *sPodGuestInstance) umountPodVolumes() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *sPodGuestInstance) getContainerVolumeMounts() map[string][]*apis.ContainerVolumeMount {
|
||||
result := make(map[string][]*apis.ContainerVolumeMount, 0)
|
||||
func (s *sPodGuestInstance) getContainerVolumeMounts() map[string][]*hostapi.ContainerVolumeMount {
|
||||
result := make(map[string][]*hostapi.ContainerVolumeMount, 0)
|
||||
for _, ctr := range s.GetDesc().Containers {
|
||||
mnts, ok := result[ctr.Id]
|
||||
if !ok {
|
||||
mnts = make([]*apis.ContainerVolumeMount, 0)
|
||||
mnts = make([]*hostapi.ContainerVolumeMount, 0)
|
||||
}
|
||||
for _, vol := range ctr.Spec.VolumeMounts {
|
||||
tmp := vol
|
||||
|
||||
@@ -516,18 +516,16 @@ func (d *SLocalDisk) DeleteSnapshot(snapshotId, convertSnapshot string) error {
|
||||
|
||||
func (d *SLocalDisk) PrepareSaveToGlance(ctx context.Context, params interface{}) (jsonutils.JSONObject, error) {
|
||||
if err := d.Probe(); err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "do probe")
|
||||
}
|
||||
destDir := d.Storage.GetImgsaveBackupPath()
|
||||
if err := procutils.NewCommand("mkdir", "-p", destDir).Run(); err != nil {
|
||||
log.Errorln(err)
|
||||
return nil, err
|
||||
return nil, errors.Wrapf(err, "mkdir -p %s", destDir)
|
||||
}
|
||||
backupPath := path.Join(destDir, fmt.Sprintf("%s.%s", d.Id, appctx.AppContextTaskId(ctx)))
|
||||
if err := procutils.NewCommand("cp", "--sparse=always", "-f", d.GetPath(), backupPath).Run(); err != nil {
|
||||
log.Errorln(err)
|
||||
procutils.NewCommand("rm", "-f", backupPath).Run()
|
||||
return nil, err
|
||||
return nil, errors.Wrapf(err, "cp %s to %s", d.getPath(), backupPath)
|
||||
}
|
||||
res := jsonutils.NewDict()
|
||||
res.Set("backup", jsonutils.NewString(backupPath))
|
||||
|
||||
@@ -31,4 +31,6 @@ type IImageCache interface {
|
||||
GetImageId() string
|
||||
|
||||
GetDesc() *remotefile.SImageDesc
|
||||
// GetAccessDirectory is used by container's volume mount
|
||||
GetAccessDirectory() (string, error)
|
||||
}
|
||||
|
||||
@@ -31,11 +31,13 @@ import (
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis"
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
imageapi "yunion.io/x/onecloud/pkg/apis/image"
|
||||
"yunion.io/x/onecloud/pkg/hostman/hostutils"
|
||||
"yunion.io/x/onecloud/pkg/hostman/storageman/remotefile"
|
||||
"yunion.io/x/onecloud/pkg/mcclient/auth"
|
||||
modules "yunion.io/x/onecloud/pkg/mcclient/modules/compute"
|
||||
"yunion.io/x/onecloud/pkg/util/fileutils2"
|
||||
"yunion.io/x/onecloud/pkg/util/procutils"
|
||||
"yunion.io/x/onecloud/pkg/util/qemuimg"
|
||||
)
|
||||
|
||||
@@ -56,7 +58,8 @@ type SLocalImageCache struct {
|
||||
cond *sync.Cond
|
||||
lastCheckTime time.Time
|
||||
|
||||
remoteFile *remotefile.SRemoteFile
|
||||
remoteFile *remotefile.SRemoteFile
|
||||
accessDirLock sync.Mutex
|
||||
}
|
||||
|
||||
func NewLocalImageCache(imageId string, imagecacheManager IImageCacheManger) *SLocalImageCache {
|
||||
@@ -64,6 +67,7 @@ func NewLocalImageCache(imageId string, imagecacheManager IImageCacheManger) *SL
|
||||
imageCache.imageId = imageId
|
||||
imageCache.Manager = imagecacheManager
|
||||
imageCache.cond = sync.NewCond(new(sync.Mutex))
|
||||
imageCache.accessDirLock = sync.Mutex{}
|
||||
return imageCache
|
||||
}
|
||||
|
||||
@@ -286,3 +290,31 @@ func (l *SLocalImageCache) GetSize() int64 {
|
||||
return fi.Size()
|
||||
}
|
||||
}
|
||||
|
||||
func (l *SLocalImageCache) getAccessDirPath() string {
|
||||
return fmt.Sprintf("%s-dir", l.GetPath())
|
||||
}
|
||||
|
||||
func (l *SLocalImageCache) GetAccessDirectory() (string, error) {
|
||||
if l.Desc.Format != imageapi.IMAGE_DISK_FORMAT_TGZ {
|
||||
return "", errors.Wrapf(errors.ErrNotSupported, "format %s", l.Desc.Format)
|
||||
}
|
||||
|
||||
l.accessDirLock.Lock()
|
||||
defer l.accessDirLock.Unlock()
|
||||
dir := l.getAccessDirPath()
|
||||
if fileutils2.Exists(dir) {
|
||||
return dir, nil
|
||||
}
|
||||
// untar cached image
|
||||
out, err := procutils.NewRemoteCommandAsFarAsPossible("mkdir", "-p", dir).Output()
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "mkdir %s: %s", dir, out)
|
||||
}
|
||||
out, err = procutils.NewRemoteCommandAsFarAsPossible("tar", "xf", l.GetPath(), "-C", dir).Output()
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "untar to %s: %s", dir, out)
|
||||
}
|
||||
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
@@ -157,3 +157,7 @@ func (c *SLVMImageCache) Remove(ctx context.Context) error {
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *SLVMImageCache) GetAccessDirectory() (string, error) {
|
||||
return "", errors.ErrNotImplemented
|
||||
}
|
||||
|
||||
@@ -161,3 +161,7 @@ func (r *SRbdImageCache) GetDesc() *remotefile.SImageDesc {
|
||||
func (r *SRbdImageCache) GetImageId() string {
|
||||
return r.imageId
|
||||
}
|
||||
|
||||
func (r *SRbdImageCache) GetAccessDirectory() (string, error) {
|
||||
return "", errors.ErrNotImplemented
|
||||
}
|
||||
|
||||
@@ -29,6 +29,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/hostman/guestman/desc"
|
||||
"yunion.io/x/onecloud/pkg/hostman/hostutils"
|
||||
"yunion.io/x/onecloud/pkg/hostman/options"
|
||||
@@ -366,7 +367,7 @@ func (s *SBaseStorage) CreateDiskByDiskinfo(ctx context.Context, params interfac
|
||||
case len(createParams.DiskInfo.SnapshotId) > 0:
|
||||
log.Infof("CreateDiskFromSnpashot %s", createParams)
|
||||
return s.CreateDiskFromSnpashot(ctx, disk, createParams)
|
||||
case len(createParams.DiskInfo.ImageId) > 0:
|
||||
case len(createParams.DiskInfo.ImageId) > 0 && createParams.DiskInfo.ImageFormat != imageapi.IMAGE_DISK_FORMAT_TGZ:
|
||||
log.Infof("CreateDiskFromTemplate %s", createParams)
|
||||
return s.CreateDiskFromTemplate(ctx, disk, createParams)
|
||||
case createParams.DiskInfo.Backup != nil:
|
||||
|
||||
@@ -228,7 +228,7 @@ func (self *SImage) CustomizedGetDetailsBody(ctx context.Context, userCred mccli
|
||||
|
||||
if self.IsGuestImage.IsFalse() {
|
||||
formatStr := jsonutils.GetAnyString(query, []string{"format", "disk_format"})
|
||||
if len(formatStr) > 0 {
|
||||
if len(formatStr) > 0 && formatStr != api.IMAGE_DISK_FORMAT_TGZ {
|
||||
subimg := ImageSubformatManager.FetchSubImage(self.Id, formatStr)
|
||||
if subimg != nil {
|
||||
if strings.HasPrefix(subimg.Location, api.LocalFilePrefix) {
|
||||
@@ -546,7 +546,6 @@ func (self *SImage) SaveImageFromStream(reader io.Reader, totalSize int64, calCh
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
format = string(img.Format)
|
||||
virtualSizeBytes = img.SizeBytes
|
||||
|
||||
var fastChksum string
|
||||
@@ -1035,6 +1034,8 @@ func (manager *SImageManager) Usage(ctx context.Context, scope rbacscope.TRbacSc
|
||||
func (self *SImage) GetImageType() api.TImageType {
|
||||
if self.DiskFormat == string(qemuimgfmt.ISO) {
|
||||
return api.ImageTypeISO
|
||||
} else if self.DiskFormat == api.IMAGE_DISK_FORMAT_TGZ {
|
||||
return api.ImageTypeTarGzip
|
||||
} else {
|
||||
return api.ImageTypeTemplate
|
||||
}
|
||||
@@ -1112,7 +1113,7 @@ func (img *SImage) isEncrypted() bool {
|
||||
}
|
||||
|
||||
func (self *SImage) makeSubImages(ctx context.Context) error {
|
||||
if self.GetImageType() == api.ImageTypeISO {
|
||||
if self.GetImageType() == api.ImageTypeISO || self.GetImageType() == api.ImageTypeTarGzip {
|
||||
// do not convert iso
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ type StorageCacheImageActionOptions struct {
|
||||
options.BaseIdOptions
|
||||
IMAGE string `help:"ID or name of image"`
|
||||
Force bool `help:"Force refresh cache, even if the image exists in cache"`
|
||||
Format string `help:"Image force" choices:"iso|vmdk|qcow2|vhd"`
|
||||
Format string `help:"Image force" choices:"iso|vmdk|qcow2|vhd|tgz"`
|
||||
}
|
||||
|
||||
func (opts *StorageCacheImageActionOptions) Params() (jsonutils.JSONObject, error) {
|
||||
|
||||
@@ -15,8 +15,10 @@
|
||||
package fileutils2
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bufio"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
@@ -402,3 +404,23 @@ func IsIsoFile(sPath string) bool {
|
||||
}
|
||||
return bytes.Equal(buffer, []byte("CD001"))
|
||||
}
|
||||
|
||||
func IsTarGzipFile(fPath string) bool {
|
||||
f, err := os.Open(fPath)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
gzf, err := gzip.NewReader(f)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
tarReader := tar.NewReader(gzf)
|
||||
_, err = tarReader.Next()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
"yunion.io/x/pkg/utils"
|
||||
|
||||
api "yunion.io/x/onecloud/pkg/apis/compute"
|
||||
imageapi "yunion.io/x/onecloud/pkg/apis/image"
|
||||
"yunion.io/x/onecloud/pkg/util/fileutils2"
|
||||
"yunion.io/x/onecloud/pkg/util/procutils"
|
||||
"yunion.io/x/onecloud/pkg/util/qemutils"
|
||||
@@ -215,6 +216,9 @@ func (img *SQemuImage) parse() error {
|
||||
if img.Format == qemuimgfmt.RAW && fileutils2.IsFile(img.Path) && fileutils2.IsIsoFile(img.Path) {
|
||||
img.Format = qemuimgfmt.ISO
|
||||
}
|
||||
if img.Format == qemuimgfmt.RAW && fileutils2.IsFile(img.Path) && fileutils2.IsTarGzipFile(img.Path) {
|
||||
img.Format = imageapi.IMAGE_DISK_FORMAT_TGZ
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user