| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451 |
- // 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 guestman
- import (
- "context"
- "fmt"
- "io/ioutil"
- "path"
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
- computeapi "yunion.io/x/onecloud/pkg/apis/compute"
- "yunion.io/x/onecloud/pkg/hostman/guestman/desc"
- deployapi "yunion.io/x/onecloud/pkg/hostman/hostdeployer/apis"
- "yunion.io/x/onecloud/pkg/hostman/options"
- "yunion.io/x/onecloud/pkg/hostman/storageman"
- "yunion.io/x/onecloud/pkg/mcclient"
- "yunion.io/x/onecloud/pkg/util/cgrouputils/cpuset"
- "yunion.io/x/onecloud/pkg/util/fileutils2"
- "yunion.io/x/onecloud/pkg/util/netutils2"
- "yunion.io/x/onecloud/pkg/util/procutils"
- )
- type GuestRuntimeInstance interface {
- GetHypervisor() string
- GetName() string
- GetInitialId() string
- GetId() string
- IsValid() bool
- HomeDir() string
- GetDesc() *desc.SGuestDesc
- SetDesc(guestDesc *desc.SGuestDesc)
- GetSourceDesc() *desc.SGuestDesc
- SetSourceDesc(guestDesc *desc.SGuestDesc)
- GetDescFilePath() string
- NicTrafficRecordPath() string
- DeployFs(ctx context.Context, userCred mcclient.TokenCredential, deployInfo *deployapi.DeployInfo) (jsonutils.JSONObject, error)
- GetSourceDescFilePath() string
- IsRunning() bool
- IsStopped() bool
- IsSuspend() bool
- IsLoaded() bool
- GetNicDescMatch(mac, ip, port, bridge string) *desc.SGuestNetwork
- GetIpv6NicMacs(bridge string) []string
- CleanGuest(ctx context.Context, params interface{}) (jsonutils.JSONObject, error)
- CleanDirtyGuest(ctx context.Context) error
- ImportServer(pendingDelete bool)
- ExitCleanup(bool)
- HandleGuestStatus(ctx context.Context, resp *computeapi.HostUploadGuestStatusInput, isBatch bool) *computeapi.HostUploadGuestStatusInput
- GetUploadStatus(ctx context.Context, reason string) (*computeapi.HostUploadGuestStatusInput, error)
- PostUploadStatus(resp *computeapi.HostUploadGuestStatusInput, reason string)
- HandleGuestStart(ctx context.Context, userCred mcclient.TokenCredential, body jsonutils.JSONObject) (jsonutils.JSONObject, error)
- HandleStop(ctx context.Context, timeout int64) error
- LoadDesc() error
- PostLoad(m *SGuestManager) 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)
- GetDependsImageIds(storageType string) []string
- SetNicDown(mac string) error
- SetNicUp(nic *desc.SGuestNetwork) error
- }
- type sBaseGuestInstance struct {
- Id string
- manager *SGuestManager
- // runtime description, generate from source desc
- Desc *desc.SGuestDesc
- // source description, input from region
- SourceDesc *desc.SGuestDesc
- Hypervisor string
- }
- func newBaseGuestInstance(id string, manager *SGuestManager, hypervisor string) *sBaseGuestInstance {
- return &sBaseGuestInstance{
- Id: id,
- manager: manager,
- Hypervisor: hypervisor,
- }
- }
- func (s *sBaseGuestInstance) GetHypervisor() string {
- return s.Hypervisor
- }
- func (s *sBaseGuestInstance) IsValid() bool {
- return s.Desc != nil && s.Desc.Uuid != ""
- }
- func (s *sBaseGuestInstance) GetInitialId() string {
- return s.Id
- }
- func (s *sBaseGuestInstance) GetId() string {
- return s.Desc.Uuid
- }
- func (s *sBaseGuestInstance) GetName() string {
- return fmt.Sprintf("%s(%s)", s.Desc.Name, s.Desc.Uuid)
- }
- func (b *sBaseGuestInstance) HomeDir() string {
- return path.Join(b.manager.ServersPath, b.Id)
- }
- func (s *sBaseGuestInstance) GetDescFilePath() string {
- return path.Join(s.HomeDir(), "desc")
- }
- func (s *sBaseGuestInstance) GetDesc() *desc.SGuestDesc {
- return s.Desc
- }
- func (s *sBaseGuestInstance) SetDesc(guestDesc *desc.SGuestDesc) {
- s.Desc = guestDesc
- }
- func (s *sBaseGuestInstance) GetSourceDesc() *desc.SGuestDesc {
- return s.SourceDesc
- }
- func (s *sBaseGuestInstance) SetSourceDesc(guestDesc *desc.SGuestDesc) {
- s.SourceDesc = guestDesc
- }
- func (s *sBaseGuestInstance) GetSourceDescFilePath() string {
- return path.Join(s.HomeDir(), "source-desc")
- }
- func (s *sBaseGuestInstance) NicTrafficRecordPath() string {
- return path.Join(s.HomeDir(), "nic_traffic.json")
- }
- func (s *sBaseGuestInstance) IsLoaded() bool {
- return s.Desc != nil
- }
- func (s *sBaseGuestInstance) IsDaemon() bool {
- return s.Desc.IsDaemon
- }
- func (s *sBaseGuestInstance) GetNicDescMatch(mac, ip, port, bridge string) *desc.SGuestNetwork {
- nics := s.Desc.Nics
- for _, nic := range nics {
- if bridge == "" && nic.Bridge != "" && nic.Bridge == options.HostOptions.OvnIntegrationBridge {
- continue
- }
- if (len(mac) == 0 || netutils2.MacEqual(nic.Mac, mac)) &&
- (len(ip) == 0 || nic.Ip == ip) &&
- (len(port) == 0 || nic.Ifname == port) &&
- (len(bridge) == 0 || nic.Bridge == bridge) {
- return nic
- }
- }
- return nil
- }
- func (s *sBaseGuestInstance) GetIpv6NicMacs(bridge string) []string {
- macs := []string{}
- for _, nic := range s.Desc.Nics {
- if nic.Bridge == bridge && len(nic.Ip6) > 0 {
- macs = append(macs, nic.Mac)
- }
- }
- return macs
- }
- func LoadGuestCpuset(m *SGuestManager, s GuestRuntimeInstance) error {
- guestDesc := s.GetDesc()
- if s.IsRunning() {
- m.cpuSet.Lock.Lock()
- defer m.cpuSet.Lock.Unlock()
- m.cpuSet.GuestIds[s.GetId()] = struct{}{}
- for _, vcpuPin := range guestDesc.VcpuPin {
- pcpuSet, err := cpuset.Parse(vcpuPin.Pcpus)
- if err != nil {
- log.Errorf("failed parse %s pcpus: %s", s.GetName(), vcpuPin.Pcpus)
- continue
- }
- vcpuSet, err := cpuset.Parse(vcpuPin.Vcpus)
- if err != nil {
- log.Errorf("failed parse %s vcpus: %s", s.GetName(), vcpuPin.Vcpus)
- continue
- }
- m.cpuSet.LoadCpus(pcpuSet.ToSlice(), vcpuSet.Size())
- }
- for _, numaCpuset := range guestDesc.CpuNumaPin {
- pcpus := make([]int, 0)
- vcpus := make([]int, 0)
- for i := range numaCpuset.VcpuPin {
- pcpus = append(pcpus, numaCpuset.VcpuPin[i].Pcpu)
- if numaCpuset.VcpuPin[i].Vcpu >= 0 {
- vcpus = append(vcpus, numaCpuset.VcpuPin[i].Vcpu)
- }
- }
- m.cpuSet.LoadNumaCpus(numaCpuset.SizeMB, int(*numaCpuset.NodeId), pcpus, len(vcpus))
- }
- }
- return nil
- }
- func ReleaseCpuNumaPin(m *SGuestManager, cpuNumaPin []*desc.SCpuNumaPin) {
- if !m.hostagentNumaAllocate {
- return
- }
- for _, numaCpus := range cpuNumaPin {
- pcpus := make([]int, 0)
- vcpus := make([]int, 0)
- for i := range numaCpus.VcpuPin {
- pcpus = append(pcpus, numaCpus.VcpuPin[i].Pcpu)
- if numaCpus.VcpuPin[i].Vcpu >= 0 {
- vcpus = append(vcpus, numaCpus.VcpuPin[i].Vcpu)
- }
- }
- m.cpuSet.ReleaseNumaCpus(numaCpus.SizeMB, int(*numaCpus.NodeId), pcpus, len(vcpus))
- }
- }
- func ReleaseGuestCpuset(m *SGuestManager, s GuestRuntimeInstance) {
- m.cpuSet.Lock.Lock()
- defer m.cpuSet.Lock.Unlock()
- delete(m.cpuSet.GuestIds, s.GetId())
- guestDesc := s.GetDesc()
- ReleaseCpuNumaPin(m, guestDesc.CpuNumaPin)
- for _, vcpuPin := range guestDesc.VcpuPin {
- pcpuSet, err := cpuset.Parse(vcpuPin.Pcpus)
- if err != nil {
- log.Errorf("failed parse %s pcpus: %s", s.GetName(), vcpuPin.Pcpus)
- continue
- }
- vcpuSet, err := cpuset.Parse(vcpuPin.Vcpus)
- if err != nil {
- log.Errorf("failed parse %s vcpus: %s", s.GetName(), vcpuPin.Vcpus)
- continue
- }
- m.cpuSet.ReleaseCpus(pcpuSet.ToSlice(), vcpuSet.Size())
- }
- guestDesc.VcpuPin = nil
- guestDesc.CpuNumaPin = nil
- SaveLiveDesc(s, guestDesc)
- }
- type GuestRuntimeManager struct {
- }
- func NewGuestRuntimeManager() *GuestRuntimeManager {
- return new(GuestRuntimeManager)
- }
- func (f *GuestRuntimeManager) NewRuntimeInstance(id string, manager *SGuestManager, hypervisor string) GuestRuntimeInstance {
- switch hypervisor {
- case computeapi.HYPERVISOR_KVM, "":
- return NewKVMGuestInstance(id, manager)
- case computeapi.HYPERVISOR_POD:
- return newPodGuestInstance(id, manager)
- }
- log.Fatalf("Invalid hypervisor for runtime: %q", hypervisor)
- return nil
- }
- func PrepareDir(s GuestRuntimeInstance) error {
- output, err := procutils.NewCommand("mkdir", "-p", s.HomeDir()).Output()
- if err != nil {
- return errors.Wrapf(err, "mkdir %s failed: %s", s.HomeDir(), output)
- }
- return nil
- }
- func (f *GuestRuntimeManager) CreateFromDesc(s GuestRuntimeInstance, desc *desc.SGuestDesc) error {
- if err := PrepareDir(s); err != nil {
- return errors.Errorf("Failed to create server dir %s", desc.Uuid)
- }
- return SaveDesc(s, desc)
- }
- func (s *sBaseGuestInstance) GetVpcNIC() *desc.SGuestNetwork {
- for _, nic := range s.Desc.Nics {
- if nic.Vpc.Provider == computeapi.VPC_PROVIDER_OVN {
- if nic.Ip != "" {
- return nic
- }
- }
- }
- return nil
- }
- func (s *sBaseGuestInstance) UpdateLiveDesc(guestDesc *desc.SGuestDesc) {
- // update guest live desc, don't be here update cpu and mem
- // cpu and memory should update from SGuestHotplugCpuMemTask
- s.Desc.SGuestControlDesc = guestDesc.SGuestControlDesc
- s.Desc.SGuestProjectDesc = guestDesc.SGuestProjectDesc
- s.Desc.SGuestRegionDesc = guestDesc.SGuestRegionDesc
- s.Desc.SGuestMetaDesc = guestDesc.SGuestMetaDesc
- }
- func LoadDesc(s GuestRuntimeInstance) error {
- descPath := s.GetDescFilePath()
- descStr, err := ioutil.ReadFile(descPath)
- if err != nil {
- return errors.Wrap(err, "read desc")
- }
- var (
- srcDescStr []byte
- srcDescPath = s.GetSourceDescFilePath()
- )
- if !fileutils2.Exists(srcDescPath) {
- err = fileutils2.FilePutContents(srcDescPath, string(descStr), false)
- if err != nil {
- return errors.Wrap(err, "save source desc")
- }
- srcDescStr = descStr
- } else {
- srcDescStr, err = ioutil.ReadFile(srcDescPath)
- if err != nil {
- return errors.Wrap(err, "read source desc")
- }
- }
- // parse source desc
- srcGuestDesc := new(desc.SGuestDesc)
- jsonSrcDesc, err := jsonutils.Parse(srcDescStr)
- if err != nil {
- return errors.Wrap(err, "json parse source desc")
- }
- err = jsonSrcDesc.Unmarshal(srcGuestDesc)
- if err != nil {
- return errors.Wrap(err, "unmarshal source desc")
- }
- s.SetSourceDesc(srcGuestDesc)
- // parse desc
- guestDesc := new(desc.SGuestDesc)
- jsonDesc, err := jsonutils.Parse(descStr)
- if err != nil {
- return errors.Wrap(err, "json parse desc")
- }
- err = jsonDesc.Unmarshal(guestDesc)
- if err != nil {
- return errors.Wrap(err, "unmarshal desc")
- }
- s.SetDesc(guestDesc)
- return nil
- }
- func SaveDesc(s GuestRuntimeInstance, guestDesc *desc.SGuestDesc) error {
- s.SetSourceDesc(guestDesc)
- // fill in ovn vpc nic bridge field
- for _, nic := range s.GetSourceDesc().Nics {
- if nic.Bridge == "" {
- nic.Bridge = getNicBridge(nic)
- }
- }
- if err := fileutils2.FilePutContents(
- s.GetSourceDescFilePath(), jsonutils.Marshal(s.GetSourceDesc()).String(), false,
- ); err != nil {
- log.Errorf("save source desc failed %s", err)
- return errors.Wrap(err, "source save desc")
- }
- if !s.IsRunning() { // if guest not running, sync live desc
- liveDesc := new(desc.SGuestDesc)
- if err := jsonutils.Marshal(s.GetSourceDesc()).Unmarshal(liveDesc); err != nil {
- return errors.Wrap(err, "unmarshal live desc")
- }
- return SaveLiveDesc(s, liveDesc)
- }
- return nil
- }
- func SaveLiveDesc(s GuestRuntimeInstance, guestDesc *desc.SGuestDesc) error {
- s.SetDesc(guestDesc)
- defaultGwCnt := 0
- defNics := netutils2.SNicInfoList{}
- // fill in ovn vpc nic bridge field
- for _, nic := range s.GetDesc().Nics {
- if nic.Bridge == "" {
- nic.Bridge = getNicBridge(nic)
- }
- if nic.IsDefault {
- defaultGwCnt++
- }
- defNics = defNics.Add(nic.Mac, nic.Ip, nic.Gateway, nic.Ip6, nic.Gateway6, nic.IsDefault)
- }
- // there should 1 and only 1 default gateway
- if defaultGwCnt != 1 {
- // fix is_default
- _, defIndex := defNics.FindDefaultNicMac()
- for i := range s.GetDesc().Nics {
- if i == defIndex {
- s.GetDesc().Nics[i].IsDefault = true
- } else {
- s.GetDesc().Nics[i].IsDefault = false
- }
- }
- }
- if err := fileutils2.FilePutContents(
- s.GetDescFilePath(), jsonutils.Marshal(s.GetDesc()).String(), false,
- ); err != nil {
- log.Errorf("save desc failed %s", err)
- return errors.Wrap(err, "save desc")
- }
- return nil
- }
- func GetPowerStates(s GuestRuntimeInstance) string {
- if s.IsRunning() {
- return computeapi.VM_POWER_STATES_ON
- } else {
- return computeapi.VM_POWER_STATES_OFF
- }
- }
- func DeleteHomeDir(s GuestRuntimeInstance) error {
- output, err := procutils.NewCommand("rm", "-rf", s.HomeDir()).Output()
- if err != nil {
- return errors.Wrapf(err, "rm %s failed: %s", s.HomeDir(), output)
- }
- return nil
- }
|