1 package org.onosproject.store.cluster.impl;
 
   3 import static com.google.common.base.Preconditions.checkNotNull;
 
   4 import static java.net.NetworkInterface.getNetworkInterfaces;
 
   5 import static org.slf4j.LoggerFactory.getLogger;
 
   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;
 
  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;
 
  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;
 
  49  * ClusterMetadataStore backed by a local file.
 
  51 @Component(immediate = true, enabled = true)
 
  53 public class StaticClusterMetadataStore
 
  54     extends AbstractStore<ClusterMetadataEvent, ClusterMetadataStoreDelegate>
 
  55     implements ClusterMetadataStore {
 
  57     private final Logger log = getLogger(getClass());
 
  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;
 
  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()) {
 
  81                 metadata.set(mapper.readValue(metadataFile, ClusterMetadata.class));
 
  82                 version = metadataFile.lastModified();
 
  83             } catch (IOException e) {
 
  84                 Throwables.propagate(e);
 
  87             String localIp = getSiteLocalAddress();
 
  88             ControllerNode localNode =
 
  89                     new DefaultControllerNode(new NodeId(localIp), IpAddress.valueOf(localIp), DEFAULT_ONOS_PORT);
 
  90             metadata.set(ClusterMetadata.builder()
 
  92                     .withControllerNodes(Arrays.asList(localNode))
 
  93                     .withPartitions(Lists.newArrayList(new Partition("p1", Lists.newArrayList(localNode.id()))))
 
  95             version = System.currentTimeMillis();
 
 101     public void deactivate() {
 
 106     public void setDelegate(ClusterMetadataStoreDelegate delegate) {
 
 107         checkNotNull(delegate, "Delegate cannot be null");
 
 108         this.delegate = delegate;
 
 112     public void unsetDelegate(ClusterMetadataStoreDelegate delegate) {
 
 113         this.delegate = null;
 
 117     public boolean hasDelegate() {
 
 118         return this.delegate != null;
 
 122     public Versioned<ClusterMetadata> getClusterMetadata() {
 
 123         return new Versioned<>(metadata.get(), version);
 
 127     public void setClusterMetadata(ClusterMetadata metadata) {
 
 128         checkNotNull(metadata);
 
 130             Files.createParentDirs(metadataFile);
 
 131             mapper.writeValue(metadataFile, metadata);
 
 132             this.metadata.set(metadata);
 
 133         } catch (IOException e) {
 
 134             Throwables.propagate(e);
 
 139     public void setActiveReplica(String partitionId, NodeId nodeId) {
 
 140         throw new UnsupportedOperationException();
 
 144     public void unsetActiveReplica(String partitionId, NodeId nodeId) {
 
 145         throw new UnsupportedOperationException();
 
 149     public Collection<NodeId> getActiveReplicas(String partitionId) {
 
 150         return metadata.get().getPartitions()
 
 152                        .filter(r -> r.getName().equals(partitionId))
 
 154                        .map(r -> r.getMembers())
 
 158     private static class ControllerNodeSerializer extends JsonSerializer<ControllerNode> {
 
 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();
 
 170     private static class ControllerNodeDeserializer extends JsonDeserializer<ControllerNode> {
 
 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);
 
 182     private static class NodeIdSerializer extends JsonSerializer<NodeId> {
 
 184         public void serialize(NodeId nodeId, JsonGenerator jgen, SerializerProvider provider)
 
 185           throws IOException, JsonProcessingException {
 
 186             jgen.writeString(nodeId.toString());
 
 190     private class NodeIdDeserializer extends JsonDeserializer<NodeId> {
 
 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());
 
 200     private static String getSiteLocalAddress() {
 
 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.
 
 206         String useOnosIp = System.getenv(ONOS_IP);
 
 207         if (useOnosIp != null) {
 
 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;
 
 217         Function<NetworkInterface, IpAddress> ipLookup = nif -> {
 
 218             for (InetAddress address : Collections.list(nif.getInetAddresses())) {
 
 219                 if (address.isSiteLocalAddress()) {
 
 220                     return IpAddress.valueOf(address);
 
 226             IpAddress ip = ipLookup.apply(NetworkInterface.getByName(useOnosInterface));
 
 228                 return ip.toString();
 
 230             for (NetworkInterface nif : Collections.list(getNetworkInterfaces())) {
 
 231                 ip = ipLookup.apply(nif);
 
 233                     return ip.toString();
 
 236         } catch (Exception e) {
 
 237             throw new IllegalStateException("Unable to get network interfaces", e);
 
 239         return IpAddress.valueOf(InetAddress.getLoopbackAddress()).toString();