blob: 06bdc3f302af8fb4ba089fec81ac2c97290441b1 [file] [log] [blame]
Madan Jampaniab7e7cd2016-01-14 14:02:32 -08001/*
2 * Copyright 2015-2016 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 */
Madan Jampaniec1df022015-10-13 21:23:03 -070016package org.onosproject.store.cluster.impl;
17
18import static com.google.common.base.Preconditions.checkNotNull;
19import static java.net.NetworkInterface.getNetworkInterfaces;
20import static org.slf4j.LoggerFactory.getLogger;
21
22import java.io.File;
23import java.io.IOException;
24import java.net.InetAddress;
David K. Bainbridge03c1fa92015-11-06 13:44:10 -080025import java.net.Inet4Address;
Madan Jampaniec1df022015-10-13 21:23:03 -070026import java.net.NetworkInterface;
27import java.util.Arrays;
28import java.util.Collection;
29import java.util.Collections;
30import java.util.concurrent.atomic.AtomicReference;
31import java.util.function.Function;
32
33import org.apache.felix.scr.annotations.Activate;
34import org.apache.felix.scr.annotations.Component;
35import org.apache.felix.scr.annotations.Deactivate;
36import org.apache.felix.scr.annotations.Service;
37import org.onlab.packet.IpAddress;
38import org.onosproject.cluster.ClusterMetadata;
39import org.onosproject.cluster.ClusterMetadataEvent;
40import org.onosproject.cluster.ClusterMetadataStore;
41import org.onosproject.cluster.ClusterMetadataStoreDelegate;
42import org.onosproject.cluster.ControllerNode;
43import org.onosproject.cluster.DefaultControllerNode;
Madan Jampani8474fdd2016-01-19 09:56:28 -080044import org.onosproject.cluster.DefaultPartition;
Madan Jampaniec1df022015-10-13 21:23:03 -070045import org.onosproject.cluster.NodeId;
46import org.onosproject.cluster.Partition;
Madan Jampaniab7e7cd2016-01-14 14:02:32 -080047import org.onosproject.cluster.PartitionId;
Madan Jampaniec1df022015-10-13 21:23:03 -070048import org.onosproject.store.AbstractStore;
49import org.onosproject.store.service.Versioned;
50import org.slf4j.Logger;
51
52import com.fasterxml.jackson.core.JsonGenerator;
53import com.fasterxml.jackson.core.JsonParser;
54import com.fasterxml.jackson.core.JsonProcessingException;
55import com.fasterxml.jackson.databind.DeserializationContext;
56import com.fasterxml.jackson.databind.JsonDeserializer;
57import com.fasterxml.jackson.databind.JsonNode;
58import com.fasterxml.jackson.databind.JsonSerializer;
59import com.fasterxml.jackson.databind.ObjectMapper;
60import com.fasterxml.jackson.databind.SerializerProvider;
61import com.fasterxml.jackson.databind.module.SimpleModule;
62import com.google.common.base.Throwables;
63import com.google.common.collect.Lists;
Madan Jampaniab7e7cd2016-01-14 14:02:32 -080064import com.google.common.collect.Sets;
Madan Jampaniec1df022015-10-13 21:23:03 -070065import com.google.common.io.Files;
66
67/**
68 * ClusterMetadataStore backed by a local file.
69 */
70@Component(immediate = true, enabled = true)
71@Service
72public class StaticClusterMetadataStore
73 extends AbstractStore<ClusterMetadataEvent, ClusterMetadataStoreDelegate>
74 implements ClusterMetadataStore {
75
76 private final Logger log = getLogger(getClass());
David K. Bainbridge34cc1022015-11-06 10:02:19 -080077
78 private static final String ONOS_IP = "ONOS_IP";
79 private static final String ONOS_INTERFACE = "ONOS_INTERFACE";
David K. Bainbridge03c1fa92015-11-06 13:44:10 -080080 private static final String ONOS_ALLOW_IPV6 = "ONOS_ALLOW_IPV6";
David K. Bainbridge34cc1022015-11-06 10:02:19 -080081 private static final String DEFAULT_ONOS_INTERFACE = "eth0";
Madan Jampaniec1df022015-10-13 21:23:03 -070082 private static final String CLUSTER_METADATA_FILE = "../config/cluster.json";
83 private static final int DEFAULT_ONOS_PORT = 9876;
84 private final File metadataFile = new File(CLUSTER_METADATA_FILE);
85 private AtomicReference<ClusterMetadata> metadata = new AtomicReference<>();
86 private ObjectMapper mapper;
87 private long version;
88
89 @Activate
90 public void activate() {
91 mapper = new ObjectMapper();
92 SimpleModule module = new SimpleModule();
93 module.addSerializer(NodeId.class, new NodeIdSerializer());
94 module.addDeserializer(NodeId.class, new NodeIdDeserializer());
95 module.addSerializer(ControllerNode.class, new ControllerNodeSerializer());
96 module.addDeserializer(ControllerNode.class, new ControllerNodeDeserializer());
Madan Jampaniab7e7cd2016-01-14 14:02:32 -080097 module.addDeserializer(Partition.class, new PartitionDeserializer());
Madan Jampani8474fdd2016-01-19 09:56:28 -080098 module.addSerializer(PartitionId.class, new PartitionIdSerializer());
99 module.addDeserializer(PartitionId.class, new PartitionIdDeserializer());
Madan Jampaniec1df022015-10-13 21:23:03 -0700100 mapper.registerModule(module);
101 File metadataFile = new File(CLUSTER_METADATA_FILE);
102 if (metadataFile.exists()) {
103 try {
104 metadata.set(mapper.readValue(metadataFile, ClusterMetadata.class));
105 version = metadataFile.lastModified();
106 } catch (IOException e) {
107 Throwables.propagate(e);
108 }
109 } else {
110 String localIp = getSiteLocalAddress();
111 ControllerNode localNode =
112 new DefaultControllerNode(new NodeId(localIp), IpAddress.valueOf(localIp), DEFAULT_ONOS_PORT);
Madan Jampani8474fdd2016-01-19 09:56:28 -0800113 // p0 partition
114 Partition basePartition = new DefaultPartition(PartitionId.from(0), Sets.newHashSet(localNode.id()));
115 // p1 partition
116 Partition extendedPartition = new DefaultPartition(PartitionId.from(1), Sets.newHashSet(localNode.id()));
Madan Jampaniec1df022015-10-13 21:23:03 -0700117 metadata.set(ClusterMetadata.builder()
118 .withName("default")
119 .withControllerNodes(Arrays.asList(localNode))
Madan Jampani8474fdd2016-01-19 09:56:28 -0800120 .withPartitions(Lists.newArrayList(basePartition, extendedPartition))
Madan Jampaniec1df022015-10-13 21:23:03 -0700121 .build());
122 version = System.currentTimeMillis();
123 }
124 log.info("Started");
125 }
126
127 @Deactivate
128 public void deactivate() {
129 log.info("Stopped");
130 }
131
132 @Override
133 public void setDelegate(ClusterMetadataStoreDelegate delegate) {
134 checkNotNull(delegate, "Delegate cannot be null");
135 this.delegate = delegate;
136 }
137
138 @Override
139 public void unsetDelegate(ClusterMetadataStoreDelegate delegate) {
140 this.delegate = null;
141 }
142
143 @Override
144 public boolean hasDelegate() {
145 return this.delegate != null;
146 }
147
148 @Override
149 public Versioned<ClusterMetadata> getClusterMetadata() {
150 return new Versioned<>(metadata.get(), version);
151 }
152
153 @Override
154 public void setClusterMetadata(ClusterMetadata metadata) {
155 checkNotNull(metadata);
156 try {
157 Files.createParentDirs(metadataFile);
158 mapper.writeValue(metadataFile, metadata);
159 this.metadata.set(metadata);
160 } catch (IOException e) {
161 Throwables.propagate(e);
162 }
163 }
164
165 @Override
Madan Jampaniab7e7cd2016-01-14 14:02:32 -0800166 public void addActivePartitionMember(PartitionId partitionId, NodeId nodeId) {
Madan Jampaniec1df022015-10-13 21:23:03 -0700167 throw new UnsupportedOperationException();
168 }
169
170 @Override
Madan Jampaniab7e7cd2016-01-14 14:02:32 -0800171 public void removeActivePartitionMember(PartitionId partitionId, NodeId nodeId) {
Madan Jampaniec1df022015-10-13 21:23:03 -0700172 throw new UnsupportedOperationException();
173 }
174
175 @Override
Madan Jampaniab7e7cd2016-01-14 14:02:32 -0800176 public Collection<NodeId> getActivePartitionMembers(PartitionId partitionId) {
Madan Jampaniec1df022015-10-13 21:23:03 -0700177 return metadata.get().getPartitions()
178 .stream()
Madan Jampaniab7e7cd2016-01-14 14:02:32 -0800179 .filter(r -> r.getId().equals(partitionId))
Madan Jampaniec1df022015-10-13 21:23:03 -0700180 .findFirst()
181 .map(r -> r.getMembers())
182 .orElse(null);
183 }
184
Madan Jampaniab7e7cd2016-01-14 14:02:32 -0800185 private static class PartitionDeserializer extends JsonDeserializer<Partition> {
186 @Override
187 public Partition deserialize(JsonParser jp, DeserializationContext ctxt)
188 throws IOException, JsonProcessingException {
189 return jp.readValueAs(DefaultPartition.class);
190 }
191 }
192
Madan Jampani8474fdd2016-01-19 09:56:28 -0800193 private static class PartitionIdSerializer extends JsonSerializer<PartitionId> {
194 @Override
195 public void serialize(PartitionId partitionId, JsonGenerator jgen, SerializerProvider provider)
196 throws IOException, JsonProcessingException {
197 jgen.writeNumber(partitionId.asInt());
198 }
199 }
200
201 private class PartitionIdDeserializer extends JsonDeserializer<PartitionId> {
202 @Override
203 public PartitionId deserialize(JsonParser jp, DeserializationContext ctxt)
204 throws IOException, JsonProcessingException {
205 JsonNode node = jp.getCodec().readTree(jp);
206 return new PartitionId(node.asInt());
207 }
208 }
209
Madan Jampaniec1df022015-10-13 21:23:03 -0700210 private static class ControllerNodeSerializer extends JsonSerializer<ControllerNode> {
211 @Override
212 public void serialize(ControllerNode node, JsonGenerator jgen, SerializerProvider provider)
213 throws IOException, JsonProcessingException {
214 jgen.writeStartObject();
215 jgen.writeStringField("id", node.id().toString());
216 jgen.writeStringField("ip", node.ip().toString());
217 jgen.writeNumberField("port", node.tcpPort());
218 jgen.writeEndObject();
219 }
220 }
221
222 private static class ControllerNodeDeserializer extends JsonDeserializer<ControllerNode> {
223 @Override
224 public ControllerNode deserialize(JsonParser jp, DeserializationContext ctxt)
225 throws IOException, JsonProcessingException {
226 JsonNode node = jp.getCodec().readTree(jp);
227 NodeId nodeId = new NodeId(node.get("id").textValue());
228 IpAddress ip = IpAddress.valueOf(node.get("ip").textValue());
229 int port = node.get("port").asInt();
230 return new DefaultControllerNode(nodeId, ip, port);
231 }
232 }
233
234 private static class NodeIdSerializer extends JsonSerializer<NodeId> {
235 @Override
236 public void serialize(NodeId nodeId, JsonGenerator jgen, SerializerProvider provider)
237 throws IOException, JsonProcessingException {
238 jgen.writeString(nodeId.toString());
239 }
240 }
241
242 private class NodeIdDeserializer extends JsonDeserializer<NodeId> {
243 @Override
244 public NodeId deserialize(JsonParser jp, DeserializationContext ctxt)
245 throws IOException, JsonProcessingException {
246 JsonNode node = jp.getCodec().readTree(jp);
247 return new NodeId(node.asText());
248 }
249 }
250
251
252 private static String getSiteLocalAddress() {
David K. Bainbridge34cc1022015-11-06 10:02:19 -0800253
254 /*
255 * If the IP ONOS should use is set via the environment variable we will assume it is valid and should be used.
256 * Setting the IP address takes presidence over setting the interface via the environment.
257 */
258 String useOnosIp = System.getenv(ONOS_IP);
259 if (useOnosIp != null) {
260 return useOnosIp;
261 }
262
263 // Read environment variables for IP interface information or set to default
264 String useOnosInterface = System.getenv(ONOS_INTERFACE);
265 if (useOnosInterface == null) {
266 useOnosInterface = DEFAULT_ONOS_INTERFACE;
267 }
268
David K. Bainbridge03c1fa92015-11-06 13:44:10 -0800269 // Capture if they want to limit IP address selection to only IPv4 (default).
270 boolean allowIPv6 = (System.getenv(ONOS_ALLOW_IPV6) != null);
271
Madan Jampaniec1df022015-10-13 21:23:03 -0700272 Function<NetworkInterface, IpAddress> ipLookup = nif -> {
David K. Bainbridge03c1fa92015-11-06 13:44:10 -0800273 IpAddress fallback = null;
274
275 // nif can be null if the interface name specified doesn't exist on the node's host
276 if (nif != null) {
277 for (InetAddress address : Collections.list(nif.getInetAddresses())) {
278 if (address.isSiteLocalAddress() && (allowIPv6 || address instanceof Inet4Address)) {
279 return IpAddress.valueOf(address);
280 }
281 if (fallback == null && !address.isLoopbackAddress() && !address.isMulticastAddress()
282 && (allowIPv6 || address instanceof Inet4Address)) {
283 fallback = IpAddress.valueOf(address);
284 }
Madan Jampaniec1df022015-10-13 21:23:03 -0700285 }
286 }
David K. Bainbridge03c1fa92015-11-06 13:44:10 -0800287 return fallback;
Madan Jampaniec1df022015-10-13 21:23:03 -0700288 };
289 try {
David K. Bainbridge34cc1022015-11-06 10:02:19 -0800290 IpAddress ip = ipLookup.apply(NetworkInterface.getByName(useOnosInterface));
Madan Jampaniec1df022015-10-13 21:23:03 -0700291 if (ip != null) {
292 return ip.toString();
293 }
294 for (NetworkInterface nif : Collections.list(getNetworkInterfaces())) {
David K. Bainbridge03c1fa92015-11-06 13:44:10 -0800295 if (!nif.getName().equals(useOnosInterface)) {
296 ip = ipLookup.apply(nif);
297 if (ip != null) {
298 return ip.toString();
299 }
Madan Jampaniec1df022015-10-13 21:23:03 -0700300 }
301 }
302 } catch (Exception e) {
303 throw new IllegalStateException("Unable to get network interfaces", e);
304 }
David K. Bainbridge03c1fa92015-11-06 13:44:10 -0800305
Madan Jampaniec1df022015-10-13 21:23:03 -0700306 return IpAddress.valueOf(InetAddress.getLoopbackAddress()).toString();
307 }
David K. Bainbridge34cc1022015-11-06 10:02:19 -0800308}