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 }