2 * ============LICENSE_START========================================================================
\r
3 * ONAP : tr-069-adapter
\r
4 * =================================================================================================
\r
5 * Copyright (C) 2020 CommScope Inc Intellectual Property.
\r
6 * =================================================================================================
\r
7 * This tr-069-adapter software file is distributed by CommScope Inc under the Apache License,
\r
8 * Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
\r
9 * may obtain a copy of the License at
\r
11 * http://www.apache.org/licenses/LICENSE-2.0
\r
13 * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
\r
14 * either express or implied. See the License for the specific language governing permissions and
\r
15 * limitations under the License.
\r
16 * ===============LICENSE_END=======================================================================
\r
19 package org.commscope.tr069adapter.netconf.rpc;
\r
21 import com.google.common.base.Preconditions;
\r
22 import com.google.common.collect.Maps;
\r
24 import java.io.File;
\r
25 import java.io.IOException;
\r
26 import java.text.SimpleDateFormat;
\r
27 import java.util.Collection;
\r
28 import java.util.Collections;
\r
29 import java.util.Date;
\r
30 import java.util.HashMap;
\r
31 import java.util.List;
\r
32 import java.util.Map;
\r
33 import java.util.Optional;
\r
34 import java.util.concurrent.Executors;
\r
35 import java.util.concurrent.ScheduledExecutorService;
\r
36 import java.util.concurrent.TimeUnit;
\r
38 import javax.xml.bind.JAXBContext;
\r
39 import javax.xml.bind.JAXBException;
\r
40 import javax.xml.bind.Unmarshaller;
\r
41 import javax.xml.bind.annotation.XmlRootElement;
\r
43 import org.opendaylight.netconf.api.NetconfMessage;
\r
44 import org.opendaylight.netconf.api.xml.XmlElement;
\r
45 import org.opendaylight.netconf.api.xml.XmlNetconfConstants;
\r
46 import org.opendaylight.netconf.api.xml.XmlUtil;
\r
47 import org.opendaylight.netconf.impl.NetconfServerSession;
\r
48 import org.opendaylight.netconf.impl.mapping.operations.DefaultNetconfOperation;
\r
49 import org.opendaylight.netconf.util.mapping.AbstractLastNetconfOperation;
\r
50 import org.slf4j.Logger;
\r
51 import org.slf4j.LoggerFactory;
\r
52 import org.w3c.dom.Document;
\r
53 import org.w3c.dom.Element;
\r
54 import org.xml.sax.SAXException;
\r
56 public class CreateSubscription extends AbstractLastNetconfOperation
\r
57 implements DefaultNetconfOperation {
\r
59 private static final Logger logger = LoggerFactory.getLogger(CreateSubscription.class);
\r
60 private final Map<Notification, NetconfMessage> notifications;
\r
61 private NetconfServerSession session;
\r
62 private ScheduledExecutorService scheduledExecutorService;
\r
63 private static Map<String, NetconfServerSession> netconfServerSessionMap = new HashMap<>();
\r
64 private String deviceID;
\r
66 public CreateSubscription(final String id, final Optional<File> notificationsFile,
\r
71 logger.debug("CreateSubscription RPC is created with macID {}", deviceID);
\r
72 this.deviceID = deviceID;
\r
74 final Optional<Notifications> notifs;
\r
76 if (notificationsFile.isPresent()) {
\r
77 notifs = Optional.of(loadNotifications(notificationsFile.get()));
\r
78 scheduledExecutorService = Executors.newScheduledThreadPool(1);
\r
80 notifs = Optional.empty();
\r
83 if (notifs.isPresent()) {
\r
84 final Collection<Notification> toCopy = notifs.get().getNotificationList();
\r
85 final Map<Notification, NetconfMessage> preparedMessages =
\r
86 Maps.newHashMapWithExpectedSize(toCopy.size());
\r
87 for (final Notification notification : toCopy) {
\r
88 final NetconfMessage parsedNotification =
\r
89 parseNetconfNotification(notification.getContent());
\r
90 preparedMessages.put(notification, parsedNotification);
\r
92 this.notifications = preparedMessages;
\r
94 this.notifications = Collections.emptyMap();
\r
98 private static Notifications loadNotifications(final File file) {
\r
100 final JAXBContext jaxbContext = JAXBContext.newInstance(Notifications.class);
\r
101 final Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
\r
102 return (Notifications) jaxbUnmarshaller.unmarshal(file);
\r
103 } catch (final JAXBException e) {
\r
104 throw new IllegalArgumentException("can not parse file " + file + " as a notifications file",
\r
110 protected String getOperationName() {
\r
111 return "create-subscription";
\r
115 protected String getOperationNamespace() {
\r
116 return "urn:ietf:params:xml:ns:netconf:notification:1.0";
\r
120 protected Element handleWithNoSubsequentOperations(final Document document,
\r
121 final XmlElement operationElement) {
\r
122 long delayAggregator = 0;
\r
123 for (final Map.Entry<Notification, NetconfMessage> notification : notifications.entrySet()) {
\r
124 for (int i = 0; i <= notification.getKey().getTimes(); i++) {
\r
126 delayAggregator += notification.getKey().getDelayInSeconds();
\r
128 scheduledExecutorService.schedule(() -> {
\r
129 Preconditions.checkState(session != null,
\r
130 "Session is not set, cannot process notifications");
\r
131 session.sendMessage(notification.getValue());
\r
132 }, delayAggregator, TimeUnit.SECONDS);
\r
135 return document.createElement(XmlNetconfConstants.OK);
\r
138 private static NetconfMessage parseNetconfNotification(String content) {
\r
139 final int startEventTime = content.indexOf("<eventTime>") + "<eventTime>".length();
\r
140 final int endEventTime = content.indexOf("</eventTime>");
\r
141 final String eventTime = content.substring(startEventTime, endEventTime);
\r
142 if (eventTime.equals("XXXX")) {
\r
143 content = content.replace(eventTime,
\r
144 new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX").format(new Date()));
\r
148 return new NetconfMessage(XmlUtil.readXmlToDocument(content));
\r
149 } catch (SAXException | IOException e) {
\r
150 throw new IllegalArgumentException("Cannot parse notifications", e);
\r
155 public void setNetconfSession(final NetconfServerSession newSession) {
\r
156 logger.debug("Adding netconf session to notification server sessions map : {}", newSession);
\r
157 logger.debug("This CreateSubscription is setup to support macID= {}", deviceID);
\r
158 this.session = newSession;
\r
159 netconfServerSessionMap.put(deviceID, newSession);
\r
162 public static void sendNotification(NetconfMessage netconfMessage, String deviceID) {
\r
163 logger.debug("Request to send notification. NetConfMessage : {}", netconfMessage);
\r
164 NetconfServerSession session = netconfServerSessionMap.get(deviceID);
\r
165 if (session != null && session.isUp()) {
\r
166 session.sendMessage(netconfMessage);
\r
167 logger.debug("Successfully send notification for deviceID: {}", deviceID);
\r
169 logger.debug("Failed to send notification. None of valid netconf session is available.");
\r
171 logger.debug("Available netconf sessions : {}", netconfServerSessionMap);
\r
175 @XmlRootElement(name = "notifications")
\r
176 public static final class Notifications {
\r
178 @javax.xml.bind.annotation.XmlElement(nillable = false, name = "notification", required = true)
\r
179 private List<Notification> notificationList;
\r
181 public List<Notification> getNotificationList() {
\r
182 return notificationList;
\r
186 public String toString() {
\r
187 final StringBuilder sb = new StringBuilder("Notifications{");
\r
188 sb.append("notificationList=").append(notificationList);
\r
190 return sb.toString();
\r
194 public static final class Notification {
\r
196 @javax.xml.bind.annotation.XmlElement(nillable = false, name = "delay")
\r
197 private long delayInSeconds;
\r
199 @javax.xml.bind.annotation.XmlElement(nillable = false, name = "times")
\r
200 private long times;
\r
202 @javax.xml.bind.annotation.XmlElement(nillable = false, name = "content", required = true)
\r
203 private String content;
\r
205 public long getDelayInSeconds() {
\r
206 return delayInSeconds;
\r
209 public long getTimes() {
\r
213 public String getContent() {
\r
218 public String toString() {
\r
219 final StringBuilder sb = new StringBuilder("Notification{");
\r
220 sb.append("delayInSeconds=").append(delayInSeconds);
\r
221 sb.append(", times=").append(times);
\r
222 sb.append(", content='").append(content).append('\'');
\r
224 return sb.toString();
\r