preface

之前我使用python写了cmdb采集的脚本,打包成exe的二进制文件后放在windows上执行,也达到了预期的效果。

但是最近部门要上open-falcon监控体系,每个服务器都要安装一个open-falcon的agent的,那么问题来了:我们可以使用open-falcon来采集服务器的基本硬件信息嘛,不然的话我们还需要维护python版本的采集脚本,这个相当于一个windows机器上跑两个agent,一个是cmdb采集硬件信息的,一个open-falcon监控的。如果不想维护两个agent,那么就需要开发一个go版的cmdb采集脚本,然后嵌入到open-falcon的agent中,反正open-falcon是开源的,你可以随意根据自己的需求改动他的源码。

好,既然可以开发一个go版的cmdb采集脚本,那么我先就说下吧,点到为止哈,虽然这个代码是我写的,但是代码是属于公司的东东,所以不宜暴露过多。

用模块什么采集基本硬件信息

python下采集windows基本信息可以使用wmi模块我记得是,那么go下我们可以使用gopsutil这个模块,模块地址是戳我

官网里面详细介绍了各个子模块的用法,我这里就不啰嗦了。

如何执行windows下的交互式命令与字符集编码问题

  1. 采集机器型号
  2. 采集机器制造商
  • 以上两个需要使用systeminfo命令采集。
  1. 机器序列号
  • 以上需要使用 wmic bios get serialnumber

需要注意的,我这里遇到个棘手的问题就是windows server 2003的系统在采集CPU的信息的时候报错,这就操蛋了,那么我就通过执行windows系统命令来采集CPU信息了,命令是 wmic cpu list brief

先说下怎么在windows下执行交互式系统命令:

1.先导入 os/exec 模块,然后我们这么执行 a := exec.Command("wmic")

2.此时我们拿到了变量a,那么就需要使用变量a的 StdinPipe 的方法传入命令到系统执行,代码如下:

  1. inpipe, err := a.StdinPipe()
  2. if err != nil {
  3. log.Fatalf("executing command wmic happend a error:", err)
  4. return false
  5. }
  6. go func() { // 启用一个go routine,设这个函数为匿名函数
  7. defer inpipe.Close()
  8. io.WriteString(inpipe, cpucmd) // 把需要的执行的命令放入进去,cpucmd就是需要执行的命令
  9. }()

3.执行完命令后,我们需要就把结果给取出来了,如下所示:

  1. out, err := a.CombinedOutput()
  2. if err != nil {
  3. log.Println("collecting CPU info by wmic happend a error", err)
  4. return false
  5. }

4.取出来结果剩下就根据自己的逻辑来处理了。

full example of how to exec windows cmd

  1. cpucmd := "cpu list brief\n"
  2. a := exec.Command("wmic")
  3. inpipe, err := a.StdinPipe()
  4. if err != nil {
  5. log.Fatalf("executing command wmic happend a error:", err)
  6. return false
  7. }
  8. go func() {
  9. defer inpipe.Close()
  10. io.WriteString(inpipe, cpucmd)
  11. }()
  12. out, err := a.CombinedOutput()
  13. if err != nil {
  14. log.Println("collecting CPU info by wmic happend a error", err)
  15. return false
  16. }
  17. rt := string(out)

想不到的是,这window server 2003依旧是报错啊,哥,没办法,那么就使用 systeminfo 来捕获CPU的颗数信息了,其他的信息没法采集了。

执行这个命令的代码如下:

  1. a := exec.Command("systeminfo")
  2. b, _ := a.Output()
  3. bb := string(b)
  4. sb, _ := Gbk2Utf(bb)
  5. // your login code 剩下你自己去处理取出来的字符串了吧。

需要注意我这里使用了一个Gbk2Utf函数,这个函数是用来转码的,exec.Comand弄出来的结果中文是乱码的,所以我们需要把gbk编码转为UTF8,windows终端好像默认是gbk编码。

