bucket2rule.go 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. package main
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "flag"
  6. "fmt"
  7. "io/ioutil"
  8. "os"
  9. "path"
  10. "path/filepath"
  11. "strings"
  12. "text/template"
  13. "unicode/utf8"
  14. "git.wecise.com/wecise/odb-go/odb"
  15. "gitee.com/wecisecode/util/logger"
  16. )
  17. // https://stackoverflow.com/questions/22367337/last-item-in-a-template-range
  18. var ErrBadPattern = path.ErrBadPattern
  19. type RuleStru struct {
  20. Class string
  21. Bucket string
  22. VarName string
  23. Param []string
  24. NickName string
  25. PrintID string
  26. PrintAtTime string
  27. SnmpResult []string // snmpResult0, ...
  28. TmplIndex []string // {{index .value 0}} , ...
  29. }
  30. //-------------------------------------------------------------------------------------------------
  31. // bucket2rule -class "class1, class2" -tmpl "myrule.tmpl" -type "etcd"/"file" -out "/opt/syslog"
  32. // ./bucket2rule -keyspace "matrix" -hosts "172.26.38.247" -class "/matrix/entity/linux" -except "*_baseline*"
  33. // ./bucket2rule -keyspace "matrix" -hosts "172.26.38.247" -class "/matrix/entity/linux" -except "*_baseline*" -type "file" -out "/opt/matrix/agent/bin/output"
  34. //-------------------------------------------------------------------------------------------------
  35. func main() {
  36. class := flag.String("class", "", "class list, sep with ',' .")
  37. myhost := flag.String("hosts", "", "no defualt ")
  38. tmpl := flag.String("tmpl", "", "template file, defualt rule.tmpl in current dir")
  39. out_type := flag.String("type", "", "output file type, defualt json for etcd")
  40. out := flag.String("out", "", "output file dir if type=file or etcd path if type=json, default /$keyspace/rules ")
  41. varname := flag.String("varname", "", "default _DATA ")
  42. keyspace := flag.String("keyspace", "", "default matrix ")
  43. bucket := flag.String("bucket", "", "bucket field list, , sep with ',' default all fields")
  44. except := flag.String("except", "", "except bucket field list, sep with ',' default none .")
  45. flag.Parse()
  46. var clazz, buckets, excepts []string
  47. if *class == "" {
  48. logger.Errorf("class can't be empty .")
  49. os.Exit(1)
  50. } else {
  51. clazz = strings.Split(*class, ",")
  52. }
  53. if *bucket != "" {
  54. buckets = strings.Split(*bucket, ",")
  55. }
  56. if *except != "" {
  57. excepts = strings.Split(*except, ",")
  58. }
  59. var hosts []string
  60. if *myhost == "" {
  61. logger.Errorf("hosts can't be empty .")
  62. os.Exit(1)
  63. } else {
  64. hosts = strings.Split(*myhost, ",")
  65. }
  66. if *keyspace == "" {
  67. *keyspace = "matrix"
  68. }
  69. if *tmpl == "" {
  70. *tmpl = "rule.tmpl"
  71. }
  72. if *out_type == "" {
  73. *out_type = "json"
  74. }
  75. if *out == "" {
  76. *out = fmt.Sprintf("/%s/rules", *keyspace)
  77. }
  78. if *varname == "" {
  79. *varname = "_DATA"
  80. }
  81. handle, err := template.ParseFiles(*tmpl)
  82. if err != nil {
  83. logger.Error(err)
  84. os.Exit(1)
  85. }
  86. strus := ruleData(*keyspace, hosts, clazz, buckets, excepts)
  87. result := map[string]string{}
  88. for _, stru := range strus {
  89. stru.PrintID = `{{.id}}`
  90. stru.PrintAtTime = `{{.attime}}`
  91. stru.SnmpResult = make([]string, len(stru.Param))
  92. stru.TmplIndex = make([]string, len(stru.Param))
  93. stru.VarName = *varname
  94. for i := range stru.Param {
  95. stru.SnmpResult[i] = fmt.Sprintf("snmpResult%d", i)
  96. stru.TmplIndex[i] = fmt.Sprintf(`{{index .value %d}}`, i)
  97. }
  98. buf := &bytes.Buffer{}
  99. if err := handle.ExecuteTemplate(buf, *tmpl, stru); err != nil {
  100. logger.Error(err)
  101. os.Exit(1)
  102. } else {
  103. switch *out_type {
  104. case "file":
  105. key := fmt.Sprintf(`%s_%s.rule`, stru.Bucket, stru.NickName)
  106. result[key] = buf.String()
  107. case "json":
  108. key := fmt.Sprintf(`%s/%s_%s.rule`, *out, stru.Bucket, stru.NickName)
  109. result[key] = buf.String()
  110. }
  111. }
  112. }
  113. switch *out_type {
  114. case "file":
  115. for k, v := range result {
  116. file := filepath.Join(*out, k)
  117. if err = ioutil.WriteFile(file, []byte(v), 0666); err != nil {
  118. logger.Error(err)
  119. os.Exit(1)
  120. }
  121. }
  122. case "json":
  123. if bs, err := json.Marshal(result); err != nil {
  124. logger.Error(err)
  125. } else {
  126. if err = ioutil.WriteFile("output.json", bs, 0666); err != nil {
  127. logger.Error(err)
  128. os.Exit(1)
  129. }
  130. }
  131. }
  132. }
  133. func ruleData(keyspace string, hosts, clazz, buckets, excepts []string) []*RuleStru {
  134. rules := []*RuleStru{}
  135. client, err := odb.NewClient(&odb.Config{
  136. Keyspace: keyspace,
  137. Hosts: hosts,
  138. })
  139. if err != nil {
  140. logger.Error(err)
  141. return rules
  142. }
  143. defer func() {
  144. if err := client.Close(); err != nil {
  145. logger.Error(err)
  146. }
  147. }()
  148. for _, class := range clazz {
  149. class = strings.TrimSpace(class)
  150. var nickname string
  151. if res, err := client.Query(fmt.Sprintf("select nickname from /system/class where name='%s'", class)).WithMeta(odb.QueryMeta{"meta": "false"}).Do(); err != nil {
  152. logger.Error(err)
  153. } else if len(res.Data) > 0 {
  154. nickname = res.Data[0]["nickname"].(string)
  155. if res, err := client.Query(fmt.Sprintf("select field, param from /system/tsdb where class='%s'", class)).WithMeta(odb.QueryMeta{"meta": "false"}).Do(); err != nil {
  156. logger.Error(err)
  157. } else {
  158. for _, row := range res.Data {
  159. field := row["field"].(string)
  160. if buckets != nil {
  161. find := false
  162. for _, bucket := range buckets {
  163. bucket = strings.TrimSpace(bucket)
  164. if bucket == field {
  165. find = true
  166. break
  167. }
  168. }
  169. if !find {
  170. continue
  171. }
  172. }
  173. if excepts != nil {
  174. find := false
  175. for _, except := range excepts {
  176. except = strings.TrimSpace(except)
  177. if strings.ContainsAny(except, "*?") {
  178. if match, _ := ExpMatch(except, field); match {
  179. find = true
  180. break
  181. }
  182. } else {
  183. if except == field {
  184. find = true
  185. break
  186. }
  187. }
  188. }
  189. if find {
  190. continue
  191. }
  192. }
  193. param := []string{}
  194. if v := row["param"]; v != nil {
  195. for _, val := range v.([]interface{}) {
  196. param = append(param, val.(string))
  197. }
  198. }
  199. rules = append(rules, &RuleStru{Class: class, NickName: nickname, Bucket: field, Param: param})
  200. }
  201. }
  202. }
  203. }
  204. return rules
  205. }
  206. func ExpMatch(pattern, name string) (bool, error) {
  207. // check some base cases
  208. patternLen, nameLen := len(pattern), len(name)
  209. if patternLen == 0 && nameLen == 0 {
  210. return true, nil
  211. }
  212. if patternLen == 0 {
  213. return false, nil
  214. }
  215. if nameLen == 0 && pattern != "*" {
  216. return false, nil
  217. }
  218. // check for matches one rune at a time
  219. patIdx, nameIdx := 0, 0
  220. for patIdx < patternLen && nameIdx < nameLen {
  221. patRune, patAdj := utf8.DecodeRuneInString(pattern[patIdx:])
  222. nameRune, nameAdj := utf8.DecodeRuneInString(name[nameIdx:])
  223. if patRune == '\\' {
  224. // handle escaped runes
  225. patIdx += patAdj
  226. patRune, patAdj = utf8.DecodeRuneInString(pattern[patIdx:])
  227. if patRune == utf8.RuneError {
  228. return false, ErrBadPattern
  229. } else if patRune == nameRune {
  230. patIdx += patAdj
  231. nameIdx += nameAdj
  232. } else {
  233. return false, nil
  234. }
  235. } else if patRune == '*' {
  236. // handle stars
  237. if patIdx += patAdj; patIdx >= patternLen {
  238. // a star at the end of a pattern will always
  239. // match the rest of the path
  240. return true, nil
  241. }
  242. // check if we can make any matches
  243. for ; nameIdx < nameLen; nameIdx += nameAdj {
  244. if m, _ := ExpMatch(pattern[patIdx:], name[nameIdx:]); m {
  245. return true, nil
  246. }
  247. }
  248. return false, nil
  249. } else if patRune == '[' {
  250. // handle character sets
  251. patIdx += patAdj
  252. endClass := indexRuneWithEscaping(pattern[patIdx:], ']')
  253. if endClass == -1 {
  254. return false, ErrBadPattern
  255. }
  256. endClass += patIdx
  257. classRunes := []rune(pattern[patIdx:endClass])
  258. classRunesLen := len(classRunes)
  259. if classRunesLen > 0 {
  260. classIdx := 0
  261. matchClass := false
  262. if classRunes[0] == '^' {
  263. classIdx++
  264. }
  265. for classIdx < classRunesLen {
  266. low := classRunes[classIdx]
  267. if low == '-' {
  268. return false, ErrBadPattern
  269. }
  270. classIdx++
  271. if low == '\\' {
  272. if classIdx < classRunesLen {
  273. low = classRunes[classIdx]
  274. classIdx++
  275. } else {
  276. return false, ErrBadPattern
  277. }
  278. }
  279. high := low
  280. if classIdx < classRunesLen && classRunes[classIdx] == '-' {
  281. // we have a range of runes
  282. if classIdx++; classIdx >= classRunesLen {
  283. return false, ErrBadPattern
  284. }
  285. high = classRunes[classIdx]
  286. if high == '-' {
  287. return false, ErrBadPattern
  288. }
  289. classIdx++
  290. if high == '\\' {
  291. if classIdx < classRunesLen {
  292. high = classRunes[classIdx]
  293. classIdx++
  294. } else {
  295. return false, ErrBadPattern
  296. }
  297. }
  298. }
  299. if low <= nameRune && nameRune <= high {
  300. matchClass = true
  301. }
  302. }
  303. if matchClass == (classRunes[0] == '^') {
  304. return false, nil
  305. }
  306. } else {
  307. return false, ErrBadPattern
  308. }
  309. patIdx = endClass + 1
  310. nameIdx += nameAdj
  311. } else if patRune == '?' || patRune == nameRune {
  312. // handle single-rune wildcard
  313. patIdx += patAdj
  314. nameIdx += nameAdj
  315. } else {
  316. return false, nil
  317. }
  318. }
  319. if patIdx >= patternLen && nameIdx >= nameLen {
  320. return true, nil
  321. }
  322. if nameIdx >= nameLen && pattern[patIdx:] == "*" || pattern[patIdx:] == "**" {
  323. return true, nil
  324. }
  325. return false, nil
  326. }
  327. // Find the first index of a rune in a string,
  328. // ignoring any times the rune is escaped using "\".
  329. func indexRuneWithEscaping(s string, r rune) int {
  330. end := strings.IndexRune(s, r)
  331. if end == -1 {
  332. return -1
  333. }
  334. if end > 0 && s[end-1] == '\\' {
  335. start := end + utf8.RuneLen(r)
  336. end = indexRuneWithEscaping(s[start:], r)
  337. if end != -1 {
  338. end += start
  339. }
  340. }
  341. return end
  342. }