Netconf active & passive component fix.

Change-Id: Ie7b533cef589fcbf16b9fc8b70184666c76f9588
diff --git a/apps/netconf/client/src/main/java/org/onosproject/netconf/client/impl/NetconfActiveComponent.java b/apps/netconf/client/src/main/java/org/onosproject/netconf/client/impl/NetconfActiveComponent.java
index ac7735e..16c226d 100644
--- a/apps/netconf/client/src/main/java/org/onosproject/netconf/client/impl/NetconfActiveComponent.java
+++ b/apps/netconf/client/src/main/java/org/onosproject/netconf/client/impl/NetconfActiveComponent.java
@@ -33,8 +33,11 @@
 import org.onosproject.netconf.client.NetconfTranslator;
 import org.onosproject.netconf.client.NetconfTranslator.OperationType;
 import org.onosproject.yang.model.DataNode;
+import org.onosproject.yang.model.InnerNode;
+import org.onosproject.yang.model.KeyLeaf;
 import org.onosproject.yang.model.LeafNode;
 import org.onosproject.yang.model.ListKey;
+import org.onosproject.yang.model.NodeKey;
 import org.onosproject.yang.model.ResourceId;
 import org.onosproject.yang.runtime.DefaultResourceData;
 import org.slf4j.Logger;
@@ -43,6 +46,11 @@
 import java.io.IOException;
 import java.net.URI;
 import java.net.URISyntaxException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import static com.google.common.base.Preconditions.checkNotNull;
 
 
 @Beta
@@ -63,10 +71,14 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected NetconfController controller;
 
+    public static final String DEVICES = "devices";
+    public static final String DEVICE = "device";
+    public static final String DEVICE_ID = "deviceid";
+
     private ResourceId resId = new ResourceId.Builder()
-            .addBranchPointSchema("device", DEVNMSPACE)
-            .addBranchPointSchema("device", DEVNMSPACE)
-            .addKeyLeaf("deviceid", DEVNMSPACE, "netconf:172.16.5.11:22")
+            .addBranchPointSchema(DEVICES, DEVNMSPACE)
+            .addBranchPointSchema(DEVICE, DEVNMSPACE)
+            .addKeyLeaf(DEVICE_ID, DEVNMSPACE, "netconf:172.16.5.11:22")
             .build();
 
     @Activate
