diff --git a/config/cloudinit/datasource/vmware/vmware.go b/config/cloudinit/datasource/vmware/vmware.go index 7f198c3a..228cca6c 100755 --- a/config/cloudinit/datasource/vmware/vmware.go +++ b/config/cloudinit/datasource/vmware/vmware.go @@ -17,9 +17,12 @@ package vmware import ( "fmt" "net" + "strings" "github.com/rancher/os/config/cloudinit/config" "github.com/rancher/os/config/cloudinit/datasource" + "github.com/rancher/os/log" + "github.com/rancher/os/netconf" ) type readConfigFunction func(key string) (string, error) @@ -48,51 +51,83 @@ func (v VMWare) ConfigRoot() string { return "/" } +func (v VMWare) read(keytmpl string, args ...interface{}) (string, error) { + key := fmt.Sprintf(keytmpl, args...) + return v.readConfig(key) +} + func (v VMWare) FetchMetadata() (metadata datasource.Metadata, err error) { + metadata.NetworkConfig = netconf.NetworkConfig{} 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 + //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++ { + val, _ := v.read("dns.server.%d", i) + if val == "" { + break } - return val + metadata.NetworkConfig.DNS.Nameservers = append(metadata.NetworkConfig.DNS.Nameservers, val) } for i := 0; ; i++ { - if nameserver := saveConfig("dns.server.%d", i); nameserver == "" { - break - } - } - - for i := 0; ; i++ { - if domain := saveConfig("dns.domain.%d", i); domain == "" { + //if domain := saveConfig("dns.domain.%d", i); domain == "" { + val, _ := v.read("dns.domain.%d", i) + if val == "" { break } + metadata.NetworkConfig.DNS.Search = append(metadata.NetworkConfig.DNS.Search, val) } + metadata.NetworkConfig.Interfaces = make(map[string]netconf.InterfaceConfig) 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 + ethName := fmt.Sprintf("eth%d", i) + netDevice := netconf.InterfaceConfig{ + DHCP: true, + Match: ethName, + Addresses: []string{}, + } + //found = (saveConfig("interface.%d.name", i) != "") || found + if val, _ := v.read("interface.%d.name", i); val != "" { + netDevice.Match = val + found = true + } + //found = (saveConfig("interface.%d.mac", i) != "") || found + if val, _ := v.read("interface.%d.mac", i); val != "" { + netDevice.Match = "mac:" + val + found = true + } + //found = (saveConfig("interface.%d.dhcp", i) != "") || found + if val, _ := v.read("interface.%d.dhcp", i); val != "" { + netDevice.DHCP = (strings.ToLower(val) != "no") + found = true + } - role, _ := v.readConfig(fmt.Sprintf("interface.%d.role", i)) + role, _ := v.read("interface.%d.role", i) for a := 0; ; a++ { - address := saveConfig("interface.%d.ip.%d.address", i, a) + address, _ := v.read("interface.%d.ip.%d.address", i, a) if address == "" { break - } else { - found = true } + netDevice.Addresses = append(netDevice.Addresses, address) + found = true + netDevice.DHCP = false ip, _, err := net.ParseCIDR(address) if err != nil { - return metadata, err + log.Error(err) + //return metadata, err } switch role { @@ -110,28 +145,34 @@ func (v VMWare) FetchMetadata() (metadata datasource.Metadata, err error) { } case "": default: - return metadata, fmt.Errorf("unrecognized role: %q", role) + //return metadata, fmt.Errorf("unrecognized role: %q", role) + log.Error(err) } } for r := 0; ; r++ { - gateway := saveConfig("interface.%d.route.%d.gateway", i, r) - destination := saveConfig("interface.%d.route.%d.destination", i, r) + gateway, _ := v.read("interface.%d.route.%d.gateway", i, r) + // TODO: do we really not do anything but default routing? + //destination, _ := v.read("interface.%d.route.%d.destination", i, r) + destination := "" if gateway == "" && destination == "" { break } else { + netDevice.Gateway = gateway found = true } } + if found { + metadata.NetworkConfig.Interfaces[ethName] = netDevice + } } - // metadata.NetworkConfig = netconf return } func (v VMWare) FetchUserdata() ([]byte, error) { - encoding, err := v.readConfig("cloud-init.data.encoding") + encoding, err := v.readConfig("cloud-init.config.data.encoding") if err != nil { return nil, err } diff --git a/config/cloudinit/datasource/vmware/vmware_test.go b/config/cloudinit/datasource/vmware/vmware_test.go index 3ec0f170..d45b9afb 100644 --- a/config/cloudinit/datasource/vmware/vmware_test.go +++ b/config/cloudinit/datasource/vmware/vmware_test.go @@ -17,6 +17,7 @@ package vmware import ( "errors" + "fmt" "io/ioutil" "net" "os" @@ -24,11 +25,13 @@ import ( "testing" "github.com/rancher/os/config/cloudinit/datasource" + "github.com/rancher/os/netconf" ) type MockHypervisor map[string]string func (h MockHypervisor) ReadConfig(key string) (string, error) { + fmt.Printf("read(%s) %s\n", key, h[key]) return h[key], nil } @@ -53,26 +56,48 @@ func TestFetchMetadata(t *testing.T) { }{ { variables: map[string]string{ + "hostname": "first", "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", - // }, + Hostname: "first", + NetworkConfig: netconf.NetworkConfig{ + Interfaces: map[string]netconf.InterfaceConfig{ + "eth0": netconf.InterfaceConfig{ + Match: "mac:test mac", + DHCP: true, + Addresses: []string{}, + }, + }, + }, + // NetworkConfig: map[string]string{ + // "interface.0.mac": "test mac", + // "interface.0.dhcp": "yes", + // }, }, }, { variables: map[string]string{ + "hostname": "second", "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", - // }, + Hostname: "second", + NetworkConfig: netconf.NetworkConfig{ + Interfaces: map[string]netconf.InterfaceConfig{ + "eth0": netconf.InterfaceConfig{ + Match: "test name", + DHCP: true, + Addresses: []string{}, + }, + }, + }, + // NetworkConfig: map[string]string{ + // "interface.0.name": "test name", + // "interface.0.dhcp": "yes", + // }, }, }, { @@ -87,6 +112,19 @@ func TestFetchMetadata(t *testing.T) { metadata: datasource.Metadata{ Hostname: "test host", PrivateIPv6: net.ParseIP("fe00::100"), + NetworkConfig: netconf.NetworkConfig{ + Interfaces: map[string]netconf.InterfaceConfig{ + "eth0": netconf.InterfaceConfig{ + Match: "mac:test mac", + DHCP: false, + Addresses: []string{ + "fe00::100/64", + }, + Gateway: "fe00::1", + //TODO: Destination + }, + }, + }, // NetworkConfig: map[string]string{ // "interface.0.mac": "test mac", // "interface.0.ip.0.address": "fe00::100/64", @@ -114,6 +152,29 @@ func TestFetchMetadata(t *testing.T) { Hostname: "test host", PublicIPv4: net.ParseIP("10.0.0.101"), PrivateIPv4: net.ParseIP("10.0.0.102"), + NetworkConfig: netconf.NetworkConfig{ + Interfaces: map[string]netconf.InterfaceConfig{ + "eth0": netconf.InterfaceConfig{ + Match: "test name", + DHCP: false, + Addresses: []string{ + "10.0.0.100/24", + "10.0.0.101/24", + }, + Gateway: "10.0.0.1", + //TODO: Destination + }, + "eth1": netconf.InterfaceConfig{ + Match: "mac:test mac", + DHCP: false, + Addresses: []string{ + "10.0.0.102/24", + }, + Gateway: "10.0.0.2", + //TODO: Destination + }, + }, + }, // NetworkConfig: map[string]string{ // "interface.0.name": "test name", // "interface.0.ip.0.address": "10.0.0.100/24", @@ -150,45 +211,45 @@ func TestFetchUserdata(t *testing.T) { }{ {}, { - variables: map[string]string{"coreos.config.data": "test config"}, + variables: map[string]string{"cloud-init.config.data": "test config"}, userdata: "test config", }, { variables: map[string]string{ - "coreos.config.data.encoding": "", - "coreos.config.data": "test config", + "cloud-init.config.data.encoding": "", + "cloud-init.config.data": "test config", }, userdata: "test config", }, { variables: map[string]string{ - "coreos.config.data.encoding": "base64", - "coreos.config.data": "dGVzdCBjb25maWc=", + "cloud-init.config.data.encoding": "base64", + "cloud-init.config.data": "dGVzdCBjb25maWc=", }, userdata: "test config", }, { variables: map[string]string{ - "coreos.config.data.encoding": "gzip+base64", - "coreos.config.data": "H4sIABaoWlUAAytJLS5RSM7PS8tMBwCQiHNZCwAAAA==", + "cloud-init.config.data.encoding": "gzip+base64", + "cloud-init.config.data": "H4sIABaoWlUAAytJLS5RSM7PS8tMBwCQiHNZCwAAAA==", }, userdata: "test config", }, { variables: map[string]string{ - "coreos.config.data.encoding": "test encoding", + "cloud-init.config.data.encoding": "test encoding", }, err: errors.New(`Unsupported encoding "test encoding"`), }, { variables: map[string]string{ - "coreos.config.url": "http://good.example.com", + "cloud-init.config.url": "http://good.example.com", }, userdata: "test config", }, { variables: map[string]string{ - "coreos.config.url": "http://bad.example.com", + "cloud-init.config.url": "http://bad.example.com", }, err: errors.New("Not found"), }, @@ -239,7 +300,7 @@ func TestOvfTransport(t *testing.T) { - + @@ -258,17 +319,29 @@ func TestOvfTransport(t *testing.T) { 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", - // }, + NetworkConfig: netconf.NetworkConfig{ + Interfaces: map[string]netconf.InterfaceConfig{ + "eth0": netconf.InterfaceConfig{ + Match: "test name", + DHCP: false, + Addresses: []string{ + "10.0.0.100/24", + "10.0.0.101/24", + }, + Gateway: "10.0.0.1", + //TODO: Destination + }, + "eth1": netconf.InterfaceConfig{ + Match: "mac:test mac", + DHCP: false, + Addresses: []string{ + "10.0.0.102/24", + }, + Gateway: "10.0.0.2", + //TODO: Destination + }, + }, + }, }, userdata: []byte("test config"), }, diff --git a/docs/os/running-rancheros/server/pxe/index.md b/docs/os/running-rancheros/server/pxe/index.md index 67a2acef..f0414a88 100644 --- a/docs/os/running-rancheros/server/pxe/index.md +++ b/docs/os/running-rancheros/server/pxe/index.md @@ -62,10 +62,29 @@ Valid cloud-init datasources for RancherOS. | gce | | | | packet | DefaultAddress | | | url | url | | -| vmware | | set `guestinfo.cloud-init.data.data`, `guestinfo.cloud-init.data.encoding`, or `guestinfo.cloud-init.data.url` | +| vmware | | set `guestinfo.cloud-init.config.data`, `guestinfo.cloud-init.config.data.encoding`, or `guestinfo.cloud-init.config.url` | | * | This will add ["configdrive", "vmware", "ec2", "digitalocean", "packet", "gce"] into the list of datasources to try | | ### Cloud-Config When booting via iPXE, RancherOS can be configured using a [cloud-config file]({{site.baseurl}}/os/configuration/#cloud-config). +### VMware guestinfo + +| GUESTINFO VARIABLE | TYPE | +|---|---| +| `hostname | hostname | +| `interface..name` | string | +| `interface..mac` | MAC address (is used to match the ethernet device's MAC address, not to set it) | +| `interface..dhcp` | {"yes", "no"} | +| `interface..role` | {"public", "private"} | +| `interface..ip..address` | CIDR IP address | +| `interface..route..gateway` | IP address | +| `interface..route..destination` | CIDR IP address (not available yet) | +| `dns.server.` | IP address | +| `dns.domain. | DNS search domain` | +| `cloud-init.config.data | string` | +| `cloud-init.config.data.encoding` | {"", "base64", "gzip+base64"} | +| `cloud-init.config.url` | URL | + +> **Note:** "n", "m", "l", "x" and "y" are 0-indexed, incrementing integers. The identifier for an interface (``) is used in the generation of the default interface name in the form `eth`.