<commit_before>// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package pipoint

import (
	"fmt"
	"os"
	"os/exec"
)

// AudioOut can play files or speech.
type AudioOut struct {
	queued chan *exec.Cmd
}

// NewAudioOut creates a new, running audio output.
func NewAudioOut() *AudioOut {
	a := &AudioOut{
		queued: make(chan *exec.Cmd, 10),
	}
	go a.run()
	return a
}

// Play plays an audio file.
func (a *AudioOut) Play(path string) {
	a.queued <- exec.Command("ogg123", path)
}

// Say plays a pre-recorded phrase, or falls back to espeak.
func (a *AudioOut) Say(text string) {
	rendered := fmt.Sprintf("phrase/%s.ogg", NormText(text))
	fi, err := os.Stat(rendered)

	if err == nil && fi.Mode().IsRegular() {
		a.Play(rendered)
	} else {
		a.queued <- exec.Command("espeak", text)
	}
}

// run executes the commands to play the sounds.
func (a *AudioOut) run() {
	done := make(chan bool)

	for {
		cmd := <-a.queued
		go func() {
			cmd.Run()
			done <- true
		}()

		for c := false; !c; {
			select {
			case <-done:
				c = true
			case <-a.queued:
				// Discard anything that comes in
				// while this command is running.
				break
			}
		}
	}
}
<commit_msg>pipoint: don't discard queued notifications.<commit_after>// Copyright 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package pipoint

import (
	"fmt"
	"os"
	"os/exec"
)

// AudioOut can play files or speech.
type AudioOut struct {
	queued chan *exec.Cmd
}

// NewAudioOut creates a new, running audio output.
func NewAudioOut() *AudioOut {
	a := &AudioOut{
		queued: make(chan *exec.Cmd, 10),
	}
	go a.run()
	return a
}

// Play plays an audio file.
func (a *AudioOut) Play(path string) {
	a.queued <- exec.Command("ogg123", path)
}

// Say plays a pre-recorded phrase, or falls back to espeak.
func (a *AudioOut) Say(text string) {
	rendered := fmt.Sprintf("phrase/%s.ogg", NormText(text))
	fi, err := os.Stat(rendered)

	if err == nil && fi.Mode().IsRegular() {
		a.Play(rendered)
	} else {
		a.queued <- exec.Command("espeak", text)
	}
}

// run executes the commands to play the sounds.
func (a *AudioOut) run() {
	for {
		cmd := <-a.queued
		cmd.Run()
	}
}
<|endoftext|>


<commit_before>package logrus

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"os"
	"sort"
	"strings"
	"time"

	"github.com/burke/ttyutils"
	"github.com/tobi/airbrake-go"
)

type Entry struct {
	logger *Logger
	Data   Fields
}

var baseTimestamp time.Time

func init() {
	baseTimestamp = time.Now()
}

func miniTS() int {
	return int(time.Since(baseTimestamp) / time.Second)
}

func NewEntry(logger *Logger) *Entry {
	return &Entry{
		logger: logger,
		// Default is three fields, give a little extra room. Shouldn't hurt the
		// scale.
		Data: make(Fields, 5),
	}
}

// TODO: Other formats?
func (entry *Entry) Reader() (*bytes.Buffer, error) {
	var serialized []byte
	var err error

	if Environment == "production" {
		serialized, err = json.Marshal(entry.Data)
		if err != nil {
			return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
		}
		serialized = append(serialized, '\n')
	} else {
		levelText := strings.ToUpper(entry.Data["level"].(string))
		levelColor := 34
		if levelText != "INFO" {
			levelColor = 31
		}
		if ttyutils.IsTerminal(os.Stdout.Fd()) {
			serialized = append(serialized, []byte(fmt.Sprintf("\x1b[%dm%s\x1b[0m[%04d] %-45s \x1b[%dm(\x1b[0m", levelColor, levelText, miniTS(), entry.Data["msg"], levelColor))...)
		}

		// TODO: Pretty-print more by coloring when stdout is a tty
		// TODO: If this is a println, it'll do a newline and then closing quote.
		keys := make([]string, 0)
		for k, _ := range entry.Data {
			if k != "level" && k != "time" && k != "msg" {
				keys = append(keys, k)
			}
		}
		sort.Strings(keys)
		first := true
		for _, k := range keys {
			v := entry.Data[k]
			if first {
				first = false
			} else {
				serialized = append(serialized, ' ')
			}
			serialized = append(serialized, []byte(fmt.Sprintf("\x1b[34m%s\x1b[0m=%v", k, v))...)
		}

		serialized = append(serialized, []byte(fmt.Sprintf("\x1b[%dm)\x1b[0m", levelColor))...)

		serialized = append(serialized, '\n')
	}

	return bytes.NewBuffer(serialized), nil
}

func (entry *Entry) WithField(key string, value interface{}) *Entry {
	entry.Data[key] = value
	return entry
}

func (entry *Entry) WithFields(fields Fields) *Entry {
	for key, value := range fields {
		entry.WithField(key, value)
	}

	return entry
}

func (entry *Entry) log(level string, msg string) string {
	// TODO: Is the default format output from String() the one we want?
	entry.Data["time"] = time.Now().String()
	entry.Data["level"] = level
	// TODO: Is this the best name?
	entry.Data["msg"] = msg

	reader, err := entry.Reader()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v", err)
	}

	// Send HTTP request in a goroutine in warning environment to not halt the
	// main thread. It's sent before logging due to panic.
	if level == "warning" {
		// TODO: new() should spawn an airbrake goroutine and this should send to
		// that channel. This prevent us from spawning hundreds of goroutines in a
		// hot code path generating a warning.
		go entry.airbrake(reader.String())
	} else if level == "fatal" || level == "panic" {
		entry.airbrake(reader.String())
	}

	entry.logger.mu.Lock()
	defer entry.logger.mu.Unlock()

	_, err = io.Copy(entry.logger.Out, reader)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v", err)
	}

	return reader.String()
}

func (entry *Entry) Debug(args ...interface{}) {
	if Level >= LevelDebug {
		entry.log("debug", fmt.Sprint(args...))
	}
}

func (entry *Entry) Info(args ...interface{}) {
	if Level >= LevelInfo {
		entry.log("info", fmt.Sprint(args...))
	}
}

func (entry *Entry) Print(args ...interface{}) {
	if Level >= LevelInfo {
		entry.log("info", fmt.Sprint(args...))
	}
}

func (entry *Entry) Warning(args ...interface{}) {
	if Level >= LevelWarning {
		entry.log("warning", fmt.Sprint(args...))
	}
}

func (entry *Entry) Fatal(args ...interface{}) {
	if Level >= LevelFatal {
		entry.log("fatal", fmt.Sprint(args...))
	}
	os.Exit(1)
}

func (entry *Entry) Panic(args ...interface{}) {
	if Level >= LevelPanic {
		msg := entry.log("panic", fmt.Sprint(args...))
		panic(msg)
	}
	panic(fmt.Sprint(args...))
}

// Entry Printf family functions

func (entry *Entry) Debugf(format string, args ...interface{}) {
	entry.Debug(fmt.Sprintf(format, args...))
}

func (entry *Entry) Infof(format string, args ...interface{}) {
	entry.Info(fmt.Sprintf(format, args...))
}

func (entry *Entry) Printf(format string, args ...interface{}) {
	entry.Print(fmt.Sprintf(format, args...))
}

func (entry *Entry) Warningf(format string, args ...interface{}) {
	entry.Warning(fmt.Sprintf(format, args...))
}

func (entry *Entry) Fatalf(format string, args ...interface{}) {
	entry.Fatal(fmt.Sprintf(format, args...))
}

func (entry *Entry) Panicf(format string, args ...interface{}) {
	entry.Panic(fmt.Sprintf(format, args...))
}

// Entry Println family functions

func (entry *Entry) Debugln(args ...interface{}) {
	entry.Debug(fmt.Sprintln(args...))
}

func (entry *Entry) Infoln(args ...interface{}) {
	entry.Info(fmt.Sprintln(args...))
}

func (entry *Entry) Println(args ...interface{}) {
	entry.Print(fmt.Sprintln(args...))
}

func (entry *Entry) Warningln(args ...interface{}) {
	entry.Warning(fmt.Sprintln(args...))
}

func (entry *Entry) Fatalln(args ...interface{}) {
	entry.Fatal(fmt.Sprintln(args...))
}

func (entry *Entry) Panicln(args ...interface{}) {
	entry.Panic(fmt.Sprintln(args...))
}

func (entry *Entry) airbrake(exception string) {
	err := airbrake.Notify(errors.New(exception))
	if err != nil {
		entry.logger.WithFields(Fields{
			"source":   "airbrake",
			"endpoint": airbrake.Endpoint,
		}).Infof("Failed to send exception to Airbrake")
	}
}
<commit_msg>entry: keys colored by severity<commit_after>package logrus

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"os"
	"sort"
	"strings"
	"time"

	"github.com/burke/ttyutils"
	"github.com/tobi/airbrake-go"
)

type Entry struct {
	logger *Logger
	Data   Fields
}

var baseTimestamp time.Time

func init() {
	baseTimestamp = time.Now()
}

func miniTS() int {
	return int(time.Since(baseTimestamp) / time.Second)
}

func NewEntry(logger *Logger) *Entry {
	return &Entry{
		logger: logger,
		// Default is three fields, give a little extra room. Shouldn't hurt the
		// scale.
		Data: make(Fields, 5),
	}
}

// TODO: Other formats?
func (entry *Entry) Reader() (*bytes.Buffer, error) {
	var serialized []byte
	var err error

	if Environment == "production" {
		serialized, err = json.Marshal(entry.Data)
		if err != nil {
			return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
		}
		serialized = append(serialized, '\n')
	} else {
		levelText := strings.ToUpper(entry.Data["level"].(string))[0:4]
		levelColor := 34

		if entry.Data["level"] == "warning" {
			levelColor = 33
		} else if entry.Data["level"] == "fatal" ||
			entry.Data["level"] == "panic" {
			levelColor = 31
		}

		if ttyutils.IsTerminal(os.Stdout.Fd()) {
			serialized = append(serialized, []byte(fmt.Sprintf("\x1b[%dm%s\x1b[0m[%04d] %-45s ", levelColor, levelText, miniTS(), entry.Data["msg"]))...)
		}

		// TODO: Pretty-print more by coloring when stdout is a tty
		// TODO: If this is a println, it'll do a newline and then closing quote.
		keys := make([]string, 0)
		for k, _ := range entry.Data {
			if k != "level" && k != "time" && k != "msg" {
				keys = append(keys, k)
			}
		}
		sort.Strings(keys)
		first := true
		for _, k := range keys {
			v := entry.Data[k]
			if first {
				first = false
			} else {
				serialized = append(serialized, ' ')
			}
			serialized = append(serialized, []byte(fmt.Sprintf("\x1b[%dm%s\x1b[0m=%v", levelColor, k, v))...)
		}

		// serialized = append(serialized, []byte(fmt.Sprintf("\x1b[%dm)\x1b[0m", levelColor))...)

		serialized = append(serialized, '\n')
	}

	return bytes.NewBuffer(serialized), nil
}

