Merge pull request #9319 from arizvisa/GH-8528.refactor
This refactors the dhcpd lease parser in the vmware builders and adds unit tests for everything.
This commit is contained in:
commit
baed65156c
|
@ -376,7 +376,7 @@ func (d *VmwareDriver) GuestIP(state multistep.StateBag) (string, error) {
|
||||||
return "", fmt.Errorf("no DHCP leases path found for device %s", device)
|
return "", fmt.Errorf("no DHCP leases path found for device %s", device)
|
||||||
}
|
}
|
||||||
|
|
||||||
// open up the lease and read its contents
|
// open up the path to the dhcpd leases
|
||||||
fh, err := os.Open(dhcpLeasesPath)
|
fh, err := os.Open(dhcpLeasesPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error while reading DHCP lease path file %s: %s", dhcpLeasesPath, err.Error())
|
log.Printf("Error while reading DHCP lease path file %s: %s", dhcpLeasesPath, err.Error())
|
||||||
|
@ -384,11 +384,16 @@ func (d *VmwareDriver) GuestIP(state multistep.StateBag) (string, error) {
|
||||||
}
|
}
|
||||||
defer fh.Close()
|
defer fh.Close()
|
||||||
|
|
||||||
dhcpBytes, err := ioutil.ReadAll(fh)
|
// and then read its contents
|
||||||
|
leaseEntries, err := ReadDhcpdLeaseEntries(fh)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse our MAC address again. There's no need to check for an
|
||||||
|
// error because we've already parsed this successfully.
|
||||||
|
hwaddr, _ := net.ParseMAC(MACAddress)
|
||||||
|
|
||||||
// start grepping through the file looking for fields that we care about
|
// start grepping through the file looking for fields that we care about
|
||||||
var lastIp string
|
var lastIp string
|
||||||
var lastLeaseEnd time.Time
|
var lastLeaseEnd time.Time
|
||||||
|
@ -396,34 +401,19 @@ func (d *VmwareDriver) GuestIP(state multistep.StateBag) (string, error) {
|
||||||
var curIp string
|
var curIp string
|
||||||
var curLeaseEnd time.Time
|
var curLeaseEnd time.Time
|
||||||
|
|
||||||
ipLineRe := regexp.MustCompile(`^lease (.+?) {$`)
|
for _, entry := range leaseEntries {
|
||||||
endTimeLineRe := regexp.MustCompile(`^\s*ends \d (.+?);$`)
|
|
||||||
macLineRe := regexp.MustCompile(`^\s*hardware ethernet (.+?);$`)
|
|
||||||
|
|
||||||
for _, line := range strings.Split(string(dhcpBytes), "\n") {
|
lastIp = entry.address
|
||||||
// Need to trim off CR character when running in windows
|
lastLeaseEnd = entry.ends
|
||||||
line = strings.TrimRight(line, "\r")
|
|
||||||
|
|
||||||
matches := ipLineRe.FindStringSubmatch(line)
|
|
||||||
if matches != nil {
|
|
||||||
lastIp = matches[1]
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
matches = endTimeLineRe.FindStringSubmatch(line)
|
|
||||||
if matches != nil {
|
|
||||||
lastLeaseEnd, _ = time.Parse("2006/01/02 15:04:05", matches[1])
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the mac address matches and this lease ends farther in the
|
// If the mac address matches and this lease ends farther in the
|
||||||
// future than the last match we might have, then choose it.
|
// future than the last match we might have, then choose it.
|
||||||
matches = macLineRe.FindStringSubmatch(line)
|
if bytes.Equal(hwaddr, entry.ether) && curLeaseEnd.Before(lastLeaseEnd) {
|
||||||
if matches != nil && strings.EqualFold(matches[1], MACAddress) && curLeaseEnd.Before(lastLeaseEnd) {
|
|
||||||
curIp = lastIp
|
curIp = lastIp
|
||||||
curLeaseEnd = lastLeaseEnd
|
curLeaseEnd = lastLeaseEnd
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if curIp != "" {
|
if curIp != "" {
|
||||||
return curIp, nil
|
return curIp, nil
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -3,39 +3,43 @@ package common
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func consumeString(s string) (out chan byte, eos sentinelSignaller) {
|
func consumeString(s string) (out chan byte) {
|
||||||
eos = make(sentinelSignaller)
|
|
||||||
out = make(chan byte)
|
out = make(chan byte)
|
||||||
go func() {
|
go func() {
|
||||||
for _, ch := range s {
|
for _, ch := range s {
|
||||||
out <- byte(ch)
|
out <- byte(ch)
|
||||||
}
|
}
|
||||||
close(eos)
|
|
||||||
close(out)
|
close(out)
|
||||||
}()
|
}()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func collectIntoStringList(in chan string) (result []string) {
|
||||||
|
for item := range in {
|
||||||
|
result = append(result, item)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func uncommentFromString(s string) string {
|
func uncommentFromString(s string) string {
|
||||||
inCh, eos := consumeString(s)
|
inCh := consumeString(s)
|
||||||
out, eoc := uncomment(eos, inCh)
|
out := uncomment(inCh)
|
||||||
|
|
||||||
result := ""
|
result := ""
|
||||||
for reading := true; reading; {
|
for reading := true; reading; {
|
||||||
select {
|
if item, ok := <-out; !ok {
|
||||||
case <-eoc:
|
break
|
||||||
reading = false
|
} else {
|
||||||
case item, ok := <-out:
|
result += string(item)
|
||||||
if ok {
|
|
||||||
result += string(item)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
close(out)
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,21 +111,17 @@ func TestParserUncomment(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func tokenizeDhcpConfigFromString(s string) []string {
|
func tokenizeDhcpConfigFromString(s string) []string {
|
||||||
inCh, eos := consumeString(s)
|
inCh := consumeString(s)
|
||||||
out, eoc := tokenizeDhcpConfig(eos, inCh)
|
out := tokenizeDhcpConfig(inCh)
|
||||||
|
|
||||||
result := make([]string, 0)
|
result := make([]string, 0)
|
||||||
for reading := true; reading; {
|
for {
|
||||||
select {
|
if item, ok := <-out; !ok {
|
||||||
case <-eoc:
|
break
|
||||||
reading = false
|
} else {
|
||||||
case item, ok := <-out:
|
result = append(result, item)
|
||||||
if ok {
|
|
||||||
result = append(result, item)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
close(out)
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,16 +224,14 @@ func consumeDhcpConfig(items []string) (tkGroup, error) {
|
||||||
out := make(chan string)
|
out := make(chan string)
|
||||||
tch := consumeTokens(items)
|
tch := consumeTokens(items)
|
||||||
|
|
||||||
end := make(sentinelSignaller)
|
|
||||||
go func() {
|
go func() {
|
||||||
for item := range tch {
|
for item := range tch {
|
||||||
out <- item
|
out <- item
|
||||||
}
|
}
|
||||||
close(end)
|
|
||||||
close(out)
|
close(out)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return parseDhcpConfig(end, out)
|
return parseDhcpConfig(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
func compareSlice(a, b []string) bool {
|
func compareSlice(a, b []string) bool {
|
||||||
|
@ -492,3 +490,597 @@ func TestParserReadNetworkMap(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func collectIntoString(in chan byte) string {
|
||||||
|
result := ""
|
||||||
|
for item := range in {
|
||||||
|
result += string(item)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParserConsumeUntilSentinel(t *testing.T) {
|
||||||
|
|
||||||
|
test_1 := "consume until a semicolon; yeh?"
|
||||||
|
expected_1 := "consume until a semicolon"
|
||||||
|
|
||||||
|
ch := consumeString(test_1)
|
||||||
|
resultch, _ := consumeUntilSentinel(';', ch)
|
||||||
|
result := string(resultch)
|
||||||
|
if expected_1 != result {
|
||||||
|
t.Errorf("expected %#v, got %#v", expected_1, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
test_2 := "; this is only a semi"
|
||||||
|
expected_2 := ""
|
||||||
|
|
||||||
|
ch = consumeString(test_2)
|
||||||
|
resultch, _ = consumeUntilSentinel(';', ch)
|
||||||
|
result = string(resultch)
|
||||||
|
if expected_2 != result {
|
||||||
|
t.Errorf("expected %#v, got %#v", expected_2, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParserFilterCharacters(t *testing.T) {
|
||||||
|
|
||||||
|
test_1 := []string{" ", "ignore all spaces"}
|
||||||
|
expected_1 := "ignoreallspaces"
|
||||||
|
|
||||||
|
ch := consumeString(test_1[1])
|
||||||
|
outch := filterOutCharacters(bytes.NewBufferString(test_1[0]).Bytes(), ch)
|
||||||
|
result := collectIntoString(outch)
|
||||||
|
if result != expected_1 {
|
||||||
|
t.Errorf("expected %#v, got %#v", expected_1, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
test_2 := []string{"\n\v\t\r ", "ignore\nall\rwhite\v\v space "}
|
||||||
|
expected_2 := "ignoreallwhitespace"
|
||||||
|
|
||||||
|
ch = consumeString(test_2[1])
|
||||||
|
outch = filterOutCharacters(bytes.NewBufferString(test_2[0]).Bytes(), ch)
|
||||||
|
result = collectIntoString(outch)
|
||||||
|
if result != expected_2 {
|
||||||
|
t.Errorf("expected %#v, got %#v", expected_2, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParserConsumeOpenClosePair(t *testing.T) {
|
||||||
|
test_1 := "(everything)"
|
||||||
|
expected_1 := []string{"", test_1}
|
||||||
|
|
||||||
|
testch := consumeString(test_1)
|
||||||
|
prefix, ch := consumeOpenClosePair('(', ')', testch)
|
||||||
|
if string(prefix) != expected_1[0] {
|
||||||
|
t.Errorf("expected prefix %#v, got %#v", expected_1[0], prefix)
|
||||||
|
}
|
||||||
|
result := collectIntoString(ch)
|
||||||
|
if result != expected_1[1] {
|
||||||
|
t.Errorf("expected %#v, got %#v", expected_1[1], test_1)
|
||||||
|
}
|
||||||
|
|
||||||
|
test_2 := "prefixed (everything)"
|
||||||
|
expected_2 := []string{"prefixed ", "(everything)"}
|
||||||
|
|
||||||
|
testch = consumeString(test_2)
|
||||||
|
prefix, ch = consumeOpenClosePair('(', ')', testch)
|
||||||
|
if string(prefix) != expected_2[0] {
|
||||||
|
t.Errorf("expected prefix %#v, got %#v", expected_2[0], prefix)
|
||||||
|
}
|
||||||
|
result = collectIntoString(ch)
|
||||||
|
if result != expected_2[1] {
|
||||||
|
t.Errorf("expected %#v, got %#v", expected_2[1], test_2)
|
||||||
|
}
|
||||||
|
|
||||||
|
test_3 := "this(is()suffixed"
|
||||||
|
expected_3 := []string{"this", "(is()"}
|
||||||
|
|
||||||
|
testch = consumeString(test_3)
|
||||||
|
prefix, ch = consumeOpenClosePair('(', ')', testch)
|
||||||
|
if string(prefix) != expected_3[0] {
|
||||||
|
t.Errorf("expected prefix %#v, got %#v", expected_3[0], prefix)
|
||||||
|
}
|
||||||
|
result = collectIntoString(ch)
|
||||||
|
if result != expected_3[1] {
|
||||||
|
t.Errorf("expected %#v, got %#v", expected_3[1], test_2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParserCombinators(t *testing.T) {
|
||||||
|
|
||||||
|
test_1 := "across # ignore\nmultiple lines;"
|
||||||
|
expected_1 := "across multiple lines"
|
||||||
|
|
||||||
|
ch := consumeString(test_1)
|
||||||
|
inch := uncomment(ch)
|
||||||
|
whch := filterOutCharacters([]byte{'\n'}, inch)
|
||||||
|
resultch, _ := consumeUntilSentinel(';', whch)
|
||||||
|
result := string(resultch)
|
||||||
|
if expected_1 != result {
|
||||||
|
t.Errorf("expected %#v, got %#v", expected_1, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
test_2 := "lease blah {\n blah\r\n# skipping this line\nblahblah # ignore semicolon;\n last item;\n\n };;;;;;"
|
||||||
|
expected_2 := []string{"lease blah ", "{ blahblahblah last item; }"}
|
||||||
|
|
||||||
|
ch = consumeString(test_2)
|
||||||
|
inch = uncomment(ch)
|
||||||
|
whch = filterOutCharacters([]byte{'\n', '\v', '\r'}, inch)
|
||||||
|
prefix, pairch := consumeOpenClosePair('{', '}', whch)
|
||||||
|
|
||||||
|
result = collectIntoString(pairch)
|
||||||
|
if string(prefix) != expected_2[0] {
|
||||||
|
t.Errorf("expected prefix %#v, got %#v", expected_2[0], prefix)
|
||||||
|
}
|
||||||
|
if result != expected_2[1] {
|
||||||
|
t.Errorf("expected %#v, got %#v", expected_2[1], result)
|
||||||
|
}
|
||||||
|
|
||||||
|
test_3 := "lease blah { # comment\n item 1;\n item 2;\n } not imortant"
|
||||||
|
expected_3_prefix := "lease blah "
|
||||||
|
expected_3 := []string{"{ item 1", " item 2", " }"}
|
||||||
|
|
||||||
|
sch := consumeString(test_3)
|
||||||
|
inch = uncomment(sch)
|
||||||
|
wch := filterOutCharacters([]byte{'\n', '\v', '\r'}, inch)
|
||||||
|
lease, itemch := consumeOpenClosePair('{', '}', wch)
|
||||||
|
if string(lease) != expected_3_prefix {
|
||||||
|
t.Errorf("expected %#v, got %#v", expected_3_prefix, string(lease))
|
||||||
|
}
|
||||||
|
|
||||||
|
result_3 := []string{}
|
||||||
|
for reading := true; reading; {
|
||||||
|
item, ok := consumeUntilSentinel(';', itemch)
|
||||||
|
result_3 = append(result_3, string(item))
|
||||||
|
if !ok {
|
||||||
|
reading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for index := range expected_3 {
|
||||||
|
if expected_3[index] != result_3[index] {
|
||||||
|
t.Errorf("expected index %d as %#v, got %#v", index, expected_3[index], result_3[index])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParserDhcpdLeaseBytesDecoder(t *testing.T) {
|
||||||
|
test_1 := "00:0d:0e:0a:0d:00"
|
||||||
|
expected_1 := []byte{0, 13, 14, 10, 13, 0}
|
||||||
|
|
||||||
|
result, err := decodeDhcpdLeaseBytes(test_1)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unable to decode address: %s", err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(result, expected_1) {
|
||||||
|
t.Errorf("expected %v, got %v", expected_1, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
test_2 := "11"
|
||||||
|
expected_2 := []byte{17}
|
||||||
|
|
||||||
|
result, err = decodeDhcpdLeaseBytes(test_2)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unable to decode address: %s", err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(result, expected_2) {
|
||||||
|
t.Errorf("expected %v, got %v", expected_2, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
failtest_1 := ""
|
||||||
|
_, err = decodeDhcpdLeaseBytes(failtest_1)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected decoding error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
failtest_2 := "000000"
|
||||||
|
_, err = decodeDhcpdLeaseBytes(failtest_2)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected decoding error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
failtest_3 := "000:00"
|
||||||
|
_, err = decodeDhcpdLeaseBytes(failtest_3)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected decoding error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
failtest_4 := "00:00:"
|
||||||
|
_, err = decodeDhcpdLeaseBytes(failtest_4)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected decoding error: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func consumeLeaseString(s string) chan byte {
|
||||||
|
sch := consumeString(s)
|
||||||
|
uncommentedch := uncomment(sch)
|
||||||
|
return filterOutCharacters([]byte{'\n', '\r', '\v'}, uncommentedch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParserReadDhcpdLeaseEntry(t *testing.T) {
|
||||||
|
test_1 := "lease 127.0.0.1 {\nhardware ethernet 00:11:22:33 ;\nuid 00:11 ;\n }"
|
||||||
|
expected_1 := map[string]string{
|
||||||
|
"address": "127.0.0.1",
|
||||||
|
"ether": "00112233",
|
||||||
|
"uid": "0011",
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := readDhcpdLeaseEntry(consumeLeaseString(test_1))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error parsing entry: %v", err)
|
||||||
|
}
|
||||||
|
if result.address != expected_1["address"] {
|
||||||
|
t.Errorf("expected address %v, got %v", expected_1["address"], result.address)
|
||||||
|
}
|
||||||
|
if hex.EncodeToString(result.ether) != expected_1["ether"] {
|
||||||
|
t.Errorf("expected ether %v, got %v", expected_1["ether"], hex.EncodeToString(result.ether))
|
||||||
|
}
|
||||||
|
if hex.EncodeToString(result.uid) != expected_1["uid"] {
|
||||||
|
t.Errorf("expected uid %v, got %v", expected_1["uid"], hex.EncodeToString(result.uid))
|
||||||
|
}
|
||||||
|
|
||||||
|
test_2 := " \n\t lease 192.168.21.254{ hardware\n ethernet 44:55:66:77:88:99;uid 00:1\n1:22:3\r3:44;\n starts 57005 2006/01/02 15:04:05;ends 57005 2006/01/03 15:04:05;\tunknown item1; unknown item2; } "
|
||||||
|
expected_2 := map[string]string{
|
||||||
|
"address": "192.168.21.254",
|
||||||
|
"ether": "445566778899",
|
||||||
|
"uid": "0011223344",
|
||||||
|
"starts": "2006-01-02 15:04:05 +0000 UTC",
|
||||||
|
"ends": "2006-01-03 15:04:05 +0000 UTC",
|
||||||
|
}
|
||||||
|
result, err = readDhcpdLeaseEntry(consumeLeaseString(test_2))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error parsing entry: %v", err)
|
||||||
|
}
|
||||||
|
if result.address != expected_2["address"] {
|
||||||
|
t.Errorf("expected address %v, got %v", expected_2["address"], result.address)
|
||||||
|
}
|
||||||
|
if hex.EncodeToString(result.ether) != expected_2["ether"] {
|
||||||
|
t.Errorf("expected ether %v, got %v", expected_2["ether"], hex.EncodeToString(result.ether))
|
||||||
|
}
|
||||||
|
if hex.EncodeToString(result.uid) != expected_2["uid"] {
|
||||||
|
t.Errorf("expected uid %v, got %v", expected_2["uid"], hex.EncodeToString(result.uid))
|
||||||
|
}
|
||||||
|
if result.starts.String() != expected_2["starts"] {
|
||||||
|
t.Errorf("expected starts %v, got %v", expected_2["starts"], result.starts)
|
||||||
|
}
|
||||||
|
if result.ends.String() != expected_2["ends"] {
|
||||||
|
t.Errorf("expected ends %v, got %v", expected_2["ends"], result.ends)
|
||||||
|
}
|
||||||
|
if result.starts_weekday != 57005 {
|
||||||
|
t.Errorf("expected starts weekday %v, got %v", 57005, result.starts_weekday)
|
||||||
|
}
|
||||||
|
if result.ends_weekday != 57005 {
|
||||||
|
t.Errorf("expected ends weekday %v, got %v", 57005, result.ends_weekday)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParserReadDhcpdLeases(t *testing.T) {
|
||||||
|
f, err := os.Open(filepath.Join("testdata", "dhcpd-example.leases"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unable to open dhcpd.leases sample: %s", err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
results, err := ReadDhcpdLeaseEntries(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error reading lease: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// some simple utilities
|
||||||
|
filter_address := func(address string, items []dhcpLeaseEntry) (result []dhcpLeaseEntry) {
|
||||||
|
for _, item := range items {
|
||||||
|
if item.address == address {
|
||||||
|
result = append(result, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
find_uid := func(uid string, items []dhcpLeaseEntry) *dhcpLeaseEntry {
|
||||||
|
for _, item := range items {
|
||||||
|
if uid == hex.EncodeToString(item.uid) {
|
||||||
|
return &item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
find_ether := func(ether string, items []dhcpLeaseEntry) *dhcpLeaseEntry {
|
||||||
|
for _, item := range items {
|
||||||
|
if ether == hex.EncodeToString(item.ether) {
|
||||||
|
return &item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// actual unit tests
|
||||||
|
test_1 := map[string]string{
|
||||||
|
"address": "127.0.0.19",
|
||||||
|
"uid": "010dead099aabb",
|
||||||
|
"ether": "0dead099aabb",
|
||||||
|
}
|
||||||
|
test_1_findings := filter_address(test_1["address"], results)
|
||||||
|
if len(test_1_findings) != 2 {
|
||||||
|
t.Errorf("expected %d matching entries, got %d", 2, len(test_1_findings))
|
||||||
|
} else {
|
||||||
|
res := find_ether(test_1["ether"], test_1_findings)
|
||||||
|
if res == nil {
|
||||||
|
t.Errorf("unable to find item with ether %v", test_1["ether"])
|
||||||
|
} else if hex.EncodeToString(res.uid) != test_1["uid"] {
|
||||||
|
t.Errorf("expected uid %s, got %s", test_1["uid"], hex.EncodeToString(res.uid))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test_2 := map[string]string{
|
||||||
|
"address": "127.0.0.19",
|
||||||
|
"uid": "010dead0667788",
|
||||||
|
"ether": "0dead0667788",
|
||||||
|
}
|
||||||
|
test_2_findings := filter_address(test_2["address"], results)
|
||||||
|
if len(test_2_findings) != 2 {
|
||||||
|
t.Errorf("expected %d matching entries, got %d", 2, len(test_2_findings))
|
||||||
|
} else {
|
||||||
|
res := find_ether(test_2["ether"], test_2_findings)
|
||||||
|
if res == nil {
|
||||||
|
t.Errorf("unable to find item with ether %v", test_2["ether"])
|
||||||
|
} else if hex.EncodeToString(res.uid) != test_2["uid"] {
|
||||||
|
t.Errorf("expected uid %s, got %s", test_2["uid"], hex.EncodeToString(res.uid))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test_3 := map[string]string{
|
||||||
|
"address": "127.0.0.17",
|
||||||
|
"uid": "010dead0334455",
|
||||||
|
"ether": "0dead0667788",
|
||||||
|
}
|
||||||
|
test_3_findings := filter_address(test_3["address"], results)
|
||||||
|
if len(test_3_findings) != 2 {
|
||||||
|
t.Errorf("expected %d matching entries, got %d", 2, len(test_3_findings))
|
||||||
|
} else {
|
||||||
|
res := find_uid(test_3["uid"], test_3_findings)
|
||||||
|
if res == nil {
|
||||||
|
t.Errorf("unable to find item with uid %v", test_3["uid"])
|
||||||
|
} else if hex.EncodeToString(res.ether) != test_3["ether"] {
|
||||||
|
t.Errorf("expected ethernet hardware %s, got %s", test_3["ether"], hex.EncodeToString(res.ether))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test_4 := map[string]string{
|
||||||
|
"address": "127.0.0.17",
|
||||||
|
"uid": "010dead0001122",
|
||||||
|
"ether": "0dead0667788",
|
||||||
|
}
|
||||||
|
test_4_findings := filter_address(test_4["address"], results)
|
||||||
|
if len(test_4_findings) != 2 {
|
||||||
|
t.Errorf("expected %d matching entries, got %d", 2, len(test_4_findings))
|
||||||
|
} else {
|
||||||
|
res := find_uid(test_4["uid"], test_4_findings)
|
||||||
|
if res == nil {
|
||||||
|
t.Errorf("unable to find item with uid %v", test_4["uid"])
|
||||||
|
} else if hex.EncodeToString(res.ether) != test_4["ether"] {
|
||||||
|
t.Errorf("expected ethernet hardware %s, got %s", test_4["ether"], hex.EncodeToString(res.ether))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParserTokenizeNetworkingConfig(t *testing.T) {
|
||||||
|
tests := []string{
|
||||||
|
"words words words",
|
||||||
|
"newlines\n\n\n\n\n\n\n\nnewlines\r\r\r\r\r\r\r\rnewlines\n\n\n\n",
|
||||||
|
" newline-less",
|
||||||
|
}
|
||||||
|
expects := [][]string{
|
||||||
|
[]string{"words", "words", "words"},
|
||||||
|
[]string{"newlines", "\n", "newlines", "\n", "newlines", "\n"},
|
||||||
|
[]string{"newline-less"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for testnum := 0; testnum < len(tests); testnum += 1 {
|
||||||
|
inCh := consumeString(tests[testnum])
|
||||||
|
outCh := tokenizeNetworkingConfig(inCh)
|
||||||
|
result := collectIntoStringList(outCh)
|
||||||
|
|
||||||
|
expected := expects[testnum]
|
||||||
|
if len(result) != len(expected) {
|
||||||
|
t.Errorf("test %d expected %d items, got %d instead", 1+testnum, len(expected), len(result))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ok := true
|
||||||
|
for index := 0; index < len(expected); index += 1 {
|
||||||
|
if result[index] != expected[index] {
|
||||||
|
ok = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("test %d expected %#v, got %#v", 1+testnum, expects[testnum], result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParserSplitNetworkingConfig(t *testing.T) {
|
||||||
|
tests := []string{
|
||||||
|
"this is a story\n\n\nabout some newlines",
|
||||||
|
"\n\n\nthat can begin and end with newlines\n\n\n",
|
||||||
|
" in\n\n\nsome\ncases\nit\ncan\nend\nwith\nan\nempty\nstring\n\n\n\n",
|
||||||
|
"\n\n\nand\nbegin\nwith\nan\nempty\nstring ",
|
||||||
|
}
|
||||||
|
expects := [][]string{
|
||||||
|
[]string{"this is a story", "about some newlines"},
|
||||||
|
[]string{"that can begin and end with newlines"},
|
||||||
|
[]string{"in", "some", "cases", "it", "can", "end", "with", "an", "empty", "string"},
|
||||||
|
[]string{"and", "begin", "with", "an", "empty", "string"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for testnum := 0; testnum < len(tests); testnum += 1 {
|
||||||
|
inCh := consumeString(tests[testnum])
|
||||||
|
stringCh := tokenizeNetworkingConfig(inCh)
|
||||||
|
outCh := splitNetworkingConfig(stringCh)
|
||||||
|
|
||||||
|
result := make([]string, 0)
|
||||||
|
for item := range outCh {
|
||||||
|
result = append(result, strings.Join(item, " "))
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := expects[testnum]
|
||||||
|
if len(result) != len(expected) {
|
||||||
|
t.Errorf("test %d expected %d items, got %d instead", 1+testnum, len(expected), len(result))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ok := true
|
||||||
|
for index := 0; index < len(expected); index += 1 {
|
||||||
|
if result[index] != expected[index] {
|
||||||
|
ok = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("test %d expected %#v, got %#v", 1+testnum, expects[testnum], result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParserParseNetworkingConfigVersion(t *testing.T) {
|
||||||
|
success_tests := []string{"VERSION=4,2"}
|
||||||
|
failure_tests := []string{
|
||||||
|
"VERSION=1=2",
|
||||||
|
"VERSION=3,4,5",
|
||||||
|
"VERSION=a,b",
|
||||||
|
}
|
||||||
|
|
||||||
|
for testnum := 0; testnum < len(success_tests); testnum += 1 {
|
||||||
|
test := []string{success_tests[testnum]}
|
||||||
|
if _, err := networkingReadVersion(test); err != nil {
|
||||||
|
t.Errorf("success-test %d parsing failed: %v", 1+testnum, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for testnum := 0; testnum < len(success_tests); testnum += 1 {
|
||||||
|
test := []string{failure_tests[testnum]}
|
||||||
|
if _, err := networkingReadVersion(test); err == nil {
|
||||||
|
t.Errorf("failure-test %d should have failed", 1+testnum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParserParseNetworkingConfigEntries(t *testing.T) {
|
||||||
|
tests := []string{
|
||||||
|
"answer VNET_999_ANYTHING option",
|
||||||
|
"remove_answer VNET_123_ALSOANYTHING",
|
||||||
|
"add_nat_portfwd 24 udp 42 127.0.0.1 24",
|
||||||
|
"remove_nat_portfwd 42 tcp 2502",
|
||||||
|
"add_dhcp_mac_to_ip 57005 00:0d:0e:0a:0d:00 127.0.0.2",
|
||||||
|
"remove_dhcp_mac_to_ip 57005 00:0d:0e:0a:0d:00",
|
||||||
|
"add_bridge_mapping string 51",
|
||||||
|
"remove_bridge_mapping string",
|
||||||
|
"add_nat_prefix 57005 /24",
|
||||||
|
"remove_nat_prefix 57005 /31",
|
||||||
|
}
|
||||||
|
|
||||||
|
for testnum := 0; testnum < len(tests); testnum += 1 {
|
||||||
|
test := strings.Split(tests[testnum], " ")
|
||||||
|
parser := NetworkingParserByCommand(test[0])
|
||||||
|
if parser == nil {
|
||||||
|
t.Errorf("test %d unable to parse command: %#v", 1+testnum, test)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
operand_parser := *parser
|
||||||
|
|
||||||
|
_, err := operand_parser(test[1:])
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("test %d unable to parse command parameters %#v: %v", 1+testnum, test, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParserReadNetworingConfig(t *testing.T) {
|
||||||
|
expected_answer_vnet_1 := map[string]string{
|
||||||
|
"DHCP": "yes",
|
||||||
|
"DHCP_CFG_HASH": "01F4CE0D79A1599698B6E5814CCB68058BB0ED5E",
|
||||||
|
"HOSTONLY_NETMASK": "255.255.255.0",
|
||||||
|
"HOSTONLY_SUBNET": "192.168.70.0",
|
||||||
|
"NAT": "no",
|
||||||
|
"VIRTUAL_ADAPTER": "yes",
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.Open(filepath.Join("testdata", "networking-example"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unable to open networking-example sample: %v", err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
config, err := ReadNetworkingConfig(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error parsing networking-example: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if vnet, ok := config.answer[1]; ok {
|
||||||
|
for ans_key := range expected_answer_vnet_1 {
|
||||||
|
result, ok := vnet[ans_key]
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("unable to find key %s in VNET_%d answer", ans_key, 1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if result != expected_answer_vnet_1[ans_key] {
|
||||||
|
t.Errorf("expected key %s for VNET_%d to be %v, got %v", ans_key, 1, expected_answer_vnet_1[ans_key], result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
t.Errorf("unable to find VNET_%d answer", 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected_answer_vnet_8 := map[string]string{
|
||||||
|
"DHCP": "yes",
|
||||||
|
"DHCP_CFG_HASH": "C30F14F65A0FE4B5DCC6C67497D7A8A33E5E538C",
|
||||||
|
"HOSTONLY_NETMASK": "255.255.255.0",
|
||||||
|
"HOSTONLY_SUBNET": "172.16.41.0",
|
||||||
|
"NAT": "yes",
|
||||||
|
"VIRTUAL_ADAPTER": "yes",
|
||||||
|
}
|
||||||
|
|
||||||
|
if vnet, ok := config.answer[8]; ok {
|
||||||
|
for ans_key := range expected_answer_vnet_8 {
|
||||||
|
result, ok := vnet[ans_key]
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("unable to find key %s in VNET_%d answer", ans_key, 8)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if result != expected_answer_vnet_8[ans_key] {
|
||||||
|
t.Errorf("expected key %s for VNET_%d to be %v, got %v", ans_key, 8, expected_answer_vnet_8[ans_key], result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
t.Errorf("unable to find VNET_%d answer", 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
expected_nat_portfwd_8 := map[string]string{
|
||||||
|
"tcp/2200": "172.16.41.129:3389",
|
||||||
|
"tcp/2201": "172.16.41.129:3389",
|
||||||
|
"tcp/2222": "172.16.41.129:22",
|
||||||
|
"tcp/3389": "172.16.41.131:3389",
|
||||||
|
"tcp/55985": "172.16.41.129:5985",
|
||||||
|
"tcp/55986": "172.16.41.129:5986",
|
||||||
|
}
|
||||||
|
|
||||||
|
if vnet, ok := config.nat_portfwd[8-1]; ok {
|
||||||
|
for nat_key := range expected_nat_portfwd_8 {
|
||||||
|
result, ok := vnet[nat_key]
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("unable to find key %s in VNET_%d nat_portfwd", nat_key, 8)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if result != expected_nat_portfwd_8[nat_key] {
|
||||||
|
t.Errorf("expected key %s for VNET_%d to be %v, got %v", nat_key, 8, expected_nat_portfwd_8[nat_key], result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Errorf("unable to find VNET_%d answer", 8-1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
# This entry is normal
|
||||||
|
lease 127.0.0.17 {
|
||||||
|
starts 3 2020/05/13 12:00:37;
|
||||||
|
ends 3 2020/05/13 12:30:37;
|
||||||
|
hardware ethernet 0d:ea:d0:66:77:88;
|
||||||
|
uid 01:0d:ea:d0:00:11:22;
|
||||||
|
}
|
||||||
|
|
||||||
|
# This entry has tabs
|
||||||
|
lease 127.0.0.17 {
|
||||||
|
starts 6 2020/06/12 22:28:54;
|
||||||
|
ends 6 2020/06/12 22:58:54;
|
||||||
|
hardware ethernet 0d:ea:d0:66:77:88;
|
||||||
|
uid 01:0d:ea:d0:33:44:55;
|
||||||
|
}
|
||||||
|
|
||||||
|
# These next two entries have the same address, but different uids
|
||||||
|
lease 127.0.0.19 {
|
||||||
|
starts 4 2020/05/28 11:35:06;
|
||||||
|
ends 4 2020/05/28 12:05:06;
|
||||||
|
hardware ethernet 0d:ea:d0:66:77:88;
|
||||||
|
uid 01:0d:ea:d0:66:77:88;
|
||||||
|
client-hostname "VAGRANT-ADA-LUZ";
|
||||||
|
}
|
||||||
|
|
||||||
|
lease 127.0.0.19 {
|
||||||
|
starts 5 2020/08/20 20:32:03;
|
||||||
|
ends 5 2020/08/20 21:02:03;
|
||||||
|
hardware ethernet 0d:ea:d0:99:aa:bb;
|
||||||
|
uid 01:0d:ea:d0:99:aa:bb;
|
||||||
|
client-hostname "WINDOWS-SQABBAS";
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
VERSION=1,0
|
||||||
|
answer VNET_1_DHCP yes
|
||||||
|
answer VNET_1_DHCP_CFG_HASH 01F4CE0D79A1599698B6E5814CCB68058BB0ED5E
|
||||||
|
answer VNET_1_HOSTONLY_NETMASK 255.255.255.0
|
||||||
|
answer VNET_1_HOSTONLY_SUBNET 192.168.70.0
|
||||||
|
answer VNET_1_NAT no
|
||||||
|
answer VNET_1_VIRTUAL_ADAPTER yes
|
||||||
|
answer VNET_8_DHCP yes
|
||||||
|
answer VNET_8_DHCP_CFG_HASH C30F14F65A0FE4B5DCC6C67497D7A8A33E5E538C
|
||||||
|
answer VNET_8_HOSTONLY_NETMASK 255.255.255.0
|
||||||
|
answer VNET_8_HOSTONLY_SUBNET 172.16.41.0
|
||||||
|
answer VNET_8_NAT yes
|
||||||
|
answer VNET_8_VIRTUAL_ADAPTER yes
|
||||||
|
add_nat_portfwd 8 tcp 2200 172.16.41.129 3389
|
||||||
|
add_nat_portfwd 8 tcp 2201 172.16.41.129 3389
|
||||||
|
add_nat_portfwd 8 tcp 2222 172.16.41.129 22
|
||||||
|
add_nat_portfwd 8 tcp 3389 172.16.41.131 3389
|
||||||
|
add_nat_portfwd 8 tcp 55985 172.16.41.129 5985
|
||||||
|
add_nat_portfwd 8 tcp 55986 172.16.41.129 5986
|
Loading…
Reference in New Issue