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