'NetWorker'에 해당되는 글 12건

  1. 2014.12.08 Automation for Networker[2] - Ansible : Part 1
  2. 2014.07.15 Programmability for Networker : Part 5
DevOps/Automation2014.12.08 10:22

 


기존에 포스팅을 시작한 Puppet에 이어서 비슷한 자동화 Tool인 Ansible에 대한 정리입니다.

Puppet를 정리를 시작하다가, 잠깐 다른 부분을 정리하다보니 Ansible 부분을 다시 먼저 정리하기 시작했습니다.

기존 Puppet도 마찬가지고 Ansible도 함께 포스팅이 될 예정이며 제목은 Automation for Networker이라는 이름으로

통합 포스팅이 될 예정입니다.

Automation Tool이 Network보다는 System쪽에 보다 촛점이 맞혀져 있겠지만,

제 Posting에서는 보다 Networker를 위한 중심으로 진행될 예정입니다. ^^;

 

수정해야 할 부분, 보완해야 할 부분이 있으면 알려주시면 감사하겠습니다.


 

 

 

Ansible

  - 시스템 구성(환경 설정), 초기 소프트웨어 Deploy는 물론 다운타임이 없이 Update에 따라 지속적인 Deploy가 가능.

 - 단순하고, 사용하기 편리하도록 하는 것이 Ansible의 목표

 - 에이전트가 없는 구조. 에이전트 관리에 신경을 쓰지 않아도 됨.

 - SSH를 통한 통신.

 - DevOps Tool

 - 네트워크 엔지니어 활용 방법

       : Configuration Management / Network Configuration Template

 

 

Ansible 기능

  - Packge management : apt, yum, ..

  - Remote execution : command, shell

  - Service management : service

  - File handling : copy, template..

  - SCM : get_url, git, subversion...

 

 

Ansible 설치

  - 별도의 Agent가 없기 때문에 서버에서만 Ansible을 설치

     ※ 단, Agent에 의해 관리받는 Host가 Python 2.4 이하 버전 사용 시에는 simplejson 모듈 설치 필요.

 

 


Ansible File


 

 

Inventory File

  - Remote Server에 대한

  - /etc/ansible/hosts/Ansible-hosts [Default]

  - '-i' 옵션으로 별도의 inventory File 지정 가능

  - Remote host Grouping 가능

 

Template File

  - 실제 처리하고자 하는 업무에 대한 Template

  - Jinja2 적용 가능(Template Task인 경우)

  - 관례상 확장자는 '.j2'

 

Playbook File

  - ansible 환경 설정 및 배포

  - yaml 문법을 사용하여 기술

  - 조건(when), 반복(with_items), Variables, include, register 지원.

  - YAML의 제일 첫 줄은 '---'로 시작하며, 이는 YAML 포맷의 시작 지점을 뜻함.

 - List의 동일한 Member들은 '-' 문자를 먼저 쓰고 뒤에 항목들을 입력한다.

 - 각 항목들의 Indent는 동일하게 구성(2칸)

 - Dictiory 혹은 Hash라고 부르는 key:value 의 형식으로 사용된다.

 - 다양한 방식의 boolean 값을 사용할 수 있다.

     ex) yes , no, True, TRUE, false

  - 하나 이상의 'play'를 가진다. (play는 여러 호스트들에 정의된 'role'과 'task'를 매핑하는 역할)

        ‡ task : ansible module을 호출하는 단위(필수)

 

 


YAML & Jinja2


 

YAML

  - 사람에게 친숙한 데이터 직렬화 양식으로 모든 프로그래밍 언어에서 사용

     * Site: http://www.yaml.org/spec/1.2/spec.html#id2759768

 

Jinja2

  - Jinja2 is a modern and designer-friendly templating language for Python, modelled after Django’s templates.

  - Jinja2 is a full featured template engine for Python.

    It has full unicode support, an optional integrated sandboxed execution environment, widely used and BSD licensed.

     * Site : http://jinja.pocoo.org/


 

 

 

 


Ansible Example 1


  우선 Ansible을 실행하기 위한 아주 기본 구성은 하나의 Template 파일(.j2), Playbook(.yml)로 구성하여 예제를 실행해봅니다.

  Playbook 파일의 대략적인 구성은 아래와 같습니다. 

hosts :

vars:

   구분자 : 실제 대입될 값

tasks:

  - name : Task명                                                                       - 출력 시에, 실행한 Task 이름을 표기

    template: src=Template파일(경로 포함)  dest =완성된 파일(경로포함)

 

