go开发多云资产管理平台

news2024/10/6 18:21:20

go开发多云资产管理平台cmdb

代码仓库github.com/yunixiangfeng/gocmdb

云主机管理

主机资源监控

开发流程

 Welcome to 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"

bee run

http://127.0.0.1:8080/

项目结构

修改配置文件

D:\gocmdb\server\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\server\conf\db.conf

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

编写启动文件

D:\gocmdb\server\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()
	}
}

登陆

用户/Token模型定义

登陆页面加载

提交用户名/密码登陆验证

验证结果处理

基础控制器

BaseController 基础控制器,用于改写默认模板(默认: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()
}

LoginRequiredController 认证控制器,用于API调用认证(session/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

bee run

http://localhost:8888/auth/login

输入用户名和密码,跳转测试页

基础控制器

LayoutController Layout控制器,用于设置layout,layoutSections,memu及expand

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"] = ""
}

请求

模板

 用户管理:登陆验证、管理页加载、用户数据加载、增/删/改/锁定/解锁、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已删除

用户管理:

用户管理(增/删(逻辑删除)/改/查/锁定/解锁)

Token管理(生成/重新生成)

登陆认证(web session/api token) 

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

用户管理页面控制器 

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"
}

添加路由

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

用户管理控制器

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

用户管理(增/删(逻辑删除)/改/查/锁定/解锁) 

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

用户管理
    一个 dialog
        创建/编辑 流程做完
        锁定/解锁/删除 流程做完

    当前登陆用户不能锁定/删除/解锁 自己
    当前用户只能查看和生成自己的Token

    修改密码
    alert => sweetalert

1. ajax请求未登录返回json
2. 创建


多云管理
管理多个云平台
    阿里云
    腾讯云
    aws
    azure
    华为云
    京东云
    青云
    Openstack
    ...

获取虚拟机
启动
停止
重启

server/cloud

plugins/aliyun
        tenant
        aws

manager
instance => vm


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


配置信息
    认证
    地址
    Region配置

Platform
    Id
    Name
    Type
    Addr
    AccessKey
    SecrectKey
    Region
    Remark
    CreatedTime
    DeletedTime
    SyncTime
    CreateUser rel,reverse
    Status


VirtualMachine
    Platform 1: n

    UUID
    Name
    CPU
    Memeory
    OS
    PrivateAddrs
    PublicAddrs
    Status string
    VmCreatedTime
    VmExpiredTime

    CreatedTime
    DeletedTime
    UpdatedTime

云平台管理页面

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

home=UserPageController.Index

云平台管理页面控制器

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{})

云平台管理控制器

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()
}

云平台数据显示list

云平台创建create

云平台修改modify

云平台操作禁用disable、启用enable、删除delete

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{})
}

虚拟机页面

虚拟机操作

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"
)

腾讯云操作

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

阿里云操作

 

 

 

虚拟机同步

用户操作虚拟机

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)
		}
	}

}

10点

腾讯云
a 10点
b 10点


=>
a
b

删了b

11 点

a 11点


11 点之前 更新未删除

多个云平台

A.
for 云平台
    B.
    for 虚拟机
        C.

    D. 统一将当前平台中的状态更新(platform过滤)

E. 统一将数据库中的更新

b B KB MB GB TB PB
 /8
b
function FileSizeb(size) {
    if(size < 8) {
        return size.toFixed(2) + "b";
    }
    size /= 8;
    var index = 0;
    var units = ["B", "KB", "MB", "GB", "TB", "PB"];

    while(size >= 1024) {
        size /= 1024;
        index += 1;
    }
    return size.toFixed(2) + units[index];
}

 

 

 

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/679984.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

津津乐道设计模式 - 模版模式详解(以女友化妆流程带你彻底明白)

&#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Micro麦可乐的博客 &#x1f425;《Docker实操教程》专栏以最新的Centos版本为基础进行Docker实操教程&#xff0c;入门到实战 &#x1f33a;《RabbitMQ》…

