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