diff --git a/lib/ansible/module_utils/network/cnos/cnos.py b/lib/ansible/module_utils/network/cnos/cnos.py index d6ecf0882b..80e22b7c2f 100644 --- a/lib/ansible/module_utils/network/cnos/cnos.py +++ b/lib/ansible/module_utils/network/cnos/cnos.py @@ -162,6 +162,32 @@ def run_commands(module, commands, check_rc=True): return responses +def run_cnos_commands(module, commands, check_rc=True): + retVal = '' + enter_config = {'command': 'configure terminal', 'prompt': None, 'answer': None} + exit_config = {'command': 'end', 'prompt': None, 'answer': None} + commands.insert(0, enter_config) + commands.append(exit_config) + for cmd in commands: + retVal = retVal + '>> ' + cmd['command'] + '\n' + try: + responses = run_commands(module, commands, check_rc) + for response in responses: + retVal = retVal + '<< ' + response + '\n' + except Exception as e: + errMsg = '' + if hasattr(e, 'message'): + errMsg = e.message + else: + errMsg = str(e) + # Exception in Exceptions + if 'VLAN_ACCESS_MAP' in errMsg: + return retVal + '<<' + errMsg + '\n' + # Add more here if required + retVal = retVal + '<< ' + 'Error-101 ' + errMsg + '\n' + return str(retVal) + + def load_config(module, config): try: conn = get_connection(module) @@ -2187,29 +2213,29 @@ def bgpConfig( # EOM -def vlanConfig( - obj, deviceType, prompt, timeout, vlanArg1, vlanArg2, vlanArg3, - vlanArg4, vlanArg5): +def vlanConfig(module, prompt, answer): - retVal = "" + retVal = '' # Wait time to get response from server - timeout = timeout + vlanArg1 = module.params['vlanArg1'] + vlanArg2 = module.params['vlanArg2'] + vlanArg3 = module.params['vlanArg3'] + vlanArg4 = module.params['vlanArg4'] + vlanArg5 = module.params['vlanArg5'] + deviceType = module.params['deviceType'] # vlan config command happens here. - command = "vlan " + command = 'vlan ' if(vlanArg1 == "access-map"): # debugOutput("access-map ") - command = command + vlanArg1 + " " + command = command + vlanArg1 + ' ' value = checkSanityofVariable( deviceType, "vlan_access_map_name", vlanArg2) if(value == "ok"): - command = command + vlanArg2 + " \n" + command = command + vlanArg2 # debugOutput(command) - retVal = waitForDeviceResponse( - command, "(config-access-map)#", timeout, obj) - retVal = retVal + vlanAccessMapConfig( - obj, deviceType, "(config-access-map)#", timeout, vlanArg3, - vlanArg4, vlanArg5) + cmd = [{'command': command, 'prompt': None, 'answer': None}] + retVal = retVal + vlanAccessMapConfig(module, cmd) return retVal else: retVal = "Error-130" @@ -2244,7 +2270,7 @@ def vlanConfig( if(value == "ok"): command = command + vlanArg3 else: - retVal = "ERROR-133" + retVal = "Error-133" return retVal else: retVal = "Error-132" @@ -2253,37 +2279,34 @@ def vlanConfig( else: value = checkSanityofVariable(deviceType, "vlan_id", vlanArg1) if(value == "ok"): - retVal = createVlan(obj, deviceType, "(config-vlan)#", - timeout, vlanArg1, vlanArg2, vlanArg3, - vlanArg4, vlanArg5) + retVal = createVlan(module, '(config-vlan)#', None) return retVal else: value = checkSanityofVariable( deviceType, "vlan_id_range", vlanArg1) if(value == "ok"): - retVal = createVlan(obj, deviceType, "(config-vlan)#", - timeout, vlanArg1, vlanArg2, vlanArg3, - vlanArg4, vlanArg5) + retVal = createVlan(module, '(config-vlan)#', None) return retVal retVal = "Error-133" return retVal # debugOutput(command) - command = command + "\n" - # debugOutput(command) - retVal = retVal + waitForDeviceResponse(command, prompt, timeout, obj) + cmd = [{'command': command, 'prompt': None, 'answer': None}] + retVal = retVal + str(run_cnos_commands(module, cmd)) return retVal # EOM -def vlanAccessMapConfig( - obj, deviceType, prompt, timeout, vlanArg3, vlanArg4, vlanArg5): - retVal = "" +def vlanAccessMapConfig(module, cmd): + retVal = '' # Wait time to get response from server - timeout = timeout - command = "" + command = '' + vlanArg3 = module.params['vlanArg3'] + vlanArg4 = module.params['vlanArg4'] + vlanArg5 = module.params['vlanArg5'] + deviceType = module.params['deviceType'] if(vlanArg3 == "action"): - command = command + vlanArg3 + " " + command = command + vlanArg3 + ' ' value = checkSanityofVariable( deviceType, "vlan_accessmap_action", vlanArg4) if(value == "ok"): @@ -2292,9 +2315,9 @@ def vlanAccessMapConfig( retVal = "Error-135" return retVal elif(vlanArg3 == "match"): - command = command + vlanArg3 + " " + command = command + vlanArg3 + ' ' if(vlanArg4 == "ip" or vlanArg4 == "mac"): - command = command + vlanArg4 + " address " + command = command + vlanArg4 + ' address ' value = checkSanityofVariable( deviceType, "vlan_access_map_name", vlanArg5) if(value == "ok"): @@ -2311,18 +2334,24 @@ def vlanAccessMapConfig( retVal = "Error-138" return retVal - command = command + "\n" + inner_cmd = [{'command': command, 'prompt': None, 'answer': None}] + cmd.extend(inner_cmd) + retVal = retVal + str(run_cnos_commands(module, cmd)) # debugOutput(command) - retVal = retVal + waitForDeviceResponse(command, prompt, timeout, obj) return retVal # EOM -def checkVlanNameNotAssigned( - obj, deviceType, prompt, timeout, vlanId, vlanName): +def checkVlanNameNotAssigned(module, prompt, answer): retVal = "ok" - command = "display vlan id " + vlanId + " \n" - retVal = waitForDeviceResponse(command, prompt, timeout, obj) + vlanId = module.params['vlanArg1'] + vlanName = module.params['vlanArg3'] + command = "show vlan id " + vlanId + cmd = [{'command': command, 'prompt': None, 'answer': None}] + retVal = str(run_cnos_commands(module, cmd)) + if(retVal.find('Error') != -1): + command = "display vlan id " + vlanId + retVal = str(run_cnos_commands(module, cmd)) if(retVal.find(vlanName) != -1): return "Nok" else: @@ -2331,25 +2360,30 @@ def checkVlanNameNotAssigned( # Utility Method to create vlan -def createVlan( - obj, deviceType, prompt, timeout, vlanArg1, vlanArg2, vlanArg3, - vlanArg4, vlanArg5): +def createVlan(module, prompt, answer): # vlan config command happens here. It creates if not present - command = "vlan " + vlanArg1 + "\n" + vlanArg1 = module.params['vlanArg1'] + vlanArg2 = module.params['vlanArg2'] + vlanArg3 = module.params['vlanArg3'] + vlanArg4 = module.params['vlanArg4'] + vlanArg5 = module.params['vlanArg5'] + deviceType = module.params['deviceType'] + retVal = '' + command = 'vlan ' + vlanArg1 # debugOutput(command) - retVal = waitForDeviceResponse(command, prompt, timeout, obj) + cmd = [{'command': command, 'prompt': None, 'answer': None}] command = "" if(vlanArg2 == "name"): # debugOutput("name") command = vlanArg2 + " " value = checkSanityofVariable(deviceType, "vlan_name", vlanArg3) if(value == "ok"): - value = checkVlanNameNotAssigned(obj, deviceType, prompt, timeout, - vlanArg1, vlanArg3) + value = checkVlanNameNotAssigned(module, prompt, answer) if(value == "ok"): command = command + vlanArg3 else: + retVal = retVal + 'VLAN Name is already assigned \n' command = "\n" else: retVal = "Error-139" @@ -2470,13 +2504,6 @@ def createVlan( retVal = "Error-149" return retVal elif (vlanArg3 == "static-group"): - # debugOutput("static-group") - # command = command + vlanArg3 + " " - # value = checkSanityofVariable(deviceType, variableId, vlanArg4) - # if(value == "ok"): - # command = command + vlanArg4 - - # else : retVal = "Error-102" return retVal elif (vlanArg3 == "version"): @@ -2519,15 +2546,10 @@ def createVlan( else: retVal = "Error-154" return retVal - command = command + "\n" + inner_cmd = [{'command': command, 'prompt': None, 'answer': None}] + cmd.extend(inner_cmd) + retVal = retVal + str(run_cnos_commands(module, cmd)) # debugOutput(command) - retVal = retVal + "\n" + \ - waitForDeviceResponse(command, prompt, timeout, obj) - # Come back to config mode - command = "exit \n" - # debugOutput(command) - retVal = retVal + waitForDeviceResponse(command, "(config)#", timeout, obj) - return retVal # EOM @@ -2771,7 +2793,7 @@ def doSecureStartupConfigBackUp( username + "@" + server + "/" + confPath + " vrf management\n" # debugOutput(command) response = waitForDeviceResponse(command, "(yes/no)", 3, obj) - if(response.lower().find("error-101")): + if(response.lower().find('error-101')): command = password + "\n" retVal = retVal + waitForDeviceResponse(command, "#", timeout, obj) return retVal @@ -2874,7 +2896,7 @@ def doSecureStartUpConfigRollback( # debugOutput(command) response = waitForDeviceResponse(command, "(yes/no)", 3, obj) - if(response.lower().find("error-101")): + if(response.lower().find('error-101')): command = password + "\n" retVal = retVal + waitForDeviceResponse(command, "[n]", timeout, obj) command = "y\n" @@ -2972,7 +2994,7 @@ def doSecureRunningConfigBackUp( username + "@" + server + "/" + confPath + " vrf management\n" # debugOutput(command) response = waitForDeviceResponse(command, "(yes/no)", 3, obj) - if(response.lower().find("error-101")): + if(response.lower().find('error-101')): command = password + "\n" retVal = retVal + waitForDeviceResponse(command, "#", timeout, obj) return retVal @@ -3071,7 +3093,7 @@ def doSecureRunningConfigRollback( # debugOutput(command) response = waitForDeviceResponse(command, "(yes/no)", 3, obj) - if(response.lower().find("error-101")): + if(response.lower().find('error-101')): command = password + "\n" retVal = retVal + waitForDeviceResponse(command, "#", timeout, obj) return retVal @@ -3135,7 +3157,7 @@ def doImageTransfer( return "Error-110" # debugOutput(command) response = waitForDeviceResponse(command, "[n]", 3, obj) - if(response.lower().find("error-101")): + if(response.lower().find('error-101')): retVal = retVal else: retVal = retVal + response @@ -3185,7 +3207,7 @@ def doSecureImageTransfer( server + "/" + imgPath + " system-image " + type + " vrf management \n" # debugOutput(command) response = waitForDeviceResponse(command, "[n]", 3, obj) - if(response.lower().find("error-101")): + if(response.lower().find('error-101')): retVal = retVal else: retVal = retVal + response @@ -3194,7 +3216,7 @@ def doSecureImageTransfer( command = "y\n" # debugOutput(command) response = waitForDeviceResponse(command, "(yes/no)?", 3, obj) - if(response.lower().find("error-101")): + if(response.lower().find('error-101')): retVal = retVal else: retVal = retVal + response @@ -3208,7 +3230,7 @@ def doSecureImageTransfer( command = "y\n" # debugOutput(command) response = waitForDeviceResponse(command, "(yes/no)?", 3, obj) - if(response.lower().find("error-101")): + if(response.lower().find('error-101')): retVal = retVal else: retVal = retVal + response @@ -3320,19 +3342,19 @@ def waitForDeviceResponse(command, prompt, timeout, obj): def checkOutputForError(output): retVal = "" - index = output.lower().find("error") + index = output.lower().find('error') startIndex = index + 6 if(index == -1): - index = output.lower().find("invalid") + index = output.lower().find('invalid') startIndex = index + 8 if(index == -1): - index = output.lower().find("cannot be enabled in l2 interface") + index = output.lower().find('cannot be enabled in l2 interface') startIndex = index + 34 if(index == -1): - index = output.lower().find("incorrect") + index = output.lower().find('incorrect') startIndex = index + 10 if(index == -1): - index = output.lower().find("failure") + index = output.lower().find('failure') startIndex = index + 8 if(index == -1): return None diff --git a/lib/ansible/modules/network/cnos/cnos_vlan.py b/lib/ansible/modules/network/cnos/cnos_vlan.py index 53f6596fbd..31b7bde93d 100644 --- a/lib/ansible/modules/network/cnos/cnos_vlan.py +++ b/lib/ansible/modules/network/cnos/cnos_vlan.py @@ -43,13 +43,12 @@ description: filter. After passing this level, there are five VLAN arguments that will perform further configurations. They are vlanArg1, vlanArg2, vlanArg3, vlanArg4, and vlanArg5. The value of vlanArg1 will determine the way - following arguments will be evaluated. For more details on how to use these - arguments, see [Overloaded Variables]. - This module uses SSH to manage network device configuration. - The results of the operation will be placed in a directory named 'results' - that must be created by the user in their local directory to where the playbook is run. - For more information about this module from Lenovo and customizing it usage for your - use cases, please visit U(http://systemx.lenovofiles.com/help/index.jsp?topic=%2Fcom.lenovo.switchmgt.ansible.doc%2Fcnos_vlan.html) + following arguments will be evaluated. This module uses SSH to manage network + device configuration. The results of the operation will be placed in a directory + named 'results' that must be created by the user in their local directory to + where the playbook is run. For more information about this module from Lenovo and + customizing it usage for your use cases, + please visit U(http://systemx.lenovofiles.com/help/index.jsp?topic=%2Fcom.lenovo.switchmgt.ansible.doc%2Fcnos_vlan.html) version_added: "2.3" extends_documentation_fragment: cnos options: @@ -182,24 +181,17 @@ msg: ''' import sys -try: - import paramiko - HAS_PARAMIKO = True -except ImportError: - HAS_PARAMIKO = False import time import socket import array import json import time import re - try: from ansible.module_utils.network.cnos import cnos HAS_LIB = True except: HAS_LIB = False - from ansible.module_utils.basic import AnsibleModule from collections import defaultdict @@ -223,58 +215,14 @@ def main(): vlanArg5=dict(required=False),), supports_check_mode=False) - username = module.params['username'] - password = module.params['password'] - enablePassword = module.params['enablePassword'] - vlanArg1 = module.params['vlanArg1'] - vlanArg2 = module.params['vlanArg2'] - vlanArg3 = module.params['vlanArg3'] - vlanArg4 = module.params['vlanArg4'] - vlanArg5 = module.params['vlanArg5'] outputfile = module.params['outputfile'] - hostIP = module.params['host'] - deviceType = module.params['deviceType'] output = "" - if not HAS_PARAMIKO: - module.fail_json(msg='paramiko is required for this module') - - # Create instance of SSHClient object - remote_conn_pre = paramiko.SSHClient() - - # Automatically add untrusted hosts (make sure okay for security policy in - # your environment) - remote_conn_pre.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - # initiate SSH connection with the switch - remote_conn_pre.connect(hostIP, username=username, password=password) - time.sleep(2) - - # Use invoke_shell to establish an 'interactive session' - remote_conn = remote_conn_pre.invoke_shell() - time.sleep(2) - - # Enable and enter configure terminal then send command - output = output + cnos.waitForDeviceResponse("\n", ">", 2, remote_conn) - - output = output + \ - cnos.enterEnableModeForDevice(enablePassword, 3, remote_conn) - - # Make terminal length = 0 - output = output + \ - cnos.waitForDeviceResponse("terminal length 0\n", "#", 2, remote_conn) - - # Go to config mode - output = output + \ - cnos.waitForDeviceResponse("configure device\n", "(config)#", 2, remote_conn) # Send the CLi command - output = output + \ - cnos.vlanConfig( - remote_conn, deviceType, "(config)#", 2, vlanArg1, vlanArg2, - vlanArg3, vlanArg4, vlanArg5) + output = output + str(cnos.vlanConfig(module, "(config)#", None)) - # Save it into the file + # Save it operation details into the file file = open(outputfile, "a") file.write(output) file.close() @@ -282,7 +230,8 @@ def main(): # need to add logic to check when changes occur or not errorMsg = cnos.checkOutputForError(output) if(errorMsg is None): - module.exit_json(changed=True, msg="VLAN configuration is accomplished") + module.exit_json(changed=True, + msg="VLAN configuration is accomplished") else: module.fail_json(msg=errorMsg) diff --git a/lib/ansible/modules/network/enos/enos_facts.py b/lib/ansible/modules/network/enos/enos_facts.py index 7ff20449b2..c8bf758b97 100644 --- a/lib/ansible/modules/network/enos/enos_facts.py +++ b/lib/ansible/modules/network/enos/enos_facts.py @@ -12,6 +12,7 @@ # GNU General Public License for more details. # # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# # Module to Collect facts from Lenovo Switches running Lenovo ENOS commands # Lenovo Networking # diff --git a/lib/ansible/plugins/cliconf/cnos.py b/lib/ansible/plugins/cliconf/cnos.py index 992ef7b766..61726382bd 100644 --- a/lib/ansible/plugins/cliconf/cnos.py +++ b/lib/ansible/plugins/cliconf/cnos.py @@ -32,39 +32,52 @@ class Cliconf(CliconfBase): device_info = {} device_info['network_os'] = 'cnos' - reply = self.get(b'display version') + reply = self.get(b'show sys-info') data = to_text(reply, errors='surrogate_or_strict').strip() - - match = re.search(r'^System version: (.*?) ', data, re.M | re.I) - if match: - device_info['network_os_version'] = match.group(1) - - match = re.search(r'^Lenovo RackSwitch (\S+)', data, re.M | re.I) - if match: - device_info['network_os_model'] = match.group(1) - - match = re.search(r'^Device name: (.*?) ', data, re.M | re.I) - if match: - device_info['network_os_hostname'] = match.group(1) - else: - device_info['network_os_hostname'] = "NA" + host = self.get(b'show hostname') + hostname = to_text(host, errors='surrogate_or_strict').strip() + if data: + device_info['network_os_version'] = self.parse_version(data) + device_info['network_os_model'] = self.parse_model(data) + device_info['network_os_hostname'] = hostname return device_info + def parse_version(self, data): + for line in data.split('\n'): + line = line.strip() + match = re.match(r'System Software Revision (.*?)', + line, re.M | re.I) + if match: + vers = line.split(':') + ver = vers[1].strip() + return ver + return "NA" + + def parse_model(self, data): + for line in data.split('\n'): + line = line.strip() + match = re.match(r'System Model (.*?)', line, re.M | re.I) + if match: + mdls = line.split(':') + mdl = mdls[1].strip() + return mdl + return "NA" + @enable_mode def get_config(self, source='running', format='text'): if source not in ('running', 'startup'): msg = "fetching configuration from %s is not supported" return self.invalid_params(msg % source) if source == 'running': - cmd = b'display running-config' + cmd = b'show running-config' else: - cmd = b'display startup-config' + cmd = b'show startup-config' return self.send_command(cmd) @enable_mode def edit_config(self, command): - for cmd in chain([b'configure device'], to_list(command), [b'end']): + for cmd in chain([b'configure terminal'], to_list(command), [b'end']): self.send_command(cmd) def get(self, command, prompt=None, answer=None, sendonly=False): diff --git a/test/units/modules/network/cnos/fixtures/cnos_vlan_config.cfg b/test/units/modules/network/cnos/fixtures/cnos_vlan_config.cfg new file mode 100644 index 0000000000..810f7d8067 --- /dev/null +++ b/test/units/modules/network/cnos/fixtures/cnos_vlan_config.cfg @@ -0,0 +1,21 @@ +VLAN Name Status IPMC FLOOD Ports +======== ================================ ======= ========== =================== +1 default ACTIVE IPv6 + Ethernet1/2(u) + Ethernet1/3(t) + Ethernet1/4(t) + Ethernet1/9(u) + Ethernet1/10(u) + Ethernet1/14(u) + Ethernet1/15(u) + Ethernet1/16(u) + Ethernet1/17(u) +13 anil ACTIVE IPv4,IPv6 + po13(t) + po17(t) + po100(t) + po1003(t) + po1004(t) + Ethernet1/3(t) + Ethernet1/4(t) + diff --git a/test/units/modules/network/cnos/test_cnos_vlan.py b/test/units/modules/network/cnos/test_cnos_vlan.py new file mode 100644 index 0000000000..97318e4fc5 --- /dev/null +++ b/test/units/modules/network/cnos/test_cnos_vlan.py @@ -0,0 +1,47 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import json +from ansible.compat.tests.mock import patch +from ansible.modules.network.cnos import cnos_vlan +from units.modules.utils import set_module_args +from .cnos_module import TestCnosModule, load_fixture + + +class TestCnosVlanModule(TestCnosModule): + + module = cnos_vlan + + def setUp(self): + super(TestCnosVlanModule, self).setUp() + + self.mock_run_cnos_commands = patch('ansible.module_utils.network.cnos.cnos.run_cnos_commands') + self.run_cnos_commands = self.mock_run_cnos_commands.start() + + def tearDown(self): + super(TestCnosVlanModule, self).tearDown() + self.mock_run_cnos_commands.stop() + + def load_fixtures(self, commands=None, transport='cli'): + self.run_cnos_commands.return_value = [load_fixture('cnos_vlan_config.cfg')] + + def test_cnos_vlan_create(self): + set_module_args({'username': 'admin', 'password': 'pass', + 'host': '10.241.107.39', 'deviceType': 'g8272_cnos', + 'outputfile': 'test.log', 'vlanArg1': '13', + 'vlanArg2': 'name', 'vlanArg3': 'anil'}) + result = self.execute_module(changed=True) + file = open('Anil.txt', "a") + file.write(str(result)) + file.close() + expected_result = 'VLAN configuration is accomplished' + self.assertEqual(result['msg'], expected_result) + + def test_cnos_vlan_state(self): + set_module_args({'username': 'admin', 'password': 'pass', + 'host': '10.241.107.39', 'deviceType': 'g8272_cnos', + 'outputfile': 'test.log', 'vlanArg1': '13', + 'vlanArg2': 'state', 'vlanArg3': 'active'}) + result = self.execute_module(changed=True) + expected_result = 'VLAN configuration is accomplished' + self.assertEqual(result['msg'], expected_result)