Development of NETCONF RPCs for tr-069 adapter to
[oam/tr069-adapter.git] / netconf-server / src / main / java / org / commscope / tr069adapter / netconf / server / helper / ServerPortAllocationHelper.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.helper;
20
21 import java.io.IOException;
22 import java.net.Socket;
23 import java.util.HashMap;
24 import java.util.Map;
25 import java.util.PriorityQueue;
26 import java.util.concurrent.Semaphore;
27
28 import javax.annotation.PostConstruct;
29
30 import org.commscope.tr069adapter.netconf.config.NetConfServerProperties;
31 import org.commscope.tr069adapter.netconf.error.ServerPortAllocationException;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34 import org.springframework.beans.factory.annotation.Autowired;
35 import org.springframework.stereotype.Component;
36
37 @Component
38 public class ServerPortAllocationHelper {
39
40   private static final Logger LOG = LoggerFactory.getLogger(ServerPortAllocationHelper.class);
41
42   private static Map<String, Semaphore> semaphoreMap = new HashMap<>();
43
44   private PriorityQueue<String> availablePorts = new PriorityQueue<>();
45
46   @Autowired
47   NetConfServerProperties config;
48
49   @PostConstruct
50   public void init() {
51     // read the port range and it the available ports.
52
53     Integer startPort = config.getDefaultNetconfStartPort();
54     Integer maxServers = config.getDefaultMaxServers();
55
56     try {
57       startPort = Integer.parseInt(config.getNetconfServersStartPort());
58     } catch (Exception e) {
59       LOG.warn(
60           "Failed to initialize the starting port from the environment {}. Hence using the default port range.",
61           config.getNetconfServersStartPort());
62     }
63
64     try {
65       maxServers = Integer.parseInt(config.getMaxNumOfNetconfServers());
66     } catch (Exception e) {
67       LOG.warn(
68           "Failed to initialize the max netconf server from the environment {} Hence using the default max servers.",
69           config.getMaxNumOfNetconfServers());
70     }
71
72     for (int i = startPort + maxServers - 1; i >= startPort; i--) {
73       semaphoreMap.put(String.valueOf(i), new Semaphore(1));
74       availablePorts.add(String.valueOf(i));
75     }
76     LOG.debug("Successfully populated available ports list.");
77   }
78
79   public synchronized String reserveServerPort() throws ServerPortAllocationException {
80
81     if (availablePorts.isEmpty()) {
82       LOG.debug(
83           "All ports are exhausted. Hence cannot allocate a port to start new netconf server.");
84       return null;
85     }
86
87     String port = availablePorts.peek();
88
89     LOG.debug("Trying to reserve port : {}", port);
90     if (isServerPortInUse(port)) {
91       LOG.debug("Port {} is already in use.", port);
92       availablePorts.poll();
93       return reserveServerPort(); // retry if current port is not available
94     }
95
96     Semaphore semaphore = semaphoreMap.get(port);
97     boolean isAcquired = semaphore.tryAcquire();
98     if (isAcquired) {
99       LOG.debug("Failed to acquire a lock for port :{}. Hence retrying...", port);
100       return reserveServerPort();
101     }
102
103     availablePorts.poll();
104     semaphore.release();
105     LOG.debug("Rserved port is {}", port);
106     return port;
107   }
108
109   public boolean unReserveServerPort(String port) {
110
111     try {
112       Semaphore semaphore = semaphoreMap.get(port);
113       semaphore.acquire();
114       availablePorts.add(port);
115       semaphore.release();
116       LOG.error("Successfully un-reserved the port {} to start netconf server.", port);
117     } catch (InterruptedException e) {
118       LOG.warn("Failed to un-reserve the port  {} {}", port, e.getMessage());
119       Thread.currentThread().interrupt();
120       return false;
121     }
122
123     return true;
124   }
125
126   public boolean checkAndReserveServerPort(String port) {
127     port = port.replaceAll("[\n|\r|\t]", "_");
128     try {
129       Semaphore semaphore = semaphoreMap.get(port);
130       semaphore.acquire();
131       if (isServerPortInUse(port)) {
132         LOG.error("Port {}  already in use.", port);
133         semaphore.release();
134         return false;
135       }
136       availablePorts.remove(port);
137       semaphore.release();
138       LOG.error("Successfully reserved the port {} to start netconf server", port);
139     } catch (InterruptedException e) {
140       Thread.currentThread().interrupt();
141       LOG.error("Failed to lock the port {} : Exception :{}", port, e.toString());
142       return checkAndReserveServerPort(port); // retry acquiring the lock.
143     }
144
145     return true;
146   }
147
148   public boolean isServerPortInUse(String port) {
149     return checkIfPortAvailable(port);
150   }
151
152   private static boolean checkIfPortAvailable(String portStr) {
153     Integer port = Integer.parseInt(portStr);
154     try (Socket ignored = new Socket("localhost", port)) {
155       return true;
156     } catch (IOException e) {
157       LOG.error("while checkIfPortAvailable {}", e.toString());
158       return false;
159     }
160   }
161 }