Feature/floppy (#15008)

* feat(region,host): add floppy,mult cdrom

* fix floppy-device

Co-authored-by: huangzekeng <huangzekeng@grgbanking.com>
This commit is contained in:
Davidzkeng
2022-09-27 21:15:52 +08:00
committed by GitHub
parent 3a9f2de4ee
commit bbaaf2da7e
35 changed files with 1052 additions and 125 deletions

View File

@@ -15,6 +15,7 @@
package compute
type GuestcdromJsonDesc struct {
Ordinal int `json:"ordinal"`
ImageId string `json:"image_id"`
Path string `json:"path"`
Name string `json:"name"`

View File

@@ -0,0 +1,23 @@
// 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 compute
type GuestfloppyJsonDesc struct {
Ordinal int `json:"ordinal"`
ImageId string `json:"image_id"`
Path string `json:"path"`
Name string `json:"name"`
Size int64 `json:"size"`
}

View File

@@ -195,6 +195,8 @@ type ServerDetails struct {
DiskCount int `json:"disk_count"`
// 是否支持ISO启动
CdromSupport bool `json:"cdrom_support"`
//是否支持Floppy启动
FloppySupport bool `json:"floppy_support"`
// 磁盘大小
// example:30720
@@ -237,7 +239,10 @@ type ServerDetails struct {
IsGpu bool `json:"is_gpu"`
// Cdrom信息
Cdrom string `json:"cdrom,allowempty"`
Cdrom []Cdrom `json:"cdrom"`
//Floppy信息
Floppy []Floppy `json:"floppy"`
// 主机在伸缩组中的状态
ScalingStatus string `json:"scaling_status"`
@@ -246,6 +251,16 @@ type ServerDetails struct {
ScalingGroupId string `json:"scaling_group_id"`
}
type Floppy struct {
Ordinal int `json:"ordinal"`
Detail string `json:"detail"`
}
type Cdrom struct {
Ordinal int `json:"ordinal"`
Detail string `json:"detail"`
}
func (self ServerDetails) GetMetricTags() map[string]string {
ret := map[string]string{
"id": self.Id,
@@ -758,7 +773,10 @@ type GuestJsonDesc struct {
Nics []*GuestnetworkJsonDesc `json:"nics"`
Disks []*GuestdiskJsonDesc `json:"disks"`
Cdrom *GuestcdromJsonDesc `json:"cdrom"`
Cdrom *GuestcdromJsonDesc `json:"cdrom"`
Cdroms []*GuestcdromJsonDesc `json:"cdroms"`
Floppys []*GuestfloppyJsonDesc `json:"floppys"`
Tenant string `json:"tenant"`
TenantId string `json:"tenant_id"`
@@ -924,6 +942,16 @@ type ServerSetPasswordInput struct {
AutoStart bool
}
type ServerInsertVfdInput struct {
FloppyOrdinal int64 `json:"floppy_ordinal"`
ImageId string `json:"image_id"`
}
type ServerEjectVfdInput struct {
FloppyOrdinal int64 `json:"floppy_ordinal"`
ImageId string `json:"image_id"`
}
type ServerSetLiveMigrateParamsInput struct {
MaxBandwidthMB *int64
DowntimeLimitMS *int64

View File

@@ -141,6 +141,11 @@ const (
ACT_ISO_ATTACH = "iso_attach"
ACT_ISO_DETACH = "iso_detach"
ACT_VFD_PREPARING = "vfd_preparing"
ACT_VFD_PREPARE_FAIL = "vfd_prepare_fail"
ACT_VFD_ATTACH = "vfd_attach"
ACT_VFD_DETACH = "vfd_detach"
ACT_EIP_ATTACH = "eip_attach"
ACT_EIP_DETACH = "eip_detach"

View File

@@ -325,7 +325,7 @@ func (self *SBaremetalGuestDriver) RequestGuestHotRemoveIso(ctx context.Context,
}
func (self *SBaremetalGuestDriver) RequestGuestCreateInsertIso(ctx context.Context, imageId string, guest *models.SGuest, task taskman.ITask) error {
return guest.StartInsertIsoTask(ctx, imageId, true, guest.HostId, task.GetUserCred(), task.GetTaskId())
return guest.StartInsertIsoTask(ctx, 0, imageId, true, guest.HostId, task.GetUserCred(), task.GetTaskId())
}
func (self *SBaremetalGuestDriver) RequestStartOnHost(ctx context.Context, guest *models.SGuest, host *models.SHost, userCred mcclient.TokenCredential, task taskman.ITask) error {
@@ -611,3 +611,7 @@ func (self *SBaremetalGuestDriver) IsSupportCdrom(guest *models.SGuest) (bool, e
}
return ipmiInfo.CdromBoot, nil
}
func (self *SBaremetalGuestDriver) IsSupportFloppy(guest *models.SGuest) (bool, error) {
return false, nil
}

View File

@@ -248,6 +248,18 @@ func (self *SBaseGuestDriver) RequestRebuildRootDisk(ctx context.Context, guest
return fmt.Errorf("Not Implement")
}
func (self *SBaseGuestDriver) NeedRequestGuestHotAddVfd(ctx context.Context, guest *models.SGuest) bool {
return false
}
func (self *SBaseGuestDriver) RequestGuestHotAddVfd(ctx context.Context, guest *models.SGuest, path string, boot bool, task taskman.ITask) error {
return fmt.Errorf("Not Implement")
}
func (self *SBaseGuestDriver) RequestGuestHotRemoveVfd(ctx context.Context, guest *models.SGuest, task taskman.ITask) 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")
}

View File

@@ -80,6 +80,10 @@ func (self *SCloudpodsGuestDriver) IsSupportCdrom(guest *models.SGuest) (bool, e
return true, nil
}
func (self *SCloudpodsGuestDriver) IsSupportFloppy(guest *models.SGuest) (bool, error) {
return false, nil
}
func (self *SCloudpodsGuestDriver) IsSupportMigrate() bool {
return true
}

View File

@@ -220,3 +220,7 @@ func (self *SContainerDriver) IsSupportGuestClone() bool {
func (self *SContainerDriver) IsSupportCdrom(guest *models.SGuest) (bool, error) {
return false, nil
}
func (self *SContainerDriver) IsSupportFloppy(guest *models.SGuest) (bool, error) {
return false, nil
}

View File

@@ -651,6 +651,18 @@ func (self *SKVMGuestDriver) RequestGuestHotRemoveIso(ctx context.Context, guest
return guest.StartSyncTask(ctx, task.GetUserCred(), false, task.GetTaskId())
}
func (self *SKVMGuestDriver) NeedRequestGuestHotAddVfd(ctx context.Context, guest *models.SGuest) bool {
return guest.Status == api.VM_RUNNING
}
func (self *SKVMGuestDriver) RequestGuestHotAddVfd(ctx context.Context, guest *models.SGuest, path string, boot bool, task taskman.ITask) error {
return guest.StartSyncTask(ctx, task.GetUserCred(), false, task.GetTaskId())
}
func (self *SKVMGuestDriver) RequestGuestHotRemoveVfd(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
return guest.StartSyncTask(ctx, task.GetUserCred(), false, task.GetTaskId())
}
func (self *SKVMGuestDriver) RequestRebuildRootDisk(ctx context.Context, guest *models.SGuest, task taskman.ITask) error {
subtask, err := taskman.TaskManager.NewTask(ctx, "KVMGuestRebuildRootTask", guest, task.GetUserCred(), task.GetParams(), task.GetTaskId(), "", nil)
if err != nil {
@@ -714,6 +726,10 @@ func (self *SKVMGuestDriver) IsSupportCdrom(guest *models.SGuest) (bool, error)
return true, nil
}
func (self *SKVMGuestDriver) IsSupportFloppy(guest *models.SGuest) (bool, error) {
return true, nil
}
func (self *SKVMGuestDriver) IsSupportMigrate() bool {
return true
}

View File

@@ -1189,6 +1189,10 @@ func (self *SManagedVirtualizedGuestDriver) IsSupportCdrom(guest *models.SGuest)
return false, nil
}
func (self *SManagedVirtualizedGuestDriver) IsSupportFloppy(guest *models.SGuest) (bool, error) {
return false, nil
}
func GetCloudVMStatus(vm cloudprovider.ICloudVM) string {
status := vm.GetStatus()
switch status {

View File

@@ -220,7 +220,7 @@ func (self *SVirtualizedGuestDriver) ChooseHostStorage(host *models.SHost, guest
}
func (self *SVirtualizedGuestDriver) RequestGuestCreateInsertIso(ctx context.Context, imageId string, guest *models.SGuest, task taskman.ITask) error {
return guest.StartInsertIsoTask(ctx, imageId, true, guest.HostId, task.GetUserCred(), task.GetTaskId())
return guest.StartInsertIsoTask(ctx, 0, imageId, true, guest.HostId, task.GetUserCred(), task.GetTaskId())
}
func (self *SVirtualizedGuestDriver) StartGuestStopTask(guest *models.SGuest, ctx context.Context, userCred mcclient.TokenCredential, params *jsonutils.JSONDict, parentTaskId string) error {

View File

@@ -1139,18 +1139,18 @@ func (self *SGuest) StartGuestStopTask(ctx context.Context, userCred mcclient.To
return self.GetDriver().StartGuestStopTask(self, ctx, userCred, params, parentTaskId)
}
func (self *SGuest) insertIso(imageId string) bool {
cdrom := self.getCdrom(true)
func (self *SGuest) insertIso(imageId string, cdromOrdinal int64) bool {
cdrom := self.getCdrom(true, cdromOrdinal)
return cdrom.insertIso(imageId)
}
func (self *SGuest) InsertIsoSucc(imageId string, path string, size int64, name string) bool {
cdrom := self.getCdrom(false)
func (self *SGuest) InsertIsoSucc(cdromOrdinal int64, imageId string, path string, size int64, name string) bool {
cdrom := self.getCdrom(false, cdromOrdinal)
return cdrom.insertIsoSucc(imageId, path, size, name)
}
func (self *SGuest) GetDetailsIso(userCred mcclient.TokenCredential) jsonutils.JSONObject {
cdrom := self.getCdrom(false)
func (self *SGuest) GetDetailsIso(cdromOrdinal int64, userCred mcclient.TokenCredential) jsonutils.JSONObject {
cdrom := self.getCdrom(false, cdromOrdinal)
desc := jsonutils.NewDict()
if len(cdrom.ImageId) > 0 {
desc.Set("image_id", jsonutils.NewString(cdrom.ImageId))
@@ -1168,7 +1168,11 @@ func (self *SGuest) PerformInsertiso(ctx context.Context, userCred mcclient.Toke
if !utils.IsInStringArray(self.Hypervisor, []string{api.HYPERVISOR_KVM, api.HYPERVISOR_BAREMETAL}) {
return nil, httperrors.NewNotAcceptableError("Not allow for hypervisor %s", self.Hypervisor)
}
cdrom := self.getCdrom(false)
cdromOrdinal, _ := data.Int("cdrom_ordinal")
if cdromOrdinal < 0 {
return nil, httperrors.NewServerStatusError("invalid cdrom_ordinal: %d", cdromOrdinal)
}
cdrom := self.getCdrom(false, cdromOrdinal)
if cdrom != nil && len(cdrom.ImageId) > 0 {
return nil, httperrors.NewBadRequestError("CD-ROM not empty, please eject first")
}
@@ -1180,7 +1184,7 @@ func (self *SGuest) PerformInsertiso(ctx context.Context, userCred mcclient.Toke
}
if utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_READY}) {
err = self.StartInsertIsoTask(ctx, image.Id, false, self.HostId, userCred, "")
err = self.StartInsertIsoTask(ctx, cdromOrdinal, image.Id, false, self.HostId, userCred, "")
return nil, err
} else {
return nil, httperrors.NewServerStatusError("Insert ISO not allowed in status %s", self.Status)
@@ -1191,20 +1195,26 @@ func (self *SGuest) PerformEjectiso(ctx context.Context, userCred mcclient.Token
if !utils.IsInStringArray(self.Hypervisor, []string{api.HYPERVISOR_KVM, api.HYPERVISOR_BAREMETAL}) {
return nil, httperrors.NewNotAcceptableError("Not allow for hypervisor %s", self.Hypervisor)
}
cdrom := self.getCdrom(false)
cdromOrdinal, _ := data.Int("cdrom_ordinal")
if cdromOrdinal < 0 {
return nil, httperrors.NewServerStatusError("invalid cdrom_ordinal: %d", cdromOrdinal)
}
cdrom := self.getCdrom(false, cdromOrdinal)
if cdrom == nil || len(cdrom.ImageId) == 0 {
return nil, httperrors.NewBadRequestError("No ISO to eject")
}
if utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_READY}) {
err := self.StartEjectisoTask(ctx, userCred, "")
err := self.StartEjectisoTask(ctx, cdromOrdinal, userCred, "")
return nil, err
} else {
return nil, httperrors.NewServerStatusError("Eject ISO not allowed in status %s", self.Status)
}
}
func (self *SGuest) StartEjectisoTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
task, err := taskman.TaskManager.NewTask(ctx, "GuestEjectISOTask", self, userCred, nil, parentTaskId, "", nil)
func (self *SGuest) StartEjectisoTask(ctx context.Context, cdromOrdinal int64, userCred mcclient.TokenCredential, parentTaskId string) error {
data := jsonutils.NewDict()
data.Add(jsonutils.NewInt(cdromOrdinal), "cdrom_ordinal")
task, err := taskman.TaskManager.NewTask(ctx, "GuestEjectISOTask", self, userCred, data, parentTaskId, "", nil)
if err != nil {
return err
}
@@ -1212,11 +1222,12 @@ func (self *SGuest) StartEjectisoTask(ctx context.Context, userCred mcclient.Tok
return nil
}
func (self *SGuest) StartInsertIsoTask(ctx context.Context, imageId string, boot bool, hostId string, userCred mcclient.TokenCredential, parentTaskId string) error {
self.insertIso(imageId)
func (self *SGuest) StartInsertIsoTask(ctx context.Context, cdromOrdinal int64, imageId string, boot bool, hostId string, userCred mcclient.TokenCredential, parentTaskId string) error {
self.insertIso(imageId, cdromOrdinal)
data := jsonutils.NewDict()
data.Add(jsonutils.NewString(imageId), "image_id")
data.Add(jsonutils.NewInt(cdromOrdinal), "cdrom_ordinal")
data.Add(jsonutils.NewString(hostId), "host_id")
if boot {
data.Add(jsonutils.JSONTrue, "boot")
@@ -1233,6 +1244,105 @@ func (self *SGuest) StartInsertIsoTask(ctx context.Context, imageId string, boot
return nil
}
func (self *SGuest) insertVfd(imageId string, floppyOrdinal int64) bool {
floppy := self.getFloppy(true, floppyOrdinal)
return floppy.insertVfd(imageId)
}
func (self *SGuest) InsertVfdSucc(floppyOrdinal int64, imageId string, path string, size int64, name string) bool {
floppy := self.getFloppy(true, floppyOrdinal)
return floppy.insertVfdSucc(imageId, path, size, name)
}
func (self *SGuest) GetDetailsVfd(floppyOrdinal int64, userCred mcclient.TokenCredential) jsonutils.JSONObject {
floppy := self.getFloppy(false, floppyOrdinal)
desc := jsonutils.NewDict()
if len(floppy.ImageId) > 0 {
desc.Set("image_id", jsonutils.NewString(floppy.ImageId))
desc.Set("status", jsonutils.NewString("inserting"))
}
if len(floppy.Path) > 0 {
desc.Set("name", jsonutils.NewString(floppy.Name))
desc.Set("size", jsonutils.NewInt(int64(floppy.Size)))
desc.Set("status", jsonutils.NewString("ready"))
}
return desc
}
func (self *SGuest) PerformInsertvfd(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data api.ServerInsertVfdInput) (jsonutils.JSONObject, error) {
if !utils.IsInStringArray(self.Hypervisor, []string{api.HYPERVISOR_KVM, api.HYPERVISOR_BAREMETAL}) {
return nil, httperrors.NewNotAcceptableError("Not allow for hypervisor %s", self.Hypervisor)
}
if data.FloppyOrdinal < 0 {
return nil, httperrors.NewServerStatusError("invalid floppy_ordinal: %d", data.FloppyOrdinal)
}
floppy := self.getFloppy(false, data.FloppyOrdinal)
if floppy != nil && len(floppy.ImageId) > 0 {
return nil, httperrors.NewBadRequestError("Floppy not empty, please eject first")
}
image, err := parseIsoInfo(ctx, userCred, data.ImageId)
if err != nil {
log.Errorln(err)
return nil, err
}
if utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_READY}) {
err = self.StartInsertVfdTask(ctx, data.FloppyOrdinal, image.Id, false, self.HostId, userCred, "")
return nil, err
} else {
return nil, httperrors.NewServerStatusError("Insert ISO not allowed in status %s", self.Status)
}
}
func (self *SGuest) PerformEjectvfd(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data api.ServerEjectVfdInput) (jsonutils.JSONObject, error) {
if !utils.IsInStringArray(self.Hypervisor, []string{api.HYPERVISOR_KVM, api.HYPERVISOR_BAREMETAL}) {
return nil, httperrors.NewNotAcceptableError("Not allow for hypervisor %s", self.Hypervisor)
}
if data.FloppyOrdinal < 0 {
return nil, httperrors.NewServerStatusError("invalid floppy_ordinal: %d", data.FloppyOrdinal)
}
floppy := self.getFloppy(false, data.FloppyOrdinal)
if floppy == nil || len(floppy.ImageId) == 0 {
return nil, httperrors.NewBadRequestError("No VFD to eject")
}
if utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_READY}) {
err := self.StartEjectvfdTask(ctx, userCred, "")
return nil, err
} else {
return nil, httperrors.NewServerStatusError("Eject ISO not allowed in status %s", self.Status)
}
}
func (self *SGuest) StartEjectvfdTask(ctx context.Context, userCred mcclient.TokenCredential, parentTaskId string) error {
task, err := taskman.TaskManager.NewTask(ctx, "GuestEjectVFDTask", self, userCred, nil, parentTaskId, "", nil)
if err != nil {
return err
}
task.ScheduleRun(nil)
return nil
}
func (self *SGuest) StartInsertVfdTask(ctx context.Context, floppyOrdinal int64, imageId string, boot bool, hostId string, userCred mcclient.TokenCredential, parentTaskId string) error {
self.insertVfd(imageId, floppyOrdinal)
data := jsonutils.NewDict()
data.Add(jsonutils.NewString(imageId), "image_id")
data.Add(jsonutils.NewString(hostId), "host_id")
if boot {
data.Add(jsonutils.JSONTrue, "boot")
}
taskName := "GuestInsertVfdTask"
if self.BackupHostId != "" {
taskName = "HaGuestInsertVfdTask"
}
task, err := taskman.TaskManager.NewTask(ctx, taskName, self, userCred, data, parentTaskId, "", nil)
if err != nil {
return err
}
task.ScheduleRun(nil)
return nil
}
func (self *SGuest) StartGueststartTask(
ctx context.Context, userCred mcclient.TokenCredential,
data *jsonutils.JSONDict, parentTaskId string,
@@ -2126,7 +2236,6 @@ func (self *SGuest) attachIsolatedDevice(ctx context.Context, userCred mcclient.
return fmt.Errorf("Isolated device already attached to another guest: %s", dev.GuestId)
}
if dev.HostId !=
self.HostId {
return fmt.Errorf("Isolated device and guest are not located in the same host")
}
@@ -4631,9 +4740,17 @@ func (self *SGuest) validateForBatchMigrate(ctx context.Context, rescueMode bool
return guest, httperrors.NewBadRequestError("guest %s status %s can't migrate", guest.Name, guest.Status)
}
if guest.Status == api.VM_RUNNING {
cdrom := guest.getCdrom(false)
if cdrom != nil && len(cdrom.ImageId) > 0 {
return guest, httperrors.NewBadRequestError("cannot migrate with cdrom")
cdroms, _ := guest.getCdroms()
for _, cdrom := range cdroms {
if len(cdrom.ImageId) > 0 {
return guest, httperrors.NewBadRequestError("cannot migrate with cdrom")
}
}
floppys, _ := guest.getFloppys()
for _, floppy := range floppys {
if len(floppy.ImageId) > 0 {
return guest, httperrors.NewBadRequestError("cannot migrate with floppy")
}
}
} else if guest.Status == api.VM_UNKNOWN {
if guest.getDefaultStorageType() == api.STORAGE_LOCAL {

View File

@@ -222,12 +222,25 @@ func (manager *SGuestManager) FetchCustomizeColumns(
gcds := fetchGuestCdroms(guestIds)
if gcds != nil {
for i := range rows {
if gcd, ok := gcds[guestIds[i]]; ok {
rows[i].Cdrom = gcd.GetDetails()
for _, gcd := range gcds[guestIds[i]] {
t := api.Cdrom{Ordinal: gcd.Ordinal, Detail: gcd.GetDetails()}
rows[i].Cdrom = append(rows[i].Cdrom, t)
}
}
}
}
if len(fields) == 0 || fields.Contains("floppy") {
gfloppys := fetchGuestFloppys(guestIds)
if gfloppys != nil {
for i := range rows {
for _, gfl := range gfloppys[guestIds[i]] {
t := api.Floppy{Ordinal: gfl.Ordinal, Detail: gfl.GetDetails()}
rows[i].Floppy = append(rows[i].Floppy, t)
}
}
}
}
if len(fields) == 0 || fields.Contains("scaling_group") {
sggs := fetchScalingGroupGuest(guestIds...)
if sggs != nil && len(sggs) != 0 {
@@ -691,16 +704,30 @@ func fetchGuestIsolatedDevices(guestIds []string) map[string][]api.SIsolatedDevi
return ret
}
func fetchGuestCdroms(guestIds []string) map[string]SGuestcdrom {
func fetchGuestCdroms(guestIds []string) map[string][]SGuestcdrom {
q := GuestcdromManager.Query().In("id", guestIds)
gcds := make([]SGuestcdrom, 0)
err := q.All(&gcds)
if err != nil {
return nil
}
ret := make(map[string]SGuestcdrom)
ret := make(map[string][]SGuestcdrom)
for i := range gcds {
ret[gcds[i].Id] = gcds[i]
ret[gcds[i].Id] = append(ret[gcds[i].Id], gcds[i])
}
return ret
}
func fetchGuestFloppys(guestIds []string) map[string][]SGuestfloppy {
q := GuestFloppyManager.Query().In("id", guestIds)
gfls := make([]SGuestfloppy, 0)
err := q.All(&gfls)
if err != nil {
return nil
}
ret := make(map[string][]SGuestfloppy)
for i := range gfls {
ret[gfls[i].Id] = append(ret[gfls[i].Id], gfls[i])
}
return ret
}

View File

@@ -47,7 +47,9 @@ func init() {
type SGuestcdrom struct {
db.SModelBase
Id string `width:"36" charset:"ascii" primary:"true"` // = Column(VARCHAR(36, charset='ascii'), primary_key=True)
RowId int64 `primary:"true" auto_increment:"true" list:"user"`
Ordinal int `nullable:"false" default:"0"` // = Column(Integer, nullable=False, default=0)
Id string `width:"36" charset:"ascii"` // = Column(VARCHAR(36, charset='ascii'), primary_key=True)
ImageId string `width:"36" charset:"ascii" nullable:"true"` // Column(VARCHAR(36, charset='ascii'), nullable=True)
Name string `width:"64" charset:"ascii" nullable:"true"` // Column(VARCHAR(64, charset='ascii'), nullable=True)
Path string `width:"256" charset:"ascii" nullable:"true"` // Column(VARCHAR(256, charset='ascii'), nullable=True)
@@ -127,6 +129,7 @@ func (self *SGuestcdrom) GetDetails() string {
func (self *SGuestcdrom) getJsonDesc() *api.GuestcdromJsonDesc {
if len(self.ImageId) > 0 && len(self.Path) > 0 {
return &api.GuestcdromJsonDesc{
Ordinal: self.Ordinal,
ImageId: self.ImageId,
Path: self.Path,
Name: self.Name,

View File

@@ -169,6 +169,10 @@ type IGuestDriver interface {
RequestGuestHotRemoveIso(ctx context.Context, guest *SGuest, task taskman.ITask) error
RequestRebuildRootDisk(ctx context.Context, guest *SGuest, task taskman.ITask) error
NeedRequestGuestHotAddVfd(ctx context.Context, guest *SGuest) bool
RequestGuestHotAddVfd(ctx context.Context, guest *SGuest, path string, boot bool, task taskman.ITask) error
RequestGuestHotRemoveVfd(ctx context.Context, guest *SGuest, task taskman.ITask) 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
@@ -193,6 +197,7 @@ type IGuestDriver interface {
CancelExpireTime(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest) error
IsSupportCdrom(guest *SGuest) (bool, error)
IsSupportFloppy(guest *SGuest) (bool, error)
IsSupportPublicipToEip() bool
RequestConvertPublicipToEip(ctx context.Context, userCred mcclient.TokenCredential, guest *SGuest, task taskman.ITask) error

View File

@@ -0,0 +1,140 @@
// 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 models
import (
"fmt"
"time"
"yunion.io/x/log"
api "yunion.io/x/onecloud/pkg/apis/compute"
"yunion.io/x/onecloud/pkg/cloudcommon/db"
)
// +onecloud:swagger-gen-ignore
type SGuestFloppyManager struct {
db.SModelBaseManager
}
var GuestFloppyManager *SGuestFloppyManager
func init() {
GuestFloppyManager = &SGuestFloppyManager{
SModelBaseManager: db.NewModelBaseManager(
SGuestfloppy{},
"guestfloppy_tbl",
"guestfloppy",
"guestfloppys",
),
}
GuestFloppyManager.SetVirtualObject(GuestFloppyManager)
}
// +onecloud:swagger-gen-ignore
type SGuestfloppy struct {
db.SModelBase
RowId int64 `primary:"true" auto_increment:"true" list:"user"`
Id string `width:"36" charset:"ascii" ` // = Column(VARCHAR(36, charset='ascii'), primary_key=True)
Ordinal int `nullable:"false" default:"0"` // = Column(Integer, nullable=False, default=0)
ImageId string `width:"36" charset:"ascii" nullable:"true"` // Column(VARCHAR(36, charset='ascii'), nullable=True)
Name string `width:"64" charset:"ascii" nullable:"true"` // Column(VARCHAR(64, charset='ascii'), nullable=True)
Path string `width:"256" charset:"ascii" nullable:"true"` // Column(VARCHAR(256, charset='ascii'), nullable=True)
Size int64 `nullable:"false" default:"0"` // = Column(Integer, nullable=False, default=0)
UpdatedAt time.Time `nullable:"false" updated_at:"true" nullable:"false"`
UpdateVersion int `default:"0" nullable:"false" auto_version:"true"`
}
func (self *SGuestfloppy) insertVfd(imageId string) bool {
if len(self.ImageId) == 0 {
_, err := db.Update(self, func() error {
self.ImageId = imageId
self.Name = ""
self.Path = ""
self.Size = 0
return nil
})
if err != nil {
log.Errorf("insertVfd saveupdate fail: %s", err)
return false
}
return true
} else {
return false
}
}
func (self *SGuestfloppy) insertVfdSucc(imageId string, path string, size int64, name string) bool {
if self.ImageId == imageId {
_, err := db.Update(self, func() error {
self.Name = name
self.Path = path
self.Size = size
return nil
})
if err != nil {
log.Errorf("insertIsoSucc saveUpdate fail %s", err)
return false
}
return true
} else {
return false
}
}
func (self *SGuestfloppy) ejectVfd() bool {
if len(self.ImageId) > 0 {
_, err := db.Update(self, func() error {
self.ImageId = ""
self.Name = ""
self.Path = ""
self.Size = 0
return nil
})
if err != nil {
log.Errorf("ejectVfd saveUpdate fail %s", err)
return false
}
return true
} else {
return false
}
}
func (self *SGuestfloppy) GetDetails() string {
if len(self.ImageId) > 0 {
if self.Size > 0 {
return fmt.Sprintf("%s(%s/%dMB)", self.Name, self.ImageId, self.Size)
} else {
return fmt.Sprintf("%s(inserting)", self.ImageId)
}
} else {
return ""
}
}
func (self *SGuestfloppy) getJsonDesc() *api.GuestfloppyJsonDesc {
if len(self.ImageId) > 0 && len(self.Path) > 0 {
return &api.GuestfloppyJsonDesc{
Ordinal: self.Ordinal,
ImageId: self.ImageId,
Path: self.Path,
Name: self.Name,
Size: self.Size,
}
}
return nil
}

View File

@@ -2227,6 +2227,7 @@ func (self *SGuest) moreExtraInfo(
}
out.CdromSupport, _ = self.GetDriver().IsSupportCdrom(self)
out.FloppySupport, _ = self.GetDriver().IsSupportFloppy(self)
return out
}
@@ -2336,18 +2337,19 @@ func (self *SGuest) getNetworksDetails() string {
}
func (self *SGuest) GetCdrom() *SGuestcdrom {
return self.getCdrom(false)
return self.getCdrom(false, 0)
}
func (self *SGuest) getCdrom(create bool) *SGuestcdrom {
func (self *SGuest) getCdrom(create bool, ordinal int64) *SGuestcdrom {
cdrom := SGuestcdrom{}
cdrom.SetModelManager(GuestcdromManager, &cdrom)
err := GuestcdromManager.Query().Equals("id", self.Id).First(&cdrom)
err := GuestcdromManager.Query().Equals("id", self.Id).Equals("ordinal", ordinal).First(&cdrom)
if err != nil {
if err == sql.ErrNoRows {
if create {
cdrom.Id = self.Id
cdrom.Ordinal = int(ordinal)
err = GuestcdromManager.TableSpec().Insert(context.TODO(), &cdrom)
if err != nil {
log.Errorf("insert cdrom fail %s", err)
@@ -2366,6 +2368,54 @@ func (self *SGuest) getCdrom(create bool) *SGuestcdrom {
}
}
func (self *SGuest) getCdroms() ([]SGuestcdrom, error) {
cdroms := make([]SGuestcdrom, 0)
q := GuestcdromManager.Query().Equals("id", self.Id)
err := db.FetchModelObjects(GuestcdromManager, q, &cdroms)
if err != nil {
return nil, errors.Wrapf(err, "db.FetchModelObjects")
}
return cdroms, nil
}
func (self *SGuest) getFloppys() ([]SGuestfloppy, error) {
floppys := make([]SGuestfloppy, 0)
q := GuestFloppyManager.Query().Equals("id", self.Id)
err := db.FetchModelObjects(GuestFloppyManager, q, &floppys)
if err != nil {
return nil, errors.Wrapf(err, "db.FetchModelObjects")
}
return floppys, nil
}
func (self *SGuest) getFloppy(create bool, ordinal int64) *SGuestfloppy {
floppy := SGuestfloppy{}
floppy.SetModelManager(GuestFloppyManager, &floppy)
err := GuestFloppyManager.Query().Equals("id", self.Id).Equals("ordinal", ordinal).First(&floppy)
if err != nil {
if err == sql.ErrNoRows {
if create {
floppy.Id = self.Id
floppy.Ordinal = int(ordinal)
err = GuestFloppyManager.TableSpec().Insert(context.TODO(), &floppy)
if err != nil {
log.Errorf("insert cdrom fail %s", err)
return nil
}
return &floppy
} else {
return nil
}
} else {
log.Errorf("getFloppy query fail %s", err)
return nil
}
} else {
return &floppy
}
}
func (self *SGuest) getKeypair() *SKeypair {
if len(self.KeypairId) > 0 {
keypair, _ := KeypairManager.FetchById(self.KeypairId)
@@ -4088,8 +4138,8 @@ func (self *SGuest) DetachAllNetworks(ctx context.Context, userCred mcclient.Tok
return GuestnetworkManager.DeleteGuestNics(ctx, userCred, gns, false)
}
func (self *SGuest) EjectIso(userCred mcclient.TokenCredential) bool {
cdrom := self.getCdrom(false)
func (self *SGuest) EjectIso(cdromOrdinal int64, userCred mcclient.TokenCredential) bool {
cdrom := self.getCdrom(false, cdromOrdinal)
if cdrom != nil && len(cdrom.ImageId) > 0 {
imageId := cdrom.ImageId
if cdrom.ejectIso() {
@@ -4100,6 +4150,48 @@ func (self *SGuest) EjectIso(userCred mcclient.TokenCredential) bool {
return false
}
func (self *SGuest) EjectAllIso(userCred mcclient.TokenCredential) bool {
cdroms, _ := self.getCdroms()
for _, cdrom := range cdroms {
if len(cdrom.ImageId) > 0 {
imageId := cdrom.ImageId
if cdrom.ejectIso() {
db.OpsLog.LogEvent(self, db.ACT_ISO_DETACH, imageId, userCred)
} else {
return false
}
}
}
return true
}
func (self *SGuest) EjectVfd(floppyOrdinal int64, userCred mcclient.TokenCredential) bool {
floppy := self.getFloppy(false, floppyOrdinal)
if floppy != nil && len(floppy.ImageId) > 0 {
imageId := floppy.ImageId
if floppy.ejectVfd() {
db.OpsLog.LogEvent(self, db.ACT_VFD_DETACH, imageId, userCred)
return true
}
}
return false
}
func (self *SGuest) EjectAllVfd(userCred mcclient.TokenCredential) bool {
floppys, _ := self.getFloppys()
for _, floppy := range floppys {
if len(floppy.ImageId) > 0 {
imageId := floppy.ImageId
if floppy.ejectVfd() {
db.OpsLog.LogEvent(self, db.ACT_ISO_DETACH, imageId, userCred)
} else {
return false
}
}
}
return true
}
func (self *SGuest) Delete(ctx context.Context, userCred mcclient.TokenCredential) error {
// self.SVirtualResourceBase.Delete(ctx, userCred)
// override
@@ -4421,10 +4513,17 @@ func (self *SGuest) GetJsonDescAtHypervisor(ctx context.Context, host *SHost) *a
desc.Disks = append(desc.Disks, diskDesc)
}
// cdrom
cdrom := self.getCdrom(false)
if cdrom != nil {
desc.Cdrom = cdrom.getJsonDesc()
cdroms, _ := self.getCdroms()
for _, cdrom := range cdroms {
cdromDesc := cdrom.getJsonDesc()
desc.Cdroms = append(desc.Cdroms, cdromDesc)
}
//floppy
floppys, _ := self.getFloppys()
for _, floppy := range floppys {
floppyDesc := floppy.getJsonDesc()
desc.Floppys = append(desc.Floppys, floppyDesc)
}
// tenant
@@ -5631,7 +5730,7 @@ func (self *SGuest) toCreateInput() *api.ServerCreateInput {
r := new(api.ServerCreateInput)
r.VmemSize = self.VmemSize
r.VcpuCount = int(self.VcpuCount)
if guestCdrom := self.getCdrom(false); guestCdrom != nil {
if guestCdrom := self.getCdrom(false, 0); guestCdrom != nil {
r.Cdrom = guestCdrom.ImageId
}
r.Vga = self.Vga

View File

@@ -127,7 +127,8 @@ func (guest *SGuest) purge(ctx context.Context, userCred mcclient.TokenCredentia
guest.RevokeAllSecgroups(ctx, userCred)
guest.LeaveAllGroups(ctx, userCred)
guest.DetachAllNetworks(ctx, userCred)
guest.EjectIso(userCred)
guest.EjectAllIso(userCred)
guest.EjectAllVfd(userCred)
guest.DeleteEip(ctx, userCred)
guest.purgeInstanceSnapshots(ctx, userCred)
guest.DeleteAllDisksInDB(ctx, userCred)

View File

@@ -63,6 +63,7 @@ func InitHandlers(app *appsrv.Application) {
db.SharedResourceManager,
db.I18nManager,
models.GuestcdromManager,
models.GuestFloppyManager,
models.NetInterfaceManager,
models.QuotaManager,

View File

@@ -359,7 +359,7 @@ func (self *GuestCreateBackupTask) StartCreateBackupDisks(ctx context.Context, g
func (self *GuestCreateBackupTask) StartInsertIso(ctx context.Context, guest *models.SGuest, imageId string) {
self.SetStage("OnInsertIso", nil)
guest.StartInsertIsoTask(ctx, imageId, false, guest.BackupHostId, self.UserCred, self.GetTaskId())
guest.StartInsertIsoTask(ctx, 0, imageId, false, guest.BackupHostId, self.UserCred, self.GetTaskId())
}
func (self *GuestCreateBackupTask) OnInsertIso(ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject) {

View File

@@ -332,7 +332,8 @@ func (self *GuestDeleteTask) OnGuestDeleteCompleteFailed(ctx context.Context, ob
func (self *GuestDeleteTask) OnGuestDeleteComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
guest := obj.(*models.SGuest)
guest.DetachAllNetworks(ctx, self.UserCred)
guest.EjectIso(self.UserCred)
guest.EjectAllIso(self.UserCred)
guest.EjectAllVfd(self.UserCred)
guest.DeleteEip(ctx, self.UserCred)
guest.GetDriver().OnDeleteGuestFinalCleanup(ctx, guest, self.UserCred)
// sync capacity used for storage

View File

@@ -39,7 +39,8 @@ func (self *GuestEjectISOTask) OnInit(ctx context.Context, obj db.IStandaloneMod
func (self *GuestEjectISOTask) startEjectIso(ctx context.Context, obj db.IStandaloneModel) {
guest := obj.(*models.SGuest)
if guest.EjectIso(self.UserCred) && guest.Status == api.VM_RUNNING {
cdromOrdinal, _ := self.Params.Int("cdrom_ordinal")
if guest.EjectIso(cdromOrdinal, self.UserCred) && guest.Status == api.VM_RUNNING {
self.SetStage("OnConfigSyncComplete", nil)
guest.GetDriver().RequestGuestHotRemoveIso(ctx, guest, self)
} else {

View File

@@ -0,0 +1,53 @@
// 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"
"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"
)
type GuestEjectVFDTask struct {
SGuestBaseTask
}
func init() {
taskman.RegisterTask(GuestEjectVFDTask{})
}
func (self *GuestEjectVFDTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
self.startEjectVfd(ctx, obj)
}
func (self *GuestEjectVFDTask) startEjectVfd(ctx context.Context, obj db.IStandaloneModel) {
guest := obj.(*models.SGuest)
floppyOrdinal, _ := self.Params.Int("floppy_ordinal")
if guest.EjectVfd(floppyOrdinal, self.UserCred) && guest.Status == api.VM_RUNNING {
self.SetStage("OnConfigSyncComplete", nil)
guest.GetDriver().RequestGuestHotRemoveVfd(ctx, guest, self)
} else {
self.SetStageComplete(ctx, nil)
}
}
func (self *GuestEjectVFDTask) OnConfigSyncComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
self.SetStageComplete(ctx, nil)
}

View File

@@ -41,6 +41,7 @@ func (self *GuestInsertIsoTask) OnInit(ctx context.Context, obj db.IStandaloneMo
func (self *GuestInsertIsoTask) prepareIsoImage(ctx context.Context, obj db.IStandaloneModel) {
guest := obj.(*models.SGuest)
imageId, _ := self.Params.GetString("image_id")
cdromOrdinal, _ := self.Params.Int("cdrom_ordinal")
db.OpsLog.LogEvent(obj, db.ACT_ISO_PREPARING, imageId, self.UserCred)
disks, _ := guest.GetGuestDisks()
@@ -57,7 +58,7 @@ func (self *GuestInsertIsoTask) prepareIsoImage(ctx context.Context, obj db.ISta
}
storageCache.StartImageCacheTask(ctx, self.UserCred, input)
} else {
guest.EjectIso(self.UserCred)
guest.EjectIso(cdromOrdinal, self.UserCred)
db.OpsLog.LogEvent(obj, db.ACT_ISO_PREPARE_FAIL, imageId, self.UserCred)
self.SetStageFailed(ctx, jsonutils.NewString("host no local storage cache"))
}
@@ -65,14 +66,16 @@ func (self *GuestInsertIsoTask) prepareIsoImage(ctx context.Context, obj db.ISta
func (self *GuestInsertIsoTask) OnIsoPrepareCompleteFailed(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
imageId, _ := self.Params.GetString("image_id")
cdromOrdinal, _ := self.Params.Int("cdrom_ordinal")
db.OpsLog.LogEvent(obj, db.ACT_ISO_PREPARE_FAIL, imageId, self.UserCred)
guest := obj.(*models.SGuest)
guest.EjectIso(self.UserCred)
guest.EjectIso(cdromOrdinal, self.UserCred)
self.SetStageFailed(ctx, data)
}
func (self *GuestInsertIsoTask) OnIsoPrepareComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
imageId, _ := data.GetString("image_id")
cdromOrdinal, _ := self.Params.Int("cdrom_ordinal")
size, err := data.Int("size")
if err != nil {
self.SetStageFailed(ctx, jsonutils.NewString(err.Error()))
@@ -81,8 +84,8 @@ func (self *GuestInsertIsoTask) OnIsoPrepareComplete(ctx context.Context, obj db
name, _ := data.GetString("name")
path, _ := data.GetString("path")
guest := obj.(*models.SGuest)
if guest.InsertIsoSucc(imageId, path, size, name) {
db.OpsLog.LogEvent(guest, db.ACT_ISO_ATTACH, guest.GetDetailsIso(self.UserCred), self.UserCred)
if guest.InsertIsoSucc(cdromOrdinal, imageId, path, size, name) {
db.OpsLog.LogEvent(guest, db.ACT_ISO_ATTACH, guest.GetDetailsIso(cdromOrdinal, self.UserCred), self.UserCred)
if guest.GetDriver().NeedRequestGuestHotAddIso(ctx, guest) {
self.SetStage("OnConfigSyncComplete", nil)
boot := jsonutils.QueryBoolean(self.Params, "boot", false)
@@ -110,6 +113,7 @@ func (self *HaGuestInsertIsoTask) OnInit(ctx context.Context, obj db.IStandalone
func (self *HaGuestInsertIsoTask) prepareIsoImage(ctx context.Context, obj db.IStandaloneModel) {
guest := obj.(*models.SGuest)
imageId, _ := self.Params.GetString("image_id")
cdromOrdinal, _ := self.Params.Int("cdrom_ordinal")
db.OpsLog.LogEvent(obj, db.ACT_ISO_PREPARING, imageId, self.UserCred)
disks, _ := guest.GetGuestDisks()
disk := disks[0].GetDisk()
@@ -124,7 +128,7 @@ func (self *HaGuestInsertIsoTask) prepareIsoImage(ctx context.Context, obj db.IS
}
storageCache.StartImageCacheTask(ctx, self.UserCred, input)
} else {
guest.EjectIso(self.UserCred)
guest.EjectIso(cdromOrdinal, self.UserCred)
db.OpsLog.LogEvent(obj, db.ACT_ISO_PREPARE_FAIL, imageId, self.UserCred)
self.SetStageFailed(ctx, jsonutils.NewString("host no local storage cache"))
}

View File

@@ -0,0 +1,147 @@
// 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"
"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"
)
type GuestInsertVfdTask struct {
SGuestBaseTask
}
func init() {
taskman.RegisterTask(GuestInsertVfdTask{})
taskman.RegisterTask(HaGuestInsertVfdTask{})
}
func (self *GuestInsertVfdTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
self.prepareVfdImage(ctx, obj)
}
func (self *GuestInsertVfdTask) prepareVfdImage(ctx context.Context, obj db.IStandaloneModel) {
guest := obj.(*models.SGuest)
imageId, _ := self.Params.GetString("image_id")
floppyOrdinal, _ := self.Params.Int("floppy_ordinal")
db.OpsLog.LogEvent(obj, db.ACT_VFD_PREPARING, imageId, self.UserCred)
disks, _ := guest.GetGuestDisks()
disk := disks[0].GetDisk()
storage, _ := disk.GetStorage()
storageCache := storage.GetStoragecache()
if storageCache != nil {
self.SetStage("OnVfdPrepareComplete", nil)
input := api.CacheImageInput{
ImageId: imageId,
Format: "raw",
ParentTaskId: self.GetTaskId(),
}
storageCache.StartImageCacheTask(ctx, self.UserCred, input)
} else {
guest.EjectVfd(floppyOrdinal, self.UserCred)
db.OpsLog.LogEvent(obj, db.ACT_VFD_PREPARE_FAIL, imageId, self.UserCred)
self.SetStageFailed(ctx, jsonutils.NewString("host no local storage cache"))
}
}
func (self *GuestInsertVfdTask) OnVfdPrepareCompleteFailed(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
imageId, _ := self.Params.GetString("image_id")
floppyOrdinal, _ := self.Params.Int("floppy_ordinal")
db.OpsLog.LogEvent(obj, db.ACT_VFD_PREPARE_FAIL, imageId, self.UserCred)
guest := obj.(*models.SGuest)
guest.EjectVfd(floppyOrdinal, self.UserCred)
self.SetStageFailed(ctx, data)
}
func (self *GuestInsertVfdTask) OnVfdPrepareComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
floppyOrdinal, _ := self.Params.Int("floppy_ordinal")
imageId, _ := data.GetString("image_id")
size, err := data.Int("size")
if err != nil {
self.SetStageFailed(ctx, jsonutils.NewString(err.Error()))
return
}
name, _ := data.GetString("name")
path, _ := data.GetString("path")
guest := obj.(*models.SGuest)
if guest.InsertVfdSucc(floppyOrdinal, imageId, path, size, name) {
db.OpsLog.LogEvent(guest, db.ACT_VFD_ATTACH, guest.GetDetailsVfd(floppyOrdinal, self.UserCred), self.UserCred)
if guest.GetDriver().NeedRequestGuestHotAddVfd(ctx, guest) {
self.SetStage("OnConfigSyncComplete", nil)
boot := jsonutils.QueryBoolean(self.Params, "boot", false)
guest.GetDriver().RequestGuestHotAddVfd(ctx, guest, path, boot, self)
} else {
self.SetStageComplete(ctx, nil)
}
} else {
self.SetStageComplete(ctx, nil)
}
}
func (self *GuestInsertVfdTask) OnConfigSyncComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
self.SetStageComplete(ctx, nil)
}
type HaGuestInsertVfdTask struct {
GuestInsertVfdTask
}
func (self *HaGuestInsertVfdTask) OnInit(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) {
self.prepareVfdImage(ctx, obj)
}
func (self *HaGuestInsertVfdTask) prepareVfdImage(ctx context.Context, obj db.IStandaloneModel) {
guest := obj.(*models.SGuest)
imageId, _ := self.Params.GetString("image_id")
floppyOrdinal, _ := self.Params.Int("floppy_ordinal")
db.OpsLog.LogEvent(obj, db.ACT_VFD_PREPARING, imageId, self.UserCred)
disks, _ := guest.GetGuestDisks()
disk := disks[0].GetDisk()
storage := disk.GetBackupStorage()
storageCache := storage.GetStoragecache()
if storageCache != nil {
self.SetStage("OnBackupVfdPrepareComplete", nil)
input := api.CacheImageInput{
ImageId: imageId,
Format: "raw",
ParentTaskId: self.GetTaskId(),
}
storageCache.StartImageCacheTask(ctx, self.UserCred, input)
} else {
guest.EjectVfd(floppyOrdinal, self.UserCred)
db.OpsLog.LogEvent(obj, db.ACT_VFD_PREPARE_FAIL, imageId, self.UserCred)
self.SetStageFailed(ctx, jsonutils.NewString("host no local storage cache"))
}
}
func (self *HaGuestInsertVfdTask) OnBackupVfdPrepareComplete(
ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject,
) {
self.GuestInsertVfdTask.prepareVfdImage(ctx, guest)
}
func (self *HaGuestInsertVfdTask) OnBackupVfdPrepareCompleteFailed(
ctx context.Context, guest *models.SGuest, data jsonutils.JSONObject,
) {
self.OnVfdPrepareCompleteFailed(ctx, guest, data)
}

View File

@@ -26,6 +26,7 @@ type Arch interface {
GenerateMemDesc() *desc.SGuestMem
GenerateMachineDesc(accel string) *desc.SGuestMachine
GenerateCdromDesc(osName string, cdrom *desc.SGuestCdrom)
GenerateFloppyDesc(osName string, floppy *desc.SGuestFloppy)
GenerateQgaDesc(qgaPath string) *desc.SGuestQga
GeneratePvpanicDesc() *desc.SGuestPvpanic
GenerateIsaSerialDesc() *desc.SGuestIsaSerial

View File

@@ -47,6 +47,10 @@ func (*ARM) GenerateCdromDesc(osName string, cdrom *desc.SGuestCdrom) {
cdrom.Id = "cd0"
}
func (*ARM) GenerateFloppyDesc(osName string, floppy *desc.SGuestFloppy) {
}
func (*ARM) GenerateMachineDesc(accel string) *desc.SGuestMachine {
gicVersion := "max"
return &desc.SGuestMachine{

View File

@@ -15,6 +15,8 @@
package arch
import (
"fmt"
"yunion.io/x/onecloud/pkg/hostman/guestman/desc"
"yunion.io/x/onecloud/pkg/hostman/guestman/qemu"
"yunion.io/x/onecloud/pkg/hostman/options"
@@ -62,7 +64,7 @@ func (*X86) GenerateCdromDesc(osName string, cdrom *desc.SGuestCdrom) {
"snapshot": "on",
}
default:
id = "ide0-cd0"
id = fmt.Sprintf("ide%d-cd0", cdrom.Ordinal)
devType = "ide-cd"
driveOpts = map[string]string{
"if": "none",
@@ -77,6 +79,27 @@ func (*X86) GenerateCdromDesc(osName string, cdrom *desc.SGuestCdrom) {
cdrom.Id = id
}
//-device floppy,drive=floppy0 -drive id=floppy0,if=none
func (*X86) GenerateFloppyDesc(osName string, floppy *desc.SGuestFloppy) {
var id, devType string
var driveOpts map[string]string
switch osName {
case qemu.OS_NAME_WINDOWS:
id = fmt.Sprintf("floppy%d", floppy.Ordinal)
devType = "floppy"
driveOpts = map[string]string{
"if": "none",
}
floppy.Floppy = &desc.FloppyDevice{
DevType: devType,
}
floppy.DriveOptions = driveOpts
floppy.Id = id
}
}
func (*X86) GenerateMachineDesc(accel string) *desc.SGuestMachine {
return &desc.SGuestMachine{
Accel: accel,

View File

@@ -91,7 +91,8 @@ type SGuestHardwareDesc struct {
VirtioScsi *SGuestVirtioScsi `json:",omitempty"`
PvScsi *SGuestPvScsi `json:",omitempty"`
Cdrom *SGuestCdrom `json:",omitempty"`
Cdroms []*SGuestCdrom `json:"cdroms,omitempty"`
Floppys []*SGuestFloppy `json:",omitempty"`
Disks []*SGuestDisk `json:",omitempty"`
Nics []*SGuestNetwork `json:",omitempty"`
NicsStandby []*SGuestNetwork `json:",omitempty"`
@@ -167,14 +168,24 @@ type SGuestDisk struct {
// -drive id=MacDVD,if=none,snapshot=on,file=%s
type SGuestCdrom struct {
Id string
Path string
Id string
Path string
Ordinal int64
Ide *IDEDevice `json:",omitempty"`
Scsi *SCSIDevice `json:",omitempty"`
DriveOptions map[string]string `json:",omitempty"`
}
type SGuestFloppy struct {
Id string
Path string
Ordinal int64
Floppy *FloppyDevice `json:",omitempty"`
DriveOptions map[string]string `json:",omitempty"`
}
type SGuestNetwork struct {
api.GuestnetworkJsonDesc
Pci *PCIDevice `json:",omitempty"`

View File

@@ -129,6 +129,19 @@ type IDEDevice struct {
Options map[string]string `json:",omitempty"`
}
type FloppyAddr struct {
Bus uint
Controller uint
}
type FloppyDevice struct {
*FloppyAddr
Id string
DevType string
Options map[string]string `json:",omitempty"`
}
type Object struct {
ObjType string
Id string

View File

@@ -221,15 +221,16 @@ type SGuestDiskSyncTask struct {
guest *SKVMGuestInstance
delDisks []*desc.SGuestDisk
addDisks []*desc.SGuestDisk
cdrom *string
cdroms []*desc.SGuestCdrom
floppys []*desc.SGuestFloppy
errors []error
callback func(...error)
checkDrivers []string
}
func NewGuestDiskSyncTask(guest *SKVMGuestInstance, delDisks, addDisks []*desc.SGuestDisk, cdrom *string) *SGuestDiskSyncTask {
return &SGuestDiskSyncTask{guest, delDisks, addDisks, cdrom, make([]error, 0), nil, nil}
func NewGuestDiskSyncTask(guest *SKVMGuestInstance, delDisks, addDisks []*desc.SGuestDisk, cdroms []*desc.SGuestCdrom, floppys []*desc.SGuestFloppy) *SGuestDiskSyncTask {
return &SGuestDiskSyncTask{guest, delDisks, addDisks, cdroms, floppys, make([]error, 0), nil, nil}
}
func (d *SGuestDiskSyncTask) Start(callback func(...error)) {
@@ -250,9 +251,15 @@ func (d *SGuestDiskSyncTask) syncDisksConf() {
d.addDisk(disk)
return
}
if d.cdrom != nil {
d.changeCdrom()
return
if len(d.cdroms) > 0 {
cdrom := d.cdroms[len(d.cdroms)-1]
d.cdroms = d.cdroms[:len(d.cdroms)-1]
d.changeCdrom(cdrom)
}
if len(d.floppys) > 0 {
floppy := d.floppys[len(d.floppys)-1]
d.floppys = d.floppys[:len(d.floppys)-1]
d.changeFloppy(floppy)
}
if idxs := d.guest.GetNeedMergeBackingFileDiskIndexs(); len(idxs) > 0 {
d.guest.StreamDisks(context.Background(),
@@ -262,40 +269,113 @@ func (d *SGuestDiskSyncTask) syncDisksConf() {
d.callback(d.errors...)
}
func (d *SGuestDiskSyncTask) changeCdrom() {
d.guest.Monitor.GetBlocks(d.onGetBlockInfo)
func (d *SGuestDiskSyncTask) changeCdrom(cdrom *desc.SGuestCdrom) {
d.guest.Monitor.GetBlocks(func(blocks []monitor.QemuBlock) {
var cdName string
for _, r := range blocks {
if regexp.MustCompile(fmt.Sprintf(`^ide%d-cd\d+$`, cdrom.Ordinal)).MatchString(r.Device) {
cdName = r.Device
break
}
}
if len(cdName) > 0 {
d.changeCdromContent(cdName, cdrom)
} else {
cdrom.Path = ""
d.syncDisksConf()
}
})
}
func (d *SGuestDiskSyncTask) onGetBlockInfo(blocks []monitor.QemuBlock) {
var cdName string
for _, r := range blocks {
if regexp.MustCompile(`^ide\d+-cd\d+$`).MatchString(r.Device) {
cdName = r.Device
break
func (d *SGuestDiskSyncTask) changeCdromContent(cdName string, cdrom *desc.SGuestCdrom) {
if cdrom.Path == "" {
d.guest.Monitor.EjectCdrom(cdName, func(s string) {
d.OnEjectCdromContentSucc(cdName)
})
} else {
d.guest.Monitor.ChangeCdrom(cdName, cdrom.Path, func(s string) {
d.OnChangeCdromContentSucc(cdrom)
})
}
}
func (d *SGuestDiskSyncTask) OnEjectCdromContentSucc(cdName string) {
for i, cdrom := range d.guest.Desc.Cdroms {
if cdrom.Id == cdName {
d.guest.Desc.Cdroms[i].Path = ""
}
}
if len(cdName) > 0 {
d.changeCdromContent(cdName)
d.syncDisksConf()
}
func (d *SGuestDiskSyncTask) OnChangeCdromContentSucc(cdrom *desc.SGuestCdrom) {
if d.guest.Desc.Cdroms == nil {
d.guest.Desc.Cdroms = make([]*desc.SGuestCdrom, options.HostOptions.CdromCount)
for i := range d.guest.Desc.Cdroms {
d.guest.Desc.Cdroms[i] = new(desc.SGuestCdrom)
d.guest.Desc.Cdroms[i].Ordinal = int64(i)
}
}
for i := range d.guest.Desc.Cdroms {
if cdrom.Ordinal == d.guest.Desc.Cdroms[i].Ordinal {
d.guest.Desc.Cdroms[i] = cdrom
}
}
d.syncDisksConf()
}
func (d *SGuestDiskSyncTask) changeFloppy(floppy *desc.SGuestFloppy) {
d.guest.Monitor.GetBlocks(func(blocks []monitor.QemuBlock) {
var flName string
for _, r := range blocks {
if regexp.MustCompile(fmt.Sprintf(`^floppy%d$`, floppy.Ordinal)).MatchString(r.Device) {
flName = r.Device
break
}
}
if len(flName) > 0 {
d.changeFloppyContent(flName, floppy)
} else {
floppy.Path = ""
d.syncDisksConf()
}
})
}
func (d *SGuestDiskSyncTask) changeFloppyContent(flName string, floppy *desc.SGuestFloppy) {
if floppy.Path == "" {
d.guest.Monitor.EjectCdrom(flName, func(s string) {
d.OnEjectFloppyContentSucc(flName)
})
} else {
d.cdrom = nil
d.syncDisksConf()
d.guest.Monitor.ChangeCdrom(flName, floppy.Path, func(s string) {
d.OnChangeFloppyContentSucc(floppy)
})
}
}
func (d *SGuestDiskSyncTask) changeCdromContent(cdName string) {
if *d.cdrom == "" {
d.guest.Monitor.EjectCdrom(cdName, d.OnChangeCdromContentSucc)
} else {
d.guest.Monitor.ChangeCdrom(cdName, *d.cdrom, d.OnChangeCdromContentSucc)
func (d *SGuestDiskSyncTask) OnEjectFloppyContentSucc(flName string) {
for i, floppy := range d.guest.Desc.Floppys {
if floppy.Id == flName {
d.guest.Desc.Floppys[i].Path = ""
}
}
d.syncDisksConf()
}
func (d *SGuestDiskSyncTask) OnChangeCdromContentSucc(results string) {
if d.guest.Desc.Cdrom == nil {
d.guest.Desc.Cdrom = new(desc.SGuestCdrom)
func (d *SGuestDiskSyncTask) OnChangeFloppyContentSucc(floppy *desc.SGuestFloppy) {
if d.guest.Desc.Floppys == nil {
d.guest.Desc.Floppys = make([]*desc.SGuestFloppy, options.HostOptions.FloppyCount)
for i := range d.guest.Desc.Floppys {
d.guest.Desc.Floppys[i] = new(desc.SGuestFloppy)
d.guest.Desc.Floppys[i].Ordinal = int64(i)
}
}
for i := range d.guest.Desc.Floppys {
if floppy.Ordinal == d.guest.Desc.Floppys[i].Ordinal {
d.guest.Desc.Floppys[i] = floppy
}
}
d.guest.Desc.Cdrom.Path = *d.cdrom
d.cdrom = nil
d.syncDisksConf()
}

View File

@@ -72,6 +72,7 @@ func (s *SKVMGuestInstance) initGuestDesc() error {
s.initVirtioSerial(pciRoot)
s.initGuestVga(pciRoot)
s.initCdromDesc()
s.initFloppyDesc()
s.initGuestDisks(pciRoot, pciBridge)
if err = s.initGuestNetworks(pciRoot, pciBridge); err != nil {
return errors.Wrap(err, "init guest networks")
@@ -327,11 +328,32 @@ func (s *SKVMGuestInstance) initIsolatedDevices(pciRoot, pciBridge *desc.PCICont
}
func (s *SKVMGuestInstance) initCdromDesc() {
if s.Desc.Cdrom == nil {
s.Desc.Cdrom = new(desc.SGuestCdrom)
if s.Desc.Cdroms == nil {
s.Desc.Cdroms = make([]*desc.SGuestCdrom, options.HostOptions.CdromCount)
for i := range s.Desc.Cdroms {
s.Desc.Cdroms[i] = new(desc.SGuestCdrom)
s.Desc.Cdroms[i].Ordinal = int64(i)
}
}
for i := range s.Desc.Cdroms {
s.archMan.GenerateCdromDesc(s.getOsname(), s.Desc.Cdroms[i])
}
}
s.archMan.GenerateCdromDesc(s.getOsname(), s.Desc.Cdrom)
func (s *SKVMGuestInstance) initFloppyDesc() {
if s.getOsname() != OS_NAME_WINDOWS {
return
}
if s.Desc.Floppys == nil {
s.Desc.Floppys = make([]*desc.SGuestFloppy, options.HostOptions.FloppyCount)
for i := range s.Desc.Floppys {
s.Desc.Floppys[i] = new(desc.SGuestFloppy)
s.Desc.Floppys[i].Ordinal = int64(i)
}
}
for i := range s.Desc.Floppys {
s.archMan.GenerateFloppyDesc(s.getOsname(), s.Desc.Floppys[i])
}
}
func (s *SKVMGuestInstance) initGuestDisks(pciRoot, pciBridge *desc.PCIController) {

View File

@@ -1688,26 +1688,60 @@ func (s *SKVMGuestInstance) compareDescIsolatedDevices(newDesc *desc.SGuestDesc,
return delDevs, addDevs
}
func (s *SKVMGuestInstance) compareDescCdrom(newDesc *desc.SGuestDesc) *string {
newCdromIsNil := newDesc.Cdrom == nil || newDesc.Cdrom.Path == ""
oldCdromIsNil := s.Desc.Cdrom == nil || s.Desc.Cdrom.Path == ""
func (s *SKVMGuestInstance) compareDescCdroms(newDesc *desc.SGuestDesc) []*desc.SGuestCdrom {
var changeCdroms []*desc.SGuestCdrom
newCdroms := newDesc.Cdroms
for i := 0; i < options.HostOptions.CdromCount; i++ {
changeCdrom := new(desc.SGuestCdrom)
changeCdrom.Ordinal = int64(i)
changeCdrom.Path = ""
s.archMan.GenerateCdromDesc(s.getOsname(), changeCdrom)
changeCdroms = append(changeCdroms, changeCdrom)
}
if oldCdromIsNil && newCdromIsNil {
return nil
} else if oldCdromIsNil && !newCdromIsNil {
cdromPath := newDesc.Cdrom.Path
return &cdromPath
} else if !oldCdromIsNil && newCdromIsNil {
var res = ""
return &res
} else {
if s.Desc.Cdrom.Path == newDesc.Cdrom.Path {
return nil
} else {
cdromPath := newDesc.Cdrom.Path
return &cdromPath
for _, newCdrom := range newCdroms {
ordinal := newCdrom.Ordinal
path := newCdrom.Path
changeCdrom := new(desc.SGuestCdrom)
changeCdrom.Ordinal = ordinal
changeCdrom.Path = path
s.archMan.GenerateCdromDesc(s.getOsname(), changeCdrom)
for i, tmp := range changeCdroms {
if tmp.Ordinal == ordinal {
changeCdroms[i] = changeCdrom
}
}
}
return changeCdroms
}
func (s *SKVMGuestInstance) compareDescFloppys(newDesc *desc.SGuestDesc) []*desc.SGuestFloppy {
var changeFloppys []*desc.SGuestFloppy
newFloppys := newDesc.Floppys
for i := 0; i < options.HostOptions.FloppyCount; i++ {
changeFloppy := new(desc.SGuestFloppy)
changeFloppy.Ordinal = int64(i)
changeFloppy.Path = ""
s.archMan.GenerateFloppyDesc(s.getOsname(), changeFloppy)
changeFloppys = append(changeFloppys, changeFloppy)
}
for _, newFloppy := range newFloppys {
ordinal := newFloppy.Ordinal
path := newFloppy.Path
changeFloppy := new(desc.SGuestFloppy)
changeFloppy.Ordinal = ordinal
changeFloppy.Path = path
s.archMan.GenerateFloppyDesc(s.getOsname(), changeFloppy)
for i, tmp := range changeFloppys { //如果新增的跟changeCdrom重叠则以新增为准
if tmp.Ordinal == ordinal {
changeFloppys[i] = changeFloppy
}
}
}
return changeFloppys
}
func (s *SKVMGuestInstance) compareDescNetworks(newDesc *desc.SGuestDesc,
@@ -1824,7 +1858,8 @@ func (s *SKVMGuestInstance) SyncConfig(
var delNetworks, addNetworks []*desc.SGuestNetwork
var changedNetworks [][2]*desc.SGuestNetwork
var delDevs, addDevs []*desc.SGuestIsolatedDevice
var cdrom *string
var cdroms []*desc.SGuestCdrom
var floppys []*desc.SGuestFloppy
if err := s.SaveSourceDesc(guestDesc); err != nil {
return nil, err
@@ -1832,7 +1867,8 @@ func (s *SKVMGuestInstance) SyncConfig(
if !fwOnly && !s.isImportFromLibvirt() {
delDisks, addDisks = s.compareDescDisks(guestDesc)
cdrom = s.compareDescCdrom(guestDesc)
cdroms = s.compareDescCdroms(guestDesc)
floppys = s.compareDescFloppys(guestDesc)
delNetworks, addNetworks, changedNetworks = s.compareDescNetworks(guestDesc)
delDevs, addDevs = s.compareDescIsolatedDevices(guestDesc)
}
@@ -1868,8 +1904,8 @@ func (s *SKVMGuestInstance) SyncConfig(
var runTaskNames = []jsonutils.JSONObject{}
var tasks = []IGuestTasks{}
if len(delDisks)+len(addDisks) > 0 || cdrom != nil {
task := NewGuestDiskSyncTask(s, delDisks, addDisks, cdrom)
if len(delDisks)+len(addDisks) > 0 || cdroms != nil || floppys != nil {
task := NewGuestDiskSyncTask(s, delDisks, addDisks, cdroms, floppys)
runTaskNames = append(runTaskNames, jsonutils.NewString("disksync"))
tasks = append(tasks, task)
}

View File

@@ -301,32 +301,58 @@ func getDiskDeviceOption(optDrv QemuOptions, disk *desc.SGuestDisk) string {
return optDrv.Device(opt)
}
func generateCdromOptions(optDrv QemuOptions, cdrom *desc.SGuestCdrom) []string {
func generateCdromOptions(optDrv QemuOptions, cdroms []*desc.SGuestCdrom) []string {
opts := make([]string, 0)
//cdromDriveId := cdrom
driveOpt := fmt.Sprintf("id=%s", cdrom.Id)
driveOpt += optionsToString(cdrom.DriveOptions)
for _, cdrom := range cdroms {
//cdromDriveId := cdrom
driveOpt := fmt.Sprintf("id=%s", cdrom.Id)
driveOpt += optionsToString(cdrom.DriveOptions)
var cdromPath = cdrom.Path
if len(cdromPath) > 0 {
driveOpt += fmt.Sprintf(",file=%s", cdromPath)
}
if cdrom.Ide != nil {
opts = append(opts, optDrv.Drive(driveOpt))
devOpt := fmt.Sprintf("%s,drive=%s,bus=ide.1",
cdrom.Ide.DevType, cdrom.Id)
// TODO: ,bus=ide.%d,unit=%d
//, cdrom.Ide.Bus, cdrom.Ide.Unit)
opts = append(opts, optDrv.Device(devOpt))
} else if cdrom.Scsi != nil {
var cdromPath = cdrom.Path
if len(cdromPath) > 0 {
driveOpt += fmt.Sprintf(",file=%s", cdromPath)
}
if cdrom.Ide != nil {
opts = append(opts, optDrv.Drive(driveOpt))
devOpt := fmt.Sprintf("%s,drive=%s,bus=ide.1",
cdrom.Ide.DevType, cdrom.Id)
// TODO: ,bus=ide.%d,unit=%d
//, cdrom.Ide.Bus, cdrom.Ide.Unit)
opts = append(opts, optDrv.Device(devOpt))
} else if cdrom.Scsi != nil {
if len(cdromPath) > 0 {
opts = append(opts, optDrv.Drive(driveOpt))
devOpt := fmt.Sprintf("%s,drive=%s", cdrom.Scsi.DevType, cdrom.Id)
devOpt += optionsToString(cdrom.Scsi.Options)
devOpt := fmt.Sprintf("%s,drive=%s", cdrom.Scsi.DevType, cdrom.Id)
devOpt += optionsToString(cdrom.Scsi.Options)
opts = append(opts, optDrv.Device(devOpt))
}
}
}
return opts
}
func generateFloppyOptions(optDrv QemuOptions, floppys []*desc.SGuestFloppy) []string {
opts := make([]string, 0)
for _, floppy := range floppys {
driveOpt := fmt.Sprintf("id=%s", floppy.Id)
driveOpt += optionsToString(floppy.DriveOptions)
var floppyPath = floppy.Path
if len(floppyPath) > 0 {
driveOpt += fmt.Sprintf(",file=%s", floppyPath)
}
if floppy.Floppy != nil {
opts = append(opts, optDrv.Drive(driveOpt))
devOpt := fmt.Sprintf("%s,drive=%s",
floppy.Floppy.DevType, floppy.Id)
// TODO: ,bus=ide.%d,unit=%d
//, cdrom.Ide.Bus, cdrom.Ide.Unit)
opts = append(opts, optDrv.Device(devOpt))
}
}
@@ -585,9 +611,14 @@ func GenerateStartOptions(
// bootOrder
enableMenu := false
if input.GuestDesc.Cdrom != nil && input.GuestDesc.Cdrom.Path != "" {
enableMenu = true
if len(input.GuestDesc.Cdroms) > 0 {
for _, cdrom := range input.GuestDesc.Cdroms {
if cdrom.Path != "" {
enableMenu = true
}
}
}
opts = append(opts, drvOpt.Boot(input.GuestDesc.BootOrder, enableMenu))
// bios
@@ -641,7 +672,10 @@ func GenerateStartOptions(
opts = append(opts, generateDisksOptions(drvOpt, input.GuestDesc.Disks, isEncrypt)...)
// cdrom
opts = append(opts, generateCdromOptions(drvOpt, input.GuestDesc.Cdrom)...)
opts = append(opts, generateCdromOptions(drvOpt, input.GuestDesc.Cdroms)...)
//floppy
opts = append(opts, generateFloppyOptions(drvOpt, input.GuestDesc.Floppys)...)
// generate nics
nicOpts, err := generateNicOptions(drvOpt, input)

View File

@@ -31,6 +31,9 @@ type SHostBaseOptions struct {
HostCpuPassthrough bool `default:"true" help:"if it is true, set qemu cpu type as -cpu host, otherwise, qemu64. default is true"`
DefaultQemuVersion string `help:"Default qemu version" default:"4.2.0"`
CdromCount int `help:"cdrom count" default:"1"`
FloppyCount int `help:"floppy count" default:"1"`
}
type SHostOptions struct {