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
65 private String swVersion;
\r
66 private String hwVersion;
\r
68 public CreateSubscription(final String id, final Optional<File> notificationsFile,
\r
69 String deviceID, String swVersion, String hwVersion) {
\r
73 logger.debug("CreateSubscription RPC is created with macID {}", deviceID);
\r
74 this.deviceID = deviceID;
\r
75 this.swVersion = swVersion;
\r
76 this.hwVersion = hwVersion;
\r
78 final Optional<Notifications> notifs;
\r
80 if (notificationsFile.isPresent()) {
\r
81 notifs = Optional.of(loadNotifications(notificationsFile.get()));
\r
82 scheduledExecutorService = Executors.newScheduledThreadPool(1);
\r
84 notifs = Optional.empty();
\r
87 if (notifs.isPresent()) {
\r
88 final Collection<Notification> toCopy = notifs.get().getNotificationList();
\r
89 final Map<Notification, NetconfMessage> preparedMessages =
\r
90 Maps.newHashMapWithExpectedSize(toCopy.size());
\r
91 for (final Notification notification : toCopy) {
\r
92 final NetconfMessage parsedNotification =
\r
93 parseNetconfNotification(notification.getContent());
\r
94 preparedMessages.put(notification, parsedNotification);
\r
96 this.notifications = preparedMessages;
\r
98 this.notifications = Collections.emptyMap();
\r
102 private static Notifications loadNotifications(final File file) {
\r
104 final JAXBContext jaxbContext = JAXBContext.newInstance(Notifications.class);
\r
105 final Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
\r
106 return (Notifications) jaxbUnmarshaller.unmarshal(file);
\r
107 } catch (final JAXBException e) {
\r
108 throw new IllegalArgumentException("can not parse file " + file + " as a notifications file",
\r
114 protected String getOperationName() {
\r
115 return "create-subscription";
\r
119 protected String getOperationNamespace() {
\r
120 return "urn:ietf:params:xml:ns:netconf:notification:1.0";
\r
124 protected Element handleWithNoSubsequentOperations(final Document document,
\r
125 final XmlElement operationElement) {
\r
126 long delayAggregator = 0;
\r
127 for (final Map.Entry<Notification, NetconfMessage> notification : notifications.entrySet()) {
\r
128 for (int i = 0; i <= notification.getKey().getTimes(); i++) {
\r
130 delayAggregator += notification.getKey().getDelayInSeconds();
\r
132 scheduledExecutorService.schedule(() -> {
\r
133 Preconditions.checkState(session != null,
\r
134 "Session is not set, cannot process notifications");
\r
135 session.sendMessage(notification.getValue());
\r
136 }, delayAggregator, TimeUnit.SECONDS);
\r
139 return document.createElement(XmlNetconfConstants.OK);
\r
142 private static NetconfMessage parseNetconfNotification(String content) {
\r
143 final int startEventTime = content.indexOf("<eventTime>") + "<eventTime>".length();
\r
144 final int endEventTime = content.indexOf("</eventTime>");
\r
145 final String eventTime = content.substring(startEventTime, endEventTime);
\r
146 if (eventTime.equals("XXXX")) {
\r
147 content = content.replace(eventTime,
\r
148 new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX").format(new Date()));
\r
152 return new NetconfMessage(XmlUtil.readXmlToDocument(content));
\r
153 } catch (SAXException | IOException e) {
\r
154 throw new IllegalArgumentException("Cannot parse notifications", e);
\r
159 public void setNetconfSession(final NetconfServerSession newSession) {
\r
160 logger.debug("Adding netconf session to notification server sessions map : {}", newSession);
\r
161 logger.debug("This CreateSubscription is setup to support macID= {}", deviceID);
\r
162 this.session = newSession;
\r
163 netconfServerSessionMap.put(deviceID, newSession);
\r
166 public static void sendNotification(NetconfMessage netconfMessage, String deviceID) {
\r
167 logger.debug("Request to send notification. NetConfMessage : {}", netconfMessage);
\r
168 NetconfServerSession session = netconfServerSessionMap.get(deviceID);
\r
169 if (session != null && session.isUp()) {
\r
170 session.sendMessage(netconfMessage);
\r
171 logger.debug("Successfully send notification for deviceID: {}", deviceID);
\r
173 logger.debug("Failed to send notification. None of valid netconf session is available.");
\r
175 logger.debug("Available netconf sessions : {}", netconfServerSessionMap);
\r
179 @XmlRootElement(name = "notifications")
\r
180 public static final class Notifications {
\r
182 @javax.xml.bind.annotation.XmlElement(nillable = false, name = "notification", required = true)
\r
183 private List<Notification> notificationList;
\r
185 public List<Notification> getNotificationList() {
\r
186 return notificationList;
\r
190 public String toString() {
\r
191 final StringBuilder sb = new StringBuilder("Notifications{");
\r
192 sb.append("notificationList=").append(notificationList);
\r
194 return sb.toString();
\r
198 public static final class Notification {
\r
200 @javax.xml.bind.annotation.XmlElement(nillable = false, name = "delay")
\r
201 private long delayInSeconds;
\r
203 @javax.xml.bind.annotation.XmlElement(nillable = false, name = "times")
\r
204 private long times;
\r
206 @javax.xml.bind.annotation.XmlElement(nillable = false, name = "content", required = true)
\r
207 private String content;
\r
209 public long getDelayInSeconds() {
\r
210 return delayInSeconds;
\r
213 public long getTimes() {
\r
217 public String getContent() {
\r
222 public String toString() {
\r
223 final StringBuilder sb = new StringBuilder("Notification{");
\r
224 sb.append("delayInSeconds=").append(delayInSeconds);
\r
225 sb.append(", times=").append(times);
\r
226 sb.append(", content='").append(content).append('\'');
\r
228 return sb.toString();
\r