Repository: ansible/ansible

Issue:
[NETCONF] Refactor IOSXR modules to support netconf plugin
##### ISSUE TYPE
<!--- Pick one below and delete the rest: -->
 - Feature Idea


##### COMPONENT NAME
iosxr

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes below -->
```
2.5
```



##### OS / ENVIRONMENT
macOS

##### SUMMARY
Refactor module codes to use netconf plugin:

- [x] iosxr_banner 
- [x] iosxr_logging
- [x] iosxr_interface
- [x] iosxr_system
- [x] iosxr_user
- [ ] ~iosxr_facts~

Note: iosxr_config, iosxr_command and iosxr_netconf shall support `cli` only. 




Discussion:
Files identified in the description:
* [lib/ansible/module_utils/iosxr.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/module_utils/iosxr.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @Qalthos @ganeshrn @gundalow @privateip @rcarrillocruz @trishnaguha
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Files identified in the description:
* [lib/ansible/module_utils/network/iosxr/__init__.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/module_utils/network/iosxr/__init__.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->

Resolving patch:
diff --git a/lib/ansible/module_utils/network/iosxr/iosxr.py b/lib/ansible/module_utils/network/iosxr/iosxr.py
--- a/lib/ansible/module_utils/network/iosxr/iosxr.py
+++ b/lib/ansible/module_utils/network/iosxr/iosxr.py
@@ -65,6 +65,8 @@
     'INTERFACE-CONFIGURATIONS_NSMAP': {None: "http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg"},
     'INFRA-STATISTICS_NSMAP': {None: "http://cisco.com/ns/yang/Cisco-IOS-XR-infra-statsd-oper"},
     'INTERFACE-PROPERTIES_NSMAP': {None: "http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-oper"},
+    'IP-DOMAIN_NSMAP': {None: "http://cisco.com/ns/yang/Cisco-IOS-XR-ip-domain-cfg"},
+    'SYSLOG_NSMAP': {None: "http://cisco.com/ns/yang/Cisco-IOS-XR-infra-syslog-cfg"},
 }
 
 iosxr_provider_spec = {
@@ -181,11 +183,17 @@ def build_xml_subtree(container_ele, xmap, param=None, opcode=None):
                     child = etree.SubElement(parent, candidates[-1])
                 child.text = text
 
+                if meta.get('attrib', None) and opcode in ('delete', 'merge'):
+                    child.set(BASE_1_0 + meta.get('attrib'), opcode)
+
     if len(meta_subtree) > 1:
         for item in meta_subtree:
             container_ele.append(item)
 
-    return sub_root
+    if sub_root == container_ele:
+        return None
+    else:
+        return sub_root
 
 
 def build_xml(container, xmap=None, params=None, opcode=None):
@@ -246,7 +254,9 @@ def build_xml(container, xmap=None, params=None, opcode=None):
             subtree_list = list()
 
             for param in to_list(params):
-                subtree_list.append(build_xml_subtree(container_ele, xmap, param, opcode=opcode))
+                subtree_ele = build_xml_subtree(container_ele, xmap, param, opcode=opcode)
+                if subtree_ele is not None:
+                    subtree_list.append(subtree_ele)
 
             for item in subtree_list:
                 container_ele.append(item)
diff --git a/lib/ansible/modules/network/iosxr/iosxr_banner.py b/lib/ansible/modules/network/iosxr/iosxr_banner.py
--- a/lib/ansible/modules/network/iosxr/iosxr_banner.py
+++ b/lib/ansible/modules/network/iosxr/iosxr_banner.py
@@ -242,7 +242,9 @@ def main():
     elif is_netconf(module):
         config_object = NCConfiguration(module)
 
-    result = config_object.run()
+    result = None
+    if config_object is not None:
+        result = config_object.run()
     module.exit_json(**result)
 
 
diff --git a/lib/ansible/modules/network/iosxr/iosxr_logging.py b/lib/ansible/modules/network/iosxr/iosxr_logging.py
--- a/lib/ansible/modules/network/iosxr/iosxr_logging.py
+++ b/lib/ansible/modules/network/iosxr/iosxr_logging.py
@@ -16,318 +16,669 @@
 ---
 module: iosxr_logging
 version_added: "2.4"
-author: "Trishna Guha (@trishnaguha)"
-short_description: Manage logging on network devices
+author:
+    - "Trishna Guha (@trishnaguha)"
+    - "Kedar Kekan (@kedarX)"
+short_description: Configuration management of system logging services on network devices
 description:
-  - This module provides declarative management of logging
+  - This module provides declarative management configuration of system logging (syslog)
     on Cisco IOS XR devices.
-extends_documentation_fragment: iosxr
 notes:
-  - Tested against IOS XR 6.1.2
+  - Tested against IOS XRv 6.1.2
 options:
   dest:
     description:
-      - Destination of the logs.
-    choices: ['on', 'hostnameprefix', console', 'monitor', 'buffered']
+      - Destination for system logging (syslog) messages.
+    choices: ['host', 'console', 'monitor', 'buffered', 'file']
   name:
     description:
-      - If value of C(dest) is I(file) it indicates file-name,
-        for I(user) it indicates username and for I(host) indicates
-        the host name to be notified.
+      - When C(dest) = I(file) name indicates file-name
+      - When C(dest) = I(host) name indicates the host-name or ip-address of syslog server.
+  vrf:
+    description:
+      - vrf name when syslog server is configured, C(dest) = C(host)
+    default: default
+    version_added: 2.5
   size:
     description:
-      - Size of buffer. The acceptable value is in range from 307200 to
-        125000000 bytes.
-    default: 307200
+      - Size of buffer when C(dest) = C(buffered). The acceptable value is in the range I(307200 to 125000000 bytes). Default 307200
+      - Size of file when C(dest) = C(file). The acceptable value is in the range I(1 to 2097152)KB. Default 2 GB
   facility:
     description:
-      - Set logging facility.
+      - To configure the type of syslog facility in which system logging (syslog) messages are sent to syslog servers
+        Optional config for C(dest) = C(host)
     default: local7
+  hostnameprefix:
+    description:
+      - To append a hostname prefix to system logging (syslog) messages logged to syslog servers.
+        Optional config for C(dest) = C(host)
+    version_added: 2.5
   level:
     description:
-      - Set logging severity levels.
+      - Specifies the severity level for the logging.
     default: debugging
+    aliases: ['severity']
   aggregate:
-    description: List of logging definitions.
+    description: List of syslog logging configuration definitions.
   state:
     description:
-      - State of the logging configuration.
+      - Existential state of the logging configuration on the node.
     default: present
     choices: ['present', 'absent']
 """
 
 EXAMPLES = """
-- name: configure hostnameprefix logging
+- name: configure logging for syslog server host
   iosxr_logging:
-    dest: hostnameprefix
-    name: 172.16.0.1
+    dest: host
+    name: 10.10.10.1
+    level: critical
     state: present
 
-- name: remove hostnameprefix logging configuration
+- name: add hostnameprefix configuration
   iosxr_logging:
-    dest: hostnameprefix
-    name: 172.16.0.1
+    hostnameprefix: host1
     state: absent
 
-- name: configure console logging level and facility
+- name: add facility configuration
+  iosxr_logging:
+    facility: local1
+    state: present
+
+- name: configure console logging level
   iosxr_logging:
     dest: console
-    facility: local7
     level: debugging
     state: present
 
-- name: enable logging to all
+- name: configure monitor logging level
   iosxr_logging:
-    dest : on
+    dest: monitor
+    level: errors
+    state: present
+
+- name: configure syslog to a file
+  iosxr_logging:
+    dest: file
+    name: file_name
+    size: 2048
+    level: errors
+    state: present
 
-- name: configure buffer size
+- name: configure buffered logging with size
   iosxr_logging:
     dest: buffered
-    size: 5000
+    size: 5100000
 
 - name: Configure logging using aggregate
   iosxr_logging:
     aggregate:
       - { dest: console, level: warning }
       - { dest: buffered, size: 4800000 }
+      - { dest: file, name: file3, size: 2048}
+      - { dest: host, name: host3, level: critical}
 
 - name: Delete logging using aggregate
   iosxr_logging:
     aggregate:
       - { dest: console, level: warning }
       - { dest: buffered, size: 4800000 }
+      - { dest: file, name: file3, size: 2048}
+      - { dest: host, name: host3, level: critical}
     state: absent
 """
 
 RETURN = """
 commands:
   description: The list of configuration mode commands to send to the device
-  returned: always
+  returned: always (empty list when no commands to send)
   type: list
   sample:
+    - logging 10.10.10.1 vrf default severity debugging
     - logging facility local7
-    - logging hostnameprefix 172.16.0.1
+    - logging hostnameprefix host1
+    - logging console critical
+    - logging buffered 2097153
+    - logging buffered warnings
+    - logging monitor errors
+    - logging file log_file maxfilesize 1024 severity info
+xml:
+  description: NetConf rpc xml sent to device with transport C(netconf)
+  returned: always (empty list when no xml rpc to send)
+  type: list
+  version_added: 2.5
+  sample:
+    - '<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <syslog xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-infra-syslog-cfg">
+    <files>
+    <file xc:operation="delete">
+    <file-name>file1</file-name>
+    <file-log-attributes>
+    <max-file-size>2097152</max-file-size>
+    <severity>2</severity>
+    </file-log-attributes>
+    </file>
+    </files>
+    </syslog>
+    </config>'
 """
 
 import re
-
+import collections
 from copy import deepcopy
 
 from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils.network.iosxr.iosxr import get_config, load_config
-from ansible.module_utils.network.iosxr.iosxr import iosxr_argument_spec
+from ansible.module_utils.network.iosxr.iosxr import get_config, load_config, build_xml
+from ansible.module_utils.network.iosxr.iosxr import iosxr_argument_spec, etree_findall
+from ansible.module_utils.network.iosxr.iosxr import is_netconf, is_cliconf, etree_find
 from ansible.module_utils.network.common.utils import remove_default_spec
 
 
-def validate_size(value, module):
-    if value:
-        if value and not int(307200) <= value <= int(125000000):
-            module.fail_json(msg='size must be between 307200 and 125000000')
-        else:
-            return value
-
-
-def map_obj_to_commands(updates, module):
-    commands = list()
-    want, have = updates
-    for w in want:
-        dest = w['dest']
-        name = w['name']
-        size = w['size']
-        facility = w['facility']
-        level = w['level']
-        state = w['state']
-        del w['state']
-
-        if state == 'absent' and w in have:
-            if dest == 'hostnameprefix':
-                commands.append('no logging hostnameprefix {}'.format(name))
-            elif dest:
-                commands.append('no logging {}'.format(dest))
-            else:
-                module.fail_json(msg='dest must be among console, monitor, buffered, hostnameprefix, on')
-
-            if facility:
-                commands.append('no logging facility {}'.format(facility))
-
-        if state == 'present' and w not in have:
-            if facility:
-                commands.append('logging facility {}'.format(facility))
+severity_level = {'emergency': '0',
+                  'alert': '1',
+                  'critical': '2',
+                  'error': '3',
+                  'warning': '4',
+                  'notice': '5',
+                  'info': '6',
+                  'debug': '7',
+                  'disable': '15'}
+
+severity_transpose = {'emergencies': 'emergency',
+                      'alerts': 'alert',
+                      'critical': 'critical',
+                      'errors': 'error',
+                      'warning': 'warning',
+                      'notifications': 'notice',
+                      'informational': 'info',
+                      'debugging': 'debug'}
+
+
+class ConfigBase(object):
+    def __init__(self, module):
+        self._flag = None
+        self._module = module
+        self._result = {'changed': False, 'warnings': []}
+        self._want = list()
+        self._have = list()
+
+    def validate_size(self, value, type=None):
+        if value:
+            if type == 'buffer':
+                if value and not int(307200) <= value <= int(125000000):
+                    self._module.fail_json(msg='buffer size must be between 307200 and 125000000')
+            elif type == 'file':
+                if value and not int(1) <= value <= int(2097152):
+                    self._module.fail_json(msg='file size must be between 1 and 2097152')
+        return value
+
+    def map_params_to_obj(self, required_if=None):
+        aggregate = self._module.params.get('aggregate')
+        if aggregate:
+            for item in aggregate:
+                for key in item:
+                    if item.get(key) is None:
+                        item[key] = self._module.params[key]
+
+                d = item.copy()
+
+                if d['dest'] not in ('host', 'file'):
+                    d['name'] = None
+
+                if d['dest'] == 'buffered':
+                    if d['size'] is not None:
+                        d['size'] = str(self.validate_size(d['size'], 'buffer'))
+                    else:
+                        d['size'] = str(307200)
+                elif d['dest'] == 'file':
+                    if d['size'] is not None:
+                        d['size'] = str(self.validate_size(d['size'], 'file'))
+                    else:
+                        d['size'] = str(2097152)
+                else:
+                    d['size'] = None
 
-            if dest == 'hostnameprefix':
-                commands.append('logging hostnameprefix {}'.format(name))
+                if self._flag == 'NC':
+                    d['level'] = severity_transpose[d['level']]
 
-            elif dest == 'on':
-                commands.append('logging on')
+                self._want.append(d)
 
-            elif dest == 'buffered' and size:
-                commands.append('logging buffered {}'.format(size))
+        else:
+            params = self._module.params
+            if params['dest'] not in ('host', 'file'):
+                params['name'] = None
 
+            if params['dest'] == 'buffered':
+                if params['size'] is not None:
+                    params['size'] = str(self.validate_size(params['size'], 'buffer'))
+                else:
+                    params['size'] = str(307200)
+            elif params['dest'] == 'file':
+                if params['size'] is not None:
+                    params['size'] = str(self.validate_size(params['size'], 'file'))
+                else:
+                    params['size'] = str(2097152)
             else:
-                dest_cmd = 'logging {}'.format(dest)
-                if level:
-                    dest_cmd += ' {}'.format(level)
-
-                commands.append(dest_cmd)
-    return commands
-
-
-def parse_facility(line):
-    match = re.search(r'logging facility (\S+)', line, re.M)
-    if match:
-        facility = match.group(1)
-    else:
-        facility = 'local7'
+                params['size'] = None
+
+            if self._flag == 'NC':
+                params['level'] = severity_transpose[params['level']]
+
+            self._want.append({
+                'dest': params['dest'],
+                'name': params['name'],
+                'vrf': params['vrf'],
+                'size': params['size'],
+                'facility': params['facility'],
+                'level': params['level'],
+                'hostnameprefix': params['hostnameprefix'],
+                'state': params['state']
+            })
 
-    return facility
 
+class CliConfiguration(ConfigBase):
+    def __init__(self, module):
+        super(CliConfiguration, self).__init__(module)
+        self._file_list = set()
+        self._host_list = set()
+
+    def map_obj_to_commands(self):
+        commands = list()
+        for want_item in self._want:
+            dest = want_item['dest']
+            name = want_item['name']
+            size = want_item['size']
+            facility = want_item['facility']
+            level = want_item['level']
+            vrf = want_item['vrf']
+            hostnameprefix = want_item['hostnameprefix']
+            state = want_item['state']
+            del want_item['state']
+
+            have_size = None
+            have_console_level = None
+            have_monitor_level = None
+            have_prefix = None
+            have_facility = None
+
+            for item in self._have:
+                if item['dest'] == 'buffered':
+                    have_size = item['size']
+                if item['dest'] == 'console':
+                    have_console_level = item['level']
+                if item['dest'] == 'monitor':
+                    have_monitor_level = item['level']
+                if item['dest'] is None and item['hostnameprefix'] is not None:
+                    have_prefix = item['hostnameprefix']
+                if item['dest'] is None and item['hostnameprefix'] is None and item['facility'] is not None:
+                    have_facility = item['facility']
+
+            if state == 'absent':
+                if dest == 'host' and name in self._host_list:
+                    commands.append('no logging {0} vrf {1}'.format(name, vrf))
+                elif dest == 'file' and name in self._file_list:
+                    commands.append('no logging file {0}'.format(name))
+                elif dest == 'console' and have_console_level is not None:
+                    commands.append('no logging {0}'.format(dest))
+                elif dest == 'monitor' and have_monitor_level:
+                    commands.append('no logging {0}'.format(dest))
+                elif dest == 'buffered' and have_size:
+                    commands.append('no logging {0}'.format(dest))
+
+                if dest is None and hostnameprefix is not None and have_prefix == hostnameprefix:
+                    commands.append('no logging hostnameprefix {0}'.format(hostnameprefix))
+                if dest is None and facility is not None and have_facility == facility:
+                    commands.append('no logging facility {0}'.format(facility))
+
+            if state == 'present':
+                if dest == 'host' and name not in self._host_list:
+                    if level == 'errors' or level == 'informational':
+                        level = severity_transpose[level]
+                    commands.append('logging {0} vrf {1} severity {2}'.format(name, vrf, level))
+                elif dest == 'file' and name not in self._file_list:
+                    if level == 'errors' or level == 'informational':
+                        level = severity_transpose[level]
+                    commands.append('logging file {0} maxfilesize {1} severity {2}'.format(name, size, level))
+                elif dest == 'buffered' and (have_size is None or (have_size is not None and size != have_size)):
+                    commands.append('logging buffered {0}'.format(size))
+                elif dest == 'console' and (have_console_level is None or
+                                            (have_console_level is not None and have_console_level != level)):
+                    commands.append('logging console {0}'.format(level))
+                elif dest == 'monitor' and (have_monitor_level is None or
+                                            (have_monitor_level is not None and have_monitor_level != level)):
+                    commands.append('logging monitor {0}'.format(level))
+
+                if dest is None and hostnameprefix is not None and (have_prefix is None or
+                                                                    (have_prefix is not None and hostnameprefix != have_prefix)):
+                    commands.append('logging hostnameprefix {0}'.format(hostnameprefix))
+                if dest is None and hostnameprefix is None and facility != have_facility:
+                    commands.append('logging facility {0}'.format(facility))
+
+        self._result['commands'] = commands
+        if commands:
+            commit = not self._module.check_mode
+            diff = load_config(self._module, commands, commit=commit)
+            if diff:
+                self._result['diff'] = dict(prepared=diff)
+            self._result['changed'] = True
+
+    def parse_facility(self, line):
+        match = re.search(r'logging facility (\S+)', line, re.M)
+        facility = None
+        if match:
+            facility = match.group(1)
 
-def parse_size(line, dest):
-    size = None
+        return facility
 
-    if dest == 'buffered':
-        match = re.search(r'logging buffered (\S+)', line, re.M)
-        if match:
-            try:
-                int_size = int(match.group(1))
-            except ValueError:
-                int_size = None
-
-            if int_size:
-                if isinstance(int_size, int):
-                    size = str(match.group(1))
-                else:
-                    size = str(307200)
+    def parse_size(self, line, dest):
+        size = None
 
-    return size
+        if dest == 'buffered':
+            match = re.search(r'logging buffered (\S+)', line, re.M)
+            if match:
+                try:
+                    int_size = int(match.group(1))
+                except ValueError:
+                    int_size = None
 
+                if int_size is not None:
+                    if isinstance(int_size, int):
+                        size = str(match.group(1))
+        return size
 
-def parse_name(line, dest):
-    if dest == 'hostnameprefix':
+    def parse_hostnameprefix(self, line):
+        prefix = None
         match = re.search(r'logging hostnameprefix (\S+)', line, re.M)
         if match:
-            name = match.group(1)
-    else:
-        name = None
-
-    return name
+            prefix = match.group(1)
+        return prefix
 
-
-def parse_level(line, dest):
-    level_group = ('emergencies', 'alerts', 'critical', 'errors', 'warning',
-                   'notifications', 'informational', 'debugging')
-
-    if dest == 'hostnameprefix':
-        level = 'debugging'
-
-    else:
-        match = re.search(r'logging {} (\S+)'.format(dest), line, re.M)
+    def parse_name(self, line, dest):
+        name = None
+        if dest == 'file':
+            match = re.search(r'logging file (\S+)', line, re.M)
+            if match:
+                name = match.group(1)
+        elif dest == 'host':
+            match = re.search(r'logging (\S+)', line, re.M)
+            if match:
+                name = match.group(1)
+
+        return name
+
+    def parse_level(self, line, dest):
+        level_group = ('emergencies', 'alerts', 'critical', 'errors', 'warning',
+                       'notifications', 'informational', 'debugging')
+
+        level = None
+        match = re.search(r'logging {0} (\S+)'.format(dest), line, re.M)
         if match:
             if match.group(1) in level_group:
                 level = match.group(1)
-            else:
-                level = 'debugging'
-        else:
-            level = 'debugging'
-
-    return level
-
 
-def map_config_to_obj(module):
-
-    obj = []
-    dest_group = ('console', 'hostnameprefix', 'monitor', 'buffered', 'on')
-
-    data = get_config(module, config_filter='logging')
-    lines = data.split("\n")
-
-    for line in lines:
-        match = re.search(r'logging (\S+)', line, re.M)
-        if match:
-            if match.group(1) in dest_group:
-                dest = match.group(1)
-                obj.append({
+        return level
+
+    def parse_dest(self, line, group):
+        dest_group = ('console', 'monitor', 'buffered', 'file')
+        dest = None
+        if group in dest_group:
+            dest = group
+        elif 'vrf' in line:
+            dest = 'host'
+
+        return dest
+
+    def parse_vrf(self, line, dest):
+        vrf = None
+        if dest == 'host':
+            match = re.search(r'logging (\S+) vrf (\S+)', line, re.M)
+            if match:
+                vrf = match.group(2)
+        return vrf
+
+    def map_config_to_obj(self):
+        data = get_config(self._module, config_filter='logging')
+        lines = data.split("\n")
+
+        for line in lines:
+            match = re.search(r'logging (\S+)', line, re.M)
+            if match:
+                dest = self.parse_dest(line, match.group(1))
+                name = self.parse_name(line, dest)
+                if dest == 'host' and name is not None:
+                    self._host_list.add(name)
+                if dest == 'file' and name is not None:
+                    self._file_list.add(name)
+
+                self._have.append({
                     'dest': dest,
-                    'name': parse_name(line, dest),
-                    'size': parse_size(line, dest),
-                    'facility': parse_facility(line),
-                    'level': parse_level(line, dest)
+                    'name': name,
+                    'size': self.parse_size(line, dest),
+                    'facility': self.parse_facility(line),
+                    'level': self.parse_level(line, dest),
+                    'vrf': self.parse_vrf(line, dest),
+                    'hostnameprefix': self.parse_hostnameprefix(line),
                 })
 
-    return obj
-
-
-def map_params_to_obj(module, required_if=None):
-    obj = []
-
-    aggregate = module.params.get('aggregate')
-    if aggregate:
-        for item in aggregate:
-            for key in item:
-                if item.get(key) is None:
-                    item[key] = module.params[key]
-
-            module._check_required_if(required_if, item)
-            d = item.copy()
-
-            if d['dest'] != 'hostnameprefix':
-                d['name'] = None
-
-            if d['dest'] == 'buffered':
-                if 'size' in d:
-                    d['size'] = str(validate_size(d['size'], module))
-                elif 'size' not in d:
-                    d['size'] = str(307200)
-                else:
-                    pass
-
-            if d['dest'] != 'buffered':
-                d['size'] = None
-
-            obj.append(d)
-
-    else:
-        if module.params['dest'] != 'hostnameprefix':
-            module.params['name'] = None
-
-        if module.params['dest'] == 'buffered':
-            if not module.params['size']:
-                module.params['size'] = str(307200)
-        else:
-            module.params['size'] = None
-
-        if module.params['size'] is None:
-            obj.append({
-                'dest': module.params['dest'],
-                'name': module.params['name'],
-                'size': module.params['size'],
-                'facility': module.params['facility'],
-                'level': module.params['level'],
-                'state': module.params['state']
-            })
-
-        else:
-            obj.append({
-                'dest': module.params['dest'],
-                'name': module.params['name'],
-                'size': str(validate_size(module.params['size'], module)),
-                'facility': module.params['facility'],
-                'level': module.params['level'],
-                'state': module.params['state']
-            })
-
-    return obj
+    def run(self):
+        self.map_params_to_obj()
+        self.map_config_to_obj()
+        self.map_obj_to_commands()
+
+        return self._result
+
+
+class NCConfiguration(ConfigBase):
+    def __init__(self, module):
+        super(NCConfiguration, self).__init__(module)
+        self._flag = 'NC'
+        self._log_file_meta = collections.OrderedDict()
+        self._log_host_meta = collections.OrderedDict()
+        self._log_console_meta = collections.OrderedDict()
+        self._log_monitor_meta = collections.OrderedDict()
+        self._log_buffered_size_meta = collections.OrderedDict()
+        self._log_buffered_level_meta = collections.OrderedDict()
+        self._log_facility_meta = collections.OrderedDict()
+        self._log_prefix_meta = collections.OrderedDict()
+
+    def map_obj_to_xml_rpc(self):
+        self._log_file_meta.update([
+            ('files', {'xpath': 'syslog/files', 'tag': True, 'operation': 'edit'}),
+            ('file', {'xpath': 'syslog/files/file', 'tag': True, 'operation': 'edit', 'attrib': "operation"}),
+            ('a:name', {'xpath': 'syslog/files/file/file-name', 'operation': 'edit'}),
+            ('file-attrib', {'xpath': 'syslog/files/file/file-log-attributes', 'tag': True, 'operation': 'edit'}),
+            ('a:size', {'xpath': 'syslog/files/file/file-log-attributes/max-file-size', 'operation': 'edit'}),
+            ('a:level', {'xpath': 'syslog/files/file/file-log-attributes/severity', 'operation': 'edit'}),
+        ])
+        self._log_host_meta.update([
+            ('host-server', {'xpath': 'syslog/host-server', 'tag': True, 'operation': 'edit'}),
+            ('vrfs', {'xpath': 'syslog/host-server/vrfs', 'tag': True, 'operation': 'edit'}),
+            ('vrf', {'xpath': 'syslog/host-server/vrfs/vrf', 'tag': True, 'operation': 'edit'}),
+            ('a:vrf', {'xpath': 'syslog/host-server/vrfs/vrf/vrf-name', 'operation': 'edit'}),
+            ('ipv4s', {'xpath': 'syslog/host-server/vrfs/vrf/ipv4s', 'tag': True, 'operation': 'edit'}),
+            ('ipv4', {'xpath': 'syslog/host-server/vrfs/vrf/ipv4s/ipv4', 'tag': True, 'operation': 'edit', 'attrib': "operation"}),
+            ('a:name', {'xpath': 'syslog/host-server/vrfs/vrf/ipv4s/ipv4/address', 'operation': 'edit'}),
+            ('ipv4-sev', {'xpath': 'syslog/host-server/vrfs/vrf/ipv4s/ipv4/ipv4-severity-port', 'tag': True, 'operation': 'edit'}),
+            ('a:level', {'xpath': 'syslog/host-server/vrfs/vrf/ipv4s/ipv4/ipv4-severity-port/severity', 'operation': 'edit'}),
+        ])
+        self._log_console_meta.update([
+            ('a:enable-console', {'xpath': 'syslog/enable-console-logging', 'operation': 'edit', 'attrib': "operation"}),
+            ('console', {'xpath': 'syslog/console-logging', 'tag': True, 'operation': 'edit', 'attrib': "operation"}),
+            ('a:console-level', {'xpath': 'syslog/console-logging/logging-level', 'operation': 'edit'}),
+        ])
+        self._log_monitor_meta.update([
+            ('monitor', {'xpath': 'syslog/monitor-logging', 'tag': True, 'operation': 'edit', 'attrib': "operation"}),
+            ('a:monitor-level', {'xpath': 'syslog/monitor-logging/logging-level', 'operation': 'edit'}),
+        ])
+        self._log_buffered_size_meta.update([
+            ('buffered', {'xpath': 'syslog/buffered-logging', 'tag': True, 'operation': 'edit', 'attrib': "operation"}),
+            ('a:size', {'xpath': 'syslog/buffered-logging/buffer-size', 'operation': 'edit'}),
+        ])
+        self._log_buffered_level_meta.update([
+            ('buffered', {'xpath': 'syslog/buffered-logging', 'tag': True, 'operation': 'edit', 'attrib': "operation"}),
+            ('a:level', {'xpath': 'syslog/buffered-logging/logging-level', 'operation': 'edit'}),
+        ])
+        self._log_facility_meta.update([
+            ('facility', {'xpath': 'syslog/logging-facilities', 'tag': True, 'operation': 'edit', 'attrib': "operation"}),
+            ('a:facility', {'xpath': 'syslog/logging-facilities/facility-level', 'operation': 'edit'}),
+        ])
+        self._log_prefix_meta.update([
+            ('a:hostnameprefix', {'xpath': 'syslog/host-name-prefix', 'operation': 'edit', 'attrib': "operation"}),
+        ])
+
+        state = self._module.params['state']
+
+        _get_filter = build_xml('syslog', opcode="filter")
+        running = get_config(self._module, source='running', config_filter=_get_filter)
+
+        file_ele = etree_findall(running, 'file')
+        file_list = list()
+        if len(file_ele):
+            for file in file_ele:
+                file_name = etree_find(file, 'file-name')
+                file_list.append(file_name.text if file_name is not None else None)
+        vrf_ele = etree_findall(running, 'vrf')
+        host_list = list()
+        for vrf in vrf_ele:
+            host_ele = etree_findall(vrf, 'ipv4')
+            for host in host_ele:
+                host_name = etree_find(host, 'address')
+                host_list.append(host_name.text if host_name is not None else None)
+
+        console_ele = etree_find(running, 'console-logging')
+        console_level = etree_find(console_ele, 'logging-level') if console_ele is not None else None
+        have_console = console_level.text if console_level is not None else None
+
+        monitor_ele = etree_find(running, 'monitor-logging')
+        monitor_level = etree_find(monitor_ele, 'logging-level') if monitor_ele is not None else None
+        have_monitor = monitor_level.text if monitor_level is not None else None
+
+        buffered_ele = etree_find(running, 'buffered-logging')
+        buffered_size = etree_find(buffered_ele, 'buffer-size') if buffered_ele is not None else None
+        have_buffered = buffered_size.text if buffered_size is not None else None
+
+        facility_ele = etree_find(running, 'logging-facilities')
+        facility_level = etree_find(facility_ele, 'facility-level') if facility_ele is not None else None
+        have_facility = facility_level.text if facility_level is not None else None
+
+        prefix_ele = etree_find(running, 'host-name-prefix')
+        have_prefix = prefix_ele.text if prefix_ele is not None else None
+
+        console_enable_ele = etree_find(running, 'enable-console-logging')
+        have_console_enable = console_enable_ele.text if console_enable_ele is not None else None
+
+        file_params = list()
+        host_params = list()
+        console_params = dict()
+        monitor_params = dict()
+        buffered_params = dict()
+        facility_params = dict()
+        prefix_params = dict()
+
+        opcode = None
+        if state == 'absent':
+            opcode = "delete"
+            for item in self._want:
+                if item['dest'] == 'file' and item['name'] in file_list:
+                    item['level'] = severity_level[item['level']]
+                    file_params.append(item)
+                elif item['dest'] == 'host' and item['name'] in host_list:
+                    item['level'] = severity_level[item['level']]
+                    host_params.append(item)
+                elif item['dest'] == 'console' and have_console and have_console_enable:
+                    console_params.update({'console-level': item['level']})
+                elif item['dest'] == 'monitor' and have_monitor:
+                    monitor_params.update({'monitor-level': item['level']})
+                elif item['dest'] == 'buffered' and have_buffered:
+                    buffered_params['size'] = str(item['size']) if item['size'] else None
+                    buffered_params['level'] = item['level'] if item['level'] else None
+                elif item['dest'] is None and item['hostnameprefix'] is None and \
+                        item['facility'] is not None and have_facility:
+                    facility_params.update({'facility': item['facility']})
+                elif item['dest'] is None and item['hostnameprefix'] is not None and have_prefix:
+                    prefix_params.update({'hostnameprefix': item['hostnameprefix']})
+        elif state == 'present':
+            opcode = 'merge'
+            for item in self._want:
+                if item['dest'] == 'file':
+                    item['level'] = severity_level[item['level']]
+                    file_params.append(item)
+                elif item['dest'] == 'host':
+                    item['level'] = severity_level[item['level']]
+                    host_params.append(item)
+                elif item['dest'] == 'console':
+                    console_params.update({'console-level': item['level']})
+                elif item['dest'] == 'monitor':
+                    monitor_params.update({'monitor-level': item['level']})
+                elif item['dest'] == 'buffered':
+                    buffered_params['size'] = str(item['size']) if item['size'] else None
+                    buffered_params['level'] = item['level'] if item['level'] else None
+                elif item['dest'] is None and item['hostnameprefix'] is None and \
+                        item['facility'] is not None:
+                    facility_params.update({'facility': item['facility']})
+                elif item['dest'] is None and item['hostnameprefix'] is not None:
+                    prefix_params.update({'hostnameprefix': item['hostnameprefix']})
+
+        self._result['xml'] = []
+        _edit_filter_list = list()
+        if opcode:
+            if len(file_params):
+                _edit_filter_list.append(build_xml('syslog', xmap=self._log_file_meta,
+                                                   params=file_params, opcode=opcode))
+            if len(host_params):
+                _edit_filter_list.append(build_xml('syslog', xmap=self._log_host_meta,
+                                                   params=host_params, opcode=opcode))
+            if len(console_params):
+                _edit_filter_list.append(build_xml('syslog', xmap=self._log_console_meta,
+                                                   params=console_params, opcode=opcode))
+            if len(monitor_params):
+                _edit_filter_list.append(build_xml('syslog', xmap=self._log_monitor_meta,
+                                                   params=monitor_params, opcode=opcode))
+            if len(buffered_params):
+                _edit_filter_list.append(build_xml('syslog', xmap=self._log_buffered_size_meta,
+                                                   params=buffered_params, opcode=opcode))
+                _edit_filter_list.append(build_xml('syslog', xmap=self._log_buffered_level_meta,
+                                                   params=buffered_params, opcode=opcode))
+            if len(facility_params):
+                _edit_filter_list.append(build_xml('syslog', xmap=self._log_facility_meta,
+                                                   params=facility_params, opcode=opcode))
+            if len(prefix_params):
+                _edit_filter_list.append(build_xml('syslog', xmap=self._log_prefix_meta,
+                                                   params=prefix_params, opcode=opcode))
+
+            diff = None
+            if len(_edit_filter_list):
+                commit = not self._module.check_mode
+                diff = load_config(self._module, _edit_filter_list, commit=commit, running=running,
+                                   nc_get_filter=_get_filter)
+
+            if diff:
+                if self._module._diff:
+                    self._result['diff'] = dict(prepared=diff)
+
+                self._result['xml'] = _edit_filter_list
+                self._result['changed'] = True
+
+    def run(self):
+        self.map_params_to_obj()
+        self.map_obj_to_xml_rpc()
+
+        return self._result
 
 
 def main():
     """ main entry point for module execution
     """
     element_spec = dict(
-        dest=dict(type='str', choices=['on', 'hostnameprefix', 'console', 'monitor', 'buffered']),
+        dest=dict(type='str', choices=['host', 'console', 'monitor', 'buffered', 'file']),
         name=dict(type='str'),
         size=dict(type='int'),
+        vrf=dict(type='str', default='default'),
         facility=dict(type='str', default='local7'),
-        level=dict(type='str', default='debugging'),
+        hostnameprefix=dict(type='str'),
+        level=dict(type='str', default='informational', aliases=['severity'],
+                   choices=['emergencies', 'alerts', 'critical', 'errors', 'warning',
+                            'notifications', 'informational', 'debugging']),
         state=dict(default='present', choices=['present', 'absent']),
     )
 
@@ -336,37 +687,37 @@ def main():
     # remove default in aggregate spec, to handle common arguments
     remove_default_spec(aggregate_spec)
 
+    mutually_exclusive = [('dest', 'facility', 'hostnameprefix')]
+
+    required_if = [('dest', 'host', ['name']),
+                   ('dest', 'file', ['name']),
+                   ('dest', 'buffered', ['size']),
+                   ('dest', 'console', ['level']),
+                   ('dest', 'monitor', ['level'])]
+
     argument_spec = dict(
-        aggregate=dict(type='list', elements='dict', options=aggregate_spec),
+        aggregate=dict(type='list', elements='dict', options=aggregate_spec,
+                       mutually_exclusive=mutually_exclusive, required_if=required_if),
     )
 
     argument_spec.update(element_spec)
     argument_spec.update(iosxr_argument_spec)
 
-    required_if = [('dest', 'hostnameprefix', ['name'])]
-
     module = AnsibleModule(argument_spec=argument_spec,
+                           mutually_exclusive=mutually_exclusive,
                            required_if=required_if,
                            supports_check_mode=True)
 
-    warnings = list()
-
-    result = {'changed': False}
-
-    want = map_params_to_obj(module, required_if=required_if)
-    have = map_config_to_obj(module)
-    commands = map_obj_to_commands((want, have), module)
-
-    result['commands'] = commands
-    result['warnings'] = warnings
-
-    if commands:
-        commit = not module.check_mode
-        diff = load_config(module, commands, commit=commit)
-        if diff:
-            result['diff'] = dict(prepared=diff)
-        result['changed'] = True
+    config_object = None
+    if is_cliconf(module):
+        module.deprecate(msg="cli support for 'iosxr_logging' is deprecated. Use transport netconf instead",
+                         version="4 releases from v2.5")
+        config_object = CliConfiguration(module)
+    elif is_netconf(module):
+        config_object = NCConfiguration(module)
 
+    if config_object:
+        result = config_object.run()
     module.exit_json(**result)
 
 if __name__ == '__main__':


Test patch:
diff --git a/test/integration/targets/iosxr_logging/tasks/main.yaml b/test/integration/targets/iosxr_logging/tasks/main.yaml
--- a/test/integration/targets/iosxr_logging/tasks/main.yaml
+++ b/test/integration/targets/iosxr_logging/tasks/main.yaml
@@ -1,2 +1,3 @@
 ---
 - { include: cli.yaml, tags: ['cli'] }
+- { include: netconf.yaml, tags: ['netconf'] }
diff --git a/test/integration/targets/iosxr_logging/tasks/netconf.yaml b/test/integration/targets/iosxr_logging/tasks/netconf.yaml
new file mode 100644
--- /dev/null
+++ b/test/integration/targets/iosxr_logging/tasks/netconf.yaml
@@ -0,0 +1,22 @@
+---
+- name: collect all netconf test cases
+  find:
+    paths: "{{ role_path }}/tests/netconf"
+    patterns: "{{ testcase }}.yaml"
+  register: test_cases
+  delegate_to: localhost
+
+- name: set test_items
+  set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
+
+- name: run test case (connection=netconf)
+  include: "{{ test_case_to_run }} ansible_connection=netconf"
+  with_items: "{{ test_items }}"
+  loop_control:
+    loop_var: test_case_to_run
+
+- name: run test case (connection=local)
+  include: "{{ test_case_to_run }} ansible_connection=local"
+  with_first_found: "{{ test_items }}"
+  loop_control:
+    loop_var: test_case_to_run
diff --git a/test/integration/targets/iosxr_logging/tests/cli/basic.yaml b/test/integration/targets/iosxr_logging/tests/cli/basic.yaml
--- a/test/integration/targets/iosxr_logging/tests/cli/basic.yaml
+++ b/test/integration/targets/iosxr_logging/tests/cli/basic.yaml
@@ -1,33 +1,33 @@
 ---
 # Remove old logging entries so that they don't conflict with tests
-- name: Remove host logging
+- name: remove host logging
   iosxr_logging:
-    dest: hostnameprefix
+    dest: host
     name: 172.16.0.1
     state: absent
     provider: "{{ cli }}"
 
-- name: Remove console logging
+- name: remove console logging
   iosxr_logging:
     dest: console
-    level: warning
     state: absent
     provider: "{{ cli }}"
   register: result
 
-- name: Remove buffer
+- name: remove buffered logging
   iosxr_logging:
     dest: buffered
-    size: 4800000
+    size: 2097155
     state: absent
     provider: "{{ cli }}"
   register: result
 
 # Start tests
-- name: Set up host logging
-  iosxr_logging:
-    dest: hostnameprefix
+- name: set up syslog host logging
+  iosxr_logging: &addhostlog
+    dest: host
     name: 172.16.0.1
+    level: errors
     state: present
     provider: "{{ cli }}"
   register: result
@@ -35,24 +35,19 @@
 - assert:
     that:
       - 'result.changed == true'
-      - '"logging hostnameprefix 172.16.0.1" in result.commands'
-      - '"logging facility local7" in result.commands'
+      - '"logging 172.16.0.1 vrf default severity error" in result.commands'
 
-- name: Set up host logging again (idempotent)
-  iosxr_logging:
-    dest: hostnameprefix
-    name: 172.16.0.1
-    state: present
-    provider: "{{ cli }}"
+- name: set up syslog host logging (idempotent)
+  iosxr_logging: *addhostlog
   register: result
 
-- assert:
+- assert: &false
     that:
       - 'result.changed == false'
 
-- name: Delete/disable host logging
-  iosxr_logging:
-    dest: hostnameprefix
+- name: delete/disable syslog host logging
+  iosxr_logging: &delhostlog
+    dest: host
     name: 172.16.0.1
     state: absent
     provider: "{{ cli }}"
@@ -61,22 +56,16 @@
 - assert:
     that:
       - 'result.changed == true'
-      - '"no logging hostnameprefix 172.16.0.1" in result.commands'
+      - '"no logging 172.16.0.1 vrf default" in result.commands'
 
-- name: Delete/disable host logging (idempotent)
-  iosxr_logging:
-    dest: hostnameprefix
-    name: 172.16.0.1
-    state: absent
-    provider: "{{ cli }}"
+- name: delete/disable syslog host logging (idempotent)
+  iosxr_logging: *delhostlog
   register: result
 
-- assert:
-    that:
-      - 'result.changed == false'
+- assert: *false
 
-- name: Console logging with level warning
-  iosxr_logging:
+- name: add console logging with level warning
+  iosxr_logging: &consolelog
     dest: console
     level: warning
     state: present
@@ -88,10 +77,29 @@
       - 'result.changed == true'
       - '"logging console warning" in result.commands'
 
-- name: Configure Buffer size
-  iosxr_logging:
+- name: console logging with level warning (idempotent)
+  iosxr_logging: *consolelog
+  register: result
+
+- assert: *false
+
+- name: remove console logging with level warning
+  iosxr_logging: 
+    dest: console
+    level: warning
+    state: absent
+    provider: "{{ cli }}"
+  register: result
+
+- assert: &true
+    that:
+      - 'result.changed == true'
+
+- name: configure buffered logging size
+  iosxr_logging: &bufferlog
     dest: buffered
     size: 4800000
+    state: present
     provider: "{{ cli }}"
   register: result
 
@@ -100,11 +108,28 @@
       - 'result.changed == true'
       - '"logging buffered 4800000" in result.commands'
 
-- name: Change logging parameters using aggregate
+- name: configure buffered logging size (idempotence)
+  iosxr_logging: *bufferlog
+  register: result
+
+- assert: *false
+
+- name: remove buffered logging size
+  iosxr_logging:
+    dest: buffered
+    size: 4800000
+    state: absent
+    provider: "{{ cli }}"
+  register: result
+
+- assert: *true
+
+- name: change logging parameters using aggregate
   iosxr_logging:
     aggregate:
       - { dest: console, level: notifications }
       - { dest: buffered, size: 4700000 }
+    state: present
     provider: "{{ cli }}"
   register: result
 
@@ -114,7 +139,7 @@
       - '"logging buffered 4700000" in result.commands'
       - '"logging console notifications" in result.commands'
 
-- name: remove logging as collection tearDown
+- name: remove logging parameters using aggregate
   iosxr_logging:
     aggregate:
       - { dest: console, level: notifications }
diff --git a/test/integration/targets/iosxr_logging/tests/netconf/basic.yaml b/test/integration/targets/iosxr_logging/tests/netconf/basic.yaml
new file mode 100644
--- /dev/null
+++ b/test/integration/targets/iosxr_logging/tests/netconf/basic.yaml
@@ -0,0 +1,174 @@
+---
+# Remove old logging entries so that they don't conflict with tests
+- name: remove host logging
+  iosxr_logging:
+    dest: host
+    name: 172.16.0.1
+    state: absent
+    provider: "{{ netconf }}"
+
+- name: remove console logging
+  iosxr_logging:
+    dest: console
+    state: absent
+    provider: "{{ netconf }}"
+  register: result
+
+- name: remove buffered logging
+  iosxr_logging:
+    dest: buffered
+    size: 2097155
+    state: absent
+    provider: "{{ netconf }}"
+  register: result
+
+# Start tests
+- name: set up syslog host logging
+  iosxr_logging: &addhostlog
+    dest: host
+    name: 172.16.0.1
+    level: errors
+    state: present
+    provider: "{{ netconf }}"
+  register: result
+
+- assert:
+    that:
+      - 'result.changed == true'
+      - '"172.16.0.1" in result.xml[0]'
+
+- name: set up syslog host logging (idempotent)
+  iosxr_logging: *addhostlog
+  register: result
+
+- assert: &false
+    that:
+      - 'result.changed == false'
+
+- name: delete/disable syslog host logging
+  iosxr_logging: &delhostlog
+    dest: host
+    name: 172.16.0.1
+    state: absent
+    provider: "{{ netconf }}"
+  register: result
+
+- assert:
+    that:
+      - 'result.changed == true'
+      - '"172.16.0.1" in result.xml[0]'
+      - '"delete" in result.xml[0]'
+
+- name: delete/disable syslog host logging (idempotent)
+  iosxr_logging: *delhostlog
+  register: result
+
+- assert: *false
+
+- name: add console logging with level warning
+  iosxr_logging: &consolelog
+    dest: console
+    level: warning
+    state: present
+    provider: "{{ netconf }}"
+  register: result
+
+- assert:
+    that:
+      - 'result.changed == true'
+      - '"console" in result.xml[0]'
+      - '"warning" in result.xml[0]'
+
+- name: console logging with level warning (idempotent)
+  iosxr_logging: *consolelog
+  register: result
+
+- assert: *false
+
+- name: remove console logging with level warning
+  iosxr_logging: 
+    dest: console
+    level: warning
+    state: absent
+    provider: "{{ netconf }}"
+  register: result
+
+- assert: &true
+    that:
+      - 'result.changed == true'
+
+- name: configure buffered logging size
+  iosxr_logging: &bufferlog
+    dest: buffered
+    size: 4800000
+    state: present
+    provider: "{{ netconf }}"
+  register: result
+
+- assert:
+    that:
+      - 'result.changed == true'
+      - '"buffered" in result.xml[0]'
+      - '"4800000" in result.xml[0]'
+
+- name: configure buffered logging size (idempotence)
+  iosxr_logging: *bufferlog
+  register: result
+
+- assert: *false
+
+- name: remove buffered logging size
+  iosxr_logging:
+    dest: buffered
+    size: 4800000
+    state: absent
+    provider: "{{ netconf }}"
+  register: result
+
+- assert: *true
+
+- name: change logging parameters using aggregate
+  iosxr_logging:
+    aggregate:
+      - { dest: console, level: notifications }
+      - { dest: buffered, size: 4700000 }
+      - { dest: monitor, level: alerts }
+      - { dest: host, name: 10.10.10.1, level: errors }
+      - { dest: host, name: 10.10.10.2 }
+      - { dest: file, name: file1, size: 2048, level: critical}
+      - { dest: file, name: file2, size: 2048 }
+      - { facility: local3 }
+      - { hostnameprefix: host3 }
+    state: present
+    provider: "{{ netconf }}"
+  register: result
+
+- assert:
+    that:
+      - 'result.changed == true'
+      - '"file1" in result.xml[0]'
+      - '"file2" in result.xml[0]'
+      - '"10.10.10.1" in result.xml[1]'
+      - '"10.10.10.2" in result.xml[1]'
+      - '"notice" in result.xml[2]'
+      - '"alert" in result.xml[3]'
+      - '"4700000" in result.xml[4]'
+      - '"info" in result.xml[5]'
+      - '"local3" in result.xml[6]'
+      - '"host3" in result.xml[7]'
+
+- name: remove logging parameters using aggregate
+  iosxr_logging:
+    aggregate:
+      - { dest: console, level: notifications }
+      - { dest: buffered, size: 4700000 }
+    state: absent
+    provider: "{{ netconf }}"
+  register: result
+
+- assert:
+    that:
+      - 'result.changed == true'
+      - '"console" in result.xml[0]'
+      - '"buffered" in result.xml[1]'
+      - '"buffered" in result.xml[2]'
diff --git a/test/sanity/pylint/ignore.txt b/test/sanity/pylint/ignore.txt
--- a/test/sanity/pylint/ignore.txt
+++ b/test/sanity/pylint/ignore.txt
@@ -94,7 +94,6 @@ lib/ansible/modules/network/ios/ios_l3_interface.py ansible-format-automatic-spe
 lib/ansible/modules/network/ios/ios_vlan.py ansible-format-automatic-specification
 lib/ansible/modules/network/iosxr/iosxr_banner.py ansible-format-automatic-specification
 lib/ansible/modules/network/iosxr/iosxr_interface.py ansible-format-automatic-specification
-lib/ansible/modules/network/iosxr/iosxr_logging.py ansible-format-automatic-specification
 lib/ansible/modules/network/nxos/nxos_logging.py ansible-format-automatic-specification
 lib/ansible/modules/network/nxos/nxos_snapshot.py ansible-format-automatic-specification
 lib/ansible/modules/network/vyos/vyos_vlan.py ansible-format-automatic-specification