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