필터로 데이터 다루기
필터로 데이터 다루기 (Using filters to manipulate data)
필터는 JSON 데이터를 YAML 데이터로 변환하고, URL을 분해해서 호스트 이름을 추출하며, 문자열의 SHA1 해시를 구하고, 정수를 더하거나 곱하는 등 다양한 일을 해요. 이 페이지에서 다루는 Ansible 전용 필터로 데이터를 다루거나, Jinja2에 내장된 표준 필터를 사용할 수 있어요. 공식 Jinja2 템플릿 문서의 내장 필터 목록을 참고하세요. 데이터 변환에 Python 메서드를 사용할 수도 있어요. Ansible 커스텀 필터를 플러그인으로 만들 수도 있는데, 일반적으로 모든 사람이 사용할 수 있도록 ansible-core 레포에 새 필터가 들어오는 것을 환영해요.
출처: 문서
본문
필터는 JSON 데이터를 YAML 데이터로 변환하고, URL을 분해해서 호스트 이름을 추출하며, 문자열의 SHA1 해시를 구하고, 정수를 더하거나 곱하는 등 다양한 일을 해요. 이 페이지에 문서화된 Ansible 전용 필터로 데이터를 다루거나, Jinja2와 함께 제공되는 표준 필터를 사용할 수도 있어요. 공식 Jinja2 템플릿 문서의 내장 필터 목록을 참고하세요. 데이터를 변환하려면 Python 메서드도 쓸 수 있어요. 커스텀 Ansible 필터를 플러그인으로 만들 수도 있지만, 일반적으로는 모두가 쓸 수 있도록 ansible-core 레포에 새 필터가 추가되기를 환영해요.
템플릿 처리는 대상 호스트가 아니라 Ansible 제어 노드에서 일어나므로, 필터는 제어 노드에서 실행되어 데이터를 로컬에서 변환해요.
정의되지 않은 변수 다루기
필터는 기본값을 제공하거나 일부 변수를 선택 사항으로 만들어 누락되거나 정의되지 않은 변수를 관리하는 데 도움을 줘요. Ansible이 대부분의 정의되지 않은 변수를 무시하도록 구성했다면, mandatory 필터로 일부 변수에 값을 요구하도록 표시할 수 있어요.
기본값 제공하기
템플릿에서 Jinja2 'default' 필터를 사용해서 변수에 기본값을 직접 제공할 수 있어요. 이는 변수가 정의되지 않았을 때 실패하는 것보다 더 나은 접근인 경우가 많아요:
{{ some_variable | default(5) }}
위 예시에서 'some_variable' 변수가 정의되지 않았다면 Ansible은 "undefined variable" 오류를 내고 실패하는 대신 기본값 5를 사용해요. 롤 안에서 작업 중이라면 롤 기본값을 추가해서 롤의 변수에 대한 기본값을 정의할 수도 있어요. 롤 기본값에 대한 자세한 내용은 롤 디렉터리 구조를 참고하세요.
버전 2.8부터 Jinja에서 Undefined 값의 속성에 접근하려고 하면 즉시 오류를 던지는 대신 또 다른 Undefined 값을 반환해요. 즉 이제 중간 값이 정의되어 있는지 모를 때 중첩 데이터 구조에서 값과 함께 default를 간단히 사용할 수 있어요 (즉 {{ foo.bar.baz | default('DEFAULT') }}).
변수가 false나 빈 문자열로 평가될 때 기본값을 사용하려면 두 번째 파라미터를 true로 설정해야 해요:
{{ lookup('env', 'MY_USER') | default('admin', true) }}
변수를 선택 사항으로 만들기
기본적으로 Ansible은 템플릿 표현식의 모든 변수에 값을 요구해요. 하지만 특정 모듈 변수를 선택 사항으로 만들 수 있어요. 예를 들어 일부 항목에는 시스템 기본값을 사용하고 다른 항목의 값은 제어하고 싶을 수 있어요. 모듈 변수를 선택 사항으로 만들려면 기본값을 특수 변수 omit으로 설정하세요:
- name: Touch files with an optional mode
ansible.builtin.file:
dest: "{{ item.path }}"
state: touch
mode: "{{ item.mode | default(omit) }}"
loop:
- path: /tmp/foo
- path: /tmp/bar
- path: /tmp/baz
mode: "0444"
이 예시에서 파일 /tmp/foo와 /tmp/bar의 기본 모드는 시스템의 umask로 결정돼요. Ansible은 mode 값은 보내지 않아요. 세 번째 파일 /tmp/baz만 mode=0444 옵션을 받아요.
참고:
default(omit)필터 뒤에 추가 필터를 "체이닝"한다면,"{{ foo | default(None) | some_filter or omit }}"처럼 해야 해요. 이 예시에서 기본None(Python null) 값은 이후 필터를 실패시키고, 그러면or omit부분이 발동해요. 이렇게omit을 쓰는 것은 체이닝하는 이후 필터에 매우 특화되어 있으므로, 이렇게 할 때는 시행착오가 있을 것에 대비하세요.
필수 값 정의하기
Ansible이 정의되지 않은 변수를 무시하도록 구성했다면 일부 값을 필수로 정의하고 싶을 수 있어요. 기본적으로 Ansible은 플레이북이나 명령의 변수가 정의되지 않으면 실패해요. DEFAULT_UNDEFINED_VAR_BEHAVIOR을 false로 설정해서 Ansible이 정의되지 않은 변수를 허용하도록 구성할 수 있어요. 그 경우 일부 변수를 정의하도록 요구하고 싶을 수 있는데, 다음으로 할 수 있어요:
{{ variable | mandatory }}
변수 값은 그대로 사용되지만, 정의되지 않았다면 템플릿 평가가 오류를 발생시켜요.
변수에 덮어쓰기를 요구하는 편리한 방법은 undef() 함수로 정의되지 않은 값을 주는 것이에요.
galaxy_url: "https://galaxy.ansible.com"
galaxy_api_key: "{{ undef(hint='You must specify your Galaxy API key') }}"
true/false/null에 대해 서로 다른 값 정의하기 (ternary)
테스트를 만든 다음, 그 테스트가 true를 반환할 때 사용할 값 하나와 false를 반환할 때 사용할 값 하나를 정의할 수 있어요 (버전 1.9에 추가됨):
{{ (status == 'needs_restart') | ternary('restart', 'continue') }}
또한 true일 때 쓸 값, false일 때 쓸 값, null일 때 쓸 세 번째 값을 정의할 수 있어요 (버전 2.8에 추가됨):
{{ enabled | ternary('no shutdown', 'shutdown', omit) }}
데이터 타입 관리하기
변수의 데이터 타입을 알아야 하거나, 바꾸거나, 설정해야 할 수 있어요. 예를 들어 등록된 변수가 딕셔너리인데 다음 작업이 리스트를 필요로 하거나, 사용자 프롬프트가 문자열을 반환하는데 플레이북이 boolean 값을 필요로 할 수 있어요. ansible.builtin.type_debug, ansible.builtin.dict2items, ansible.builtin.items2dict 필터를 사용해서 데이터 타입을 관리할 수 있어요. 데이터 타입 자체를 사용해서 값을 특정 데이터 타입으로 캐스팅할 수도 있어요.
데이터 타입 발견하기
(버전 2.3에 추가됨)
변수의 기저 Python 타입이 확실하지 않다면 ansible.builtin.type_debug 필터로 표시할 수 있어요. 이것은 특정 타입의 변수가 필요할 때 디버깅에 유용해요:
{{ myvar | type_debug }}
이것이 변수에 올바른 타입의 데이터가 있는지 확인하는 유용한 필터처럼 보일 수도 있지만, 특정 데이터 타입을 테스트할 수 있게 해 주는 타입 테스트를 선호하는 경우가 많다는 점을 기억하세요.
문자열을 리스트로 변환하기
ansible.builtin.split 필터를 사용해서 문자/문자열로 구분된 문자열을 루프에 적합한 항목 리스트로 변환하세요. 예를 들어 문자열 변수 fruits를 쉼표로 분할하려면:
{{ fruits | split(',') }}
ansible.builtin.split 필터를 적용하기 전의 문자열 데이터:
fruits: apple,banana,orange
ansible.builtin.split 필터를 적용한 후의 리스트 데이터:
- apple
- banana
- orange
딕셔너리를 리스트로 변환하기
(버전 2.6에 추가됨)
ansible.builtin.dict2items 필터를 사용해서 딕셔너리를 루프에 적합한 항목 리스트로 변환하세요:
{{ dict | dict2items }}
ansible.builtin.dict2items 필터를 적용하기 전의 딕셔너리 데이터:
tags:
Application: payment
Environment: dev
ansible.builtin.dict2items 필터를 적용한 후의 리스트 데이터:
- key: Application
value: payment
- key: Environment
value: dev
(버전 2.8에 추가됨)
ansible.builtin.dict2items 필터는 ansible.builtin.items2dict 필터의 역방향이에요.
키의 이름을 구성하고 싶다면 ansible.builtin.dict2items 필터는 2개의 키워드 인자를 받아요. key_name과 value_name 인자를 전달해서 리스트 출력의 키 이름을 구성하세요:
{{ files | dict2items(key_name='file', value_name='path') }}
ansible.builtin.dict2items 필터를 적용하기 전의 딕셔너리 데이터:
files:
users: /etc/passwd
groups: /etc/group
ansible.builtin.dict2items 필터를 적용한 후의 리스트 데이터:
- file: users
path: /etc/passwd
- file: groups
path: /etc/group
리스트를 딕셔너리로 변환하기
(버전 2.7에 추가됨)
ansible.builtin.items2dict 필터를 사용해서 리스트를 딕셔너리로 변환하고, 내용을 key: value 쌍으로 매핑하세요:
{{ tags | items2dict }}
ansible.builtin.items2dict 필터를 적용하기 전의 리스트 데이터:
tags:
- key: Application
value: payment
- key: Environment
value: dev
ansible.builtin.items2dict 필터를 적용한 후의 딕셔너리 데이터:
Application: payment
Environment: dev
ansible.builtin.items2dict 필터는 ansible.builtin.dict2items 필터의 역방향이에요.
모든 리스트가 키를 key로, 값을 value로 지정하지는 않아요. 예:
fruits:
- fruit: apple
color: red
- fruit: pear
color: yellow
- fruit: grapefruit
color: yellow
이 예시에서는 변환을 구성하기 위해 key_name과 value_name 인자를 전달해야 해요. 예:
{{ fruits | items2dict(key_name='fruit', value_name='color') }}
이 인자들을 전달하지 않거나 리스트에 대해 올바른 값을 전달하지 않으면 KeyError: key 또는 KeyError: my_typo가 보일 거예요.
데이터 타입 강제하기
값을 특정 타입으로 캐스팅할 수 있어요. 예를 들어 vars_prompt에서 "True" 입력을 기대하고 Ansible이 그것을 문자열 대신 boolean 값으로 인식하길 원한다면:
- ansible.builtin.debug:
msg: test
when: some_string_value | bool
팩트에 대해 수학적 비교를 수행하고 Ansible이 그것을 문자열 대신 정수로 인식하길 원한다면:
- shell: echo "only on Red Hat 6, derivatives, and later"
when: ansible_facts['os_family'] == "RedHat" and ansible_facts['lsb']['major_release'] | int >= 6
(버전 1.6에 추가됨)
데이터 포맷하기: YAML과 JSON
템플릿의 데이터 구조를 JSON 또는 YAML 형식으로 전환할 수 있으며, 포맷, 들여쓰기, 데이터 로드 옵션도 있어요. 기본 필터는 디버깅에 가끔 유용해요:
{{ some_variable | to_json }}
{{ some_variable | to_yaml }}
이 필터들의 문서는 ansible.builtin.to_json과 ansible.builtin.to_yaml을 참고하세요.
사람이 읽기 좋은 출력을 원한다면:
{{ some_variable | to_nice_json }}
{{ some_variable | to_nice_yaml }}
이 필터들의 문서는 ansible.builtin.to_nice_json과 ansible.builtin.to_nice_yaml을 참고하세요.
어느 형식이든 들여쓰기를 바꿀 수 있어요:
{{ some_variable | to_nice_json(indent=2) }}
{{ some_variable | to_nice_yaml(indent=8) }}
ansible.builtin.to_yaml과 ansible.builtin.to_nice_yaml 필터는 기본 80자 문자열 길이 제한이 있는 PyYAML 라이브러리를 사용해요. 이 때문에 80번째 기호 뒤에 예상치 못한 줄바꿈이 생겨요 (80번째 기호 뒤에 공백이 있는 경우). 이러한 동작을 피하고 긴 줄을 만들려면 width 옵션을 사용하세요. 필터가 Python 함수 프록시를 지원하지 않으므로 float("inf") 같은 구조 대신 하드코딩된 숫자로 폭을 정의해야 해요. 예:
{{ some_variable | to_yaml(indent=8, width=1337) }}
{{ some_variable | to_nice_yaml(indent=8, width=1337) }}
필터는 다른 YAML 파라미터를 통과시키는 것도 지원해요. 전체 목록은 dump()에 대한 PyYAML 문서를 참고하세요.
이미 포맷된 데이터를 읽어들이고 있다면:
{{ some_variable | from_json }}
{{ some_variable | from_yaml }}
예:
tasks:
- name: Register JSON output as a variable
ansible.builtin.shell: cat /some/path/to/file.json
register: result
- name: Set a variable
ansible.builtin.set_fact:
myvar: "{{ result.stdout | from_json }}"
to_json 필터와 유니코드 지원
기본적으로 ansible.builtin.to_json과 ansible.builtin.to_nice_json은 받은 데이터를 ASCII로 변환해요. 그래서:
{{ 'München'| to_json }}
다음을 반환해요:
'M\u00fcnchen'
유니코드 문자를 유지하려면 필터에 ensure_ascii=False 파라미터를 전달하세요:
{{ 'München'| to_json(ensure_ascii=False) }}
'München'
(버전 2.7에 추가됨)
다중 문서 YAML 문자열을 파싱하려면 ansible.builtin.from_yaml_all 필터가 제공돼요. ansible.builtin.from_yaml_all 필터는 파싱된 YAML 문서의 제너레이터를 반환해요.
예:
tasks:
- name: Register a file content as a variable
ansible.builtin.shell: cat /some/path/to/multidoc-file.yaml
register: result
- name: Print the transformed variable
ansible.builtin.debug:
msg: '{{ item }}'
loop: '{{ result.stdout | from_yaml_all | list }}'
데이터 결합하고 선택하기
여러 소스와 타입의 데이터를 결합하고, 큰 데이터 구조에서 값을 선택할 수 있어서 복잡한 데이터를 정밀하게 제어할 수 있어요.
여러 리스트의 항목 결합하기: zip과 zip_longest
(버전 2.3에 추가됨)
다른 리스트들의 요소를 결합한 리스트를 얻으려면 ansible.builtin.zip을 사용하세요:
- name: Give me list combo of two lists
ansible.builtin.debug:
msg: "{{ [1,2,3,4,5,6] | zip(['a','b','c','d','e','f']) | list }}"
# => [[1, "a"], [2, "b"], [3, "c"], [4, "d"], [5, "e"], [6, "f"]]
- name: Give me the shortest combo of two lists
ansible.builtin.debug:
msg: "{{ [1,2,3] | zip(['a','b','c','d','e','f']) | list }}"
# => [[1, "a"], [2, "b"], [3, "c"]]
항상 모든 리스트를 소진하려면 ansible.builtin.zip_longest을 사용하세요:
- name: Give me the longest combo of three lists, fill with X
ansible.builtin.debug:
msg: "{{ [1,2,3] | zip_longest(['a','b','c','d','e','f'], [21, 22, 23], fillvalue='X') | list }}"
# => [[1, "a", 21], [2, "b", 22], [3, "c", 23], ["X", "d", "X"], ["X", "e", "X"], ["X", "f", "X"]]
위에서 언급한 ansible.builtin.items2dict 필터의 출력과 유사하게, 이 필터들을 사용해서 dict를 구성할 수 있어요:
{{ dict(keys_list | zip(values_list)) }}
ansible.builtin.zip 필터를 적용하기 전의 리스트 데이터:
keys_list:
- one
- two
values_list:
- apple
- orange
ansible.builtin.zip 필터를 적용한 후의 딕셔너리 데이터:
one: apple
two: orange
객체와 하위 요소 결합하기
(버전 2.7에 추가됨)
ansible.builtin.subelements 필터는 객체와 그 객체의 하위 요소 값의 곱(product)을 생성해요. ansible.builtin.subelements 룩업과 비슷해요. 이것은 템플릿에서 사용할 개별 하위 요소를 지정할 수 있게 해 줘요. 예를 들어 이 표현식:
{{ users | subelements('groups', skip_missing=True) }}
ansible.builtin.subelements 필터를 적용하기 전의 데이터:
users:
- name: alice
authorized:
- /tmp/alice/onekey.pub
- /tmp/alice/twokey.pub
groups:
- wheel
- docker
- name: bob
authorized:
- /tmp/bob/id_rsa.pub
groups:
- docker
ansible.builtin.subelements 필터를 적용한 후의 데이터:
-
- name: alice
groups:
- wheel
- docker
authorized:
- /tmp/alice/onekey.pub
- /tmp/alice/twokey.pub
- wheel
-
- name: alice
groups:
- wheel
- docker
authorized:
- /tmp/alice/onekey.pub
- /tmp/alice/twokey.pub
- docker
-
- name: bob
authorized:
- /tmp/bob/id_rsa.pub
groups:
- docker
- docker
변환된 데이터를 loop와 함께 사용해서 여러 객체에 대해 같은 하위 요소를 반복할 수 있어요:
- name: Set authorized ssh key, extracting just that data from 'users'
ansible.posix.authorized_key:
user: "{{ item.0.name }}"
key: "{{ lookup('file', item.1) }}"
loop: "{{ users | subelements('authorized') }}"
해시/딕셔너리 결합하기
(버전 2.0에 추가됨)
ansible.builtin.combine 필터를 사용하면 해시를 병합할 수 있어요. 예를 들어 다음은 한 해시의 키를 덮어써요:
{{ {'a':1, 'b':2} | combine({'b':3}) }}
결과 해시는 다음과 같아요:
{'a':1, 'b':3}
필터는 병합할 여러 인자를 받을 수도 있어요:
{{ a | combine(b, c, d) }}
{{ [a, b, c, d] | combine }}
이 경우 d의 키가 c를, c의 키가 b를 덮어쓰는 식이에요.
필터는 두 개의 선택적 파라미터 recursive와 list_merge도 받아요.
recursive — boolean이며 기본값은 False예요. ansible.builtin.combine이 중첩 해시를 재귀적으로 병합해야 하는지 여부. ansible.cfg의 hash_behaviour 설정 값에 의존하지 않아요.
list_merge — 문자열이며 가능한 값은 replace(기본), keep, append, prepend, append_rp 또는 prepend_rp예요. 병합할 해시에 배열/리스트가 포함될 때 ansible.builtin.combine의 동작을 수정해요.
default:
a:
x: default
y: default
b: default
c: default
patch:
a:
y: patch
z: patch
b: patch
recursive=False(기본)이면 중첩 해시가 병합되지 않아요:
{{ default | combine(patch) }}
결과는:
a:
y: patch
z: patch
b: patch
c: default
recursive=True이면 중첩 해시로 재귀해서 키를 병합해요:
{{ default | combine(patch, recursive=True) }}
결과는:
a:
x: default
y: patch
z: patch
b: patch
c: default
list_merge='replace'(기본)이면 오른쪽 해시의 배열이 왼쪽 해시의 배열을 "대체"해요:
default:
a:
- default
patch:
a:
- patch
{{ default | combine(patch) }}
결과는:
a:
- patch
list_merge='keep'이면 왼쪽 해시의 배열이 유지돼요:
{{ default | combine(patch, list_merge='keep') }}
결과는:
a:
- default
list_merge='append'이면 오른쪽 해시의 배열이 왼쪽 해시의 배열에 추가돼요:
{{ default | combine(patch, list_merge='append') }}
결과는:
a:
- default
- patch
list_merge='prepend'이면 오른쪽 해시의 배열이 왼쪽 해시의 배열 앞에 붙어요:
{{ default | combine(patch, list_merge='prepend') }}
결과는:
a:
- patch
- default
list_merge='append_rp'이면 오른쪽 해시의 배열이 왼쪽 배열에 추가돼요. 왼쪽 배열에서 오른쪽 해시의 해당 배열에도 있는 요소는 제거돼요("rp"는 "remove present"를 뜻해요). 양쪽 해시에 모두 있지 않은 중복 요소는 유지돼요:
default:
a:
- 1
- 1
- 2
- 3
patch:
a:
- 3
- 4
- 5
- 5
{{ default | combine(patch, list_merge='append_rp') }}
결과는:
a:
- 1
- 1
- 2
- 3
- 4
- 5
- 5
list_merge='prepend_rp'이면 동작은 append_rp와 비슷하지만 오른쪽 해시 배열의 요소가 앞에 붙어요:
{{ default | combine(patch, list_merge='prepend_rp') }}
결과는:
a:
- 3
- 4
- 5
- 5
- 1
- 1
- 2
recursive와 list_merge를 함께 사용할 수 있어요:
default:
a:
a':
x: default_value
y: default_value
list:
- default_value
b:
- 1
- 1
- 2
- 3
patch:
a:
a':
y: patch_value
z: patch_value
list:
- patch_value
b:
- 3
- 4
- 4
- key: value
{{ default | combine(patch, recursive=True, list_merge='append_rp') }}
결과는:
a:
a':
x: default_value
y: patch_value
z: patch_value
list:
- default_value
- patch_value
b:
- 1
- 1
- 2
- 3
- 4
- 4
- key: value
배열 또는 해시 테이블에서 값 선택하기
(버전 2.1에 추가됨)
extract 필터는 인덱스 목록을 컨테이너(해시 또는 배열)의 값 목록에 매핑하는 데 사용돼요:
{{ [0,2] | map('extract', ['x','y','z']) | list }}
{{ ['x','y'] | map('extract', {'x': 42, 'y': 31}) | list }}
위 표현식의 결과는:
['x', 'z']
[42, 31]
필터는 또 다른 인자를 받을 수 있어요:
{{ groups['x'] | map('extract', hostvars, 'ec2_ip_address') | list }}
이것은 그룹 'x'의 호스트 목록을 가져와서 hostvars에서 찾고, 그 결과의 ec2_ip_address를 조회해요. 최종 결과는 그룹 'x'의 호스트에 대한 IP 주소 목록이에요.
필터의 세 번째 인자는 컨테이너 안에서의 재귀 조회를 위한 리스트일 수도 있어요:
{{ ['a'] | map('extract', b, ['x','y']) | list }}
이것은 b['a']['x']['y']의 값을 포함한 리스트를 반환해요.
리스트 결합하기
이 필터 집합은 결합된 리스트의 리스트를 반환해요.
permutations
리스트의 순열(permutations)을 얻으려면:
- name: Give me the largest permutations (order matters)
ansible.builtin.debug:
msg: "{{ [1,2,3,4,5] | ansible.builtin.permutations | list }}"
# => # => [(1, 2, 3, 4, 5), (1, 2, 3, 5, 4), (1, 2, 4, 3, 5), (1, 2, 4, 5, 3), (1, 2, 5, 3, 4), (1, 2, 5, 4, 3), (1, 3, 2, 4, 5), (1, 3, 2, 5, 4), (1, 3, 4, 2, 5), (1, 3, 4, 5, 2), (1, 3, 5, 2, 4), (1, 3, 5, 4, 2), (1, 4, 2, 3, 5), (1, 4, 2, 5, 3), (1, 4, 3, 2, 5), (1, 4, 3, 5, 2), (1, 4, 5, 2, 3), (1, 4, 5, 3, 2), (1, 5, 2, 3, 4), (1, 5, 2, 4, 3), (1, 5, 3, 2, 4), (1, 5, 3, 4, 2), (1, 5, 4, 2, 3), (1, 5, 4, 3, 2), (2, 1, 3, 4, 5), (2, 1, 3, 5, 4), (2, 1, 4, 3, 5), (2, 1, 4, 5, 3), (2, 1, 5, 3, 4), (2, 1, 5, 4, 3), (2, 3, 1, 4, 5), (2, 3, 1, 5, 4), (2, 3, 4, 1, 5), (2, 3, 4, 5, 1), (2, 3, 5, 1, 4), (2, 3, 5, 4, 1), (2, 4, 1, 3, 5), (2, 4, 1, 5, 3), (2, 4, 3, 1, 5), (2, 4, 3, 5, 1), (2, 4, 5, 1, 3), (2, 4, 5, 3, 1), (2, 5, 1, 3, 4), (2, 5, 1, 4, 3), (2, 5, 3, 1, 4), (2, 5, 3, 4, 1), (2, 5, 4, 1, 3), (2, 5, 4, 3, 1), (3, 1, 2, 4, 5), (3, 1, 2, 5, 4), (3, 1, 4, 2, 5), (3, 1, 4, 5, 2), (3, 1, 5, 2, 4), (3, 1, 5, 4, 2), (3, 2, 1, 4, 5), (3, 2, 1, 5, 4), (3, 2, 4, 1, 5), (3, 2, 4, 5, 1), (3, 2, 5, 1, 4), (3, 2, 5, 4, 1), (3, 4, 1, 2, 5), (3, 4, 1, 5, 2), (3, 4, 2, 1, 5), (3, 4, 2, 5, 1), (3, 4, 5, 1, 2), (3, 4, 5, 2, 1), (3, 5, 1, 2, 4), (3, 5, 1, 4, 2), (3, 5, 2, 1, 4), (3, 5, 2, 4, 1), (3, 5, 4, 1, 2), (3, 5, 4, 2, 1), (4, 1, 2, 3, 5), (4, 1, 2, 5, 3), (4, 1, 3, 2, 5), (4, 1, 3, 5, 2), (4, 1, 5, 2, 3), (4, 1, 5, 3, 2), (4, 2, 1, 3, 5), (4, 2, 1, 5, 3), (4, 2, 3, 1, 5), (4, 2, 3, 5, 1), (4, 2, 5, 1, 3), (4, 2, 5, 3, 1), (4, 3, 1, 2, 5), (4, 3, 1, 5, 2), (4, 3, 2, 1, 5), (4, 3, 2, 5, 1), (4, 3, 5, 1, 2), (4, 3, 5, 2, 1), (4, 5, 1, 2, 3), (4, 5, 1, 3, 2), (4, 5, 2, 1, 3), (4, 5, 2, 3, 1), (4, 5, 3, 1, 2), (4, 5, 3, 2, 1), (5, 1, 2, 3, 4), (5, 1, 2, 4, 3), (5, 1, 3, 2, 4), (5, 1, 3, 4, 2), (5, 1, 4, 2, 3), (5, 1, 4, 3, 2), (5, 2, 1, 3, 4), (5, 2, 1, 4, 3), (5, 2, 3, 1, 4), (5, 2, 3, 4, 1), (5, 2, 4, 1, 3), (5, 2, 4, 3, 1), (5, 3, 1, 2, 4), (5, 3, 1, 4, 2), (5, 3, 2, 1, 4), (5, 3, 2, 4, 1), (5, 3, 4, 1, 2), (5, 3, 4, 2, 1), (5, 4, 1, 2, 3), (5, 4, 1, 3, 2), (5, 4, 2, 1, 3), (5, 4, 2, 3, 1), (5, 4, 3, 1, 2), (5, 4, 3, 2, 1)]
- name: Give me permutations of sets of three
ansible.builtin.debug:
msg: "{{ [1,2,3,4,5] | ansible.builtin.permutations(3) | list }}"
# => [(1, 2, 3), (1, 2, 4), (1, 2, 5), (1, 3, 2), (1, 3, 4), (1, 3, 5), (1, 4, 2), (1, 4, 3), (1, 4, 5), (1, 5, 2), (1, 5, 3), (1, 5, 4), (2, 1, 3), (2, 1, 4), (2, 1, 5), (2, 3, 1), (2, 3, 4), (2, 3, 5), (2, 4, 1), (2, 4, 3), (2, 4, 5), (2, 5, 1), (2, 5, 3), (2, 5, 4), (3, 1, 2), (3, 1, 4), (3, 1, 5), (3, 2, 1), (3, 2, 4), (3, 2, 5), (3, 4, 1), (3, 4, 2), (3, 4, 5), (3, 5, 1), (3, 5, 2), (3, 5, 4), (4, 1, 2), (4, 1, 3), (4, 1, 5), (4, 2, 1), (4, 2, 3), (4, 2, 5), (4, 3, 1), (4, 3, 2), (4, 3, 5), (4, 5, 1), (4, 5, 2), (4, 5, 3), (5, 1, 2), (5, 1, 3), (5, 1, 4), (5, 2, 1), (5, 2, 3), (5, 2, 4), (5, 3, 1), (5, 3, 2), (5, 3, 4), (5, 4, 1), (5, 4, 2), (5, 4, 3)]
combinations
Combinations(조합)은 항상 집합 크기가 필요해요:
- name: Give me combinations for sets of two
ansible.builtin.debug:
msg: "{{ [1,2,3,4,5] | ansible.builtin.combinations(2) | list }}"
# => [(1, 2), (1, 3), (1, 4), (1, 5), (2, 3), (2, 4), (2, 5), (3, 4), (3, 5), (4, 5)]
zip과 zip_longest 필터도 참고하세요.
products
product 필터는 입력 iterable들의 데카르트 곱(cartesian product)을 반환해요. 이는 제너레이터 표현식에서의 중첩 for 루프와 대략 동등해요.
{{ ['foo', 'bar'] | product(['com']) }}
# => [('foo', 'com'), ('bar', 'com')]
예를 들어 여러 호스트 이름을 생성해요:
{{ ['foo', 'bar'] | product(['com']) | map('join', '.') }}
# => ['foo.com', 'bar.com']
JSON 데이터 선택하기: JSON 쿼리
JSON 형식의 복잡한 데이터 구조(예: Ansible 팩트)에서 단일 요소나 데이터 하위 집합을 선택하려면 community.general.json_query 필터를 사용하세요. community.general.json_query 필터는 복잡한 JSON 구조를 쿼리하고 루프 구조로 반복할 수 있게 해 줘요.
참고: 이 필터는 community.general 컬렉션으로 이동했어요. 설치 지침을 따라 그 컬렉션을 설치하세요.
참고: 이 필터를 사용하기 전에 Ansible 제어 노드에 jmespath 의존성을 수동으로 설치해야 해요. 이 필터는 jmespath를 기반으로 하며 같은 문법을 사용할 수 있어요. 예시는 jmespath 예시를 참고하세요.
이 데이터 구조를 고려해 볼게요:
{
"domain": {
"cluster": [
{
"name": "cluster1"
},
{
"name": "cluster2"
}
],
"server": [
{
"name": "server11",
"cluster": "cluster1",
"port": "8080"
},
{
"name": "server12",
"cluster": "cluster1",
"port": "8090"
},
{
"name": "server21",
"cluster": "cluster2",
"port": "9080"
},
{
"name": "server22",
"cluster": "cluster2",
"port": "9090"
}
],
"library": [
{
"name": "lib1",
"target": "cluster1"
},
{
"name": "lib2",
"target": "cluster2"
}
]
}
}
이 구조에서 모든 클러스터를 추출하려면 다음 쿼리를 사용할 수 있어요:
- name: Display all cluster names
ansible.builtin.debug:
var: item
loop: "{{ domain_definition | community.general.json_query('domain.cluster[*].name') }}"
모든 서버 이름을 추출하려면:
- name: Display all server names
ansible.builtin.debug:
var: item
loop: "{{ domain_definition | community.general.json_query('domain.server[*].name') }}"
cluster1에서 포트를 추출하려면:
- name: Display all ports from cluster1
ansible.builtin.debug:
var: item
loop: "{{ domain_definition | community.general.json_query(server_name_cluster1_query) }}"
vars:
server_name_cluster1_query: "domain.server[?cluster=='cluster1'].port"
참고: 변수를 사용해서 쿼리를 더 읽기 쉽게 만들 수 있어요.
cluster1의 포트를 쉼표로 구분된 문자열로 출력하려면:
- name: Display all ports from cluster1 as a string
ansible.builtin.debug:
msg: "{{ domain_definition | community.general.json_query('domain.server[?cluster==`cluster1`].port') | join(', ') }}"
참고: 위 예시에서 백틱으로 리터럴을 인용하면 따옴표 이스케이프를 피하고 가독성을 유지해요.
YAML 작은따옴표 이스케이프를 사용할 수 있어요:
- name: Display all ports from cluster1
ansible.builtin.debug:
var: item
loop: "{{ domain_definition | community.general.json_query('domain.server[?cluster==''cluster1''].port') }}"
참고: YAML에서 작은따옴표 안에서 작은따옴표를 이스케이프하는 것은 작은따옴표를 두 번 쓰는 것입니다.
클러스터의 모든 포트와 이름을 가진 해시 맵을 얻으려면:
- name: Display all server ports and names from cluster1
ansible.builtin.debug:
var: item
loop: "{{ domain_definition | community.general.json_query(server_name_cluster1_query) }}"
vars:
server_name_cluster1_query: "domain.server[?cluster=='cluster1'].{name: name, port: port}"
이름이 'server1'로 시작하는 모든 클러스터의 포트를 추출하려면:
- name: Display ports from all clusters with the name starting with 'server1'
ansible.builtin.debug:
msg: "{{ domain_definition | to_json | from_json | community.general.json_query(server_name_query) }}"
vars:
server_name_query: "domain.server[?starts_with(name,'server1')].port"
이름에 'server1'이 포함된 모든 클러스터의 포트를 추출하려면:
- name: Display ports from all clusters with the name containing 'server1'
ansible.builtin.debug:
msg: "{{ domain_definition | to_json | from_json | community.general.json_query(server_name_query) }}"
vars:
server_name_query: "domain.server[?contains(name,'server1')].port"
참고:
starts_with와contains를 사용할 때는 데이터 구조를 올바르게 파싱하기 위해to_json | from_json필터를 사용해야 해요.
데이터 무작위화하기
무작위로 생성된 값이 필요할 때는 다음 필터 중 하나를 사용하세요.
무작위 MAC 주소
(버전 2.6에 추가됨)
이 필터는 문자열 접두사에서 무작위 MAC 주소를 생성하는 데 사용할 수 있어요.
참고: 이 필터는 community.general 컬렉션으로 이동했어요. 설치 지침을 따라 그 컬렉션을 설치하세요.
'52:54:00'으로 시작하는 문자열 접두사에서 무작위 MAC 주소를 얻으려면:
"{{ '52:54:00' | community.general.random_mac }}"
# => '52:54:00:ef:1c:03'
접두사 문자열에 문제가 있으면 필터가 오류를 발생시킨다는 점을 기억하세요.
(버전 2.9에 추가됨)
Ansible 버전 2.9부터 시드(seed)로 난수 생성기를 초기화해서 무작위이지만 멱등적인(idempotent) MAC 주소를 만들 수도 있어요:
"{{ '52:54:00' | community.general.random_mac(seed=inventory_hostname) }}"
무작위 항목 또는 숫자
Ansible의 ansible.builtin.random 필터는 기본 Jinja2 random 필터의 확장이며, 항목 시퀀스에서 무작위 항목을 반환하거나 범위에 기반해 무작위 숫자를 생성하는 데 사용할 수 있어요.
리스트에서 무작위 항목을 얻으려면:
"{{ ['a','b','c'] | random }}"
# => 'c'
0(포함)과 지정된 정수(제외) 사이의 무작위 숫자를 얻으려면:
"{{ 60 | random }} * * * * root /script/from/cron"
# => '21 * * * * root /script/from/cron'
0부터 100까지 10 단위로 무작위 숫자를 얻으려면:
{{ 101 | random(step=10) }}
# => 70
1부터 100까지 10 단위로 무작위 숫자를 얻으려면:
{{ 101 | random(1, 10) }}
# => 31
{{ 101 | random(start=1, step=10) }}
# => 51
시드로 난수 생성기를 초기화해서 무작위이지만 멱등적인 숫자를 만들 수 있어요:
"{{ 60 | random(seed=inventory_hostname) }} * * * * root /script/from/cron"
리스트 섞기
ansible.builtin.shuffle 필터는 기존 리스트를 무작위화해서 호출마다 다른 순서를 줘요.
기존 list에서 무작위 리스트를 얻으려면:
{{ ['a','b','c'] | shuffle }}
# => ['c','a','b']
{{ ['a','b','c'] | shuffle }}
# => ['b','c','a']
시드로 셔플 생성기를 초기화해서 무작위이지만 멱등적인 순서를 생성할 수 있어요:
{{ ['a','b','c'] | shuffle(seed=inventory_hostname) }}
# => ['b','a','c']
shuffle 필터는 가능하면 항상 리스트를 반환해요. '리스트가 아닌' 항목과 함께 사용하면 필터는 아무 일도 하지 않아요.
리스트 변수 관리하기
리스트에서 최소값이나 최대값을 검색하거나, 다중 레벨 리스트를 평탄화할 수 있어요.
숫자 리스트에서 최소값을 얻으려면:
{{ [3, 4, 2] | min }}
# => 2
(버전 2.11에 추가됨)
객체 리스트에서 최소값을 얻으려면:
{{ [{'val': 1}, {'val': 2}] | min(attribute='val') }}
# => {'val': 1}
숫자 리스트에서 최대값을 얻으려면:
{{ [3, 4, 2] | max }}
# => 4
(버전 2.11에 추가됨)
객체 리스트에서 최대값을 얻으려면:
{{ [{'val': 1}, {'val': 2}] | max(attribute='val') }}
# => {'val': 2}
(버전 2.5에 추가됨)
리스트 평탄화하기 (flatten 룩업이 하는 것과 같음):
{{ [3, [4, 2] ] | flatten }}
# => [3, 4, 2]
리스트의 첫 레벨만 평탄화하기 (items 룩업과 유사):
{{ [3, [4, [2]] ] | flatten(levels=1) }}
# => [3, 4, [2]]
(버전 2.11에 추가됨)
리스트에서 null 보존하기 (flatten은 기본적으로 null을 제거해요):
{{ [3, None, [4, [2]] ] | flatten(levels=1, skip_nulls=False) }}
# => [3, None, 4, [2]]
리스트를 N번 반복한 리스트 만들기:
{{ 3*[1, 2, 3, "foo"] }}
# => [1, 2, 3, 'foo', 1, 2, 3, 'foo', 1, 2, 3, 'foo']
집합 또는 리스트에서 선택하기 (집합 이론)
집합이나 리스트에서 항목을 선택하거나 결합할 수 있어요. 참고로 다중집합(multiset)은 현재 지원되지 않으며 다음 모든 필터는 유일성(uniqueness)을 암시해요. 즉 중복 요소는 결과에서 제거돼요.
(버전 1.4에 추가됨)
리스트에서 유일한 집합을 얻으려면:
# list1: [1, 2, 5, 1, 3, 4, 10]
{{ list1 | unique }}
# => [1, 2, 5, 3, 4, 10]
두 리스트의 합집합(중복 요소 제거)을 얻으려면:
# list1: [1, 2, 5, 1, 3, 4, 10]
# list2: [1, 2, 3, 4, 5, 11, 99]
{{ list1 | union(list2) }}
# => [1, 2, 3, 4, 5, 99, 10, 11]
두 리스트의 교집합(두 리스트 모두에 존재하는 항목의 유일한 목록)을 얻으려면:
# list1: [1, 2, 5, 3, 4, 10]
# list2: [1, 2, 3, 4, 5, 11, 99]
{{ list1 | intersect(list2) }}
# => [1, 2, 3, 4, 5]
두 리스트의 차집합(첫 번째 리스트에 있고 두 번째에는 없는 항목)을 얻으려면:
# list1: [1, 2, 5, 1, 3, 4, 10]
# list2: [1, 2, 3, 4, 5, 11, 99]
{{ list1 | difference(list2) }}
# => [10]
두 리스트의 대칭 차집합(각 리스트에만 있는 항목)을 얻으려면:
# list1: [1, 2, 5, 1, 3, 4, 10]
# list2: [1, 2, 3, 4, 5, 11, 99]
{{ list1 | symmetric_difference(list2) }}
# => [99, 10, 11]
숫자 계산하기 (수학)
(버전 1.9에 추가됨)
Ansible 필터로 숫자의 로그, 거듭제곱, 제곱근을 계산할 수 있어요. Jinja2는 abs()와 round() 같은 다른 수학 함수도 제공해요.
로그 구하기 (기본은 e):
{{ 8 | log }}
# => 2.0794415416798357
밑이 10인 로그 구하기:
{{ 8 | log(10) }}
# => 0.9030899869919435
2의 거듭제곱! (또는 5):
{{ 8 | pow(5) }}
# => 32768.0
제곱근, 또는 5제곱근:
{{ 8 | root }}
# => 2.8284271247461903
{{ 8 | root(5) }}
# => 1.5157165665103982
네트워크 상호작용 관리하기
이 필터들은 일반적인 네트워크 작업을 돕는 데 사용해요.
참고: 이 필터들은 ansible.utils 컬렉션으로 이동했어요. 설치 지침을 따라 그 컬렉션을 설치하세요.
IP 주소 필터
(버전 1.9에 추가됨)
문자열이 유효한 IP 주소인지 테스트하려면:
{{ myvar | ansible.utils.ipaddr }}
특정 IP 프로토콜 버전을 요구할 수도 있어요:
{{ myvar | ansible.utils.ipv4 }}
{{ myvar | ansible.utils.ipv6 }}
IP 주소 필터는 IP 주소에서 특정 정보를 추출하는 데도 사용할 수 있어요. 예를 들어 CIDR에서 IP 주소 자체를 얻으려면:
{{ '192.0.2.1/24' | ansible.utils.ipaddr('address') }}
# => 192.0.2.1
ansible.utils.ipaddr 필터와 완전한 사용 가이드에 대한 자세한 내용은 Ansible.Utils에서 찾을 수 있어요.
네트워크 CLI 필터
(버전 2.4에 추가됨)
네트워크 장치 CLI 명령의 출력을 구조화된 JSON 출력으로 변환하려면 ansible.netcommon.parse_cli 필터를 사용하세요:
{{ output | ansible.netcommon.parse_cli('path/to/spec') }}
ansible.netcommon.parse_cli 필터는 spec 파일을 로드하고 명령 출력을 통과시켜 JSON 출력을 반환해요. YAML spec 파일은 CLI 출력을 파싱하는 방법을 정의해요.
spec 파일은 유효하게 포맷된 YAML이어야 해요. CLI 출력을 어떻게 파싱하고 JSON 데이터를 반환할지 정의해요. 아래는 show vlan 명령의 출력을 파싱하는 유효한 spec 파일 예시예요.
---
vars:
vlan:
vlan_id: "{{ item.vlan_id }}"
name: "{{ item.name }}"
enabled: "{{ item.state != 'act/lshut' }}"
state: "{{ item.state }}"
keys:
vlans:
value: "{{ vlan }}"
items: "^(?P<vlan_id>\\d+)\\s+(?P<name>\\w+)\\s+(?P<state>active|act/lshut|suspended)"
state_static:
value: present
위 spec 파일은 파싱된 VLAN 정보를 가진 해시 목록인 JSON 데이터 구조를 반환해요.
같은 명령을 key와 values 지시문을 사용해서 해시로 파싱할 수 있어요. 같은 show vlan 명령으로 출력을 해시 값으로 파싱하는 예시예요:
---
vars:
vlan:
key: "{{ item.vlan_id }}"
values:
vlan_id: "{{ item.vlan_id }}"
name: "{{ item.name }}"
enabled: "{{ item.state != 'act/lshut' }}"
state: "{{ item.state }}"
keys:
vlans:
value: "{{ vlan }}"
items: "^(?P<vlan_id>\\d+)\\s+(?P<name>\\w+)\\s+(?P<state>active|act/lshut|suspended)"
state_static:
value: present
CLI 명령을 파싱하는 또 다른 일반적인 사용 사례는 큰 명령을 파싱할 수 있는 블록으로 나누는 것이에요. start_block과 end_block 지시문으로 명령을 파싱 가능한 블록으로 나눌 수 있어요.
---
vars:
interface:
name: "{{ item[0].match[0] }}"
state: "{{ item[1].state }}"
mode: "{{ item[2].match[0] }}"
keys:
interfaces:
value: "{{ interface }}"
start_block: "^Ethernet.*$"
end_block: "^$"
items:
- "^(?P<name>Ethernet\\d\\/\\d*)"
- "admin state is (?P<state>.+),"
- "Port mode is (.+)"
위 예시는 show interface의 출력을 해시 목록으로 파싱해요.
네트워크 필터는 TextFSM 라이브러리를 사용한 CLI 명령 출력 파싱도 지원해요. TextFSM으로 CLI 출력을 파싱하려면 다음 필터를 사용하세요:
{{ output.stdout[0] | ansible.netcommon.parse_cli_textfsm('path/to/fsm') }}
TextFSM 필터를 사용하려면 TextFSM 라이브러리가 설치되어 있어야 해요.
네트워크 XML 필터
(버전 2.5에 추가됨)
네트워크 장치 명령의 XML 출력을 구조화된 JSON 출력으로 변환하려면 ansible.netcommon.parse_xml 필터를 사용하세요:
{{ output | ansible.netcommon.parse_xml('path/to/spec') }}
ansible.netcommon.parse_xml 필터는 spec 파일을 로드하고 명령 출력을 JSON으로 포맷해서 전달해요.
spec 파일은 유효하게 포맷된 YAML이어야 해요. XML 출력을 어떻게 파싱하고 JSON 데이터를 반환할지 정의해요.
아래는 show vlan | display xml 명령의 출력을 파싱하는 유효한 spec 파일 예시예요.
---
vars:
vlan:
vlan_id: "{{ item.vlan_id }}"
name: "{{ item.name }}"
desc: "{{ item.desc }}"
enabled: "{{ item.state.get('inactive') != 'inactive' }}"
state: "{% if item.state.get('inactive') == 'inactive'%} inactive {% else %} active {% endif %}"
keys:
vlans:
value: "{{ vlan }}"
top: configuration/vlans/vlan
items:
vlan_id: vlan-id
name: name
desc: description
state: ".[@inactive='inactive']"
위 spec 파일은 파싱된 VLAN 정보를 가진 해시 목록인 JSON 데이터 구조를 반환해요.
같은 명령을 key와 values 지시문을 사용해서 해시로 파싱할 수 있어요. 같은 show vlan | display xml 명령으로 출력을 해시 값으로 파싱하는 예시예요:
---
vars:
vlan:
key: "{{ item.vlan_id }}"
values:
vlan_id: "{{ item.vlan_id }}"
name: "{{ item.name }}"
desc: "{{ item.desc }}"
enabled: "{{ item.state.get('inactive') != 'inactive' }}"
state: "{% if item.state.get('inactive') == 'inactive'%} inactive {% else %} active {% endif %}"
keys:
vlans:
value: "{{ vlan }}"
top: configuration/vlans/vlan
items:
vlan_id: vlan-id
name: name
desc: description
state: ".[@inactive='inactive']"
top의 값은 XML 루트 노드에 상대적인 XPath예요. 아래에 주어진 XML 출력 예시에서 top의 값은 configuration/vlans/vlan인데, 이는 루트 노드(<rpc-reply>)에 상대적인 XPath 표현식이에요. top 값의 configuration은 가장 바깥쪽 컨테이너 노드이고, vlan은 가장 안쪽 컨테이너 노드예요.
items는 사용자 정의 이름을 요소를 선택하는 XPath 표현식에 매핑하는 키-값 쌍의 딕셔너리예요. XPath 표현식은 top에 담긴 XPath 값에 상대적이에요. 예를 들어 spec 파일의 vlan_id는 사용자 정의 이름이고 그 값 vlan-id는 top의 XPath 값에 상대적이에요.
XML 태그의 속성은 XPath 표현식으로 추출할 수 있어요. spec의 state 값은 출력 XML에서 vlan 태그의 속성을 얻는 데 사용되는 XPath 표현식이에요:
<rpc-reply>
<configuration>
<vlans>
<vlan inactive="inactive">
<name>vlan-1</name>
<vlan-id>200</vlan-id>
<description>This is vlan-1</description>
</vlan>
</vlans>
</configuration>
</rpc-reply>
참고: 지원되는 XPath 표현식에 대한 자세한 내용은 XPath 지원 (XPath Support)을 참고하세요.
네트워크 VLAN 필터
(버전 2.8에 추가됨)
ansible.netcommon.vlan_parser 필터를 사용해서 정렬되지 않은 VLAN 정수 목록을 IOS 스타일 VLAN 목록 규칙에 따라 정렬된 정수 문자열 목록으로 변환하세요. 이 목록은 다음 속성을 가져요:
- VLAN은 오름차순으로 나열돼요.
- 세 개 이상의 연속 VLAN은 대시로 나열돼요.
- 목록의 첫 줄은 first_line_len 문자만큼 길 수 있어요.
- 이후 목록 줄은 other_line_len 문자일 수 있어요.
VLAN 목록을 정렬하려면:
{{ [3003, 3004, 3005, 100, 1688, 3002, 3999] | ansible.netcommon.vlan_parser }}
이 예시는 다음 정렬된 목록을 렌더링해요:
['100,1688,3002-3005,3999']
또 다른 Jinja 템플릿 예시:
{% set parsed_vlans = vlans | ansible.netcommon.vlan_parser %}
switchport trunk allowed vlan {{ parsed_vlans[0] }}
{% for i in range (1, parsed_vlans | count) %}
switchport trunk allowed vlan add {{ parsed_vlans[i] }}
{% endfor %}
이것은 Cisco IOS 태그 인터페이스에서 VLAN 목록을 동적으로 생성하게 해 줘요. 인터페이스에 필요한 정확한 VLAN의 전체 원시 목록을 저장한 다음, 구성에 대해 실제로 생성될 파싱된 IOS 출력과 비교할 수 있어요.
문자열과 비밀번호 해싱하고 암호화하기
(버전 1.9에 추가됨)
문자열의 sha1 해시를 얻으려면:
{{ 'test1' | hash('sha1') }}
# => "b444ac06613fc8d63795be9ad0beaf55011936ac"
문자열의 md5 해시를 얻으려면:
{{ 'test1' | hash('md5') }}
# => "5a105e8b9d40e1329780d62ea2265d8a"
문자열 체크섬 얻기:
{{ 'test2' | checksum }}
# => "109f4b3c50d7b0df729d299bc6f8e9ef9066971f"
다른 해시 (플랫폼 의존적):
{{ 'test2' | hash('blowfish') }}
sha512 비밀번호 해시(무작위 salt)를 얻으려면:
{{ 'passwordsaresecret' | password_hash('sha512') }}
# => "$6$UIv3676O/ilZzWEE$ktEfFF19NQPF2zyxqxGkAceTnbEgpEKuGBtk6MlU4v2ZorWaVQUMyurgmHCh2Fr4wpmQ/Y.AlXMJkRnIS4RfH/"
특정 salt로 sha256 비밀번호 해시를 얻으려면:
{{ 'secretpassword' | password_hash('sha256', 'mysecretsalt') }}
# => "$5$mysecretsalt$ReKNyDYjkKNqRVwouShhsEqZ3VOE8eoVO4exihOfvG4"
시스템별로 고유한 해시를 생성하는 멱등적인 방법은 실행 간에 일관된 salt를 사용하는 것이에요:
{{ 'secretpassword' | password_hash('sha512', 65534 | random(seed=inventory_hostname) | string) }}
# => "$6$43927$lQxPKz2M2X.NWO.gK.t7phLwOKQMcSq72XxDZQ0XzYV6DlL1OD72h417aj16OnHTGxNzhftXJQBcjbunLEepM0"
사용 가능한 해시 타입은 Ansible을 실행하는 제어 시스템에 달려 있어요. ansible.builtin.hash은 hashlib에, ansible.builtin.password_hash은 passlib에 의존해요. passlib이 설치되지 않았다면 crypt 모듈이 폴백으로 사용돼요.
(버전 2.7에 추가됨)
일부 해시 타입은 rounds 파라미터 제공을 허용해요:
{{ 'secretpassword' | password_hash('sha256', 'mysecretsalt', rounds=10000) }}
# => "$5$rounds=10000$mysecretsalt$Tkm80llAxD4YHll6AgNIztKn0vzAACsuuEfYeGP7tm7"
password_hash 필터는 passlib을 설치했는지 여부에 따라 다른 결과를 만들어요.
멱등성을 보장하려면 rounds를 crypt나 passlib의 기본값이 아닌 값으로 지정하세요. crypt의 경우 5000이고, passlib의 경우 가변 값(sha256은 535000, sha512는 656000)이에요:
{{ 'secretpassword' | password_hash('sha256', 'mysecretsalt', rounds=5001) }}
# => "$5$rounds=5001$mysecretsalt$wXcTWWXbfcR8er5IVf7NuquLvnUA6s8/qdtOhAZ.xN."
'blowfish'(BCrypt) 해시 타입은 BCrypt 알고리즘의 버전을 지정하는 기능을 제공해요.
{{ 'secretpassword' | password_hash('blowfish', '1234567890123456789012', ident='2b') }}
# => "$2b$12$123456789012345678901uuJ4qFdej6xnWjOQT.FStqfdoY8dYUPC"
참고: 이 파라미터는 blowfish (BCrypt)에서만 사용할 수 있어요. 다른 해시 타입은 이 파라미터를 그냥 무시해요. 이 파라미터의 유효한 값은 ['2', '2a', '2y', '2b']예요.
(버전 2.12에 추가됨)
Ansible ansible.builtin.vault 필터를 사용해서 데이터를 암호화할 수도 있어요:
# simply encrypt my key in a vault
vars:
myvaultedkey: "{{ keyrawdata|vault(passphrase) }}"
tasks:
- name: save templated vaulted data
template: src=dump_template_data.j2 dest=/some/key/vault.txt
vars:
mysalt: '{{ 2**256|random(seed=inventory_hostname) }}'
template_data: '{{ secretdata|vault(vaultsecret, salt=mysalt) }}'
그런 다음 unvault 필터로 복호화해요:
# simply decrypt my key from a vault
vars:
mykey: "{{ myvaultedkey|unvault(passphrase) }}"
tasks:
- name: save templated unvaulted data
template: src=dump_template_data.j2 dest=/some/key/clear.txt
vars:
template_data: '{{ secretdata|unvault(vaultsecret) }}'
텍스트 조작하기
여러 필터가 URL, 파일 이름, 경로 이름을 포함한 텍스트와 함께 동작해요.
파일에 주석 추가하기
ansible.builtin.comment 필터는 다양한 주석 스타일로 템플릿의 텍스트에서 파일에 주석을 만들 수 있게 해 줘요. 기본적으로 Ansible은 #로 주석 줄을 시작하고 주석 텍스트 위아래에 빈 주석 줄을 추가해요. 예를 들어 다음은:
{{ "Plain style (default)" | comment }}
이 출력을 만들어요:
#
# Plain style (default)
#
Ansible은 C(//...), C 블록(/*...*/), Erlang(%...), XML(<!--...-->) 주석 스타일을 제공해요:
{{ "C style" | comment('c') }}
{{ "C block style" | comment('cblock') }}
{{ "Erlang style" | comment('erlang') }}
{{ "XML style" | comment('xml') }}
커스텀 주석 문자를 정의할 수 있어요. 다음 필터는:
{{ "My Special Case" | comment(decoration="! ") }}
다음을 만들어요:
!
! My Special Case
!
주석 스타일을 완전히 커스터마이즈할 수 있어요:
{{ "Custom style" | comment('plain', prefix='#######\n#', postfix='#\n#######\n ###\n #') }}
그러면 다음 출력이 생성돼요:
#######
#
# Custom style
#
#######
###
#
URLEncode 변수
urlencode 필터는 UTF-8을 사용해서 URL 경로나 쿼리에서 사용하기 위해 데이터를 인용(quote)해요:
{{ 'Trollhättan' | urlencode }}
# => 'Trollh%C3%A4ttan'
URL 분할하기
(버전 2.4에 추가됨)
ansible.builtin.urlsplit 필터는 URL에서 fragment, hostname, netloc, password, path, port, query, scheme, username을 추출해요. 인자 없이 쓰면 모든 필드의 딕셔너리를 반환해요:
{{ "http://user:[email protected]:9000/dir/index.html?query=term#fragment" | urlsplit('hostname') }}
# => 'www.acme.com'
{{ "http://user:[email protected]:9000/dir/index.html?query=term#fragment" | urlsplit('netloc') }}
# => 'user:[email protected]:9000'
{{ "http://user:[email protected]:9000/dir/index.html?query=term#fragment" | urlsplit('username') }}
# => 'user'
{{ "http://user:[email protected]:9000/dir/index.html?query=term#fragment" | urlsplit('password') }}
# => 'password'
{{ "http://user:[email protected]:9000/dir/index.html?query=term#fragment" | urlsplit('path') }}
# => '/dir/index.html'
{{ "http://user:[email protected]:9000/dir/index.html?query=term#fragment" | urlsplit('port') }}
# => '9000'
{{ "http://user:[email protected]:9000/dir/index.html?query=term#fragment" | urlsplit('scheme') }}
# => 'http'
{{ "http://user:[email protected]:9000/dir/index.html?query=term#fragment" | urlsplit('query') }}
# => 'query=term'
{{ "http://user:[email protected]:9000/dir/index.html?query=term#fragment" | urlsplit('fragment') }}
# => 'fragment'
{{ "http://user:[email protected]:9000/dir/index.html?query=term#fragment" | urlsplit }}
# =>
# {
# "fragment": "fragment",
# "hostname": "www.acme.com",
# "netloc": "user:[email protected]:9000",
# "password": "password",
# "path": "/dir/index.html",
# "port": 9000,
# "query": "query=term",
# "scheme": "http",
# "username": "user"
# }
정규 표현식으로 문자열 검색하기
정규 표현식으로 문자열을 검색하거나 문자열의 일부를 추출하려면 ansible.builtin.regex_search 필터를 사용하세요:
# Extracts the database name from a string
{{ 'server1/database42' | regex_search('database[0-9]+') }}
# => 'database42'
# Example for a case insensitive search in multiline mode
{{ 'foo\nBAR' | regex_search('^bar', multiline=True, ignorecase=True) }}
# => 'BAR'
# Example for a case insensitive search in multiline mode using inline regex flags
{{ 'foo\nBAR' | regex_search('(?im)^bar') }}
# => 'BAR'
# Extracts server and database id from a string
{{ 'server1/database42' | regex_search('server([0-9]+)/database([0-9]+)', '\\1', '\\2') }}
# => ['1', '42']
# Extracts dividend and divisor from a division
{{ '21/42' | regex_search('(?P<dividend>[0-9]+)/(?P<divisor>[0-9]+)', '\\g<dividend>', '\\g<divisor>') }}
# => ['21', '42']
문자열에서 정규 표현식 일치 항목의 모든 발생을 추출하려면 ansible.builtin.regex_findall 필터를 사용하세요:
# Returns a list of all IPv4 addresses in the string
{{ 'Some DNS servers are 8.8.8.8 and 8.8.4.4' | regex_findall('\\b(?:[0-9]{1,3}\\.){3}[0-9]{1,3}\\b') }}
# => ['8.8.8.8', '8.8.4.4']
# Returns all lines that end with "ar"
{{ 'CAR\ntar\nfoo\nbar\n' | regex_findall('^.ar$', multiline=True, ignorecase=True) }}
# => ['CAR', 'tar', 'bar']
# Returns all lines that end with "ar" using inline regex flags for multiline and ignorecase
{{ 'CAR\ntar\nfoo\nbar\n' | regex_findall('(?im)^.ar$') }}
# => ['CAR', 'tar', 'bar']
정규 표현식으로 문자열의 텍스트를 바꾸려면 ansible.builtin.regex_replace 필터를 사용하세요:
# Convert "ansible" to "able"
{{ 'ansible' | regex_replace('^a.*i(.*)$', 'a\\1') }}
# => 'able'
# Convert "foobar" to "bar"
{{ 'foobar' | regex_replace('^f.*o(.*)$', '\\1') }}
# => 'bar'
# Convert "localhost:80" to "localhost, 80" using named groups
{{ 'localhost:80' | regex_replace('^(?P<host>.+):(?P<port>\\d+)$', '\\g<host>, \\g<port>') }}
# => 'localhost, 80'
# Convert "localhost:80" to "localhost"
{{ 'localhost:80' | regex_replace(':80') }}
# => 'localhost'
# Comment all lines that end with "ar"
{{ 'CAR\ntar\nfoo\nbar\n' | regex_replace('^(.ar)$', '#\\1', multiline=True, ignorecase=True) }}
# => '#CAR\n#tar\nfoo\n#bar\n'
# Comment all lines that end with "ar" using inline regex flags for multiline and ignorecase
{{ 'CAR\ntar\nfoo\nbar\n' | regex_replace('(?im)^(.ar)$', '#\\1') }}
# => '#CAR\n#tar\nfoo\n#bar\n'
참고: 전체 문자열을 일치시키고
*를 사용한다면 정규 표현식을 시작/끝 앵커로 항상 감싸야 해요. 예를 들어^(.*)$는 항상 결과 하나만 일치시키지만,(.*)는 일부 Python 버전에서 전체 문자열과 끝의 빈 문자열을 일치시켜 두 번의 치환이 일어나요:
# add "https://" prefix to each item in a list
GOOD:
{{ hosts | map('regex_replace', '^(.*)$', 'https://\\1') | list }}
{{ hosts | map('regex_replace', '(.+)', 'https://\\1') | list }}
{{ hosts | map('regex_replace', '^', 'https://') | list }}
BAD:
{{ hosts | map('regex_replace', '(.*)', 'https://\\1') | list }}
# append ':80' to each item in a list
GOOD:
{{ hosts | map('regex_replace', '^(.*)$', '\\1:80') | list }}
{{ hosts | map('regex_replace', '(.+)', '\\1:80') | list }}
{{ hosts | map('regex_replace', '$', ':80') | list }}
BAD:
{{ hosts | map('regex_replace', '(.*)', '\\1:80') | list }}
참고: ansible 2.0 이전에는 ansible.builtin.regex_replace 필터를 YAML 인자 안의 변수와 함께(더 단순한 'key=value' 인자와 달리) 사용했다면, 역참조(예:
\\1)를 2개(\\) 대신 4개의 백슬래시(\\\\)로 이스케이프해야 했어요.
(버전 2.0에 추가됨)
표준 Python 정규 표현식 안의 특수 문자를 이스케이프하려면 ansible.builtin.regex_escape 필터를 사용하세요 (기본 re_type='python' 옵션 사용):
# convert '^f.*o(.*)$' to '\^f\.\*o\(\.\*\)\$'
{{ '^f.*o(.*)$' | regex_escape() }}
(버전 2.8에 추가됨)
POSIX 기본 정규 표현식 안의 특수 문자를 이스케이프하려면 ansible.builtin.regex_escape 필터를 re_type='posix_basic' 옵션과 함께 사용하세요:
# convert '^f.*o(.*)$' to '\^f\.\*o(\.\*)\$'
{{ '^f.*o(.*)$' | regex_escape('posix_basic') }}
파일 이름과 경로 이름 관리하기
'/etc/asdf/foo.txt'에서 'foo.txt' 같은 파일 경로의 마지막 이름을 얻으려면:
{{ path | basename }}
Windows 스타일 파일 경로의 마지막 이름을 얻으려면 (버전 2.0에 추가됨):
{{ path | win_basename }}
파일 경로에서 Windows 드라이브 문자를 분리하려면 (버전 2.0에 추가됨):
{{ path | win_splitdrive }}
Windows 드라이브 문자만 얻으려면:
{{ path | win_splitdrive | first }}
드라이브 문자 없이 나머지 경로를 얻으려면:
{{ path | win_splitdrive | last }}
경로에서 디렉터리를 얻으려면:
{{ path | dirname }}
Windows 경로에서 디렉터리를 얻으려면 (버전 2.0에 추가됨):
{{ path | win_dirname }}
물결표( ~ ) 문자가 포함된 경로를 확장하려면 (버전 1.5에 추가됨):
{{ path | expanduser }}
환경 변수가 포함된 경로를 확장하려면:
{{ path | expandvars }}
참고: expandvars는 로컬 변수를 확장해요. 원격 경로에 사용하면 오류가 생길 수 있어요.
(버전 2.6에 추가됨)
링크의 실제 경로를 얻으려면 (버전 1.8에 추가됨):
{{ path | realpath }}
시작점에서 링크의 상대 경로를 얻으려면 (버전 1.7에 추가됨):
{{ path | relpath('/etc') }}
경로나 파일 이름의 루트와 확장자를 얻으려면 (버전 2.0에 추가됨):
# with path == 'nginx.conf' the return would be ('nginx', '.conf')
{{ path | splitext }}
ansible.builtin.splitext 필터는 항상 문자열 쌍을 반환해요. 개별 구성 요소는 first와 last 필터로 접근할 수 있어요:
# with path == 'nginx.conf' the return would be 'nginx'
{{ path | splitext | first }}
# with path == 'nginx.conf' the return would be '.conf'
{{ path | splitext | last }}
하나 이상의 경로 구성 요소를 결합하려면:
{{ ('/etc', path, 'subdir', file) | path_join }}
(버전 2.10에 추가됨)
문자열 조작하기
셸 사용을 위해 따옴표를 추가하려면:
- name: Run a shell command
ansible.builtin.shell: echo {{ string_value | quote }}
(문서: ansible.builtin.quote)
리스트를 문자열로 연결하려면:
{{ list | join(" ") }}
문자열을 리스트로 분할하려면:
{{ csv_string | split(",") }}
(버전 2.11에 추가됨)
Base64 인코딩된 문자열로 작업하려면:
{{ encoded | b64decode }}
{{ decoded | string | b64encode }}
(문서: ansible.builtin.b64encode)
버전 2.6부터 사용할 인코딩 타입을 정의할 수 있으며 기본값은 utf-8이에요:
{{ encoded | b64decode(encoding='utf-16-le') }}
{{ decoded | string | b64encode(encoding='utf-16-le') }}
(문서: ansible.builtin.b64decode)
참고:
string필터는 Python 2에서만 필요하며, 인코딩할 텍스트가 유니코드 문자열임을 보장해요. b64encode 앞에 그 필터가 없으면 잘못된 값이 인코딩돼요.
참고: b64decode의 반환 값은 문자열이에요. b64decode로 이진 blob을 복호화한 다음 사용하려고 하면(예: copy로 파일에 쓰기) 이진 데이터가 손상되는 것을 발견할 가능성이 높아요. base64로 인코딩된 이진 데이터를 받아 디스크에 쓰려면, shell 모듈과 함께 시스템
base64명령을 사용해서stdin파라미터로 인코딩된 데이터를 파이프하는 것이 가장 좋아요. 예:shell: cmd="base64 --decode > myfile.bin" stdin="{{ encoded }}".
(버전 2.6에 추가됨)
UUID 관리하기
네임스페이스된 UUIDv5를 만들려면:
{{ string | to_uuid(namespace='11111111-2222-3333-4444-555555555555') }}
(버전 2.10에 추가됨)
기본 Ansible 네임스페이스 '361E6D51-FAEC-444A-9079-341386DA8E2E'를 사용한 네임스페이스된 UUIDv5를 만들려면:
{{ string | to_uuid }}
(버전 1.9에 추가됨)
복잡한 변수 리스트의 각 항목에서 한 속성을 사용하려면 Jinja2 map 필터를 사용하세요:
# get a comma-separated list of the mount points (for example, "/,/mnt/stuff") on a host
{{ ansible_mounts | map(attribute='mount') | join(',') }}
날짜와 시간 다루기
문자열에서 날짜 객체를 얻으려면 to_datetime 필터를 사용하세요:
# Get the total amount of seconds between two dates. Default date format is %Y-%m-%d %H:%M:%S but you can pass your own format
{{ (("2016-08-14 20:00:12" | to_datetime) - ("2015-12-25" | to_datetime('%Y-%m-%d'))).total_seconds() }}
# Get remaining seconds after delta has been calculated. NOTE: This does NOT convert years, days, hours, and so on to seconds. For that, use total_seconds()
{{ (("2016-08-14 20:00:12" | to_datetime) - ("2016-08-14 18:00:00" | to_datetime)).seconds }}
# This expression evaluates to "12" and not "132". Delta is 2 hours, 12 seconds
# get amount of days between two dates. This returns only the number of days and discards remaining hours, minutes, and seconds
{{ (("2016-08-14 20:00:12" | to_datetime) - ("2015-12-25" | to_datetime('%Y-%m-%d'))).days }}
참고: Python 날짜 형식 문자열 작업을 위한 형식 코드의 전체 목록은 python datetime 문서를 참고하세요.
(버전 2.4에 추가됨)
문자열로 날짜를 포맷하려면(셸 date 명령처럼) "strftime" 필터를 사용하세요:
# Display year-month-day
{{ '%Y-%m-%d' | strftime }}
# => "2021-03-19"
# Display hour:min:sec
{{ '%H:%M:%S' | strftime }}
# => "21:51:04"
# Use ansible_date_time.epoch fact
{{ '%Y-%m-%d %H:%M:%S' | strftime(ansible_date_time.epoch) }}
# => "2021-03-19 21:54:09"
# Use arbitrary epoch value
{{ '%Y-%m-%d' | strftime(0) }} # => 1970-01-01
{{ '%Y-%m-%d' | strftime(1441357287) }} # => 2015-09-04
(버전 2.13에 추가됨)
strftime은 선택적 utc 인자를 받으며 기본값은 False로, 시간이 로컬 시간대임을 뜻해요:
{{ '%H:%M:%S' | strftime }} # time now in local timezone
{{ '%H:%M:%S' | strftime(utc=True) }} # time now in UTC
참고: 모든 문자열 가능성을 보려면 https://docs.python.org/3/library/time.html#time.strftime을 확인하세요.
Kubernetes 리소스 이름 얻기
참고: 이 필터들은 kubernetes.core 컬렉션으로 이동했어요. 설치 지침을 따라 그 컬렉션을 설치하세요.
"k8s_config_resource_name" 필터를 사용해서 해시를 포함한 Kubernetes ConfigMap 또는 Secret의 이름을 얻으세요:
{{ configmap_resource_definition | kubernetes.core.k8s_config_resource_name }}
그런 다음 이것을 Pod 스펙의 해시를 참조하는 데 사용할 수 있어요:
my_secret:
kind: Secret
metadata:
name: my_secret_name
deployment_resource:
kind: Deployment
spec:
template:
spec:
containers:
- envFrom:
- secretRef:
name: {{ my_secret | kubernetes.core.k8s_config_resource_name }}
(버전 2.8에 추가됨)
더 알아보기 (Learn more)
- 테스트 (Tests) — 타입 테스트로 데이터 타입 확인하기
- Jinja2 내장 필터 — 표준 필터 목록
- Python 정규 표현식 연산 — 인라인 정규 표현식 플래그 사용법