Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add a new module to create isis instance. #63716

Merged
merged 15 commits into from Nov 26, 2019
331 changes: 331 additions & 0 deletions lib/ansible/modules/network/cloudengine/ce_is_is_instance.py
@@ -0,0 +1,331 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright 2019 Red Hat
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#

from __future__ import absolute_import, division, print_function
__metaclass__ = type

ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}

DOCUMENTATION = r'''
---
module: ce_is_is_instance
version_added: "2.10"
author: xuxiaowei0512 (@CloudEngine-Ansible)
short_description: Manages isis process id configuration on HUAWEI CloudEngine devices.
description:
- Manages isis process id, creates a isis instance id or deletes a process id on HUAWEI CloudEngine devices.
notes:
- This module requires the netconf system service be enabled on the remote device being managed.
- This module works with connection C(netconf).
options:
instance_id:
description:
- Specifies the id of a isis process.The value is a number of 1 to 4294967295.
required: true
type: int
vpn_name:
description:
- VPN Instance, associate the VPN instance with the corresponding IS-IS process.
type: str
state:
description:
- Determines whether the config should be present or not on the device.
default: present
type: str
choices: ['present', 'absent']
'''

EXAMPLES = r'''
- name: Set isis process
ce_is_is_instance:
instance_id: 3
state: present

- name: Unset isis process
ce_is_is_instance:
instance_id: 3
state: absent

- name: check isis process
ce_is_is_instance:
instance_id: 4294967296
state: present

- name: Set vpn name
ce_is_is_instance:
instance_id: 22
vpn_name: vpn1
state: present

- name: check vpn name
ce_is_is_instance:
instance_id: 22
vpn_name: vpn1234567896321452212221556asdasdasdasdsadvdv
state: present
'''

RETURN = r'''
proposed:
description: k/v pairs of parameters passed into module
returned: always
type: dict
sample: {
"instance_id": 1,
"vpn_name": null
}
existing:
description: k/v pairs of existing configuration
returned: always
type: dict
sample: {
"session": {}
}
end_state:
description: k/v pairs of configuration after module execution
returned: always
type: dict
sample: {
"session": {
"instance_id": 1,
"vpn_name": null
}
}
updates:
description: commands sent to the device
returned: always
type: list
sample: [
"isis 1"
]
changed:
description: check to see if a change was made on the device
returned: always
type: bool
sample: true
'''

from xml.etree import ElementTree
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.cloudengine.ce import get_nc_config, set_nc_config

CE_NC_GET_ISIS = """
<filter type="subtree">
<isiscomm xmlns="http://www.huawei.com/netconf/vrp" content-version="1.0" format-version="1.0">
%s
</isiscomm>
</filter>
"""

CE_NC_GET_ISIS_INSTANCE = """
<isSites>
<isSite>
<instanceId>%s</instanceId>
<vpnName></vpnName>
</isSite>
</isSites>
"""


def is_valid_ip_vpn(vpname):
"""check ip vpn"""

if not vpname:
return False

if vpname == "_public_":
return False

if len(vpname) < 1 or len(vpname) > 31:
return False

return True


class ISIS_Instance(object):
"""Manages ISIS Instance"""

def __init__(self, argument_spec):
self.spec = argument_spec
self.module = None
self.__init_module__()

# module input info
self.instance_id = self.module.params['instance_id']
self.vpn_name = self.module.params['vpn_name']
self.state = self.module.params['state']

# state
self.changed = False
self.isis_dict = dict()
self.updates_cmd = list()
self.commands = list()
self.results = dict()
self.proposed = dict()
self.existing = dict()
self.end_state = dict()

def __init_module__(self):
"""init module"""
self.module = AnsibleModule(
argument_spec=self.spec, supports_check_mode=True)

def get_isis_dict(self):
"""isis config dict"""
isis_dict = dict()
isis_dict["instance"] = dict()
conf_str = CE_NC_GET_ISIS % (
(CE_NC_GET_ISIS_INSTANCE % self.instance_id))

xml_str = get_nc_config(self.module, conf_str)
if "<data/>" in xml_str:
return isis_dict

xml_str = xml_str.replace('\r', '').replace('\n', '').\
replace('xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"', "").\
replace('xmlns="http://www.huawei.com/netconf/vrp"', "")
root = ElementTree.fromstring(xml_str)

# get isis info
glb = root.find("isiscomm/isSites/isSite")
if glb:
for attr in glb:
isis_dict["instance"][attr.tag] = attr.text

