8b0001d8d5c8e5768337f0012e350cba28e7db39
[onosfw.git] /
1 /*
2  * Copyright 2015 Open Networking Laboratory
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.onosproject.store.cluster.impl;
17
18 import com.google.common.collect.ImmutableSet;
19 import com.google.common.collect.Sets;
20 import org.apache.felix.scr.annotations.Activate;
21 import org.apache.felix.scr.annotations.Component;
22 import org.apache.felix.scr.annotations.Deactivate;
23 import org.apache.felix.scr.annotations.Service;
24 import org.onlab.packet.IpAddress;
25 import org.onosproject.cluster.ClusterDefinitionService;
26 import org.onosproject.cluster.ControllerNode;
27 import org.onosproject.cluster.DefaultControllerNode;
28 import org.onosproject.cluster.NodeId;
29 import org.onosproject.store.consistent.impl.DatabaseDefinition;
30 import org.onosproject.store.consistent.impl.DatabaseDefinitionStore;
31 import org.slf4j.Logger;
32
33 import java.io.File;
34 import java.io.IOException;
35 import java.net.InetAddress;
36 import java.net.NetworkInterface;
37 import java.net.SocketException;
38 import java.util.Enumeration;
39 import java.util.Set;
40 import java.util.stream.Collectors;
41
42 import static java.net.NetworkInterface.getNetworkInterfaces;
43 import static java.util.Collections.list;
44 import static org.onosproject.cluster.DefaultControllerNode.DEFAULT_PORT;
45 import static org.onosproject.store.consistent.impl.DatabaseManager.PARTITION_DEFINITION_FILE;
46 import static org.slf4j.LoggerFactory.getLogger;
47
48 /**
49  * Implementation of ClusterDefinitionService.
50  */
51 @Component(immediate = true)
52 @Service
53 public class ClusterDefinitionManager implements ClusterDefinitionService {
54
55     public static final String CLUSTER_DEFINITION_FILE = "../config/cluster.json";
56     private static final String ONOS_NIC = "ONOS_NIC";
57     private static final Logger log = getLogger(ClusterDefinitionManager.class);
58     private ControllerNode localNode;
59     private Set<ControllerNode> seedNodes;
60
61     @Activate
62     public void activate() {
63         File clusterDefinitionFile = new File(CLUSTER_DEFINITION_FILE);
64         ClusterDefinitionStore clusterDefinitionStore =
65                 new ClusterDefinitionStore(clusterDefinitionFile.getPath());
66
67         if (!clusterDefinitionFile.exists()) {
68             createDefaultClusterDefinition(clusterDefinitionStore);
69         }
70
71         try {
72             ClusterDefinition clusterDefinition = clusterDefinitionStore.read();
73             establishSelfIdentity(clusterDefinition);
74             seedNodes = ImmutableSet
75                     .copyOf(clusterDefinition.getNodes())
76                     .stream()
77                     .filter(n -> !localNode.id().equals(new NodeId(n.getId())))
78                     .map(n -> new DefaultControllerNode(new NodeId(n.getId()),
79                                                         IpAddress.valueOf(n.getIp()),
80                                                         n.getTcpPort()))
81                     .collect(Collectors.toSet());
82         } catch (IOException e) {
83             throw new IllegalStateException("Failed to read cluster definition.", e);
84         }
85
86         log.info("Started");
87     }
88
89     @Deactivate
90     public void deactivate() {
91         log.info("Stopped");
92     }
93
94     @Override
95     public ControllerNode localNode() {
96         return localNode;
97     }
98
99     @Override
100     public Set<ControllerNode> seedNodes() {
101         return seedNodes;
102     }
103
104     @Override
105     public void formCluster(Set<ControllerNode> nodes, String ipPrefix) {
106         try {
107             Set<NodeInfo> infos = Sets.newHashSet();
108             nodes.forEach(n -> infos.add(NodeInfo.from(n.id().toString(),
109                                                        n.ip().toString(),
110                                                        n.tcpPort())));
111
112             ClusterDefinition cdef = ClusterDefinition.from(infos, ipPrefix);
113             new ClusterDefinitionStore(CLUSTER_DEFINITION_FILE).write(cdef);
114
115             DatabaseDefinition ddef = DatabaseDefinition.from(infos);
116             new DatabaseDefinitionStore(PARTITION_DEFINITION_FILE).write(ddef);
117         } catch (IOException e) {
118             log.error("Unable to form cluster", e);
119         }
120     }
121
122     private IpAddress findLocalIp(ClusterDefinition clusterDefinition) throws SocketException {
123         Enumeration<NetworkInterface> interfaces =
124                 NetworkInterface.getNetworkInterfaces();
125         while (interfaces.hasMoreElements()) {
126             NetworkInterface iface = interfaces.nextElement();
127             Enumeration<InetAddress> inetAddresses = iface.getInetAddresses();
128             while (inetAddresses.hasMoreElements()) {
129                 IpAddress ip = IpAddress.valueOf(inetAddresses.nextElement());
130                 if (clusterDefinition.getNodes().stream()
131                         .map(NodeInfo::getIp)
132                         .map(IpAddress::valueOf)
133                         .anyMatch(nodeIp -> ip.equals(nodeIp))) {
134                     return ip;
135                 }
136             }
137         }
138         throw new IllegalStateException("Unable to determine local ip");
139     }
140
141     private void establishSelfIdentity(ClusterDefinition clusterDefinition) {
142         try {
143             IpAddress ip = findLocalIp(clusterDefinition);
144             localNode = new DefaultControllerNode(new NodeId(ip.toString()), ip);
145         } catch (SocketException e) {
146             throw new IllegalStateException("Cannot determine local IP", e);
147         }
148     }
149
150     private void createDefaultClusterDefinition(ClusterDefinitionStore store) {
151         // Assumes IPv4 is returned.
152         String ip = getSiteLocalAddress();
153         String ipPrefix = ip.replaceFirst("\\.[0-9]*$", ".*");
154         NodeInfo node = NodeInfo.from(ip, ip, DEFAULT_PORT);
155         try {
156             store.write(ClusterDefinition.from(ImmutableSet.of(node), ipPrefix));
157         } catch (IOException e) {
158             log.warn("Unable to write default cluster definition", e);
159         }
160     }
161
162     /**
163      * Returns the address that matches the IP prefix given in ONOS_NIC
164      * environment variable if one was specified, or the first site local
165      * address if one can be found or the loopback address otherwise.
166      *
167      * @return site-local address in string form
168      */
169     public static String getSiteLocalAddress() {
170         try {
171             String ipPrefix = System.getenv(ONOS_NIC);
172             for (NetworkInterface nif : list(getNetworkInterfaces())) {
173                 for (InetAddress address : list(nif.getInetAddresses())) {
174                     IpAddress ip = IpAddress.valueOf(address);
175                     if (ipPrefix == null && address.isSiteLocalAddress() ||
176                             ipPrefix != null && matchInterface(ip.toString(), ipPrefix)) {
177                         return ip.toString();
178                     }
179                 }
180             }
181         } catch (SocketException e) {
182             log.error("Unable to get network interfaces", e);
183         }
184
185         return IpAddress.valueOf(InetAddress.getLoopbackAddress()).toString();
186     }
187
188     // Indicates whether the specified interface address matches the given prefix.
189     // FIXME: Add a facility to IpPrefix to make this more robust
190     private static boolean matchInterface(String ip, String ipPrefix) {
191         String s = ipPrefix.replaceAll("\\.\\*", "");
192         return ip.startsWith(s);
193     }
194 }