blob: 3cd992bb8aeae54a085fcb1b26d1ef94c2c1aaa6 [file] [log] [blame]
Madan Jampaniec1df022015-10-13 21:23:03 -07001package org.onosproject.store.cluster.impl;
2
3import static com.google.common.base.Preconditions.checkNotNull;
4import static java.net.NetworkInterface.getNetworkInterfaces;
5import static org.slf4j.LoggerFactory.getLogger;
6
7import java.io.File;
8import java.io.IOException;
9import java.net.InetAddress;
David K. Bainbridge03c1fa92015-11-06 13:44:10 -080010import java.net.Inet4Address;
Madan Jampaniec1df022015-10-13 21:23:03 -070011import java.net.NetworkInterface;
12import java.util.Arrays;
13import java.util.Collection;
14import java.util.Collections;
15import java.util.concurrent.atomic.AtomicReference;
16import java.util.function.Function;
17
18import org.apache.felix.scr.annotations.Activate;
19import org.apache.felix.scr.annotations.Component;
20import org.apache.felix.scr.annotations.Deactivate;
21import org.apache.felix.scr.annotations.Service;
22import org.onlab.packet.IpAddress;
23import org.onosproject.cluster.ClusterMetadata;
24import org.onosproject.cluster.ClusterMetadataEvent;
25import org.onosproject.cluster.ClusterMetadataStore;
26import org.onosproject.cluster.ClusterMetadataStoreDelegate;
27import org.onosproject.cluster.ControllerNode;
28import org.onosproject.cluster.DefaultControllerNode;
29import org.onosproject.cluster.NodeId;
30import org.onosproject.cluster.Partition;
31import org.onosproject.store.AbstractStore;
32import org.onosproject.store.service.Versioned;
33import org.slf4j.Logger;
34
35import com.fasterxml.jackson.core.JsonGenerator;
36import com.fasterxml.jackson.core.JsonParser;
37import com.fasterxml.jackson.core.JsonProcessingException;
38import com.fasterxml.jackson.databind.DeserializationContext;
39import com.fasterxml.jackson.databind.JsonDeserializer;
40import com.fasterxml.jackson.databind.JsonNode;
41import com.fasterxml.jackson.databind.JsonSerializer;
42import com.fasterxml.jackson.databind.ObjectMapper;
43import com.fasterxml.jackson.databind.SerializerProvider;
44import com.fasterxml.jackson.databind.module.SimpleModule;
45import com.google.common.base.Throwables;
46import com.google.common.collect.Lists;
47import com.google.common.io.Files;
48
49/**
50 * ClusterMetadataStore backed by a local file.
51 */
52@Component(immediate = true, enabled = true)
53@Service
54public class StaticClusterMetadataStore
55 extends AbstractStore<ClusterMetadataEvent, ClusterMetadataStoreDelegate>
56 implements ClusterMetadataStore {
57
58 private final Logger log = getLogger(getClass());
David K. Bainbridge34cc1022015-11-06 10:02:19 -080059
60 private static final String ONOS_IP = "ONOS_IP";
61 private static final String ONOS_INTERFACE = "ONOS_INTERFACE";
David K. Bainbridge03c1fa92015-11-06 13:44:10 -080062 private static final String ONOS_ALLOW_IPV6 = "ONOS_ALLOW_IPV6";
David K. Bainbridge34cc1022015-11-06 10:02:19 -080063 private static final String DEFAULT_ONOS_INTERFACE = "eth0";
Madan Jampaniec1df022015-10-13 21:23:03 -070064 private static final String CLUSTER_METADATA_FILE = "../config/cluster.json";
65 private static final int DEFAULT_ONOS_PORT = 9876;
66 private final File metadataFile = new File(CLUSTER_METADATA_FILE);
67 private AtomicReference<ClusterMetadata> metadata = new AtomicReference<>();
68 private ObjectMapper mapper;
69 private long version;
70
71 @Activate
72 public void activate() {
73 mapper = new ObjectMapper();
74 SimpleModule module = new SimpleModule();
75 module.addSerializer(NodeId.class, new NodeIdSerializer());
76 module.addDeserializer(NodeId.class, new NodeIdDeserializer());
77 module.addSerializer(ControllerNode.class, new ControllerNodeSerializer());
78 module.addDeserializer(ControllerNode.class, new ControllerNodeDeserializer());
79 mapper.registerModule(module);
80 File metadataFile = new File(CLUSTER_METADATA_FILE);
81 if (metadataFile.exists()) {
82 try {
83 metadata.set(mapper.readValue(metadataFile, ClusterMetadata.class));
84 version = metadataFile.lastModified();
85 } catch (IOException e) {
86 Throwables.propagate(e);
87 }
88 } else {
89 String localIp = getSiteLocalAddress();
90 ControllerNode localNode =
91 new DefaultControllerNode(new NodeId(localIp), IpAddress.valueOf(localIp), DEFAULT_ONOS_PORT);
92 metadata.set(ClusterMetadata.builder()
93 .withName("default")
94 .withControllerNodes(Arrays.asList(localNode))
95 .withPartitions(Lists.newArrayList(new Partition("p1", Lists.newArrayList(localNode.id()))))
96 .build());
97 version = System.currentTimeMillis();
98 }
99 log.info("Started");
100 }
101
102 @Deactivate
103 public void deactivate() {
104 log.info("Stopped");
105 }
106
107 @Override
108 public void setDelegate(ClusterMetadataStoreDelegate delegate) {
109 checkNotNull(delegate, "Delegate cannot be null");
110 this.delegate = delegate;
111 }
112
113 @Override
114 public void unsetDelegate(ClusterMetadataStoreDelegate delegate) {
115 this.delegate = null;
116 }
117
118 @Override
119 public boolean hasDelegate() {
120 return this.delegate != null;
121 }
122
123 @Override
124 public Versioned<ClusterMetadata> getClusterMetadata() {
125 return new Versioned<>(metadata.get(), version);
126 }
127
128 @Override
129 public void setClusterMetadata(ClusterMetadata metadata) {
130 checkNotNull(metadata);
131 try {
132 Files.createParentDirs(metadataFile);
133 mapper.writeValue(metadataFile, metadata);
134 this.metadata.set(metadata);
135 } catch (IOException e) {
136 Throwables.propagate(e);
137 }
138 }
139
140 @Override
141 public void setActiveReplica(String partitionId, NodeId nodeId) {
142 throw new UnsupportedOperationException();
143 }
144
145 @Override
146 public void unsetActiveReplica(String partitionId, NodeId nodeId) {
147 throw new UnsupportedOperationException();
148 }
149
150 @Override
151 public Collection<NodeId> getActiveReplicas(String partitionId) {
152 return metadata.get().getPartitions()
153 .stream()
154 .filter(r -> r.getName().equals(partitionId))
155 .findFirst()
156 .map(r -> r.getMembers())
157 .orElse(null);
158 }
159
160 private static class ControllerNodeSerializer extends JsonSerializer<ControllerNode> {
161 @Override
162 public void serialize(ControllerNode node, JsonGenerator jgen, SerializerProvider provider)
163 throws IOException, JsonProcessingException {
164 jgen.writeStartObject();
165 jgen.writeStringField("id", node.id().toString());
166 jgen.writeStringField("ip", node.ip().toString());
167 jgen.writeNumberField("port", node.tcpPort());
168 jgen.writeEndObject();
169 }
170 }
171
172 private static class ControllerNodeDeserializer extends JsonDeserializer<ControllerNode> {
173 @Override
174 public ControllerNode deserialize(JsonParser jp, DeserializationContext ctxt)
175 throws IOException, JsonProcessingException {
176 JsonNode node = jp.getCodec().readTree(jp);
177 NodeId nodeId = new NodeId(node.get("id").textValue());
178 IpAddress ip = IpAddress.valueOf(node.get("ip").textValue());
179 int port = node.get("port").asInt();
180 return new DefaultControllerNode(nodeId, ip, port);
181 }
182 }
183
184 private static class NodeIdSerializer extends JsonSerializer<NodeId> {
185 @Override
186 public void serialize(NodeId nodeId, JsonGenerator jgen, SerializerProvider provider)
187 throws IOException, JsonProcessingException {
188 jgen.writeString(nodeId.toString());
189 }
190 }
191
192 private class NodeIdDeserializer extends JsonDeserializer<NodeId> {
193 @Override
194 public NodeId deserialize(JsonParser jp, DeserializationContext ctxt)
195 throws IOException, JsonProcessingException {
196 JsonNode node = jp.getCodec().readTree(jp);
197 return new NodeId(node.asText());
198 }
199 }
200
201
202 private static String getSiteLocalAddress() {
David K. Bainbridge34cc1022015-11-06 10:02:19 -0800203
204 /*
205 * If the IP ONOS should use is set via the environment variable we will assume it is valid and should be used.
206 * Setting the IP address takes presidence over setting the interface via the environment.
207 */
208 String useOnosIp = System.getenv(ONOS_IP);
209 if (useOnosIp != null) {
210 return useOnosIp;
211 }
212
213 // Read environment variables for IP interface information or set to default
214 String useOnosInterface = System.getenv(ONOS_INTERFACE);
215 if (useOnosInterface == null) {
216 useOnosInterface = DEFAULT_ONOS_INTERFACE;
217 }
218
David K. Bainbridge03c1fa92015-11-06 13:44:10 -0800219 // Capture if they want to limit IP address selection to only IPv4 (default).
220 boolean allowIPv6 = (System.getenv(ONOS_ALLOW_IPV6) != null);
221
Madan Jampaniec1df022015-10-13 21:23:03 -0700222 Function<NetworkInterface, IpAddress> ipLookup = nif -> {
David K. Bainbridge03c1fa92015-11-06 13:44:10 -0800223 IpAddress fallback = null;
224
225 // nif can be null if the interface name specified doesn't exist on the node's host
226 if (nif != null) {
227 for (InetAddress address : Collections.list(nif.getInetAddresses())) {
228 if (address.isSiteLocalAddress() && (allowIPv6 || address instanceof Inet4Address)) {
229 return IpAddress.valueOf(address);
230 }
231 if (fallback == null && !address.isLoopbackAddress() && !address.isMulticastAddress()
232 && (allowIPv6 || address instanceof Inet4Address)) {
233 fallback = IpAddress.valueOf(address);
234 }
Madan Jampaniec1df022015-10-13 21:23:03 -0700235 }
236 }
David K. Bainbridge03c1fa92015-11-06 13:44:10 -0800237 return fallback;
Madan Jampaniec1df022015-10-13 21:23:03 -0700238 };
239 try {
David K. Bainbridge34cc1022015-11-06 10:02:19 -0800240 IpAddress ip = ipLookup.apply(NetworkInterface.getByName(useOnosInterface));
Madan Jampaniec1df022015-10-13 21:23:03 -0700241 if (ip != null) {
242 return ip.toString();
243 }
244 for (NetworkInterface nif : Collections.list(getNetworkInterfaces())) {
David K. Bainbridge03c1fa92015-11-06 13:44:10 -0800245 if (!nif.getName().equals(useOnosInterface)) {
246 ip = ipLookup.apply(nif);
247 if (ip != null) {
248 return ip.toString();
249 }
Madan Jampaniec1df022015-10-13 21:23:03 -0700250 }
251 }
252 } catch (Exception e) {
253 throw new IllegalStateException("Unable to get network interfaces", e);
254 }
David K. Bainbridge03c1fa92015-11-06 13:44:10 -0800255
Madan Jampaniec1df022015-10-13 21:23:03 -0700256 return IpAddress.valueOf(InetAddress.getLoopbackAddress()).toString();
257 }
David K. Bainbridge34cc1022015-11-06 10:02:19 -0800258}