1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3
4# Copyright: (c) 2017, Ansible by Red Hat, inc
5# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6
7from __future__ import absolute_import, division, print_function
8__metaclass__ = type
9
10
11ANSIBLE_METADATA = {'metadata_version': '1.1',
12                    'status': ['deprecated'],
13                    'supported_by': 'network'}
14
15
16DOCUMENTATION = """
17---
18module: iosxr_interface
19version_added: "2.4"
20author:
21    - "Ganesh Nalawade (@ganeshrn)"
22    - "Kedar Kekan (@kedarX)"
23short_description: Manage Interface on Cisco IOS XR network devices
24description:
25  - This module provides declarative management of Interfaces
26    on Cisco IOS XR network devices.
27deprecated:
28  removed_in: '2.13'
29  alternative: iosxr_interfaces
30  why: Newer and updated modules released with more functionality in Ansible 2.9
31requirements:
32    - ncclient >= 0.5.3 when using netconf
33    - lxml >= 4.1.1 when using netconf
34extends_documentation_fragment: iosxr
35notes:
36  - This module works with connection C(network_cli) and C(netconf). See L(the IOS-XR Platform Options,../network/user_guide/platform_iosxr.html).
37  - Tested against IOS XRv 6.1.3.
38  - Preconfiguration of physical interfaces is not supported with C(netconf) transport.
39options:
40  name:
41    description:
42      - Name of the interface to configure in C(type + path) format. e.g. C(GigabitEthernet0/0/0/0)
43    required: true
44  description:
45    description:
46      - Description of Interface being configured.
47  enabled:
48    description:
49      - Removes the shutdown configuration, which removes the forced administrative down on the interface,
50        enabling it to move to an up or down state.
51    type: bool
52    default: True
53  active:
54    description:
55      - Whether the interface is C(active) or C(preconfigured). Preconfiguration allows you to configure modular
56        services cards before they are inserted into the router. When the cards are inserted, they are instantly
57        configured. Active cards are the ones already inserted.
58    choices: ['active', 'preconfigure']
59    default: active
60    version_added: 2.5
61  speed:
62    description:
63      - Configure the speed for an interface. Default is auto-negotiation when not configured.
64    choices: ['10', '100', '1000']
65  mtu:
66    description:
67      - Sets the MTU value for the interface. Range is between 64 and 65535'
68  duplex:
69    description:
70      - Configures the interface duplex mode. Default is auto-negotiation when not configured.
71    choices: ['full', 'half']
72  tx_rate:
73    description:
74      - Transmit rate in bits per second (bps).
75      - This is state check parameter only.
76      - Supports conditionals, see L(Conditionals in Networking Modules,../network/user_guide/network_working_with_command_output.html)
77  rx_rate:
78    description:
79      - Receiver rate in bits per second (bps).
80      - This is state check parameter only.
81      - Supports conditionals, see L(Conditionals in Networking Modules,../network/user_guide/network_working_with_command_output.html)
82  aggregate:
83    description:
84      - List of Interface definitions. Include multiple interface configurations together,
85        one each on a separate line
86  delay:
87    description:
88      - Time in seconds to wait before checking for the operational state on remote
89        device. This wait is applicable for operational state argument which are
90        I(state) with values C(up)/C(down), I(tx_rate) and I(rx_rate).
91    default: 10
92  state:
93    description:
94      - State of the Interface configuration, C(up) means present and
95        operationally up and C(down) means present and operationally C(down)
96    default: present
97    choices: ['present', 'absent', 'up', 'down']
98"""
99
100EXAMPLES = """
101- name: configure interface
102  iosxr_interface:
103      name: GigabitEthernet0/0/0/2
104      description: test-interface
105      speed: 100
106      duplex: half
107      mtu: 512
108
109- name: remove interface
110  iosxr_interface:
111    name: GigabitEthernet0/0/0/2
112    state: absent
113
114- name: make interface up
115  iosxr_interface:
116    name: GigabitEthernet0/0/0/2
117    enabled: True
118
119- name: make interface down
120  iosxr_interface:
121    name: GigabitEthernet0/0/0/2
122    enabled: False
123
124- name: Create interface using aggregate
125  iosxr_interface:
126    aggregate:
127    - name: GigabitEthernet0/0/0/3
128    - name: GigabitEthernet0/0/0/2
129    speed: 100
130    duplex: full
131    mtu: 512
132    state: present
133
134- name: Create interface using aggregate along with additional params in aggregate
135  iosxr_interface:
136    aggregate:
137    - { name: GigabitEthernet0/0/0/3, description: test-interface 3 }
138    - { name: GigabitEthernet0/0/0/2, description: test-interface 2 }
139    speed: 100
140    duplex: full
141    mtu: 512
142    state: present
143
144- name: Delete interface using aggregate
145  iosxr_interface:
146    aggregate:
147    - name: GigabitEthernet0/0/0/3
148    - name: GigabitEthernet0/0/0/2
149    state: absent
150
151- name: Check intent arguments
152  iosxr_interface:
153    name: GigabitEthernet0/0/0/5
154    state: up
155    delay: 20
156
157- name: Config + intent
158  iosxr_interface:
159    name: GigabitEthernet0/0/0/5
160    enabled: False
161    state: down
162    delay: 20
163"""
164
165RETURN = """
166commands:
167  description: The list of configuration mode commands sent to device with transport C(cli)
168  returned: always (empty list when no commands to send)
169  type: list
170  sample:
171  - interface GigabitEthernet0/0/0/2
172  - description test-interface
173  - duplex half
174  - mtu 512
175
176xml:
177  description: NetConf rpc xml sent to device with transport C(netconf)
178  returned: always (empty list when no xml rpc to send)
179  type: list
180  version_added: 2.5
181  sample:
182  - '<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
183    <interface-configurations xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg">
184    <interface-configuration xc:operation="merge">
185    <active>act</active>
186    <interface-name>GigabitEthernet0/0/0/0</interface-name>
187    <description>test-interface-0</description>
188    <mtus><mtu>
189    <owner>GigabitEthernet</owner>
190    <mtu>512</mtu>
191    </mtu></mtus>
192    <ethernet xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-drivers-media-eth-cfg">
193    <speed>100</speed>
194    <duplex>half</duplex>
195    </ethernet>
196    </interface-configuration>
197    </interface-configurations></config>'
198"""
199import re
200from time import sleep
201from copy import deepcopy
202import collections
203
204from ansible.module_utils.basic import AnsibleModule
205from ansible.module_utils.network.iosxr.iosxr import get_config, load_config, build_xml
206from ansible.module_utils.network.iosxr.iosxr import run_commands, iosxr_argument_spec, get_oper
207from ansible.module_utils.network.iosxr.iosxr import is_netconf, is_cliconf, etree_findall, etree_find
208from ansible.module_utils.network.common.utils import conditional, remove_default_spec
209
210
211def validate_mtu(value):
212    if value and not 64 <= int(value) <= 65535:
213        return False, 'mtu must be between 64 and 65535'
214    return True, None
215
216
217class ConfigBase(object):
218    def __init__(self, module):
219        self._module = module
220        self._result = {'changed': False, 'warnings': []}
221        self._want = list()
222        self._have = list()
223
224    def validate_param_values(self, param=None):
225        for key, value in param.items():
226            # validate the param value (if validator func exists)
227            validator = globals().get('validate_%s' % key)
228            if callable(validator):
229                rc, msg = validator(value)
230                if not rc:
231                    self._module.fail_json(msg=msg)
232
233    def map_params_to_obj(self):
234        aggregate = self._module.params.get('aggregate')
235        if aggregate:
236            for item in aggregate:
237                for key in item:
238                    if item.get(key) is None:
239                        item[key] = self._module.params[key]
240
241                self.validate_param_values(item)
242                d = item.copy()
243
244                match = re.match(r"(^[a-z]+)([0-9/]+$)", d['name'], re.I)
245                if match:
246                    d['owner'] = match.groups()[0]
247
248                if d['active'] == 'preconfigure':
249                    d['active'] = 'pre'
250                else:
251                    d['active'] = 'act'
252
253                self._want.append(d)
254
255        else:
256            self.validate_param_values(self._module.params)
257            params = {
258                'name': self._module.params['name'],
259                'description': self._module.params['description'],
260                'speed': self._module.params['speed'],
261                'mtu': self._module.params['mtu'],
262                'duplex': self._module.params['duplex'],
263                'state': self._module.params['state'],
264                'delay': self._module.params['delay'],
265                'tx_rate': self._module.params['tx_rate'],
266                'rx_rate': self._module.params['rx_rate'],
267                'enabled': self._module.params['enabled'],
268                'active': self._module.params['active'],
269            }
270
271            match = re.match(r"(^[a-z]+)([0-9/]+$)", params['name'], re.I)
272            if match:
273                params['owner'] = match.groups()[0]
274
275                if params['active'] == 'preconfigure':
276                    params['active'] = 'pre'
277                else:
278                    params['active'] = 'act'
279
280            self._want.append(params)
281
282
283class CliConfiguration(ConfigBase):
284    def __init__(self, module):
285        super(CliConfiguration, self).__init__(module)
286
287    def parse_shutdown(self, intf_config):
288        for cfg in intf_config:
289            match = re.search(r'%s' % 'shutdown', cfg, re.M)
290            if match:
291                return True
292        return False
293
294    def parse_config_argument(self, intf_config, arg):
295        for cfg in intf_config:
296            match = re.search(r'%s (.+)$' % arg, cfg, re.M)
297            if match:
298                return match.group(1)
299
300    def search_obj_in_list(self, name):
301        for obj in self._have:
302            if obj['name'] == name:
303                return obj
304        return None
305
306    def map_config_to_obj(self):
307        data = get_config(self._module, config_filter='interface')
308        data_lines = data.splitlines()
309        start_indexes = [i for i, e in enumerate(data_lines) if e.startswith('interface')]
310        end_indexes = [i for i, e in enumerate(data_lines) if e == '!']
311
312        intf_configs = list()
313        for start_index, end_index in zip(start_indexes, end_indexes):
314            intf_configs.append([i.strip() for i in data_lines[start_index:end_index]])
315
316        if not intf_configs:
317            return list()
318
319        for intf_config in intf_configs:
320            name = intf_config[0].strip().split()[1]
321
322            active = 'act'
323            if name == 'preconfigure':
324                active = 'pre'
325                name = intf_config[0].strip().split()[2]
326
327            obj = {
328                'name': name,
329                'description': self.parse_config_argument(intf_config, 'description'),
330                'speed': self.parse_config_argument(intf_config, 'speed'),
331                'duplex': self.parse_config_argument(intf_config, 'duplex'),
332                'mtu': self.parse_config_argument(intf_config, 'mtu'),
333                'enabled': not bool(self.parse_shutdown(intf_config)),
334                'active': active,
335                'state': 'present'
336            }
337            self._have.append(obj)
338
339    def map_obj_to_commands(self):
340        commands = list()
341
342        args = ('speed', 'description', 'duplex', 'mtu')
343        for want_item in self._want:
344            name = want_item['name']
345            disable = not want_item['enabled']
346            state = want_item['state']
347
348            obj_in_have = self.search_obj_in_list(name)
349            interface = 'interface ' + name
350
351            if state == 'absent' and obj_in_have:
352                commands.append('no ' + interface)
353
354            elif state in ('present', 'up', 'down'):
355                if obj_in_have:
356                    for item in args:
357                        candidate = want_item.get(item)
358                        running = obj_in_have.get(item)
359                        if candidate != running:
360                            if candidate:
361                                cmd = interface + ' ' + item + ' ' + str(candidate)
362                                commands.append(cmd)
363
364                    if disable and obj_in_have.get('enabled', False):
365                        commands.append(interface + ' shutdown')
366                    elif not disable and not obj_in_have.get('enabled', False):
367                        commands.append('no ' + interface + ' shutdown')
368                else:
369                    for item in args:
370                        value = want_item.get(item)
371                        if value:
372                            commands.append(interface + ' ' + item + ' ' + str(value))
373                    if not disable:
374                        commands.append('no ' + interface + ' shutdown')
375        self._result['commands'] = commands
376
377        if commands:
378            commit = not self._module.check_mode
379            diff = load_config(self._module, commands, commit=commit)
380            if diff:
381                self._result['diff'] = dict(prepared=diff)
382            self._result['changed'] = True
383
384    def check_declarative_intent_params(self):
385        failed_conditions = []
386        for want_item in self._want:
387            want_state = want_item.get('state')
388            want_tx_rate = want_item.get('tx_rate')
389            want_rx_rate = want_item.get('rx_rate')
390            if want_state not in ('up', 'down') and not want_tx_rate and not want_rx_rate:
391                continue
392
393            if self._result['changed']:
394                sleep(want_item['delay'])
395
396            command = 'show interfaces {0!s}'.format(want_item['name'])
397            out = run_commands(self._module, command)[0]
398
399            if want_state in ('up', 'down'):
400                match = re.search(r'%s (\w+)' % 'line protocol is', out, re.M)
401                have_state = None
402                if match:
403                    have_state = match.group(1)
404                    if have_state.strip() == 'administratively':
405                        match = re.search(r'%s (\w+)' % 'administratively', out, re.M)
406                        if match:
407                            have_state = match.group(1)
408
409                if have_state is None or not conditional(want_state, have_state.strip()):
410                    failed_conditions.append('state ' + 'eq({0!s})'.format(want_state))
411
412            if want_tx_rate:
413                match = re.search(r'%s (\d+)' % 'output rate', out, re.M)
414                have_tx_rate = None
415                if match:
416                    have_tx_rate = match.group(1)
417
418                if have_tx_rate is None or not conditional(want_tx_rate, have_tx_rate.strip(), cast=int):
419                    failed_conditions.append('tx_rate ' + want_tx_rate)
420
421            if want_rx_rate:
422                match = re.search(r'%s (\d+)' % 'input rate', out, re.M)
423                have_rx_rate = None
424                if match:
425                    have_rx_rate = match.group(1)
426
427                if have_rx_rate is None or not conditional(want_rx_rate, have_rx_rate.strip(), cast=int):
428                    failed_conditions.append('rx_rate ' + want_rx_rate)
429
430        if failed_conditions:
431            msg = 'One or more conditional statements have not been satisfied'
432            self._module.fail_json(msg=msg, failed_conditions=failed_conditions)
433
434    def run(self):
435        self.map_params_to_obj()
436        self.map_config_to_obj()
437        self.map_obj_to_commands()
438        self.check_declarative_intent_params()
439
440        return self._result
441
442
443class NCConfiguration(ConfigBase):
444    def __init__(self, module):
445        super(NCConfiguration, self).__init__(module)
446
447        self._intf_meta = collections.OrderedDict()
448        self._shut_meta = collections.OrderedDict()
449        self._data_rate_meta = collections.OrderedDict()
450        self._line_state_meta = collections.OrderedDict()
451
452    def map_obj_to_xml_rpc(self):
453        self._intf_meta.update([
454            ('interface-configuration', {'xpath': 'interface-configurations/interface-configuration', 'tag': True, 'attrib': 'operation'}),
455            ('a:active', {'xpath': 'interface-configurations/interface-configuration/active', 'operation': 'edit'}),
456            ('a:name', {'xpath': 'interface-configurations/interface-configuration/interface-name'}),
457            ('a:description', {'xpath': 'interface-configurations/interface-configuration/description', 'operation': 'edit'}),
458            ('mtus', {'xpath': 'interface-configurations/interface-configuration/mtus', 'tag': True, 'operation': 'edit'}),
459            ('mtu', {'xpath': 'interface-configurations/interface-configuration/mtus/mtu', 'tag': True, 'operation': 'edit'}),
460            ('a:owner', {'xpath': 'interface-configurations/interface-configuration/mtus/mtu/owner', 'operation': 'edit'}),
461            ('a:mtu', {'xpath': 'interface-configurations/interface-configuration/mtus/mtu/mtu', 'operation': 'edit'}),
462            ('CEthernet', {'xpath': 'interface-configurations/interface-configuration/ethernet', 'tag': True, 'operation': 'edit', 'ns': True}),
463            ('a:speed', {'xpath': 'interface-configurations/interface-configuration/ethernet/speed', 'operation': 'edit'}),
464            ('a:duplex', {'xpath': 'interface-configurations/interface-configuration/ethernet/duplex', 'operation': 'edit'}),
465        ])
466
467        self._shut_meta.update([
468            ('interface-configuration', {'xpath': 'interface-configurations/interface-configuration', 'tag': True}),
469            ('a:active', {'xpath': 'interface-configurations/interface-configuration/active', 'operation': 'edit'}),
470            ('a:name', {'xpath': 'interface-configurations/interface-configuration/interface-name'}),
471            ('shutdown', {'xpath': 'interface-configurations/interface-configuration/shutdown', 'tag': True, 'operation': 'edit', 'attrib': 'operation'}),
472        ])
473        state = self._module.params['state']
474
475        _get_filter = build_xml('interface-configurations', xmap=self._intf_meta, params=self._want, opcode="filter")
476
477        running = get_config(self._module, source='running', config_filter=_get_filter)
478        intfcfg_nodes = etree_findall(running, 'interface-configuration')
479
480        intf_list = set()
481        shut_list = set()
482        for item in intfcfg_nodes:
483            intf_name = etree_find(item, 'interface-name').text
484            if intf_name is not None:
485                intf_list.add(intf_name)
486
487                if etree_find(item, 'shutdown') is not None:
488                    shut_list.add(intf_name)
489
490        intf_params = list()
491        shut_params = list()
492        noshut_params = list()
493        for index, item in enumerate(self._want):
494            if item['name'] in intf_list:
495                intf_params.append(item)
496            if not item['enabled']:
497                shut_params.append(item)
498            if item['name'] in shut_list and item['enabled']:
499                noshut_params.append(item)
500
501        opcode = None
502        if state == 'absent':
503            if intf_params:
504                opcode = "delete"
505        elif state in ('present', 'up', 'down'):
506            intf_params = self._want
507            opcode = 'merge'
508
509        self._result['xml'] = []
510        _edit_filter_list = list()
511        if opcode:
512            _edit_filter_list.append(build_xml('interface-configurations', xmap=self._intf_meta,
513                                               params=intf_params, opcode=opcode))
514
515            if opcode == 'merge':
516                if len(shut_params):
517                    _edit_filter_list.append(build_xml('interface-configurations', xmap=self._shut_meta,
518                                                       params=shut_params, opcode='merge'))
519                if len(noshut_params):
520                    _edit_filter_list.append(build_xml('interface-configurations', xmap=self._shut_meta,
521                                                       params=noshut_params, opcode='delete'))
522            diff = None
523            if len(_edit_filter_list):
524                commit = not self._module.check_mode
525                diff = load_config(self._module, _edit_filter_list, commit=commit, running=running,
526                                   nc_get_filter=_get_filter)
527
528            if diff:
529                if self._module._diff:
530                    self._result['diff'] = dict(prepared=diff)
531
532                self._result['xml'] = _edit_filter_list
533                self._result['changed'] = True
534
535    def check_declarative_intent_params(self):
536        failed_conditions = []
537
538        self._data_rate_meta.update([
539            ('interfaces', {'xpath': 'infra-statistics/interfaces', 'tag': True}),
540            ('interface', {'xpath': 'infra-statistics/interfaces/interface', 'tag': True}),
541            ('a:name', {'xpath': 'infra-statistics/interfaces/interface/interface-name'}),
542            ('cache', {'xpath': 'infra-statistics/interfaces/interface/cache', 'tag': True}),
543            ('data-rate', {'xpath': 'infra-statistics/interfaces/interface/cache/data-rate', 'tag': True}),
544            ('input-data-rate', {'xpath': 'infra-statistics/interfaces/interface/cache/data-rate/input-data-rate', 'tag': True}),
545            ('output-data-rate', {'xpath': 'infra-statistics/interfaces/interface/cache/data-rate/output-data-rate', 'tag': True}),
546        ])
547
548        self._line_state_meta.update([
549            ('data-nodes', {'xpath': 'interface-properties/data-nodes', 'tag': True}),
550            ('data-node', {'xpath': 'interface-properties/data-nodes/data-node', 'tag': True}),
551            ('system-view', {'xpath': 'interface-properties/data-nodes/data-node/system-view', 'tag': True}),
552            ('interfaces', {'xpath': 'interface-properties/data-nodes/data-node/system-view/interfaces', 'tag': True}),
553            ('interface', {'xpath': 'interface-properties/data-nodes/data-node/system-view/interfaces/interface', 'tag': True}),
554            ('a:name', {'xpath': 'interface-properties/data-nodes/data-node/system-view/interfaces/interface/interface-name'}),
555            ('line-state', {'xpath': 'interface-properties/data-nodes/data-node/system-view/interfaces/interface/line-state', 'tag': True}),
556        ])
557
558        _rate_filter = build_xml('infra-statistics', xmap=self._data_rate_meta, params=self._want, opcode="filter")
559        out = get_oper(self._module, filter=_rate_filter)
560        data_rate_list = etree_findall(out, 'interface')
561        data_rate_map = dict()
562        for item in data_rate_list:
563            data_rate_map.update({etree_find(item, 'interface-name').text: dict()})
564            data_rate_map[etree_find(item, 'interface-name').text].update({'input-data-rate': etree_find(item, 'input-data-rate').text,
565                                                                           'output-data-rate': etree_find(item, 'output-data-rate').text})
566
567        _line_state_filter = build_xml('interface-properties', xmap=self._line_state_meta, params=self._want, opcode="filter")
568        out = get_oper(self._module, filter=_line_state_filter)
569        line_state_list = etree_findall(out, 'interface')
570        line_state_map = dict()
571        for item in line_state_list:
572            line_state_map.update({etree_find(item, 'interface-name').text: etree_find(item, 'line-state').text})
573
574        for want_item in self._want:
575            want_state = want_item.get('state')
576            want_tx_rate = want_item.get('tx_rate')
577            want_rx_rate = want_item.get('rx_rate')
578            if want_state not in ('up', 'down') and not want_tx_rate and not want_rx_rate:
579                continue
580
581            if self._result['changed']:
582                sleep(want_item['delay'])
583
584            if want_state in ('up', 'down'):
585                if want_state not in line_state_map[want_item['name']]:
586                    failed_conditions.append('state ' + 'eq({0!s})'.format(want_state))
587
588            if want_tx_rate:
589                if want_tx_rate != data_rate_map[want_item['name']]['output-data-rate']:
590                    failed_conditions.append('tx_rate ' + want_tx_rate)
591
592            if want_rx_rate:
593                if want_rx_rate != data_rate_map[want_item['name']]['input-data-rate']:
594                    failed_conditions.append('rx_rate ' + want_rx_rate)
595
596        if failed_conditions:
597            msg = 'One or more conditional statements have not been satisfied'
598            self._module.fail_json(msg=msg, failed_conditions=failed_conditions)
599
600    def run(self):
601        self.map_params_to_obj()
602        self.map_obj_to_xml_rpc()
603        self.check_declarative_intent_params()
604        return self._result
605
606
607def main():
608    """ main entry point for module execution
609    """
610    element_spec = dict(
611        name=dict(type='str'),
612        description=dict(type='str'),
613        speed=dict(choices=['10', '100', '1000']),
614        mtu=dict(),
615        duplex=dict(choices=['full', 'half']),
616        enabled=dict(default=True, type='bool'),
617        active=dict(default='active', type='str', choices=['active', 'preconfigure']),
618        tx_rate=dict(),
619        rx_rate=dict(),
620        delay=dict(default=10, type='int'),
621        state=dict(default='present',
622                   choices=['present', 'absent', 'up', 'down'])
623    )
624
625    aggregate_spec = deepcopy(element_spec)
626    aggregate_spec['name'] = dict(required=True)
627
628    # remove default in aggregate spec, to handle common arguments
629    remove_default_spec(aggregate_spec)
630
631    argument_spec = dict(
632        aggregate=dict(type='list', elements='dict', options=aggregate_spec),
633    )
634
635    argument_spec.update(element_spec)
636    argument_spec.update(iosxr_argument_spec)
637
638    required_one_of = [['name', 'aggregate']]
639    mutually_exclusive = [['name', 'aggregate']]
640
641    module = AnsibleModule(argument_spec=argument_spec,
642                           required_one_of=required_one_of,
643                           mutually_exclusive=mutually_exclusive,
644                           supports_check_mode=True)
645
646    config_object = None
647    if is_cliconf(module):
648        # Commenting the below cliconf deprecation support call for Ansible 2.9 as it'll be continued to be supported
649        # module.deprecate("cli support for 'iosxr_interface' is deprecated. Use transport netconf instead",
650        #                  version='2.9')
651        config_object = CliConfiguration(module)
652    elif is_netconf(module):
653        if module.params['active'] == 'preconfigure':
654            module.fail_json(msg="Physical interface pre-configuration is not supported with transport 'netconf'")
655        config_object = NCConfiguration(module)
656
657    result = {}
658    if config_object:
659        result = config_object.run()
660    module.exit_json(**result)
661
662
663if __name__ == '__main__':
664    main()
665