字符集转换代码如下(我也是copy来的):

  1. import // 需要先两个包
  2. "golang.org/x/text/encoding/simplifiedchinese"
  3. "golang.org/x/text/transform"

  4. func Gbk2Utf(s string) (string, error) {
  5. /* 由gbk ---> utf8 */
  6. bs := []byte(s)
  7. reader := transform.NewReader(bytes.NewReader(bs), simplifiedchinese.GBK.NewDecoder())
  8. res, err := ioutil.ReadAll(reader)
  9. if err != nil {
  10. return "", err
  11. }
  12. return string(res), nil
  13. }
  14. func Utf2Gbk(s string) (string, error) {
  15. /* utf8 ---> gbk */
  16. bs := []byte(s)
  17. reader := transform.NewReader(bytes.NewReader(bs), simplifiedchinese.GBK.NewEncoder())
  18. res, err := ioutil.ReadAll(reader)
  19. if err != nil {
  20. return "", err
  21. }
  22. return string(res), nil
  23. }

IP地址取出来以后子网掩码的转换

使用gopsutil取出来的IP发现子网掩码是数字形式的,例如192.168.6.2/32,不是我们常见的255.255.255.0这样的形式,怎么办,肯定要转换啊。使用了math模块,math.Pow这个是求幂函数的。 转换代码如下:

  1. func calcNetmask(netmask int) (return_netmask string) {
  2. /*
  3. 计算子网掩码,由数字类型转为xx.xxx.xx.xx
  4. 算法是这样的: 得到的数字先除以8,得到的商就是有多少个255,余数就需要再计算,余数等于1,那么最后一位子网眼码就是2**(8-1),8是一段子网眼码长度,
  5. 为8个1,1111111,二进制计算。最后一段眼码计算方式如下:
  6. 余数为1,即2**7,
  7. 余数为2,即2**7+2**6
  8. 余数为3,即2**7+2**6+2**5
  9. 依次类推
  10. :param netmask: tmp netmask
  11. :return:
  12. */
  13. factor := netmask / 8 // 商
  14. remainder := netmask % 8 // 余数
  15. mi := 8 - remainder // 计算这个数字的幂
  16. var tmp_last_mask float64
  17. for mi <= 7 { // 判断掩码长度是否超过了7,超过了长度跳出循环,因为掩码的长度最多是8.
  18. tmp_last_mask = math.Pow(float64(2), float64(mi)) + tmp_last_mask
  19. mi = mi + 1
  20. }
  21. last_mask := int(tmp_last_mask)
  22. switch {
  23. case factor == 1 && last_mask == 0: // 意味着是8位的子网掩码
  24. return_netmask = fmt.Sprintf("%s%s", addStr(factor, "255."), "0.0.0")
  25. case (factor == 1 && last_mask != 0): //意味着8-24之间的子网掩码
  26. return_netmask = fmt.Sprintf("%s%d.%s", addStr(factor, "255."), last_mask, "0.0")
  27. case (factor == 2 && last_mask == 0): //意味着16位整的子网掩码
  28. return_netmask = fmt.Sprintf("%s0.0", addStr(factor, "255."))
  29. case (factor == 3 && last_mask == 0): // 意味着24位整的掩码
  30. return_netmask = fmt.Sprintf("%s%s", addStr(factor, "255."), "0")
  31. case (factor == 3 && last_mask != 0): // 意味着24-32之间的子网掩码
  32. return_netmask = fmt.Sprintf("%s%d", addStr(factor, "255."), last_mask)
  33. case (factor == 2 && last_mask != 0): // 意味着16-24位之间子网掩码
  34. return_netmask = fmt.Sprintf("%s%d.%s", addStr(factor, "255."), last_mask, "0")
  35. case factor == 4: // 意味着4个255
  36. return_netmask = fmt.Sprintf("%s255", addStr(factor-1, "255."))
  37. case factor == 0: // 小于8位的掩码
  38. return_netmask = fmt.Sprintf("%d.0.0.0", last_mask)
  39. }
  40. return return_netmask
  41. }

判断存储资产ID的文件在不在

判断文件在不在,我们可以使用第三方模块,这样就不需要自己来写了,我依稀记得自己写判断文件是否存在的代码花了我10几分钟来弄好,没办法,刚入门的菜鸟不太熟悉模块用法,既然有了别人写好的模块,干嘛不使用呢,节省时间提高工作效率。

模块地址如下:"github.com/toolkits/file"