@@ -83,7 +95,34 @@
 
     @Override
     public boolean isRelevant(DynamicConfigEvent event) {
-        return event.subject().equals(resId);
+        return checkIfRelevant(event.subject());
+    }
+
+    /**
+     * Checks if this event is relevant for active component.
+     *
+     * @param id resource id
+     * @return true if relevant
+     */
+    private boolean checkIfRelevant(ResourceId id) {
+        checkNotNull(id, "resource id can't be null");
+        List<NodeKey> nodeKeys = id.nodeKeys();
+        if (nodeKeys != null && !nodeKeys.isEmpty() && nodeKeys.size() == 2) {
+            NodeKey key = nodeKeys.get(0);
+            if (key.schemaId().name().equals(DEVICES)) {
+                key = nodeKeys.get(1);
+                if (key.schemaId().name().equals(DEVICE)) {
+                    ListKey listKey = (ListKey) key;
+                    List<KeyLeaf> keyLeaves = listKey.keyLeafs();
+                    if (keyLeaves != null && !keyLeaves.isEmpty()) {
+                        return keyLeaves.get(0).leafSchema().name()
+                                .equals(DEVICE_ID);
+                    }
+                }
+            }
+        }
+        log.debug("not a relevant event for netconf active component");
+        return false;
     }
 
     public boolean isMaster(DeviceId deviceId) {
@@ -154,14 +193,21 @@
     private boolean parseAndEdit(DataNode node, DeviceId deviceId,
                                  ResourceId resourceId,
                                  NetconfTranslator.OperationType operationType) {
+
+        //add all low level nodes of devices
+        Iterator<Map.Entry<NodeKey, DataNode>> it = ((InnerNode) node)
+                .childNodes().entrySet().iterator();
+        DefaultResourceData.Builder builder = DefaultResourceData.builder();
+        while (it.hasNext()) {
+            DataNode n = it.next().getValue();
+            if (!n.key().schemaId().name().equals("deviceid")) {
+                builder.addDataNode(n);
+            }
+        }
+        //add resouce id //TODO: check if it is correct
         try {
             return netconfTranslator.editDeviceConfig(
-                    deviceId,
-                    DefaultResourceData.builder()
-                            .addDataNode(node)
-                            .resourceId(resourceId)
-                            .build(),
-                    operationType);
+                    deviceId, builder.build(), operationType);
         } catch (IOException e) {
             e.printStackTrace();
             return false;
diff --git a/apps/netconf/client/src/main/java/org/onosproject/netconf/client/impl/NetconfTranslatorImpl.java b/apps/netconf/client/src/main/java/org/onosproject/netconf/client/impl/NetconfTranslatorImpl.java
index 6e38729..b66a63e 100644
--- a/apps/netconf/client/src/main/java/org/onosproject/netconf/client/impl/NetconfTranslatorImpl.java
+++ b/apps/netconf/client/src/main/java/org/onosproject/netconf/client/impl/NetconfTranslatorImpl.java
@@ -16,42 +16,46 @@
 
 package org.onosproject.netconf.client.impl;
 
+import com.google.common.annotations.Beta;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
 import org.onosproject.cluster.NodeId;
 import org.onosproject.net.DeviceId;
-import org.onosproject.netconf.client.NetconfTranslator;
 import org.onosproject.netconf.NetconfController;
 import org.onosproject.netconf.NetconfDevice;
+import org.onosproject.netconf.NetconfException;
 import org.onosproject.netconf.NetconfSession;
-
-import org.onosproject.yang.model.ResourceData;
+import org.onosproject.netconf.client.NetconfTranslator;
+import org.onosproject.yang.model.DataNode;
+import org.onosproject.yang.model.InnerNode;
 import org.onosproject.yang.model.KeyLeaf;
-import org.onosproject.yang.model.ListKey;
 import org.onosproject.yang.model.LeafListKey;
+import org.onosproject.yang.model.LeafNode;
+import org.onosproject.yang.model.ListKey;
+import org.onosproject.yang.model.NodeKey;
+import org.onosproject.yang.model.ResourceData;
+import org.onosproject.yang.model.ResourceId;
 import org.onosproject.yang.model.SchemaContext;
 import org.onosproject.yang.model.SchemaContextProvider;
 import org.onosproject.yang.model.SchemaId;
-import org.onosproject.yang.model.DataNode;
-import org.onosproject.yang.model.NodeKey;
-import org.onosproject.yang.model.ResourceId;
-import org.onosproject.yang.model.InnerNode;
-import org.onosproject.yang.model.LeafNode;
-
-import org.onosproject.yang.runtime.DefaultCompositeData;
-import org.onosproject.yang.runtime.DefaultRuntimeContext;
-import org.onosproject.yang.runtime.YangRuntimeService;
-import org.onosproject.yang.runtime.DefaultCompositeStream;
 import org.onosproject.yang.runtime.CompositeStream;
 import org.onosproject.yang.runtime.DefaultAnnotatedNodeInfo;
 import org.onosproject.yang.runtime.DefaultAnnotation;
+import org.onosproject.yang.runtime.DefaultCompositeData;
+import org.onosproject.yang.runtime.DefaultCompositeStream;
 import org.onosproject.yang.runtime.DefaultResourceData;
-import org.onosproject.yang.runtime.YangSerializerContext;
+import org.onosproject.yang.runtime.DefaultRuntimeContext;
 import org.onosproject.yang.runtime.DefaultYangSerializerContext;
-/*FIXME these imports are not visible using OSGI*/
+import org.onosproject.yang.runtime.YangRuntimeService;
+import org.onosproject.yang.runtime.YangSerializerContext;
 import org.onosproject.yang.runtime.helperutils.SerializerHelper;
-
-import static org.onosproject.yang.model.DataNode.Type.SINGLE_INSTANCE_LEAF_VALUE_NODE;
-import static org.onosproject.yang.runtime.helperutils.SerializerHelper.addDataNode;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
@@ -59,32 +63,26 @@
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.nio.charset.StandardCharsets;
+import java.util.Iterator;
+import java.util.List;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-import java.util.List;
-import java.util.Iterator;
-
-import com.google.common.annotations.Beta;
-
-import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.Deactivate;
-import org.apache.felix.scr.annotations.Reference;
-import org.apache.felix.scr.annotations.ReferenceCardinality;
-
-import org.osgi.service.component.ComponentContext;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.yang.model.DataNode.Type.SINGLE_INSTANCE_LEAF_VALUE_NODE;
+import static org.onosproject.yang.runtime.helperutils.SerializerHelper.addDataNode;
+
+/*FIXME these imports are not visible using OSGI*/
+
+/*FIXME these imports are not visible using OSGI*/
 
 /*TODO once the API's are finalized this comment will be made more specified.*/
+
 /**
  * Translator which accepts data types defined for the DynamicConfigService and
  * makes the appropriate calls to NETCONF devices before encoding and returning
  * responses in formats suitable for the DynamicConfigService.
- *
+ * <p>
  * NOTE: This entity does not ensure you are the master of a device you attempt
  * to contact. If you are not the master an error will be thrown because there
  * will be no session available.
@@ -114,7 +112,7 @@
 
     private static final String GET_URI = "urn:ietf:params:xml:ns:yang:" +
             "yrt-ietf-network:networks/network/node";
-    private static final String XML_ENCODING_SPECIFIER = "XML";
+    private static final String XML_ENCODING_SPECIFIER = "xml";
     private static final String OP_SPECIFIER = "xc:operation";
     private static final String REPLACE_OP_SPECIFIER = "replace";
     private static final String DELETE_OP_SPECIFIER = "delete";
@@ -167,40 +165,46 @@
         SchemaContext context = schemaContextProvider
                 .getSchemaContext(ResourceId.builder().addBranchPointSchema("/", null).build());
         ResourceData modifiedPathResourceData = getResourceData(resourceData.resourceId(),
-                                                  resourceData.dataNodes(),
-                                                       new DefaultYangSerializerContext(context, null));
+                                                                resourceData.dataNodes(),
+                                                                new DefaultYangSerializerContext(context, null));
         DefaultCompositeData.Builder compositeDataBuilder = DefaultCompositeData
                 .builder()
                 .resourceData(modifiedPathResourceData);
         for (DataNode node : resourceData.dataNodes()) {
             ResourceId resourceId = resourceData.resourceId();
             if (operationType != OperationType.DELETE) {
-                resourceId = getAnnotatedNodeResourceId(resourceData.resourceId(), node);
+                resourceId = getAnnotatedNodeResourceId(
+                        resourceData.resourceId(), node);
             }
             if (resourceId != null) {
-                DefaultAnnotatedNodeInfo.Builder annotatedNodeInfo = DefaultAnnotatedNodeInfo.builder();
+                DefaultAnnotatedNodeInfo.Builder annotatedNodeInfo =
+                        DefaultAnnotatedNodeInfo.builder();
                 annotatedNodeInfo.resourceId(resourceId);
-                annotatedNodeInfo.addAnnotation(new DefaultAnnotation(OP_SPECIFIER,
-                                                                      operationType == OperationType.DELETE ?
-                                                                          DELETE_OP_SPECIFIER : REPLACE_OP_SPECIFIER));
+                annotatedNodeInfo.addAnnotation(
+                        new DefaultAnnotation(
+                                OP_SPECIFIER, operationType == OperationType.DELETE ?
+                                DELETE_OP_SPECIFIER : REPLACE_OP_SPECIFIER));
                 compositeDataBuilder.addAnnotatedNodeInfo(annotatedNodeInfo.build());
             }
         }
-        CompositeStream config = yangRuntimeService.encode(compositeDataBuilder.build(),
-                                                           new DefaultRuntimeContext.Builder()
-                                                                   .setDataFormat(XML_ENCODING_SPECIFIER)
-                                                                   .addAnnotation(
-                                                                           new DefaultAnnotation(
-                                                                                   XMLNS_XC_SPECIFIER,
-                                                                                   NETCONF_1_0_BASE_NAMESPACE))
-                                                                   .build());
+        CompositeStream config = yangRuntimeService.encode(
+                compositeDataBuilder.build(),
+                new DefaultRuntimeContext.Builder()
+                        .setDataFormat(XML_ENCODING_SPECIFIER)
+                        .addAnnotation(new DefaultAnnotation(
+                                XMLNS_XC_SPECIFIER, NETCONF_1_0_BASE_NAMESPACE))
+                        .build());
         /* FIXME need to fix to string conversion. */
-        if (session.editConfig(streamToString(config.resourceData()))) {
-           /* NOTE: a failure to edit is reflected as a NetconfException.*/
-            return true;
+
+        try {
+            String reply = session.requestSync(Utils.editConfig(streamToString(
+                    config.resourceData())));
+        } catch (NetconfException e) {
+            log.error("failed to send a request sync", e);
+            return false;
         }
-        log.warn("Editing of the netconf device: {} failed.", deviceId);
-        return false;
+            /* NOTE: a failure to edit is reflected as a NetconfException.*/
+        return true;
     }
 
     @Override
@@ -210,23 +214,25 @@
         String reply = session.get(null, null);
         Matcher protocolStripper = GET_CORE_MESSAGE_PATTERN.matcher(reply);
         reply = protocolStripper.group(GET_CORE_MESSAGE_GROUP);
-        return yangRuntimeService.decode(new DefaultCompositeStream(
-                null,
+        return yangRuntimeService.decode(
+                new DefaultCompositeStream(
+                        null,
                 /*FIXME is UTF_8 the appropriate encoding? */
-                new ByteArrayInputStream(reply.toString().getBytes(StandardCharsets.UTF_8))),
-                                         new DefaultRuntimeContext.Builder()
-                                                 .setDataFormat(XML_ENCODING_SPECIFIER)
-                                                 .addAnnotation(
-                                                         new DefaultAnnotation(
-                                                                 XMLNS_SPECIFIER,
-                                                                 NETCONF_1_0_BASE_NAMESPACE))
-                                                 .build()).resourceData();
+                        new ByteArrayInputStream(reply.getBytes(StandardCharsets.UTF_8))),
+                new DefaultRuntimeContext.Builder()
+                        .setDataFormat(XML_ENCODING_SPECIFIER)
+                        .addAnnotation(
+                                new DefaultAnnotation(
+                                        XMLNS_SPECIFIER,
+                                        NETCONF_1_0_BASE_NAMESPACE))
+                        .build()).resourceData();
         /* NOTE: a failure to get is reflected as a NetconfException.*/
     }
 
     /**
      * Returns a session for the specified deviceId if this node is its master,
      * returns null otherwise.
+     *
      * @param deviceId the id of node for witch we wish to retrieve a session
      * @return a NetconfSession with the specified node or null
      */
@@ -261,7 +267,7 @@
      * Returns resource data having resource id as "/" and data node tree
      * starting from "/" by creating data nodes for given resource id(parent
      * node for given list of nodes) and list of child nodes.
-     *
+     * <p>
      * This api will be used in encode flow only.
      *
      * @param rid   resource identifier till parent node
@@ -271,14 +277,18 @@
      */
     public static ResourceData getResourceData(
             ResourceId rid, List<DataNode> nodes, YangSerializerContext cont) {
+        if (rid == null) {
+            ResourceData.Builder resData = DefaultResourceData.builder();
+            for (DataNode node : nodes) {
+                resData.addDataNode(node);
+            }
+            return resData.build();
+        }
         List<NodeKey> keys = rid.nodeKeys();
         Iterator<NodeKey> it = keys.iterator();
         DataNode.Builder dbr = SerializerHelper.initializeDataNode(cont);
 
         // checking the resource id weather it is getting started from / or not
-        if (it.next().schemaId().name() != "/") {
-            //exception
-        }
 
         while (it.hasNext()) {
             NodeKey nodekey = it.next();
@@ -322,15 +332,16 @@
         resData.resourceId(null);
         return resData.build();
     }
+
     /**
      * Returns resource id for annotated data node by adding resource id of top
      * level data node to given resource id.
-     *
+     * <p>
      * Annotation will be added to node based on the updated resource id.
      * This api will be used in encode flow only.
      *
-     * @param rid   resource identifier till parent node
-     * @param node  data node
+     * @param rid  resource identifier till parent node
+     * @param node data node
      * @return updated resource id.
      */
     public static ResourceId getAnnotatedNodeResourceId(ResourceId rid,
@@ -338,10 +349,14 @@
 
         String val;
         ResourceId.Builder rIdBldr = ResourceId.builder();
-        try {
-            rIdBldr = rid.copyBuilder();
-        } catch (CloneNotSupportedException e) {
-            e.printStackTrace();
+        if (rid != null) {
+            try {
+                rIdBldr = rid.copyBuilder();
+            } catch (CloneNotSupportedException e) {
+                e.printStackTrace();
+            }
+        } else {
+            rIdBldr.addBranchPointSchema("/", null);
         }
         DataNode.Type type = node.type();
         NodeKey k = node.key();
diff --git a/apps/netconf/client/src/main/java/org/onosproject/netconf/client/impl/Utils.java b/apps/netconf/client/src/main/java/org/onosproject/netconf/client/impl/Utils.java
new file mode 100644
index 0000000..96a1ecc
--- /dev/null
+++ b/apps/netconf/client/src/main/java/org/onosproject/netconf/client/impl/Utils.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * 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.
+ */
+
+package org.onosproject.netconf.client.impl;
+
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+import java.io.StringReader;
+import java.io.StringWriter;
+
+/**
+ * Represents utilities for huawei driver.
+ */
+public final class Utils {
+
+    /**
+     * Prevents creation of utils instance.
+     */
+    private Utils() {
+    }
+
+    // Default namespace given in yang files
+    private static final String XMLNS_STRING = "xmlns=\"ne-l3vpn-api\"";
+    private static final String XMLNS_HUA_STRING = "xmlns=\"http://www.huawei" +
+            ".com/netconf/vrp\" format-version=\"1.0\" content-version=\"1.0\"";
+
+    /**
+     * YMS encode the java object into a xml string with xml namespace equals to
+     * the namespace defined in YANG file. Huawei driver overwriting this
+     * default xml namespace in generated xml string with xml string for Huawei.
+     *
+     * @param request xml string as an output of YMS encode operation
+     * @return formatted string
+     */
+    private static String formatMessage(String request) {
+        if (request.contains(XMLNS_STRING)) {
+            request = request.replaceFirst(XMLNS_STRING, XMLNS_HUA_STRING);
+        }
+        return request;
+    }
+
+    /**
+     * Returns the appended provided xml string with device specific rpc
+     * request tags.
+     *
+     * @param encodedString xml string need to be updated
+     * @return appended new tags xml string
+     */
+    static String editConfig(String encodedString) {
+
+        // Add opening protocol edit config tags.
+        StringBuilder rpc =
+                new StringBuilder(
+                        "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0" +
+                                "\" " +
+                                "message-id=\"1\">");
+        rpc.append("<edit-config>");
+        rpc.append("<target>");
+        rpc.append("<running/>");
+        rpc.append("</target>");
+
+        // Get the formatted XML namespace string.
+        encodedString = formatMessage(encodedString);
+
+        // Add the closing protocol edit config tags.
+        rpc.append(encodedString);
+        rpc.append("</edit-config>");
+        rpc.append("</rpc>");
+
+        return rpc.toString();
+    }
+
+    /**
+     * Converts xml string to pretty format.
+     *
+     * @param input xml string to be converted to pretty format
+     * @return pretty format xml string
+     */
+    static String prettyFormat(String input) {
+        // Prepare input and output stream
+        Source xmlInput = new StreamSource(new StringReader(input));
+        StringWriter stringWriter = new StringWriter();
+        StreamResult xmlOutput = new StreamResult(stringWriter);
+
+        // Create transformer
+        TransformerFactory transformerFactory = TransformerFactory.newInstance();
+        Transformer transformer = null;
+
+        try {
+            transformer = transformerFactory.newTransformer();
+        } catch (TransformerConfigurationException e) {
+            e.printStackTrace();
+        }
+
+        // Need to omit the xml header and set indent to 4
+        if (transformer != null) {
+            transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
+            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+            transformer.setOutputProperty("{http://xml.apache" +
+                                                  ".org/xslt}indent-amount", "4");
+
+            // Covert input string to xml pretty format and return
+            try {
+                transformer.transform(xmlInput, xmlOutput);
+            } catch (TransformerException e) {
+                e.printStackTrace();
+            }
+        }
+        return xmlOutput.getWriter().toString();
+    }
+}