blob: da4ec9945df1f7aa04d475cd8a14f920d83bfaae [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;
44import org.onosproject.cluster.NodeId;
45import org.onosproject.cluster.Partition;
Madan Jampaniab7e7cd2016-01-14 14:02:32 -080046import org.onosproject.cluster.PartitionId;
Madan Jampaniec1df022015-10-13 21:23:03 -070047import org.onosproject.store.AbstractStore;
48import org.onosproject.store.service.Versioned;
49import org.slf4j.Logger;
50
51import com.fasterxml.jackson.core.JsonGenerator;
52import com.fasterxml.jackson.core.JsonParser;
53import com.fasterxml.jackson.core.JsonProcessingException;
54import com.fasterxml.jackson.databind.DeserializationContext;
55import com.fasterxml.jackson.databind.JsonDeserializer;
56import com.fasterxml.jackson.databind.JsonNode;
57import com.fasterxml.jackson.databind.JsonSerializer;
58import com.fasterxml.jackson.databind.ObjectMapper;
59import com.fasterxml.jackson.databind.SerializerProvider;
60import com.fasterxml.jackson.databind.module.SimpleModule;
61import com.google.common.base.Throwables;
62import com.google.common.collect.Lists;
Madan Jampaniab7e7cd2016-01-14 14:02:32 -080063import com.google.common.collect.Sets;
Madan Jampaniec1df022015-10-13 21:23:03 -070064import com.google.common.io.Files;
65
66/**
67 * ClusterMetadataStore backed by a local file.
68 */
69@Component(immediate = true, enabled = true)
70@Service
71public class StaticClusterMetadataStore
72 extends AbstractStore<ClusterMetadataEvent, ClusterMetadataStoreDelegate>
73 implements ClusterMetadataStore {
74
75 private final Logger log = getLogger(getClass());
David K. Bainbridge34cc1022015-11-06 10:02:19 -080076
77 private static final String ONOS_IP = "ONOS_IP";
78 private static final String ONOS_INTERFACE = "ONOS_INTERFACE";
David K. Bainbridge03c1fa92015-11-06 13:44:10 -080079 private static final String ONOS_ALLOW_IPV6 = "ONOS_ALLOW_IPV6";
David K. Bainbridge34cc1022015-11-06 10:02:19 -080080 private static final String DEFAULT_ONOS_INTERFACE = "eth0";
Madan Jampaniec1df022015-10-13 21:23:03 -070081 private static final String CLUSTER_METADATA_FILE = "../config/cluster.json";
82 private static final int DEFAULT_ONOS_PORT = 9876;
83 private final File metadataFile = new File(CLUSTER_METADATA_FILE);
84 private AtomicReference<ClusterMetadata> metadata = new AtomicReference<>();
85 private ObjectMapper mapper;
86 private long version;
87
88 @Activate
89 public void activate() {
90 mapper = new ObjectMapper();
91 SimpleModule module = new SimpleModule();
92 module.addSerializer(NodeId.class, new NodeIdSerializer());
93 module.addDeserializer(NodeId.class, new NodeIdDeserializer());
94 module.addSerializer(ControllerNode.class, new ControllerNodeSerializer());
95 module.addDeserializer(ControllerNode.class, new ControllerNodeDeserializer());
Madan Jampaniab7e7cd2016-01-14 14:02:32 -080096 module.addDeserializer(Partition.class, new PartitionDeserializer());
Madan Jampaniec1df022015-10-13 21:23:03 -070097 mapper.registerModule(module);
98 File metadataFile = new File(CLUSTER_METADATA_FILE);
99 if (metadataFile.exists()) {
100 try {
101 metadata.set(mapper.readValue(metadataFile, ClusterMetadata.class));
102 version = metadataFile.lastModified();
103 } catch (IOException e) {
104 Throwables.propagate(e);
105 }
106 } else {
107 String localIp = getSiteLocalAddress();
108 ControllerNode localNode =
109 new DefaultControllerNode(new NodeId(localIp), IpAddress.valueOf(localIp), DEFAULT_ONOS_PORT);
Madan Jampaniab7e7cd2016-01-14 14:02:32 -0800110 Partition defaultPartition = new Partition() {
111 @Override
112 public PartitionId getId() {
113 return PartitionId.from(1);
114 }
115
116 @Override
117 public Collection<NodeId> getMembers() {
118 return Sets.newHashSet(localNode.id());
119 }
120 };
Madan Jampaniec1df022015-10-13 21:23:03 -0700121 metadata.set(ClusterMetadata.builder()
122 .withName("default")
123 .withControllerNodes(Arrays.asList(localNode))
Madan Jampaniab7e7cd2016-01-14 14:02:32 -0800124 .withPartitions(Lists.newArrayList(defaultPartition))
Madan Jampaniec1df022015-10-13 21:23:03 -0700125 .build());
126 version = System.currentTimeMillis();
127 }
128 log.info("Started");
129 }
130
131 @Deactivate
132 public void deactivate() {
133 log.info("Stopped");
134 }
135
136 @Override
137 public void setDelegate(ClusterMetadataStoreDelegate delegate) {
138 checkNotNull(delegate, "Delegate cannot be null");
139 this.delegate = delegate;
140 }
141
142 @Override
143 public void unsetDelegate(ClusterMetadataStoreDelegate delegate) {
144 this.delegate = null;
145 }
146
147 @Override
148 public boolean hasDelegate() {
149 return this.delegate != null;
150 }
151
152 @Override
153 public Versioned<ClusterMetadata> getClusterMetadata() {
154 return new Versioned<>(metadata.get(), version);
155 }
156
157 @Override
158 public void setClusterMetadata(ClusterMetadata metadata) {
159 checkNotNull(metadata);
160 try {
161 Files.createParentDirs(metadataFile);
162 mapper.writeValue(metadataFile, metadata);
163 this.metadata.set(metadata);
164 } catch (IOException e) {
165 Throwables.propagate(e);
166 }
167 }
168
169 @Override
Madan Jampaniab7e7cd2016-01-14 14:02:32 -0800170 public void addActivePartitionMember(PartitionId partitionId, NodeId nodeId) {
Madan Jampaniec1df022015-10-13 21:23:03 -0700171 throw new UnsupportedOperationException();
172 }
173
174 @Override
Madan Jampaniab7e7cd2016-01-14 14:02:32 -0800175 public void removeActivePartitionMember(PartitionId partitionId, NodeId nodeId) {
Madan Jampaniec1df022015-10-13 21:23:03 -0700176 throw new UnsupportedOperationException();
177 }
178
179 @Override
Madan Jampaniab7e7cd2016-01-14 14:02:32 -0800180 public Collection<NodeId> getActivePartitionMembers(PartitionId partitionId) {
Madan Jampaniec1df022015-10-13 21:23:03 -0700181 return metadata.get().getPartitions()
182 .stream()
Madan Jampaniab7e7cd2016-01-14 14:02:32 -0800183 .filter(r -> r.getId().equals(partitionId))
Madan Jampaniec1df022015-10-13 21:23:03 -0700184 .findFirst()
185 .map(r -> r.getMembers())
186 .orElse(null);
187 }
188
Madan Jampaniab7e7cd2016-01-14 14:02:32 -0800189 private static class PartitionDeserializer extends JsonDeserializer<Partition> {
190 @Override
191 public Partition deserialize(JsonParser jp, DeserializationContext ctxt)
192 throws IOException, JsonProcessingException {
193 return jp.readValueAs(DefaultPartition.class);
194 }
195 }
196
Madan Jampaniec1df022015-10-13 21:23:03 -0700197 private static class ControllerNodeSerializer extends JsonSerializer<ControllerNode> {
198 @Override
199 public void serialize(ControllerNode node, JsonGenerator jgen, SerializerProvider provider)
200 throws IOException, JsonProcessingException {
201 jgen.writeStartObject();
202 jgen.writeStringField("id", node.id().toString());
203 jgen.writeStringField("ip", node.ip().toString());
204 jgen.writeNumberField("port", node.tcpPort());
205 jgen.writeEndObject();
206 }
207 }
208
209 private static class ControllerNodeDeserializer extends JsonDeserializer<ControllerNode> {
210 @Override
211 public ControllerNode deserialize(JsonParser jp, DeserializationContext ctxt)
212 throws IOException, JsonProcessingException {
213 JsonNode node = jp.getCodec().readTree(jp);
214 NodeId nodeId = new NodeId(node.get("id").textValue());
215 IpAddress ip = IpAddress.valueOf(node.get("ip").textValue());
216 int port = node.get("port").asInt();
217 return new DefaultControllerNode(nodeId, ip, port);
218 }
219 }
220
221 private static class NodeIdSerializer extends JsonSerializer<NodeId> {
222 @Override
223 public void serialize(NodeId nodeId, JsonGenerator jgen, SerializerProvider provider)
224 throws IOException, JsonProcessingException {
225 jgen.writeString(nodeId.toString());
226 }
227 }
228
229 private class NodeIdDeserializer extends JsonDeserializer<NodeId> {
230 @Override
231 public NodeId deserialize(JsonParser jp, DeserializationContext ctxt)
232 throws IOException, JsonProcessingException {
233 JsonNode node = jp.getCodec().readTree(jp);
234 return new NodeId(node.asText());
235 }
236 }
237
238
239 private static String getSiteLocalAddress() {
David K. Bainbridge34cc1022015-11-06 10:02:19 -0800240
241 /*
242 * If the IP ONOS should use is set via the environment variable we will assume it is valid and should be used.
243 * Setting the IP address takes presidence over setting the interface via the environment.
244 */
245 String useOnosIp = System.getenv(ONOS_IP);
246 if (useOnosIp != null) {
247 return useOnosIp;
248 }
249
250 // Read environment variables for IP interface information or set to default
251 String useOnosInterface = System.getenv(ONOS_INTERFACE);
252 if (useOnosInterface == null) {
253 useOnosInterface = DEFAULT_ONOS_INTERFACE;
254 }
255
David K. Bainbridge03c1fa92015-11-06 13:44:10 -0800256 // Capture if they want to limit IP address selection to only IPv4 (default).
257 boolean allowIPv6 = (System.getenv(ONOS_ALLOW_IPV6) != null);
258
Madan Jampaniec1df022015-10-13 21:23:03 -0700259 Function<NetworkInterface, IpAddress> ipLookup = nif -> {
David K. Bainbridge03c1fa92015-11-06 13:44:10 -0800260 IpAddress fallback = null;
261
262 // nif can be null if the interface name specified doesn't exist on the node's host
263 if (nif != null) {
264 for (InetAddress address : Collections.list(nif.getInetAddresses())) {
265 if (address.isSiteLocalAddress() && (allowIPv6 || address instanceof Inet4Address)) {
266 return IpAddress.valueOf(address);
267 }
268 if (fallback == null && !address.isLoopbackAddress() && !address.isMulticastAddress()
269 && (allowIPv6 || address instanceof Inet4Address)) {
270 fallback = IpAddress.valueOf(address);
271 }
Madan Jampaniec1df022015-10-13 21:23:03 -0700272 }
273 }
David K. Bainbridge03c1fa92015-11-06 13:44:10 -0800274 return fallback;
Madan Jampaniec1df022015-10-13 21:23:03 -0700275 };
276 try {
David K. Bainbridge34cc1022015-11-06 10:02:19 -0800277 IpAddress ip = ipLookup.apply(NetworkInterface.getByName(useOnosInterface));
Madan Jampaniec1df022015-10-13 21:23:03 -0700278 if (ip != null) {
279 return ip.toString();
280 }
281 for (NetworkInterface nif : Collections.list(getNetworkInterfaces())) {
David K. Bainbridge03c1fa92015-11-06 13:44:10 -0800282 if (!nif.getName().equals(useOnosInterface)) {
283 ip = ipLookup.apply(nif);
284 if (ip != null) {
285 return ip.toString();
286 }
Madan Jampaniec1df022015-10-13 21:23:03 -0700287 }
288 }
289 } catch (Exception e) {
290 throw new IllegalStateException("Unable to get network interfaces", e);
291 }
David K. Bainbridge03c1fa92015-11-06 13:44:10 -0800292
Madan Jampaniec1df022015-10-13 21:23:03 -0700293 return IpAddress.valueOf(InetAddress.getLoopbackAddress()).toString();
294 }
David K. Bainbridge34cc1022015-11-06 10:02:19 -0800295}