用法如下:

  1. asset_id = `c:/asset_id` // windows必须使用反撇号来包裹文件路径,不然乱码!切记。
  2. if !file.IsExist(asset_id) {
  3. errMsg := fmt.Sprintf("not found the asset_id(%s)", asset_id)
  4. log.Printf(errMsg)
  5. return "", errors.New(errMsg)
  6. }

读取文件与写入文件

参考我的另一篇博客,go下如何读取与写入文件

如何把json格式数据通过HTTP-POST请求发送到服务器

这个我也踩了几个坑。

首先说第一个坑:我们设定http的head头部的时候,contentType必须是 application/x-www-form-urlencoded 不然服务器接受不到你的任何内容。

第二个坑:发送的数据由于是json格式的,通过json.Marshal 函数处理后返回来的是[]byte类型,但是net/http 要求你的数据必须是字符串类型,fuck,静态编译型语言感觉最恶心的就是数据类型的转换与强制性,一但数据类型不合就抛错.

代码如下:


  1. jsondata, err := json.Marshal(collectInfo)
  2. postJsondata := string(jsondata) //[]byte 转为字符串类型
  3. http.Post(url,
  4. "application/x-www-form-urlencoded",
  5. strings.NewReader(fmt.Sprintf("asset_data=%s", postJsondata))) // Sprintf来格式化数据

json反向格式化 收到来自于服务器的数据

服务器发过来的数据是json格式的map类型,也就是python里面的dict类型数据,那么我们本地收到数据后格式的话,需要预先定义一个map,map格式为map[string]interface{},这样就可以接受返回来的任何数据了只要key为string类型的,value类型为任意

  1. responseInfo = make(map[string]interface{})
  2. result := connectCmdbServer(reportAssetWithoutId, method, jsondata)
  3. json.Unmarshal(result, &responseInfo)
  4. v, ok := responseInfo["asset_id"] // 注意这个v是interface类型
  5. if ok {
  6. if saveAssetId(v) {
  7. log.Println("already saved asset id :", responseInfo["asset_id"])
  8. }
  9. } else {
  10. fmt.Println(string(result))
  11. }

我们手下saveAssetId里面的这个数据类型,因为我们写入到文件的是字符串类型的,而传入的是interface{}类型的数据,那么怎么办,使用reflect到具体数据类型的转换。

  1. func saveAssetId(assetId interface{}) bool {
  2. // other logic code
  3. d, ok := assetId.(float64) // 转为float64后再转为字符串类型。可以使用fmt.Printf("%T") 来查看她的具体类型。
  4. if ok == false {
  5. log.Println("can't convert the asset id from interface to float64")
  6. return false
  7. }
  8. }

18 如何使用go来采集windows的基本硬件信息后发送到CMDB的服务器上的更多相关文章

  1. Python使用WMI模块获取Windows系统的硬件信息,并使用pyinstaller库编译打包成exe的可执行文件

    由于公司现阶段大多数应用软件都是基于Windows系统开发和部署,很多软件安装部署都是在windows server 2012.windows server 2008之类的服务器上,部门同事每次测试一 ...

  2. Windows系统及硬件信息读取

    Windows桌面端开发常常会需要读取系统信息或硬件信息作为用户标识,比如用于确认该设配是否已经激活程序.也可以使用随机生成的UUID来作为唯一标识,但是如果重装系统或重装软件都有可能导致标识丢失,因 ...

  3. sigar获取Windows系统的硬件信息进行JAVA后台系统资源监控

    windows下,将sigar-amd64-winnt.dll复制到jdk的bin目录下或者拷贝到WEB-INF/lib下 linux下,将libsigar-amd64-linux.so拷贝到jdk的 ...

  4. Windows Server 2012 FTP配置 后客户机一直登录不上

    配置FTP时出现奇怪的问题,服务器配置完成后客户机访问FTP站点时输入用户名密码一直不对. 经多次试验后发现,默认情况下是如下图配置.将用户下次登录时必须更改密码勾掉,勾选密码永不过期即可正常登录.

  5. 分享一个windows下检测硬件信息的bat脚本

    文件名必须以.bat结尾,如果出现闪退,请右击鼠标,以管理身份运行即可 @echo offcolor 0atitle 硬件检测 mode con cols=90sc config winmgmt st ...

  6. 无法在web服务器上启动调试。调试失败,因为没有启用集成windows身份验证

    ----注意:以管理员身份运行VS C#中ASP.NET Web应用程序编译时的错误:无法在web服务器上启动调试.调试失败,因为没有启用集成windows身份验证. 解决:打开IIS,在IIS里查看 ...

  7. 使用Putty实现windows向阿里云的Linux云服务器上传文件

    1.首先获取PSCP工具 PuTTY小巧方便.但若需要向网络中的Linux系统上传文件,则可以使用PuTTY官方提供的PSCP工具来实现上传.PSCP是基于ssh协议实现. 可以点击这里下载 2.启动 ...

  8. 定时备份windows机器上的文件到linux服务器上的操作梳理(rsync)

    由于需要对网络设备做备份,备份文件是放到windows机器上的.现在需要将备份数据同步到linux备份机器上,想到的方案有三种: 1)将windows的备份目录共享出来,然后在linux服务器上进行挂 ...

  9. 用EasyClient开源项目采集Windows摄像头/麦克风的音视频进行RTSP直播

    EasyClient是EasyDarwin开源流媒体团队开发的一款功能丰富的开源PC客户端项目,目前支持Windows.Android版本,后续将支持ios版本,其中Windows版本的EasyCli ...

