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
11 * http://www.apache.org/licenses/LICENSE-2.0
\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
19 package org.commscope.tr069adapter.netconf.server;
\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
58 public class NetConfServerManagerImpl {
\r
60 private static final Logger LOG = LoggerFactory.getLogger(NetConfServerManagerImpl.class);
\r
63 ServerPortAllocationHelper serverPortAllocator;
\r
66 NetConfServerDetailsRepository netconfDAO;
\r
69 NetConfServerProperties config;
\r
72 NetconfServerStarter ncServerStarter;
\r
75 RestartNetconfServerHandler restartServersHandler;
\r
78 VESNotificationSender vesNotificationSender;
\r
81 DeviceVersionManager versionManager;
\r
83 ExecutorService executorService = Executors.newFixedThreadPool(10);
\r
85 public boolean loadSchemas() {
\r
86 LOG.debug("Loading yang schema started");
\r
87 List<ProfileDefinition> profiles = versionManager.getSupportedProfileDefinitions();
\r
89 String commonSchemaPath = config.getSchemaDirPath() + "/common";
\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
97 if (!schemaVerDir.isDirectory()) {
\r
98 LOG.error("No folder path found for given version path {}",
\r
99 schemaVerDir.getAbsolutePath());
\r
104 FileUtils.copyDirectory(schemaDir, schemaVerDir);
\r
105 } catch (IOException e) {
\r
106 LOG.error("Failed to copy directory " + e.getMessage());
\r
108 boolean isSchemaLoaded = ncServerStarter.loadSchemas(schemaVerDir);
\r
109 if (!isSchemaLoaded) {
\r
110 LOG.debug("Failed to load schema for profile {}", profile.getProfileId());
\r
114 } catch (Exception e) {
\r
115 LOG.error("Load schema's failed in netconf server {}", e.getMessage());
\r
118 LOG.debug("Loading yang schema completed");
\r
122 public void restartServers() {
\r
123 LOG.debug("Restarting all netconf servers during startup...");
\r
124 Iterable<NetConfServerDetailsEntity> entities = netconfDAO.findAll();
\r
126 for (NetConfServerDetailsEntity entity : entities) {
\r
127 boolean isReserved = serverPortAllocator.checkAndReserveServerPort(entity.getListenPort());
\r
129 ServerStartTask task = new ServerStartTask(entity, this);
\r
130 executorService.execute(task);
\r
133 restartServersHandler.restart(entity);
\r
134 } catch (RetryFailedException e) {
\r
135 LOG.error("submit task for restarting is failed {}", e.toString());
\r
139 LOG.debug("Restarting netconf servers during startup is completed.");
\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
151 // none is specified
\r
153 "Both deviceID and enodeBName are null. Hence failed to create the netconf server.");
\r
157 if (null != entity && ncServerStarter.isNetConfServerRunning(deviceId)) {
\r
158 if (isVersionChanged(entity, swVersion, hwVersion)) {
\r
159 return restartOnVersionChange(deviceId, enodeBName, swVersion, hwVersion);
\r
162 // found the entity. server is already running. double check and run
\r
164 // required. else return the details.
\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
175 String port = serverPortAllocator.reserveServerPort();
\r
176 if (port == null) {
\r
177 result.setError(NetconfServerManagementError.PORT_NOT_AVAILBLE);
\r
179 "All ports are exhausted. Hence cannot allocate a port to start new netconf server");
\r
182 LOG.debug("Successfully reserved a port for deviceID={} ,port={}", deviceId, port);
\r
184 // start the server
\r
185 boolean isServerStarted = ncServerStarter.startServer(port, deviceId, swVersion, hwVersion);
\r
186 boolean isPortInUse = serverPortAllocator.isServerPortInUse(port);
\r
188 if (!isServerStarted || !isPortInUse) {
\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
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
206 result = getNetConfServerDetails(deviceId, entity);
\r
207 LOG.debug("Successfully started netconf server for deviceID= {}, port={}", deviceId, port);
\r
209 } catch (ServerPortAllocationException e) {
\r
210 LOG.error("Failed to allocate a port {}", e.toString());
\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
220 result.setListenAddress(netconfListenAddress);
\r
221 result.setError(NetconfServerManagementError.SUCCESS);
\r
226 public NetConfServerDetails restartOnVersionChange(String deviceId, String enodeBName,
\r
227 String swVersion, String hwVersion) {
\r
229 NetConfServerDetailsEntity entity = null;
\r
230 if (deviceId != null) {
\r
231 entity = netconfDAO.findByDeviceId(deviceId);
\r
233 if (entity == null) {
\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
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
248 boolean isSuccess = startNetConfServerInstance(entity);
\r
252 restartServersHandler.restart(entity);
\r
253 } catch (RetryFailedException e) {
\r
259 return getNetConfServerDetails(deviceId, entity);
\r
263 public boolean startNetConfServerInstance(NetConfServerDetailsEntity entity) {
\r
264 boolean isSuccess = false;
\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
274 final String baseUrl = config.getMapperPath() + "/registerNetconfServer";
\r
277 uri = new URI(baseUrl);
\r
278 } catch (URISyntaxException e) {
\r
279 LOG.error("error while contructing the URI {}", e.toString());
\r
281 RestTemplate restTemplate = new RestTemplate();
\r
282 HttpHeaders headers = new HttpHeaders();
\r
283 HttpEntity<NetConfServerDetails> httpentity = new HttpEntity<>(details, headers);
\r
285 isSuccess = restTemplate.postForObject(uri, httpentity, Boolean.class);
\r
289 LOG.error("Netconf Register request is failed update the updated host details..");
\r
291 LOG.debug("successfully started the server");
\r
294 LOG.error("Failed to restart NETCONF server {} on port {} upon application startup.",
\r
295 entity.getDeviceId(), entity.getListenPort());
\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
312 result.setListenAddress(netconfListenAddress);
\r
313 result.setError(NetconfServerManagementError.SUCCESS);
\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
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
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
336 boolean result = this.ncServerStarter.stopServer(deviceId);
\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
345 resultMsg = "Failed to unregister the device " + deviceId + ", enodeBName=" + enodeBName;
\r
346 LOG.error(resultMsg);
\r
352 private void delteHeartBeatTimer(String deviceId) {
\r
353 VESNotification vesNotification = new VESNotification();
\r
355 vesNotification.seteNodeBName(deviceId);
\r
357 ParameterDTO paramDTO = new ParameterDTO();
\r
358 paramDTO.setParamName(NetConfServerConstants.HEART_BEAT);
\r
360 List<ParameterDTO> paramDTOList = new ArrayList<>();
\r
361 paramDTOList.add(paramDTO);
\r
363 OperationDetails opDetails = new OperationDetails();
\r
364 opDetails.setOpCode(TR069OperationCode.DELETE_OBJECT);
\r
365 opDetails.setParmeters(paramDTOList);
\r
367 vesNotification.setOperationDetails(opDetails);
\r
369 VESNotificationResponse response =
\r
370 vesNotificationSender.sendDeleteConfigNotification(vesNotification);
\r
372 if (response.getStatusCode() == NetConfServerConstants.SUCCESS) {
\r
373 LOG.info("Heart beat timer is deleted successfully for device {}", deviceId);
\r
375 LOG.error("Failed to delete heart beat timer for device {}. ErrorMsg : {}", deviceId,
\r
376 response.getResponseMsg());
\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
387 List<NetConfServerDetails> result = new ArrayList<>();
\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
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
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
421 class ServerStartTask implements Runnable {
\r
423 NetConfServerDetailsEntity entity;
\r
424 NetConfServerManagerImpl netconfServerManager;
\r
426 public ServerStartTask(NetConfServerDetailsEntity entity,
\r
427 NetConfServerManagerImpl netconfServerManager) {
\r
428 this.entity = entity;
\r
429 this.netconfServerManager = netconfServerManager;
\r
433 public void run() {
\r
434 boolean isSuccess = netconfServerManager.startNetConfServerInstance(entity);
\r
437 netconfServerManager.restartServersHandler.restart(entity);
\r
438 } catch (RetryFailedException e) {
\r