github.com/blixtra/nomad@v0.7.2-0.20171221000451-da9a1d7bb050/client/fingerprint/network.go (about)

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