diff --git a/DomCon/DomainUpdate.go b/DomCon/DomainUpdate.go new file mode 100644 index 0000000..a93a7b7 --- /dev/null +++ b/DomCon/DomainUpdate.go @@ -0,0 +1,13 @@ +package domCon + +// Di injector for domain xml unparse and update + +// func (DC *DomListControl) UpdateDomainStatus(Dom *Domain, logger *zap.Logger) error { + +// domcnf, err := domStatus.XMLUnparse(Dom.Domain) +// if err != nil { +// logger.Error("failed to unparse domain XML", zap.Error(err)) +// return err +// } + +// } \ No newline at end of file diff --git a/DomCon/control_type.go b/DomCon/control_type.go index 864aec6..df32585 100755 --- a/DomCon/control_type.go +++ b/DomCon/control_type.go @@ -3,6 +3,7 @@ package domCon import ( "sync" + domStatus "github.com/easy-cloud-Knet/KWS_Core/DomCon/domain_status" "libvirt.org/go/libvirt" ) @@ -11,20 +12,37 @@ import ( // DomainList 에서 uuid형태로 각각의 도메인을 관리 + + + + type DomListControl struct { DomainList map[string]*Domain domainListMutex sync.Mutex + DomainListStatus * domStatus.DomainListStatus } +// 각 도메인을 관리하는 인메모리 구조체 +// mutex 를 통해 동시성 제어 +// 메모리 누수방지 + libvirt 접근 최소화 위해 libvirt.Domain 포인터를 보유 + + type Domain struct { Domain *libvirt.Domain domainMutex sync.Mutex } +// 각 도메인의 상태변경시에 사용하는 구조체 +// mutex 를 통해 동시성 제어 + + type DomainSeekingByUUID struct { LibvirtInst *libvirt.Connect UUID string } +// 도메인 탐색 인터페이스 +// 인메모리 도메인 리스트에 없을 경우 libvirt 에서 도메인을 찾아 반환하는 역할 + type DomainSeeker interface { ReturnDomain() (*Domain, error) diff --git a/DomCon/domain_list.go b/DomCon/domain_list.go index 2be770b..f8698f3 100755 --- a/DomCon/domain_list.go +++ b/DomCon/domain_list.go @@ -4,6 +4,7 @@ import ( "fmt" "sync" + domStatus "github.com/easy-cloud-Knet/KWS_Core/DomCon/domain_status" virerr "github.com/easy-cloud-Knet/KWS_Core/error" "go.uber.org/zap" "libvirt.org/go/libvirt" @@ -20,15 +21,32 @@ func DomListConGen() *DomListControl { return &DomListControl{ domainListMutex: sync.Mutex{}, DomainList: make(map[string]*Domain), + DomainListStatus: &domStatus.DomainListStatus{}, } +}// 전역적으로 사용되는 도메인 리스트 컨트롤러 생성 + + +func (DC *DomListControl) AddNewDomain(domain *Domain, uuid string) error { + DC.domainListMutex.Lock() + defer DC.domainListMutex.Unlock() + + DC.DomainList[uuid] = domain + vcpu, err :=domain.Domain.GetMaxVcpus() + if err != nil { + Err:=fmt.Errorf("%v error while getting vcpu count during adding new domain",err) + return Err + } + DC.DomainListStatus.AddAllocatedCPU(int(vcpu)) + return nil } -func (DC *DomListControl) AddNewDomain(domain *Domain, uuid string) { +func (DC *DomListControl) AddExistingDomain(domain *Domain, uuid string) { DC.domainListMutex.Lock() defer DC.domainListMutex.Unlock() DC.DomainList[uuid] = domain } +// Exstring Domain only called from initial booting, and adding specs is not its role func (DC *DomListControl) GetDomain(uuid string, LibvirtInst *libvirt.Connect) (*Domain, error) { fmt.Println(DC) @@ -51,15 +69,18 @@ func (DC *DomListControl) GetDomain(uuid string, LibvirtInst *libvirt.Connect) ( return domain, nil } -func (DC *DomListControl) DeleteDomain(Domain *libvirt.Domain, uuid string) error { +func (DC *DomListControl) DeleteDomain(Domain *libvirt.Domain, uuid string, vcpu int) error { DC.domainListMutex.Lock() delete(DC.DomainList, uuid) Domain.Free() DC.domainListMutex.Unlock() + DC.DomainListStatus.TakeAllocatedCPU(vcpu) return nil } func (DC *DomListControl) FindAndDeleteDomain(LibvirtInst *libvirt.Connect, uuid string) error { + //아직 활용처가 없어서, vcpu 삭제를 추가하지 않았음/ + // DeleteDomain 함수의 TakeAllocatedCPU 호출을 참고.. DC.domainListMutex.Lock() domain, Exist := DC.DomainList[uuid] DC.domainListMutex.Unlock() @@ -90,24 +111,35 @@ func (DC *DomListControl) retrieveDomainsByState(LibvirtInst *libvirt.Connect, s return err } - DC.domainListMutex.Lock() - defer DC.domainListMutex.Unlock() + dataDog := domStatus.NewDataDog(state) + wg:= &sync.WaitGroup{} for _, dom := range domains { uuid, err := dom.GetUUIDString() if err != nil { logger.Sugar().Error("Failed to get UUID for domain", err) continue } - - DC.DomainList[uuid] = &Domain{ + NewDom:= &Domain{ Domain: &dom, domainMutex: sync.Mutex{}, } - // logger.Infof("Added domain: UUID=%s", uuid) + DC.AddExistingDomain(NewDom,uuid) + + wg.Add(1) + go func(targetDom libvirt.Domain) { + defer wg.Done() + retrieveFunc := dataDog.Retreive(&targetDom, DC.DomainListStatus, *logger) + if retrieveFunc != nil { + logger.Sugar().Errorf("Failed to retrieve status for domain UUID=%s: %v", uuid, retrieveFunc) + } + + }(dom) logger.Sugar().Infof("Added domain: UUID=%s", uuid) } - + wg.Wait() + fmt.Printf("%+v", *DC.DomainListStatus) + logger.Sugar().Infof("Total %d domains added (state: %d)", len(domains), state) return nil } @@ -139,3 +171,5 @@ func (DC *DomListControl) GetAllUUIDs() []string { } return uuids } + +//////////////////////////////////////////////// \ No newline at end of file diff --git a/DomCon/domain_status/cpu_status.go b/DomCon/domain_status/cpu_status.go new file mode 100644 index 0000000..3795e2a --- /dev/null +++ b/DomCon/domain_status/cpu_status.go @@ -0,0 +1,34 @@ +package domStatus + +import ( + "runtime" + "sync/atomic" +) + +func (dls *DomainListStatus) UpdateCPUTotal() { + totalCPU := runtime.NumCPU() + dls.VCPUTotal = int64(totalCPU) +} + +func (dls *DomainListStatus) AddAllocatedCPU(vcpu int) error { + atomic.AddInt64(&dls.VcpuAllocated, int64(vcpu)) + return nil +} + +func (dls *DomainListStatus) AddSleepingCPU(vcpu int) error { + atomic.AddInt64(&dls.VcpuSleeping, int64(vcpu)) + return nil +} +func (dls *DomainListStatus) TakeAllocatedCPU(vcpu int) error { + + atomic.AddInt64(&dls.VcpuAllocated, -int64(vcpu)) + return nil +} + +func (dls *DomainListStatus) TakeSleepingCPU(vcpu int) error { + + atomic.AddInt64(&dls.VcpuSleeping, -int64(vcpu)) + return nil +} + + diff --git a/DomCon/domain_status/cpu_status_test.go b/DomCon/domain_status/cpu_status_test.go new file mode 100644 index 0000000..ad28ecb --- /dev/null +++ b/DomCon/domain_status/cpu_status_test.go @@ -0,0 +1,51 @@ +package domStatus + +import ( + "sync" + "testing" +) + + + +func TestVCPUAtomic(t *testing.T){ + dls := &DomainListStatus{} + + wg:= sync.WaitGroup{} + + wg.Add(1000) + for i:=0; i<1000; i++{ + go func(){ + dls.AddAllocatedCPU(4) + dls.AddSleepingCPU(1) + defer wg.Done() + }() + } + wg.Wait() + + result:= dls.VcpuAllocated + if result != 4000{ + t.Errorf("Expected 4000 allocated CPUs, got %d", result) + } + result1:=dls.VcpuSleeping + if result1 != 1000{ + t.Errorf("Expected 1000 sleeping CPUs, got %d", result) + } + wg.Add(450) + + for i:=0; i<450; i++{ + go func(){ + dls.TakeAllocatedCPU(4) + dls.TakeSleepingCPU(1) + defer wg.Done() + }() + } + wg.Wait() + finalAllocated:= dls.VcpuAllocated + if finalAllocated != 2200{ + t.Errorf("Expected 2200 allocated CPUs after taking, got %d", finalAllocated) + } + finalSleeping:= dls.VcpuSleeping + if finalSleeping != 550{ + t.Errorf("Expected 550 sleeping CPUs after taking, got %d", finalSleeping) + } +} \ No newline at end of file diff --git a/DomCon/domain_status/newStatus.go b/DomCon/domain_status/newStatus.go new file mode 100644 index 0000000..a41fcbf --- /dev/null +++ b/DomCon/domain_status/newStatus.go @@ -0,0 +1,49 @@ +package domStatus + +import ( + "fmt" + + "go.uber.org/zap" + "libvirt.org/go/libvirt" +) + + + +func NewDataDog(state libvirt.ConnectListAllDomainsFlags) DataDog { + switch state { + case libvirt.CONNECT_LIST_DOMAINS_ACTIVE: + fmt.Println("returning active") + return &libvirtStatus{} + case libvirt.CONNECT_LIST_DOMAINS_INACTIVE: + fmt.Println("returning inactive") + return &XMLStatus{} + default: + return nil + } +} + + +func (ds *XMLStatus) Retreive(dom *libvirt.Domain,DLS *DomainListStatus, logger zap.Logger) ( error) { + domcnf, err := XMLUnparse(dom) + if err != nil { + logger.Error("failed to unparse domain XML", zap.Error(err)) + return nil + } + DLS.AddAllocatedCPU(int(domcnf.VCPU.Value)) + DLS.AddSleepingCPU(int(domcnf.VCPU.Value)) + return nil + +} + +func (ls *libvirtStatus) Retreive(dom *libvirt.Domain, DLS *DomainListStatus, logger zap.Logger) (error) { + cpuCount, err := dom.GetMaxVcpus() + if err != nil { + logger.Error("failed to get live vcpu count", zap.Error(err)) + return nil + } + + fmt.Printf("%+v", cpuCount) + DLS.AddAllocatedCPU(int(cpuCount)) + return nil + +} \ No newline at end of file diff --git a/DomCon/domain_status/status.go b/DomCon/domain_status/status.go new file mode 100644 index 0000000..c01470f --- /dev/null +++ b/DomCon/domain_status/status.go @@ -0,0 +1,67 @@ +package domStatus + +import ( + "go.uber.org/zap" + "libvirt.org/go/libvirt" +) + +// cpu 는 각각 4개의 상태를 가짐 +// 할당되어 활동중임 +// 할당되어 있으나 유휴상태임(도메인이 꺼져있는 상태) +// 할당되어 있지 않음 +// 총 cpu 수 + +// 우선순위 = 할당 되어 있지 않은 cpu 부터 +// 그 이후 할당되어 있으나 유휴 상태인 cpu + +type DataDog interface { + Retreive(*libvirt.Domain,*DomainListStatus ,zap.Logger) ( error ) +} + +type XMLStatus struct{ +} +// 꺼져있는 도메인의 cpu 수 + +type libvirtStatus struct{ +} +// 할당되어 활동중인 cpu 수 + +type DomainListStatus struct { + VCPUTotal int64 // 호스트 전체 cpu 수 + VcpuAllocated int64 // 할당 된 vcpu 수 + VcpuSleeping int64 // 유휴 상태인 vcpu 수 + // vcpuIdle = 할당되어 있지 않은 vcpu 수 + //VcpuIdle = VcpuTotal-VcpuAllocated +} + +////////////////////////////// + + +type StatusEmitter interface{ + EmitStatus(dls *DomainListStatus) ( error) +} +// 상태 반환을 위한 인터페이스 +// 각 상태 구조체는 EmitStatus 메서드를 구현해야함 +// status service 에서 사용 + + +type VCPUStatus struct{ + Total int `json:"total"` + Allocated int `json:"allocated"` + Sleeping int `json:"sleeping"` + Idle int `json:"idle"` +} +// 인터페이스 구현체 + +func (vs *VCPUStatus) EmitStatus(dls *DomainListStatus) ( error) { + vs.Total = int(dls.VCPUTotal) + vs.Allocated = int(dls.VcpuAllocated) + vs.Sleeping = int(dls.VcpuSleeping) + + vs.Idle = vs.Total - vs.Allocated + if vs.Idle < 0 { + vs.Idle = 0 + } + + return nil +} diff --git a/DomCon/domain_status/xml_unparse.go b/DomCon/domain_status/xml_unparse.go new file mode 100644 index 0000000..1f60b86 --- /dev/null +++ b/DomCon/domain_status/xml_unparse.go @@ -0,0 +1,26 @@ +package domStatus + +import ( + "fmt" + + "libvirt.org/go/libvirt" + libvirtxml "libvirt.org/libvirt-go-xml" +) + +// 꺼져있는 도메인의 xml 을 파싱하여 도메인 상태를 업데이트 +func XMLUnparse(domain * libvirt.Domain) (*libvirtxml.Domain, error) { + + domainXML, err := domain.GetXMLDesc(0) + if err != nil { + return nil,fmt.Errorf("%xerror occured while calling xml specification", err) + } + domcnf := &libvirtxml.Domain{} + + err= domcnf.Unmarshal(domainXML) + if err != nil { + return nil, fmt.Errorf("%x error occured while unmarshalling xml, check for crushed format", err) + } + + return domcnf, nil +} + diff --git a/KWS_Server b/KWS_CORE similarity index 53% rename from KWS_Server rename to KWS_CORE index 5ce5712..6e7807c 100755 Binary files a/KWS_Server and b/KWS_CORE differ diff --git a/KWS_Core b/KWS_Core index ac6f839..742fd8b 100755 Binary files a/KWS_Core and b/KWS_Core differ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3a7d67c --- /dev/null +++ b/LICENSE @@ -0,0 +1,23 @@ +The MIT License + +Copyright (c) 2024-2025 Kwon Taekyoung +Copyright (c) 2024-2025 Jeon ByungChan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/api/Control.go b/api/Control.go index 71db0e1..b658d7f 100755 --- a/api/Control.go +++ b/api/Control.go @@ -10,70 +10,92 @@ import ( "libvirt.org/go/libvirt" ) - - -func (i *InstHandler)ForceShutDownVM(w http.ResponseWriter, r *http.Request){ - param:= &DeleteDomain{} - resp:=ResponseGen[any]("domain number of"+param.UUID+", Force Shutdown VM") - - if err:=HttpDecoder(r,param); err!=nil{ - resp.ResponseWriteErr(w,virerr.ErrorJoin(err,fmt.Errorf("error shutting down vm, from forceShutdown vm ")), http.StatusInternalServerError) +func (i *InstHandler) ForceShutDownVM(w http.ResponseWriter, r *http.Request) { + param := &DeleteDomain{} + resp := ResponseGen[any]("domain number of" + param.UUID + ", Force Shutdown VM") + + if err := HttpDecoder(r, param); err != nil { + ERR := virerr.ErrorJoin(err, fmt.Errorf("error shutting down vm, from forceShutdown vm ")) + resp.ResponseWriteErr(w, ERR, http.StatusInternalServerError) + i.Logger.Error(ERR.Error()) return } fmt.Println(resp) - dom,err:= i.DomainControl.GetDomain(param.UUID,i.LibvirtInst) - if err!= nil{ - resp.ResponseWriteErr(w,virerr.ErrorJoin(err,fmt.Errorf("error shutting down vm, retreving Get domin error ")), http.StatusInternalServerError) + dom, err := i.DomainControl.GetDomain(param.UUID, i.LibvirtInst) + if err != nil { + ERR := virerr.ErrorJoin(err, fmt.Errorf("error shutting down vm, retreving Get domin error ")) + resp.ResponseWriteErr(w, ERR, http.StatusInternalServerError) + i.Logger.Error(ERR.Error()) return } - - DomainTerminator,_:= termination.DomainTerminatorFactory(dom) - - _,err=DomainTerminator.TerminateDomain() - if err!= nil{ - resp.ResponseWriteErr(w,virerr.ErrorJoin(err,fmt.Errorf("error shutting down vm, retreving Get domin error ")), http.StatusInternalServerError) + vcpu, err := dom.Domain.GetMaxVcpus() + if err != nil { + ERR := virerr.ErrorJoin(err, fmt.Errorf("error shutting down vm, retreving Get domin error ")) + resp.ResponseWriteErr(w, ERR, http.StatusInternalServerError) + i.Logger.Error(ERR.Error()) return } + i.DomainControl.DomainListStatus.AddSleepingCPU(int(vcpu)) - - resp.ResponseWriteOK(w,nil) - } - + DomainTerminator, _ := termination.DomainTerminatorFactory(dom) + _, err = DomainTerminator.TerminateDomain() + if err != nil { + resp.ResponseWriteErr(w, virerr.ErrorJoin(err, fmt.Errorf("error shutting down vm, retreving Get domin error ")), http.StatusInternalServerError) + return + } + resp.ResponseWriteOK(w, nil) +} -func (i *InstHandler)DeleteVM(w http.ResponseWriter, r *http.Request){ - param:=&DeleteDomain{} - resp:= ResponseGen[libvirt.DomainInfo]("Deleting Vm") +func (i *InstHandler) DeleteVM(w http.ResponseWriter, r *http.Request) { + param := &DeleteDomain{} + resp := ResponseGen[libvirt.DomainInfo]("Deleting Vm") - if err:=HttpDecoder(r,param); err!=nil{ - resp.ResponseWriteErr(w,virerr.ErrorJoin(err,fmt.Errorf("error deleting vm, retreving Get domin error ")),http.StatusInternalServerError) + if err := HttpDecoder(r, param); err != nil { + ERR := virerr.ErrorJoin(err, fmt.Errorf("error deleting vm, unparsing HTTP request ")) + resp.ResponseWriteErr(w, ERR, http.StatusInternalServerError) + i.Logger.Error(ERR.Error()) return } - if _, err := domCon.ReturnUUID(param.UUID); err!=nil{ - resp.ResponseWriteErr(w,virerr.ErrorJoin(err,fmt.Errorf("error deleting vm, retreving Get domin error ")),http.StatusInternalServerError) + if _, err := domCon.ReturnUUID(param.UUID); err != nil { + ERR := virerr.ErrorJoin(err, fmt.Errorf("error deleting vm, invalid UUID ")) + resp.ResponseWriteErr(w, ERR, http.StatusInternalServerError) + i.Logger.Error(ERR.Error()) return } // uuid 가 적합한지 확인 - - domain, err := i.DomainControl.GetDomain(param.UUID,i.LibvirtInst) - if err!=nil{ - resp.ResponseWriteErr(w,virerr.ErrorJoin(err,fmt.Errorf("error deleting vm, retreving Get domin error ")),http.StatusInternalServerError) + domain, err := i.DomainControl.GetDomain(param.UUID, i.LibvirtInst) + if err != nil { + ERR := virerr.ErrorJoin(err, fmt.Errorf("error deleting vm, retreving Get domin error ")) + resp.ResponseWriteErr(w, ERR, http.StatusInternalServerError) + i.Logger.Error(ERR.Error()) return - //error handling + //error handling } - DomainDeleter,_:=termination.DomainDeleterFactory(domain, param.DeletionType, param.UUID) - domDeleted,err:=DomainDeleter.DeleteDomain() - if err!=nil{ - resp.ResponseWriteErr(w,virerr.ErrorJoin(err,fmt.Errorf("error deleting vm, retreving Get domin error ")),http.StatusInternalServerError) - fmt.Println(err) + vcpu, err := domain.Domain.GetMaxVcpus() + if err != nil { + ERR := virerr.ErrorJoin(err, fmt.Errorf("error can't retreving vcpu count ")) + resp.ResponseWriteErr(w, ERR, http.StatusInternalServerError) + i.Logger.Error(ERR.Error()) + + vcpu = 2 + //return + //일단 지금은 해당 경우에 vcpu 숫자를 2로 설정 + } // 삭제된 도메인에서는 vcpu count 를 가져올 수 없으므로 미리 가져옴 . 맘에 안듦. 나중에 수정할 예정 + // TODO: GETMAXVCPU는 꺼진 도메인에 대해 동작하지 않음. DATADOG와 같은 인터페이스를 활용해서 상관없이 삭제할 수 있도록 + + DomainDeleter, _ := termination.DomainDeleterFactory(domain, param.DeletionType, param.UUID) + domDeleted, err := DomainDeleter.DeleteDomain() + if err != nil { + ERR := virerr.ErrorJoin(err, fmt.Errorf("error deleting vm, retreving Get domin error ")) + resp.ResponseWriteErr(w, ERR, http.StatusInternalServerError) + i.Logger.Error(ERR.Error()) return } - i.DomainControl.DeleteDomain(domDeleted,param.UUID) - + i.DomainControl.DeleteDomain(domDeleted, param.UUID, int(vcpu)) - resp.ResponseWriteOK(w,nil) + resp.ResponseWriteOK(w, nil) } - diff --git a/api/Create.go b/api/Create.go index 9aaf680..038537d 100755 --- a/api/Create.go +++ b/api/Create.go @@ -11,11 +11,54 @@ import ( "libvirt.org/go/libvirt" ) +func (i *InstHandler) BootVM(w http.ResponseWriter, r *http.Request) { + resp := ResponseGen[libvirt.DomainInfo]("BootVM") + param := &StartDomain{} + + if err := HttpDecoder(r, param); err != nil { + resp.ResponseWriteErr(w, err, http.StatusBadRequest) + i.Logger.Error("error occured while decoding user's parameter of requested creation") + return + } + i.Logger.Info("Handling Boot VM", zap.String("uuid", param.UUID)) + + domCon,_:= i.domainConGetter() + + DomainExisting, _ := domCon.GetDomain(param.UUID, i.LibvirtInst) + if (DomainExisting==nil){ + resp.ResponseWriteErr(w, nil, http.StatusBadRequest) + i.Logger.Error("error handling booting vm, domain not found", zap.String("uuid",param.UUID)) + return + } + + err:= DomainExisting.Domain.Create() + if err != nil { + newErr := virerr.ErrorGen(virerr.DomainGenerationError, fmt.Errorf(" %w error while booting domain, from BootVM", err)) + i.Logger.Error("error from booting vm", zap.Error(newErr)) + resp.ResponseWriteErr(w, newErr, http.StatusInternalServerError) + return + } + + vcpu, err := DomainExisting.Domain.GetMaxVcpus() + if err != nil { + resp.ResponseWriteErr(w, err, http.StatusInternalServerError) + return + } + i.DomainControl.DomainListStatus.TakeSleepingCPU(int(vcpu)) + + + resp.ResponseWriteOK(w, nil) + i.Logger.Info("Boot VM request handled successfully", zap.String("uuid", param.UUID)) +} + + + func (i *InstHandler) CreateVMFromBase(w http.ResponseWriter, r *http.Request) { resp := ResponseGen[libvirt.DomainInfo]("CreateVm") param := &parsor.VM_Init_Info{} + domCon,_:= i.domainConGetter() if domCon==nil{ fmt.Println("emrpy domcon") @@ -48,8 +91,13 @@ func (i *InstHandler) CreateVMFromBase(w http.ResponseWriter, r *http.Request) { return } - domCon.AddNewDomain(newDomain,param.UUID) - + err =domCon.AddNewDomain(newDomain,param.UUID) + if err!=nil{ + i.Logger.Error("error from createvm" , zap.Error(err)) + resp.ResponseWriteErr(w, err, http.StatusInternalServerError) + return + } + resp.ResponseWriteOK(w, nil) } diff --git a/api/snapshot.go b/api/snapshot.go new file mode 100644 index 0000000..5c91820 --- /dev/null +++ b/api/snapshot.go @@ -0,0 +1,176 @@ +package api + +import ( + "fmt" + "net/http" + + snapshotpkg "github.com/easy-cloud-Knet/KWS_Core/vm/service/snapshot" + "go.uber.org/zap" +) + +// Snapshot API structures +type SnapshotRequest struct { + UUID string `json:"UUID"` + Name string `json:"Name,omitempty"` +} + +// CreateSnapshot creates a snapshot for the specified domain UUID +func (i *InstHandler) CreateSnapshot(w http.ResponseWriter, r *http.Request) { + param := &SnapshotRequest{} + resp := ResponseGen[string]("Create Snapshot") + + if err := HttpDecoder(r, param); err != nil { + resp.ResponseWriteErr(w, err, http.StatusInternalServerError) + i.Logger.Error("snapshot create decode failed", zap.Error(err)) + return + } + + if i.LibvirtInst == nil || i.DomainControl == nil { + resp.ResponseWriteErr(w, fmt.Errorf("libvirt not initialized"), http.StatusInternalServerError) + i.Logger.Error("libvirt not initialized") + return + } + + name := param.Name + if name == "" { + name = param.UUID + "-snap" + } + + i.Logger.Info("snapshot create start", zap.String("domain_uuid", param.UUID), zap.String("snapshot_name", name)) + + dom, err := i.DomainControl.GetDomain(param.UUID, i.LibvirtInst) + if err != nil { + resp.ResponseWriteErr(w, err, http.StatusInternalServerError) + i.Logger.Error("snapshot create failed - domain not found", zap.String("domain_uuid", param.UUID), zap.Error(err)) + return + } + + snapName, err := snapshotpkg.CreateSnapshot(dom, name) + if err != nil { + resp.ResponseWriteErr(w, err, http.StatusInternalServerError) + i.Logger.Error("snapshot create failed", zap.String("domain_uuid", param.UUID), zap.String("snapshot_name", name), zap.Error(err)) + return + } + + i.Logger.Info("snapshot create success", zap.String("domain_uuid", param.UUID), zap.String("snapshot_name", snapName)) + resp.ResponseWriteOK(w, &snapName) +} + +// ListSnapshots returns all snapshot names for the specified domain UUID +func (i *InstHandler) ListSnapshots(w http.ResponseWriter, r *http.Request) { + param := &SnapshotRequest{} + resp := ResponseGen[[]string]("List Snapshots") + + if err := HttpDecoder(r, param); err != nil { + resp.ResponseWriteErr(w, err, http.StatusInternalServerError) + i.Logger.Error("snapshot list decode failed", zap.Error(err)) + return + } + + if i.LibvirtInst == nil || i.DomainControl == nil { + resp.ResponseWriteErr(w, fmt.Errorf("libvirt not initialized"), http.StatusInternalServerError) + i.Logger.Error("libvirt not initialized") + return + } + + i.Logger.Info("snapshot list start", zap.String("domain_uuid", param.UUID)) + + dom, err := i.DomainControl.GetDomain(param.UUID, i.LibvirtInst) + if err != nil { + resp.ResponseWriteErr(w, err, http.StatusInternalServerError) + i.Logger.Error("snapshot list failed - domain not found", zap.String("domain_uuid", param.UUID), zap.Error(err)) + return + } + + names, err := snapshotpkg.ListSnapshots(dom) + if err != nil { + resp.ResponseWriteErr(w, err, http.StatusInternalServerError) + i.Logger.Error("snapshot list failed", zap.String("domain_uuid", param.UUID), zap.Error(err)) + return + } + + i.Logger.Info("snapshot list success", zap.String("domain_uuid", param.UUID), zap.Int("snapshot_count", len(names))) + resp.ResponseWriteOK(w, &names) +} + +// RevertSnapshot reverts the domain to a named snapshot +func (i *InstHandler) RevertSnapshot(w http.ResponseWriter, r *http.Request) { + param := &SnapshotRequest{} + resp := ResponseGen[any]("Revert Snapshot") + + if err := HttpDecoder(r, param); err != nil { + resp.ResponseWriteErr(w, err, http.StatusInternalServerError) + i.Logger.Error("snapshot revert decode failed", zap.Error(err)) + return + } + + if i.LibvirtInst == nil || i.DomainControl == nil { + resp.ResponseWriteErr(w, fmt.Errorf("libvirt not initialized"), http.StatusInternalServerError) + i.Logger.Error("libvirt not initialized") + return + } + + if param.Name == "" { + resp.ResponseWriteErr(w, fmt.Errorf("snapshot name required"), http.StatusBadRequest) + return + } + + i.Logger.Info("snapshot revert start", zap.String("domain_uuid", param.UUID), zap.String("snapshot_name", param.Name)) + + dom, err := i.DomainControl.GetDomain(param.UUID, i.LibvirtInst) + if err != nil { + resp.ResponseWriteErr(w, err, http.StatusInternalServerError) + i.Logger.Error("snapshot revert failed - domain not found", zap.String("domain_uuid", param.UUID), zap.Error(err)) + return + } + + if err := snapshotpkg.RevertToSnapshot(dom, param.Name); err != nil { + resp.ResponseWriteErr(w, err, http.StatusInternalServerError) + i.Logger.Error("snapshot revert failed", zap.String("domain_uuid", param.UUID), zap.String("snapshot_name", param.Name), zap.Error(err)) + return + } + + i.Logger.Info("snapshot revert success", zap.String("domain_uuid", param.UUID), zap.String("snapshot_name", param.Name)) + resp.ResponseWriteOK(w, nil) +} + +// DeleteSnapshot deletes a snapshot by name +func (i *InstHandler) DeleteSnapshot(w http.ResponseWriter, r *http.Request) { + param := &SnapshotRequest{} + resp := ResponseGen[any]("Delete Snapshot") + + if err := HttpDecoder(r, param); err != nil { + resp.ResponseWriteErr(w, err, http.StatusInternalServerError) + i.Logger.Error("snapshot delete decode failed", zap.Error(err)) + return + } + + if i.LibvirtInst == nil || i.DomainControl == nil { + resp.ResponseWriteErr(w, fmt.Errorf("libvirt not initialized"), http.StatusInternalServerError) + i.Logger.Error("libvirt not initialized") + return + } + + if param.Name == "" { + resp.ResponseWriteErr(w, fmt.Errorf("snapshot name required"), http.StatusBadRequest) + return + } + + i.Logger.Info("snapshot delete start", zap.String("domain_uuid", param.UUID), zap.String("snapshot_name", param.Name)) + + dom, err := i.DomainControl.GetDomain(param.UUID, i.LibvirtInst) + if err != nil { + resp.ResponseWriteErr(w, err, http.StatusInternalServerError) + i.Logger.Error("snapshot delete failed - domain not found", zap.String("domain_uuid", param.UUID), zap.Error(err)) + return + } + + if err := snapshotpkg.DeleteSnapshot(dom, param.Name); err != nil { + resp.ResponseWriteErr(w, err, http.StatusInternalServerError) + i.Logger.Error("snapshot delete failed", zap.String("domain_uuid", param.UUID), zap.String("snapshot_name", param.Name), zap.Error(err)) + return + } + + i.Logger.Info("snapshot delete success", zap.String("domain_uuid", param.UUID), zap.String("snapshot_name", param.Name)) + resp.ResponseWriteOK(w, nil) +} diff --git a/api/status.go b/api/status.go index 5c53cee..9f27963 100755 --- a/api/status.go +++ b/api/status.go @@ -12,6 +12,11 @@ import ( "go.uber.org/zap" ) + + + + + func (i *InstHandler) ReturnStatusUUID(w http.ResponseWriter, r *http.Request) { param := &ReturnDomainFromUUID{} resp := ResponseGen[status.DataTypeHandler]("domain Status UUID") @@ -42,6 +47,8 @@ func (i *InstHandler) ReturnStatusUUID(w http.ResponseWriter, r *http.Request) { } + + func (i *InstHandler) ReturnStatusHost(w http.ResponseWriter, r *http.Request) { param := &ReturnHostFromStatus{} resp := ResponseGen[status.HostDataTypeHandler]("Host Status Return") @@ -57,8 +64,10 @@ func (i *InstHandler) ReturnStatusHost(w http.ResponseWriter, r *http.Request) { resp.ResponseWriteErr(w, err, http.StatusInternalServerError) return } + fmt.Println("data sending", reflect.TypeOf(dataHandle)) - host, err := status.HostDetailFactory(dataHandle) + + host, err := status.HostInfoHandler(dataHandle, i.DomainControl.DomainListStatus) if err != nil { resp.ResponseWriteErr(w, err, http.StatusInternalServerError) } diff --git a/build/node_exporter/LICENSE b/build/node_exporter/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/build/node_exporter/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/build/node_exporter/NOTICE b/build/node_exporter/NOTICE new file mode 100644 index 0000000..970a9b2 --- /dev/null +++ b/build/node_exporter/NOTICE @@ -0,0 +1,17 @@ +Configurable modular Prometheus exporter for various node metrics. +Copyright 2013-2015 The Prometheus Authors + +This product includes software developed at +SoundCloud Ltd. (http://soundcloud.com/). + +The following components are included in this product: + +wifi +https://github.com/mdlayher/wifi +Copyright 2016-2017 Matt Layher +Licensed under the MIT License + +netlink +https://github.com/mdlayher/netlink +Copyright 2016-2017 Matt Layher +Licensed under the MIT License diff --git a/build/node_exporter/node_exporter b/build/node_exporter/node_exporter new file mode 100755 index 0000000..97db22e Binary files /dev/null and b/build/node_exporter/node_exporter differ diff --git a/build/node_exporter/node_exporter-1.9.1.linux-amd64/LICENSE b/build/node_exporter/node_exporter-1.9.1.linux-amd64/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/build/node_exporter/node_exporter-1.9.1.linux-amd64/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/build/node_exporter/node_exporter-1.9.1.linux-amd64/NOTICE b/build/node_exporter/node_exporter-1.9.1.linux-amd64/NOTICE new file mode 100644 index 0000000..970a9b2 --- /dev/null +++ b/build/node_exporter/node_exporter-1.9.1.linux-amd64/NOTICE @@ -0,0 +1,17 @@ +Configurable modular Prometheus exporter for various node metrics. +Copyright 2013-2015 The Prometheus Authors + +This product includes software developed at +SoundCloud Ltd. (http://soundcloud.com/). + +The following components are included in this product: + +wifi +https://github.com/mdlayher/wifi +Copyright 2016-2017 Matt Layher +Licensed under the MIT License + +netlink +https://github.com/mdlayher/netlink +Copyright 2016-2017 Matt Layher +Licensed under the MIT License diff --git a/build/node_exporter/node_exporter-1.9.1.linux-amd64/node_exporter b/build/node_exporter/node_exporter-1.9.1.linux-amd64/node_exporter new file mode 100755 index 0000000..97db22e Binary files /dev/null and b/build/node_exporter/node_exporter-1.9.1.linux-amd64/node_exporter differ diff --git a/go.mod b/go.mod index 5ee1f5a..668b39d 100755 --- a/go.mod +++ b/go.mod @@ -1,6 +1,8 @@ module github.com/easy-cloud-Knet/KWS_Core -go 1.22.6 +go 1.24.0 + +toolchain go1.24.10 require ( gopkg.in/yaml.v3 v3.0.1 @@ -11,6 +13,7 @@ require ( github.com/google/uuid v1.6.0 github.com/shirou/gopsutil v3.21.11+incompatible go.uber.org/zap v1.27.0 + libvirt.org/libvirt-go-xml v7.4.0+incompatible ) require ( @@ -19,5 +22,5 @@ require ( github.com/tklauser/numcpus v0.8.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/sys v0.29.0 // indirect + golang.org/x/sys v0.38.0 // indirect ) diff --git a/go.sum b/go.sum index 3d3dff3..0981a6e 100644 --- a/go.sum +++ b/go.sum @@ -23,11 +23,13 @@ go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN8 go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= libvirt.org/go/libvirt v1.10006.0 h1:VzbLKReneWBIiplgOvZHxMiLLJ0HxAyp4MMPcYTHJjY= libvirt.org/go/libvirt v1.10006.0/go.mod h1:1WiFE8EjZfq+FCVog+rvr1yatKbKZ9FaFMZgEqxEJqQ= +libvirt.org/libvirt-go-xml v7.4.0+incompatible h1:NaCRjbtz//xuTZOp1nDHbe0eu5BQlhIy5PPuc09EWtU= +libvirt.org/libvirt-go-xml v7.4.0+incompatible/go.mod h1:FL+H1+hKNWDdkKQGGS4sGCZJ3pGWcjt6VbxZvPlQJkY= diff --git a/main.go b/main.go index afb9d57..596df8f 100755 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( _ "context" + "fmt" _ "log" "runtime/debug" @@ -23,10 +24,11 @@ func main() { } libvirtInst.LibvirtConnection() + libvirtInst.DomainControl.DomainListStatus.UpdateCPUTotal() libvirtInst.DomainControl.RetrieveAllDomain(libvirtInst.LibvirtInst, logger) go server.InitServer(8080, &libvirtInst, *logger) - + fmt.Println("asfd") defer func() { logger.Info("Shutting down gracefully...") // 종료 시 로깅 logger.Sync() diff --git a/server/server.go b/server/server.go index f1e8a2a..963a84f 100755 --- a/server/server.go +++ b/server/server.go @@ -14,6 +14,7 @@ func InitServer(portNum int, libvirtInst *api.InstHandler, logger zap.Logger) { logger.Sugar().Infof("Starting server on %d", portNum) mux := http.NewServeMux() + mux.HandleFunc("POST /BOOTVM", libvirtInst.BootVM) //post mux.HandleFunc("POST /createVM", libvirtInst.CreateVMFromBase) //post mux.HandleFunc("GET /getStatusUUID", libvirtInst.ReturnStatusUUID) //Get mux.HandleFunc("POST /forceShutDownUUID", libvirtInst.ForceShutDownVM) //POST @@ -23,6 +24,12 @@ func InitServer(portNum int, libvirtInst *api.InstHandler, logger zap.Logger) { mux.HandleFunc("GET /getAllUUIDs", libvirtInst.ReturnAllUUIDs) //Get mux.HandleFunc("GET /getAll-uuidstatusList", libvirtInst.ReturnAllDomainStates) + // Snapshot operations + mux.HandleFunc("POST /CreateSnapshot", libvirtInst.CreateSnapshot) + mux.HandleFunc("GET /ListSnapshots", libvirtInst.ListSnapshots) + mux.HandleFunc("POST /RevertSnapshot", libvirtInst.RevertSnapshot) + mux.HandleFunc("POST /DeleteSnapshot", libvirtInst.DeleteSnapshot) + sysloggerHttp := syslogger.LoggerMiddleware(mux, &logger) log.Fatal(http.ListenAndServe(":"+strconv.Itoa(portNum), sysloggerHttp)) diff --git a/vm/parsor/XML_Parsor.go b/vm/parsor/XML_Parsor.go index 6b5c63c..61dbc41 100755 --- a/vm/parsor/XML_Parsor.go +++ b/vm/parsor/XML_Parsor.go @@ -93,6 +93,7 @@ func (XP *VM_CREATE_XML) XML_Parsor(spec *VM_Init_Info) error { InterfaceID: spec.SDNUUID, }, }, + MTU: MTU{ Size: 1450}, // MTU 설정 추가 Model: InterfaceModel{ Type: "virtio", }, diff --git a/vm/parsor/cloud-init/userConf.go b/vm/parsor/cloud-init/userConf.go index ca1334c..6f74ccf 100755 --- a/vm/parsor/cloud-init/userConf.go +++ b/vm/parsor/cloud-init/userConf.go @@ -3,6 +3,7 @@ package userconfig import ( "bytes" "fmt" + "log" "os" "os/exec" "path/filepath" @@ -64,16 +65,37 @@ func (u *User_data_yaml) ParseData(param *parsor.VM_Init_Info) error { }) } File_Appendor := u.configNetworkIP(param.NetConf.Ips) - File_Appendor = u.newWaitDisable(File_Appendor) + appending,err:= u.fetchos() + if err!=nil{ + log.Printf("error from appending file, %v, ignoring", err) + } + File_Appendor = append(File_Appendor,appending... ) u.Users = Users_Detail u.Write_files = File_Appendor u.configNetworkCommand() u.configQEMU() u.configSsh() + return nil } +func (u *User_data_yaml) fetchos() ([]User_write_file,error){ + var fetchFile []User_write_file + + data, err := os.ReadFile("/var/lib/kws/baseimg/fetchos") + if err != nil { + return fetchFile,fmt.Errorf("fetchos parsing error, ingnorable but needs report%v",err) + } + + fetchFile= append(fetchFile, User_write_file{ + Path: "/etc/profile.d/99-my-motd.sh", + Permissions: "0644", + Content: string(data), + }) + return fetchFile,nil +} + func (u *User_data_yaml) configNetworkIP(ips []string) []User_write_file { var File_Appendor []User_write_file @@ -92,16 +114,7 @@ func (u *User_data_yaml) configNetworkIP(ips []string) []User_write_file { } -func (u *User_data_yaml) newWaitDisable(File_Appendor []User_write_file) []User_write_file { - File_Appendor = append(File_Appendor, User_write_file{ - Path: "/etc/systemd/system/systemd-networkd-wait-online.service.d/override.conf", - Permissions: "0644", - Content: fmt.Sprintf("[Service]\nExecStart=\nExecStart=/usr/bin/true\n"), - }) - - return File_Appendor -} func (u *User_data_yaml) configSsh(){ u.Runcmd = append(u.Runcmd, "sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/g' /etc/ssh/sshd_config.d/60-cloudimg-settings.conf") diff --git a/vm/parsor/type.go b/vm/parsor/type.go index ed44706..d75893c 100755 --- a/vm/parsor/type.go +++ b/vm/parsor/type.go @@ -30,6 +30,7 @@ type User_info_VM struct { type HardwareInfo struct { CPU int `json:"cpu"` Memory int `json:"memory"` + Disk int `json:"disk"` } // gonna replace fields in VM_Init_Info @@ -152,7 +153,12 @@ type Interface struct { Virtualport VirPort `xml:"virtualport"` Model InterfaceModel `xml:"model"` MacAddress MacAddress `xml:"mac"` + MTU MTU `xml:"mtu"` } +type MTU struct { + Size int `xml:"size,attr"` +} + type VirPort struct { Type string `xml:"type,attr"` Parameter Parameter `xml:"parameters"` diff --git a/vm/service/creation/common.go b/vm/service/creation/common.go index 9adda9b..41ded9f 100755 --- a/vm/service/creation/common.go +++ b/vm/service/creation/common.go @@ -9,14 +9,14 @@ import ( ) -func (DB localConfigurer)CreateDiskImage(dirPath string) error { +func (DB localConfigurer)CreateDiskImage(dirPath string, diskSize int) error { baseImage := fmt.Sprintf("/var/lib/kws/baseimg/%s", DB.VMDescription.OS) targetImage := filepath.Join(dirPath, fmt.Sprintf("%s.qcow2", DB.VMDescription.UUID)) qemuImgCmd := exec.Command("qemu-img", "create", "-b", baseImage, "-f", "qcow2", "-F", "qcow2", - targetImage, "15G", + targetImage, fmt.Sprintf("%dG", diskSize), // 10G ) if err := qemuImgCmd.Run(); err != nil { errorDescription := fmt.Errorf("generating Disk image error, may have duplicdated uuid or lack of HD capacity %s, %v", dirPath, err) diff --git a/vm/service/creation/local_domain.go b/vm/service/creation/local_domain.go index 76deae6..eb1384d 100755 --- a/vm/service/creation/local_domain.go +++ b/vm/service/creation/local_domain.go @@ -96,7 +96,7 @@ func (DB localConfigurer) Generate(LibvirtInst *libvirt.Connect, logger *zap.Log } logger.Info("generating configuration file successfully done", zap.String("filePath", dirPath)) - if err := DB.CreateDiskImage(dirPath); err != nil { + if err := DB.CreateDiskImage(dirPath, DB.VMDescription.HardwardInfo.Disk); err != nil { errorEncapsed := virerr.ErrorJoin(err, fmt.Errorf("in domain-parsor,")) logger.Error(errorEncapsed.Error()) return errorEncapsed diff --git a/vm/service/snapshot/operations.go b/vm/service/snapshot/operations.go new file mode 100644 index 0000000..1104744 --- /dev/null +++ b/vm/service/snapshot/operations.go @@ -0,0 +1,124 @@ +package snapshot + +import ( + "fmt" + + domCon "github.com/easy-cloud-Knet/KWS_Core/DomCon" + "libvirt.org/go/libvirt" +) + +// CreateSnapshot creates a libvirt snapshot and records basic metadata. +func CreateSnapshot(domain *domCon.Domain, name string) (string, error) { + if domain == nil || domain.Domain == nil { + return "", fmt.Errorf("nil domain") + } + + snapXML := fmt.Sprintf(`%ssnapshot created by KWS`, name) + + snap, err := domain.Domain.CreateSnapshotXML(snapXML, 0) + if err != nil { + return "", fmt.Errorf("failed to create snapshot: %w", err) + } + defer snap.Free() + + snapName, err := snap.GetName() + if err != nil { + return "", fmt.Errorf("snapshot created but failed to read name: %w", err) + } + + return snapName, nil +} + +// ListSnapshots lists snapshot names for the domain. +func ListSnapshots(domain *domCon.Domain) ([]string, error) { + if domain == nil || domain.Domain == nil { + return nil, fmt.Errorf("nil domain") + } + + snaps, err := domain.Domain.ListAllSnapshots(0) + if err != nil { + return nil, fmt.Errorf("failed to list snapshots: %w", err) + } + + names := make([]string, 0, len(snaps)) + for _, s := range snaps { + n, err := s.GetName() + if err == nil { + names = append(names, n) + } + s.Free() + } + + return names, nil +} + +// RevertToSnapshot reverts the domain to the given snapshot name. +func RevertToSnapshot(domain *domCon.Domain, snapName string) error { + if domain == nil || domain.Domain == nil { + return fmt.Errorf("nil domain") + } + + snaps, err := domain.Domain.ListAllSnapshots(0) + if err != nil { + return fmt.Errorf("failed to list snapshots: %w", err) + } + defer func() { + for _, s := range snaps { + s.Free() + } + }() + + var target *libvirt.DomainSnapshot + for i := range snaps { + n, err := snaps[i].GetName() + if err != nil { + continue + } + if n == snapName { + target = &snaps[i] + break + } + } + + if target == nil { + return fmt.Errorf("snapshot %s not found", snapName) + } + + if err := target.RevertToSnapshot(0); err != nil { + return fmt.Errorf("failed to revert to snapshot %s: %w", snapName, err) + } + + return nil +} + +// DeleteSnapshot deletes a snapshot by name. +func DeleteSnapshot(domain *domCon.Domain, snapName string) error { + if domain == nil || domain.Domain == nil { + return fmt.Errorf("nil domain") + } + + snaps, err := domain.Domain.ListAllSnapshots(0) + if err != nil { + return fmt.Errorf("failed to list snapshots: %w", err) + } + defer func() { + for _, s := range snaps { + s.Free() + } + }() + + for _, s := range snaps { + n, err := s.GetName() + if err != nil { + continue + } + if n == snapName { + if err := s.Delete(0); err != nil { + return fmt.Errorf("failed to delete snapshot %s: %w", snapName, err) + } + return nil + } + } + + return fmt.Errorf("snapshot %s not found", snapName) +} diff --git a/vm/service/status/Status.go b/vm/service/status/Status.go index 48be9d6..e628bb7 100755 --- a/vm/service/status/Status.go +++ b/vm/service/status/Status.go @@ -2,6 +2,7 @@ package status import ( domCon "github.com/easy-cloud-Knet/KWS_Core/DomCon" + domStatus "github.com/easy-cloud-Knet/KWS_Core/DomCon/domain_status" "libvirt.org/go/libvirt" ) @@ -10,7 +11,7 @@ type HostDetail struct { } type HostDataTypeHandler interface { - GetHostInfo() error + GetHostInfo(*domStatus.DomainListStatus) error } type HostDataType uint @@ -28,6 +29,7 @@ type HostCpuInfo struct { System float64 `json:"system_time"` Idle float64 `json:"idle_time"` Usage float64 `json:"usage_percent"` + Desc *domStatus.VCPUStatus `json:"vcpu_status"` } type HostMemoryInfo struct { @@ -36,6 +38,8 @@ type HostMemoryInfo struct { Available uint64 `json:"available_gb"` UsedPercent float64 `json:"used_percent"` ReservedMemory uint64 `json:"reservedmem"` + Desc *domStatus.VCPUStatus `json:"vcpu_status"` + } type HostDiskInfo struct { @@ -43,6 +47,8 @@ type HostDiskInfo struct { Used uint64 `json:"used_gb"` Free uint64 `json:"free_gb"` UsedPercent float64 `json:"used_percent"` + Desc *domStatus.VCPUStatus `json:"vcpu_status"` + } type HostSystemInfo struct { diff --git a/vm/service/status/host_status.go b/vm/service/status/host_status.go index c1a5f0a..85e13c9 100755 --- a/vm/service/status/host_status.go +++ b/vm/service/status/host_status.go @@ -6,6 +6,7 @@ import ( "log" "time" + domStatus "github.com/easy-cloud-Knet/KWS_Core/DomCon/domain_status" virerr "github.com/easy-cloud-Knet/KWS_Core/error" "github.com/shirou/gopsutil/cpu" "github.com/shirou/gopsutil/disk" @@ -13,7 +14,7 @@ import ( "github.com/shirou/gopsutil/mem" ) -func (CI *HostCpuInfo) GetHostInfo() error { +func (CI *HostCpuInfo) GetHostInfo(status *domStatus.DomainListStatus) error { t, err := cpu.Times(false) //time if err != nil { log.Println(err) @@ -32,10 +33,14 @@ func (CI *HostCpuInfo) GetHostInfo() error { } CI.Usage = p[0] + CI.Desc.EmitStatus(status) + + + return nil } -func (MI *HostMemoryInfo) GetHostInfo() error { +func (MI *HostMemoryInfo) GetHostInfo(status *domStatus.DomainListStatus) error { v, err := mem.VirtualMemory() if err != nil { log.Println(err) @@ -51,7 +56,7 @@ func (MI *HostMemoryInfo) GetHostInfo() error { return nil } -func (HDI *HostDiskInfo) GetHostInfo() error { +func (HDI *HostDiskInfo) GetHostInfo(status *domStatus.DomainListStatus) error { d, err := disk.Usage("/") if err != nil { log.Println(err) @@ -67,16 +72,16 @@ func (HDI *HostDiskInfo) GetHostInfo() error { return nil } -func (SI *HostGeneralInfo) GetHostInfo() error { - err:=SI.CPU.GetHostInfo() +func (SI *HostGeneralInfo) GetHostInfo(status *domStatus.DomainListStatus) error { + err:=SI.CPU.GetHostInfo(status) if err!=nil{ return virerr.ErrorGen(virerr.HostStatusError, fmt.Errorf("general Status:error retreving host Status %w",err)) } - err=SI.Disk.GetHostInfo() + err=SI.Disk.GetHostInfo(status) if err!=nil{ return virerr.ErrorGen(virerr.HostStatusError, fmt.Errorf("general Status:error retreving host Status %w",err)) } - err=SI.Memory.GetHostInfo() + err=SI.Memory.GetHostInfo(status) if err!=nil{ return virerr.ErrorGen(virerr.HostStatusError, fmt.Errorf("general Status:error retreving host Status %w",err)) } @@ -88,7 +93,7 @@ func (SI *HostGeneralInfo) GetHostInfo() error { -func (HSI *HostSystemInfo) GetHostInfo() error { +func (HSI *HostSystemInfo) GetHostInfo(status *domStatus.DomainListStatus) error { u, err := host.Uptime() if err != nil { log.Println(err) @@ -121,14 +126,22 @@ func (HSI *HostSystemInfo) GetHostInfo() error { } func HostDataTypeRouter(types HostDataType) (HostDataTypeHandler, error) { + // implemeantation of factory pattern + // can be extened as DI pattern if there are more complex dependencies switch types { case CpuInfo: - return &HostCpuInfo{}, nil + return &HostCpuInfo{ + Desc: &domStatus.VCPUStatus{}, + }, nil case MemInfo: - return &HostMemoryInfo{}, nil + return &HostMemoryInfo{ + Desc: &domStatus.VCPUStatus{},// --- IGNORE --- + }, nil case DiskInfoHi: - return &HostDiskInfo{}, nil + return &HostDiskInfo{ + Desc: &domStatus.VCPUStatus{},// --- IGNORE --- + }, nil case SystemInfoHi: return &HostSystemInfo{}, nil case GeneralInfo: @@ -140,8 +153,8 @@ func HostDataTypeRouter(types HostDataType) (HostDataTypeHandler, error) { -func HostDetailFactory(handler HostDataTypeHandler) (*HostDetail, error) { - if err := handler.GetHostInfo(); err != nil { +func HostInfoHandler(handler HostDataTypeHandler, status *domStatus.DomainListStatus) (*HostDetail, error) { + if err := handler.GetHostInfo(status); err != nil { fmt.Println(err) return nil, err }