exchanging new version yang models on firmware upgrade
[oam/tr069-adapter.git] / netconf-server / src / main / java / org / commscope / tr069adapter / netconf / server / NetConfServerManagerImpl.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.server;\r
20 \r
21 import java.io.File;\r
22 import java.io.IOException;\r
23 import java.net.URI;\r
24 import java.net.URISyntaxException;\r
25 import java.util.ArrayList;\r
26 import java.util.List;\r
27 import java.util.Map;\r
28 import java.util.Map.Entry;\r
29 import java.util.concurrent.ExecutorService;\r
30 import java.util.concurrent.Executors;\r
31 import org.apache.commons.io.FileUtils;\r
32 import org.commscope.tr069adapter.acs.common.OperationDetails;\r
33 import org.commscope.tr069adapter.acs.common.ParameterDTO;\r
34 import org.commscope.tr069adapter.acs.common.dto.TR069OperationCode;\r
35 import org.commscope.tr069adapter.common.deviceversion.DeviceVersionManager;\r
36 import org.commscope.tr069adapter.common.deviceversion.ProfileDefinition;\r
37 import org.commscope.tr069adapter.mapper.model.NetConfServerDetails;\r
38 import org.commscope.tr069adapter.mapper.model.NetconfServerManagementError;\r
39 import org.commscope.tr069adapter.mapper.model.VESNotification;\r
40 import org.commscope.tr069adapter.mapper.model.VESNotificationResponse;\r
41 import org.commscope.tr069adapter.netconf.config.NetConfServerProperties;\r
42 import org.commscope.tr069adapter.netconf.dao.NetConfServerDetailsRepository;\r
43 import org.commscope.tr069adapter.netconf.entity.NetConfServerDetailsEntity;\r
44 import org.commscope.tr069adapter.netconf.error.RetryFailedException;\r
45 import org.commscope.tr069adapter.netconf.error.ServerPortAllocationException;\r
46 import org.commscope.tr069adapter.netconf.server.helper.ServerPortAllocationHelper;\r
47 import org.commscope.tr069adapter.netconf.server.utils.NetConfServerConstants;\r
48 import org.commscope.tr069adapter.netconf.server.ves.VESNotificationSender;\r
49 import org.slf4j.Logger;\r
50 import org.slf4j.LoggerFactory;\r
51 import org.springframework.beans.factory.annotation.Autowired;\r
52 import org.springframework.http.HttpEntity;\r
53 import org.springframework.http.HttpHeaders;\r
54 import org.springframework.stereotype.Component;\r
55 import org.springframework.web.client.RestTemplate;\r
56 \r
57 @Component\r
58 public class NetConfServerManagerImpl {\r
59 \r
60   private static final Logger LOG = LoggerFactory.getLogger(NetConfServerManagerImpl.class);\r
61 \r
62   @Autowired\r
63   ServerPortAllocationHelper serverPortAllocator;\r
64 \r
65   @Autowired\r
66   NetConfServerDetailsRepository netconfDAO;\r
67 \r
68   @Autowired\r
69   NetConfServerProperties config;\r
70 \r
71   @Autowired\r
72   NetconfServerStarter ncServerStarter;\r
73 \r
74   @Autowired\r
75   RestartNetconfServerHandler restartServersHandler;\r
76 \r
77   @Autowired\r
78   VESNotificationSender vesNotificationSender;\r
79 \r
80   @Autowired\r
81   DeviceVersionManager versionManager;\r
82 \r
83   ExecutorService executorService = Executors.newFixedThreadPool(10);\r
84 \r
85   public boolean loadSchemas() {\r
86     LOG.debug("Loading yang schema started");\r
87     List<ProfileDefinition> profiles = versionManager.getSupportedProfileDefinitions();\r
88     try {\r
89       String commonSchemaPath = config.getSchemaDirPath() + "/common";\r
90 \r
91       for (ProfileDefinition profile : profiles) {\r
92         String verSpecificSchemaPath =\r
93             config.getSchemaDirPath() + File.separator + profile.getNetConfSchemaPath();\r
94         File schemaDir = new File(commonSchemaPath);\r
95         File schemaVerDir = new File(verSpecificSchemaPath);\r
96 \r
97         if (!schemaVerDir.isDirectory()) {\r
98           LOG.error("No folder path found for given version path {}",\r
99               schemaVerDir.getAbsolutePath());\r
100           return false;\r
101         }\r
102 \r
103         try {\r
104           FileUtils.copyDirectory(schemaDir, schemaVerDir);\r
105         } catch (IOException e) {\r
106           LOG.error("Failed to copy directory " + e.getMessage());\r
107         }\r
108         boolean isSchemaLoaded = ncServerStarter.loadSchemas(schemaVerDir);\r
109         if (!isSchemaLoaded) {\r
110           LOG.debug("Failed to load schema for profile {}", profile.getProfileId());\r
111           return false;\r
112         }\r
113       }\r
114     } catch (Exception e) {\r
115       LOG.error("Load schema's failed in netconf server {}", e.getMessage());\r
116       return false;\r
117     }\r
118     LOG.debug("Loading yang schema completed");\r
119     return true;\r
120   }\r
121 \r
122   public void restartServers() {\r
123     LOG.debug("Restarting all netconf servers during startup...");\r
124     Iterable<NetConfServerDetailsEntity> entities = netconfDAO.findAll();\r
125 \r
126     for (NetConfServerDetailsEntity entity : entities) {\r
127       boolean isReserved = serverPortAllocator.checkAndReserveServerPort(entity.getListenPort());\r
128       if (isReserved) {\r
129         ServerStartTask task = new ServerStartTask(entity, this);\r
130         executorService.execute(task);\r
131       } else {\r
132         try {\r
133           restartServersHandler.restart(entity);\r
134         } catch (RetryFailedException e) {\r
135           LOG.error("submit task for restarting is failed {}", e.toString());\r
136         }\r
137       }\r
138     }\r
139     LOG.debug("Restarting netconf servers during startup is completed.");\r
140   }\r
141 \r
142   public NetConfServerDetails createServer(String deviceId, String enodeBName, String swVersion,\r
143       String hwVersion) {\r
144     NetConfServerDetails result = new NetConfServerDetails();\r
145     NetConfServerDetailsEntity entity = null;\r
146     if (deviceId != null) {\r
147       entity = netconfDAO.findByDeviceId(deviceId);\r
148     } else if (enodeBName != null) {\r
149       entity = netconfDAO.findByEnodeBName(enodeBName);\r
150     } else {\r
151       // none is specified\r
152       LOG.error(\r
153           "Both deviceID and enodeBName are null. Hence failed to create the netconf server.");\r
154       return null;\r
155     }\r
156 \r
157     if (null != entity && ncServerStarter.isNetConfServerRunning(deviceId)) {\r
158       if (isVersionChanged(entity, swVersion, hwVersion)) {\r
159         return restartOnVersionChange(deviceId, enodeBName, swVersion, hwVersion);\r
160       }\r
161 \r
162       // found the entity. server is already running. double check and run\r
163       // if\r
164       // required. else return the details.\r
165 \r
166       // update the ENB Name if Changed\r
167       entity.setEnodeBName(enodeBName);\r
168       netconfDAO.save(entity);\r
169       result = getNetConfServerDetails(deviceId, entity);\r
170       return result;\r
171     }\r
172 \r
173     try {\r
174 \r
175       String port = serverPortAllocator.reserveServerPort();\r
176       if (port == null) {\r
177         result.setError(NetconfServerManagementError.PORT_NOT_AVAILBLE);\r
178         LOG.error(\r
179             "All ports are exhausted. Hence cannot allocate a port to start new netconf server");\r
180         return result;\r
181       } else {\r
182         LOG.debug("Successfully reserved a port for deviceID={} ,port={}", deviceId, port);\r
183 \r
184         // start the server\r
185         boolean isServerStarted = ncServerStarter.startServer(port, deviceId, swVersion, hwVersion);\r
186         boolean isPortInUse = serverPortAllocator.isServerPortInUse(port);\r
187 \r
188         if (!isServerStarted || !isPortInUse) {\r
189           LOG.error(\r
190               "Failed to start netconf server for deviceID: {}, at port:{} , isServerStarted={} ,isPortInUse={}",\r
191               deviceId, port, isServerStarted, isPortInUse);\r
192           result.setError(NetconfServerManagementError.PORT_IN_USE);\r
193           return result;\r
194         }\r
195       }\r
196 \r
197       // save the record in db\r
198       entity = new NetConfServerDetailsEntity();\r
199       entity.setDeviceId(deviceId);\r
200       entity.setListenPort(port);\r
201       entity.setEnodeBName(enodeBName);\r
202       entity.setSwVersion(swVersion);\r
203       entity.setHwVersion(hwVersion);\r
204       netconfDAO.save(entity);\r
205 \r
206       result = getNetConfServerDetails(deviceId, entity);\r
207       LOG.debug("Successfully started netconf server for deviceID= {}, port={}", deviceId, port);\r
208 \r
209     } catch (ServerPortAllocationException e) {\r
210       LOG.error("Failed to allocate a port {}", e.toString());\r
211     }\r
212 \r
213     if (entity != null) {\r
214       result.setDeviceId(deviceId);\r
215       result.setListenPort(entity.getListenPort());\r
216       String netconfListenAddress = getServiceHost();\r
217       if (netconfListenAddress == null) {\r
218         netconfListenAddress = config.getNetconfServerIP();\r
219       }\r
220       result.setListenAddress(netconfListenAddress);\r
221       result.setError(NetconfServerManagementError.SUCCESS);\r
222     }\r
223     return result;\r
224   }\r
225 \r
226   public NetConfServerDetails restartOnVersionChange(String deviceId, String enodeBName,\r
227       String swVersion, String hwVersion) {\r
228 \r
229     NetConfServerDetailsEntity entity = null;\r
230     if (deviceId != null) {\r
231       entity = netconfDAO.findByDeviceId(deviceId);\r
232     }\r
233     if (entity == null) {\r
234       return null;\r
235     }\r
236 \r
237     boolean isVersionChanged = isVersionChanged(entity, swVersion, hwVersion);\r
238     if (isVersionChanged) {\r
239       LOG.debug("software version changed, stopping the the existing netconf instance");\r
240       boolean result = this.ncServerStarter.stopServer(deviceId);\r
241       if (result) {\r
242         LOG.debug(\r
243             "successfully stopped the netconf instance; trying to start with new version yang models");\r
244         entity.setSwVersion(swVersion);\r
245         entity.setHwVersion(hwVersion);\r
246         netconfDAO.save(entity);\r
247 \r
248         boolean isSuccess = startNetConfServerInstance(entity);\r
249 \r
250         if (!isSuccess) {\r
251           try {\r
252             restartServersHandler.restart(entity);\r
253           } catch (RetryFailedException e) {\r
254             LOG.debug("");\r
255           }\r
256         }\r
257       }\r
258     }\r
259     return getNetConfServerDetails(deviceId, entity);\r
260   }\r
261 \r
262 \r
263   public boolean startNetConfServerInstance(NetConfServerDetailsEntity entity) {\r
264     boolean isSuccess = false;\r
265 \r
266     boolean isServerStarted = ncServerStarter.startServer(entity.getListenPort(),\r
267         entity.getDeviceId(), entity.getSwVersion(), entity.getHwVersion());\r
268     if (isServerStarted) {\r
269       LOG.info("Successfully restarted NETCONF server {}  on port {} .",\r
270           entity.getDeviceId(), entity.getListenPort());\r
271       // we need to push the pnfEntry for IP updated\r
272       NetConfServerDetails details = getNetConfServerDetails(entity.getDeviceId(), entity);\r
273 \r
274       final String baseUrl = config.getMapperPath() + "/registerNetconfServer";\r
275       URI uri = null;\r
276       try {\r
277         uri = new URI(baseUrl);\r
278       } catch (URISyntaxException e) {\r
279         LOG.error("error while contructing the URI {}", e.toString());\r
280       }\r
281       RestTemplate restTemplate = new RestTemplate();\r
282       HttpHeaders headers = new HttpHeaders();\r
283       HttpEntity<NetConfServerDetails> httpentity = new HttpEntity<>(details, headers);\r
284       if (uri != null) {\r
285         isSuccess = restTemplate.postForObject(uri, httpentity, Boolean.class);\r
286       }\r
287 \r
288       if (!isSuccess) {\r
289         LOG.error("Netconf Register request is failed update the updated host details..");\r
290       } else {\r
291         LOG.debug("successfully started the server");\r
292       }\r
293     } else {\r
294       LOG.error("Failed to restart NETCONF server {}  on port {}  upon application startup.",\r
295           entity.getDeviceId(), entity.getListenPort());\r
296     }\r
297     return isSuccess;\r
298   }\r
299 \r
300   private NetConfServerDetails getNetConfServerDetails(String deviceId,\r
301       NetConfServerDetailsEntity entity) {\r
302     NetConfServerDetails result = new NetConfServerDetails();\r
303     result.setDeviceId(deviceId);\r
304     result.setListenPort(entity.getListenPort());\r
305     result.setEnodeBName(entity.getEnodeBName());\r
306     result.setSwVersion(entity.getSwVersion());\r
307     result.setHwVersion(entity.getHwVersion());\r
308     String netconfListenAddress = getServiceHost();\r
309     if (netconfListenAddress == null) {\r
310       netconfListenAddress = config.getNetconfServerIP();\r
311     }\r
312     result.setListenAddress(netconfListenAddress);\r
313     result.setError(NetconfServerManagementError.SUCCESS);\r
314     return result;\r
315   }\r
316 \r
317   public String unregister(String deviceId, String enodeBName) {\r
318     String resultMsg = null;\r
319     NetConfServerDetailsEntity entity = null;\r
320     if (deviceId != null) {\r
321       entity = this.netconfDAO.findByDeviceId(deviceId);\r
322     } else if (enodeBName != null) {\r
323       entity = this.netconfDAO.findByEnodeBName(enodeBName);\r
324     } else {\r
325       LOG.error(\r
326           "Both deviceID and enodeBName are null. Hence failed to unregister the netconf server.");\r
327       resultMsg = "Failed to unregister the device " + deviceId + ", enodeBName=" + enodeBName\r
328           + ". Invalid deviceId/enodeBName specified.";\r
329     }\r
330     if (entity == null) {\r
331       resultMsg = "Failed to unregister the device " + deviceId + ", enodeBName=" + enodeBName\r
332           + ". Invalid deviceId/enodeBName specified.";\r
333       LOG.info(resultMsg);\r
334       return resultMsg;\r
335     }\r
336     boolean result = this.ncServerStarter.stopServer(deviceId);\r
337     if (result) {\r
338       resultMsg =\r
339           "Successfully unregistered the device " + deviceId + " and enodeBName=" + enodeBName;\r
340       this.serverPortAllocator.unReserveServerPort(entity.getListenPort());\r
341       this.netconfDAO.delete(entity);\r
342       LOG.info(resultMsg);\r
343       delteHeartBeatTimer(deviceId);\r
344     } else {\r
345       resultMsg = "Failed to unregister the device " + deviceId + ", enodeBName=" + enodeBName;\r
346       LOG.error(resultMsg);\r
347     }\r
348 \r
349     return resultMsg;\r
350   }\r
351 \r
352   private void delteHeartBeatTimer(String deviceId) {\r
353     VESNotification vesNotification = new VESNotification();\r
354 \r
355     vesNotification.seteNodeBName(deviceId);\r
356 \r
357     ParameterDTO paramDTO = new ParameterDTO();\r
358     paramDTO.setParamName(NetConfServerConstants.HEART_BEAT);\r
359 \r
360     List<ParameterDTO> paramDTOList = new ArrayList<>();\r
361     paramDTOList.add(paramDTO);\r
362 \r
363     OperationDetails opDetails = new OperationDetails();\r
364     opDetails.setOpCode(TR069OperationCode.DELETE_OBJECT);\r
365     opDetails.setParmeters(paramDTOList);\r
366 \r
367     vesNotification.setOperationDetails(opDetails);\r
368 \r
369     VESNotificationResponse response =\r
370         vesNotificationSender.sendDeleteConfigNotification(vesNotification);\r
371 \r
372     if (response.getStatusCode() == NetConfServerConstants.SUCCESS) {\r
373       LOG.info("Heart beat timer is deleted successfully for device {}", deviceId);\r
374     } else {\r
375       LOG.error("Failed to delete heart beat timer for device {}. ErrorMsg : {}", deviceId,\r
376           response.getResponseMsg());\r
377     }\r
378 \r
379   }\r
380 \r
381   public List<NetConfServerDetails> getServersInfo() {\r
382     Iterable<NetConfServerDetailsEntity> serverEntities = netconfDAO.findAll();\r
383     String netconfListenAddress = getServiceHost();\r
384     if (netconfListenAddress == null) {\r
385       netconfListenAddress = config.getNetconfServerIP();\r
386     }\r
387     List<NetConfServerDetails> result = new ArrayList<>();\r
388 \r
389     for (NetConfServerDetailsEntity entity : serverEntities) {\r
390       NetConfServerDetails server = new NetConfServerDetails();\r
391       server.setDeviceId(entity.getDeviceId());\r
392       server.setEnodeBName(entity.getEnodeBName());\r
393       server.setError(NetconfServerManagementError.SUCCESS);\r
394       server.setListenAddress(netconfListenAddress);\r
395       server.setListenPort(entity.getListenPort());\r
396       server.setSwVersion(entity.getSwVersion());\r
397       server.setHwVersion(entity.getHwVersion());\r
398       result.add(server);\r
399     }\r
400     return result;\r
401   }\r
402 \r
403   private String getServiceHost() {\r
404     Map<String, String> envs = System.getenv();\r
405     for (Entry<String, String> entry : envs.entrySet()) {\r
406       if (entry.getKey() != null && entry.getKey().endsWith("_NETCONF_SERVICE_SERVICE_HOST")) {\r
407         return entry.getValue();\r
408       }\r
409     }\r
410     return null;\r
411   }\r
412 \r
413   private boolean isVersionChanged(NetConfServerDetailsEntity entity, String swVersion,\r
414       String hwVersion) {\r
415     String existingProfileId =\r
416         versionManager.getAssociatedProfileId(entity.getSwVersion(), entity.getHwVersion());\r
417     String newProfiled = versionManager.getAssociatedProfileId(swVersion, hwVersion);\r
418     return !existingProfileId.equalsIgnoreCase(newProfiled) ? true : false;\r
419   }\r
420 \r
421   class ServerStartTask implements Runnable {\r
422 \r
423     NetConfServerDetailsEntity entity;\r
424     NetConfServerManagerImpl netconfServerManager;\r
425 \r
426     public ServerStartTask(NetConfServerDetailsEntity entity,\r
427         NetConfServerManagerImpl netconfServerManager) {\r
428       this.entity = entity;\r
429       this.netconfServerManager = netconfServerManager;\r
430     }\r
431 \r
432     @Override\r
433     public void run() {\r
434       boolean isSuccess = netconfServerManager.startNetConfServerInstance(entity);\r
435       if (!isSuccess) {\r
436         try {\r
437           netconfServerManager.restartServersHandler.restart(entity);\r
438         } catch (RetryFailedException e) {\r
439           LOG.debug("");\r
440         }\r
441       }\r
442     }\r
443 \r
444   }\r
445 }\r