18 如何使用go来采集windows的基本硬件信息后发送到CMDB的服务器上
2017-09-18 21:17
691 查看
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下的交互式命令与字符集编码问题
采集机器型号采集机器制造商
以上两个需要使用
systeminfo命令采集。
机器序列号
以上需要使用
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 的方法传入命令到系统执行,代码如下:
inpipe, err := a.StdinPipe() if err != nil { log.Fatalf("executing command wmic happend a error:", err) return false } go func() { // 启用一个go routine,设这个函数为匿名函数 defer inpipe.Close() io.WriteString(inpipe, cpucmd) // 把需要的执行的命令放入进去,cpucmd就是需要执行的命令 }()
3.执行完命令后,我们需要就把结果给取出来了,如下所示:
out, err := a.CombinedOutput() if err != nil { log.Println("collecting CPU info by wmic happend a error", err) return false }
4.取出来结果剩下就根据自己的逻辑来处理了。
full example of how to exec windows cmd
cpucmd := "cpu list brief\n"
a := exec.Command("wmic")
inpipe, err := a.StdinPipe()
if err != nil {
log.Fatalf("executing command wmic happend a error:", err)
return false
}
go func() {
defer inpipe.Close()
io.WriteString(inpipe, cpucmd)
}()
out, err := a.CombinedOutput() if err != nil { log.Println("collecting CPU info by wmic happend a error", err) return false }
rt := string(out)
想不到的是,这window server 2003依旧是报错啊,哥,没办法,那么就使用
systeminfo来捕获CPU的颗数信息了,其他的信息没法采集了。
执行这个命令的代码如下:
a := exec.Command("systeminfo") b, _ := a.Output() bb := string(b) sb, _ := Gbk2Utf(bb) // your login code 剩下你自己去处理取出来的字符串了吧。
需要注意我这里使用了一个Gbk2Utf函数,这个函数是用来转码的,exec.Comand弄出来的结果中文是乱码的,所以我们需要把gbk编码转为UTF8,windows终端好像默认是gbk编码。
字符集转换代码如下(我也是copy来的):
import ( // 需要先两个包 "golang.org/x/text/encoding/simplifiedchinese" "golang.org/x/text/transform" ) func Gbk2Utf(s string) (string, error) { /* 由gbk ---> utf8 */ bs := []byte(s) reader := transform.NewReader(bytes.NewReader(bs), simplifiedchinese.GBK.NewDecoder()) res, err := ioutil.ReadAll(reader) if err != nil { return "", err } return string(res), nil } func Utf2Gbk(s string) (string, error) { /* utf8 ---> gbk */ bs := []byte(s) reader := transform.NewReader(bytes.NewReader(bs), simplifiedchinese.GBK.NewEncoder()) res, err := ioutil.ReadAll(reader) if err != nil { return "", err } return string(res), nil }
IP地址取出来以后子网掩码的转换
使用gopsutil取出来的IP发现子网掩码是数字形式的,例如192.168.6.2/32,不是我们常见的255.255.255.0这样的形式,怎么办,肯定要转换啊。使用了math模块,math.Pow这个是求幂函数的。 转换代码如下:func calcNetmask(netmask int) (return_netmask string) { /* 计算子网掩码,由数字类型转为xx.xxx.xx.xx 算法是这样的: 得到的数字先除以8,得到的商就是有多少个255,余数就需要再计算,余数等于1,那么最后一位子网眼码就是2**(8-1),8是一段子网眼码长度, 为8个1,1111111,二进制计算。最后一段眼码计算方式如下: 余数为1,即2**7, 余数为2,即2**7+2**6 余数为3,即2**7+2**6+2**5 依次类推 :param netmask: tmp netmask :return: */ factor := netmask / 8 // 商 remainder := netmask % 8 // 余数 mi := 8 - remainder // 计算这个数字的幂 var tmp_last_mask float64 for mi <= 7 { // 判断掩码长度是否超过了7,超过了长度跳出循环,因为掩码的长度最多是8. tmp_last_mask = math.Pow(float64(2), float64(mi)) + tmp_last_mask mi = mi + 1 } last_mask := int(tmp_last_mask) switch { case factor == 1 && last_mask == 0: // 意味着是8位的子网掩码 return_netmask = fmt.Sprintf("%s%s", addStr(factor, "255."), "0.0.0") case (factor == 1 && last_mask != 0): //意味着8-24之间的子网掩码 return_netmask = fmt.Sprintf("%s%d.%s", addStr(factor, "255."), last_mask, "0.0") case (factor == 2 && last_mask == 0): //意味着16位整的子网掩码 return_netmask = fmt.Sprintf("%s0.0", addStr(factor, "255.")) case (factor == 3 && last_mask == 0): // 意味着24位整的掩码 return_netmask = fmt.Sprintf("%s%s", addStr(factor, "255."), "0") case (factor == 3 && last_mask != 0): // 意味着24-32之间的子网掩码 return_netmask = fmt.Sprintf("%s%d", addStr(factor, "255."), last_mask) case (factor == 2 && last_mask != 0): // 意味着16-24位之间子网掩码 return_netmask = fmt.Sprintf("%s%d.%s", addStr(factor, "255."), last_mask, "0") case factor == 4: // 意味着4个255 return_netmask = fmt.Sprintf("%s255", addStr(factor-1, "255.")) case factor == 0: // 小于8位的掩码 return_netmask = fmt.Sprintf("%d.0.0.0", last_mask) } return return_netmask }
判断存储资产ID的文件在不在
判断文件在不在,我们可以使用第三方模块,这样就不需要自己来写了,我依稀记得自己写判断文件是否存在的代码花了我10几分钟来弄好,没办法,刚入门的菜鸟不太熟悉模块用法,既然有了别人写好的模块,干嘛不使用呢,节省时间提高工作效率。模块地址如下:
"github.com/toolkits/file"
用法如下:
asset_id = `c:/asset_id` // windows必须使用反撇号来包裹文件路径,不然乱码!切记。 if !file.IsExist(asset_id) { errMsg := fmt.Sprintf("not found the asset_id(%s)", asset_id) log.Printf(errMsg) return "", errors.New(errMsg) }
读取文件与写入文件
参考我的另一篇博客,go下如何读取与写入文件如何把json格式数据通过HTTP-POST请求发送到服务器
这个我也踩了几个坑。首先说第一个坑:我们设定http的head头部的时候,contentType必须是
application/x-www-form-urlencoded不然服务器接受不到你的任何内容。
第二个坑:发送的数据由于是json格式的,通过json.Marshal 函数处理后返回来的是[]byte类型,但是
net/http要求你的数据必须是字符串类型,fuck,静态编译型语言感觉最恶心的就是数据类型的转换与强制性,一但数据类型不合就抛错.
代码如下:
jsondata, err := json.Marshal(collectInfo) postJsondata := string(jsondata) //[]byte 转为字符串类型 http.Post(url, "application/x-www-form-urlencoded", strings.NewReader(fmt.Sprintf("asset_data=%s", postJsondata))) // Sprintf来格式化数据
json反向格式化 收到来自于服务器的数据
服务器发过来的数据是json格式的map类型,也就是python里面的dict类型数据,那么我们本地收到数据后格式的话,需要预先定义一个map,map格式为map[string]interface{},这样就可以接受返回来的任何数据了只要key为string类型的,value类型为任意
responseInfo = make(map[string]interface{}) result := connectCmdbServer(reportAssetWithoutId, method, jsondata) json.Unmarshal(result, &responseInfo) v, ok := responseInfo["asset_id"] // 注意这个v是interface类型 if ok { if saveAssetId(v) { log.Println("already saved asset id :", responseInfo["asset_id"]) } } else { fmt.Println(string(result)) }
我们手下saveAssetId里面的这个数据类型,因为我们写入到文件的是字符串类型的,而传入的是interface{}类型的数据,那么怎么办,使用reflect到具体数据类型的转换。
func saveAssetId(assetId interface{}) bool { // other logic code d, ok := assetId.(float64) // 转为float64后再转为字符串类型。可以使用fmt.Printf("%T") 来查看她的具体类型。 if ok == false { log.Println("can't convert the asset id from interface to float64") return false } }
相关文章推荐
- PHP中如何使用header发送头部信息
- Windows 7环境下如何使用远程服务器管理工具
- 如何使用Win7 IIS信息服务器搭建局域网FTP服务器【图文教程】
- 使用iphlpapi.h获得windows下一些基本的网络连接信息
- 如何使用slmgr架设KMS激活服务器激活Windows 7/Server 2008 R2
- windows下使用curl获取响应头信息,以及服务器隐藏响应头信息的办法
- 使用Windows操作系统的客户端如何设置NTP网络服务器
- 在Windows环境下查看服务器基本信息
- windows下WMI使用C++查询用户硬件信息
- java使用siger 获取服务器硬件信息
- PHP中如何使用header发送头部信息
- 如何在WINDOWS(XP)下使用copSSH配置GIT服务器+TortiseGIT客户端
- linxu 系统 安装 使用Dmidecode命令,查看服务器硬件信息
- PHP中如何使用header发送头部信息
- PHP中如何使用header发送头部信息
- 如何在WINDOWS(XP)下使用copSSH配置GIT服务器+TortiseGIT客户端
- linxu 使用dmidecode命令,查看服务器硬件信息(包括维保序列号)
- 如何使用 Backup Exec for Windows Servers Exchange Agent 保护 Exchange 2000 与 2003 服务器?
- 【PERL】使用wmi获取windows硬件信息