1#!/usr/bin/python
2#
3# This file is part of Ansible
4#
5# Ansible is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 3 of the License, or
8# (at your option) any later version.
9#
10# Ansible is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
17#
18
19ANSIBLE_METADATA = {'metadata_version': '1.1',
20                    'status': ['preview'],
21                    'supported_by': 'community'}
22
23DOCUMENTATION = '''
24---
25module: ce_mlag_interface
26version_added: "2.4"
27short_description: Manages MLAG interfaces on HUAWEI CloudEngine switches.
28description:
29    - Manages MLAG interface attributes on HUAWEI CloudEngine switches.
30author:
31    - Li Yanfeng (@QijunPan)
32notes:
33    - This module requires the netconf system service be enabled on the remote device being managed.
34    - Recommended connection is C(netconf).
35    - This module also works with C(local) connections for legacy playbooks.
36options:
37    eth_trunk_id:
38        description:
39            - Name of the local M-LAG interface. The value is ranging from 0 to 511.
40    dfs_group_id:
41        description:
42            - ID of a DFS group.The value is 1.
43        default: present
44    mlag_id:
45        description:
46            - ID of the M-LAG. The value is an integer that ranges from 1 to 2048.
47    mlag_system_id:
48        description:
49            - M-LAG global LACP system MAC address. The value is a string of 0 to 255 characters. The default value
50              is the MAC address of the Ethernet port of MPU.
51    mlag_priority_id:
52        description:
53            - M-LAG global LACP system priority. The value is an integer ranging from 0 to 65535.
54              The default value is 32768.
55    interface:
56        description:
57            - Name of the interface that enters the Error-Down state when the peer-link fails.
58              The value is a string of 1 to 63 characters.
59    mlag_error_down:
60        description:
61            - Configure the interface on the slave device to enter the Error-Down state.
62        choices: ['enable','disable']
63    state:
64        description:
65            - Specify desired state of the resource.
66        default: present
67        choices: ['present','absent']
68
69'''
70
71EXAMPLES = '''
72- name: mlag interface module test
73  hosts: cloudengine
74  connection: local
75  gather_facts: no
76  vars:
77    cli:
78      host: "{{ inventory_hostname }}"
79      port: "{{ ansible_ssh_port }}"
80      username: "{{ username }}"
81      password: "{{ password }}"
82      transport: cli
83
84  tasks:
85
86  - name: Set interface mlag error down
87    ce_mlag_interface:
88      interface: 10GE2/0/1
89      mlag_error_down: enable
90      provider: "{{ cli }}"
91  - name: Create mlag
92    ce_mlag_interface:
93      eth_trunk_id: 1
94      dfs_group_id: 1
95      mlag_id: 4
96      provider: "{{ cli }}"
97  - name: Set mlag global attribute
98    ce_mlag_interface:
99      mlag_system_id: 0020-1409-0407
100      mlag_priority_id: 5
101      provider: "{{ cli }}"
102  - name: Set mlag interface attribute
103    ce_mlag_interface:
104      eth_trunk_id: 1
105      mlag_system_id: 0020-1409-0400
106      mlag_priority_id: 3
107      provider: "{{ cli }}"
108'''
109
110RETURN = '''
111changed:
112    description: check to see if a change was made on the device
113    returned: always
114    type: bool
115    sample: true
116proposed:
117    description: k/v pairs of parameters passed into module
118    returned: always
119    type: dict
120    sample: { "interface": "eth-trunk1",
121              "mlag_error_down": "disable",
122              "state": "present"
123            }
124existing:
125    description: k/v pairs of existing aaa server
126    returned: always
127    type: dict
128    sample: {   "mlagErrorDownInfos": [
129                {
130                    "dfsgroupId": "1",
131                    "portName": "Eth-Trunk1"
132                }
133              ]
134            }
135end_state:
136    description: k/v pairs of aaa params after module execution
137    returned: always
138    type: dict
139    sample: {}
140updates:
141    description: command sent to the device
142    returned: always
143    type: list
144    sample: { "interface eth-trunk1",
145              "undo m-lag unpaired-port suspend"}
146'''
147
148import re
149from xml.etree import ElementTree
150from ansible.module_utils.basic import AnsibleModule
151from ansible.module_utils.network.cloudengine.ce import load_config
152from ansible.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config, ce_argument_spec
153
154CE_NC_GET_MLAG_INFO = """
155<filter type="subtree">
156<mlag xmlns="http://www.huawei.com/netconf/vrp" content-version="1.0" format-version="1.0">
157  <mlagInstances>
158    <mlagInstance>
159    </mlagInstance>
160  </mlagInstances>
161</mlag>
162</filter>
163"""
164
165CE_NC_CREATE_MLAG_INFO = """
166<config>
167<mlag xmlns="http://www.huawei.com/netconf/vrp" content-version="1.0" format-version="1.0">
168  <mlagInstances>
169    <mlagInstance operation="create">
170      <dfsgroupId>%s</dfsgroupId>
171      <mlagId>%s</mlagId>
172      <localMlagPort>%s</localMlagPort>
173    </mlagInstance>
174  </mlagInstances>
175</mlag>
176</config>
177"""
178
179CE_NC_DELETE_MLAG_INFO = """
180<config>
181<mlag xmlns="http://www.huawei.com/netconf/vrp" content-version="1.0" format-version="1.0">
182  <mlagInstances>
183    <mlagInstance operation="delete">
184      <dfsgroupId>%s</dfsgroupId>
185      <mlagId>%s</mlagId>
186      <localMlagPort>%s</localMlagPort>
187    </mlagInstance>
188  </mlagInstances>
189</mlag>
190</config>
191"""
192
193CE_NC_GET_LACP_MLAG_INFO = """
194<filter type="subtree">
195  <ifmtrunk xmlns="http://www.huawei.com/netconf/vrp" content-version="1.0" format-version="1.0">
196    <TrunkIfs>
197      <TrunkIf>
198        <ifName>%s</ifName>
199        <lacpMlagIf>
200          <lacpMlagSysId></lacpMlagSysId>
201          <lacpMlagPriority></lacpMlagPriority>
202        </lacpMlagIf>
203      </TrunkIf>
204    </TrunkIfs>
205  </ifmtrunk>
206</filter>
207"""
208
209CE_NC_SET_LACP_MLAG_INFO_HEAD = """
210<config>
211  <ifmtrunk xmlns="http://www.huawei.com/netconf/vrp" content-version="1.0" format-version="1.0">
212    <TrunkIfs>
213      <TrunkIf>
214        <ifName>%s</ifName>
215        <lacpMlagIf operation="merge">
216"""
217
218CE_NC_SET_LACP_MLAG_INFO_TAIL = """
219        </lacpMlagIf>
220      </TrunkIf>
221    </TrunkIfs>
222  </ifmtrunk>
223</config>
224"""
225
226CE_NC_GET_GLOBAL_LACP_MLAG_INFO = """
227<filter type="subtree">
228  <ifmtrunk xmlns="http://www.huawei.com/netconf/vrp" content-version="1.0" format-version="1.0">
229    <lacpSysInfo>
230      <lacpMlagGlobal>
231        <lacpMlagSysId></lacpMlagSysId>
232        <lacpMlagPriority></lacpMlagPriority>
233      </lacpMlagGlobal>
234    </lacpSysInfo>
235  </ifmtrunk>
236</filter>
237"""
238
239CE_NC_SET_GLOBAL_LACP_MLAG_INFO_HEAD = """
240<config>
241  <ifmtrunk xmlns="http://www.huawei.com/netconf/vrp" content-version="1.0" format-version="1.0">
242    <lacpSysInfo>
243      <lacpMlagGlobal operation="merge">
244"""
245
246CE_NC_SET_GLOBAL_LACP_MLAG_INFO_TAIL = """
247      </lacpMlagGlobal>
248    </lacpSysInfo>
249  </ifmtrunk>
250</config>
251"""
252
253CE_NC_GET_MLAG_ERROR_DOWN_INFO = """
254<filter type="subtree">
255<mlag xmlns="http://www.huawei.com/netconf/vrp" content-version="1.0" format-version="1.0">
256  <errordowns>
257    <errordown>
258      <dfsgroupId></dfsgroupId>
259      <portName></portName>
260      <portState></portState>
261    </errordown>
262  </errordowns>
263</mlag>
264</filter>
265
266"""
267
268CE_NC_CREATE_MLAG_ERROR_DOWN_INFO = """
269<config>
270<mlag xmlns="http://www.huawei.com/netconf/vrp" content-version="1.0" format-version="1.0">
271  <errordowns>
272    <errordown operation="create">
273      <dfsgroupId>1</dfsgroupId>
274      <portName>%s</portName>
275    </errordown>
276  </errordowns>
277</mlag>
278</config>
279"""
280
281CE_NC_DELETE_MLAG_ERROR_DOWN_INFO = """
282<config>
283<mlag xmlns="http://www.huawei.com/netconf/vrp" content-version="1.0" format-version="1.0">
284  <errordowns>
285    <errordown operation="delete">
286      <dfsgroupId>1</dfsgroupId>
287      <portName>%s</portName>
288    </errordown>
289  </errordowns>
290</mlag>
291</config>
292
293"""
294
295
296def get_interface_type(interface):
297    """Gets the type of interface, such as 10GE, ETH-TRUNK, VLANIF..."""
298
299    if interface is None:
300        return None
301
302    iftype = None
303
304    if interface.upper().startswith('GE'):
305        iftype = 'ge'
306    elif interface.upper().startswith('10GE'):
307        iftype = '10ge'
308    elif interface.upper().startswith('25GE'):
309        iftype = '25ge'
310    elif interface.upper().startswith('40GE'):
311        iftype = '40ge'
312    elif interface.upper().startswith('100GE'):
313        iftype = '100ge'
314    elif interface.upper().startswith('ETH-TRUNK'):
315        iftype = 'eth-trunk'
316    elif interface.upper().startswith('NULL'):
317        iftype = 'null'
318    else:
319        return None
320
321    return iftype.lower()
322
323
324class MlagInterface(object):
325    """
326    Manages Manages MLAG interface information.
327    """
328
329    def __init__(self, argument_spec):
330        self.spec = argument_spec
331        self.module = None
332        self.init_module()
333
334        # module input info
335        self.eth_trunk_id = self.module.params['eth_trunk_id']
336        self.dfs_group_id = self.module.params['dfs_group_id']
337        self.mlag_id = self.module.params['mlag_id']
338        self.mlag_system_id = self.module.params['mlag_system_id']
339        self.mlag_priority_id = self.module.params['mlag_priority_id']
340        self.interface = self.module.params['interface']
341        self.mlag_error_down = self.module.params['mlag_error_down']
342        self.state = self.module.params['state']
343
344        # state
345        self.changed = False
346        self.updates_cmd = list()
347        self.results = dict()
348        self.existing = dict()
349        self.proposed = dict()
350        self.end_state = dict()
351
352        # mlag info
353        self.commands = list()
354        self.mlag_info = None
355        self.mlag_global_info = None
356        self.mlag_error_down_info = None
357        self.mlag_trunk_attribute_info = None
358
359    def init_module(self):
360        """ init module """
361
362        self.module = AnsibleModule(
363            argument_spec=self.spec, supports_check_mode=True)
364
365    def check_response(self, xml_str, xml_name):
366        """Check if response message is already succeed."""
367
368        if "<ok/>" not in xml_str:
369            self.module.fail_json(msg='Error: %s failed.' % xml_name)
370
371    def cli_add_command(self, command, undo=False):
372        """add command to self.update_cmd and self.commands"""
373
374        if undo and command.lower() not in ["quit", "return"]:
375            cmd = "undo " + command
376        else:
377            cmd = command
378
379        self.commands.append(cmd)          # set to device
380        if command.lower() not in ["quit", "return"]:
381            self.updates_cmd.append(cmd)   # show updates result
382
383    def cli_load_config(self, commands):
384        """load config by cli"""
385
386        if not self.module.check_mode:
387            load_config(self.module, commands)
388
389    def get_mlag_info(self):
390        """ get mlag info."""
391
392        mlag_info = dict()
393        conf_str = CE_NC_GET_MLAG_INFO
394        xml_str = get_nc_config(self.module, conf_str)
395        if "<data/>" in xml_str:
396            return mlag_info
397        else:
398            xml_str = xml_str.replace('\r', '').replace('\n', '').\
399                replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\
400                replace('xmlns="http://www.huawei.com/netconf/vrp"', "")
401
402            mlag_info["mlagInfos"] = list()
403            root = ElementTree.fromstring(xml_str)
404            dfs_mlag_infos = root.findall(
405                "data/mlag/mlagInstances/mlagInstance")
406
407            if dfs_mlag_infos:
408                for dfs_mlag_info in dfs_mlag_infos:
409                    mlag_dict = dict()
410                    for ele in dfs_mlag_info:
411                        if ele.tag in ["dfsgroupId", "mlagId", "localMlagPort"]:
412                            mlag_dict[ele.tag] = ele.text
413                    mlag_info["mlagInfos"].append(mlag_dict)
414            return mlag_info
415
416    def get_mlag_global_info(self):
417        """ get mlag global info."""
418
419        mlag_global_info = dict()
420        conf_str = CE_NC_GET_GLOBAL_LACP_MLAG_INFO
421        xml_str = get_nc_config(self.module, conf_str)
422        if "<data/>" in xml_str:
423            return mlag_global_info
424        else:
425            xml_str = xml_str.replace('\r', '').replace('\n', '').\
426                replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\
427                replace('xmlns="http://www.huawei.com/netconf/vrp"', "")
428
429            root = ElementTree.fromstring(xml_str)
430            global_info = root.findall(
431                "data/ifmtrunk/lacpSysInfo/lacpMlagGlobal")
432
433            if global_info:
434                for tmp in global_info:
435                    for site in tmp:
436                        if site.tag in ["lacpMlagSysId", "lacpMlagPriority"]:
437                            mlag_global_info[site.tag] = site.text
438            return mlag_global_info
439
440    def get_mlag_trunk_attribute_info(self):
441        """ get mlag global info."""
442
443        mlag_trunk_attribute_info = dict()
444        eth_trunk = "Eth-Trunk"
445        eth_trunk += self.eth_trunk_id
446        conf_str = CE_NC_GET_LACP_MLAG_INFO % eth_trunk
447        xml_str = get_nc_config(self.module, conf_str)
448        if "<data/>" in xml_str:
449            return mlag_trunk_attribute_info
450        else:
451            xml_str = xml_str.replace('\r', '').replace('\n', '').\
452                replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\
453                replace('xmlns="http://www.huawei.com/netconf/vrp"', "")
454
455            root = ElementTree.fromstring(xml_str)
456            global_info = root.findall(
457                "data/ifmtrunk/TrunkIfs/TrunkIf/lacpMlagIf")
458
459            if global_info:
460                for tmp in global_info:
461                    for site in tmp:
462                        if site.tag in ["lacpMlagSysId", "lacpMlagPriority"]:
463                            mlag_trunk_attribute_info[site.tag] = site.text
464            return mlag_trunk_attribute_info
465
466    def get_mlag_error_down_info(self):
467        """ get error down info."""
468
469        mlag_error_down_info = dict()
470        conf_str = CE_NC_GET_MLAG_ERROR_DOWN_INFO
471        xml_str = get_nc_config(self.module, conf_str)
472        if "<data/>" in xml_str:
473            return mlag_error_down_info
474        else:
475            xml_str = xml_str.replace('\r', '').replace('\n', '').\
476                replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\
477                replace('xmlns="http://www.huawei.com/netconf/vrp"', "")
478
479            mlag_error_down_info["mlagErrorDownInfos"] = list()
480            root = ElementTree.fromstring(xml_str)
481            mlag_error_infos = root.findall(
482                "data/mlag/errordowns/errordown")
483
484            if mlag_error_infos:
485                for mlag_error_info in mlag_error_infos:
486                    mlag_error_dict = dict()
487                    for ele in mlag_error_info:
488                        if ele.tag in ["dfsgroupId", "portName"]:
489                            mlag_error_dict[ele.tag] = ele.text
490                    mlag_error_down_info[
491                        "mlagErrorDownInfos"].append(mlag_error_dict)
492            return mlag_error_down_info
493
494    def check_macaddr(self):
495        """check mac-address whether valid"""
496
497        valid_char = '0123456789abcdef-'
498        mac = self.mlag_system_id
499
500        if len(mac) > 16:
501            return False
502
503        mac_list = re.findall(r'([0-9a-fA-F]+)', mac)
504        if len(mac_list) != 3:
505            return False
506
507        if mac.count('-') != 2:
508            return False
509
510        for _, value in enumerate(mac, start=0):
511            if value.lower() not in valid_char:
512                return False
513
514        return True
515
516    def check_params(self):
517        """Check all input params"""
518
519        # eth_trunk_id check
520        if self.eth_trunk_id:
521            if not self.eth_trunk_id.isdigit():
522                self.module.fail_json(
523                    msg='Error: The value of eth_trunk_id is an integer.')
524            if int(self.eth_trunk_id) < 0 or int(self.eth_trunk_id) > 511:
525                self.module.fail_json(
526                    msg='Error: The value of eth_trunk_id is not in the range from 0 to 511.')
527
528        # dfs_group_id check
529        if self.dfs_group_id:
530            if self.dfs_group_id != "1":
531                self.module.fail_json(
532                    msg='Error: The value of dfs_group_id must be 1.')
533
534        # mlag_id check
535        if self.mlag_id:
536            if not self.mlag_id.isdigit():
537                self.module.fail_json(
538                    msg='Error: The value of mlag_id is an integer.')
539            if int(self.mlag_id) < 1 or int(self.mlag_id) > 2048:
540                self.module.fail_json(
541                    msg='Error: The value of mlag_id is not in the range from 1 to 2048.')
542
543        # mlag_system_id check
544        if self.mlag_system_id:
545            if not self.check_macaddr():
546                self.module.fail_json(
547                    msg="Error: mlag_system_id has invalid value %s." % self.mlag_system_id)
548
549        # mlag_priority_id check
550        if self.mlag_priority_id:
551            if not self.mlag_priority_id.isdigit():
552                self.module.fail_json(
553                    msg='Error: The value of mlag_priority_id is an integer.')
554            if int(self.mlag_priority_id) < 0 or int(self.mlag_priority_id) > 254:
555                self.module.fail_json(
556                    msg='Error: The value of mlag_priority_id is not in the range from 0 to 254.')
557
558        # interface check
559        if self.interface:
560            intf_type = get_interface_type(self.interface)
561            if not intf_type:
562                self.module.fail_json(
563                    msg='Error: Interface name of %s '
564                        'is error.' % self.interface)
565
566    def is_mlag_info_change(self):
567        """whether mlag info change"""
568
569        if not self.mlag_info:
570            return True
571
572        eth_trunk = "Eth-Trunk"
573        eth_trunk += self.eth_trunk_id
574        for info in self.mlag_info["mlagInfos"]:
575            if info["mlagId"] == self.mlag_id and info["localMlagPort"] == eth_trunk:
576                return False
577        return True
578
579    def is_mlag_info_exist(self):
580        """whether mlag info exist"""
581
582        if not self.mlag_info:
583            return False
584
585        eth_trunk = "Eth-Trunk"
586        eth_trunk += self.eth_trunk_id
587
588        for info in self.mlag_info["mlagInfos"]:
589            if info["mlagId"] == self.mlag_id and info["localMlagPort"] == eth_trunk:
590                return True
591        return False
592
593    def is_mlag_error_down_info_change(self):
594        """whether mlag error down info change"""
595
596        if not self.mlag_error_down_info:
597            return True
598
599        for info in self.mlag_error_down_info["mlagErrorDownInfos"]:
600            if info["portName"].upper() == self.interface.upper():
601                return False
602        return True
603
604    def is_mlag_error_down_info_exist(self):
605        """whether mlag error down info exist"""
606
607        if not self.mlag_error_down_info:
608            return False
609
610        for info in self.mlag_error_down_info["mlagErrorDownInfos"]:
611            if info["portName"].upper() == self.interface.upper():
612                return True
613        return False
614
615    def is_mlag_interface_info_change(self):
616        """whether mlag interface attribute info change"""
617
618        if not self.mlag_trunk_attribute_info:
619            return True
620
621        if self.mlag_system_id:
622            if self.mlag_trunk_attribute_info["lacpMlagSysId"] != self.mlag_system_id:
623                return True
624        if self.mlag_priority_id:
625            if self.mlag_trunk_attribute_info["lacpMlagPriority"] != self.mlag_priority_id:
626                return True
627        return False
628
629    def is_mlag_interface_info_exist(self):
630        """whether mlag interface attribute info exist"""
631
632        if not self.mlag_trunk_attribute_info:
633            return False
634
635        if self.mlag_system_id:
636            if self.mlag_priority_id:
637                if self.mlag_trunk_attribute_info["lacpMlagSysId"] == self.mlag_system_id \
638                        and self.mlag_trunk_attribute_info["lacpMlagPriority"] == self.mlag_priority_id:
639                    return True
640            else:
641                if self.mlag_trunk_attribute_info["lacpMlagSysId"] == self.mlag_system_id:
642                    return True
643
644        if self.mlag_priority_id:
645            if self.mlag_system_id:
646                if self.mlag_trunk_attribute_info["lacpMlagSysId"] == self.mlag_system_id \
647                        and self.mlag_trunk_attribute_info["lacpMlagPriority"] == self.mlag_priority_id:
648                    return True
649            else:
650                if self.mlag_trunk_attribute_info["lacpMlagPriority"] == self.mlag_priority_id:
651                    return True
652
653        return False
654
655    def is_mlag_global_info_change(self):
656        """whether mlag global attribute info change"""
657
658        if not self.mlag_global_info:
659            return True
660
661        if self.mlag_system_id:
662            if self.mlag_global_info["lacpMlagSysId"] != self.mlag_system_id:
663                return True
664        if self.mlag_priority_id:
665            if self.mlag_global_info["lacpMlagPriority"] != self.mlag_priority_id:
666                return True
667        return False
668
669    def is_mlag_global_info_exist(self):
670        """whether mlag global attribute info exist"""
671
672        if not self.mlag_global_info:
673            return False
674
675        if self.mlag_system_id:
676            if self.mlag_priority_id:
677                if self.mlag_global_info["lacpMlagSysId"] == self.mlag_system_id \
678                        and self.mlag_global_info["lacpMlagPriority"] == self.mlag_priority_id:
679                    return True
680            else:
681                if self.mlag_global_info["lacpMlagSysId"] == self.mlag_system_id:
682                    return True
683
684        if self.mlag_priority_id:
685            if self.mlag_system_id:
686                if self.mlag_global_info["lacpMlagSysId"] == self.mlag_system_id \
687                        and self.mlag_global_info["lacpMlagPriority"] == self.mlag_priority_id:
688                    return True
689            else:
690                if self.mlag_global_info["lacpMlagPriority"] == self.mlag_priority_id:
691                    return True
692
693        return False
694
695    def create_mlag(self):
696        """create mlag info"""
697
698        if self.is_mlag_info_change():
699            mlag_port = "Eth-Trunk"
700            mlag_port += self.eth_trunk_id
701            conf_str = CE_NC_CREATE_MLAG_INFO % (
702                self.dfs_group_id, self.mlag_id, mlag_port)
703            recv_xml = set_nc_config(self.module, conf_str)
704            if "<ok/>" not in recv_xml:
705                self.module.fail_json(
706                    msg='Error: create mlag info failed.')
707
708            self.updates_cmd.append("interface %s" % mlag_port)
709            self.updates_cmd.append("dfs-group %s m-lag %s" %
710                                    (self.dfs_group_id, self.mlag_id))
711            self.changed = True
712
713    def delete_mlag(self):
714        """delete mlag info"""
715
716        if self.is_mlag_info_exist():
717            mlag_port = "Eth-Trunk"
718            mlag_port += self.eth_trunk_id
719            conf_str = CE_NC_DELETE_MLAG_INFO % (
720                self.dfs_group_id, self.mlag_id, mlag_port)
721            recv_xml = set_nc_config(self.module, conf_str)
722            if "<ok/>" not in recv_xml:
723                self.module.fail_json(
724                    msg='Error: delete mlag info failed.')
725
726            self.updates_cmd.append("interface %s" % mlag_port)
727            self.updates_cmd.append(
728                "undo dfs-group %s m-lag %s" % (self.dfs_group_id, self.mlag_id))
729            self.changed = True
730
731    def create_mlag_error_down(self):
732        """create mlag error down info"""
733
734        if self.is_mlag_error_down_info_change():
735            conf_str = CE_NC_CREATE_MLAG_ERROR_DOWN_INFO % self.interface
736            recv_xml = set_nc_config(self.module, conf_str)
737            if "<ok/>" not in recv_xml:
738                self.module.fail_json(
739                    msg='Error: create mlag error down info failed.')
740
741            self.updates_cmd.append("interface %s" % self.interface)
742            self.updates_cmd.append("m-lag unpaired-port suspend")
743            self.changed = True
744
745    def delete_mlag_error_down(self):
746        """delete mlag error down info"""
747
748        if self.is_mlag_error_down_info_exist():
749
750            conf_str = CE_NC_DELETE_MLAG_ERROR_DOWN_INFO % self.interface
751            recv_xml = set_nc_config(self.module, conf_str)
752            if "<ok/>" not in recv_xml:
753                self.module.fail_json(
754                    msg='Error: delete mlag error down info failed.')
755
756            self.updates_cmd.append("interface %s" % self.interface)
757            self.updates_cmd.append("undo m-lag unpaired-port suspend")
758            self.changed = True
759
760    def set_mlag_interface(self):
761        """set mlag interface attribute info"""
762
763        if self.is_mlag_interface_info_change():
764            mlag_port = "Eth-Trunk"
765            mlag_port += self.eth_trunk_id
766            conf_str = CE_NC_SET_LACP_MLAG_INFO_HEAD % mlag_port
767            if self.mlag_priority_id:
768                conf_str += "<lacpMlagPriority>%s</lacpMlagPriority>" % self.mlag_priority_id
769            if self.mlag_system_id:
770                conf_str += "<lacpMlagSysId>%s</lacpMlagSysId>" % self.mlag_system_id
771            conf_str += CE_NC_SET_LACP_MLAG_INFO_TAIL
772            recv_xml = set_nc_config(self.module, conf_str)
773            if "<ok/>" not in recv_xml:
774                self.module.fail_json(
775                    msg='Error: set mlag interface attribute info failed.')
776
777            self.updates_cmd.append("interface %s" % mlag_port)
778            if self.mlag_priority_id:
779                self.updates_cmd.append(
780                    "lacp m-lag priority %s" % self.mlag_priority_id)
781
782            if self.mlag_system_id:
783                self.updates_cmd.append(
784                    "lacp m-lag system-id %s" % self.mlag_system_id)
785            self.changed = True
786
787    def delete_mlag_interface(self):
788        """delete mlag interface attribute info"""
789
790        if self.is_mlag_interface_info_exist():
791            mlag_port = "Eth-Trunk"
792            mlag_port += self.eth_trunk_id
793
794            cmd = "interface %s" % mlag_port
795            self.cli_add_command(cmd)
796
797            if self.mlag_priority_id:
798                cmd = "lacp m-lag priority %s" % self.mlag_priority_id
799                self.cli_add_command(cmd, True)
800
801            if self.mlag_system_id:
802                cmd = "lacp m-lag system-id %s" % self.mlag_system_id
803                self.cli_add_command(cmd, True)
804
805            if self.commands:
806                self.cli_load_config(self.commands)
807                self.changed = True
808
809    def set_mlag_global(self):
810        """set mlag global attribute info"""
811
812        if self.is_mlag_global_info_change():
813            conf_str = CE_NC_SET_GLOBAL_LACP_MLAG_INFO_HEAD
814            if self.mlag_priority_id:
815                conf_str += "<lacpMlagPriority>%s</lacpMlagPriority>" % self.mlag_priority_id
816            if self.mlag_system_id:
817                conf_str += "<lacpMlagSysId>%s</lacpMlagSysId>" % self.mlag_system_id
818            conf_str += CE_NC_SET_GLOBAL_LACP_MLAG_INFO_TAIL
819            recv_xml = set_nc_config(self.module, conf_str)
820            if "<ok/>" not in recv_xml:
821                self.module.fail_json(
822                    msg='Error: set mlag interface attribute info failed.')
823
824            if self.mlag_priority_id:
825                self.updates_cmd.append(
826                    "lacp m-lag priority %s" % self.mlag_priority_id)
827
828            if self.mlag_system_id:
829                self.updates_cmd.append(
830                    "lacp m-lag system-id %s" % self.mlag_system_id)
831            self.changed = True
832
833    def delete_mlag_global(self):
834        """delete mlag global attribute info"""
835
836        if self.is_mlag_global_info_exist():
837            if self.mlag_priority_id:
838                cmd = "lacp m-lag priority %s" % self.mlag_priority_id
839                self.cli_add_command(cmd, True)
840
841            if self.mlag_system_id:
842                cmd = "lacp m-lag system-id %s" % self.mlag_system_id
843                self.cli_add_command(cmd, True)
844
845            if self.commands:
846                self.cli_load_config(self.commands)
847                self.changed = True
848
849    def get_proposed(self):
850        """get proposed info"""
851
852        if self.eth_trunk_id:
853            self.proposed["eth_trunk_id"] = self.eth_trunk_id
854        if self.dfs_group_id:
855            self.proposed["dfs_group_id"] = self.dfs_group_id
856        if self.mlag_id:
857            self.proposed["mlag_id"] = self.mlag_id
858        if self.mlag_system_id:
859            self.proposed["mlag_system_id"] = self.mlag_system_id
860        if self.mlag_priority_id:
861            self.proposed["mlag_priority_id"] = self.mlag_priority_id
862        if self.interface:
863            self.proposed["interface"] = self.interface
864        if self.mlag_error_down:
865            self.proposed["mlag_error_down"] = self.mlag_error_down
866        if self.state:
867            self.proposed["state"] = self.state
868
869    def get_existing(self):
870        """get existing info"""
871
872        self.mlag_info = self.get_mlag_info()
873        self.mlag_global_info = self.get_mlag_global_info()
874        self.mlag_error_down_info = self.get_mlag_error_down_info()
875
876        if self.eth_trunk_id or self.dfs_group_id or self.mlag_id:
877            if not self.mlag_system_id and not self.mlag_priority_id:
878                if self.mlag_info:
879                    self.existing["mlagInfos"] = self.mlag_info["mlagInfos"]
880
881        if self.mlag_system_id or self.mlag_priority_id:
882            if self.eth_trunk_id:
883                if self.mlag_trunk_attribute_info:
884                    if self.mlag_system_id:
885                        self.end_state["lacpMlagSysId"] = self.mlag_trunk_attribute_info[
886                            "lacpMlagSysId"]
887                    if self.mlag_priority_id:
888                        self.end_state["lacpMlagPriority"] = self.mlag_trunk_attribute_info[
889                            "lacpMlagPriority"]
890            else:
891                if self.mlag_global_info:
892                    if self.mlag_system_id:
893                        self.end_state["lacpMlagSysId"] = self.mlag_global_info[
894                            "lacpMlagSysId"]
895                    if self.mlag_priority_id:
896                        self.end_state["lacpMlagPriority"] = self.mlag_global_info[
897                            "lacpMlagPriority"]
898
899        if self.interface or self.mlag_error_down:
900            if self.mlag_error_down_info:
901                self.existing["mlagErrorDownInfos"] = self.mlag_error_down_info[
902                    "mlagErrorDownInfos"]
903
904    def get_end_state(self):
905        """get end state info"""
906
907        if self.eth_trunk_id or self.dfs_group_id or self.mlag_id:
908            self.mlag_info = self.get_mlag_info()
909            if not self.mlag_system_id and not self.mlag_priority_id:
910                if self.mlag_info:
911                    self.end_state["mlagInfos"] = self.mlag_info["mlagInfos"]
912
913        if self.mlag_system_id or self.mlag_priority_id:
914            if self.eth_trunk_id:
915                self.mlag_trunk_attribute_info = self.get_mlag_trunk_attribute_info()
916                if self.mlag_trunk_attribute_info:
917                    if self.mlag_system_id:
918                        self.end_state["lacpMlagSysId"] = self.mlag_trunk_attribute_info[
919                            "lacpMlagSysId"]
920                    if self.mlag_priority_id:
921                        self.end_state["lacpMlagPriority"] = self.mlag_trunk_attribute_info[
922                            "lacpMlagPriority"]
923            else:
924                self.mlag_global_info = self.get_mlag_global_info()
925                if self.mlag_global_info:
926                    if self.mlag_system_id:
927                        self.end_state["lacpMlagSysId"] = self.mlag_global_info[
928                            "lacpMlagSysId"]
929                    if self.mlag_priority_id:
930                        self.end_state["lacpMlagPriority"] = self.mlag_global_info[
931                            "lacpMlagPriority"]
932
933        if self.interface or self.mlag_error_down:
934            self.mlag_error_down_info = self.get_mlag_error_down_info()
935            if self.mlag_error_down_info:
936                self.end_state["mlagErrorDownInfos"] = self.mlag_error_down_info[
937                    "mlagErrorDownInfos"]
938
939    def work(self):
940        """worker"""
941
942        self.check_params()
943        self.get_proposed()
944        self.get_existing()
945
946        if self.eth_trunk_id or self.dfs_group_id or self.mlag_id:
947            self.mlag_info = self.get_mlag_info()
948            if self.eth_trunk_id and self.dfs_group_id and self.mlag_id:
949                if self.state == "present":
950                    self.create_mlag()
951                else:
952                    self.delete_mlag()
953            else:
954                if not self.mlag_system_id and not self.mlag_priority_id:
955                    self.module.fail_json(
956                        msg='Error: eth_trunk_id, dfs_group_id, mlag_id must be config at the same time.')
957
958        if self.mlag_system_id or self.mlag_priority_id:
959
960            if self.eth_trunk_id:
961                self.mlag_trunk_attribute_info = self.get_mlag_trunk_attribute_info()
962                if self.mlag_system_id or self.mlag_priority_id:
963                    if self.state == "present":
964                        self.set_mlag_interface()
965                    else:
966                        self.delete_mlag_interface()
967            else:
968                self.mlag_global_info = self.get_mlag_global_info()
969                if self.mlag_system_id or self.mlag_priority_id:
970                    if self.state == "present":
971                        self.set_mlag_global()
972                    else:
973                        self.delete_mlag_global()
974
975        if self.interface or self.mlag_error_down:
976            self.mlag_error_down_info = self.get_mlag_error_down_info()
977            if self.interface and self.mlag_error_down:
978                if self.mlag_error_down == "enable":
979                    self.create_mlag_error_down()
980                else:
981                    self.delete_mlag_error_down()
982            else:
983                self.module.fail_json(
984                    msg='Error: interface, mlag_error_down must be config at the same time.')
985
986        self.get_end_state()
987        self.results['changed'] = self.changed
988        self.results['proposed'] = self.proposed
989        self.results['existing'] = self.existing
990        self.results['end_state'] = self.end_state
991        if self.changed:
992            self.results['updates'] = self.updates_cmd
993        else:
994            self.results['updates'] = list()
995
996        self.module.exit_json(**self.results)
997
998
999def main():
1000    """ Module main """
1001
1002    argument_spec = dict(
1003        eth_trunk_id=dict(type='str'),
1004        dfs_group_id=dict(type='str'),
1005        mlag_id=dict(type='str'),
1006        mlag_system_id=dict(type='str'),
1007        mlag_priority_id=dict(type='str'),
1008        interface=dict(type='str'),
1009        mlag_error_down=dict(type='str', choices=['enable', 'disable']),
1010        state=dict(type='str', default='present',
1011                   choices=['present', 'absent'])
1012    )
1013    argument_spec.update(ce_argument_spec)
1014    module = MlagInterface(argument_spec=argument_spec)
1015    module.work()
1016
1017
1018if __name__ == '__main__':
1019    main()
1020