쓸 일이 있기도 해서 Ansible에서 사용할 수 있는 from_toml, to_toml Jinja2 필터를 만들어 본다. 또 나는 파이썬을 잘 모르지만 필터를 작성하는데엔 무리가 없었다. 다만 같은 이유로 이 글에서 파이썬과 관련한 자세한 설명은 생략한다.
준비
빈 경로에 다음과 같은 파일을 써준다:
❯ tree ..├── localhost└── var.yaml0 directories, 2 files❯ tail -n 10 *==> localhost <==[local]localhost==> var.yaml <==var: a: b: c: string d: 1
Shell
복사
•
localhost: Ansible ad-hoc 명령을 로컬에 하기 위한 호스트 INI 파일
•
var.yaml: to_toml의 입력으로 사용할 변수
변수는 최소한의 검증하고 싶은 부분만 커버하도록 만들었다: - 중첩 키에 대해 동작하는지 - 값 타입 - 정수(숫자) - 문자열
필터 작성
Ansible이 파이썬 필터를 로드하는 기본 위치는 다음 명령에서 확인할 수 있다:
❯ ansible-config list | grep filter_plugins -B7 -A3DEFAULT_FILTER_PLUGIN_PATH: default: ~/.ansible/plugins/filter:/usr/share/ansible/plugins/filter description: Colon separated paths in which Ansible will search for Jinja2 Filter Plugins. env: - name: ANSIBLE_FILTER_PLUGINS ini: - key: filter_plugins section: defaults name: Jinja2 Filter Plugins Path type: pathspec
Shell
복사
이 실습에선 default 경로에 파일을 만들지 않고 별도 경로에 만든 후 설정(ANSIBLE_FILTER_PLUGINS)하여 로드한다:
❯ mkdir filters
Shell
복사
# filters/toml.pyimport tomldef from_toml(toml_string): return toml.loads(toml_string)def to_toml(yaml_variable): return toml.dumps(dict(yaml_variable))class FilterModule(object): def filters(self): return { 'from_toml': from_toml, 'to_toml': to_toml }
Python
복사
먼저 FilterModule 클래스를 선언한 것은 Ansible lib/ansible/plugins/filter/core.py와 인터넷에서 찾아볼 수 있는 필터 플러그인 예제를 참고했다(문서에서 상세히 설명하지 않는다). filters() 메소드가 overwrite 되지 않는 모양인데 이것이 파이썬 특징인지, Ansible 필터 플러그인의 구현이 그런지는 확인하지 않았다.
새로 정의한 from_toml, to_toml 필터 동작은 파이썬 패키지 toml에 의존한다. 엣지 케이스(?)에 대한 처리는 하지 않고 변수명에서 from_toml TOML 문자열 입력만 받을 것을, to_toml은 YAML 변수 입력만 받을 것을 암시한다. 정의한 메소드를 같은 이름의 키의 사전으로 FilterModule.filters()에서 반환한다.
to_toml
❯ ANSIBLE_FILTER_PLUGINS=filters ansible local -i localhost \ -m debug \ -a "msg={{ var | to_toml }}" \ -e "@var.yaml"localhost | SUCCESS => { "msg": "[a]\nd = 2\n\n[a.b]\nc = [ \"s\", \"t\", \"r\", \"i\", \"n\", \"g\",]\n"}~/tmp/toml on ☁️ (ap-northeast-2) on ☁️ flavono123@gmail.com❯ echo -e "[a]\nd = 2\n\n[a.b]\nc = [ \"s\", \"t\", \"r\", \"i\", \"n\", \"g\",]\n"[a]d = 2[a.b]c = [ "s", "t", "r", "i", "n", "g",]
Shell
복사
Ansible에서 YAML 문자열 객체를 <class 'ansible.parsing.YAML.objects.AnsibleUnicode'>로 다루어 일어나는 문제라고 한다. 이 문제는 YAML 문자열을 홑 또는 쌍따옴표로 감싸도 발생한다. TOML은 문자열을 쌍따옴표(escaped) 또는 홑따옴표(literal)로 감싸는데 반해 YAML은 그러지 않아도 동작하니 파이썬에서 다루는 것이 다를 것이라 생각했다. 역시 이 문제에 대해 파이썬 코드 레벨까지 보진 않았다. 아무튼 Ansible YAML 문자열 객체를 별다른 처리하지 않으면 파이썬 TOML에선 각 문자를 원소로 하는 배열로 반환해버린다.
이 문제를 해결하기 위해, 위 블로그 글에서 소개하는 트릭을 사용했다. YAML변수를 JSON 객체로 덤프하여 Ansible YAML 문자열 객체를 파이썬 문자열로 바꾼 후 다시 JSON -> TOML로 로드/덤프하는 것이다:
import jsondef to_toml(yaml_variable): j = json.dumps(dict(yaml_variable)) d = json.loads(j) return toml.dumps(d)
Python
복사
filters/toml.py를 수정 후 다시 테스트하면 문자열에 대해 잘 동작한다:
❯ ANSIBLE_FILTER_PLUGINS=filters ansible local -i localhost \ -m debug \ -a "msg={{ var | to_toml }}" \ -e "@var.yaml"localhost | SUCCESS => { "msg": "[a]\nd = 2\n\n[a.b]\nc = \"string\"\n"}
Shell
복사
from_toml
위 to_toml 테스트의 결과를 다시 입력 변수로 from_toml은 간단하게 테스트 할 수 있다. 변수 전체를 escaped한 쌍따옴표로 감싸는 것에 주의한다:
❯ ANSIBLE_FILTER_PLUGINS=filters ansible local -i localhost \ -m debug \ -a "msg={{ var | from_toml }}" \ -e "var=\"[a]\nd = 2\n\n[a.b]\nc = \"string\"\n\""localhost | SUCCESS => { "msg": { "a": { "b": { "c": "string" }, "d": 2 } }}
Shell
복사
정리
필터 자체는 아주 간단하지만, Ansible 필터 플러그인을 만들기 위해 필요한 설정과 파일 형식을 알 수 있었다: - 필터 파일 설정 경로 - 환경변수: ANSIBLE_FILTER_PLUGINS - ansible.cfg(INI): [defaults] 섹션, filter_plugins 키 - 기본 값: ~/.ansible/plugins/filter:/usr/share/ansible/plugins/filter - 필터 파이썬 파일 포맷 - 필터로 동작할 메소드 정의 - 필터 이름 키와 정의한 메소드 값 쌍의 사전을 FilterModule.filters()에서 반환
참고
•
https://www.iops.tech/blog/generate-toml-using-ansible-template/
•
https://toml.io/en/