Go desenvolve uma plataforma de gerenciamento de ativos multinuvem

Go desenvolve uma plataforma cmdb de gerenciamento de ativos multinuvem 

Armazém de código github.com/yunixiangfeng/gocmdb

Gerenciamento de host na nuvem

Monitoramento de recursos do host

Processo de desenvolvimento

 Bem-vindo ao Beego | Beego

bee new gocmdb/server

cd gocmdb/server

go mod tidy

go get -u github.com/beego/beego/v2
go get -u "github.com/astaxie/beego/orm"
go get -u "github.com/go-sql-driver/mysql"

corrida de abelha

http://127.0.0.1:8080/

estrutura do projeto

Modifique o arquivo de configuração

D:\gocmdb\servidor\conf\app.conf

appname=CMDB
runmode=${RUNMODE||dev}

sessionon=true
sessionprovider=file
sessionproviderconfig=temp/session
sessionname=sid

enablexsrf=true
xsrfexpire=3600
xsrfkey=ac2e5a098492610c97ccd28ffb621014

login=AuthController.Login
home=TestController.Test

include "db.conf"

[dev]
httpport=8888

[prod]
httpport=80

D:\gocmdb\servidor\conf\db.conf

dsn=root:1234@tcp(192.168.204.130:3306)/gocmdb?charset=utf8mb4&loc=Local&parseTime=True

Gravar arquivo de inicialização

D:\gocmdb\servidor\web.go

package main

import (
	"flag"
	"fmt"
	"os"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/orm"
	_ "github.com/go-sql-driver/mysql"

	"github.com/yunixiangfeng/gocmdb/server/models"
	"github.com/yunixiangfeng/gocmdb/server/utils"

	_ "github.com/yunixiangfeng/gocmdb/server/routers"
)

func main() {
	// 初始化命令行参数
	h := flag.Bool("h", false, "help")
	help := flag.Bool("help", false, "help")
	init := flag.Bool("init", false, "init server")
	syncdb := flag.Bool("syncdb", false, "sync db")
	force := flag.Bool("force", false, "force sync db(drop table)")
	verbose := flag.Bool("v", false, "verbose")

	flag.Usage = func() {
		fmt.Println("usage: web -h")
		flag.PrintDefaults()
	}
	// 解析命令行参数
	flag.Parse()

	if *h || *help {
		flag.Usage()
		os.Exit(0)
	}

	// 设置日志到文件
	beego.SetLogger("file", `{
		"filename" : "logs/web.log",
		"level" : 7}`,
	)
	if !*verbose {
		//删除控制台日志
		beego.BeeLogger.DelLogger("console")
	} else {
		orm.Debug = true
	}

	// 初始化orm
	orm.RegisterDriver("mysql", orm.DRMySQL)
	orm.RegisterDataBase("default", "mysql", beego.AppConfig.String("dsn"))

	// 测试数据库连接是否正常
	if db, err := orm.GetDB(); err != nil || db.Ping() != nil {
		beego.Error("数据库连接错误")
		os.Exit(-1)
	}
	// 根据参数选择执行流程
	switch {
	case *init:
		orm.RunSyncdb("default", *force, *verbose)
		ormer := orm.NewOrm()
		admin := &models.User{Name: "admin", IsSuperman: true}
		if err := ormer.Read(admin, "Name"); err == orm.ErrNoRows {
			password := utils.RandString(6)
			admin.SetPassword(password)
			if _, err := ormer.Insert(admin); err == nil {
				beego.Informational("初始化admin成功, 默认密码:", password)
			} else {
				beego.Error("初始化用户失败, 错误:", err)
			}
		} else {
			beego.Informational("admin用户已存在, 跳过")
		}
	case *syncdb:
		orm.RunSyncdb("default", *force, *verbose)
		beego.Informational("同步数据库")
	default:
		beego.Run()
	}
}

Conecte-se

Definição de modelo de usuário/token

carregamento da página de destino

Enviar verificação de login de nome de usuário/senha

Processamento do resultado da verificação

controlador de base

Controlador base BaseController, usado para substituir o modelo padrão (padrão: controllerName/actionName.tpl)

D:\Workspace\Go\src\gocmdb\server\controllers\base\base.go

package base

import (
	"github.com/astaxie/beego"
)

type BaseController struct {
	beego.Controller
}

func (c *BaseController) Prepare() {
	c.Data["xsrf_token"] = c.XSRFToken()
}

Controlador de autenticação LoginRequiredController, usado para autenticação de chamada de API (sessão/token)

D:\Workspace\Go\src\gocmdb\server\controllers\auth\auth.go

package auth

import (
	"gocmdb/server/controllers/base"

	"gocmdb/server/models"
)

type LoginRequiredController struct {
	base.BaseController

	User *models.User
}

func (c *LoginRequiredController) Prepare() {
	c.BaseController.Prepare()

	if user := DefaultManger.IsLogin(c); user == nil {
		// 未登陆
		DefaultManger.GoToLoginPage(c) // todo 需要修改参数
		c.StopRun()
	} else {
		// 已登陆
		c.User = user
		c.Data["user"] = user
	}
}

type AuthController struct {
	base.BaseController
}

func (c *AuthController) Login() {
	DefaultManger.Login(c)
}

func (c *AuthController) Logout() {
	DefaultManger.Logout(c)
}

 D:\Workspace\Go\src\gocmdb\server\controllers\auth\manager.go

package auth

import (
	"github.com/astaxie/beego/context"
	"gocmdb/server/models"
)

type AuthPlugin interface {
	Name() string
	Is(*context.Context) bool
	IsLogin(*LoginRequiredController) *models.User
	GoToLoginPage(*LoginRequiredController)
	Login(*AuthController) bool
	Logout(*AuthController)
}

type Manager struct {
	plugins map[string]AuthPlugin
}

func NewManager() *Manager {
	return &Manager{
		plugins: map[string]AuthPlugin{},
	}
}

func (m *Manager) Register(p AuthPlugin) {
	m.plugins[p.Name()] = p
}

func (m *Manager) GetPlugin(c *context.Context) AuthPlugin {
	for _, plugin := range m.plugins {
		if plugin.Is(c) {
			return plugin
		}
	}
	return nil
}

func (m *Manager) IsLogin(c *LoginRequiredController) *models.User {
	if plugin := m.GetPlugin(c.Ctx); plugin != nil {
		return plugin.IsLogin(c)
	}
	return nil
}

func (m *Manager) GoToLoginPage(c *LoginRequiredController) {
	if plugin := m.GetPlugin(c.Ctx); plugin != nil {
		plugin.GoToLoginPage(c)
	}
}

func (m *Manager) Login(c *AuthController) bool {
	if plugin := m.GetPlugin(c.Ctx); plugin != nil {
		return plugin.Login(c)
	}
	return false
}

func (m *Manager) Logout(c *AuthController) {
	if plugin := m.GetPlugin(c.Ctx); plugin != nil {
		plugin.Logout(c)
	}
}

var DefaultManger = NewManager()

D:\Workspace\Go\src\gocmdb\server\controllers\auth\plugin.go

package auth

import (
	"github.com/astaxie/beego/context"
	"gocmdb/server/models"
	"net/http"
	"strings"

	"github.com/beego/beego"
	"github.com/beego/beego/validation"
	"gocmdb/server/forms"
)

type Session struct {
}

func (s *Session) Name() string {
	return "session"
}

func (s *Session) Is(c *context.Context) bool {
	return c.Input.Header("Authentication") == ""
}

func (s *Session) IsLogin(c *LoginRequiredController) *models.User {
	if session := c.GetSession("user"); session != nil {
		if uid, ok := session.(int); ok {
			return models.DefaultUserManager.GetById(uid)
		}
	}
	return nil
}

func (s *Session) GoToLoginPage(c *LoginRequiredController) {
	c.Redirect(beego.URLFor(beego.AppConfig.String("login")), http.StatusFound)
}

func (s *Session) Login(c *AuthController) bool {
	form := &forms.LoginForm{}
	valid := &validation.Validation{}
	if c.Ctx.Input.IsPost() {
		if err := c.ParseForm(form); err != nil {
			valid.SetError("error", err.Error())
		} else {
			if ok, err := valid.Valid(form); err != nil {
				valid.SetError("error", err.Error())
			} else if ok {
				c.SetSession("user", form.User.Id)
				c.Redirect(beego.URLFor(beego.AppConfig.String("home")), http.StatusFound)
				return true
			}
		}
	}

	c.TplName = "auth/login.html"
	c.Data["form"] = form
	c.Data["valid"] = valid
	return false
}

func (s *Session) Logout(c *AuthController) {
	c.DestroySession()
	c.Redirect(beego.URLFor(beego.AppConfig.String("login")), http.StatusFound)
}

type Token struct {
}

func (t *Token) Name() string {
	return "token"
}

func (t *Token) Is(c *context.Context) bool {
	return strings.ToLower(strings.TrimSpace(c.Input.Header("Authentication"))) == "token"
}

func (t *Token) IsLogin(c *LoginRequiredController) *models.User {
	accessKey := strings.TrimSpace(c.Ctx.Input.Header("AccessKey"))
	secrectKey := strings.TrimSpace(c.Ctx.Input.Header("SecrectKey"))
	if token := models.DefaultTokenManager.GetByKey(accessKey, secrectKey); token != nil && token.User.DeletedTime == nil {
		return token.User
	}
	return nil
}

func (t *Token) GoToLoginPage(c *LoginRequiredController) {
	c.Data["json"] = map[string]interface{}{
		"code":   403,
		"text":   "请使用正确Token发起请求",
		"result": nil,
	}
	c.ServeJSON()
}

func (t *Token) Login(c *AuthController) bool {
	c.Data["json"] = map[string]interface{}{
		"code":   200,
		"text":   "请使用Token请求API",
		"result": nil,
	}
	c.ServeJSON()
	return false
}

func (t *Token) Logout(c *AuthController) {
	c.Data["json"] = map[string]interface{}{
		"code":   200,
		"text":   "退出登陆成功",
		"result": nil,
	}
	c.ServeJSON()
}

func init() {
	DefaultManger.Register(new(Session))
	DefaultManger.Register(new(Token))
}

D:\Workspace\Go\src\gocmdb\server\forms\auth.go

package forms

import (
	"gocmdb/server/models"
	"strings"

	"github.com/beego/beego/validation"
)

type LoginForm struct {
	Name     string `form:"name"`
	Password string `form:"password"`

	User *models.User
}

func (f *LoginForm) Valid(v *validation.Validation) {
	f.Name = strings.TrimSpace(f.Name)
	f.Password = strings.TrimSpace(f.Password)

	if f.Name == "" || f.Password == "" {
		v.SetError("error", "用户名或密码错误")
	} else {
		if user := models.DefaultUserManager.GetByName(f.Name); user == nil || !user.ValidatePassword(f.Password) {
			v.SetError("error", "用户名或密码错误")
		} else if user.IsLock() {
			v.SetError("error", "用户名被锁定")
		} else {
			f.User = user
		}
	}
}

 D:\Workspace\Go\src\gocmdb\server\models\user.go

package models

import (
	"time"

	"gocmdb/server/utils"

	"github.com/beego/beego/orm"
)

type User struct {
	Id          int        `orm:"column(id);"`
	Name        string     `orm:"column(name);size(32);"`
	Password    string     `orm:"column(password);size(1024);"`
	Gender      int        `orm:"column(gender);default(0);"`
	Birthday    *time.Time `orm:"column(birthday);null;default(null);"`
	Tel         string     `orm:"column(tel);size(1024);"`
	Email       string     `orm:"column(email);size(1024);"`
	Addr        string     `orm:"column(addr);size(1024);"`
	Remark      string     `orm:"column(remark);size(1024);"`
	IsSuperman  bool       `orm:"column(is_superman);default(false);"`
	Status      int        `orm:"column(status);"`
	CreatedTime *time.Time `orm:"column(created_time);auto_now_add;"`
	UpdatedTime *time.Time `orm:"column(update_time);auto_now;"`
	DeletedTime *time.Time `orm:"column(deleted_time);null;default(null);"`

	Token *Token `orm:"reverse(one);"`
}

func (u *User) SetPassword(password string) {
	u.Password = utils.Md5Salt(password, "")
}

func (u *User) ValidatePassword(password string) bool {
	salt, _ := utils.SplitMd5Salt(u.Password)
	return utils.Md5Salt(password, salt) == u.Password
}

func (u *User) IsLock() bool {
	return u.Status == StatusLock
}

type UserManager struct{}

func NewUserManager() *UserManager {
	return &UserManager{}
}

func (m *UserManager) GetById(id int) *User {
	user := &User{}
	err := orm.NewOrm().QueryTable(user).Filter("Id__exact", id).Filter("DeletedTime__isnull", true).One(user)
	if err == nil {
		return user
	}
	return nil
}

func (m *UserManager) GetByName(name string) *User {
	user := &User{}
	err := orm.NewOrm().QueryTable(user).Filter("Name__exact", name).Filter("DeletedTime__isnull", true).One(user)
	if err == nil {
		return user
	}

	return nil
}

type Token struct {
	Id          int        `orm:"column(id);"`
	User        *User      `orm:"column(user);rel(one);"`
	AccessKey   string     `orm:"column(access_key);size(1024);"`
	SecrectKey  string     `orm:"column(secrect_key);size(1024);"`
	CreatedTime *time.Time `orm:"column(created_time);auto_now_add;"`
	UpdateTime  *time.Time `orm:"column(updated_time);auto_now;"`
}

type TokenManager struct {
}

func NewTokenManager() *TokenManager {
	return &TokenManager{}
}

func (m *TokenManager) GetByKey(accessKey, secrectKey string) *Token {
	token := &Token{AccessKey: accessKey, SecrectKey: secrectKey}
	ormer := orm.NewOrm()
	if err := ormer.Read(token, "AccessKey", "SecrectKey"); err == nil {
		ormer.LoadRelated(token, "User")
		return token
	}
	return nil
}

var DefaultUserManager = NewUserManager()
var DefaultTokenManager = NewTokenManager()

func init() {
	orm.RegisterModel(new(User), new(Token))
}

D:\Workspace\Go\src\gocmdb\server\utils\crypto.go

package utils

import (
	"crypto/md5"
	"fmt"
	"strings"
)

func Md5Salt(text string, salt string) string {
	if salt == "" {
		salt = RandString(8)
	}
	return fmt.Sprintf("%s:%x", salt, md5.Sum([]byte(fmt.Sprintf("%s:%s", salt, text))))
}

func SplitMd5Salt(text string) (string, string) {
	nodes := strings.SplitN(text, ":", 2)
	if len(nodes) >= 2 {
		return nodes[0], nodes[1]
	} else {
		return "", nodes[0]
	}
}

D:\Workspace\Go\src\gocmdb\server\utils\rand.go

package utils

import (
	"math/rand"
	"time"
)

func RandString(length int) string {
	letters := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
	count := len(letters)
	chars := make([]byte, length)
	for i := 0; i < length; i++ {
		chars[i] = letters[rand.Int()%count]
		// rand.Intn(count)
	}
	return string(chars)
}

func init() {
	rand.Seed(time.Now().UnixNano())
}

D:\Workspace\Go\src\gocmdb\server\models\enum.go

package models

const (
	StatusUnlock = 0
	StatusLock   = 1
)

D:\Workspace\Go\src\gocmdb\server\routers\router.go

package routers

import (
	"github.com/astaxie/beego"

	"gocmdb/server/controllers"
	"gocmdb/server/controllers/auth"
)

func init() {
	beego.AutoRouter(&auth.AuthController{})
	beego.AutoRouter(&controllers.TestController{})
}

D:\Workspace\Go\src\gocmdb\server\views\auth\login.html

corrida de abelha

http://localhost:8888/auth/login

Digite o nome de usuário e a senha para pular para a página de teste

controlador de base

LayoutController Controlador de layout, usado para definir layout, layoutSections, memu e expandir

D:\Workspace\Go\src\gocmdb\server\controllers\layout.go

package controllers

import "gocmdb/server/controllers/auth"

type LayoutController struct {
	auth.LoginRequiredController
}

func (c *LayoutController) Prepare() {
	c.LoginRequiredController.Prepare()
	c.Layout = "layouts/base.html"

	c.LayoutSections = map[string]string{
		"LayoutStyle":  "",
		"LayoutScript": "",
	}

	c.Data["menu"] = ""
	c.Data["expand"] = ""
}

perguntar

modelo

Gerenciamento de usuários:

Verificação de login, carregamento da página de gerenciamento, carregamento de dados do usuário, adicionar/excluir/modificar/bloquear/desbloquear, visualização/geração de token

用户属性
Id
name varchar(32) 默认空字符串
password varchar(1024) 默认空字符串
gender int 默认0, 0 女 1 男
birthday date 允许为null, 默认值为null
tel varchar(32) 默认为空字符串
email varchar(64) 默认为空字符
addr varchar(512) 默认为空字符串
remark varchar(1024) 默认为空字符串
is_superuser bool 默认为false, true超级管理员,false普通管理员
status int 默认为0, 0表示正常,1表示锁定
created_time datetime 添加时初始化事件
updated_time datetime 更新时间,允许为null,默认为null
逻辑删除
deleted_time datetime 删除时间,允许为null,默认为null, null未删除,非null已删除

Gerenciamento de usuários:

Gerenciamento de usuários (adicionar/excluir (excluir lógica)/modificar/verificar/bloquear/desbloquear)

Gerenciamento de token (gerar/regenerar)

Autenticação de login (sessão da web/token de API) 

D:\Workspace\Go\src\gocmdb\server\controllers\user.go

Controlador da página de gerenciamento de usuários 

package controllers

type UserPageController struct {
	LayoutController
}

func (c *UserPageController) Index() {
	c.Data["menu"] = "user_management"
	c.Data["expand"] = "system_management"
	c.TplName = "user_page/index.html"
	c.LayoutSections["LayoutScript"] = "user_page/index.script.html"
}

adicionar rota

D:\Workspace\Go\src\gocmdb\server\routers\router.go

beego.AutoRouter(&controllers.UserPageController{})

D:\Workspace\Go\src\gocmdb\server\views\user_page\index.html

D:\Workspace\Go\src\gocmdb\server\views\user_page\index.script.html

http://localhost:8888/userpage/index

Controlador de gerenciamento de usuários

D:\Workspace\Go\src\gocmdb\server\controllers\user.go

Gerenciamento de usuários (adicionar/excluir (excluir lógica)/modificar/verificar/bloquear/desbloquear) 

type UserController struct {
	auth.LoginRequiredController
}

func (c *UserController) List() {
	//draw,start, length, q
	draw, _ := c.GetInt("draw")
	start, _ := c.GetInt64("start")
	length, _ := c.GetInt("length")
	q := strings.TrimSpace(c.GetString("q"))

	// []*User, total, queryTotal
	users, total, queryTotal := models.DefaultUserManager.Query(q, start, length)

	c.Data["json"] = map[string]interface{}{
		"code":            200,
		"text":            "获取成功",
		"result":          users,
		"draw":            draw,
		"recordsTotal":    total,
		"recordsFiltered": queryTotal,
	}
	c.ServeJSON()
}

func (c *UserController) Create() {
	if c.Ctx.Input.IsPost() {
		json := map[string]interface{}{
			"code": 400,
			"text": "提交数据错误",
		}

		form := &forms.UserCreateForm{}
		valid := &validation.Validation{}
		if err := c.ParseForm(form); err == nil {
			if ok, err := valid.Valid(form); err != nil {
				valid.SetError("error", err.Error())
				json["result"] = valid.Errors
			} else if ok {
				user, err := models.DefaultUserManager.Create(form.Name, form.Password, form.Gender, form.BirthdayTime, form.Tel, form.Email, form.Addr, form.Remark)
				if err == nil {
					json = map[string]interface{}{
						"code":   200,
						"text":   "创建成功",
						"result": user,
					}
				} else {
					json = map[string]interface{}{
						"code": 500,
						"text": "服务器错误",
					}
				}
			} else {
				json["result"] = valid.Errors
			}
		} else {
			valid.SetError("error", err.Error())
			json["result"] = valid.Errors
		}
		c.Data["json"] = json
		c.ServeJSON()
	} else {
		//get
		c.TplName = "user/create.html"
	}
}

func (c *UserController) Modify() {
	if c.Ctx.Input.IsPost() {
		json := map[string]interface{}{
			"code": 400,
			"text": "提交数据错误",
		}
		form := &forms.UserModifyForm{}
		valid := &validation.Validation{}
		if err := c.ParseForm(form); err == nil {
			if ok, err := valid.Valid(form); err != nil {
				valid.SetError("error", err.Error())
				json["result"] = valid.Errors
			} else if ok {
				user, err := models.DefaultUserManager.Modify(form.Id, form.Name, form.Gender, form.BirthdayTime, form.Tel, form.Email, form.Addr, form.Remark)

				if err == nil {
					json = map[string]interface{}{
						"code":   200,
						"text":   "更新成功",
						"result": user,
					}
				} else {
					json = map[string]interface{}{
						"code": 500,
						"text": "服务器错误",
					}
				}
			} else {
				json["result"] = valid.Errors
			}
		} else {
			valid.SetError("error", err.Error())
			json["result"] = valid.Errors
		}
		c.Data["json"] = json
		c.ServeJSON()

	} else {
		//get
		pk, _ := c.GetInt("pk")
		c.TplName = "user/modify.html"
		c.Data["object"] = models.DefaultUserManager.GetById(pk)
	}
}

func (c *UserController) Delete() {
	pk, _ := c.GetInt("pk")
	models.DefaultUserManager.DeleteById(pk)
	c.Data["json"] = map[string]interface{}{
		"code":   200,
		"text":   "删除成功",
		"result": nil, //可以返回删除的用户
	}
	c.ServeJSON()
}

func (c *UserController) Lock() {
	pk, _ := c.GetInt("pk")
	models.DefaultUserManager.SetStatusById(pk, 1)
	c.Data["json"] = map[string]interface{}{
		"code":   200,
		"text":   "锁定成功",
		"result": nil, //可以返回删除的用户
	}
	c.ServeJSON()
}

func (c *UserController) UnLock() {
	pk, _ := c.GetInt("pk")
	models.DefaultUserManager.SetStatusById(pk, 0)
	c.Data["json"] = map[string]interface{}{
		"code":   200,
		"text":   "解锁成功",
		"result": nil, //可以返回删除的用户
	}
	c.ServeJSON()
}

func (c *UserController) Password() {
	if c.Ctx.Input.IsPost() {
		json := map[string]interface{}{
			"code": 400,
			"text": "提交数据错误",
		}

		form := &forms.UserPasswordForm{User: c.User}
		valid := &validation.Validation{}
		if err := c.ParseForm(form); err == nil {
			if ok, err := valid.Valid(form); err != nil {
				valid.SetError("error", err.Error())
				json["result"] = valid.Errors
			} else if ok {
				err := models.DefaultUserManager.UpdatePassword(c.User.Id, form.Password)
				if err == nil {
					json = map[string]interface{}{
						"code": 200,
						"text": "修改密码成功",
					}
				} else {
					json = map[string]interface{}{
						"code": 500,
						"text": "服务器错误",
					}
				}
			} else {
				json["result"] = valid.Errors
			}
		} else {
			valid.SetError("error", err.Error())
			json["result"] = valid.Errors
		}

		c.Data["json"] = json
		c.ServeJSON()
	} else {
		c.TplName = "user/password.html"
	}
}

