// UpdateNodeDrain is used to update the drain of a node func (s *StateStore) UpdateNodeDrain(index uint64, nodeID string, drain bool) error { txn := s.db.Txn(true) defer txn.Abort() // Lookup the node existing, err := txn.First("nodes", "id", nodeID) if err != nil { return fmt.Errorf("node lookup failed: %v", err) } if existing == nil { return fmt.Errorf("node not found") } // Copy the existing node existingNode := existing.(*structs.Node) copyNode := new(structs.Node) *copyNode = *existingNode // Update the drain in the copy copyNode.Drain = drain copyNode.ModifyIndex = index // Insert the node if err := txn.Insert("nodes", copyNode); err != nil { return fmt.Errorf("node update failed: %v", err) } if err := txn.Insert("index", &IndexEntry{"nodes", index}); err != nil { return fmt.Errorf("index update failed: %v", err) } txn.Commit() return nil }
// UpsertNode is used to register a node or update a node definition // This is assumed to be triggered by the client, so we retain the value // of drain which is set by the scheduler. func (s *StateStore) UpsertNode(index uint64, node *structs.Node) error { txn := s.db.Txn(true) defer txn.Abort() // Check if the node already exists existing, err := txn.First("nodes", "id", node.ID) if err != nil { return fmt.Errorf("node lookup failed: %v", err) } // Setup the indexes correctly if existing != nil { exist := existing.(*structs.Node) node.CreateIndex = exist.CreateIndex node.ModifyIndex = index node.Drain = exist.Drain // Retain the drain mode } else { node.CreateIndex = index node.ModifyIndex = index } // Insert the node if err := txn.Insert("nodes", node); err != nil { return fmt.Errorf("node insert failed: %v", err) } if err := txn.Insert("index", &IndexEntry{"nodes", index}); err != nil { return fmt.Errorf("index update failed: %v", err) } txn.Commit() return nil }
func (f *NetworkFingerprint) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { // newNetwork is populated and addded to the Nodes resources newNetwork := &structs.NetworkResource{} // eth0 is the default device for Linux, and en0 is default for OS X defaultDevice := "eth0" if "darwin" == runtime.GOOS { defaultDevice = "en0" } newNetwork.Device = defaultDevice if ip := f.ifConfig(defaultDevice); ip != "" { node.Attributes["network.ip-address"] = ip newNetwork.IP = ip newNetwork.CIDR = newNetwork.IP + "/32" } if throughput := f.linkSpeed(defaultDevice); throughput > 0 { newNetwork.MBits = throughput } if node.Resources == nil { node.Resources = &structs.Resources{} } node.Resources.Networks = append(node.Resources.Networks, newNetwork) // return true, because we have a network connection return true, nil }
func (f *StorageFingerprint) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { // Initialize these to empty defaults node.Attributes["unique.storage.volume"] = "" node.Attributes["unique.storage.bytestotal"] = "" node.Attributes["unique.storage.bytesfree"] = "" if node.Resources == nil { node.Resources = &structs.Resources{} } // Guard against unset AllocDir storageDir := cfg.AllocDir if storageDir == "" { var err error storageDir, err = os.Getwd() if err != nil { return false, fmt.Errorf("unable to get CWD from filesystem: %s", err) } } volume, total, free, err := f.diskFree(storageDir) if err != nil { return false, fmt.Errorf("failed to determine disk space for %s: %v", storageDir, err) } node.Attributes["unique.storage.volume"] = volume node.Attributes["unique.storage.bytestotal"] = strconv.FormatUint(total, 10) node.Attributes["unique.storage.bytesfree"] = strconv.FormatUint(free, 10) node.Resources.DiskMB = int(free / bytesPerMegabyte) return true, nil }
func (f *CPUFingerprint) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { if err := stats.Init(); err != nil { return false, fmt.Errorf("Unable to obtain CPU information: %v", err) } modelName := stats.CPUModelName() if modelName != "" { node.Attributes["cpu.modelname"] = modelName } mhz := stats.CPUMHzPerCore() node.Attributes["cpu.frequency"] = fmt.Sprintf("%.0f", mhz) f.logger.Printf("[DEBUG] fingerprint.cpu: frequency: %.0f MHz", mhz) numCores := stats.CPUNumCores() node.Attributes["cpu.numcores"] = fmt.Sprintf("%d", numCores) f.logger.Printf("[DEBUG] fingerprint.cpu: core count: %d", numCores) tt := stats.TotalTicksAvailable() node.Attributes["cpu.totalcompute"] = fmt.Sprintf("%.0f", tt) if node.Resources == nil { node.Resources = &structs.Resources{} } node.Resources.CPU = int(tt) return true, nil }
func (f *ConsulFingerprint) Fingerprint(config *client.Config, node *structs.Node) (bool, error) { // Guard against uninitialized Links if node.Links == nil { node.Links = map[string]string{} } // Only create the client once to avoid creating too many connections to // Consul. if f.client == nil { address := config.ReadDefault("consul.address", "127.0.0.1:8500") timeout, err := time.ParseDuration(config.ReadDefault("consul.timeout", "10ms")) if err != nil { return false, fmt.Errorf("Unable to parse consul.timeout: %s", err) } consulConfig := consul.DefaultConfig() consulConfig.Address = address consulConfig.HttpClient.Timeout = timeout f.client, err = consul.NewClient(consulConfig) if err != nil { return false, fmt.Errorf("Failed to initialize consul client: %s", err) } } // We'll try to detect consul by making a query to to the agent's self API. // If we can't hit this URL consul is probably not running on this machine. info, err := f.client.Agent().Self() if err != nil { // Clear any attributes set by a previous fingerprint. f.clearConsulAttributes(node) // Print a message indicating that the Consul Agent is not available // anymore if f.lastState == consulAvailable { f.logger.Printf("[INFO] fingerprint.consul: consul agent is unavailable") } f.lastState = consulUnavailable return false, nil } node.Attributes["consul.server"] = strconv.FormatBool(info["Config"]["Server"].(bool)) node.Attributes["consul.version"] = info["Config"]["Version"].(string) node.Attributes["consul.revision"] = info["Config"]["Revision"].(string) node.Attributes["consul.name"] = info["Config"]["NodeName"].(string) node.Attributes["consul.datacenter"] = info["Config"]["Datacenter"].(string) node.Links["consul"] = fmt.Sprintf("%s.%s", node.Attributes["consul.datacenter"], node.Attributes["consul.name"]) // If the Consul Agent was previously unavailable print a message to // indicate the Agent is available now if f.lastState == consulUnavailable { f.logger.Printf("[INFO] fingerprint.consul: consul agent is available") } f.lastState = consulAvailable return true, nil }
// findPreferredNode finds the preferred node for an allocation func (s *GenericScheduler) findPreferredNode(allocTuple *allocTuple) (node *structs.Node, err error) { if allocTuple.Alloc != nil { taskGroup := allocTuple.Alloc.Job.LookupTaskGroup(allocTuple.Alloc.TaskGroup) if taskGroup == nil { err = fmt.Errorf("can't find task group of existing allocation %q", allocTuple.Alloc.ID) return } if taskGroup.EphemeralDisk.Sticky == true { var preferredNode *structs.Node preferredNode, err = s.state.NodeByID(allocTuple.Alloc.NodeID) if preferredNode.Ready() { node = preferredNode } } } return }
func (f *CPUFingerprint) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { cpuInfo, err := cpu.Info() if err != nil { f.logger.Println("[WARN] Error reading CPU information:", err) return false, err } var numCores int32 var mhz float64 var modelName string // Assume all CPUs found have same Model. Log if not. // If CPUInfo() returns nil above, this loop is still safe for _, c := range cpuInfo { numCores += c.Cores mhz += c.Mhz if modelName != "" && modelName != c.ModelName { f.logger.Println("[WARN] Found different model names in the same CPU information. Recording last found") } modelName = c.ModelName } // Get average CPU frequency mhz /= float64(len(cpuInfo)) if mhz > 0 { node.Attributes["cpu.frequency"] = fmt.Sprintf("%.6f", mhz) f.logger.Printf("[DEBUG] fingerprint.cpu: frequency: %02.1fMHz", mhz) } if numCores <= 0 { const defaultCPUCoreCount = 1 f.logger.Printf("[DEBUG] fingerprint.cpu: unable to find core count, defaulting to %d", defaultCPUCoreCount) numCores = defaultCPUCoreCount } if numCores > 0 { node.Attributes["cpu.numcores"] = fmt.Sprintf("%d", numCores) f.logger.Printf("[DEBUG] fingerprint.cpu: core count: %d", numCores) } if mhz > 0 && numCores > 0 { tc := float64(numCores) * mhz node.Attributes["cpu.totalcompute"] = fmt.Sprintf("%.6f", tc) if node.Resources == nil { node.Resources = &structs.Resources{} } node.Resources.CPU = int(tc) } if modelName != "" { node.Attributes["cpu.modelname"] = modelName } return true, nil }
func (f *NetworkFingerprint) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { // newNetwork is populated and addded to the Nodes resources newNetwork := &structs.NetworkResource{} defaultDevice := "" ip := "" // 1. Use user-defined network device // 2. Use first interface found in the system for non-dev mode. (dev mode uses lo by default.) if cfg.NetworkInterface != "" { defaultDevice = cfg.NetworkInterface ip = f.ipAddress(defaultDevice) } else { intfs, err := net.Interfaces() if err != nil { return false, err } for _, i := range intfs { if (i.Flags&net.FlagUp != 0) && (i.Flags&(net.FlagLoopback|net.FlagPointToPoint) == 0) { if ip = f.ipAddress(i.Name); ip != "" { defaultDevice = i.Name break } } } } if (defaultDevice != "") && (ip != "") { newNetwork.Device = defaultDevice node.Attributes["network.ip-address"] = ip newNetwork.IP = ip newNetwork.CIDR = newNetwork.IP + "/32" } else { return false, fmt.Errorf("Unable to find any network interface which has IP address") } if throughput := f.linkSpeed(defaultDevice); throughput > 0 { newNetwork.MBits = throughput } else { f.logger.Printf("[DEBUG] fingerprint.network: Unable to read link speed; setting to default %v", cfg.NetworkSpeed) newNetwork.MBits = cfg.NetworkSpeed } if node.Resources == nil { node.Resources = &structs.Resources{} } node.Resources.Networks = append(node.Resources.Networks, newNetwork) // return true, because we have a network connection return true, nil }
// UpdateNodeStatus is used to update the status of a node func (s *StateStore) UpdateNodeStatus(index uint64, nodeID, status string) error { txn := s.db.Txn(true) defer txn.Abort() watcher := watch.NewItems() watcher.Add(watch.Item{Table: "nodes"}) watcher.Add(watch.Item{Node: nodeID}) // Lookup the node existing, err := txn.First("nodes", "id", nodeID) if err != nil { return fmt.Errorf("node lookup failed: %v", err) } if existing == nil { return fmt.Errorf("node not found") } // Copy the existing node existingNode := existing.(*structs.Node) copyNode := new(structs.Node) *copyNode = *existingNode // Update the status in the copy copyNode.Status = status copyNode.ModifyIndex = index // Insert the node if err := txn.Insert("nodes", copyNode); err != nil { return fmt.Errorf("node update failed: %v", err) } if err := txn.Insert("index", &IndexEntry{"nodes", index}); err != nil { return fmt.Errorf("index update failed: %v", err) } txn.Defer(func() { s.watch.notify(watcher) }) txn.Commit() return nil }
func (f *NetworkFingerprint) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { // newNetwork is populated and addded to the Nodes resources newNetwork := &structs.NetworkResource{} var ip string intf, err := f.findInterface(cfg.NetworkInterface) switch { case err != nil: return false, fmt.Errorf("Error while detecting network interface during fingerprinting: %v", err) case intf == nil: // No interface could be found return false, nil } if ip, err = f.ipAddress(intf); err != nil { return false, fmt.Errorf("Unable to find IP address of interface: %s, err: %v", intf.Name, err) } newNetwork.Device = intf.Name node.Attributes["unique.network.ip-address"] = ip newNetwork.IP = ip newNetwork.CIDR = newNetwork.IP + "/32" f.logger.Printf("[DEBUG] fingerprint.network: Detected interface %v with IP %v during fingerprinting", intf.Name, ip) throughput := f.linkSpeed(intf.Name) if cfg.NetworkSpeed != 0 { newNetwork.MBits = cfg.NetworkSpeed f.logger.Printf("[DEBUG] fingerprint.network: setting link speed to user configured speed: %d", newNetwork.MBits) } else if throughput != 0 { newNetwork.MBits = throughput f.logger.Printf("[DEBUG] fingerprint.network: link speed for %v set to %v", intf.Name, newNetwork.MBits) } else { newNetwork.MBits = defaultNetworkSpeed f.logger.Printf("[DEBUG] fingerprint.network: link speed could not be detected and no speed specified by user. Defaulting to %d", defaultNetworkSpeed) } if node.Resources == nil { node.Resources = &structs.Resources{} } node.Resources.Networks = append(node.Resources.Networks, newNetwork) // return true, because we have a network connection return true, nil }
func (f *MemoryFingerprint) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { memInfo, err := mem.VirtualMemory() if err != nil { f.logger.Printf("[WARN] Error reading memory information: %s", err) return false, err } if memInfo.Total > 0 { node.Attributes["memory.totalbytes"] = fmt.Sprintf("%d", memInfo.Total) if node.Resources == nil { node.Resources = &structs.Resources{} } node.Resources.MemoryMB = int(memInfo.Total / 1024 / 1024) } return true, nil }
func (f *NetworkFingerprint) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { // newNetwork is populated and addded to the Nodes resources newNetwork := &structs.NetworkResource{} var ip string intf, err := f.findInterface(cfg.NetworkInterface) if err != nil { return false, fmt.Errorf("Error while detecting network interface during fingerprinting: %v", err) } // No interface could be found if intf == nil { return false, nil } if ip, err = f.ipAddress(intf); err != nil { return false, fmt.Errorf("Unable to find IP address of interface: %s, err: %v", intf.Name, err) } newNetwork.Device = intf.Name node.Attributes["network.ip-address"] = ip newNetwork.IP = ip newNetwork.CIDR = newNetwork.IP + "/32" f.logger.Printf("[DEBUG] fingerprint.network: Detected interface %v with IP %v during fingerprinting", intf.Name, ip) if throughput := f.linkSpeed(intf.Name); throughput > 0 { newNetwork.MBits = throughput } else { f.logger.Printf("[DEBUG] fingerprint.network: Unable to read link speed; setting to default %v", cfg.NetworkSpeed) newNetwork.MBits = cfg.NetworkSpeed } if node.Resources == nil { node.Resources = &structs.Resources{} } node.Resources.Networks = append(node.Resources.Networks, newNetwork) // return true, because we have a network connection return true, nil }
func (f *NetworkFingerprint) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { // newNetwork is populated and addded to the Nodes resources newNetwork := &structs.NetworkResource{} // eth0 is the default device for Linux, and en0 is default for OS X defaultDevice := "eth0" if "darwin" == runtime.GOOS { defaultDevice = "en0" } // User-defined override for the default interface if cfg.NetworkInterface != "" { defaultDevice = cfg.NetworkInterface } newNetwork.Device = defaultDevice if ip := f.ipAddress(defaultDevice); ip != "" { node.Attributes["network.ip-address"] = ip newNetwork.IP = ip newNetwork.CIDR = newNetwork.IP + "/32" } else { return false, fmt.Errorf("Unable to determine IP on network interface %v", defaultDevice) } if throughput := f.linkSpeed(defaultDevice); throughput > 0 { newNetwork.MBits = throughput } else { f.logger.Printf("[DEBUG] fingerprint.network: Unable to read link speed; setting to default %v", cfg.NetworkSpeed) newNetwork.MBits = cfg.NetworkSpeed } if node.Resources == nil { node.Resources = &structs.Resources{} } node.Resources.Networks = append(node.Resources.Networks, newNetwork) // return true, because we have a network connection return true, nil }
func (f *ConsulFingerprint) Fingerprint(config *client.Config, node *structs.Node) (bool, error) { // Guard against uninitialized Links if node.Links == nil { node.Links = map[string]string{} } address := config.ReadDefault("consul.address", "127.0.0.1:8500") timeout, err := time.ParseDuration(config.ReadDefault("consul.timeout", "10ms")) if err != nil { return false, fmt.Errorf("Unable to parse consul.timeout: %s", err) } consulConfig := consul.DefaultConfig() consulConfig.Address = address consulConfig.HttpClient.Timeout = timeout consulClient, err := consul.NewClient(consulConfig) if err != nil { return false, fmt.Errorf("Failed to initialize consul client: %s", err) } // We'll try to detect consul by making a query to to the agent's self API. // If we can't hit this URL consul is probably not running on this machine. info, err := consulClient.Agent().Self() if err != nil { return false, fmt.Errorf("Failed to query consul for agent status: %s", err) } node.Attributes["consul.server"] = strconv.FormatBool(info["Config"]["Server"].(bool)) node.Attributes["consul.version"] = info["Config"]["Version"].(string) node.Attributes["consul.revision"] = info["Config"]["Revision"].(string) node.Attributes["consul.name"] = info["Config"]["NodeName"].(string) node.Attributes["consul.datacenter"] = info["Config"]["Datacenter"].(string) node.Links["consul"] = fmt.Sprintf("%s.%s", node.Attributes["consul.datacenter"], node.Attributes["consul.name"]) return true, nil }
func (f *StorageFingerprint) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { // Initialize these to empty defaults node.Attributes["storage.volume"] = "" node.Attributes["storage.bytestotal"] = "" node.Attributes["storage.bytesfree"] = "" if node.Resources == nil { node.Resources = &structs.Resources{} } // Guard against unset AllocDir storageDir := cfg.AllocDir if storageDir == "" { var err error storageDir, err = os.Getwd() if err != nil { return false, fmt.Errorf("Unable to get CWD from filesystem: %s", err) } } if runtime.GOOS == "windows" { path, err := filepath.Abs(storageDir) if err != nil { return false, fmt.Errorf("Failed to detect volume for storage directory %s: %s", storageDir, err) } volume := filepath.VolumeName(path) node.Attributes["storage.volume"] = volume out, err := exec.Command("fsutil", "volume", "diskfree", volume).Output() if err != nil { return false, fmt.Errorf("Failed to inspect free space from volume %s: %s", volume, err) } outstring := string(out) totalMatches := reWindowsTotalSpace.FindStringSubmatch(outstring) if len(totalMatches) == 2 { node.Attributes["storage.bytestotal"] = totalMatches[1] total, err := strconv.ParseInt(totalMatches[1], 10, 64) if err != nil { return false, fmt.Errorf("Failed to parse storage.bytestotal in bytes: %s", err) } // Convert from bytes to to MB node.Resources.DiskMB = int(total / 1024 / 1024) } else { return false, fmt.Errorf("Failed to parse output from fsutil") } freeMatches := reWindowsFreeSpace.FindStringSubmatch(outstring) if len(freeMatches) == 2 { node.Attributes["storage.bytesfree"] = freeMatches[1] _, err := strconv.ParseInt(freeMatches[1], 10, 64) if err != nil { return false, fmt.Errorf("Failed to parse storage.bytesfree in bytes: %s", err) } } else { return false, fmt.Errorf("Failed to parse output from fsutil") } } else { path, err := filepath.Abs(storageDir) if err != nil { return false, fmt.Errorf("Failed to determine absolute path for %s", storageDir) } // Use -k to standardize the output values between darwin and linux mountOutput, err := exec.Command("df", "-k", path).Output() if err != nil { return false, fmt.Errorf("Failed to determine mount point for %s", path) } // Output looks something like: // Filesystem 1024-blocks Used Available Capacity iused ifree %iused Mounted on // /dev/disk1 487385240 423722532 63406708 87% 105994631 15851677 87% / // [0] volume [1] capacity [2] SKIP [3] free lines := strings.Split(string(mountOutput), "\n") if len(lines) < 2 { return false, fmt.Errorf("Failed to parse `df` output; expected at least 2 lines") } fields := strings.Fields(lines[1]) if len(fields) < 4 { return false, fmt.Errorf("Failed to parse `df` output; expected at least 4 columns") } node.Attributes["storage.volume"] = fields[0] total, err := strconv.ParseInt(fields[1], 10, 64) if err != nil { return false, fmt.Errorf("Failed to parse storage.bytestotal size in kilobytes") } node.Attributes["storage.bytestotal"] = strconv.FormatInt(total*1024, 10) free, err := strconv.ParseInt(fields[3], 10, 64) if err != nil { return false, fmt.Errorf("Failed to parse storage.bytesfree size in kilobytes") } // Convert from KB to MB node.Resources.DiskMB = int(free / 1024) // Convert from KB to bytes node.Attributes["storage.bytesfree"] = strconv.FormatInt(free*1024, 10) } return true, nil }
func (f *EnvGCEFingerprint) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { if !f.isGCE() { return false, nil } if node.Links == nil { node.Links = make(map[string]string) } keys := []string{ "hostname", "id", "cpu-platform", "scheduling/automatic-restart", "scheduling/on-host-maintenance", } for _, k := range keys { value, err := f.Get(k, false) if err != nil { return false, checkError(err, f.logger, k) } // assume we want blank entries key := strings.Replace(k, "/", ".", -1) node.Attributes["platform.gce."+key] = strings.Trim(string(value), "\n") } // These keys need everything before the final slash removed to be usable. keys = []string{ "machine-type", "zone", } for _, k := range keys { value, err := f.Get(k, false) if err != nil { return false, checkError(err, f.logger, k) } node.Attributes["platform.gce."+k] = strings.Trim(lastToken(value), "\n") } // Get internal and external IPs (if they exist) value, err := f.Get("network-interfaces/", true) var interfaces []GCEMetadataNetworkInterface if err := json.Unmarshal([]byte(value), &interfaces); err != nil { f.logger.Printf("[WARN] fingerprint.env_gce: Error decoding network interface information: %s", err.Error()) } for _, intf := range interfaces { prefix := "platform.gce.network." + lastToken(intf.Network) node.Attributes[prefix] = "true" node.Attributes[prefix+".ip"] = strings.Trim(intf.Ip, "\n") for index, accessConfig := range intf.AccessConfigs { node.Attributes[prefix+".external-ip."+strconv.Itoa(index)] = accessConfig.ExternalIp } } var tagList []string value, err = f.Get("tags", false) if err != nil { return false, checkError(err, f.logger, "tags") } if err := json.Unmarshal([]byte(value), &tagList); err != nil { f.logger.Printf("[WARN] fingerprint.env_gce: Error decoding instance tags: %s", err.Error()) } for _, tag := range tagList { node.Attributes["platform.gce.tag."+tag] = "true" } var attrDict map[string]string value, err = f.Get("attributes/", true) if err != nil { return false, checkError(err, f.logger, "attributes/") } if err := json.Unmarshal([]byte(value), &attrDict); err != nil { f.logger.Printf("[WARN] fingerprint.env_gce: Error decoding instance attributes: %s", err.Error()) } for k, v := range attrDict { node.Attributes["platform.gce.attr."+k] = strings.Trim(v, "\n") } // populate Links node.Links["gce"] = node.Attributes["platform.gce.id"] return true, nil }
func (f *EnvAWSFingerprint) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { if !isAWS() { return false, nil } // newNetwork is populated and addded to the Nodes resources newNetwork := &structs.NetworkResource{ Device: "eth0", } if node.Links == nil { node.Links = make(map[string]string) } metadataURL := os.Getenv("AWS_ENV_URL") if metadataURL == "" { metadataURL = DEFAULT_AWS_URL } // assume 2 seconds is enough time for inside AWS network client := &http.Client{ Timeout: 2 * time.Second, Transport: cleanhttp.DefaultTransport(), } keys := []string{ "ami-id", "hostname", "instance-id", "instance-type", "local-hostname", "local-ipv4", "public-hostname", "public-ipv4", "placement/availability-zone", } for _, k := range keys { res, err := client.Get(metadataURL + k) if err != nil { // if it's a URL error, assume we're not in an AWS environment // TODO: better way to detect AWS? Check xen virtualization? if _, ok := err.(*url.Error); ok { return false, nil } // not sure what other errors it would return return false, err } resp, err := ioutil.ReadAll(res.Body) res.Body.Close() if err != nil { f.logger.Printf("[ERR]: fingerprint.env_aws: Error reading response body for AWS %s", k) } // assume we want blank entries key := strings.Replace(k, "/", ".", -1) node.Attributes["platform.aws."+key] = strings.Trim(string(resp), "\n") } // copy over network specific information if node.Attributes["platform.aws.local-ipv4"] != "" { node.Attributes["network.ip-address"] = node.Attributes["platform.aws.local-ipv4"] newNetwork.IP = node.Attributes["platform.aws.local-ipv4"] newNetwork.CIDR = newNetwork.IP + "/32" } // find LinkSpeed from lookup if throughput := f.linkSpeed(); throughput > 0 { newNetwork.MBits = throughput } if node.Resources == nil { node.Resources = &structs.Resources{} } node.Resources.Networks = append(node.Resources.Networks, newNetwork) // populate Node Network Resources // populate Links node.Links["aws.ec2"] = node.Attributes["platform.aws.placement.availability-zone"] + "." + node.Attributes["platform.aws.instance-id"] return true, nil }
func (f *EnvAWSFingerprint) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { if !f.isAWS() { return false, nil } // newNetwork is populated and addded to the Nodes resources newNetwork := &structs.NetworkResource{ Device: "eth0", } if node.Links == nil { node.Links = make(map[string]string) } metadataURL := os.Getenv("AWS_ENV_URL") if metadataURL == "" { metadataURL = DEFAULT_AWS_URL } // assume 2 seconds is enough time for inside AWS network client := &http.Client{ Timeout: 2 * time.Second, Transport: cleanhttp.DefaultTransport(), } // Keys and whether they should be namespaced as unique. Any key whose value // uniquely identifies a node, such as ip, should be marked as unique. When // marked as unique, the key isn't included in the computed node class. keys := map[string]bool{ "ami-id": true, "hostname": true, "instance-id": true, "instance-type": false, "local-hostname": true, "local-ipv4": true, "public-hostname": true, "public-ipv4": true, "placement/availability-zone": false, } for k, unique := range keys { res, err := client.Get(metadataURL + k) if res.StatusCode != http.StatusOK { f.logger.Printf("[WARN]: fingerprint.env_aws: Could not read value for attribute %q", k) continue } if err != nil { // if it's a URL error, assume we're not in an AWS environment // TODO: better way to detect AWS? Check xen virtualization? if _, ok := err.(*url.Error); ok { return false, nil } // not sure what other errors it would return return false, err } resp, err := ioutil.ReadAll(res.Body) res.Body.Close() if err != nil { f.logger.Printf("[ERR]: fingerprint.env_aws: Error reading response body for AWS %s", k) } // assume we want blank entries key := "platform.aws." + strings.Replace(k, "/", ".", -1) if unique { key = structs.UniqueNamespace(key) } node.Attributes[key] = strings.Trim(string(resp), "\n") } // copy over network specific information if val := node.Attributes["unique.platform.aws.local-ipv4"]; val != "" { node.Attributes["unique.network.ip-address"] = val newNetwork.IP = val newNetwork.CIDR = newNetwork.IP + "/32" } // find LinkSpeed from lookup throughput := f.linkSpeed() if cfg.NetworkSpeed != 0 { throughput = cfg.NetworkSpeed } else if throughput == 0 { // Failed to determine speed. Check if the network fingerprint got it found := false if node.Resources != nil && len(node.Resources.Networks) > 0 { for _, n := range node.Resources.Networks { if n.IP == newNetwork.IP { throughput = n.MBits found = true break } } } // Nothing detected so default if !found { throughput = defaultNetworkSpeed } } // populate Node Network Resources if node.Resources == nil { node.Resources = &structs.Resources{} } newNetwork.MBits = throughput node.Resources.Networks = []*structs.NetworkResource{newNetwork} // populate Links node.Links["aws.ec2"] = fmt.Sprintf("%s.%s", node.Attributes["platform.aws.placement.availability-zone"], node.Attributes["unique.platform.aws.instance-id"]) return true, nil }
func (f *EnvGCEFingerprint) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { if !f.isGCE() { return false, nil } if node.Links == nil { node.Links = make(map[string]string) } // Keys and whether they should be namespaced as unique. Any key whose value // uniquely identifies a node, such as ip, should be marked as unique. When // marked as unique, the key isn't included in the computed node class. keys := map[string]bool{ "hostname": true, "id": true, "cpu-platform": false, "scheduling/automatic-restart": false, "scheduling/on-host-maintenance": false, } for k, unique := range keys { value, err := f.Get(k, false) if err != nil { return false, checkError(err, f.logger, k) } // assume we want blank entries key := "platform.gce." + strings.Replace(k, "/", ".", -1) if unique { key = structs.UniqueNamespace(key) } node.Attributes[key] = strings.Trim(string(value), "\n") } // These keys need everything before the final slash removed to be usable. keys = map[string]bool{ "machine-type": false, "zone": false, } for k, unique := range keys { value, err := f.Get(k, false) if err != nil { return false, checkError(err, f.logger, k) } key := "platform.gce." + k if unique { key = structs.UniqueNamespace(key) } node.Attributes[key] = strings.Trim(lastToken(value), "\n") } // Get internal and external IPs (if they exist) value, err := f.Get("network-interfaces/", true) var interfaces []GCEMetadataNetworkInterface if err := json.Unmarshal([]byte(value), &interfaces); err != nil { f.logger.Printf("[WARN] fingerprint.env_gce: Error decoding network interface information: %s", err.Error()) } for _, intf := range interfaces { prefix := "platform.gce.network." + lastToken(intf.Network) uniquePrefix := "unique." + prefix node.Attributes[prefix] = "true" node.Attributes[uniquePrefix+".ip"] = strings.Trim(intf.Ip, "\n") for index, accessConfig := range intf.AccessConfigs { node.Attributes[uniquePrefix+".external-ip."+strconv.Itoa(index)] = accessConfig.ExternalIp } } var tagList []string value, err = f.Get("tags", false) if err != nil { return false, checkError(err, f.logger, "tags") } if err := json.Unmarshal([]byte(value), &tagList); err != nil { f.logger.Printf("[WARN] fingerprint.env_gce: Error decoding instance tags: %s", err.Error()) } for _, tag := range tagList { attr := "platform.gce.tag." var key string // If the tag is namespaced as unique, we strip it from the tag and // prepend to the whole attribute. if structs.IsUniqueNamespace(tag) { tag = strings.TrimPrefix(tag, structs.NodeUniqueNamespace) key = fmt.Sprintf("%s%s%s", structs.NodeUniqueNamespace, attr, tag) } else { key = fmt.Sprintf("%s%s", attr, tag) } node.Attributes[key] = "true" } var attrDict map[string]string value, err = f.Get("attributes/", true) if err != nil { return false, checkError(err, f.logger, "attributes/") } if err := json.Unmarshal([]byte(value), &attrDict); err != nil { f.logger.Printf("[WARN] fingerprint.env_gce: Error decoding instance attributes: %s", err.Error()) } for k, v := range attrDict { attr := "platform.gce.attr." var key string // If the key is namespaced as unique, we strip it from the // key and prepend to the whole attribute. if structs.IsUniqueNamespace(k) { k = strings.TrimPrefix(k, structs.NodeUniqueNamespace) key = fmt.Sprintf("%s%s%s", structs.NodeUniqueNamespace, attr, k) } else { key = fmt.Sprintf("%s%s", attr, k) } node.Attributes[key] = strings.Trim(v, "\n") } // populate Links node.Links["gce"] = node.Attributes["unique.platform.gce.id"] return true, nil }