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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=======================================================================
19 package org.commscope.tr069adapter.netconf.server;
22 import java.io.IOException;
24 import java.net.URISyntaxException;
25 import java.util.ArrayList;
26 import java.util.List;
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;
58 public class NetConfServerManagerImpl {
60 private static final Logger LOG = LoggerFactory.getLogger(NetConfServerManagerImpl.class);
63 ServerPortAllocationHelper serverPortAllocator;
66 NetConfServerDetailsRepository netconfDAO;
69 NetConfServerProperties config;
72 NetconfServerStarter ncServerStarter;
75 RestartNetconfServerHandler restartServersHandler;
78 VESNotificationSender vesNotificationSender;
81 DeviceVersionManager versionManager;
83 ExecutorService executorService = Executors.newFixedThreadPool(10);
85 private static final String ENODEBNAME = "enodeBName";
87 private static final String FAIL_DEVICE_UNREGISTER = "Failed to unregister the device ";
89 public boolean loadSchemas() {
90 LOG.debug("Loading yang schema started");
91 List<ProfileDefinition> profiles = versionManager.getSupportedProfileDefinitions();
93 String commonSchemaPath = config.getSchemaDirPath() + "/common";
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);
101 if (!schemaVerDir.isDirectory()) {
102 LOG.error("No folder path found for given version path {}",
103 schemaVerDir.getAbsolutePath());
107 copyDir(schemaDir, schemaVerDir);
108 boolean isSchemaLoaded = ncServerStarter.loadSchemas(schemaVerDir);
109 if (!isSchemaLoaded) {
110 LOG.debug("Failed to load schema for profile {}", profile.getProfileId());
114 } catch (Exception e) {
115 LOG.error("Load schemas failed in netconf server {}", e.getMessage());
118 LOG.debug("Loading yang schema completed");
122 private void copyDir(File schemaDir, File schemaVerDir) {
124 FileUtils.copyDirectory(schemaDir, schemaVerDir);
125 } catch (IOException e) {
126 LOG.error("Failed to copy directory {} ", e.getMessage());
130 public void restartServers() {
131 LOG.debug("Restarting all netconf servers during startup...");
132 Iterable<NetConfServerDetailsEntity> entities = netconfDAO.findAll();
134 for (NetConfServerDetailsEntity entity : entities) {
135 boolean isReserved = serverPortAllocator.checkAndReserveServerPort(entity.getListenPort());
137 ServerStartTask task = new ServerStartTask(entity, this);
138 executorService.execute(task);
141 restartServersHandler.restart(entity);
142 } catch (RetryFailedException e) {
143 LOG.error("Error while restarting netconf servers {}",e.getMessage());
147 LOG.debug("Restarting netconf servers during startup is completed.");
150 public NetConfServerDetails createServer(String deviceId, String enodeBName, String swVersion,
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);
161 "Both deviceID and enodeBName are null. Hence failed to create the netconf server.");
165 if (null != entity && ncServerStarter.isNetConfServerRunning(deviceId)) {
166 if (isVersionChanged(entity, swVersion, hwVersion)) {
167 return restartOnVersionChange(deviceId, enodeBName, swVersion, hwVersion);
170 // found the entity. server is already running. double check and run
172 // required. else return the details.
174 // update the ENB Name if Changed
175 entity.setEnodeBName(enodeBName);
176 netconfDAO.save(entity);
177 result = getNetConfServerDetails(deviceId, entity);
183 String port = serverPortAllocator.reserveServerPort();
185 result.setError(NetconfServerManagementError.PORT_NOT_AVAILBLE);
187 "All ports are exhausted. Hence cannot allocate a port to start new netconf server");
190 LOG.debug("Successfully reserved a port for deviceID={} ,port={}", deviceId, port);
193 boolean isServerStarted = ncServerStarter.startServer(port, deviceId, swVersion, hwVersion);
194 boolean isPortInUse = serverPortAllocator.isServerPortInUse(port);
196 if (!isServerStarted || !isPortInUse) {
198 "Failed to start netconf server for deviceID: {}, at port:{} , isServerStarted={} ,isPortInUse={}",
199 deviceId, port, isServerStarted, isPortInUse);
200 result.setError(NetconfServerManagementError.PORT_IN_USE);
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);
214 result = getNetConfServerDetails(deviceId, entity);
215 LOG.debug("Successfully started netconf server for deviceID= {}, port={}", deviceId, port);
217 } catch (ServerPortAllocationException e) {
218 LOG.error("Failed to allocate a port {}", e.toString());
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();
228 result.setListenAddress(netconfListenAddress);
229 result.setError(NetconfServerManagementError.SUCCESS);
234 public NetConfServerDetails restartOnVersionChange(String deviceId, String enodeBName,
235 String swVersion, String hwVersion) {
237 NetConfServerDetailsEntity entity = null;
238 if (deviceId != null) {
239 entity = netconfDAO.findByDeviceId(deviceId);
241 if (entity == null) {
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);
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);
256 boolean isSuccess = startNetConfServerInstance(entity);
260 restartServersHandler.restart(entity);
261 } catch (RetryFailedException e) {
267 return getNetConfServerDetails(deviceId, entity);
271 public boolean startNetConfServerInstance(NetConfServerDetailsEntity entity) {
272 boolean isSuccess = false;
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);
282 final String baseUrl = config.getMapperPath() + "/registerNetconfServer";
285 uri = new URI(baseUrl);
286 } catch (URISyntaxException e) {
287 LOG.error("error while contructing the URI {}", e.toString());
289 RestTemplate restTemplate = new RestTemplate();
290 HttpHeaders headers = new HttpHeaders();
291 HttpEntity<NetConfServerDetails> httpentity = new HttpEntity<>(details, headers);
293 isSuccess = restTemplate.postForObject(uri, httpentity, Boolean.class);
297 LOG.error("Netconf Register request is failed update the updated host details..");
299 LOG.debug("successfully started the server");
302 LOG.error("Failed to restart NETCONF server {} on port {} upon application startup.",
303 entity.getDeviceId(), entity.getListenPort());
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();
320 result.setListenAddress(netconfListenAddress);
321 result.setError(NetconfServerManagementError.SUCCESS);
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);
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.";
339 if (entity == null) {
340 resultMsg = FAIL_DEVICE_UNREGISTER + deviceId + ", " + ENODEBNAME + "=" + enodeBName
341 + ". Invalid deviceId/enodeBName specified.";
345 boolean result = this.ncServerStarter.stopServer(deviceId);
348 "Successfully unregistered the device " + deviceId + " and enodeBName=" + enodeBName;
349 this.serverPortAllocator.unReserveServerPort(entity.getListenPort());
350 this.netconfDAO.delete(entity);
352 delteHeartBeatTimer(deviceId);
354 resultMsg = FAIL_DEVICE_UNREGISTER + deviceId + ", " + ENODEBNAME + "=" + enodeBName;
355 LOG.error(resultMsg);
361 private void delteHeartBeatTimer(String deviceId) {
362 VESNotification vesNotification = new VESNotification();
364 vesNotification.seteNodeBName(deviceId);
366 ParameterDTO paramDTO = new ParameterDTO();
367 paramDTO.setParamName(NetConfServerConstants.HEART_BEAT);
369 List<ParameterDTO> paramDTOList = new ArrayList<>();
370 paramDTOList.add(paramDTO);
372 OperationDetails opDetails = new OperationDetails();
373 opDetails.setOpCode(TR069OperationCode.DELETE_OBJECT);
374 opDetails.setParmeters(paramDTOList);
376 vesNotification.setOperationDetails(opDetails);
378 VESNotificationResponse response =
379 vesNotificationSender.sendDeleteConfigNotification(vesNotification);
381 if (response.getStatusCode() == NetConfServerConstants.SUCCESS) {
382 LOG.info("Heart beat timer is deleted successfully for device {}", deviceId);
384 LOG.error("Failed to delete heart beat timer for device {}. ErrorMsg : {}", deviceId,
385 response.getResponseMsg());
390 public List<NetConfServerDetails> getServersInfo() {
391 Iterable<NetConfServerDetailsEntity> serverEntities = netconfDAO.findAll();
392 String netconfListenAddress = getServiceHost();
393 if (netconfListenAddress == null) {
394 netconfListenAddress = config.getNetconfServerIP();
396 List<NetConfServerDetails> result = new ArrayList<>();
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());
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();
422 private boolean isVersionChanged(NetConfServerDetailsEntity entity, String swVersion,
424 String existingProfileId =
425 versionManager.getAssociatedProfileId(entity.getSwVersion(), entity.getHwVersion());
426 String newProfiled = versionManager.getAssociatedProfileId(swVersion, hwVersion);
427 return !existingProfileId.equalsIgnoreCase(newProfiled) ? true : false;
430 class ServerStartTask implements Runnable {
432 NetConfServerDetailsEntity entity;
433 NetConfServerManagerImpl netconfServerManager;
435 public ServerStartTask(NetConfServerDetailsEntity entity,
436 NetConfServerManagerImpl netconfServerManager) {
437 this.entity = entity;
438 this.netconfServerManager = netconfServerManager;
443 boolean isSuccess = netconfServerManager.startNetConfServerInstance(entity);
446 netconfServerManager.restartServersHandler.restart(entity);
447 } catch (RetryFailedException e) {