VOL-381 add unum container to support ONOS cluster formation under swarm
Change-Id: Ic260edda19bb199ed040f05164ab605f28c919d0
diff --git a/unum/generate.go b/unum/generate.go
new file mode 100644
index 0000000..597c95f
--- /dev/null
+++ b/unum/generate.go
@@ -0,0 +1,126 @@
+// Copyright 2017 the original author or authors.
+//
+// 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 main
+
+import (
+ "encoding/json"
+ "github.com/Sirupsen/logrus"
+)
+
+/*
+ * Structure used to capture the cluster configuraiton for ONOS and
+ * generate the appropriate JSON
+ */
+
+// ClusterNode ONOS node information
+type ClusterNode struct {
+ ID string `json:"id,omitempty"`
+ IP string `json:"ip,omitempty"`
+ Port int `json:"port,omitempty"`
+}
+
+// ClusterPartition ONOS partition record
+type ClusterPartition struct {
+ ID int `json:"id,omitempty"`
+ Members []string `json:"members,omitempty"`
+}
+
+// ClusterConfig ONOS cluster configuration
+type ClusterConfig struct {
+ Name string `json:"name,omitempty"`
+ Nodes []ClusterNode `json:"nodes,omitempty"`
+ Partitions []ClusterPartition `json:"partitions,omitempty"`
+}
+
+func minInt(a, b int) int {
+ if a < b {
+ return a
+ }
+ return b
+}
+
+//
+func GenerateConfig(info *ClusterInfo, update chan []byte) {
+ if info == nil || info.Expected < 1 {
+ log.Debugf("Expected instance count, %d, less than cluster minimum, generating empty config",
+ info.Expected)
+ update <- make([]byte, 0)
+ return
+ } else if info.Expected != uint64(len(info.Nodes)) {
+ // If the expected and actual node count differ then don't update
+ // config, a node might come back ater all
+ log.Debugf("Expected instances, %d, differs from actual, %d, dropping",
+ info.Expected, len(info.Nodes))
+ return
+ }
+
+ // Verify connectivity to nodes on port 9876
+ if err := VerifyNodes(info.Nodes, 9876); err != nil {
+ // Not able to verify connection to ONOS cluster instance
+ // so do not generate cluster meta data
+ log.Debugf("Unable to verify connection to all nodes : %s, configuration not generated",
+ err.Error())
+ return
+ }
+
+ next := ClusterConfig{
+ Name: "default",
+ }
+ next.Nodes = make([]ClusterNode, len(info.Nodes))
+ for idx, node := range info.Nodes {
+ next.Nodes[idx].ID = node
+ next.Nodes[idx].IP = node
+ next.Nodes[idx].Port = 9876
+ }
+
+ // Select a partition count that make "sense" depending on number
+ // of nodes
+ count := 1
+ if len(info.Nodes) >= 5 {
+ count = 5
+ } else if len(info.Nodes) >= 3 {
+ count = 3
+ }
+ next.Partitions = make([]ClusterPartition, count)
+
+ log.Debugf("Generating config for %d nodes, with %d partitions",
+ len(info.Nodes), count)
+ start := 0
+ perPart := minInt(len(info.Nodes), 3)
+ for idx := 0; idx < count; idx += 1 {
+ next.Partitions[idx].ID = idx + 1
+ next.Partitions[idx].Members = make([]string, perPart)
+ for midx := 0; midx < perPart; midx += 1 {
+ key := (start + midx) % len(info.Nodes)
+ next.Partitions[idx].Members[midx] = info.Nodes[key]
+ }
+ start += 1
+ }
+
+ data, err := json.Marshal(&next)
+ if err != nil {
+ log.Errorf("Unable to marshal cluster configuration : %s", err.Error())
+ } else {
+ if log.Level == logrus.DebugLevel {
+ ppData, err := json.MarshalIndent(&next, "CONFIG: ", " ")
+ if err != nil {
+ log.Warnf("Unable to marshal cluster configuration, for debug : %s", err.Error())
+ } else {
+ log.Debug(string(ppData))
+ }
+ }
+ update <- data
+ }
+}