func (*UtilsSuite) TestParseInterfaceType(c *gc.C) { fakeSysPath := filepath.Join(c.MkDir(), network.SysClassNetPath) err := os.MkdirAll(fakeSysPath, 0700) c.Check(err, jc.ErrorIsNil) writeFakeUEvent := func(interfaceName string, lines ...string) string { fakeInterfacePath := filepath.Join(fakeSysPath, interfaceName) err := os.MkdirAll(fakeInterfacePath, 0700) c.Check(err, jc.ErrorIsNil) fakeUEventPath := filepath.Join(fakeInterfacePath, "uevent") contents := strings.Join(lines, "\n") err = ioutil.WriteFile(fakeUEventPath, []byte(contents), 0644) c.Check(err, jc.ErrorIsNil) return fakeUEventPath } result := network.ParseInterfaceType(fakeSysPath, "missing") c.Check(result, gc.Equals, network.UnknownInterface) writeFakeUEvent("eth0", "IFINDEX=1", "INTERFACE=eth0") result = network.ParseInterfaceType(fakeSysPath, "eth0") c.Check(result, gc.Equals, network.UnknownInterface) fakeUEventPath := writeFakeUEvent("eth0.42", "DEVTYPE=vlan") result = network.ParseInterfaceType(fakeSysPath, "eth0.42") c.Check(result, gc.Equals, network.VLAN_8021QInterface) os.Chmod(fakeUEventPath, 0000) // permission denied error is OK result = network.ParseInterfaceType(fakeSysPath, "eth0.42") c.Check(result, gc.Equals, network.UnknownInterface) writeFakeUEvent("bond0", "DEVTYPE=bond") result = network.ParseInterfaceType(fakeSysPath, "bond0") c.Check(result, gc.Equals, network.BondInterface) writeFakeUEvent("br-ens4", "DEVTYPE=bridge") result = network.ParseInterfaceType(fakeSysPath, "br-ens4") c.Check(result, gc.Equals, network.BridgeInterface) // First DEVTYPE found wins. writeFakeUEvent("foo", "DEVTYPE=vlan", "DEVTYPE=bridge") result = network.ParseInterfaceType(fakeSysPath, "foo") c.Check(result, gc.Equals, network.VLAN_8021QInterface) writeFakeUEvent("fake", "DEVTYPE=warp-drive") result = network.ParseInterfaceType(fakeSysPath, "fake") c.Check(result, gc.Equals, network.UnknownInterface) }
// GetObservedNetworkConfig uses the given source to find all available network // interfaces and their assigned addresses, and returns the result as // []params.NetworkConfig. In addition to what the source returns, a few // additional transformations are done: // // * On any OS, the state (UP/DOWN) of each interface and the DeviceIndex field, // will be correctly populated. Loopback interfaces are also properly detected // and will have InterfaceType set LoopbackInterface. // * On Linux only, the InterfaceType field will be reliably detected for a few // types: BondInterface, BridgeInterface, VLAN_8021QInterface. // * Also on Linux, for interfaces that are discovered to be ports on a bridge, // the ParentInterfaceName will be populated with the name of the bridge. // * ConfigType fields will be set to ConfigManual when no address is detected, // or ConfigStatic when it is. // * TODO: any IPv6 addresses found will be ignored and treated as empty ATM. // // Result entries will be grouped by InterfaceName, in the same order they are // returned by the given source. func GetObservedNetworkConfig(source NetworkConfigSource) ([]params.NetworkConfig, error) { logger.Tracef("discovering observed machine network config...") interfaces, err := source.Interfaces() if err != nil { return nil, errors.Annotate(err, "cannot get network interfaces") } var namesOrder []string nameToConfigs := make(map[string][]params.NetworkConfig) sysClassNetPath := source.SysClassNetPath() for _, nic := range interfaces { nicType := network.ParseInterfaceType(sysClassNetPath, nic.Name) nicConfig := interfaceToNetworkConfig(nic, nicType) if nicType == network.BridgeInterface { updateParentForBridgePorts(nic.Name, sysClassNetPath, nameToConfigs) } seenSoFar := false if existing, ok := nameToConfigs[nic.Name]; ok { nicConfig.ParentInterfaceName = existing[0].ParentInterfaceName // If only ParentInterfaceName was set in a previous iteration (e.g. // if the bridge appeared before the port), treat the interface as // not yet seen. seenSoFar = existing[0].InterfaceName != "" } if !seenSoFar { nameToConfigs[nic.Name] = []params.NetworkConfig(nil) namesOrder = append(namesOrder, nic.Name) } addrs, err := source.InterfaceAddresses(nic.Name) if err != nil { return nil, errors.Annotatef(err, "cannot get interface %q addresses", nic.Name) } if len(addrs) == 0 { logger.Infof("no addresses observed on interface %q", nic.Name) nameToConfigs[nic.Name] = append(nameToConfigs[nic.Name], nicConfig) continue } for _, addr := range addrs { addressConfig, err := interfaceAddressToNetworkConfig(nic.Name, nicConfig.ConfigType, addr) if err != nil { return nil, errors.Trace(err) } // Need to copy nicConfig so only the fields relevant for the // current address are updated. nicConfigCopy := nicConfig nicConfigCopy.Address = addressConfig.Address nicConfigCopy.CIDR = addressConfig.CIDR nicConfigCopy.ConfigType = addressConfig.ConfigType nameToConfigs[nic.Name] = append(nameToConfigs[nic.Name], nicConfigCopy) } } // Return all interfaces configs in input order. var observedConfig []params.NetworkConfig for _, name := range namesOrder { observedConfig = append(observedConfig, nameToConfigs[name]...) } logger.Tracef("observed network config: %+v", observedConfig) return observedConfig, nil }