D:\Workspace\Go\src\gocmdb\server\forms\user.go

package forms

import (
	"strings"
	"time"

	"gocmdb/server/models"

	"github.com/astaxie/beego/validation"
)

type UserCreateForm struct {
	Name           string `form:"name"`
	Password       string `form:"password"`
	PasswordVerify string `form:"passwordVerify"`
	Gender         int    `form:"gender"`
	Birthday       string `form:"birthday"`
	Tel            string `form:"tel"`
	Email          string `form:"email"`
	Addr           string `form:"addr"`
	Remark         string `form:"remark"`

	BirthdayTime *time.Time
}

func (f *UserCreateForm) Valid(v *validation.Validation) {
	f.Name = strings.TrimSpace(f.Name)
	f.Password = strings.TrimSpace(f.Password)
	f.PasswordVerify = strings.TrimSpace(f.PasswordVerify)
	f.Tel = strings.TrimSpace(f.Tel)
	f.Email = strings.TrimSpace(f.Email)
	f.Addr = strings.TrimSpace(f.Addr)
	f.Remark = strings.TrimSpace(f.Remark)

	v.AlphaDash(f.Name, "name.name").Message("用户名只能由数字、英文字母、中划线和下划线组成")
	v.MinSize(f.Name, 5, "name.name").Message("用户名长度必须在%d-%d之内", 5, 32)
	v.MaxSize(f.Name, 32, "name.name").Message("用户名长度必须在%d-%d之内", 5, 32)

	if _, ok := v.ErrorsMap["name"]; !ok && models.DefaultUserManager.GetByName(f.Name) != nil {
		v.SetError("name", "用户名已存在")
	}

	v.MinSize(f.Password, 6, "password.password").Message("密码长度必须在%d-%d之内", 6, 32)
	v.MaxSize(f.Password, 32, "password.password").Message("密码长度必须在%d-%d之内", 6, 32)

	if f.PasswordVerify != f.PasswordVerify {
		v.SetError("passwordVerify", "两次输入密码不一致")
	}

	v.Range(f.Gender, 0, 1, "gender.gender").Message("性别选择不正确")

	if birthday, err := time.Parse("2006-01-02", f.Birthday); err != nil {
		v.SetError("birthday", "出生日期不正确")
	} else {
		f.BirthdayTime = &birthday
	}

	v.Phone(f.Tel, "tel.tel").Message("电话格式不正确")
	v.Email(f.Email, "email.email").Message("邮箱格式不正确")

	v.MaxSize(f.Addr, 512, "addr.addr").Message("住址长度必须在512个字符之内")
	v.MaxSize(f.Remark, 512, "remark.remark").Message("备注长度必须在512个字符之内")
}

type UserModifyForm struct {
	Id       int    `form:"id"`
	Name     string `form:"name"`
	Gender   int    `form:"gender"`
	Birthday string `form:"birthday"`
	Tel      string `form:"tel"`
	Email    string `form:"email"`
	Addr     string `form:"addr"`
	Remark   string `form:"remark"`

	BirthdayTime *time.Time
}

func (f *UserModifyForm) Valid(v *validation.Validation) {
	f.Name = strings.TrimSpace(f.Name)
	f.Tel = strings.TrimSpace(f.Tel)
	f.Email = strings.TrimSpace(f.Email)
	f.Addr = strings.TrimSpace(f.Addr)
	f.Remark = strings.TrimSpace(f.Remark)

	if models.DefaultUserManager.GetById(f.Id) == nil {
		v.SetError("error", "操作对象不存在")
		return
	}

	v.AlphaDash(f.Name, "name.name").Message("用户名只能由数字、英文字母、中划线和下划线组成")
	v.MinSize(f.Name, 5, "name.name").Message("用户名长度必须在%d-%d之内", 5, 32)
	v.MaxSize(f.Name, 32, "name.name").Message("用户名长度必须在%d-%d之内", 5, 32)

	if _, ok := v.ErrorsMap["name"]; !ok {
		if user := models.DefaultUserManager.GetByName(f.Name); user != nil && user.Id != f.Id {
			v.SetError("name", "用户名已存在")
		}
	}

	v.Range(f.Gender, 0, 1, "gender.gender").Message("性别选择不正确")

	if birthday, err := time.Parse("2006-01-02", f.Birthday); err != nil {
		v.SetError("birthday", "出生日期不正确")
	} else {
		f.BirthdayTime = &birthday
	}

	v.Phone(f.Tel, "tel.tel").Message("电话格式不正确")
	v.Email(f.Email, "email.email").Message("邮箱格式不正确")

	v.MaxSize(f.Addr, 512, "addr.addr").Message("住址长度必须在512个字符之内")
	v.MaxSize(f.Remark, 512, "remark.remark").Message("备注长度必须在512个字符之内")
}

type UserPasswordForm struct {
	OldPassword    string `form:"oldPassword"`
	Password       string `form:"password"`
	PasswordVerify string `form:"passwordVerify"`

	User *models.User
}

func (f *UserPasswordForm) Valid(v *validation.Validation) {
	f.OldPassword = strings.TrimSpace(f.OldPassword)
	f.Password = strings.TrimSpace(f.Password)
	f.PasswordVerify = strings.TrimSpace(f.PasswordVerify)

	if !f.User.ValidatePassword(f.OldPassword) {
		v.SetError("oldPassword", "密码不正确")
	}

	v.MinSize(f.Password, 6, "password.password").Message("密码长度必须在%d-%d之内", 6, 32)
	v.MaxSize(f.Password, 32, "password.password").Message("密码长度必须在%d-%d之内", 6, 32)

	if f.PasswordVerify != f.PasswordVerify {
		v.SetError("passwordVerify", "两次输入密码不一致")
	}
}

D:\Workspace\Go\src\gocmdb\server\routers\router.go

beego.AutoRouter(&controllers.UserController{})

D:\Workspace\Go\src\gocmdb\server\views\user\create.html

D:\Workspace\Go\src\gocmdb\server\views\user\modify.html

D:\Workspace\Go\src\gocmdb\server\views\user\password.html

D:\Workspace\Go\src\gocmdb\server\controllers\user.go

type TokenController struct {
	auth.LoginRequiredController
}

func (c *TokenController) Generate() {
	if c.Ctx.Input.IsPost() {
		pk, _ := c.GetInt("pk")
		models.DefaultTokenManager.GenerateByUser(models.DefaultUserManager.GetById(pk))
		c.Data["json"] = map[string]interface{}{
			"code":   200,
			"text":   "生成Token成功",
			"result": nil, //可以返回Token
		}
		c.ServeJSON()
	} else {
		pk, _ := c.GetInt("pk")
		c.Data["object"] = models.DefaultUserManager.GetById(pk)
		c.TplName = "token/index.html"
	}
}

 D:\Workspace\Go\src\gocmdb\server\routers\router.go

    beego.AutoRouter(&controllers.TokenController{})

D:\Workspace\Go\src\gocmdb\server\views\token\index.html

Gerenciamento de usuários

    Um
        processo de criação/edição de diálogo é concluído
        e o processo de bloqueio/desbloqueio/exclusão é concluído

    O usuário atual conectado não pode bloquear/excluir/desbloquear a si mesmo.
    O usuário atual só pode visualizar e gerar seu próprio token

    alerta de mudança de senha
    => sweetalert

1. A solicitação Ajax não está logada e retorna json
2. Criar


gerenciamento multinuvem

Gerencie várias plataformas de nuvem
    Alibaba Cloud
    Tencent Cloud
    aws
    azure
    Huawei Cloud
    JD Cloud
    Qingyun
    Openstack
    ...

obter máquina virtual
iniciar
parar
reiniciar

servidor/nuvem

plugins/aliyun
        inquilino
        aws

instância do gerenciador
=> vm


Digite string
Name string
Init(addr region accessKey, secretKey)
TestConnect() error
GetInstances() []*Instance
StartInstance(uuid) error
StopInstance(uuid) error
RebootInstance(uuid) error


informações de configuração

    Configuração da região
    do endereço     de autenticação


    ID da plataforma
    Nome
    Tipo
    Addr
    AccessKey
    SecretKey
    Região
    Observação
    CreatedTime
    DeletedTime
    SyncTime
    CreateUser rel,reverse
    Status


Plataforma de máquina virtual
    1: n

    UUID
    Nome
    CPU
    Memeory
    OS
    PrivateAddrs
    PublicAddrs
    Status string
    VmCreatedTime
    VmExpiredTime

    Hora criadaHora
    excluídaHora
    atualizadaHora

Página de gerenciamento da plataforma em nuvem

D:\Workspace\Go\src\gocmdb\server\conf\app.conf

home=UserPageController.Index

Controlador de página de gerenciamento de plataforma em nuvem

D:\Workspace\Go\src\gocmdb\server\controllers\cloud.go

package controllers

type CloudPlatformPageController struct {
	LayoutController
}

func (c *CloudPlatformPageController) Index() {
	c.Data["expand"] = "cloud_management"
	c.Data["menu"] = "cloud_platform_management"

	c.TplName = "cloud_platform_page/index.html"
	c.LayoutSections["LayoutScript"] = "cloud_platform_page/index.script.html"
}

D:\Workspace\Go\src\gocmdb\server\views\cloud_platform_page\index.html

D:\Workspace\Go\src\gocmdb\server\views\cloud_platform_page\index.script.html

D:\Workspace\Go\src\gocmdb\server\routers\router.go

	// 认证
	beego.AutoRouter(&auth.AuthController{})

	// 用户页面
	beego.AutoRouter(&controllers.UserPageController{})

	// 用户
	beego.AutoRouter(&controllers.UserController{})
	beego.AutoRouter(&controllers.TokenController{})

	// 云平台页面
	beego.AutoRouter(&controllers.CloudPlatformPageController{})

Controlador de gerenciamento de plataforma em nuvem

D:\Workspace\Go\src\gocmdb\server\controllers\cloud.go

type CloudPlatformController struct {
	auth.LoginRequiredController
}

func (c *CloudPlatformController) List() {
	//draw,start, length, q
	draw, _ := c.GetInt("draw")
	start, _ := c.GetInt64("start")
	length, _ := c.GetInt("length")
	q := strings.TrimSpace(c.GetString("q"))

	result, total, queryTotal := models.DefaultCloudPlatformManager.Query(q, start, length)

	c.Data["json"] = map[string]interface{}{
		"code":            200,
		"text":            "获取成功",
		"result":          result,
		"draw":            draw,
		"recordsTotal":    total,
		"recordsFiltered": queryTotal,
	}
	c.ServeJSON()
}

func (c *CloudPlatformController) Create() {
	if c.Ctx.Input.IsPost() {
		form := &forms.CloudPlatformCreateForm{}
		valid := &validation.Validation{}
		json := map[string]interface{}{
			"code":   400,
			"text":   "提交数据错误",
			"result": nil,
		}

		if err := c.ParseForm(form); err != nil {
			valid.SetError("error", err.Error())
			json["result"] = valid.Errors
		} else {
			if ok, err := valid.Valid(form); err != nil {
				valid.SetError("error", err.Error())
				json["result"] = valid.Errors
			} else if ok {
				result, err := models.DefaultCloudPlatformManager.Create(
					form.Name,
					form.Type,
					form.Addr,
					form.Region,
					form.AccessKey,
					form.SecrectKey,
					form.Remark,
					c.User,
				)
				if err == nil {
					json = map[string]interface{}{
						"code":   200,
						"text":   "创建成功",
						"result": result,
					}
				} else {
					json = map[string]interface{}{
						"code":   500,
						"text":   "创建失败, 请重试",
						"result": nil,
					}
				}
			} else {
				json["result"] = valid.Errors
			}
		}
		c.Data["json"] = json
		c.ServeJSON()
	} else {
		c.TplName = "cloud_platform/create.html"
		c.Data["types"] = cloud.DefaultManager.Plugins
	}
}

func (c *CloudPlatformController) Delete() {
	if c.Ctx.Input.IsPost() {
		pk, _ := c.GetInt("pk")
		models.DefaultCloudPlatformManager.DeleteById(pk)
	}
	c.Data["json"] = map[string]interface{}{
		"code":   200,
		"text":   "删除成功",
		"result": nil,
	}
	c.ServeJSON()
}

Lista de exibição de dados da plataforma de nuvem

Criação de plataforma de nuvem criar

Modificação da modificação da plataforma de nuvem

Operações de plataforma em nuvem desabilitar desabilitar, habilitar habilitar, deletar deletar

D:\Workspace\Go\src\gocmdb\server\models\cloud.go

package models

import (
	"time"

	"github.com/astaxie/beego/orm"
)

type CloudPlatform struct {
	Id          int        `orm:"column(id);" json:"id"`
	Name        string     `orm:"column(name);size(64);" json:"name"`
	Type        string     `orm:"column(type);size(32);" json:"type"`
	Addr        string     `orm:"column(addr);size(1024);" json:"addr"`
	AccessKey   string     `orm:"column(access_key);size(1024);" json:"-"`
	SecrectKey  string     `orm:"column(secrect_key);size(1024);" json:"-"`
	Region      string     `orm:"column(region);size(64);" json:"region"`
	Remark      string     `orm:"column(remark);size(1024);" json:"remark"`
	CreatedTime *time.Time `orm:"column(created_time);type(datetime);auto_now_add;" json:"created_time"`
	DeletedTime *time.Time `orm:"column(deleted_time);type(datetime);null;" json:"deleted_time"`
	SyncTime    *time.Time `orm:"column(sync_time);type(datetime);null;" json:"sync_time"`
	User        *User      `orm:"column(user);rel(fk);" json:"user"`
	Status      int        `orm:"column(status);" json:"status"`

	VirtualMachines []*VirtualMachine `orm:"reverse(many);" json:"virtual_machines"`
}

func (p *CloudPlatform) IsEnable() bool {
	return p.Status == 0
}

type CloudPlatformManager struct{}

func (m *CloudPlatformManager) Query(q string, start int64, length int) ([]*CloudPlatform, int64, int64) {
	ormer := orm.NewOrm()
	queryset := ormer.QueryTable(&CloudPlatform{})

	condition := orm.NewCondition()
	condition = condition.And("deleted_time__isnull", true)

	total, _ := queryset.SetCond(condition).Count()

	qtotal := total
	if q != "" {
		query := orm.NewCondition()
		query = query.Or("name__icontains", q)
		query = query.Or("addr__icontains", q)
		query = query.Or("remark__icontains", q)
		query = query.Or("region__icontains", q)
		condition = condition.AndCond(query)

		qtotal, _ = queryset.SetCond(condition).Count()
	}
	var result []*CloudPlatform

	queryset.SetCond(condition).Limit(length).Offset(start).All(&result)
	return result, total, qtotal
}

func NewCloudPlatformManager() *CloudPlatformManager {
	return &CloudPlatformManager{}
}

func (m *CloudPlatformManager) GetByName(name string) *CloudPlatform {
	ormer := orm.NewOrm()
	// var result CloudPlatform
	result := &CloudPlatform{}
	err := ormer.QueryTable(&CloudPlatform{}).Filter("deleted_time__isnull", true).Filter("name__exact", name).One(result)
	if err == nil {
		return result
	}
	return nil
}

func (m *CloudPlatformManager) Create(name, typ, addr, region, accessKey, secrectKey, remark string, user *User) (*CloudPlatform, error) {
	ormer := orm.NewOrm()
	result := &CloudPlatform{
		Name:       name,
		Type:       typ,
		Addr:       addr,
		Region:     region,
		AccessKey:  accessKey,
		SecrectKey: secrectKey,
		Remark:     remark,
		User:       user,
		Status:     0,
	}
	if _, err := ormer.Insert(result); err != nil {
		return nil, err
	}
	return result, nil
}

func (m *CloudPlatformManager) DeleteById(id int) error {
	orm.NewOrm().QueryTable(&CloudPlatform{}).Filter("Id__exact", id).Update(orm.Params{"DeletedTime": time.Now()})
	return nil
}

type VirtualMachine struct {
	Id            int            `orm:"column(id)" json:"id"`
	Platform      *CloudPlatform `orm:"column(platform);rel(fk);" json:"platform"`
	UUID          string         `orm:"column(uuid);size(128);" json:"uuid"`
	Name          string         `orm:"column(name);size(64);" json:"name"`
	CPU           int            `orm:"column(cpu);" json:"cpu"`
	Mem           int64          `orm:"column(mem);" json:"mem"`
	OS            string         `orm:"column(os);size(128);" json:"os"`
	PrivateAddrs  string         `orm:"column(private_addrs);size(1024);" json:"private_addrs"`
	PublicAddrs   string         `orm:"column(public_addrs);size(1024);" json:"public_addrs"`
	Status        string         `orm:"column(status);size(32);" json:"status"`
	VmCreatedTime string         `orm:"column(vm_created_time);" json:"vm_created_time"`
	VmExpiredTime string         `orm:"column(vm_expired_time);" json:"vm_expired_time"`

	CreatedTime *time.Time `orm:"column(created_time);auto_now_add;type(datetime);" json:"created_time"`
	DeletedTime *time.Time `orm:"column(deleted_time);type(datetime);null" json:"deleted_time"`
	UpdatedTime *time.Time `orm:"column(updated_time);auto_now;type(datetime);" json:"updated_time"`
}

type VirtualMachineManager struct{}

func NewVirtualMachineManager() *VirtualMachineManager {
	return &VirtualMachineManager{}
}

func (m *VirtualMachineManager) Query(q string, start int64, length int) ([]*VirtualMachine, int64, int64) {
	ormer := orm.NewOrm()
	queryset := ormer.QueryTable(&VirtualMachine{})

	condition := orm.NewCondition()
	condition = condition.And("deleted_time__isnull", true)

	total, _ := queryset.SetCond(condition).Count()

	qtotal := total
	if q != "" {
		query := orm.NewCondition()
		query = query.Or("name__icontains", q)
		query = query.Or("public_addrs__icontains", q)
		query = query.Or("private_addrs__icontains", q)
		query = query.Or("os__icontains", q)
		condition = condition.AndCond(query)

		qtotal, _ = queryset.SetCond(condition).Count()
	}
	var result []*VirtualMachine

	queryset.SetCond(condition).Limit(length).Offset(start).All(&result)
	return result, total, qtotal
}

var DefaultCloudPlatformManager = NewCloudPlatformManager()
var DefaultVirtualMachineManager = NewVirtualMachineManager()

func init() {
	orm.RegisterModel(&CloudPlatform{}, new(VirtualMachine))
}

D:\Workspace\Go\src\gocmdb\server\forms\cloud.go

package forms

import (
	"strings"

	"github.com/astaxie/beego/validation"

	"gocmdb/server/cloud"
	"gocmdb/server/models"
)

type CloudPlatformCreateForm struct {
	Name       string `form:"name"`
	Type       string `form:"type"`
	Addr       string `form:"addr"`
	AccessKey  string `form:"access_key"`
	SecrectKey string `form:"secrect_key"`
	Region     string `form:"region"`
	Remark     string `form:"remark"`
}

func (f *CloudPlatformCreateForm) Valid(v *validation.Validation) {
	f.Name = strings.TrimSpace(f.Name)
	f.Type = strings.TrimSpace(f.Type)
	f.Addr = strings.TrimSpace(f.Addr)
	f.AccessKey = strings.TrimSpace(f.AccessKey)
	f.SecrectKey = strings.TrimSpace(f.SecrectKey)
	f.Region = strings.TrimSpace(f.Region)
	f.Remark = strings.TrimSpace(f.Remark)

	v.AlphaDash(f.Name, "name.name").Message("名字只能由大小写英文、数字、下划线和中划线组成")
	v.MinSize(f.Name, 5, "name.name").Message("名字长度必须在%d-%d之内", 5, 32)
	v.MaxSize(f.Name, 32, "name.name").Message("名字长度必须在%d-%d之内", 5, 32)

	if _, ok := v.ErrorsMap["name"]; !ok && models.DefaultCloudPlatformManager.GetByName(f.Name) != nil {
		v.SetError("name", "名称已存在")
	}

	v.MinSize(f.Addr, 1, "addr.addr").Message("地址不能为空且长度必须在%d之内", 1024)
	v.MaxSize(f.Addr, 1024, "addr.addr").Message("地址不能为空且长度必须在%d之内", 1024)

	v.MinSize(f.Region, 1, "region.region").Message("区域不能为空且长度必须在%d之内", 64)
	v.MaxSize(f.Region, 64, "region.region").Message("区域不能为空且长度必须在%d之内", 64)

	v.MinSize(f.AccessKey, 1, "access_key.access_key").Message("AccessKey不能为空且长度必须在%d之内", 1024)
	v.MaxSize(f.AccessKey, 1024, "access_key.access_key").Message("AccessKey不能为空不能为空且长度必须在%d之内", 1024)

	v.MinSize(f.SecrectKey, 1, "secrect_key.secrect_key").Message("SecrectKey不能为空且长度必须在%d之内", 1024)
	v.MaxSize(f.SecrectKey, 1024, "secrect_key.secrect_key").Message("SecrectKey不能为空且长度必须在%d之内", 1024)

	v.MaxSize(f.Remark, 1024, "remark.remark").Message("备注长度必须在%d之内", 1024)

	if sdk, ok := cloud.DefaultManager.Cloud(f.Type); !ok {
		v.SetError("type", "类型错误")
	} else if !v.HasErrors() {
		sdk.Init(f.Addr, f.Region, f.AccessKey, f.SecrectKey)
		if sdk.TestConnect() != nil {
			v.SetError("type", "配置参数错误")
		}
	}
}

D:\Workspace\Go\src\gocmdb\server\controllers\cloud.go

type VirtualMachinePageController struct {
	LayoutController
}

func (c *VirtualMachinePageController) Index() {
	c.Data["expand"] = "cloud_management"
	c.Data["menu"] = "virtual_machine_management"

	c.TplName = "virtual_machine_page/index.html"
	c.LayoutSections["LayoutScript"] = "virtual_machine_page/index.script.html"
}


type VirtualMachineController struct {
	auth.LoginRequiredController
}

func (c *VirtualMachineController) List() {
	//draw,start, length, q
	draw, _ := c.GetInt("draw")
	start, _ := c.GetInt64("start")
	length, _ := c.GetInt("length")
	q := strings.TrimSpace(c.GetString("q"))

	result, total, queryTotal := models.DefaultVirtualMachineManager.Query(q, start, length)

	c.Data["json"] = map[string]interface{}{
		"code":            200,
		"text":            "获取成功",
		"result":          result,
		"draw":            draw,
		"recordsTotal":    total,
		"recordsFiltered": queryTotal,
	}
	c.ServeJSON()
}

 D:\Workspace\Go\src\gocmdb\server\routers\router.go

package routers

import (
	"github.com/astaxie/beego"

	"gocmdb/server/controllers"
	"gocmdb/server/controllers/auth"
)