return isis_dict

def config_session(self):
"""configures isis"""
xml_str = ""
instance = self.isis_dict["instance"]
if not self.instance_id:
return xml_str

if self.state == "present":
xml_str = "<instanceId>%s</instanceId>" % self.instance_id
self.updates_cmd.append("isis %s" % self.instance_id)

if self.vpn_name:
xml_str += "<vpnName>%s</vpnName>" % self.vpn_name
self.updates_cmd.append("vpn-instance %s" % self.vpn_name)
else:
# absent
if self.instance_id and str(self.instance_id) == instance.get("instanceId"):
xml_str = "<instanceId>%s</instanceId>" % self.instance_id
self.updates_cmd.append("undo isis %s" % self.instance_id)

if self.state == "present":
return '<isSites><isSite operation="merge">' + xml_str + '</isSite></isSites>'
else:
if xml_str:
return '<isSites><isSite operation="delete">' + xml_str + '</isSite></isSites>'

def netconf_load_config(self, xml_str):
"""load isis config by netconf"""

if not xml_str:
return

xml_cfg = """
<config>
<isiscomm xmlns="http://www.huawei.com/netconf/vrp" content-version="1.0" format-version="1.0">
%s
</isiscomm>
</config>""" % xml_str
set_nc_config(self.module, xml_cfg)
self.changed = True

def check_params(self):
"""Check all input params"""

# check instance id
if not self.instance_id:
self.module.fail_json(msg="Error: Missing required arguments: instance_id.")

if self.instance_id:
if self.instance_id < 1 or self.instance_id > 4294967295:
self.module.fail_json(msg="Error: Instance id is not ranges from 1 to 4294967295.")

# check vpn_name
if self.vpn_name:
if not is_valid_ip_vpn(self.vpn_name):
self.module.fail_json(msg="Error: Session vpn_name is invalid.")

def get_proposed(self):
"""get proposed info"""
# base config
self.proposed["instance_id"] = self.instance_id
self.proposed["vpn_name"] = self.vpn_name
self.proposed["state"] = self.state

def get_existing(self):
"""get existing info"""

if not self.isis_dict:
self.existing["instance"] = None

self.existing["instance"] = self.isis_dict.get("instance")

def get_end_state(self):
"""get end state info"""

isis_dict = self.get_isis_dict()
if not isis_dict:
self.end_state["instance"] = None

self.end_state["instance"] = isis_dict.get("instance")

if self.end_state == self.existing:
self.changed = False

def work(self):
"""worker"""
self.check_params()
self.isis_dict = self.get_isis_dict()
self.get_existing()
self.get_proposed()

# deal present or absent
xml_str = ''
if self.instance_id:
cfg_str = self.config_session()
if cfg_str:
xml_str += cfg_str

# update to device
if xml_str:
self.netconf_load_config(xml_str)
self.changed = True

self.get_end_state()
self.results['changed'] = self.changed
self.results['proposed'] = self.proposed
self.results['existing'] = self.existing
self.results['end_state'] = self.end_state
if self.changed:
self.results['updates'] = self.updates_cmd
else:
self.results['updates'] = list()

self.module.exit_json(**self.results)


def main():
"""Module main"""
argument_spec = dict(
instance_id=dict(required=True, type='int'),
vpn_name=dict(required=False, type='str'),
state=dict(required=False, default='present', choices=['present', 'absent'])
)

module = ISIS_Instance(argument_spec)
module.work()


if __name__ == '__main__':
main()
3 changes: 3 additions & 0 deletions test/integration/targets/ce_is_is_instance/defaults/main.yaml
@@ -0,0 +1,3 @@
---
testcase: "[^_].*"
test_items: []
1 change: 1 addition & 0 deletions test/integration/targets/ce_is_is_instance/meta/main.yml
@@ -0,0 +1 @@

2 changes: 2 additions & 0 deletions test/integration/targets/ce_is_is_instance/tasks/main.yaml
@@ -0,0 +1,2 @@
---
- { include: netconf.yaml, tags: ['netconf'] }
17 changes: 17 additions & 0 deletions test/integration/targets/ce_is_is_instance/tasks/netconf.yaml
@@ -0,0 +1,17 @@
---
- name: collect all netconf test cases
find:
paths: "{{ role_path }}/tests/netconf"
patterns: "{{ testcase }}.yaml"
use_regex: true
connection: local
register: test_cases

- 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