diff --git a/conf/conf.go b/conf/conf.go index 75ca0db..ffd8baf 100644 --- a/conf/conf.go +++ b/conf/conf.go @@ -8,12 +8,14 @@ import ( "github.com/udhos/jazigo/store" ) +// Change stores info about last config change. type Change struct { When time.Time By string From string } +// AppConfig is persistent global configuration. type AppConfig struct { MaxConfigFiles int Holdtime time.Duration @@ -24,6 +26,7 @@ type AppConfig struct { Comment string // free user-defined field } +// NewAppConfigFromString creates AppConfig from string. func NewAppConfigFromString(str string) (*AppConfig, error) { b := []byte(str) c := &AppConfig{} @@ -33,6 +36,7 @@ func NewAppConfigFromString(str string) (*AppConfig, error) { return c, nil } +// Dump exports AppConfig as YAML. func (a *AppConfig) Dump() ([]byte, error) { b, err := yaml.Marshal(a) if err != nil { @@ -41,6 +45,7 @@ func (a *AppConfig) Dump() ([]byte, error) { return b, nil } +// NewDevAttr creates a new set of DevAttributes. func NewDevAttr() DevAttributes { a := DevAttributes{ ErrlogHistSize: 60, // default max number of lines in errlog history @@ -49,6 +54,7 @@ func NewDevAttr() DevAttributes { return a } +// DevAttributes is per-model set of default attributes for device. type DevAttributes struct { NeedLoginChat bool // need login chat NeedEnabledMode bool // need enabled mode @@ -83,6 +89,7 @@ type DevAttributes struct { CommandMatchTimeout time.Duration // larger timeout for slow responses (slow show running) } +// DevConfig is full set of device properties. type DevConfig struct { Debug bool Deleted bool @@ -98,6 +105,7 @@ type DevConfig struct { Attr DevAttributes } +// NewDeviceFromString creates device configuration from string. func NewDeviceFromString(str string) (*DevConfig, error) { b := []byte(str) c := &DevConfig{} @@ -107,6 +115,7 @@ func NewDeviceFromString(str string) (*DevConfig, error) { return c, nil } +// Dump exports device properties as YAML. func (c *DevConfig) Dump() ([]byte, error) { b, err := yaml.Marshal(c) if err != nil { @@ -115,11 +124,13 @@ func (c *DevConfig) Dump() ([]byte, error) { return b, nil } +// Config is full (global+devices) app configuration. type Config struct { Options AppConfig Devices []DevConfig } +// New creates new full app configuration. func New() *Config { return &Config{ Options: AppConfig{ @@ -133,6 +144,7 @@ func New() *Config { } } +// Load loads a Config from file. func Load(path string, maxSize int64) (*Config, error) { b, readErr := store.FileRead(path, maxSize) if readErr != nil { @@ -145,6 +157,7 @@ func Load(path string, maxSize int64) (*Config, error) { return c, nil } +// Dump exports device properties as YAML. func (c *Config) Dump() ([]byte, error) { b, err := yaml.Marshal(c) if err != nil { diff --git a/conf/options.go b/conf/options.go index ed71103..eb6599b 100644 --- a/conf/options.go +++ b/conf/options.go @@ -4,15 +4,18 @@ import ( "sync" ) +// Options provides concurrency-safe access to AppConfig. type Options struct { options AppConfig lock sync.RWMutex } +// NewOptions creates a new set of options. func NewOptions() *Options { return &Options{} } +// Get creates a copy of AppConfig. func (o *Options) Get() *AppConfig { o.lock.RLock() defer o.lock.RUnlock() @@ -20,6 +23,7 @@ func (o *Options) Get() *AppConfig { return &opt } +// Set updates the AppConfig from a copy. func (o *Options) Set(c *AppConfig) { o.lock.Lock() defer o.lock.Unlock() diff --git a/dev/errlog.go b/dev/errlog.go index 4ae3271..9a8cc45 100644 --- a/dev/errlog.go +++ b/dev/errlog.go @@ -9,6 +9,7 @@ import ( "time" ) +// ErrlogPath builds the full pathname for errlog file. func ErrlogPath(pathPrefix, id string) string { dir := filepath.Dir(pathPrefix) path := filepath.Join(dir, id) + ".errlog" @@ -19,7 +20,7 @@ func errlog(logger hasPrintf, result FetchResult, pathPrefix string, debug bool, now := time.Now() - path := ErrlogPath(pathPrefix, result.DevId) + path := ErrlogPath(pathPrefix, result.DevID) f, openErr := os.OpenFile(path, os.O_CREATE|os.O_RDWR, 0640) if openErr != nil { @@ -58,7 +59,7 @@ func errlog(logger hasPrintf, result FetchResult, pathPrefix string, debug bool, now.String(), result.Code == fetchErrNone, result.End.Sub(result.Begin), - result.Model, result.DevId, result.DevHostPort, result.Transport, result.Code, result.Msg) + result.Model, result.DevID, result.DevHostPort, result.Transport, result.Code, result.Msg) if debug { logger.Printf("errlog debug: push: '%s': [%s]", path, msg) diff --git a/dev/filter.go b/dev/filter.go index 4ec65ff..f2b7eff 100644 --- a/dev/filter.go +++ b/dev/filter.go @@ -5,6 +5,7 @@ import ( "strconv" ) +// FilterTable stores line filters for custom line-by-line processing of configuration. type FilterTable struct { table map[string]FilterFunc re1 *regexp.Regexp @@ -13,8 +14,10 @@ type FilterTable struct { re4 *regexp.Regexp } +// FilterFunc is a helper function type for line filters. type FilterFunc func(hasPrintf, bool, *FilterTable, []byte, int) []byte +// NewFilterTable creates a filter table. func NewFilterTable(logger hasPrintf) *FilterTable { t := &FilterTable{ table: map[string]FilterFunc{}, diff --git a/dev/model.go b/dev/model.go index 1e4db82..8332a7c 100644 --- a/dev/model.go +++ b/dev/model.go @@ -12,11 +12,13 @@ import ( "github.com/udhos/jazigo/store" ) +// Model provides default attributes for model of devices. type Model struct { name string defaultAttr conf.DevAttributes } +// Device is an specific device. type Device struct { conf.DevConfig @@ -28,6 +30,7 @@ type Device struct { lastElapsed time.Duration } +// Username gets the username for login into a device. func (d *Device) Username() string { if d.Model() == "mikrotik" { return d.DevConfig.LoginUser + "+cte" @@ -35,35 +38,43 @@ func (d *Device) Username() string { return d.DevConfig.LoginUser } +// Printf formats device-specific messages into logs. func (d *Device) Printf(format string, v ...interface{}) { prefix := fmt.Sprintf("%s %s %s: ", d.DevConfig.Model, d.Id, d.HostPort) d.logger.Printf(prefix+format, v...) } +// Model gets the model name. func (d *Device) Model() string { return d.devModel.name } +// LastStatus gets a status string for last configuration backup. func (d *Device) LastStatus() bool { return d.lastStatus } +// LastTry provides the timestamp for the last backup attempt. func (d *Device) LastTry() time.Time { return d.lastTry } +// LastSuccess informs the timestamp for the last successful backup. func (d *Device) LastSuccess() time.Time { return d.lastSuccess } +// LastElapsed gets the elapsed time for the last backup attempt. func (d *Device) LastElapsed() time.Duration { return d.lastElapsed } +// Holdtime informs the devices' remaining holdtime. func (d *Device) Holdtime(now time.Time, holdtime time.Duration) time.Duration { return holdtime - now.Sub(d.lastSuccess) } +// RegisterModels adds known device models. func RegisterModels(logger hasPrintf, t *DeviceTable) { registerModelFortiOS(logger, t) registerModelCiscoAPIC(logger, t) @@ -76,6 +87,7 @@ func RegisterModels(logger hasPrintf, t *DeviceTable) { registerModelMikrotik(logger, t) } +// CreateDevice creates a new device in the device table. func CreateDevice(tab *DeviceTable, logger hasPrintf, modelName, id, hostPort, transports, user, pass, enable string, debug bool, change *conf.Change) error { logger.Printf("CreateDevice: %s %s %s %s", modelName, id, hostPort, transports) @@ -101,6 +113,7 @@ func CreateDevice(tab *DeviceTable, logger hasPrintf, modelName, id, hostPort, t return nil } +// NewDeviceFromConf creates a new device from a DevConfig. func NewDeviceFromConf(tab *DeviceTable, logger hasPrintf, cfg *conf.DevConfig) (*Device, error) { mod, getErr := tab.GetModel(cfg.Model) if getErr != nil { @@ -110,6 +123,7 @@ func NewDeviceFromConf(tab *DeviceTable, logger hasPrintf, cfg *conf.DevConfig) return d, nil } +// NewDevice creates a new device. func NewDevice(logger hasPrintf, mod *Model, id, hostPort, transports, loginUser, loginPassword, enablePassword string, debug bool) *Device { d := &Device{logger: logger, devModel: mod, DevConfig: conf.DevConfig{Model: mod.name, Id: id, HostPort: hostPort, Transports: transports, LoginUser: loginUser, LoginPassword: loginPassword, EnablePassword: enablePassword, Debug: debug}} d.Attr = mod.defaultAttr @@ -127,14 +141,16 @@ const ( fetchErrSave = 7 ) +// FetchRequest is a request for fetching a device configuration. type FetchRequest struct { Id string // fetch this device ReplyChan chan FetchResult // reply on this channel } +// FetchResult reports the result for fetching a device configuration. type FetchResult struct { Model string - DevId string + DevID string DevHostPort string Transport string Msg string // result error message @@ -151,7 +167,8 @@ type dialog struct { save [][]byte } -// fetch runs in a per-device goroutine +// Fetch captures a configuration for a device. +// Fetch runs in a per-device goroutine. func (d *Device) Fetch(tab DeviceUpdater, logger hasPrintf, resultCh chan FetchResult, delay time.Duration, repository, logPathPrefix string, opt *conf.AppConfig, ft *FilterTable) { result := d.fetch(logger, delay, repository, opt.MaxConfigFiles, ft) @@ -191,7 +208,7 @@ func (d *Device) fetch(logger hasPrintf, delay time.Duration, repository string, session, transport, logged, err := d.createTransport(logger) if err != nil { - return FetchResult{Model: modelName, DevId: d.Id, DevHostPort: d.HostPort, Transport: transport, Msg: fmt.Sprintf("fetch transport: %v", err), Code: fetchErrTransp, Begin: begin} + return FetchResult{Model: modelName, DevID: d.Id, DevHostPort: d.HostPort, Transport: transport, Msg: fmt.Sprintf("fetch transport: %v", err), Code: fetchErrTransp, Begin: begin} } defer session.Close() @@ -207,7 +224,7 @@ func (d *Device) fetch(logger hasPrintf, delay time.Duration, repository string, if d.Attr.NeedLoginChat && !logged { e, loginErr := d.login(logger, session, &capture) if loginErr != nil { - return FetchResult{Model: modelName, DevId: d.Id, DevHostPort: d.HostPort, Transport: transport, Msg: fmt.Sprintf("fetch login: %v", loginErr), Code: fetchErrLogin, Begin: begin} + return FetchResult{Model: modelName, DevID: d.Id, DevHostPort: d.HostPort, Transport: transport, Msg: fmt.Sprintf("fetch login: %v", loginErr), Code: fetchErrLogin, Begin: begin} } if e { enabled = true @@ -219,7 +236,7 @@ func (d *Device) fetch(logger hasPrintf, delay time.Duration, repository string, if d.Attr.NeedEnabledMode && !enabled { enableErr := d.enable(logger, session, &capture) if enableErr != nil { - return FetchResult{Model: modelName, DevId: d.Id, DevHostPort: d.HostPort, Transport: transport, Msg: fmt.Sprintf("fetch enable: %v", enableErr), Code: fetchErrEnable, Begin: begin} + return FetchResult{Model: modelName, DevID: d.Id, DevHostPort: d.HostPort, Transport: transport, Msg: fmt.Sprintf("fetch enable: %v", enableErr), Code: fetchErrEnable, Begin: begin} } } @@ -228,7 +245,7 @@ func (d *Device) fetch(logger hasPrintf, delay time.Duration, repository string, if d.Attr.NeedPagingOff { pagingErr := d.pagingOff(logger, session, &capture) if pagingErr != nil { - return FetchResult{Model: modelName, DevId: d.Id, DevHostPort: d.HostPort, Transport: transport, Msg: fmt.Sprintf("fetch pager off: %v", pagingErr), Code: fetchErrPager, Begin: begin} + return FetchResult{Model: modelName, DevID: d.Id, DevHostPort: d.HostPort, Transport: transport, Msg: fmt.Sprintf("fetch pager off: %v", pagingErr), Code: fetchErrPager, Begin: begin} } } @@ -236,16 +253,16 @@ func (d *Device) fetch(logger hasPrintf, delay time.Duration, repository string, if cmdErr := d.sendCommands(logger, session, &capture); cmdErr != nil { d.saveRollback(logger, &capture) - return FetchResult{Model: modelName, DevId: d.Id, DevHostPort: d.HostPort, Transport: transport, Msg: fmt.Sprintf("commands: %v", cmdErr), Code: fetchErrCommands, Begin: begin} + return FetchResult{Model: modelName, DevID: d.Id, DevHostPort: d.HostPort, Transport: transport, Msg: fmt.Sprintf("commands: %v", cmdErr), Code: fetchErrCommands, Begin: begin} } d.debugf("will save results") if saveErr := d.saveCommit(logger, &capture, repository, maxFiles, ft); saveErr != nil { - return FetchResult{Model: modelName, DevId: d.Id, DevHostPort: d.HostPort, Transport: transport, Msg: fmt.Sprintf("save commit: %v", saveErr), Code: fetchErrSave, Begin: begin} + return FetchResult{Model: modelName, DevID: d.Id, DevHostPort: d.HostPort, Transport: transport, Msg: fmt.Sprintf("save commit: %v", saveErr), Code: fetchErrSave, Begin: begin} } - return FetchResult{Model: modelName, DevId: d.Id, DevHostPort: d.HostPort, Transport: transport, Code: fetchErrNone, Begin: begin} + return FetchResult{Model: modelName, DevID: d.Id, DevHostPort: d.HostPort, Transport: transport, Code: fetchErrNone, Begin: begin} } func (d *Device) saveRollback(logger hasPrintf, capture *dialog) { @@ -256,18 +273,22 @@ func deviceDirectory(repository, id string) string { return filepath.Join(repository, id) } +// DeviceDir gets the directory used as device repository. func (d *Device) DeviceDir(repository string) string { return deviceDirectory(repository, d.Id) } +// DeviceFullPrefix gets the full path prefix for a device repository. func DeviceFullPrefix(repository, id string) string { return filepath.Join(deviceDirectory(repository, id), id+".") } +// DeviceFullPath get the full file path for a device repository. func DeviceFullPath(repository, id, file string) string { return filepath.Join(repository, id, file) } +// DevicePathPrefix gets the full path prefix for a device repository. func (d *Device) DevicePathPrefix(devDir string) string { return filepath.Join(devDir, d.Id+".") } @@ -449,10 +470,11 @@ READ_LOOP: } } +// Some constants. const ( - BS = 'H' - '@' - CR = '\r' - LF = '\n' + BS = 'H' - '@' // BS backspace + CR = '\r' // CR carriage return + LF = '\n' // LF linefeed ) func findLastLine(buf []byte) []byte { @@ -745,7 +767,7 @@ func round(val float64) int { return int(val + 0.5) } -// ClearDeviceStatus: forget about last success (expire holdtime). +// ClearDeviceStatus forgets about last success (expire holdtime). // Otherwise holdtime could prevent immediate backup. func ClearDeviceStatus(tab DeviceUpdater, devId string, logger hasPrintf, holdtime time.Duration) (*Device, error) { d, getErr := tab.GetDevice(devId) @@ -766,7 +788,7 @@ func ClearDeviceStatus(tab DeviceUpdater, devId string, logger hasPrintf, holdti return d, nil } -// UpdateLastSuccess: load device last success from filesystem. +// UpdateLastSuccess loads device last success from filesystem. func UpdateLastSuccess(tab *DeviceTable, logger hasPrintf, repository string) { for _, d := range tab.ListDevices() { prefix := d.DevicePathPrefix(d.DeviceDir(repository)) diff --git a/dev/scan.go b/dev/scan.go index 5400405..a26e440 100644 --- a/dev/scan.go +++ b/dev/scan.go @@ -7,7 +7,7 @@ import ( "github.com/udhos/jazigo/conf" ) -// Spawner: launches new goroutines to fetch requests received on channel reqChan +// Spawner launches new goroutines to fetch requests received on channel reqChan. func Spawner(tab DeviceUpdater, logger hasPrintf, reqChan chan FetchRequest, repository, logPathPrefix string, options *conf.Options, ft *FilterTable) { logger.Printf("Spawner: starting") @@ -21,12 +21,12 @@ func Spawner(tab DeviceUpdater, logger hasPrintf, reqChan chan FetchRequest, rep replyChan := req.ReplyChan // alias - devId := req.Id - d, getErr := tab.GetDevice(devId) + devID := req.Id + d, getErr := tab.GetDevice(devID) if getErr != nil { if replyChan != nil { now := time.Now() - replyChan <- FetchResult{DevId: devId, Msg: fmt.Sprintf("Spawner: could not find device: %v", getErr), Code: fetchErrGetDev, Begin: now, End: now} + replyChan <- FetchResult{DevID: devID, Msg: fmt.Sprintf("Spawner: could not find device: %v", getErr), Code: fetchErrGetDev, Begin: now, End: now} } continue } @@ -38,7 +38,7 @@ func Spawner(tab DeviceUpdater, logger hasPrintf, reqChan chan FetchRequest, rep logger.Printf("Spawner: exiting") } -// Scan: new scheduler +// Scan scans the list of devices dispatching backup requests to the Spawner thru the request channel reqChan. func Scan(tab DeviceUpdater, devices []*Device, logger hasPrintf, opt *conf.AppConfig, reqChan chan FetchRequest) (int, int, int) { deviceCount := len(devices) @@ -97,7 +97,7 @@ func Scan(tab DeviceUpdater, devices []*Device, logger hasPrintf, opt *conf.AppC end := time.Now() elap := end.Sub(r.Begin) - logger.Printf("Scan: recv %s %s %s %s msg=[%s] code=%d wait=%d remain=%d skipped=%d elap=%s", r.Model, r.DevId, r.DevHostPort, r.Transport, r.Msg, r.Code, wait, deviceCount-nextDevice, skipped, elap) + logger.Printf("Scan: recv %s %s %s %s msg=[%s] code=%d wait=%d remain=%d skipped=%d elap=%s", r.Model, r.DevID, r.DevHostPort, r.Transport, r.Msg, r.Code, wait, deviceCount-nextDevice, skipped, elap) good := r.Code == fetchErrNone @@ -120,10 +120,10 @@ func Scan(tab DeviceUpdater, devices []*Device, logger hasPrintf, opt *conf.AppC return success, deviceCount - success, skipped + deleted } -func updateDeviceStatus(tab DeviceUpdater, devId string, good bool, last time.Time, elapsed time.Duration, logger hasPrintf, holdtime time.Duration) { - d, getErr := tab.GetDevice(devId) +func updateDeviceStatus(tab DeviceUpdater, devID string, good bool, last time.Time, elapsed time.Duration, logger hasPrintf, holdtime time.Duration) { + d, getErr := tab.GetDevice(devID) if getErr != nil { - logger.Printf("updateDeviceStatus: '%s' not found: %v", devId, getErr) + logger.Printf("updateDeviceStatus: '%s' not found: %v", devID, getErr) return } @@ -140,5 +140,5 @@ func updateDeviceStatus(tab DeviceUpdater, devId string, good bool, last time.Ti tab.UpdateDevice(d) h2 := d.Holdtime(now, holdtime) - logger.Printf("updateDeviceStatus: device %s holdtime: old=%v new=%v", devId, h1, h2) + logger.Printf("updateDeviceStatus: device %s holdtime: old=%v new=%v", devID, h1, h2) } diff --git a/dev/table.go b/dev/table.go index fa56b7a..36f2c2b 100644 --- a/dev/table.go +++ b/dev/table.go @@ -7,7 +7,7 @@ import ( "sync" ) -// DeviceTable: goroutine concurrency-safe DeviceTable. +// DeviceTable is goroutine concurrency-safe list of devices. // Data is fully copied when either entering or leaving DeviceTable. // Data is not shared with pointers. type DeviceTable struct { @@ -16,15 +16,18 @@ type DeviceTable struct { lock sync.RWMutex } +// DeviceUpdater is helper interface for a device store which can provide and update device information. type DeviceUpdater interface { GetDevice(id string) (*Device, error) UpdateDevice(d *Device) error } +// NewDeviceTable creates a device table. func NewDeviceTable() *DeviceTable { return &DeviceTable{models: map[string]*Model{}, devices: map[string]*Device{}, lock: sync.RWMutex{}} } +// GetModel looks up a model in the device table. func (t *DeviceTable) GetModel(modelName string) (*Model, error) { t.lock.RLock() defer t.lock.RUnlock() @@ -37,6 +40,7 @@ func (t *DeviceTable) GetModel(modelName string) (*Model, error) { return nil, fmt.Errorf("DeviceTable.GetModel: not found") } +// SetModel adds a model to the device table. func (t *DeviceTable) SetModel(m *Model, logger hasPrintf) error { logger.Printf("DeviceTable.SetModel: registering model: '%s'", m.name) @@ -53,6 +57,7 @@ func (t *DeviceTable) SetModel(m *Model, logger hasPrintf) error { return nil } +// GetDevice finds a device in the device table. func (t *DeviceTable) GetDevice(id string) (*Device, error) { t.lock.RLock() defer t.lock.RUnlock() @@ -65,6 +70,7 @@ func (t *DeviceTable) GetDevice(id string) (*Device, error) { return nil, fmt.Errorf("DeviceTable.GetDevice: not found") } +// SetDevice adds a device into the device table. func (t *DeviceTable) SetDevice(d *Device) error { t.lock.Lock() defer t.lock.Unlock() @@ -79,6 +85,7 @@ func (t *DeviceTable) SetDevice(d *Device) error { return nil } +// DeleteDevice sets a device as deleted in the device table. func (t *DeviceTable) DeleteDevice(id string) { t.lock.Lock() defer t.lock.Unlock() @@ -88,6 +95,7 @@ func (t *DeviceTable) DeleteDevice(id string) { } } +// PurgeDevice actually removes a device from the device table. func (t *DeviceTable) PurgeDevice(id string) { t.lock.Lock() defer t.lock.Unlock() @@ -95,6 +103,7 @@ func (t *DeviceTable) PurgeDevice(id string) { delete(t.devices, id) } +// UpdateDevice updates device info in the device table. func (t *DeviceTable) UpdateDevice(d *Device) error { t.lock.Lock() defer t.lock.Unlock() @@ -109,6 +118,7 @@ func (t *DeviceTable) UpdateDevice(d *Device) error { return nil } +// ListDevices gets the list of devices. func (t *DeviceTable) ListDevices() []*Device { t.lock.RLock() defer t.lock.RUnlock() @@ -127,7 +137,8 @@ func copyDeviceMapToSlice(m map[string]*Device) []*Device { return devices } -func (t *DeviceTable) FindDeviceFreeId(prefix string) string { +// FindDeviceFreeID finds an ID available for a new device. +func (t *DeviceTable) FindDeviceFreeID(prefix string) string { pLen := len(prefix) devices := t.ListDevices() highest := 0 @@ -149,6 +160,7 @@ func (t *DeviceTable) FindDeviceFreeId(prefix string) string { return prefix + strconv.Itoa(free) } +// ListModels gets the list of models. func (t *DeviceTable) ListModels() []string { t.lock.RLock() defer t.lock.RUnlock() diff --git a/dev/transport.go b/dev/transport.go index 827c16e..80bcbd7 100644 --- a/dev/transport.go +++ b/dev/transport.go @@ -151,14 +151,14 @@ func (s *transpSSH) Close() error { return nil } -func openTransportPipe(logger hasPrintf, modelName, devId, hostPort, transports, user, pass string, args []string, debug bool, timeout time.Duration) (transp, string, bool, error) { - s, err := openPipe(logger, modelName, devId, hostPort, transports, user, pass, args, debug, timeout) +func openTransportPipe(logger hasPrintf, modelName, devID, hostPort, transports, user, pass string, args []string, debug bool, timeout time.Duration) (transp, string, bool, error) { + s, err := openPipe(logger, modelName, devID, hostPort, transports, user, pass, args, debug, timeout) return s, "pipe", true, err } -func openPipe(logger hasPrintf, modelName, devId, hostPort, transports, user, pass string, args []string, debug bool, timeout time.Duration) (transp, error) { +func openPipe(logger hasPrintf, modelName, devID, hostPort, transports, user, pass string, args []string, debug bool, timeout time.Duration) (transp, error) { - devLabel := fmt.Sprintf("%s %s %s", modelName, devId, hostPort) + devLabel := fmt.Sprintf("%s %s %s", modelName, devID, hostPort) logger.Printf("openPipe: %s - opening", devLabel) @@ -194,7 +194,7 @@ func openPipe(logger hasPrintf, modelName, devId, hostPort, transports, user, pa logger.Printf("openPipe: %s - starting", devLabel) - os.Setenv("JAZIGO_DEV_ID", devId) + os.Setenv("JAZIGO_DEV_ID", devID) os.Setenv("JAZIGO_DEV_HOSTPORT", hostPort) os.Setenv("JAZIGO_DEV_USER", user) os.Setenv("JAZIGO_DEV_PASS", pass) @@ -209,7 +209,7 @@ func openPipe(logger hasPrintf, modelName, devId, hostPort, transports, user, pa return s, nil } -func openTransport(logger hasPrintf, modelName, devId, hostPort, transports, user, pass string) (transp, string, bool, error) { +func openTransport(logger hasPrintf, modelName, devID, hostPort, transports, user, pass string) (transp, string, bool, error) { tList := strings.Split(transports, ",") if len(tList) < 1 { return nil, transports, false, fmt.Errorf("openTransport: missing transports: [%s]", transports) @@ -223,7 +223,7 @@ func openTransport(logger hasPrintf, modelName, devId, hostPort, transports, use switch t { case "ssh": hp := forceHostPort(hostPort, "22") - s, err := openSSH(logger, modelName, devId, hp, timeout, user, pass) + s, err := openSSH(logger, modelName, devID, hp, timeout, user, pass) if err == nil { return s, t, true, nil } @@ -231,14 +231,14 @@ func openTransport(logger hasPrintf, modelName, devId, hostPort, transports, use lastErr = err case "telnet": hp := forceHostPort(hostPort, "23") - s, err := openTelnet(logger, modelName, devId, hp, timeout) + s, err := openTelnet(logger, modelName, devID, hp, timeout) if err == nil { return s, t, false, nil } logger.Printf("openTransport: %v", err) lastErr = err default: - s, err := openTCP(logger, modelName, devId, hostPort, timeout) + s, err := openTCP(logger, modelName, devID, hostPort, timeout) if err == nil { return s, t, false, nil } @@ -247,7 +247,7 @@ func openTransport(logger hasPrintf, modelName, devId, hostPort, transports, use } } - return nil, transports, false, fmt.Errorf("openTransport: %s %s %s %s - unable to open transport: last error: %v", modelName, devId, hostPort, transports, lastErr) + return nil, transports, false, fmt.Errorf("openTransport: %s %s %s %s - unable to open transport: last error: %v", modelName, devID, hostPort, transports, lastErr) } func forceHostPort(hostPort, defaultPort string) string { @@ -258,11 +258,11 @@ func forceHostPort(hostPort, defaultPort string) string { return hostPort } -func openSSH(logger hasPrintf, modelName, devId, hostPort string, timeout time.Duration, user, pass string) (transp, error) { +func openSSH(logger hasPrintf, modelName, devID, hostPort string, timeout time.Duration, user, pass string) (transp, error) { conn, dialErr := net.DialTimeout("tcp", hostPort, timeout) if dialErr != nil { - return nil, fmt.Errorf("openSSH: Dial: %s %s %s - %v", modelName, devId, hostPort, dialErr) + return nil, fmt.Errorf("openSSH: Dial: %s %s %s - %v", modelName, devID, hostPort, dialErr) } conf := &ssh.Config{} @@ -280,12 +280,12 @@ func openSSH(logger hasPrintf, modelName, devId, hostPort string, timeout time.D c, chans, reqs, connErr := ssh.NewClientConn(conn, hostPort, config) if connErr != nil { - return nil, fmt.Errorf("openSSH: NewClientConn: %s %s %s - %v", modelName, devId, hostPort, connErr) + return nil, fmt.Errorf("openSSH: NewClientConn: %s %s %s - %v", modelName, devID, hostPort, connErr) } cli := ssh.NewClient(c, chans, reqs) - s := &transpSSH{conn: conn, client: cli, devLabel: fmt.Sprintf("%s %s %s", modelName, devId, hostPort) /*, logger: logger*/} + s := &transpSSH{conn: conn, client: cli, devLabel: fmt.Sprintf("%s %s %s", modelName, devID, hostPort) /*, logger: logger*/} ses, sessionErr := s.client.NewSession() if sessionErr != nil { @@ -328,21 +328,21 @@ func openSSH(logger hasPrintf, modelName, devId, hostPort string, timeout time.D return s, nil } -func openTelnet(logger hasPrintf, modelName, devId, hostPort string, timeout time.Duration) (transp, error) { +func openTelnet(logger hasPrintf, modelName, devID, hostPort string, timeout time.Duration) (transp, error) { conn, err := net.DialTimeout("tcp", hostPort, timeout) if err != nil { - return nil, fmt.Errorf("openTelnet: %s %s %s - %v", modelName, devId, hostPort, err) + return nil, fmt.Errorf("openTelnet: %s %s %s - %v", modelName, devID, hostPort, err) } return &transpTelnet{conn, logger}, nil } -func openTCP(logger hasPrintf, modelName, devId, hostPort string, timeout time.Duration) (transp, error) { +func openTCP(logger hasPrintf, modelName, devID, hostPort string, timeout time.Duration) (transp, error) { conn, err := net.DialTimeout("tcp", hostPort, timeout) if err != nil { - return nil, fmt.Errorf("openTCP: %s %s %s - %v", modelName, devId, hostPort, err) + return nil, fmt.Errorf("openTCP: %s %s %s - %v", modelName, devID, hostPort, err) } return &transpTCP{conn}, nil diff --git a/jazigo/logg.go b/jazigo/logg.go index 262eaca..63f8bb1 100644 --- a/jazigo/logg.go +++ b/jazigo/logg.go @@ -9,7 +9,8 @@ import ( "github.com/udhos/jazigo/store" ) -type logfile struct { +// Logfile log stream implements io.Writer in order to be attachable to log.New() +type Logfile struct { logPathPrefix string maxFiles int maxFileSize int64 @@ -20,8 +21,8 @@ type logfile struct { } // NewLogfile creates a new log stream capable of automatically saving to filesystem. -func NewLogfile(prefix string, maxFiles int, maxSize int64, checkInterval time.Duration) *logfile { - l := &logfile{ +func NewLogfile(prefix string, maxFiles int, maxSize int64, checkInterval time.Duration) *Logfile { + l := &Logfile{ logPathPrefix: prefix, maxFiles: maxFiles, maxFileSize: maxSize, @@ -56,7 +57,7 @@ func touchFunc(w store.HasWrite) error { return wrErr } -func (l *logfile) rotate() { +func (l *Logfile) rotate() { if l.output != nil { l.output.Close() l.output = nil @@ -68,19 +69,19 @@ func (l *logfile) rotate() { l.output.Close() l.output = nil } - l.logger.Printf("logfile.rotate: could not find log path: %v", newErr) + l.logger.Printf("Logfile.rotate: could not find log path: %v", newErr) return } var openErr error l.output, openErr = openAppend(outputPath) if openErr != nil { - l.logger.Printf("logfile.rotate: could not open log: %v", openErr) + l.logger.Printf("Logfile.rotate: could not open log: %v", openErr) } } // Write implements io.Writer in order to be attached to log.New(). -func (l *logfile) Write(b []byte) (int, error) { +func (l *Logfile) Write(b []byte) (int, error) { if l.output == nil { l.rotate() diff --git a/jazigo/main.go b/jazigo/main.go index 0b0c35b..75a1e2f 100644 --- a/jazigo/main.go +++ b/jazigo/main.go @@ -380,7 +380,7 @@ func manageDeviceList(jaz *app, imp, del, purge, list bool) error { jaz.logf("reading device list from stdin") autoID := "auto" - nextID := jaz.table.FindDeviceFreeId(autoID) + nextID := jaz.table.FindDeviceFreeID(autoID) valueStr := nextID[len(autoID):] value, valErr := strconv.Atoi(valueStr) if valErr != nil { diff --git a/jazigo/web_ui.go b/jazigo/web_ui.go index 73a6fd0..a1786c6 100644 --- a/jazigo/web_ui.go +++ b/jazigo/web_ui.go @@ -790,7 +790,7 @@ func buildCreateDevPanel(jaz *app, s gwu.Session, refresh func(gwu.Event), creat createAutoId := func() { if strings.HasPrefix(textId.Text(), autoIdPrefix) { - textId.SetText(jaz.table.FindDeviceFreeId(autoIdPrefix)) + textId.SetText(jaz.table.FindDeviceFreeID(autoIdPrefix)) } } @@ -803,7 +803,7 @@ func buildCreateDevPanel(jaz *app, s gwu.Session, refresh func(gwu.Event), creat id := textId.Text() if id == autoIdPrefix { - id = jaz.table.FindDeviceFreeId(autoIdPrefix) + id = jaz.table.FindDeviceFreeID(autoIdPrefix) } change := conf.Change{ diff --git a/store/s3.go b/store/s3.go index 0120da9..efeeb88 100644 --- a/store/s3.go +++ b/store/s3.go @@ -415,6 +415,8 @@ func s3fileCompare(p1, p2 string, maxSize int64) (bool, error) { return cmp.CompareReader(r1, r2) } +// S3URL builds the URL for an S3 bucket. +// The path is an ARN: "arn:aws:s3:region::bucket/folder/file.xxx" func S3URL(path string) string { region, bucket, key := s3parse(path) diff --git a/store/store.go b/store/store.go index e106b1c..7f855bd 100644 --- a/store/store.go +++ b/store/store.go @@ -21,31 +21,32 @@ type hasPrintf interface { Printf(fmt string, v ...interface{}) } -type sortByCommitId struct { +type sortByCommitID struct { data []string logger hasPrintf } -func (s sortByCommitId) Len() int { +func (s sortByCommitID) Len() int { return len(s.data) } -func (s sortByCommitId) Swap(i, j int) { +func (s sortByCommitID) Swap(i, j int) { s.data[i], s.data[j] = s.data[j], s.data[i] } -func (s sortByCommitId) Less(i, j int) bool { +func (s sortByCommitID) Less(i, j int) bool { s1 := s.data[i] - id1, err1 := ExtractCommitIdFromFilename(s1) + id1, err1 := ExtractCommitIDFromFilename(s1) if err1 != nil { - s.logger.Printf("sortByCommitId.Less: error parsing config file path: '%s': %v", s1, err1) + s.logger.Printf("sortByCommitID.Less: error parsing config file path: '%s': %v", s1, err1) } s2 := s.data[j] - id2, err2 := ExtractCommitIdFromFilename(s2) + id2, err2 := ExtractCommitIDFromFilename(s2) if err2 != nil { - s.logger.Printf("sortByCommitId.Less: error parsing config file path: '%s': %v", s2, err2) + s.logger.Printf("sortByCommitID.Less: error parsing config file path: '%s': %v", s2, err2) } return id1 < id2 } +// Init starts the store by providing a logger and default S3 region. func Init(logger hasPrintf, region string) { if logger == nil { panic("store.Init: nil logger") @@ -53,7 +54,8 @@ func Init(logger hasPrintf, region string) { s3init(logger, region) } -func ExtractCommitIdFromFilename(filename string) (int, error) { +// ExtractCommitIDFromFilename gets the commit from a filename: "aaa.1" => 1 +func ExtractCommitIDFromFilename(filename string) (int, error) { lastDot := strings.LastIndexByte(filename, '.') commitId := filename[lastDot+1:] id, err := strconv.Atoi(commitId) @@ -99,6 +101,7 @@ func tryShortcut(configPathPrefix string, logger hasPrintf) string { return "" // not found } +// FindLastConfig finds the last file under a path prefix. func FindLastConfig(configPathPrefix string, logger hasPrintf) (string, error) { if path := tryShortcut(configPathPrefix, logger); path != "" { @@ -124,7 +127,7 @@ func FindLastConfig(configPathPrefix string, logger hasPrintf) (string, error) { maxId := -1 last := "" for _, m := range matches { - id, idErr := ExtractCommitIdFromFilename(m) + id, idErr := ExtractCommitIDFromFilename(m) if idErr != nil { return "", fmt.Errorf("FindLastConfig: bad commit id: %s: %v", m, idErr) } @@ -141,6 +144,7 @@ func FindLastConfig(configPathPrefix string, logger hasPrintf) (string, error) { return lastPath, nil } +// ListConfigSorted retrieves the sorted list of files under a path prefix. func ListConfigSorted(configPathPrefix string, reverse bool, logger hasPrintf) (string, []string, error) { dirname, matches, err := ListConfig(configPathPrefix, logger) @@ -149,9 +153,9 @@ func ListConfigSorted(configPathPrefix string, reverse bool, logger hasPrintf) ( } if reverse { - sort.Sort(sort.Reverse(sortByCommitId{data: matches, logger: logger})) + sort.Sort(sort.Reverse(sortByCommitID{data: matches, logger: logger})) } else { - sort.Sort(sortByCommitId{data: matches, logger: logger}) + sort.Sort(sortByCommitID{data: matches, logger: logger}) } return dirname, matches, nil @@ -180,6 +184,7 @@ func dirList(path string) (string, []string, error) { return dirname, names, nil } +// ListConfig retrieves files under a path prefix. func ListConfig(configPathPrefix string, logger hasPrintf) (string, []string, error) { var dirname string @@ -206,6 +211,7 @@ func ListConfig(configPathPrefix string, logger hasPrintf) (string, []string, er return dirname, matches, nil } +// HasWrite is a helper interface for types providing the method Write(). type HasWrite interface { Write(p []byte) (int, error) } @@ -249,6 +255,7 @@ func fileRename(p1, p2 string) error { return os.Rename(p1, p2) } +// FileRead reads bytes from file. func FileRead(path string, maxSize int64) ([]byte, error) { var r *io.LimitedReader @@ -324,6 +331,7 @@ func writeFile(path string, writeFunc func(HasWrite) error, contentType string) return nil } +// SaveNewConfig saves data to a new file. The function writeFunc must be provided to issue the actual data. func SaveNewConfig(configPathPrefix string, maxFiles int, logger hasPrintf, writeFunc func(HasWrite) error, changesOnly bool, contentType string) (string, error) { // get tmp file @@ -351,7 +359,7 @@ func SaveNewConfig(configPathPrefix string, maxFiles int, logger hasPrintf, writ previousFound = false } - id, err2 := ExtractCommitIdFromFilename(lastConfig) + id, err2 := ExtractCommitIDFromFilename(lastConfig) if err2 != nil { logger.Printf("SaveNewConfig: error parsing config path: [%s]: %v", lastConfig, err2) } @@ -440,6 +448,7 @@ func eraseOldFiles(configPathPrefix string, maxFiles int, logger hasPrintf) { } } +// FileInfo returns file modification time and size. func FileInfo(path string) (time.Time, int64, error) { if s3path(path) { @@ -465,6 +474,7 @@ func fileCompare(p1, p2 string) (bool, error) { return cmp.CompareFile(p1, p2) } +// MkDir creates a new directory. func MkDir(path string) error { if s3path(path) {