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