blob: 7b33afb272668c1aa9119ac8cd6bd2d95855e24f [file] [log] [blame]
David K. Bainbridge215e0242017-09-05 23:18:24 -07001// Copyright 2017 the original author or authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package main
16
17import (
18 "fmt"
19 "net/url"
20 "os"
21 "text/tabwriter"
22 "time"
23
24 "github.com/Sirupsen/logrus"
25 _ "github.com/dimiro1/banner/autoload"
26 "github.com/kelseyhightower/envconfig"
27)
28
29const (
30 // configTemplate used to display the configuration values for informational/debbug purposes
31 configTemplate = `This service is configured by the environment. The following
32are the configuration values:
33 KEY VALUE DESCRIPTION
34 {{range .}}{{usage_key .}} {{.Field}} {{usage_description .}}
35 {{end}}`
36)
37
38// configSpec configuration options for the cluster manager, see envconfig project
39type configSpec struct {
40 Orchestrator string `envconfig:"ORCHESTRATOR" desc:"specifies how to connect to the container orchestration system" default:"swarm://"`
41 Labels map[string]string `envconfig:"LABELS" desc:"labels to match service" default:"org.onosproject.cluster:true"`
42 Network map[string]string `envconfig:"NETWORK" desc:"labels to match against connected networks to determine IP selection for ONOS instance" default:"org.onosproject.cluster:true"`
43 Listen string `envconfig:"LISTEN" desc:"interface on which to listen for cluster configuration requests" default:"0.0.0.0:5411"`
44 Period time.Duration `envconfig:"PERIOD" desc:"how often should the container orchestrator be queried" default:"1s"`
45 LogLevel string `envconfig:"LOG_LEVEL" desc:"detail level for logging" default:"warning"`
46 LogFormat string `envconfig:"LOG_FORMAT" desc:"log output format, text or json" default:"text"`
47}
48
49var log = logrus.New()
50
51// myStruct testing
52type myStruct struct{}
53
54// labelMatch returns true if the labels and values specified in needs are in the label map of has
55func labelMatch(has map[string]string, needs map[string]string) bool {
56 for label, val1 := range needs {
57 if val2, ok := has[label]; !ok || val2 != val1 {
58 return false
59 }
60 }
61 return true
62}
63
64func main() {
65
66 // Load configuration values and output for information/debug purposes
67 var config configSpec
68 envconfig.Process("", &config)
69 tabs := tabwriter.NewWriter(os.Stdout, 4, 4, 4, ' ', 0)
70 err := envconfig.Usagef("", &config, tabs, configTemplate)
71 if err != nil {
72 panic(err)
73 }
74 tabs.Flush()
75 fmt.Println()
76
77 // Establish logging configuraton
78 switch config.LogFormat {
79 case "json":
80 log.Formatter = &logrus.JSONFormatter{}
81 default:
82 log.Formatter = &logrus.TextFormatter{
83 FullTimestamp: true,
84 ForceColors: true,
85 }
86 }
87 level, err := logrus.ParseLevel(config.LogLevel)
88 if err != nil {
89 log.Errorf("Invalid error level specified: '%s', defaulting to WARN level", config.LogLevel)
90 level = logrus.WarnLevel
91 }
92 log.Level = level
93
94 log.Info("Starting ONOS Cluster Manager (unum)")
95
96 // Get adapter to orchestrator
97 url, err := url.Parse(config.Orchestrator)
98 if err != nil {
99 log.Errorf("Unable to parse specified orchestrator URL, '%s' : %s", config.Orchestrator, err.Error())
100 panic(err)
101 }
102
103 // Create client for container orchestratory based on URL
104 orch, err := NewOrchestrationClient(url)
105 if err != nil {
106 log.Errorf("Unable to establish connection to container orchestrator, '%s' : %s",
107 url, err.Error())
108 panic(err)
109 }
110
111 // Get cluster information and build initial cluster configuration
112 info, err := orch.GetInfo(config.Labels, config.Network)
113 if err != nil {
114 log.Warnf("Unable to query cluster information from container orchestratory : %s",
115 err.Error())
116 }
117
118 listener := &Listener{
119 ListenOn: config.Listen,
120 }
121 listener.Init()
122
123 // Set the initial cluster configuration
124 if err == nil {
125 GenerateConfig(info, listener.Update)
126 }
127
128 // Set up the REST listener for the ONOS cluster configuraiton server
129 go listener.ListenAndServe()
130
131 // Loop forever, quering for cluster information and updating the cluster configuration
132 // when needed
133 for {
134 info, err := orch.GetInfo(config.Labels, config.Network)
135 if err != nil {
136 log.Warnf("Unable to query cluster information from container orchestrator : %s",
137 err.Error())
138 } else {
139
140 log.Debugf("EXPECTED: %d, HAVE: %d", info.Expected, len(info.Nodes))
141 if info.Expected == uint64(len(info.Nodes)) || info.Expected == 0 {
142 GenerateConfig(info, listener.Update)
143 }
144 }
145
146 // configurable pause
147 time.Sleep(config.Period)
148 }
149}