blob: b1b2f3315193304c5926d6e475a58ab9aab2d95d [file] [log] [blame]
Aaron Kruglikov309068e2017-03-17 15:25:54 -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
Thomas Vachuska59d24eb2017-03-22 10:57:34 -070017package org.onosproject.netconf.client.impl;
Aaron Kruglikov309068e2017-03-17 15:25:54 -070018
19import com.google.common.annotations.Beta;
20import org.apache.felix.scr.annotations.Activate;
21import org.apache.felix.scr.annotations.Component;
Aaron Kruglikov309068e2017-03-17 15:25:54 -070022import org.apache.felix.scr.annotations.Deactivate;
23import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
25import org.onosproject.config.DynamicConfigEvent;
26import org.onosproject.config.DynamicConfigListener;
27import org.onosproject.config.DynamicConfigService;
28import org.onosproject.config.Filter;
29import org.onosproject.mastership.MastershipService;
30import org.onosproject.net.DeviceId;
Thomas Vachuska59d24eb2017-03-22 10:57:34 -070031import org.onosproject.netconf.NetconfController;
32import org.onosproject.netconf.NetconfException;
Aaron Kruglikov309068e2017-03-17 15:25:54 -070033import org.onosproject.netconf.client.NetconfTranslator;
34import org.onosproject.netconf.client.NetconfTranslator.OperationType;
35import org.onosproject.yang.model.DataNode;
Bharat saraswalb0dbe6b2017-03-24 22:51:06 +053036import org.onosproject.yang.model.InnerNode;
37import org.onosproject.yang.model.KeyLeaf;
Sithara Punnassery425837f2017-03-21 12:58:00 -070038import org.onosproject.yang.model.LeafNode;
Sithara Punnasserybda82502017-03-22 19:08:19 -070039import org.onosproject.yang.model.ListKey;
Bharat saraswalb0dbe6b2017-03-24 22:51:06 +053040import org.onosproject.yang.model.NodeKey;
Aaron Kruglikov309068e2017-03-17 15:25:54 -070041import org.onosproject.yang.model.ResourceId;
42import org.onosproject.yang.runtime.DefaultResourceData;
Sithara Punnassery326b61d2017-03-24 14:15:04 -070043import org.onlab.util.AbstractAccumulator;
44import org.onlab.util.Accumulator;
Aaron Kruglikov309068e2017-03-17 15:25:54 -070045import org.slf4j.Logger;
46import org.slf4j.LoggerFactory;
47
48import java.io.IOException;
Thomas Vachuska59d24eb2017-03-22 10:57:34 -070049import java.net.URI;
50import java.net.URISyntaxException;
Bharat saraswalb0dbe6b2017-03-24 22:51:06 +053051import java.util.Iterator;
52import java.util.List;
53import java.util.Map;
Sithara Punnassery326b61d2017-03-24 14:15:04 -070054import java.util.Timer;
Bharat saraswalb0dbe6b2017-03-24 22:51:06 +053055
56import static com.google.common.base.Preconditions.checkNotNull;
Aaron Kruglikov309068e2017-03-17 15:25:54 -070057
Sithara Punnassery425837f2017-03-21 12:58:00 -070058
Aaron Kruglikov309068e2017-03-17 15:25:54 -070059@Beta
60@Component(immediate = true)
Sithara Punnassery425837f2017-03-21 12:58:00 -070061public class NetconfActiveComponent implements DynamicConfigListener {
Aaron Kruglikov309068e2017-03-17 15:25:54 -070062
Sithara Punnassery425837f2017-03-21 12:58:00 -070063 private static final Logger log = LoggerFactory.getLogger(NetconfActiveComponent.class);
Aaron Kruglikov309068e2017-03-17 15:25:54 -070064 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Sithara Punnassery425837f2017-03-21 12:58:00 -070065 protected DynamicConfigService cfgService;
Aaron Kruglikov309068e2017-03-17 15:25:54 -070066
67 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
68 protected NetconfTranslator netconfTranslator;
69
70 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
71 protected MastershipService mastershipService;
72
Sithara Punnassery425837f2017-03-21 12:58:00 -070073 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
74 protected NetconfController controller;
75
Sithara Punnassery326b61d2017-03-24 14:15:04 -070076 private final Accumulator<DynamicConfigEvent> accumulator = new InternalEventAccummulator();
77 private static final String DEVNMSPACE = "ne-l3vpn-api";
78 private static final String DEVICES = "devices";
79 private static final String DEVICE = "device";
80 private static final String DEVICE_ID = "deviceid";
81
82 //Symbolic constants for use with the accumulator
83 private static final int MAX_EVENTS = 1000;
84 private static final int MAX_BATCH_MS = 5000;
85 private static final int MAX_IDLE_MS = 1000;
Bharat saraswalb0dbe6b2017-03-24 22:51:06 +053086
Aaron Kruglikov309068e2017-03-17 15:25:54 -070087 private ResourceId resId = new ResourceId.Builder()
Bharat saraswalb0dbe6b2017-03-24 22:51:06 +053088 .addBranchPointSchema(DEVICES, DEVNMSPACE)
89 .addBranchPointSchema(DEVICE, DEVNMSPACE)
90 .addKeyLeaf(DEVICE_ID, DEVNMSPACE, "netconf:172.16.5.11:22")
Aaron Kruglikov309068e2017-03-17 15:25:54 -070091 .build();
Thomas Vachuska59d24eb2017-03-22 10:57:34 -070092
Aaron Kruglikov309068e2017-03-17 15:25:54 -070093 @Activate
94 protected void activate() {
Sithara Punnassery425837f2017-03-21 12:58:00 -070095 cfgService.addListener(this);
Aaron Kruglikov309068e2017-03-17 15:25:54 -070096 log.info("Started");
97 }
98
99 @Deactivate
100 protected void deactivate() {
Sithara Punnassery425837f2017-03-21 12:58:00 -0700101 cfgService.removeListener(this);
Aaron Kruglikov309068e2017-03-17 15:25:54 -0700102 log.info("Stopped");
103 }
104
105 @Override
106 public boolean isRelevant(DynamicConfigEvent event) {
Bharat saraswalb0dbe6b2017-03-24 22:51:06 +0530107 return checkIfRelevant(event.subject());
108 }
109
110 /**
111 * Checks if this event is relevant for active component.
112 *
113 * @param id resource id
114 * @return true if relevant
115 */
116 private boolean checkIfRelevant(ResourceId id) {
117 checkNotNull(id, "resource id can't be null");
118 List<NodeKey> nodeKeys = id.nodeKeys();
119 if (nodeKeys != null && !nodeKeys.isEmpty() && nodeKeys.size() == 2) {
120 NodeKey key = nodeKeys.get(0);
121 if (key.schemaId().name().equals(DEVICES)) {
122 key = nodeKeys.get(1);
123 if (key.schemaId().name().equals(DEVICE)) {
124 ListKey listKey = (ListKey) key;
125 List<KeyLeaf> keyLeaves = listKey.keyLeafs();
126 if (keyLeaves != null && !keyLeaves.isEmpty()) {
127 return keyLeaves.get(0).leafSchema().name()
128 .equals(DEVICE_ID);
129 }
130 }
131 }
132 }
133 log.debug("not a relevant event for netconf active component");
134 return false;
Aaron Kruglikov309068e2017-03-17 15:25:54 -0700135 }
Sithara Punnassery425837f2017-03-21 12:58:00 -0700136
137 public boolean isMaster(DeviceId deviceId) {
Thomas Vachuska59d24eb2017-03-22 10:57:34 -0700138 return mastershipService.isLocalMaster(deviceId);
Sithara Punnassery425837f2017-03-21 12:58:00 -0700139 }
140
Sithara Punnassery326b61d2017-03-24 14:15:04 -0700141
Aaron Kruglikov309068e2017-03-17 15:25:54 -0700142 @Override
143 public void event(DynamicConfigEvent event) {
Sithara Punnassery326b61d2017-03-24 14:15:04 -0700144 accumulator.add(event);
Aaron Kruglikov309068e2017-03-17 15:25:54 -0700145 }
146
147 /**
148 * Performs the delete operation corresponding to the passed event.
Thomas Vachuska59d24eb2017-03-22 10:57:34 -0700149 *
150 * @param node a relevant dataNode
151 * @param deviceId the deviceId of the device to be updated
Aaron Kruglikov309068e2017-03-17 15:25:54 -0700152 * @param resourceId the resourceId of the root of the subtree to be edited
153 * @return true if the update succeeds false otherwise
154 */
155 private boolean configDelete(DataNode node, DeviceId deviceId, ResourceId resourceId) {
156 return parseAndEdit(node, deviceId, resourceId, OperationType.DELETE);
157 }
158
159 /**
160 * Performs the update operation corresponding to the passed event.
Thomas Vachuska59d24eb2017-03-22 10:57:34 -0700161 *
162 * @param node a relevant dataNode
163 * @param deviceId the deviceId of the device to be updated
Aaron Kruglikov309068e2017-03-17 15:25:54 -0700164 * @param resourceId the resourceId of the root of the subtree to be edited
165 * @return true if the update succeeds false otherwise
166 */
167 private boolean configUpdate(DataNode node, DeviceId deviceId, ResourceId resourceId) {
168 return parseAndEdit(node, deviceId, resourceId, OperationType.REPLACE);
169 }
170
171 /**
172 * Parses the incoming event and pushes configuration to the effected
173 * device.
Thomas Vachuska59d24eb2017-03-22 10:57:34 -0700174 *
175 * @param node the dataNode effecting a particular device of which this node
176 * is master
177 * @param deviceId the deviceId of the device to be modified
178 * @param resourceId the resourceId of the root of the subtree to be edited
Aaron Kruglikov309068e2017-03-17 15:25:54 -0700179 * @param operationType the type of editing to be performed
180 * @return true if the operation succeeds, false otherwise
181 */
182 private boolean parseAndEdit(DataNode node, DeviceId deviceId,
183 ResourceId resourceId,
184 NetconfTranslator.OperationType operationType) {
Bharat saraswalb0dbe6b2017-03-24 22:51:06 +0530185
186 //add all low level nodes of devices
187 Iterator<Map.Entry<NodeKey, DataNode>> it = ((InnerNode) node)
188 .childNodes().entrySet().iterator();
189 DefaultResourceData.Builder builder = DefaultResourceData.builder();
190 while (it.hasNext()) {
191 DataNode n = it.next().getValue();
192 if (!n.key().schemaId().name().equals("deviceid")) {
193 builder.addDataNode(n);
194 }
195 }
196 //add resouce id //TODO: check if it is correct
Aaron Kruglikov309068e2017-03-17 15:25:54 -0700197 try {
198 return netconfTranslator.editDeviceConfig(
Bharat saraswalb0dbe6b2017-03-24 22:51:06 +0530199 deviceId, builder.build(), operationType);
Aaron Kruglikov309068e2017-03-17 15:25:54 -0700200 } catch (IOException e) {
201 e.printStackTrace();
202 return false;
203 }
204 }
205
206 /**
Sithara Punnassery425837f2017-03-21 12:58:00 -0700207 * Retrieves device id from Data node.
208 *
209 * @param node the node associated with the event
Aaron Kruglikov309068e2017-03-17 15:25:54 -0700210 * @return the deviceId of the effected device
211 */
Sithara Punnasserybda82502017-03-22 19:08:19 -0700212 @Beta
Sithara Punnassery425837f2017-03-21 12:58:00 -0700213 public DeviceId getDeviceId(DataNode node) {
214 String[] temp;
215 String ip, port;
216 if (node.type() == DataNode.Type.SINGLE_INSTANCE_LEAF_VALUE_NODE) {
217 temp = ((LeafNode) node).asString().split("\\:");
218 if (temp.length != 3) {
219 throw new RuntimeException(new NetconfException("Invalid device id form, cannot apply"));
220 }
221 ip = temp[1];
222 port = temp[2];
Sithara Punnasserybda82502017-03-22 19:08:19 -0700223 } else if (node.type() == DataNode.Type.MULTI_INSTANCE_NODE) {
224 ListKey key = (ListKey) node.key();
225 temp = key.keyLeafs().get(0).leafValAsString().split("\\:");
226 if (temp.length != 3) {
227 throw new RuntimeException(new NetconfException("Invalid device id form, cannot apply"));
228 }
229 ip = temp[1];
230 port = temp[2];
Sithara Punnassery425837f2017-03-21 12:58:00 -0700231 } else {
232 throw new RuntimeException(new NetconfException("Invalid device id type, cannot apply"));
233 }
234 try {
235 return DeviceId.deviceId(new URI("netconf", ip + ":" + port, (String) null));
236 } catch (URISyntaxException var4) {
237 throw new IllegalArgumentException("Unable to build deviceID for device " + ip + ":" + port, var4);
238 }
239 }
240
241 /**
242 * Inititates a Netconf connection to the device.
243 *
244 * @param deviceId of the added device
245 */
246 private void initiateConnection(DeviceId deviceId) {
247 if (controller.getNetconfDevice(deviceId) == null) {
248 try {
249 //if (this.isReachable(deviceId)) {
Thomas Vachuska59d24eb2017-03-22 10:57:34 -0700250 this.controller.connectDevice(deviceId);
Sithara Punnassery425837f2017-03-21 12:58:00 -0700251 //}
252 } catch (Exception ex) {
Thomas Vachuska59d24eb2017-03-22 10:57:34 -0700253 throw new RuntimeException(new NetconfException("Unable to connect to NETCONF device on " +
Sithara Punnassery326b61d2017-03-24 14:15:04 -0700254 deviceId, ex));
255 }
256 }
257 }
258
259 /* Accumulates events to allow processing after a desired number of events were accumulated.
260 */
261 private class InternalEventAccummulator extends AbstractAccumulator<DynamicConfigEvent> {
262 protected InternalEventAccummulator() {
263 super(new Timer("dyncfgevt-timer"), MAX_EVENTS, MAX_BATCH_MS, MAX_IDLE_MS);
264 }
265 @Override
266 public void processItems(List<DynamicConfigEvent> events) {
267 DynamicConfigEvent event = null;
268 for (DynamicConfigEvent e : events) {
269 //checkIfRelevant already filters only the relevant events
270 event = e;
271 checkNotNull(event, "process:Event cannot be null");
Sithara Punnassery326b61d2017-03-24 14:15:04 -0700272 switch (event.type()) {
273 case NODE_ADDED:
274 case NODE_UPDATED:
275 case NODE_REPLACED:
Sithara Punnasserye4ab4f22017-03-27 19:11:00 -0700276 Filter filt = new Filter();
277 DataNode node = cfgService.readNode(event.subject(), filt);
278 DeviceId deviceId = getDeviceId(node);
279 if (!isMaster(deviceId)) {
280 log.info("NetConfListener: not master, ignoring config for {}", event.type());
281 return;
282 }
283 initiateConnection(deviceId);
Sithara Punnassery326b61d2017-03-24 14:15:04 -0700284 configUpdate(node, deviceId, event.subject());
285 break;
286 case NODE_DELETED:
Sithara Punnasserye4ab4f22017-03-27 19:11:00 -0700287 log.info("NetConfListener: RXD DELETE EVT for {}", event.type());
288 //configDelete(null, null, event.subject());
Sithara Punnassery326b61d2017-03-24 14:15:04 -0700289 break;
290 case UNKNOWN_OPRN:
291 default:
292 log.warn("NetConfListener: unknown event: {}", event.type());
293 break;
294 }
Sithara Punnassery425837f2017-03-21 12:58:00 -0700295 }
296 }
Aaron Kruglikov309068e2017-03-17 15:25:54 -0700297 }
298}