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 }