func init() {
	// 认证
	beego.AutoRouter(&auth.AuthController{})

	// 用户页面
	beego.AutoRouter(&controllers.UserPageController{})

	// 用户
	beego.AutoRouter(&controllers.UserController{})
	beego.AutoRouter(&controllers.TokenController{})

	// 云平台页面
	beego.AutoRouter(&controllers.CloudPlatformPageController{})

	// 云平台
	beego.AutoRouter(&controllers.CloudPlatformController{})

	// 云主机页面
	beego.AutoRouter(&controllers.VirtualMachinePageController{})
	// 云主机
	beego.AutoRouter(&controllers.VirtualMachineController{})
}

página da máquina virtual

operação da máquina virtual

D:\Workspace\Go\src\gocmdb\server\cloud\base.go

package cloud

type Instance struct {
}

type ICloud interface {
	Type() string
	Name() string
	Init(string, string, string, string)
	TestConnect() error
	GetInstance() []*Instance
	StartInstance(string) error
	StopInstance(string) error
	RebootInstance(string) error
}

D:\Workspace\Go\src\gocmdb\server\cloud\manager.go

package cloud

type Manager struct {
	Plugins map[string]ICloud
}

func NewManager() *Manager {
	return &Manager{
		Plugins: make(map[string]ICloud),
	}
}

func (m *Manager) Register(c ICloud) {
	m.Plugins[c.Type()] = c
}

func (m *Manager)Cloud(typ string) (ICloud, bool) {
	cloud, ok := m.Plugins[typ]
	return cloud, ok
}


var DefaultManager = NewManager()

D:\Workspace\Go\src\gocmdb\server\cloud\plugins\init.go

package plugins

import (
	_ "gocmdb/server/cloud/plugins/tenant"
)

Tencent Cloud Operations

D:\Workspace\Go\src\gocmdb\server\cloud\plugins\tenant\tenant.go

package tenant

import (
	"fmt"

	"gocmdb/server/cloud"

	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
	cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
)

type TenantCloud struct {
	addr       string
	region     string
	accessKey  string
	secrectKey string
	credential *common.Credential
	profile    *profile.ClientProfile
}

func (c *TenantCloud) Type() string {
	return "tenant"
}

func (c *TenantCloud) Name() string {
	return "腾讯云"
}

func (c *TenantCloud) Init(addr, region, accessKey, secrectKey string) {
	c.addr = addr
	c.region = region
	c.accessKey = accessKey
	c.secrectKey = secrectKey

	c.credential = common.NewCredential(c.accessKey, c.secrectKey)
	c.profile = profile.NewClientProfile()
	c.profile.HttpProfile.Endpoint = c.addr
}

func (c *TenantCloud) TestConnect() error {
	client, err := cvm.NewClient(c.credential, c.region, c.profile)
	if err != nil {
		fmt.Println(err)
		return err
	}
	request := cvm.NewDescribeRegionsRequest()
	_, err = client.DescribeRegions(request)
	fmt.Println(err)
	return err
}

func (c *TenantCloud) GetInstance() []*cloud.Instance {
	return nil
}

func (c *TenantCloud) StartInstance(uuid string) error {
	return nil
}

func (c *TenantCloud) StopInstance(uuid string) error {
	return nil
}

func (c *TenantCloud) RebootInstance(uuid string) error {
	return nil
}

func init() {
	cloud.DefaultManager.Register(new(TenantCloud))
}

D:\Workspace\Go\src\gocmdb\server\views\cloud_platform\create.html

D:\Workspace\Go\src\gocmdb\server\views\virtual_machine_page\index.html

D:\Workspace\Go\src\gocmdb\server\views\virtual_machine_page\index.script.html

Operações em Nuvem do Alibaba

D:\Workspace\Go\src\gocmdb\server\cloud\plugins\aliyun\aliyun.go

package aliyun

import (
	"gocmdb/server/cloud"

	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
	"github.com/aliyun/alibaba-cloud-sdk-go/services/ecs"
)

type Aliyun struct {
	addr       string
	region     string
	accessKey  string
	secrectKey string
}

func (c *Aliyun) Type() string {
	return "aliyun"
}

func (c *Aliyun) Name() string {
	return "阿里云"
}

func (c *Aliyun) Init(addr, region, accessKey, secrectKey string) {
	c.addr = addr
	c.region = region
	c.accessKey = accessKey
	c.secrectKey = secrectKey
}

func (c *Aliyun) TestConnect() error {
	client, err := ecs.NewClientWithAccessKey(c.region, c.accessKey, c.secrectKey)
	if err != nil {
		return err
	}

	request := ecs.CreateDescribeRegionsRequest()
	request.Scheme = "https"

	_, err = client.DescribeRegions(request)
	return err
}

func (c *Aliyun) GetInstance() []*cloud.Instance {
	var (
		offset int = 1
		limit  int = 100
		total  int = 2
		rt     []*cloud.Instance
	)

	for offset < total {
		var instances []*cloud.Instance
		total, instances = c.getInstanceByOffsetLimit(offset, limit)
		if offset == 1 {
			rt = make([]*cloud.Instance, 0, total)
		}
		rt = append(rt, instances...)
	}
	return rt
}

func (c *Aliyun) transformStatus(status string) string {
	smap := map[string]string{
		"Running":  cloud.StatusRunning,
		"Stopped":  cloud.StatusStopped,
		"Starting": cloud.StatusStarting,
		"Stopping": cloud.StatusStopping,
	}

	if rt, ok := smap[status]; ok {
		return rt
	}
	return cloud.StatusUnknow
}

func (c *Aliyun) getInstanceByOffsetLimit(offset, limit int) (int, []*cloud.Instance) {
	client, err := ecs.NewClientWithAccessKey(c.region, c.accessKey, c.secrectKey)
	if err != nil {
		return 0, nil
	}

	request := ecs.CreateDescribeInstancesRequest()
	request.Scheme = "https"

	request.PageNumber = requests.NewInteger(offset)
	request.PageSize = requests.NewInteger(100)

	response, err := client.DescribeInstances(request)
	if err != nil {
		return 0, nil
	}

	total := response.TotalCount
	instances := response.Instances.Instance

	rt := make([]*cloud.Instance, len(instances))

	for index, instance := range instances {
		publicAddrs := make([]string, 0)
		privateAddrs := make([]string, 0)

		if "" != instance.EipAddress.IpAddress {
			publicAddrs = append(publicAddrs, instance.EipAddress.IpAddress)
		}
		publicAddrs = append(publicAddrs, instance.PublicIpAddress.IpAddress...)

		privateAddrs = append(privateAddrs, instance.InnerIpAddress.IpAddress...)
		privateAddrs = append(instance.VpcAttributes.PrivateIpAddress.IpAddress)

		rt[index] = &cloud.Instance{
			UUID:         instance.InstanceId,
			Name:         instance.InstanceName,
			OS:           instance.OSName,
			Mem:          int64(instance.Memory),
			CPU:          instance.Cpu,
			PublicAddrs:  publicAddrs,
			PrivateAddrs: privateAddrs,
			Status:       c.transformStatus(instance.Status),
			CreatedTime:  instance.CreationTime,
			ExpiredTime:  instance.ExpiredTime,
		}
	}

	return total, rt
}

func (c *Aliyun) StartInstance(uuid string) error {
	client, err := ecs.NewClientWithAccessKey(c.region, c.accessKey, c.secrectKey)
	if err != nil {
		return err
	}

	request := ecs.CreateStartInstanceRequest()
	request.Scheme = "https"

	request.InstanceId = uuid

	_, err = client.StartInstance(request)
	return err
}

func (c *Aliyun) StopInstance(uuid string) error {
	client, err := ecs.NewClientWithAccessKey(c.region, c.accessKey, c.secrectKey)
	if err != nil {
		return err
	}

	request := ecs.CreateStopInstanceRequest()
	request.Scheme = "https"

	request.InstanceId = uuid

	_, err = client.StopInstance(request)
	return err
}

func (c *Aliyun) RebootInstance(uuid string) error {
	client, err := ecs.NewClientWithAccessKey(c.region, c.accessKey, c.secrectKey)
	if err != nil {
		return err
	}

	request := ecs.CreateRebootInstanceRequest()
	request.Scheme = "https"

	request.InstanceId = uuid

	_, err = client.RebootInstance(request)
	return err
}

func init() {
	cloud.DefaultManager.Register(new(Aliyun))
}

Tencent Cloud Operations

D:\Workspace\Go\src\gocmdb\server\cloud\plugins\tenant\tenant.go

package tenant

import (
	"fmt"

	"gocmdb/server/cloud"

	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
	cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
)

type TenantCloud struct {
	addr       string
	region     string
	accessKey  string
	secrectKey string
	credential *common.Credential
	profile    *profile.ClientProfile
}

func (c *TenantCloud) Type() string {
	return "tenant"
}

func (c *TenantCloud) Name() string {
	return "腾讯云"
}

func (c *TenantCloud) Init(addr, region, accessKey, secrectKey string) {
	c.addr = addr
	c.region = region
	c.accessKey = accessKey
	c.secrectKey = secrectKey

	c.credential = common.NewCredential(c.accessKey, c.secrectKey)
	c.profile = profile.NewClientProfile()
	c.profile.HttpProfile.Endpoint = c.addr
}

func (c *TenantCloud) TestConnect() error {
	client, err := cvm.NewClient(c.credential, c.region, c.profile)
	if err != nil {
		fmt.Println(err)
		return err
	}
	request := cvm.NewDescribeRegionsRequest()
	_, err = client.DescribeRegions(request)
	fmt.Println(err)
	return err
}

func (c *TenantCloud) GetInstance() []*cloud.Instance {
	var (
		offset int64 = 0
		limit  int64 = 100
		total  int64 = 1
		rt     []*cloud.Instance
	)

	for offset < total {
		var instances []*cloud.Instance
		total, instances = c.getInstanceByOffsetLimit(offset, limit)
		// 判断第一次请求初始化rt
		if offset == 0 {
			rt = make([]*cloud.Instance, 0, total)
		}
		rt = append(rt, instances...)
		offset += limit
	}

	return rt
}

func (c *TenantCloud) transformStatus(status string) string {
	smap := map[string]string{
		"PENDING":       cloud.StatusPending,
		"LAUNCH_FAILED": cloud.StatusLaunchFailed,
		"RUNNING":       cloud.StatusRunning,
		"STOPPED":       cloud.StatusStopped,
		"STARTING":      cloud.StatusStarting,
		"STOPPING":      cloud.StatusStopping,
		"REBOOTING":     cloud.StatusRebooting,
		"SHUTDOWN":      cloud.StatusShutdown,
		"TERMINATING":   cloud.StatusTerminating,
	}

	if rt, ok := smap[status]; ok {
		return rt
	}
	return cloud.StatusUnknow
}

func (c *TenantCloud) getInstanceByOffsetLimit(offset, limit int64) (int64, []*cloud.Instance) {
	client, err := cvm.NewClient(c.credential, c.region, c.profile)
	if err != nil {
		// 通过log记录
		return 0, nil
	}

	request := cvm.NewDescribeInstancesRequest()
	request.Limit = &limit
	request.Offset = &offset

	response, err := client.DescribeInstances(request)
	if err != nil {
		// 通过log记录
		return 0, nil
	}

	//总数
	total := *response.Response.TotalCount
	instances := response.Response.InstanceSet

	rt := make([]*cloud.Instance, len(instances))

	for index, instance := range instances {
		publicAddrs := make([]string, len(instance.PublicIpAddresses))
		privateAddrs := make([]string, len(instance.PrivateIpAddresses))
		for index, addr := range instance.PublicIpAddresses {
			publicAddrs[index] = *addr
		}

		for index, addr := range instance.PrivateIpAddresses {
			privateAddrs[index] = *addr
		}

		rt[index] = &cloud.Instance{
			UUID:         *instance.InstanceId,
			Name:         *instance.InstanceName,
			OS:           *instance.OsName,
			CPU:          int(*instance.CPU),
			Mem:          *instance.Memory * 1024,
			PublicAddrs:  publicAddrs,
			PrivateAddrs: privateAddrs,
			Status:       c.transformStatus(*instance.InstanceState),
			CreatedTime:  *instance.CreatedTime,
			ExpiredTime:  *instance.ExpiredTime,
		}
	}

	return total, rt
}

func (c *TenantCloud) StartInstance(uuid string) error {
	client, err := cvm.NewClient(c.credential, c.region, c.profile)
	if err != nil {
		return err
	}

	request := cvm.NewStartInstancesRequest()
	request.InstanceIds = []*string{&uuid}

	_, err = client.StartInstances(request)
	return err
}

func (c *TenantCloud) StopInstance(uuid string) error {
	client, err := cvm.NewClient(c.credential, c.region, c.profile)
	if err != nil {
		return err
	}

	request := cvm.NewStopInstancesRequest()
	request.InstanceIds = []*string{&uuid}

	_, err = client.StopInstances(request)
	return err

}

func (c *TenantCloud) RebootInstance(uuid string) error {
	client, err := cvm.NewClient(c.credential, c.region, c.profile)
	if err != nil {
		return err
	}

	request := cvm.NewRebootInstancesRequest()
	request.InstanceIds = []*string{&uuid}

	_, err = client.RebootInstances(request)
	return err
}

func init() {
	cloud.DefaultManager.Register(new(TenantCloud))
}

D:\Workspace\Go\src\gocmdb\server\cloud\plugins\init.go

package plugins

import (
	_ "gocmdb/server/cloud/plugins/tenant"

	_ "gocmdb/server/cloud/plugins/aliyun"
)

D:\Workspace\Go\src\gocmdb\server\cloud\base.go

package cloud

const (
	StatusPending = "创建中"
	StatusLaunchFailed = "创建失败"
	StatusRunning = "运行中"
	StatusStopped = "已停止"
	StatusStarting = "开机中"
	StatusStopping = "停止中"
	StatusRebooting = "重启中"
	StatusTerminating = "销毁中"
	StatusShutdown = "停止待销毁"
	StatusUnknow = "未知"
)

type Instance struct {
	UUID string
	Name string
	OS string
	CPU int
	Mem int64
	PublicAddrs []string
	PrivateAddrs []string
	Status string
	CreatedTime string
	ExpiredTime string
}

func (i *Instance) String() string {
	return i.Name
}

type ICloud interface {
	Type() string
	Name() string
	Init(string, string, string, string)
	TestConnect() error
	GetInstance() []*Instance
	StartInstance(string) error
	StopInstance(string) error
	RebootInstance(string) error
}

sincronização de máquina virtual

O usuário opera a máquina virtual

D:\Workspace\Go\src\gocmdb\server\controllers\cloud.go

func (c *CloudPlatformController) Create() {
	if c.Ctx.Input.IsPost() {
		form := &forms.CloudPlatformCreateForm{}
		valid := &validation.Validation{}
		json := map[string]interface{}{
			"code":   400,
			"text":   "提交数据错误",
			"result": nil,
		}

		if err := c.ParseForm(form); err != nil {
			valid.SetError("error", err.Error())
			json["result"] = valid.Errors
		} else {
			if ok, err := valid.Valid(form); err != nil {
				valid.SetError("error", err.Error())
				json["result"] = valid.Errors
			} else if ok {
				result, err := models.DefaultCloudPlatformManager.Create(
					form.Name,
					form.Type,
					form.Addr,
					form.Region,
					form.AccessKey,
					form.SecrectKey,
					form.Remark,
					c.User,
				)
				if err == nil {
					json = map[string]interface{}{
						"code":   200,
						"text":   "创建成功",
						"result": result,
					}
				} else {
					json = map[string]interface{}{
						"code":   500,
						"text":   "创建失败, 请重试",
						"result": nil,
					}
				}
			} else {
				json["result"] = valid.Errors
			}
		}
		c.Data["json"] = json
		c.ServeJSON()
	} else {
		c.TplName = "cloud_platform/create.html"
		c.Data["types"] = cloud.DefaultManager.Plugins
	}
}

func (c *CloudPlatformController) Delete() {
	if c.Ctx.Input.IsPost() {
		pk, _ := c.GetInt("pk")
		models.DefaultCloudPlatformManager.DeleteById(pk)
	}
	c.Data["json"] = map[string]interface{}{
		"code":   200,
		"text":   "删除成功",
		"result": nil,
	}
	c.ServeJSON()
}

type VirtualMachinePageController struct {
	LayoutController
}

func (c *VirtualMachinePageController) Index() {
	c.Data["expand"] = "cloud_management"
	c.Data["menu"] = "virtual_machine_management"

	c.TplName = "virtual_machine_page/index.html"
	c.LayoutSections["LayoutScript"] = "virtual_machine_page/index.script.html"
}

type VirtualMachineController struct {
	auth.LoginRequiredController
}

func (c *VirtualMachineController) List() {
	//draw,start, length, q
	draw, _ := c.GetInt("draw")
	start, _ := c.GetInt64("start")
	length, _ := c.GetInt("length")
	q := strings.TrimSpace(c.GetString("q"))

	result, total, queryTotal := models.DefaultVirtualMachineManager.Query(q, start, length)

	c.Data["json"] = map[string]interface{}{
		"code":            200,
		"text":            "获取成功",
		"result":          result,
		"draw":            draw,
		"recordsTotal":    total,
		"recordsFiltered": queryTotal,
	}
	c.ServeJSON()
}

func (c *VirtualMachineController) Start() {
	pk, _ := c.GetInt("pk")

	if vm := models.DefaultVirtualMachineManager.GetById(pk); vm != nil {
		fmt.Println(vm, vm.Platform)
		if sdk, ok := cloud.DefaultManager.Cloud(vm.Platform.Type); ok {
			fmt.Println(sdk)
			sdk.Init(vm.Platform.Addr, vm.Platform.Region, vm.Platform.AccessKey, vm.Platform.SecrectKey)
			if nil == sdk.StartInstance(vm.UUID) {
				c.Data["json"] = map[string]interface{}{
					"code":   200,
					"text":   "启动成功",
					"result": nil,
				}
				c.ServeJSON()
			}
		}
	}

	c.Data["json"] = map[string]interface{}{
		"code":   400,
		"text":   "启动失败",
		"result": nil,
	}
	c.ServeJSON()
}

func (c *VirtualMachineController) Stop() {
	pk, _ := c.GetInt("pk")

	if vm := models.DefaultVirtualMachineManager.GetById(pk); vm != nil {
		if sdk, ok := cloud.DefaultManager.Cloud(vm.Platform.Type); ok {
			sdk.Init(vm.Platform.Addr, vm.Platform.Region, vm.Platform.AccessKey, vm.Platform.SecrectKey)
			if nil == sdk.StopInstance(vm.UUID) {
				c.Data["json"] = map[string]interface{}{
					"code":   200,
					"text":   "停止成功",
					"result": nil,
				}

				c.ServeJSON()
			}
		}
	}

	c.Data["json"] = map[string]interface{}{
		"code":   400,
		"text":   "停止失败",
		"result": nil,
	}
	c.ServeJSON()
}

func (c *VirtualMachineController) Reboot() {
	pk, _ := c.GetInt("pk")

	if vm := models.DefaultVirtualMachineManager.GetById(pk); vm != nil {
		if sdk, ok := cloud.DefaultManager.Cloud(vm.Platform.Type); ok {
			sdk.Init(vm.Platform.Addr, vm.Platform.Region, vm.Platform.AccessKey, vm.Platform.SecrectKey)
			if nil == sdk.RebootInstance(vm.UUID) {
				c.Data["json"] = map[string]interface{}{
					"code":   200,
					"text":   "重启成功",
					"result": nil,
				}

				c.ServeJSON()
			}
		}
	}

	c.Data["json"] = map[string]interface{}{
		"code":   400,
		"text":   "重启失败",
		"result": nil,
	}
	c.ServeJSON()
}

D:\Workspace\Go\src\gocmdb\server\cloud.go

package main

import (
	"flag"
	"fmt"
	"os"
	"time"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/orm"
	_ "github.com/go-sql-driver/mysql"

	"gocmdb/server/models"
)

func main() {
	// 初始化命令行参数
	h := flag.Bool("h", false, "help")
	help := flag.Bool("help", false, "help")
	verbose := flag.Bool("v", false, "verbose")

	flag.Usage = func() {
		fmt.Println("usage: cloud -h")
		flag.PrintDefaults()
	}
	// 解析命令行参数
	flag.Parse()

	if *h || *help {
		flag.Usage()
		os.Exit(0)
	}

	// 设置日志到文件
	beego.SetLogger("file", `{
		"filename" : "logs/cloud.log",
		"level" : 7}`,
	)
	if !*verbose {
		//删除控制台日志
		beego.BeeLogger.DelLogger("console")
	} else {
		orm.Debug = true
	}

	// 初始化orm
	orm.RegisterDriver("mysql", orm.DRMySQL)
	orm.RegisterDataBase("default", "mysql", beego.AppConfig.String("dsn"))

	// 测试数据库连接是否正常
	if db, err := orm.GetDB(); err != nil || db.Ping() != nil {
		beego.Error("数据库连接错误")
		os.Exit(-1)
	}

	for now := range time.Tick(10 * time.Second) {
		fmt.Println(now)
		platforms, _, _ := models.DefaultCloudPlatformManager.Query("", 0, 0)
		for _, platform := range platforms {
			if !platform.IsEnable() {
				continue
			}
			fmt.Println(platform)
		}
	}

}

D:\Workspace\Go\src\gocmdb\server\models\cloud.go

package models

import (
	"fmt"
	"gocmdb/server/cloud"
	"strings"
	"time"

	"github.com/astaxie/beego/orm"
)

type CloudPlatform struct {
	Id          int        `orm:"column(id);" json:"id"`
	Name        string     `orm:"column(name);size(64);" json:"name"`
	Type        string     `orm:"column(type);size(32);" json:"type"`
	Addr        string     `orm:"column(addr);size(1024);" json:"addr"`
	AccessKey   string     `orm:"column(access_key);size(1024);" json:"-"`
	SecrectKey  string     `orm:"column(secrect_key);size(1024);" json:"-"`
	Region      string     `orm:"column(region);size(64);" json:"region"`
	Remark      string     `orm:"column(remark);size(1024);" json:"remark"`
	CreatedTime *time.Time `orm:"column(created_time);type(datetime);auto_now_add;" json:"created_time"`
	DeletedTime *time.Time `orm:"column(deleted_time);type(datetime);null;" json:"deleted_time"`
	SyncTime    *time.Time `orm:"column(sync_time);type(datetime);null;" json:"sync_time"`
	User        *User      `orm:"column(user);rel(fk);" json:"user"`
	Status      int        `orm:"column(status);" json:"status"`
	Msg         string     `orm:"column(msg);size(1024)" json:"msg"`

	VirtualMachines []*VirtualMachine `orm:"reverse(many);" json:"virtual_machines"`
}

func (p *CloudPlatform) IsEnable() bool {
	return p.Status == 0
}

type CloudPlatformManager struct{}

func (m *CloudPlatformManager) Query(q string, start int64, length int) ([]*CloudPlatform, int64, int64) {
	ormer := orm.NewOrm()
	queryset := ormer.QueryTable(&CloudPlatform{})

	condition := orm.NewCondition()
	condition = condition.And("deleted_time__isnull", true)

	total, _ := queryset.SetCond(condition).Count()

	qtotal := total
	if q != "" {
		query := orm.NewCondition()
		query = query.Or("name__icontains", q)
		query = query.Or("addr__icontains", q)
		query = query.Or("remark__icontains", q)
		query = query.Or("region__icontains", q)
		condition = condition.AndCond(query)

		qtotal, _ = queryset.SetCond(condition).Count()
	}
	var result []*CloudPlatform

	queryset.SetCond(condition).Limit(length).Offset(start).All(&result)
	return result, total, qtotal
}

