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 }