datasource: add vmware
This commit is contained in:
parent
13dc11abf3
commit
d675638776
154
datasource/vmware/vmware.go
Normal file
154
datasource/vmware/vmware.go
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
// Copyright 2015 CoreOS, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package vmware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/coreos/coreos-cloudinit/config"
|
||||||
|
"github.com/coreos/coreos-cloudinit/datasource"
|
||||||
|
|
||||||
|
"github.com/coreos/coreos-cloudinit/Godeps/_workspace/src/github.com/sigma/vmw-guestinfo/rpcvmx"
|
||||||
|
"github.com/coreos/coreos-cloudinit/Godeps/_workspace/src/github.com/sigma/vmw-guestinfo/vmcheck"
|
||||||
|
)
|
||||||
|
|
||||||
|
type vmware struct {
|
||||||
|
readConfig func(key string) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDatasource() *vmware {
|
||||||
|
return &vmware{readConfig}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v vmware) IsAvailable() bool {
|
||||||
|
return vmcheck.IsVirtualWorld()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v vmware) AvailabilityChanges() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v vmware) ConfigRoot() string {
|
||||||
|
return "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v vmware) FetchMetadata() (metadata datasource.Metadata, err error) {
|
||||||
|
metadata.Hostname, _ = v.readConfig("hostname")
|
||||||
|
|
||||||
|
netconf := map[string]string{}
|
||||||
|
saveConfig := func(key string, args ...interface{}) string {
|
||||||
|
key = fmt.Sprintf(key, args...)
|
||||||
|
val, _ := v.readConfig(key)
|
||||||
|
if val != "" {
|
||||||
|
netconf[key] = val
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; ; i++ {
|
||||||
|
if nameserver := saveConfig("dns.server.%d", i); nameserver == "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
found := true
|
||||||
|
for i := 0; found; i++ {
|
||||||
|
found = false
|
||||||
|
|
||||||
|
found = (saveConfig("interface.%d.name", i) != "") || found
|
||||||
|
found = (saveConfig("interface.%d.mac", i) != "") || found
|
||||||
|
found = (saveConfig("interface.%d.dhcp", i) != "") || found
|
||||||
|
|
||||||
|
role, _ := v.readConfig(fmt.Sprintf("interface.%d.role", i))
|
||||||
|
for a := 0; ; a++ {
|
||||||
|
address := saveConfig("interface.%d.ip.%d.address", i, a)
|
||||||
|
if address == "" {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
|
||||||
|
ip, _, err := net.ParseCIDR(address)
|
||||||
|
if err != nil {
|
||||||
|
return metadata, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch role {
|
||||||
|
case "public":
|
||||||
|
if ip.To4() != nil {
|
||||||
|
metadata.PublicIPv4 = ip
|
||||||
|
} else {
|
||||||
|
metadata.PublicIPv6 = ip
|
||||||
|
}
|
||||||
|
case "private":
|
||||||
|
if ip.To4() != nil {
|
||||||
|
metadata.PrivateIPv4 = ip
|
||||||
|
} else {
|
||||||
|
metadata.PrivateIPv6 = ip
|
||||||
|
}
|
||||||
|
case "":
|
||||||
|
default:
|
||||||
|
return metadata, fmt.Errorf("unrecognized role: %q", role)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for r := 0; ; r++ {
|
||||||
|
gateway := saveConfig("interface.%d.route.%d.gateway", i, r)
|
||||||
|
destination := saveConfig("interface.%d.route.%d.destination", i, r)
|
||||||
|
|
||||||
|
if gateway == "" && destination == "" {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
metadata.NetworkConfig = netconf
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v vmware) FetchUserdata() ([]byte, error) {
|
||||||
|
encoding, err := v.readConfig("coreos.config.data.encoding")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := v.readConfig("coreos.config.data")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if encoding != "" {
|
||||||
|
return config.DecodeContent(data, encoding)
|
||||||
|
}
|
||||||
|
return []byte(data), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v vmware) Type() string {
|
||||||
|
return "vmware"
|
||||||
|
}
|
||||||
|
|
||||||
|
func readConfig(key string) (string, error) {
|
||||||
|
data, err := rpcvmx.NewConfig().GetString(key, "")
|
||||||
|
if err == nil {
|
||||||
|
log.Printf("Read from %q: %q\n", key, data)
|
||||||
|
} else {
|
||||||
|
log.Printf("Failed to read from %q: %v\n", key, err)
|
||||||
|
}
|
||||||
|
return data, err
|
||||||
|
}
|
189
datasource/vmware/vmware_test.go
Normal file
189
datasource/vmware/vmware_test.go
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
// Copyright 2015 CoreOS, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package vmware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/coreos/coreos-cloudinit/datasource"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MockHypervisor map[string]string
|
||||||
|
|
||||||
|
func (h MockHypervisor) ReadConfig(key string) (string, error) {
|
||||||
|
return h[key], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFetchMetadata(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
variables MockHypervisor
|
||||||
|
|
||||||
|
metadata datasource.Metadata
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
variables: map[string]string{
|
||||||
|
"interface.0.mac": "test mac",
|
||||||
|
"interface.0.dhcp": "yes",
|
||||||
|
},
|
||||||
|
metadata: datasource.Metadata{
|
||||||
|
NetworkConfig: map[string]string{
|
||||||
|
"interface.0.mac": "test mac",
|
||||||
|
"interface.0.dhcp": "yes",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
variables: map[string]string{
|
||||||
|
"interface.0.name": "test name",
|
||||||
|
"interface.0.dhcp": "yes",
|
||||||
|
},
|
||||||
|
metadata: datasource.Metadata{
|
||||||
|
NetworkConfig: map[string]string{
|
||||||
|
"interface.0.name": "test name",
|
||||||
|
"interface.0.dhcp": "yes",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
variables: map[string]string{
|
||||||
|
"hostname": "test host",
|
||||||
|
"interface.0.mac": "test mac",
|
||||||
|
"interface.0.role": "private",
|
||||||
|
"interface.0.ip.0.address": "fe00::100/64",
|
||||||
|
"interface.0.route.0.gateway": "fe00::1",
|
||||||
|
"interface.0.route.0.destination": "::",
|
||||||
|
},
|
||||||
|
metadata: datasource.Metadata{
|
||||||
|
Hostname: "test host",
|
||||||
|
PrivateIPv6: net.ParseIP("fe00::100"),
|
||||||
|
NetworkConfig: map[string]string{
|
||||||
|
"interface.0.mac": "test mac",
|
||||||
|
"interface.0.ip.0.address": "fe00::100/64",
|
||||||
|
"interface.0.route.0.gateway": "fe00::1",
|
||||||
|
"interface.0.route.0.destination": "::",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
variables: map[string]string{
|
||||||
|
"hostname": "test host",
|
||||||
|
"interface.0.name": "test name",
|
||||||
|
"interface.0.role": "public",
|
||||||
|
"interface.0.ip.0.address": "10.0.0.100/24",
|
||||||
|
"interface.0.ip.1.address": "10.0.0.101/24",
|
||||||
|
"interface.0.route.0.gateway": "10.0.0.1",
|
||||||
|
"interface.0.route.0.destination": "0.0.0.0",
|
||||||
|
"interface.1.mac": "test mac",
|
||||||
|
"interface.1.role": "private",
|
||||||
|
"interface.1.route.0.gateway": "10.0.0.2",
|
||||||
|
"interface.1.route.0.destination": "0.0.0.0",
|
||||||
|
"interface.1.ip.0.address": "10.0.0.102/24",
|
||||||
|
},
|
||||||
|
metadata: datasource.Metadata{
|
||||||
|
Hostname: "test host",
|
||||||
|
PublicIPv4: net.ParseIP("10.0.0.101"),
|
||||||
|
PrivateIPv4: net.ParseIP("10.0.0.102"),
|
||||||
|
NetworkConfig: map[string]string{
|
||||||
|
"interface.0.name": "test name",
|
||||||
|
"interface.0.ip.0.address": "10.0.0.100/24",
|
||||||
|
"interface.0.ip.1.address": "10.0.0.101/24",
|
||||||
|
"interface.0.route.0.gateway": "10.0.0.1",
|
||||||
|
"interface.0.route.0.destination": "0.0.0.0",
|
||||||
|
"interface.1.mac": "test mac",
|
||||||
|
"interface.1.route.0.gateway": "10.0.0.2",
|
||||||
|
"interface.1.route.0.destination": "0.0.0.0",
|
||||||
|
"interface.1.ip.0.address": "10.0.0.102/24",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range tests {
|
||||||
|
v := vmware{tt.variables.ReadConfig}
|
||||||
|
metadata, err := v.FetchMetadata()
|
||||||
|
if !reflect.DeepEqual(tt.err, err) {
|
||||||
|
t.Errorf("bad error (#%d): want %v, got %v", i, tt.err, err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(tt.metadata, metadata) {
|
||||||
|
t.Errorf("bad metadata (#%d): want %#v, got %#v", i, tt.metadata, metadata)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFetchUserdata(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
variables MockHypervisor
|
||||||
|
|
||||||
|
userdata string
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
variables: map[string]string{"coreos.config.data": "test config"},
|
||||||
|
userdata: "test config",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
variables: map[string]string{
|
||||||
|
"coreos.config.data.encoding": "",
|
||||||
|
"coreos.config.data": "test config",
|
||||||
|
},
|
||||||
|
userdata: "test config",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
variables: map[string]string{
|
||||||
|
"coreos.config.data.encoding": "base64",
|
||||||
|
"coreos.config.data": "dGVzdCBjb25maWc=",
|
||||||
|
},
|
||||||
|
userdata: "test config",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
variables: map[string]string{
|
||||||
|
"coreos.config.data.encoding": "gzip+base64",
|
||||||
|
"coreos.config.data": "H4sIABaoWlUAAytJLS5RSM7PS8tMBwCQiHNZCwAAAA==",
|
||||||
|
},
|
||||||
|
userdata: "test config",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
variables: map[string]string{
|
||||||
|
"coreos.config.data.encoding": "test encoding",
|
||||||
|
},
|
||||||
|
err: errors.New(`Unsupported encoding "test encoding"`),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range tests {
|
||||||
|
v := vmware{tt.variables.ReadConfig}
|
||||||
|
userdata, err := v.FetchUserdata()
|
||||||
|
if !reflect.DeepEqual(tt.err, err) {
|
||||||
|
t.Errorf("bad error (#%d): want %v, got %v", i, nil, err)
|
||||||
|
}
|
||||||
|
if tt.userdata != string(userdata) {
|
||||||
|
t.Errorf("bad userdata (#%d): want %q, got %q", i, tt.userdata, userdata)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFetchUserdataError(t *testing.T) {
|
||||||
|
testErr := errors.New("test error")
|
||||||
|
_, err := vmware{func(_ string) (string, error) { return "", testErr }}.FetchUserdata()
|
||||||
|
|
||||||
|
if testErr != err {
|
||||||
|
t.Errorf("bad error: want %v, got %v", testErr, err)
|
||||||
|
}
|
||||||
|
}
|
174
network/vmware.go
Normal file
174
network/vmware.go
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
// Copyright 2015 CoreOS, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package network
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ProcessVMwareNetconf(config map[string]string) ([]InterfaceGenerator, error) {
|
||||||
|
log.Println("Processing VMware network config")
|
||||||
|
|
||||||
|
log.Println("Parsing nameservers")
|
||||||
|
var nameservers []net.IP
|
||||||
|
for i := 0; ; i++ {
|
||||||
|
if ipStr, ok := config[fmt.Sprintf("dns.server.%d", i)]; ok {
|
||||||
|
if ip := net.ParseIP(ipStr); ip != nil {
|
||||||
|
nameservers = append(nameservers, ip)
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("invalid nameserver: %q", ipStr)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Printf("Parsed %d nameservers", len(nameservers))
|
||||||
|
|
||||||
|
var interfaces []InterfaceGenerator
|
||||||
|
for i := 0; ; i++ {
|
||||||
|
var addresses []net.IPNet
|
||||||
|
var routes []route
|
||||||
|
var err error
|
||||||
|
var dhcp bool
|
||||||
|
iface := &physicalInterface{}
|
||||||
|
|
||||||
|
log.Printf("Proccessing interface %d", i)
|
||||||
|
|
||||||
|
log.Println("Processing DHCP")
|
||||||
|
if dhcp, err = processDHCPConfig(config, fmt.Sprintf("interface.%d.", i)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Processing addresses")
|
||||||
|
if as, err := processAddressConfig(config, fmt.Sprintf("interface.%d.", i)); err == nil {
|
||||||
|
addresses = append(addresses, as...)
|
||||||
|
} else {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Processing routes")
|
||||||
|
if rs, err := processRouteConfig(config, fmt.Sprintf("interface.%d.", i)); err == nil {
|
||||||
|
routes = append(routes, rs...)
|
||||||
|
} else {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if mac, ok := config[fmt.Sprintf("interface.%d.mac", i)]; ok {
|
||||||
|
log.Printf("Parsing interface %d MAC address: %q", i, mac)
|
||||||
|
if hwaddr, err := net.ParseMAC(mac); err == nil {
|
||||||
|
iface.hwaddr = hwaddr
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("error while parsing MAC address: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if name, ok := config[fmt.Sprintf("interface.%d.name", i)]; ok {
|
||||||
|
log.Printf("Parsing interface %d name: %q", i, name)
|
||||||
|
iface.name = name
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(addresses) > 0 || len(routes) > 0 {
|
||||||
|
iface.config = configMethodStatic{
|
||||||
|
hwaddress: iface.hwaddr,
|
||||||
|
addresses: addresses,
|
||||||
|
nameservers: nameservers,
|
||||||
|
routes: routes,
|
||||||
|
}
|
||||||
|
} else if dhcp {
|
||||||
|
iface.config = configMethodDHCP{
|
||||||
|
hwaddress: iface.hwaddr,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
interfaces = append(interfaces, iface)
|
||||||
|
}
|
||||||
|
|
||||||
|
return interfaces, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func processAddressConfig(config map[string]string, prefix string) (addresses []net.IPNet, err error) {
|
||||||
|
for a := 0; ; a++ {
|
||||||
|
prefix := fmt.Sprintf("%sip.%d.", prefix, a)
|
||||||
|
|
||||||
|
addressStr, ok := config[prefix+"address"]
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
ip, network, err := net.ParseCIDR(addressStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid address: %q", addressStr)
|
||||||
|
}
|
||||||
|
addresses = append(addresses, net.IPNet{
|
||||||
|
IP: ip,
|
||||||
|
Mask: network.Mask,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func processRouteConfig(config map[string]string, prefix string) (routes []route, err error) {
|
||||||
|
for r := 0; ; r++ {
|
||||||
|
prefix := fmt.Sprintf("%sroute.%d.", prefix, r)
|
||||||
|
|
||||||
|
gatewayStr, gok := config[prefix+"gateway"]
|
||||||
|
destinationStr, dok := config[prefix+"destination"]
|
||||||
|
if gok && !dok {
|
||||||
|
return nil, fmt.Errorf("missing destination key")
|
||||||
|
} else if !gok && dok {
|
||||||
|
return nil, fmt.Errorf("missing gateway key")
|
||||||
|
} else if !gok && !dok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
gateway := net.ParseIP(gatewayStr)
|
||||||
|
if gateway == nil {
|
||||||
|
return nil, fmt.Errorf("invalid gateway: %q", gatewayStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, destination, err := net.ParseCIDR(destinationStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
routes = append(routes, route{
|
||||||
|
destination: *destination,
|
||||||
|
gateway: gateway,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func processDHCPConfig(config map[string]string, prefix string) (dhcp bool, err error) {
|
||||||
|
dhcpStr, ok := config[prefix+"dhcp"]
|
||||||
|
if !ok {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch dhcpStr {
|
||||||
|
case "yes":
|
||||||
|
return true, nil
|
||||||
|
case "no":
|
||||||
|
return false, nil
|
||||||
|
default:
|
||||||
|
return false, fmt.Errorf("invalid DHCP option: %q", dhcpStr)
|
||||||
|
}
|
||||||
|
}
|
361
network/vmware_test.go
Normal file
361
network/vmware_test.go
Normal file
@ -0,0 +1,361 @@
|
|||||||
|
// Copyright 2015 CoreOS, Inc.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package network
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func mustParseMac(mac net.HardwareAddr, err error) net.HardwareAddr {
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return mac
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProcessVMwareNetconf(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
config map[string]string
|
||||||
|
|
||||||
|
interfaces []InterfaceGenerator
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
config: map[string]string{
|
||||||
|
"interface.0.dhcp": "yes",
|
||||||
|
},
|
||||||
|
interfaces: []InterfaceGenerator{
|
||||||
|
&physicalInterface{logicalInterface{
|
||||||
|
config: configMethodDHCP{},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
config: map[string]string{
|
||||||
|
"interface.0.mac": "00:11:22:33:44:55",
|
||||||
|
"interface.0.dhcp": "yes",
|
||||||
|
},
|
||||||
|
interfaces: []InterfaceGenerator{
|
||||||
|
&physicalInterface{logicalInterface{
|
||||||
|
hwaddr: mustParseMac(net.ParseMAC("00:11:22:33:44:55")),
|
||||||
|
config: configMethodDHCP{hwaddress: mustParseMac(net.ParseMAC("00:11:22:33:44:55"))},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
config: map[string]string{
|
||||||
|
"interface.0.name": "eth0",
|
||||||
|
"interface.0.dhcp": "yes",
|
||||||
|
},
|
||||||
|
interfaces: []InterfaceGenerator{
|
||||||
|
&physicalInterface{logicalInterface{
|
||||||
|
name: "eth0",
|
||||||
|
config: configMethodDHCP{},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
config: map[string]string{
|
||||||
|
"interface.0.mac": "00:11:22:33:44:55",
|
||||||
|
"interface.0.ip.0.address": "10.0.0.100/24",
|
||||||
|
"interface.0.route.0.gateway": "10.0.0.1",
|
||||||
|
"interface.0.route.0.destination": "0.0.0.0/0",
|
||||||
|
},
|
||||||
|
interfaces: []InterfaceGenerator{
|
||||||
|
&physicalInterface{logicalInterface{
|
||||||
|
hwaddr: mustParseMac(net.ParseMAC("00:11:22:33:44:55")),
|
||||||
|
config: configMethodStatic{
|
||||||
|
hwaddress: mustParseMac(net.ParseMAC("00:11:22:33:44:55")),
|
||||||
|
addresses: []net.IPNet{net.IPNet{IP: net.ParseIP("10.0.0.100"), Mask: net.CIDRMask(24, net.IPv4len*8)}},
|
||||||
|
// I realize how upset you must be that I am shoving an IPMask into an IP. This is because net.IPv4zero is
|
||||||
|
// actually a magic IPv6 address which ruins our equality check. What's that? Just use IP::Equal()? I'd rather
|
||||||
|
// DeepEqual just handle that for me, but until Go gets operator overloading, we are stuck with this.
|
||||||
|
routes: []route{route{
|
||||||
|
destination: net.IPNet{IP: net.IP(net.CIDRMask(0, net.IPv4len*8)), Mask: net.CIDRMask(0, net.IPv4len*8)},
|
||||||
|
gateway: net.ParseIP("10.0.0.1")},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
config: map[string]string{
|
||||||
|
"dns.server.0": "1.2.3.4",
|
||||||
|
"dns.server.1": "5.6.7.8",
|
||||||
|
"interface.0.mac": "00:11:22:33:44:55",
|
||||||
|
"interface.0.ip.0.address": "10.0.0.100/24",
|
||||||
|
"interface.0.ip.1.address": "10.0.0.101/24",
|
||||||
|
"interface.0.route.0.gateway": "10.0.0.1",
|
||||||
|
"interface.0.route.0.destination": "0.0.0.0/0",
|
||||||
|
"interface.1.name": "eth0",
|
||||||
|
"interface.1.ip.0.address": "10.0.1.100/24",
|
||||||
|
"interface.1.route.0.gateway": "10.0.1.1",
|
||||||
|
"interface.1.route.0.destination": "0.0.0.0/0",
|
||||||
|
"interface.2.dhcp": "yes",
|
||||||
|
"interface.2.mac": "00:11:22:33:44:77",
|
||||||
|
},
|
||||||
|
interfaces: []InterfaceGenerator{
|
||||||
|
&physicalInterface{logicalInterface{
|
||||||
|
hwaddr: mustParseMac(net.ParseMAC("00:11:22:33:44:55")),
|
||||||
|
config: configMethodStatic{
|
||||||
|
hwaddress: mustParseMac(net.ParseMAC("00:11:22:33:44:55")),
|
||||||
|
addresses: []net.IPNet{
|
||||||
|
net.IPNet{IP: net.ParseIP("10.0.0.100"), Mask: net.CIDRMask(24, net.IPv4len*8)},
|
||||||
|
net.IPNet{IP: net.ParseIP("10.0.0.101"), Mask: net.CIDRMask(24, net.IPv4len*8)},
|
||||||
|
},
|
||||||
|
routes: []route{route{
|
||||||
|
destination: net.IPNet{IP: net.IP(net.CIDRMask(0, net.IPv4len*8)), Mask: net.CIDRMask(0, net.IPv4len*8)},
|
||||||
|
gateway: net.ParseIP("10.0.0.1")},
|
||||||
|
},
|
||||||
|
nameservers: []net.IP{net.ParseIP("1.2.3.4"), net.ParseIP("5.6.7.8")},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
&physicalInterface{logicalInterface{
|
||||||
|
name: "eth0",
|
||||||
|
config: configMethodStatic{
|
||||||
|
addresses: []net.IPNet{net.IPNet{IP: net.ParseIP("10.0.1.100"), Mask: net.CIDRMask(24, net.IPv4len*8)}},
|
||||||
|
routes: []route{route{
|
||||||
|
destination: net.IPNet{IP: net.IP(net.CIDRMask(0, net.IPv4len*8)), Mask: net.CIDRMask(0, net.IPv4len*8)},
|
||||||
|
gateway: net.ParseIP("10.0.1.1")},
|
||||||
|
},
|
||||||
|
nameservers: []net.IP{net.ParseIP("1.2.3.4"), net.ParseIP("5.6.7.8")},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
&physicalInterface{logicalInterface{
|
||||||
|
hwaddr: mustParseMac(net.ParseMAC("00:11:22:33:44:77")),
|
||||||
|
config: configMethodDHCP{hwaddress: mustParseMac(net.ParseMAC("00:11:22:33:44:77"))},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
config: map[string]string{"dns.server.0": "test dns"},
|
||||||
|
err: errors.New(`invalid nameserver: "test dns"`),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range tests {
|
||||||
|
interfaces, err := ProcessVMwareNetconf(tt.config)
|
||||||
|
if !reflect.DeepEqual(tt.err, err) {
|
||||||
|
t.Errorf("bad error (#%d): want %v, got %v", i, tt.err, err)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(tt.interfaces, interfaces) {
|
||||||
|
t.Errorf("bad interfaces (#%d): want %#v, got %#v", i, tt.interfaces, interfaces)
|
||||||
|
for _, iface := range tt.interfaces {
|
||||||
|
t.Logf(" want: %#v", iface)
|
||||||
|
}
|
||||||
|
for _, iface := range interfaces {
|
||||||
|
t.Logf(" got: %#v", iface)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProcessAddressConfig(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
config map[string]string
|
||||||
|
prefix string
|
||||||
|
|
||||||
|
addresses []net.IPNet
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
{},
|
||||||
|
|
||||||
|
// static - ipv4
|
||||||
|
{
|
||||||
|
config: map[string]string{
|
||||||
|
"ip.0.address": "10.0.0.100/24",
|
||||||
|
},
|
||||||
|
|
||||||
|
addresses: []net.IPNet{{IP: net.ParseIP("10.0.0.100"), Mask: net.CIDRMask(24, net.IPv4len*8)}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
config: map[string]string{
|
||||||
|
"this.is.a.prefix.ip.0.address": "10.0.0.100/24",
|
||||||
|
},
|
||||||
|
prefix: "this.is.a.prefix.",
|
||||||
|
|
||||||
|
addresses: []net.IPNet{{IP: net.ParseIP("10.0.0.100"), Mask: net.CIDRMask(24, net.IPv4len*8)}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
config: map[string]string{
|
||||||
|
"ip.0.address": "10.0.0.100/24",
|
||||||
|
"ip.1.address": "10.0.0.101/24",
|
||||||
|
"ip.2.address": "10.0.0.102/24",
|
||||||
|
},
|
||||||
|
|
||||||
|
addresses: []net.IPNet{
|
||||||
|
{IP: net.ParseIP("10.0.0.100"), Mask: net.CIDRMask(24, net.IPv4len*8)},
|
||||||
|
{IP: net.ParseIP("10.0.0.101"), Mask: net.CIDRMask(24, net.IPv4len*8)},
|
||||||
|
{IP: net.ParseIP("10.0.0.102"), Mask: net.CIDRMask(24, net.IPv4len*8)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// static - ipv6
|
||||||
|
{
|
||||||
|
config: map[string]string{
|
||||||
|
"ip.0.address": "fe00::100/64",
|
||||||
|
},
|
||||||
|
|
||||||
|
addresses: []net.IPNet{{IP: net.ParseIP("fe00::100"), Mask: net.IPMask(net.CIDRMask(64, net.IPv6len*8))}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
config: map[string]string{
|
||||||
|
"ip.0.address": "fe00::100/64",
|
||||||
|
"ip.1.address": "fe00::101/64",
|
||||||
|
"ip.2.address": "fe00::102/64",
|
||||||
|
},
|
||||||
|
|
||||||
|
addresses: []net.IPNet{
|
||||||
|
{IP: net.ParseIP("fe00::100"), Mask: net.CIDRMask(64, net.IPv6len*8)},
|
||||||
|
{IP: net.ParseIP("fe00::101"), Mask: net.CIDRMask(64, net.IPv6len*8)},
|
||||||
|
{IP: net.ParseIP("fe00::102"), Mask: net.CIDRMask(64, net.IPv6len*8)},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// invalid
|
||||||
|
{
|
||||||
|
config: map[string]string{
|
||||||
|
"ip.0.address": "test address",
|
||||||
|
},
|
||||||
|
|
||||||
|
err: errors.New(`invalid address: "test address"`),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range tests {
|
||||||
|
addresses, err := processAddressConfig(tt.config, tt.prefix)
|
||||||
|
if !reflect.DeepEqual(tt.err, err) {
|
||||||
|
t.Errorf("bad error (#%d): want %v, got %v", i, tt.err, err)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(tt.addresses, addresses) {
|
||||||
|
t.Errorf("bad addresses (#%d): want %#v, got %#v", i, tt.addresses, addresses)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProcessRouteConfig(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
config map[string]string
|
||||||
|
prefix string
|
||||||
|
|
||||||
|
routes []route
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
{},
|
||||||
|
|
||||||
|
{
|
||||||
|
config: map[string]string{
|
||||||
|
"route.0.gateway": "10.0.0.1",
|
||||||
|
"route.0.destination": "0.0.0.0/0",
|
||||||
|
},
|
||||||
|
|
||||||
|
routes: []route{{destination: net.IPNet{IP: net.IP(net.CIDRMask(0, net.IPv4len*8)), Mask: net.CIDRMask(0, net.IPv4len*8)}, gateway: net.ParseIP("10.0.0.1")}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
config: map[string]string{
|
||||||
|
"this.is.a.prefix.route.0.gateway": "10.0.0.1",
|
||||||
|
"this.is.a.prefix.route.0.destination": "0.0.0.0/0",
|
||||||
|
},
|
||||||
|
prefix: "this.is.a.prefix.",
|
||||||
|
|
||||||
|
routes: []route{{destination: net.IPNet{IP: net.IP(net.CIDRMask(0, net.IPv4len*8)), Mask: net.CIDRMask(0, net.IPv4len*8)}, gateway: net.ParseIP("10.0.0.1")}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
config: map[string]string{
|
||||||
|
"route.0.gateway": "fe00::1",
|
||||||
|
"route.0.destination": "::/0",
|
||||||
|
},
|
||||||
|
|
||||||
|
routes: []route{{destination: net.IPNet{IP: net.IPv6zero, Mask: net.IPMask(net.IPv6zero)}, gateway: net.ParseIP("fe00::1")}},
|
||||||
|
},
|
||||||
|
|
||||||
|
// invalid
|
||||||
|
{
|
||||||
|
config: map[string]string{
|
||||||
|
"route.0.gateway": "test gateway",
|
||||||
|
"route.0.destination": "0.0.0.0/0",
|
||||||
|
},
|
||||||
|
|
||||||
|
err: errors.New(`invalid gateway: "test gateway"`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
config: map[string]string{
|
||||||
|
"route.0.gateway": "10.0.0.1",
|
||||||
|
"route.0.destination": "test destination",
|
||||||
|
},
|
||||||
|
|
||||||
|
err: &net.ParseError{Type: "CIDR address", Text: "test destination"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range tests {
|
||||||
|
routes, err := processRouteConfig(tt.config, tt.prefix)
|
||||||
|
if !reflect.DeepEqual(tt.err, err) {
|
||||||
|
t.Errorf("bad error (#%d): want %v, got %v", i, tt.err, err)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(tt.routes, routes) {
|
||||||
|
t.Errorf("bad routes (#%d): want %#v, got %#v", i, tt.routes, routes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProcessDHCPConfig(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
config map[string]string
|
||||||
|
prefix string
|
||||||
|
|
||||||
|
dhcp bool
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
{},
|
||||||
|
|
||||||
|
// prefix
|
||||||
|
{config: map[string]string{"this.is.a.prefix.mac": ""}, prefix: "this.is.a.prefix.", dhcp: false},
|
||||||
|
{config: map[string]string{"this.is.a.prefix.dhcp": "yes"}, prefix: "this.is.a.prefix.", dhcp: true},
|
||||||
|
|
||||||
|
// dhcp
|
||||||
|
{config: map[string]string{"dhcp": "yes"}, dhcp: true},
|
||||||
|
{config: map[string]string{"dhcp": "no"}, dhcp: false},
|
||||||
|
|
||||||
|
// invalid
|
||||||
|
{config: map[string]string{"dhcp": "blah"}, err: errors.New(`invalid DHCP option: "blah"`)},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range tests {
|
||||||
|
dhcp, err := processDHCPConfig(tt.config, tt.prefix)
|
||||||
|
if !reflect.DeepEqual(tt.err, err) {
|
||||||
|
t.Errorf("bad error (#%d): want %v, got %v", i, tt.err, err)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.dhcp != dhcp {
|
||||||
|
t.Errorf("bad dhcp (#%d): want %v, got %v", i, tt.dhcp, dhcp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user