func NewCloudPlatformManager() *CloudPlatformManager {
	return &CloudPlatformManager{}
}

func (m *CloudPlatformManager) GetByName(name string) *CloudPlatform {
	ormer := orm.NewOrm()
	// var result CloudPlatform
	result := &CloudPlatform{}
	err := ormer.QueryTable(&CloudPlatform{}).Filter("deleted_time__isnull", true).Filter("name__exact", name).One(result)
	if err == nil {
		return result
	}
	return nil
}

func (m *CloudPlatformManager) Create(name, typ, addr, region, accessKey, secrectKey, remark string, user *User) (*CloudPlatform, error) {
	ormer := orm.NewOrm()
	result := &CloudPlatform{
		Name:       name,
		Type:       typ,
		Addr:       addr,
		Region:     region,
		AccessKey:  accessKey,
		SecrectKey: secrectKey,
		Remark:     remark,
		User:       user,
		Status:     0,
	}
	if _, err := ormer.Insert(result); err != nil {
		return nil, err
	}
	return result, nil
}

func (m *CloudPlatformManager) DeleteById(id int) error {
	orm.NewOrm().QueryTable(&CloudPlatform{}).Filter("Id__exact", id).Update(orm.Params{"DeletedTime": time.Now()})
	return nil
}

func (m *CloudPlatformManager) SyncInfo(platform *CloudPlatform, now time.Time, msg string) error {
	platform.SyncTime = &now
	platform.Msg = msg
	_, err := orm.NewOrm().Update(platform)
	return err
}

type VirtualMachine struct {
	Id            int            `orm:"column(id)" json:"id"`
	Platform      *CloudPlatform `orm:"column(platform);rel(fk);" json:"platform"`
	UUID          string         `orm:"column(uuid);size(128);" json:"uuid"`
	Name          string         `orm:"column(name);size(64);" json:"name"`
	CPU           int            `orm:"column(cpu);" json:"cpu"`
	Mem           int64          `orm:"column(mem);" json:"mem"`
	OS            string         `orm:"column(os);size(128);" json:"os"`
	PrivateAddrs  string         `orm:"column(private_addrs);size(1024);" json:"private_addrs"`
	PublicAddrs   string         `orm:"column(public_addrs);size(1024);" json:"public_addrs"`
	Status        string         `orm:"column(status);size(32);" json:"status"`
	VmCreatedTime string         `orm:"column(vm_created_time);" json:"vm_created_time"`
	VmExpiredTime string         `orm:"column(vm_expired_time);" json:"vm_expired_time"`

	CreatedTime *time.Time `orm:"column(created_time);auto_now_add;type(datetime);" json:"created_time"`
	DeletedTime *time.Time `orm:"column(deleted_time);type(datetime);null" json:"deleted_time"`
	UpdatedTime *time.Time `orm:"column(updated_time);auto_now;type(datetime);" json:"updated_time"`
}

type VirtualMachineManager struct{}

func NewVirtualMachineManager() *VirtualMachineManager {
	return &VirtualMachineManager{}
}

func (m *VirtualMachineManager) Query(q string, platform int, start int64, length int) ([]*VirtualMachine, int64, int64) {
	ormer := orm.NewOrm()
	queryset := ormer.QueryTable(&VirtualMachine{})

	condition := orm.NewCondition()
	condition = condition.And("deleted_time__isnull", true)

	total, _ := queryset.SetCond(condition).Count()

	if q != "" {
		query := orm.NewCondition()
		query = query.Or("name__icontains", q)
		query = query.Or("public_addrs__icontains", q)
		query = query.Or("private_addrs__icontains", q)
		query = query.Or("os__icontains", q)
		condition = condition.AndCond(query)
	}

	if platform > 0 {
		condition = condition.And("platform__exact", platform)
	}
	var result []*VirtualMachine

	qtotal, _ := queryset.SetCond(condition).Count()
	queryset.SetCond(condition).RelatedSel().Limit(length).Offset(start).All(&result)
	return result, total, qtotal
}
func (m *VirtualMachineManager) SyncInstance(instance *cloud.Instance, platform *CloudPlatform) {
	ormer := orm.NewOrm()
	vm := &VirtualMachine{UUID: instance.UUID, Platform: platform}
	// 是否创建, id,error
	if _, _, err := ormer.ReadOrCreate(vm, "UUID", "Platform"); err != nil {
		fmt.Println(err)
		return
	}

	vm.Name = instance.Name
	vm.OS = instance.OS
	vm.CPU = instance.CPU
	vm.Mem = instance.Mem
	vm.Status = instance.Status
	vm.VmCreatedTime = instance.CreatedTime
	vm.VmExpiredTime = instance.ExpiredTime
	vm.PublicAddrs = strings.Join(instance.PublicAddrs, ",")
	vm.PrivateAddrs = strings.Join(instance.PrivateAddrs, ",")
	ormer.Update(vm)
}

func (m *VirtualMachineManager) SyncInstanceStatus(now time.Time, platform *CloudPlatform) {
	orm.NewOrm().QueryTable(&VirtualMachine{}).Filter("Platform__exact", platform).Filter("UpdatedTime__lt", now).Update(orm.Params{"DeletedTime": now})
	orm.NewOrm().QueryTable(&VirtualMachine{}).Filter("Platform__exact", platform).Filter("UpdatedTime__gte", now).Update(orm.Params{"DeletedTime": nil})
}

func (m *VirtualMachineManager) GetById(id int) *VirtualMachine {
	vm := &VirtualMachine{}
	if nil == orm.NewOrm().QueryTable(new(VirtualMachine)).RelatedSel().Filter("id__exact", id).Filter("DeletedTime__isnull", true).One(vm) {
		return vm
	}
	return nil
}

func (m *VirtualMachineManager) DeleteByPlatform(platform *CloudPlatform) {
	orm.NewOrm().QueryTable(&VirtualMachine{}).Filter("Platform__exact", platform).Update(orm.Params{"DeletedTime": time.Now()})
}

func (m *VirtualMachineManager) DeleteByPlatformId(platform int) {
	orm.NewOrm().QueryTable(&VirtualMachine{}).Filter("Platform__exact", platform).Update(orm.Params{"DeletedTime": time.Now()})
}

var DefaultCloudPlatformManager = NewCloudPlatformManager()
var DefaultVirtualMachineManager = NewVirtualMachineManager()

func init() {
	orm.RegisterModel(&CloudPlatform{}, new(VirtualMachine))
}

D:\Workspace\Go\src\gocmdb\server\controllers\cloud.go

func (c *VirtualMachineController) List() {
	//draw,start, length, q
	draw, _ := c.GetInt("draw")
	start, _ := c.GetInt64("start")
	length, _ := c.GetInt("length")
	q := strings.TrimSpace(c.GetString("q"))
	platform, _ := c.GetInt("platform")

	result, total, queryTotal := models.DefaultVirtualMachineManager.Query(q, platform, start, length)

	c.Data["json"] = map[string]interface{}{
		"code":            200,
		"text":            "获取成功",
		"result":          result,
		"draw":            draw,
		"recordsTotal":    total,
		"recordsFiltered": queryTotal,
	}
	c.ServeJSON()
}

D:\Workspace\Go\src\gocmdb\server\cloud.go

func main() {
	// 初始化命令行参数
	h := flag.Bool("h", false, "help")
	help := flag.Bool("help", false, "help")
	verbose := flag.Bool("v", false, "verbose")

	flag.Usage = func() {
		fmt.Println("usage: cloud -h")
		flag.PrintDefaults()
	}
	// 解析命令行参数
	flag.Parse()

	if *h || *help {
		flag.Usage()
		os.Exit(0)
	}

	// 设置日志到文件
	beego.SetLogger("file", `{
		"filename" : "logs/cloud.log",
		"level" : 7}`,
	)
	if !*verbose {
		//删除控制台日志
		beego.BeeLogger.DelLogger("console")
	} else {
		orm.Debug = true
	}

	// 初始化orm
	orm.RegisterDriver("mysql", orm.DRMySQL)
	orm.RegisterDataBase("default", "mysql", beego.AppConfig.String("dsn"))

	// 测试数据库连接是否正常
	if db, err := orm.GetDB(); err != nil || db.Ping() != nil {
		beego.Error("数据库连接错误")
		os.Exit(-1)
	}

	for now := range time.Tick(10 * time.Second) {
		fmt.Println(now)
		platforms, _, _ := models.DefaultCloudPlatformManager.Query("", 0, 0)
		for _, platform := range platforms {
			if !platform.IsEnable() {
				continue
			}
			if sdk, ok := cloud.DefaultManager.Cloud(platform.Type); !ok {
				fmt.Println("云平台未注册")
			} else {
				sdk.Init(platform.Addr, platform.Region, platform.AccessKey, platform.SecrectKey)

				if err := sdk.TestConnect(); err != nil {
					fmt.Println("测试链接失败:", err)
					models.DefaultCloudPlatformManager.SyncInfo(platform, now, fmt.Sprintf("测试链接失败: %s", err.Error()))
				} else {
					for _, instance := range sdk.GetInstance() {
						models.DefaultVirtualMachineManager.SyncInstance(instance, platform)
					}
					models.DefaultVirtualMachineManager.SyncInstanceStatus(now, platform)
					models.DefaultCloudPlatformManager.SyncInfo(platform, now, "")
				}
			}


		}

	}

}

vá executar cloud.go

vá correr web.go

10 horas

Tencent Cloud
a 10 pontos
b 10 pontos


= >
ab

apagado b

11 horas

umas 11 horas


Atualizações antes das 11 horas não são excluídas

várias plataformas de nuvem

A.
para plataforma de nuvem
    B.
    para máquina virtual
        C.

    D. Unificar a atualização de status na plataforma atual (filtragem de plataforma)

E. Unificar as atualizações no banco de dados

b B KB MB GB TB PB
 /8
b
função FileSizeb(size) {     if(size < 8) {         return size.toFixed(2) + "b";     }     tamanho /= 8;     var índice = 0;     var unidades = ["B", "KB", "MB", "GB", "TB", "PB"];





    while(tamanho >= 1024) {         tamanho /= 1024;         índice += 1;     }     return size.toFixed(2) + unidades[índice]; }




desenvolvimento de agente

biblioteca de registros logrus

Endereço: github.com/sirupsen/logrus

configurar

logrus.SetLevel(logrus.DebugLevel)

logrus.SetFormatter(&logrus.TextFormatter{FullTimestamp: true})

logrus.SetOutput(os.Stdout)

log de gravação

logrus.WithFields(logrus.Fields{}).Debug (msg)

logrus.WithFields(logrus.Fields{}).Info (msg)

logrus.WithFields(logrus.Fields{}).Error(msg)

identifica exclusivamente uuid

Endereço: github.com/google/uuid

Gerar uuid.New().String()

pare o sinal de serviço

Acesso a informação

cliente http

comece

configuração

ENScommunication

processamento de pulsação

Processamento de informações cadastrais

processamento de log

gerenciamento de plugins

Inicialização e inicialização do plug-in

plug-in de pulsação

mensagem de registro

mensagem de registro

informações de recursos

informações de recursos

informações de registro

registro

Armazém de código github.com/yunixinagfeng/gocmdb/agent

forma de comunicação

 agente

    Principais funções:
        Down => Up (servidor)
        heartbeat
        Informações do servidor do agente
            IP/Hostname/CPU/memória/disco rígido
        para coletar informações de dados
            CPU/memória/uso de disco/IO...


Atravesse todos os plug-ins de loop a cada segundo
    Compare se o próximo tempo de execução do plug-in é < o tempo atual
        deve ser executado
1. url?a=b&c=d
2. formulário:
    codificação do corpo do post => arquivo/ não arquivo
    arquivo : multipart/form-data
            application/ x-www-form-urlencode

    corpo json

      agente json
=> servidor

solicitar

Linha de solicitação GET url? params HTTP/1.1
cabeçalho de solicitação COOKIE: xxx
       HOST: XX

corpo do pedido

url =》 controlelr/ação

api/heartbeat/123123123123/
:uuid

api/xxx/uuid/


uuid
Hostname
IP
OS
ARCH
CPU
RAM
DISK
BootTime

time="2023-06-25T11:18:51+08:00" level=debug msg="插件执行" Name=resource Result="{74d2472e61c2448f969fe99d32a8b63b 1 {\"load\":\"{\\\"load1\\\":0,\\\"load5\\\":0,\\\"load15\\\":0}\",\"cpu_percent\":3.125,\"ram_percent\":61,\"disk_percent\":\"{\\\"C:\\\":52.42117791025501,\\\"D:\\\":31.402267189477897,\\\"E:\\\":28.87792364710162,\\\"F:\\\":33.71282548371619}\"} 2023-06-25 11:18:51.2330884 +0800 CST m=+185.998250001}"
time="2023-06-25T11:18:51+08:00" level=debug msg="日志上传成功" result="map[code:200 result:<nil> text:成功]"time="2023-06-25T11:18:58+08:00" level=debug msg="插件执行" Name=heartbeat Result="{74d2472e61c2448f969fe99d32a8b63b 2023-06-25 11:18:58.8535355 +0800 CST m=+193.618716201}"
time="2023-06-25T11:18:58+08:00" level=debug msg="上传心跳信息成功" result="map[code:200 result:<nil> text:成
功]"

desenvolvimento terminal

início: principal

D:\Workspace\Go\src\gocmdb\agent\agentd.go

package main

import (
	"os"
	"os/signal"
	"syscall"

	"github.com/sirupsen/logrus"

	"gocmdb/agent/config"
	"gocmdb/agent/ens"

	"gocmdb/agent/plugins"
	_ "gocmdb/agent/plugins/init"
)

func main() {
	logrus.SetLevel(logrus.DebugLevel)
	gconf, err := config.NewConfig()
	if err != nil {
		logrus.Error("读取配置出错:", err)
		os.Exit(-1)
	}
	defer func() {
		os.Remove(gconf.PidFile)
	}()
	log, err := os.OpenFile(gconf.LogFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, os.ModePerm)
	if err != nil {
		logrus.Error("打开日志文件出错:", err)
		os.Exit(-1)
	}
	defer func() {
		log.Close()
	}()

	// logrus.SetFormatter(&logrus.JSONFormatter{})
	logrus.SetFormatter(&logrus.TextFormatter{})
	// logrus.SetOutput(log)
	logrus.WithFields(logrus.Fields{
		"PID":  gconf.PID,
		"UUID": gconf.UUID,
	}).Info("Agent启动")

	plugins.DefaultManager.Init(gconf)

	ens.NewENS(gconf).Start()
	plugins.DefaultManager.Start()

	ch := make(chan os.Signal, 1)
	signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
	<-ch

	logrus.WithFields(logrus.Fields{
		"PID":  gconf.PID,
		"UUID": gconf.UUID,
	}).Info("Agent退出")
}

Configuração: configuração

D:\Workspace\Go\src\gocmdb\agent\config\config.go

package config

import (
	"io/ioutil"
	"os"
	"strconv"
	"strings"

	"github.com/google/uuid"
)

type Config struct {
	UUID     string
	UUIDFile string

	Endpoint string
	Token    string

	LogFile string

	PID     int
	PidFile string

	Heartbeat chan interface{}
	Register  chan interface{}
	Log       chan interface{}
}

func NewConfig() (*Config, error) {
	UUIDFile := "agentd.uuid"
	PidFile := "agentd.pid"
	LogFile := "logs/agent.log"

	UUID := ""
	if cxt, err := ioutil.ReadFile(UUIDFile); err == nil {
		UUID = string(cxt)
	} else if os.IsNotExist(err) {
		//strings.Replace(uuid.New().String(), "-", "", -1)
		UUID = strings.ReplaceAll(uuid.New().String(), "-", "")
		ioutil.WriteFile(UUIDFile, []byte(UUID), os.ModePerm)
	} else {
		return nil, err
	}

	PID := os.Getpid()
	ioutil.WriteFile(PidFile, []byte(strconv.Itoa(PID)), os.ModePerm)

	return &Config{
		Endpoint:  "http://localhost:8888/v1/api",
		UUID:      UUID,
		UUIDFile:  UUIDFile,
		LogFile:   LogFile,
		PID:       PID,
		PidFile:   PidFile,
		Heartbeat: make(chan interface{}, 64),
		Register:  make(chan interface{}, 64),
		Log:       make(chan interface{}, 10240),
	}, nil
}

Módulo de comunicação: ens

D:\Workspace\Go\src\gocmdb\agent\ens\ens.go

package ens

import (
	"fmt"

	"github.com/imroc/req"
	"github.com/sirupsen/logrus"

	"gocmdb/agent/config"
)

type ENS struct {
	conf *config.Config
}

func NewENS(conf *config.Config) *ENS {
	return &ENS{conf: conf}
}

func (s *ENS) Start() {
	logrus.Info("ENS 开始运行")
	go func() {
		endpoint := fmt.Sprintf("%s/heartbeat/%s/", s.conf.Endpoint, s.conf.UUID)
		for evt := range s.conf.Heartbeat {
			response, err := req.New().Post(endpoint, req.BodyJSON(evt))
			if err == nil {
				result := map[string]interface{}{}
				response.ToJSON(&result)
				logrus.WithFields(logrus.Fields{
					"result": result,
				}).Debug("上传心跳信息成功")
			} else {
				logrus.WithFields(logrus.Fields{
					"error": err,
				}).Error("上传心跳信息失败")
			}
		}
	}()

	go func() {
		endpoint := fmt.Sprintf("%s/register/%s/", s.conf.Endpoint, s.conf.UUID)
		for evt := range s.conf.Register {
			response, err := req.New().Post(endpoint, req.BodyJSON(evt))
			if err == nil {
				result := map[string]interface{}{}
				response.ToJSON(&result)
				logrus.WithFields(logrus.Fields{
					"result": result,
				}).Debug("注册成功")
			} else {
				logrus.WithFields(logrus.Fields{
					"error": err,
				}).Error("注册失败")
			}
		}
	}()
	go func() {
		endpoint := fmt.Sprintf("%s/log/%s/", s.conf.Endpoint, s.conf.UUID)
		for evt := range s.conf.Log {
			response, err := req.New().Post(endpoint, req.BodyJSON(evt))
			if err == nil {
				result := map[string]interface{}{}
				response.ToJSON(&result)
				logrus.WithFields(logrus.Fields{
					"result": result,
				}).Debug("日志上传成功")
			} else {
				logrus.WithFields(logrus.Fields{
					"error": err,
				}).Error("日志上传成功")
			}
		}
	}()
}

D:\Workspace\Go\src\gocmdb\agent\entity\heartbeat.go

package entity

import "time"

type Heartbeat struct {
	UUID string    `json:"uuid"`
	Time time.Time `json:"time"`
}

func NewHeartbeat(uuid string) Heartbeat {
	return Heartbeat{
		UUID: uuid,
		Time: time.Now(),
	}
}

D:\Workspace\Go\src\gocmdb\agent\entity\log.go

package entity

import (
	"time"
	"encoding/json"
)

const (
	LOGResource = 0X0001
)

type Log struct {
	UUID string    `json:"uuid"`
	Type int       `json:"type"`
	Msg  string    `json:"msg"`
	Time time.Time `json:"time"`
}

func NewLog(uuid string, typ int, msg interface{}) Log {
	bytes, _ := json.Marshal(msg)
	return Log{
		UUID: uuid,
		Type: typ,
		Msg:  string(bytes),
		Time: time.Now(),
	}
}

D:\Workspace\Go\src\gocmdb\agent\entity\register.go

package entity

import (
	"encoding/json"
	"strings"
	"time"

	"github.com/shirou/gopsutil/cpu"
	"github.com/shirou/gopsutil/disk"
	"github.com/shirou/gopsutil/host"
	"github.com/shirou/gopsutil/mem"
	"github.com/shirou/gopsutil/net"
)

type Register struct {
	UUID     string    `json:"uuid"`
	Hostname string    `json:"hostname"`
	IP       string    `json:"ip"`
	OS       string    `json:"os"`
	Arch     string    `json:"arch"`
	CPU      int       `json:"cpu"`
	RAM      int64     `json:"ram"` // MB
	Disk     string    `json:"disk"`
	BootTime time.Time `json:"boottime"`
	Time     time.Time `json:"time"`
}

func NewRegister(uuid string) Register {
	hostInfo, _ := host.Info()

	ips := []string{}
	interfaceStatList, _ := net.Interfaces()
	for _, interfaceStat := range interfaceStatList {
		for _, addr := range interfaceStat.Addrs {
			if strings.Index(addr.Addr, ":") >= 0 {
				continue
			}
			if strings.Index(addr.Addr, "127.") == 0 {
				continue
			}
			nodes := strings.Split(addr.Addr, "/")
			ips = append(ips, nodes[0])
		}
	}

	ip, _ := json.Marshal(ips)

	cores := 0
	cpuInfoList, _ := cpu.Info()
	for _, cpuInfo := range cpuInfoList {
		cores += int(cpuInfo.Cores)
	}
	memInfo, _ := mem.VirtualMemory()

	partitionInfoList, _ := disk.Partitions(true)

	disks := map[string]int64{}
	for _, partitionInfo := range partitionInfoList {
		usageInfo, err := disk.Usage(partitionInfo.Device)
		if err != nil {
			continue
		}
		disks[usageInfo.Path] = int64(usageInfo.Total / 1024 / 1024 / 1024)
	}

	disk, _ := json.Marshal(disks)
	return Register{
		UUID:     uuid,
		Hostname: hostInfo.Hostname,
		OS:       hostInfo.OS,
		IP:       string(ip),
		Arch:     "", //hostInfo.KernelArch,
		CPU:      cores,
		RAM:      int64(memInfo.Total / 1024 / 1024),
		Disk:     string(disk),
		BootTime: time.Unix(int64(hostInfo.BootTime), 0),
		Time:     time.Now(),
	}
}

D:\Workspace\Go\src\gocmdb\agent\entity\resource.go

package entity

import (
	"encoding/json"
	"time"

	"github.com/shirou/gopsutil/cpu"
	"github.com/shirou/gopsutil/disk"
	"github.com/shirou/gopsutil/load"
	"github.com/shirou/gopsutil/mem"
)

type Resource struct {
	Load        string  `json:"load"`
	CPUPrecent  float64 `json:"cpu_percent"`
	RAMPrecent  float64 `json:"ram_percent"`
	DiskPrecent string  `json:"disk_percent"`
}

func NewResource() Resource {
	loadAvgStat, _ := load.Avg()
	cpuPercents, _ := cpu.Percent(time.Second, false)

	memInfo, _ := mem.VirtualMemory()

	disks := map[string]float64{}

	partitionInfoList, _ := disk.Partitions(true)
	for _, partitionInfo := range partitionInfoList {
		usageInfo, err := disk.Usage(partitionInfo.Device)
		if err != nil {
			continue
		}
		disks[usageInfo.Path] = usageInfo.UsedPercent
	}

	load, _ := json.Marshal(loadAvgStat)
	disk, _ := json.Marshal(disks)
	return Resource{
		Load:       string(load),
		CPUPrecent: cpuPercents[0],
		RAMPrecent: memInfo.UsedPercent,
		DiskPrecent: string(disk),
	}
}

