diff --git a/src/app.go b/src/app.go index 731e823d..743f79fd 100644 --- a/src/app.go +++ b/src/app.go @@ -16,6 +16,7 @@ import ( networkdata "be.ems/src/modules/network_data" networkelement "be.ems/src/modules/network_element" "be.ems/src/modules/system" + "be.ems/src/modules/tool" "be.ems/src/modules/trace" "be.ems/src/modules/ws" @@ -143,6 +144,8 @@ func initModulesRoute(app *gin.Engine) { trace.Setup(app) // 图表模块 chart.Setup(app) + // 工具模块 + tool.Setup(app) // ws 模块 ws.Setup(app) // 调度任务模块--暂无接口 diff --git a/src/assets/dependency/iperf3/deb/iperf3_3.1.3-1_amd64.deb b/src/assets/dependency/iperf3/deb/iperf3_3.1.3-1_amd64.deb new file mode 100644 index 00000000..0c687e31 Binary files /dev/null and b/src/assets/dependency/iperf3/deb/iperf3_3.1.3-1_amd64.deb differ diff --git a/src/assets/dependency/iperf3/deb/libiperf0_3.1.3-1_amd64.deb b/src/assets/dependency/iperf3/deb/libiperf0_3.1.3-1_amd64.deb new file mode 100644 index 00000000..c81f6622 Binary files /dev/null and b/src/assets/dependency/iperf3/deb/libiperf0_3.1.3-1_amd64.deb differ diff --git a/src/assets/dependency/iperf3/deb/libsctp1_1.0.19+dfsg-1build1_amd64.deb b/src/assets/dependency/iperf3/deb/libsctp1_1.0.19+dfsg-1build1_amd64.deb new file mode 100644 index 00000000..03c3947e Binary files /dev/null and b/src/assets/dependency/iperf3/deb/libsctp1_1.0.19+dfsg-1build1_amd64.deb differ diff --git a/src/assets/dependency/iperf3/rpm/iperf3-3.6-6.ky10.aarch64.rpm b/src/assets/dependency/iperf3/rpm/iperf3-3.6-6.ky10.aarch64.rpm new file mode 100644 index 00000000..b5d5a4a5 Binary files /dev/null and b/src/assets/dependency/iperf3/rpm/iperf3-3.6-6.ky10.aarch64.rpm differ diff --git a/src/assets/dependency/iperf3/rpm/iperf3-help-3.6-6.ky10.noarch.rpm b/src/assets/dependency/iperf3/rpm/iperf3-help-3.6-6.ky10.noarch.rpm new file mode 100644 index 00000000..78a425f8 Binary files /dev/null and b/src/assets/dependency/iperf3/rpm/iperf3-help-3.6-6.ky10.noarch.rpm differ diff --git a/src/modules/tool/controller/iperf.go b/src/modules/tool/controller/iperf.go new file mode 100644 index 00000000..57fe7cc8 --- /dev/null +++ b/src/modules/tool/controller/iperf.go @@ -0,0 +1,71 @@ +package controller + +// https://iperf.fr/iperf-download.php +// https://launchpad.net/ubuntu/jammy/amd64/libsctp1/1.0.19+dfsg-1build1 + +import ( + "strings" + + "be.ems/src/framework/i18n" + "be.ems/src/framework/utils/ctx" + "be.ems/src/framework/vo/result" + "be.ems/src/modules/tool/service" + "github.com/gin-gonic/gin" +) + +// 实例化控制层 IPerfController 结构体 +var NewIPerf = &IPerfController{ + iperfService: service.NewIPerf, +} + +// iperf 网络性能测试工具 +// +// PATH /tool/iperf +type IPerfController struct { + // IPerf3 网络性能测试工具服务 + iperfService *service.IPerf +} + +// iperf 版本信息 +// +// GET /v +func (s *IPerfController) Version(c *gin.Context) { + language := ctx.AcceptLanguage(c) + var query struct { + NeType string `form:"neType" binding:"required"` // 网元类型 + NeID string `form:"neId" binding:"required"` // 网元ID + } + if err := c.ShouldBindQuery(&query); err != nil { + c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) + return + } + + output, err := s.iperfService.Version(query.NeType, query.NeID) + if err != nil { + c.JSON(200, result.ErrMsg(i18n.TKey(language, err.Error()))) + return + } + data := strings.Split(output, "\n") + c.JSON(200, result.OkData(data)) +} + +// iperf 软件安装 +// +// GET /i +func (s *IPerfController) Install(c *gin.Context) { + language := ctx.AcceptLanguage(c) + var body struct { + NeType string `json:"neType" binding:"required"` // 网元类型 + NeID string `json:"neId" binding:"required"` // 网元ID + } + if err := c.ShouldBindBodyWithJSON(&body); err != nil { + c.JSON(400, result.CodeMsg(400, i18n.TKey(language, "app.common.err400"))) + return + } + + if err := s.iperfService.Install(body.NeType, body.NeID); err != nil { + c.JSON(200, result.ErrMsg(i18n.TKey(language, err.Error()))) + return + } + c.JSON(200, result.OkData(nil)) +} diff --git a/src/modules/tool/model/iperf.go b/src/modules/tool/model/iperf.go new file mode 100644 index 00000000..54950d43 --- /dev/null +++ b/src/modules/tool/model/iperf.go @@ -0,0 +1,8 @@ +package model + +// IPerf G6关系图数据对象 chart_graph +type IPerf struct { + RowID int64 `json:"rowId,omitempty" gorm:"column:row_id;primaryKey;autoIncrement"` // 记录ID + RowType string `json:"rowType,omitempty" gorm:"column:row_type"` // 记录类型(node/edge/combo) + RowGroup string `json:"rowGroup,omitempty" gorm:"column:row_group"` // 记录组名 +} diff --git a/src/modules/tool/service/iperf.go b/src/modules/tool/service/iperf.go new file mode 100644 index 00000000..947d419a --- /dev/null +++ b/src/modules/tool/service/iperf.go @@ -0,0 +1,110 @@ +package service + +import ( + "fmt" + "io" + "strings" + + "be.ems/src/framework/config" + neService "be.ems/src/modules/network_element/service" +) + +// 实例化服务层 IPerf 结构体 +var NewIPerf = &IPerf{ + neInfoService: neService.NewNeInfoImpl, +} + +// IPerf 网络性能测试工具 服务层处理 +type IPerf struct { + // 网元信息服务 + neInfoService neService.INeInfo +} + +// Version 查询版本信息 +func (s *IPerf) Version(meType, neId string) (string, error) { + // 网元主机的SSH客户端 + sshClient, err := s.neInfoService.NeRunSSHClient(meType, neId) + if err != nil { + return "", err + } + defer sshClient.Close() + + // 检查是否安装iperf3 + output, err := sshClient.RunCMD("iperf3 --version") + if err != nil { + return "", fmt.Errorf("iperf3 not installed") + } + return strings.TrimSpace(output), err +} + +// Install 安装iperf3 +func (s *IPerf) Install(meType, neId string) error { + // 网元主机的SSH客户端 + sshClient, err := s.neInfoService.NeRunSSHClient(meType, neId) + if err != nil { + return err + } + defer sshClient.Close() + // 网元主机的SSH客户端进行文件传输 + sftpClient, err := sshClient.NewClientSFTP() + if err != nil { + return err + } + defer sftpClient.Close() + + nePath := "/tmp" + depPkg := "sudo dpkg -i" + depDir := "assets/dependency/iperf3/deb" + + // 检查平台类型 + if _, err := sshClient.RunCMD("sudo dpkg --version"); err == nil { + depPkg = "sudo dpkg -i" + depDir = "assets/dependency/iperf3/deb" + // sudo apt remove iperf3 libiperf0 libsctp1 libsctp-dev lksctp-tools + } else if _, err := sshClient.RunCMD("sudo yum --version"); err == nil { + depPkg = "sudo rpm -Uvh --force" + depDir = "assets/dependency/iperf3/rpm" + // yum remove iperf3 iperf3-help.noarch + } + + // 从 embed.FS 中读取默认配置文件内容 + assetsDir := config.GetAssetsDirFS() + fsDirEntrys, err := assetsDir.ReadDir(depDir) + if err != nil { + return err + } + neFilePaths := []string{} + for _, d := range fsDirEntrys { + // 打开本地文件 + localFile, err := assetsDir.Open(fmt.Sprintf("%s/%s", depDir, d.Name())) + if err != nil { + return fmt.Errorf("iperf3 file local error") + } + defer localFile.Close() + // 创建远程文件 + remotePath := fmt.Sprintf("%s/%s", nePath, d.Name()) + remoteFile, err := sftpClient.Client.Create(remotePath) + if err != nil { + return fmt.Errorf("iperf3 file remote error") + } + defer remoteFile.Close() + // 使用 io.Copy 将嵌入的文件内容复制到目标文件 + if _, err := io.Copy(remoteFile, localFile); err != nil { + return fmt.Errorf("iperf3 file copy error") + } + neFilePaths = append(neFilePaths, remotePath) + } + + // 删除软件包 + defer func() { + pkgRemove := fmt.Sprintf("sudo rm %s", strings.Join(neFilePaths, " ")) + sshClient.RunCMD(pkgRemove) + }() + + // 安装软件包 + pkgInstall := fmt.Sprintf("%s %s", depPkg, strings.Join(neFilePaths, " ")) + if _, err := sshClient.RunCMD(pkgInstall); err != nil { + return fmt.Errorf("iperf3 install error") + } + return err +} diff --git a/src/modules/tool/tool.go b/src/modules/tool/tool.go new file mode 100644 index 00000000..6d91e856 --- /dev/null +++ b/src/modules/tool/tool.go @@ -0,0 +1,29 @@ +package tool + +import ( + "be.ems/src/framework/logger" + "be.ems/src/framework/middleware" + "be.ems/src/framework/middleware/collectlogs" + "be.ems/src/modules/tool/controller" + + "github.com/gin-gonic/gin" +) + +// 模块路由注册 +func Setup(router *gin.Engine) { + logger.Infof("开始加载 ====> tool 模块路由") + + // iperf 网络性能测试工具 + iperfGroup := router.Group("/tool/iperf") + { + iperfGroup.GET("/v", + middleware.PreAuthorize(nil), + controller.NewIPerf.Version, + ) + iperfGroup.POST("/i", + middleware.PreAuthorize(nil), + collectlogs.OperateLog(collectlogs.OptionNew("log.operate.title.iperf", collectlogs.BUSINESS_TYPE_OTHER)), + controller.NewIPerf.Install, + ) + } +}