실제 예제 파일의 내용입니다. . 

Template파일에는 기본적인 Configuration 설정이 있고, Hostname 부분에만 {{ }}로 묶인 것이 있습니다.

이 부분이 Template에서 변경이 이뤄질 부분입니다.

 

아래 Playbook 파일을 보면, vars 란에 hostname이라는 부분이 있습니다. 이것이 Template에 {{ }} 안에 변수명입니다.

즉 Template의 {{ }} 선언된 변수명이 Playbook 내에서 Vars 항목에 선언되어 있어야 합니다.

task는 실제 처리할 내용입니다. myT.j2(Template) 내용을 처리해서, hostname.cfg라는 파일로 만들어지게 됩니다.

 

이제 Playbook을 실행해봅니다.

실행은

    ansible-playbook playbook_file

으로 실행하면 됩니다.

 

정상적으로 실행된 것을 확인할 수 있으며(실제 처음 실행하게 되면, Ok뒤의 Changed로도 체크가 됩니다.)

결과 파일을 살펴보면, hostname 부분이 vars에서 선언한 내용으로 변경되어 있음을 찾아볼 수 있습니다.

 

 


 Ansible Example 2


  두 번째 예제는 다수의 Variable을 사용하는 방법입니다.

  vars 항목 대신에 task에서 with_items라는 항목을 넣고, 변수명:데이터값 을 지정하게 됩니다.

  여기에서 선언한 개수만큼 해당 Task는 실행이 되게 됩니다. 

  간단한 예제로 보겠습니다.

 

 

hosts :

tasks:

  - name : Task명

    template: src=Template파일(경로 포함)

                      dest =/home/zigi/{{item.변수명}}

    with_items:                                                                                     // 순환문 처럼 아래 변수에 대해서 차례대로 순환

       - 변수명 : 실제 대입될 값1

       - 변수명 : 실제 대입될 값2

       - 변수명 : 실제 대입될 값3

       - 변수명 : 실제 대입될 값4  

 

 예제 1과 Template은 동일하지만, Playbook 파일을 보면, vars부분 대신에 task의 with_items이 생긴 것을 볼 수 있습니다.

 

