blob: 7f739b1efdc7ba1f7646fe005c04d8bd400516a2 [file] [log] [blame]
Yoonseon Han9e043792017-05-03 15:43:33 -07001/*
2 * Copyright 2017-present 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 */
16
17package org.onosproject.incubator.store.virtual.impl;
18
19import com.google.common.collect.ComparisonChain;
20import com.google.common.collect.Lists;
21import com.google.common.collect.Maps;
22import org.apache.felix.scr.annotations.Activate;
23import org.apache.felix.scr.annotations.Component;
24import org.apache.felix.scr.annotations.Deactivate;
25import org.apache.felix.scr.annotations.Service;
26import org.onosproject.incubator.net.virtual.NetworkId;
27import org.onosproject.incubator.net.virtual.VirtualNetworkIntentStore;
28import org.onosproject.net.intent.Intent;
29import org.onosproject.net.intent.IntentData;
30import org.onosproject.net.intent.IntentEvent;
31import org.onosproject.net.intent.IntentState;
32import org.onosproject.net.intent.IntentStoreDelegate;
33import org.onosproject.net.intent.Key;
34import org.onosproject.store.Timestamp;
35import org.slf4j.Logger;
36
37import java.util.List;
38import java.util.Map;
39import java.util.stream.Collectors;
40
41import static com.google.common.base.Preconditions.checkArgument;
42import static com.google.common.base.Preconditions.checkNotNull;
43import static org.onosproject.net.intent.IntentState.PURGE_REQ;
44import static org.slf4j.LoggerFactory.getLogger;
45
46/**
47 * Simple single-instance implementation of the intent store for virtual networks.
48 */
49
50@Component(immediate = true)
51@Service
52public class SimpleVirtualIntentStore
53 extends AbstractVirtualStore<IntentEvent, IntentStoreDelegate>
54 implements VirtualNetworkIntentStore {
55
56 private final Logger log = getLogger(getClass());
57
58 private final Map<NetworkId, Map<Key, IntentData>> currentByNetwork =
59 Maps.newConcurrentMap();
60 private final Map<NetworkId, Map<Key, IntentData>> pendingByNetwork =
61 Maps.newConcurrentMap();
62
63 @Activate
64 public void activate() {
65 log.info("Started");
66 }
67
68 @Deactivate
69 public void deactivate() {
70 log.info("Stopped");
71 }
72
73
74 @Override
75 public long getIntentCount(NetworkId networkId) {
76 return getCurrentMap(networkId).size();
77 }
78
79 @Override
80 public Iterable<Intent> getIntents(NetworkId networkId) {
81 return getCurrentMap(networkId).values().stream()
82 .map(IntentData::intent)
83 .collect(Collectors.toList());
84 }
85
86 @Override
87 public Iterable<IntentData> getIntentData(NetworkId networkId,
88 boolean localOnly, long olderThan) {
89 if (localOnly || olderThan > 0) {
90 long older = System.nanoTime() - olderThan * 1_000_000; //convert ms to ns
91 final SystemClockTimestamp time = new SystemClockTimestamp(older);
92 return getCurrentMap(networkId).values().stream()
93 .filter(data -> data.version().isOlderThan(time) &&
94 (!localOnly || isMaster(networkId, data.key())))
95 .collect(Collectors.toList());
96 }
97 return Lists.newArrayList(getCurrentMap(networkId).values());
98 }
99
100 @Override
101 public IntentState getIntentState(NetworkId networkId, Key intentKey) {
102 IntentData data = getCurrentMap(networkId).get(intentKey);
103 return (data != null) ? data.state() : null;
104 }
105
106 @Override
107 public List<Intent> getInstallableIntents(NetworkId networkId, Key intentKey) {
108 IntentData data = getCurrentMap(networkId).get(intentKey);
109 if (data != null) {
110 return data.installables();
111 }
112 return null;
113 }
114
115 @Override
116 public void write(NetworkId networkId, IntentData newData) {
117 checkNotNull(newData);
118
119 synchronized (this) {
120 // TODO this could be refactored/cleaned up
121 IntentData currentData = getCurrentMap(networkId).get(newData.key());
122 IntentData pendingData = getPendingMap(networkId).get(newData.key());
123
124 if (IntentData.isUpdateAcceptable(currentData, newData)) {
125 if (pendingData != null) {
126 if (pendingData.state() == PURGE_REQ) {
127 getCurrentMap(networkId).remove(newData.key(), newData);
128 } else {
129 getCurrentMap(networkId).put(newData.key(), new IntentData(newData));
130 }
131
132 if (pendingData.version().compareTo(newData.version()) <= 0) {
133 // pendingData version is less than or equal to newData's
134 // Note: a new update for this key could be pending (it's version will be greater)
135 getPendingMap(networkId).remove(newData.key());
136 }
137 }
138 IntentEvent.getEvent(newData).ifPresent(e -> notifyDelegate(networkId, e));
139 }
140 }
141 }
142
143 @Override
144 public void batchWrite(NetworkId networkId, Iterable<IntentData> updates) {
145 for (IntentData data : updates) {
146 write(networkId, data);
147 }
148 }
149
150 @Override
151 public Intent getIntent(NetworkId networkId, Key key) {
152 IntentData data = getCurrentMap(networkId).get(key);
153 return (data != null) ? data.intent() : null;
154 }
155
156 @Override
157 public IntentData getIntentData(NetworkId networkId, Key key) {
158 IntentData currentData = getCurrentMap(networkId).get(key);
159 if (currentData == null) {
160 return null;
161 }
162 return new IntentData(currentData);
163 }
164
165 @Override
166 public void addPending(NetworkId networkId, IntentData data) {
167 if (data.version() == null) { // recompiled intents will already have a version
168 data = new IntentData(data.intent(), data.state(), new SystemClockTimestamp());
169 }
170 synchronized (this) {
171 IntentData existingData = getPendingMap(networkId).get(data.key());
172 if (existingData == null ||
173 // existing version is strictly less than data's version
174 // Note: if they are equal, we already have the update
175 // TODO maybe we should still make this <= to be safe?
176 existingData.version().compareTo(data.version()) < 0) {
177 getPendingMap(networkId).put(data.key(), data);
178
179 checkNotNull(delegateMap.get(networkId), "Store delegate is not set")
180 .process(new IntentData(data));
181 IntentEvent.getEvent(data).ifPresent(e -> notifyDelegate(networkId, e));
182 } else {
183 log.debug("IntentData {} is older than existing: {}",
184 data, existingData);
185 }
186 //TODO consider also checking the current map at this point
187 }
188 }
189
190 @Override
191 public boolean isMaster(NetworkId networkId, Key intentKey) {
192 return true;
193 }
194
195 @Override
196 public Iterable<Intent> getPending(NetworkId networkId) {
197 return getPendingMap(networkId).values().stream()
198 .map(IntentData::intent)
199 .collect(Collectors.toList());
200 }
201
202 @Override
203 public Iterable<IntentData> getPendingData(NetworkId networkId) {
204 return Lists.newArrayList(getPendingMap(networkId).values());
205 }
206
207 @Override
208 public IntentData getPendingData(NetworkId networkId, Key intentKey) {
209 return getPendingMap(networkId).get(intentKey);
210 }
211
212 @Override
213 public Iterable<IntentData> getPendingData(NetworkId networkId,
214 boolean localOnly, long olderThan) {
215 long older = System.nanoTime() - olderThan * 1_000_000; //convert ms to ns
216 final SystemClockTimestamp time = new SystemClockTimestamp(older);
217 return getPendingMap(networkId).values().stream()
218 .filter(data -> data.version().isOlderThan(time) &&
219 (!localOnly || isMaster(networkId, data.key())))
220 .collect(Collectors.toList());
221 }
222
223 /**
224 * Returns the current intent map for a specific virtual network.
225 *
226 * @param networkId a virtual network identifier
227 * @return the current map for the requested virtual network
228 */
229 private Map<Key, IntentData> getCurrentMap(NetworkId networkId) {
230 currentByNetwork.computeIfAbsent(networkId,
231 n -> Maps.newConcurrentMap());
232 return currentByNetwork.get(networkId);
233 }
234
235 /**
236 * Returns the pending intent map for a specific virtual network.
237 *
238 * @param networkId a virtual network identifier
239 * @return the pending intent map for the requested virtual network
240 */
241 private Map<Key, IntentData> getPendingMap(NetworkId networkId) {
242 pendingByNetwork.computeIfAbsent(networkId,
243 n -> Maps.newConcurrentMap());
244 return pendingByNetwork.get(networkId);
245 }
246
247 public class SystemClockTimestamp implements Timestamp {
248
249 private final long nanoTimestamp;
250
251 public SystemClockTimestamp() {
252 nanoTimestamp = System.nanoTime();
253 }
254
255 public SystemClockTimestamp(long timestamp) {
256 nanoTimestamp = timestamp;
257 }
258
259 @Override
260 public int compareTo(Timestamp o) {
261 checkArgument(o instanceof SystemClockTimestamp,
262 "Must be SystemClockTimestamp", o);
263 SystemClockTimestamp that = (SystemClockTimestamp) o;
264
265 return ComparisonChain.start()
266 .compare(this.nanoTimestamp, that.nanoTimestamp)
267 .result();
268 }
269 }
270}