github.com/smithx10/nomad@v0.9.1-rc1/client/fingerprint/network.go (about)

     1  package fingerprint
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  
     7  	log "github.com/hashicorp/go-hclog"
     8  	sockaddr "github.com/hashicorp/go-sockaddr"
     9  	"github.com/hashicorp/nomad/nomad/structs"
    10  )
    11  
    12  const (
    13  	// defaultNetworkSpeed is the speed set if the network link speed could not
    14  	// be detected.
    15  	defaultNetworkSpeed = 1000
    16  
    17  	// networkDisallowLinkLocalOption/Default are used to allow the operator to
    18  	// decide how the fingerprinter handles an interface that only contains link
    19  	// local addresses.
    20  	networkDisallowLinkLocalOption  = "fingerprint.network.disallow_link_local"
    21  	networkDisallowLinkLocalDefault = false
    22  )
    23  
    24  // NetworkFingerprint is used to fingerprint the Network capabilities of a node
    25  type NetworkFingerprint struct {
    26  	StaticFingerprinter
    27  	logger            log.Logger
    28  	interfaceDetector NetworkInterfaceDetector
    29  }
    30  
    31  // An interface to isolate calls to various api in net package
    32  // This facilitates testing where we can implement
    33  // fake interfaces and addresses to test varios code paths
    34  type NetworkInterfaceDetector interface {
    35  	Interfaces() ([]net.Interface, error)
    36  	InterfaceByName(name string) (*net.Interface, error)
    37  	Addrs(intf *net.Interface) ([]net.Addr, error)
    38  }
    39  
    40  // Implements the interface detector which calls net directly
    41  type DefaultNetworkInterfaceDetector struct {
    42  }
    43  
    44  func (b *DefaultNetworkInterfaceDetector) Interfaces() ([]net.Interface, error) {
    45  	return net.Interfaces()
    46  }
    47  
    48  func (b *DefaultNetworkInterfaceDetector) InterfaceByName(name string) (*net.Interface, error) {
    49  	return net.InterfaceByName(name)
    50  }
    51  
    52  func (b *DefaultNetworkInterfaceDetector) Addrs(intf *net.Interface) ([]net.Addr, error) {
    53  	return intf.Addrs()
    54  }
    55  
    56  // NewNetworkFingerprint returns a new NetworkFingerprinter with the given
    57  // logger
    58  func NewNetworkFingerprint(logger log.Logger) Fingerprint {
    59  	f := &NetworkFingerprint{logger: logger.Named("network"), interfaceDetector: &DefaultNetworkInterfaceDetector{}}
    60  	return f
    61  }
    62  
    63  func (f *NetworkFingerprint) Fingerprint(req *FingerprintRequest, resp *FingerprintResponse) error {
    64  	cfg := req.Config
    65  
    66  	// Find the named interface
    67  	intf, err := f.findInterface(cfg.NetworkInterface)
    68  	switch {
    69  	case err != nil:
    70  		return fmt.Errorf("Error while detecting network interface during fingerprinting: %v", err)
    71  	case intf == nil:
    72  		// No interface could be found
    73  		return nil
    74  	}
    75  
    76  	// Record the throughput of the interface
    77  	var mbits int
    78  	throughput := f.linkSpeed(intf.Name)
    79  	if cfg.NetworkSpeed != 0 {
    80  		mbits = cfg.NetworkSpeed
    81  		f.logger.Debug("setting link speed to user configured speed", "mbits", mbits)
    82  	} else if throughput != 0 {
    83  		mbits = throughput
    84  		f.logger.Debug("link speed detected", "interface", intf.Name, "mbits", mbits)
    85  	} else {
    86  		mbits = defaultNetworkSpeed
    87  		f.logger.Debug("link speed could not be detected and no speed specified by user, falling back to default speed", "mbits", defaultNetworkSpeed)
    88  	}
    89  
    90  	// Create the network resources from the interface
    91  	disallowLinkLocal := cfg.ReadBoolDefault(networkDisallowLinkLocalOption, networkDisallowLinkLocalDefault)
    92  	nwResources, err := f.createNetworkResources(mbits, intf, disallowLinkLocal)
    93  	if err != nil {
    94  		return err
    95  	}
    96  
    97  	// COMPAT(0.10): Remove in 0.10
    98  	resp.Resources = &structs.Resources{
    99  		Networks: nwResources,
   100  	}
   101  
   102  	resp.NodeResources = &structs.NodeResources{
   103  		Networks: nwResources,
   104  	}
   105  
   106  	for _, nwResource := range nwResources {
   107  		f.logger.Debug("detected interface IP", "interface", intf.Name, "IP", nwResource.IP)
   108  	}
   109  
   110  	// Deprecated, setting the first IP as unique IP for the node
   111  	if len(nwResources) > 0 {
   112  		resp.AddAttribute("unique.network.ip-address", nwResources[0].IP)
   113  	}
   114  	resp.Detected = true
   115  
   116  	return nil
   117  }
   118  
   119  // createNetworkResources creates network resources for every IP
   120  func (f *NetworkFingerprint) createNetworkResources(throughput int, intf *net.Interface, disallowLinkLocal bool) ([]*structs.NetworkResource, error) {
   121  	// Find the interface with the name
   122  	addrs, err := f.interfaceDetector.Addrs(intf)
   123  	if err != nil {
   124  		return nil, err
   125  	}
   126  
   127  	nwResources := make([]*structs.NetworkResource, 0)
   128  	linkLocals := make([]*structs.NetworkResource, 0)
   129  
   130  	for _, addr := range addrs {
   131  		// Create a new network resource
   132  		newNetwork := &structs.NetworkResource{
   133  			Device: intf.Name,
   134  			MBits:  throughput,
   135  		}
   136  
   137  		// Find the IP Addr and the CIDR from the Address
   138  		var ip net.IP
   139  		switch v := (addr).(type) {
   140  		case *net.IPNet:
   141  			ip = v.IP
   142  		case *net.IPAddr:
   143  			ip = v.IP
   144  		}
   145  
   146  		newNetwork.IP = ip.String()
   147  		if ip.To4() != nil {
   148  			newNetwork.CIDR = newNetwork.IP + "/32"
   149  		} else {
   150  			newNetwork.CIDR = newNetwork.IP + "/128"
   151  		}
   152  
   153  		// If the ip is link-local then we ignore it unless the user allows it
   154  		// and we detect nothing else
   155  		if ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() {
   156  			linkLocals = append(linkLocals, newNetwork)
   157  			continue
   158  		}
   159  
   160  		nwResources = append(nwResources, newNetwork)
   161  	}
   162  
   163  	if len(nwResources) == 0 && len(linkLocals) != 0 {
   164  		if disallowLinkLocal {
   165  			f.logger.Debug("ignoring detected link-local address on interface", "interface", intf.Name)
   166  			return nwResources, nil
   167  		}
   168  
   169  		return linkLocals, nil
   170  	}
   171  
   172  	return nwResources, nil
   173  }
   174  
   175  // Returns the interface with the name passed by user. If the name is blank, we
   176  // use the interface attached to the default route.
   177  func (f *NetworkFingerprint) findInterface(deviceName string) (*net.Interface, error) {
   178  	// If we aren't given a device, look it up by using the interface with the default route
   179  	if deviceName == "" {
   180  		ri, err := sockaddr.NewRouteInfo()
   181  		if err != nil {
   182  			return nil, err
   183  		}
   184  
   185  		defaultIfName, err := ri.GetDefaultInterfaceName()
   186  		if err != nil {
   187  			return nil, err
   188  		}
   189  		if defaultIfName == "" {
   190  			return nil, fmt.Errorf("no network_interface given and failed to determine interface attached to default route")
   191  		}
   192  		deviceName = defaultIfName
   193  	}
   194  
   195  	return f.interfaceDetector.InterfaceByName(deviceName)
   196  }