playbook을 실행하면 정상적으로 수행됨을 볼 수가 있고, Task에서도 5번의 changed가 생긴 것을 볼 수 있습니다.
(ex1에서 얘기했듯이 최초의 실행을 하게 되면, changed도 함께 표기가 됩니다. 파일을 생성자체가 변경사항이기 때문입니다.

 

  실제 만들어진 파일의 내부를 보면, with_items에 선언된 값으로 변경되어 있음을 볼 수 있습니다.

 

 

 


  Ansible Example 3


  세 번째 예제는 하나의 Template에 하나의 값이 아니라 다수의 값을 지정하려고 하는 경우입니다.

  기존의 변수와 값을 선언하던 것을 { }로 묶어서, 내부에서는 ','로 구분만 해주면 됩니다.

  아래 예제를 보면 보다 쉽게 이해할 수 있습니다.

 

hosts :

tasks:

  - name : Task명

    template: src=Template파일(경로 포함)                                        // Templeate 파일

                      dest =/home/zigi/{{item.변수명}}

    with_items:                                                                                     // 순환문 처럼 아래 변수에 대해서 차례대로 순환

       - {변수명1: 실제 대입될 값1-1, 변수명2:실제 대입될 값1-2, ... }

       - {변수명2: 실제 대입될 값2-1, 변수명2:실제 대입될 값2-2, ... }

       - {변수명3: 실제 대입될 값3-1, 변수명2:실제 대입될 값3-2, ... }

       - {변수명4: 실제 대입될 값4-1, 변수명2:실제 대입될 값4-2, ... }

 

 

아래 Template을 보시면, hostname뿐만이 아니라 Loopback의 address부분/ ip route의 nexthop이 추가된 것을 볼 수 있습니다.

playbook에서도 with_items 부분에 { }로 각 변수와 값이 들어가 있습니다

 

 

 Playbook을 실행하면, Task에 각 item들이 입력된 값을 볼 수 있습니다.

 

실제 생성된 파일의 내용을 보면

정상적으로 각 variable 란에 지정된 변수 값으로 데이터가 들어간 것을 볼 수 있습니다.

 

 


 

 

 

Posted by 네떡지기
프로그래밍/Python2014.07.15 00:56

이전까지 다뤄졌던 pingrange Python 예제를 변형해봅니다.

 

코드를 모두 짤 수 없다면, 잘 짜여진 기본 에제를 바탕으로 필요한 내용을 수정/보완하는 것도 중요할 것입니다.

물론 그렇게 하기 위해서는 기존 코드를 잘 이해하는 것이 매우 중요합니다.

 

기존 예제 소스를 이해하기 위해서 이론 정리만 했다면 이번 변형 예제는 기존 예제와 달라진 점을 비교해보고

또한 변형 예제에 대해서는 나름대로.. 친절하게 주석을 달았습니다.

물론 이론적인 부분이 함께 알아두고, 코드를 잘 쪼개서 볼 수 있어야 이해가 쉬울 것입니다.

 

 기존 예제 포스팅은 아래와 같습니다.

 

Python for Networker : Part 2                  Python for Networker : Part 3                    Python for Networker : Part 4 

 

기존 예제는 아래에서 볼 수 있습니다.

[ 예제 링크 ] https://github.com/datacenter/who-moved-my-cli

 

※ 본 내용의 진행은 Nexus 5548 6.0(2)N2(4) 기준입니다.

 

※ Modified Version Code는 다음에서 받을 수 있스니다.

      - https://github.com/NetworkZIGI/Python_for_Network

 


 

 

pingrange.py

import re

from cisco import cli

from argparse import ArgumentParser

def expandrange(rangefunc):

    hosts = []

    octets = rangefunc.split('.')

    for i,octet in enumerate(octets):

        if '-' in octet:

            octetrange = octet.split('-')

            for digit in range(int(octetrange[0]), int(octetrange[1])+1):

                ip = '.'.join(octets[:i] + [str(digit)] + octets[i+1:])

                hosts += expandrange(ip)

            break

    else:

        hosts.append(rangefunc)

    return hosts

 

parser = ArgumentParser('pingrange')

parser.add_argument('ip', help='IP range to ping, e.g., 10.1.0-1.0-255 will expand to 10.1.0.0/23')

parser.add_argument('options', nargs='*', help='Options to pass to ping', default=['count 1'])

args = parser.parse_args()

targets = expandrange(args.ip)

for ip in targets:

    tupleping  = cli('ping %s %s' % (ip, ' '.join(args.options)))

    strping = str(tupleping)

    m = re.search('([0-9\.]+)% packet loss',strping )

    print('%s - %s' % (ip, 'UP' if float(m.group(1)) == 0.0 else 'DOWN'))

 

 

기존 pingrange의 변형된 코드의 달라진 점

      IP범위를 잘못 입력했을 경우에 예외 처리를 하였다.  (옥텟별로 1~255 사이)

             : checkRagne 메서드 정의.

             : 잘못된 입력인 경우에는 오류 메시지를 출력하고 시스템을 종료(sys.exit())를 한다.

      지정된 IP범위에서 증가값을 옵션으로 임의로 지정 할 수 있게 하였다.

              : 별도의 범위를 지정하지 않으면, default 1씩 증가한다.

 

 ◇ 기타 대부분의 코드는 아래의 변형된 코드와 유사하기 때문에 변형 코드의 주석과 기존 Part 2~4에서 다루었던 코드 이해하기

     이론 내용 등을 함께 보면 기존 코드를 이해하는 데 도움이 될 수 있을 것 같습니다.

 

 

pingrange Code : Modified Version

import sys                                                             # 시스템 패키지. 시스템 종료 메서드를 호출하기 위해 사용

import re                                                              # 정규식을 쓰기 위한 패키지

import cisco                                                         # Cisco 패키지  : Cli 명령을 사용하기 위해 사용.

from argparse import ArgumentParser               # 실행시의 매개변수를 다루기 위한 패키지

 

def checkRange(checkIP):                                       # 입력받은 IP 옥텟이 IP범위에 해당하는지 확인하기 위한 메서드

    octets = checkIP.split('.')                                  # 입력받은 IP를 '.으로 나눠서 각 옥텟별로 만듬

    for octet in octets:                                            # 각 옥텟이 저장된 List의 항목(IP의 각 옥텟)을 순환

        ip = int(octet)                                              # int 형 데이터로 변경

        if (ip < 0 or ip > 255):                                 # IP의 적정 범위인지 확인하여, 그 이외의 숫자인 경우에는 에러 발생

            print "Invalid Input Value"

            print "IP value is not less than 0 or greater than 255."

            sys.exit()                                                 # 시스템 종료

    return True

 

 

def expandrange(rangefunc,stepcnt):   # 사용자가 요청한 IP 범위 / 체크할 IP띄어쓸 범위

                                                                          # 체크할 IP 범위를 별도로 지정하지 않으면 1default 값으로 함.

    hosts = []                                                      # ping을 확인할 IP를 저장하기 위한 리스트 변수

    step = str(stepcnt[0])                                   # 매개변수 두번째를 리스트 type으로 받기 때문에 문자열 처리를 위해서 변경

    octets = rangefunc.split('.')                         # IP리스트를 옥텟별로 분리하여 LIST 변수에 저장

    for i,octet in enumerate(octets):                 # 옥텍별로 확인

        if '-' in octet:                                            # 만약에 옥텟안에 '-'이 있다면 해당 옥텟은 범위를 뜻하는 것.

            octetrange = octet.split('-')                # IP 범위를 LIST로 분리 (제일 앞 대역과 제일 대역)

            sip = int(octetrange[0])                      # IP 범위의 앞 부분을 sip로 int type으로 저장

            dip = int(octetrange[1])                     # IP 범위의 뒷 부분을 dip로 int type으로 저장

            for digit in range(sip,dip+1, int(step) if i==3 else 1):     

                                                                        # 순환문을 위한 값 지정 : 범위 지정의 첫번째 IP / IP / 증가값

                                         # 증가값의 경우에는 1~3번째 옥텟에는 해당하지 않고, 마지막 옥텟에만 해당되기 떄문에

                                         # 4번째 옥텟일 경우에만(i가 3인 경우가 옥텟이 4번째인 경우임, 0,1,2,3) 증가값을 적용하고

                                         # 그 이외의 경우에는 기본 증가값인 1을 사용한다.

                ip = '.'.join(octets[:i] + [str(digit)] + octets[i+1:])   # 범위의 옥텟전 / 범위옥텟 / 범위 옥텟후를 더해서 IP로만듬

                                        # List의 경우 [] 안에 시작이나 종료점을 쓰지 않으면, 처음부터 끝까지를 나타냄.

                hosts += expandrange(ip,stepcnt)    # 옥텟에서 아직 범위가 있을 수 있기 때문에 다시 expandrage 호출

            break

    else:                                                                        # for문이 break로 종료되지 않고 정상 종료된 경우에 실행.

        if checkRange(rangefunc):                   # for문이 정상 종료된 경우 범위가 지정되지 않은 숫자로만 이뤄진 IP이나

            hosts.append(rangefunc)            # IP가 정상적인 IP범위에 해당하는지 checkRange메서드 호출해서 true인경우만

    return hosts                                          # Host에 IP를 추가하고 리턴해준다.

 

parser = ArgumentParser('AdvPing')

parser.add_argument('ip', help='IP range to ping, e.g., 10.1.0-1.0-255 will expand to 10.1.0.0/23')

                                                                # 매개변수 관리용 : 'ip' 이름으로 매개변수 1번째를 관리

parser.add_argument('options', type=str, default=['1'], nargs='*', help='Options to pass to ping')

                                                                # 매개변수 관리용 : 'options'로 처리하며, typestr로 하고 default값은 '1' 로 한다.

args = parser.parse_args()                     # 매개변수 손쉽게 사용하기 위해서 parse_args()메서드 호출

targets = expandrange(args.ip,args.options)    # IP와 option(IP 증가값)을 가지고 호출

for ip in targets:     # Ping을 체크할 전체 IP 리스트를 순환하면서 실행

    tupleping  = cisco.cli('ping %s' % ip)       # Ping 명령을 실행하고 그에 대한 결과값을 받음.

    strping = str(tupleping) # Tuple형식의 결과값을 str type으로 변형

    m = re.search('([0-9\.]+)% packet loss',strping ) # 정규식을 활용하여 IP가 UP/Down 확인

    print('%s - %s' % (ip, 'UP' if float(m.group(1)) == 0.0 else 'DOWN')) # 해당 IP의 UP/Down 표기

 

 

 

pingrange Code : Modified Version : 결과값

NX-OS# python pingrange2.py 10.9.8.1-200 32

10.9.8.1 - UP

10.9.8.33 - DOWN

10.9.8.65 - UP

10.9.8.97 - UP

10.9.8.129 - DOWN

10.9.8.161 - DOWN

10.9.8.193 - DOWN

 

Posted by 네떡지기

티스토리 툴바