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
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
|
@ -2430,3 +2430,144 @@ func ReadDhcpdLeaseEntries(fd *os.File) ([]dhcpLeaseEntry, error) {
|
|||
}
|
||||
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) {
|
||||
tests := []string{
|
||||
"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