updated to use a common logging library and enable log level configuration
Change-Id: Ib473615f25318c3b40cd6cf3bd49248e8a3d4fb1
diff --git a/automation/Dockerfile b/automation/Dockerfile
index 22ce5f7..c864076 100644
--- a/automation/Dockerfile
+++ b/automation/Dockerfile
@@ -1,4 +1,4 @@
-FROM golang:alpine
+FROM golang:1.6-alpine
RUN apk --update add openssh-client git
@@ -7,13 +7,9 @@
ADD . /go/src/gerrit.opencord.org/maas/cord-maas-automation
WORKDIR /go/src/gerrit.opencord.org/maas/cord-maas-automation
-RUN /go/bin/godep restore
+RUN /go/bin/godep restore || true
WORKDIR /go
-
RUN go install gerrit.opencord.org/maas/cord-maas-automation
-RUN mkdir -p /root/.ssh
-COPY ssh-config /root/.ssh/config
-
ENTRYPOINT ["/go/bin/cord-maas-automation"]
diff --git a/automation/Godeps/Godeps.json b/automation/Godeps/Godeps.json
index 88da63d..c601bc6 100644
--- a/automation/Godeps/Godeps.json
+++ b/automation/Godeps/Godeps.json
@@ -24,6 +24,10 @@
"ImportPath": "github.com/kelseyhightower/envconfig",
"Comment": "1.1.0-17-g91921eb",
"Rev": "91921eb4cf999321cdbeebdba5a03555800d493b"
+ },
+ {
+ "ImportPath": "github.com/Sirupsen/logrus",
+ "Rev": "f3cfb454f4c209e6668c95216c4744b8fddb2356"
}
]
}
diff --git a/automation/maas-flow.go b/automation/maas-flow.go
index be9459f..7bacddb 100644
--- a/automation/maas-flow.go
+++ b/automation/maas-flow.go
@@ -3,8 +3,8 @@
import (
"encoding/json"
"flag"
+ "github.com/Sirupsen/logrus"
"github.com/kelseyhightower/envconfig"
- "log"
"net/url"
"os"
"strings"
@@ -35,6 +35,8 @@
PowerHelperScript string `default:"" envconfig:"POWER_HELPER_SCRIPT"`
ProvisionUrl string `default:"" envconfig:"PROVISION_URL"`
ProvisionTtl string `default:"1h" envconfig:"PROVISION_TTL"`
+ LogLevel string `default:"warning" envconfig:"LOG_LEVEL"`
+ LogFormat string `default:"text" envconfig:"LOG_FORMAT"`
}
var apiKey = flag.String("apikey", "", "key with which to access MAAS server")
@@ -44,7 +46,6 @@
var preview = flag.Bool("preview", false, "displays the action that would be taken, but does not do the action, in this mode the nodes are processed only once")
var mappings = flag.String("mappings", "{}", "the mac to name mappings")
var always = flag.Bool("always-rename", true, "attempt to rename at every stage of workflow")
-var verbose = flag.Bool("verbose", false, "display verbose logging")
var filterSpec = flag.String("filter", strings.Map(func(r rune) rune {
if unicode.IsSpace(r) {
return -1
@@ -56,7 +57,7 @@
// return false.
func checkError(err error, message string, v ...interface{}) bool {
if err != nil {
- log.Fatalf("[error] "+message, v)
+ log.Fatalf(message, v...)
}
return false
}
@@ -65,7 +66,7 @@
// return true, else return false.
func checkWarn(err error, message string, v ...interface{}) bool {
if err != nil {
- log.Printf("[warn] "+message, v)
+ log.Warningf(message, v...)
return true
}
return false
@@ -93,16 +94,19 @@
return nodes, nil
}
+var log = logrus.New()
+
func main() {
flag.Parse()
config := Config{}
err := envconfig.Process("AUTOMATION", &config)
- checkError(err, "[error] unable to parse environment options : %s", err)
+ if err != nil {
+ log.Fatalf("Unable to parse configuration options : %s", err)
+ }
options := ProcessingOptions{
Preview: *preview,
- Verbose: *verbose,
AlwaysRename: *always,
Provisioner: NewProvisioner(&ProvisionerConfig{Url: config.ProvisionUrl}),
ProvisionURL: config.ProvisionUrl,
@@ -111,8 +115,24 @@
PowerHelperHost: config.PowerHelperHost,
}
+ switch config.LogFormat {
+ case "json":
+ log.Formatter = &logrus.JSONFormatter{}
+ default:
+ log.Formatter = &logrus.TextFormatter{
+ FullTimestamp: true,
+ ForceColors: true,
+ }
+ }
+
+ level, err := logrus.ParseLevel(config.LogLevel)
+ if err != nil {
+ level = logrus.WarnLevel
+ }
+ log.Level = level
+
options.ProvisionTTL, err = time.ParseDuration(config.ProvisionTtl)
- checkError(err, "[error] unable to parse specified duration of '%s' : %s", err)
+ checkError(err, "unable to parse specified duration of '%s' : %s", err)
// Determine the filter, this can either be specified on the the command
// line as a value or a file reference. If none is specified the default
@@ -121,17 +141,17 @@
if (*filterSpec)[0] == '@' {
name := os.ExpandEnv((*filterSpec)[1:])
file, err := os.OpenFile(name, os.O_RDONLY, 0)
- checkError(err, "[error] unable to open file '%s' to load the filter : %s", name, err)
+ checkError(err, "unable to open file '%s' to load the filter : %s", name, err)
decoder := json.NewDecoder(file)
err = decoder.Decode(&options.Filter)
- checkError(err, "[error] unable to parse filter configuration from file '%s' : %s", name, err)
+ checkError(err, "unable to parse filter configuration from file '%s' : %s", name, err)
} else {
err := json.Unmarshal([]byte(*filterSpec), &options.Filter)
- checkError(err, "[error] unable to parse filter specification: '%s' : %s", *filterSpec, err)
+ checkError(err, "unable to parse filter specification: '%s' : %s", *filterSpec, err)
}
} else {
err := json.Unmarshal([]byte(defaultFilter), &options.Filter)
- checkError(err, "[error] unable to parse default filter specificiation: '%s' : %s", defaultFilter, err)
+ checkError(err, "unable to parse default filter specificiation: '%s' : %s", defaultFilter, err)
}
// Determine the mac to name mapping, this can either be specified on the the command
@@ -141,44 +161,45 @@
if (*mappings)[0] == '@' {
name := os.ExpandEnv((*mappings)[1:])
file, err := os.OpenFile(name, os.O_RDONLY, 0)
- checkError(err, "[error] unable to open file '%s' to load the mac name mapping : %s", name, err)
+ checkError(err, "unable to open file '%s' to load the mac name mapping : %s", name, err)
decoder := json.NewDecoder(file)
err = decoder.Decode(&options.Mappings)
- checkError(err, "[error] unable to parse filter configuration from file '%s' : %s", name, err)
+ checkError(err, "unable to parse filter configuration from file '%s' : %s", name, err)
} else {
err := json.Unmarshal([]byte(*mappings), &options.Mappings)
- checkError(err, "[error] unable to parse mac name mapping: '%s' : %s", *mappings, err)
+ checkError(err, "unable to parse mac name mapping: '%s' : %s", *mappings, err)
}
} else {
err := json.Unmarshal([]byte(defaultMapping), &options.Mappings)
- checkError(err, "[error] unable to parse default mac name mappings: '%s' : %s", defaultMapping, err)
+ checkError(err, "unable to parse default mac name mappings: '%s' : %s", defaultMapping, err)
}
// Verify the specified period for queries can be converted into a Go duration
period, err := time.ParseDuration(*queryPeriod)
- checkError(err, "[error] unable to parse specified query period duration: '%s': %s", queryPeriod, err)
+ checkError(err, "unable to parse specified query period duration: '%s': %s", queryPeriod, err)
- log.Printf(`Configuration:
+ log.Infof(`Configuration:
MAAS URL: %s
MAAS API Version: %s
MAAS Query Interval: %s
Node Filter: %s
Node Name Mappings: %s
Preview: %v
- Verbose: %v
Always Rename: %v
Provision URL: %s
Provision TTL: %s
Power Helper: %s
Power Helper User: %s
- Power Helper Host: %s`,
+ Power Helper Host: %s
+ Log Level: %s
+ Log Format: %s`,
*maasURL, *apiVersion, *queryPeriod, *filterSpec, *mappings, options.Preview,
- options.Verbose, options.AlwaysRename, options.ProvisionURL, options.ProvisionTTL,
- options.PowerHelper, options.PowerHelperUser, options.PowerHelperHost)
+ options.AlwaysRename, options.ProvisionURL, options.ProvisionTTL,
+ options.PowerHelper, options.PowerHelperUser, options.PowerHelperHost, config.LogLevel, config.LogFormat)
authClient, err := maas.NewAuthenticatedClient(*maasURL, *apiKey, *apiVersion)
if err != nil {
- checkError(err, "[error] Unable to use specified client key, '%s', to authenticate to the MAAS server: %s", *apiKey, err)
+ checkError(err, "Unable to use specified client key, '%s', to authenticate to the MAAS server: %s", *apiKey, err)
}
// Create an object through which we will communicate with MAAS
@@ -197,7 +218,7 @@
// Create a ticker and fetch and process the nodes every "period"
ticker := time.NewTicker(period)
for t := range ticker.C {
- log.Printf("[info] query server at %s", t)
+ log.Infof("query server at %s", t)
nodes, _ := fetchNodes(client)
ProcessAll(client, nodes, options)
}
diff --git a/automation/node.go b/automation/node.go
index 9e045e6..a54579a 100644
--- a/automation/node.go
+++ b/automation/node.go
@@ -2,8 +2,6 @@
import (
"fmt"
- "log"
-
maas "github.com/juju/gomaasapi"
"net/url"
)
@@ -89,7 +87,7 @@
}
_, err := n.Update(values)
if err != nil {
- log.Printf("[error] error updating power settings : %s", err.Error())
+ log.Errorf("error updating power settings : %s", err.Error())
}
}
diff --git a/automation/state.go b/automation/state.go
index 7fe1760..5058d17 100644
--- a/automation/state.go
+++ b/automation/state.go
@@ -3,7 +3,6 @@
import (
"encoding/json"
"fmt"
- "log"
"net/url"
"os/exec"
"regexp"
@@ -44,7 +43,6 @@
}
}
Mappings map[string]interface{}
- Verbose bool
Preview bool
AlwaysRename bool
Provisioner Provisioner
@@ -130,7 +128,7 @@
if name, ok := entry.(map[string]interface{})["hostname"]; ok && current != name.(string) {
nodesObj := client.GetSubObject("nodes")
nodeObj := nodesObj.GetSubObject(node.ID())
- log.Printf("RENAME '%s' to '%s'\n", node.Hostname(), name.(string))
+ log.Infof("RENAME '%s' to '%s'\n", node.Hostname(), name.(string))
if !options.Preview {
nodeObj.Update(url.Values{"hostname": []string{name.(string)}})
@@ -143,9 +141,7 @@
// Reset we are at the target state, nothing to do
var Reset = func(client *maas.MAASObject, node MaasNode, options ProcessingOptions) error {
- if options.Verbose {
- log.Printf("RESET: %s", node.Hostname())
- }
+ log.Debugf("RESET: %s", node.Hostname())
if options.AlwaysRename {
updateNodeName(client, node, options)
@@ -158,9 +154,7 @@
// Provision we are at the target state, nothing to do
var Provision = func(client *maas.MAASObject, node MaasNode, options ProcessingOptions) error {
- if options.Verbose {
- log.Printf("CHECK PROVISION: %s", node.Hostname())
- }
+ log.Debugf("CHECK PROVISION: %s", node.Hostname())
if options.AlwaysRename {
updateNodeName(client, node, options)
@@ -168,7 +162,7 @@
record, err := options.Provisioner.Get(node.ID())
if err != nil {
- log.Printf("[warn] unable to retrieve provisioning state of node '%s' : %s", node.Hostname(), err)
+ log.Warningf("unable to retrieve provisioning state of node '%s' : %s", node.Hostname(), err)
} else if record == nil || record.Status == Failed {
var label string
if record == nil {
@@ -176,9 +170,7 @@
} else {
label = record.Status.String()
}
- if options.Verbose {
- log.Printf("[info] Current state of node '%s' is '%s'", node.Hostname(), label)
- }
+ log.Debugf("Current state of node '%s' is '%s'", node.Hostname(), label)
ips := node.IPs()
ip := ""
if len(ips) > 0 {
@@ -189,10 +181,7 @@
if len(macs) > 0 {
mac = macs[0]
}
- if options.Verbose {
- log.Printf("[info] POSTing '%s' (%s) to '%s'", node.Hostname(),
- node.ID(), options.ProvisionURL)
- }
+ log.Debugf("POSTing '%s' (%s) to '%s'", node.Hostname(), node.ID(), options.ProvisionURL)
err = options.Provisioner.Provision(&ProvisionRequest{
Id: node.ID(),
Name: node.Hostname(),
@@ -201,16 +190,16 @@
})
if err != nil {
- log.Printf("[error] unable to provision '%s' (%s) : %s", node.ID, node.Hostname(), err)
+ log.Errorf("unable to provision '%s' (%s) : %s", node.ID, node.Hostname(), err)
}
} else if options.ProvisionTTL > 0 &&
record.Status == Running && time.Since(time.Unix(record.Timestamp, 0)) > options.ProvisionTTL {
- log.Printf("[error] Provisioning of node '%s' has passed provisioning TTL of '%v'",
+ log.Errorf("Provisioning of node '%s' has passed provisioning TTL of '%v'",
node.Hostname(), options.ProvisionTTL)
options.Provisioner.Clear(node.ID())
- } else if options.Verbose {
- log.Printf("[info] Not invoking provisioning for '%s', current state is '%s'", node.Hostname(),
+ } else {
+ log.Debugf("Not invoking provisioning for '%s', current state is '%s'", node.Hostname(),
record.Status.String())
}
@@ -223,9 +212,7 @@
// log this fact unless we are in verbose mode. I suspect it would be
// nice to log it once when the device transitions from a non COMPLETE
// state to a complete state, but that would require keeping state.
- if options.Verbose {
- log.Printf("COMPLETE: %s", node.Hostname())
- }
+ log.Debugf("COMPLETE: %s", node.Hostname())
if options.AlwaysRename {
updateNodeName(client, node, options)
@@ -236,7 +223,7 @@
// Deploy cause a node to deploy
var Deploy = func(client *maas.MAASObject, node MaasNode, options ProcessingOptions) error {
- log.Printf("DEPLOY: %s", node.Hostname())
+ log.Infof("DEPLOY: %s", node.Hostname())
if options.AlwaysRename {
updateNodeName(client, node, options)
@@ -249,7 +236,7 @@
// a parameter default
_, err := myNode.CallPost("start", url.Values{"distro_series": []string{"trusty"}})
if err != nil {
- log.Printf("ERROR: DEPLOY '%s' : '%s'", node.Hostname(), err)
+ log.Errorf("DEPLOY '%s' : '%s'", node.Hostname(), err)
return err
}
}
@@ -258,7 +245,7 @@
// Aquire aquire a machine to a specific operator
var Aquire = func(client *maas.MAASObject, node MaasNode, options ProcessingOptions) error {
- log.Printf("AQUIRE: %s", node.Hostname())
+ log.Infof("AQUIRE: %s", node.Hostname())
nodesObj := client.GetSubObject("nodes")
if options.AlwaysRename {
@@ -352,7 +339,7 @@
_, err = nodesObj.CallPost("acquire",
url.Values{"name": []string{node.Hostname()}})
if err != nil {
- log.Printf("ERROR: AQUIRE '%s' : '%s'", node.Hostname(), err)
+ log.Errorf("AQUIRE '%s' : '%s'", node.Hostname(), err)
return err
}
}
@@ -369,21 +356,21 @@
switch state {
case "on":
// Attempt to turn the node off
- log.Printf("POWER DOWN: %s", node.Hostname())
+ log.Infof("POWER DOWN: %s", node.Hostname())
if !options.Preview {
//POST /api/1.0/nodes/{system_id}/ op=stop
nodesObj := client.GetSubObject("nodes")
nodeObj := nodesObj.GetSubObject(node.ID())
_, err := nodeObj.CallPost("stop", url.Values{"stop_mode": []string{"soft"}})
if err != nil {
- log.Printf("ERROR: Commission '%s' : changing power start to off : '%s'", node.Hostname(), err)
+ log.Errorf("Commission '%s' : changing power start to off : '%s'", node.Hostname(), err)
}
return err
}
break
case "off":
// We are off so move to commissioning
- log.Printf("COMISSION: %s", node.Hostname())
+ log.Infof("COMISSION: %s", node.Hostname())
if !options.Preview {
nodesObj := client.GetSubObject("nodes")
nodeObj := nodesObj.GetSubObject(node.ID())
@@ -392,14 +379,14 @@
_, err := nodeObj.CallPost("commission", url.Values{})
if err != nil {
- log.Printf("ERROR: Commission '%s' : '%s'", node.Hostname(), err)
+ log.Errorf("Commission '%s' : '%s'", node.Hostname(), err)
}
return err
}
break
default:
// We are in a state from which we can't move forward.
- log.Printf("[warn]: %s has invalid power state '%s'", node.Hostname(), state)
+ log.Warningf("%s has invalid power state '%s'", node.Hostname(), state)
// If a power helper script is set, we have an unknown power state, and
// we have not power type then attempt to use the helper script to discover
@@ -410,14 +397,14 @@
node.MACs()...)...)
stdout, err := cmd.Output()
if err != nil {
- log.Printf("[error] Failed while executing power helper script '%s' : %s",
+ log.Errorf("Failed while executing power helper script '%s' : %s",
options.PowerHelper, err)
return err
}
power := Power{}
err = json.Unmarshal(stdout, &power)
if err != nil {
- log.Printf("[error] Failed to parse output of power helper script '%s' : %s",
+ log.Errorf("Failed to parse output of power helper script '%s' : %s",
options.PowerHelper, err)
return err
}
@@ -430,7 +417,7 @@
}
node.UpdatePowerParameters(power.Name, params)
default:
- log.Printf("[warn] Unsupported power type discovered '%s'", power.Name)
+ log.Warningf("Unsupported power type discovered '%s'", power.Name)
}
}
break
@@ -440,32 +427,32 @@
// Wait a do nothing state, while work is being done
var Wait = func(client *maas.MAASObject, node MaasNode, options ProcessingOptions) error {
- log.Printf("WAIT: %s", node.Hostname())
+ log.Infof("WAIT: %s", node.Hostname())
return nil
}
// Fail a state from which we cannot, currently, automatically recover
var Fail = func(client *maas.MAASObject, node MaasNode, options ProcessingOptions) error {
- log.Printf("FAIL: %s", node.Hostname())
+ log.Infof("FAIL: %s", node.Hostname())
return nil
}
// AdminState an administrative state from which we should make no automatic transition
var AdminState = func(client *maas.MAASObject, node MaasNode, options ProcessingOptions) error {
- log.Printf("ADMIN: %s", node.Hostname())
+ log.Infof("ADMIN: %s", node.Hostname())
return nil
}
func findActions(target string, current string) ([]Action, error) {
targets, ok := Transitions[target]
if !ok {
- log.Printf("[warn] unable to find transitions to target state '%s'", target)
+ log.Warningf("unable to find transitions to target state '%s'", target)
return nil, fmt.Errorf("Could not find transition to target state '%s'", target)
}
actions, ok := targets[current]
if !ok {
- log.Printf("[warn] unable to find transition from current state '%s' to target state '%s'",
+ log.Warningf("unable to find transition from current state '%s' to target state '%s'",
current, target)
return nil, fmt.Errorf("Could not find transition from current state '%s' to target state '%s'",
current, target)
@@ -479,7 +466,7 @@
var err error
for _, action := range actions {
if err = action(client, node, options); err != nil {
- log.Printf("[error] Error while processing action for node '%s' : %s",
+ log.Errorf("Error while processing action for node '%s' : %s",
node.Hostname(), err)
break
}
@@ -554,16 +541,12 @@
errors[i] = nil
}
} else {
- if options.Verbose {
- log.Printf("[info] ignoring node '%s' as its zone '%s' didn't match include zone name filter '%v'",
- node.Hostname(), node.Zone(), options.Filter.Zones.Include)
- }
+ log.Debugf("ignoring node '%s' as its zone '%s' didn't match include zone name filter '%v'",
+ node.Hostname(), node.Zone(), options.Filter.Zones.Include)
}
} else {
- if options.Verbose {
- log.Printf("[info] ignoring node '%s' as it didn't match include hostname filter '%v'",
- node.Hostname(), options.Filter.Hosts.Include)
- }
+ log.Debugf("ignoring node '%s' as it didn't match include hostname filter '%v'",
+ node.Hostname(), options.Filter.Hosts.Include)
}
}
return errors
diff --git a/ip-allocator/Dockerfile b/ip-allocator/Dockerfile
index 870381c..4f62112 100644
--- a/ip-allocator/Dockerfile
+++ b/ip-allocator/Dockerfile
@@ -7,7 +7,7 @@
ADD . /go/src/gerrit.opencord.org/maas/cord-ip-allocator
WORKDIR /go/src/gerrit.opencord.org/maas/cord-ip-allocator
-RUN /go/bin/godep restore
+RUN /go/bin/godep restore || true
WORKDIR /go
diff --git a/ip-allocator/Godeps/Godeps.json b/ip-allocator/Godeps/Godeps.json
index 8929877..8b2d186 100644
--- a/ip-allocator/Godeps/Godeps.json
+++ b/ip-allocator/Godeps/Godeps.json
@@ -20,6 +20,10 @@
"ImportPath": "github.com/kelseyhightower/envconfig",
"Comment": "1.1.0-17-g91921eb",
"Rev": "91921eb4cf999321cdbeebdba5a03555800d493b"
- }
+ },
+ {
+ "ImportPath": "github.com/Sirupsen/logrus",
+ "Rev": "f3cfb454f4c209e6668c95216c4744b8fddb2356"
+ }
]
}
diff --git a/ip-allocator/allocator.go b/ip-allocator/allocator.go
index e5a64cb..27e237a 100644
--- a/ip-allocator/allocator.go
+++ b/ip-allocator/allocator.go
@@ -2,37 +2,60 @@
import (
"fmt"
+ "github.com/Sirupsen/logrus"
"github.com/gorilla/mux"
"github.com/kelseyhightower/envconfig"
- "log"
"net/http"
)
type Config struct {
- Port int `default:"4242"`
- Listen string `default:"0.0.0.0"`
- Network string `default:"10.0.0.0/24"`
- Skip int `default:"1"`
+ Port int `default:"4242"`
+ Listen string `default:"0.0.0.0"`
+ Network string `default:"10.0.0.0/24"`
+ Skip int `default:"1"`
+ LogLevel string `default:"warning" envconfig:"LOG_LEVEL"`
+ LogFormat string `default:"text" envconfig:"LOG_FORMAT"`
}
type Context struct {
storage Storage
}
+var log = logrus.New()
+
func main() {
context := &Context{}
config := Config{}
err := envconfig.Process("ALLOCATE", &config)
if err != nil {
- log.Fatalf("[error] Unable to parse configuration options : %s", err)
+ log.Fatalf("Unable to parse configuration options : %s", err)
}
- log.Printf(`Configuration:
+ switch config.LogFormat {
+ case "json":
+ log.Formatter = &logrus.JSONFormatter{}
+ default:
+ log.Formatter = &logrus.TextFormatter{
+ FullTimestamp: true,
+ ForceColors: true,
+ }
+ }
+
+ level, err := logrus.ParseLevel(config.LogLevel)
+ if err != nil {
+ level = logrus.WarnLevel
+ }
+ log.Level = level
+
+ log.Infof(`Configuration:
Listen: %s
Port: %d
Network: %s
- SKip: %d`, config.Listen, config.Port, config.Network, config.Skip)
+ SKip: %d
+ Log Level: %s
+ Log Format: %s`, config.Listen, config.Port, config.Network, config.Skip,
+ config.LogLevel, config.LogFormat)
context.storage = &MemoryStorage{}
context.storage.Init(config.Network, config.Skip)
diff --git a/provisioner/Dockerfile b/provisioner/Dockerfile
index 429203a..b2721f0 100644
--- a/provisioner/Dockerfile
+++ b/provisioner/Dockerfile
@@ -39,7 +39,7 @@
ADD . /go/src/gerrit.opencord.org/maas/cord-provisioner
WORKDIR /go/src/gerrit.opencord.org/maas/cord-provisioner
-RUN $GOPATH/bin/godep restore
+RUN $GOPATH/bin/godep restore || true
WORKDIR $GOPATH
RUN go install gerrit.opencord.org/maas/cord-provisioner
diff --git a/provisioner/Godeps/Godeps.json b/provisioner/Godeps/Godeps.json
index a96e9e3..2870953 100644
--- a/provisioner/Godeps/Godeps.json
+++ b/provisioner/Godeps/Godeps.json
@@ -22,6 +22,10 @@
"ImportPath": "github.com/kelseyhightower/envconfig",
"Comment": "1.1.0-17-g91921eb",
"Rev": "91921eb4cf999321cdbeebdba5a03555800d493b"
- }
+ },
+ {
+ "ImportPath": "github.com/Sirupsen/logrus",
+ "Rev": "f3cfb454f4c209e6668c95216c4744b8fddb2356"
+ }
]
}
diff --git a/provisioner/consul_storage.go b/provisioner/consul_storage.go
index 5701c72..0a0c62d 100644
--- a/provisioner/consul_storage.go
+++ b/provisioner/consul_storage.go
@@ -3,7 +3,6 @@
import (
"encoding/json"
consul "github.com/hashicorp/consul/api"
- "log"
"net/url"
)
@@ -27,7 +26,7 @@
Scheme: "http",
}
- log.Printf("%+v", cfg)
+ log.Debugf("Consul config = %+v", cfg)
client, err := consul.NewClient(&cfg)
if err != nil {
diff --git a/provisioner/dispatcher.go b/provisioner/dispatcher.go
index d448e19..eaa65cb 100644
--- a/provisioner/dispatcher.go
+++ b/provisioner/dispatcher.go
@@ -1,7 +1,6 @@
package main
import (
- "log"
"os/exec"
"time"
)
@@ -51,7 +50,7 @@
case work := <-w.Work:
// Receive a work request.
w.StatusChan <- StatusMsg{&work, w.ID, Running, "", time.Now().Unix()}
- log.Printf("[debug] RUN: %s %s %s %s %s %s",
+ log.Debugf("RUN: %s %s %s %s %s %s",
work.Script, work.Info.Id, work.Info.Name,
work.Info.Ip, work.Info.Mac, work.Role)
err := exec.Command(work.Script, work.Info.Id, work.Info.Name,
@@ -65,7 +64,7 @@
}
case <-w.QuitChan:
// We have been asked to stop.
- log.Printf("worker%d stopping\n", w.ID)
+ log.Infof("worker%d stopping\n", w.ID)
return
}
}
@@ -112,7 +111,7 @@
func (d *Dispatcher) Start() {
// Now, create all of our workers.
for i := 0; i < d.NumWorkers; i++ {
- log.Printf("Creating worker %d", i)
+ log.Infof("Creating worker %d", i)
worker := NewWorker(i, d.WorkerQueue, d.StatusChan)
worker.Start()
}
@@ -121,24 +120,24 @@
for {
select {
case work := <-d.WorkQueue:
- log.Println("[debug] Received work requeust")
+ log.Debugf("Received work requeust")
d.StatusChan <- StatusMsg{&work, -1, Pending, "", time.Now().Unix()}
go func() {
worker := <-d.WorkerQueue
- log.Println("[debug] Dispatching work request")
+ log.Debugf("Dispatching work request")
worker <- work
}()
case update := <-d.StatusChan:
err := d.Storage.Put(update.Request.Info.Id, update)
if err != nil {
- log.Printf("[error] Unable to update storage with status for '%s' : %s",
+ log.Errorf("Unable to update storage with status for '%s' : %s",
update.Request.Info.Id, err)
} else {
- log.Printf("[debug] Storage updated for '%s'", update.Request.Info.Id)
+ log.Debugf("Storage updated for '%s'", update.Request.Info.Id)
}
case <-d.QuitChan:
- log.Println("[info] Stopping dispatcher")
+ log.Infof("Stopping dispatcher")
return
}
}
diff --git a/provisioner/handlers.go b/provisioner/handlers.go
index 7fd53fa..bb93de4 100644
--- a/provisioner/handlers.go
+++ b/provisioner/handlers.go
@@ -4,7 +4,6 @@
"bufio"
"encoding/json"
"github.com/gorilla/mux"
- "log"
"net/http"
"strings"
)
@@ -59,20 +58,20 @@
decoder := json.NewDecoder(r.Body)
defer r.Body.Close()
if err := decoder.Decode(&info); err != nil {
- log.Printf("[error] Unable to decode request to provision : %s", err)
+ log.Errorf("Unable to decode request to provision : %s", err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if !c.validateData(&info) {
- log.Printf("[error] Provisioning request not valid for '%s'", info.Name)
+ log.Errorf("Provisioning request not valid for '%s'", info.Name)
w.WriteHeader(http.StatusBadRequest)
return
}
role, err := c.GetRole(&info)
if err != nil {
- log.Printf("[error] unable to get provisioning role for node '%s' : %s", info.Name, err)
+ log.Errorf("unable to get provisioning role for node '%s' : %s", info.Name, err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
@@ -84,7 +83,7 @@
}
err = c.dispatcher.Dispatch(&info, role, script)
if err != nil {
- log.Printf("[error] unable to dispatch provisioning request for node '%s' : %s", info.Name, err)
+ log.Errorf("unable to dispatch provisioning request for node '%s' : %s", info.Name, err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
@@ -112,7 +111,7 @@
err := c.storage.Delete(id)
if err != nil {
- log.Printf("[warn] Error while deleting status fo '%s' from storage : %s", id, err)
+ log.Errorf("Error while deleting status fo '%s' from storage : %s", id, err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
@@ -129,7 +128,7 @@
}
s, err := c.storage.Get(id)
if err != nil {
- log.Printf("[warn] Error while retrieving status for '%s' from strorage : %s", id, err)
+ log.Errorf("Error while retrieving status for '%s' from strorage : %s", id, err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
@@ -139,7 +138,7 @@
}
bytes, err := json.Marshal(s)
if err != nil {
- log.Printf("[error] Error while attempting to marshal status for '%s' from storage : %s", id, err)
+ log.Errorf("Error while attempting to marshal status for '%s' from storage : %s", id, err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
diff --git a/provisioner/provisioner.go b/provisioner/provisioner.go
index 41240b5..6c02c14 100644
--- a/provisioner/provisioner.go
+++ b/provisioner/provisioner.go
@@ -2,9 +2,9 @@
import (
"fmt"
+ "github.com/Sirupsen/logrus"
"github.com/gorilla/mux"
"github.com/kelseyhightower/envconfig"
- "log"
"net/http"
)
@@ -15,6 +15,8 @@
DefaultRole string `default:"compute-node" envconfig:"default_role"`
Script string `default:"do-ansible"`
StorageURL string `default:"memory:" envconfig:"storage_url"`
+ LogLevel string `default:"warning" envconfig:"LOG_LEVEL"`
+ LogFormat string `default:"text" envconfig:"LOG_FORMAT"`
}
type Context struct {
@@ -24,23 +26,44 @@
dispatcher *Dispatcher
}
+var log = logrus.New()
+
func main() {
context := &Context{}
err := envconfig.Process("PROVISION", &(context.config))
if err != nil {
- log.Fatalf("[error] Unable to parse configuration options : %s", err)
+ log.Fatalf("[ERRO] Unable to parse configuration options : %s", err)
}
- log.Printf(`Configuration:
+ switch context.config.LogFormat {
+ case "json":
+ log.Formatter = &logrus.JSONFormatter{}
+ default:
+ log.Formatter = &logrus.TextFormatter{
+ FullTimestamp: true,
+ ForceColors: true,
+ }
+ }
+
+ level, err := logrus.ParseLevel(context.config.LogLevel)
+ if err != nil {
+ level = logrus.WarnLevel
+ }
+ log.Level = level
+
+ log.Infof(`Configuration:
Listen: %s
Port: %d
RoleSelectorURL: %s
DefaultRole: %s
Script: %s
- StorageURL: %s`,
+ StorageURL: %s
+ Log Level: %s
+ Log Format: %s`,
context.config.Listen, context.config.Port, context.config.RoleSelectorURL,
- context.config.DefaultRole, context.config.Script, context.config.StorageURL)
+ context.config.DefaultRole, context.config.Script, context.config.StorageURL,
+ context.config.LogLevel, context.config.LogFormat)
context.storage, err = NewStorage(context.config.StorageURL)
if err != nil {
diff --git a/roles/maas/templates/automation-compose.yml.j2 b/roles/maas/templates/automation-compose.yml.j2
index a7e9f49..c6c9c28 100644
--- a/roles/maas/templates/automation-compose.yml.j2
+++ b/roles/maas/templates/automation-compose.yml.j2
@@ -12,6 +12,7 @@
- "/etc/maas/automation/storage:/consul/data"
network_mode: host
command: agent --server --bind {{ mgmt_ip_address.stdout }} --client {{ mgmt_ip_address.stdout }} --bootstrap-expect=1
+ restart: unless-stopped
allocator:
image: "docker-registry:5000/cord-ip-allocator:{{ docker.image_version }}"
@@ -26,6 +27,8 @@
- "ALLOCATE_LISTEN=0.0.0.0"
- "ALLOCATE_NETWORK={{ networks.fabric }}"
- "ALLOCATE_SKIP=2"
+ - "ALLOCATE_LOG_LEVEL=debug"
+ - "ALLOCATE_LOG_FORMAT=text"
restart: unless-stopped
provisioner:
@@ -36,6 +39,7 @@
- "lab.component=provisioner"
links:
- allocator
+ - storage
environment:
# need to explicitly set the resolver, else go will skip the /etc/hosts file
- "GODEBUG=netdns=go"
@@ -45,6 +49,8 @@
- "PROVISION_DEFAULT_ROLE=compute-node"
- "PROVISION_SCRIPT=/etc/maas/ansible/do-ansible"
- "PROVISION_STORAGE_URL=consul://{{ mgmt_ip_address.stdout }}:8500"
+ - "PROVISION_LOG_LEVEL=debug"
+ - "PROVISION_LOG_FORMAT=text"
volumes:
- "/etc/maas/ansible:/etc/maas/ansible"
restart: unless-stopped
@@ -63,6 +69,8 @@
- "SWITCHQ_PROVISION_TTL=0s"
- "SWITCHQ_DEFAULT_ROLE=fabric-switch"
- "SWITCHQ_ADDRESS_URL=file:///switchq/dhcp/dhcp_harvest.inc"
+ - "SWITCHQ_LOG_LEVEL=debug"
+ - "SWITCHQ_LOG_FORMAT=text"
volumes:
- "/etc/bind/maas:/switchq/dhcp"
restart: unless-stopped
@@ -85,11 +93,13 @@
- "AUTOMATION_POWER_HELPER_USER={{ virtualbox.power_helper_user }}"
- "AUTOMATION_POWER_HELPER_HOST={{ virtualbox_host }}"
{% endif %}
+ - "AUTOMATION_LOG_FORMAT=text"
+ - "AUTOMATION_LOG_LEVEL=debug"
volumes:
- "/etc/maas:/mappings"
{% if virtualbox_support is defined and virtualbox_support == "1" %}
- "/etc/maas/virtualbox:/etc/maas/virtualbox"
{% endif %}
- command: [ "-apiVersion", "1.0", "-apikey", "{{ apikey.stdout }}", "-maas", "http://{{ mgmt_ip_address.stdout }}/MAAS", "-period", "30s", "-mappings", "@/mappings/mappings.json", "-always-rename", "-verbose" ]
+ command: [ "-apiVersion", "1.0", "-apikey", "{{ apikey.stdout }}", "-maas", "http://{{ mgmt_ip_address.stdout }}/MAAS", "-period", "30s", "-mappings", "@/mappings/mappings.json", "-always-rename" ]
restart: unless-stopped
diff --git a/switchq/Dockerfile b/switchq/Dockerfile
index a0966e8..cf30306 100644
--- a/switchq/Dockerfile
+++ b/switchq/Dockerfile
@@ -42,7 +42,7 @@
ADD . $GOPATH/src/gerrit.opencord.com/maas/switchq
WORKDIR $GOPATH/src/gerrit.opencord.com/maas/switchq
-RUN $GOPATH/bin/godep restore
+RUN $GOPATH/bin/godep restore || true
WORKDIR $GOPATH
RUN go install gerrit.opencord.com/maas/switchq
diff --git a/switchq/Godeps/Godeps.json b/switchq/Godeps/Godeps.json
index 00c42a5..cc3867f 100644
--- a/switchq/Godeps/Godeps.json
+++ b/switchq/Godeps/Godeps.json
@@ -7,6 +7,10 @@
"ImportPath": "github.com/kelseyhightower/envconfig",
"Comment": "1.1.0-17-g91921eb",
"Rev": "91921eb4cf999321cdbeebdba5a03555800d493b"
- }
+ },
+ {
+ "ImportPath": "github.com/Sirupsen/logrus",
+ "Rev": "f3cfb454f4c209e6668c95216c4744b8fddb2356"
+ }
]
}
diff --git a/switchq/switchq.go b/switchq/switchq.go
index b586d75..2c084ed 100644
--- a/switchq/switchq.go
+++ b/switchq/switchq.go
@@ -4,8 +4,8 @@
"bytes"
"encoding/json"
"fmt"
+ "github.com/Sirupsen/logrus"
"github.com/kelseyhightower/envconfig"
- "log"
"net/http"
"time"
)
@@ -20,6 +20,8 @@
RoleSelectorURL string `default:"" envconfig:"role_selector_url"`
DefaultRole string `default:"fabric-switch" envconfig:"default_role"`
Script string `default:"do-ansible"`
+ LogLevel string `default:"warning" envconfig:"LOG_LEVEL"`
+ LogFormat string `default:"text" envconfig:"LOG_FORMAT"`
vendors Vendors
storage Storage
@@ -35,16 +37,16 @@
}
func (c *Config) getProvisionedState(rec AddressRec) (int, string, error) {
- log.Printf("[debug] Fetching provisioned state of device '%s' (%s, %s)",
+ log.Debugf("Fetching provisioned state of device '%s' (%s, %s)",
rec.Name, rec.IP, rec.MAC)
resp, err := http.Get(c.ProvisionURL + rec.MAC)
if err != nil {
- log.Printf("[error] Error while retrieving provisioning state for device '%s (%s, %s)' : %s",
+ log.Errorf("Error while retrieving provisioning state for device '%s (%s, %s)' : %s",
rec.Name, rec.IP, rec.MAC, err)
return -1, "", err
}
if resp.StatusCode != 404 && int(resp.StatusCode/100) != 2 {
- log.Printf("[error] Error while retrieving provisioning state for device '%s (%s, %s)' : %s",
+ log.Errorf("Error while retrieving provisioning state for device '%s (%s, %s)' : %s",
rec.Name, rec.IP, rec.MAC, resp.Status)
return -1, "", fmt.Errorf(resp.Status)
}
@@ -54,7 +56,7 @@
var raw interface{}
err = decoder.Decode(&raw)
if err != nil {
- log.Printf("[error] Unmarshal provisioning service response for device '%s (%s, %s)' : %s",
+ log.Errorf("Unmarshal provisioning service response for device '%s (%s, %s)' : %s",
rec.Name, rec.IP, rec.MAC, err)
return -1, "", err
}
@@ -68,7 +70,7 @@
return 3, status["message"].(string), nil
default:
err = fmt.Errorf("unknown provisioning status : %d", status["status"])
- log.Printf("[error] received unknown provisioning status for device '%s (%s)' : %s",
+ log.Errorf("received unknown provisioning status for device '%s (%s)' : %s",
rec.Name, rec.MAC, err)
return -1, "", err
}
@@ -80,7 +82,7 @@
}
func (c *Config) provision(rec AddressRec) error {
- log.Printf("[info] POSTing to '%s' for provisioning of '%s (%s)'", c.ProvisionURL, rec.Name, rec.MAC)
+ log.Infof("POSTing to '%s' for provisioning of '%s (%s)'", c.ProvisionURL, rec.Name, rec.MAC)
data := map[string]string{
"id": rec.MAC,
"name": rec.Name,
@@ -101,25 +103,25 @@
var b []byte
b, err := json.Marshal(data)
if err != nil {
- log.Printf("[error] Unable to marshal provisioning data : %s", err)
+ log.Errorf("Unable to marshal provisioning data : %s", err)
return err
}
req, err := http.NewRequest("POST", c.ProvisionURL, bytes.NewReader(b))
if err != nil {
- log.Printf("[error] Unable to construct POST request to provisioner : %s", err)
+ log.Errorf("Unable to construct POST request to provisioner : %s", err)
return err
}
req.Header.Add("Content-Type", "application/json")
resp, err := hc.Do(req)
if err != nil {
- log.Printf("[error] Unable to POST request to provisioner : %s", err)
+ log.Errorf("Unable to POST request to provisioner : %s", err)
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusAccepted {
- log.Printf("[error] Provisioning request not accepted by provisioner : %s", resp.Status)
+ log.Errorf("Provisioning request not accepted by provisioner : %s", resp.Status)
return err
}
@@ -134,7 +136,7 @@
if !ok {
// Not something we care about
- log.Printf("[debug] host with IP '%s' and MAC '%s' and named '%s' not a known switch type",
+ log.Debugf("host with IP '%s' and MAC '%s' and named '%s' not a known switch type",
rec.IP, rec.MAC, rec.Name)
return nil
}
@@ -145,10 +147,10 @@
}
if last == nil {
- log.Printf("[debug] no TTL for device '%s' (%s, %s)",
+ log.Debugf("no TTL for device '%s' (%s, %s)",
rec.Name, rec.IP, rec.MAC)
} else {
- log.Printf("[debug] TTL for device '%s' (%s, %s) is %v",
+ log.Debugf("TTL for device '%s' (%s, %s) is %v",
rec.Name, rec.IP, rec.MAC, *last)
}
@@ -157,23 +159,23 @@
state, message, err := c.getProvisionedState(rec)
switch state {
case 0, 1: // Pending or Running
- log.Printf("[debug] device '%s' (%s, %s) is being provisioned",
+ log.Debugf("device '%s' (%s, %s) is being provisioned",
rec.Name, rec.IP, rec.MAC)
return nil
case 2: // Complete
- log.Printf("[debug] device '%s' (%s, %s) has completed provisioning",
+ log.Debugf("device '%s' (%s, %s) has completed provisioning",
rec.Name, rec.IP, rec.MAC)
// If no last record then set the TTL
if last == nil {
now := time.Now()
last = &now
c.storage.MarkProvisioned(rec.MAC, last)
- log.Printf("[debug] Storing TTL for device '%s' (%s, %s) as %v",
+ log.Debugf("Storing TTL for device '%s' (%s, %s) as %v",
rec.Name, rec.IP, rec.MAC, now)
return nil
}
case 3: // Failed
- log.Printf("[debug] device '%s' (%s, %s) failed last provisioning with message '%s', reattempt",
+ log.Debugf("device '%s' (%s, %s) failed last provisioning with message '%s', reattempt",
rec.Name, rec.IP, rec.MAC, message)
c.storage.ClearProvisioned(rec.MAC)
last = nil
@@ -184,25 +186,46 @@
if last == nil || (c.ttl > 0 && time.Since(*last) > c.ttl) {
if last != nil {
c.storage.ClearProvisioned(rec.MAC)
- log.Printf("[debug] device '%s' (%s, %s) TTL expired, reprovisioning",
+ log.Debugf("device '%s' (%s, %s) TTL expired, reprovisioning",
rec.Name, rec.IP, rec.MAC)
}
c.provision(rec)
} else if c.ttl == 0 {
- log.Printf("[debug] device '%s' (%s, %s) has completed its one time provisioning, with a TTL set to %s",
+ log.Debugf("device '%s' (%s, %s) has completed its one time provisioning, with a TTL set to %s",
rec.Name, rec.IP, rec.MAC, c.ProvisionTTL)
} else {
- log.Printf("[debug] device '%s' (%s, %s) has completed provisioning within the specified TTL of %s",
+ log.Debugf("device '%s' (%s, %s) has completed provisioning within the specified TTL of %s",
rec.Name, rec.IP, rec.MAC, c.ProvisionTTL)
}
return nil
}
+var log = logrus.New()
+
func main() {
var err error
config := Config{}
- envconfig.Process("SWITCHQ", &config)
+ err = envconfig.Process("SWITCHQ", &config)
+ if err != nil {
+ log.Fatalf("Unable to parse configuration options : %s", err)
+ }
+
+ switch config.LogFormat {
+ case "json":
+ log.Formatter = &logrus.JSONFormatter{}
+ default:
+ log.Formatter = &logrus.TextFormatter{
+ FullTimestamp: true,
+ ForceColors: true,
+ }
+ }
+
+ level, err := logrus.ParseLevel(config.LogLevel)
+ if err != nil {
+ level = logrus.WarnLevel
+ }
+ log.Level = level
config.vendors, err = NewVendors(config.VendorsURL)
checkError(err, "Unable to create known vendors list from specified URL '%s' : %s", config.VendorsURL, err)
@@ -219,7 +242,7 @@
config.ttl, err = time.ParseDuration(config.ProvisionTTL)
checkError(err, "Unable to parse specified provision TTL value of '%s' : %s", config.ProvisionTTL, err)
- log.Printf(`Configuration:
+ log.Infof(`Configuration:
Vendors URL: %s
Storage URL: %s
Poll Interval: %s
@@ -228,25 +251,28 @@
Provision URL: %s
Role Selector URL: %s
Default Role: %s
- Script: %s`,
+ Script: %s
+ Log Level: %s
+ Log Format: %s`,
config.VendorsURL, config.StorageURL, config.PollInterval, config.AddressURL, config.ProvisionTTL,
- config.ProvisionURL, config.RoleSelectorURL, config.DefaultRole, config.Script)
+ config.ProvisionURL, config.RoleSelectorURL, config.DefaultRole, config.Script,
+ config.LogLevel, config.LogFormat)
// We use two methods to attempt to find the MAC (hardware) address associated with an IP. The first
// is to look in the table. The second is to send an ARP packet.
for {
- log.Printf("[info] Checking for switches @ %s", time.Now())
+ log.Infof("Checking for switches @ %s", time.Now())
addresses, err := config.addressSource.GetAddresses()
if err != nil {
- log.Printf("[error] unable to read addresses from address source : %s", err)
+ log.Errorf("unable to read addresses from address source : %s", err)
} else {
- log.Printf("[info] Queried %d addresses from address source", len(addresses))
+ log.Infof("Queried %d addresses from address source", len(addresses))
for _, rec := range addresses {
- log.Printf("[debug] Processing %s(%s, %s)", rec.Name, rec.IP, rec.MAC)
+ log.Debugf("Processing %s(%s, %s)", rec.Name, rec.IP, rec.MAC)
if err := config.processRecord(rec); err != nil {
- log.Printf("[error] Error when processing IP '%s' : %s", rec.IP, err)
+ log.Errorf("Error when processing IP '%s' : %s", rec.IP, err)
}
}
}
diff --git a/switchq/vendors.go b/switchq/vendors.go
index d2a0cc8..c0de108 100644
--- a/switchq/vendors.go
+++ b/switchq/vendors.go
@@ -2,9 +2,8 @@
import (
"encoding/json"
- "log"
- "strings"
"net/http"
+ "strings"
)
type Vendors interface {
@@ -43,7 +42,7 @@
for _, rec := range data {
v.Vendors[rec.Prefix] = rec
}
- log.Printf("[debug] %v", v.Vendors)
+ log.Debugf("known vendors %+v", v.Vendors)
return &v, nil
}