2 * ============LICENSE_START========================================================================
3 * ONAP : tr-069-adapter
4 * =================================================================================================
5 * Copyright (C) 2020 CommScope Inc Intellectual Property.
6 * =================================================================================================
7 * This tr-069-adapter software file is distributed by CommScope Inc under the Apache License,
8 * Version 2.0 (the "License"); you may not use this file except in compliance with the License. You
9 * may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
14 * either express or implied. See the License for the specific language governing permissions and
15 * limitations under the License.
16 * ===============LICENSE_END=======================================================================
19 package org.commscope.tr069adapter.netconf.rpc;
21 import com.google.common.base.Preconditions;
22 import com.google.common.collect.Maps;
25 import java.io.IOException;
26 import java.text.SimpleDateFormat;
27 import java.util.Collection;
28 import java.util.Collections;
29 import java.util.Date;
30 import java.util.HashMap;
31 import java.util.List;
33 import java.util.Optional;
34 import java.util.concurrent.Executors;
35 import java.util.concurrent.ScheduledExecutorService;
36 import java.util.concurrent.TimeUnit;
38 import javax.xml.bind.JAXBContext;
39 import javax.xml.bind.JAXBException;
40 import javax.xml.bind.Unmarshaller;
41 import javax.xml.bind.annotation.XmlRootElement;
42 import org.commscope.tr069adapter.netconf.error.NetconfNotificationException;
43 import org.opendaylight.netconf.api.NetconfMessage;
44 import org.opendaylight.netconf.api.xml.XmlElement;
45 import org.opendaylight.netconf.api.xml.XmlNetconfConstants;
46 import org.opendaylight.netconf.api.xml.XmlUtil;
47 import org.opendaylight.netconf.impl.NetconfServerSession;
48 import org.opendaylight.netconf.impl.mapping.operations.DefaultNetconfOperation;
49 import org.opendaylight.netconf.util.mapping.AbstractLastNetconfOperation;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
52 import org.w3c.dom.Document;
53 import org.w3c.dom.Element;
54 import org.xml.sax.SAXException;
56 public class CreateSubscription extends AbstractLastNetconfOperation
57 implements DefaultNetconfOperation {
59 private static final Logger logger = LoggerFactory.getLogger(CreateSubscription.class);
60 private final Map<Notification, NetconfMessage> notifications;
61 private NetconfServerSession session;
62 private ScheduledExecutorService scheduledExecutorService;
63 private static Map<String, NetconfServerSession> netconfServerSessionMap = new HashMap<>();
64 private String deviceID;
65 private String swVersion;
66 private String hwVersion;
68 public CreateSubscription(final String id, final Optional<File> notificationsFile,
69 String deviceID, String swVersion, String hwVersion) {
73 logger.debug("CreateSubscription RPC is created with macID {}", deviceID);
74 this.deviceID = deviceID;
75 this.swVersion = swVersion;
76 this.hwVersion = hwVersion;
78 final Optional<Notifications> notifs;
80 if (notificationsFile.isPresent()) {
81 notifs = Optional.of(loadNotifications(notificationsFile.get()));
82 scheduledExecutorService = Executors.newScheduledThreadPool(1);
84 notifs = Optional.empty();
87 if (notifs.isPresent()) {
88 final Collection<Notification> toCopy = notifs.get().getNotificationList();
89 final Map<Notification, NetconfMessage> preparedMessages =
90 Maps.newHashMapWithExpectedSize(toCopy.size());
91 for (final Notification notification : toCopy) {
92 final NetconfMessage parsedNotification =
93 parseNetconfNotification(notification.getContent());
94 preparedMessages.put(notification, parsedNotification);
96 this.notifications = preparedMessages;
98 this.notifications = Collections.emptyMap();
102 private static Notifications loadNotifications(final File file) {
104 final JAXBContext jaxbContext = JAXBContext.newInstance(Notifications.class);
105 final Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
106 return (Notifications) jaxbUnmarshaller.unmarshal(file);
107 } catch (final JAXBException e) {
108 throw new IllegalArgumentException("can not parse file " + file + " as a notifications file",
114 protected String getOperationName() {
115 return "create-subscription";
119 protected String getOperationNamespace() {
120 return "urn:ietf:params:xml:ns:netconf:notification:1.0";
123 public String getSwVersion() {
127 public String getHwVersion() {
132 protected Element handleWithNoSubsequentOperations(final Document document,
133 final XmlElement operationElement) {
134 long delayAggregator = 0;
135 for (final Map.Entry<Notification, NetconfMessage> notification : notifications.entrySet()) {
136 for (int i = 0; i <= notification.getKey().getTimes(); i++) {
138 delayAggregator += notification.getKey().getDelayInSeconds();
140 scheduledExecutorService.schedule(() -> {
141 Preconditions.checkState(session != null,
142 "Session is not set, cannot process notifications");
143 session.sendMessage(notification.getValue());
144 }, delayAggregator, TimeUnit.SECONDS);
147 return document.createElement(XmlNetconfConstants.OK);
150 private static NetconfMessage parseNetconfNotification(String content) {
151 final int startEventTime = content.indexOf("<eventTime>") + "<eventTime>".length();
152 final int endEventTime = content.indexOf("</eventTime>");
153 final String eventTime = content.substring(startEventTime, endEventTime);
154 if (eventTime.equals("XXXX")) {
155 content = content.replace(eventTime,
156 new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX").format(new Date()));
160 return new NetconfMessage(XmlUtil.readXmlToDocument(content));
161 } catch (SAXException | IOException e) {
162 throw new IllegalArgumentException("Cannot parse notifications", e);
167 public void setNetconfSession(final NetconfServerSession newSession) {
168 logger.debug("Adding netconf session to notification server sessions map : {}", newSession);
169 logger.debug("This CreateSubscription is setup to support macID= {}", deviceID);
170 this.session = newSession;
171 netconfServerSessionMap.put(deviceID, newSession);
174 public static void sendNotification(NetconfMessage netconfMessage, String deviceID)
175 throws NetconfNotificationException {
176 logger.debug("Request to send notification. NetConfMessage : {}", netconfMessage);
177 NetconfServerSession session = netconfServerSessionMap.get(deviceID);
178 if (session != null && session.isUp()) {
180 session.sendMessage(netconfMessage);
181 logger.debug("Successfully send notification for deviceID: {}", deviceID);
182 } catch (Exception e) {
183 logger.error("Failed to send notification. while posting got error. {}", e.toString());
184 throw new NetconfNotificationException("Exception while posting the netconf message", e);
189 "Failed to send notification. None of valid netconf session is available for {}.",
191 logger.debug("Available netconf sessions : {}", netconfServerSessionMap);
192 throw new NetconfNotificationException("NetConf active session deosn't not exist");
196 @XmlRootElement(name = "notifications")
197 public static final class Notifications {
199 @javax.xml.bind.annotation.XmlElement(nillable = false, name = "notification", required = true)
200 private List<Notification> notificationList;
202 public List<Notification> getNotificationList() {
203 return notificationList;
207 public String toString() {
208 final StringBuilder sb = new StringBuilder("Notifications{");
209 sb.append("notificationList=").append(notificationList);
211 return sb.toString();
215 public static final class Notification {
217 @javax.xml.bind.annotation.XmlElement(nillable = false, name = "delay")
218 private long delayInSeconds;
220 @javax.xml.bind.annotation.XmlElement(nillable = false, name = "times")
223 @javax.xml.bind.annotation.XmlElement(nillable = false, name = "content", required = true)
224 private String content;
226 public long getDelayInSeconds() {
227 return delayInSeconds;
230 public long getTimes() {
234 public String getContent() {
239 public String toString() {
240 final StringBuilder sb = new StringBuilder("Notification{");
241 sb.append("delayInSeconds=").append(delayInSeconds);
242 sb.append(", times=").append(times);
243 sb.append(", content='").append(content).append('\'');
245 return sb.toString();