Plugins (gerenciamento, ciclo de plugins)

D:\Workspace\Go\src\gocmdb\agent\plugins\manager.go

package plugins

import (
	"gocmdb/agent/config"
	"time"

	"github.com/sirupsen/logrus"
)

type Manager struct {
	Cycles map[string]CyclePlugin
}

func NewManager() *Manager {
	return &Manager{
		Cycles: make(map[string]CyclePlugin),
	}
}

func (m *Manager) RegisterCycle(p CyclePlugin) {
	m.Cycles[p.Name()] = p
	logrus.WithFields(logrus.Fields{
		"Name": p.Name(),
	}).Info("插件注册")
}

func (m *Manager) Init(conf *config.Config) {
	for name, plugin := range m.Cycles {
		plugin.Init(conf)
		logrus.WithFields(logrus.Fields{
			"Name": name,
		}).Info("初始化插件")
	}
}

func (m *Manager) Start() {
	go m.StartCycle()
}

func (m *Manager) StartCycle() {
	for now := range time.Tick(time.Second) {
		for name, plugin := range m.Cycles {
			if now.After(plugin.NextTime()) {
				if evt, err := plugin.Call(); err == nil {
					logrus.WithFields(logrus.Fields{
						"Name":   name,
						"Result": evt,
					}).Debug("插件执行")
					plugin.Pipline() <- evt
				} else {
					logrus.WithFields(logrus.Fields{
						"Name":  name,
						"error": err,
					}).Debug("插件执行失败")
				}
			}
		}
	}
}

var DefaultManager = NewManager()

D:\Workspace\Go\src\gocmdb\agent\plugins\base.go

package plugins

import (
	"gocmdb/agent/config"
	"time"
)

type CyclePlugin interface {
	Name() string
	Init(*config.Config)
	NextTime() time.Time
	Call() (interface{}, error)
	Pipline() chan interface{}
}

D:\Workspace\Go\src\gocmdb\agent\plugins\init\cycle.go

package init

import (
	"gocmdb/agent/plugins"
	"gocmdb/agent/plugins/cycle"
)

func init() {
	plugins.DefaultManager.RegisterCycle(&cycle.Heartbeat{})
	plugins.DefaultManager.RegisterCycle(&cycle.Register{})
	plugins.DefaultManager.RegisterCycle(&cycle.Resource{})
}

D:\Workspace\Go\src\gocmdb\agent\plugins\cycle\heartbeat.go

package cycle

import (
	"gocmdb/agent/config"
	"gocmdb/agent/entity"
	"time"
)

type Heartbeat struct {
	conf     *config.Config
	interval time.Duration
	nextTime time.Time
}

func (p *Heartbeat) Name() string {
	return "heartbeat"
}

func (p *Heartbeat) Init(conf *config.Config) {
	p.conf = conf
	p.interval = 10 * time.Second
	p.nextTime = time.Now()
}

func (p *Heartbeat) NextTime() time.Time {
	return p.nextTime
}

func (p *Heartbeat) Call() (interface{}, error) {
	p.nextTime = p.nextTime.Add(p.interval)
	return entity.NewHeartbeat(p.conf.UUID), nil
}

func (p *Heartbeat) Pipline() chan interface{} {
	return p.conf.Heartbeat
}

D:\Workspace\Go\src\gocmdb\agent\plugins\cycle\register.go

package cycle

import (
	"time"
	"gocmdb/agent/config"
	"gocmdb/agent/entity"
)

type Register struct {
	conf *config.Config
	interval time.Duration
	nextTime time.Time
}

func (p *Register) Name() string {
	return "register"
}

func (p *Register) Init(conf *config.Config) {
	p.conf = conf
	// p.interval = time.Hour
	p.interval = time.Second * 30
	p.nextTime = time.Now()
}

func (p *Register) NextTime() time.Time {
	return p.nextTime
}

func (p *Register) Call() (interface{}, error) {
	p.nextTime = p.nextTime.Add(p.interval)
	return entity.NewRegister(p.conf.UUID), nil
}

func (p *Register) Pipline() chan interface{} {
	return p.conf.Register
}

D:\Workspace\Go\src\gocmdb\agent\plugins\cycle\resource.go

package cycle

import (
	"gocmdb/agent/config"
	"gocmdb/agent/entity"
	"time"
)

type Resource struct {
	conf     *config.Config
	interval time.Duration
	nextTime time.Time
}

func (p *Resource) Name() string {
	return "resource"
}

func (p *Resource) Init(conf *config.Config) {
	p.conf = conf
	p.interval = time.Minute
	p.nextTime = time.Now()
}

func (p *Resource) NextTime() time.Time {
	return p.nextTime
}

func (p *Resource) Call() (interface{}, error) {
	p.nextTime = p.nextTime.Add(p.interval)
	return entity.NewLog(p.conf.UUID, entity.LOGResource, entity.NewResource()), nil
}

func (p *Resource) Pipline() chan interface{} {
	return p.conf.Log
}

Leitura de configuração (viper)

viper: https://github.com/spf13/viper

Suporta yaml, json e outros arquivos de configuração de formato

Use: viper.New()

viper.SetConfigName(“agente”)

viper.AddConfigPath(“.”)

viper.SetConfigType(“yaml”)

viper.ReadInConfig()

viper.GetXXX()

lado do servidor

D:\Workspace\Go\src\gocmdb\server\controllers\api\base.go

package api

import (
	"github.com/astaxie/beego"
)

type BaseController struct {
	beego.Controller
}

func (c *BaseController) Prepare() {
	c.EnableXSRF = false
}

D:\Workspace\Go\src\gocmdb\server\controllers\api\v1\api.go

package v1

import (
	"encoding/json"
	"gocmdb/server/controllers/api"
	"gocmdb/server/models"
)

type APIController struct {
	api.BaseController
}

func (c *APIController) Heartbeat() {
	models.DefaultAgentManager.Heartbeat(c.Ctx.Input.Param(":uuid"))
	c.Data["json"] = map[string]interface{}{
		"code":   200,
		"text":   "成功",
		"result": nil,
	}
	c.ServeJSON()
}

func (c *APIController) Register() {
	rt := map[string]interface{}{
		"code":   400,
		"text":   "成功",
		"result": nil,
	}
	agent := &models.Agent{}

	if err := json.Unmarshal(c.Ctx.Input.RequestBody, agent); err == nil {
		agent, created, err := models.DefaultAgentManager.CreateOrReplace(agent)
		if err == nil {
			rt = map[string]interface{}{
				"code": 200,
				"text": "成功",
				"result": map[string]interface{}{
					"created": created,
					"agent":   agent,
				},
			}
		} else {
			rt["text"] = err.Error()
		}
	} else {
		rt["text"] = err.Error()
	}
	c.Data["json"] = rt
	c.ServeJSON()
}

func (c *APIController) Log() {
	log := &models.Log{}
	if err := json.Unmarshal(c.Ctx.Input.RequestBody, log); err == nil {
		models.DefaultLogManager.Create(log)
	}
	c.Data["json"] = map[string]interface{}{
		"code":   200,
		"text":   "成功",
		"result": nil,
	}
	c.ServeJSON()
}

D:\Workspace\Go\src\gocmdb\server\models\log.go

package models

import (
	"encoding/json"
	"time"

	"github.com/astaxie/beego/orm"
)

const (
	LOGResource = 0X0001
)

type Log struct {
	UUID string     `json:"uuid"`
	Type int        `json:"type"`
	Msg  string     `json:"msg"`
	Time *time.Time `json:"time"`
}

type LogManager struct{}

func NewLogManager() *LogManager {
	return &LogManager{}
}

func (m *LogManager) Create(log *Log) {
	switch log.Type {
	case LOGResource:
		resource := &Resource{}
		if err := json.Unmarshal([]byte(log.Msg), resource); err == nil {
			DefaultResourceManager.Create(log, resource)
		}
	}
}

type Resource struct {
	Id          int        `orm:"column(id);" json:"id"`
	UUID        string     `orm:"column(uuid);size(64);" json:"uuid"`
	Load        string     `orm:"column(load);size(1024);"  json:"load"`
	CPUPrecent  float64    `orm:"column(cpu_percent);" json:"cpu_percent"`
	RAMPrecent  float64    `orm:"column(ram_percent);" json:"ram_percent"`
	DiskPrecent string     `orm:"column(disk_percent);size(4096);" json:"disk_percent"`
	Time        *time.Time `orm:"column(time);" json:"time"`
	CreatedTime *time.Time `orm:"column(created_time);auto_now_add;" json:"created_time"`
	DeletedTime *time.Time `orm:"column(deleted_time);null;" json:"deleted_time"`
}

type ResourceManager struct{}

func NewResourceManager() *ResourceManager {
	return &ResourceManager{}
}
func (m *ResourceManager) Create(log *Log, resource *Resource) {
	resource.UUID = log.UUID
	resource.Time = log.Time
	orm.NewOrm().Insert(resource)
}

var DefaultLogManager = NewLogManager()
var DefaultResourceManager = NewResourceManager()

func init() {
	orm.RegisterModel(new(Resource))
}

D:\Workspace\Go\src\gocmdb\server\models\agent.go

package models

import (
	"time"

	"github.com/astaxie/beego/orm"
)

type Agent struct {
	Id       int        `orm:"column(id);" json:"id"`
	UUID     string     `orm:"column(uuid);size(64);" json:"uuid"`
	Hostname string     `orm:"column(hostname);size(64);" json:"hostname"`
	IP       string     `orm:"column(ip);size(4096);" json:"ip"`
	OS       string     `orm:"column(os);size(64);" json:"os"`
	Arch     string     `orm:"column(arch);size(64);" json:"arch"`
	CPU      int        `orm:"column(cpu);" json:"cpu"`
	RAM      int64      `orm:"column(ram);" json:"ram"` // MB
	Disk     string     `orm:"column(disk);size(4096);" json:"disk"`
	BootTime *time.Time `orm:"column(boot_time);null;" json:"boottime"`
	Time     *time.Time `orm:"column(time);null;" json:"time"`

	HeartbeatTime *time.Time `orm:"column(heartbeat_time);null;" json:"heartbeat_time"`
	CreatedTime   *time.Time `orm:"column(created_time);auto_now_add;" json:"created_time"`
	DeletedTime   *time.Time `orm:"column(deleted_time);null;" json:"deleted_time"`

	IsOnline bool `orm:"-" json:"is_onlne"`
}

type AgentManager struct{}

func NewAgentManager() *AgentManager {
	return &AgentManager{}
}

func (m *AgentManager) CreateOrReplace(agent *Agent) (*Agent, bool, error) {
	now := time.Now()
	ormer := orm.NewOrm()
	orgAgent := &Agent{UUID: agent.UUID}
	if created, _, err := ormer.ReadOrCreate(orgAgent, "UUID"); err != nil {
		return nil, false, err
	} else {
		orgAgent.Hostname = agent.Hostname
		orgAgent.IP = agent.IP
		orgAgent.OS = agent.OS
		orgAgent.CPU = agent.CPU
		orgAgent.RAM = agent.RAM
		orgAgent.Disk = agent.Disk
		orgAgent.BootTime = agent.BootTime
		orgAgent.Time = agent.Time
		orgAgent.DeletedTime = nil

		orgAgent.HeartbeatTime = &now
		ormer.Update(orgAgent)
		return orgAgent, created, nil
	}
}

func (m *AgentManager) Heartbeat(uuid string) {
	orm.NewOrm().QueryTable(&Agent{}).Filter("UUID__exact", uuid).Update(orm.Params{"HeartbeatTime": time.Now()})
}

var DefaultAgentManager = NewAgentManager()

func init() {
	orm.RegisterModel(new(Agent))
}

D:\Workspace\Go\src\gocmdb\server\routers\router.go

	v1 "github.com/imsilence/gocmdb/server/controllers/api/v1"

	v1Namespace := beego.NewNamespace("/v1",
		beego.NSRouter("api/heartbeat/:uuid/", &v1.APIController{}, "*:Heartbeat"),
		beego.NSRouter("api/register/:uuid/", &v1.APIController{}, "*:Register"),
		beego.NSRouter("api/log/:uuid/", &v1.APIController{}, "*:Log"),
	)
	beego.AddNamespace(v1Namespace)

Linha de solicitação: GET URL?a=b
Cabeçalho da solicitação HTTP/1.1: Host:
        Cookie:
        Token:
Linha vazia
Corpo da solicitação a=b&c=d
      {"a":1, "b":2}

registrar/uuid/


1. Exibição do terminal

    consulta hostname/ip

    Ação: excluir edição:             a descrição da propriedade
    não é relevante para uploads de terminal


    Abra a página /url/
        tabela de dados da tabela de exibição de renderização

    AgentPageController
        LayoutController

    Index()
        menu = ""
        expand = ""
        tplName = ""


    lista ajax         Lista
    AgentController         Excluir


2. Registro:

    Exibição da consulta (uuid => nome do terminal)
    condição de consulta: agente uuid
             time [start_time, end_time]

3. Agente -v
    tem -v log definido para depurar, abra req.DEBUG
    nenhum log definido como info, feche req.DEBUG


A empresa possui 200 máquinas virtuais

Desenvolvimento => 5 dias
v1 => 10 máquinas testando a funcionalidade (5 dias)
    => 20 máquinas

Após 1 mês,
20 executa a versão v1

Questão:
url curl inicia uma solicitação
Autenticação: Token

vou desenvolver:

Alterei o servidor v1 na base original => alterei o
                terminal de desenvolvimento de código, recompilei e reimplementei

Servidor v2 => adicionar autenticação com base em v1, v1 api v2
    servidor api fica online

    Adicione parâmetros de token sobre o código original
    para reimplantar


        Configuração
        binária     de atualização automática

Token: Armazene você mesmo o valor do Token app.conf
Carregamento do agente Token
Header: Token: xxx


últimas 12 horas

13:30 - 16:00 Sem início

17:30

5:30 - 17:30

13:229 16:01


1 2 3 4 5ab
0 0 c

1 2 5
abc

1: a
2: b
5: c


1 2 3 4 5ab
0 0 c


startTime endTime 1min

starTime starTime + 1min startTime + 2min startTime + 3min .... startTime + n min <= endTime

17:45:32

17:45:30
hora chave: registro

para intervalo []*Recurso
    resoruce.create_time => recurso


[]*Recurso


3 uso consecutivo da CPU > 90% de alarmes

1 agente1 CPU 80
2 agente2 CPU 60
3 agente3 CPU 90
4 agente1 CPU 88
5 agente2 CPU 98
6 99
7 91


No último período X, a taxa de uso excede Z por Y vezes.
selecione uuid, count(*) where created_Time > startTime and cpu > 90 group by uuid hanving count(*) > Y


tendo


alarme

created_time tempo de geração
uuid
tipo de terminal tipo (1: off-line, 2: CPU, 3: memória)
descrição alarme descrição
status status (0, não processado, 1: processando, 1: processado)
deal_time tempo de processamento
motivo descrição do alarme


método de notificação de avisos (sms, e-mail)

Autenticação de token

ler configuração

 

Introdução aos echarts de componentes visuais

Uso de CPU e memória nas últimas 12/24 horas

Iniciar atualização de tempo setInterval ao abrir a caixa de diálogo de monitoramento

Interromper a atualização do tempo clearInterval quando a caixa de diálogo for fechada

Use jQuery.get para obter informações de uso de recursos do terminal

Use echarts.setOption para atualizar as informações do gráfico 

 

Terminal de alarme off-line

A diferença entre a hora do batimento cardíaco e a hora atual é de X minutos

SELECT * FROM agente onde heartbeat_time < X

Alarme de uso de CPU e memória

Controle de frequência de alarme

Alarme off-line:

Máximo de um alarme por dia por terminal

CPU, uso de memória

Máximo de um alarme por hora por terminal 

E-mail

O pacote gomail.v2 fornecido pelo gopkg.in suporta o envio de e-mail

Endereço do pacote: gopkg.in/gomail.v2

usar:

Defina o objeto Message (informações de e-mail, incluindo: assunto, remetente, destinatário, conteúdo, anexo, etc.)

Definir objeto de conexão do servidor STMP

Conecte-se ao servidor STMP e envie e-mail

Mensagem curta

Tencent Cloud API https://cloud.tencent.com/document/api/382/38778

desenvolvimento de agente

agente.yaml

endpoint: http://localhost:8888/v2/api
token: abc1234567
uuidfile: agentd.uuid
pidfile: agentd.pid
logfile: logs/agent.log

D:\Workspace\Go\src\gocmdb\agent\agentd.go

func main() {

	logrus.SetLevel(logrus.DebugLevel)
	req.Debug = true

	configReader := viper.New()
	configReader.SetConfigName("agent")
	configReader.SetConfigType("yaml")
	configReader.AddConfigPath("etc/")

	err := configReader.ReadInConfig()
	if err != nil {
		logrus.Error("读取配置出错:", err)
		os.Exit(-1)
	}


	gconf, err := config.NewConfig(configReader)
	if err != nil {
		logrus.Error("读取配置出错:", err)
		os.Exit(-1)
	}
	defer func() {
		os.Remove(gconf.PidFile)
	}()
	log, err := os.OpenFile(gconf.LogFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, os.ModePerm)
	if err != nil {
		logrus.Error("打开日志文件出错:", err)
		os.Exit(-1)
	}
	defer func() {
		log.Close()
	}()

	// logrus.SetFormatter(&logrus.JSONFormatter{})
	logrus.SetFormatter(&logrus.TextFormatter{})
	// logrus.SetOutput(log)
	logrus.WithFields(logrus.Fields{
		"PID":  gconf.PID,
		"UUID": gconf.UUID,
	}).Info("Agent启动")

	plugins.DefaultManager.Init(gconf)

	ens.NewENS(gconf).Start()
	plugins.DefaultManager.Start()

	ch := make(chan os.Signal, 1)
	signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
	<-ch

	logrus.WithFields(logrus.Fields{
		"PID":  gconf.PID,
		"UUID": gconf.UUID,
	}).Info("Agent退出")
}

 D:\Workspace\Go\src\gocmdb\agent\config\config.go

func NewConfig(configReader *viper.Viper) (*Config, error) {
	UUIDFile := configReader.GetString("uuidfile")
	if UUIDFile == "" {
		UUIDFile = "agentd.uuid"
	}

	PidFile := configReader.GetString("pidfile")
	if PidFile == "" {
		PidFile = "agentd.pid"
	}

	LogFile := configReader.GetString("logfile")
	if LogFile == "" {
		LogFile = "logs/agent.log"
	}
	Endpoint := configReader.GetString("endpoint")
	if Endpoint == "" {
		Endpoint = "http://localhost:8888/v1/api"
	}

	Token := configReader.GetString("token")

	UUID := ""
	if cxt, err := ioutil.ReadFile(UUIDFile); err == nil {
		UUID = string(cxt)
	} else if os.IsNotExist(err) {
		//strings.Replace(uuid.New().String(), "-", "", -1)
		UUID = strings.ReplaceAll(uuid.New().String(), "-", "")
		ioutil.WriteFile(UUIDFile, []byte(UUID), os.ModePerm)
	} else {
		return nil, err
	}

	PID := os.Getpid()
	ioutil.WriteFile(PidFile, []byte(strconv.Itoa(PID)), os.ModePerm)

	return &Config{
		Endpoint: Endpoint,
		Token: Token,
		UUID:     UUID,
		UUIDFile: UUIDFile,
		LogFile:  LogFile,
		PID:      PID,
		PidFile:  PidFile,
		Heartbeat: make(chan interface{}, 64),
		Register: make(chan interface{}, 64),
		Log: make(chan interface{}, 10240),
	}, nil
}

desenvolvimento do lado do servidor

D:\Workspace\Go\src\gocmdb\server\conf\app.conf

copyrequestbody=true

incluir "agente.conf"

D:\Workspace\Go\src\gocmdb\server\conf\agent.conf

[agente]

token=abc1234567

D:\Workspace\Go\src\gocmdb\server\controllers\agent.go

package controllers

import (
	"strings"

	"gocmdb/server/controllers/auth"
	"gocmdb/server/models"
)

type AgentPageController struct {
	LayoutController
}

func (c *AgentPageController) Index() {
	c.Data["menu"] = "agent_management"

	c.TplName = "agent_page/index.html"
	c.LayoutSections["LayoutScript"] = "agent_page/index.script.html"
}

type AgentController struct {
	auth.LoginRequiredController
}

func (c *AgentController) List() {
	//draw,start, length, q
	draw, _ := c.GetInt("draw")
	start, _ := c.GetInt64("start")
	length, _ := c.GetInt("length")
	q := strings.TrimSpace(c.GetString("q"))

	result, total, queryTotal := models.DefaultAgentManager.Query(q, start, length)

	c.Data["json"] = map[string]interface{}{
		"code":            200,
		"text":            "获取成功",
		"result":          result,
		"draw":            draw,
		"recordsTotal":    total,
		"recordsFiltered": queryTotal,
	}
	c.ServeJSON()
}

func (c *AgentController) Delete() {
	if c.Ctx.Input.IsPost() {
		pk, _ := c.GetInt("pk")
		models.DefaultAgentManager.DeleteById(pk)
	}
	c.Data["json"] = map[string]interface{}{
		"code":   200,
		"text":   "删除成功",
		"result": nil,
	}
	c.ServeJSON()
}

D:\Workspace\Go\src\gocmdb\server\controllers\log.go

package controllers

import (
	"strings"

	"gocmdb/server/controllers/auth"
	"gocmdb/server/models"
)

type ResourcePageController struct {
	LayoutController
}

func (c *ResourcePageController) Index() {
	c.Data["menu"] = "resource"
	c.Data["expand"] = "log_management"

	c.TplName = "resource_page/index.html"
	c.LayoutSections["LayoutScript"] = "resource_page/index.script.html"
}

type ResourceController struct {
	auth.LoginRequiredController
}

func (c *ResourceController) List() {
	//draw,start, length, q
	draw, _ := c.GetInt("draw")
	start, _ := c.GetInt64("start")
	length, _ := c.GetInt("length")
	q := strings.TrimSpace(c.GetString("q"))

	result, total, queryTotal := models.DefaultResourceManager.Query(q, start, length)

	c.Data["json"] = map[string]interface{}{
		"code":            200,
		"text":            "获取成功",
		"result":          result,
		"draw":            draw,
		"recordsTotal":    total,
		"recordsFiltered": queryTotal,
	}
	c.ServeJSON()
}

func (c *ResourceController) Trend() {
	uuid := strings.TrimSpace(c.GetString("uuid"))

	result := models.DefaultResourceManager.Trend(uuid)

	c.Data["json"] = map[string]interface{}{
		"code":   200,
		"text":   "获取成功",
		"result": result,
	}
	c.ServeJSON()
}

 D:\Workspace\Go\src\gocmdb\server\models\agent.go

package models

import (
	"github.com/astaxie/beego/orm"
	"time"
	"encoding/json"
)