JDBC-->java如何连接数据库(详细版小白必备)

个人名片&#xff1a; &#x1f405;作者简介&#xff1a;一名大二在校生&#xff0c;热爱生活&#xff0c;爱好敲码&#xff01; \ &#x1f485;个人主页 &#x1f947;&#xff1a;holy-wangle ➡系列内容&#xff1a; &#x1f5bc;️ tkinter前端窗口界面创建与优化 &…

工业相机——显微镜头/放大镜头

校准尺&#xff0c;最小测量刻度为0.1mm 上图为手机拍的看不清&#xff0c;放了一个网上找的图&#xff0c;校准的详细参数见下图 例如&#xff1a;物距为116mm的显微镜头&#xff0c;这种镜头没有景深&#xff0c;只能测镜头前端到物体116mm的物体 &#xff0c;几乎没有景深&a…

Vscode配置C/C++环境出现报错,导致不能运行代码,报错如下:

Vscode配置C/C环境出现报错&#xff0c;导致不能运行代码&#xff0c;报错如下&#xff1a; 问题描述—gcc : 无法将“gcc”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写&#xff0c;如果包括路径&#xff0c;请确保路径正确&#xff0c;然后再试一次…

Hexo博客搭建 —— Next主题配置

Hexo博客搭建 —— Next主题配置 文章目录 Hexo博客搭建 —— Next主题配置配置文件区分站点配置文件主题配置文件 配置文件描述简称引入next主题-sitenext主题页面模式设置-next设置首页不显示全文-site设置博客文章持久化连接-site下载插件URL Setting 参考文档 配置文件区分…

【Docker】一文了解DockerFile

文章目录 Dockerfile 概念DockerFile的指令1、FROM 指定基础镜像2、RUN 执行命令3、COPY 复制文件4、ADD 更高级的复制文件5、ENV 设置环境变量6、EXPOSE7、VOLUME 定义匿名卷8、CMD容器启动命令9、ENTRYPOINT入口点10、USER 指定当前用户11、WORKDIR 指定工作目录12、LABEL为镜…

网络安全、Web安全、渗透测试之笔经面经总结含答案

以下为网络安全各个方向涉及的面试题&#xff0c;星数越多代表问题出现的几率越大&#xff0c;祝各位都能找到满意的工作。 注&#xff1a;本套面试题&#xff0c;已整理成pdf文档&#xff0c;但内容还在持续更新中&#xff0c;因为无论如何都不可能覆盖所有的面试问题&#xf…

4、数据库操作语句:聚合函数

目录 1、定义 2、常用的聚合函数 1&#xff09;Avg/sum&#xff1a;只适用于数值类型的字段&#xff08;或变量&#xff09;。 2&#xff09;Max/min:适用于数值类型、字符串类型、日期时间类型的字段&#xff08;或变量&#xff09; 3&#xff09;Count&#xff1a; ①作…

一起来学孟德尔随机化(Mendelian Randomization)

孟德尔随机化最近实在是太火了&#xff0c;想不关注都不行&#xff0c;最近也花了点时间研究了一下&#xff0c;和大家分享一下&#xff0c;共同学习。 什么是孟德尔随机化&#xff1f; 在19世纪&#xff0c;孟德尔用豌豆花作为实验材料&#xff0c;通过对豌豆花颜色、形状等特…

ESP-BOX的GUI移植

因为squareline studio软件中适配了ESP-BOX&#xff0c;所以作者本想直接使用该软件创建的工程&#xff0c;但是会出现花屏的现象&#xff0c;也不知道是不是没有做好esp-box-lite的适配。 因此只能先用squareline studio设计好GUI&#xff0c;然后再导出其代码&#xff0c;在其…

jmeter-13-使用JSR223断言(推荐)

文章目录 前言一、JSR 223 进行断言二、总结 前言 之前都在使用 BeanShell 前后置、断言等&#xff0c;但是查看官方文档时发现推荐使用 JSR223 其实 BeanShell 是 JSR223 里面的一种&#xff0c;下面我们继续了解下。 官网介绍&#xff1a;Apache JMeter - User’s Manual: C…

