github.com/taylorchu/nomad@v0.5.3-rc1.0.20170407200202-db11e7dd7b55/client/fingerprint/network.go (about)

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