diff --git a/cmd/climc/shell/compute/servers.go b/cmd/climc/shell/compute/servers.go index 3347f6d75c..db8cd53fbc 100644 --- a/cmd/climc/shell/compute/servers.go +++ b/cmd/climc/shell/compute/servers.go @@ -160,6 +160,7 @@ func init() { cmd.Perform("add-network-secgroup", new(options.ServerNetworkSecGroupsOptions)) cmd.Perform("revoke-network-secgroup", new(options.ServerNetworkSecGroupsOptions)) cmd.Perform("change-disk-driver", new(options.ServerChangeDiskDriverOptions)) + cmd.Perform("set-iso", new(options.ServerSetIsoOptions)) cmd.GetProperty(&options.ServerStatusStatisticsOptions{}) cmd.GetProperty(&options.ServerProjectStatisticsOptions{}) diff --git a/pkg/apis/compute/api.go b/pkg/apis/compute/api.go index 1b6286c3f2..93332a5c0f 100644 --- a/pkg/apis/compute/api.go +++ b/pkg/apis/compute/api.go @@ -413,7 +413,10 @@ type ServerConfigs struct { // default: kvm Hypervisor string `json:"hypervisor"` - // swagger:ignore + // specific qemu version, eg: 4.2.0, 10.0.7 + QemuVersion string `json:"qemu_version"` + + // swagger: ignore Provider string `json:"provider"` // 包年包月资源池 diff --git a/pkg/apis/compute/guest_const.go b/pkg/apis/compute/guest_const.go index 2413ab74f1..705f1f2a8b 100644 --- a/pkg/apis/compute/guest_const.go +++ b/pkg/apis/compute/guest_const.go @@ -357,6 +357,8 @@ const ( VM_METADATA_ENABLE_TPM = "enable_tpm" + VM_METADATA_QEMU_VERSION = "qemu_version" + VM_METADATA_RELEASED_DEVICES = "released_devices" VM_METADATA_CPU_NUMA_PIN = "__cpu_numa_pin" diff --git a/pkg/apis/compute/guests.go b/pkg/apis/compute/guests.go index f7f115fd11..2d0bfb05c1 100644 --- a/pkg/apis/compute/guests.go +++ b/pkg/apis/compute/guests.go @@ -1337,6 +1337,12 @@ type ServerSetPasswordInput struct { AutoStart bool `json:"auto_start"` } +type ServerSetIsoInput struct { + CdromOrdinal int64 `json:"cdrom_ordinal"` + ImageId string `json:"image_id"` + BootIndex *int8 `json:"boot_index"` +} + type ServerInsertVfdInput struct { FloppyOrdinal int64 `json:"floppy_ordinal"` ImageId string `json:"image_id"` diff --git a/pkg/apis/image/consts.go b/pkg/apis/image/consts.go index 8cc504337d..709242fee1 100644 --- a/pkg/apis/image/consts.go +++ b/pkg/apis/image/consts.go @@ -78,6 +78,7 @@ const ( IMAGE_VDI_PROTOCOL = "vdi_protocol" IMAGE_INTERNAL_PATH_MAP = "internal_path_map" IMAGE_USED_BY_POST_OVERLAY = "used_by_post_overlay" + IMAGE_WIN_VIRTIO_NET = "win_virtio_net" IMAGE_STATUS_UPDATING = "updating" ) diff --git a/pkg/apis/scheduler/api.go b/pkg/apis/scheduler/api.go index 0f6f9ce36d..2598fbc858 100644 --- a/pkg/apis/scheduler/api.go +++ b/pkg/apis/scheduler/api.go @@ -89,6 +89,7 @@ type ScheduleInput struct { CpuMode string `json:"cpu_mode"` OsArch string `json:"os_arch"` ResetCpuNumaPin bool `json:"reset_cpu_numa_pin"` + QemuVersion string `json:"qemu_version"` ExtraCpuCount int `json:"extra_cpu_count"` CpuNumaPin []SCpuNumaPin `json:"cpu_numa_pin"` diff --git a/pkg/compute/models/guest_actions.go b/pkg/compute/models/guest_actions.go index 0c3ed138cb..f51c0b3e64 100644 --- a/pkg/compute/models/guest_actions.go +++ b/pkg/compute/models/guest_actions.go @@ -1519,6 +1519,56 @@ func (self *SGuest) GetDetailsKickstart(ctx context.Context, userCred mcclient.T return result, nil } +func (self *SGuest) PerformSetIso(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, input *api.ServerSetIsoInput) (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 !utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_READY}) { + return nil, httperrors.NewServerStatusError("Set ISO not allowed in status %s", self.Status) + } + if input.ImageId != "" { + isoImage, err := parseIsoInfo(ctx, userCred, input.ImageId) + if err != nil { + return nil, errors.Wrap(err, "parseIsoInfo") + } + input.ImageId = isoImage.Id + } + + cdrom := self.getCdrom(false, input.CdromOrdinal) + if input.BootIndex != nil { + bd8 := *input.BootIndex + if cdrom == nil || cdrom.BootIndex != bd8 { + if isDup, err := self.isBootIndexDuplicated(bd8); err != nil { + return nil, err + } else if isDup { + return nil, httperrors.NewInputParameterError("boot index %d is duplicated", bd8) + } + } + } + srcImage := "" + if cdrom != nil { + srcImage = cdrom.ImageId + } + if srcImage == "" && input.ImageId == "" { + return nil, nil + } else if srcImage != "" && input.ImageId != "" { + if srcImage == input.ImageId { + return nil, nil + } + // eject && insert + err := self.StartEjectisoTask(ctx, input.CdromOrdinal, userCred, input.BootIndex, input.ImageId, "") + return nil, err + } else if srcImage == "" && input.ImageId != "" { + // insert only + err := self.StartInsertIsoTask(ctx, input.CdromOrdinal, input.ImageId, false, input.BootIndex, self.HostId, userCred, "") + return nil, err + } else { + // eject only + err := self.StartEjectisoTask(ctx, input.CdromOrdinal, userCred, nil, "", "") + return nil, err + } +} + // 挂载ISO镜像 func (self *SGuest) PerformInsertiso(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { if !utils.IsInStringArray(self.Hypervisor, []string{api.HYPERVISOR_KVM, api.HYPERVISOR_BAREMETAL}) { @@ -1573,16 +1623,23 @@ func (self *SGuest) PerformEjectiso(ctx context.Context, userCred mcclient.Token return nil, httperrors.NewBadRequestError("No ISO to eject") } if utils.IsInStringArray(self.Status, []string{api.VM_RUNNING, api.VM_READY}) { - err := self.StartEjectisoTask(ctx, cdromOrdinal, userCred, "") + err := self.StartEjectisoTask(ctx, cdromOrdinal, userCred, nil, "", "") return nil, err } else { return nil, httperrors.NewServerStatusError("Eject ISO not allowed in status %s", self.Status) } } -func (self *SGuest) StartEjectisoTask(ctx context.Context, cdromOrdinal int64, userCred mcclient.TokenCredential, parentTaskId string) error { +func (self *SGuest) StartEjectisoTask(ctx context.Context, cdromOrdinal int64, userCred mcclient.TokenCredential, bootIndex *int8, newImageId, parentTaskId string) error { data := jsonutils.NewDict() data.Add(jsonutils.NewInt(cdromOrdinal), "cdrom_ordinal") + if newImageId != "" { + data.Add(jsonutils.NewString(newImageId), "new_image_id") + if bootIndex != nil { + data.Add(jsonutils.NewInt(int64(*bootIndex)), "boot_index") + } + } + task, err := taskman.TaskManager.NewTask(ctx, "GuestEjectISOTask", self, userCred, data, parentTaskId, "", nil) if err != nil { return err @@ -7397,6 +7454,14 @@ func (self *SGuest) PerformEnableMemclean(ctx context.Context, userCred mcclient return nil, self.SetMetadata(ctx, api.VM_METADATA_ENABLE_MEMCLEAN, "true", userCred) } +func (self *SGuest) PerformSetQemuVersion(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { + if data.Contains(api.VM_METADATA_QEMU_VERSION) { + qemuVersion, _ := data.GetString(api.VM_METADATA_QEMU_VERSION) + return nil, self.SetMetadata(ctx, api.VM_METADATA_QEMU_VERSION, qemuVersion, userCred) + } + return nil, self.SetMetadata(ctx, api.VM_METADATA_QEMU_VERSION, "", userCred) +} + func (self *SGuest) PerformSetTpm(ctx context.Context, userCred mcclient.TokenCredential, query jsonutils.JSONObject, data jsonutils.JSONObject) (jsonutils.JSONObject, error) { enableTpm := jsonutils.QueryBoolean(data, api.VM_METADATA_ENABLE_TPM, false) if enableTpm { diff --git a/pkg/compute/models/guests.go b/pkg/compute/models/guests.go index a9089f70ab..72968b0c3c 100644 --- a/pkg/compute/models/guests.go +++ b/pkg/compute/models/guests.go @@ -2081,6 +2081,13 @@ func (manager *SGuestManager) validateCreateData( osType = osProf.OSType input.OsType = osType } + if imgIsWindows { + netDriver := api.NETWORK_DRIVER_E1000 + if hasVirtioNetDrvier := imgProperties[imageapi.IMAGE_WIN_VIRTIO_NET] == "true"; hasVirtioNetDrvier { + netDriver = api.NETWORK_DRIVER_VIRTIO + } + osProf.NetDriver = netDriver + } input.OsProfile = jsonutils.Marshal(osProf) } @@ -2948,6 +2955,9 @@ func (guest *SGuest) PostCreate(ctx context.Context, userCred mcclient.TokenCred if matcherJson != nil { guest.SetMetadata(ctx, api.BAREMETAL_SERVER_METATA_ROOT_DISK_MATCHER, matcherJson, userCred) } + if qemuVersion, _ := data.GetString(api.VM_METADATA_QEMU_VERSION); qemuVersion != "" { + guest.SetMetadata(ctx, api.VM_METADATA_QEMU_VERSION, qemuVersion, userCred) + } userData, _ := data.GetString("user_data") if len(userData) > 0 { diff --git a/pkg/compute/tasks/guest/guest_eject_iso_task.go b/pkg/compute/tasks/guest/guest_eject_iso_task.go index 35e44a595f..ab93d56cee 100644 --- a/pkg/compute/tasks/guest/guest_eject_iso_task.go +++ b/pkg/compute/tasks/guest/guest_eject_iso_task.go @@ -41,25 +41,51 @@ func (self *GuestEjectISOTask) OnInit(ctx context.Context, obj db.IStandaloneMod func (self *GuestEjectISOTask) startEjectIso(ctx context.Context, obj db.IStandaloneModel) { guest := obj.(*models.SGuest) cdromOrdinal, _ := self.Params.Int("cdrom_ordinal") - if guest.EjectIso(cdromOrdinal, self.UserCred) && guest.Status == api.VM_RUNNING { - self.SetStage("OnConfigSyncComplete", nil) - drv, err := guest.GetDriver() - if err != nil { - self.SetStageFailed(ctx, jsonutils.NewString(err.Error())) - return + if guest.EjectIso(cdromOrdinal, self.UserCred) { + if guest.Status == api.VM_RUNNING { + self.SetStage("OnConfigSyncComplete", nil) + drv, err := guest.GetDriver() + if err != nil { + self.SetStageFailed(ctx, jsonutils.NewString(err.Error())) + return + } + drv.RequestGuestHotRemoveIso(ctx, guest, self) + } else { + self.OnConfigSyncComplete(ctx, obj, nil) } - drv.RequestGuestHotRemoveIso(ctx, guest, self) + } else { + self.OnConfigSyncCompleteFailed(ctx, obj, jsonutils.NewString("eject iso failed")) + } + +} + +func (self *GuestEjectISOTask) OnConfigSyncComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { + logclient.AddActionLogWithContext(ctx, obj, logclient.ACT_ISO_DETACH, nil, self.UserCred, true) + if newImageId, _ := self.Params.GetString("new_image_id"); newImageId != "" { + cdromOrdinal, _ := self.Params.Int("cdrom_ordinal") + var bootIndex *int8 + if data.Contains("boot_index") { + bootIndex64, _ := data.Int("boot_index") + bootIndex8 := int8(bootIndex64) + bootIndex = &bootIndex8 + } + guest := obj.(*models.SGuest) + self.SetStage("OnInsertNewIsoComplete", nil) + guest.StartInsertIsoTask(ctx, cdromOrdinal, newImageId, false, bootIndex, guest.HostId, self.UserCred, self.GetTaskId()) } else { self.SetStageComplete(ctx, nil) } } -func (self *GuestEjectISOTask) OnConfigSyncComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { - logclient.AddActionLogWithContext(ctx, obj, logclient.ACT_ISO_DETACH, nil, self.UserCred, true) +func (self *GuestEjectISOTask) OnConfigSyncCompleteFailed(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { + logclient.AddActionLogWithContext(ctx, obj, logclient.ACT_ISO_DETACH, nil, self.UserCred, false) + self.SetStageFailed(ctx, data) +} + +func (self *GuestEjectISOTask) OnInsertNewIsoComplete(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { self.SetStageComplete(ctx, nil) } -func (self *GuestEjectISOTask) OnConfigSyncCompleteFailed(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { - logclient.AddActionLogWithContext(ctx, obj, logclient.ACT_ISO_DETACH, nil, self.UserCred, false) - self.SetStageFailed(ctx, nil) +func (self *GuestEjectISOTask) OnInsertNewIsoCompleteFailed(ctx context.Context, obj db.IStandaloneModel, data jsonutils.JSONObject) { + self.SetStageFailed(ctx, data) } diff --git a/pkg/hostman/diskutils/fsutils/fsutils.go b/pkg/hostman/diskutils/fsutils/fsutils.go index 38e4b6b162..2d9466de31 100644 --- a/pkg/hostman/diskutils/fsutils/fsutils.go +++ b/pkg/hostman/diskutils/fsutils/fsutils.go @@ -774,11 +774,12 @@ func ProbeImageInfo(d deploy_iface.IDeployer) (*apis.ImageInfo, error) { } partition := rootfs.GetPartition() imageInfo := &apis.ImageInfo{ - OsInfo: rootfs.GetReleaseInfo(partition), - OsType: rootfs.GetOs(), - IsLvmPartition: d.IsLVMPartition(), - IsReadonly: partition.IsReadonly(), - IsInstalledCloudInit: rootfs.IsCloudinitInstall(), + OsInfo: rootfs.GetReleaseInfo(partition), + OsType: rootfs.GetOs(), + IsLvmPartition: d.IsLVMPartition(), + IsReadonly: partition.IsReadonly(), + IsInstalledCloudInit: rootfs.IsCloudinitInstall(), + IsWindowsVirtioNetSupport: rootfs.IsWindowsVirtioNetSupport(partition), } d.UmountRootfs(rootfs) @@ -787,6 +788,6 @@ func ProbeImageInfo(d deploy_iface.IDeployer) (*apis.ImageInfo, error) { imageInfo.IsUefiSupport = d.DetectIsUEFISupport(rootfs) imageInfo.PhysicalPartitionType = partition.GetPhysicalPartitionType() imageInfo.IsBiosSupport = d.DetectIsBIOSSupport(rootfs) - log.Infof("ProbeImageInfo response %s", imageInfo) + log.Infof("ProbeImageInfo response %#v", imageInfo) return imageInfo, nil } diff --git a/pkg/hostman/guestfs/fsdriver/base.go b/pkg/hostman/guestfs/fsdriver/base.go index 0080c0ffd9..ac1056a224 100644 --- a/pkg/hostman/guestfs/fsdriver/base.go +++ b/pkg/hostman/guestfs/fsdriver/base.go @@ -166,6 +166,10 @@ func (l *sGuestRootFsDriver) MountProcfs() bool { return false } +func (l *sGuestRootFsDriver) IsWindowsVirtioNetSupport(IDiskPartition) bool { + return false +} + const ( modeAuthorizedKeysRWX = syscall.S_IRUSR | syscall.S_IWUSR | syscall.S_IXUSR modeAuthorizedKeysRW = syscall.S_IRUSR | syscall.S_IWUSR diff --git a/pkg/hostman/guestfs/fsdriver/interface.go b/pkg/hostman/guestfs/fsdriver/interface.go index f0da37c57a..728588546b 100644 --- a/pkg/hostman/guestfs/fsdriver/interface.go +++ b/pkg/hostman/guestfs/fsdriver/interface.go @@ -96,6 +96,7 @@ type IRootFsDriver interface { IsCloudinitInstall() bool IsResizeFsPartitionSupport() bool MountProcfs() bool + IsWindowsVirtioNetSupport(part IDiskPartition) bool PrepareFsForTemplate(IDiskPartition) error CleanNetworkScripts(rootFs IDiskPartition) error diff --git a/pkg/hostman/guestfs/fsdriver/windows.go b/pkg/hostman/guestfs/fsdriver/windows.go index e2f979235c..330a015e4b 100644 --- a/pkg/hostman/guestfs/fsdriver/windows.go +++ b/pkg/hostman/guestfs/fsdriver/windows.go @@ -691,3 +691,10 @@ func (w *SWindowsRootFs) DeployTelegraf(config string) (bool, error) { func (w *SWindowsRootFs) ConfigSshd(loginAccount, loginPassword string, sshPort int) error { return nil } + +func (w *SWindowsRootFs) IsWindowsVirtioNetSupport(part IDiskPartition) bool { + confPath := part.GetLocalPath("/windows/system32/config", true) + tool := winutils.NewWinRegTool(confPath) + tool.CheckPath() + return tool.IsNetKVMInstalled() +} diff --git a/pkg/hostman/guestman/qemu-kvm.go b/pkg/hostman/guestman/qemu-kvm.go index 4b39668897..9653796627 100644 --- a/pkg/hostman/guestman/qemu-kvm.go +++ b/pkg/hostman/guestman/qemu-kvm.go @@ -2003,6 +2003,9 @@ func (s *SKVMGuestInstance) HandleGuestStart(ctx context.Context, userCred mccli func (s *SKVMGuestInstance) StartGuest(ctx context.Context, userCred mcclient.TokenCredential, params *jsonutils.JSONDict) error { var err error params, err = s.prepareEncryptKeyForStart(ctx, userCred, params) + if qemuVersion, ok := s.Desc.Metadata["qemu_version"]; ok && !params.Contains("qemu_version") { + params.Set("qemu_version", jsonutils.NewString(qemuVersion)) + } if err != nil { return errors.Wrap(err, "prepareEncryptKeyForStart") } diff --git a/pkg/hostman/hostdeployer/apis/deploy.pb.go b/pkg/hostman/hostdeployer/apis/deploy.pb.go index a7e6615c18..2b36375fb2 100644 --- a/pkg/hostman/hostdeployer/apis/deploy.pb.go +++ b/pkg/hostman/hostdeployer/apis/deploy.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.20.0 -// protoc v3.21.5 +// protoc v3.20.3 // source: deploy.proto // protoc --version=libprotoc 3.11.3 @@ -1828,14 +1828,15 @@ type ImageInfo struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - OsInfo *ReleaseInfo `protobuf:"bytes,1,opt,name=os_info,json=osInfo,proto3" json:"os_info,omitempty"` - OsType string `protobuf:"bytes,2,opt,name=os_type,json=osType,proto3" json:"os_type,omitempty"` - IsUefiSupport bool `protobuf:"varint,3,opt,name=is_uefi_support,json=isUefiSupport,proto3" json:"is_uefi_support,omitempty"` - IsLvmPartition bool `protobuf:"varint,4,opt,name=is_lvm_partition,json=isLvmPartition,proto3" json:"is_lvm_partition,omitempty"` - IsReadonly bool `protobuf:"varint,5,opt,name=is_readonly,json=isReadonly,proto3" json:"is_readonly,omitempty"` - PhysicalPartitionType string `protobuf:"bytes,6,opt,name=physical_partition_type,json=physicalPartitionType,proto3" json:"physical_partition_type,omitempty"` - IsInstalledCloudInit bool `protobuf:"varint,7,opt,name=is_installed_cloud_init,json=isInstalledCloudInit,proto3" json:"is_installed_cloud_init,omitempty"` - IsBiosSupport bool `protobuf:"varint,8,opt,name=is_bios_support,json=isBiosSupport,proto3" json:"is_bios_support,omitempty"` + OsInfo *ReleaseInfo `protobuf:"bytes,1,opt,name=os_info,json=osInfo,proto3" json:"os_info,omitempty"` + OsType string `protobuf:"bytes,2,opt,name=os_type,json=osType,proto3" json:"os_type,omitempty"` + IsUefiSupport bool `protobuf:"varint,3,opt,name=is_uefi_support,json=isUefiSupport,proto3" json:"is_uefi_support,omitempty"` + IsLvmPartition bool `protobuf:"varint,4,opt,name=is_lvm_partition,json=isLvmPartition,proto3" json:"is_lvm_partition,omitempty"` + IsReadonly bool `protobuf:"varint,5,opt,name=is_readonly,json=isReadonly,proto3" json:"is_readonly,omitempty"` + PhysicalPartitionType string `protobuf:"bytes,6,opt,name=physical_partition_type,json=physicalPartitionType,proto3" json:"physical_partition_type,omitempty"` + IsInstalledCloudInit bool `protobuf:"varint,7,opt,name=is_installed_cloud_init,json=isInstalledCloudInit,proto3" json:"is_installed_cloud_init,omitempty"` + IsBiosSupport bool `protobuf:"varint,8,opt,name=is_bios_support,json=isBiosSupport,proto3" json:"is_bios_support,omitempty"` + IsWindowsVirtioNetSupport bool `protobuf:"varint,9,opt,name=is_windows_virtio_net_support,json=isWindowsVirtioNetSupport,proto3" json:"is_windows_virtio_net_support,omitempty"` } func (x *ImageInfo) Reset() { @@ -1926,6 +1927,13 @@ func (x *ImageInfo) GetIsBiosSupport() bool { return false } +func (x *ImageInfo) GetIsWindowsVirtioNetSupport() bool { + if x != nil { + return x.IsWindowsVirtioNetSupport + } + return false +} + type EsxiDiskInfo struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2458,7 +2466,7 @@ var file_deploy_proto_rawDesc = []byte{ 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x50, 0x72, 0x61, 0x6d, 0x61, 0x73, 0x12, 0x2b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x44, 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, - 0x08, 0x64, 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0xda, 0x02, 0x0a, 0x09, 0x49, 0x6d, + 0x08, 0x64, 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x9c, 0x03, 0x0a, 0x09, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2a, 0x0a, 0x07, 0x6f, 0x73, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x52, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x6f, 0x73, 0x49, @@ -2480,71 +2488,75 @@ var file_deploy_proto_rawDesc = []byte{ 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x69, 0x73, 0x5f, 0x62, 0x69, 0x6f, 0x73, 0x5f, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x69, 0x73, 0x42, 0x69, 0x6f, 0x73, 0x53, - 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x22, 0x2b, 0x0a, 0x0c, 0x45, 0x73, 0x78, 0x69, 0x44, 0x69, - 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, 0x5f, 0x70, - 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x69, 0x73, 0x6b, 0x50, - 0x61, 0x74, 0x68, 0x22, 0x7d, 0x0a, 0x16, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x45, 0x73, - 0x78, 0x69, 0x44, 0x69, 0x73, 0x6b, 0x73, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x2e, 0x0a, - 0x09, 0x76, 0x64, 0x64, 0x6b, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x11, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x56, 0x44, 0x44, 0x4b, 0x43, 0x6f, 0x6e, 0x49, - 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x76, 0x64, 0x64, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x33, 0x0a, - 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x45, 0x73, 0x78, 0x69, 0x44, 0x69, - 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x49, 0x6e, - 0x66, 0x6f, 0x22, 0x43, 0x0a, 0x17, 0x45, 0x73, 0x78, 0x69, 0x44, 0x69, 0x73, 0x6b, 0x73, 0x43, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x28, 0x0a, - 0x05, 0x64, 0x69, 0x73, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x61, - 0x70, 0x69, 0x73, 0x2e, 0x45, 0x73, 0x78, 0x69, 0x44, 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x05, 0x64, 0x69, 0x73, 0x6b, 0x73, 0x22, 0x67, 0x0a, 0x0b, 0x42, 0x6f, 0x6f, 0x74, 0x44, - 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x42, 0x6f, 0x6f, 0x74, 0x4f, 0x72, - 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x42, 0x6f, 0x6f, 0x74, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x44, 0x65, 0x76, 0x54, 0x79, 0x70, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x44, 0x65, 0x76, 0x54, 0x79, 0x70, 0x65, 0x12, 0x20, - 0x0a, 0x0b, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x0b, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, - 0x22, 0x60, 0x0a, 0x13, 0x4f, 0x76, 0x6d, 0x66, 0x42, 0x6f, 0x6f, 0x74, 0x4f, 0x72, 0x64, 0x65, - 0x72, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x4f, 0x76, 0x6d, 0x66, 0x56, - 0x61, 0x72, 0x73, 0x50, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x4f, - 0x76, 0x6d, 0x66, 0x56, 0x61, 0x72, 0x73, 0x50, 0x61, 0x74, 0x68, 0x12, 0x25, 0x0a, 0x04, 0x64, - 0x65, 0x76, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x61, 0x70, 0x69, 0x73, - 0x2e, 0x42, 0x6f, 0x6f, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x04, 0x64, 0x65, - 0x76, 0x73, 0x32, 0x82, 0x04, 0x0a, 0x0b, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x41, 0x67, 0x65, - 0x6e, 0x74, 0x12, 0x40, 0x0a, 0x0d, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x47, 0x75, 0x65, 0x73, - 0x74, 0x46, 0x73, 0x12, 0x12, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, - 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x44, - 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x47, 0x75, 0x65, 0x73, 0x74, 0x46, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x69, 0x7a, 0x65, 0x46, 0x73, - 0x12, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x52, 0x65, 0x73, 0x69, 0x7a, 0x65, 0x46, 0x73, - 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x0b, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x45, 0x6d, - 0x70, 0x74, 0x79, 0x12, 0x2d, 0x0a, 0x08, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x46, 0x73, 0x12, - 0x14, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x46, 0x73, 0x50, - 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x0b, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x12, 0x44, 0x0a, 0x0c, 0x53, 0x61, 0x76, 0x65, 0x54, 0x6f, 0x47, 0x6c, 0x61, 0x6e, - 0x63, 0x65, 0x12, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x54, 0x6f, - 0x47, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x1a, 0x2e, 0x61, - 0x70, 0x69, 0x73, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x54, 0x6f, 0x47, 0x6c, 0x61, 0x6e, 0x63, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x62, - 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1a, 0x2e, 0x61, 0x70, 0x69, - 0x73, 0x2e, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, - 0x50, 0x72, 0x61, 0x6d, 0x61, 0x73, 0x1a, 0x0f, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x49, 0x6d, - 0x61, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x4f, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x6e, 0x65, - 0x63, 0x74, 0x45, 0x73, 0x78, 0x69, 0x44, 0x69, 0x73, 0x6b, 0x73, 0x12, 0x1c, 0x2e, 0x61, 0x70, - 0x69, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x45, 0x73, 0x78, 0x69, 0x44, 0x69, - 0x73, 0x6b, 0x73, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x73, - 0x2e, 0x45, 0x73, 0x78, 0x69, 0x44, 0x69, 0x73, 0x6b, 0x73, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x41, 0x0a, 0x13, 0x44, 0x69, 0x73, 0x63, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x45, 0x73, 0x78, 0x69, 0x44, 0x69, 0x73, 0x6b, 0x73, 0x12, - 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x45, 0x73, 0x78, 0x69, 0x44, 0x69, 0x73, 0x6b, 0x73, - 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x1a, 0x0b, - 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3a, 0x0a, 0x10, 0x53, - 0x65, 0x74, 0x4f, 0x76, 0x6d, 0x66, 0x42, 0x6f, 0x6f, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, - 0x19, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x4f, 0x76, 0x6d, 0x66, 0x42, 0x6f, 0x6f, 0x74, 0x4f, - 0x72, 0x64, 0x65, 0x72, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x0b, 0x2e, 0x61, 0x70, 0x69, - 0x73, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x34, 0x5a, 0x32, 0x79, 0x75, 0x6e, 0x69, 0x6f, - 0x6e, 0x2e, 0x69, 0x6f, 0x2f, 0x78, 0x2f, 0x6f, 0x6e, 0x65, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, - 0x70, 0x6b, 0x67, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x6d, 0x61, 0x6e, 0x2f, 0x68, 0x6f, 0x73, 0x74, - 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x73, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x40, 0x0a, 0x1d, 0x69, 0x73, 0x5f, 0x77, 0x69, 0x6e, + 0x64, 0x6f, 0x77, 0x73, 0x5f, 0x76, 0x69, 0x72, 0x74, 0x69, 0x6f, 0x5f, 0x6e, 0x65, 0x74, 0x5f, + 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x19, 0x69, + 0x73, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x56, 0x69, 0x72, 0x74, 0x69, 0x6f, 0x4e, 0x65, + 0x74, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x22, 0x2b, 0x0a, 0x0c, 0x45, 0x73, 0x78, 0x69, + 0x44, 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x69, 0x73, 0x6b, + 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x69, 0x73, + 0x6b, 0x50, 0x61, 0x74, 0x68, 0x22, 0x7d, 0x0a, 0x16, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x45, 0x73, 0x78, 0x69, 0x44, 0x69, 0x73, 0x6b, 0x73, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, + 0x2e, 0x0a, 0x09, 0x76, 0x64, 0x64, 0x6b, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x56, 0x44, 0x44, 0x4b, 0x43, 0x6f, + 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x76, 0x64, 0x64, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, + 0x33, 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x45, 0x73, 0x78, 0x69, + 0x44, 0x69, 0x73, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x43, 0x0a, 0x17, 0x45, 0x73, 0x78, 0x69, 0x44, 0x69, 0x73, 0x6b, + 0x73, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, + 0x28, 0x0a, 0x05, 0x64, 0x69, 0x73, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, + 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x45, 0x73, 0x78, 0x69, 0x44, 0x69, 0x73, 0x6b, 0x49, 0x6e, + 0x66, 0x6f, 0x52, 0x05, 0x64, 0x69, 0x73, 0x6b, 0x73, 0x22, 0x67, 0x0a, 0x0b, 0x42, 0x6f, 0x6f, + 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x42, 0x6f, 0x6f, 0x74, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x42, 0x6f, 0x6f, + 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x44, 0x65, 0x76, 0x54, 0x79, 0x70, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x44, 0x65, 0x76, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x20, 0x0a, 0x0b, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x4f, 0x72, 0x64, + 0x65, 0x72, 0x22, 0x60, 0x0a, 0x13, 0x4f, 0x76, 0x6d, 0x66, 0x42, 0x6f, 0x6f, 0x74, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x4f, 0x76, 0x6d, + 0x66, 0x56, 0x61, 0x72, 0x73, 0x50, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0c, 0x4f, 0x76, 0x6d, 0x66, 0x56, 0x61, 0x72, 0x73, 0x50, 0x61, 0x74, 0x68, 0x12, 0x25, 0x0a, + 0x04, 0x64, 0x65, 0x76, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x61, 0x70, + 0x69, 0x73, 0x2e, 0x42, 0x6f, 0x6f, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x04, + 0x64, 0x65, 0x76, 0x73, 0x32, 0x82, 0x04, 0x0a, 0x0b, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x41, + 0x67, 0x65, 0x6e, 0x74, 0x12, 0x40, 0x0a, 0x0d, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x47, 0x75, + 0x65, 0x73, 0x74, 0x46, 0x73, 0x12, 0x12, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x44, 0x65, 0x70, + 0x6c, 0x6f, 0x79, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x73, + 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x47, 0x75, 0x65, 0x73, 0x74, 0x46, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x69, 0x7a, 0x65, + 0x46, 0x73, 0x12, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x52, 0x65, 0x73, 0x69, 0x7a, 0x65, + 0x46, 0x73, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x0b, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x2d, 0x0a, 0x08, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x46, + 0x73, 0x12, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x46, + 0x73, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x0b, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x45, + 0x6d, 0x70, 0x74, 0x79, 0x12, 0x44, 0x0a, 0x0c, 0x53, 0x61, 0x76, 0x65, 0x54, 0x6f, 0x47, 0x6c, + 0x61, 0x6e, 0x63, 0x65, 0x12, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x53, 0x61, 0x76, 0x65, + 0x54, 0x6f, 0x47, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x1a, + 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x53, 0x61, 0x76, 0x65, 0x54, 0x6f, 0x47, 0x6c, 0x61, 0x6e, + 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3d, 0x0a, 0x0e, 0x50, 0x72, + 0x6f, 0x62, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1a, 0x2e, 0x61, + 0x70, 0x69, 0x73, 0x2e, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x49, 0x6e, + 0x66, 0x6f, 0x50, 0x72, 0x61, 0x6d, 0x61, 0x73, 0x1a, 0x0f, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, + 0x49, 0x6d, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x4f, 0x0a, 0x10, 0x43, 0x6f, 0x6e, + 0x6e, 0x65, 0x63, 0x74, 0x45, 0x73, 0x78, 0x69, 0x44, 0x69, 0x73, 0x6b, 0x73, 0x12, 0x1c, 0x2e, + 0x61, 0x70, 0x69, 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x45, 0x73, 0x78, 0x69, + 0x44, 0x69, 0x73, 0x6b, 0x73, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x1d, 0x2e, 0x61, 0x70, + 0x69, 0x73, 0x2e, 0x45, 0x73, 0x78, 0x69, 0x44, 0x69, 0x73, 0x6b, 0x73, 0x43, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x41, 0x0a, 0x13, 0x44, 0x69, + 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x45, 0x73, 0x78, 0x69, 0x44, 0x69, 0x73, 0x6b, + 0x73, 0x12, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x45, 0x73, 0x78, 0x69, 0x44, 0x69, 0x73, + 0x6b, 0x73, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, + 0x1a, 0x0b, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3a, 0x0a, + 0x10, 0x53, 0x65, 0x74, 0x4f, 0x76, 0x6d, 0x66, 0x42, 0x6f, 0x6f, 0x74, 0x4f, 0x72, 0x64, 0x65, + 0x72, 0x12, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x4f, 0x76, 0x6d, 0x66, 0x42, 0x6f, 0x6f, + 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x0b, 0x2e, 0x61, + 0x70, 0x69, 0x73, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x34, 0x5a, 0x32, 0x79, 0x75, 0x6e, + 0x69, 0x6f, 0x6e, 0x2e, 0x69, 0x6f, 0x2f, 0x78, 0x2f, 0x6f, 0x6e, 0x65, 0x63, 0x6c, 0x6f, 0x75, + 0x64, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x6d, 0x61, 0x6e, 0x2f, 0x68, 0x6f, + 0x73, 0x74, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x65, 0x72, 0x2f, 0x61, 0x70, 0x69, 0x73, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/pkg/hostman/hostdeployer/apis/deploy.proto b/pkg/hostman/hostdeployer/apis/deploy.proto index 4c7a73239f..f20c3a37e2 100644 --- a/pkg/hostman/hostdeployer/apis/deploy.proto +++ b/pkg/hostman/hostdeployer/apis/deploy.proto @@ -203,6 +203,7 @@ message ImageInfo { string physical_partition_type = 6; bool is_installed_cloud_init = 7; bool is_bios_support = 8; + bool is_windows_virtio_net_support = 9; } message EsxiDiskInfo { diff --git a/pkg/hostman/hostinfo/hostinfo.go b/pkg/hostman/hostinfo/hostinfo.go index afdb54f0e3..bb716381d3 100644 --- a/pkg/hostman/hostinfo/hostinfo.go +++ b/pkg/hostman/hostinfo/hostinfo.go @@ -977,6 +977,10 @@ func (h *SHostInfo) detectSyssoftwareInfo() error { log.Errorf("detect qemu version: %s", err.Error()) h.AppendHostError(fmt.Sprintf("detect qemu version: %s", err.Error())) } + h.sysinfo.QemuVersions = qemutils.GetUsrLocalQemuVersions() + if !utils.IsInStringArray(h.sysinfo.QemuVersion, h.sysinfo.QemuVersions) { + h.sysinfo.QemuVersions = append(h.sysinfo.QemuVersions, h.sysinfo.QemuVersion) + } } h.detectOvsVersion() if err := h.detectOvsKOVersion(); err != nil { diff --git a/pkg/hostman/hostinfo/hostinfohelper.go b/pkg/hostman/hostinfo/hostinfohelper.go index bf739f2dd7..4c2f1e38d1 100644 --- a/pkg/hostman/hostinfo/hostinfohelper.go +++ b/pkg/hostman/hostinfo/hostinfohelper.go @@ -424,6 +424,8 @@ type SSysInfo struct { CpuMicrocode string `json:"cpu_microcode"` CgroupVersion string `json:"cgroup_version"` + QemuVersions []string `json:"qemu_versions"` + StorageType string `json:"storage_type"` HugepagesOption string `json:"hugepages_option"` diff --git a/pkg/image/models/images.go b/pkg/image/models/images.go index a7f1368885..90c14d4a86 100644 --- a/pkg/image/models/images.go +++ b/pkg/image/models/images.go @@ -1850,6 +1850,9 @@ func (image *SImage) updateImageInfo( imageProperties.Set(api.IMAGE_IS_LVM_PARTITION, jsonutils.NewBool(imageInfo.IsLvmPartition)) imageProperties.Set(api.IMAGE_IS_READONLY, jsonutils.NewBool(imageInfo.IsReadonly)) imageProperties.Set(api.IMAGE_INSTALLED_CLOUDINIT, jsonutils.NewBool(imageInfo.IsInstalledCloudInit)) + if imageInfo.IsWindowsVirtioNetSupport { + imageProperties.Set(api.IMAGE_WIN_VIRTIO_NET, jsonutils.JSONTrue) + } return ImagePropertyManager.SaveProperties(ctx, userCred, image.Id, imageProperties) } diff --git a/pkg/mcclient/options/compute/servers.go b/pkg/mcclient/options/compute/servers.go index 1cda6152cb..7a613e90d4 100644 --- a/pkg/mcclient/options/compute/servers.go +++ b/pkg/mcclient/options/compute/servers.go @@ -400,6 +400,7 @@ type ServerConfigs struct { BackupHost string `help:"Preferred host where virtual backup server should be created"` AutoSwitchToBackupOnHostDown bool `help:"Auto switch to backup server on host down"` Daemon *bool `help:"Set as a daemon server" json:"is_daemon"` + QemuVersion string `help:"specific server guest start version" json:"qemu_version"` RaidConfig []string `help:"Baremetal raid config" json:"-"` RootDiskMatcher string `help:"Baremetal root disk matcher, e.g. 'device=/dev/sdb' 'size=900G' 'size_start=800G,size_end=900G'" json:"-"` @@ -414,6 +415,7 @@ func (o ServerConfigs) Data() (*computeapi.ServerConfigs, error) { data.PreferBackupHost = o.BackupHost data.IsDaemon = o.Daemon data.Hypervisor = o.Hypervisor + data.QemuVersion = o.QemuVersion if len(o.RaidConfig) > 0 { // if data.Hypervisor != "baremetal" { // return nil, fmt.Errorf("RaidConfig is applicable to baremetal ONLY") @@ -1728,6 +1730,18 @@ func (o *ServerSetNetworkNumQueues) Params() (jsonutils.JSONObject, error) { return jsonutils.Marshal(o), nil } +type ServerSetIsoOptions struct { + ServerIdOptions + + CDROM_ORDINAL int64 `json:"cdrom_ordinal" help:"cdrom ordinal"` + ImageId string `help:"Iso image id, eject on image id empty"` + BootIndex *int8 `help:"Iso boot index"` +} + +func (o *ServerSetIsoOptions) Params() (jsonutils.JSONObject, error) { + return jsonutils.Marshal(o), nil +} + type ServerIsoOptions struct { ServerIdOptions Ordinal int `help:"server iso ordinal, default 0"` diff --git a/pkg/scheduler/algorithm/predicates/error.go b/pkg/scheduler/algorithm/predicates/error.go index 5a9cb156c1..ec46c1c9e5 100644 --- a/pkg/scheduler/algorithm/predicates/error.go +++ b/pkg/scheduler/algorithm/predicates/error.go @@ -46,6 +46,7 @@ const ( ErrPrepaidHostOccupied = `prepaid host occupied` ErrHostCpuArchitectureNotMatch = `host cpu architecture not match` ErrHostKvmVcpuMaxNotEnough = `host kvm vcpu max not enough` + ErrHostQemuVersionNotMatch = `host qemu version not match` ErrUnknown = `unknown error` ) diff --git a/pkg/scheduler/algorithm/predicates/guest/hypervisor_predicate.go b/pkg/scheduler/algorithm/predicates/guest/hypervisor_predicate.go index 6842ef169f..5d45ca64db 100644 --- a/pkg/scheduler/algorithm/predicates/guest/hypervisor_predicate.go +++ b/pkg/scheduler/algorithm/predicates/guest/hypervisor_predicate.go @@ -18,6 +18,7 @@ import ( "context" "yunion.io/x/log" + "yunion.io/x/pkg/utils" computeapi "yunion.io/x/onecloud/pkg/apis/compute" "yunion.io/x/onecloud/pkg/scheduler/algorithm/predicates" @@ -71,7 +72,17 @@ func (f *HypervisorPredicate) Execute(ctx context.Context, u *core.Unit, c core. hostType := c.Getter().HostType() guestNeedType := u.SchedData().Hypervisor - + if hostType == computeapi.HOST_TYPE_HYPERVISOR { + if u.SchedData().QemuVersion != "" { + host := c.Getter().Host() + qemuVersions := make([]string, 0) + host.SysInfo.Unmarshal(&qemuVersions, "qemu_versions") + if !utils.IsInStringArray(u.SchedData().QemuVersion, qemuVersions) { + h.Exclude(predicates.ErrHostQemuVersionNotMatch) + return h.GetResult() + } + } + } if guestNeedType != hostType { if guestNeedType == computeapi.HYPERVISOR_POD && hostAllowRunContainer(c) { return h.GetResult() diff --git a/pkg/util/qemutils/qemutils.go b/pkg/util/qemutils/qemutils.go index 7d12e63a0a..ad64fb28b0 100644 --- a/pkg/util/qemutils/qemutils.go +++ b/pkg/util/qemutils/qemutils.go @@ -47,6 +47,10 @@ func GetQemu(version string) string { return getQemuCmd(qemuSystemCmd, version) } +func GetUsrLocalQemuVersions() []string { + return getUsrLocalQemuVersions(qemuSystemCmd) +} + func GetQemuNbd() string { return getQemuCmd("qemu-nbd", "") } @@ -108,6 +112,25 @@ func getCmdVersion(cmd string) string { return s[1:] } +func getUsrLocalQemuVersions(cmd string) []string { + var qemus = make([]string, 0) + if files, err := procutils.RemoteReadDir("/usr/local"); err == nil { + for i := 0; i < len(files); i++ { + if strings.HasPrefix(files[i].Name(), "qemu-") { + p := fmt.Sprintf("/usr/local/%s/bin/%s", files[i].Name(), cmd) + if _, err := procutils.RemoteStat(p); err == nil { + qemuVersion := getQemuVersion(files[i].Name()) + if qemuVersion == "" { + continue + } + qemus = append(qemus, qemuVersion) + } + } + } + } + return qemus +} + func getQemuDefaultCmd(cmd string) string { var qemus = make([]string, 0) if files, err := procutils.RemoteReadDir("/usr/local"); err == nil { diff --git a/pkg/util/winutils/winutils.go b/pkg/util/winutils/winutils.go index 55d5432edf..6497222b35 100644 --- a/pkg/util/winutils/winutils.go +++ b/pkg/util/winutils/winutils.go @@ -774,3 +774,20 @@ func (w *SWinRegTool) ResetUSBProfile() { key = w.GetCcsKeyPath() + `\Enum\USB` w.DelRegistry(key) } + +func (w *SWinRegTool) IsNetKVMInstalled() bool { + key := w.GetCcsKeyPath() + `\Services\netkvm` + + if !w.KeyExists(key) { + log.Errorf("key %s not exist", key) + return false + } + + imagePath := w.GetRegistry(key + `\ImagePath`) + if imagePath == "" { + log.Errorf("image path is empty") + return false + } + + return true +}