1024 lines
28 KiB
Go
1024 lines
28 KiB
Go
|
|
/*
|
||
|
|
* Copyright (c) 2019 Huawei Technologies Co., Ltd.
|
||
|
|
* A-Tune is licensed under the Mulan PSL v1.
|
||
|
|
* You can use this software according to the terms and conditions of the Mulan PSL v1.
|
||
|
|
* You may obtain a copy of Mulan PSL v1 at:
|
||
|
|
* http://license.coscl.org.cn/MulanPSL
|
||
|
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
||
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
||
|
|
* PURPOSE.
|
||
|
|
* See the Mulan PSL v1 for more details.
|
||
|
|
* Create: 2019-10-29
|
||
|
|
*/
|
||
|
|
|
||
|
|
package main
|
||
|
|
|
||
|
|
import (
|
||
|
|
PB "atune/api/profile"
|
||
|
|
"atune/common/checker"
|
||
|
|
"atune/common/config"
|
||
|
|
"atune/common/http"
|
||
|
|
"atune/common/log"
|
||
|
|
"atune/common/models"
|
||
|
|
"atune/common/profile"
|
||
|
|
"atune/common/project"
|
||
|
|
"atune/common/schedule"
|
||
|
|
SVC "atune/common/service"
|
||
|
|
"atune/common/sqlstore"
|
||
|
|
"atune/common/tuning"
|
||
|
|
"atune/common/utils"
|
||
|
|
"bufio"
|
||
|
|
"context"
|
||
|
|
"encoding/json"
|
||
|
|
"fmt"
|
||
|
|
"io/ioutil"
|
||
|
|
"os"
|
||
|
|
"os/exec"
|
||
|
|
"path"
|
||
|
|
"regexp"
|
||
|
|
"sort"
|
||
|
|
"strconv"
|
||
|
|
"strings"
|
||
|
|
"syscall"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"github.com/go-ini/ini"
|
||
|
|
"github.com/urfave/cli"
|
||
|
|
"google.golang.org/grpc"
|
||
|
|
yaml "gopkg.in/yaml.v2"
|
||
|
|
)
|
||
|
|
|
||
|
|
// Monitor : the body send to monitor service
|
||
|
|
type Monitor struct {
|
||
|
|
Module string `json:"module"`
|
||
|
|
Purpose string `json:"purpose"`
|
||
|
|
Field string `json:"field"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// CollectorPost : the body send to collection service
|
||
|
|
type CollectorPost struct {
|
||
|
|
Monitors []Monitor `json:"monitors"`
|
||
|
|
SampleNum int `json:"sample_num"`
|
||
|
|
Pipe string `json:"pipe"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// RespCollectorPost : the response of collection servie
|
||
|
|
type RespCollectorPost struct {
|
||
|
|
Path string `json:"path"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// ClassifyPostBody : the body send to classify service
|
||
|
|
type ClassifyPostBody struct {
|
||
|
|
Data string `json:"data"`
|
||
|
|
ModelPath string `json:"modelpath,omitempty"`
|
||
|
|
Model string `json:"model,omitempty"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// RespClassify : the response of classify model
|
||
|
|
type RespClassify struct {
|
||
|
|
ResourceLimit string `json:"resource_limit"`
|
||
|
|
WorkloadType string `json:"workload_type"`
|
||
|
|
Percentage float32 `json:"percentage"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// ProfileServer : the type impletent the grpc server
|
||
|
|
type ProfileServer struct {
|
||
|
|
utils.MutexLock
|
||
|
|
ConfPath string
|
||
|
|
ScriptPath string
|
||
|
|
Raw *ini.File
|
||
|
|
}
|
||
|
|
|
||
|
|
func init() {
|
||
|
|
svc := SVC.ProfileService{
|
||
|
|
Name: "opt.profile",
|
||
|
|
Desc: "opt profile module",
|
||
|
|
NewInst: NewProfileServer,
|
||
|
|
}
|
||
|
|
if err := SVC.AddService(&svc); err != nil {
|
||
|
|
fmt.Printf("Failed to load service project : %s\n", err)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
log.Info("load profile service successfully\n")
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
// NewProfileServer method new a instance of the grpc server
|
||
|
|
func NewProfileServer(ctx *cli.Context, opts ...interface{}) (interface{}, error) {
|
||
|
|
defaultConfigFile := path.Join(config.DefaultConfPath, "atuned.cnf")
|
||
|
|
|
||
|
|
exist, err := utils.PathExist(defaultConfigFile)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
if !exist {
|
||
|
|
return nil, fmt.Errorf("Could not find default config file")
|
||
|
|
}
|
||
|
|
|
||
|
|
cfg, err := ini.Load(defaultConfigFile)
|
||
|
|
if err != nil {
|
||
|
|
return nil, fmt.Errorf("Faild to parse %s, %v", defaultConfigFile, err)
|
||
|
|
}
|
||
|
|
|
||
|
|
return &ProfileServer{
|
||
|
|
Raw: cfg,
|
||
|
|
}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// RegisterServer method register the grpc service
|
||
|
|
func (s *ProfileServer) RegisterServer(server *grpc.Server) error {
|
||
|
|
PB.RegisterProfileMgrServer(server, s)
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// Healthy method, implement SvrService interface
|
||
|
|
func (s *ProfileServer) Healthy(opts ...interface{}) error {
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// Post method send POST to start analysis the workload type
|
||
|
|
func (p *ClassifyPostBody) Post() (*RespClassify, error) {
|
||
|
|
url := config.GetUrl(config.ClassificationURI)
|
||
|
|
response, err := http.Post(url, p)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
defer response.Body.Close()
|
||
|
|
if response.StatusCode != 200 {
|
||
|
|
return nil, fmt.Errorf("online learning faild")
|
||
|
|
}
|
||
|
|
resBody, err := ioutil.ReadAll(response.Body)
|
||
|
|
resPostIns := new(RespClassify)
|
||
|
|
err = json.Unmarshal(resBody, resPostIns)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
return resPostIns, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// Post method send POST to start collection data
|
||
|
|
func (c *CollectorPost) Post() (*RespCollectorPost, error) {
|
||
|
|
url := config.GetUrl(config.CollectorURI)
|
||
|
|
response, err := http.Post(url, c)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
defer response.Body.Close()
|
||
|
|
if response.StatusCode != 200 {
|
||
|
|
return nil, fmt.Errorf("collect data faild")
|
||
|
|
}
|
||
|
|
resBody, err := ioutil.ReadAll(response.Body)
|
||
|
|
resPostIns := new(RespCollectorPost)
|
||
|
|
err = json.Unmarshal(resBody, resPostIns)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
return resPostIns, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// Profile method set the workload type to effective manual
|
||
|
|
func (s *ProfileServer) Profile(profileInfo *PB.ProfileInfo, stream PB.ProfileMgr_ProfileServer) error {
|
||
|
|
profileNames := profileInfo.GetName()
|
||
|
|
|
||
|
|
profile, ok := profile.LoadFromWorkloadType(profileNames)
|
||
|
|
|
||
|
|
if !ok {
|
||
|
|
fmt.Println("Failure Load ", profileInfo.GetName())
|
||
|
|
return fmt.Errorf("Load profile %s Faild", profileInfo.GetName())
|
||
|
|
}
|
||
|
|
ch := make(chan *PB.AckCheck)
|
||
|
|
ctx, cancel := context.WithCancel(context.Background())
|
||
|
|
defer close(ch)
|
||
|
|
defer cancel()
|
||
|
|
|
||
|
|
go func(ctx context.Context) error {
|
||
|
|
for {
|
||
|
|
select {
|
||
|
|
case value := <-ch:
|
||
|
|
stream.Send(value)
|
||
|
|
case <-ctx.Done():
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}(ctx)
|
||
|
|
|
||
|
|
if err := profile.RollbackActive(ch); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// ListWorkload method list support workload
|
||
|
|
func (s *ProfileServer) ListWorkload(profileInfo *PB.ProfileInfo, stream PB.ProfileMgr_ListWorkloadServer) error {
|
||
|
|
log.Debug("Begin to inquire all workloads\n")
|
||
|
|
workloads := &sqlstore.GetClass{}
|
||
|
|
err := sqlstore.GetClasses(workloads)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
for _, classProfile := range workloads.Result {
|
||
|
|
stream.Send(&PB.ListMessage{WorkloadType: classProfile.Class, ProfileNames: classProfile.ProfileType, Active: strconv.FormatBool(classProfile.Active)})
|
||
|
|
}
|
||
|
|
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// CheckInitProfile method check the system init information
|
||
|
|
// like BIOS version, memory balanced...
|
||
|
|
func (s *ProfileServer) CheckInitProfile(profileInfo *PB.ProfileInfo, stream PB.ProfileMgr_CheckInitProfileServer) error {
|
||
|
|
elf := profileInfo.GetName()
|
||
|
|
|
||
|
|
rd, err := ioutil.ReadDir(config.DefaultCheckerPath)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
ch := make(chan *PB.AckCheck, 0)
|
||
|
|
defer close(ch)
|
||
|
|
go func() error {
|
||
|
|
for {
|
||
|
|
select {
|
||
|
|
case value, ok := <-ch:
|
||
|
|
if ok {
|
||
|
|
stream.Send(value)
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
break
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}()
|
||
|
|
|
||
|
|
elf = strings.Trim(elf, "")
|
||
|
|
if elf != "" {
|
||
|
|
if exist, _ := utils.PathExist(elf); exist {
|
||
|
|
program := checker.ELF{FileName: elf}
|
||
|
|
program.Check(ch)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
for _, dir := range rd {
|
||
|
|
if dir.IsDir() {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
|
||
|
|
if !strings.HasSuffix(dir.Name(), ".xml") {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
|
||
|
|
filename := strings.TrimSuffix(dir.Name(), ".xml")
|
||
|
|
switch filename {
|
||
|
|
case "mem_topo":
|
||
|
|
memTopo := checker.MemTopo{Path: path.Join(config.DefaultCheckerPath, dir.Name())}
|
||
|
|
memTopo.Check(ch)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
time.Sleep(time.Duration(2) * time.Second)
|
||
|
|
stream.Send(&PB.AckCheck{Name: "check system finished"})
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// Analysis method analysis the system traffic load
|
||
|
|
func (s *ProfileServer) Analysis(message *PB.AnalysisMessage, stream PB.ProfileMgr_AnalysisServer) error {
|
||
|
|
if !s.TryLock() {
|
||
|
|
return fmt.Errorf("analysis has been in running")
|
||
|
|
}
|
||
|
|
defer s.Unlock()
|
||
|
|
|
||
|
|
stream.Send(&PB.AckCheck{Name: "1. Analysis system runtime information: CPU Memory IO and Network..."})
|
||
|
|
|
||
|
|
npipe, err := utils.CreateNamedPipe()
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("create named pipe faild")
|
||
|
|
}
|
||
|
|
|
||
|
|
defer os.Remove(npipe)
|
||
|
|
|
||
|
|
go func() {
|
||
|
|
file, _ := os.OpenFile(npipe, os.O_RDONLY, os.ModeNamedPipe)
|
||
|
|
reader := bufio.NewReader(file)
|
||
|
|
|
||
|
|
scanner := bufio.NewScanner(reader)
|
||
|
|
|
||
|
|
for scanner.Scan() {
|
||
|
|
line := scanner.Text()
|
||
|
|
stream.Send(&PB.AckCheck{Name: line, Status: utils.INFO})
|
||
|
|
}
|
||
|
|
|
||
|
|
}()
|
||
|
|
|
||
|
|
//1. get the dimension structure of the system data to be collected
|
||
|
|
collections, err := sqlstore.GetCollections()
|
||
|
|
if err != nil {
|
||
|
|
log.Errorf("inquery collection tables error: %v", err)
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
// 1.1 send the collect data command to the monitor service
|
||
|
|
monitors := make([]Monitor, 0)
|
||
|
|
for _, collection := range collections {
|
||
|
|
re := regexp.MustCompile(`\{([^}]+)\}`)
|
||
|
|
matches := re.FindAllStringSubmatch(collection.Metrics, -1)
|
||
|
|
if len(matches) > 0 {
|
||
|
|
for _, match := range matches {
|
||
|
|
if !s.Raw.Section("system").Haskey(match[1]) {
|
||
|
|
return fmt.Errorf("%s is not exist in the system section", match[1])
|
||
|
|
}
|
||
|
|
value := s.Raw.Section("system").Key(match[1]).Value()
|
||
|
|
collection.Metrics = re.ReplaceAllString(collection.Metrics, value)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
monitor := Monitor{Module: collection.Module, Purpose: collection.Purpose, Field: collection.Metrics}
|
||
|
|
monitors = append(monitors, monitor)
|
||
|
|
}
|
||
|
|
|
||
|
|
sampleNum := s.Raw.Section("server").Key("sample_num").MustInt(20)
|
||
|
|
collectorBody := new(CollectorPost)
|
||
|
|
collectorBody.SampleNum = sampleNum
|
||
|
|
collectorBody.Monitors = monitors
|
||
|
|
collectorBody.Pipe = npipe
|
||
|
|
|
||
|
|
respCollectPost, err := collectorBody.Post()
|
||
|
|
if err != nil {
|
||
|
|
stream.Send(&PB.AckCheck{Name: err.Error()})
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
//2. send the collected data to the model for completion type identification
|
||
|
|
body := new(ClassifyPostBody)
|
||
|
|
body.Data = respCollectPost.Path
|
||
|
|
body.ModelPath = path.Join(config.DefaultAnalysisPath, "models")
|
||
|
|
|
||
|
|
if message.GetModel() != "" {
|
||
|
|
body.Model = message.GetModel()
|
||
|
|
}
|
||
|
|
respPostIns, err := body.Post()
|
||
|
|
|
||
|
|
if err != nil {
|
||
|
|
stream.Send(&PB.AckCheck{Name: err.Error()})
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
workloadType := respPostIns.WorkloadType
|
||
|
|
stream.Send(&PB.AckCheck{Name: fmt.Sprintf("\n 2. Current System Workload Characterization is %s", workloadType)})
|
||
|
|
|
||
|
|
//3. judge the workload type is exist in the database
|
||
|
|
classProfile := &sqlstore.GetClass{Class: workloadType}
|
||
|
|
if err := sqlstore.GetClasses(classProfile); err != nil {
|
||
|
|
log.Errorf("inquery workload type table faild %v", err)
|
||
|
|
return fmt.Errorf("inquery workload type table faild %v", err)
|
||
|
|
}
|
||
|
|
if len(classProfile.Result) == 0 {
|
||
|
|
log.Errorf("%s is not exist in the table", workloadType)
|
||
|
|
return fmt.Errorf("%s is not exist in the table", workloadType)
|
||
|
|
}
|
||
|
|
|
||
|
|
// the workload type is already actived
|
||
|
|
if classProfile.Result[0].Active {
|
||
|
|
log.Infof("analysis result %s is the same with current active workload type", workloadType)
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
//4. inquery the support app of the workload type
|
||
|
|
classApps := &sqlstore.GetClassApp{Class: workloadType}
|
||
|
|
err = sqlstore.GetClassApps(classApps)
|
||
|
|
if err != nil {
|
||
|
|
log.Errorf("inquery support app depend on class error: %v", err)
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
if len(classApps.Result) == 0 {
|
||
|
|
return fmt.Errorf("class %s is not exist in the tables", workloadType)
|
||
|
|
}
|
||
|
|
apps := classApps.Result[0].Apps
|
||
|
|
log.Infof("workload %s support app: %s", workloadType, apps)
|
||
|
|
log.Infof("workload %s resource limit: %s, cluster result resource limit: %s", workloadType, apps, respPostIns.ResourceLimit)
|
||
|
|
|
||
|
|
stream.Send(&PB.AckCheck{Name: "\n 3. Build the best resource model..."})
|
||
|
|
|
||
|
|
//5. get the profile type depend on the workload type
|
||
|
|
profileType := classProfile.Result[0].ProfileType
|
||
|
|
profileNames := strings.Split(profileType, ",")
|
||
|
|
if len(profileNames) == 0 {
|
||
|
|
log.Errorf("No profile or invaild profiles were specified.")
|
||
|
|
return fmt.Errorf("No profile or invaild profiles were specified")
|
||
|
|
}
|
||
|
|
|
||
|
|
//6. get the profile info depend on the profile type
|
||
|
|
log.Infof("the resource model of the profile type is %s", profileType)
|
||
|
|
stream.Send(&PB.AckCheck{Name: fmt.Sprintf("\n 4. Match profile: %s", profileType)})
|
||
|
|
pro, _ := profile.Load(profileNames)
|
||
|
|
pro.SetWorkloadType(workloadType)
|
||
|
|
|
||
|
|
stream.Send(&PB.AckCheck{Name: fmt.Sprintf("\n 5. bengin to set static profile")})
|
||
|
|
log.Infof("bengin to set static profile")
|
||
|
|
|
||
|
|
//static profile setting
|
||
|
|
ch := make(chan *PB.AckCheck, 0)
|
||
|
|
go func() error {
|
||
|
|
for {
|
||
|
|
select {
|
||
|
|
case value, ok := <-ch:
|
||
|
|
if ok {
|
||
|
|
stream.Send(value)
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
break
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}()
|
||
|
|
pro.RollbackActive(ch)
|
||
|
|
|
||
|
|
rules := &sqlstore.GetRuleTuned{Class: workloadType}
|
||
|
|
if err := sqlstore.GetRuleTuneds(rules); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
if len(rules.Result) < 1 {
|
||
|
|
stream.Send(&PB.AckCheck{Name: fmt.Sprintf("Completed optimization, please restart application!")})
|
||
|
|
log.Info("no rules to tuned")
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
log.Info("begin to dynamic tunning depending on rules")
|
||
|
|
stream.Send(&PB.AckCheck{Name: fmt.Sprintf("\n 6. bengin to set dynamic profile")})
|
||
|
|
if err := tuning.RuleTuned(workloadType); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
stream.Send(&PB.AckCheck{Name: fmt.Sprintf("Completed optimization, please restart application!")})
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// Tuning method calling the bayes search method to tuned parameters
|
||
|
|
func (s *ProfileServer) Tuning(profileInfo *PB.ProfileInfo, stream PB.ProfileMgr_TuningServer) error {
|
||
|
|
//dynamic profle setting
|
||
|
|
data := profileInfo.GetName()
|
||
|
|
fmt.Println(data)
|
||
|
|
|
||
|
|
var byteData []byte = []byte(data)
|
||
|
|
project := project.YamlPrj{}
|
||
|
|
if err := yaml.Unmarshal(byteData, &project); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
log.Info("begin to dynamic optimizer search")
|
||
|
|
stream.Send(&PB.AckCheck{Name: fmt.Sprintf("bengin to dynamic optimizer search")})
|
||
|
|
|
||
|
|
ch := make(chan *PB.AckCheck, 0)
|
||
|
|
go func() error {
|
||
|
|
for {
|
||
|
|
select {
|
||
|
|
case value, ok := <-ch:
|
||
|
|
if ok {
|
||
|
|
stream.Send(value)
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
break
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}()
|
||
|
|
|
||
|
|
optimizer := tuning.Optimizer{Prj: &project}
|
||
|
|
err := optimizer.DynamicTuned(ch)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
UpgradeProfile method update the db file
|
||
|
|
*/
|
||
|
|
func (s *ProfileServer) UpgradeProfile(profileInfo *PB.ProfileInfo, stream PB.ProfileMgr_UpgradeProfileServer) error {
|
||
|
|
log.Debug("Begin to upgrade profiles\n")
|
||
|
|
currenDbPath := path.Join(config.DatabasePath, config.DatabaseName)
|
||
|
|
newDbPath := profileInfo.GetName()
|
||
|
|
|
||
|
|
exist, err := utils.PathExist(config.DefaultTempPath)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
if !exist {
|
||
|
|
os.MkdirAll(config.DefaultTempPath, os.ModePerm)
|
||
|
|
}
|
||
|
|
timeUnix := strconv.FormatInt(time.Now().Unix(), 10) + ".db"
|
||
|
|
tempFile := path.Join(config.DefaultTempPath, timeUnix)
|
||
|
|
|
||
|
|
if err := utils.CopyFile(tempFile, currenDbPath); err != nil {
|
||
|
|
stream.Send(&PB.AckCheck{Name: err.Error(), Status: utils.FAILD})
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := utils.CopyFile(currenDbPath, newDbPath); err != nil {
|
||
|
|
stream.Send(&PB.AckCheck{Name: err.Error(), Status: utils.FAILD})
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := sqlstore.Reload(currenDbPath); err != nil {
|
||
|
|
stream.Send(&PB.AckCheck{Name: err.Error(), Status: utils.FAILD})
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
stream.Send(&PB.AckCheck{Name: fmt.Sprintf("upgrade success"), Status: utils.SUCCESS})
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
InfoProfile method display the content of the specified workload type
|
||
|
|
*/
|
||
|
|
func (s *ProfileServer) InfoProfile(profileInfo *PB.ProfileInfo, stream PB.ProfileMgr_InfoProfileServer) error {
|
||
|
|
workloadType := profileInfo.GetName()
|
||
|
|
classProfile := &sqlstore.GetClass{Class: workloadType}
|
||
|
|
err := sqlstore.GetClasses(classProfile)
|
||
|
|
if err != nil {
|
||
|
|
log.Errorf("inquery class_profile table faild")
|
||
|
|
return fmt.Errorf("inquery class_profile table faild")
|
||
|
|
}
|
||
|
|
|
||
|
|
if len(classProfile.Result) == 0 {
|
||
|
|
log.Errorf("%s is not exist in the class_profile table", workloadType)
|
||
|
|
return fmt.Errorf("%s is not exist in the class_profile table", workloadType)
|
||
|
|
}
|
||
|
|
|
||
|
|
profileType := classProfile.Result[0].ProfileType
|
||
|
|
profileNames := strings.Split(profileType, ",")
|
||
|
|
for _, name := range profileNames {
|
||
|
|
name = strings.Trim(name, " ")
|
||
|
|
context, _ := sqlstore.GetContext(name)
|
||
|
|
context = "\n*** " + name + ":\n" + context
|
||
|
|
stream.Send(&PB.ProfileInfo{Name: context})
|
||
|
|
}
|
||
|
|
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
CheckActiveProfile method check current active profile is effective
|
||
|
|
*/
|
||
|
|
func (s *ProfileServer) CheckActiveProfile(profileInfo *PB.ProfileInfo, stream PB.ProfileMgr_CheckActiveProfileServer) error {
|
||
|
|
log.Debug("Begin to check active profiles\n")
|
||
|
|
profiles := &sqlstore.GetClass{Active: true}
|
||
|
|
err := sqlstore.GetClasses(profiles)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
if len(profiles.Result) != 1 {
|
||
|
|
return fmt.Errorf("No active profile or more than 1 active profile")
|
||
|
|
}
|
||
|
|
|
||
|
|
workloadType := profiles.Result[0].Class
|
||
|
|
profile, ok := profile.LoadFromWorkloadType(workloadType)
|
||
|
|
|
||
|
|
if !ok {
|
||
|
|
log.WithField("profile", workloadType).Errorf("Load profile %s Faild", workloadType)
|
||
|
|
return fmt.Errorf("Load workload type %s Faild", workloadType)
|
||
|
|
}
|
||
|
|
|
||
|
|
ch := make(chan *PB.AckCheck, 0)
|
||
|
|
defer close(ch)
|
||
|
|
go func() error {
|
||
|
|
for {
|
||
|
|
select {
|
||
|
|
case value, ok := <-ch:
|
||
|
|
if ok {
|
||
|
|
stream.Send(value)
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
break
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}()
|
||
|
|
|
||
|
|
if err := profile.Check(ch); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// ProfileRollback method rollback the profile to init state
|
||
|
|
func (s *ProfileServer) ProfileRollback(profileInfo *PB.ProfileInfo, stream PB.ProfileMgr_ProfileRollbackServer) error {
|
||
|
|
|
||
|
|
profileLogs, err := sqlstore.GetProfileLogs()
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
if len(profileLogs) < 1 {
|
||
|
|
stream.Send(&PB.AckCheck{Name: "no profile need to rollback"})
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
sort.Slice(profileLogs, func(i, j int) bool {
|
||
|
|
return profileLogs[i].ID > profileLogs[j].ID
|
||
|
|
})
|
||
|
|
|
||
|
|
//static profile setting
|
||
|
|
ch := make(chan *PB.AckCheck, 0)
|
||
|
|
go func() error {
|
||
|
|
for {
|
||
|
|
select {
|
||
|
|
case value, ok := <-ch:
|
||
|
|
if ok {
|
||
|
|
stream.Send(value)
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
break
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}()
|
||
|
|
|
||
|
|
for _, pro := range profileLogs {
|
||
|
|
log.Infof("begin to restore profile id: %s", pro.ID)
|
||
|
|
profileInfo := profile.HistoryProfile{}
|
||
|
|
profileInfo.Load(pro.Context)
|
||
|
|
profileInfo.Resume(ch)
|
||
|
|
|
||
|
|
// delete profile log after restored
|
||
|
|
if err := sqlstore.DelProfileLogByID(pro.ID); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
//delete backup dir
|
||
|
|
if err := os.RemoveAll(pro.BackupPath); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
// update active profile after restored
|
||
|
|
if err := sqlstore.ActiveProfile(pro.ProfileID); err != nil {
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := sqlstore.InActiveProfile(); err != nil {
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Collection method call collection script to collect system data.
|
||
|
|
*/
|
||
|
|
func (s *ProfileServer) Collection(message *PB.CollectFlag, stream PB.ProfileMgr_CollectionServer) error {
|
||
|
|
classApps := &sqlstore.GetClassApp{Class: message.GetType()}
|
||
|
|
err := sqlstore.GetClassApps(classApps)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
if len(classApps.Result) == 0 {
|
||
|
|
return fmt.Errorf("workload type %s is not exist, please use define command first", message.GetType())
|
||
|
|
}
|
||
|
|
|
||
|
|
exist, err := utils.PathExist(message.GetOutputPath())
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
if !exist {
|
||
|
|
return fmt.Errorf("output_path %s is not exist", message.GetOutputPath())
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := utils.InterfaceByName(message.GetNetwork()); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := utils.DiskByName(message.GetBlock()); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
collector := path.Join(config.DefaultCollectorPath, "collect_training_data.sh")
|
||
|
|
script := make([]string, 0)
|
||
|
|
|
||
|
|
script = append(script, collector)
|
||
|
|
script = append(script, message.GetWorkload())
|
||
|
|
script = append(script, strconv.FormatInt(message.GetDuration(), 10))
|
||
|
|
script = append(script, strconv.FormatInt(message.GetInterval(), 10))
|
||
|
|
script = append(script, message.GetOutputPath())
|
||
|
|
script = append(script, message.GetBlock())
|
||
|
|
script = append(script, message.GetNetwork())
|
||
|
|
script = append(script, message.GetType())
|
||
|
|
|
||
|
|
newScript := strings.Join(script, " ")
|
||
|
|
cmd := exec.Command("sudo", "sh", "-c", newScript)
|
||
|
|
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
|
||
|
|
|
||
|
|
stdout, err := cmd.StdoutPipe()
|
||
|
|
stderr, err := cmd.StderrPipe()
|
||
|
|
|
||
|
|
ctx := stream.Context()
|
||
|
|
go func() {
|
||
|
|
for {
|
||
|
|
select {
|
||
|
|
case <-ctx.Done():
|
||
|
|
syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}()
|
||
|
|
|
||
|
|
go func() {
|
||
|
|
scanner := bufio.NewScanner(stdout)
|
||
|
|
for scanner.Scan() {
|
||
|
|
line := scanner.Text()
|
||
|
|
stream.Send(&PB.AckCheck{Name: line})
|
||
|
|
}
|
||
|
|
}()
|
||
|
|
|
||
|
|
go func() {
|
||
|
|
scanner := bufio.NewScanner(stderr)
|
||
|
|
for scanner.Scan() {
|
||
|
|
line := scanner.Text()
|
||
|
|
stream.Send(&PB.AckCheck{Name: line})
|
||
|
|
}
|
||
|
|
}()
|
||
|
|
|
||
|
|
err = cmd.Start()
|
||
|
|
if err != nil {
|
||
|
|
log.Error(err)
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
err = cmd.Wait()
|
||
|
|
|
||
|
|
if err != nil {
|
||
|
|
log.Error(err)
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Training method train the collected data to generate the model
|
||
|
|
*/
|
||
|
|
func (s *ProfileServer) Training(message *PB.TrainMessage, stream PB.ProfileMgr_TrainingServer) error {
|
||
|
|
DataPath := message.GetDataPath()
|
||
|
|
OutputPath := message.GetOutputPath()
|
||
|
|
|
||
|
|
trainBody := new(models.Training)
|
||
|
|
trainBody.DataPath = DataPath
|
||
|
|
trainBody.OutputPath = OutputPath
|
||
|
|
trainBody.ModelPath = path.Join(config.DefaultAnalysisPath, "models")
|
||
|
|
|
||
|
|
success, err := trainBody.Post()
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
if success {
|
||
|
|
stream.Send(&PB.AckCheck{Name: "training the self collect data success"})
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
stream.Send(&PB.AckCheck{Name: "training the self collect data faild"})
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// Charaterization method will be deprecate in the future
|
||
|
|
func (s *ProfileServer) Charaterization(profileInfo *PB.ProfileInfo, stream PB.ProfileMgr_CharaterizationServer) error {
|
||
|
|
stream.Send(&PB.AckCheck{Name: "1. Analysis system runtime information: CPU Memory IO and Network..."})
|
||
|
|
|
||
|
|
npipe, err := utils.CreateNamedPipe()
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("create named pipe faild")
|
||
|
|
}
|
||
|
|
|
||
|
|
defer os.Remove(npipe)
|
||
|
|
|
||
|
|
go func() {
|
||
|
|
file, _ := os.OpenFile(npipe, os.O_RDONLY, os.ModeNamedPipe)
|
||
|
|
reader := bufio.NewReader(file)
|
||
|
|
|
||
|
|
scanner := bufio.NewScanner(reader)
|
||
|
|
|
||
|
|
for scanner.Scan() {
|
||
|
|
line := scanner.Text()
|
||
|
|
stream.Send(&PB.AckCheck{Name: line, Status: utils.INFO})
|
||
|
|
}
|
||
|
|
|
||
|
|
}()
|
||
|
|
|
||
|
|
//1. get the dimension structure of the system data to be collected
|
||
|
|
collections, err := sqlstore.GetCollections()
|
||
|
|
if err != nil {
|
||
|
|
log.Errorf("inquery collection tables error: %v", err)
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
// 1.1 send the collect data command to the monitor service
|
||
|
|
monitors := make([]Monitor, 0)
|
||
|
|
for _, collection := range collections {
|
||
|
|
re := regexp.MustCompile(`\{([^}]+)\}`)
|
||
|
|
matches := re.FindAllStringSubmatch(collection.Metrics, -1)
|
||
|
|
if len(matches) > 0 {
|
||
|
|
for _, match := range matches {
|
||
|
|
if !s.Raw.Section("system").Haskey(match[1]) {
|
||
|
|
return fmt.Errorf("%s is not exist in the system section", match[1])
|
||
|
|
}
|
||
|
|
value := s.Raw.Section("system").Key(match[1]).Value()
|
||
|
|
collection.Metrics = re.ReplaceAllString(collection.Metrics, value)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
monitor := Monitor{Module: collection.Module, Purpose: collection.Purpose, Field: collection.Metrics}
|
||
|
|
monitors = append(monitors, monitor)
|
||
|
|
}
|
||
|
|
|
||
|
|
collectorBody := new(CollectorPost)
|
||
|
|
collectorBody.SampleNum = 5
|
||
|
|
collectorBody.Monitors = monitors
|
||
|
|
collectorBody.Pipe = npipe
|
||
|
|
|
||
|
|
respCollectPost, err := collectorBody.Post()
|
||
|
|
if err != nil {
|
||
|
|
stream.Send(&PB.AckCheck{Name: err.Error()})
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
//2. send the collected data to the model for completion type identification
|
||
|
|
body := new(ClassifyPostBody)
|
||
|
|
body.Data = respCollectPost.Path
|
||
|
|
body.ModelPath = path.Join(config.DefaultAnalysisPath, "models")
|
||
|
|
|
||
|
|
respPostIns, err := body.Post()
|
||
|
|
|
||
|
|
if err != nil {
|
||
|
|
stream.Send(&PB.AckCheck{Name: err.Error()})
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
workloadType := respPostIns.WorkloadType
|
||
|
|
stream.Send(&PB.AckCheck{Name: fmt.Sprintf("\n 2. Current System Workload Characterization is %s", workloadType)})
|
||
|
|
return nil
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
// Define method user define workload type and profile
|
||
|
|
func (s *ProfileServer) Define(ctx context.Context, message *PB.DefineMessage) (*PB.Ack, error) {
|
||
|
|
workloadType := message.GetWorkloadType()
|
||
|
|
profileName := message.GetProfileName()
|
||
|
|
content := string(message.GetContent())
|
||
|
|
|
||
|
|
workloadTypeExist, err := sqlstore.ExistWorkloadType(workloadType)
|
||
|
|
if err != nil {
|
||
|
|
return &PB.Ack{}, err
|
||
|
|
}
|
||
|
|
if workloadTypeExist {
|
||
|
|
return &PB.Ack{Status: fmt.Sprintf("%s is already exist", workloadType)}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
profileExist, err := sqlstore.ExistProfile(profileName)
|
||
|
|
if err != nil {
|
||
|
|
return &PB.Ack{}, err
|
||
|
|
}
|
||
|
|
|
||
|
|
if profileExist {
|
||
|
|
return &PB.Ack{Status: fmt.Sprintf("%s is already exist", profileName)}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := sqlstore.InsertClassApps(&sqlstore.ClassApps{Class: workloadType, Deletable: true}); err != nil {
|
||
|
|
return &PB.Ack{}, err
|
||
|
|
}
|
||
|
|
if err := sqlstore.InsertClassProfile(&sqlstore.ClassProfile{Class: workloadType, ProfileType: profileName, Active: false}); err != nil {
|
||
|
|
return &PB.Ack{}, err
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := sqlstore.InsertProfile(&sqlstore.Profile{ProfileType: profileName, ProfileInformation: content}); err != nil {
|
||
|
|
return &PB.Ack{}, err
|
||
|
|
}
|
||
|
|
|
||
|
|
return &PB.Ack{Status: "OK"}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// Delete method delete the self define workload type from database
|
||
|
|
func (s *ProfileServer) Delete(ctx context.Context, message *PB.DefineMessage) (*PB.Ack, error) {
|
||
|
|
workloadType := message.GetWorkloadType()
|
||
|
|
|
||
|
|
classApps := &sqlstore.GetClassApp{Class: workloadType}
|
||
|
|
err := sqlstore.GetClassApps(classApps)
|
||
|
|
if err != nil {
|
||
|
|
return &PB.Ack{}, err
|
||
|
|
}
|
||
|
|
|
||
|
|
log.Infof("the result length: %d, query the classapp table of workload: %s", len(classApps.Result), workloadType)
|
||
|
|
if len(classApps.Result) != 1 {
|
||
|
|
return &PB.Ack{Status: fmt.Sprintf("workload type %s may be not exist in the table", workloadType)}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
classApp := classApps.Result[0]
|
||
|
|
if classApp.Deletable != true {
|
||
|
|
return &PB.Ack{Status: "only self defined workload type can be deleted"}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := sqlstore.DeleteClassApps(workloadType); err != nil {
|
||
|
|
return &PB.Ack{}, err
|
||
|
|
}
|
||
|
|
|
||
|
|
// get the profile type from the classprofile table
|
||
|
|
classProfile := &sqlstore.GetClass{Class: workloadType}
|
||
|
|
if err := sqlstore.GetClasses(classProfile); err != nil {
|
||
|
|
log.Errorf("inquery workload type table faild %v", err)
|
||
|
|
return &PB.Ack{}, fmt.Errorf("inquery workload type table faild %v", err)
|
||
|
|
}
|
||
|
|
if len(classProfile.Result) == 0 {
|
||
|
|
log.Errorf("%s is not exist in the table", workloadType)
|
||
|
|
return &PB.Ack{}, fmt.Errorf("%s is not exist in the table", workloadType)
|
||
|
|
}
|
||
|
|
|
||
|
|
profileType := classProfile.Result[0].ProfileType
|
||
|
|
profileNames := strings.Split(profileType, ",")
|
||
|
|
if len(profileNames) == 0 {
|
||
|
|
log.Errorf("No profile or invaild profiles were specified.")
|
||
|
|
return &PB.Ack{}, fmt.Errorf("No profile or invaild profiles were specified")
|
||
|
|
}
|
||
|
|
|
||
|
|
// delete profile depend the profiletype
|
||
|
|
for _, profileName := range profileNames {
|
||
|
|
if err := sqlstore.DeleteProfile(profileName); err != nil {
|
||
|
|
log.Errorf("delete item from profile table faild %v ", err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// delete classprofile depend the workloadType
|
||
|
|
if err := sqlstore.DeleteClassProfile(workloadType); err != nil {
|
||
|
|
log.Errorf("delete item from classprofile tabble faild.")
|
||
|
|
return &PB.Ack{}, err
|
||
|
|
}
|
||
|
|
return &PB.Ack{Status: "OK"}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// Update method update the content of the specified workload type from database
|
||
|
|
func (s *ProfileServer) Update(ctx context.Context, message *PB.DefineMessage) (*PB.Ack, error) {
|
||
|
|
workloadType := message.GetWorkloadType()
|
||
|
|
profileName := message.GetProfileName()
|
||
|
|
content := string(message.GetContent())
|
||
|
|
|
||
|
|
workloadTypeExist, err := sqlstore.ExistWorkloadType(workloadType)
|
||
|
|
if err != nil {
|
||
|
|
return &PB.Ack{}, err
|
||
|
|
}
|
||
|
|
if !workloadTypeExist {
|
||
|
|
return &PB.Ack{Status: fmt.Sprintf("workload type %s is not exist in the table", workloadType)}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// get the profile type from the classprofile table
|
||
|
|
classProfile := &sqlstore.GetClass{Class: workloadType}
|
||
|
|
if err := sqlstore.GetClasses(classProfile); err != nil {
|
||
|
|
log.Errorf("inquery workload type table faild %v", err)
|
||
|
|
return &PB.Ack{}, fmt.Errorf("inquery workload type table faild %v", err)
|
||
|
|
}
|
||
|
|
if len(classProfile.Result) == 0 {
|
||
|
|
log.Errorf("%s is not exist in the table", workloadType)
|
||
|
|
return &PB.Ack{}, fmt.Errorf("%s is not exist in the table", workloadType)
|
||
|
|
}
|
||
|
|
|
||
|
|
profileType := classProfile.Result[0].ProfileType
|
||
|
|
profileNames := strings.Split(profileType, ",")
|
||
|
|
if len(profileNames) == 0 {
|
||
|
|
log.Errorf("No profile or invaild profiles were specified.")
|
||
|
|
return &PB.Ack{}, fmt.Errorf("no profile exist corresponding to workload type")
|
||
|
|
}
|
||
|
|
|
||
|
|
exist := false
|
||
|
|
for _, profile := range profileNames {
|
||
|
|
if profileName == strings.TrimSpace(profile) {
|
||
|
|
exist = true
|
||
|
|
break
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if !exist {
|
||
|
|
return &PB.Ack{}, fmt.Errorf("profile name %s is exist corresponding to workload type %s", profileName, workloadType)
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := sqlstore.UpdateProfile(&sqlstore.Profile{ProfileType: profileName, ProfileInformation: content}); err != nil {
|
||
|
|
return &PB.Ack{}, err
|
||
|
|
}
|
||
|
|
return &PB.Ack{Status: "OK"}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
* schedule cpu/irq/numa ...
|
||
|
|
*/
|
||
|
|
func (s *ProfileServer) Schedule(message *PB.ScheduleMessage, stream PB.ProfileMgr_ScheduleServer) error {
|
||
|
|
_ = message.GetApp()
|
||
|
|
Type := message.GetType()
|
||
|
|
Strategy := message.GetStrategy()
|
||
|
|
|
||
|
|
scheduler := schedule.GetScheduler()
|
||
|
|
scheduler.Schedule(Type, Strategy, true)
|
||
|
|
|
||
|
|
return nil
|
||
|
|
}
|