CORD-239 refactor of harvester uservice
Change-Id: I0fdb587267b6c5fb1c53bb35d77cd5921b937b6d
diff --git a/harvester/harvester.go b/harvester/harvester.go
new file mode 100644
index 0000000..9d47830
--- /dev/null
+++ b/harvester/harvester.go
@@ -0,0 +1,135 @@
+// Copyright 2016 Open Networking Laboratory
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package main
+
+import (
+ "fmt"
+ "github.com/Sirupsen/logrus"
+ "github.com/gorilla/mux"
+ "github.com/kelseyhightower/envconfig"
+ "net/http"
+ "strconv"
+ "sync"
+ "text/template"
+ "time"
+)
+
+// application application configuration and internal state
+type application struct {
+ Port int `default:"4246" desc:"port on which the service will listen for requests"`
+ Listen string `default:"0.0.0.0" desc:"IP on which the service will listen for requests"`
+ LogLevel string `default:"warning" envconfig:"LOG_LEVEL" desc:"log output level"`
+ LogFormat string `default:"text" envconfig:"LOG_FORMAT" desc:"format of log messages"`
+ DHCPLeaseFile string `default:"/harvester/dhcpd.leases" envconfig:"DHCP_LEASE_FILE" desc:"lease file to parse for lease information"`
+ OutputFile string `envconfig:"OUTPUT_FILE" desc:"name of file to output discovered lease in bind9 format"`
+ OutputFormat string `default:"{{.ClientHostname}}\tIN A {{.IPAddress}}\t; {{.HardwareAddress}}" envconfig:"OUTPUT_FORMAT" desc:"specifies the single entry format when outputing to a file"`
+ VerifyLeases bool `default:"true" envconfig:"VERIFY_LEASES" desc:"verifies leases with a ping"`
+ VerifyTimeout time.Duration `default:"1s" envconfig:"VERIFY_TIMEOUT" desc:"max timeout (RTT) to wait for verification pings"`
+ VerifyWithUDP bool `default:"false" envconfig:"VERIFY_WITH_UDP" desc:"use UDP instead of raw sockets for ping verification"`
+ QueryPeriod time.Duration `default:"30s" envconfig:"QUERY_PERIOD" desc:"period at which the DHCP lease file is processed"`
+ QuietPeriod time.Duration `default:"2s" envconfing:"QUIET_PERIOD" desc:"period to wait between accepting parse requests"`
+ RequestTimeout time.Duration `default:"10s" envconfig:"REQUEST_TIMEOUT" desc:"period to wait for processing when requesting a DHCP lease database parsing"`
+ RNDCUpdate bool `default:"false" envconfig:"RNDC_UPDATE" desc:"determines if the harvester reloads the DNS servers after harvest"`
+ RNDCAddress string `default:"127.0.0.1" envconfig:"RNDC_ADDRESS" desc:"IP address of the DNS server to contact via RNDC"`
+ RNDCPort int `default:"954" envconfig:"RNDC_PORT" desc:"port of the DNS server to contact via RNDC"`
+ RNDCKeyFile string `default:"/key/rndc.conf.maas" envconfig:"RNDC_KEY_FILE" desc:"key file, with default, to contact DNS server"`
+ RNDCZone string `default:"cord.lab" envconfig:"RNDC_ZONE" desc:"zone to reload"`
+
+ log *logrus.Logger `ignored:"true"`
+ interchange sync.RWMutex `ignored:"true"`
+ leases map[string]*Lease `ignored:"true"`
+ byHardware map[string]*Lease `ignored:"true"`
+ byHostname map[string]*Lease `ignored:"true"`
+ outputTemplate *template.Template `ignored:"true"`
+ requests chan *chan uint `ignored:"true"`
+}
+
+func main() {
+ // initialize application state
+ app := &application{
+ log: logrus.New(),
+ requests: make(chan *chan uint, 100),
+ }
+
+ // process and validate the application configuration
+ err := envconfig.Process("HARVESTER", app)
+ if err != nil {
+ app.log.Fatalf("unable to parse configuration options : %s", err)
+ }
+ switch app.LogFormat {
+ case "json":
+ app.log.Formatter = &logrus.JSONFormatter{}
+ default:
+ app.log.Formatter = &logrus.TextFormatter{
+ FullTimestamp: true,
+ ForceColors: true,
+ }
+ }
+ level, err := logrus.ParseLevel(app.LogLevel)
+ if err != nil {
+ level = logrus.WarnLevel
+ }
+ app.log.Level = level
+
+ app.outputTemplate, err = template.New("harvester").Parse(app.OutputFormat)
+ if err != nil {
+ app.log.Fatalf("invalid output file format specified : %s", err)
+ }
+
+ // output the configuration
+ app.log.Infof(`Configuration:
+ LISTEN: %s
+ PORT: %d
+ LOG_LEVEL: %s
+ LOG_FORMAT: %s
+ DHCP_LEASE_FILE: %s
+ OUTPUT_FILE: %s
+ OUTPUT_FORMAT: %s
+ VERIFY_LEASES: %t
+ VERIFY_TIMEOUT: %s
+ VERIFY_WITH_UDP: %t
+ QUERY_PERIOD: %s
+ QUIET_PERIOD: %s
+ REQUEST_TIMEOUT: %s
+ RNDC_UPDATE: %t
+ RNDC_ADDRESS: %s
+ RNDC_PORT: %d
+ RNDC_KEY_FILE: %s
+ RNDC_ZONE: %s`,
+ app.Listen, app.Port,
+ app.LogLevel, app.LogFormat,
+ app.DHCPLeaseFile, app.OutputFile, strconv.Quote(app.OutputFormat),
+ app.VerifyLeases, app.VerifyTimeout, app.VerifyWithUDP,
+ app.QueryPeriod, app.QuietPeriod, app.RequestTimeout,
+ app.RNDCUpdate, app.RNDCAddress, app.RNDCPort, app.RNDCKeyFile, app.RNDCZone)
+
+ // establish REST end points
+ router := mux.NewRouter()
+ router.HandleFunc("/lease/", app.listLeasesHandler).Methods("GET")
+ router.HandleFunc("/lease/{ip}", app.getLeaseHandler).Methods("GET")
+ router.HandleFunc("/lease/hardware/{mac}", app.getLeaseByHardware).Methods("GET")
+ router.HandleFunc("/lease/hostname/{name}", app.getLeaseByHostname).Methods("GET")
+ router.HandleFunc("/harvest/", app.doHarvestHandler).Methods("POST")
+ router.HandleFunc("/harvest", app.doHarvestHandler).Methods("POST")
+ http.Handle("/", router)
+
+ // start DHCP lease file synchronization handler
+ go app.syncRequestHandler(app.requests)
+
+ // start loop to periodically synchronize DHCP lease file
+ go app.syncFromDHCPLeaseFileLoop(app.requests)
+
+ // listen for REST requests
+ http.ListenAndServe(fmt.Sprintf("%s:%d", app.Listen, app.Port), nil)
+}