Device Software version management
[oam/tr069-adapter.git] / netconf-server / src / main / java / org / commscope / tr069adapter / netconf / server / NetconfServerStarter.java
1 /*\r
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
10  *\r
11  * http://www.apache.org/licenses/LICENSE-2.0\r
12  *\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
17  */\r
18 \r
19 package org.commscope.tr069adapter.netconf.server;\r
20 \r
21 import java.io.BufferedReader;\r
22 import java.io.File;\r
23 import java.io.FileReader;\r
24 import java.io.IOException;\r
25 import java.util.Arrays;\r
26 import java.util.Collections;\r
27 import java.util.HashMap;\r
28 import java.util.List;\r
29 import java.util.Map;\r
30 import java.util.concurrent.TimeUnit;\r
31 import java.util.regex.Matcher;\r
32 import java.util.regex.Pattern;\r
33 import org.apache.commons.io.FileUtils;\r
34 import org.commscope.tr069adapter.common.deviceversion.DeviceVersionManager;\r
35 import org.commscope.tr069adapter.netconf.config.NetConfServerProperties;\r
36 import org.commscope.tr069adapter.netconf.operations.CustomOperationsCreator;\r
37 import org.opendaylight.netconf.test.tool.NetconfDeviceSimulator;\r
38 import org.opendaylight.netconf.test.tool.config.Configuration;\r
39 import org.opendaylight.netconf.test.tool.config.ConfigurationBuilder;\r
40 import org.opendaylight.netconf.test.tool.operations.OperationsCreator;\r
41 import org.slf4j.Logger;\r
42 import org.slf4j.LoggerFactory;\r
43 import org.springframework.beans.factory.annotation.Autowired;\r
44 import org.springframework.context.annotation.Scope;\r
45 import org.springframework.stereotype.Component;\r
46 import com.google.common.base.Preconditions;\r
47 \r
48 \r
49 @Component\r
50 @Scope("singleton")\r
51 public class NetconfServerStarter {\r
52 \r
53   private static final Logger LOG = LoggerFactory.getLogger(NetconfServerStarter.class);\r
54 \r
55   private static Map<String, NetconfDevice> serversMap = new HashMap<>();\r
56 \r
57   @Autowired\r
58   NetConfServerProperties config;\r
59 \r
60   @Autowired\r
61   DeviceVersionManager versionManager;\r
62 \r
63   public boolean startServer(String netConfPort, String macID, String swVersion, String hwVersion) {\r
64 \r
65     if (netConfPort == null) {\r
66       LOG.error("Invalid NETCONF port for deviceID: {}, port is null.", macID);\r
67       return false;\r
68     }\r
69 \r
70     LOG.debug(\r
71         "Starting Netconf server for MACID :{}, on port :{}, softwareVersion {}, hardwareVersion {}",\r
72         macID, netConfPort, swVersion, hwVersion);\r
73     boolean result =\r
74         startServer(netConfPort, config.getSchemaDirPath(), macID, swVersion, hwVersion);\r
75     LOG.debug("Completed starting Netconf server for MACID :{} , on port :{}, server status={}",\r
76         macID, netConfPort, result);\r
77 \r
78     return result;\r
79   }\r
80 \r
81   @SuppressWarnings({"resource", "deprecation"})\r
82   private boolean startServer(String portStr, String schemaDirPath, String macID, String swVersion,\r
83       String hwVersion) {\r
84 \r
85     // creating configuration for the netconf instance\r
86     final Configuration configuration = new ConfigurationBuilder().build();\r
87     OperationsCreator operationsCreator = new CustomOperationsCreator(macID, swVersion, hwVersion);\r
88     configuration.setOperationsCreator(operationsCreator);\r
89     configuration.setGenerateConfigsTimeout((int) TimeUnit.MINUTES.toMillis(30));\r
90 \r
91     String versionPath = versionManager.getNetconfYangSchemaPath(swVersion, hwVersion);\r
92     if (versionPath == null && swVersion != null) {\r
93       LOG.error("Failed to get version path for software version {}, calling base version",\r
94           swVersion);\r
95       versionPath = versionManager.getBaseNetconfYangSchemaPath();\r
96     } else if (swVersion == null) {\r
97       LOG.error("Software version is null {}", swVersion);\r
98       return false;\r
99     }\r
100     String schemaCommonPath = schemaDirPath + "/common";\r
101     String schemaVerPath = schemaDirPath + "/" + versionPath;\r
102     if (portStr != null) {\r
103       try {\r
104         int port = Integer.parseInt(portStr);\r
105         configuration.setStartingPort(port);\r
106       } catch (Exception e) {\r
107         LOG.error("Failed to get netconf service instance port for parameters {}", e.toString());\r
108         return false;\r
109       }\r
110     }\r
111     configuration.setDeviceCount(1);\r
112     configuration.setSsh(Boolean.TRUE);\r
113     File schemaDir = new File(schemaCommonPath);\r
114     configuration.setCapabilities(Configuration.DEFAULT_BASE_CAPABILITIES_EXI);\r
115     configuration.setIp("0.0.0.0");\r
116 \r
117     File schemaVerDir = new File(schemaVerPath);\r
118     if (!schemaVerDir.isDirectory()) {\r
119       LOG.error("No folder path found for given version path {}", schemaVerDir.getAbsolutePath());\r
120       return false;\r
121     }\r
122 \r
123     try {\r
124       FileUtils.copyDirectory(schemaDir, schemaVerDir);\r
125     } catch (IOException e) {\r
126       LOG.error("Failed to copy directory " + e.getMessage());\r
127     }\r
128     configuration.setSchemasDir(schemaVerDir);\r
129     boolean isSchemaLoaded = loadSchemas(schemaVerDir);\r
130     if (!isSchemaLoaded) {\r
131       LOG.debug("Failed to load schema for netconf server instance {}", macID);\r
132       return false;\r
133     }\r
134 \r
135     try (final NetconfDevice netconfDevice = new NetconfDevice(configuration)) {\r
136       final List<Integer> openDevices = netconfDevice.start();\r
137       if (openDevices.isEmpty()) {\r
138         LOG.debug("Failed to start netconf server instance {}", macID);\r
139         return false;\r
140       }\r
141       netconfDevice.setAutoClose(false);\r
142       serversMap.put(macID, netconfDevice);\r
143     } catch (RuntimeException e) {\r
144       LOG.error("Unhandled exception. Failed to start the server", e);\r
145       return false;\r
146     }\r
147 \r
148     return true;\r
149   }\r
150 \r
151   public boolean stopServer(String macID) {\r
152     try {\r
153       LOG.debug("Stopping Netconf server for MACID {}", macID);\r
154       NetconfDevice netconf = serversMap.get(macID);\r
155       netconf.setAutoClose(true);\r
156       netconf.close();\r
157       LOG.debug("Completed stopping Netconf server for MACID {}", macID);\r
158       return true;\r
159     } catch (Exception e) {\r
160       LOG.debug("Error while stopping Netconf server for MACID {}; error message {}", macID,\r
161           e.getMessage());\r
162     }\r
163 \r
164     return false;\r
165   }\r
166 \r
167   private boolean loadSchemas(File schemasDir) {\r
168     if (schemasDir != null) {\r
169       if (!schemasDir.exists() || !schemasDir.isDirectory() || !schemasDir.canRead()) {\r
170         LOG.error("Failed to load schema. schema location is not valid.");\r
171         return false;\r
172       }\r
173 \r
174       Pattern yangregex = Pattern.compile("(?<name>.*)@(?<revision>\\d{4}-\\d{2}-\\d{2})\\.yang");\r
175       Pattern revisionregex = Pattern.compile("revision\\s+\"?(\\d{4}-\\d{2}-\\d{2})\"?");\r
176 \r
177       final File[] filesArray = schemasDir.listFiles();\r
178       final List<File> files =\r
179           filesArray != null ? Arrays.asList(filesArray) : Collections.emptyList();\r
180       for (final File file : files) {\r
181         final Matcher yangMatcher = yangregex.matcher(file.getName());\r
182         if (!yangMatcher.matches()) {\r
183           try (BufferedReader reader = new BufferedReader(new FileReader(file))) {\r
184             String line = reader.readLine();\r
185             while (line != null && !revisionregex.matcher(line).find()) {\r
186               line = reader.readLine();\r
187             }\r
188             loadSchemaPattren(line, file, revisionregex);\r
189           } catch (final IOException e) {\r
190             LOG.error("Unhandled exception. Failed to load the schema.{}", e.toString());\r
191             return false;\r
192           }\r
193         }\r
194       }\r
195     }\r
196     return true;\r
197   }\r
198 \r
199   private void loadSchemaPattren(String line, File file, Pattern revisionregex) {\r
200     if (line != null) {\r
201       final Matcher m = revisionregex.matcher(line);\r
202       Preconditions.checkState(m.find());\r
203       String moduleName = file.getAbsolutePath();\r
204       if (file.getName().endsWith(".yang")) {\r
205         moduleName = moduleName.substring(0, moduleName.length() - 5);\r
206       }\r
207       final String revision = m.group(1);\r
208       final String correctName = moduleName + "@" + revision + ".yang";\r
209       final File correctNameFile = new File(correctName);\r
210       if (!file.renameTo(correctNameFile)) {\r
211         throw new IllegalStateException("Failed to rename '%s'." + file);\r
212       }\r
213     }\r
214   }\r
215 \r
216 }\r
217 \r
218 \r
219 class NetconfDevice extends NetconfDeviceSimulator {\r
220   boolean autoClose = true;\r
221 \r
222   public NetconfDevice(Configuration configuration) {\r
223     super(configuration);\r
224   }\r
225 \r
226   @Override\r
227   public void close() {\r
228     if (autoClose)\r
229       super.close();\r
230   }\r
231 \r
232   public void setAutoClose(boolean autoClose) {\r
233     this.autoClose = autoClose;\r
234   }\r
235 \r
236 }\r