exchanging new version yang models on firmware upgrade
[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 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
53 \r
54 public class CreateSubscription extends AbstractLastNetconfOperation\r
55     implements DefaultNetconfOperation {\r
56 \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
65 \r
66   public CreateSubscription(final String id, final Optional<File> notificationsFile,\r
67       String deviceID, String swVersion, String hwVersion) {\r
68 \r
69     super(id);\r
70 \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
75 \r
76     final Optional<Notifications> notifs;\r
77 \r
78     if (notificationsFile.isPresent()) {\r
79       notifs = Optional.of(loadNotifications(notificationsFile.get()));\r
80       scheduledExecutorService = Executors.newScheduledThreadPool(1);\r
81     } else {\r
82       notifs = Optional.empty();\r
83     }\r
84 \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
93       }\r
94       this.notifications = preparedMessages;\r
95     } else {\r
96       this.notifications = Collections.emptyMap();\r
97     }\r
98   }\r
99 \r
100   private static Notifications loadNotifications(final File file) {\r
101     try {\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
107           e);\r
108     }\r
109   }\r
110 \r
111   @Override\r
112   protected String getOperationName() {\r
113     return "create-subscription";\r
114   }\r
115 \r
116   @Override\r
117   protected String getOperationNamespace() {\r
118     return "urn:ietf:params:xml:ns:netconf:notification:1.0";\r
119   }\r
120 \r
121   @Override\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
127 \r
128         delayAggregator += notification.getKey().getDelayInSeconds();\r
129 \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
135       }\r
136     }\r
137     return document.createElement(XmlNetconfConstants.OK);\r
138   }\r
139 \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
147     }\r
148 \r
149     try {\r
150       return new NetconfMessage(XmlUtil.readXmlToDocument(content));\r
151     } catch (SAXException | IOException e) {\r
152       throw new IllegalArgumentException("Cannot parse notifications", e);\r
153     }\r
154   }\r
155 \r
156   @Override\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
162   }\r
163 \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
169       try {\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
175       }\r
176 \r
177     } else {\r
178       logger.debug(\r
179           "Failed to send notification. None of valid netconf session is available for {}.",\r
180           deviceID);\r
181       logger.debug("Available netconf sessions : {}", netconfServerSessionMap);\r
182       throw new NetconfNotificationException("NetConf active session deosn't not exist");\r
183     }\r
184   }\r
185 \r
186   @XmlRootElement(name = "notifications")\r
187   public static final class Notifications {\r
188 \r
189     @javax.xml.bind.annotation.XmlElement(nillable = false, name = "notification", required = true)\r
190     private List<Notification> notificationList;\r
191 \r
192     public List<Notification> getNotificationList() {\r
193       return notificationList;\r
194     }\r
195 \r
196     @Override\r
197     public String toString() {\r
198       final StringBuilder sb = new StringBuilder("Notifications{");\r
199       sb.append("notificationList=").append(notificationList);\r
200       sb.append('}');\r
201       return sb.toString();\r
202     }\r
203   }\r
204 \r
205   public static final class Notification {\r
206 \r
207     @javax.xml.bind.annotation.XmlElement(nillable = false, name = "delay")\r
208     private long delayInSeconds;\r
209 \r
210     @javax.xml.bind.annotation.XmlElement(nillable = false, name = "times")\r
211     private long times;\r
212 \r
213     @javax.xml.bind.annotation.XmlElement(nillable = false, name = "content", required = true)\r
214     private String content;\r
215 \r
216     public long getDelayInSeconds() {\r
217       return delayInSeconds;\r
218     }\r
219 \r
220     public long getTimes() {\r
221       return times;\r
222     }\r
223 \r
224     public String getContent() {\r
225       return content;\r
226     }\r
227 \r
228     @Override\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
234       sb.append('}');\r
235       return sb.toString();\r
236     }\r
237   }\r
238 }\r