func (entry *Entry) WithField(key string, value interface{}) *Entry {
	entry.Data[key] = value
	return entry
}

func (entry *Entry) WithFields(fields Fields) *Entry {
	for key, value := range fields {
		entry.WithField(key, value)
	}

	return entry
}

func (entry *Entry) log(level string, msg string) string {
	// TODO: Is the default format output from String() the one we want?
	entry.Data["time"] = time.Now().String()
	entry.Data["level"] = level
	// TODO: Is this the best name?
	entry.Data["msg"] = msg

	reader, err := entry.Reader()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v", err)
	}

	if Environment != "development" {
		// Send HTTP request in a goroutine in warning environment to not halt the
		// main thread. It's sent before logging due to panic.
		if level == "warning" {
			// TODO: new() should spawn an airbrake goroutine and this should send to
			// that channel. This prevent us from spawning hundreds of goroutines in a
			// hot code path generating a warning.
			go entry.airbrake(reader.String())
		} else if level == "fatal" || level == "panic" {
			entry.airbrake(reader.String())
		}
	}

	entry.logger.mu.Lock()
	defer entry.logger.mu.Unlock()

	_, err = io.Copy(entry.logger.Out, reader)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v", err)
	}

	return reader.String()
}

func (entry *Entry) Debug(args ...interface{}) {
	if Level >= LevelDebug {
		entry.log("debug", fmt.Sprint(args...))
	}
}

func (entry *Entry) Info(args ...interface{}) {
	if Level >= LevelInfo {
		entry.log("info", fmt.Sprint(args...))
	}
}

func (entry *Entry) Print(args ...interface{}) {
	if Level >= LevelInfo {
		entry.log("info", fmt.Sprint(args...))
	}
}

func (entry *Entry) Warning(args ...interface{}) {
	if Level >= LevelWarning {
		entry.log("warning", fmt.Sprint(args...))
	}
}

func (entry *Entry) Fatal(args ...interface{}) {
	if Level >= LevelFatal {
		entry.log("fatal", fmt.Sprint(args...))
	}
	os.Exit(1)
}

func (entry *Entry) Panic(args ...interface{}) {
	if Level >= LevelPanic {
		msg := entry.log("panic", fmt.Sprint(args...))
		panic(msg)
	}
	panic(fmt.Sprint(args...))
}

// Entry Printf family functions

func (entry *Entry) Debugf(format string, args ...interface{}) {
	entry.Debug(fmt.Sprintf(format, args...))
}

func (entry *Entry) Infof(format string, args ...interface{}) {
	entry.Info(fmt.Sprintf(format, args...))
}

func (entry *Entry) Printf(format string, args ...interface{}) {
	entry.Print(fmt.Sprintf(format, args...))
}

func (entry *Entry) Warningf(format string, args ...interface{}) {
	entry.Warning(fmt.Sprintf(format, args...))
}

func (entry *Entry) Fatalf(format string, args ...interface{}) {
	entry.Fatal(fmt.Sprintf(format, args...))
}

func (entry *Entry) Panicf(format string, args ...interface{}) {
	entry.Panic(fmt.Sprintf(format, args...))
}

// Entry Println family functions

func (entry *Entry) Debugln(args ...interface{}) {
	entry.Debug(fmt.Sprintln(args...))
}

func (entry *Entry) Infoln(args ...interface{}) {
	entry.Info(fmt.Sprintln(args...))
}

func (entry *Entry) Println(args ...interface{}) {
	entry.Print(fmt.Sprintln(args...))
}

func (entry *Entry) Warningln(args ...interface{}) {
	entry.Warning(fmt.Sprintln(args...))
}

func (entry *Entry) Fatalln(args ...interface{}) {
	entry.Fatal(fmt.Sprintln(args...))
}

func (entry *Entry) Panicln(args ...interface{}) {
	entry.Panic(fmt.Sprintln(args...))
}

func (entry *Entry) airbrake(exception string) {
	err := airbrake.Notify(errors.New(exception))
	if err != nil {
		entry.logger.WithFields(Fields{
			"source":   "airbrake",
			"endpoint": airbrake.Endpoint,
		}).Infof("Failed to send exception to Airbrake")
	}
}
<|endoftext|>


<commit_before>/*
 Copyright 2022 Google LLC

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

      https://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
*/

package system_base_test

import (
	"testing"
	"time"

	"github.com/openconfig/ondatra"
)

