blob: c4529bcb99182705ee074a0e92f858d9ef02bde7 [file] [log] [blame]
Yi Tsengc927a062017-05-02 15:02:37 -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.net.intent.impl;
18
19import com.google.common.collect.ArrayListMultimap;
20import com.google.common.collect.Maps;
21import com.google.common.collect.Sets;
22
23import org.onosproject.net.intent.Intent;
24import org.onosproject.net.intent.IntentData;
25import org.onosproject.net.intent.IntentInstallationContext;
26import org.onosproject.net.intent.IntentOperationContext;
27import org.onosproject.net.intent.IntentInstaller;
28import org.onosproject.net.intent.IntentStore;
29import org.slf4j.Logger;
30
31import java.util.Collections;
32import java.util.List;
33import java.util.Map;
34import java.util.Optional;
35import java.util.Set;
36
37import static org.onosproject.net.intent.IntentState.*;
38import static org.slf4j.LoggerFactory.getLogger;
39
40/**
41 * Implementation of IntentInstallCoordinator.
42 */
43public class InstallCoordinator {
44 private static final String INSTALLER_NOT_FOUND = "Intent installer not found, Intent: {}";
Yuta HIGUCHI965da1f2017-05-15 14:47:04 -070045 private final Logger log = getLogger(InstallCoordinator.class);
Yi Tsengc927a062017-05-02 15:02:37 -070046
47 private InstallerRegistry installerRegistry;
48 private IntentStore intentStore;
49
50 /**
51 * Creates an InstallCoordinator.
52 *
53 * @param installerRegistry the installer registry
54 * @param intentStore the Intent store
55 */
56 public InstallCoordinator(InstallerRegistry installerRegistry,
57 IntentStore intentStore) {
58 this.installerRegistry = installerRegistry;
59 this.intentStore = intentStore;
60 }
61
62 /**
63 * Applies Intent data to be uninstalled and to be installed.
64 *
65 * @param toUninstall Intent data to be uninstalled
66 * @param toInstall Intent data to be installed
67 */
68 public void installIntents(Optional<IntentData> toUninstall, Optional<IntentData> toInstall) {
69 // If no any Intents to be uninstalled or installed, ignore it.
70 if (!toUninstall.isPresent() && !toInstall.isPresent()) {
71 return;
72 }
73
74 // Classify installable Intents to different installers.
75 ArrayListMultimap<IntentInstaller, Intent> uninstallInstallers;
76 ArrayListMultimap<IntentInstaller, Intent> installInstallers;
77 Set<IntentInstaller> allInstallers = Sets.newHashSet();
78
79 if (toUninstall.isPresent()) {
80 uninstallInstallers = getInstallers(toUninstall.get());
81 allInstallers.addAll(uninstallInstallers.keySet());
82 } else {
83 uninstallInstallers = ArrayListMultimap.create();
84 }
85
86 if (toInstall.isPresent()) {
87 installInstallers = getInstallers(toInstall.get());
88 allInstallers.addAll(installInstallers.keySet());
89 } else {
90 installInstallers = ArrayListMultimap.create();
91 }
92
93 // Generates an installation context for the high level Intent.
94 IntentInstallationContext installationContext =
95 new IntentInstallationContext(toUninstall.orElse(null), toInstall.orElse(null));
96
97 //Generates different operation context for different installable Intents.
98 Map<IntentInstaller, IntentOperationContext> contexts = Maps.newHashMap();
99 allInstallers.forEach(installer -> {
100 List<Intent> intentsToUninstall = uninstallInstallers.get(installer);
101 List<Intent> intentsToInstall = installInstallers.get(installer);
102
103 // Connect context to high level installation context
104 IntentOperationContext context =
105 new IntentOperationContext(intentsToUninstall, intentsToInstall,
106 installationContext);
107 installationContext.addPendingContext(context);
108 contexts.put(installer, context);
109 });
110
111 // Apply contexts to installers
112 contexts.forEach((installer, context) -> {
113 installer.apply(context);
114 });
115 }
116
117 /**
118 * Generates a mapping for installable Intents to installers.
119 *
120 * @param intentData the Intent data which contains installable Intents
121 * @return the mapping for installable Intents to installers
122 */
123 private ArrayListMultimap<IntentInstaller, Intent> getInstallers(IntentData intentData) {
124 ArrayListMultimap<IntentInstaller, Intent> intentInstallers = ArrayListMultimap.create();
125 intentData.installables().forEach(intent -> {
126 IntentInstaller installer = installerRegistry.getInstaller(intent.getClass());
127 if (installer != null) {
128 intentInstallers.put(installer, intent);
129 } else {
130 log.warn(INSTALLER_NOT_FOUND, intent);
131 }
132 });
133 return intentInstallers;
134 }
135
136 /**
137 * Handles success operation context.
138 *
139 * @param context the operation context
140 */
141 public void success(IntentOperationContext context) {
142 IntentInstallationContext intentInstallationContext =
143 context.intentInstallationContext();
144 intentInstallationContext.removePendingContext(context);
145
146 if (intentInstallationContext.isPendingContextsEmpty()) {
147 finish(intentInstallationContext);
148 }
149 }
150
151 /**
152 * Handles failed operation context.
153 *
154 * @param context the operation context
155 */
156 public void failed(IntentOperationContext context) {
157 IntentInstallationContext intentInstallationContext =
158 context.intentInstallationContext();
159 intentInstallationContext.addErrorContext(context);
160 intentInstallationContext.removePendingContext(context);
161
162 if (intentInstallationContext.isPendingContextsEmpty()) {
163 finish(intentInstallationContext);
164 }
165 }
166
167 /**
168 * Completed the installation context and update the Intent store.
169 *
170 * @param intentInstallationContext the installation context
171 */
172 private void finish(IntentInstallationContext intentInstallationContext) {
173 Set<IntentOperationContext> errCtxs = intentInstallationContext.errorContexts();
174 Optional<IntentData> toUninstall = intentInstallationContext.toUninstall();
175 Optional<IntentData> toInstall = intentInstallationContext.toInstall();
176
177 // Intent install success
178 if (errCtxs == null || errCtxs.isEmpty()) {
179 if (toInstall.isPresent()) {
180 IntentData installData = toInstall.get();
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700181 log.debug("Completed installing: {}:{}",
182 installData.key(),
183 installData.intent().id());
Yi Tseng49a2b2e2017-05-11 14:32:16 -0700184 installData = new IntentData(installData, installData.installables());
Yi Tsengc927a062017-05-02 15:02:37 -0700185 installData.setState(INSTALLED);
186 intentStore.write(installData);
187 } else if (toUninstall.isPresent()) {
188 IntentData uninstallData = toUninstall.get();
Yi Tseng49a2b2e2017-05-11 14:32:16 -0700189 uninstallData = new IntentData(uninstallData, Collections.emptyList());
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700190 log.debug("Completed withdrawing: {}:{}",
191 uninstallData.key(),
192 uninstallData.intent().id());
Yi Tsengc927a062017-05-02 15:02:37 -0700193 switch (uninstallData.request()) {
194 case INSTALL_REQ:
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700195 // INSTALLED intent was damaged & clean up is now complete
Yi Tsengc927a062017-05-02 15:02:37 -0700196 uninstallData.setState(FAILED);
197 break;
198 case WITHDRAW_REQ:
199 default: //TODO "default" case should not happen
200 uninstallData.setState(WITHDRAWN);
201 break;
202 }
203 // Intent has been withdrawn; we can clear the installables
Yi Tseng49a2b2e2017-05-11 14:32:16 -0700204 intentStore.write(uninstallData);
Yi Tsengc927a062017-05-02 15:02:37 -0700205 }
206 } else {
207 // if toInstall was cause of error, then recompile (manage/increment counter, when exceeded -> CORRUPT)
208 if (toInstall.isPresent()) {
209 IntentData installData = toInstall.get();
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700210 intentStore.write(IntentData.corrupt(installData));
Yi Tsengc927a062017-05-02 15:02:37 -0700211 }
212 // if toUninstall was cause of error, then CORRUPT (another job will clean this up)
213 if (toUninstall.isPresent()) {
214 IntentData uninstallData = toUninstall.get();
Yuta HIGUCHI4f8a3772017-05-16 20:23:49 -0700215 intentStore.write(IntentData.corrupt(uninstallData));
Yi Tsengc927a062017-05-02 15:02:37 -0700216 }
217 }
218 }
219}