diff --git a/build/docker/Dockerfile.host-deployer b/build/docker/Dockerfile.host-deployer index 1fecd601c7..150ca07cca 100644 --- a/build/docker/Dockerfile.host-deployer +++ b/build/docker/Dockerfile.host-deployer @@ -1,4 +1,4 @@ -FROM registry.cn-beijing.aliyuncs.com/yunionio/host-deployer-base:1.4.9 +FROM registry.cn-beijing.aliyuncs.com/yunionio/host-deployer-base:1.4.10 MAINTAINER "Yaoqi Wan wanyaoqi@yunionyun.com" diff --git a/cmd/container-examples/create_steam.go b/cmd/container-examples/create_steam.go index e6a528790d..81b7c73102 100644 --- a/cmd/container-examples/create_steam.go +++ b/cmd/container-examples/create_steam.go @@ -1,3 +1,17 @@ +// 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 main import ( diff --git a/pkg/apis/cloudprovider/doc.go b/pkg/apis/cloudprovider/doc.go index c6f37fc532..b617c8ad6d 100644 --- a/pkg/apis/cloudprovider/doc.go +++ b/pkg/apis/cloudprovider/doc.go @@ -1,3 +1,17 @@ +// 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. + //go:build !ignore_autogenerated // +build !ignore_autogenerated diff --git a/pkg/apis/compute/guest_const.go b/pkg/apis/compute/guest_const.go index 1a8bd3d7a3..c6376a607d 100644 --- a/pkg/apis/compute/guest_const.go +++ b/pkg/apis/compute/guest_const.go @@ -261,6 +261,9 @@ const ( VM_VIDEO_STANDARD = "std" VM_VIDEO_QXL = "qxl" VM_VIDEO_VIRTIO = "virtio" + + VM_BOOT_MODE_BIOS = "BIOS" + VM_BOOT_MODE_UEFI = "UEFI" ) var VM_RUNNING_STATUS = []string{VM_START_START, VM_STARTING, VM_RUNNING, VM_BLOCK_STREAM, VM_BLOCK_STREAM_FAIL} diff --git a/pkg/compute/guestdrivers/kvm.go b/pkg/compute/guestdrivers/kvm.go index 63bf466a7c..ed6d4b3860 100644 --- a/pkg/compute/guestdrivers/kvm.go +++ b/pkg/compute/guestdrivers/kvm.go @@ -666,6 +666,9 @@ func (self *SKVMGuestDriver) RequestSyncConfigOnHost(ctx context.Context, guest if fw_only, _ := task.GetParams().Bool("fw_only"); fw_only { body.Add(jsonutils.JSONTrue, "fw_only") } + if setUefiBootOrder, _ := task.GetParams().Bool("set_uefi_boot_order"); setUefiBootOrder { + body.Add(jsonutils.JSONTrue, "set_uefi_boot_order") + } url := fmt.Sprintf("%s/servers/%s/sync", host.ManagerUri, guest.Id) header := self.getTaskRequestHeader(task) _, _, err = httputils.JSONRequest(httputils.GetDefaultClient(), ctx, "POST", url, header, body, false) diff --git a/pkg/compute/models/guest_actions.go b/pkg/compute/models/guest_actions.go index 499160ac31..290e19e72d 100644 --- a/pkg/compute/models/guest_actions.go +++ b/pkg/compute/models/guest_actions.go @@ -6394,6 +6394,14 @@ func (self *SGuest) PerformSetBootIndex(ctx context.Context, userCred mcclient.T } } + if self.Bios == api.VM_BOOT_MODE_UEFI { + data := jsonutils.NewDict() + data.Set("set_uefi_boot_order", jsonutils.JSONTrue) + if err := self.startSyncTask(ctx, userCred, false, "", data); err != nil { + return nil, err + } + } + return nil, nil } diff --git a/pkg/hostman/guestman/guestman.go b/pkg/hostman/guestman/guestman.go index b930d03a59..9d30566976 100644 --- a/pkg/hostman/guestman/guestman.go +++ b/pkg/hostman/guestman/guestman.go @@ -1072,7 +1072,8 @@ func (m *SGuestManager) GuestSync(ctx context.Context, params interface{}) (json } fwOnly := jsonutils.QueryBoolean(syncParams.Body, "fw_only", false) - return guest.SyncConfig(ctx, guestDesc, fwOnly) + setUefiBootOrder := jsonutils.QueryBoolean(syncParams.Body, "set_uefi_boot_order", false) + return guest.SyncConfig(ctx, guestDesc, fwOnly, setUefiBootOrder) } return nil, nil } diff --git a/pkg/hostman/guestman/pod.go b/pkg/hostman/guestman/pod.go index a1e1663c6d..6aa4f50347 100644 --- a/pkg/hostman/guestman/pod.go +++ b/pkg/hostman/guestman/pod.go @@ -1069,7 +1069,7 @@ func (s *sPodGuestInstance) PostLoad(m *SGuestManager) error { return LoadGuestCpuset(m, s) } -func (s *sPodGuestInstance) SyncConfig(ctx context.Context, guestDesc *desc.SGuestDesc, fwOnly bool) (jsonutils.JSONObject, error) { +func (s *sPodGuestInstance) SyncConfig(ctx context.Context, guestDesc *desc.SGuestDesc, fwOnly, setUefiBootOrder bool) (jsonutils.JSONObject, error) { if err := SaveDesc(s, guestDesc); err != nil { return nil, errors.Wrap(err, "SaveDesc") } diff --git a/pkg/hostman/guestman/qemu-kvm.go b/pkg/hostman/guestman/qemu-kvm.go index c2097044e3..f04f46b055 100644 --- a/pkg/hostman/guestman/qemu-kvm.go +++ b/pkg/hostman/guestman/qemu-kvm.go @@ -2520,9 +2520,7 @@ func (s *SKVMGuestInstance) onNicChange(oldNic, newNic *desc.SGuestNetwork) erro return nil } -func (s *SKVMGuestInstance) SyncConfig( - ctx context.Context, guestDesc *desc.SGuestDesc, fwOnly bool, -) (jsonutils.JSONObject, error) { +func (s *SKVMGuestInstance) SyncConfig(ctx context.Context, guestDesc *desc.SGuestDesc, fwOnly, setUefiBootOrder bool) (jsonutils.JSONObject, error) { var delDisks, addDisks []*desc.SGuestDisk var delNetworks, addNetworks []*desc.SGuestNetwork var changedNetworks [][2]*desc.SGuestNetwork @@ -2553,6 +2551,12 @@ func (s *SKVMGuestInstance) SyncConfig( } if !s.IsRunning() { + if setUefiBootOrder && s.getBios() == api.VM_BOOT_MODE_UEFI { + if err := s.setUefiBootOrder(ctx); err != nil { + log.Errorf("failed set uefi boot order %s", err) + return nil, errors.Wrap(err, "setUefiBootOrder") + } + } return nil, nil } @@ -2602,6 +2606,13 @@ func (s *SKVMGuestInstance) SyncConfig( } } + if setUefiBootOrder && s.getBios() == api.VM_BOOT_MODE_UEFI { + if err := s.setUefiBootOrder(ctx); err != nil { + log.Errorf("failed set uefi boot order %s", err) + errs = append(errs, err) + } + } + if len(errs) == 0 { hostutils.TaskComplete(ctx, nil) } else { diff --git a/pkg/hostman/guestman/qemu-kvmhelper.go b/pkg/hostman/guestman/qemu-kvmhelper.go index ebfeaf02a5..cdacb27132 100644 --- a/pkg/hostman/guestman/qemu-kvmhelper.go +++ b/pkg/hostman/guestman/qemu-kvmhelper.go @@ -15,6 +15,7 @@ package guestman import ( + "context" "fmt" "net" "path" @@ -36,6 +37,9 @@ import ( "yunion.io/x/onecloud/pkg/hostman/guestman/desc" "yunion.io/x/onecloud/pkg/hostman/guestman/qemu" qemucerts "yunion.io/x/onecloud/pkg/hostman/guestman/qemu/certs" + deployapi "yunion.io/x/onecloud/pkg/hostman/hostdeployer/apis" + "yunion.io/x/onecloud/pkg/hostman/hostdeployer/deployclient" + "yunion.io/x/onecloud/pkg/hostman/hostdeployer/uefi" "yunion.io/x/onecloud/pkg/hostman/monitor" "yunion.io/x/onecloud/pkg/hostman/options" "yunion.io/x/onecloud/pkg/hostman/storageman" @@ -244,7 +248,7 @@ func (s *SKVMGuestInstance) getMachine() string { func (s *SKVMGuestInstance) getBios() string { bios := s.Desc.Bios if bios == "" { - bios = "bios" + bios = api.VM_BOOT_MODE_BIOS } return bios } @@ -501,6 +505,7 @@ function nic_mtu() { if s.Desc.Bios == qemu.BIOS_UEFI { if len(input.OVMFPath) == 0 { input.OVMFPath = options.HostOptions.OvmfPath + input.OVMFVarsPath = options.HostOptions.OvmfVarsPath } } @@ -1116,3 +1121,69 @@ func (s *SKVMGuestInstance) vfioDevCount() int { } return res } + +func (s *SKVMGuestInstance) getOvmfVarsPath() string { + return path.Join(s.HomeDir(), "OVMF_VARS.fd") +} + +func (s *SKVMGuestInstance) getDiskBootOrderType(driver string) uefi.OvmfDevicePathType { + switch driver { + case qemu.DISK_DRIVER_VIRTIO: + return uefi.DEVICE_TYPE_PCI + case qemu.DISK_DRIVER_SCSI, qemu.DISK_DRIVER_PVSCSI: + return uefi.DEVICE_TYPE_SCSI + case qemu.DISK_DRIVER_IDE: + if s.manager.host.IsAarch64() { + return uefi.DEVICE_TYPE_SCSI + } + return uefi.DEVICE_TYPE_IDE + case qemu.DISK_DRIVER_SATA: + if s.manager.host.IsAarch64() { + return uefi.DEVICE_TYPE_SCSI + } + return uefi.DEVICE_TYPE_SATA + } + return uefi.DEVICE_TYPE_UNKNOWN +} + +func (s *SKVMGuestInstance) getCdromBootOrder() uefi.OvmfDevicePathType { + if s.manager.host.IsAarch64() { + return uefi.DEVICE_TYPE_SCSI_CDROM + } + return uefi.DEVICE_TYPE_CDROM +} + +func (s *SKVMGuestInstance) setUefiBootOrder(ctx context.Context) error { + params := &deployapi.OvmfBootOrderParams{ + OvmfVarsPath: s.getOvmfVarsPath(), + } + devs := make([]*deployapi.BootDevices, 0) + for i := range s.Desc.Cdroms { + if s.Desc.Cdroms[i].BootIndex == nil || *s.Desc.Disks[i].BootIndex < 0 { + continue + } + dev := &deployapi.BootDevices{ + BootOrder: int32(*s.Desc.Cdroms[i].BootIndex), + AttachOrder: int32(s.Desc.Cdroms[i].Ordinal), + DevType: int32(s.getCdromBootOrder()), + } + devs = append(devs, dev) + } + for i := range s.Desc.Disks { + if s.Desc.Disks[i].BootIndex == nil || *s.Desc.Disks[i].BootIndex < 0 { + continue + } + dev := &deployapi.BootDevices{ + BootOrder: int32(*s.Desc.Disks[i].BootIndex), + AttachOrder: int32(s.Desc.Disks[i].Index), + DevType: int32(s.getDiskBootOrderType(s.Desc.Disks[i].Driver)), + } + devs = append(devs, dev) + } + params.Devs = devs + _, err := deployclient.GetDeployClient().SetOvmfBootOrder(ctx, params) + if err != nil { + return errors.Wrap(err, "SetOvmfBootOrder") + } + return nil +} diff --git a/pkg/hostman/guestman/qemu/generate.go b/pkg/hostman/guestman/qemu/generate.go index 1605d7275e..418c16a6e0 100644 --- a/pkg/hostman/guestman/qemu/generate.go +++ b/pkg/hostman/guestman/qemu/generate.go @@ -665,6 +665,7 @@ type GenerateStartOptionsInput struct { OVNIntegrationBridge string Devices []string OVMFPath string + OVMFVarsPath string VNCPort uint VNCPassword bool EnableLog bool @@ -763,7 +764,7 @@ func GenerateStartOptions( if input.OVMFPath == "" { return "", errors.Errorf("input OVMF path is empty") } - fmOpt, err := drvOpt.BIOS(input.OVMFPath, input.HomeDir) + fmOpt, err := drvOpt.BIOS(input.OVMFPath, input.OVMFVarsPath, input.HomeDir) if err != nil { return "", errors.Wrap(err, "bios option") } diff --git a/pkg/hostman/guestman/qemu/qemu.go b/pkg/hostman/guestman/qemu/qemu.go index 306d3ac3cf..3d10ba8365 100644 --- a/pkg/hostman/guestman/qemu/qemu.go +++ b/pkg/hostman/guestman/qemu/qemu.go @@ -97,7 +97,7 @@ type QemuOptions interface { MemDev(sizeMB uint64) string MemFd(sizeMB uint64) string Boot(order *string, enableMenu bool) string - BIOS(ovmfPath, homedir string) (string, error) + BIOS(ovmfPath, ovmfVarsPath, homedir string) (string, error) Device(devStr string) string Drive(driveStr string) string Chardev(backend string, id string, name string) string @@ -272,17 +272,21 @@ func (o baseOptions) Boot(order *string, enableMenu bool) string { return fmt.Sprintf("-boot %s", strings.Join(opts, ",")) } -func (o baseOptions) BIOS(ovmfPath, homedir string) (string, error) { - ovmfVarsPath := path.Join(homedir, "OVMF_VARS.fd") - if !fileutils2.Exists(ovmfVarsPath) { - err := procutils.NewRemoteCommandAsFarAsPossible("cp", "-f", ovmfPath, ovmfVarsPath).Run() +func (o baseOptions) BIOS(ovmfPath, ovmfVarsPath, homedir string) (string, error) { + if ovmfVarsPath == "" || !fileutils2.Exists(ovmfVarsPath) { + ovmfVarsPath = ovmfPath + } + + destOvmfVarsPath := path.Join(homedir, "OVMF_VARS.fd") + if !fileutils2.Exists(destOvmfVarsPath) { + err := procutils.NewRemoteCommandAsFarAsPossible("cp", "-f", ovmfVarsPath, destOvmfVarsPath).Run() if err != nil { return "", errors.Wrap(err, "failed copy ovmf vars") } } return fmt.Sprintf( "-drive if=pflash,format=raw,unit=0,file=%s,readonly=on -drive if=pflash,format=raw,unit=1,file=%s", - ovmfPath, ovmfVarsPath, + ovmfPath, destOvmfVarsPath, ), nil } diff --git a/pkg/hostman/guestman/runtime.go b/pkg/hostman/guestman/runtime.go index f5cdc3863e..6e2b029425 100644 --- a/pkg/hostman/guestman/runtime.go +++ b/pkg/hostman/guestman/runtime.go @@ -70,7 +70,7 @@ type GuestRuntimeInstance interface { LoadDesc() error PostLoad(m *SGuestManager) error - SyncConfig(ctx context.Context, guestDesc *desc.SGuestDesc, fwOnly bool) (jsonutils.JSONObject, error) + SyncConfig(ctx context.Context, guestDesc *desc.SGuestDesc, fwOnly, setUefiBootOrder bool) (jsonutils.JSONObject, error) DoSnapshot(ctx context.Context, params *SDiskSnapshot) (jsonutils.JSONObject, error) DeleteSnapshot(ctx context.Context, params *SDeleteDiskSnapshot) (jsonutils.JSONObject, error) OnlineResizeDisk(ctx context.Context, disk storageman.IDisk, sizeMB int64) diff --git a/pkg/hostman/hostdeployer/apis/deploy.pb.go b/pkg/hostman/hostdeployer/apis/deploy.pb.go index 96d1772e3a..a4f585ae06 100644 --- a/pkg/hostman/hostdeployer/apis/deploy.pb.go +++ b/pkg/hostman/hostdeployer/apis/deploy.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.1 +// protoc-gen-go v1.20.0 // protoc v3.21.5 // source: deploy.proto @@ -14,6 +14,7 @@ import ( reflect "reflect" sync "sync" + proto "github.com/golang/protobuf/proto" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) @@ -25,6 +26,10 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 + type GuestDesc struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1960,6 +1965,125 @@ func (x *EsxiDisksConnectionInfo) GetDisks() []*EsxiDiskInfo { return nil } +// cdrom, scsi cdrom, hard drive, scsi, pci +type BootDevices struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + BootOrder int32 `protobuf:"varint,1,opt,name=BootOrder,proto3" json:"BootOrder,omitempty"` + DevType int32 `protobuf:"varint,2,opt,name=DevType,proto3" json:"DevType,omitempty"` + AttachOrder int32 `protobuf:"varint,3,opt,name=AttachOrder,proto3" json:"AttachOrder,omitempty"` +} + +func (x *BootDevices) Reset() { + *x = BootDevices{} + if protoimpl.UnsafeEnabled { + mi := &file_deploy_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BootDevices) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BootDevices) ProtoMessage() {} + +func (x *BootDevices) ProtoReflect() protoreflect.Message { + mi := &file_deploy_proto_msgTypes[24] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BootDevices.ProtoReflect.Descriptor instead. +func (*BootDevices) Descriptor() ([]byte, []int) { + return file_deploy_proto_rawDescGZIP(), []int{24} +} + +func (x *BootDevices) GetBootOrder() int32 { + if x != nil { + return x.BootOrder + } + return 0 +} + +func (x *BootDevices) GetDevType() int32 { + if x != nil { + return x.DevType + } + return 0 +} + +func (x *BootDevices) GetAttachOrder() int32 { + if x != nil { + return x.AttachOrder + } + return 0 +} + +type OvmfBootOrderParams struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + OvmfVarsPath string `protobuf:"bytes,1,opt,name=OvmfVarsPath,proto3" json:"OvmfVarsPath,omitempty"` + Devs []*BootDevices `protobuf:"bytes,2,rep,name=devs,proto3" json:"devs,omitempty"` +} + +func (x *OvmfBootOrderParams) Reset() { + *x = OvmfBootOrderParams{} + if protoimpl.UnsafeEnabled { + mi := &file_deploy_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *OvmfBootOrderParams) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OvmfBootOrderParams) ProtoMessage() {} + +func (x *OvmfBootOrderParams) ProtoReflect() protoreflect.Message { + mi := &file_deploy_proto_msgTypes[25] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use OvmfBootOrderParams.ProtoReflect.Descriptor instead. +func (*OvmfBootOrderParams) Descriptor() ([]byte, []int) { + return file_deploy_proto_rawDescGZIP(), []int{25} +} + +func (x *OvmfBootOrderParams) GetOvmfVarsPath() string { + if x != nil { + return x.OvmfVarsPath + } + return "" +} + +func (x *OvmfBootOrderParams) GetDevs() []*BootDevices { + if x != nil { + return x.Devs + } + return nil +} + var File_deploy_proto protoreflect.FileDescriptor var file_deploy_proto_rawDesc = []byte{ @@ -2235,40 +2359,56 @@ var file_deploy_proto_rawDesc = []byte{ 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, 0x32, 0xc6, 0x03, 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, 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, + 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 ( @@ -2283,7 +2423,7 @@ func file_deploy_proto_rawDescGZIP() []byte { return file_deploy_proto_rawDescData } -var file_deploy_proto_msgTypes = make([]protoimpl.MessageInfo, 24) +var file_deploy_proto_msgTypes = make([]protoimpl.MessageInfo, 26) var file_deploy_proto_goTypes = []interface{}{ (*GuestDesc)(nil), // 0: apis.GuestDesc (*Disk)(nil), // 1: apis.Disk @@ -2309,6 +2449,8 @@ var file_deploy_proto_goTypes = []interface{}{ (*EsxiDiskInfo)(nil), // 21: apis.EsxiDiskInfo (*ConnectEsxiDisksParams)(nil), // 22: apis.ConnectEsxiDisksParams (*EsxiDisksConnectionInfo)(nil), // 23: apis.EsxiDisksConnectionInfo + (*BootDevices)(nil), // 24: apis.BootDevices + (*OvmfBootOrderParams)(nil), // 25: apis.OvmfBootOrderParams } var file_deploy_proto_depIdxs = []int32{ 2, // 0: apis.GuestDesc.nics:type_name -> apis.Nic @@ -2333,25 +2475,28 @@ var file_deploy_proto_depIdxs = []int32{ 3, // 19: apis.ConnectEsxiDisksParams.vddk_info:type_name -> apis.VDDKConInfo 21, // 20: apis.ConnectEsxiDisksParams.access_info:type_name -> apis.EsxiDiskInfo 21, // 21: apis.EsxiDisksConnectionInfo.disks:type_name -> apis.EsxiDiskInfo - 11, // 22: apis.DeployAgent.DeployGuestFs:input_type -> apis.DeployParams - 12, // 23: apis.DeployAgent.ResizeFs:input_type -> apis.ResizeFsParams - 15, // 24: apis.DeployAgent.FormatFs:input_type -> apis.FormatFsParams - 17, // 25: apis.DeployAgent.SaveToGlance:input_type -> apis.SaveToGlanceParams - 19, // 26: apis.DeployAgent.ProbeImageInfo:input_type -> apis.ProbeImageInfoPramas - 22, // 27: apis.DeployAgent.ConnectEsxiDisks:input_type -> apis.ConnectEsxiDisksParams - 23, // 28: apis.DeployAgent.DisconnectEsxiDisks:input_type -> apis.EsxiDisksConnectionInfo - 9, // 29: apis.DeployAgent.DeployGuestFs:output_type -> apis.DeployGuestFsResponse - 8, // 30: apis.DeployAgent.ResizeFs:output_type -> apis.Empty - 8, // 31: apis.DeployAgent.FormatFs:output_type -> apis.Empty - 18, // 32: apis.DeployAgent.SaveToGlance:output_type -> apis.SaveToGlanceResponse - 20, // 33: apis.DeployAgent.ProbeImageInfo:output_type -> apis.ImageInfo - 23, // 34: apis.DeployAgent.ConnectEsxiDisks:output_type -> apis.EsxiDisksConnectionInfo - 8, // 35: apis.DeployAgent.DisconnectEsxiDisks:output_type -> apis.Empty - 29, // [29:36] is the sub-list for method output_type - 22, // [22:29] is the sub-list for method input_type - 22, // [22:22] is the sub-list for extension type_name - 22, // [22:22] is the sub-list for extension extendee - 0, // [0:22] is the sub-list for field type_name + 24, // 22: apis.OvmfBootOrderParams.devs:type_name -> apis.BootDevices + 11, // 23: apis.DeployAgent.DeployGuestFs:input_type -> apis.DeployParams + 12, // 24: apis.DeployAgent.ResizeFs:input_type -> apis.ResizeFsParams + 15, // 25: apis.DeployAgent.FormatFs:input_type -> apis.FormatFsParams + 17, // 26: apis.DeployAgent.SaveToGlance:input_type -> apis.SaveToGlanceParams + 19, // 27: apis.DeployAgent.ProbeImageInfo:input_type -> apis.ProbeImageInfoPramas + 22, // 28: apis.DeployAgent.ConnectEsxiDisks:input_type -> apis.ConnectEsxiDisksParams + 23, // 29: apis.DeployAgent.DisconnectEsxiDisks:input_type -> apis.EsxiDisksConnectionInfo + 25, // 30: apis.DeployAgent.SetOvmfBootOrder:input_type -> apis.OvmfBootOrderParams + 9, // 31: apis.DeployAgent.DeployGuestFs:output_type -> apis.DeployGuestFsResponse + 8, // 32: apis.DeployAgent.ResizeFs:output_type -> apis.Empty + 8, // 33: apis.DeployAgent.FormatFs:output_type -> apis.Empty + 18, // 34: apis.DeployAgent.SaveToGlance:output_type -> apis.SaveToGlanceResponse + 20, // 35: apis.DeployAgent.ProbeImageInfo:output_type -> apis.ImageInfo + 23, // 36: apis.DeployAgent.ConnectEsxiDisks:output_type -> apis.EsxiDisksConnectionInfo + 8, // 37: apis.DeployAgent.DisconnectEsxiDisks:output_type -> apis.Empty + 8, // 38: apis.DeployAgent.SetOvmfBootOrder:output_type -> apis.Empty + 31, // [31:39] is the sub-list for method output_type + 23, // [23:31] is the sub-list for method input_type + 23, // [23:23] is the sub-list for extension type_name + 23, // [23:23] is the sub-list for extension extendee + 0, // [0:23] is the sub-list for field type_name } func init() { file_deploy_proto_init() } @@ -2648,6 +2793,30 @@ func file_deploy_proto_init() { return nil } } + file_deploy_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BootDevices); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_deploy_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OvmfBootOrderParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -2655,7 +2824,7 @@ func file_deploy_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_deploy_proto_rawDesc, NumEnums: 0, - NumMessages: 24, + NumMessages: 26, NumExtensions: 0, NumServices: 1, }, diff --git a/pkg/hostman/hostdeployer/apis/deploy.proto b/pkg/hostman/hostdeployer/apis/deploy.proto index cbaa42db82..ee7584f2ba 100644 --- a/pkg/hostman/hostdeployer/apis/deploy.proto +++ b/pkg/hostman/hostdeployer/apis/deploy.proto @@ -206,6 +206,19 @@ message EsxiDisksConnectionInfo { repeated EsxiDiskInfo disks = 1; } + +// cdrom, scsi cdrom, hard drive, scsi, pci +message BootDevices { + int32 BootOrder = 1; + int32 DevType = 2; + int32 AttachOrder = 3; +} + +message OvmfBootOrderParams { + string OvmfVarsPath = 1; + repeated BootDevices devs = 2; +} + service DeployAgent { rpc DeployGuestFs (DeployParams) returns (DeployGuestFsResponse); rpc ResizeFs (ResizeFsParams) returns (Empty); @@ -214,4 +227,5 @@ service DeployAgent { rpc ProbeImageInfo(ProbeImageInfoPramas) returns (ImageInfo); rpc ConnectEsxiDisks(ConnectEsxiDisksParams) returns (EsxiDisksConnectionInfo); rpc DisconnectEsxiDisks(EsxiDisksConnectionInfo) returns (Empty); + rpc SetOvmfBootOrder(OvmfBootOrderParams) returns (Empty); } diff --git a/pkg/hostman/hostdeployer/apis/deploy_grpc.pb.go b/pkg/hostman/hostdeployer/apis/deploy_grpc.pb.go index 6d0845193b..1f578fb7bb 100644 --- a/pkg/hostman/hostdeployer/apis/deploy_grpc.pb.go +++ b/pkg/hostman/hostdeployer/apis/deploy_grpc.pb.go @@ -1,8 +1,4 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. -// versions: -// - protoc-gen-go-grpc v1.2.0 -// - protoc v3.21.5 -// source: deploy.proto package apis @@ -16,7 +12,6 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. const _ = grpc.SupportPackageIsVersion7 // DeployAgentClient is the client API for DeployAgent service. @@ -30,6 +25,7 @@ type DeployAgentClient interface { ProbeImageInfo(ctx context.Context, in *ProbeImageInfoPramas, opts ...grpc.CallOption) (*ImageInfo, error) ConnectEsxiDisks(ctx context.Context, in *ConnectEsxiDisksParams, opts ...grpc.CallOption) (*EsxiDisksConnectionInfo, error) DisconnectEsxiDisks(ctx context.Context, in *EsxiDisksConnectionInfo, opts ...grpc.CallOption) (*Empty, error) + SetOvmfBootOrder(ctx context.Context, in *OvmfBootOrderParams, opts ...grpc.CallOption) (*Empty, error) } type deployAgentClient struct { @@ -103,6 +99,15 @@ func (c *deployAgentClient) DisconnectEsxiDisks(ctx context.Context, in *EsxiDis return out, nil } +func (c *deployAgentClient) SetOvmfBootOrder(ctx context.Context, in *OvmfBootOrderParams, opts ...grpc.CallOption) (*Empty, error) { + out := new(Empty) + err := c.cc.Invoke(ctx, "/apis.DeployAgent/SetOvmfBootOrder", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // DeployAgentServer is the server API for DeployAgent service. // All implementations must embed UnimplementedDeployAgentServer // for forward compatibility @@ -114,6 +119,7 @@ type DeployAgentServer interface { ProbeImageInfo(context.Context, *ProbeImageInfoPramas) (*ImageInfo, error) ConnectEsxiDisks(context.Context, *ConnectEsxiDisksParams) (*EsxiDisksConnectionInfo, error) DisconnectEsxiDisks(context.Context, *EsxiDisksConnectionInfo) (*Empty, error) + SetOvmfBootOrder(context.Context, *OvmfBootOrderParams) (*Empty, error) mustEmbedUnimplementedDeployAgentServer() } @@ -142,6 +148,9 @@ func (UnimplementedDeployAgentServer) ConnectEsxiDisks(context.Context, *Connect func (UnimplementedDeployAgentServer) DisconnectEsxiDisks(context.Context, *EsxiDisksConnectionInfo) (*Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method DisconnectEsxiDisks not implemented") } +func (UnimplementedDeployAgentServer) SetOvmfBootOrder(context.Context, *OvmfBootOrderParams) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetOvmfBootOrder not implemented") +} func (UnimplementedDeployAgentServer) mustEmbedUnimplementedDeployAgentServer() {} // UnsafeDeployAgentServer may be embedded to opt out of forward compatibility for this service. @@ -152,7 +161,7 @@ type UnsafeDeployAgentServer interface { } func RegisterDeployAgentServer(s grpc.ServiceRegistrar, srv DeployAgentServer) { - s.RegisterService(&DeployAgent_ServiceDesc, srv) + s.RegisterService(&_DeployAgent_serviceDesc, srv) } func _DeployAgent_DeployGuestFs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { @@ -281,10 +290,25 @@ func _DeployAgent_DisconnectEsxiDisks_Handler(srv interface{}, ctx context.Conte return interceptor(ctx, in, info, handler) } -// DeployAgent_ServiceDesc is the grpc.ServiceDesc for DeployAgent service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var DeployAgent_ServiceDesc = grpc.ServiceDesc{ +func _DeployAgent_SetOvmfBootOrder_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(OvmfBootOrderParams) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DeployAgentServer).SetOvmfBootOrder(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/apis.DeployAgent/SetOvmfBootOrder", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DeployAgentServer).SetOvmfBootOrder(ctx, req.(*OvmfBootOrderParams)) + } + return interceptor(ctx, in, info, handler) +} + +var _DeployAgent_serviceDesc = grpc.ServiceDesc{ ServiceName: "apis.DeployAgent", HandlerType: (*DeployAgentServer)(nil), Methods: []grpc.MethodDesc{ @@ -316,6 +340,10 @@ var DeployAgent_ServiceDesc = grpc.ServiceDesc{ MethodName: "DisconnectEsxiDisks", Handler: _DeployAgent_DisconnectEsxiDisks_Handler, }, + { + MethodName: "SetOvmfBootOrder", + Handler: _DeployAgent_SetOvmfBootOrder_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "deploy.proto", diff --git a/pkg/hostman/hostdeployer/deployclient/deployclient.go b/pkg/hostman/hostdeployer/deployclient/deployclient.go index faa623135e..99e80b87da 100644 --- a/pkg/hostman/hostdeployer/deployclient/deployclient.go +++ b/pkg/hostman/hostdeployer/deployclient/deployclient.go @@ -127,3 +127,13 @@ func (c *DeployClient) DisconnectEsxiDisks( client := deployapi.NewDeployAgentClient(conn) return client.DisconnectEsxiDisks(ctx, in, opts...) } + +func (c *DeployClient) SetOvmfBootOrder(ctx context.Context, in *deployapi.OvmfBootOrderParams, opts ...grpc.CallOption) (*deployapi.Empty, error) { + conn, err := grcpDialWithUnixSocket(ctx, c.socketPath) + if err != nil { + return nil, err + } + defer conn.Close() + client := deployapi.NewDeployAgentClient(conn) + return client.SetOvmfBootOrder(ctx, in, opts...) +} diff --git a/pkg/hostman/hostdeployer/deployserver/deployserver.go b/pkg/hostman/hostdeployer/deployserver/deployserver.go index 51416e3183..1f22d2bdb3 100644 --- a/pkg/hostman/hostdeployer/deployserver/deployserver.go +++ b/pkg/hostman/hostdeployer/deployserver/deployserver.go @@ -22,6 +22,7 @@ import ( "net" "os" "runtime/debug" + "sort" "strings" "time" @@ -32,6 +33,7 @@ import ( "yunion.io/x/log" "yunion.io/x/pkg/errors" "yunion.io/x/pkg/util/netutils" + "yunion.io/x/pkg/utils" app_common "yunion.io/x/onecloud/pkg/cloudcommon/app" commonconsts "yunion.io/x/onecloud/pkg/cloudcommon/consts" @@ -45,6 +47,7 @@ import ( "yunion.io/x/onecloud/pkg/hostman/guestfs/fsdriver" deployapi "yunion.io/x/onecloud/pkg/hostman/hostdeployer/apis" "yunion.io/x/onecloud/pkg/hostman/hostdeployer/consts" + "yunion.io/x/onecloud/pkg/hostman/hostdeployer/uefi" "yunion.io/x/onecloud/pkg/util/fileutils2" "yunion.io/x/onecloud/pkg/util/procutils" "yunion.io/x/onecloud/pkg/util/qemuimg" @@ -264,6 +267,75 @@ func (*DeployerServer) DisconnectEsxiDisks( return new(deployapi.Empty), nil } +func (*DeployerServer) SetOvmfBootOrder(ctx context.Context, req *deployapi.OvmfBootOrderParams) (*deployapi.Empty, error) { + log.Infof("Request SetOvmfBootOrder of %s", req.OvmfVarsPath) + if !fileutils2.Exists(req.OvmfVarsPath) { + return new(deployapi.Empty), errors.Errorf("ovmf %s not found", req.OvmfVarsPath) + } + + // parse boot entry from ovmf vars + bootEntries, bootOrder, ovmfJsonTmpPath, err := uefi.ParseUefiVars(req.OvmfVarsPath) + if err != nil { + return new(deployapi.Empty), errors.Wrapf(err, "failed parse uefi vars %s", req.OvmfVarsPath) + } + + sort.Slice(req.Devs, func(i, j int) bool { + return req.Devs[i].AttachOrder < req.Devs[j].AttachOrder + }) + bootEntryIdx := map[string]*deployapi.BootDevices{} + bootentryOrder := map[int32]string{} + findBootentry := func(dev *deployapi.BootDevices) { + for x, bootEntry := range bootEntries { + if _, ok := bootEntryIdx[bootEntry.ID]; ok { + continue + } + log.Errorf("bootentry %v type %v", x, bootEntry.GetType()) + if bootEntry.GetType() == uefi.OvmfDevicePathType(dev.DevType) { + bootEntryIdx[bootEntry.ID] = dev + bootentryOrder[dev.BootOrder] = bootEntry.ID + break + } + } + } + for i := range req.Devs { + findBootentry(req.Devs[i]) + } + sort.Slice(req.Devs, func(i, j int) bool { + return req.Devs[i].BootOrder < req.Devs[j].BootOrder + }) + newBootOrder := []uint16{} + for i := range req.Devs { + bentry, ok := bootentryOrder[req.Devs[i].BootOrder] + if !ok { + continue + } + order, err := uefi.ParseBootentryToBootorder(bentry) + if err != nil { + log.Errorf("failed ParseBootentryToBootorder %s %s", bentry, err) + continue + } + newBootOrder = append(newBootOrder, order) + } + for i := range bootOrder { + if utils.IsInArray(bootOrder[i], newBootOrder) { + continue + } + newBootOrder = append(newBootOrder, bootOrder[i]) + } + + if err := uefi.UpdateBootOrderInJson(ovmfJsonTmpPath, newBootOrder); err != nil { + log.Errorf("failed UpdateBootOrderInJson %s", err) + return new(deployapi.Empty), errors.Wrapf(err, "failed UpdateBootOrderInJson %v", newBootOrder) + } + out, err := uefi.ApplyJsonToVars(ovmfJsonTmpPath, req.OvmfVarsPath, req.OvmfVarsPath) + if err != nil { + log.Errorf("failed ApplyJsonToVars %s %s", out, err) + return new(deployapi.Empty), errors.Wrapf(err, "failed ApplyJsonToVars %v", out) + } + + return new(deployapi.Empty), nil +} + type SDeployService struct { *service.SServiceBase diff --git a/pkg/hostman/hostdeployer/uefi/bootentry.go b/pkg/hostman/hostdeployer/uefi/bootentry.go new file mode 100644 index 0000000000..15b0eb7060 --- /dev/null +++ b/pkg/hostman/hostdeployer/uefi/bootentry.go @@ -0,0 +1,175 @@ +// 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 uefi + +import ( + "encoding/binary" + "encoding/hex" + "fmt" + "strings" +) + +type OvmfDevicePathType int + +const ( + DEVICE_TYPE_UNKNOWN OvmfDevicePathType = 0 + DEVICE_TYPE_CDROM OvmfDevicePathType = 1 + DEVICE_TYPE_IDE OvmfDevicePathType = 2 + DEVICE_TYPE_SCSI OvmfDevicePathType = 3 + DEVICE_TYPE_SCSI_CDROM OvmfDevicePathType = 4 + DEVICE_TYPE_PCI OvmfDevicePathType = 5 + DEVICE_TYPE_SATA OvmfDevicePathType = 6 +) + +type BootEntry struct { + ID string // Boot0000, Boot0001, etc. + Name string // Entry title + DevPaths []*DevicePathElement // Device path elements + RawData string // Raw hex data +} + +func (b *BootEntry) GetType() OvmfDevicePathType { + lenElements := len(b.DevPaths) + if lenElements == 0 { + return DEVICE_TYPE_UNKNOWN + } + devElement := b.DevPaths[lenElements-1] + // fetch last device path element type + switch devElement.devType { + case DevicePathTypeHardware: + if devElement.subType == 0x01 { + return DEVICE_TYPE_PCI + } + case DevicePathTypeMessaging: + switch devElement.subType { + case 0x01: + if strings.HasPrefix(b.Name, "UEFI QEMU DVD-ROM") { + return DEVICE_TYPE_CDROM + } else if strings.HasPrefix(b.Name, "UEFI QEMU HARDDISK") { + return DEVICE_TYPE_IDE + } + case 0x02: + if strings.HasPrefix(b.Name, "UEFI QEMU QEMU CD-ROM") { + return DEVICE_TYPE_SCSI_CDROM + } else if strings.HasPrefix(b.Name, "UEFI QEMU QEMU HARDDISK") { + return DEVICE_TYPE_SCSI + } + case 0x12: + return DEVICE_TYPE_SATA + } + } + + return DEVICE_TYPE_UNKNOWN +} + +// ParseBootEntryData parses a boot entry from hex data +func ParseBootEntryData(hexData string) (string, []*DevicePathElement, error) { + // Decode hex data + data, err := hex.DecodeString(hexData) + if err != nil { + return "", nil, fmt.Errorf("failed to decode hex data: %v", err) + } + + // Check minimum length + if len(data) < 8 { + return "", nil, fmt.Errorf("data too short") + } + + // Parse attributes and path list length + // attributes := binary.LittleEndian.Uint32(data[0:4]) + pathListLen := binary.LittleEndian.Uint16(data[4:6]) + + // Extract description string + descData := data[6:] + descBytes, strLen := ExtractUCS16String(descData) + name := DecodeUTF16LE(descBytes) + + // Calculate path list start + pathListStart := 6 + uint32(strLen) + + // Check if we have enough data for the path list + if pathListLen == 0 { + return name, []*DevicePathElement{}, nil + } + + if uint32(len(data)) < pathListStart+uint32(pathListLen) { + return name, nil, fmt.Errorf("invalid path list length") + } + + // Extract path list + pathListData := data[pathListStart : pathListStart+uint32(pathListLen)] + + // Parse device path elements + devPaths, err := ParseDevicePathElements(pathListData) + if err != nil { + return name, nil, fmt.Errorf("failed to parse device path: %v", err) + } + + return name, devPaths, nil +} + +// ParseBootOrder parses a boot order from hex data +func ParseBootOrder(hexData string) ([]uint16, error) { + // Decode hex data + data, err := hex.DecodeString(hexData) + if err != nil { + return nil, fmt.Errorf("failed to decode hex data: %v", err) + } + + // Check data length + if len(data) == 0 { + return []uint16{}, nil + } + + // Check if data length is valid (must be even) + if len(data)%2 != 0 { + return nil, fmt.Errorf("invalid boot order data length (must be even)") + } + + // Parse boot order (2 bytes per entry) + var bootOrder []uint16 + for i := 0; i < len(data); i += 2 { + entryNum := binary.LittleEndian.Uint16(data[i : i+2]) + bootOrder = append(bootOrder, entryNum) + } + + return bootOrder, nil +} + +func ParseBootentryToBootorder(entry string) (uint16, error) { + if !strings.HasPrefix(entry, "Boot") { + return 0, fmt.Errorf("unknonw boot entry %s", entry) + } + hexData := entry[4:] + // Decode hex data + data, err := hex.DecodeString(hexData) + if err != nil { + return 0, fmt.Errorf("failed to decode hex data %s: %v", hexData, err) + } + return binary.BigEndian.Uint16(data), nil +} + +// BuildBootOrderHex builds a hex string from boot order list +func BuildBootOrderHex(bootOrder []uint16) string { + // Allocate space for boot order (2 bytes per entry) + data := make([]byte, len(bootOrder)*2) + for i, entry := range bootOrder { + // Write little-endian uint16 + binary.LittleEndian.PutUint16(data[i*2:], entry) + } + + // Return hex string + return hex.EncodeToString(data) +} diff --git a/pkg/hostman/hostdeployer/uefi/devpath.go b/pkg/hostman/hostdeployer/uefi/devpath.go new file mode 100644 index 0000000000..e341ef48a7 --- /dev/null +++ b/pkg/hostman/hostdeployer/uefi/devpath.go @@ -0,0 +1,98 @@ +// 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 uefi + +import ( + "encoding/binary" + "fmt" +) + +// DevicePathType constants +const ( + DevicePathTypeHardware = 0x01 + DevicePathTypeACPI = 0x02 + DevicePathTypeMessaging = 0x03 + DevicePathTypeMedia = 0x04 + DevicePathTypeEnd = 0x7F +) + +// DevicePathElement represents a UEFI device path element +type DevicePathElement struct { + devType byte + subType byte + data []byte +} + +func (e *DevicePathElement) String() string { + return fmt.Sprintf("devType %v, subType %v", e.devType, e.subType) +} + +// Type returns the device path type +func (e *DevicePathElement) Type() byte { + return e.devType +} + +// SubType returns the device path subtype +func (e *DevicePathElement) SubType() byte { + return e.subType +} + +// ParseDevicePathElements parses a device path from binary data +func ParseDevicePathElements(data []byte) ([]*DevicePathElement, error) { + if len(data) == 0 { + return nil, fmt.Errorf("empty device path data") + } + + var elements []*DevicePathElement + pos := 0 + + for pos < len(data) { + // Check if we have enough data for the header + if pos+4 > len(data) { + return nil, fmt.Errorf("truncated device path data") + } + + // Parse header + devType := data[pos] + subType := data[pos+1] + length := binary.LittleEndian.Uint16(data[pos+2 : pos+4]) + + // Validate length + if length < 4 { + return nil, fmt.Errorf("invalid device path element length") + } + + // Check if we have enough data for the element + if pos+int(length) > len(data) { + return nil, fmt.Errorf("truncated device path element") + } + + // Check if this is the end of the device path + if devType == DevicePathTypeEnd { + break + } + + element := &DevicePathElement{ + devType: devType, + subType: subType, + data: data[pos+4 : pos+int(length)], + } + elements = append(elements, element) + + pos += int(length) + } + + return elements, nil +} diff --git a/pkg/hostman/hostdeployer/uefi/doc.go b/pkg/hostman/hostdeployer/uefi/doc.go new file mode 100644 index 0000000000..b3cb5fdd8c --- /dev/null +++ b/pkg/hostman/hostdeployer/uefi/doc.go @@ -0,0 +1,15 @@ +// 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 uefi // import "yunion.io/x/onecloud/pkg/hostman/hostdeployer/uefi" diff --git a/pkg/hostman/hostdeployer/uefi/ucs16.go b/pkg/hostman/hostdeployer/uefi/ucs16.go new file mode 100644 index 0000000000..1bd3e61f57 --- /dev/null +++ b/pkg/hostman/hostdeployer/uefi/ucs16.go @@ -0,0 +1,76 @@ +// 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 uefi + +import ( + "unicode/utf16" +) + +// ExtractUCS16String extracts a UCS-16 string from a byte array +// Returns the string data and the total length (including null terminator) +func ExtractUCS16String(data []byte) ([]byte, uint32) { + // Find the null terminator (two consecutive zero bytes) + var i int + for i = 0; i < len(data)-1; i += 2 { + if data[i] == 0 && data[i+1] == 0 { + break + } + } + + // Include the null terminator in the length + strLen := i + 2 + + // If we reached the end without finding a null terminator, + // use the entire data length + if i >= len(data)-1 { + strLen = len(data) + i = len(data) + if i%2 != 0 { + i-- + } + } + + // Return the string data and length + return data[:i], uint32(strLen) +} + +// DecodeUTF16LE decodes a UTF-16LE byte array to a string +func DecodeUTF16LE(b []byte) string { + // Check if the byte array is empty + if len(b) == 0 { + return "" + } + + // Convert bytes to uint16 array + u16s := make([]uint16, len(b)/2) + for i := range u16s { + // Little-endian: low byte first, then high byte + u16s[i] = uint16(b[i*2]) | (uint16(b[i*2+1]) << 8) + } + + // Decode UTF-16 to UTF-8 + return string(utf16.Decode(u16s)) +} + +// EncodeUTF16LE encodes a string to UTF-16LE bytes +func EncodeUTF16LE(s string) []byte { + u16s := utf16.Encode([]rune(s)) + bytes := make([]byte, len(u16s)*2) + for i, u16 := range u16s { + bytes[i*2] = byte(u16) + bytes[i*2+1] = byte(u16 >> 8) + } + return bytes +} diff --git a/pkg/hostman/hostdeployer/uefi/utils.go b/pkg/hostman/hostdeployer/uefi/utils.go new file mode 100644 index 0000000000..5ca230f5ee --- /dev/null +++ b/pkg/hostman/hostdeployer/uefi/utils.go @@ -0,0 +1,59 @@ +// 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 uefi + +import ( + "fmt" + "io/ioutil" + "os" + "sort" + + "yunion.io/x/pkg/errors" + + "yunion.io/x/onecloud/pkg/util/procutils" +) + +func DumpOvmfVarsToJson(ovmfVarsPath string) (string, error) { + // Create temporary file for JSON output + jsonFile, err := ioutil.TempFile("", "ovmf-vars-*.json") + if err != nil { + return "", fmt.Errorf("failed to create temporary file: %v", err) + } + jsonPath := jsonFile.Name() + jsonFile.Close() + + output, err := procutils.NewCommand("virt-fw-vars", "-i", ovmfVarsPath, "--output-json", jsonPath).Output() + if err != nil { + os.Remove(jsonPath) + return "", errors.Wrapf(err, "virt-fw-vars failed dump to json %s", output) + } + return jsonPath, nil +} + +func ParseUefiVars(ovmfVarsPath string) ([]*BootEntry, []uint16, string, error) { + jsonPath, err := DumpOvmfVarsToJson(ovmfVarsPath) + if err != nil { + return nil, nil, "", errors.Wrap(err, "DumpOvmfVarsToJson") + } + + bootEntry, bootOrder, err := ParseVarsJson(jsonPath) + if err != nil { + return nil, nil, "", errors.Wrap(err, "ParseVarsJson") + } + sort.Slice(bootEntry, func(i, j int) bool { + return bootEntry[i].ID < bootEntry[j].ID + }) + return bootEntry, bootOrder, jsonPath, nil +} diff --git a/pkg/hostman/hostdeployer/uefi/vars.go b/pkg/hostman/hostdeployer/uefi/vars.go new file mode 100644 index 0000000000..02956609ba --- /dev/null +++ b/pkg/hostman/hostdeployer/uefi/vars.go @@ -0,0 +1,157 @@ +// 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 uefi + +import ( + "encoding/json" + "fmt" + "io/ioutil" + + "yunion.io/x/log" + + "yunion.io/x/onecloud/pkg/util/procutils" +) + +// VarsData represents the UEFI variables data +type VarsData struct { + Version int `json:"version"` + Variables []Variable `json:"variables"` +} + +// Variable represents a UEFI variable +type Variable struct { + Name string `json:"name"` + GUID string `json:"guid"` + Attr int `json:"attr"` + Data string `json:"data"` +} + +// EFI_GLOBAL_VARIABLE_GUID is the GUID for EFI global variables +const EFI_GLOBAL_VARIABLE_GUID = "8be4df61-93ca-11d2-aa0d-00e098032b8c" + +// ParseVarsJson parses UEFI variables from a JSON file +func ParseVarsJson(jsonPath string) ([]*BootEntry, []uint16, error) { + // Read JSON file + data, err := ioutil.ReadFile(jsonPath) + if err != nil { + return nil, nil, fmt.Errorf("failed to read JSON file: %v", err) + } + + // Parse JSON + var varsData VarsData + err = json.Unmarshal(data, &varsData) + if err != nil { + return nil, nil, fmt.Errorf("failed to parse JSON: %v", err) + } + + // Parse boot entries and boot order + var bootEntries []*BootEntry + var bootOrder []uint16 + + for _, v := range varsData.Variables { + // Check if this is a boot entry + if len(v.Name) >= 8 && v.Name[:4] == "Boot" && v.GUID == EFI_GLOBAL_VARIABLE_GUID { + // Check if this is the boot order + if v.Name == "BootOrder" { + var err error + bootOrder, err = ParseBootOrder(v.Data) + if err != nil { + return nil, nil, fmt.Errorf("failed to parse boot order: %v", err) + } + continue + } + + // Parse boot entry + name, devPaths, err := ParseBootEntryData(v.Data) + if err != nil { + log.Errorf("failed to parse boot entry %s: %s", v.Name, err) + continue + } + + // Create boot entry + entry := &BootEntry{ + ID: v.Name, + Name: name, + DevPaths: devPaths, + RawData: v.Data, + } + + // Add entry to list + bootEntries = append(bootEntries, entry) + } + } + + return bootEntries, bootOrder, nil +} + +// UpdateBootOrderInJson updates the boot order in a UEFI variables JSON file +func UpdateBootOrderInJson(jsonPath string, bootOrder []uint16) error { + // Read JSON file + data, err := ioutil.ReadFile(jsonPath) + if err != nil { + return fmt.Errorf("failed to read JSON file: %v", err) + } + + // Parse JSON + var varsData VarsData + err = json.Unmarshal(data, &varsData) + if err != nil { + return fmt.Errorf("failed to parse JSON: %v", err) + } + + bootOrderHex := BuildBootOrderHex(bootOrder) + bootOrderFound := false + for i, v := range varsData.Variables { + if v.Name == "BootOrder" && v.GUID == EFI_GLOBAL_VARIABLE_GUID { + varsData.Variables[i].Data = bootOrderHex + bootOrderFound = true + break + } + } + + // Add boot order if not found + if !bootOrderFound { + varsData.Variables = append(varsData.Variables, Variable{ + Name: "BootOrder", + GUID: EFI_GLOBAL_VARIABLE_GUID, + Attr: 7, // NV+BS+RT + Data: bootOrderHex, + }) + } + + // Write updated JSON + updatedData, err := json.MarshalIndent(varsData, "", " ") + if err != nil { + return fmt.Errorf("failed to marshal JSON: %v", err) + } + + err = ioutil.WriteFile(jsonPath, updatedData, 0644) + if err != nil { + return fmt.Errorf("failed to write JSON file: %v", err) + } + + return nil +} + +// ApplyJsonToVars applies a JSON file to OVMF_VARS.fd +func ApplyJsonToVars(jsonPath, inputVarsPath, outputVarsPath string) ([]byte, error) { + // Execute virt-fw-vars to apply JSON + output, err := procutils.NewCommand("virt-fw-vars", "-i", inputVarsPath, "-o", outputVarsPath, "--set-json", jsonPath).Output() + if err != nil { + return output, fmt.Errorf("failed to execute virt-fw-vars --set-json command: %v", err) + } + + return output, nil +} diff --git a/pkg/hostman/isolated_device/container_device/nvidia_gpu_share.go b/pkg/hostman/isolated_device/container_device/nvidia_gpu_share.go index 2e79a817cd..6fb43d4068 100644 --- a/pkg/hostman/isolated_device/container_device/nvidia_gpu_share.go +++ b/pkg/hostman/isolated_device/container_device/nvidia_gpu_share.go @@ -1,3 +1,17 @@ +// 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 container_device import ( diff --git a/pkg/hostman/options/options.go b/pkg/hostman/options/options.go index 248db45171..1eae83b503 100644 --- a/pkg/hostman/options/options.go +++ b/pkg/hostman/options/options.go @@ -105,8 +105,9 @@ type SHostOptions struct { DnsServer string `help:"Address of host DNS server"` DnsServerLegacy string `help:"Deprecated Address of host DNS server"` - ChntpwPath string `help:"path to chntpw tool" default:"/usr/local/bin/chntpw.static"` - OvmfPath string `help:"Path to OVMF.fd" default:"/opt/cloud/contrib/OVMF.fd"` + ChntpwPath string `help:"path to chntpw tool" default:"/usr/local/bin/chntpw.static"` + OvmfPath string `help:"Path to OVMF.fd" default:"/opt/cloud/contrib/OVMF.fd"` + OvmfVarsPath string `help:"Path to OVMF_VARS.fd" default:"/opt/cloud/contrib/OVMF_VARS.fd"` LinuxDefaultRootUser bool `help:"Default account for linux system is root"` WindowsDefaultAdminUser bool `default:"true" help:"Default account for Windows system is Administrator"` diff --git a/pkg/util/cgrouputils/cgroup/doc.go b/pkg/util/cgrouputils/cgroup/doc.go index db03c717c0..16fe6e60f0 100644 --- a/pkg/util/cgrouputils/cgroup/doc.go +++ b/pkg/util/cgrouputils/cgroup/doc.go @@ -1 +1,15 @@ +// 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 cgroup // import "yunion.io/x/onecloud/pkg/util/cgrouputils/cgroup" diff --git a/pkg/util/cgrouputils/cgroupv1/manager.go b/pkg/util/cgrouputils/cgroupv1/manager.go index dfd7556944..01406c5b3f 100644 --- a/pkg/util/cgrouputils/cgroupv1/manager.go +++ b/pkg/util/cgrouputils/cgroupv1/manager.go @@ -1,3 +1,17 @@ +// 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 cgroupv1 import ( diff --git a/pkg/util/cgrouputils/cgroupv2/doc.go b/pkg/util/cgrouputils/cgroupv2/doc.go index b7133d67a4..1250224418 100644 --- a/pkg/util/cgrouputils/cgroupv2/doc.go +++ b/pkg/util/cgrouputils/cgroupv2/doc.go @@ -1 +1,15 @@ +// 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 cgroupv2 // import "yunion.io/x/onecloud/pkg/util/cgrouputils/cgroupv2"