type Agent struct {
	Id       int        `orm:"column(id);" json:"id"`
	UUID     string     `orm:"column(uuid);size(64);" json:"uuid"`
	Hostname string     `orm:"column(hostname);size(64);" json:"hostname"`
	IP       string     `orm:"column(ip);size(4096);" json:"ip"`
	OS       string     `orm:"column(os);size(64);" json:"os"`
	Arch     string     `orm:"column(arch);size(64);" json:"arch"`
	CPU      int        `orm:"column(cpu);" json:"cpu"`
	RAM      int64      `orm:"column(ram);" json:"ram"` // MB
	Disk     string     `orm:"column(disk);size(4096);" json:"disk"`
	BootTime *time.Time `orm:"column(boot_time);null;" json:"boottime"`
	Time     *time.Time `orm:"column(time);null;" json:"time"`

	HeartbeatTime *time.Time `orm:"column(heartbeat_time);null;" json:"heartbeat_time"`
	CreatedTime   *time.Time `orm:"column(created_time);auto_now_add;" json:"created_time"`
	DeletedTime   *time.Time `orm:"column(deleted_time);null;" json:"deleted_time"`

	IsOnline bool `orm:"-" json:"is_online"`
	IPList []string `orm:"-" json:"ip_list"`
	Disks map[string]float64 `orm:"-" json:"disks"`
}

func (a *Agent) Patch() {
	if time.Since(*a.HeartbeatTime) < 5 * time.Minute {
		a.IsOnline = true
	}

	if a.IP != "" {
		json.Unmarshal([]byte(a.IP), &a.IPList)
	}

	if a.Disk != "" {
		json.Unmarshal([]byte(a.Disk), &a.Disks)
	}

}

type AgentManager struct{}

func NewAgentManager() *AgentManager {
	return &AgentManager{}
}

func (m *AgentManager) CreateOrReplace(agent *Agent) (*Agent, bool, error) {
	now := time.Now()
	ormer := orm.NewOrm()
	orgAgent := &Agent{UUID: agent.UUID}
	if created, _, err := ormer.ReadOrCreate(orgAgent, "UUID"); err != nil {
		return nil, false, err
	} else {
		orgAgent.Hostname = agent.Hostname
		orgAgent.IP = agent.IP
		orgAgent.OS = agent.OS
		orgAgent.CPU = agent.CPU
		orgAgent.RAM = agent.RAM
		orgAgent.Disk = agent.Disk
		orgAgent.BootTime = agent.BootTime
		orgAgent.Time = agent.Time
		orgAgent.DeletedTime = nil

		orgAgent.HeartbeatTime = &now
		ormer.Update(orgAgent)
		return orgAgent, created, nil
	}
}

func (m *AgentManager) Heartbeat(uuid string) {
	orm.NewOrm().QueryTable(&Agent{}).Filter("UUID__exact", uuid).Update(orm.Params{"HeartbeatTime": time.Now(), "DeletedTime": nil})
}

func (m *AgentManager) Query(q string, start int64, length int) ([]*Agent, int64, int64) {
	ormer := orm.NewOrm()
	queryset := ormer.QueryTable(&Agent{})

	condition := orm.NewCondition()
	condition = condition.And("deleted_time__isnull", true)

	total, _ := queryset.SetCond(condition).Count()

	qtotal := total
	if q != "" {
		query := orm.NewCondition()
		query = query.Or("hostname__icontains", q)
		query = query.Or("ip__icontains", q)
		query = query.Or("os__icontains", q)
		query = query.Or("arch__icontains", q)
		condition = condition.AndCond(query)

		qtotal, _ = queryset.SetCond(condition).Count()
	}
	var result []*Agent

	queryset.SetCond(condition).RelatedSel().Limit(length).Offset(start).All(&result)
	for _, agent := range result {
		agent.Patch()
	}
	return result, total, qtotal
}

func (m *AgentManager) DeleteById(id int) error {
	orm.NewOrm().QueryTable(&Agent{}).Filter("Id__exact", id).Update(orm.Params{"DeletedTime": time.Now()})
	return nil
}

var DefaultAgentManager = NewAgentManager()


func init() {
	orm.RegisterModel(new(Agent))
}

D:\Workspace\Go\src\gocmdb\server\models\log.go

package models

import (
	"encoding/json"
	"time"

	"github.com/astaxie/beego/orm"
)

const (
	LOGResource = 0X0001
)

type Log struct {
	UUID string     `json:"uuid"`
	Type int        `json:"type"`
	Msg  string     `json:"msg"`
	Time *time.Time `json:"time"`
}

type LogManager struct{}

func NewLogManager() *LogManager {
	return &LogManager{}
}

func (m *LogManager) Create(log *Log) {
	switch log.Type {
	case LOGResource:
		resource := &Resource{}
		if err := json.Unmarshal([]byte(log.Msg), resource); err == nil {
			DefaultResourceManager.Create(log, resource)
		}
	}
}

type Resource struct {
	Id          int        `orm:"column(id);" json:"id"`
	UUID        string     `orm:"column(uuid);size(64);" json:"uuid"`
	Load        string     `orm:"column(load);size(1024);"  json:"load"`
	CPUPrecent  float64    `orm:"column(cpu_percent);" json:"cpu_percent"`
	RAMPrecent  float64    `orm:"column(ram_percent);" json:"ram_percent"`
	DiskPrecent string     `orm:"column(disk_percent);size(4096);" json:"disk_percent"`
	Time        *time.Time `orm:"column(time);" json:"time"`
	CreatedTime *time.Time `orm:"column(created_time);auto_now_add;" json:"created_time"`
	DeletedTime *time.Time `orm:"column(deleted_time);null;" json:"deleted_time"`
}

type ResourceManager struct{}

func NewResourceManager() *ResourceManager {
	return &ResourceManager{}
}

func (m *ResourceManager) Create(log *Log, resource *Resource) {
	resource.UUID = log.UUID
	resource.Time = log.Time
	orm.NewOrm().Insert(resource)
}


func (m *ResourceManager) Query(q string, start int64, length int) ([]*Resource, int64, int64) {
	ormer := orm.NewOrm()
	queryset := ormer.QueryTable(&Resource{})
	condition := orm.NewCondition()

	condition = condition.And("deleted_time__isnull", true)

	total, _ := queryset.SetCond(condition).Count()

	qtotal := total
	if q != "" {
		query := orm.NewCondition()

		condition = condition.AndCond(query)

		qtotal, _ = queryset.SetCond(condition).Count()
	}
	var result []*Resource
	queryset.SetCond(condition).OrderBy("-created_time").Limit(length).Offset(start).All(&result)
	return result, total, qtotal
}

func (m *ResourceManager) Trend(uuid string) []*Resource {
	endTime := time.Now()
	startTime := endTime.Add(-1 * time.Hour)

	condition := orm.NewCondition()
	condition = condition.And("deleted_time__isnull", true)
	condition = condition.And("uuid__exact", uuid)
	condition = condition.And("created_time__gte", startTime)

	var items []*Resource
	orm.NewOrm().QueryTable(new(Resource)).SetCond(condition).OrderBy("created_time").All(&items)

	var itemMap map[string]*Resource = make(map[string]*Resource)
	for _, item := range items {
		itemMap[item.CreatedTime.Format("2006-01-02 15:04")] = item
	}

	var result []*Resource = make([]*Resource, 0, len(items))

	for startTime.Before(endTime) {
		key := startTime.Format("2006-01-02 15:04")
		if item, ok := itemMap[key]; ok {
			result = append(result, item)
		} else {
			result = append(result, &Resource{CreatedTime: &startTime})
		}
		startTime = startTime.Add(time.Minute)
	}

	return result
}


var DefaultLogManager = NewLogManager()
var DefaultResourceManager = NewResourceManager()

func init() {
	orm.RegisterModel(new(Resource))
}

D:\Workspace\Go\src\gocmdb\server\routers\router.go

	v2 "gocmdb/server/controllers/api/v2"	

    v2Namespace := beego.NewNamespace("/v2",
		beego.NSRouter("api/heartbeat/:uuid/", &v2.APIController{}, "*:Heartbeat"),
		beego.NSRouter("api/register/:uuid/", &v2.APIController{}, "*:Register"),
		beego.NSRouter("api/log/:uuid/", &v2.APIController{}, "*:Log"),
	)
	beego.AddNamespace(v2Namespace)

D:\Workspace\Go\src\gocmdb\server\controllers\api\v2\api.go

package v1

import (
	"encoding/json"
	"gocmdb/server/controllers/api"
	"gocmdb/server/models"

	"github.com/beego/beego"
)

type APIController struct {
	api.BaseController
}

func (c *APIController) Prepare() {
	c.BaseController.Prepare()
	if beego.AppConfig.String("agent::token") != c.Ctx.Input.Header("Token") {
		c.Data["json"] = map[string]interface{}{
			"code":   400,
			"text":   "Token不正确",
			"result": nil,
		}
		c.ServeJSON()
		c.StopRun()
	}
}

func (c *APIController) Heartbeat() {
	models.DefaultAgentManager.Heartbeat(c.Ctx.Input.Param(":uuid"))
	c.Data["json"] = map[string]interface{}{
		"code":   200,
		"text":   "成功",
		"result": nil,
	}
	c.ServeJSON()
}

func (c *APIController) Register() {
	rt := map[string]interface{}{
		"code":   400,
		"text":   "成功",
		"result": nil,
	}
	agent := &models.Agent{}

	if err := json.Unmarshal(c.Ctx.Input.RequestBody, agent); err == nil {
		agent, created, err := models.DefaultAgentManager.CreateOrReplace(agent)
		if err == nil {
			rt = map[string]interface{}{
				"code": 200,
				"text": "成功",
				"result": map[string]interface{}{
					"created": created,
					"agent":   agent,
				},
			}
		} else {
			rt["text"] = err.Error()
		}
	} else {
		rt["text"] = err.Error()
	}
	c.Data["json"] = rt
	c.ServeJSON()
}

func (c *APIController) Log() {
	log := &models.Log{}
	if err := json.Unmarshal(c.Ctx.Input.RequestBody, log); err == nil {
		models.DefaultLogManager.Create(log)
	}
	c.Data["json"] = map[string]interface{}{
		"code":   200,
		"text":   "成功",
		"result": nil,
	}
	c.ServeJSON()
}

D:\Workspace\Go\src\gocmdb\server\views\agent_page\index.html

D:\Workspace\Go\src\gocmdb\server\views\agent_page\index.script.html

D:\Workspace\Go\src\gocmdb\server\views\resource_page\index.html

D:\Workspace\Go\src\gocmdb\server\views\resource_page\index.script.html

D:\Workspace\Go\src\gocmdb\server\alarm.go

package main

import (
	"flag"
	"fmt"
	"os"
	"time"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/orm"
	_ "github.com/go-sql-driver/mysql"

	_ "gocmdb/server/routers"
)

func main() {
	// 初始化命令行参数
	h := flag.Bool("h", false, "help")
	help := flag.Bool("help", false, "help")
	verbose := flag.Bool("v", false, "verbose")

	flag.Usage = func() {
		fmt.Println("usage: alarm -h")
		flag.PrintDefaults()
	}
	// 解析命令行参数
	flag.Parse()

	if *h || *help {
		flag.Usage()
		os.Exit(0)
	}

	// 设置日志到文件
	beego.SetLogger("file", `{
		"filename" : "logs/alarm.log",
		"level" : 7}`,
	)
	if !*verbose {
		//删除控制台日志
		beego.BeeLogger.DelLogger("console")
	} else {
		orm.Debug = true
	}

	// 初始化orm
	orm.RegisterDriver("mysql", orm.DRMySQL)
	orm.RegisterDataBase("default", "mysql", beego.AppConfig.String("dsn"))

	// 测试数据库连接是否正常
	if db, err := orm.GetDB(); err != nil || db.Ping() != nil {
		beego.Error("数据库连接错误")
		os.Exit(-1)
	}

	go func() {
		// 离线告警
		for now := range time.Tick(time.Minute) {
			fmt.Println("离线告警", now)
			offlineTime := 5
			endTime := now.Add(-1 * time.Duration(offlineTime) * time.Minute) // 5 根据配置
			var result []orm.Params
			orm.NewOrm().Raw("SELECT uuid from agent where deleted_time is null and heartbeat_time < ?", endTime).Values(&result)
			fmt.Println(result)
		}
	}()

	go func() {

		// CPU使用率
		for now := range time.Tick(time.Minute) {
			fmt.Println("CPU使用率告警", now)
			windowTime := 5
			cpuThreshold := 30
			cpuCounter := 3
			startTime := now.Add(-1 * time.Duration(windowTime) * time.Minute) // 5 根据配置
			var result []orm.Params
			orm.NewOrm().Raw("SELECT uuid, count(*) as cnt from resource where deleted_time is null and created_time >= ? and cpu_percent >= ? group by uuid having count(*) >= ?", startTime, cpuThreshold, cpuCounter).Values(&result)
			fmt.Println(result)
		}
	}()

	// 内存使用率
	for now := range time.Tick(time.Minute) {
		fmt.Println("内存使用率告警", now)
		windowTime := 5
		ramThreshold := 40
		ramCounter := 3
		startTime := now.Add(-1 * time.Duration(windowTime) * time.Minute) // 5 根据配置
		var result []orm.Params
		orm.NewOrm().Raw("SELECT uuid, count(*) as cnt from resource where deleted_time is null and created_time >= ? and ram_percent >= ? group by uuid having count(*) >= ?", startTime, ramThreshold, ramCounter).Values(&result)
		fmt.Println(result)
	}

}

desenvolvimento do lado do servidor

D:\Workspace\Go\src\gocmdb\server\conf\app.conf

incluir "smtp.conf"

incluir "sms.conf"

D:\Workspace\Go\src\gocmdb\server\conf\sms.conf

[sms]
endpoint=sms.tencentcloudapi.com
secretId=AKIDA8ta3JL6pKaicHsxxtkOmvpVv59y3u0r
secretKey=pSr30c0JOEo9F6h8UeaGkFXeXQ7bsuiH
appid=1400287583
sign=imuk网
templateOfflineId=510285
templateRamId=510288
templateCPUId=510287
phones=8613129091210;8613610847443

D:\Workspace\Go\src\gocmdb\server\conf\smtp.conf

[smtp]
host=smtp.qq.com
port=465
[email protected]
password=bsmujneemttzbbfe
[email protected]

Notificação de lembrete de alarme 

Exibir as últimas 10 informações de alarme não tratadas

Painel

Número de terminais online/número de terminais offline/número total de alarmes não processados

Distribuição de Alarmes Não Processados

Tendências de vários alarmes de status nos últimos 7 dias

Tarefa

Exemplo: Obter informações sobre o processo de execução atual do terminal

criação de tarefa

aquisição de tarefa

execução de tarefa

Upload do resultado da tarefa

Armazenamento de resultados de tarefas

Redis

Documentação chinesa: http://doc.redisfans.com/

Tipos de dados e operações comuns

corda

a lista

cerquilha

juntar

conjunto ordenado 

Vá operação Redis

Biblioteca de terceiros: github.com/gomodule/redigo/redis

1. Servidor de envio de correio stmp protocol
    socket server

    ip,
    autenticação de porta (nome de usuário/senha)

    Enviar formulário de e-mail
    para     conteúdo     de título
    CC     html/texto



Fornecer serviço web para enviar e-mail
Chamar serviço web

corresponder

url

Autenticação (Token)

para, assunto, conteúdo

enviar email

frequência limite


Contagem 1 hora na memória
uuid type count createTime 1 2

uuid+tipo

Julgando createTime < agora - 1h
    e recontando
, caso contrário
    contando + 1



Use redis/mem de armazenamento de memória de terceiros


uuid tipo ttl

1. Celular ==> modem SMS
2. API de serviço SMS
    Alibaba Cloud
    Tencent Cloud


a := 1

b := &a

c := []*int{}
c = anexar(b)
c = anexar(b)
c = anexar(b)

* b = 3


a
*c[0], *c[1], *c[2]


Problema:
    falha no envio de SMS, sem mecanismo de repetição

    Faça um serviço de SMS (web)
        para impedir que outras pessoas
        tentem novamente para garantir o sucesso do envio de SMS

código válido uma vez

Login com código de verificação de e-mail

Use o código de verificação do celular para fazer login
    1. O usuário preenche o número do celular (enviar o código de verificação) => enviar solicitação ao servidor (telefone)
    2. Verifique a validade do número do celular
        => o número do celular está errado
        => você pode enviar mensagens de texto
            para limitar a frequência (Enviado uma vez a cada 1 minuto)
                Sim enviado => Solicitação enviada para
                Não enviado
                    => Gerar um código de verificação aleatório
                        => Enviar para o usuário e armazenar o código de verificação de login correspondente ao telefone móvel Criar/atualizar código do telefone code_created_time (o tempo de expiração do código é de 5 minutos)
    3. Usuário Preencha o código de verificação para fazer login => envie os dados para o servidor (telefone, código)
        dados de pesquisa de telefone
            sem dados => número do celular ou código de verificação
            está errado => verifique se o código é ""
                está vazio => usado, número de celular ou erro de verificação de código
                não está vazio => verifique se o código está correto
                    ou não => número de celular ou código de verificação está incorreto
                    => determine se code_created_time expirou
                        Expirado => número de celular ou erro de código de verificação
                        não expirou => login bem-sucedido
                            para limpar o código

                            Apagar registro

Registro de Auditoria/Registro de Operação


gerenciamento de alarme

    hora, terminal, tipo, estado

    Processamento de alarme Abra a caixa de diálogo para modificar o status e o motivo


O número de cada alarme e cada status de processamento por dia nos últimos 7 dias

where deleted_time alarmed_time
dias do grupo, tipo de alarme, status

D:\Workspace\Go\src\gocmdb\server\alarm.go

func main() {
	// 初始化命令行参数
	h := flag.Bool("h", false, "help")
	help := flag.Bool("help", false, "help")
	verbose := flag.Bool("v", false, "verbose")

	flag.Usage = func() {
		fmt.Println("usage: alarm -h")
		flag.PrintDefaults()
	}
	// 解析命令行参数
	flag.Parse()

	if *h || *help {
		flag.Usage()
		os.Exit(0)
	}

	// 设置日志到文件
	beego.SetLogger("file", `{
		"filename" : "logs/alarm.log",
		"level" : 7}`,
	)
	if !*verbose {
		//删除控制台日志
		beego.BeeLogger.DelLogger("console")
	} else {
		orm.Debug = true
	}

	// 初始化orm
	orm.RegisterDriver("mysql", orm.DRMySQL)
	orm.RegisterDataBase("default", "mysql", beego.AppConfig.String("dsn"))

	// 测试数据库连接是否正常
	if db, err := orm.GetDB(); err != nil || db.Ping() != nil {
		beego.Error("数据库连接错误")
		os.Exit(-1)
	}
	host := beego.AppConfig.String("smtp::host")
	port, _ := beego.AppConfig.Int("smtp::port")
	user := beego.AppConfig.String("smtp::user")
	password := beego.AppConfig.String("smtp::password")
	to := beego.AppConfig.Strings("smtp::to")
	emailSender := utils.NewEmail(host, port, user, password)


	smsSender := utils.NewSms(
		beego.AppConfig.String("sms::endpoint"),
		beego.AppConfig.String("sms::secretId"),
		beego.AppConfig.String("sms::secretKey"),
		beego.AppConfig.String("sms::appid"),
		beego.AppConfig.String("sms::sign"),
	)

	templateOfflineId := beego.AppConfig.String("sms::templateOfflineId")
	templateCPUId := beego.AppConfig.String("sms::templateCPUId")
	templateRamId := beego.AppConfig.String("sms::templateRamId")
	phones := beego.AppConfig.Strings("sms::phones")


	go func() {
		// 离线告警
		offlineTime := 5
		noticeWindowTime := 60
		noticeCounter := int64(2)

		for now := range time.Tick(time.Minute) {
			beego.Debug("离线告警", now)
			endTime := now.Add(-1 * time.Duration(offlineTime) * time.Minute) // 5 根据配置
			noticeStartTime := now.Add(-1 * time.Duration(noticeWindowTime) * time.Minute)
			var result []orm.Params
			orm.NewOrm().Raw("SELECT uuid,heartbeat_time from agent where deleted_time is null and heartbeat_time < ?", endTime).Values(&result)
			for _, line := range result {
				uuid, _ := line["uuid"].(string)
				heartbeat_time, _ := line["heartbeat_time"].(string)

				content := fmt.Sprintf("终端[%s]最后一次发送心跳时间为%s, 已超过离线时间%d分钟", uuid, heartbeat_time, offlineTime)

				alarmCnt := models.DefaultAlarmManager.GetCountByUuidAndType(uuid, models.AlarmTypeOffline, noticeStartTime)
				if alarmCnt >= noticeCounter {
					beego.Info(fmt.Sprintf("通知次数(%d)超过限制(%d), %s", alarmCnt, noticeCounter, content))
					continue
				}

				emailErr := emailSender.Send(to, "[CMDB]终端离线告警", content, []string{})

				params := []string{uuid, heartbeat_time, strconv.Itoa(offlineTime)}
				smsErr := smsSender.Send(templateOfflineId, phones, params)

				beego.Info("终端离线告警: ", content, ", email通知:", emailErr, ", sms通知:", smsErr)

				models.DefaultAlarmManager.Create(uuid, models.AlarmTypeOffline, content, now)
			}
		}
	}()


	go func() {
		windowTime := 5
		cpuThreshold := 10
		cpuCounter := 3
		noticeWindowTime := 60
		noticeCounter := int64(2)

		// CPU使用率
		for now := range time.Tick(time.Minute) {
			beego.Debug("CPU使用率告警", now)

			startTime := now.Add(-1 * time.Duration(windowTime) * time.Minute) // 5 根据配置
			noticeStartTime := now.Add(-1 * time.Duration(noticeWindowTime) * time.Minute)
			var result []orm.Params
			orm.NewOrm().Raw("SELECT uuid, count(*) as cnt from resource where deleted_time is null and created_time >= ? and cpu_percent >= ? group by uuid having count(*) >= ?", startTime, cpuThreshold, cpuCounter).Values(&result)

			for _, line := range result {
				uuid, _ := line["uuid"].(string)
				cntString, _ := line["cnt"].(string)
				cnt, _ := strconv.Atoi(cntString)
				content := fmt.Sprintf("终端[%s]在最近%d分钟内CPU使用率大于%d%%的次数为%d, 已超过%d次", uuid, windowTime, cpuThreshold, cnt, cpuCounter)

				alarmCnt := models.DefaultAlarmManager.GetCountByUuidAndType(uuid, models.AlarmTypeCPU, noticeStartTime)
				if alarmCnt >= noticeCounter {
					beego.Info(fmt.Sprintf("通知次数(%d)超过限制(%d), %s", alarmCnt, noticeCounter, content))
					continue
				}

				emailErr := emailSender.Send(to, "[CMDB]终端CPU告警", content, []string{})

				params := []string{uuid, strconv.Itoa(windowTime), strconv.Itoa(cpuThreshold), strconv.Itoa(cnt), strconv.Itoa(cpuCounter)}
				smsErr := smsSender.Send(templateCPUId, phones, params)

				beego.Info("终端CPU告警: ", content, ", email通知:", emailErr, ", sms通知:", smsErr)
				models.DefaultAlarmManager.Create(uuid, models.AlarmTypeCPU, content, now)
			}
		}
	}()

	// 内存使用率
	windowTime := 5
	ramThreshold := 10
	ramCounter := 3
	noticeWindowTime := 60
	noticeCounter := int64(2)

	for now := range time.Tick(time.Minute) {
		beego.Debug("内存使用率告警", now)
		startTime := now.Add(-1 * time.Duration(windowTime) * time.Minute) // 5 根据配置
		noticeStartTime := now.Add(-1 * time.Duration(noticeWindowTime) * time.Minute) // 5 根据配置
		var result []orm.Params
		orm.NewOrm().Raw("SELECT uuid, count(*) as cnt from resource where deleted_time is null and created_time >= ? and ram_percent >= ? group by uuid having count(*) >= ?", startTime, ramThreshold, ramCounter).Values(&result)
		for _, line := range result {
			uuid, _ := line["uuid"].(string)
			cntString, _ := line["cnt"].(string)
			cnt, _ := strconv.Atoi(cntString)

			content := fmt.Sprintf("终端[%s]在最近%d分钟内内存使用率大于%d%%的次数为%d, 已超过%d次", uuid, windowTime, ramThreshold, cnt, ramCounter)

			alarmCnt := models.DefaultAlarmManager.GetCountByUuidAndType(uuid, models.AlarmTypeRam, noticeStartTime)
			if alarmCnt >= noticeCounter {
				beego.Info(fmt.Sprintf("通知次数(%d)超过限制(%d), %s", alarmCnt, noticeCounter, content))
				continue
			}
			emailErr := emailSender.Send(to, "[CMDB]终端内存告警", content, []string{})

			params := []string{uuid, strconv.Itoa(windowTime), strconv.Itoa(ramThreshold), strconv.Itoa(cnt), strconv.Itoa(ramCounter)}
			smsErr := smsSender.Send(templateRamId, phones, params)

			beego.Info("终端内存告警: ", content, ", email通知:", emailErr, ", sms通知:", smsErr)
			models.DefaultAlarmManager.Create(uuid, models.AlarmTypeRam, content, now)
		}
	}

}

