blob: 317a4b4706410de87bb96e8398e7e4e5b74ecc25 [file] [log] [blame]
Thomas Vachuskae6360222015-07-21 10:10:36 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2015-present Open Networking Foundation
Thomas Vachuskae6360222015-07-21 10:10:36 -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 */
Thomas Vachuska4998caa2015-08-26 13:28:38 -070016package org.onosproject.net.config.impl;
Thomas Vachuskae6360222015-07-21 10:10:36 -070017
Jonathan Harted4fdcd2015-09-08 14:13:37 -070018import com.fasterxml.jackson.databind.JsonNode;
Thomas Vachuskae6360222015-07-21 10:10:36 -070019import com.fasterxml.jackson.databind.ObjectMapper;
20import com.fasterxml.jackson.databind.node.ObjectNode;
Aaron Kruglikovbd1eb3f2015-07-21 10:39:06 -070021import com.google.common.collect.Maps;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070022import org.osgi.service.component.annotations.Activate;
23import org.osgi.service.component.annotations.Component;
24import org.osgi.service.component.annotations.Deactivate;
25import org.osgi.service.component.annotations.Reference;
26import org.osgi.service.component.annotations.ReferenceCardinality;
Jonathan Hartc43fd1c2016-01-18 20:02:20 -080027import org.onosproject.net.config.BasicNetworkConfigService;
Jonathan Hart4cb39882015-08-12 23:50:55 -040028import org.onosproject.net.config.Config;
Ray Milkeya4122362015-08-18 15:19:08 -070029import org.onosproject.net.config.NetworkConfigEvent;
30import org.onosproject.net.config.NetworkConfigListener;
31import org.onosproject.net.config.NetworkConfigService;
Thomas Vachuskae6360222015-07-21 10:10:36 -070032import org.slf4j.Logger;
33import org.slf4j.LoggerFactory;
34
35import java.io.File;
Aaron Kruglikovbd1eb3f2015-07-21 10:39:06 -070036import java.util.Iterator;
37import java.util.Map;
38import java.util.Objects;
Thomas Vachuskae6360222015-07-21 10:10:36 -070039
40/**
41 * Component for loading the initial network configuration.
42 */
43@Component(immediate = true)
44public class NetworkConfigLoader {
45
46 private static final File CFG_FILE = new File("../config/network-cfg.json");
47
48 private final Logger log = LoggerFactory.getLogger(getClass());
49
Jonathan Hartc43fd1c2016-01-18 20:02:20 -080050 // Dependency to ensure the basic subject factories are properly initialized
51 // before we start loading configs from file
Ray Milkeyd84f89b2018-08-17 14:54:17 -070052 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jonathan Hartc43fd1c2016-01-18 20:02:20 -080053 protected BasicNetworkConfigService basicConfigs;
54
Ray Milkeyd84f89b2018-08-17 14:54:17 -070055 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuskae6360222015-07-21 10:10:36 -070056 protected NetworkConfigService networkConfigService;
57
58 // FIXME: Add mutual exclusion to make sure this happens only once per startup.
59
Jonathan Harted4fdcd2015-09-08 14:13:37 -070060 private final Map<InnerConfigPosition, JsonNode> jsons = Maps.newConcurrentMap();
Aaron Kruglikovbd1eb3f2015-07-21 10:39:06 -070061
62 private final NetworkConfigListener configListener = new InnerConfigListener();
63
Jonathan Hart4cb39882015-08-12 23:50:55 -040064 private ObjectNode root;
Thomas Vachuskae6360222015-07-21 10:10:36 -070065
66 @Activate
67 public void activate() {
Aaron Kruglikovbd1eb3f2015-07-21 10:39:06 -070068 //TODO Maybe this should be at the bottom to avoid a potential race
69 networkConfigService.addListener(configListener);
Thomas Vachuskae6360222015-07-21 10:10:36 -070070 try {
71 if (CFG_FILE.exists()) {
Aaron Kruglikovbd1eb3f2015-07-21 10:39:06 -070072 root = (ObjectNode) new ObjectMapper().readTree(CFG_FILE);
Thomas Vachuskae6360222015-07-21 10:10:36 -070073
Aaron Kruglikovbd1eb3f2015-07-21 10:39:06 -070074 populateConfigurations();
Thomas Vachuskae6360222015-07-21 10:10:36 -070075
Deepa Vaddireddy7fae1892016-06-15 17:13:15 +053076 if (applyConfigurations()) {
77 log.info("Loaded initial network configuration from {}", CFG_FILE);
78 } else {
79 log.error("Partially loaded initial network configuration from {}", CFG_FILE);
80 }
Thomas Vachuskae6360222015-07-21 10:10:36 -070081 }
82 } catch (Exception e) {
Deepa Vaddireddy7fae1892016-06-15 17:13:15 +053083 log.warn("Unable to load initial network configuration from {}", CFG_FILE, e);
Thomas Vachuskae6360222015-07-21 10:10:36 -070084 }
85 }
86
Aaron Kruglikovbd1eb3f2015-07-21 10:39:06 -070087 @Deactivate
88 public void deactivate() {
89 networkConfigService.removeListener(configListener);
90 }
Thomas Vachuskae6360222015-07-21 10:10:36 -070091 // sweep through pending config jsons and try to add them
92
93 /**
Aaron Kruglikovbd1eb3f2015-07-21 10:39:06 -070094 * Inner class that allows for handling of newly added NetConfig types.
Thomas Vachuskae6360222015-07-21 10:10:36 -070095 */
Aaron Kruglikovbd1eb3f2015-07-21 10:39:06 -070096 private final class InnerConfigListener implements NetworkConfigListener {
97
98 @Override
99 public void event(NetworkConfigEvent event) {
100 //TODO should this be done for other types of NetworkConfigEvents?
101 if (event.type() == NetworkConfigEvent.Type.CONFIG_REGISTERED ||
102 event.type() == NetworkConfigEvent.Type.CONFIG_ADDED) {
103 applyConfigurations();
104 }
105
106 }
Thomas Vachuskae6360222015-07-21 10:10:36 -0700107 }
108
Aaron Kruglikovbd1eb3f2015-07-21 10:39:06 -0700109 /**
110 * Inner class that allows for tracking of JSON class configurations.
111 */
112 private final class InnerConfigPosition {
Jonathan Hart4cb39882015-08-12 23:50:55 -0400113 private final String subjectKey, subject, configKey;
Aaron Kruglikovbd1eb3f2015-07-21 10:39:06 -0700114
Jonathan Hart4cb39882015-08-12 23:50:55 -0400115 private String subjectKey() {
Aaron Kruglikovbd1eb3f2015-07-21 10:39:06 -0700116 return subjectKey;
117 }
118
Jonathan Hart4cb39882015-08-12 23:50:55 -0400119 private String subject() {
Aaron Kruglikovbd1eb3f2015-07-21 10:39:06 -0700120 return subject;
121 }
122
Jonathan Hart4cb39882015-08-12 23:50:55 -0400123 private String configKey() {
124 return configKey;
Aaron Kruglikovbd1eb3f2015-07-21 10:39:06 -0700125 }
126
Jonathan Hart4cb39882015-08-12 23:50:55 -0400127 private InnerConfigPosition(String subjectKey, String subject, String configKey) {
Aaron Kruglikovbd1eb3f2015-07-21 10:39:06 -0700128 this.subjectKey = subjectKey;
129 this.subject = subject;
Jonathan Hart4cb39882015-08-12 23:50:55 -0400130 this.configKey = configKey;
Aaron Kruglikovbd1eb3f2015-07-21 10:39:06 -0700131 }
132
133 @Override
134 public boolean equals(Object obj) {
135 if (this == obj) {
136 return true;
137 }
138 if (obj instanceof InnerConfigPosition) {
139 final InnerConfigPosition that = (InnerConfigPosition) obj;
Jonathan Hart4cb39882015-08-12 23:50:55 -0400140 return Objects.equals(this.subjectKey, that.subjectKey)
141 && Objects.equals(this.subject, that.subject)
142 && Objects.equals(this.configKey, that.configKey);
Aaron Kruglikovbd1eb3f2015-07-21 10:39:06 -0700143 }
144 return false;
145 }
146
147 @Override
148 public int hashCode() {
Jonathan Hart4cb39882015-08-12 23:50:55 -0400149 return Objects.hash(subjectKey, subject, configKey);
Aaron Kruglikovbd1eb3f2015-07-21 10:39:06 -0700150 }
151 }
152
Aaron Kruglikova598c9e2015-07-23 16:56:27 -0700153 /**
154 * Save the JSON leaves associated with a specific subject key.
155 *
156 * @param sk the subject key string.
157 * @param node the node associated with the subject key.
158 */
Aaron Kruglikovbd1eb3f2015-07-21 10:39:06 -0700159 private void saveJson(String sk, ObjectNode node) {
160 node.fieldNames().forEachRemaining(s ->
161 saveSubjectJson(sk, s, (ObjectNode) node.path(s)));
162 }
163
Aaron Kruglikova598c9e2015-07-23 16:56:27 -0700164 /**
165 * Save the JSON leaves of the tree rooted as the node 'node' with subject key 'sk'.
166 *
167 * @param sk the string of the subject key.
168 * @param s the subject name.
169 * @param node the node rooting this subtree.
170 */
Aaron Kruglikovbd1eb3f2015-07-21 10:39:06 -0700171 private void saveSubjectJson(String sk,
172 String s, ObjectNode node) {
173 node.fieldNames().forEachRemaining(c ->
Jonathan Harted4fdcd2015-09-08 14:13:37 -0700174 this.jsons.put(new InnerConfigPosition(sk, s, c), node.path(c)));
Aaron Kruglikovbd1eb3f2015-07-21 10:39:06 -0700175 }
176
Aaron Kruglikova598c9e2015-07-23 16:56:27 -0700177 /**
178 * Iterate through the JSON and populate a list of the leaf nodes of the structure.
179 */
Aaron Kruglikovbd1eb3f2015-07-21 10:39:06 -0700180 private void populateConfigurations() {
181 root.fieldNames().forEachRemaining(sk ->
182 saveJson(sk, (ObjectNode) root.path(sk)));
183
184 }
185
Aaron Kruglikova598c9e2015-07-23 16:56:27 -0700186 /**
Jonathan Hart4cb39882015-08-12 23:50:55 -0400187 * Apply the configurations associated with all of the config classes that
188 * are imported and have not yet been applied.
Deepa Vaddireddy7fae1892016-06-15 17:13:15 +0530189 *
190 * @return false if any of the configuration parsing fails
Aaron Kruglikova598c9e2015-07-23 16:56:27 -0700191 */
Deepa Vaddireddy7fae1892016-06-15 17:13:15 +0530192 private boolean applyConfigurations() {
Jonathan Harted4fdcd2015-09-08 14:13:37 -0700193 Iterator<Map.Entry<InnerConfigPosition, JsonNode>> iter = jsons.entrySet().iterator();
Aaron Kruglikova598c9e2015-07-23 16:56:27 -0700194
Jonathan Harted4fdcd2015-09-08 14:13:37 -0700195 Map.Entry<InnerConfigPosition, JsonNode> entry;
Aaron Kruglikova598c9e2015-07-23 16:56:27 -0700196 InnerConfigPosition key;
Jonathan Harted4fdcd2015-09-08 14:13:37 -0700197 JsonNode node;
Aaron Kruglikova598c9e2015-07-23 16:56:27 -0700198 String subjectKey;
Jonathan Hart4cb39882015-08-12 23:50:55 -0400199 String subjectString;
200 String configKey;
Deepa Vaddireddy7fae1892016-06-15 17:13:15 +0530201 boolean isSuccess = true;
Aaron Kruglikovbd1eb3f2015-07-21 10:39:06 -0700202 while (iter.hasNext()) {
203 entry = iter.next();
Aaron Kruglikova598c9e2015-07-23 16:56:27 -0700204 node = entry.getValue();
205 key = entry.getKey();
Jonathan Hart4cb39882015-08-12 23:50:55 -0400206 subjectKey = key.subjectKey();
207 subjectString = key.subject();
208 configKey = key.configKey();
209
210 Class<? extends Config> configClass =
211 networkConfigService.getConfigClass(subjectKey, configKey);
Aaron Kruglikova598c9e2015-07-23 16:56:27 -0700212 //Check that the config class has been imported
Jonathan Hart4cb39882015-08-12 23:50:55 -0400213 if (configClass != null) {
214
215 Object subject = networkConfigService.getSubjectFactory(subjectKey).
216 createSubject(subjectString);
Aaron Kruglikova598c9e2015-07-23 16:56:27 -0700217
Deepa Vaddireddy7fae1892016-06-15 17:13:15 +0530218 try {
219 //Apply the configuration
220 networkConfigService.applyConfig(subject, configClass, node);
221 } catch (IllegalArgumentException e) {
222 log.warn("Error parsing config " + subjectKey + "/" + subject + "/" + configKey);
223 isSuccess = false;
224 }
Aaron Kruglikova598c9e2015-07-23 16:56:27 -0700225
226 //Now that it has been applied the corresponding JSON entry is no longer needed
Jonathan Hart4cb39882015-08-12 23:50:55 -0400227 iter.remove();
Aaron Kruglikovbd1eb3f2015-07-21 10:39:06 -0700228 }
Aaron Kruglikovbd1eb3f2015-07-21 10:39:06 -0700229 }
Deepa Vaddireddy7fae1892016-06-15 17:13:15 +0530230 return isSuccess;
231 }
Thomas Vachuskae6360222015-07-21 10:10:36 -0700232
233}