blob: 922f1ba3153cb814c07c89ffadf3b9a9f6204e32 [file] [log] [blame]
David K. Bainbridgef694f5a2016-06-10 16:21:27 -07001package main
2
3import (
4 "fmt"
5 "github.com/kelseyhightower/envconfig"
6 "log"
7 "time"
8)
9
10type Config struct {
11 StorageURL string `default:"memory:///switchq/vendors.json" envconfig:"storage_url"`
12 AddressURL string `default:"file:///switchq/dhcp_harvest.inc" envconfig:"address_url"`
13 PollInterval string `default:"1m" envconfig:"poll_interval"`
14 ProvisionTTL string `default:"1h" envconfig:"check_ttl"`
15
16 storage Storage
17 addressSource AddressSource
18 interval time.Duration
19 ttl time.Duration
20}
21
22func checkError(err error, msg string, args ...interface{}) {
23 if err != nil {
24 log.Fatalf(msg, args...)
25 }
26}
27
28func (c *Config) processRecord(rec AddressRec) error {
29 if c.ttl == 0 {
30 // One provisioning only please
31 return nil
32 }
33
34 ok, err := c.storage.Switchq(rec.MAC)
35 if err != nil {
36 return fmt.Errorf("unable to determine ventor of MAC '%s' (%s)", rec.MAC, err)
37 }
38
39 if !ok {
40 // Not something we care about
41 log.Printf("[debug] host with IP '%s' and MAC '%s' and named '%s' not a known switch type",
42 rec.IP, rec.MAC, rec.Name)
43 return nil
44 }
45
46 last, err := c.storage.LastProvisioned(rec.MAC)
47 if err != nil {
48 return err
49 }
50 if last == nil || time.Since(*last) > c.ttl {
51 log.Printf("[debug] time to provision %s", rec.MAC)
52 }
53 return nil
54}
55
56func main() {
57
58 var err error
59 config := Config{}
60 envconfig.Process("SWITCHQ", &config)
61
62 config.storage, err = NewStorage(config.StorageURL)
63 checkError(err, "Unable to create require storage for specified URL '%s' : %s", config.StorageURL, err)
64
65 config.addressSource, err = NewAddressSource(config.AddressURL)
66 checkError(err, "Unable to create required address source for specified URL '%s' : %s", config.AddressURL, err)
67
68 config.interval, err = time.ParseDuration(config.PollInterval)
69 checkError(err, "Unable to parse specified poll interface '%s' : %s", config.PollInterval, err)
70
71 config.ttl, err = time.ParseDuration(config.ProvisionTTL)
72 checkError(err, "Unable to parse specified provision TTL value of '%s' : %s", config.ProvisionTTL, err)
73
74 log.Printf(`Configuration:
75 Storage URL: %s
76 Poll Interval: %s
77 Address Source: %s
78 Provision TTL: %s`,
79 config.StorageURL, config.PollInterval, config.AddressURL, config.ProvisionTTL)
80
81 // We use two methods to attempt to find the MAC (hardware) address associated with an IP. The first
82 // is to look in the table. The second is to send an ARP packet.
83 for {
84 log.Printf("[info] Checking for switches @ %s", time.Now())
85 addresses, err := config.addressSource.GetAddresses()
86
87 if err != nil {
88 log.Printf("[error] unable to read addresses from address source : %s", err)
89 } else {
90 log.Printf("[info] Queried %d addresses from address source", len(addresses))
91
92 for _, rec := range addresses {
93 log.Printf("[debug] Processing %s(%s, %s)", rec.Name, rec.IP, rec.MAC)
94 if err := config.processRecord(rec); err != nil {
95 log.Printf("[error] Error when processing IP '%s' : %s", rec.IP, err)
96 }
97 }
98 }
99
100 time.Sleep(config.interval)
101 }
102}