D:\Workspace\Go\src\gocmdb\server\models\alarm.go

package models

import (
	"fmt"
	"strconv"
	"time"

	"github.com/astaxie/beego/orm"
)

type Alarm struct {
	Id          int        `orm:"column(id);" json:"id"`
	UUID        string     `orm:"column(uuid);size(64);" json:"uuid"`
	Type        int        `orm:"column(type)" json:"type"`
	Content     string     `orm:"column(content);type(text);" json:"content"`
	AlarmedTime *time.Time `orm:"column(alarmed_time);" json:"alarmed_time"`
	Status      int        `orm:"column(status);" json:"status"`
	Reason      string     `orm:"column(reason);type(text);" json:"reason"`

	CreatedTime *time.Time `orm:"column(created_time);auto_now_add;" json:"created_time"`
	DeletedTime *time.Time `orm:"column(deleted_time);null;" json:"deleted_time"`
}

type AlarmManager struct{}

func NewAlarmManager() *AlarmManager {
	return &AlarmManager{}
}

func (m *AlarmManager) Create(uuid string, typ int, content string, alarmedTime time.Time) error {
	alarm := &Alarm{
		UUID:        uuid,
		Type:        typ,
		Content:     content,
		AlarmedTime: &alarmedTime,
		Status:      AlarmStatusNew,
		Reason:      "",
	}

	_, err := orm.NewOrm().Insert(alarm)
	return err
}

func (m *AlarmManager) GetCountByUuidAndType(uuid string, typ int, startTime time.Time) int64 {
	cnt, _ := orm.NewOrm().QueryTable(new(Alarm)).Filter("uuid__exact", uuid).Filter("type__exact", typ).Filter("alarmed_time__gte", startTime).Filter("deleted_time__isnull", true).Count()
	return cnt
}

func (m *AlarmManager) GetNotification(limit int) (int64, []*Alarm) {
	ormer := orm.NewOrm()
	queryset := ormer.QueryTable(new(Alarm))

	cnt, _ := queryset.Filter("status__exact", AlarmStatusNew).Filter("deleted_time__isnull", true).Count()

	var result []*Alarm
	queryset.Filter("status__exact", AlarmStatusNew).Filter("deleted_time__isnull", true).OrderBy("-alarmed_time").Limit(limit).All(&result)

	return cnt, result
}

func (m *AlarmManager) GetCountForNoComplete() int64 {
	// total, _ := orm.NewOrm().QueryTable(new(Alarm)).Filter("deleted_time__isnull", true).Exclude("status__exact", AlarmStatusComplete).Count()
	// fmt.Println(total)
	total, _ := orm.NewOrm().QueryTable(new(Alarm)).Filter("deleted_time__isnull", true).Filter("status__in", AlarmStatusNew, AlarmStatusDoing).Count()
	return total
}

func (m *AlarmManager) GetStatForNotComplete() map[string]int64 {

	var lines []orm.Params
	orm.NewOrm().Raw("select type, count(*) as cnt from alarm where deleted_time is null and status in (?, ?) group by type", []int{AlarmStatusDoing, AlarmStatusNew}).Values(&lines)

	result := map[string]int64{}
	for _, line := range lines {
		typ := line["type"].(string)
		cntString := line["cnt"].(string)
		cnt, _ := strconv.ParseInt(cntString, 10, 64)
		result[typ] = cnt
	}
	return result
}

func (m *AlarmManager) GetLastestNStat(day int) ([]string, map[string][]int64) {

	endTime := time.Now()
	startTime := endTime.Add(-24*time.Duration(day-1)*time.Hour - 1)
	var lines []orm.Params

	orm.NewOrm().Raw("select date_format(alarmed_time, '%Y-%m-%d') as day, type, status, count(*) as cnt from alarm where deleted_time is null and alarmed_time >= ? group by day, type, status", startTime).Values(&lines)

	//key type+status = day : cnt
	tempStat := map[string]map[string]int64{}

	for _, line := range lines {
		day, _ := line["day"].(string)
		status, _ := line["status"].(string)
		typ, _ := line["type"].(string)
		cntString, _ := line["cnt"].(string)
		cnt, _ := strconv.ParseInt(cntString, 10, 64)
		key := fmt.Sprintf("%s-%s", typ, status)

		if _, ok := tempStat[key]; !ok {
			tempStat[key] = map[string]int64{}
		}
		tempStat[key][day] = cnt

	}

	// type+status : [1, 2, 3, 3, 4]
	// "int-int"
	days := []string{}
	result := map[string][]int64{}

	for startTime.Before(endTime) {
		day := startTime.Format("2006-01-02")
		days = append(days, day)

		for _, typ := range []int{AlarmTypeOffline, AlarmTypeCPU, AlarmTypeRam} {
			for _, status := range []int{AlarmStatusNew, AlarmStatusDoing, AlarmStatusComplete} {
				key := fmt.Sprintf("%d-%d", typ, status)
				value := int64(0)
				if stat, ok := tempStat[key]; ok {
					value = stat[day]
				} else {
				}
				result[key] = append(result[key], value)
			}
		}
		startTime = startTime.Add(24 * time.Hour)
	}

	return days, result
}

var DefaultAlarmManager = NewAlarmManager()

func init() {
	orm.RegisterModel(new(Alarm))
}

D:\Workspace\Go\src\gocmdb\server\models\enum.go

package models

const (
	StatusUnlock = 0
	StatusLock   = 1
)

const (
	AlarmTypeOffline = iota
	AlarmTypeCPU
	AlarmTypeRam
)

const (
	AlarmStatusNew = iota
	AlarmStatusDoing
	AlarmStatusComplete
)

D:\Workspace\Go\src\gocmdb\server\utils\email.go

package utils

import (
	"gopkg.in/gomail.v2"
)

type Email struct {
	host     string
	port     int
	user     string
	password string
}

func NewEmail(host string, port int, user string, password string) *Email {
	return &Email{
		host:     host,
		port:     port,
		user:     user,
		password: password,
	}
}

func (e *Email) Send(to []string, subject string, msg string, attaches []string) error {
	m := gomail.NewMessage()
	m.SetHeader("From", e.user)
	m.SetHeader("To", to...)
	m.SetHeader("Subject", subject)
	m.SetBody("text/html", msg)
	for _, attach := range attaches {
		m.Attach(attach)
	}

	d := gomail.NewDialer(e.host, e.port, e.user, e.password)

	return d.DialAndSend(m)
}

D:\Workspace\Go\src\gocmdb\server\utils\sms.go

package utils

import (
	"fmt"

	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
	sms "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/sms/v20190711"
)

type Sms struct {
	endpoint  string
	secretId  string
	secretKey string
	appid     string
	sign      string
}

func NewSms(endpoint, secretId, secretKey, appid, sign string) *Sms {
	return &Sms{
		endpoint:  endpoint,
		secretId:  secretId,
		secretKey: secretKey,
		appid:     appid,
		sign:      sign,
	}
}

func (s *Sms) Send(templateId string, phones []string, params []string) error {
	credential := common.NewCredential(s.secretId, s.secretKey)

	cpf := profile.NewClientProfile()
	cpf.HttpProfile.Endpoint = s.endpoint
	client, err := sms.NewClient(credential, "", cpf)
	if err != nil {
		return err
	}

	phoneSet := []*string{}
	for _, phone := range phones {
		temp := phone
		phoneSet = append(phoneSet, &temp)
	}
	paramSet := []*string{}
	for _, param := range params {
		temp := string([]rune(param)[:12])
		paramSet = append(paramSet, &temp)
	}

	request := sms.NewSendSmsRequest()
	request.PhoneNumberSet = phoneSet
	request.TemplateID = &templateId
	request.SmsSdkAppid = &s.appid
	request.Sign = &s.sign
	request.TemplateParamSet = paramSet

	response, err := client.SendSms(request)
	if err != nil {
		return err
	}
	fmt.Printf("%s", response.ToJsonString())
	return err
}

D:\Workspace\Go\src\gocmdb\server\controllers\dashboard.go

package controllers

import (
	"gocmdb/server/controllers/auth"
	"gocmdb/server/models"
)

type DashboardPageController struct {
	LayoutController
}

func (c *DashboardPageController) Index() {
	c.Data["menu"] = "dashboard"
	c.TplName = "dashboard_page/index.html"
	c.LayoutSections["LayoutScript"] = "dashboard_page/index.script.html"
}

type DashboardController struct {
	auth.LoginRequiredController
}

func (c *DashboardController) Stat() {
	onlineCnt, offlineCnt := models.DefaultAgentManager.GetStat()
	alarm_trend_days, alarm_trend_data := models.DefaultAlarmManager.GetLastestNStat(7)

	c.Data["json"] = map[string]interface{}{
		"code": 200,
		"text": "获取成功",
		"result": map[string]interface{}{
			"agent_offline_count": offlineCnt,
			"agent_online_count":  onlineCnt,
			"alarm_count":         models.DefaultAlarmManager.GetCountForNoComplete(),
			"alarm_dist":          models.DefaultAlarmManager.GetStatForNotComplete(),
			"alarm_trend": map[string]interface{}{
				"days": alarm_trend_days,
				"data": alarm_trend_data,
			},
		},
	}
	c.ServeJSON()
}

D:\Workspace\Go\src\gocmdb\server\models\agent.go

func (m *AgentManager) GetStat() (int64, int64) {
	now := time.Now()
	onlineTime := now.Add(-5 * time.Minute)

	queryset := orm.NewOrm().QueryTable(new(Agent)).Filter("deleted_time__isnull", true)

	onlineCnt, _ := queryset.Filter("heartbeat_time__gte", onlineTime).Count()
	offlineCnt, _ := queryset.Filter("heartbeat_time__lt", onlineTime).Count()
	return onlineCnt, offlineCnt
}

D:\Workspace\Go\src\gocmdb\server\routers\router.go 

	// 认证
	beego.Router("/", &controllers.IndexController{}, "get:Index")

	// 认证
	beego.AutoRouter(&auth.AuthController{})

	// Dashboard
	beego.AutoRouter(&controllers.DashboardPageController{})

	// Dashboard
	beego.AutoRouter(&controllers.DashboardController{})

	// 用户页面
	beego.AutoRouter(&controllers.UserPageController{})

D:\Workspace\Go\src\gocmdb\server\controllers\default.go

package controllers

import (
	"github.com/astaxie/beego"
	"net/http"
)

type IndexController struct {
	beego.Controller
}

func (c *IndexController) Index() {
	c.Redirect(beego.URLFor(beego.AppConfig.String("home")), http.StatusFound)
}

D:\Workspace\Go\src\gocmdb\server\views\dashboard_page\index.html

D:\Workspace\Go\src\gocmdb\server\views\dashboard_page\index.script.html

Tarefa

    Terminal
    Plugin
    Parâmetros do Plugin
    Status
    Hora inicial
    Hora final

Resultado da Tarefa
    Razão     da Falha do
    Resultado da Tarefa


AGENTE COMO OBTER TAREFAS

    Solicite tarefas do servidor regularmente e pergunte ao servidor a cada 10s, quais tarefas (recém-criadas) devo executar
    (ENS) vou verificar, 2 tarefas, marque a tarefa como devolvida ao AGENTE (executando), retorne ao AGENTE

tarefa executa
    cada tarefa => plug-in
    gerenciamento de plug-in => responsável pela execução

    ENS => (Pipeline) => Comunicação do gerente


Redis operações comuns


teste de ping de autenticação de autenticação

Tipo de processamento
, del, keys, ttl, expire, existe para KEY


    string para cada tipo de dado
        set
        get
        mset
        mget
        setnx
        incr
        incrby
        decr
        decrby
    list
        lpush
        rpush
        lpop
        rpop
        lrange
        llen
        brpop
        blpop
    hash
        hset
        hget
        hmset
        hmget
        hsetnx
        hdel
        hexists
        hlen
        hgetall
    set
        sadd
        scard
        smembers
        sismember
        srem
    sorted
        setzadd
        zcard
        zrange
        zrevrange
        zrangebyscore
        zrevrangebyscore
        zrem

    Publicar
        subscrever
        publicar
operação e manutenção


heartbeat => db
heartbeat => zset
uuid score time unix

zrangebyscore uuid
agora - 5 agora


Supervisor

1. yum install/pip install
2. systemd

arquitetura de implantação

 cache

diagrama de arquitetura

processo de comunicação 

 configuração

Tarefa 

Processo de entrega e execução de tarefas

Redis

NGINXGenericName

Compilação Cruzada e Compilação Condicional

outro

Websocket

elasticsearch

Vá operação Redis

Biblioteca de terceiros: github.com/gomodule/redigo/redis

manipulação de string

operação de CHAVE

operação LIST

operação HASH

operação DEFINIR

operação ZSET

Publicar operação de assinatura

aplicativo REDIS

Cache: como SESSION

Comunicação do processo: fila (produtor, consumidor)

escolha o mestre

Classificação: Conjunto Ordenado

Difusão de mensagens: Publicar Inscrever-se 

BEEGO usa REDIS para armazenar SESSION

configuração

pacote de importação

Use o cache Redis no BEEGO 

Use o cache Redis no BEEGO

Implantação da web Beego

vá construir web.go

Copie o conf das exibições estáticas do web.exe para o ambiente de implantação

iniciar programa web 

NGINXGenericName

Nginx é um servidor web HTTP e proxy reverso de alto desempenho

algoritmo de balanceamento de carga

sondagem

atribuir peso

ip_hash

justo

url_hash

Proxy NGINX Aplicativo da Web Beego

Beego web pode ser modificado apenas para acesso local (httpaddr)

Configure nginx.conf e inicie

daemon off;
#user  nobody;
worker_processes  4;


#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

pid        logs/nginx.pid;


events {
    worker_connections  10240;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;


    upstream app_server_cmdb {
        server 127.0.0.1:8888;
    }

    server {
        listen       80;
        server_name  cmdb;

        charset utf-8;

        error_log logs/cmdb.error.log debug;
        access_log  logs/cmdb.access.log  main;

        root D:\\codes\\gocmdb-deploy;

        gzip on;
        gzip_min_length 1024;
        gzip_comp_level 2;
        gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
        gzip_vary on;

        client_body_temp_path temp/client_body_temp_cmdb 1 2 ;
        proxy_temp_path  temp/proxy_temp_cmdb 1 2;
        fastcgi_temp_path temp/fastcgi_temp_cmdb 1 2;
        uwsgi_temp_path temp/uwsgi_temp_cmdb 1 2;
        scgi_temp_path temp/scgi_temp_cmdb 1 2;

        location /static/ {
            alias D:\\codes\\gocmdb-deploy\\static\\;
            expires 1d;
            access_log off;
        }

        location / {
            try_files $uri @proxy_to_app;
        }

        location @proxy_to_app {
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header Host $http_host;
            proxy_redirect off;
            proxy_pass http://app_server_cmdb;
        }
    }

    upstream app_server_test {
        server 127.0.0.1:9990 weight=2;
        server 127.0.0.1:9991;
    }

    server {
        listen       8080;
        server_name  test;

        charset utf-8;

        error_log logs/test.error.log debug;
        access_log  logs/test.access.log  main;

        root D:\\codes\\test;

        gzip on;
        gzip_min_length 1024;
        gzip_comp_level 2;
        gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
        gzip_vary on;

        client_body_temp_path temp/client_body_temp_cmdb 1 2 ;
        proxy_temp_path  temp/proxy_temp_cmdb 1 2;
        fastcgi_temp_path temp/fastcgi_temp_cmdb 1 2;
        uwsgi_temp_path temp/uwsgi_temp_cmdb 1 2;
        scgi_temp_path temp/scgi_temp_cmdb 1 2;

        location /static/ {
            alias D:\\codes\\test\\static\\;
            expires 1d;
            access_log off;
        }

        location / {
            try_files $uri @proxy_to_app;
        }

        location @proxy_to_app {
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header Host $http_host;
            proxy_redirect off;
            proxy_pass http://app_server_test;
        }
    }
}

Uso de balanceamento de carga NGINX

Inicie o programa de cache para as portas 9990 e 9991, respectivamente

Compilação Cruzada e Compilação Condicional

A compilação cruzada só está disponível quando não estiver usando CGO

$GOOS.go

$GOOS_$GOARCH.go

Teste de performance

API para banco de dados

API para Redis 

desenvolvimento de agente

D:\Workspace\Go\src\gocmdb\agent\config\config.go

type Config struct {
	Task chan interface{}
	TaskResult chan interface{}
}
	return &Config{
		Task: make(chan interface{}, 128),
		TaskResult: make(chan interface{}, 128),
	}, nil

D:\Workspace\Go\src\gocmdb\agent\ens\ens.go

func (s *ENS) Start() {
	logrus.Info("ENS 开始运行")

	headers := req.Header{"Token": s.conf.Token}
	request := req.New()
	
	go func() {
		endpoint := fmt.Sprintf("%s/result/%s/", s.conf.Endpoint, s.conf.UUID)
		for evt := range s.conf.TaskResult {
			response, err := request.Post(endpoint, req.BodyJSON(evt), headers)
			if err == nil {
				result := map[string]interface{}{}
				response.ToJSON(&result)
				logrus.WithFields(logrus.Fields{
					"taskResult": evt,
					"result":     result,
				}).Debug("任务结果上传成功")
			} else {
				logrus.WithFields(logrus.Fields{
					"taskResult": evt,
					"error":      err,
				}).Error("任务结果上传失败")
			}
		}
	}()

	go func() {
		endpoint := fmt.Sprintf("%s/task/%s/", s.conf.Endpoint, s.conf.UUID)
		for now := range time.Tick(10 * time.Second) {
			response, err := request.Get(endpoint, req.QueryParam{"time": now.Unix()}, headers)
			if err == nil {
				result := map[string]interface{}{}
				response.ToJSON(&result)
				logrus.WithFields(logrus.Fields{
					"result": result,
				}).Debug("获取任务成功")
				tasks, _ := result["result"].([]interface{})
				for _, taskMap := range tasks {
					if task, err := entity.NewTask(taskMap); err == nil {
						s.conf.Task <- task
					}
				}
			} else {
				logrus.WithFields(logrus.Fields{
					"error": err,
				}).Error("获取任务失败")
			}
		}
	}()
}

D:\Workspace\Go\src\gocmdb\agent\entity\task.go

package entity

import (
	"encoding/json"
)

type Task struct {
	Id int `json:"id"`

	Plugin  string `json:"plugin"`
	Params  string `json:"params"`
	Timeout int    `json:"timeout"`
}

func NewTask(taskMap interface{}) (Task, error) {
	var task Task
	if taskBytes, err := json.Marshal(taskMap); err != nil {
		return task, err
	} else if err = json.Unmarshal(taskBytes, &task); err != nil {
		return task, err
	}
	return task, nil
}

type Result struct {
	TaskId int    `json:"task_id"`
	Status int    `json:"status"`
	Result string `json:"result"`
	Err    string `json:"err"`
}

func NewResult(task Task, result interface{}, err error) Result {
	bytes, _ := json.Marshal(result)
	errInfo := ""
	status := 0
	if err != nil {
		status = 1
		errInfo = err.Error()
	}
	return Result{
		TaskId: task.Id,
		Status: status,
		Result: string(bytes),
		Err:    errInfo,
	}
}

D:\Workspace\Go\src\gocmdb\agent\plugins\init\task.go

package init

import (
	"gocmdb/agent/plugins"
	"gocmdb/agent/plugins/task"
)

func init() {
	plugins.DefaultManager.RegisterTask(&task.Process{})
}

D:\Workspace\Go\src\gocmdb\agent\plugins\task\process.go

package task

import (
	"gocmdb/agent/config"

	"github.com/shirou/gopsutil/process"
)

type Process struct {
	conf *config.Config
}

func (p *Process) Name() string {
	return "process"
}

func (p *Process) Init(conf *config.Config) {
	p.conf = conf
}

func (p *Process) Call(params string) (interface{}, error) {
	pids, err := process.Pids()
	if err != nil {
		return nil, err
	}
	rs := make([]map[string]interface{}, len(pids))
	for index, pid := range pids {
		ps, err := process.NewProcess(pid)
		if err != nil {
			continue
		}
		name, _ := ps.Name()
		exe, _ := ps.Exe()
		cmd, _ := ps.Cmdline()
		createdTime, _ := ps.CreateTime()
		ppid, _ := ps.Ppid()
		cwd, _ := ps.Cwd()
		numFDs, _ := ps.NumFDs()

		numThreads, _ := ps.NumThreads()
		memoryInfo, _ := ps.MemoryInfo()
		connections, _ := ps.Connections()
		rs[index] = map[string]interface{}{
			"pid":         pid,
			"ppid":        ppid,
			"name":        name,
			"exe":         exe,
			"cmd":         cmd,
			"createdTime": createdTime,
			"cwd":         cwd,
			"numFDs":      numFDs,
			"numThreads":  numThreads,
			"memoryInfo":  memoryInfo,
			"connections": connections,
		}
	}
	return rs, nil
}

 D:\Workspace\Go\src\gocmdb\agent\plugins\base.go

type TaskPlugin interface {
	Name() string
	Init(*config.Config)
	Call(params string) (interface{}, error)
}

 D:\Workspace\Go\src\gocmdb\agent\plugins\manager.go

package plugins

