-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
336 lines (290 loc) · 7.8 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
package main
import (
"fmt"
"github.com/beevik/etree"
log "github.com/sirupsen/logrus"
snmp "github.com/soniah/gosnmp"
"github.com/urfave/cli"
"net"
"os"
"strconv"
"time"
)
func main() { // Main always gets called as the entry point
log.SetReportCaller(true) // Enable long format logging
// Setup CLI Info
app := cli.NewApp()
app.Name = "UptimeParserGo"
app.Usage = ""
app.HideVersion = true
app.HideHelp = false
app.EnableBashCompletion = true
// Setup flags here
var DebugMode bool
var CidrIpAddress_in string
var Snmp_in string
var overTime_in int
flags := []cli.Flag{
cli.BoolFlag{
Name: "debug, d",
Usage: "Enable debug mode",
Destination: &DebugMode,
},
cli.StringFlag{
Name: "ip, i",
Usage: "IP address to scan",
Destination: &CidrIpAddress_in,
},
cli.StringFlag{
Name: "snmp, s",
Usage: "SNMP community string ('public' default)",
Destination: &Snmp_in,
},
cli.IntFlag{
Name: "overTime, o",
Usage: "Time over threshold (24hr default)",
Destination: &overTime_in,
},
}
// Commands to be run go here, after parsing variables
app.Commands = []cli.Command{
{
UseShortOptionHandling: true,
Name: "xml",
Aliases: []string{"x"},
Usage: "Export as XML",
Category: "output",
Action: func(c *cli.Context) error {
// Set defaults
var CidrIpAddress_out string
if CidrIpAddress_in == "" {
println("IP Address missing from args")
os.Exit(1)
} else {
CidrIpAddress_out = CidrIpAddress_in
}
var overTime_out int
if overTime_in == 0 {
overTime_out = 24
} else {
overTime_out = overTime_in
}
var Snmp_out string
if Snmp_in == "" {
Snmp_out = "public"
} else {
Snmp_out = Snmp_in
}
// Pass CLI args and run main logic method
output := MainLogic(CidrIpAddress_out, Snmp_out, overTime_out)
print(output)
return nil
},
},
{
UseShortOptionHandling: true,
Name: "json",
Aliases: []string{"j"},
Usage: "export as JSON",
Category: "output",
Action: func(c *cli.Context) error {
args := c.Args()
log.Debug(args)
log.Fatal("NOT Implemented yet :(")
//DO WORK HERE
return nil
},
},
}
app.Flags = flags // Assign flags via parse right before we start work
app.Before = func(c *cli.Context) error {
// Actions to run before running parsed commands
if DebugMode {
log.SetLevel(5) // 5=Debug 4=Info
log.Info("Debug Mode")
} else {
log.SetLevel(3)
// open a file
f, err := os.OpenFile("uptime.log", os.O_CREATE|os.O_RDWR, 0666) // Create new log file every run
//f, err := os.OpenFile("uptime.log", os.O_APPEND | os.O_CREATE | os.O_RDWR, 0666)
if err != nil {
fmt.Printf("error opening file: %v", err)
}
log.SetOutput(f)
log.Warn("Normal Mode")
}
return nil
}
// Parse Commands and flags here
err := app.Run(os.Args)
if err != nil {
log.Fatal(err)
}
log.Debug("EOP")
}
func MainLogic(ip_CIDR_in string, snmp_in string, overTime int) string {
// Setup output variables ahead of time
var outputToConsole string
// Generate list of IP addresses from console input
var IPlist, err = Hosts(ip_CIDR_in)
if err != nil {
log.Fatal("Invalid IP")
}
// Generate list of devices
log.Debug(IPlist)
var device_list []Device
for _, i := range IPlist {
x := Device{}
x.name = i
x.snmp_comm = snmp_in
device_list = append(device_list, x)
}
// Update list of devices
device_list = UpdateDeviceObjUptimeList(device_list)
// Generate device over limit count
var device_over_limit int
var sec_in_day = uint(60 * 60 * overTime)
for _, i := range device_list {
if i.up_time_sec > sec_in_day {
log.Debug("Overtime - ", i.name)
device_over_limit = device_over_limit + 1
}
}
var XML_output = map[string]int{} //Key value pairs (like a dictionary)
XML_output["Up Device count"] = len(device_list)
XML_output["Device over time limit"] = device_over_limit
outputToConsole = GenerateXML(XML_output, "Ok")
return outputToConsole
}
func UpdateDeviceObjUptimeList(device_list_in []Device) []Device {
// Make list of channels
log.Info("Creating channels")
var chan_list []chan Device
for range device_list_in {
dev_chan := make(chan Device)
chan_list = append(chan_list, dev_chan)
}
// Dispatch work to threads
log.Info("Starting work...")
for i, item := range device_list_in {
go Update_single_device_uptime(item, chan_list[i])
}
// Generate list of devices for output
log.Info("Merging channels...")
var device_list_proc []Device
for i, item := range chan_list {
log.Debug("Received: ", i)
device_list_proc = append(device_list_proc, <-item)
close(item)
}
// Print Debug output
log.Info("Cleaning output...")
var device_list_out []Device
for _, item := range device_list_proc {
if item.up_time_sec != 0 {
device_list_out = append(device_list_out, item)
log.Info("Name: " + item.name + " Uptime:" + fmt.Sprint(item.up_time_sec))
}
}
return device_list_out
}
// For Multithread processing
func Update_single_device_uptime(device_in Device, out_dev chan Device) {
device_in.UpdateUptime()
out_dev <- device_in
}
func Hosts(cidr string) ([]string, error) {
ip, ipnet, err := net.ParseCIDR(cidr)
if err != nil {
return nil, err
}
var mask = cidr[len(cidr)-2:]
if mask == "32" {
var ips []string
var singleIp = cidr[:len(cidr)-3]
ips = append(ips, singleIp)
return ips, nil
}
var ips []string
for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); host_inc(ip) {
ips = append(ips, ip.String())
}
return ips[1 : len(ips)-1], nil // pop network address and broadcast address
}
func host_inc(ip net.IP) {
for i := len(ip) - 1; i >= 0; i-- {
ip[i]++
if ip[i] > 0 {
break
}
}
}
//GenerateXML Takes key value pairs and output XML that PRTG can ingest
func GenerateXML(data_in map[string]int, msg_in string) string {
doc := etree.NewDocument()
prtg := doc.CreateElement("prtg")
for k, v := range data_in {
result := prtg.CreateElement("result")
chan_ele := result.CreateElement("channel")
chan_ele.CreateText(k)
val_ele := result.CreateElement("value")
val_ele.CreateText(strconv.Itoa(v))
}
text := prtg.CreateElement("text")
text.CreateText(msg_in)
doc.Indent(0)
XmlOutput, _ := doc.WriteToString()
return XmlOutput
}
func GenerateJSON() {
}
type Device struct {
//TODO maybe change uptime to a signed int and use -1 to indicate error?
name string
up_time_sec uint // Value of 0 == bad lookup
snmp_comm string
}
//Constructor
func (d *Device) NewDevice(Name_in string, Snmp_comm_in string) Device {
obj := new(Device)
obj.name = Snmp_comm_in
obj.snmp_comm = Name_in
return *obj
}
func (d *Device) UpdateUptime() {
d.up_time_sec = d.GetSNMP().(uint) / 100
}
//GetSNMP Defaults to getting up time if no oid is specified
func (d *Device) GetSNMP(oid_in ...string) interface{} {
var oids []string
if oid_in == nil {
oids = []string{"1.3.6.1.2.1.1.3.0"}
} else {
oids = oid_in
}
// build our own GoSNMP struct, rather than using snmp.Default
params := &snmp.GoSNMP{
Target: d.name,
Port: 161, // When trying to pass a uint16 or convert from int to uint16,
// the call freezes, just going to hard code it
Version: snmp.Version2c,
Timeout: time.Duration(2) * time.Second,
Community: d.snmp_comm,
}
log.Debug("Trying: " + d.name)
err := params.Connect()
if err != nil {
log.Debug("Connect() err: %v", err) // Normally this would log as a FATAL
}
defer params.Conn.Close()
result, err2 := params.Get(oids) // Get() accepts up to snmp.MAX_OIDS
if err2 != nil {
log.Debug("Get() err: ", d.name, " - ", err2)
}
if err != nil || err2 != nil {
return uint(0) // Normally we would return a better value, but we will deal with it up stream
} else {
log.Debug("Result: ", d.name, " ", result.Variables[0].Value)
return result.Variables[0].Value
}
}