github.com/anuvu/nomad@v0.8.7-atom1/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  	cstructs "github.com/hashicorp/nomad/client/structs"
    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(req *cstructs.FingerprintRequest, resp *cstructs.FingerprintResponse) error {
    65  	cfg := req.Config
    66  
    67  	// Find the named interface
    68  	intf, err := f.findInterface(cfg.NetworkInterface)
    69  	switch {
    70  	case err != nil:
    71  		return fmt.Errorf("Error while detecting network interface during fingerprinting: %v", err)
    72  	case intf == nil:
    73  		// No interface could be found
    74  		return nil
    75  	}
    76  
    77  	// Record the throughput of the interface
    78  	var mbits int
    79  	throughput := f.linkSpeed(intf.Name)
    80  	if cfg.NetworkSpeed != 0 {
    81  		mbits = cfg.NetworkSpeed
    82  		f.logger.Printf("[DEBUG] fingerprint.network: setting link speed to user configured speed: %d", mbits)
    83  	} else if throughput != 0 {
    84  		mbits = throughput
    85  		f.logger.Printf("[DEBUG] fingerprint.network: link speed for %v set to %v", intf.Name, mbits)
    86  	} else {
    87  		mbits = defaultNetworkSpeed
    88  		f.logger.Printf("[DEBUG] fingerprint.network: link speed could not be detected and no speed specified by user. Defaulting to %d", defaultNetworkSpeed)
    89  	}
    90  
    91  	// Create the network resources from the interface
    92  	disallowLinkLocal := cfg.ReadBoolDefault(networkDisallowLinkLocalOption, networkDisallowLinkLocalDefault)
    93  	nwResources, err := f.createNetworkResources(mbits, intf, disallowLinkLocal)
    94  	if err != nil {
    95  		return err
    96  	}
    97  
    98  	resp.Resources = &structs.Resources{
    99  		Networks: nwResources,
   100  	}
   101  	for _, nwResource := range nwResources {
   102  		f.logger.Printf("[DEBUG] fingerprint.network: Detected interface %v with IP: %v", intf.Name, nwResource.IP)
   103  	}
   104  
   105  	// Deprecated, setting the first IP as unique IP for the node
   106  	if len(nwResources) > 0 {
   107  		resp.AddAttribute("unique.network.ip-address", nwResources[0].IP)
   108  	}
   109  	resp.Detected = true
   110  
   111  	return nil
   112  }
   113  
   114  // createNetworkResources creates network resources for every IP
   115  func (f *NetworkFingerprint) createNetworkResources(throughput int, intf *net.Interface, disallowLinkLocal bool) ([]*structs.NetworkResource, error) {
   116  	// Find the interface with the name
   117  	addrs, err := f.interfaceDetector.Addrs(intf)
   118  	if err != nil {
   119  		return nil, err
   120  	}
   121  
   122  	nwResources := make([]*structs.NetworkResource, 0)
   123  	linkLocals := make([]*structs.NetworkResource, 0)
   124  
   125  	for _, addr := range addrs {
   126  		// Create a new network resource
   127  		newNetwork := &structs.NetworkResource{
   128  			Device: intf.Name,
   129  			MBits:  throughput,
   130  		}
   131  
   132  		// Find the IP Addr and the CIDR from the Address
   133  		var ip net.IP
   134  		switch v := (addr).(type) {
   135  		case *net.IPNet:
   136  			ip = v.IP
   137  		case *net.IPAddr:
   138  			ip = v.IP
   139  		}
   140  
   141  		newNetwork.IP = ip.String()
   142  		if ip.To4() != nil {
   143  			newNetwork.CIDR = newNetwork.IP + "/32"
   144  		} else {
   145  			newNetwork.CIDR = newNetwork.IP + "/128"
   146  		}
   147  
   148  		// If the ip is link-local then we ignore it unless the user allows it
   149  		// and we detect nothing else
   150  		if ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() {
   151  			linkLocals = append(linkLocals, newNetwork)
   152  			continue
   153  		}
   154  
   155  		nwResources = append(nwResources, newNetwork)
   156  	}
   157  
   158  	if len(nwResources) == 0 && len(linkLocals) != 0 {
   159  		if disallowLinkLocal {
   160  			f.logger.Printf("[DEBUG] fingerprint.network: ignoring detected link-local address on interface %v", intf.Name)
   161  			return nwResources, nil
   162  		}
   163  
   164  		return linkLocals, nil
   165  	}
   166  
   167  	return nwResources, nil
   168  }
   169  
   170  // Returns the interface with the name passed by user. If the name is blank, we
   171  // use the interface attached to the default route.
   172  func (f *NetworkFingerprint) findInterface(deviceName string) (*net.Interface, error) {
   173  	// If we aren't given a device, look it up by using the interface with the default route
   174  	if deviceName == "" {
   175  		ri, err := sockaddr.NewRouteInfo()
   176  		if err != nil {
   177  			return nil, err
   178  		}
   179  
   180  		defaultIfName, err := ri.GetDefaultInterfaceName()
   181  		if err != nil {
   182  			return nil, err
   183  		}
   184  		if defaultIfName == "" {
   185  			return nil, fmt.Errorf("no network_interface given and failed to determine interface attached to default route")
   186  		}
   187  		deviceName = defaultIfName
   188  	}
   189  
   190  	return f.interfaceDetector.InterfaceByName(deviceName)
   191  }