Added parser for VMware Fusion's networking file. Replaced VmwareDriver's NetmapConfPath with a NetworkMapperInterface in order to handle the differences between VMware Fusion and the rest of the VMware suite.

This commit is contained in:
Ali Rizvi-Santiago 2017-10-23 20:26:16 -05:00
parent 069d00f70b
commit b2fec18b1e
8 changed files with 922 additions and 46 deletions

View File

@ -274,7 +274,10 @@ type VmwareDriver struct {
DhcpLeasesPath func(string) string DhcpLeasesPath func(string) string
DhcpConfPath func(string) string DhcpConfPath func(string) string
VmnetnatConfPath func(string) string VmnetnatConfPath func(string) string
NetmapConfPath func() string
/// This method returns an object with the NetworkNameMapper interface
/// that maps network to device and vice-versa.
NetworkMapper func() (NetworkNameMapper, error)
} }
func (d *VmwareDriver) GuestAddress(state multistep.StateBag) (string, error) { func (d *VmwareDriver) GuestAddress(state multistep.StateBag) (string, error) {
@ -304,12 +307,8 @@ func (d *VmwareDriver) GuestAddress(state multistep.StateBag) (string, error) {
func (d *VmwareDriver) GuestIP(state multistep.StateBag) (string, error) { func (d *VmwareDriver) GuestIP(state multistep.StateBag) (string, error) {
// read netmap config // grab network mapper
pathNetmap := d.NetmapConfPath() netmap, err := d.NetworkMapper()
if _, err := os.Stat(pathNetmap); err != nil {
return "", fmt.Errorf("Could not find netmap conf file: %s", pathNetmap)
}
netmap, err := ReadNetmapConfig(pathNetmap)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -401,12 +400,8 @@ func (d *VmwareDriver) GuestIP(state multistep.StateBag) (string, error) {
func (d *VmwareDriver) HostAddress(state multistep.StateBag) (string, error) { func (d *VmwareDriver) HostAddress(state multistep.StateBag) (string, error) {
// parse network<->device mapping // grab mapper for converting network<->device
pathNetmap := d.NetmapConfPath() netmap, err := d.NetworkMapper()
if _, err := os.Stat(pathNetmap); err != nil {
return "", fmt.Errorf("Could not find netmap conf file: %s", pathNetmap)
}
netmap, err := ReadNetmapConfig(pathNetmap)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -471,12 +466,8 @@ func (d *VmwareDriver) HostAddress(state multistep.StateBag) (string, error) {
func (d *VmwareDriver) HostIP(state multistep.StateBag) (string, error) { func (d *VmwareDriver) HostIP(state multistep.StateBag) (string, error) {
// parse network<->device mapping // grab mapper for converting network<->device
pathNetmap := d.NetmapConfPath() netmap, err := d.NetworkMapper()
if _, err := os.Stat(pathNetmap); err != nil {
return "", fmt.Errorf("Could not find netmap conf file: %s", pathNetmap)
}
netmap, err := ReadNetmapConfig(pathNetmap)
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@ -152,9 +152,19 @@ func (d *Fusion5Driver) Verify() error {
d.VmwareDriver.VmnetnatConfPath = func(device string) string { d.VmwareDriver.VmnetnatConfPath = func(device string) string {
return filepath.Join(libpath, device, "nat.conf") return filepath.Join(libpath, device, "nat.conf")
} }
d.VmwareDriver.NetmapConfPath = func() string { d.VmwareDriver.NetworkMapper = func() (NetworkNameMapper, error) {
// FIXME: Suggested by @phekmat. This will need another parser to be implemented. pathNetworking := filepath.Join(libpath, "networking")
return filepath.Join(libpath, "networking") if _, err := os.Stat(pathNetworking); err != nil {
return nil, fmt.Errorf("Could not find networking conf file: %s", pathNetworking)
}
fd, err := os.Open(pathNetworking)
if err != nil {
return nil, err
}
defer fd.Close()
return ReadNetworkingConfig(fd)
} }
return nil return nil

View File

@ -78,9 +78,19 @@ func (d *Fusion6Driver) Verify() error {
d.VmwareDriver.VmnetnatConfPath = func(device string) string { d.VmwareDriver.VmnetnatConfPath = func(device string) string {
return filepath.Join(libpath, device, "nat.conf") return filepath.Join(libpath, device, "nat.conf")
} }
d.VmwareDriver.NetmapConfPath = func() string { d.VmwareDriver.NetworkMapper = func() (NetworkNameMapper, error) {
// FIXME: Suggested by @phekmat. This will need another parser to be implemented. pathNetworking := filepath.Join(libpath, "networking")
return filepath.Join(libpath, "networking") if _, err := os.Stat(pathNetworking); err != nil {
return nil, fmt.Errorf("Could not find networking conf file: %s", pathNetworking)
}
fd, err := os.Open(pathNetworking)
if err != nil {
return nil, err
}
defer fd.Close()
return ReadNetworkingConfig(fd)
} }
return compareVersions(matches[1], VMWARE_FUSION_VERSION, "Fusion Professional") return compareVersions(matches[1], VMWARE_FUSION_VERSION, "Fusion Professional")

View File

@ -92,6 +92,20 @@ type DriverMock struct {
VerifyErr error VerifyErr error
} }
type NetworkMapperMock struct {
NameIntoDeviceCalled int
DeviceIntoNameCalled int
}
func (m NetworkMapperMock) NameIntoDevice(name string) (string, error) {
m.NameIntoDeviceCalled += 1
return "", nil
}
func (m NetworkMapperMock) DeviceIntoName(device string) (string, error) {
m.DeviceIntoNameCalled += 1
return "", nil
}
func (d *DriverMock) Clone(dst string, src string) error { func (d *DriverMock) Clone(dst string, src string) error {
d.CloneCalled = true d.CloneCalled = true
d.CloneDst = dst d.CloneDst = dst
@ -247,8 +261,8 @@ func (d *DriverMock) GetVmwareDriver() VmwareDriver {
state.VmnetnatConfPath = func(string) string { state.VmnetnatConfPath = func(string) string {
return "/path/to/vmnetnat.conf" return "/path/to/vmnetnat.conf"
} }
state.NetmapConfPath = func() string { state.NetworkMapper = func() (NetworkNameMapper, error) {
return "/path/to/netmap.conf" return NetworkMapperMock{}, nil
} }
return state return state
} }

View File

@ -3,8 +3,11 @@ package common
import ( import (
"fmt" "fmt"
"io" "io"
"log"
"math"
"net" "net"
"os" "os"
"reflect"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
@ -1053,6 +1056,11 @@ func (e *DhcpConfiguration) HostByName(host string) (configDeclaration, error) {
/*** Network Map */ /*** Network Map */
type NetworkMap []map[string]string type NetworkMap []map[string]string
type NetworkNameMapper interface {
NameIntoDevice(string) (string, error)
DeviceIntoName(string) (string, error)
}
func ReadNetworkMap(fd *os.File) (NetworkMap, error) { func ReadNetworkMap(fd *os.File) (NetworkMap, error) {
fromfile, eof := consumeFile(fd) fromfile, eof := consumeFile(fd)
@ -1066,16 +1074,16 @@ func ReadNetworkMap(fd *os.File) (NetworkMap, error) {
return result, nil return result, nil
} }
func (e *NetworkMap) NameIntoDevice(name string) (string, error) { func (e NetworkMap) NameIntoDevice(name string) (string, error) {
for _, val := range *e { for _, val := range e {
if strings.ToLower(val["name"]) == strings.ToLower(name) { if strings.ToLower(val["name"]) == strings.ToLower(name) {
return val["device"], nil return val["device"], nil
} }
} }
return "", fmt.Errorf("Network name not found : %v", name) return "", fmt.Errorf("Network name not found : %v", name)
} }
func (e *NetworkMap) DeviceIntoName(device string) (string, error) { func (e NetworkMap) DeviceIntoName(device string) (string, error) {
for _, val := range *e { for _, val := range e {
if strings.ToLower(val["device"]) == strings.ToLower(device) { if strings.ToLower(val["device"]) == strings.ToLower(device) {
return val["name"], nil return val["name"], nil
} }
@ -1091,7 +1099,836 @@ func (e *NetworkMap) repr() string {
return strings.Join(result, "\n") return strings.Join(result, "\n")
} }
/** main */ /*** parser for VMware Fusion's networking file */
func tokenizeNetworkingConfig(eof sentinelSignaller, in chan byte) chan string {
var ch byte
var state string
var repeat_newline bool
out := make(chan string)
go func(out chan string) {
for reading := true; reading; {
select {
case <-eof:
reading = false
case ch = <-in:
switch ch {
case '\r':
fallthrough
case '\t':
fallthrough
case ' ':
if len(state) == 0 {
continue
}
out <- state
state = ""
case '\n':
if repeat_newline {
continue
}
if len(state) > 0 {
out <- state
}
out <- string(ch)
state = ""
repeat_newline = true
continue
default:
state += string(ch)
}
repeat_newline = false
}
}
if len(state) > 0 {
out <- state
}
}(out)
return out
}
func splitNetworkingConfig(eof sentinelSignaller, in chan string) chan []string {
var out chan []string
out = make(chan []string)
go func(out chan []string) {
row := make([]string, 0)
for reading := true; reading; {
select {
case <-eof:
reading = false
case tk := <-in:
switch tk {
case "\n":
if len(row) > 0 {
out <- row
}
row = make([]string, 0)
default:
row = append(row, tk)
}
}
}
if len(row) > 0 {
out <- row
}
}(out)
return out
}
/// All token types in networking file.
// VERSION token
type networkingVERSION struct {
value string
}
func networkingReadVersion(row []string) (*networkingVERSION, error) {
if len(row) != 1 {
return nil, fmt.Errorf("Unexpected format for VERSION entry : %v", row)
}
res := &networkingVERSION{value: row[0]}
if !res.Valid() {
return nil, fmt.Errorf("Unexpected format for VERSION entry : %v", row)
}
return res, nil
}
func (s networkingVERSION) Repr() string {
if !s.Valid() {
return fmt.Sprintf("VERSION{INVALID=\"%v\"}", s.value)
}
return fmt.Sprintf("VERSION{%f}", s.Number())
}
func (s networkingVERSION) Valid() bool {
tokens := strings.SplitN(s.value, "=", 2)
if len(tokens) != 2 || tokens[0] != "VERSION" {
return false
}
tokens = strings.Split(tokens[1], ",")
if len(tokens) != 2 {
return false
}
for _, t := range tokens {
_, err := strconv.ParseUint(t, 10, 64)
if err != nil {
return false
}
}
return true
}
func (s networkingVERSION) Number() float64 {
var result float64
tokens := strings.SplitN(s.value, "=", 2)
tokens = strings.Split(tokens[1], ",")
integer, err := strconv.ParseUint(tokens[0], 10, 64)
if err != nil {
integer = 0
}
result = float64(integer)
mantissa, err := strconv.ParseUint(tokens[1], 10, 64)
if err != nil {
return result
}
denomination := math.Pow(10.0, float64(len(tokens[1])))
return result + (float64(mantissa) / denomination)
}
// VNET_X token
type networkingVNET struct {
value string
}
func (s networkingVNET) Valid() bool {
if strings.ToUpper(s.value) != s.value {
return false
}
tokens := strings.SplitN(s.value, "_", 3)
if len(tokens) != 3 || tokens[0] != "VNET" {
return false
}
_, err := strconv.ParseUint(tokens[1], 10, 64)
if err != nil {
return false
}
return true
}
func (s networkingVNET) Number() int {
tokens := strings.SplitN(s.value, "_", 3)
res, err := strconv.Atoi(tokens[1])
if err != nil {
return ^int(0)
}
return res
}
func (s networkingVNET) Option() string {
tokens := strings.SplitN(s.value, "_", 3)
if len(tokens) == 3 {
return tokens[2]
}
return ""
}
func (s networkingVNET) Repr() string {
if !s.Valid() {
tokens := strings.SplitN(s.value, "_", 3)
return fmt.Sprintf("VNET{INVALID=%v}", tokens)
}
return fmt.Sprintf("VNET{%d} %s", s.Number(), s.Option())
}
// Interface name
type networkingInterface struct {
name string
}
func (s networkingInterface) Interface() (*net.Interface, error) {
return net.InterfaceByName(s.name)
}
// networking command entry types
type networkingCommandEntry_answer struct {
vnet networkingVNET
value string
}
type networkingCommandEntry_remove_answer struct {
vnet networkingVNET
}
type networkingCommandEntry_add_nat_portfwd struct {
vnet int
protocol string
port int
target_host net.IP
target_port int
}
type networkingCommandEntry_remove_nat_portfwd struct {
vnet int
protocol string
port int
}
type networkingCommandEntry_add_dhcp_mac_to_ip struct {
vnet int
mac net.HardwareAddr
ip net.IP
}
type networkingCommandEntry_remove_dhcp_mac_to_ip struct {
vnet int
mac net.HardwareAddr
}
type networkingCommandEntry_add_bridge_mapping struct {
intf networkingInterface
vnet int
}
type networkingCommandEntry_remove_bridge_mapping struct {
intf networkingInterface
}
type networkingCommandEntry_add_nat_prefix struct {
vnet int
prefix int
}
type networkingCommandEntry_remove_nat_prefix struct {
vnet int
prefix int
}
type networkingCommandEntry struct {
entry interface{}
answer *networkingCommandEntry_answer
remove_answer *networkingCommandEntry_remove_answer
add_nat_portfwd *networkingCommandEntry_add_nat_portfwd
remove_nat_portfwd *networkingCommandEntry_remove_nat_portfwd
add_dhcp_mac_to_ip *networkingCommandEntry_add_dhcp_mac_to_ip
remove_dhcp_mac_to_ip *networkingCommandEntry_remove_dhcp_mac_to_ip
add_bridge_mapping *networkingCommandEntry_add_bridge_mapping
remove_bridge_mapping *networkingCommandEntry_remove_bridge_mapping
add_nat_prefix *networkingCommandEntry_add_nat_prefix
remove_nat_prefix *networkingCommandEntry_remove_nat_prefix
}
func (e networkingCommandEntry) Name() string {
switch e.entry.(type) {
case networkingCommandEntry_answer:
return "answer"
case networkingCommandEntry_remove_answer:
return "remove_answer"
case networkingCommandEntry_add_nat_portfwd:
return "add_nat_portfwd"
case networkingCommandEntry_remove_nat_portfwd:
return "remove_nat_portfwd"
case networkingCommandEntry_add_dhcp_mac_to_ip:
return "add_dhcp_mac_to_ip"
case networkingCommandEntry_remove_dhcp_mac_to_ip:
return "remove_dhcp_mac_to_ip"
case networkingCommandEntry_add_bridge_mapping:
return "add_bridge_mapping"
case networkingCommandEntry_remove_bridge_mapping:
return "remove_bridge_mapping"
case networkingCommandEntry_add_nat_prefix:
return "add_nat_prefix"
case networkingCommandEntry_remove_nat_prefix:
return "remove_nat_prefix"
}
return ""
}
func (e networkingCommandEntry) Entry() reflect.Value {
this := reflect.ValueOf(e)
switch e.entry.(type) {
case networkingCommandEntry_answer:
return reflect.Indirect(this.FieldByName("answer"))
case networkingCommandEntry_remove_answer:
return reflect.Indirect(this.FieldByName("remove_answer"))
case networkingCommandEntry_add_nat_portfwd:
return reflect.Indirect(this.FieldByName("add_nat_portfwd"))
case networkingCommandEntry_remove_nat_portfwd:
return reflect.Indirect(this.FieldByName("remove_nat_portfwd"))
case networkingCommandEntry_add_dhcp_mac_to_ip:
return reflect.Indirect(this.FieldByName("add_dhcp_mac_to_ip"))
case networkingCommandEntry_remove_dhcp_mac_to_ip:
return reflect.Indirect(this.FieldByName("remove_dhcp_mac_to_ip"))
case networkingCommandEntry_add_bridge_mapping:
return reflect.Indirect(this.FieldByName("add_bridge_mapping"))
case networkingCommandEntry_remove_bridge_mapping:
return reflect.Indirect(this.FieldByName("remove_bridge_mapping"))
case networkingCommandEntry_add_nat_prefix:
return reflect.Indirect(this.FieldByName("add_nat_prefix"))
case networkingCommandEntry_remove_nat_prefix:
return reflect.Indirect(this.FieldByName("remove_nat_prefix"))
}
return reflect.Value{}
}
func (e networkingCommandEntry) Repr() string {
var result map[string]interface{}
result = make(map[string]interface{})
entryN, entry := e.Name(), e.Entry()
entryT := entry.Type()
for i := 0; i < entry.NumField(); i++ {
fld, fldT := entry.Field(i), entryT.Field(i)
result[fldT.Name] = fld
}
return fmt.Sprintf("%s -> %v", entryN, result)
}
// networking command entry parsers
func parseNetworkingCommand_answer(row []string) (*networkingCommandEntry, error) {
if len(row) != 2 {
return nil, fmt.Errorf("Expected %d arguments. Received only %d.", 2, len(row))
}
vnet := networkingVNET{value: row[0]}
if !vnet.Valid() {
return nil, fmt.Errorf("Invalid format for VNET.")
}
value := row[1]
result := networkingCommandEntry_answer{vnet: vnet, value: value}
return &networkingCommandEntry{entry: result, answer: &result}, nil
}
func parseNetworkingCommand_remove_answer(row []string) (*networkingCommandEntry, error) {
if len(row) != 1 {
return nil, fmt.Errorf("Expected %d argument. Received %d.", 1, len(row))
}
vnet := networkingVNET{value: row[0]}
if !vnet.Valid() {
return nil, fmt.Errorf("Invalid format for VNET.")
}
result := networkingCommandEntry_remove_answer{vnet: vnet}
return &networkingCommandEntry{entry: result, remove_answer: &result}, nil
}
func parseNetworkingCommand_add_nat_portfwd(row []string) (*networkingCommandEntry, error) {
if len(row) != 5 {
return nil, fmt.Errorf("Expected %d arguments. Received only %d.", 5, len(row))
}
vnet, err := strconv.Atoi(row[0])
if err != nil {
return nil, fmt.Errorf("Unable to parse first argument as an integer. : %v", row[0])
}
protocol := strings.ToLower(row[1])
if !(protocol == "tcp" || protocol == "udp") {
return nil, fmt.Errorf("Expected \"tcp\" or \"udp\" for second argument. : %v", row[1])
}
sport, err := strconv.Atoi(row[2])
if err != nil {
return nil, fmt.Errorf("Unable to parse third argument as an integer. : %v", row[2])
}
dest := net.ParseIP(row[3])
if dest == nil {
return nil, fmt.Errorf("Unable to parse fourth argument as an IPv4 address. : %v", row[2])
}
dport, err := strconv.Atoi(row[4])
if err != nil {
return nil, fmt.Errorf("Unable to parse fifth argument as an integer. : %v", row[4])
}
result := networkingCommandEntry_add_nat_portfwd{vnet: vnet - 1, protocol: protocol, port: sport, target_host: dest, target_port: dport}
return &networkingCommandEntry{entry: result, add_nat_portfwd: &result}, nil
}
func parseNetworkingCommand_remove_nat_portfwd(row []string) (*networkingCommandEntry, error) {
if len(row) != 3 {
return nil, fmt.Errorf("Expected %d arguments. Received only %d.", 3, len(row))
}
vnet, err := strconv.Atoi(row[0])
if err != nil {
return nil, fmt.Errorf("Unable to parse first argument as an integer. : %v", row[0])
}
protocol := strings.ToLower(row[1])
if !(protocol == "tcp" || protocol == "udp") {
return nil, fmt.Errorf("Expected \"tcp\" or \"udp\" for second argument. : %v", row[1])
}
sport, err := strconv.Atoi(row[2])
if err != nil {
return nil, fmt.Errorf("Unable to parse third argument as an integer. : %v", row[2])
}
result := networkingCommandEntry_remove_nat_portfwd{vnet: vnet - 1, protocol: protocol, port: sport}
return &networkingCommandEntry{entry: result, remove_nat_portfwd: &result}, nil
}
func parseNetworkingCommand_add_dhcp_mac_to_ip(row []string) (*networkingCommandEntry, error) {
if len(row) != 3 {
return nil, fmt.Errorf("Expected %d arguments. Received only %d.", 3, len(row))
}
vnet, err := strconv.Atoi(row[0])
if err != nil {
return nil, fmt.Errorf("Unable to parse first argument as an integer. : %v", row[0])
}
mac, err := net.ParseMAC(row[1])
if err != nil {
return nil, fmt.Errorf("Unable to parse second argument as hwaddr. : %v", row[1])
}
ip := net.ParseIP(row[2])
if ip != nil {
return nil, fmt.Errorf("Unable to parse third argument as ipv4. : %v", row[2])
}
result := networkingCommandEntry_add_dhcp_mac_to_ip{vnet: vnet - 1, mac: mac, ip: ip}
return &networkingCommandEntry{entry: result, add_dhcp_mac_to_ip: &result}, nil
}
func parseNetworkingCommand_remove_dhcp_mac_to_ip(row []string) (*networkingCommandEntry, error) {
if len(row) != 2 {
return nil, fmt.Errorf("Expected %d arguments. Received only %d.", 2, len(row))
}
vnet, err := strconv.Atoi(row[0])
if err != nil {
return nil, fmt.Errorf("Unable to parse first argument as an integer. : %v", row[0])
}
mac, err := net.ParseMAC(row[1])
if err != nil {
return nil, fmt.Errorf("Unable to parse second argument as hwaddr. : %v", row[1])
}
result := networkingCommandEntry_remove_dhcp_mac_to_ip{vnet: vnet - 1, mac: mac}
return &networkingCommandEntry{entry: result, remove_dhcp_mac_to_ip: &result}, nil
}
func parseNetworkingCommand_add_bridge_mapping(row []string) (*networkingCommandEntry, error) {
if len(row) != 2 {
return nil, fmt.Errorf("Expected %d arguments. Received only %d.", 2, len(row))
}
intf := networkingInterface{name: row[0]}
vnet, err := strconv.Atoi(row[1])
if err != nil {
return nil, fmt.Errorf("Unable to parse second argument as an integer. : %v", row[2])
}
result := networkingCommandEntry_add_bridge_mapping{intf: intf, vnet: vnet - 1}
return &networkingCommandEntry{entry: result, add_bridge_mapping: &result}, nil
}
func parseNetworkingCommand_remove_bridge_mapping(row []string) (*networkingCommandEntry, error) {
if len(row) != 1 {
return nil, fmt.Errorf("Expected %d argument. Received %d.", 1, len(row))
}
intf := networkingInterface{name: row[0]}
/*
number, err := strconv.Atoi(row[0])
if err != nil {
return nil, fmt.Errorf("Unable to parse first argument as an integer. : %v", row[0])
}
*/
result := networkingCommandEntry_remove_bridge_mapping{intf: intf}
return &networkingCommandEntry{entry: result, remove_bridge_mapping: &result}, nil
}
func parseNetworkingCommand_add_nat_prefix(row []string) (*networkingCommandEntry, error) {
if len(row) != 2 {
return nil, fmt.Errorf("Expected %d arguments. Received only %d.", 2, len(row))
}
vnet, err := strconv.Atoi(row[0])
if err != nil {
return nil, fmt.Errorf("Unable to parse first argument as an integer. : %v", row[0])
}
if !strings.HasPrefix(row[1], "/") {
return nil, fmt.Errorf("Expected second argument to begin with \"/\". : %v", row[1])
}
prefix, err := strconv.Atoi(row[1][1:])
if err != nil {
return nil, fmt.Errorf("Unable to parse prefix out of second argument. : %v", row[1])
}
result := networkingCommandEntry_add_nat_prefix{vnet: vnet - 1, prefix: prefix}
return &networkingCommandEntry{entry: result, add_nat_prefix: &result}, nil
}
func parseNetworkingCommand_remove_nat_prefix(row []string) (*networkingCommandEntry, error) {
if len(row) != 2 {
return nil, fmt.Errorf("Expected %d arguments. Received only %d.", 2, len(row))
}
vnet, err := strconv.Atoi(row[0])
if err != nil {
return nil, fmt.Errorf("Unable to parse first argument as an integer. : %v", row[0])
}
if !strings.HasPrefix(row[1], "/") {
return nil, fmt.Errorf("Expected second argument to begin with \"/\". : %v", row[1])
}
prefix, err := strconv.Atoi(row[1][1:])
if err != nil {
return nil, fmt.Errorf("Unable to parse prefix out of second argument. : %v", row[1])
}
result := networkingCommandEntry_remove_nat_prefix{vnet: vnet - 1, prefix: prefix}
return &networkingCommandEntry{entry: result, remove_nat_prefix: &result}, nil
}
type networkingCommandParser struct {
command string
callback func([]string) (*networkingCommandEntry, error)
}
var NetworkingCommandParsers = []networkingCommandParser{
/* DictRecordParseFunct */ {command: "answer", callback: parseNetworkingCommand_answer},
/* DictRecordParseFunct */ {command: "remove_answer", callback: parseNetworkingCommand_remove_answer},
/* NatFwdRecordParseFunct */ {command: "add_nat_portfwd", callback: parseNetworkingCommand_add_nat_portfwd},
/* NatFwdRecordParseFunct */ {command: "remove_nat_portfwd", callback: parseNetworkingCommand_remove_nat_portfwd},
/* DhcpMacRecordParseFunct */ {command: "add_dhcp_mac_to_ip", callback: parseNetworkingCommand_add_dhcp_mac_to_ip},
/* DhcpMacRecordParseFunct */ {command: "remove_dhcp_mac_to_ip", callback: parseNetworkingCommand_remove_dhcp_mac_to_ip},
/* BridgeMappingRecordParseFunct */ {command: "add_bridge_mapping", callback: parseNetworkingCommand_add_bridge_mapping},
/* BridgeMappingRecordParseFunct */ {command: "remove_bridge_mapping", callback: parseNetworkingCommand_remove_bridge_mapping},
/* NatPrefixRecordParseFunct */ {command: "add_nat_prefix", callback: parseNetworkingCommand_add_nat_prefix},
/* NatPrefixRecordParseFunct */ {command: "remove_nat_prefix", callback: parseNetworkingCommand_remove_nat_prefix},
}
func NetworkingParserByCommand(command string) *func([]string) (*networkingCommandEntry, error) {
for _, p := range NetworkingCommandParsers {
if p.command == command {
return &p.callback
}
}
return nil
}
func parseNetworkingConfig(eof sentinelSignaller, rows chan []string) chan networkingCommandEntry {
var out chan networkingCommandEntry
out = make(chan networkingCommandEntry)
go func(in chan []string, out chan networkingCommandEntry) {
for reading := true; reading; {
select {
case <-eof:
reading = false
case row := <-in:
if len(row) >= 1 {
parser := NetworkingParserByCommand(row[0])
if parser == nil {
log.Printf("Invalid command : %v", row)
continue
}
callback := *parser
entry, err := callback(row[1:])
if err != nil {
log.Printf("Unable to parse command : %v %v", err, row)
continue
}
out <- *entry
}
}
}
}(rows, out)
return out
}
type NetworkingConfig struct {
answer map[int]map[string]string
nat_portfwd map[int]map[string]string
dhcp_mac_to_ip map[int]map[string]net.IP
//bridge_mapping map[net.Interface]uint64 // XXX: we don't need the actual interface for anything but informing the user.
bridge_mapping map[string]int
nat_prefix map[int][]int
}
func (c NetworkingConfig) repr() string {
return fmt.Sprintf("answer -> %v\nnat_portfwd -> %v\ndhcp_mac_to_ip -> %v\nbridge_mapping -> %v\nnat_prefix -> %v", c.answer, c.nat_portfwd, c.dhcp_mac_to_ip, c.bridge_mapping, c.nat_prefix)
}
func flattenNetworkingConfig(eof sentinelSignaller, in chan networkingCommandEntry) NetworkingConfig {
var result NetworkingConfig
var vmnet int
result.answer = make(map[int]map[string]string)
result.nat_portfwd = make(map[int]map[string]string)
result.dhcp_mac_to_ip = make(map[int]map[string]net.IP)
result.bridge_mapping = make(map[string]int)
result.nat_prefix = make(map[int][]int)
for reading := true; reading; {
select {
case <-eof:
reading = false
case e := <-in:
switch e.entry.(type) {
case networkingCommandEntry_answer:
vnet := e.answer.vnet
answers, exists := result.answer[vnet.Number()]
if !exists {
answers = make(map[string]string)
result.answer[vnet.Number()] = answers
}
answers[vnet.Option()] = e.answer.value
case networkingCommandEntry_remove_answer:
vnet := e.remove_answer.vnet
answers, exists := result.answer[vnet.Number()]
if exists {
delete(answers, vnet.Option())
} else {
log.Printf("Unable to remove answer %s as specified by `remove_answer`.\n", vnet.Repr())
}
case networkingCommandEntry_add_nat_portfwd:
vmnet = e.add_nat_portfwd.vnet
protoport := fmt.Sprintf("%s/%d", e.add_nat_portfwd.protocol, e.add_nat_portfwd.port)
target := fmt.Sprintf("%s:%d", e.add_nat_portfwd.target_host, e.add_nat_portfwd.target_port)
portfwds, exists := result.nat_portfwd[vmnet]
if !exists {
portfwds = make(map[string]string)
result.nat_portfwd[vmnet] = portfwds
}
portfwds[protoport] = target
case networkingCommandEntry_remove_nat_portfwd:
vmnet = e.remove_nat_portfwd.vnet
protoport := fmt.Sprintf("%s/%d", e.remove_nat_portfwd.protocol, e.remove_nat_portfwd.port)
portfwds, exists := result.nat_portfwd[vmnet]
if exists {
delete(portfwds, protoport)
} else {
log.Printf("Unable to remove nat port-forward %s from interface %s%d as requested by `remove_nat_portfwd`.\n", protoport, NetworkingInterfacePrefix, vmnet)
}
case networkingCommandEntry_add_dhcp_mac_to_ip:
vmnet = e.add_dhcp_mac_to_ip.vnet
dhcpmacs, exists := result.dhcp_mac_to_ip[vmnet]
if !exists {
dhcpmacs = make(map[string]net.IP)
result.dhcp_mac_to_ip[vmnet] = dhcpmacs
}
dhcpmacs[e.add_dhcp_mac_to_ip.mac.String()] = e.add_dhcp_mac_to_ip.ip
case networkingCommandEntry_remove_dhcp_mac_to_ip:
vmnet = e.remove_dhcp_mac_to_ip.vnet
dhcpmacs, exists := result.dhcp_mac_to_ip[vmnet]
if exists {
delete(dhcpmacs, e.remove_dhcp_mac_to_ip.mac.String())
} else {
log.Printf("Unable to remove dhcp_mac_to_ip entry %v from interface %s%d as specified by `remove_dhcp_mac_to_ip`.\n", e.remove_dhcp_mac_to_ip, NetworkingInterfacePrefix, vmnet)
}
case networkingCommandEntry_add_bridge_mapping:
intf := e.add_bridge_mapping.intf
if _, err := intf.Interface(); err != nil {
log.Printf("Interface \"%s\" as specified by `add_bridge_mapping` was not found on the current platform. This is a non-critical error. Ignoring.", intf.name)
}
result.bridge_mapping[intf.name] = e.add_bridge_mapping.vnet
case networkingCommandEntry_remove_bridge_mapping:
intf := e.remove_bridge_mapping.intf
if _, err := intf.Interface(); err != nil {
log.Printf("Interface \"%s\" as specified by `remove_bridge_mapping` was not found on the current platform. This is a non-critical error. Ignoring.", intf.name)
}
delete(result.bridge_mapping, intf.name)
case networkingCommandEntry_add_nat_prefix:
vmnet = e.add_nat_prefix.vnet
_, exists := result.nat_prefix[vmnet]
if exists {
result.nat_prefix[vmnet] = append(result.nat_prefix[vmnet], e.add_nat_prefix.prefix)
} else {
result.nat_prefix[vmnet] = []int{e.add_nat_prefix.prefix}
}
case networkingCommandEntry_remove_nat_prefix:
vmnet = e.remove_nat_prefix.vnet
prefixes, exists := result.nat_prefix[vmnet]
if exists {
for index := 0; index < len(prefixes); index++ {
if prefixes[index] == e.remove_nat_prefix.prefix {
result.nat_prefix[vmnet] = append(prefixes[:index], prefixes[index+1:]...)
break
}
}
} else {
log.Printf("Unable to remove nat prefix /%d from interface %s%d as specified by `remove_nat_prefix`.\n", e.remove_nat_prefix.prefix, NetworkingInterfacePrefix, vmnet)
}
}
}
}
return result
}
// Constructor for networking file
func ReadNetworkingConfig(fd *os.File) (NetworkingConfig, error) {
// start piecing together different parts of the file
fromfile, eof := consumeFile(fd)
tokenized := tokenizeNetworkingConfig(eof, fromfile)
rows := splitNetworkingConfig(eof, tokenized)
entries := parseNetworkingConfig(eof, rows)
// parse the version
parsed_version, err := networkingReadVersion(<-rows)
if err != nil {
return NetworkingConfig{}, err
}
// verify that it's 1.0 since that's all we support.
version := parsed_version.Number()
if version != 1.0 {
return NetworkingConfig{}, fmt.Errorf("Expected version %f of networking file. Received version %f.", 1.0, version)
}
// convert to a configuration
result := flattenNetworkingConfig(eof, entries)
return result, nil
}
// netmapper interface
type NetworkingType int
const (
NetworkingType_HOSTONLY = iota + 1
NetworkingType_NAT
NetworkingType_BRIDGED
)
func networkingConfig_InterfaceTypes(config NetworkingConfig) map[int]NetworkingType {
var result map[int]NetworkingType
result = make(map[int]NetworkingType)
// defaults
result[0] = NetworkingType_BRIDGED
result[1] = NetworkingType_HOSTONLY
result[8] = NetworkingType_NAT
// walk through config collecting bridged interfaces
for _, vmnet := range config.bridge_mapping {
result[vmnet] = NetworkingType_BRIDGED
}
// walk through answers finding out which ones are nat versus hostonly
for vmnet, table := range config.answer {
// everything should be defined as a virtual adapter...
if table["VIRTUAL_ADAPTER"] == "yes" {
// validate that the VNET entry contains everything we expect it to
_, subnetQ := table["HOSTONLY_SUBNET"]
_, netmaskQ := table["HOSTONLY_NETMASK"]
if !(subnetQ && netmaskQ) {
log.Printf("Interface %s%d is missing some expected keys (HOSTONLY_SUBNET, HOSTONLY_NETMASK). This is non-critical. Ignoring..", NetworkingInterfacePrefix, vmnet)
}
// distinguish between nat or hostonly
if table["NAT"] == "yes" {
result[vmnet] = NetworkingType_NAT
} else {
result[vmnet] = NetworkingType_HOSTONLY
}
// if it's not a virtual_adapter, then it must be an alias (really a bridge).
} else {
result[vmnet] = NetworkingType_BRIDGED
}
}
return result
}
func networkingConfig_NamesToVmnet(config NetworkingConfig) map[NetworkingType][]int {
types := networkingConfig_InterfaceTypes(config)
// now sort the keys
var keys []int
for vmnet := range types {
keys = append(keys, vmnet)
}
sort.Ints(keys)
// build result dictionary
var result map[NetworkingType][]int
result = make(map[NetworkingType][]int)
for i := 0; i < len(keys); i++ {
t := types[keys[i]]
result[t] = append(result[t], keys[i])
}
return result
}
const NetworkingInterfacePrefix = "vmnet"
func (e NetworkingConfig) NameIntoDevice(name string) (string, error) {
netmapper := networkingConfig_NamesToVmnet(e)
name = strings.ToLower(name)
var vmnet int
if name == "hostonly" && len(netmapper[NetworkingType_HOSTONLY]) > 0 {
vmnet = netmapper[NetworkingType_HOSTONLY][0]
} else if name == "nat" && len(netmapper[NetworkingType_NAT]) > 0 {
vmnet = netmapper[NetworkingType_NAT][0]
} else if name == "bridged" && len(netmapper[NetworkingType_BRIDGED]) > 0 {
vmnet = netmapper[NetworkingType_BRIDGED][0]
} else {
return "", fmt.Errorf("Network name not found : %v", name)
}
return fmt.Sprintf("%s%d", NetworkingInterfacePrefix, vmnet), nil
}
func (e NetworkingConfig) DeviceIntoName(device string) (string, error) {
types := networkingConfig_InterfaceTypes(e)
lowerdevice := strings.ToLower(device)
if !strings.HasPrefix(lowerdevice, NetworkingInterfacePrefix) {
return device, nil
}
vmnet, err := strconv.Atoi(lowerdevice[len(NetworkingInterfacePrefix):])
if err != nil {
return "", err
}
network := types[vmnet]
switch network {
case NetworkingType_HOSTONLY:
return "hostonly", nil
case NetworkingType_NAT:
return "nat", nil
case NetworkingType_BRIDGED:
return "bridged", nil
}
return "", fmt.Errorf("Unable to determine network type for device %s%d.", NetworkingInterfacePrefix, vmnet)
}
/** generic async file reader */
func consumeFile(fd *os.File) (chan byte, sentinelSignaller) { func consumeFile(fd *os.File) (chan byte, sentinelSignaller) {
fromfile := make(chan byte) fromfile := make(chan byte)
eof := make(sentinelSignaller) eof := make(sentinelSignaller)

View File

@ -196,8 +196,19 @@ func (d *Player5Driver) Verify() error {
return playerVmnetnatConfPath(device) return playerVmnetnatConfPath(device)
} }
d.VmwareDriver.NetmapConfPath = func() string { d.VmwareDriver.NetworkMapper = func() (NetworkNameMapper, error) {
return playerNetmapConfPath() pathNetmap := playerNetmapConfPath()
if _, err := os.Stat(pathNetmap); err != nil {
return nil, fmt.Errorf("Could not find netmap conf file: %s", pathNetmap)
}
fd, err := os.Open(pathNetmap)
if err != nil {
return nil, err
}
defer fd.Close()
return ReadNetworkMap(fd)
} }
return nil return nil
} }

View File

@ -157,8 +157,19 @@ func (d *Workstation9Driver) Verify() error {
return workstationVmnetnatConfPath(device) return workstationVmnetnatConfPath(device)
} }
d.VmwareDriver.NetmapConfPath = func() string { d.VmwareDriver.NetworkMapper = func() (NetworkNameMapper, error) {
return workstationNetmapConfPath() pathNetmap := workstationNetmapConfPath()
if _, err := os.Stat(pathNetmap); err != nil {
return nil, fmt.Errorf("Could not find netmap conf file: %s", pathNetmap)
}
fd, err := os.Open(pathNetmap)
if err != nil {
return nil, err
}
defer fd.Close()
return ReadNetworkMap(fd)
} }
return nil return nil
} }

View File

@ -373,16 +373,8 @@ func (s *stepCreateVMX) Run(_ context.Context, state multistep.StateBag) multist
driver := state.Get("driver").(vmwcommon.Driver).GetVmwareDriver() driver := state.Get("driver").(vmwcommon.Driver).GetVmwareDriver()
// read netmap config // read netmap config
pathNetmap := driver.NetmapConfPath() netmap, err := driver.NetworkMapper()
if _, err := os.Stat(pathNetmap); err != nil { if err != nil {
err := fmt.Errorf("Could not find netmap conf file: %s", pathNetmap)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
netmap, res := vmwcommon.ReadNetmapConfig(pathNetmap)
if res != nil {
err := fmt.Errorf("Unable to read netmap conf file: %s: %v", pathNetmap, res)
state.Put("error", err) state.Put("error", err)
ui.Error(err.Error()) ui.Error(err.Error())
return multistep.ActionHalt return multistep.ActionHalt