import (
	"errors"
	"gocmdb/agent/config"
	"time"

	"gocmdb/agent/entity"

	"github.com/sirupsen/logrus"
)

type Manager struct {
	Conf   *config.Config
	Cycles map[string]CyclePlugin
	Tasks  map[string]TaskPlugin
}

func NewManager() *Manager {
	return &Manager{
		Cycles: make(map[string]CyclePlugin),
		Tasks:  make(map[string]TaskPlugin),
	}
}

func (m *Manager) RegisterCycle(p CyclePlugin) {
	m.Cycles[p.Name()] = p
	logrus.WithFields(logrus.Fields{
		"Name": p.Name(),
		"Type": "周期型",
	}).Info("插件注册")
}

func (m *Manager) RegisterTask(p TaskPlugin) {
	m.Tasks[p.Name()] = p
	logrus.WithFields(logrus.Fields{
		"Name": p.Name(),
		"Type": "任务型",
	}).Info("插件注册")
}

func (m *Manager) Init(conf *config.Config) {
	for name, plugin := range m.Cycles {
		plugin.Init(conf)
		logrus.WithFields(logrus.Fields{
			"Name": name,
		}).Info("初始化插件")

	}
	for name, plugin := range m.Tasks {
		plugin.Init(conf)
		logrus.WithFields(logrus.Fields{
			"Name": name,
		}).Info("初始化插件")
	}
}

func (m *Manager) Start() {
	go m.StartCycle()
	go m.StartTask()
}

func (m *Manager) StartCycle() {
	for now := range time.Tick(time.Second) {
		for name, plugin := range m.Cycles {
			if now.After(plugin.NextTime()) {
				if evt, err := plugin.Call(); err == nil {
					logrus.WithFields(logrus.Fields{
						"Name":   name,
						"Result": evt,
					}).Debug("插件执行")
					plugin.Pipline() <- evt
				} else {
					logrus.WithFields(logrus.Fields{
						"Name":  name,
						"error": err,
					}).Debug("插件执行失败")
				}
			}
		}
	}
}

func (m *Manager) StartTask() {
	for task := range m.Conf.Task {
		taskObj, _ := task.(entity.Task)
		if plugin, ok := m.Tasks[taskObj.Plugin]; !ok {
			logrus.WithFields(logrus.Fields{
				"task": taskObj,
			}).Error("插件执行失败, 插件不存在")
			m.Conf.TaskResult <- entity.NewResult(taskObj, nil, errors.New("插件不存在"))
		} else {
			go func(pluginName string, plugin TaskPlugin) {
				result, err := plugin.Call(taskObj.Params)
				logrus.WithFields(logrus.Fields{
					"Name":   pluginName,
					"task":   taskObj,
					"Result": result,
					"Err":    err,
				}).Error("插件执行完成")

				m.Conf.TaskResult <- entity.NewResult(taskObj, result, err)
			}(plugin.Name(), plugin)

		}
	}
}

var DefaultManager = NewManager()

desenvolvimento do lado do servidor

D:\Workspace\Go\src\gocmdb\server\web.go

package main

import (
	"flag"
	"fmt"
	"os"

	"gocmdb/server/models"
	_ "gocmdb/server/routers"
	"gocmdb/server/utils"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/orm"
	_ "github.com/go-sql-driver/mysql"

	_ "gocmdb/server/cloud/plugins"

	_ "github.com/astaxie/beego/session/redis"
)

func main() {
	// 初始化命令行参数
	h := flag.Bool("h", false, "help")
	help := flag.Bool("help", false, "help")
	init := flag.Bool("init", false, "init server")
	syncdb := flag.Bool("syncdb", false, "sync db")
	force := flag.Bool("force", false, "force sync db(drop table)")
	verbose := flag.Bool("v", false, "verbose")

	flag.Usage = func() {
		fmt.Println("usage: web -h")
		flag.PrintDefaults()
	}
	// 解析命令行参数
	flag.Parse()

	if *h || *help {
		flag.Usage()
		os.Exit(0)
	}

	// 设置日志到文件
	beego.SetLogger("file", `{
		"filename" : "logs/web.log",
		"level" : 7}`,
	)
	if !*verbose {
		//删除控制台日志
		beego.BeeLogger.DelLogger("console")
	} else {
		orm.Debug = true
	}

	// 初始化orm
	orm.RegisterDriver("mysql", orm.DRMySQL)
	orm.RegisterDataBase("default", "mysql", beego.AppConfig.String("dsn"))

	// 测试数据库连接是否正常
	if db, err := orm.GetDB(); err != nil || db.Ping() != nil {
		beego.Error("数据库连接错误")
		os.Exit(-1)
	}
	// 根据参数选择执行流程
	switch {
	case *init:
		orm.RunSyncdb("default", *force, *verbose)
		ormer := orm.NewOrm()
		admin := &models.User{Name: "admin", IsSuperman: true}
		if err := ormer.Read(admin, "Name"); err == orm.ErrNoRows {
			password := utils.RandString(6)
			admin.SetPassword(password)
			if _, err := ormer.Insert(admin); err == nil {
				beego.Informational("初始化admin成功, 默认密码:", password)
			} else {
				beego.Error("初始化用户失败, 错误:", err)
			}
		} else {
			beego.Informational("admin用户已存在, 跳过")
		}
	case *syncdb:
		orm.RunSyncdb("default", *force, *verbose)
		beego.Informational("同步数据库")
	default:
		beego.Run()
	}
}

D:\Workspace\Go\src\gocmdb\server\alarm.go

package main

import (
	"flag"
	"fmt"
	"os"
	"os/signal"
	"strconv"
	"syscall"
	"time"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/orm"
	_ "github.com/go-sql-driver/mysql"

	"gocmdb/server/models"
	_ "gocmdb/server/routers"
	"gocmdb/server/utils"
)

func main() {
	// 初始化命令行参数
	h := flag.Bool("h", false, "help")
	help := flag.Bool("help", false, "help")
	verbose := flag.Bool("v", false, "verbose")

	flag.Usage = func() {
		fmt.Println("usage: alarm -h")
		flag.PrintDefaults()
	}
	// 解析命令行参数
	flag.Parse()

	if *h || *help {
		flag.Usage()
		os.Exit(0)
	}

	// 设置日志到文件
	beego.SetLogger("file", `{
		"filename" : "logs/alarm.log",
		"level" : 7}`,
	)
	if !*verbose {
		//删除控制台日志
		beego.BeeLogger.DelLogger("console")
	} else {
		orm.Debug = true
	}

	// 初始化orm
	orm.RegisterDriver("mysql", orm.DRMySQL)
	orm.RegisterDataBase("default", "mysql", beego.AppConfig.String("dsn"))

	// 测试数据库连接是否正常
	if db, err := orm.GetDB(); err != nil || db.Ping() != nil {
		beego.Error("数据库连接错误")
		os.Exit(-1)
	}
	host := beego.AppConfig.String("smtp::host")
	port, _ := beego.AppConfig.Int("smtp::port")
	user := beego.AppConfig.String("smtp::user")
	password := beego.AppConfig.String("smtp::password")
	to := beego.AppConfig.Strings("smtp::to")
	emailSender := utils.NewEmail(host, port, user, password)

	smsSender := utils.NewSms(
		beego.AppConfig.String("sms::endpoint"),
		beego.AppConfig.String("sms::secretId"),
		beego.AppConfig.String("sms::secretKey"),
		beego.AppConfig.String("sms::appid"),
		beego.AppConfig.String("sms::sign"),
	)

	templateOfflineId := beego.AppConfig.String("sms::templateOfflineId")
	templateCPUId := beego.AppConfig.String("sms::templateCPUId")
	templateRamId := beego.AppConfig.String("sms::templateRamId")
	phones := beego.AppConfig.Strings("sms::phones")

	go func() {
		// 离线告警
		offlineTime := 5
		noticeWindowTime := 60
		noticeCounter := int64(2)

		for now := range time.Tick(time.Minute) {
			beego.Debug("离线告警", now)
			endTime := now.Add(-1 * time.Duration(offlineTime) * time.Minute) // 5 根据配置
			noticeStartTime := now.Add(-1 * time.Duration(noticeWindowTime) * time.Minute)
			var result []orm.Params
			orm.NewOrm().Raw("SELECT uuid,heartbeat_time from agent where deleted_time is null and heartbeat_time < ?", endTime).Values(&result)
			for _, line := range result {
				uuid, _ := line["uuid"].(string)
				heartbeat_time, _ := line["heartbeat_time"].(string)

				content := fmt.Sprintf("终端[%s]最后一次发送心跳时间为%s, 已超过离线时间%d分钟", uuid, heartbeat_time, offlineTime)

				alarmCnt := models.DefaultAlarmManager.GetCountByUuidAndType(uuid, models.AlarmTypeOffline, noticeStartTime)
				if alarmCnt >= noticeCounter {
					beego.Info(fmt.Sprintf("通知次数(%d)超过限制(%d), %s", alarmCnt, noticeCounter, content))
					continue
				}

				emailErr := emailSender.Send(to, "[CMDB]终端离线告警", content, []string{})

				params := []string{uuid, heartbeat_time, strconv.Itoa(offlineTime)}
				smsErr := smsSender.Send(templateOfflineId, phones, params)

				beego.Info("终端离线告警: ", content, ", email通知:", emailErr, ", sms通知:", smsErr)

				models.DefaultAlarmManager.Create(uuid, models.AlarmTypeOffline, content, now)
			}
		}
	}()

	go func() {
		windowTime := 5
		cpuThreshold := 10
		cpuCounter := 3
		noticeWindowTime := 60
		noticeCounter := int64(2)

		// CPU使用率
		for now := range time.Tick(time.Minute) {
			beego.Debug("CPU使用率告警", now)

			startTime := now.Add(-1 * time.Duration(windowTime) * time.Minute) // 5 根据配置
			noticeStartTime := now.Add(-1 * time.Duration(noticeWindowTime) * time.Minute)
			var result []orm.Params
			orm.NewOrm().Raw("SELECT uuid, count(*) as cnt from resource where deleted_time is null and created_time >= ? and cpu_percent >= ? group by uuid having count(*) >= ?", startTime, cpuThreshold, cpuCounter).Values(&result)

			for _, line := range result {
				uuid, _ := line["uuid"].(string)
				cntString, _ := line["cnt"].(string)
				cnt, _ := strconv.Atoi(cntString)
				content := fmt.Sprintf("终端[%s]在最近%d分钟内CPU使用率大于%d%%的次数为%d, 已超过%d次", uuid, windowTime, cpuThreshold, cnt, cpuCounter)

				alarmCnt := models.DefaultAlarmManager.GetCountByUuidAndType(uuid, models.AlarmTypeCPU, noticeStartTime)
				if alarmCnt >= noticeCounter {
					beego.Info(fmt.Sprintf("通知次数(%d)超过限制(%d), %s", alarmCnt, noticeCounter, content))
					continue
				}

				emailErr := emailSender.Send(to, "[CMDB]终端CPU告警", content, []string{})

				params := []string{uuid, strconv.Itoa(windowTime), strconv.Itoa(cpuThreshold), strconv.Itoa(cnt), strconv.Itoa(cpuCounter)}
				smsErr := smsSender.Send(templateCPUId, phones, params)

				beego.Info("终端CPU告警: ", content, ", email通知:", emailErr, ", sms通知:", smsErr)
				models.DefaultAlarmManager.Create(uuid, models.AlarmTypeCPU, content, now)
			}
		}
	}()

	// 内存使用率
	go func() {
		windowTime := 5
		ramThreshold := 10
		ramCounter := 3
		noticeWindowTime := 60
		noticeCounter := int64(2)

		for now := range time.Tick(time.Minute) {
			beego.Debug("内存使用率告警", now)
			startTime := now.Add(-1 * time.Duration(windowTime) * time.Minute)             // 5 根据配置
			noticeStartTime := now.Add(-1 * time.Duration(noticeWindowTime) * time.Minute) // 5 根据配置
			var result []orm.Params
			orm.NewOrm().Raw("SELECT uuid, count(*) as cnt from resource where deleted_time is null and created_time >= ? and ram_percent >= ? group by uuid having count(*) >= ?", startTime, ramThreshold, ramCounter).Values(&result)
			for _, line := range result {
				uuid, _ := line["uuid"].(string)
				cntString, _ := line["cnt"].(string)
				cnt, _ := strconv.Atoi(cntString)

				content := fmt.Sprintf("终端[%s]在最近%d分钟内内存使用率大于%d%%的次数为%d, 已超过%d次", uuid, windowTime, ramThreshold, cnt, ramCounter)

				alarmCnt := models.DefaultAlarmManager.GetCountByUuidAndType(uuid, models.AlarmTypeRam, noticeStartTime)
				if alarmCnt >= noticeCounter {
					beego.Info(fmt.Sprintf("通知次数(%d)超过限制(%d), %s", alarmCnt, noticeCounter, content))
					continue
				}
				emailErr := emailSender.Send(to, "[CMDB]终端内存告警", content, []string{})

				params := []string{uuid, strconv.Itoa(windowTime), strconv.Itoa(ramThreshold), strconv.Itoa(cnt), strconv.Itoa(ramCounter)}
				smsErr := smsSender.Send(templateRamId, phones, params)

				beego.Info("终端内存告警: ", content, ", email通知:", emailErr, ", sms通知:", smsErr)
				models.DefaultAlarmManager.Create(uuid, models.AlarmTypeRam, content, now)
			}
		}
	}()

	ch := make(chan os.Signal, 1)
	signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
	<-ch
}

D:\Workspace\Go\src\gocmdb\server\controllers\alarm.go

package controllers

import (
	"strings"

	"gocmdb/server/controllers/auth"
	"gocmdb/server/models"
)

type AlarmPageController struct {
	LayoutController
}

func (c *AlarmPageController) Index() {
	c.Data["menu"] = "alarm_management"
	c.Data["expand"] = "monitoring"

	c.TplName = "alarm_page/index.html"
	c.LayoutSections["LayoutScript"] = "alarm_page/index.script.html"
}

type AlarmController struct {
	auth.LoginRequiredController
}

func (c *AlarmController) List() {
	//draw,start, length, q
	draw, _ := c.GetInt("draw")
	start, _ := c.GetInt64("start")
	length, _ := c.GetInt("length")
	q := strings.TrimSpace(c.GetString("q"))

	result, total, queryTotal := models.DefaultAlarmManager.Query(q, start, length)

	c.Data["json"] = map[string]interface{}{
		"code":            200,
		"text":            "获取成功",
		"result":          result,
		"draw":            draw,
		"recordsTotal":    total,
		"recordsFiltered": queryTotal,
	}
	c.ServeJSON()
}

D:\Workspace\Go\src\gocmdb\server\controllers\api\v2\api.go

func (c *APIController) Task() {
	c.Data["json"] = map[string]interface{}{
		"code":   200,
		"text":   "成功",
		"result": models.DefaultTaskManager.GetByUuid(c.Ctx.Input.Param(":uuid")),
	}
	c.ServeJSON()
}

func (c *APIController) TaskResult() {
	rt := map[string]interface{}{
		"code":   400,
		"text":   "",
		"result": nil,
	}

	result := &models.Result{}

	if err := json.Unmarshal(c.Ctx.Input.RequestBody, result); err == nil {
		if err := models.DefaultTaskManager.Result(c.Ctx.Input.Param(":uuid"), result); err != nil {
			rt["text"] = err.Error()
		} else {
			rt = map[string]interface{}{
				"code":   200,
				"text":   "成功",
				"result": nil,
			}
		}
	} else {
		rt["text"] = err.Error()
	}
	c.Data["json"] = rt
	c.ServeJSON()
}

D:\Workspace\Go\src\gocmdb\server\controllers\layout.go

func (c *LayoutController) Prepare() {	
    alarmCount, alarms := models.DefaultAlarmManager.GetNotification(10)

	c.Data["alarm"] = map[string]interface{}{
		"count": alarmCount,
		"list":  alarms,
	}
}

 D:\Workspace\Go\src\gocmdb\server\models\task.go

package models

import (
	"fmt"
	"time"

	"github.com/astaxie/beego/orm"
)

type Task struct {
	Id   int    `orm:"column(id);" json:"id"`
	UUID string `orm:"column(uuid);size(64);" json:"uuid"`

	Plugin  string `orm:"column(plugin);size(32);" json:"plugin"`
	Params  string `orm:"column(params);type(text);" json:"params"`
	Timeout int    `orm:"column(timeout);" json:"timeout"`

	Status        int        `orm:"column(status);" json:"status"`
	CreatedTime   *time.Time `orm:"column(created_time);auto_now_add;" json:"created_time"`
	CompletedTime *time.Time `orm:"column(completed_time);null;" json:"completed_time"`
	DeletedTime   *time.Time `orm:"column(deleted_time);null;" json:"deleted_time"`

	Result *Result `orm:"column(result);reverse(one);" json:"result"`
}

type TaskManager struct{}

func NewTaskManager() *TaskManager {
	return &TaskManager{}
}

func (m *TaskManager) Create(uuid string, plugin string, params string, timeout int) error {
	ormer := orm.NewOrm()
	task := &Task{
		UUID:    uuid,
		Plugin:  plugin,
		Params:  params,
		Timeout: timeout,
		Status:  TaskStatusNew,
	}
	if _, err := ormer.Insert(task); err != nil {
		return err
	}
	return nil
}

func (m *TaskManager) GetByUuid(uuid string) []*Task {
	ormer := orm.NewOrm()
	queryset := ormer.QueryTable(new(Task))

	condition := orm.NewCondition()
	condition = condition.And("deleted_time__isnull", true)
	condition = condition.And("uuid__exact", uuid)
	condition = condition.And("status__in", TaskStatusNew)

	var result []*Task
	queryset.SetCond(condition).All(&result)
	queryset.SetCond(condition).Update(orm.Params{"status": TaskStatusExecing})
	return result
}

func (m *TaskManager) GetByIdAndUuid(id int, uuid string) *Task {
	ormer := orm.NewOrm()
	task := &Task{Id: id, UUID: uuid}
	if err := ormer.Read(task, "id", "uuid"); err == nil {
		return task
	}
	return nil
}

func (m *TaskManager) Result(uuid string, result *Result) error {
	ormer := orm.NewOrm()
	task := m.GetByIdAndUuid(result.TaskId, uuid)
	if task == nil {
		return fmt.Errorf("针对终端%s任务%s不存在", uuid, result.TaskId)
	}
	now := time.Now()

	task.Status = TaskStatusSuccess
	if result.Status != 0 {
		task.Status = TaskStatusFailure
	}
	task.CompletedTime = &now
	if _, err := ormer.Update(task); err != nil {
		return err
	}

	result.Task = task
	if _, err := ormer.Insert(result); err != nil {
		return err
	}
	return nil
}

var DefaultTaskManager = NewTaskManager()

type Result struct {
	Id     int   `orm:"column(id);" json:"id"`
	Task   *Task `orm:"column(task);rel(one);" json:"task"`
	TaskId int   `orm:"-" json:"task_id"`

	Status int    `orm:"-" json:"status"`
	Result string `orm:"column(result);type(text);" json:"result"`
	Err    string `orm:"column(err);type(text);" json:"err"`

	CreatedTime *time.Time `orm:"column(created_time);auto_now_add;" json:"created_time"`
	DeletedTime *time.Time `orm:"column(deleted_time);null;" json:"deleted_time"`
}

func init() {
	orm.RegisterModel(new(Task), new(Result))
}

D:\Workspace\Go\src\gocmdb\server\models\enum.go

const (

    TaskStatusNew = iota

    TarefaStatusCancelar

    TarefaStatusAgendamento

    TarefaStatusExecutando

    TarefaStatusSuccess

    TaskStatusFailure

)

D:\Workspace\Go\src\gocmdb\server\models\alarm.go

func (m *AlarmManager) Query(q string, start int64, length int) ([]*Alarm, int64, int64) {
	ormer := orm.NewOrm()
	queryset := ormer.QueryTable(&Alarm{})
	condition := orm.NewCondition()

	condition = condition.And("deleted_time__isnull", true)

	total, _ := queryset.SetCond(condition).Count()

	qtotal := total
	if q != "" {
		query := orm.NewCondition()

		condition = condition.AndCond(query)

		qtotal, _ = queryset.SetCond(condition).Count()
	}
	var result []*Alarm
	queryset.SetCond(condition).OrderBy("-created_time").Limit(length).Offset(start).All(&result)
	return result, total, qtotal
}

D:\Workspace\Go\src\gocmdb\server\controllers\api\v1\api.go

func (c *APIController) Task() {
	c.Data["json"] = map[string]interface{}{
		"code":   200,
		"text":   "成功",
		"result": models.DefaultTaskManager.GetByUuid(c.Ctx.Input.Param(":uuid")),
	}
	c.ServeJSON()
}

func (c *APIController) TaskResult() {
	rt := map[string]interface{}{
		"code":   400,
		"text":   "",
		"result": nil,
	}

	result := &models.Result{}

	if err := json.Unmarshal(c.Ctx.Input.RequestBody, result); err == nil {
		if err := models.DefaultTaskManager.Result(c.Ctx.Input.Param(":uuid"), result); err != nil {
			rt["text"] = err.Error()
		} else {
			rt = map[string]interface{}{
				"code":   200,
				"text":   "成功",
				"result": nil,
			}
		}
	} else {
		rt["text"] = err.Error()
	}
	c.Data["json"] = rt
	c.ServeJSON()
}

D:\Workspace\Go\src\gocmdb\server\routers\router.go

	// 告警页面
	beego.AutoRouter(&controllers.AlarmPageController{})

	// 告警
	beego.AutoRouter(&controllers.AlarmController{})

	v1Namespace := beego.NewNamespace("/v1",
		beego.NSRouter("api/heartbeat/:uuid/", &v1.APIController{}, "*:Heartbeat"),
		beego.NSRouter("api/register/:uuid/", &v1.APIController{}, "*:Register"),
		beego.NSRouter("api/log/:uuid/", &v1.APIController{}, "*:Log"),
		beego.NSRouter("api/task/:uuid/", &v1.APIController{}, "*:Task"),
		beego.NSRouter("api/result/:uuid/", &v1.APIController{}, "*:TaskResult"),
	)

	beego.AddNamespace(v1Namespace)

	v2Namespace := beego.NewNamespace("/v2",
		beego.NSRouter("api/heartbeat/:uuid/", &v2.APIController{}, "*:Heartbeat"),
		beego.NSRouter("api/register/:uuid/", &v2.APIController{}, "*:Register"),
		beego.NSRouter("api/log/:uuid/", &v2.APIController{}, "*:Log"),
		beego.NSRouter("api/task/:uuid/", &v2.APIController{}, "*:Task"),
		beego.NSRouter("api/result/:uuid/", &v2.APIController{}, "*:TaskResult"),
	)
	beego.AddNamespace(v2Namespace)

D:\Workspace\Go\src\gocmdb\server\views\alarm_page\index.html

D:\Workspace\Go\src\gocmdb\server\views\alarm_page\index.script.html 

Exibir

Painel

terminal 

Plataforma de nuvem de gerenciamento de alarme adicionada

Nuvem Tencent

 

 

 

Acho que você gosta

Origin blog.csdn.net/niwoxiangyu/article/details/131360324
Recomendado
Clasificación