CORD-735 rewrite client hostnames that are invalid

Change-Id: I895019f3165fb7a05e350a34d305c2fd62b96df1
(cherry picked from commit 17911b4da7c47062d26b0d400ae613e322ebffae)
diff --git a/harvester/parse.go b/harvester/parse.go
index d61e791..906b23c 100644
--- a/harvester/parse.go
+++ b/harvester/parse.go
@@ -15,6 +15,7 @@
 
 import (
 	"bufio"
+	"bytes"
 	"fmt"
 	"net"
 	"os"
@@ -39,8 +40,25 @@
 	bindFileFormat = "{{.ClientHostname}}\tIN A {{.IPAddress}}\t; {{.HardwareAddress}}"
 )
 
+// generateClientHostname generates a client name based on hardware address
+func (app *application) generateClientHostname(lease *Lease) string {
+	var buf bytes.Buffer
+
+	app.log.Debugf("Generating client-hostname for MAC '%s'", lease.HardwareAddress.String())
+
+	err := app.clientNameTemplate.Execute(&buf, lease)
+	if err != nil {
+		app.log.Errorf("Unable to generate client host name for lease with HW address '%s' : %s",
+			lease.HardwareAddress.String(), err)
+		return strings.ToUpper("UNK-" +
+			strings.Replace(lease.HardwareAddress.String(), ":", "", -1))
+	}
+
+	return buf.String()
+}
+
 // parseLease parses a single lease from the lease file
-func parseLease(scanner *bufio.Scanner, lease *Lease) error {
+func (app *application) parseLease(scanner *bufio.Scanner, lease *Lease) error {
 	var err error
 	for scanner.Scan() {
 		fields := strings.Fields(scanner.Text())
@@ -49,12 +67,16 @@
 			case "}":
 				// If no client-hostname was specified, generate one
 				if len(lease.ClientHostname) == 0 {
-					lease.ClientHostname = strings.ToUpper("UNK-" +
-						strings.Replace(lease.HardwareAddress.String(), ":", "", -1))
+					lease.ClientHostname = app.generateClientHostname(lease)
 				}
 				return nil
 			case "client-hostname":
 				lease.ClientHostname = strings.Trim(fields[1], "\";")
+
+				// Validate client-hostname
+				if _, ok := app.badClientNames[lease.ClientHostname]; ok {
+					lease.ClientHostname = app.generateClientHostname(lease)
+				}
 			case "hardware":
 				lease.HardwareAddress, err = net.ParseMAC(strings.Trim(fields[2], ";"))
 				if err != nil {
@@ -84,7 +106,7 @@
 }
 
 // parseLeaseFile parses the entire lease file
-func parseLeaseFile(filename string, filterFunc leaseFilterFunc) (map[string]*Lease, error) {
+func (app *application) parseLeaseFile(filename string, filterFunc leaseFilterFunc) (map[string]*Lease, error) {
 	leases := make(map[string]*Lease)
 
 	file, err := os.Open(filename)
@@ -100,7 +122,7 @@
 		if len(fields) > 0 && fields[0] == "lease" {
 			lease := Lease{}
 			lease.IPAddress = net.ParseIP(fields[1])
-			parseLease(scanner, &lease)
+			app.parseLease(scanner, &lease)
 			if filterFunc(&lease) {
 				leases[lease.IPAddress.String()] = &lease
 			}
@@ -136,7 +158,7 @@
 
 		// process the lease database
 		app.log.Infof("Synchronizing DHCP lease database")
-		leases, err := parseLeaseFile(app.DHCPLeaseFile,
+		leases, err := app.parseLeaseFile(app.DHCPLeaseFile,
 			func(lease *Lease) bool {
 				return lease.BindingState != Free &&
 					lease.Ends.After(now) &&