// TestHostname verifies that the hostname configuration paths can be read,
// updated, and deleted.
//
// config_path:/system/config/hostname
// telemetry_path:/system/state/hostname
func TestHostname(t *testing.T) {
	testCases := []struct {
		description string
		hostname    string
	}{
		{"15 Letters", "abcdefghijkmnop"},
		{"15 Numbers", "123456789012345"},
		{"Single Character", "x"},
		{"Dash and Underscore", "foo_bar-baz"},
		{"Periods", "test.name.example"},
		{"63 Characters", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"},
	}

	dut := ondatra.DUT(t, "dut")

	for _, testCase := range testCases {
		t.Run(testCase.description, func(t *testing.T) {
			config := dut.Config().System().Hostname()
			state := dut.Telemetry().System().Hostname()

			config.Replace(t, testCase.hostname)

			t.Run("Get Hostname Config", func(t *testing.T) {
				configGot := config.Get(t)
				if configGot != testCase.hostname {
					t.Errorf("Config hostname: got %s, want %s", configGot, testCase.hostname)
				}
			})

			t.Run("Get Hostname Telemetry", func(t *testing.T) {
				stateGot := state.Await(t, 5*time.Second, testCase.hostname)
				if stateGot.Val(t) != testCase.hostname {
					t.Errorf("Telemetry hostname: got %v, want %s", stateGot, testCase.hostname)
				}
			})

			t.Run("Delete Hostname", func(t *testing.T) {
				config.Delete(t)
				if qs := config.Lookup(t); qs.IsPresent() == true {
					t.Errorf("Delete hostname fail: got %v", qs)
				}
			})
		})
	}
}

// TestDomainName verifies that the domainname configuration paths can be read,
// updated, and deleted.
//
// config_path:/system/config/domain-name
// telemetry_path:/system/state/domain-name
func TestDomainName(t *testing.T) {
	testCases := []struct {
		description string
		domainname  string
	}{
		{"15 Letters", "abcdefghijkmnop"},
		{"15 Numbers", "123456789012345"},
		{"Single Character", "x"},
		{"Dash and Underscore", "foo_bar-baz"},
		{"Periods", "test.name.example"},
		{"63 Characters", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"},
	}

	dut := ondatra.DUT(t, "dut")

	for _, testCase := range testCases {
		t.Run(testCase.description, func(t *testing.T) {
			config := dut.Config().System().DomainName()
			state := dut.Telemetry().System().DomainName()

			config.Replace(t, testCase.domainname)

			t.Run("Get Domainname Config", func(t *testing.T) {
				configGot := config.Get(t)
				if configGot != testCase.domainname {
					t.Errorf("Config domainname: got %s, want %s", configGot, testCase.domainname)
				}
			})

			t.Run("Get Domainname Telemetry", func(t *testing.T) {
				stateGot := state.Await(t, 5*time.Second, testCase.domainname)
				if stateGot.Val(t) != testCase.domainname {
					t.Errorf("Telemetry domainname: got %v, want %s", stateGot, testCase.domainname)
				}
			})

			t.Run("Delete Domainname", func(t *testing.T) {
				config.Delete(t)
				if qs := config.Lookup(t); qs.IsPresent() == true {
					t.Errorf("Delete domainname fail: got %v", qs)
				}
			})
		})
	}
}
<commit_msg>Remove underscore from hostname test (#175)<commit_after>/*
 Copyright 2022 Google LLC

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

      https://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
*/

package system_base_test

import (
	"testing"
	"time"

	"github.com/openconfig/ondatra"
)

// TestHostname verifies that the hostname configuration paths can be read,
// updated, and deleted.
//
// config_path:/system/config/hostname
// telemetry_path:/system/state/hostname
func TestHostname(t *testing.T) {
	testCases := []struct {
		description string
		hostname    string
	}{
		{"15 Letters", "abcdefghijkmnop"},
		{"15 Numbers", "123456789012345"},
		{"Single Character", "x"},
		{"Periods", "test.name.example"},
		{"63 Characters", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"},
	}

	dut := ondatra.DUT(t, "dut")

	for _, testCase := range testCases {
		t.Run(testCase.description, func(t *testing.T) {
			config := dut.Config().System().Hostname()
			state := dut.Telemetry().System().Hostname()

			config.Replace(t, testCase.hostname)

			t.Run("Get Hostname Config", func(t *testing.T) {
				configGot := config.Get(t)
				if configGot != testCase.hostname {
					t.Errorf("Config hostname: got %s, want %s", configGot, testCase.hostname)
				}
			})

			t.Run("Get Hostname Telemetry", func(t *testing.T) {
				stateGot := state.Await(t, 5*time.Second, testCase.hostname)
				if stateGot.Val(t) != testCase.hostname {
					t.Errorf("Telemetry hostname: got %v, want %s", stateGot, testCase.hostname)
				}
			})

			t.Run("Delete Hostname", func(t *testing.T) {
				config.Delete(t)
				if qs := config.Lookup(t); qs.IsPresent() == true {
					t.Errorf("Delete hostname fail: got %v", qs)
				}
			})
		})
	}
}

// TestDomainName verifies that the domainname configuration paths can be read,
// updated, and deleted.
//
// config_path:/system/config/domain-name
// telemetry_path:/system/state/domain-name
func TestDomainName(t *testing.T) {
	testCases := []struct {
		description string
		domainname  string
	}{
		{"15 Letters", "abcdefghijkmnop"},
		{"15 Numbers", "123456789012345"},
		{"Single Character", "x"},
		{"Dash and Underscore", "foo_bar-baz"},
		{"Periods", "test.name.example"},
		{"63 Characters", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"},
	}

	dut := ondatra.DUT(t, "dut")

	for _, testCase := range testCases {
		t.Run(testCase.description, func(t *testing.T) {
			config := dut.Config().System().DomainName()
			state := dut.Telemetry().System().DomainName()

			config.Replace(t, testCase.domainname)

			t.Run("Get Domainname Config", func(t *testing.T) {
				configGot := config.Get(t)
				if configGot != testCase.domainname {
					t.Errorf("Config domainname: got %s, want %s", configGot, testCase.domainname)
				}
			})

			t.Run("Get Domainname Telemetry", func(t *testing.T) {
				stateGot := state.Await(t, 5*time.Second, testCase.domainname)
				if stateGot.Val(t) != testCase.domainname {
					t.Errorf("Telemetry domainname: got %v, want %s", stateGot, testCase.domainname)
				}
			})

			t.Run("Delete Domainname", func(t *testing.T) {
				config.Delete(t)
				if qs := config.Lookup(t); qs.IsPresent() == true {
					t.Errorf("Delete domainname fail: got %v", qs)
				}
			})
		})
	}
}
<|endoftext|>


<commit_before>package textdistance

import "testing"

type jaroDistanceTest struct {
	s1       string
	s2       string
	expected float64
}

func (jdt *jaroDistanceTest) equals(dis float64) bool {
	if jdt.expected-1e-3 < dis && jdt.expected < jdt.expected+1e-3 {
		return true
	}
	return false
}

var JaroDistanceTests = []jaroDistanceTest{
	{
		"MARTHA",
		"MARHTA",
		0.94444,
	},
	{
		"DWAYNE",
		"DUANE",
		0.822,
	},
	{
		"DIXON",
		"DICKSONX",
		0.767,
	},
}

func TestJaroDistance(t *testing.T) {
	for _, jt := range JaroDistanceTests {
		actual := JaroDistance(jt.s1, jt.s2)
		if !jt.equals(actual) {
			t.Errorf("Jaro distance of %s and %s: want %f got %f", jt.s1, jt.s2, jt.expected, actual)
		}
	}
}
<commit_msg>Added japanese test<commit_after>package textdistance

import "testing"

type jaroDistanceTest struct {
	s1       string
	s2       string
	expected float64
}

func (jdt *jaroDistanceTest) equals(dis float64) bool {
	if jdt.expected-1e-3 < dis && jdt.expected < jdt.expected+1e-3 {
		return true
	}
	return false
}

var JaroDistanceTests = []jaroDistanceTest{
	{
		"MARTHA",
		"MARHTA",
		0.94444,
	},
	{
		"DWAYNE",
		"DUANE",
		0.822,
	},
	{
		"DIXON",
		"DICKSONX",
		0.767,
	},
	{
		"あいうえおか",
		"あいうおえか",
		0.94444,
	},
}

func TestJaroDistance(t *testing.T) {
	for _, jt := range JaroDistanceTests {
		actual := JaroDistance(jt.s1, jt.s2)
		if !jt.equals(actual) {
			t.Errorf("Jaro distance of %s and %s: want %f got %f", jt.s1, jt.s2, jt.expected, actual)
		}
	}
}
<|endoftext|>


<commit_before>package blueprint

import (
	"fmt"
	"strings"
)

const EOF = -1

var (
	defaultEscaper = strings.NewReplacer(
		"\n", "$\n")
	inputEscaper = strings.NewReplacer(
		"\n", "$\n",
		" ", "$ ")
	outputEscaper = strings.NewReplacer(
		"\n", "$\n",
		" ", "$ ",
		":", "$:")
)

type ninjaString struct {
	strings   []string
	variables []Variable
}

type variableLookup interface {
	LookupVariable(name string) (Variable, error)
}

func simpleNinjaString(str string) *ninjaString {
	return &ninjaString{
		strings: []string{str},
	}
}

// parseNinjaString parses an unescaped ninja string (i.e. all $<something>
// occurrences are expected to be variables or $$) and returns a list of the
// variable names that the string references.
func parseNinjaString(scope variableLookup, str string) (*ninjaString, error) {
	type stateFunc func(int, rune) (stateFunc, error)
	var (
		stringState      stateFunc
		dollarStartState stateFunc
		dollarState      stateFunc
		bracketsState    stateFunc
	)

	var stringStart, varStart int
	var result ninjaString

	stringState = func(i int, r rune) (stateFunc, error) {
		switch {
		case r == '$':
			varStart = i + 1
			return dollarStartState, nil

		case r == EOF:
			result.strings = append(result.strings, str[stringStart:i])
			return nil, nil

		default:
			return stringState, nil
		}
	}

	dollarStartState = func(i int, r rune) (stateFunc, error) {
		switch {
		case r >= 'a' && r <= 'z', r >= 'A' && r <= 'Z',
			r >= '0' && r <= '9', r == '_', r == '-':
			// The beginning of a of the variable name.  Output the string and
			// keep going.
			result.strings = append(result.strings, str[stringStart:i-1])
			return dollarState, nil

		case r == '$':
			// Just a "$$".  Go back to stringState without changing
			// stringStart.
			return stringState, nil

		case r == '{':
			// This is a bracketted variable name (e.g. "${blah.blah}").  Output
			// the string and keep going.
			result.strings = append(result.strings, str[stringStart:i-1])
			varStart = i + 1
			return bracketsState, nil

		case r == EOF:
			return nil, fmt.Errorf("unexpected end of string after '$'")

		default:
			// This was some arbitrary character following a dollar sign,
			// which is not allowed.
			return nil, fmt.Errorf("invalid character after '$' at byte "+
				"offset %d", i)
		}
	}

	dollarState = func(i int, r rune) (stateFunc, error) {
		switch {
		case r >= 'a' && r <= 'z', r >= 'A' && r <= 'Z',
			r >= '0' && r <= '9', r == '_', r == '-':
			// A part of the variable name.  Keep going.
			return dollarState, nil

		case r == '$':
			// A dollar after the variable name (e.g. "$blah$").  Output the
			// variable we have and start a new one.
			v, err := scope.LookupVariable(str[varStart:i])
			if err != nil {
				return nil, err
			}

			result.variables = append(result.variables, v)
			varStart = i + 1
			return dollarState, nil

		case r == EOF:
			// This is the end of the variable name.
			v, err := scope.LookupVariable(str[varStart:i])
			if err != nil {
				return nil, err
			}

			result.variables = append(result.variables, v)

			// We always end with a string, even if it's an empty one.
			result.strings = append(result.strings, "")

			return nil, nil

		default:
			// We've just gone past the end of the variable name, so record what
			// we have.
			v, err := scope.LookupVariable(str[varStart:i])
			if err != nil {
				return nil, err
			}

			result.variables = append(result.variables, v)
			stringStart = i
			return stringState, nil
		}
	}

	bracketsState = func(i int, r rune) (stateFunc, error) {
		switch {
		case r >= 'a' && r <= 'z', r >= 'A' && r <= 'Z',
			r >= '0' && r <= '9', r == '_', r == '-', r == '.':
			// A part of the variable name.  Keep going.
			return bracketsState, nil

		case r == '}':
			if varStart == i {
				// The brackets were immediately closed.  That's no good.
				return nil, fmt.Errorf("empty variable name at byte offset %d",
					i)
			}

			// This is the end of the variable name.
			v, err := scope.LookupVariable(str[varStart:i])
			if err != nil {
				return nil, err
			}

			result.variables = append(result.variables, v)
			stringStart = i + 1
			return stringState, nil

		case r == EOF:
			return nil, fmt.Errorf("unexpected end of string in variable name")

		default:
			// This character isn't allowed in a variable name.
			return nil, fmt.Errorf("invalid character in variable name at "+
				"byte offset %d", i)
		}
	}

	state := stringState
	var err error
	for i, r := range str {
		state, err = state(i, r)
		if err != nil {
			return nil, err
		}
	}

	_, err = state(len(str), EOF)
	if err != nil {
		return nil, err
	}

	return &result, nil
}

func parseNinjaStrings(scope variableLookup, strs []string) ([]*ninjaString,
	error) {

	if len(strs) == 0 {
		return nil, nil
	}
	result := make([]*ninjaString, len(strs))
	for i, str := range strs {
		ninjaStr, err := parseNinjaString(scope, str)
		if err != nil {
			return nil, fmt.Errorf("error parsing element %d: %s", i, err)
		}
		result[i] = ninjaStr
	}
	return result, nil
}

func (n *ninjaString) Value(pkgNames map[*pkg]string) string {
	return n.ValueWithEscaper(pkgNames, defaultEscaper)
}

func (n *ninjaString) ValueWithEscaper(pkgNames map[*pkg]string,
	escaper *strings.Replacer) string {

	str := escaper.Replace(n.strings[0])
	for i, v := range n.variables {
		str += "${" + v.fullName(pkgNames) + "}"
		str += escaper.Replace(n.strings[i+1])
	}
	return str
}

func validateNinjaName(name string) error {
	for i, r := range name {
		valid := (r >= 'a' && r <= 'z') ||
			(r >= 'A' && r <= 'Z') ||
			(r >= '0' && r <= '9') ||
			(r == '_') ||
			(r == '-') ||
			(r == '.')
		if !valid {
			return fmt.Errorf("%q contains an invalid Ninja name character "+
				"at byte offset %d", name, i)
		}
	}
	return nil
}

var builtinRuleArgs = []string{"out", "in"}

func validateArgName(argName string) error {
	err := validateNinjaName(argName)
	if err != nil {
		return err
	}

	// We only allow globals within the rule's package to be used as rule
	// arguments.  A global in another package can always be mirrored into
	// the rule's package by defining a new variable, so this doesn't limit
	// what's possible.  This limitation prevents situations where a Build
	// invocation in another package must use the rule-defining package's
	// import name for a 3rd package in order to set the rule's arguments.
	if strings.ContainsRune(argName, '.') {
		return fmt.Errorf("%q contains a '.' character", argName)
	}

	for _, builtin := range builtinRuleArgs {
		if argName == builtin {
			return fmt.Errorf("%q conflicts with Ninja built-in", argName)
		}
	}

	return nil
}

func validateArgNames(argNames []string) error {
	for _, argName := range argNames {
		err := validateArgName(argName)
		if err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>Don't export the EOF constant.<commit_after>package blueprint

import (
	"fmt"
	"strings"
)

const eof = -1

var (
	defaultEscaper = strings.NewReplacer(
		"\n", "$\n")
	inputEscaper = strings.NewReplacer(
		"\n", "$\n",
		" ", "$ ")
	outputEscaper = strings.NewReplacer(
		"\n", "$\n",
		" ", "$ ",
		":", "$:")
)

type ninjaString struct {
	strings   []string
	variables []Variable
}

type variableLookup interface {
	LookupVariable(name string) (Variable, error)
}

func simpleNinjaString(str string) *ninjaString {
	return &ninjaString{
		strings: []string{str},
	}
}

// parseNinjaString parses an unescaped ninja string (i.e. all $<something>
// occurrences are expected to be variables or $$) and returns a list of the
// variable names that the string references.
func parseNinjaString(scope variableLookup, str string) (*ninjaString, error) {
	type stateFunc func(int, rune) (stateFunc, error)
	var (
		stringState      stateFunc
		dollarStartState stateFunc
		dollarState      stateFunc
		bracketsState    stateFunc
	)

	var stringStart, varStart int
	var result ninjaString

	stringState = func(i int, r rune) (stateFunc, error) {
		switch {
		case r == '$':
			varStart = i + 1
			return dollarStartState, nil

		case r == eof:
			result.strings = append(result.strings, str[stringStart:i])
			return nil, nil

		default:
			return stringState, nil
		}
	}

	dollarStartState = func(i int, r rune) (stateFunc, error) {
		switch {
		case r >= 'a' && r <= 'z', r >= 'A' && r <= 'Z',
			r >= '0' && r <= '9', r == '_', r == '-':
			// The beginning of a of the variable name.  Output the string and
			// keep going.
			result.strings = append(result.strings, str[stringStart:i-1])
			return dollarState, nil

		case r == '$':
			// Just a "$$".  Go back to stringState without changing
			// stringStart.
			return stringState, nil

		case r == '{':
			// This is a bracketted variable name (e.g. "${blah.blah}").  Output
			// the string and keep going.
			result.strings = append(result.strings, str[stringStart:i-1])
			varStart = i + 1
			return bracketsState, nil

		case r == eof:
			return nil, fmt.Errorf("unexpected end of string after '$'")

		default:
			// This was some arbitrary character following a dollar sign,
			// which is not allowed.
			return nil, fmt.Errorf("invalid character after '$' at byte "+
				"offset %d", i)
		}
	}

	dollarState = func(i int, r rune) (stateFunc, error) {
		switch {
		case r >= 'a' && r <= 'z', r >= 'A' && r <= 'Z',
			r >= '0' && r <= '9', r == '_', r == '-':
			// A part of the variable name.  Keep going.
			return dollarState, nil

		case r == '$':
			// A dollar after the variable name (e.g. "$blah$").  Output the
			// variable we have and start a new one.
			v, err := scope.LookupVariable(str[varStart:i])
			if err != nil {
				return nil, err
			}

			result.variables = append(result.variables, v)
			varStart = i + 1
			return dollarState, nil

		case r == eof:
			// This is the end of the variable name.
			v, err := scope.LookupVariable(str[varStart:i])
			if err != nil {
				return nil, err
			}

			result.variables = append(result.variables, v)

			// We always end with a string, even if it's an empty one.
			result.strings = append(result.strings, "")

			return nil, nil

		default:
			// We've just gone past the end of the variable name, so record what
			// we have.
			v, err := scope.LookupVariable(str[varStart:i])
			if err != nil {
				return nil, err
			}

			result.variables = append(result.variables, v)
			stringStart = i
			return stringState, nil
		}
	}

	bracketsState = func(i int, r rune) (stateFunc, error) {
		switch {
		case r >= 'a' && r <= 'z', r >= 'A' && r <= 'Z',
			r >= '0' && r <= '9', r == '_', r == '-', r == '.':
			// A part of the variable name.  Keep going.
			return bracketsState, nil

		case r == '}':
			if varStart == i {
				// The brackets were immediately closed.  That's no good.
				return nil, fmt.Errorf("empty variable name at byte offset %d",
					i)
			}

			// This is the end of the variable name.
			v, err := scope.LookupVariable(str[varStart:i])
			if err != nil {
				return nil, err
			}

			result.variables = append(result.variables, v)
			stringStart = i + 1
			return stringState, nil

		case r == eof:
			return nil, fmt.Errorf("unexpected end of string in variable name")

		default:
			// This character isn't allowed in a variable name.
			return nil, fmt.Errorf("invalid character in variable name at "+
				"byte offset %d", i)
		}
	}

	state := stringState
	var err error
	for i, r := range str {
		state, err = state(i, r)
		if err != nil {
			return nil, err
		}
	}

	_, err = state(len(str), eof)
	if err != nil {
		return nil, err
	}

	return &result, nil
}

func parseNinjaStrings(scope variableLookup, strs []string) ([]*ninjaString,
	error) {

	if len(strs) == 0 {
		return nil, nil
	}
	result := make([]*ninjaString, len(strs))
	for i, str := range strs {
		ninjaStr, err := parseNinjaString(scope, str)
		if err != nil {
			return nil, fmt.Errorf("error parsing element %d: %s", i, err)
		}
		result[i] = ninjaStr
	}
	return result, nil
}

func (n *ninjaString) Value(pkgNames map[*pkg]string) string {
	return n.ValueWithEscaper(pkgNames, defaultEscaper)
}

func (n *ninjaString) ValueWithEscaper(pkgNames map[*pkg]string,
	escaper *strings.Replacer) string {

	str := escaper.Replace(n.strings[0])
	for i, v := range n.variables {
		str += "${" + v.fullName(pkgNames) + "}"
		str += escaper.Replace(n.strings[i+1])
	}
	return str
}

func validateNinjaName(name string) error {
	for i, r := range name {
		valid := (r >= 'a' && r <= 'z') ||
			(r >= 'A' && r <= 'Z') ||
			(r >= '0' && r <= '9') ||
			(r == '_') ||
			(r == '-') ||
			(r == '.')
		if !valid {
			return fmt.Errorf("%q contains an invalid Ninja name character "+
				"at byte offset %d", name, i)
		}
	}
	return nil
}

var builtinRuleArgs = []string{"out", "in"}

func validateArgName(argName string) error {
	err := validateNinjaName(argName)
	if err != nil {
		return err
	}

	// We only allow globals within the rule's package to be used as rule
	// arguments.  A global in another package can always be mirrored into
	// the rule's package by defining a new variable, so this doesn't limit
	// what's possible.  This limitation prevents situations where a Build
	// invocation in another package must use the rule-defining package's
	// import name for a 3rd package in order to set the rule's arguments.
	if strings.ContainsRune(argName, '.') {
		return fmt.Errorf("%q contains a '.' character", argName)
	}

	for _, builtin := range builtinRuleArgs {
		if argName == builtin {
			return fmt.Errorf("%q conflicts with Ninja built-in", argName)
		}
	}

	return nil
}

func validateArgNames(argNames []string) error {
	for _, argName := range argNames {
		err := validateArgName(argName)
		if err != nil {
			return err
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package openconfig_telemetry

import (
	"io"
	"log"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/influxdata/telegraf"
	"github.com/influxdata/telegraf/plugins/inputs"
	"github.com/influxdata/telegraf/plugins/inputs/openconfig_telemetry/oc"
	"github.com/influxdata/telegraf/plugins/parsers"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

type OpenConfigTelemetry struct {
	Server          string
	Sensors         []string
	SampleFrequency uint32
	CertFile        string
	Debug           bool

	parser parsers.Parser
	sync.Mutex

	// keep the accumulator internally:
	acc            telegraf.Accumulator
	grpcClientConn *grpc.ClientConn
}

var sampleConfig = `
  server = "localhost:1883"

  ## Frequency to get data in milliseconds
  sampleFrequency = 1000

  ## Sensors to subscribe for
  ## A identifier for each sensor can be provided in path by separating with space
  ## Else sensor path will be used as identifier
  ## When identifier is used, we can provide a list of space separated sensors.
  ## A single subscription will be created with all these sensors and data will
  ## be saved to measurement with this identifier name
  sensors = [
   "/interfaces/",
   "collection /components/ /lldp",
  ]

  ## x509 Certificate to use with TLS connection. If it is not provided, an insecure
  ## channel will be opened with server
  certFile = "/path/to/x509_cert_file"

  ## To see data being received from gRPC server, set debug to true
  debug = true
`

func (m *OpenConfigTelemetry) SampleConfig() string {
	return sampleConfig
}

func (m *OpenConfigTelemetry) Description() string {
	return "Read OpenConfig Telemetry from listed sensors"
}

func (m *OpenConfigTelemetry) SetParser(parser parsers.Parser) {
	m.parser = parser
}

func (m *OpenConfigTelemetry) Gather(acc telegraf.Accumulator) error {
	return nil
}

func (m *OpenConfigTelemetry) Stop() {
	m.Lock()
	defer m.Unlock()
	m.grpcClientConn.Close()
}

// Takes in XML path with predicates and returns list of tags+values along with a final XML path without predicates
func spitTagsNPath(xmlpath string) (string, map[string]string) {
	re := regexp.MustCompile("\\/([^\\/]*)\\[([A-Za-z0-9\\-\\/]*)\\=([^\\[]*)\\]")
	subs := re.FindAllStringSubmatch(xmlpath, -1)
	tags := make(map[string]string)

	// Given XML path, this will spit out final path without predicates
	if len(subs) > 0 {
		for _, sub := range subs {
			tagKey := strings.Split(xmlpath, sub[0])[0]
			tagKey += "/" + strings.TrimSpace(sub[1]) + "/@" + strings.TrimSpace(sub[2])
			tagValue := strings.Replace(sub[3], "'", "", -1)

			tags[tagKey] = tagValue
			xmlpath = strings.Replace(xmlpath, sub[0], "/"+strings.TrimSpace(sub[1]), 1)
		}
	}

	return xmlpath, tags
}

func (m *OpenConfigTelemetry) Start(acc telegraf.Accumulator) error {
	log.Print("I! Started OpenConfig Telemetry plugin\n")

	m.Lock()
	defer m.Unlock()

	m.acc = acc

	// Extract device name / IP
	s := strings.Split(m.Server, ":")
	grpc_server, grpc_port := s[0], s[1]

	var err error

	// If a certificate is provided, open a secure channel. Else open insecure one
	if m.CertFile != "" {
		creds, err := credentials.NewClientTLSFromFile(m.CertFile, "")
		if err != nil {
			log.Fatalf("E! Failed to read certificate: %v", err)
		}
		m.grpcClientConn, err = grpc.Dial(m.Server, grpc.WithTransportCredentials(creds))
	} else {
		m.grpcClientConn, err = grpc.Dial(m.Server, grpc.WithInsecure())
	}
	if err != nil {
		log.Fatalf("E! Failed to connect: %v", err)
	}

	c := telemetry.NewOpenConfigTelemetryClient(m.grpcClientConn)
	if m.Debug {
		log.Printf("I! Opened a new gRPC session to %s on port %s", grpc_server, grpc_port)
	}

	for _, sensor := range m.Sensors {
		go func(sensor string, acc telegraf.Accumulator) {
			spathSplit := strings.SplitN(sensor, " ", -1)
			var sensorName string
			var pathlist []*telemetry.Path
			customReportingRate := false
			var reportingRate uint64

			reportingRate = uint64(m.SampleFrequency)

			if len(spathSplit) > 1 {
				for i, path := range spathSplit {
					// We allow custom reporting rate per sensor by specifying rate in milliseconds
					// followed by measurement name or sensor path
					if i == 0 {
						sensorName = path
						reportingRate, err = strconv.ParseUint(path, 10, 32)
						if err == nil {
							customReportingRate = true
						} else {
							reportingRate = uint64(m.SampleFrequency)
							// If the first word is not an integer and starts with /, we treat it as both sensor and a measurement name
							if strings.HasPrefix(sensorName, "/") {
								pathlist = append(pathlist, &telemetry.Path{Path: path, SampleFrequency: uint32(reportingRate)})
							}
						}
					} else if customReportingRate && i == 1 {
						// If our first word is reporting rate for this list of sensors, we treat second word as measurement name and if it has /
						// we treat it as another sensor to be monitored
						sensorName = path
						if strings.HasPrefix(path, "/") {
							pathlist = append(pathlist, &telemetry.Path{Path: path, SampleFrequency: uint32(reportingRate)})
						}
					} else {
						pathlist = append(pathlist, &telemetry.Path{Path: path, SampleFrequency: uint32(reportingRate)})
					}
				}
			} else {
				sensorName = sensor
				pathlist = append(pathlist, &telemetry.Path{Path: sensor, SampleFrequency: uint32(reportingRate)})
			}
			stream, err := c.TelemetrySubscribe(context.Background(),
				&telemetry.SubscriptionRequest{PathList: pathlist})
			if err != nil {
				log.Fatalf("E! Could not subscribe: %v", err)
			}
			for {
				r, err := stream.Recv()
				if err == io.EOF {
					break
				}
				if err != nil {
					log.Fatalf("E! Failed to read: %v", err)
				}

				// Print incoming data as info if debug is set
				if m.Debug {
					log.Printf("I! Received: %v", r)
				}

				// Create a point and add to batch
				tags := make(map[string]string)

				if err != nil {
					log.Fatalln("E! Error: %v", err)
				}

				// Use empty prefix. We will update this when we iterate over key-value pairs
				prefix := ""

				// Insert additional tags
				tags["device"] = grpc_server

				dgroups := []DataGroup{}

				for _, v := range r.Kv {
					kv := make(map[string]interface{})

					if v.Key == "__prefix__" {
						prefix = v.GetStrValue()
					}

					// Also, lets use prefix if there is one
					xmlpath, finaltags := spitTagsNPath(prefix + v.Key)
					finaltags["device"] = grpc_server

					switch v.Value.(type) {
					case *telemetry.KeyValue_StrValue:
						// If this is actually a integer value but wrongly encoded as string,
						// convert and use it as value to field
						if val, err := strconv.ParseInt(v.GetStrValue(), 10, 64); err == nil {
							kv[xmlpath] = val
						} else {
							tags[v.Key] = v.GetStrValue()
						}
						break
					case *telemetry.KeyValue_DoubleValue:
						kv[xmlpath] = v.GetDoubleValue()
						break
					case *telemetry.KeyValue_IntValue:
						kv[xmlpath] = v.GetIntValue()
						break
					case *telemetry.KeyValue_UintValue:
						kv[xmlpath] = v.GetUintValue()
						break
					case *telemetry.KeyValue_SintValue:
						kv[xmlpath] = v.GetSintValue()
						break
					case *telemetry.KeyValue_BoolValue:
						kv[xmlpath] = v.GetBoolValue()
						break
					case *telemetry.KeyValue_BytesValue:
						kv[xmlpath] = v.GetBytesValue()
						break
					}

					// Insert other tags from message
					finaltags["_system_id"] = r.SystemId
					finaltags["_path"] = r.Path

					// Insert derived key and value
					dgroups = CollectionByKeys(dgroups).Insert(finaltags, kv)

					// Insert data from message header
					dgroups = CollectionByKeys(dgroups).Insert(finaltags, map[string]interface{}{"_sequence": r.SequenceNumber})
					dgroups = CollectionByKeys(dgroups).Insert(finaltags, map[string]interface{}{"_timestamp": r.Timestamp})
					dgroups = CollectionByKeys(dgroups).Insert(finaltags, map[string]interface{}{"_component_id": r.ComponentId})
					dgroups = CollectionByKeys(dgroups).Insert(finaltags, map[string]interface{}{"_subcomponent_id": r.SubComponentId})
				}

				// Print final data collection
				if m.Debug {
					log.Printf("I! Available collection is: %v", dgroups)
				}

				tnow := time.Now()
				// Iterate through data groups and add them
				for _, group := range dgroups {
					if len(group.tags) == 0 {
						acc.AddFields(sensorName, group.data, tags, tnow)
					} else {
						acc.AddFields(sensorName, group.data, group.tags, tnow)
					}
				}
			}
		}(sensor, acc)

	}

	return nil
}

func init() {
	inputs.Add("openconfig_telemetry", func() telegraf.Input {
		return &OpenConfigTelemetry{}
	})
}
<commit_msg>Do not add prefix to db and add string values to tags<commit_after>package openconfig_telemetry

import (
	"io"
	"log"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/influxdata/telegraf"
	"github.com/influxdata/telegraf/plugins/inputs"
	"github.com/influxdata/telegraf/plugins/inputs/openconfig_telemetry/oc"
	"github.com/influxdata/telegraf/plugins/parsers"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

type OpenConfigTelemetry struct {
	Server          string
	Sensors         []string
	SampleFrequency uint32
	CertFile        string
	Debug           bool

	parser parsers.Parser
	sync.Mutex

	// keep the accumulator internally:
	acc            telegraf.Accumulator
	grpcClientConn *grpc.ClientConn
}

var sampleConfig = `
  server = "localhost:1883"

  ## Frequency to get data in milliseconds
  sampleFrequency = 1000

  ## Sensors to subscribe for
  ## A identifier for each sensor can be provided in path by separating with space
  ## Else sensor path will be used as identifier
  ## When identifier is used, we can provide a list of space separated sensors.
  ## A single subscription will be created with all these sensors and data will
  ## be saved to measurement with this identifier name
  sensors = [
   "/interfaces/",
   "collection /components/ /lldp",
  ]

  ## x509 Certificate to use with TLS connection. If it is not provided, an insecure
  ## channel will be opened with server
  certFile = "/path/to/x509_cert_file"

  ## To see data being received from gRPC server, set debug to true
  debug = true
`

func (m *OpenConfigTelemetry) SampleConfig() string {
	return sampleConfig
}

func (m *OpenConfigTelemetry) Description() string {
	return "Read OpenConfig Telemetry from listed sensors"
}

func (m *OpenConfigTelemetry) SetParser(parser parsers.Parser) {
	m.parser = parser
}

func (m *OpenConfigTelemetry) Gather(acc telegraf.Accumulator) error {
	return nil
}

func (m *OpenConfigTelemetry) Stop() {
	m.Lock()
	defer m.Unlock()
	m.grpcClientConn.Close()
}

// Takes in XML path with predicates and returns list of tags+values along with a final XML path without predicates
func spitTagsNPath(xmlpath string) (string, map[string]string) {
	re := regexp.MustCompile("\\/([^\\/]*)\\[([A-Za-z0-9\\-\\/]*)\\=([^\\[]*)\\]")
	subs := re.FindAllStringSubmatch(xmlpath, -1)
	tags := make(map[string]string)

	// Given XML path, this will spit out final path without predicates
	if len(subs) > 0 {
		for _, sub := range subs {
			tagKey := strings.Split(xmlpath, sub[0])[0]
			tagKey += "/" + strings.TrimSpace(sub[1]) + "/@" + strings.TrimSpace(sub[2])
			tagValue := strings.Replace(sub[3], "'", "", -1)

			tags[tagKey] = tagValue
			xmlpath = strings.Replace(xmlpath, sub[0], "/"+strings.TrimSpace(sub[1]), 1)
		}
	}

	return xmlpath, tags
}

func (m *OpenConfigTelemetry) Start(acc telegraf.Accumulator) error {
	log.Print("I! Started OpenConfig Telemetry plugin\n")

	m.Lock()
	defer m.Unlock()

	m.acc = acc

	// Extract device name / IP
	s := strings.Split(m.Server, ":")
	grpc_server, grpc_port := s[0], s[1]

	var err error

	// If a certificate is provided, open a secure channel. Else open insecure one
	if m.CertFile != "" {
		creds, err := credentials.NewClientTLSFromFile(m.CertFile, "")
		if err != nil {
			log.Fatalf("E! Failed to read certificate: %v", err)
		}
		m.grpcClientConn, err = grpc.Dial(m.Server, grpc.WithTransportCredentials(creds))
	} else {
		m.grpcClientConn, err = grpc.Dial(m.Server, grpc.WithInsecure())
	}
	if err != nil {
		log.Fatalf("E! Failed to connect: %v", err)
	}

	c := telemetry.NewOpenConfigTelemetryClient(m.grpcClientConn)
	if m.Debug {
		log.Printf("I! Opened a new gRPC session to %s on port %s", grpc_server, grpc_port)
	}

	for _, sensor := range m.Sensors {
		go func(sensor string, acc telegraf.Accumulator) {
			spathSplit := strings.SplitN(sensor, " ", -1)
			var sensorName string
			var pathlist []*telemetry.Path
			customReportingRate := false
			var reportingRate uint64

			reportingRate = uint64(m.SampleFrequency)

			if len(spathSplit) > 1 {
				for i, path := range spathSplit {
					// We allow custom reporting rate per sensor by specifying rate in milliseconds
					// followed by measurement name or sensor path
					if i == 0 {
						sensorName = path
						reportingRate, err = strconv.ParseUint(path, 10, 32)
						if err == nil {
							customReportingRate = true
						} else {
							reportingRate = uint64(m.SampleFrequency)
							// If the first word is not an integer and starts with /, we treat it as both sensor and a measurement name
							if strings.HasPrefix(sensorName, "/") {
								pathlist = append(pathlist, &telemetry.Path{Path: path, SampleFrequency: uint32(reportingRate)})
							}
						}
					} else if customReportingRate && i == 1 {
						// If our first word is reporting rate for this list of sensors, we treat second word as measurement name and if it has /
						// we treat it as another sensor to be monitored
						sensorName = path
						if strings.HasPrefix(path, "/") {
							pathlist = append(pathlist, &telemetry.Path{Path: path, SampleFrequency: uint32(reportingRate)})
						}
					} else {
						pathlist = append(pathlist, &telemetry.Path{Path: path, SampleFrequency: uint32(reportingRate)})
					}
				}
			} else {
				sensorName = sensor
				pathlist = append(pathlist, &telemetry.Path{Path: sensor, SampleFrequency: uint32(reportingRate)})
			}
			stream, err := c.TelemetrySubscribe(context.Background(),
				&telemetry.SubscriptionRequest{PathList: pathlist})
			if err != nil {
				log.Fatalf("E! Could not subscribe: %v", err)
			}
			for {
				r, err := stream.Recv()
				if err == io.EOF {
					break
				}
				if err != nil {
					log.Fatalf("E! Failed to read: %v", err)
				}

				// Print incoming data as info if debug is set
				if m.Debug {
					log.Printf("I! Received: %v", r)
				}

				// Create a point and add to batch
				tags := make(map[string]string)

				if err != nil {
					log.Fatalln("E! Error: %v", err)
				}

				// Use empty prefix. We will update this when we iterate over key-value pairs
				prefix := ""

				// Insert additional tags
				tags["device"] = grpc_server

				dgroups := []DataGroup{}

				for _, v := range r.Kv {
					kv := make(map[string]interface{})

					if v.Key == "__prefix__" {
						prefix = v.GetStrValue()
						continue
					}

					// Also, lets use prefix if there is one
					xmlpath, finaltags := spitTagsNPath(prefix + v.Key)
					finaltags["device"] = grpc_server

					switch v.Value.(type) {
					case *telemetry.KeyValue_StrValue:
						// If this is actually a integer value but wrongly encoded as string,
						// convert and use it as value to field
						if val, err := strconv.ParseInt(v.GetStrValue(), 10, 64); err == nil {
							kv[xmlpath] = val
						} else {
							finaltags[xmlpath] = v.GetStrValue()
						}
						break
					case *telemetry.KeyValue_DoubleValue:
						kv[xmlpath] = v.GetDoubleValue()
						break
					case *telemetry.KeyValue_IntValue:
						kv[xmlpath] = v.GetIntValue()
						break
					case *telemetry.KeyValue_UintValue:
						kv[xmlpath] = v.GetUintValue()
						break
					case *telemetry.KeyValue_SintValue:
						kv[xmlpath] = v.GetSintValue()
						break
					case *telemetry.KeyValue_BoolValue:
						kv[xmlpath] = v.GetBoolValue()
						break
					case *telemetry.KeyValue_BytesValue:
						kv[xmlpath] = v.GetBytesValue()
						break
					}

					// Insert other tags from message
					finaltags["_system_id"] = r.SystemId
					finaltags["_path"] = r.Path

					// Insert derived key and value
					dgroups = CollectionByKeys(dgroups).Insert(finaltags, kv)

					// Insert data from message header
					dgroups = CollectionByKeys(dgroups).Insert(finaltags, map[string]interface{}{"_sequence": r.SequenceNumber})
					dgroups = CollectionByKeys(dgroups).Insert(finaltags, map[string]interface{}{"_timestamp": r.Timestamp})
					dgroups = CollectionByKeys(dgroups).Insert(finaltags, map[string]interface{}{"_component_id": r.ComponentId})
					dgroups = CollectionByKeys(dgroups).Insert(finaltags, map[string]interface{}{"_subcomponent_id": r.SubComponentId})
				}

				// Print final data collection
				if m.Debug {
					log.Printf("I! Available collection is: %v", dgroups)
				}

				tnow := time.Now()
				// Iterate through data groups and add them
				for _, group := range dgroups {
					if len(group.tags) == 0 {
						acc.AddFields(sensorName, group.data, tags, tnow)
					} else {
						acc.AddFields(sensorName, group.data, group.tags, tnow)
					}
				}
			}
		}(sensor, acc)

	}

	return nil
}

func init() {
	inputs.Add("openconfig_telemetry", func() telegraf.Input {
		return &OpenConfigTelemetry{}
	})
}
<|endoftext|>


<commit_before>// Copyright 2014 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.

package exec

import (
	"bytes"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"syscall"

	"github.com/juju/errors"

	"github.com/juju/loggo"
)

var logger = loggo.GetLogger("juju.util.exec")

// Parameters for RunCommands.  Commands contains one or more commands to be
// executed using '/bin/bash -s'.  If WorkingDir is set, this is passed
// through to bash.  Similarly if the Environment is specified, this is used
// for executing the command.
type RunParams struct {
	Commands    string
	WorkingDir  string
	Environment []string

	stdout *bytes.Buffer
	stderr *bytes.Buffer
	ps     *exec.Cmd
}

// ExecResponse contains the return code and output generated by executing a
// command.
type ExecResponse struct {
	Code   int
	Stdout []byte
	Stderr []byte
}

// mergeEnvironment takes in a string array representing the desired environment
// and merges it with the current environment. On Windows, clearing the environment,
// or having missing environment variables, may lead to standard go packages not working
// (os.TempDir relies on $env:TEMP), and powershell erroring out
// Currently this function is only used for windows
func mergeEnvironment(env []string) []string {
	if env == nil {
		return nil
	}
	m := make(map[string]string)
	var tmpEnv []string
	for _, val := range os.Environ() {
		varSplit := strings.SplitN(val, "=", 2)
		m[varSplit[0]] = varSplit[1]
	}

	for _, val := range env {
		varSplit := strings.SplitN(val, "=", 2)
		m[varSplit[0]] = varSplit[1]
	}

	for key, val := range m {
		tmpEnv = append(tmpEnv, key+"="+val)
	}

	return tmpEnv
}

// shellAndArgs is a helper function that returns an OS specific
// shell and arguments for that particular shell
func shellAndArgs() (string, []string) {
	var com []string
	switch runtime.GOOS {
	case "windows":
		com = []string{
			"powershell.exe",
			"-noprofile",
			"-noninteractive",
			"-command",
			"try{$input|iex; exit $LastExitCode}catch{Write-Error -Message $Error[0]; exit 1}",
		}
	default:
		com = []string{
			"/bin/bash",
			"-s",
		}
	}
	return com[0], com[1:]
}

func (r *RunParams) Run() error {
	if runtime.GOOS == "windows" {
		r.Environment = mergeEnvironment(r.Environment)
	}
	shell, args := shellAndArgs()
	r.ps = exec.Command(shell, args...)
	if r.Environment != nil {
		r.ps.Env = r.Environment
	}
	if r.WorkingDir != "" {
		r.ps.Dir = r.WorkingDir
	}
	r.ps.Stdin = bytes.NewBufferString(r.Commands)

	r.stdout = &bytes.Buffer{}
	r.stderr = &bytes.Buffer{}

	r.ps.Stdout = r.stdout
	r.ps.Stderr = r.stderr

	err := r.ps.Start()
	if err != nil {
		return err
	}
	return nil
}

func (r *RunParams) Process() *os.Process {
	if r.ps != nil && r.ps.Process != nil {
		return r.ps.Process
	}
	return nil
}

func (r *RunParams) Wait() (*ExecResponse, error) {
	var err error
	if r.ps == nil {
		return nil, errors.New("No process has been started yet")
	}
	err = r.ps.Wait()

	result := &ExecResponse{
		Stdout: r.stdout.Bytes(),
		Stderr: r.stderr.Bytes(),
	}

	if ee, ok := err.(*exec.ExitError); ok && err != nil {
		status := ee.ProcessState.Sys().(syscall.WaitStatus)
		if status.Exited() {
			// A non-zero return code isn't considered an error here.
			result.Code = status.ExitStatus()
			err = nil
		}
		logger.Infof("run result: %v", ee)
	}
	return result, err
}

// RunCommands executes the Commands specified in the RunParams using
// powershell on windows, and '/bin/bash -s' on everything else,
// passing the commands through as stdin, and collecting
// stdout and stderr.  If a non-zero return code is returned, this is
// collected as the code for the response and this does not classify as an
// error.
func RunCommands(run RunParams) (*ExecResponse, error) {
	err := run.Run()
	if err != nil {
		return nil, err
	}
	return run.Wait()
}
<commit_msg>Added comments<commit_after>// Copyright 2014 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.

package exec

import (
	"bytes"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"syscall"

	"github.com/juju/errors"

	"github.com/juju/loggo"
)

var logger = loggo.GetLogger("juju.util.exec")

// Parameters for RunCommands.  Commands contains one or more commands to be
// executed using '/bin/bash -s'.  If WorkingDir is set, this is passed
// through to bash.  Similarly if the Environment is specified, this is used
// for executing the command.
type RunParams struct {
	Commands    string
	WorkingDir  string
	Environment []string

	stdout *bytes.Buffer
	stderr *bytes.Buffer
	ps     *exec.Cmd
}

// ExecResponse contains the return code and output generated by executing a
// command.
type ExecResponse struct {
	Code   int
	Stdout []byte
	Stderr []byte
}

// mergeEnvironment takes in a string array representing the desired environment
// and merges it with the current environment. On Windows, clearing the environment,
// or having missing environment variables, may lead to standard go packages not working
// (os.TempDir relies on $env:TEMP), and powershell erroring out
// Currently this function is only used for windows
func mergeEnvironment(env []string) []string {
	if env == nil {
		return nil
	}
	m := make(map[string]string)
	var tmpEnv []string
	for _, val := range os.Environ() {
		varSplit := strings.SplitN(val, "=", 2)
		m[varSplit[0]] = varSplit[1]
	}

	for _, val := range env {
		varSplit := strings.SplitN(val, "=", 2)
		m[varSplit[0]] = varSplit[1]
	}

	for key, val := range m {
		tmpEnv = append(tmpEnv, key+"="+val)
	}

	return tmpEnv
}

// shellAndArgs is a helper function that returns an OS specific
// shell and arguments for that particular shell
func shellAndArgs() (string, []string) {
	var com []string
	switch runtime.GOOS {
	case "windows":
		com = []string{
			"powershell.exe",
			"-noprofile",
			"-noninteractive",
			"-command",
			"try{$input|iex; exit $LastExitCode}catch{Write-Error -Message $Error[0]; exit 1}",
		}
	default:
		com = []string{
			"/bin/bash",
			"-s",
		}
	}
	return com[0], com[1:]
}

// Run sets up the command environment (environment variables, working dir)
// and starts the process. The commands are passed into '/bin/bash -s' through stdin
// on Linux machines and to powershell on Windows machines.
func (r *RunParams) Run() error {
	if runtime.GOOS == "windows" {
		r.Environment = mergeEnvironment(r.Environment)
	}
	shell, args := shellAndArgs()
	r.ps = exec.Command(shell, args...)
	if r.Environment != nil {
		r.ps.Env = r.Environment
	}
	if r.WorkingDir != "" {
		r.ps.Dir = r.WorkingDir
	}
	r.ps.Stdin = bytes.NewBufferString(r.Commands)

	r.stdout = &bytes.Buffer{}
	r.stderr = &bytes.Buffer{}

	r.ps.Stdout = r.stdout
	r.ps.Stderr = r.stderr

	err := r.ps.Start()
	if err != nil {
		return err
	}
	return nil
}

// Process returns the *os.Process instance of the current running process
// This will allow us to kill the process if needed, or get more information
// on the process
func (r *RunParams) Process() *os.Process {
	if r.ps != nil && r.ps.Process != nil {
		return r.ps.Process
	}
	return nil
}

// Wait blocks until the process exits, and returns an ExecResponse type
// containing stdout, stderr and the return code of the process. If a non-zero
// return code is returned, this is collected as the code for the response and
// this does not classify as an error.
func (r *RunParams) Wait() (*ExecResponse, error) {
	var err error
	if r.ps == nil {
		return nil, errors.New("No process has been started yet")
	}
	err = r.ps.Wait()

	result := &ExecResponse{
		Stdout: r.stdout.Bytes(),
		Stderr: r.stderr.Bytes(),
	}

	if ee, ok := err.(*exec.ExitError); ok && err != nil {
		status := ee.ProcessState.Sys().(syscall.WaitStatus)
		if status.Exited() {
			// A non-zero return code isn't considered an error here.
			result.Code = status.ExitStatus()
			err = nil
		}
		logger.Infof("run result: %v", ee)
	}
	return result, err
}

// RunCommands executes the Commands specified in the RunParams using
// powershell on windows, and '/bin/bash -s' on everything else,
// passing the commands through as stdin, and collecting
// stdout and stderr.  If a non-zero return code is returned, this is
// collected as the code for the response and this does not classify as an
// error.
func RunCommands(run RunParams) (*ExecResponse, error) {
	err := run.Run()
	if err != nil {
		return nil, err
	}
	return run.Wait()
}
<|endoftext|>


<commit_before>// Copyright 2016 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package app

import (
	"io"

	"github.com/issue9/term/colors"
	"golang.org/x/text/message"
)

var localePrinter *message.Printer

// PrintPrefix 向终端输出不同颜色的提示信息，颜色仅对 prefix 参数启作用。
// 具体内容可通过返回的 *message.Printer 来输出。
func PrintPrefix(out int, color colors.Color, prefix string) *message.Printer {
	_, err := colors.Print(out, color, colors.Default, prefix)
	if err != nil {
		panic(err)
	}

	return localePrinter
}

// Warn 输出警告性信息的前缀内容，具体内容可通过返回的 *message.Printer 来输出。
func Warn() *message.Printer {
	return PrintPrefix(colors.Stderr, colors.Cyan, "[WARN] ")
}

// Error 输出错误信息的前缀内容，具体内容可通过返回的 *message.Printer 来输出。
func Error(v ...interface{}) *message.Printer {
	return PrintPrefix(colors.Stderr, colors.Red, "[ERROR] ")
}

// Info 输出提示信息信息的前缀内容，具体内容可通过返回的 *message.Printer 来输出。
func Info(v ...interface{}) *message.Printer {
	return PrintPrefix(colors.Stdout, colors.Green, "[INFO] ")
}

func Print(v ...interface{}) (int, error) {
	return localePrinter.Print(v...)
}

func Println(v ...interface{}) (int, error) {
	return localePrinter.Println(v...)
}

func Printf(key string, v ...interface{}) (int, error) {
	return localePrinter.Printf(key, v...)
}

func Sprint(v ...interface{}) string {
	return localePrinter.Sprint(v...)
}

func Sprintln(v ...interface{}) string {
	return localePrinter.Sprintln(v...)
}

func Sprintf(key message.Reference, v ...interface{}) string {
	return localePrinter.Sprintf(key, v...)
}

func Fprint(w io.Writer, v ...interface{}) (int, error) {
	return localePrinter.Fprint(w, v...)
}

func Fprintln(w io.Writer, v ...interface{}) (int, error) {
	return localePrinter.Fprintln(w, v...)
}

func Fprintf(w io.Writer, key message.Reference, v ...interface{}) (int, error) {
	return localePrinter.Fprintf(w, key, v...)
}
<commit_msg>修正 app.Error可以传递参数的错误<commit_after>// Copyright 2016 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package app

import (
	"io"

	"github.com/issue9/term/colors"
	"golang.org/x/text/message"
)

var localePrinter *message.Printer

// PrintPrefix 向终端输出不同颜色的提示信息，颜色仅对 prefix 参数启作用。
// 具体内容可通过返回的 *message.Printer 来输出。
func PrintPrefix(out int, color colors.Color, prefix string) *message.Printer {
	_, err := colors.Print(out, color, colors.Default, prefix)
	if err != nil {
		panic(err)
	}

	return localePrinter
}

// Warn 输出警告性信息的前缀内容，具体内容可通过返回的 *message.Printer 来输出。
func Warn() *message.Printer {
	return PrintPrefix(colors.Stderr, colors.Cyan, "[WARN] ")
}

// Error 输出错误信息的前缀内容，具体内容可通过返回的 *message.Printer 来输出。
func Error() *message.Printer {
	return PrintPrefix(colors.Stderr, colors.Red, "[ERROR] ")
}

// Info 输出提示信息信息的前缀内容，具体内容可通过返回的 *message.Printer 来输出。
func Info() *message.Printer {
	return PrintPrefix(colors.Stdout, colors.Green, "[INFO] ")
}

// Print 类型 fmt.Print，与特定的语言绑定。
func Print(v ...interface{}) (int, error) {
	return localePrinter.Print(v...)
}

// Println 类型 fmt.Println，与特定的语言绑定。
func Println(v ...interface{}) (int, error) {
	return localePrinter.Println(v...)
}

// Printf 类型 fmt.Printf，与特定的语言绑定。
func Printf(key string, v ...interface{}) (int, error) {
	return localePrinter.Printf(key, v...)
}

// Sprint 类型 fmt.Sprint，与特定的语言绑定。
func Sprint(v ...interface{}) string {
	return localePrinter.Sprint(v...)
}

// Sprintln 类型 fmt.Sprintln，与特定的语言绑定。
func Sprintln(v ...interface{}) string {
	return localePrinter.Sprintln(v...)
}

// Sprintf 类型 fmt.Sprintf，与特定的语言绑定。
func Sprintf(key message.Reference, v ...interface{}) string {
	return localePrinter.Sprintf(key, v...)
}

// Fprint 类型 fmt.Fprint，与特定的语言绑定。
func Fprint(w io.Writer, v ...interface{}) (int, error) {
	return localePrinter.Fprint(w, v...)
}

// Fprintln 类型 fmt.Fprintln，与特定的语言绑定。
func Fprintln(w io.Writer, v ...interface{}) (int, error) {
	return localePrinter.Fprintln(w, v...)
}

// Fprintf 类型 fmt.Fprintf，与特定的语言绑定。
func Fprintf(w io.Writer, key message.Reference, v ...interface{}) (int, error) {
	return localePrinter.Fprintf(w, key, v...)
}
<|endoftext|>


<commit_before>// Copyright 2013 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package app

import (
	"fmt"
	"github.com/globocom/tsuru/log"
	"github.com/globocom/tsuru/provision"
	"github.com/globocom/tsuru/queue"
	"io/ioutil"
)

const (
	regenerateApprc = "regenerate-apprc"
	startApp        = "start-app"
)

func ensureAppIsStarted(msg *queue.Message) (App, error) {
	a := App{Name: msg.Args[0]}
	err := a.Get()
	if err != nil {
		return a, fmt.Errorf("Error handling %q: app %q does not exist.", msg.Action, a.Name)
	}
	units := getUnits(&a, msg.Args[1:])
	if a.State != "started" || !units.Started() {
		format := "Error handling %q for the app %q:"
		switch a.State {
		case "error":
			format += " the app is in %q state."
			msg.Delete()
		case "down":
			format += " the app is %s."
			msg.Delete()
		default:
			format += ` The status of the app and all units should be "started" (the app is %q).`
			msg.Release(0)
		}
		return a, fmt.Errorf(format, msg.Action, a.Name, a.State)
	}
	return a, nil
}

func handle(msg *queue.Message) {
	switch msg.Action {
	case regenerateApprc:
		if len(msg.Args) < 1 {
			log.Printf("Error handling %q: this action requires at least 1 argument.", msg.Action)
			return
		}
		app, err := ensureAppIsStarted(msg)
		if err != nil {
			log.Print(err)
			return
		}
		msg.Delete()
		app.SerializeEnvVars()
	case startApp:
		if len(msg.Args) < 1 {
			log.Printf("Error handling %q: this action requires at least 1 argument.", msg.Action)
		}
		app, err := ensureAppIsStarted(msg)
		if err != nil {
			log.Print(err)
			return
		}
		err = app.Restart(ioutil.Discard)
		if err != nil {
			log.Printf("Error handling %q. App failed to start:\n%s.", msg.Action, err)
			return
		}
		msg.Delete()
	default:
		log.Printf("Error handling %q: invalid action.", msg.Action)
		msg.Release(0)
	}
}

type unitList []Unit

func (l unitList) Started() bool {
	for _, unit := range l {
		if unit.State != string(provision.StatusStarted) {
			return false
		}
	}
	return true
}

func getUnits(a *App, names []string) unitList {
	var units []Unit
	if len(names) > 0 {
		units = make([]Unit, len(names))
		i := 0
		for _, unitName := range names {
			for _, appUnit := range a.Units {
				if appUnit.Name == unitName {
					units[i] = appUnit
					i++
					break
				}
			}
		}
	}
	return unitList(units)
}

var handler = &queue.Handler{F: handle}

func enqueue(msgs ...queue.Message) {
	for _, msg := range msgs {
		copy := msg
		copy.Put(0)
	}
	handler.Start()
}
<commit_msg>app: use delayed release in the queue when app is not ready<commit_after>// Copyright 2013 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package app

import (
	"fmt"
	"github.com/globocom/tsuru/log"
	"github.com/globocom/tsuru/provision"
	"github.com/globocom/tsuru/queue"
	"io/ioutil"
)

const (
	regenerateApprc = "regenerate-apprc"
	startApp        = "start-app"
)

func ensureAppIsStarted(msg *queue.Message) (App, error) {
	a := App{Name: msg.Args[0]}
	err := a.Get()
	if err != nil {
		return a, fmt.Errorf("Error handling %q: app %q does not exist.", msg.Action, a.Name)
	}
	units := getUnits(&a, msg.Args[1:])
	if a.State != "started" || !units.Started() {
		format := "Error handling %q for the app %q:"
		switch a.State {
		case "error":
			format += " the app is in %q state."
			msg.Delete()
		case "down":
			format += " the app is %s."
			msg.Delete()
		default:
			format += ` The status of the app and all units should be "started" (the app is %q).`
			msg.Release(5e9)
		}
		return a, fmt.Errorf(format, msg.Action, a.Name, a.State)
	}
	return a, nil
}

func handle(msg *queue.Message) {
	switch msg.Action {
	case regenerateApprc:
		if len(msg.Args) < 1 {
			log.Printf("Error handling %q: this action requires at least 1 argument.", msg.Action)
			return
		}
		app, err := ensureAppIsStarted(msg)
		if err != nil {
			log.Print(err)
			return
		}
		msg.Delete()
		app.SerializeEnvVars()
	case startApp:
		if len(msg.Args) < 1 {
			log.Printf("Error handling %q: this action requires at least 1 argument.", msg.Action)
		}
		app, err := ensureAppIsStarted(msg)
		if err != nil {
			log.Print(err)
			return
		}
		err = app.Restart(ioutil.Discard)
		if err != nil {
			log.Printf("Error handling %q. App failed to start:\n%s.", msg.Action, err)
			return
		}
		msg.Delete()
	default:
		log.Printf("Error handling %q: invalid action.", msg.Action)
		msg.Release(0)
	}
}

type unitList []Unit

func (l unitList) Started() bool {
	for _, unit := range l {
		if unit.State != string(provision.StatusStarted) {
			return false
		}
	}
	return true
}

func getUnits(a *App, names []string) unitList {
	var units []Unit
	if len(names) > 0 {
		units = make([]Unit, len(names))
		i := 0
		for _, unitName := range names {
			for _, appUnit := range a.Units {
				if appUnit.Name == unitName {
					units[i] = appUnit
					i++
					break
				}
			}
		}
	}
	return unitList(units)
}

var handler = &queue.Handler{F: handle}

func enqueue(msgs ...queue.Message) {
	for _, msg := range msgs {
		copy := msg
		copy.Put(0)
	}
	handler.Start()
}
<|endoftext|>


<commit_before>/**
This package is used to provide an implementation of the
Chord network protocol. It can be used to provide a DHT
which is tolerant to churn in the member ndoes.
*/
package chord

import (
	"crypto/sha1"
	"hash"
	"sort"
	"time"
)

// Implements the methods needed for a Chord ring
type Transport interface {
	// Ping a Vnode, check for liveness
	Ping(*Vnode) (bool, error)

	// Request a nodes predecessor
	GetPredecessor(*Vnode) (*Vnode, error)

	// Notify our successor of ourselves
	Notify(target, self *Vnode) ([]*Vnode, error)
}

// Delegate to notify on ring events
type Delegate interface {
	NewSuccessor(local *Vnode, remoteNew *Vnode, remotePrev *Vnode)
	NewPredecessor(local *Vnode, remoteNew *Vnode, remotePrev *Vnode)
	PredecessorLeaving(local *Vnode, remote *Vnode)
	SuccessorLeaving(local *Vnode, remote *Vnode)
}

// Configuration for Chord nodes
type Config struct {
	Hostname      string           // Local host name
	NumVnodes     int              // Number of vnodes per physical node
	HashFunc      func() hash.Hash // Hash function to use
	HashBits      int              // Bit size of the hash function
	StabilizeMin  time.Duration    // Minimum stabilization time
	StabilizeMax  time.Duration    // Maximum stabilization time
	NumSuccessors int              // Number of successors to maintain
	Delegate      Delegate         // Invoked to handle ring events
}

// Represents an Vnode, local or remote
type Vnode struct {
	Id   []byte // Virtual ID
	Host string // Host identifier
}

// Represents a local Vnode
type localVnode struct {
	Vnode
	ring        *Ring
	successors  []*Vnode
	finger      []*Vnode
	predecessor *Vnode
	stabilized  time.Time
	timer       *time.Timer
}

// Stores the state required for a Chord ring
type Ring struct {
	config    *Config
	transport Transport
	vnodes    []localVnode
	shutdown  bool
}

// Creates an iterator over Vnodes
type VnodeIterator interface {
	Next() (*Vnode, error) // Returns the next vnode
	Done() bool            // Returns true if all vnodes exhausted
}

// Returns the default Ring configuration
func DefaultConfig(hostname string) *Config {
	return &Config{
		hostname,
		8,        // 8 vnodes
		sha1.New, // SHA1
		160,      // 160bit hash function
		time.Duration(15 * time.Second),
		time.Duration(45 * time.Second),
		3,   // 3 successors
		nil, // No delegate
	}
}

// Creates a new Chord ring given the config and transport
func Create(conf *Config, trans Transport) (*Ring, error) {
	vnodes := make([]localVnode, conf.NumVnodes)
	ring := &Ring{conf, trans, vnodes, false}
	for i := 0; i < conf.NumVnodes; i++ {
		vn := &vnodes[i]
		vn.ring = ring
		if err := vn.init(i); err != nil {
			return nil, err
		}
	}

	// Sort the vnodes
	sort.Sort(ring)

	// Setup each vnode successors
	for idx, vn := range vnodes {
		if idx == len(vnodes) {
			vn.successors[0] = &vnodes[0].Vnode
		} else {
			vn.successors[0] = &vnodes[idx+1].Vnode
		}
	}

	// Schedule each Vnode
	for _, vn := range vnodes {
		vn.schedule()
	}
	return ring, nil
}

// Joins an existing Chord ring
func Join(conf *Config, trans Transport, existing string) (*Ring, error) {
	return nil, nil
}

// Leaves a given Chord ring
func (*Ring) Leave() error {
	return nil
}

// Shutdown shuts down the local processes in a given Chord ring
func (r *Ring) Shutdown() error {
	r.shutdown = true
	return nil
}

// Does a key lookup
func (*Ring) Lookup(key []byte) (VnodeIterator, error) {
	return nil, nil
}

// Does a key lookup, returning only the primary Vnode
func (r *Ring) LookupPrimary(key []byte) (*Vnode, error) {
	iter, err := r.Lookup(key)
	if err != nil {
		return nil, err
	}
	prim, err := iter.Next()
	if err != nil {
		return nil, err
	}
	return prim, nil
}
<commit_msg>Track the last finger index, increase to 8 successors by default<commit_after>/**
This package is used to provide an implementation of the
Chord network protocol. It can be used to provide a DHT
which is tolerant to churn in the member ndoes.
*/
package chord

import (
	"crypto/sha1"
	"hash"
	"sort"
	"time"
)

// Implements the methods needed for a Chord ring
type Transport interface {
	// Ping a Vnode, check for liveness
	Ping(*Vnode) (bool, error)

	// Request a nodes predecessor
	GetPredecessor(*Vnode) (*Vnode, error)

	// Notify our successor of ourselves
	Notify(target, self *Vnode) ([]*Vnode, error)
}

// Delegate to notify on ring events
type Delegate interface {
	NewSuccessor(local *Vnode, remoteNew *Vnode, remotePrev *Vnode)
	NewPredecessor(local *Vnode, remoteNew *Vnode, remotePrev *Vnode)
	PredecessorLeaving(local *Vnode, remote *Vnode)
	SuccessorLeaving(local *Vnode, remote *Vnode)
}

// Configuration for Chord nodes
type Config struct {
	Hostname      string           // Local host name
	NumVnodes     int              // Number of vnodes per physical node
	HashFunc      func() hash.Hash // Hash function to use
	HashBits      int              // Bit size of the hash function
	StabilizeMin  time.Duration    // Minimum stabilization time
	StabilizeMax  time.Duration    // Maximum stabilization time
	NumSuccessors int              // Number of successors to maintain
	Delegate      Delegate         // Invoked to handle ring events
}

// Represents an Vnode, local or remote
type Vnode struct {
	Id   []byte // Virtual ID
	Host string // Host identifier
}

// Represents a local Vnode
type localVnode struct {
	Vnode
	ring        *Ring
	successors  []*Vnode
	finger      []*Vnode
	last_finger int
	predecessor *Vnode
	stabilized  time.Time
	timer       *time.Timer
}

// Stores the state required for a Chord ring
type Ring struct {
	config    *Config
	transport Transport
	vnodes    []localVnode
	shutdown  bool
}

// Creates an iterator over Vnodes
type VnodeIterator interface {
	Next() (*Vnode, error) // Returns the next vnode
	Done() bool            // Returns true if all vnodes exhausted
}

// Returns the default Ring configuration
func DefaultConfig(hostname string) *Config {
	return &Config{
		hostname,
		8,        // 8 vnodes
		sha1.New, // SHA1
		160,      // 160bit hash function
		time.Duration(15 * time.Second),
		time.Duration(45 * time.Second),
		8,   // 8 successors
		nil, // No delegate
	}
}

// Creates a new Chord ring given the config and transport
func Create(conf *Config, trans Transport) (*Ring, error) {
	vnodes := make([]localVnode, conf.NumVnodes)
	ring := &Ring{conf, trans, vnodes, false}
	for i := 0; i < conf.NumVnodes; i++ {
		vn := &vnodes[i]
		vn.ring = ring
		if err := vn.init(i); err != nil {
			return nil, err
		}
	}

	// Sort the vnodes
	sort.Sort(ring)

	// Setup each vnode successors
	for idx, vn := range vnodes {
		if idx == len(vnodes) {
			vn.successors[0] = &vnodes[0].Vnode
		} else {
			vn.successors[0] = &vnodes[idx+1].Vnode
		}
	}

	// Schedule each Vnode
	for _, vn := range vnodes {
		vn.schedule()
	}
	return ring, nil
}

// Joins an existing Chord ring
func Join(conf *Config, trans Transport, existing string) (*Ring, error) {
	return nil, nil
}

// Leaves a given Chord ring
func (*Ring) Leave() error {
	return nil
}

// Shutdown shuts down the local processes in a given Chord ring
func (r *Ring) Shutdown() error {
	r.shutdown = true
	return nil
}

// Does a key lookup
func (*Ring) Lookup(key []byte) (VnodeIterator, error) {
	return nil, nil
}

// Does a key lookup, returning only the primary Vnode
func (r *Ring) LookupPrimary(key []byte) (*Vnode, error) {
	iter, err := r.Lookup(key)
	if err != nil {
		return nil, err
	}
	prim, err := iter.Next()
	if err != nil {
		return nil, err
	}
	return prim, nil
}
<|endoftext|>