aboutsummaryrefslogtreecommitdiffstats
path: root/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson
diff options
context:
space:
mode:
authora.sreekumar <ajith.sreekumar@ericsson.com>2018-10-24 16:07:36 +0100
committera.sreekumar <ajith.sreekumar@ericsson.com>2018-10-24 16:08:17 +0100
commitf46c20006c23d119ffc1c83117d203ed649f687c (patch)
treea04482e2a418f557e7423f7cacd375db7c318687 /vnfs/TestVNF/netconfserver/src/main/java/com/ericsson
parent2233d21abae761e91d7b056644c6ac972e662d57 (diff)
Adding TestVNF netconf server
TestVNF netconf server is a partial implementation of a netconfserver for netconf termination. TestVNF is configurable and can be used for testing purposes. Issue-ID: INT-355 Change-Id: I98594d7df57ca14582159bb006d8df51dca74ec7 Signed-off-by: a.sreekumar <ajith.sreekumar@ericsson.com>
Diffstat (limited to 'vnfs/TestVNF/netconfserver/src/main/java/com/ericsson')
-rw-r--r--vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/Server.java112
-rw-r--r--vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/builder/ServerBuilder.java83
-rw-r--r--vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/helper/ActionHelper.java124
-rw-r--r--vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/helper/CustomParser.java177
-rw-r--r--vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/helper/HelperUtils.java92
-rw-r--r--vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/models/Hello.java34
-rw-r--r--vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/models/NetconfMessage.java39
-rw-r--r--vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/models/RpcData.java84
-rw-r--r--vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/models/SchemaDetails.java53
-rw-r--r--vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/netconf/NetconfHandler.java171
-rw-r--r--vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/netconf/NetconfSubsystem.java120
-rw-r--r--vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/requestqueue/RequestQueueHandler.java75
12 files changed, 1164 insertions, 0 deletions
diff --git a/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/Server.java b/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/Server.java
new file mode 100644
index 00000000..51651bac
--- /dev/null
+++ b/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/Server.java
@@ -0,0 +1,112 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package com.ericsson.testvnf.server;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Properties;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.ericsson.testvnf.server.builder.ServerBuilder;
+
+/**
+ * Netconf server using Apache MINA SSHD and netconf4j
+ * The SSHD server extends a netconf subsystem
+ *
+ * @author Ajith Sreekumar
+ *
+ */
+public class Server {
+
+ private static final int DEFAULT_PORT = 2052;
+
+ private static final String DEFAULT_HOST = "0.0.0.0";
+
+ private static final Log log = LogFactory.getLog(Server.class);
+
+ private static String netconfTemplatesDirectory;
+
+ private static String timeDelayForSendingEvents; // time delay in milliseconds
+
+ public static String getNetconfTemplatesDirectory() {
+ return netconfTemplatesDirectory;
+ }
+
+ public static void setNetconfTemplatesDirectory(String netconfTemplatesDirectory) {
+ Server.netconfTemplatesDirectory = netconfTemplatesDirectory;
+ }
+
+ public static String getTimeDelayForSendingEvents() {
+ return timeDelayForSendingEvents;
+ }
+
+ public static void setTimeDelayForSendingEvents(String timeDelayForSendingEvents) {
+ Server.timeDelayForSendingEvents = timeDelayForSendingEvents;
+ }
+
+
+ public static void main(String[] args) throws IOException{
+ setNetconfTemplatesDirectory(args[0]); // the directory which contains the groovy files to perform actions based on netconf requests, and also contains other netconf templates to be sent back to the client
+ setTimeDelayForSendingEvents(args[1]); // the delay between sending each event by the VNF
+ ServerBuilder serverBuilder = new ServerBuilder();
+
+ Properties prop = new Properties();
+ String host = DEFAULT_HOST;
+ int port = DEFAULT_PORT;
+ try {
+ InputStream input = new FileInputStream(getNetconfTemplatesDirectory()+ "/netconftemplates/server-config.properties");
+ log.info("Setting host and port from the properties file.");
+ prop.load(input);
+ if(null!=prop.getProperty("host"))
+ host = prop.getProperty("host");
+ if(null!=prop.getProperty("port"))
+ port = Integer.parseInt(prop.getProperty("port"));
+ }catch(FileNotFoundException e) {
+ log.info("server-config.properties file not found. " + e);
+ log.info("Using default host and port");
+
+ }
+
+ serverBuilder.initializeServer(host,port);
+ serverBuilder.startServer();
+
+ // read lines from input
+ BufferedReader buffer = new BufferedReader(new InputStreamReader(System.in));
+
+ while (true) {
+ if (buffer.readLine().equalsIgnoreCase("EXIT")) {
+ break;
+ }
+ }
+
+ log.info("Exiting");
+ serverBuilder.stopServer();
+ System.exit(0);
+ }
+
+
+} \ No newline at end of file
diff --git a/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/builder/ServerBuilder.java b/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/builder/ServerBuilder.java
new file mode 100644
index 00000000..d6ec18ad
--- /dev/null
+++ b/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/builder/ServerBuilder.java
@@ -0,0 +1,83 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package com.ericsson.testvnf.server.builder;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.server.SshServer;
+import org.apache.sshd.server.command.Command;
+import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
+import org.apache.sshd.server.session.ServerSession;
+
+import com.ericsson.testvnf.server.netconf.NetconfSubsystem;
+
+/**
+ * Build the server by extending a netconf subsystem
+ */
+public class ServerBuilder {
+
+ private static final Log log = LogFactory.getLog(ServerBuilder.class);
+ private SshServer sshd;
+
+ // initialize the server
+ public void initializeServer(String host, int listeningPort) {
+ log.info("Configuring server...");
+ sshd = SshServer.setUpDefaultServer();
+ sshd.setHost(host);
+ sshd.setPort(listeningPort);
+
+ log.info("Host: '" + host + "', listenig port: " + listeningPort);
+
+ // set the password authenticator, here the access is granted always.
+ sshd.setPasswordAuthenticator((String username, String password, ServerSession session) -> true);
+
+ sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider());
+
+ List<NamedFactory<Command>> subsystemFactories = new ArrayList<>();
+ subsystemFactories.add(NetconfSubsystem.Factory.createFactory());
+ sshd.setSubsystemFactories(subsystemFactories); // add the netconf subystem to the server.
+
+ log.info("Server configured.");
+ }
+
+ // start the server
+ public void startServer(){
+ log.info("Starting server...");
+ try {
+ sshd.start();
+ } catch (IOException e) {
+ log.error("Error starting server!", e);
+ }
+ log.info("Server started.");
+ }
+
+ // stop the server
+ public void stopServer() throws IOException {
+ log.info("Stopping server...");
+ sshd.stop();
+ log.info("Server stopped.");
+ }
+}
diff --git a/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/helper/ActionHelper.java b/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/helper/ActionHelper.java
new file mode 100644
index 00000000..d9714ff7
--- /dev/null
+++ b/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/helper/ActionHelper.java
@@ -0,0 +1,124 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package com.ericsson.testvnf.server.helper;
+
+import java.io.OutputStream;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.ericsson.testvnf.server.Server;
+import com.ericsson.testvnf.server.models.NetconfMessage;
+import com.ericsson.testvnf.server.models.RpcData;
+
+/*
+ * The helper class which performs actions based on netconf requests
+ */
+public class ActionHelper {
+
+ private static final Log log = LogFactory.getLog(ActionHelper.class);
+ private OutputStream out;
+ private Thread groovyCallerThread;
+
+ public ActionHelper(OutputStream out){
+ this.out = out;
+ }
+
+ public boolean doActionForRPCRequest(NetconfMessage netconfMessage, Map<String, Boolean> connectionResetMap, boolean sessionClosedStatus){
+ // if the client is sending an rpc request for any protocol operation
+ RpcData rpcData = (RpcData) netconfMessage;
+ if (null != rpcData.getTargetName()) {
+ // the connectionResetMap is a shared between multiple client connections coming in for a single netconf server instance
+ // true value for a target indicates that sending events to that target needs to be stopped
+ // edit-config operation with a target determines the termination of sending events to that target.
+ if (rpcData.getOperation().equals("edit-config")) {
+ connectionResetMap.put(rpcData.getTargetName(), true);
+ } else {
+ connectionResetMap.put(rpcData.getTargetName(), false);
+ }
+ }
+ if (rpcData.getOperation().equals("\"close-session\"")) {
+ sessionClosedStatus = true;
+ }
+ String operation = rpcData.getOperation();
+ log.info("Received query");
+
+ // on getting a get-schema request from client, the server sends back the yang
+ // model of the particular functionality/capability asked for.
+ if (operation.equals("get-schema")) {
+ doActionForGetSchemaOperation(rpcData);
+ } else {
+ doActionForOperations(rpcData, operation, connectionResetMap);
+ }
+ return sessionClosedStatus;
+ }
+
+ public void doActionForOperations(RpcData rpcData, String operation, Map<String, Boolean> connectionResetMap) {
+ groovyCallerThread = new Thread("Groovy caller") {
+ @Override
+ public void run() {
+ rpcData.setTimeDelayForSendingEvents(Server.getTimeDelayForSendingEvents());
+ String result ="";
+ // pick up and execute the correct groovy file based on the operation and configuration datastore identified from the rpc request
+ result = HelperUtils.executeGroovy(Server.getNetconfTemplatesDirectory()
+ + "/netconftemplates/"+operation+"/"+rpcData.getConfigurationDatastore()+"/response.groovy", rpcData, connectionResetMap);
+ if (!result.equals("ignore")) {
+ result = result.replaceAll("<MID>", rpcData.getMessageId());
+ HelperUtils.sendAsBytesToOutput(result.getBytes(), out);
+ log.info("Finished writing " + result);
+ }
+ }
+ };
+ groovyCallerThread.start();
+ log.info("groovyCallerThread started");
+ }
+
+ public void doActionForGetSchemaOperation(RpcData rpcData){
+ log.info("get-schema received.");
+ if (null != rpcData.getSchemaDetails()
+ && null != rpcData.getSchemaDetails().getIdentifier()) {
+ log.info("Sending schema for capability...");
+ String schemaString = HelperUtils.readFile(Server.getNetconfTemplatesDirectory()
+ + "/netconftemplates/"+rpcData.getSchemaDetails().getIdentifier()+"-schema.yang"); // the yang model has to be in the netconftemplates directory with the proper naming format : '<operation>-schema.yang'
+ String schemaStringValue = schemaString.replaceAll("<MID>", rpcData.getMessageId()); // Put the correct message id in the response as it is in the request.
+ HelperUtils.sendAsBytesToOutput(schemaStringValue.getBytes(), out);
+ log.info("Finished writing the schema information");
+ }
+ }
+
+ // the method sends a hello message to the client, hello.xml in the netconftemplates directory is used to send the hello message
+ public void sendHelloMessage(){
+ log.info("Send hello message.");
+ String helloMessage = HelperUtils.readFile(Server.getNetconfTemplatesDirectory()+"/netconftemplates/hello.xml");
+ HelperUtils.sendAsBytesToOutput(helloMessage.getBytes(), out);
+ log.info("Hello message sent.");
+ }
+
+ public void interruptGroovyCallerThread() {
+ // kill thread if it don't finish naturally
+ if (groovyCallerThread!=null && groovyCallerThread.isAlive()) {
+ log.info("Killing groovy caller thread");
+ groovyCallerThread.interrupt();
+ }
+
+ }
+}
diff --git a/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/helper/CustomParser.java b/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/helper/CustomParser.java
new file mode 100644
index 00000000..c615d108
--- /dev/null
+++ b/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/helper/CustomParser.java
@@ -0,0 +1,177 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package com.ericsson.testvnf.server.helper;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.ext.DefaultHandler2;
+
+import com.ericsson.testvnf.server.models.Hello;
+import com.ericsson.testvnf.server.models.RpcData;
+import com.ericsson.testvnf.server.models.SchemaDetails;
+import com.ericsson.testvnf.server.requestqueue.RequestQueueHandler;
+
+/*
+ * Parses the xml requests and populates netconf related data to do operations
+ */
+public class CustomParser extends DefaultHandler2 {
+
+ private Log log = LogFactory.getLog(CustomParser.class);
+ private RequestQueueHandler requestQueueHandler;
+
+ private Hello hello;
+ private RpcData rpcData;
+ private boolean nextIsOperationTag = false;
+ private boolean insideOperationTag = false;
+ private String operationTagValue = "";
+ private StringBuilder operationTagContent = new StringBuilder();
+ private boolean insideConfigurationDatastore = false;
+ private boolean insideSchemaDetailsTag = false;
+ private StringBuilder individualSchemaDetailsTagContent = new StringBuilder();
+ private SchemaDetails schemaDetails;
+ private boolean insideTargetNameTag = false;
+ private StringBuilder targetNameContent = new StringBuilder();
+
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
+ super.startElement(uri, localName, qName, attributes);
+ String messageId;
+ if (nextIsOperationTag) {
+ insideOperationTag = true;
+ rpcData.setOperation(localName);
+ operationTagValue = localName;
+ nextIsOperationTag = false;
+ if (localName.equalsIgnoreCase("get-schema")) {
+ insideSchemaDetailsTag = true;
+ schemaDetails = new SchemaDetails();
+ }
+ }
+
+ if (insideOperationTag) {
+ populateStartElementData(localName, attributes);
+ }
+
+ if (localName.equalsIgnoreCase("hello")) {
+ hello = new Hello();
+ } else if (localName.equalsIgnoreCase("rpc")) {
+ rpcData = new RpcData();
+
+ messageId = attributes.getValue("message-id");
+ if (messageId == null)
+ throw new SAXException("Received <rpc> message without a message ID");
+
+ rpcData.setMessageId(messageId);
+ nextIsOperationTag = true;
+ }
+
+ }
+
+ @Override
+ public void characters(char[] ch, int start, int length) throws SAXException {
+ super.characters(ch, start, length);
+
+ if (insideOperationTag) {
+ operationTagContent.append(ch, start, length);
+ if (insideSchemaDetailsTag) {
+ individualSchemaDetailsTagContent.append(ch, start, length);
+ }else if (insideTargetNameTag) {
+ targetNameContent.append(ch, start, length);
+ }
+ }
+ }
+ @Override
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ super.endElement(uri, localName, qName);
+
+ if (insideOperationTag) {
+ populateEndElementData(localName);
+ }
+ if (localName.equalsIgnoreCase("hello")) {
+ requestQueueHandler.addToQueue(hello);
+ hello = null;
+ /* Query tags and operations */
+ } else if (localName.equalsIgnoreCase("rpc")) {
+ requestQueueHandler.addToQueue(rpcData);
+ rpcData = null;
+ }else if(operationTagValue.equals(localName)) {
+ rpcData.setOperationTagContent(operationTagContent.toString());
+ insideOperationTag = false;
+ operationTagContent = new StringBuilder();
+ if (localName.equalsIgnoreCase("get-schema")) {
+ insideSchemaDetailsTag = false;
+ rpcData.setSchemaDetails(schemaDetails);
+ schemaDetails = new SchemaDetails();
+ }
+ }
+ }
+
+ private void populateStartElementData(String localName, Attributes attributes) {
+ if (insideConfigurationDatastore) {
+ rpcData.setConfigurationDatastore(localName);
+ }
+ if (localName.equalsIgnoreCase("target") || localName.equalsIgnoreCase("source")) {
+ insideConfigurationDatastore = true;
+ } else if(localName.equalsIgnoreCase("target-name")) {
+ insideTargetNameTag = true;
+ }
+ operationTagContent.append("<" + localName);
+ for (int i = 0; i < attributes.getLength(); i++) {
+ operationTagContent.append(" " + attributes.getQName(i) + "=\"" + attributes.getValue(i) + "\"");
+ }
+ operationTagContent.append(">");
+ }
+
+ private void populateEndElementData(String localName) {
+ if (localName.equalsIgnoreCase("target") || localName.equalsIgnoreCase("source")) {
+ insideConfigurationDatastore = false;
+ } else if(localName.equalsIgnoreCase("target-name")) {
+ insideTargetNameTag = false;
+ rpcData.setTargetName(targetNameContent.toString());
+ targetNameContent = new StringBuilder();
+ } else if (insideSchemaDetailsTag) {
+ if(localName.equalsIgnoreCase("identifier")) {
+ schemaDetails.setIdentifier(individualSchemaDetailsTagContent.toString().trim());
+ individualSchemaDetailsTagContent = new StringBuilder();
+ } else if(localName.equalsIgnoreCase("version")) {
+ schemaDetails.setVersion(individualSchemaDetailsTagContent.toString().trim());
+ individualSchemaDetailsTagContent = new StringBuilder();
+ }
+ }
+ operationTagContent.append("</" + localName + ">");
+ }
+
+ public void setRequestQueueHandler(RequestQueueHandler queue) {
+ this.requestQueueHandler = queue;
+ }
+
+ @Override
+ public void error(SAXParseException e) throws SAXException {
+ log.warn(e.getMessage());
+ }
+
+ @Override
+ public void fatalError(SAXParseException e) throws SAXException {
+ log.warn(e.getMessage());
+ }
+} \ No newline at end of file
diff --git a/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/helper/HelperUtils.java b/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/helper/HelperUtils.java
new file mode 100644
index 00000000..cec61354
--- /dev/null
+++ b/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/helper/HelperUtils.java
@@ -0,0 +1,92 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package com.ericsson.testvnf.server.helper;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.ericsson.testvnf.server.models.NetconfMessage;
+
+import groovy.lang.Binding;
+import groovy.util.GroovyScriptEngine;
+
+/*
+ * The utils class
+ */
+public class HelperUtils{
+
+ private static final Log log = LogFactory.getLog(HelperUtils.class);
+
+ private HelperUtils() {
+ super();
+ }
+
+ // executes the groovy file specified in the path
+ public static String executeGroovy(String groovyFilePath, NetconfMessage data, Map<String, Boolean> connectionResetMap) {
+
+ Object result = "";
+ try {
+ log.info("groovy path------" + groovyFilePath);
+ File file = new File(groovyFilePath);
+ GroovyScriptEngine engine = new GroovyScriptEngine(file.getParent());
+ Binding binding = new Binding();
+ binding.setVariable("RpcData", data);
+ binding.setVariable("connectionResetMap", connectionResetMap);
+ log.info("binding " + binding + " " + file.getParent() + " " + file.getName());
+ result = engine.run(file.getName(), binding);
+ } catch (Exception e) {
+ log.error("Exception while trying to execute groovy file", e);
+ }
+ return result.toString();
+ }
+
+ // send bytes to output stream
+ public static void sendAsBytesToOutput(byte[] buffer, OutputStream out){
+ try {
+ log.info("Sending message as bytes..\n");
+ int len = buffer.length;
+ out.write(buffer, 0, len);
+ String tail = "]]>]]>";
+ out.write(tail.getBytes(), 0, tail.getBytes().length);
+ out.flush();
+ }catch(Exception e) {
+ log.info("Error while sending response message as bytes: "+e);
+ }
+ }
+
+ // the method is used to read the contents of the file specified
+ public static String readFile(String filename) {
+ String fileAsString= "";
+ try {
+ fileAsString = new String(Files.readAllBytes(Paths.get(filename)));
+ } catch (IOException e) {
+ log.error("Error reading file: "+ filename);
+ }
+ return fileAsString;
+ }
+} \ No newline at end of file
diff --git a/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/models/Hello.java b/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/models/Hello.java
new file mode 100644
index 00000000..b37fb2a2
--- /dev/null
+++ b/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/models/Hello.java
@@ -0,0 +1,34 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package com.ericsson.testvnf.server.models;
+
+/*
+ * Hello message class
+ */
+public class Hello extends NetconfMessage {
+
+ private static final long serialVersionUID = -3711327850654046146L;
+
+ public Hello() {
+ // constructor of Hello class
+ }
+
+}
diff --git a/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/models/NetconfMessage.java b/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/models/NetconfMessage.java
new file mode 100644
index 00000000..9e1f3311
--- /dev/null
+++ b/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/models/NetconfMessage.java
@@ -0,0 +1,39 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package com.ericsson.testvnf.server.models;
+
+/*
+ * NetconfMessage - base class for all the netconf messages
+ */
+public class NetconfMessage implements java.io.Serializable {
+
+ private static final long serialVersionUID = -4596032857456097952L;
+
+ private String messageId;
+
+ public String getMessageId() {
+ return messageId;
+ }
+
+ public void setMessageId(String messageId) {
+ this.messageId = messageId;
+ }
+}
diff --git a/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/models/RpcData.java b/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/models/RpcData.java
new file mode 100644
index 00000000..7e3eb8ed
--- /dev/null
+++ b/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/models/RpcData.java
@@ -0,0 +1,84 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package com.ericsson.testvnf.server.models;
+/*
+ * RPC operation message class
+ */
+public class RpcData extends NetconfMessage implements java.io.Serializable {
+
+ private static final long serialVersionUID = -8318907964396287877L;
+
+ private String operation;
+ private String targetName;
+ private String operationTagContent;
+ private String configurationDatastore = "NA";
+ private SchemaDetails schemaDetails; // Parameters for get-schema
+ private String timeDelayForSendingEvents;// time delay in milliseconds
+
+ public String getConfigurationDatastore() {
+ return configurationDatastore;
+ }
+
+ public void setConfigurationDatastore(String configurationDatastore) {
+ this.configurationDatastore = configurationDatastore;
+ }
+
+ public String getOperationTagContent() {
+ return operationTagContent;
+ }
+
+ public void setOperationTagContent(String operationTagContent) {
+ this.operationTagContent = operationTagContent;
+ }
+
+ public String getTimeDelayForSendingEvents() {
+ return timeDelayForSendingEvents;
+ }
+
+ public void setTimeDelayForSendingEvents(String timeDelayForSendingEvents) {
+ this.timeDelayForSendingEvents = timeDelayForSendingEvents;
+ }
+
+ public String getTargetName() {
+ return targetName;
+ }
+
+ public void setTargetName(String targetName) {
+ this.targetName = targetName;
+ }
+
+ public SchemaDetails getSchemaDetails() {
+ return schemaDetails;
+ }
+
+ public void setSchemaDetails(SchemaDetails schemaDetails) {
+ this.schemaDetails = schemaDetails;
+ }
+
+ public String getOperation() {
+ return operation;
+ }
+
+ public void setOperation(String operation) {
+ this.operation = operation;
+ }
+
+}
diff --git a/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/models/SchemaDetails.java b/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/models/SchemaDetails.java
new file mode 100644
index 00000000..190a53c2
--- /dev/null
+++ b/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/models/SchemaDetails.java
@@ -0,0 +1,53 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package com.ericsson.testvnf.server.models;
+
+/*
+ * Schema details class which deals with client requests asking for schema of an rpc operation
+ */
+public class SchemaDetails implements java.io.Serializable {
+
+ private static final long serialVersionUID = -1869159770045750695L;
+
+ private String identifier;
+ private String version;
+ private String format;
+
+ public String getIdentifier() {
+ return identifier;
+ }
+ public void setIdentifier(String identifier) {
+ this.identifier = identifier;
+ }
+ public String getVersion() {
+ return version;
+ }
+ public void setVersion(String version) {
+ this.version = version;
+ }
+ public String getFormat() {
+ return format;
+ }
+ public void setFormat(String format) {
+ this.format = format;
+ }
+
+}
diff --git a/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/netconf/NetconfHandler.java b/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/netconf/NetconfHandler.java
new file mode 100644
index 00000000..6d2d4965
--- /dev/null
+++ b/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/netconf/NetconfHandler.java
@@ -0,0 +1,171 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package com.ericsson.testvnf.server.netconf;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.StringReader;
+import java.util.Map;
+import java.util.stream.Stream;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+import com.ericsson.testvnf.server.helper.ActionHelper;
+import com.ericsson.testvnf.server.helper.CustomParser;
+import com.ericsson.testvnf.server.models.Hello;
+import com.ericsson.testvnf.server.models.NetconfMessage;
+import com.ericsson.testvnf.server.models.RpcData;
+import com.ericsson.testvnf.server.requestqueue.RequestQueueHandler;
+
+/*
+ * NetconfHandler class which handles the netconf communication with a client
+ */
+public class NetconfHandler implements Runnable {
+
+ private InputStream in;
+ private Map<String, Boolean> connectionResetMap;
+ private XMLReader xmlReader;
+ private boolean sessionClosed;
+ private RequestQueueHandler requestQueueHandler;
+ private ActionHelper actionHelper;
+ private Thread requestHandlerThread;
+ private static final Log log = LogFactory.getLog(NetconfHandler.class);
+
+ public NetconfHandler(InputStream in, OutputStream out, Map<String, Boolean> connectionResetMap) {
+ this.connectionResetMap = connectionResetMap;
+ this.in = in;
+ actionHelper = new ActionHelper(out);
+ this.sessionClosed = false;
+ }
+
+ public void run() {
+ // initialize rpc request handler and request queue
+ try {
+ requestQueueHandler = new RequestQueueHandler();
+ CustomParser customParser = new CustomParser();
+ customParser.setRequestQueueHandler(requestQueueHandler); // input requests once processed are added to the request queue.
+ xmlReader = XMLReaderFactory.createXMLReader();
+ xmlReader.setContentHandler(customParser);
+ xmlReader.setErrorHandler(customParser);
+ } catch (SAXException e) {
+ log.error("Error creating custom rpc request parser.", e);
+ return;
+ }
+ actionHelper.sendHelloMessage(); // server sends hello to the client as soon as the client initiates a connection with the server
+ startRequestHandler();
+ // start and wait for request handler
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ log.warn("Error waiting for thread.", e);
+ }
+ readInputRequest();
+
+ }
+
+ // read input requests from the client
+ private void readInputRequest(){
+
+ StringBuilder netconfMessage = new StringBuilder();
+ try (BufferedReader br = new BufferedReader(new InputStreamReader(in));
+ Stream<String> lineStream = br.lines();){
+ log.info("Parsing message.");
+ // rpc requests from clients are parsed and processed
+ lineStream.forEach(ele -> {
+ ele = ele.trim();
+ log.info("current element::" + ele);
+ if (ele.contains("]]>]]>")) {
+ ele = ele.replace("]]>]]>", "");
+ }
+ if (ele.contains("</hello>") || ele.contains("</rpc>")) {
+ netconfMessage.append(ele + '\n');
+ String messageAsString = netconfMessage.toString();
+ try {
+ log.info("Parsing message---:\n" + messageAsString);
+ xmlReader.parse(new InputSource(new StringReader(messageAsString.trim()))); //xmlParser parses the rpc requests
+ log.info("Parsing done..");
+ } catch (Exception e) {
+ log.error("Error parsing. Message---: \n" + messageAsString, e);
+ sessionClosed = true;
+ }
+ netconfMessage.setLength(0);// reset the message as one whole request is complete
+ }else {
+ netconfMessage.append(ele + '\n');
+ }
+ });
+ } catch (Exception e) {
+ log.error("Exception caught in NetconfHandler readInputRequest: "+ e.getMessage());
+ } finally {
+ interruptThreads();
+ }
+ }
+
+ // method that performs actions based on the message coming in from the client
+ private void startRequestHandler() {
+ log.info("start RequestHandler.");
+ requestHandlerThread = new Thread("Request handler") {
+ @Override
+ public void run() {
+ while (!sessionClosed) {
+
+ NetconfMessage netconfMessage = null;
+ try {
+ netconfMessage = requestQueueHandler.waitAndGetMessageFromQueue(); // get the message received
+ } catch (InterruptedException e) {
+ log.warn("Interrupted exception");
+ Thread.currentThread().interrupt();
+ break;
+ }
+
+ if (netconfMessage instanceof Hello) { // if client sends a hello, send a hello message back
+ actionHelper.sendHelloMessage();
+ } else if (netconfMessage instanceof RpcData) {
+ sessionClosed = actionHelper.doActionForRPCRequest(netconfMessage, connectionResetMap, sessionClosed);
+ }else {
+ log.warn("Unknown message received.");
+ }
+ }
+ log.info("Request handler ended");
+ }
+
+ };
+ requestHandlerThread.start();
+ log.info("Request handler thread started.");
+ }
+
+ public void interruptThreads() {
+ actionHelper.interruptGroovyCallerThread();
+ if (requestHandlerThread!=null && requestHandlerThread.isAlive()) {
+ log.info("Killing request handler thread");
+ requestHandlerThread.interrupt();
+ }
+
+ }
+
+}
diff --git a/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/netconf/NetconfSubsystem.java b/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/netconf/NetconfSubsystem.java
new file mode 100644
index 00000000..a6022bea
--- /dev/null
+++ b/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/netconf/NetconfSubsystem.java
@@ -0,0 +1,120 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package com.ericsson.testvnf.server.netconf;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.server.Environment;
+import org.apache.sshd.server.ExitCallback;
+import org.apache.sshd.server.command.Command;
+
+/*
+ * NetconfSubsystem class
+ */
+public class NetconfSubsystem implements Command {
+
+ private static final Log log = LogFactory.getLog(Factory.class);
+ private InputStream in;
+ private OutputStream out;
+ private OutputStream error;
+ private Thread netconfHandlerThread;
+ private Map<String, Boolean> connectionResetMap;
+ private NetconfHandler netconfHandler;
+
+ public NetconfSubsystem(Map<String, Boolean> connectionResetMap) {
+ this.connectionResetMap = connectionResetMap;
+ }
+
+ public void start(Environment env) throws IOException {
+ // initialize netconf handler
+ netconfHandler = new NetconfHandler(in, out, connectionResetMap);
+ netconfHandlerThread = new Thread(netconfHandler, "netconfHandler thread");
+ netconfHandlerThread.start();
+ }
+
+ public void destroy() {
+ netconfHandler.interruptThreads();
+ try {
+ netconfHandlerThread.join(2000);
+ } catch (InterruptedException e) {
+ log.info("netconfHandler thread joining failed." + e.getMessage());
+ Thread.currentThread().interrupt();
+ }
+ netconfHandlerThread.interrupt();
+ log.info("Netconf Subsystem destroyed");
+ }
+
+ public static class Factory implements NamedFactory<Command> {
+
+ // a connectionResetMap is maintained for each running instance of a netconf system.
+ // this is a simple data structure to determine when sending events to a target needs to be terminated.
+ private static Map<String, Boolean> connectionResetMap = new HashMap<>();
+
+ public static Factory createFactory() {
+ return new Factory();
+ }
+
+ public String getName() {
+ return "netconf";
+ }
+
+ public Command create() {
+ log.info("Creating subsystem for netconf");
+ return new NetconfSubsystem(connectionResetMap);
+ }
+
+ }
+
+ public InputStream getInputStream() {
+ return in;
+ }
+
+ public void setInputStream(InputStream in) {
+ this.in = in;
+ }
+
+ public OutputStream getOutputStream() {
+ return out;
+ }
+
+ public void setOutputStream(OutputStream out) {
+ this.out = out;
+ }
+
+ public OutputStream getErrorStream() {
+ return error;
+ }
+
+ public void setErrorStream(OutputStream error) {
+ this.error = error;
+ }
+
+ public void setExitCallback(ExitCallback callback) {
+ //Set the callback that the shell has to call when it is closed.
+ }
+} \ No newline at end of file
diff --git a/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/requestqueue/RequestQueueHandler.java b/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/requestqueue/RequestQueueHandler.java
new file mode 100644
index 00000000..29702bd4
--- /dev/null
+++ b/vnfs/TestVNF/netconfserver/src/main/java/com/ericsson/testvnf/server/requestqueue/RequestQueueHandler.java
@@ -0,0 +1,75 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package com.ericsson.testvnf.server.requestqueue;
+
+import java.util.Queue;
+import java.util.concurrent.LinkedBlockingQueue;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.ericsson.testvnf.server.models.NetconfMessage;
+
+/**
+ * RequestQueueHandler manages incoming netconf requests from clients
+ *
+ * @author Ajith Sreekumar
+ *
+ */
+public class RequestQueueHandler {
+
+ private static final Log log = LogFactory.getLog(RequestQueueHandler.class);
+
+ private volatile Queue<NetconfMessage> requestQueue;
+
+ public RequestQueueHandler() {
+ requestQueue = new LinkedBlockingQueue<>();
+ }
+
+ // wait until an element is added to queue, once added return it
+ public NetconfMessage waitAndGetMessageFromQueue() throws InterruptedException {
+ NetconfMessage netconfMessage = null;
+ synchronized (requestQueue) {
+ while (true) {
+ log.info("Waiting for message to be added to queue..");
+ if (!requestQueue.isEmpty()) {
+ netconfMessage = requestQueue.element();
+ requestQueue.remove(requestQueue.element());
+ }
+ if (netconfMessage != null) {
+ log.info("Message received in queue is taken for processing.");
+ break;
+ }
+ requestQueue.wait();
+ }
+ }
+ return netconfMessage;
+ }
+
+ //add message into the queue
+ public void addToQueue(NetconfMessage netconfMessage) {
+ synchronized (requestQueue) {
+ log.info("Received a new message in queue.");
+ requestQueue.add(netconfMessage);
+ requestQueue.notifyAll();
+ }
+ }
+
+}