ebb2074af1f47101ec07827690a25de833e68a32
[oam/tr069-adapter.git] / netconf-server / src / main / java / org / commscope / tr069adapter / netconf / rpc / CreateSubscription.java
1 /*\r
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
10  *\r
11  * http://www.apache.org/licenses/LICENSE-2.0\r
12  *\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
17  */\r
18 \r
19 package org.commscope.tr069adapter.netconf.rpc;\r
20 \r
21 import com.google.common.base.Preconditions;\r
22 import com.google.common.collect.Maps;\r
23 \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
37 \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
42 \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
55 \r
56 public class CreateSubscription extends AbstractLastNetconfOperation\r
57     implements DefaultNetconfOperation {\r
58 \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
67 \r
68   public CreateSubscription(final String id, final Optional<File> notificationsFile,\r
69       String deviceID, String swVersion, String hwVersion) {\r
70 \r
71     super(id);\r
72 \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
77 \r
78     final Optional<Notifications> notifs;\r
79 \r
80     if (notificationsFile.isPresent()) {\r
81       notifs = Optional.of(loadNotifications(notificationsFile.get()));\r
82       scheduledExecutorService = Executors.newScheduledThreadPool(1);\r
83     } else {\r
84       notifs = Optional.empty();\r
85     }\r
86 \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
95       }\r
96       this.notifications = preparedMessages;\r
97     } else {\r
98       this.notifications = Collections.emptyMap();\r
99     }\r
100   }\r
101 \r
102   private static Notifications loadNotifications(final File file) {\r
103     try {\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
109           e);\r
110     }\r
111   }\r
112 \r
113   @Override\r
114   protected String getOperationName() {\r
115     return "create-subscription";\r
116   }\r
117 \r
118   @Override\r
119   protected String getOperationNamespace() {\r
120     return "urn:ietf:params:xml:ns:netconf:notification:1.0";\r
121   }\r
122 \r
123   @Override\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
129 \r
130         delayAggregator += notification.getKey().getDelayInSeconds();\r
131 \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
137       }\r
138     }\r
139     return document.createElement(XmlNetconfConstants.OK);\r
140   }\r
141 \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
149     }\r
150 \r
151     try {\r
152       return new NetconfMessage(XmlUtil.readXmlToDocument(content));\r
153     } catch (SAXException | IOException e) {\r
154       throw new IllegalArgumentException("Cannot parse notifications", e);\r
155     }\r
156   }\r
157 \r
158   @Override\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
164   }\r
165 \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
172     } else {\r
173       logger.debug("Failed to send notification. None of valid netconf session is available.");\r
174     }\r
175     logger.debug("Available netconf sessions : {}", netconfServerSessionMap);\r
176 \r
177   }\r
178 \r
179   @XmlRootElement(name = "notifications")\r
180   public static final class Notifications {\r
181 \r
182     @javax.xml.bind.annotation.XmlElement(nillable = false, name = "notification", required = true)\r
183     private List<Notification> notificationList;\r
184 \r
185     public List<Notification> getNotificationList() {\r
186       return notificationList;\r
187     }\r
188 \r
189     @Override\r
190     public String toString() {\r
191       final StringBuilder sb = new StringBuilder("Notifications{");\r
192       sb.append("notificationList=").append(notificationList);\r
193       sb.append('}');\r
194       return sb.toString();\r
195     }\r
196   }\r
197 \r
198   public static final class Notification {\r
199 \r
200     @javax.xml.bind.annotation.XmlElement(nillable = false, name = "delay")\r
201     private long delayInSeconds;\r
202 \r
203     @javax.xml.bind.annotation.XmlElement(nillable = false, name = "times")\r
204     private long times;\r
205 \r
206     @javax.xml.bind.annotation.XmlElement(nillable = false, name = "content", required = true)\r
207     private String content;\r
208 \r
209     public long getDelayInSeconds() {\r
210       return delayInSeconds;\r
211     }\r
212 \r
213     public long getTimes() {\r
214       return times;\r
215     }\r
216 \r
217     public String getContent() {\r
218       return content;\r
219     }\r
220 \r
221     @Override\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
227       sb.append('}');\r
228       return sb.toString();\r
229     }\r
230   }\r
231 }\r