【数据分享】1929-2022年全球站点的逐月平均露点数据(Shp\Excel\12000个站点)

气象数据是在各项研究中都经常使用的数据&#xff0c;气象指标包括气温、风速、降水、能见度等指标&#xff0c;说到气象数据&#xff0c;最详细的气象数据是具体到气象监测站点的数据&#xff01; 对于具体到监测站点的气象数据&#xff0c;之前我们分享过1929-2022年全球气象…

ffmpeg+nginx-rtmp转发视频流

本篇博客最早发布于实验室公共博客&#xff0c;但已无人维护&#xff0c;现迁移至个人博客 nginx与nginx-rtmp-module安装 画了好几天图&#xff0c;实在有些乏力&#xff0c;找点有意思的事情做做 觉得视频流传输挺有意思&#xff0c;B站找了些视频&#xff0c;但感觉有些大…

【论文简述】IS-MVSNet:Importance Sampling-based MVSNet(ECCV 2022)

一、论文简述 1. 第一作者&#xff1a;Likang Wang 2. 发表年份&#xff1a;2022 3. 发表期刊&#xff1a;ECCV 4. 关键词&#xff1a;MVS、3D重建、重要性采样、无监督误差分布估计 5. 探索动机&#xff1a;以粗到细的方式预测深度图&#xff0c;部分缓解了对于分辨率的限…

Apache James邮件服务器搭建(linux)

1、配置jdk&#xff0c;此处不再赘述&#xff0c;随便搜一下然后照着操作即可 注意&#xff1a;后续james的版本是3.5.0&#xff0c;需要与jdk8配套 2、下载apache james &#xff0c;apacheJames 下载之后可以直接运行&#xff0c;无需安装 注意&#xff1a;此处选择3.5.0版…

人工智能数据集处理——数据清理1

目录 一、概述 二、缺失值 1、检测缺失值 使用isna() 方法检测na_df中是否存在缺失值 使用natna() 方法 2、缺失值的处理 (1) 删除缺失值 使用删除dropna() 方法删除na_df 对象中缺失值所在的一行数据 删除全为缺失值的行 删除有缺失值的行 (2) 填充缺失值 使用fill…

win10 下搭建hadoop(成功截图)

背景&#xff1a; 搭建hadoop是大数据的启蒙第一步&#xff0c;对初学者有很好的信心建设&#xff0c;建议安装版本和作者一样。话不多说&#xff0c;开始正文。 1. 下载hadoop 本文版本为3.0.0 可以Apache.org官网下载&#xff0c;也可以本人csdn资源处下载https://download.…

数据结构之静态链表

定义 用两个数组实现链表&#xff0c;一个数组存储数据&#xff0c;另一个数组记录当前数据的后继的下标。 示例 数据&#xff1a;data[] {-1, 34, 28, 53, 16, 25, -1, -1, -1, -1} 后继&#xff1a;next[] { 1, 2, 3, 4, 5, -1, -1, -1, -1, -1} 说明 -1: 表示无效值 …

Linux2.基础指令(下)

1.uname -r :输出Linux内核版本信息。 2.linux2.6.*内核默认支持的文件系统有ext3,ext2,ext4,xfs&#xff0c;不支持ufs。 3.linux查看CPU占用的命令:top。 4.题目 5.题目 6.题目 7.重定向 echo "字符串1" :在屏幕上打印字符串1。 echo "字符串1" &g…

C语言督学营(初级阶段)

文章目录 初级阶段1.编程环境搭建、调试C的历史故事写代码快速提升的方法快捷键编程环境 Clion、断点调试(单步调试) 2.数据类型、标准输入输出数据分类printf整型进制转换内存视图ASCII码表计算器 scanf的原理 3.运算符与表达式C语言的13种运算符运算符优先级 4.选择、循环1.选…