美文网首页Ansible
Ansible 开发模块 之【连接华为交换机】

Ansible 开发模块 之【连接华为交换机】

作者: lework | 来源:发表于2017-05-31 21:40 被阅读470次

    需求


    远程在switch上执行命令

    实现想法


    使用ssh连接到switch上,远程输入命令到switch,获取返回结果。

    测试环境

    华为的s5700交换机

    没有实体机的话,可以使用ensp,下图是我的测试环境


    image.png
    image.png

    交换机开启ssh连接


    s5700

    <Huawei>system-view 
    [Huawei]rsa local-key-pair create 
    The key name will be: Huawei_Host
    % RSA keys defined for Huawei_Host already exist.
    Confirm to replace them? [y/n]:y
    The range of public key size is (512 ~ 2048). 
    NOTES: If the key modulus is greater than 512, 
           it will take a few minutes.
    Input the bits in the modulus[default = 512]:2048
    Generating keys...
    ..........+++
    .....................................................................+++
    ..............++++++++
    .............++++++++
    
    [Huawei]user-interface vty 0 4
    [Huawei-ui-vty0-4]authentication-mode aaa
    [Huawei-ui-vty0-4]protocol inbound ssh
    [Huawei-ui-vty0-4]quit
    [Huawei]aaa
    [Huawei-aaa]local-user user1 password cipher Huawei@123
    [Huawei-aaa]local-user user1 service-type ssh
    [Huawei-aaa]quit
    [Huawei]ssh user user1 authentication-type password
    Info: Succeeded in adding a new SSH user.
    [Huawei]ssh user user1 service-type stelnet
    [Huawei]stelnet server enable
    Info: Succeeded in starting the Stelnet server.
    

    就可以使用ssh方式连接交换机了。

    modules


    1. 先使用伪代码定义Hwcon类以及方法
    class Hwcon(object):
    
        def __init__(self, address, username, password, port=22):
            # 初始化ssh连接
        def close(self):
            # 关闭连接
        def openShell(self):
            # 在ssh连接中打开一个terminal
        def send_command(self, command=''):
            # 向terminal发送命令
        def get_command_result(self, cmd):
            # 获取命令的输出结果
        def parse_result_data(self, data):
            # 解析命令的输出结果
        def save_config(self):
            # 保存配置
        def run(self, cmd):
            # 执行命令
    

    使用方法:

    connection = Hwcon(b_host, b_user, b_password, module.params['sport'])
    connection.openShell()
    rc,stdout,stderr = connection.run(b_command)
    connection.save_config()
    connection.close()
    

    就可完成我们执行命令的功能,当然这只是伪代码,下面我们开始填充里面的功能代码。

    1. 框架我们定义好了,下面定义命令的错误,正确输出信息的正则表达式,以便我们能确认命令输出结果是否正确。
    terminal_stdout_re = [
            re.compile(r'[\r\n]?<.+>(?:\s*)$'),
            re.compile(r'[\r\n]?\[.+\](?:\s*)$'),
        ]
    terminal_stderr_re = [
            re.compile(r"% ?Error: "),
            re.compile(r"^% \w+", re.M),
            re.compile(r"% ?Bad secret"),
            re.compile(r"invalid input", re.I),
            re.compile(r"(?:incomplete|ambiguous) command", re.I),
            re.compile(r"connection timed out", re.I),
            re.compile(r"[^\r\n]+ not found", re.I),
            re.compile(r"'[^']' +returned error code: ?\d+"),
            re.compile(r"syntax error"),
            re.compile(r"unknown command"),
            re.compile(r"Error\[\d+\]: ", re.I),
            re.compile(r"Error:", re.I)
        ]
    
    1. 导入我们需要的库
    import time
    import re
    from ansible.module_utils.basic import AnsibleModule
    from ansible.module_utils._text import to_bytes
    from ansible.errors import AnsibleError, AnsibleConnectionFailure
    try:
      import paramiko
    except ImportError:
      raise AnsibleError("paramiko is not installed, please use pip install paramiko")
    try:
        from __main__ import display
    except ImportError:
        from ansible.utils.display import Display
        display = Display()
    
    1. 填充我们的hwcon类
    class Hwcon(object):
        shell = None
        client = None
    
        def __init__(self, address, username, password, port=22):
            display.vv("Connecting to network device on ip", str(address) + ".")
            self.client = paramiko.client.SSHClient()
            self.client.set_missing_host_key_policy(paramiko.client.AutoAddPolicy())
            self.client.connect(address, port=port, username=username, password=password, look_for_keys=False,
                                allow_agent=False)
    
        def close(self):
            if self.client is not None:
                self.client.close()
    
        def openShell(self):
            self.shell = self.client.invoke_shell()
    
        def send_command(self, command=''):
            if self.shell:
                if command not in ('?',):
                    command += "\n"
                self.shell.send(command)
    
            while True:
                if self.shell.recv_ready() or self.shell.recv_stderr_ready():
                    break
                time.sleep(0.1)
    
        def get_command_result(self, cmd):
            buffersize = 4096
            self.send_command()
            self.shell.recv(buffersize)
            self.send_command(cmd)
            stdout = self.shell.recv(buffersize)
            b_data = stdout.split('\r\n')
            result_tmp = ''
            while '- More -' in b_data[-1]:
                self.shell.send("\n")
                time.sleep(0.1)
                tmp = self.shell.recv(buffersize)
                b_data = tmp.split('\r\n')
                result_tmp += tmp
    
            if self.shell.recv_stderr_ready():
                stderr = self.shell.recv_stderr(buffersize)
            else:
                stderr = ''
            stdout = '\r\n'.join(stdout.split('\r\n')[:-1]) + '\n' + result_tmp
            stdout = stdout.replace('  ---- More ----', '').replace(
                '\x1b[42D                                          \x1b[42D', '')
            return stdout, stderr
    
        def parse_result_data(self, data):
            b_data = data.split('\r\n')
            result = b_data[1:-1]
            return '\r\n'.join(result)
    
        def save_config(self):
            rc = 1
            buffersize = 4096
            self.send_command()
            stdout = self.shell.recv(buffersize)
            t1 = terminal_stdout_re[0].findall(stdout)
            while not t1:
               self.send_command('quit')
               stdout = self.shell.recv(buffersize)
               t1 = terminal_stdout_re[0].findall(stdout)
            self.send_command('save')
            time.sleep(0.1)
            self.send_command('y')
            time.sleep(3)
            stdout = self.shell.recv(buffersize)
            if stdout.find('successfully'):
                rc = 0
            return rc
    
        def run(self, cmd):
            rc = 1
            stdout, stderr = self.get_command_result(cmd)
            for regex in terminal_stderr_re:
                r1 = regex.findall(stdout)
            if not r1:
               stdout = self.parse_result_data(stdout)
               rc = 0
            return rc, stdout, stderr
    
    1. 把使用方法填入main方法中
    def main():
        module = AnsibleModule(
            argument_spec = dict(
            command=dict(required=True, type='str'),
            shost=dict(required=True, type='str'),
            sport=dict(required=False, type='int', default=22),
            suser=dict(required=True, type='str'),
            spass=dict(required=True, type='str', no_log=True),
            save=dict(required=False, type='bool')
          ),
              supports_check_mode=True
        )
    
        b_command = to_bytes(module.params['command'], errors='surrogate_or_strict')
        b_host = to_bytes(module.params['shost'], errors='surrogate_or_strict')
        b_user = to_bytes(module.params['suser'], errors='surrogate_or_strict')
        b_password = to_bytes(module.params['spass'], errors='surrogate_or_strict')
        result = {'changed': False}
    
        try:
          connection = Hwcon(b_host, b_user, b_password, module.params['sport'])
        except Exception as e:
          raise AnsibleConnectionFailure(str(e))
        try:
          connection.openShell()
        except Exception as e:
          msg = "Failed to open session"
          if len(str(e)) > 0:
            msg += ": %s" % str(e)
          raise AnsibleConnectionFailure(msg)
        display.vvv("EXEC %s" % b_command, host=b_host)
        try:
          rc,stdout,stderr = connection.run(b_command)
        except Exception as e:
           raise AnsibleError('Exec command error.\n' + str(e) )
    
        if rc == 0 and b_command.find('display'):
            result['changed'] = True
        elif rc == 1:
        module.fail_json(msg=stdout+stderr)
    
        if module.params['save']:
            try:
                save_rc = connection.save_config()
            except Exception as e:
              raise AnsibleError('Save config error.\n' + str(e) )
            if save_rc != 0:
                msg= "not save config!"
                module.fail_json(msg=msg)
    
        connection.close()
    
        result.update({
        'command': b_command,
            'rc': rc,
            'stdout': stdout,
            'stderr': stderr})
    
        module.exit_json(**result)
    
    if __name__ == '__main__':
        main()
    

    添加完描述信息,完整的代码如下

    #!/usr/bin/python
    
    ANSIBLE_METADATA = {'status': ['preview'],
                        'supported_by': 'community',
                        'metadata_version': '1.0'}
    
    DOCUMENTATION = ''' 
    ---
    module: hwos_command
    version_added: "2.3"
    short_description: Run arbitrary command on HUAWEI network devices.
    description:
         - Run arbitrary command on HUAWEI network devices.
    options:
      command:
        description:
          - HUAWEI network devices command
        required: true
      shost:
        description:
          - remote network devices ip address
        required: true
      sport:
        description:
          - remote network devices ssh port
        required: false
        default: 22
      suser:
        description:
          - remote network devices ssh user
        required: true
      spass:
        description:
          - remote network devices ssh user password
        required: true
      save:
        description:
          - save current configuration
        required: false
        default: no
    author:
        - "Lework"
    '''
    
    EXAMPLES = """
    - hosts: localhost
      gather_facts: no
      connection: local
      vars:
        sport: 22
        suser: "user1"
        spass: "test"
        shost: "192.168.77.140"
    
      tasks:
        - name: display version
          hwos_command:
            sport: "{{ sport }}"
            shost: "{{ shost }}"
            suser: "{{ suser }}"
            spass: "{{ spass }}"
            command: display version
    
        - name: add vlan 800 and int 0/0/11
          hwos_command:
            sport: "{{ sport }}"
            shost: "{{ shost }}"
            suser: "{{ suser }}"
            spass: "{{ spass }}"
            save: true
            command: |
              system-view
              vlan 800
              quit
              interface GigabitEthernet 0/0/12
              port link-type access
              vlan 800
              port GigabitEthernet 0/0/12
    """
    
    RETUN = """
    stdout:
      description: the set of responses from the commands
      returned: always
      type: list
      sample: ['...', '...']
    
    stdout_lines:
      description: The value of stdout split into a list
      returned: always
      type: list
      sample: [['...', '...'], ['...'], ['...']]
    
    command:
      description: rum command
      returned: always
      type: str
      sample: display version
    """
    
    
    import time
    import re
    
    from ansible.module_utils.basic import AnsibleModule
    from ansible.module_utils._text import to_bytes
    from ansible.errors import AnsibleError, AnsibleConnectionFailure
    
    try:
      import paramiko
    except ImportError:
      raise AnsibleError("paramiko is not installed, please use pip install paramiko")
    try:
        from __main__ import display
    except ImportError:
        from ansible.utils.display import Display
        display = Display()
    
    
    terminal_stdout_re = [
            re.compile(r'[\r\n]?<.+>(?:\s*)$'),
            re.compile(r'[\r\n]?\[.+\](?:\s*)$'),
        ]
    
    terminal_stderr_re = [
            re.compile(r"% ?Error: "),
            re.compile(r"^% \w+", re.M),
            re.compile(r"% ?Bad secret"),
            re.compile(r"invalid input", re.I),
            re.compile(r"(?:incomplete|ambiguous) command", re.I),
            re.compile(r"connection timed out", re.I),
            re.compile(r"[^\r\n]+ not found", re.I),
            re.compile(r"'[^']' +returned error code: ?\d+"),
            re.compile(r"syntax error"),
            re.compile(r"unknown command"),
            re.compile(r"Error\[\d+\]: ", re.I),
            re.compile(r"Error:", re.I)
        ]
    
    
    class Hwcon(object):
        shell = None
        client = None
    
        def __init__(self, address, username, password, port=22):
            display.vv("Connecting to network device on ip", str(address) + ".")
            self.client = paramiko.client.SSHClient()
            self.client.set_missing_host_key_policy(paramiko.client.AutoAddPolicy())
            self.client.connect(address, port=port, username=username, password=password, look_for_keys=False,
                                allow_agent=False)
    
        def close(self):
            if self.client is not None:
                self.client.close()
    
        def openShell(self):
            self.shell = self.client.invoke_shell()
    
        def send_command(self, command=''):
            if self.shell:
                if command not in ('?',):
                    command += "\n"
                self.shell.send(command)
    
            while True:
                if self.shell.recv_ready() or self.shell.recv_stderr_ready():
                    break
                time.sleep(0.1)
    
        def get_command_result(self, cmd):
            buffersize = 4096
            self.send_command()
            self.shell.recv(buffersize)
            self.send_command(cmd)
            stdout = self.shell.recv(buffersize)
            b_data = stdout.split('\r\n')
            result_tmp = ''
            while '- More -' in b_data[-1]:
                self.shell.send("\n")
                time.sleep(0.1)
                tmp = self.shell.recv(buffersize)
                b_data = tmp.split('\r\n')
                result_tmp += tmp
    
            if self.shell.recv_stderr_ready():
                stderr = self.shell.recv_stderr(buffersize)
            else:
                stderr = ''
            stdout = '\r\n'.join(stdout.split('\r\n')[:-1]) + '\n' + result_tmp
            stdout = stdout.replace('  ---- More ----', '').replace(
                '\x1b[42D                                          \x1b[42D', '')
            return stdout, stderr
    
        def parse_result_data(self, data):
            b_data = data.split('\r\n')
            result = b_data[1:-1]
            return '\r\n'.join(result)
    
        def save_config(self):
            rc = 1
            buffersize = 4096
            self.send_command()
            stdout = self.shell.recv(buffersize)
            t1 = terminal_stdout_re[0].findall(stdout)
            while not t1:
               self.send_command('quit')
               stdout = self.shell.recv(buffersize)
               t1 = terminal_stdout_re[0].findall(stdout)
            self.send_command('save')
            time.sleep(0.1)
            self.send_command('y')
            time.sleep(3)
            stdout = self.shell.recv(buffersize)
            if stdout.find('successfully'):
                rc = 0
            return rc
    
        def run(self, cmd):
            rc = 1
            stdout, stderr = self.get_command_result(cmd)
            for regex in terminal_stderr_re:
                r1 = regex.findall(stdout)
            if not r1:
               stdout = self.parse_result_data(stdout)
               rc = 0
            return rc, stdout, stderr
    
    
    def main():
        module = AnsibleModule(
            argument_spec = dict(
            command=dict(required=True, type='str'),
            shost=dict(required=True, type='str'),
            sport=dict(required=False, type='int', default=22),
            suser=dict(required=True, type='str'),
            spass=dict(required=True, type='str', no_log=True),
            save=dict(required=False, type='bool')
          ),
              supports_check_mode=True
        )
    
        b_command = to_bytes(module.params['command'], errors='surrogate_or_strict')
        b_host = to_bytes(module.params['shost'], errors='surrogate_or_strict')
        b_user = to_bytes(module.params['suser'], errors='surrogate_or_strict')
        b_password = to_bytes(module.params['spass'], errors='surrogate_or_strict')
        result = {'changed': False}
    
        try:
          connection = Hwcon(b_host, b_user, b_password, module.params['sport'])
        except Exception as e:
          raise AnsibleConnectionFailure(str(e))
        try:
          connection.openShell()
        except Exception as e:
          msg = "Failed to open session"
          if len(str(e)) > 0:
            msg += ": %s" % str(e)
          raise AnsibleConnectionFailure(msg)
        display.vvv("EXEC %s" % b_command, host=b_host)
        try:
          rc,stdout,stderr = connection.run(b_command)
        except Exception as e:
           raise AnsibleError('Exec command error.\n' + str(e) )
    
        if rc == 0 and b_command.find('display'):
            result['changed'] = True
        elif rc == 1:
        module.fail_json(msg=stdout+stderr)
    
        if module.params['save']:
            try:
                save_rc = connection.save_config()
            except Exception as e:
              raise AnsibleError('Save config error.\n' + str(e) )
            if save_rc != 0:
                msg= "not save config!"
                module.fail_json(msg=msg)
    
        connection.close()
    
        result.update({
        'command': b_command,
            'rc': rc,
            'stdout': stdout,
            'stderr': stderr})
    
        module.exit_json(**result)
    
    if __name__ == '__main__':
        main()
    

    执行模块


    playbook

    ---
    
    - hosts: localhost
      gather_facts: no
      vars:
        sport: 22
        suser: "user1"
        spass: "Huawei@123"
        shost: "192.168.77.140"
    
      tasks:
        - name: display version
          hwos_command:
            sport: "{{ sport }}"
            shost: "{{ shost }}"
            suser: "{{ suser }}"
            spass: "{{ spass }}"
            command: display version
    
        - name: display vlan 900
          hwos_command:
            sport: "{{ sport }}"
            shost: "{{ shost }}"
            suser: "{{ suser }}"
            spass: "{{ spass }}"
            command: display vlan 900
          ignore_errors: true
          register: result
     
        - debug: msg={{result.stdout_lines | d()}}
    
        - name: add vlan 900 and int 0/0/13
          hwos_command:
            sport: "{{ sport }}"
            shost: "{{ shost }}"
            suser: "{{ suser }}"
            spass: "{{ spass }}"
            save: true
            command: |
              system-view
              vlan 900
              quit
              interface GigabitEthernet 0/0/13
              port link-type access
              vlan 900
              port GigabitEthernet 0/0/13
              
        - name: display vlan 900
          hwos_command:
            sport: "{{ sport }}"
            shost: "{{ shost }}"
            suser: "{{ suser }}"
            spass: "{{ spass }}"
            command: display vlan 900
          register: result
     
        - debug: msg={{result.stdout_lines}}
    

    主机清单

    localhost ansible_connection=local
    

    执行结果

    image.png

    原图
    http:https://img.haomeiwen.com/i3629406/0e2b53612861c5d5.png

    相关文章

      网友评论

        本文标题:Ansible 开发模块 之【连接华为交换机】

        本文链接:https://www.haomeiwen.com/subject/tnjhfxtx.html