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