70350f89c0f92dc55221b03ae1177f305b27e117
[oam/tr069-adapter.git] / netconf-server / src / main / java / org / commscope / tr069adapter / netconf / rpc / CreateSubscription.java
1 /*
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
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
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=======================================================================
17  */
18
19 package org.commscope.tr069adapter.netconf.rpc;
20
21 import com.google.common.base.Preconditions;
22 import com.google.common.collect.Maps;
23
24 import java.io.File;
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;
32 import java.util.Map;
33 import java.util.Optional;
34 import java.util.concurrent.Executors;
35 import java.util.concurrent.ScheduledExecutorService;
36 import java.util.concurrent.TimeUnit;
37
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;
55
56 public class CreateSubscription extends AbstractLastNetconfOperation
57     implements DefaultNetconfOperation {
58
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;
67
68   public CreateSubscription(final String id, final Optional<File> notificationsFile,
69       String deviceID, String swVersion, String hwVersion) {
70
71     super(id);
72
73     logger.debug("CreateSubscription RPC is created with macID {}", deviceID);
74     this.deviceID = deviceID;
75     this.swVersion = swVersion;
76     this.hwVersion = hwVersion;
77
78     final Optional<Notifications> notifs;
79
80     if (notificationsFile.isPresent()) {
81       notifs = Optional.of(loadNotifications(notificationsFile.get()));
82       scheduledExecutorService = Executors.newScheduledThreadPool(1);
83     } else {
84       notifs = Optional.empty();
85     }
86
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);
95       }
96       this.notifications = preparedMessages;
97     } else {
98       this.notifications = Collections.emptyMap();
99     }
100   }
101
102   private static Notifications loadNotifications(final File file) {
103     try {
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",
109           e);
110     }
111   }
112
113   @Override
114   protected String getOperationName() {
115     return "create-subscription";
116   }
117
118   @Override
119   protected String getOperationNamespace() {
120     return "urn:ietf:params:xml:ns:netconf:notification:1.0";
121   }
122
123   @Override
124   protected Element handleWithNoSubsequentOperations(final Document document,
125       final XmlElement operationElement) {
126     long delayAggregator = 0;
127     for (final Map.Entry<Notification, NetconfMessage> notification : notifications.entrySet()) {
128       for (int i = 0; i <= notification.getKey().getTimes(); i++) {
129
130         delayAggregator += notification.getKey().getDelayInSeconds();
131
132         scheduledExecutorService.schedule(() -> {
133           Preconditions.checkState(session != null,
134               "Session is not set, cannot process notifications");
135           session.sendMessage(notification.getValue());
136         }, delayAggregator, TimeUnit.SECONDS);
137       }
138     }
139     return document.createElement(XmlNetconfConstants.OK);
140   }
141
142   private static NetconfMessage parseNetconfNotification(String content) {
143     final int startEventTime = content.indexOf("<eventTime>") + "<eventTime>".length();
144     final int endEventTime = content.indexOf("</eventTime>");
145     final String eventTime = content.substring(startEventTime, endEventTime);
146     if (eventTime.equals("XXXX")) {
147       content = content.replace(eventTime,
148           new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX").format(new Date()));
149     }
150
151     try {
152       return new NetconfMessage(XmlUtil.readXmlToDocument(content));
153     } catch (SAXException | IOException e) {
154       throw new IllegalArgumentException("Cannot parse notifications", e);
155     }
156   }
157
158   @Override
159   public void setNetconfSession(final NetconfServerSession newSession) {
160     logger.debug("Adding netconf session to notification server sessions map : {}", newSession);
161     logger.debug("This CreateSubscription is setup to support  macID= {}", deviceID);
162     this.session = newSession;
163     netconfServerSessionMap.put(deviceID, newSession);
164   }
165
166   public static void sendNotification(NetconfMessage netconfMessage, String deviceID)
167       throws NetconfNotificationException {
168     logger.debug("Request to send notification. NetConfMessage : {}", netconfMessage);
169     NetconfServerSession session = netconfServerSessionMap.get(deviceID);
170     if (session != null && session.isUp()) {
171       try {
172         session.sendMessage(netconfMessage);
173         logger.debug("Successfully send notification for deviceID: {}", deviceID);
174       } catch (Exception e) {
175         logger.error("Failed to send notification. while posting got error. {}", e.toString());
176         throw new NetconfNotificationException("Exception while posting the netconf message", e);
177       }
178
179     } else {
180       logger.debug(
181           "Failed to send notification. None of valid netconf session is available for {}.",
182           deviceID);
183       logger.debug("Available netconf sessions : {}", netconfServerSessionMap);
184       throw new NetconfNotificationException("NetConf active session deosn't not exist");
185     }
186   }
187
188   @XmlRootElement(name = "notifications")
189   public static final class Notifications {
190
191     @javax.xml.bind.annotation.XmlElement(nillable = false, name = "notification", required = true)
192     private List<Notification> notificationList;
193
194     public List<Notification> getNotificationList() {
195       return notificationList;
196     }
197
198     @Override
199     public String toString() {
200       final StringBuilder sb = new StringBuilder("Notifications{");
201       sb.append("notificationList=").append(notificationList);
202       sb.append('}');
203       return sb.toString();
204     }
205   }
206
207   public static final class Notification {
208
209     @javax.xml.bind.annotation.XmlElement(nillable = false, name = "delay")
210     private long delayInSeconds;
211
212     @javax.xml.bind.annotation.XmlElement(nillable = false, name = "times")
213     private long times;
214
215     @javax.xml.bind.annotation.XmlElement(nillable = false, name = "content", required = true)
216     private String content;
217
218     public long getDelayInSeconds() {
219       return delayInSeconds;
220     }
221
222     public long getTimes() {
223       return times;
224     }
225
226     public String getContent() {
227       return content;
228     }
229
230     @Override
231     public String toString() {
232       final StringBuilder sb = new StringBuilder("Notification{");
233       sb.append("delayInSeconds=").append(delayInSeconds);
234       sb.append(", times=").append(times);
235       sb.append(", content='").append(content).append('\'');
236       sb.append('}');
237       return sb.toString();
238     }
239   }
240 }