80c65e9cae65d1bd9b18bc1e3c95db0ca965bd64
[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 schemas 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           e.printStackTrace();\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) {\r
158       // found the entity. server is already running. double check and run\r
159       // if\r
160       // required. else return the details.\r
161 \r
162       // update the ENB Name if Changed\r
163       entity.setEnodeBName(enodeBName);\r
164       entity.setSwVersion(swVersion);\r
165       entity.setHwVersion(hwVersion);\r
166       netconfDAO.save(entity);\r
167       result = getNetConfServerDetails(deviceId, entity);\r
168       return result;\r
169     }\r
170 \r
171     try {\r
172 \r
173       String port = serverPortAllocator.reserveServerPort();\r
174       if (port == null) {\r
175         result.setError(NetconfServerManagementError.PORT_NOT_AVAILBLE);\r
176         LOG.error(\r
177             "All ports are exhausted. Hence cannot allocate a port to start new netconf server");\r
178         return result;\r
179       } else {\r
180         LOG.debug("Successfully reserved a port for deviceID={} ,port={}", deviceId, port);\r
181 \r
182         // start the server\r
183         boolean isServerStarted = ncServerStarter.startServer(port, deviceId, swVersion, hwVersion);\r
184         boolean isPortInUse = serverPortAllocator.isServerPortInUse(port);\r
185 \r
186         if (!isServerStarted || !isPortInUse) {\r
187           LOG.error(\r
188               "Failed to start netconf server for deviceID: {}, at port:{} , isServerStarted={} ,isPortInUse={}",\r
189               deviceId, port, isServerStarted, isPortInUse);\r
190           result.setError(NetconfServerManagementError.PORT_IN_USE);\r
191           return result;\r
192         }\r
193       }\r
194 \r
195       // save the record in db\r
196       entity = new NetConfServerDetailsEntity();\r
197       entity.setDeviceId(deviceId);\r
198       entity.setListenPort(port);\r
199       entity.setEnodeBName(enodeBName);\r
200       entity.setSwVersion(swVersion);\r
201       entity.setHwVersion(hwVersion);\r
202       netconfDAO.save(entity);\r
203 \r
204       result = getNetConfServerDetails(deviceId, entity);\r
205       LOG.debug("Successfully started netconf server for deviceID= {}, port={}", deviceId, port);\r
206 \r
207     } catch (ServerPortAllocationException e) {\r
208       LOG.error("Failed to allocate a port {}", e.toString());\r
209     }\r
210 \r
211     if (entity != null) {\r
212       result.setDeviceId(deviceId);\r
213       result.setListenPort(entity.getListenPort());\r
214       String netconfListenAddress = getServiceHost();\r
215       if (netconfListenAddress == null) {\r
216         netconfListenAddress = config.getNetconfServerIP();\r
217       }\r
218       result.setListenAddress(netconfListenAddress);\r
219       result.setError(NetconfServerManagementError.SUCCESS);\r
220     }\r
221     return result;\r
222   }\r
223 \r
224   public NetConfServerDetails restartServer(String deviceId, String enodeBName, String swVersion,\r
225       String hwVersion) {\r
226 \r
227     NetConfServerDetailsEntity entity = null;\r
228     if (deviceId != null) {\r
229       entity = netconfDAO.findByDeviceId(deviceId);\r
230     }\r
231     if (entity != null) {\r
232       boolean result = this.ncServerStarter.stopServer(deviceId);\r
233       restartServersOnStartup(entity);\r
234     }\r
235 \r
236     return null;\r
237   }\r
238 \r
239 \r
240   public boolean restartServersOnStartup(NetConfServerDetailsEntity entity) {\r
241     boolean isSuccess = false;\r
242 \r
243     boolean isServerStarted = ncServerStarter.startServer(entity.getListenPort(),\r
244         entity.getDeviceId(), entity.getSwVersion(), entity.getHwVersion());\r
245     if (isServerStarted) {\r
246       LOG.info("Successfully restarted NETCONF server {}  on port {}  upon application startup.",\r
247           entity.getDeviceId(), entity.getListenPort());\r
248       // we need to push the pnfEntry for IP updated\r
249       NetConfServerDetails details = getNetConfServerDetails(entity.getDeviceId(), entity);\r
250 \r
251       final String baseUrl = config.getMapperPath() + "/registerNetconfServer";\r
252       URI uri = null;\r
253       try {\r
254         uri = new URI(baseUrl);\r
255       } catch (URISyntaxException e) {\r
256         LOG.error("error while contructing the URI {}", e.toString());\r
257       }\r
258       RestTemplate restTemplate = new RestTemplate();\r
259       HttpHeaders headers = new HttpHeaders();\r
260       HttpEntity<NetConfServerDetails> httpentity = new HttpEntity<>(details, headers);\r
261       if (uri != null) {\r
262         isSuccess = restTemplate.postForObject(uri, httpentity, Boolean.class);\r
263       }\r
264 \r
265       if (!isSuccess) {\r
266         LOG.error("Netconf Register request is failed update the updated host details..");\r
267       } else {\r
268         LOG.debug("successfully started the server");\r
269       }\r
270     } else {\r
271       LOG.error("Failed to restart NETCONF server {}  on port {}  upon application startup.",\r
272           entity.getDeviceId(), entity.getListenPort());\r
273     }\r
274     return isSuccess;\r
275   }\r
276 \r
277   private NetConfServerDetails getNetConfServerDetails(String deviceId,\r
278       NetConfServerDetailsEntity entity) {\r
279     NetConfServerDetails result = new NetConfServerDetails();\r
280     result.setDeviceId(deviceId);\r
281     result.setListenPort(entity.getListenPort());\r
282     result.setEnodeBName(entity.getEnodeBName());\r
283     result.setSwVersion(entity.getSwVersion());\r
284     result.setHwVersion(entity.getHwVersion());\r
285     String netconfListenAddress = getServiceHost();\r
286     if (netconfListenAddress == null) {\r
287       netconfListenAddress = config.getNetconfServerIP();\r
288     }\r
289     result.setListenAddress(netconfListenAddress);\r
290     result.setError(NetconfServerManagementError.SUCCESS);\r
291     return result;\r
292   }\r
293 \r
294   public String unregister(String deviceId, String enodeBName) {\r
295     String resultMsg = null;\r
296     NetConfServerDetailsEntity entity = null;\r
297     if (deviceId != null) {\r
298       entity = this.netconfDAO.findByDeviceId(deviceId);\r
299     } else if (enodeBName != null) {\r
300       entity = this.netconfDAO.findByEnodeBName(enodeBName);\r
301     } else {\r
302       LOG.error(\r
303           "Both deviceID and enodeBName are null. Hence failed to unregister the netconf server.");\r
304       resultMsg = "Failed to unregister the device " + deviceId + ", enodeBName=" + enodeBName\r
305           + ". Invalid deviceId/enodeBName specified.";\r
306     }\r
307     if (entity == null) {\r
308       resultMsg = "Failed to unregister the device " + deviceId + ", enodeBName=" + enodeBName\r
309           + ". Invalid deviceId/enodeBName specified.";\r
310       LOG.info(resultMsg);\r
311     }\r
312     boolean result = this.ncServerStarter.stopServer(deviceId);\r
313     if (result) {\r
314       resultMsg =\r
315           "Successfully unregistered the device " + deviceId + " and enodeBName=" + enodeBName;\r
316       this.serverPortAllocator.unReserveServerPort(entity.getListenPort());\r
317       this.netconfDAO.delete(entity);\r
318       LOG.info(resultMsg);\r
319       delteHeartBeatTimer(deviceId);\r
320     } else {\r
321       resultMsg = "Failed to unregister the device " + deviceId + ", enodeBName=" + enodeBName;\r
322       LOG.error(resultMsg);\r
323     }\r
324 \r
325     return resultMsg;\r
326   }\r
327 \r
328   private void delteHeartBeatTimer(String deviceId) {\r
329     VESNotification vesNotification = new VESNotification();\r
330 \r
331     vesNotification.seteNodeBName(deviceId);\r
332 \r
333     ParameterDTO paramDTO = new ParameterDTO();\r
334     paramDTO.setParamName(NetConfServerConstants.HEART_BEAT);\r
335 \r
336     List<ParameterDTO> paramDTOList = new ArrayList<>();\r
337     paramDTOList.add(paramDTO);\r
338 \r
339     OperationDetails opDetails = new OperationDetails();\r
340     opDetails.setOpCode(TR069OperationCode.DELETE_OBJECT);\r
341     opDetails.setParmeters(paramDTOList);\r
342 \r
343     vesNotification.setOperationDetails(opDetails);\r
344 \r
345     VESNotificationResponse response =\r
346         vesNotificationSender.sendDeleteConfigNotification(vesNotification);\r
347 \r
348     if (response.getStatusCode() == NetConfServerConstants.SUCCESS) {\r
349       LOG.info("Heart beat timer is deleted successfully for device {}", deviceId);\r
350     } else {\r
351       LOG.error("Failed to delete heart beat timer for device {}. ErrorMsg : {}", deviceId,\r
352           response.getResponseMsg());\r
353     }\r
354 \r
355   }\r
356 \r
357   public List<NetConfServerDetails> getServersInfo() {\r
358     Iterable<NetConfServerDetailsEntity> serverEntities = netconfDAO.findAll();\r
359     String netconfListenAddress = getServiceHost();\r
360     if (netconfListenAddress == null) {\r
361       netconfListenAddress = config.getNetconfServerIP();\r
362     }\r
363     List<NetConfServerDetails> result = new ArrayList<>();\r
364 \r
365     for (NetConfServerDetailsEntity entity : serverEntities) {\r
366       NetConfServerDetails server = new NetConfServerDetails();\r
367       server.setDeviceId(entity.getDeviceId());\r
368       server.setEnodeBName(entity.getEnodeBName());\r
369       server.setError(NetconfServerManagementError.SUCCESS);\r
370       server.setListenAddress(netconfListenAddress);\r
371       server.setListenPort(entity.getListenPort());\r
372       server.setSwVersion(entity.getSwVersion());\r
373       server.setHwVersion(entity.getHwVersion());\r
374       result.add(server);\r
375     }\r
376     return result;\r
377   }\r
378 \r
379   private String getServiceHost() {\r
380     Map<String, String> envs = System.getenv();\r
381     for (Entry<String, String> entry : envs.entrySet()) {\r
382       if (entry.getKey() != null && entry.getKey().endsWith("_NETCONF_SERVICE_SERVICE_HOST")) {\r
383         return entry.getValue();\r
384       }\r
385     }\r
386     return null;\r
387   }\r
388 \r
389   class ServerStartTask implements Runnable {\r
390 \r
391     NetConfServerDetailsEntity entity;\r
392     NetConfServerManagerImpl netconfServerManager;\r
393 \r
394     public ServerStartTask(NetConfServerDetailsEntity entity,\r
395         NetConfServerManagerImpl netconfServerManager) {\r
396       this.entity = entity;\r
397       this.netconfServerManager = netconfServerManager;\r
398     }\r
399 \r
400     @Override\r
401     public void run() {\r
402       boolean isSuccess = netconfServerManager.restartServersOnStartup(entity);\r
403       if (!isSuccess) {\r
404         try {\r
405           netconfServerManager.restartServersHandler.restart(entity);\r
406         } catch (RetryFailedException e) {\r
407           LOG.debug("");\r
408         }\r
409       }\r
410     }\r
411 \r
412   }\r
413 }\r