diff --git a/lib/ansible/module_utils/netconf.py b/lib/ansible/module_utils/netconf.py
new file mode 100644
index 00000000000..cf49019766b
--- /dev/null
+++ b/lib/ansible/module_utils/netconf.py
@@ -0,0 +1,81 @@
+# This code is part of Ansible, but is an independent component.
+# This particular file snippet, and this file snippet only, is BSD licensed.
+# Modules you write using this snippet, which is embedded dynamically by Ansible
+# still belong to the author of the module, and may assign their own license
+# to the complete work.
+#
+# (c) 2016 Red Hat Inc.
+#
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+#
+#    * Redistributions of source code must retain the above copyright
+#      notice, this list of conditions and the following disclaimer.
+#    * Redistributions in binary form must reproduce the above copyright notice,
+#      this list of conditions and the following disclaimer in the documentation
+#      and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+from contextlib import contextmanager
+
+from ncclient.xml_ import new_ele, sub_ele, to_xml, to_ele
+
+def send_request(module, obj, check_rc=True):
+    request = to_xml(obj)
+    rc, out, err = module.exec_command(request)
+    if rc != 0:
+        if check_rc:
+            module.fail_json(msg=str(err))
+        return to_ele(err)
+    return to_ele(out)
+
+def children(root, iterable):
+    for item in iterable:
+        try:
+            ele = sub_ele(ele, item)
+        except NameError:
+            ele = sub_ele(root, item)
+
+def lock(module, target='candidate'):
+    obj = new_ele('lock')
+    children(obj, ('target', target))
+    return send_request(module, obj)
+
+def unlock(module, target='candidate'):
+    obj = new_ele('unlock')
+    children(obj, ('target', target))
+    return send_request(module, obj)
+
+def commit(module):
+    return send_request(module, new_ele('commit'))
+
+def discard_changes(module):
+    return send_request(module, new_ele('discard-changes'))
+
+def validate(module):
+    obj = new_ele('validate')
+    children(obj, ('source', 'candidate'))
+    return send_request(module, obj)
+
+def get_config(module, source='running', filter=None):
+    obj = new_ele('get-config')
+    children(obj, ('source', source))
+    children(obj, ('filter', filter))
+    return send_request(module, obj)
+
+@contextmanager
+def locked_config(module):
+    try:
+        lock(module)
+        yield
+    finally:
+        unlock(module)