make packer compatible MacOS BigSur by making vmware fusion drivers able to lookup the VM IP address in apple dhcpd leases instead of vmware leases.
This commit is contained in:
parent
d8277aa455
commit
7201ce9248
|
@ -430,6 +430,56 @@ func (d *VmwareDriver) PotentialGuestIP(state multistep.StateBag) ([]string, err
|
||||||
return addrs, nil
|
return addrs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if runtime.GOOS == "darwin" {
|
||||||
|
// We have match no vmware DHCP lease for this MAC. We'll try to match it in Apple DHCP leases.
|
||||||
|
// As a remember, VMWare is no longer able to rely on its own dhcpd server on MacOS BigSur and is
|
||||||
|
// forced to use Apple DHCPD server instead.
|
||||||
|
// https://communities.vmware.com/t5/VMware-Fusion-Discussions/Big-Sur-hosts-with-Fusion-Is-vmnet-dhcpd-vmnet8-leases-file/m-p/2298927/highlight/true#M140003
|
||||||
|
|
||||||
|
// set the apple dhcp leases path
|
||||||
|
appleDhcpLeasesPath := "/var/db/dhcpd_leases"
|
||||||
|
log.Printf("Trying Apple DHCP leases path: %s", appleDhcpLeasesPath)
|
||||||
|
|
||||||
|
// open up the path to the apple dhcpd leases
|
||||||
|
fh, err := os.Open(appleDhcpLeasesPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error while reading apple DHCP lease path file %s: %s", appleDhcpLeasesPath, err.Error())
|
||||||
|
} else {
|
||||||
|
defer fh.Close()
|
||||||
|
|
||||||
|
// and then read its contents
|
||||||
|
leaseEntries, err := ReadAppleDhcpdLeaseEntries(fh)
|
||||||
|
if err != nil {
|
||||||
|
return []string{}, 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)
|
||||||
|
|
||||||
|
// Go through our available lease entries and see which ones are within
|
||||||
|
// scope, and that match to our hardware address.
|
||||||
|
available_lease_entries := make([]appleDhcpLeaseEntry, 0)
|
||||||
|
for _, entry := range leaseEntries {
|
||||||
|
// Next check for any where the hardware address matches.
|
||||||
|
if bytes.Equal(hwaddr, []byte(entry.hwAddress)) {
|
||||||
|
available_lease_entries = append(available_lease_entries, entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we found any lease entries that correspond to us. If so, then we
|
||||||
|
// need to map() them in order to extract the address field to return to the
|
||||||
|
// caller.
|
||||||
|
if len(available_lease_entries) > 0 {
|
||||||
|
addrs := make([]string, 0)
|
||||||
|
for _, entry := range available_lease_entries {
|
||||||
|
addrs = append(addrs, entry.ipAddress)
|
||||||
|
}
|
||||||
|
return addrs, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return []string{}, fmt.Errorf("None of the found device(s) %v has a DHCP lease for MAC %s", devices, MACAddress)
|
return []string{}, fmt.Errorf("None of the found device(s) %v has a DHCP lease for MAC %s", devices, MACAddress)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2430,3 +2430,144 @@ func ReadDhcpdLeaseEntries(fd *os.File) ([]dhcpLeaseEntry, error) {
|
||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*** Apple Dhcp Leases */
|
||||||
|
|
||||||
|
// Here is what an Apple DHCPD lease entry looks like:
|
||||||
|
// {
|
||||||
|
// ip_address=192.168.111.2
|
||||||
|
// hw_address=1,0:50:56:20:ac:33
|
||||||
|
// identifier=1,0:50:56:20:ac:33
|
||||||
|
// lease=0x5fd72edc
|
||||||
|
// name=vagrant-2019
|
||||||
|
// }
|
||||||
|
|
||||||
|
type appleDhcpLeaseEntry struct {
|
||||||
|
ipAddress string
|
||||||
|
hwAddress, id []byte
|
||||||
|
lease string
|
||||||
|
name string
|
||||||
|
extra map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func readAppleDhcpdLeaseEntry(in chan byte) (entry *appleDhcpLeaseEntry, err error) {
|
||||||
|
entry = &appleDhcpLeaseEntry{extra: map[string]string{}}
|
||||||
|
validFieldCount := 0
|
||||||
|
// Read up to the lease item and validate that it actually matches
|
||||||
|
_, ch := consumeOpenClosePair('{', '}', in)
|
||||||
|
for insideBraces := true; insideBraces; {
|
||||||
|
item, ok := consumeUntilSentinel('\n', ch)
|
||||||
|
item_s := strings.TrimSpace(string(item))
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
insideBraces = false
|
||||||
|
}
|
||||||
|
if item_s == "{" || item_s == "}" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
splittedLine := strings.Split(item_s, "=")
|
||||||
|
var key, val string
|
||||||
|
switch len(splittedLine) {
|
||||||
|
case 0:
|
||||||
|
// should never happens as Split always returns at least 1 item
|
||||||
|
fallthrough
|
||||||
|
case 1:
|
||||||
|
log.Printf("Error parsing invalid line: `%s`", item_s)
|
||||||
|
continue
|
||||||
|
case 2:
|
||||||
|
key = strings.TrimSpace(splittedLine[0])
|
||||||
|
val = strings.TrimSpace(splittedLine[1])
|
||||||
|
default:
|
||||||
|
// There were more than one '=' on this line, we'll keep the part before the first '=' as the key and
|
||||||
|
// the rest will be the value
|
||||||
|
key = strings.TrimSpace(splittedLine[0])
|
||||||
|
val = strings.TrimSpace(strings.Join(splittedLine[1:], "="))
|
||||||
|
}
|
||||||
|
switch key {
|
||||||
|
case "ip_address":
|
||||||
|
entry.ipAddress = val
|
||||||
|
validFieldCount++
|
||||||
|
case "identifier":
|
||||||
|
fallthrough
|
||||||
|
case "hw_address":
|
||||||
|
if strings.Count(val, ",") != 1 {
|
||||||
|
log.Printf("Error %s `%s` is not properly formatted for entry %s", key, val, entry.name)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
splittedVal := strings.Split(val, ",")
|
||||||
|
mac := splittedVal[1]
|
||||||
|
splittedMac := strings.Split(mac, ":")
|
||||||
|
// Pad the retrieved hw address with '0' when necessary
|
||||||
|
for idx := range splittedMac {
|
||||||
|
if len(splittedMac[idx]) == 1 {
|
||||||
|
splittedMac[idx] = "0" + splittedMac[idx]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mac = strings.Join(splittedMac, ":")
|
||||||
|
decodedLease, err := decodeDhcpdLeaseBytes(mac)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error trying to parse %s (%v) for entry %s - %v", key, val, entry.name, mac)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if key == "identifier" {
|
||||||
|
entry.id = decodedLease
|
||||||
|
} else {
|
||||||
|
entry.hwAddress = decodedLease
|
||||||
|
}
|
||||||
|
validFieldCount++
|
||||||
|
case "lease":
|
||||||
|
entry.lease = val
|
||||||
|
validFieldCount++
|
||||||
|
case "name":
|
||||||
|
entry.name = val
|
||||||
|
validFieldCount++
|
||||||
|
default:
|
||||||
|
// Just stash it for now because we have no idea what it is.
|
||||||
|
entry.extra[key] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// we have most likely parsed the whole file
|
||||||
|
if validFieldCount == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
// an entry is composed of 5 mandatory fields, we'll check that they all have been set during the parsing
|
||||||
|
if validFieldCount < 5 {
|
||||||
|
return entry, fmt.Errorf("Error entry `%v` is missing mandatory information", entry)
|
||||||
|
}
|
||||||
|
return entry, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadAppleDhcpdLeaseEntries(fd *os.File) ([]appleDhcpLeaseEntry, error) {
|
||||||
|
fch := consumeFile(fd)
|
||||||
|
uncommentedch := uncomment(fch)
|
||||||
|
wch := filterOutCharacters([]byte{'\r', '\v'}, uncommentedch)
|
||||||
|
|
||||||
|
result := make([]appleDhcpLeaseEntry, 0)
|
||||||
|
errors := make([]error, 0)
|
||||||
|
|
||||||
|
// Consume apple dhcpd lease entries from the channel until we just plain run out.
|
||||||
|
for i := 0; ; i++ {
|
||||||
|
if entry, err := readAppleDhcpdLeaseEntry(wch); entry == nil {
|
||||||
|
// If our entry is nil, then we've run out of input and finished
|
||||||
|
// parsing the file to completion.
|
||||||
|
break
|
||||||
|
|
||||||
|
} else if err != nil {
|
||||||
|
// If we received an error, then log it and keep track of it. This
|
||||||
|
// way we can warn the user later which entries we had issues with.
|
||||||
|
log.Printf("Error parsing apple dhcpd lease entry #%d: %s", 1+i, err)
|
||||||
|
errors = append(errors, err)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// If we've parsed an entry successfully, then aggregate it to
|
||||||
|
// our slice of results.
|
||||||
|
result = append(result, *entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we received any errors then include alongside our results.
|
||||||
|
if len(errors) > 0 {
|
||||||
|
return result, fmt.Errorf("Errors found while parsing apple dhcpd lease entries: %v", errors)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
|
@ -865,6 +865,166 @@ func TestParserReadDhcpdLeases(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func consumeAppleLeaseString(s string) chan byte {
|
||||||
|
sch := consumeString(s)
|
||||||
|
uncommentedch := uncomment(sch)
|
||||||
|
return filterOutCharacters([]byte{'\r', '\v'}, uncommentedch)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParserReadAppleDhcpdLeaseEntry(t *testing.T) {
|
||||||
|
test_1 := `{
|
||||||
|
ip_address=192.168.111.3
|
||||||
|
hw_address=1,0:c:56:3c:e7:22
|
||||||
|
identifier=1,0:c:56:3c:e7:22
|
||||||
|
lease=0x5fd78ae2
|
||||||
|
name=vagrant-2019
|
||||||
|
fake=field
|
||||||
|
}`
|
||||||
|
expected_1 := map[string]string{
|
||||||
|
"ipAddress": "192.168.111.3",
|
||||||
|
"hwAddress": "000c563ce722",
|
||||||
|
"id": "000c563ce722",
|
||||||
|
"lease": "0x5fd78ae2",
|
||||||
|
"name": "vagrant-2019",
|
||||||
|
}
|
||||||
|
expected_extra_1 := map[string]string{
|
||||||
|
"fake": "field",
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := readAppleDhcpdLeaseEntry(consumeAppleLeaseString(test_1))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error parsing entry: %v", err)
|
||||||
|
}
|
||||||
|
if result.ipAddress != expected_1["ipAddress"] {
|
||||||
|
t.Errorf("expected ipAddress %v, got %v", expected_1["ipAddress"], result.ipAddress)
|
||||||
|
}
|
||||||
|
if hex.EncodeToString(result.hwAddress) != expected_1["hwAddress"] {
|
||||||
|
t.Errorf("expected hwAddress %v, got %v", expected_1["hwAddress"], hex.EncodeToString(result.hwAddress))
|
||||||
|
}
|
||||||
|
if hex.EncodeToString(result.id) != expected_1["id"] {
|
||||||
|
t.Errorf("expected id %v, got %v", expected_1["id"], hex.EncodeToString(result.id))
|
||||||
|
}
|
||||||
|
if result.lease != expected_1["lease"] {
|
||||||
|
t.Errorf("expected lease %v, got %v", expected_1["lease"], result.lease)
|
||||||
|
}
|
||||||
|
if result.name != expected_1["name"] {
|
||||||
|
t.Errorf("expected name %v, got %v", expected_1["name"], result.name)
|
||||||
|
}
|
||||||
|
if result.extra["fake"] != expected_extra_1["fake"] {
|
||||||
|
t.Errorf("expected extra %v, got %v", expected_extra_1["fake"], result.extra["fake"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParserReadAppleDhcpdLeases(t *testing.T) {
|
||||||
|
f, err := os.Open(filepath.Join("testdata", "apple-dhcpd-example.leases"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unable to open dhcpd.leases sample: %s", err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
results, err := ReadAppleDhcpdLeaseEntries(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error reading lease: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// some simple utilities
|
||||||
|
filter_ipAddr := func(ipAddress string, items []appleDhcpLeaseEntry) (result []appleDhcpLeaseEntry) {
|
||||||
|
for _, item := range items {
|
||||||
|
if item.ipAddress == ipAddress {
|
||||||
|
result = append(result, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
find_id := func(id string, items []appleDhcpLeaseEntry) *appleDhcpLeaseEntry {
|
||||||
|
for _, item := range items {
|
||||||
|
if id == hex.EncodeToString(item.id) {
|
||||||
|
return &item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
find_hwAddr := func(hwAddr string, items []appleDhcpLeaseEntry) *appleDhcpLeaseEntry {
|
||||||
|
for _, item := range items {
|
||||||
|
if hwAddr == hex.EncodeToString(item.hwAddress) {
|
||||||
|
return &item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// actual unit tests
|
||||||
|
test_1 := map[string]string{
|
||||||
|
"ipAddress": "127.0.0.19",
|
||||||
|
"id": "0dead099aabb",
|
||||||
|
"hwAddress": "0dead099aabb",
|
||||||
|
}
|
||||||
|
test_1_findings := filter_ipAddr(test_1["ipAddress"], results)
|
||||||
|
if len(test_1_findings) != 2 {
|
||||||
|
t.Errorf("expected %d matching entries, got %d", 2, len(test_1_findings))
|
||||||
|
} else {
|
||||||
|
res := find_hwAddr(test_1["hwAddress"], test_1_findings)
|
||||||
|
if res == nil {
|
||||||
|
t.Errorf("unable to find item with hwAddress %v", test_1["hwAddress"])
|
||||||
|
} else if hex.EncodeToString(res.id) != test_1["id"] {
|
||||||
|
t.Errorf("expected id %s, got %s", test_1["id"], hex.EncodeToString(res.id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test_2 := map[string]string{
|
||||||
|
"ipAddress": "127.0.0.19",
|
||||||
|
"id": "0dead0667788",
|
||||||
|
"hwAddress": "0dead0667788",
|
||||||
|
}
|
||||||
|
test_2_findings := filter_ipAddr(test_2["ipAddress"], results)
|
||||||
|
if len(test_2_findings) != 2 {
|
||||||
|
t.Errorf("expected %d matching entries, got %d", 2, len(test_2_findings))
|
||||||
|
} else {
|
||||||
|
res := find_hwAddr(test_2["hwAddress"], test_2_findings)
|
||||||
|
if res == nil {
|
||||||
|
t.Errorf("unable to find item with hwAddress %v", test_2["hwAddress"])
|
||||||
|
} else if hex.EncodeToString(res.id) != test_2["id"] {
|
||||||
|
t.Errorf("expected id %s, got %s", test_2["id"], hex.EncodeToString(res.id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test_3 := map[string]string{
|
||||||
|
"ipAddress": "127.0.0.17",
|
||||||
|
"id": "0dead0334455",
|
||||||
|
"hwAddress": "0dead0667788",
|
||||||
|
}
|
||||||
|
test_3_findings := filter_ipAddr(test_3["ipAddress"], results)
|
||||||
|
if len(test_3_findings) != 2 {
|
||||||
|
t.Errorf("expected %d matching entries, got %d", 2, len(test_3_findings))
|
||||||
|
} else {
|
||||||
|
res := find_id(test_3["id"], test_3_findings)
|
||||||
|
if res == nil {
|
||||||
|
t.Errorf("unable to find item with id %v", test_3["id"])
|
||||||
|
} else if hex.EncodeToString(res.hwAddress) != test_3["hwAddress"] {
|
||||||
|
t.Errorf("expected hardware address %s, got %s", test_3["hwAddress"], hex.EncodeToString(res.hwAddress))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test_4 := map[string]string{
|
||||||
|
"ipAddress": "127.0.0.17",
|
||||||
|
"id": "0dead0001122",
|
||||||
|
"hwAddress": "0dead0667788",
|
||||||
|
}
|
||||||
|
test_4_findings := filter_ipAddr(test_4["ipAddress"], results)
|
||||||
|
if len(test_4_findings) != 2 {
|
||||||
|
t.Errorf("expected %d matching entries, got %d", 2, len(test_4_findings))
|
||||||
|
} else {
|
||||||
|
res := find_id(test_4["id"], test_4_findings)
|
||||||
|
if res == nil {
|
||||||
|
t.Errorf("unable to find item with id %v", test_4["id"])
|
||||||
|
} else if hex.EncodeToString(res.hwAddress) != test_4["hwAddress"] {
|
||||||
|
t.Errorf("expected hardware address %s, got %s", test_4["hwAddress"], hex.EncodeToString(res.hwAddress))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestParserTokenizeNetworkingConfig(t *testing.T) {
|
func TestParserTokenizeNetworkingConfig(t *testing.T) {
|
||||||
tests := []string{
|
tests := []string{
|
||||||
"words words words",
|
"words words words",
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
# this entry is normal
|
||||||
|
{
|
||||||
|
ip_address=127.0.0.17
|
||||||
|
hw_address=1,d:ea:d0:66:77:88
|
||||||
|
identifier=1,d:ea:d0:0:11:22
|
||||||
|
lease=0x5fd78ae2
|
||||||
|
name=vagrant-2019
|
||||||
|
}
|
||||||
|
|
||||||
|
# this entry has tabs
|
||||||
|
{
|
||||||
|
ip_address=127.0.0.17
|
||||||
|
hw_address=1,d:ea:d0:66:77:88
|
||||||
|
identifier=1,d:ea:d0:33:44:55
|
||||||
|
lease=0x5fd7b4e5
|
||||||
|
name=vagrant-2019
|
||||||
|
}
|
||||||
|
|
||||||
|
# These next two entries have the same address, but different uids
|
||||||
|
{
|
||||||
|
ip_address=127.0.0.19
|
||||||
|
hw_address=1,d:ea:d0:66:77:88
|
||||||
|
identifier=1,d:ea:d0:66:77:88
|
||||||
|
lease=0x5fd72edc
|
||||||
|
name=vagrant-2019
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ip_address=127.0.0.19
|
||||||
|
hw_address=1,d:ea:d0:99:aa:bb
|
||||||
|
identifier=1,d:ea:d0:99:aa:bb
|
||||||
|
lease=0x5fd72edc
|
||||||
|
name=vagrant-2019
|
||||||
|
}
|
Loading…
Reference in New Issue