github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/apiserver/common/networkingcommon/types.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package networkingcommon
     5  
     6  import (
     7  	"net"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/utils/set"
    11  	"gopkg.in/juju/names.v2"
    12  
    13  	"github.com/juju/juju/apiserver/params"
    14  	"github.com/juju/juju/environs"
    15  	"github.com/juju/juju/network"
    16  	providercommon "github.com/juju/juju/provider/common"
    17  	"github.com/juju/juju/state"
    18  )
    19  
    20  // BackingSubnet defines the methods supported by a Subnet entity
    21  // stored persistently.
    22  //
    23  // TODO(dimitern): Once the state backing is implemented, remove this
    24  // and just use *state.Subnet.
    25  type BackingSubnet interface {
    26  	CIDR() string
    27  	VLANTag() int
    28  	ProviderId() network.Id
    29  	AvailabilityZones() []string
    30  	Status() string
    31  	SpaceName() string
    32  	Life() params.Life
    33  }
    34  
    35  // BackingSubnetInfo describes a single subnet to be added in the
    36  // backing store.
    37  //
    38  // TODO(dimitern): Replace state.SubnetInfo with this and remove
    39  // BackingSubnetInfo, once the rest of state backing methods and the
    40  // following pre-reqs are done:
    41  // * subnetDoc.AvailabilityZone becomes subnetDoc.AvailabilityZones,
    42  //   adding an upgrade step to migrate existing non empty zones on
    43  //   subnet docs. Also change state.Subnet.AvailabilityZone to
    44  // * add subnetDoc.SpaceName - no upgrade step needed, as it will only
    45  //   be used for new space-aware subnets.
    46  // * Subnets need a reference count to calculate Status.
    47  // * ensure EC2 and MAAS providers accept empty IDs as Subnets() args
    48  //   and return all subnets, including the AvailabilityZones (for EC2;
    49  //   empty for MAAS as zones are orthogonal to networks).
    50  type BackingSubnetInfo struct {
    51  	// ProviderId is a provider-specific network id. This may be empty.
    52  	ProviderId network.Id
    53  
    54  	// CIDR of the network, in 123.45.67.89/24 format.
    55  	CIDR string
    56  
    57  	// VLANTag needs to be between 1 and 4094 for VLANs and 0 for normal
    58  	// networks. It's defined by IEEE 802.1Q standard.
    59  	VLANTag int
    60  
    61  	// AvailabilityZones describes which availability zone(s) this
    62  	// subnet is in. It can be empty if the provider does not support
    63  	// availability zones.
    64  	AvailabilityZones []string
    65  
    66  	// SpaceName holds the juju network space this subnet is
    67  	// associated with. Can be empty if not supported.
    68  	SpaceName string
    69  
    70  	// Status holds the status of the subnet. Normally this will be
    71  	// calculated from the reference count and Life of a subnet.
    72  	Status string
    73  
    74  	// Live holds the life of the subnet
    75  	Life params.Life
    76  }
    77  
    78  // BackingSpace defines the methods supported by a Space entity stored
    79  // persistently.
    80  type BackingSpace interface {
    81  	// Name returns the space name.
    82  	Name() string
    83  
    84  	// Subnets returns the subnets in the space
    85  	Subnets() ([]BackingSubnet, error)
    86  
    87  	// ProviderId returns the network ID of the provider
    88  	ProviderId() network.Id
    89  
    90  	// Zones returns a list of availability zone(s) that this
    91  	// space is in. It can be empty if the provider does not support
    92  	// availability zones.
    93  	Zones() []string
    94  
    95  	// Life returns the lifecycle state of the space
    96  	Life() params.Life
    97  }
    98  
    99  // Backing defines the methods needed by the API facade to store and
   100  // retrieve information from the underlying persistency layer (state
   101  // DB).
   102  type NetworkBacking interface {
   103  	environs.EnvironConfigGetter
   104  
   105  	// AvailabilityZones returns all cached availability zones (i.e.
   106  	// not from the provider, but in state).
   107  	AvailabilityZones() ([]providercommon.AvailabilityZone, error)
   108  
   109  	// SetAvailabilityZones replaces the cached list of availability
   110  	// zones with the given zones.
   111  	SetAvailabilityZones([]providercommon.AvailabilityZone) error
   112  
   113  	// AddSpace creates a space
   114  	AddSpace(Name string, ProviderId network.Id, Subnets []string, Public bool) error
   115  
   116  	// AllSpaces returns all known Juju network spaces.
   117  	AllSpaces() ([]BackingSpace, error)
   118  
   119  	// AddSubnet creates a backing subnet for an existing subnet.
   120  	AddSubnet(BackingSubnetInfo) (BackingSubnet, error)
   121  
   122  	// AllSubnets returns all backing subnets.
   123  	AllSubnets() ([]BackingSubnet, error)
   124  
   125  	// ModelTag returns the tag of the model this state is associated to.
   126  	ModelTag() names.ModelTag
   127  }
   128  
   129  func BackingSubnetToParamsSubnet(subnet BackingSubnet) params.Subnet {
   130  	cidr := subnet.CIDR()
   131  	vlantag := subnet.VLANTag()
   132  	providerid := subnet.ProviderId()
   133  	zones := subnet.AvailabilityZones()
   134  	status := subnet.Status()
   135  	var spaceTag names.SpaceTag
   136  	if subnet.SpaceName() != "" {
   137  		spaceTag = names.NewSpaceTag(subnet.SpaceName())
   138  	}
   139  
   140  	return params.Subnet{
   141  		CIDR:       cidr,
   142  		VLANTag:    vlantag,
   143  		ProviderId: string(providerid),
   144  		Zones:      zones,
   145  		Status:     status,
   146  		SpaceTag:   spaceTag.String(),
   147  		Life:       subnet.Life(),
   148  	}
   149  }
   150  
   151  // NetworkConfigFromInterfaceInfo converts a slice of network.InterfaceInfo into
   152  // the equivalent params.NetworkConfig slice.
   153  func NetworkConfigFromInterfaceInfo(interfaceInfos []network.InterfaceInfo) []params.NetworkConfig {
   154  	result := make([]params.NetworkConfig, len(interfaceInfos))
   155  	for i, v := range interfaceInfos {
   156  		var dnsServers []string
   157  		for _, nameserver := range v.DNSServers {
   158  			dnsServers = append(dnsServers, nameserver.Value)
   159  		}
   160  		result[i] = params.NetworkConfig{
   161  			DeviceIndex:         v.DeviceIndex,
   162  			MACAddress:          v.MACAddress,
   163  			CIDR:                v.CIDR,
   164  			MTU:                 v.MTU,
   165  			ProviderId:          string(v.ProviderId),
   166  			ProviderSubnetId:    string(v.ProviderSubnetId),
   167  			ProviderSpaceId:     string(v.ProviderSpaceId),
   168  			ProviderVLANId:      string(v.ProviderVLANId),
   169  			ProviderAddressId:   string(v.ProviderAddressId),
   170  			VLANTag:             v.VLANTag,
   171  			InterfaceName:       v.InterfaceName,
   172  			ParentInterfaceName: v.ParentInterfaceName,
   173  			InterfaceType:       string(v.InterfaceType),
   174  			Disabled:            v.Disabled,
   175  			NoAutoStart:         v.NoAutoStart,
   176  			ConfigType:          string(v.ConfigType),
   177  			Address:             v.Address.Value,
   178  			DNSServers:          dnsServers,
   179  			DNSSearchDomains:    v.DNSSearchDomains,
   180  			GatewayAddress:      v.GatewayAddress.Value,
   181  		}
   182  	}
   183  	return result
   184  }
   185  
   186  // NetworkConfigsToStateArgs splits the given networkConfig into a slice of
   187  // state.LinkLayerDeviceArgs and a slice of state.LinkLayerDeviceAddress. The
   188  // input is expected to come from MergeProviderAndObservedNetworkConfigs and to
   189  // be sorted.
   190  func NetworkConfigsToStateArgs(networkConfig []params.NetworkConfig) (
   191  	[]state.LinkLayerDeviceArgs,
   192  	[]state.LinkLayerDeviceAddress,
   193  ) {
   194  	var devicesArgs []state.LinkLayerDeviceArgs
   195  	var devicesAddrs []state.LinkLayerDeviceAddress
   196  
   197  	logger.Tracef("transforming network config to state args: %+v", networkConfig)
   198  	seenDeviceNames := set.NewStrings()
   199  	for _, netConfig := range networkConfig {
   200  		logger.Tracef("transforming device %q", netConfig.InterfaceName)
   201  		if !seenDeviceNames.Contains(netConfig.InterfaceName) {
   202  			// First time we see this, add it to devicesArgs.
   203  			seenDeviceNames.Add(netConfig.InterfaceName)
   204  			var mtu uint
   205  			if netConfig.MTU >= 0 {
   206  				mtu = uint(netConfig.MTU)
   207  			}
   208  			args := state.LinkLayerDeviceArgs{
   209  				Name:        netConfig.InterfaceName,
   210  				MTU:         mtu,
   211  				ProviderID:  network.Id(netConfig.ProviderId),
   212  				Type:        state.LinkLayerDeviceType(netConfig.InterfaceType),
   213  				MACAddress:  netConfig.MACAddress,
   214  				IsAutoStart: !netConfig.NoAutoStart,
   215  				IsUp:        !netConfig.Disabled,
   216  				ParentName:  netConfig.ParentInterfaceName,
   217  			}
   218  			logger.Tracef("state device args for device: %+v", args)
   219  			devicesArgs = append(devicesArgs, args)
   220  		}
   221  
   222  		if netConfig.CIDR == "" || netConfig.Address == "" {
   223  			logger.Tracef(
   224  				"skipping empty CIDR %q and/or Address %q of %q",
   225  				netConfig.CIDR, netConfig.Address, netConfig.InterfaceName,
   226  			)
   227  			continue
   228  		}
   229  		_, ipNet, err := net.ParseCIDR(netConfig.CIDR)
   230  		if err != nil {
   231  			logger.Warningf("FIXME: ignoring unexpected CIDR format %q: %v", netConfig.CIDR, err)
   232  			continue
   233  		}
   234  		ipAddr := net.ParseIP(netConfig.Address)
   235  		if ipAddr == nil {
   236  			logger.Warningf("FIXME: ignoring unexpected Address format %q", netConfig.Address)
   237  			continue
   238  		}
   239  		ipNet.IP = ipAddr
   240  		cidrAddress := ipNet.String()
   241  
   242  		var derivedConfigMethod state.AddressConfigMethod
   243  		switch method := state.AddressConfigMethod(netConfig.ConfigType); method {
   244  		case state.StaticAddress, state.DynamicAddress,
   245  			state.LoopbackAddress, state.ManualAddress:
   246  			derivedConfigMethod = method
   247  		case "dhcp": // awkward special case
   248  			derivedConfigMethod = state.DynamicAddress
   249  		default:
   250  			derivedConfigMethod = state.StaticAddress
   251  		}
   252  
   253  		addr := state.LinkLayerDeviceAddress{
   254  			DeviceName:       netConfig.InterfaceName,
   255  			ProviderID:       network.Id(netConfig.ProviderAddressId),
   256  			ConfigMethod:     derivedConfigMethod,
   257  			CIDRAddress:      cidrAddress,
   258  			DNSServers:       netConfig.DNSServers,
   259  			DNSSearchDomains: netConfig.DNSSearchDomains,
   260  			GatewayAddress:   netConfig.GatewayAddress,
   261  		}
   262  		logger.Tracef("state address args for device: %+v", addr)
   263  		devicesAddrs = append(devicesAddrs, addr)
   264  	}
   265  	logger.Tracef("seen devices: %+v", seenDeviceNames.SortedValues())
   266  	logger.Tracef("network config transformed to state args:\n%+v\n%+v", devicesArgs, devicesAddrs)
   267  	return devicesArgs, devicesAddrs
   268  }
   269  
   270  // NetworkingEnvironFromModelConfig constructs and returns
   271  // environs.NetworkingEnviron using the given configGetter. Returns an error
   272  // satisfying errors.IsNotSupported() if the model config does not support
   273  // networking features.
   274  func NetworkingEnvironFromModelConfig(configGetter environs.EnvironConfigGetter) (environs.NetworkingEnviron, error) {
   275  	modelConfig, err := configGetter.ModelConfig()
   276  	if err != nil {
   277  		return nil, errors.Annotate(err, "failed to get model config")
   278  	}
   279  	if modelConfig.Type() == "dummy" {
   280  		return nil, errors.NotSupportedf("dummy provider network config")
   281  	}
   282  	env, err := environs.GetEnviron(configGetter, environs.New)
   283  	if err != nil {
   284  		return nil, errors.Annotate(err, "failed to construct a model from config")
   285  	}
   286  	netEnviron, supported := environs.SupportsNetworking(env)
   287  	if !supported {
   288  		// " not supported" will be appended to the message below.
   289  		return nil, errors.NotSupportedf("model %q networking", modelConfig.Name())
   290  	}
   291  	return netEnviron, nil
   292  }
   293  
   294  // NetworkConfigSource defines the necessary calls to obtain the network
   295  // configuration of a machine.
   296  type NetworkConfigSource interface {
   297  	// SysClassNetPath returns the Linux kernel userspace SYSFS path used by
   298  	// this source. DefaultNetworkConfigSource() uses network.SysClassNetPath.
   299  	SysClassNetPath() string
   300  
   301  	// Interfaces returns information about all network interfaces on the
   302  	// machine as []net.Interface.
   303  	Interfaces() ([]net.Interface, error)
   304  
   305  	// InterfaceAddresses returns information about all addresses assigned to
   306  	// the network interface with the given name.
   307  	InterfaceAddresses(name string) ([]net.Addr, error)
   308  }
   309  
   310  type netPackageConfigSource struct{}
   311  
   312  // SysClassNetPath implements NetworkConfigSource.
   313  func (n *netPackageConfigSource) SysClassNetPath() string {
   314  	return network.SysClassNetPath
   315  }
   316  
   317  // Interfaces implements NetworkConfigSource.
   318  func (n *netPackageConfigSource) Interfaces() ([]net.Interface, error) {
   319  	return net.Interfaces()
   320  }
   321  
   322  // InterfaceAddresses implements NetworkConfigSource.
   323  func (n *netPackageConfigSource) InterfaceAddresses(name string) ([]net.Addr, error) {
   324  	iface, err := net.InterfaceByName(name)
   325  	if err != nil {
   326  		return nil, errors.Trace(err)
   327  	}
   328  	return iface.Addrs()
   329  }
   330  
   331  // DefaultNetworkConfigSource returns a NetworkConfigSource backed by the net
   332  // package, to be used with GetObservedNetworkConfig().
   333  func DefaultNetworkConfigSource() NetworkConfigSource {
   334  	return &netPackageConfigSource{}
   335  }
   336  
   337  // GetObservedNetworkConfig uses the given source to find all available network
   338  // interfaces and their assigned addresses, and returns the result as
   339  // []params.NetworkConfig. In addition to what the source returns, a few
   340  // additional transformations are done:
   341  //
   342  // * On any OS, the state (UP/DOWN) of each interface and the DeviceIndex field,
   343  //   will be correctly populated. Loopback interfaces are also properly detected
   344  //   and will have InterfaceType set LoopbackInterface.
   345  // * On Linux only, the InterfaceType field will be reliably detected for a few
   346  //   types: BondInterface, BridgeInterface, VLAN_8021QInterface.
   347  // * Also on Linux, for interfaces that are discovered to be ports on a bridge,
   348  //   the ParentInterfaceName will be populated with the name of the bridge.
   349  // * ConfigType fields will be set to ConfigManual when no address is detected,
   350  //   or ConfigStatic when it is.
   351  // * TODO: any IPv6 addresses found will be ignored and treated as empty ATM.
   352  //
   353  // Result entries will be grouped by InterfaceName, in the same order they are
   354  // returned by the given source.
   355  func GetObservedNetworkConfig(source NetworkConfigSource) ([]params.NetworkConfig, error) {
   356  	logger.Tracef("discovering observed machine network config...")
   357  
   358  	interfaces, err := source.Interfaces()
   359  	if err != nil {
   360  		return nil, errors.Annotate(err, "cannot get network interfaces")
   361  	}
   362  
   363  	var namesOrder []string
   364  	nameToConfigs := make(map[string][]params.NetworkConfig)
   365  	sysClassNetPath := source.SysClassNetPath()
   366  	for _, nic := range interfaces {
   367  		nicType := network.ParseInterfaceType(sysClassNetPath, nic.Name)
   368  		nicConfig := interfaceToNetworkConfig(nic, nicType)
   369  
   370  		if nicType == network.BridgeInterface {
   371  			updateParentForBridgePorts(nic.Name, sysClassNetPath, nameToConfigs)
   372  		}
   373  
   374  		seenSoFar := false
   375  		if existing, ok := nameToConfigs[nic.Name]; ok {
   376  			nicConfig.ParentInterfaceName = existing[0].ParentInterfaceName
   377  			// If only ParentInterfaceName was set in a previous iteration (e.g.
   378  			// if the bridge appeared before the port), treat the interface as
   379  			// not yet seen.
   380  			seenSoFar = existing[0].InterfaceName != ""
   381  		}
   382  
   383  		if !seenSoFar {
   384  			nameToConfigs[nic.Name] = []params.NetworkConfig(nil)
   385  			namesOrder = append(namesOrder, nic.Name)
   386  		}
   387  
   388  		addrs, err := source.InterfaceAddresses(nic.Name)
   389  		if err != nil {
   390  			return nil, errors.Annotatef(err, "cannot get interface %q addresses", nic.Name)
   391  		}
   392  
   393  		if len(addrs) == 0 {
   394  			logger.Infof("no addresses observed on interface %q", nic.Name)
   395  			nameToConfigs[nic.Name] = append(nameToConfigs[nic.Name], nicConfig)
   396  			continue
   397  		}
   398  
   399  		for _, addr := range addrs {
   400  			addressConfig, err := interfaceAddressToNetworkConfig(nic.Name, nicConfig.ConfigType, addr)
   401  			if err != nil {
   402  				return nil, errors.Trace(err)
   403  			}
   404  
   405  			// Need to copy nicConfig so only the fields relevant for the
   406  			// current address are updated.
   407  			nicConfigCopy := nicConfig
   408  			nicConfigCopy.Address = addressConfig.Address
   409  			nicConfigCopy.CIDR = addressConfig.CIDR
   410  			nicConfigCopy.ConfigType = addressConfig.ConfigType
   411  			nameToConfigs[nic.Name] = append(nameToConfigs[nic.Name], nicConfigCopy)
   412  		}
   413  	}
   414  
   415  	// Return all interfaces configs in input order.
   416  	var observedConfig []params.NetworkConfig
   417  	for _, name := range namesOrder {
   418  		observedConfig = append(observedConfig, nameToConfigs[name]...)
   419  	}
   420  	logger.Tracef("observed network config: %+v", observedConfig)
   421  	return observedConfig, nil
   422  }
   423  
   424  func interfaceToNetworkConfig(nic net.Interface, nicType network.InterfaceType) params.NetworkConfig {
   425  	configType := network.ConfigManual // assume manual initially, until we parse the address.
   426  	isUp := nic.Flags&net.FlagUp > 0
   427  	isLoopback := nic.Flags&net.FlagLoopback > 0
   428  	isUnknown := nicType == network.UnknownInterface
   429  
   430  	switch {
   431  	case isUnknown && isLoopback:
   432  		nicType = network.LoopbackInterface
   433  		configType = network.ConfigLoopback
   434  	case isUnknown:
   435  		nicType = network.EthernetInterface
   436  	}
   437  
   438  	return params.NetworkConfig{
   439  		DeviceIndex:   nic.Index,
   440  		MACAddress:    nic.HardwareAddr.String(),
   441  		ConfigType:    string(configType),
   442  		MTU:           nic.MTU,
   443  		InterfaceName: nic.Name,
   444  		InterfaceType: string(nicType),
   445  		NoAutoStart:   !isUp,
   446  		Disabled:      !isUp,
   447  	}
   448  }
   449  
   450  func updateParentForBridgePorts(bridgeName, sysClassNetPath string, nameToConfigs map[string][]params.NetworkConfig) {
   451  	ports := network.GetBridgePorts(sysClassNetPath, bridgeName)
   452  	for _, portName := range ports {
   453  		portConfigs, ok := nameToConfigs[portName]
   454  		if ok {
   455  			portConfigs[0].ParentInterfaceName = bridgeName
   456  		} else {
   457  			portConfigs = []params.NetworkConfig{{ParentInterfaceName: bridgeName}}
   458  		}
   459  		nameToConfigs[portName] = portConfigs
   460  	}
   461  }
   462  
   463  func interfaceAddressToNetworkConfig(interfaceName, configType string, address net.Addr) (params.NetworkConfig, error) {
   464  	config := params.NetworkConfig{
   465  		ConfigType: configType,
   466  	}
   467  
   468  	cidrAddress := address.String()
   469  	if cidrAddress == "" {
   470  		return config, nil
   471  	}
   472  
   473  	ip, ipNet, err := net.ParseCIDR(cidrAddress)
   474  	if err != nil {
   475  		logger.Infof("cannot parse %q on interface %q as CIDR, trying as IP address: %v", cidrAddress, interfaceName, err)
   476  		if ip = net.ParseIP(cidrAddress); ip == nil {
   477  			return config, errors.Errorf("cannot parse IP address %q on interface %q", cidrAddress, interfaceName)
   478  		} else {
   479  			ipNet = &net.IPNet{IP: ip}
   480  		}
   481  	}
   482  	if ip.To4() == nil {
   483  		logger.Debugf("skipping observed IPv6 address %q on %q: not fully supported yet", ip, interfaceName)
   484  		// TODO(dimitern): Treat IPv6 addresses as empty until we can handle
   485  		// them reliably.
   486  		return config, nil
   487  	}
   488  
   489  	if ipNet.Mask != nil {
   490  		config.CIDR = ipNet.String()
   491  	}
   492  	config.Address = ip.String()
   493  	if configType != string(network.ConfigLoopback) {
   494  		config.ConfigType = string(network.ConfigStatic)
   495  	}
   496  
   497  	// TODO(dimitern): Add DNS servers, search domains, and gateway
   498  	// later.
   499  
   500  	return config, nil
   501  }
   502  
   503  // MergeProviderAndObservedNetworkConfigs returns the effective network configs,
   504  // using observedConfigs as a base and selectively updating it using the
   505  // matching providerConfigs for each interface.
   506  func MergeProviderAndObservedNetworkConfigs(providerConfigs, observedConfigs []params.NetworkConfig) []params.NetworkConfig {
   507  
   508  	providerConfigByName := networkConfigsByName(providerConfigs)
   509  	logger.Tracef("known provider config by name: %+v", providerConfigByName)
   510  
   511  	providerConfigByAddress := networkConfigsByAddress(providerConfigs)
   512  	logger.Tracef("known provider config by address: %+v", providerConfigByAddress)
   513  
   514  	var results []params.NetworkConfig
   515  	for _, observed := range observedConfigs {
   516  
   517  		name, ipAddress := observed.InterfaceName, observed.Address
   518  		finalConfig := observed
   519  
   520  		providerConfig, known := providerConfigByName[name]
   521  		if known {
   522  			finalConfig = mergeObservedAndProviderInterfaceConfig(finalConfig, providerConfig)
   523  			logger.Debugf("updated observed interface config for %q with: %+v", name, providerConfig)
   524  		}
   525  
   526  		providerConfig, known = providerConfigByAddress[ipAddress]
   527  		if known {
   528  			finalConfig = mergeObservedAndProviderAddressConfig(finalConfig, providerConfig)
   529  			logger.Debugf("updated observed address config for %q with: %+v", name, providerConfig)
   530  		}
   531  
   532  		results = append(results, finalConfig)
   533  		logger.Debugf("merged config for %q: %+v", name, finalConfig)
   534  	}
   535  
   536  	return results
   537  }
   538  
   539  func networkConfigsByName(input []params.NetworkConfig) map[string]params.NetworkConfig {
   540  	configsByName := make(map[string]params.NetworkConfig, len(input))
   541  	for _, config := range input {
   542  		configsByName[config.InterfaceName] = config
   543  	}
   544  	return configsByName
   545  }
   546  
   547  func networkConfigsByAddress(input []params.NetworkConfig) map[string]params.NetworkConfig {
   548  	configsByAddress := make(map[string]params.NetworkConfig, len(input))
   549  	for _, config := range input {
   550  		configsByAddress[config.Address] = config
   551  	}
   552  	return configsByAddress
   553  }
   554  
   555  func mergeObservedAndProviderInterfaceConfig(observedConfig, providerConfig params.NetworkConfig) params.NetworkConfig {
   556  	finalConfig := observedConfig
   557  
   558  	// The following fields cannot be observed and are only known by the
   559  	// provider.
   560  	finalConfig.ProviderId = providerConfig.ProviderId
   561  	finalConfig.ProviderVLANId = providerConfig.ProviderVLANId
   562  	finalConfig.ProviderSubnetId = providerConfig.ProviderSubnetId
   563  
   564  	// The following few fields are only updated if their observed values are
   565  	// empty.
   566  
   567  	if observedConfig.InterfaceType == "" {
   568  		finalConfig.InterfaceType = providerConfig.InterfaceType
   569  	}
   570  
   571  	if observedConfig.VLANTag == 0 {
   572  		finalConfig.VLANTag = providerConfig.VLANTag
   573  	}
   574  
   575  	if observedConfig.ParentInterfaceName == "" {
   576  		finalConfig.ParentInterfaceName = providerConfig.ParentInterfaceName
   577  	}
   578  
   579  	return finalConfig
   580  }
   581  
   582  func mergeObservedAndProviderAddressConfig(observedConfig, providerConfig params.NetworkConfig) params.NetworkConfig {
   583  	finalConfig := observedConfig
   584  
   585  	// The following fields cannot be observed and are only known by the
   586  	// provider.
   587  	finalConfig.ProviderAddressId = providerConfig.ProviderAddressId
   588  	finalConfig.ProviderSubnetId = providerConfig.ProviderSubnetId
   589  	finalConfig.ProviderSpaceId = providerConfig.ProviderSpaceId
   590  
   591  	// The following few fields are only updated if their observed values are
   592  	// empty.
   593  
   594  	if observedConfig.ProviderVLANId == "" {
   595  		finalConfig.ProviderVLANId = providerConfig.ProviderVLANId
   596  	}
   597  
   598  	if observedConfig.VLANTag == 0 {
   599  		finalConfig.VLANTag = providerConfig.VLANTag
   600  	}
   601  
   602  	if observedConfig.ConfigType == "" {
   603  		finalConfig.ConfigType = providerConfig.ConfigType
   604  	}
   605  
   606  	if observedConfig.CIDR == "" {
   607  		finalConfig.CIDR = providerConfig.CIDR
   608  	}
   609  
   610  	if observedConfig.GatewayAddress == "" {
   611  		finalConfig.GatewayAddress = providerConfig.GatewayAddress
   612  	}
   613  
   614  	if len(observedConfig.DNSServers) == 0 {
   615  		finalConfig.DNSServers = providerConfig.DNSServers
   616  	}
   617  
   618  	if len(observedConfig.DNSSearchDomains) == 0 {
   619  		finalConfig.DNSSearchDomains = providerConfig.DNSSearchDomains
   620  	}
   621  
   622  	return finalConfig
   623  }