blob: e0103ce8fc0f07032a538350194bd2ed3d99d9c6 [file] [log] [blame]
Sho SHIMIZUb0a47d42015-02-19 13:26:30 -08001/*
2 * Copyright 2015 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 */
16package org.onosproject.net.intent.impl;
17
18import com.google.common.collect.ImmutableMap;
19import org.onosproject.net.flow.FlowRuleOperation;
20import org.onosproject.net.flow.FlowRuleOperations;
21import org.onosproject.net.flow.FlowRuleOperationsContext;
22import org.onosproject.net.intent.Intent;
23import org.onosproject.net.intent.IntentData;
24import org.onosproject.net.intent.IntentException;
25import org.onosproject.net.intent.IntentInstaller;
26import org.onosproject.net.intent.IntentStore;
27import org.slf4j.Logger;
28import org.slf4j.LoggerFactory;
29
30import java.util.ArrayList;
31import java.util.Collection;
32import java.util.Collections;
33import java.util.Iterator;
34import java.util.List;
35import java.util.Map;
36import java.util.concurrent.ConcurrentHashMap;
37import java.util.concurrent.ConcurrentMap;
38
39import static com.google.common.base.Preconditions.checkState;
40import static org.onlab.util.Tools.isNullOrEmpty;
41import static org.onosproject.net.intent.IntentState.FAILED;
42import static org.onosproject.net.intent.IntentState.INSTALLED;
43import static org.onosproject.net.intent.IntentState.WITHDRAWN;
44
45// TODO: consider a better name
46class InstallerRegistry {
47
48 private static final Logger log = LoggerFactory.getLogger(InstallerRegistry.class);
49
50 private final ConcurrentMap<Class<? extends Intent>,
51 IntentInstaller<? extends Intent>> installers = new ConcurrentHashMap<>();
52 /**
53 * Registers the specified installer for the given installable intent class.
54 *
55 * @param cls installable intent class
56 * @param installer intent installer
57 * @param <T> the type of installable intent
58 */
59 <T extends Intent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
60 installers.put(cls, installer);
61 }
62
63 /**
64 * Unregisters the installer for the given installable intent class.
65 *
66 * @param cls installable intent class
67 * @param <T> the type of installable intent
68 */
69 <T extends Intent> void unregisterInstaller(Class<T> cls) {
70 installers.remove(cls);
71 }
72
73 /**
74 * Returns immutable set of bindings of currently registered intent installers.
75 *
76 * @return the set of installer bindings
77 */
78 Map<Class<? extends Intent>, IntentInstaller<? extends Intent>> getInstallers() {
79 return ImmutableMap.copyOf(installers);
80 }
81
82 /**
83 * Returns the corresponding intent installer to the specified installable intent.
84 *
85 * @param intent intent
86 * @param <T> the type of installable intent
87 * @return intent installer corresponding to the specified installable intent
88 */
89 private <T extends Intent> IntentInstaller<T> getInstaller(T intent) {
90 @SuppressWarnings("unchecked")
91 IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
92 if (installer == null) {
93 throw new IntentException("no installer for class " + intent.getClass());
94 }
95 return installer;
96 }
97
98 /**
99 * Registers an intent installer of the specified intent if an intent installer
100 * for the intent is not registered. This method traverses the class hierarchy of
101 * the intent. Once an intent installer for a parent type is found, this method
102 * registers the found intent installer.
103 *
104 * @param intent intent
105 */
106 private void registerSubclassInstallerIfNeeded(Intent intent) {
107 if (!installers.containsKey(intent.getClass())) {
108 Class<?> cls = intent.getClass();
109 while (cls != Object.class) {
110 // As long as we're within the Intent class descendants
111 if (Intent.class.isAssignableFrom(cls)) {
112 IntentInstaller<?> installer = installers.get(cls);
113 if (installer != null) {
114 installers.put(intent.getClass(), installer);
115 return;
116 }
117 }
118 cls = cls.getSuperclass();
119 }
120 }
121 }
122
123 /**
124 * Generate a {@link FlowRuleOperations} instance from the specified intent data.
125 *
126 * @param current intent data stored in the store
127 * @param pending intent data being processed
128 * @param store intent store saving the intent state in this method
129 * @param trackerService objective tracker that is used in this method
130 * @return flow rule operations
131 */
132 public FlowRuleOperations coordinate(IntentData current, IntentData pending,
133 IntentStore store, ObjectiveTrackerService trackerService) {
134 List<Intent> oldInstallables = (current != null) ? current.installables() : null;
135 List<Intent> newInstallables = pending.installables();
136
137 checkState(isNullOrEmpty(oldInstallables) ||
138 oldInstallables.size() == newInstallables.size(),
139 "Old and New Intent must have equivalent installable intents.");
140
141 List<List<Collection<FlowRuleOperation>>> plans = new ArrayList<>();
142 for (int i = 0; i < newInstallables.size(); i++) {
143 Intent newInstallable = newInstallables.get(i);
144 registerSubclassInstallerIfNeeded(newInstallable);
145 //TODO consider migrating installers to FlowRuleOperations
146 /* FIXME
147 - we need to do another pass on this method about that doesn't
148 require the length of installables to be equal, and also doesn't
149 depend on ordering
150 - we should also reconsider when to start/stop tracking resources
151 */
152 if (isNullOrEmpty(oldInstallables)) {
153 plans.add(getInstaller(newInstallable).install(newInstallable));
154 } else {
155 Intent oldInstallable = oldInstallables.get(i);
156 checkState(oldInstallable.getClass().equals(newInstallable.getClass()),
157 "Installable Intent type mismatch.");
158 trackerService.removeTrackedResources(pending.key(), oldInstallable.resources());
159 plans.add(getInstaller(newInstallable).replace(oldInstallable, newInstallable));
160 }
161 trackerService.addTrackedResources(pending.key(), newInstallable.resources());
162// } catch (IntentException e) {
163// log.warn("Unable to update intent {} due to:", oldIntent.id(), e);
164// //FIXME... we failed. need to uninstall (if same) or revert (if different)
165// trackerService.removeTrackedResources(newIntent.id(), newInstallable.resources());
166// exception = e;
167// batches = uninstallIntent(oldIntent, oldInstallables);
168// }
169 }
170
171 return merge(plans).build(new FlowRuleOperationsContext() { // TODO move this out
172 @Override
173 public void onSuccess(FlowRuleOperations ops) {
174 log.debug("Completed installing: {}", pending.key());
175 pending.setState(INSTALLED);
176 store.write(pending);
177 }
178
179 @Override
180 public void onError(FlowRuleOperations ops) {
181 log.warn("Failed installation: {} {} on {}", pending.key(),
182 pending.intent(), ops);
183 //TODO store.write(pending.setState(BROKEN));
184 pending.setState(FAILED);
185 store.write(pending);
186 }
187 });
188 }
189
190 /**
191 * Generate a {@link FlowRuleOperations} instance from the specified intent data.
192 *
193 * @param current intent data stored in the store
194 * @param pending intent date being processed
195 * @param store intent store saving the intent state in this method
196 * @param trackerService objective tracker that is used in this method
197 * @return flow rule operations
198 */
199 FlowRuleOperations uninstallCoordinate(IntentData current, IntentData pending,
200 IntentStore store, ObjectiveTrackerService trackerService) {
201 List<Intent> installables = current.installables();
202 List<List<Collection<FlowRuleOperation>>> plans = new ArrayList<>();
203 for (Intent installable : installables) {
204 plans.add(getInstaller(installable).uninstall(installable));
205 trackerService.removeTrackedResources(pending.key(), installable.resources());
206 }
207
208 return merge(plans).build(new FlowRuleOperationsContext() {
209 @Override
210 public void onSuccess(FlowRuleOperations ops) {
211 log.debug("Completed withdrawing: {}", pending.key());
212 pending.setState(WITHDRAWN);
213 pending.setInstallables(Collections.emptyList());
214 store.write(pending);
215 }
216
217 @Override
218 public void onError(FlowRuleOperations ops) {
219 log.warn("Failed withdraw: {}", pending.key());
220 pending.setState(FAILED);
221 store.write(pending);
222 }
223 });
224 }
225
226
227 // TODO needs tests... or maybe it's just perfect
228 private FlowRuleOperations.Builder merge(List<List<Collection<FlowRuleOperation>>> plans) {
229 FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
230 // Build a batch one stage at a time
231 for (int stageNumber = 0;; stageNumber++) {
232 // Get the sub-stage from each plan (List<Set<FlowRuleOperation>)
233 for (Iterator<List<Collection<FlowRuleOperation>>> itr = plans.iterator(); itr.hasNext();) {
234 List<Collection<FlowRuleOperation>> plan = itr.next();
235 if (plan.size() <= stageNumber) {
236 // we have consumed all stages from this plan, so remove it
237 itr.remove();
238 continue;
239 }
240 // write operations from this sub-stage into the builder
241 Collection<FlowRuleOperation> stage = plan.get(stageNumber);
242 for (FlowRuleOperation entry : stage) {
243 builder.operation(entry);
244 }
245 }
246 // we are done with the stage, start the next one...
247 if (plans.isEmpty()) {
248 break; // we don't need to start a new stage, we are done.
249 }
250 builder.newStage();
251 }
252 return builder;
253 }
254}