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 java.io.File;
\r
22 import java.io.IOException;
\r
23 import java.text.SimpleDateFormat;
\r
24 import java.util.Collection;
\r
25 import java.util.Collections;
\r
26 import java.util.Date;
\r
27 import java.util.HashMap;
\r
28 import java.util.List;
\r
29 import java.util.Map;
\r
30 import java.util.Optional;
\r
31 import java.util.concurrent.Executors;
\r
32 import java.util.concurrent.ScheduledExecutorService;
\r
33 import java.util.concurrent.TimeUnit;
\r
34 import javax.xml.bind.JAXBContext;
\r
35 import javax.xml.bind.JAXBException;
\r
36 import javax.xml.bind.Unmarshaller;
\r
37 import javax.xml.bind.annotation.XmlRootElement;
\r
38 import org.commscope.tr069adapter.netconf.error.NetconfNotificationException;
\r
39 import org.opendaylight.netconf.api.NetconfMessage;
\r
40 import org.opendaylight.netconf.api.xml.XmlElement;
\r
41 import org.opendaylight.netconf.api.xml.XmlNetconfConstants;
\r
42 import org.opendaylight.netconf.api.xml.XmlUtil;
\r
43 import org.opendaylight.netconf.impl.NetconfServerSession;
\r
44 import org.opendaylight.netconf.impl.mapping.operations.DefaultNetconfOperation;
\r
45 import org.opendaylight.netconf.util.mapping.AbstractLastNetconfOperation;
\r
46 import org.slf4j.Logger;
\r
47 import org.slf4j.LoggerFactory;
\r
48 import org.w3c.dom.Document;
\r
49 import org.w3c.dom.Element;
\r
50 import org.xml.sax.SAXException;
\r
51 import com.google.common.base.Preconditions;
\r
52 import com.google.common.collect.Maps;
\r
54 public class CreateSubscription extends AbstractLastNetconfOperation
\r
55 implements DefaultNetconfOperation {
\r
57 private static final Logger logger = LoggerFactory.getLogger(CreateSubscription.class);
\r
58 private final Map<Notification, NetconfMessage> notifications;
\r
59 private NetconfServerSession session;
\r
60 private ScheduledExecutorService scheduledExecutorService;
\r
61 private static Map<String, NetconfServerSession> netconfServerSessionMap = new HashMap<>();
\r
62 private String deviceID;
\r
63 private String swVersion;
\r
64 private String hwVersion;
\r
66 public CreateSubscription(final String id, final Optional<File> notificationsFile,
\r
67 String deviceID, String swVersion, String hwVersion) {
\r
71 logger.debug("CreateSubscription RPC is created with macID {}", deviceID);
\r
72 this.deviceID = deviceID;
\r
73 this.swVersion = swVersion;
\r
74 this.hwVersion = hwVersion;
\r
76 final Optional<Notifications> notifs;
\r
78 if (notificationsFile.isPresent()) {
\r
79 notifs = Optional.of(loadNotifications(notificationsFile.get()));
\r
80 scheduledExecutorService = Executors.newScheduledThreadPool(1);
\r
82 notifs = Optional.empty();
\r
85 if (notifs.isPresent()) {
\r
86 final Collection<Notification> toCopy = notifs.get().getNotificationList();
\r
87 final Map<Notification, NetconfMessage> preparedMessages =
\r
88 Maps.newHashMapWithExpectedSize(toCopy.size());
\r
89 for (final Notification notification : toCopy) {
\r
90 final NetconfMessage parsedNotification =
\r
91 parseNetconfNotification(notification.getContent());
\r
92 preparedMessages.put(notification, parsedNotification);
\r
94 this.notifications = preparedMessages;
\r
96 this.notifications = Collections.emptyMap();
\r
100 private static Notifications loadNotifications(final File file) {
\r
102 final JAXBContext jaxbContext = JAXBContext.newInstance(Notifications.class);
\r
103 final Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
\r
104 return (Notifications) jaxbUnmarshaller.unmarshal(file);
\r
105 } catch (final JAXBException e) {
\r
106 throw new IllegalArgumentException("can not parse file " + file + " as a notifications file",
\r
112 protected String getOperationName() {
\r
113 return "create-subscription";
\r
117 protected String getOperationNamespace() {
\r
118 return "urn:ietf:params:xml:ns:netconf:notification:1.0";
\r
122 protected Element handleWithNoSubsequentOperations(final Document document,
\r
123 final XmlElement operationElement) {
\r
124 long delayAggregator = 0;
\r
125 for (final Map.Entry<Notification, NetconfMessage> notification : notifications.entrySet()) {
\r
126 for (int i = 0; i <= notification.getKey().getTimes(); i++) {
\r
128 delayAggregator += notification.getKey().getDelayInSeconds();
\r
130 scheduledExecutorService.schedule(() -> {
\r
131 Preconditions.checkState(session != null,
\r
132 "Session is not set, cannot process notifications");
\r
133 session.sendMessage(notification.getValue());
\r
134 }, delayAggregator, TimeUnit.SECONDS);
\r
137 return document.createElement(XmlNetconfConstants.OK);
\r
140 private static NetconfMessage parseNetconfNotification(String content) {
\r
141 final int startEventTime = content.indexOf("<eventTime>") + "<eventTime>".length();
\r
142 final int endEventTime = content.indexOf("</eventTime>");
\r
143 final String eventTime = content.substring(startEventTime, endEventTime);
\r
144 if (eventTime.equals("XXXX")) {
\r
145 content = content.replace(eventTime,
\r
146 new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX").format(new Date()));
\r
150 return new NetconfMessage(XmlUtil.readXmlToDocument(content));
\r
151 } catch (SAXException | IOException e) {
\r
152 throw new IllegalArgumentException("Cannot parse notifications", e);
\r
157 public void setNetconfSession(final NetconfServerSession newSession) {
\r
158 logger.debug("Adding netconf session to notification server sessions map : {}", newSession);
\r
159 logger.debug("This CreateSubscription is setup to support macID= {}", deviceID);
\r
160 this.session = newSession;
\r
161 netconfServerSessionMap.put(deviceID, newSession);
\r
164 public static void sendNotification(NetconfMessage netconfMessage, String deviceID)
\r
165 throws NetconfNotificationException {
\r
166 logger.debug("Request to send notification. NetConfMessage : {}", netconfMessage);
\r
167 NetconfServerSession session = netconfServerSessionMap.get(deviceID);
\r
168 if (session != null && session.isUp()) {
\r
170 session.sendMessage(netconfMessage);
\r
171 logger.debug("Successfully send notification for deviceID: {}", deviceID);
\r
172 } catch (Exception e) {
\r
173 logger.error("Failed to send notification. while posting got error. {}", e.toString());
\r
174 throw new NetconfNotificationException("Exception while posting the netconf message", e);
\r
179 "Failed to send notification. None of valid netconf session is available for {}.",
\r
181 logger.debug("Available netconf sessions : {}", netconfServerSessionMap);
\r
182 throw new NetconfNotificationException("NetConf active session deosn't not exist");
\r
186 @XmlRootElement(name = "notifications")
\r
187 public static final class Notifications {
\r
189 @javax.xml.bind.annotation.XmlElement(nillable = false, name = "notification", required = true)
\r
190 private List<Notification> notificationList;
\r
192 public List<Notification> getNotificationList() {
\r
193 return notificationList;
\r
197 public String toString() {
\r
198 final StringBuilder sb = new StringBuilder("Notifications{");
\r
199 sb.append("notificationList=").append(notificationList);
\r
201 return sb.toString();
\r
205 public static final class Notification {
\r
207 @javax.xml.bind.annotation.XmlElement(nillable = false, name = "delay")
\r
208 private long delayInSeconds;
\r
210 @javax.xml.bind.annotation.XmlElement(nillable = false, name = "times")
\r
211 private long times;
\r
213 @javax.xml.bind.annotation.XmlElement(nillable = false, name = "content", required = true)
\r
214 private String content;
\r
216 public long getDelayInSeconds() {
\r
217 return delayInSeconds;
\r
220 public long getTimes() {
\r
224 public String getContent() {
\r
229 public String toString() {
\r
230 final StringBuilder sb = new StringBuilder("Notification{");
\r
231 sb.append("delayInSeconds=").append(delayInSeconds);
\r
232 sb.append(", times=").append(times);
\r
233 sb.append(", content='").append(content).append('\'');
\r
235 return sb.toString();
\r