VOL-381 add unum container to support ONOS cluster formation under swarm
Change-Id: Ic260edda19bb199ed040f05164ab605f28c919d0
diff --git a/unum/swarm.go b/unum/swarm.go
new file mode 100644
index 0000000..87480d5
--- /dev/null
+++ b/unum/swarm.go
@@ -0,0 +1,121 @@
+// 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 (
+ "context"
+ "net"
+ "net/url"
+
+ "github.com/docker/docker/api/types"
+ "github.com/docker/docker/api/types/swarm"
+ "github.com/docker/docker/client"
+)
+
+type SwarmClient struct {
+ client *client.Client
+}
+
+func (c *SwarmClient) Init(aurl *url.URL) error {
+ var err error
+ c.client, err = client.NewEnvClient()
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (c *SwarmClient) GetInfo(labels map[string]string, networkLabels map[string]string) (*ClusterInfo, error) {
+ info := ClusterInfo{}
+
+ nodeState := make(map[string]bool)
+
+ services, err := c.client.ServiceList(context.Background(), types.ServiceListOptions{})
+ if err != nil {
+ log.Errorf("Error while quering services : %s", err.Error())
+ }
+
+ tasks, err := c.client.TaskList(context.Background(), types.TaskListOptions{})
+ if err != nil {
+ log.Error("Error while quering tasks")
+ }
+
+ nodes, err := c.client.NodeList(context.Background(), types.NodeListOptions{})
+ if err != nil {
+ log.Error("Error while quering nodes")
+ }
+
+ for _, node := range nodes {
+ nodeState[node.ID] = (node.Status.State == swarm.NodeStateReady)
+ log.Debugf("NODE STATUS: %s : %t", node.ID, nodeState[node.ID])
+ }
+
+ log.Debugf("SERVICE COUNT: %d", len(services))
+ for _, service := range services {
+ log.Debugf("NAME: %s, WANT: %s, HAVE: %s", service.Spec.Name, labels, service.Spec.Labels)
+ if labelMatch(service.Spec.Labels, labels) {
+ log.Debugf("MATCH: NAME: %s, EXPECTED: %d", service.Spec.Name, *service.Spec.Mode.Replicated.Replicas)
+ info.Expected += *service.Spec.Mode.Replicated.Replicas
+
+ for _, task := range tasks {
+ // If tasks is not associated with the matching serice, reject it
+ if task.ServiceID != service.ID {
+ continue
+ }
+
+ // If the task is not on a running node, reject it
+ if !nodeState[task.NodeID] || task.Status.State != swarm.TaskStateRunning {
+ log.Debugf("Found matching task '%s' [%s], on node '%s' [%t]",
+ task.ID, task.Status.State, task.NodeID, nodeState[task.NodeID])
+ continue
+ }
+ log.Debugf("Found matching task '%s' on node '%s', with a state of %s",
+ task.ID, task.NodeID, task.Status.State)
+
+ /*
+ * Need to discover an IP for the container to use for clustering
+ * purposes. The search is prioritized as:
+ * 1. If there is a labeled network then use the IP from that
+ * 2. If there is only a single network, besides the default ingress
+ * network, then use that
+ * 3. If there is only a single network, than use that.
+ * 4. Else ignore task as we can't deterine which network to
+ * use
+ */
+ found := false
+ count := len(networkLabels)
+ for _, network := range task.NetworksAttachments {
+ if count == 0 || labelMatch(network.Network.Spec.Labels, networkLabels) {
+ ip, _, _ := net.ParseCIDR(network.Addresses[0])
+ info.Nodes = append(info.Nodes, ip.String())
+ found = true
+ break
+ }
+ }
+ if !found {
+ log.Warnf("Unable to determine network (IP) information for task '%s'",
+ task.ID)
+ }
+ }
+ }
+ }
+
+ return &info, nil
+}
+
+func (c *SwarmClient) Close() error {
+ return nil
+}