随机推荐

  1. 新浪微博 oauth2.0 redirect_uri_mismatch

    新浪微博开放平台出来很久了,现在才开始研究,貌似有点晚了.... 第一次折腾,总是出现这样那样的问题,即使照着别人成功的例子也是一样,这不,开始运行的时候,运行下面的例子,总是报error:redir ...

  2. PPP或PPPOE身份验证PAP和CHAP

    PPP或PPPOE都支持身份验证,有两种验证方式:PAP和CHAP. PAP,Passwd Authentication Protocol,密码验证协议,以客户端明文方式传递用户名和密码,服务器和本省 ...

  3. 最近玩Bootstrap , 一些小工具 记录在案。

    最近玩Bootstrap , 一些小工具 记录在案. 1 定制Bootstrap ,所见即所得的修改Bootstrap的各种变量,即时查看样式的变化. http://bootswatchr.com/ ...

  4. Java设计模式(10)代理模式(Proxy模式)

    理解并使用设计模式,能够培养我们良好的面向对象编程习惯,同时在实际应用中,可以如鱼得水,享受游刃有余的乐趣. Proxy是比较有用途的一种模式,而且变种较多,应用场合覆盖从小结构到整个系统的大结构,P ...

  5. JS DOM操作思维导图

  6. 快速掌握Vue.js使用【转】

    Vue.js是当下很火的一个JavaScript MVVM库,它是以数据驱动和组件化的思想构建的.相比于Angular.js,Vue.js提供了更加简洁.更易于理解的API,使得我们能够快速地上手并使 ...

  7. 8个日志级别(OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE、 ALL)

    log4j定义了8个级别的log(除去OFF和ALL,可以说分为6个级别),优先级从高到低依次为:OFF.FATAL.ERROR.WARN.INFO.DEBUG.TRACE. ALL. ALL 最低等 ...

  8. 自然语言交流系统 phxnet团队 创新实训 项目博客 (八)

    在本项目中使用到的“文本转语音”的技术总结: 文本转语音,使用的是科大讯飞的接口,因为此作品之中语音包不是重点,所以语音包的转换我们统一调用的科大讯飞的语音包接口,依旧是在线的文字转语音,客户端将来自 ...

  9. 【转】【WPF】WPF绑定用法

    一.简介 为了后面行文顺利,在进入正文之前,我们首先对本文所涉及到的绑定知识进行简单地介绍.该节包含绑定的基本组成以及构建方式. WPF中的绑定完成了绑定源和绑定目标的联动.一个绑定常常由四部分组成: ...

  10. (笔记)Mysql命令drop table:删除数据表

    drop table命令用于删除数据表. drop table命令格式:drop table <表名>; 例如,删除表名为 MyClass 的表:    mysql> drop ta ...