lib/filesourcer.go (144 lines of code) (raw):

/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ package dhcplb import ( "bufio" "net" "os" "path/filepath" "strconv" "sync" "github.com/fsnotify/fsnotify" "github.com/golang/glog" ) // FileSourcer holds various information about json the config files, list of // stable and rc servers, the fsnotify Watcher and stuff needed for // synchronization. type FileSourcer struct { stablePath string rcPath string version int watcher *fsnotify.Watcher lock sync.RWMutex stableServers []*DHCPServer rcServers []*DHCPServer } // NewFileSourcer returns a new FileSourcer, stablePath and rcPath are the paths // of the text files containing list of servers. If rcPath is empty it will be // ignored, stablePath must be not null, version is the protocol version and // should be either 4 or 6. func NewFileSourcer(stablePath, rcPath string, version int) (*FileSourcer, error) { watcher, err := fsnotify.NewWatcher() if err != nil { glog.Fatal(err) } err = watcher.Add(filepath.Dir(stablePath)) if err != nil { glog.Fatalf("Error watching stable: %s", err) } // RC is optional, only add to fsnotify and read if rcPath is present if len(rcPath) > 0 { err = watcher.Add(filepath.Dir(rcPath)) if err != nil { glog.Fatalf("Error watching rc: %s", err) } } sourcer, err := &FileSourcer{ stablePath: stablePath, rcPath: rcPath, version: version, watcher: watcher, }, nil sourcer.lock.Lock() sourcer.stableServers, err = sourcer.GetServersFromTier(stablePath) if err != nil { glog.Errorf("Failed to load stable servers: %s", err) } if len(rcPath) > 0 { sourcer.rcServers, err = sourcer.GetServersFromTier(rcPath) if err != nil { glog.Errorf("Failed to load RC servers: %s", err) } } sourcer.lock.Unlock() go sourcer.watchFsnotifyEvents() return sourcer, err } // GetServersFromTier returns a list of DHCPServer from a file func (fs *FileSourcer) GetServersFromTier(path string) ([]*DHCPServer, error) { inputFile, err := os.Open(path) if err != nil { return nil, err } defer inputFile.Close() scanner := bufio.NewScanner(inputFile) var servers []*DHCPServer for scanner.Scan() { var ( hostname string port int64 ) line := scanner.Text() h, p, err := net.SplitHostPort(line) if err != nil { hostname = line if fs.version == 4 { port = 67 } else { port = 547 } } else { hostname = h var errPort error port, errPort = strconv.ParseInt(p, 10, 32) if errPort != nil { glog.Errorf("Can't convert port %s to int", p) continue } } ip := net.ParseIP(hostname) if ip == nil { ips, err := net.LookupHost(hostname) if err != nil { glog.Errorf("Can't resolve IPv4 for %s", hostname) continue } for i := range ips { addr := net.ParseIP(ips[i]) if addr != nil { if fs.version == 4 && addr.To4() != nil { ip = addr break } if fs.version == 6 && addr.To16() != nil { ip = addr break } } } } server := NewDHCPServer(hostname, ip, int(port)) servers = append(servers, server) } return servers, nil } func (fs *FileSourcer) watchFsnotifyEvents() { for { select { case ev := <-fs.watcher.Events: if ev.Op&fsnotify.Write != 0 { glog.Infof("Event: %s File changed, reloading host list", ev) fs.lock.Lock() var err error fs.stableServers, err = fs.GetServersFromTier(fs.stablePath) if err != nil { glog.Errorf("Failed to load stable servers: %s", err) } if len(fs.rcPath) > 0 { fs.rcServers, err = fs.GetServersFromTier(fs.rcPath) if err != nil { glog.Errorf("Failed to RC stable servers: %s", err) } } fs.lock.Unlock() } case err := <-fs.watcher.Errors: glog.Error("Error: ", err) } } } // GetStableServers returns a list of stable dhcp servers func (fs *FileSourcer) GetStableServers() ([]*DHCPServer, error) { return fs.stableServers, nil } // GetRCServers returns a list of rc dhcp servers func (fs *FileSourcer) GetRCServers() ([]*DHCPServer, error) { return fs.rcServers, nil }