github.com/hhrutter/nomad@v0.6.0-rc2.0.20170723054333-80c4b03f0705/client/fingerprint/network.go (about)

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