├── tests
├── __init__.py
├── files
│ ├── encode_erlang_atom.in
│ ├── encode_yaml_null.in
│ ├── encode_yaml_null.out
│ ├── encode_erlang_atom.out
│ ├── encode_ini_null.out
│ ├── encode_yaml_block.out
│ ├── encode_json_string.in
│ ├── encode_lua_string.in
│ ├── encode_toml_string.in
│ ├── encode_yaml_string.in
│ ├── encode_yaml_string.out
│ ├── encode_yaml_string_quote.out
│ ├── encode_lua_string.out
│ ├── encode_toml_string.out
│ ├── encode_yaml_block.in
│ ├── encode_toml_array.out
│ ├── encode_toml_string_quote.out
│ ├── encode_apache_boolean.out
│ ├── encode_apache_number.out
│ ├── encode_json_string.out
│ ├── encode_erlang_boolean.in
│ ├── encode_erlang_number.in
│ ├── encode_ini_general.out
│ ├── encode_apache_boolean_convert.out
│ ├── encode_apache_number_convert.out
│ ├── encode_apache_number_quote.out
│ ├── encode_xml_element.in
│ ├── encode_ini_general.in
│ ├── encode_toml_array.in
│ ├── encode_erlang_boolean.out
│ ├── encode_erlang_number.out
│ ├── encode_xml_attribute.out
│ ├── encode_erlang_boolean_convert.out
│ ├── encode_erlang_number_convert.out
│ ├── encode_ini_null.in
│ ├── encode_ini_section.out
│ ├── encode_xml_attribute.in
│ ├── encode_xml_element.out
│ ├── encode_erlang_string.in
│ ├── encode_ini_mixed.out
│ ├── encode_ini_section.in
│ ├── encode_apache_string.out
│ ├── encode_ini_mixed_comment.out
│ ├── encode_ini_mixed_ucase.out
│ ├── encode_apache_boolean.in
│ ├── encode_apache_string_quote.out
│ ├── encode_ini_mixed.in
│ ├── encode_ini_mixed_indent.out
│ ├── encode_json_number.in
│ ├── encode_lua_number.in
│ ├── encode_toml_number.in
│ ├── encode_yaml_number.in
│ ├── encode_apache_number.in
│ ├── encode_ini_mixed_quote.out
│ ├── encode_yaml_number.out
│ ├── encode_yaml_number_convert.out
│ ├── encode_ini_mixed_delimiter.out
│ ├── encode_toml_number.out
│ ├── encode_toml_number_convert.out
│ ├── encode_erlang_string.out
│ ├── encode_lua_number.out
│ ├── encode_lua_number_convert.out
│ ├── encode_lua_list.in
│ ├── encode_json_list.in
│ ├── encode_toml_table.out
│ ├── encode_yaml_list.in
│ ├── encode_yaml_list.out
│ ├── encode_json_dict.in
│ ├── encode_lua_dict.in
│ ├── encode_yaml_dict.in
│ ├── encode_apache_string.in
│ ├── encode_json_number.out
│ ├── encode_json_number_convert.out
│ ├── encode_json_boolean.in
│ ├── encode_lua_boolean.in
│ ├── encode_toml_boolean.in
│ ├── encode_toml_table.in
│ ├── encode_yaml_boolean.in
│ ├── encode_yaml_boolean.out
│ ├── encode_yaml_boolean_convert.out
│ ├── encode_yaml_dict.out
│ ├── encode_toml_boolean.out
│ ├── encode_toml_boolean_convert.out
│ ├── encode_lua_boolean.out
│ ├── encode_lua_boolean_convert.out
│ ├── encode_yaml_list_indent.out
│ ├── encode_apache_vhost.out
│ ├── encode_json_boolean.out
│ ├── encode_json_boolean_convert.out
│ ├── encode_json_dict.out
│ ├── encode_lua_list_indent.out
│ ├── encode_lua_dict.out
│ ├── encode_json_list.out
│ ├── encode_toml_table_array.in
│ ├── encode_lua_list.out
│ ├── encode_apache_vhost.in
│ ├── encode_erlang_mixed.in
│ ├── encode_toml_table_array.out
│ ├── encode_json_list_indent.out
│ ├── encode_erlang_mixed.out
│ ├── encode_toml_table_grafana.in
│ └── encode_toml_table_grafana.out
├── test_config_encoders.include
├── test_config_encoders.yaml
└── test_config_encoders.py
├── filter_plugins
├── __init__.py
└── config_encoders.py
├── .gitignore
├── test-requirements.txt
├── .travis.yml
├── tox.ini
├── meta
└── main.yaml
├── LICENSE
└── README.rst
/tests/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/filter_plugins/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .tox
2 | *.pyc
3 | *.retry
4 |
--------------------------------------------------------------------------------
/test-requirements.txt:
--------------------------------------------------------------------------------
1 | flake8==3.9.2
2 |
--------------------------------------------------------------------------------
/tests/files/encode_erlang_atom.in:
--------------------------------------------------------------------------------
1 | - aaa: :bbb
2 |
--------------------------------------------------------------------------------
/tests/files/encode_yaml_null.in:
--------------------------------------------------------------------------------
1 | var1:
2 | var2: null
3 |
--------------------------------------------------------------------------------
/tests/files/encode_yaml_null.out:
--------------------------------------------------------------------------------
1 | var1: null
2 | var2: null
3 |
--------------------------------------------------------------------------------
/tests/files/encode_erlang_atom.out:
--------------------------------------------------------------------------------
1 | [
2 | {aaa, bbb}
3 | ].
4 |
--------------------------------------------------------------------------------
/tests/files/encode_ini_null.out:
--------------------------------------------------------------------------------
1 | var3
2 |
3 | [section1]
4 | var3
5 |
--------------------------------------------------------------------------------
/tests/files/encode_yaml_block.out:
--------------------------------------------------------------------------------
1 | zzz: |-
2 | aaa = bbb
3 | ccc = ddd
4 |
--------------------------------------------------------------------------------
/tests/files/encode_json_string.in:
--------------------------------------------------------------------------------
1 | var1: my string
2 | var2: "my' \"string\""
3 |
--------------------------------------------------------------------------------
/tests/files/encode_lua_string.in:
--------------------------------------------------------------------------------
1 | var1: my string
2 | var2: "my' \"string\""
3 |
--------------------------------------------------------------------------------
/tests/files/encode_toml_string.in:
--------------------------------------------------------------------------------
1 | var1: my string
2 | var2: "my' \"string\""
3 |
--------------------------------------------------------------------------------
/tests/files/encode_yaml_string.in:
--------------------------------------------------------------------------------
1 | var1: my string
2 | var2: "my' \"string\""
3 |
--------------------------------------------------------------------------------
/tests/files/encode_yaml_string.out:
--------------------------------------------------------------------------------
1 | var1: "my string"
2 | var2: "my' \"string\""
3 |
--------------------------------------------------------------------------------
/tests/files/encode_yaml_string_quote.out:
--------------------------------------------------------------------------------
1 | var1: my string
2 | var2: my' "string"
3 |
--------------------------------------------------------------------------------
/tests/files/encode_lua_string.out:
--------------------------------------------------------------------------------
1 | var1 = "my string";
2 | var2 = "my' \"string\"";
3 |
--------------------------------------------------------------------------------
/tests/files/encode_toml_string.out:
--------------------------------------------------------------------------------
1 | var1 = "my string"
2 | var2 = "my' \"string\""
3 |
--------------------------------------------------------------------------------
/tests/files/encode_yaml_block.in:
--------------------------------------------------------------------------------
1 | zzz: |-
2 | ;;;|-
3 | aaa = bbb
4 | ccc = ddd
5 |
--------------------------------------------------------------------------------
/tests/files/encode_toml_array.out:
--------------------------------------------------------------------------------
1 | arr1 = ["some text", [123, 987.654], true, "false"]
2 |
--------------------------------------------------------------------------------
/tests/files/encode_toml_string_quote.out:
--------------------------------------------------------------------------------
1 | var1 = 'my string'
2 | var2 = 'my\' "string"'
3 |
--------------------------------------------------------------------------------
/tests/files/encode_apache_boolean.out:
--------------------------------------------------------------------------------
1 | option1 true
2 | option2 false
3 | option3 true
4 | option4 False
5 |
--------------------------------------------------------------------------------
/tests/files/encode_apache_number.out:
--------------------------------------------------------------------------------
1 | option1 123
2 | option2 987.654
3 | option3 123
4 | option4 987.654
5 |
--------------------------------------------------------------------------------
/tests/files/encode_json_string.out:
--------------------------------------------------------------------------------
1 | {
2 | "var1": "my string",
3 | "var2": "my' \"string\""
4 | }
5 |
--------------------------------------------------------------------------------
/tests/files/encode_erlang_boolean.in:
--------------------------------------------------------------------------------
1 | - var1: yes
2 | - var2: false
3 | - var3: "True"
4 | - var4: "false"
5 |
--------------------------------------------------------------------------------
/tests/files/encode_erlang_number.in:
--------------------------------------------------------------------------------
1 | - var1: 123
2 | - var2: 987.654
3 | - var3: "123"
4 | - var4: "987.654"
5 |
--------------------------------------------------------------------------------
/tests/files/encode_ini_general.out:
--------------------------------------------------------------------------------
1 | var1=some text
2 | var2=123
3 | var3=987.654
4 | var4=True
5 | var4=false
6 |
--------------------------------------------------------------------------------
/tests/files/encode_apache_boolean_convert.out:
--------------------------------------------------------------------------------
1 | option1 true
2 | option2 false
3 | option3 true
4 | option4 false
5 |
--------------------------------------------------------------------------------
/tests/files/encode_apache_number_convert.out:
--------------------------------------------------------------------------------
1 | option1 123
2 | option2 987.654
3 | option3 123
4 | option4 987.654
5 |
--------------------------------------------------------------------------------
/tests/files/encode_apache_number_quote.out:
--------------------------------------------------------------------------------
1 | option1 "123"
2 | option2 "987.654"
3 | option3 123
4 | option4 987.654
5 |
--------------------------------------------------------------------------------
/tests/files/encode_xml_element.in:
--------------------------------------------------------------------------------
1 | element:
2 | - element1:
3 | - element2: ''
4 | - element3: some text
5 |
--------------------------------------------------------------------------------
/tests/files/encode_ini_general.in:
--------------------------------------------------------------------------------
1 | var1: some text
2 | var2: 123
3 | var3: 987.654
4 | var4:
5 | - yes
6 | - "false"
7 |
--------------------------------------------------------------------------------
/tests/files/encode_toml_array.in:
--------------------------------------------------------------------------------
1 | arr1:
2 | - some text
3 | -
4 | - 123
5 | - 987.654
6 | - yes
7 | - "false"
8 |
--------------------------------------------------------------------------------
/tests/files/encode_erlang_boolean.out:
--------------------------------------------------------------------------------
1 | [
2 | {var1, true},
3 | {var2, false},
4 | {var3, "True"},
5 | {var4, "false"}
6 | ].
7 |
--------------------------------------------------------------------------------
/tests/files/encode_erlang_number.out:
--------------------------------------------------------------------------------
1 | [
2 | {var1, 123},
3 | {var2, 987.654},
4 | {var3, "123"},
5 | {var4, "987.654"}
6 | ].
7 |
--------------------------------------------------------------------------------
/tests/files/encode_xml_attribute.out:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/tests/files/encode_erlang_boolean_convert.out:
--------------------------------------------------------------------------------
1 | [
2 | {var1, true},
3 | {var2, false},
4 | {var3, true},
5 | {var4, false}
6 | ].
7 |
--------------------------------------------------------------------------------
/tests/files/encode_erlang_number_convert.out:
--------------------------------------------------------------------------------
1 | [
2 | {var1, 123},
3 | {var2, 987.654},
4 | {var3, 123},
5 | {var4, 987.654}
6 | ].
7 |
--------------------------------------------------------------------------------
/tests/files/encode_ini_null.in:
--------------------------------------------------------------------------------
1 | var1: null
2 | var2:
3 | var3: "!!!null"
4 |
5 | section1:
6 | var1: null
7 | var2:
8 | var3: "!!!null"
9 |
--------------------------------------------------------------------------------
/tests/files/encode_ini_section.out:
--------------------------------------------------------------------------------
1 | [section1]
2 | var1=some text
3 | var2=123
4 | var3=987.654
5 |
6 | [section2]
7 | var4=True
8 | var5=false
9 |
--------------------------------------------------------------------------------
/tests/files/encode_xml_attribute.in:
--------------------------------------------------------------------------------
1 | element:
2 | - ^attr1: val1
3 | - element1:
4 | - ^attr3: val3
5 | - ^attr4: val4
6 | - ^attr2: val2
7 |
--------------------------------------------------------------------------------
/tests/files/encode_xml_element.out:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | some text
5 |
6 |
7 |
--------------------------------------------------------------------------------
/tests/files/encode_erlang_string.in:
--------------------------------------------------------------------------------
1 | - var1: some_text
2 | - var2: some text
3 | - var3: "some 'text'"
4 | - var4: 'some "text"'
5 | - var5: "some \"text\""
6 |
--------------------------------------------------------------------------------
/tests/files/encode_ini_mixed.out:
--------------------------------------------------------------------------------
1 | var1=some text
2 | var2=123
3 |
4 | [section1]
5 | var3=987.654
6 |
7 | [section2]
8 | var4=True
9 | var5=false
10 |
--------------------------------------------------------------------------------
/tests/files/encode_ini_section.in:
--------------------------------------------------------------------------------
1 | section1:
2 | var1: some text
3 | var2: 123
4 | var3: 987.654
5 |
6 | section2:
7 | var4: yes
8 | var5: "false"
9 |
--------------------------------------------------------------------------------
/tests/files/encode_apache_string.out:
--------------------------------------------------------------------------------
1 | option1 some_text
2 | option2 "some text"
3 | option3 "some 'text'"
4 | option4 "some \"text\""
5 | option5 "some \"text\""
6 |
--------------------------------------------------------------------------------
/tests/files/encode_ini_mixed_comment.out:
--------------------------------------------------------------------------------
1 | var1=some text
2 | var2=123
3 |
4 | # section1
5 | var3=987.654
6 |
7 | # section2
8 | var4=True
9 | var5=false
10 |
--------------------------------------------------------------------------------
/tests/files/encode_ini_mixed_ucase.out:
--------------------------------------------------------------------------------
1 | VAR1=some text
2 | VAR2=123
3 |
4 | [section1]
5 | VAR3=987.654
6 |
7 | [section2]
8 | VAR4=True
9 | VAR5=false
10 |
--------------------------------------------------------------------------------
/tests/files/encode_apache_boolean.in:
--------------------------------------------------------------------------------
1 | content:
2 | - options:
3 | - option1: yes
4 | - option2: no
5 | - option3: "true"
6 | - option4: "False"
7 |
--------------------------------------------------------------------------------
/tests/files/encode_apache_string_quote.out:
--------------------------------------------------------------------------------
1 | option1 "some_text"
2 | option2 "some text"
3 | option3 "some 'text'"
4 | option4 "some \"text\""
5 | option5 "some \"text\""
6 |
--------------------------------------------------------------------------------
/tests/files/encode_ini_mixed.in:
--------------------------------------------------------------------------------
1 | var1: some text
2 | var2: 123
3 |
4 | section1:
5 | var3: 987.654
6 |
7 | section2:
8 | var4: yes
9 | var5: "false"
10 |
--------------------------------------------------------------------------------
/tests/files/encode_ini_mixed_indent.out:
--------------------------------------------------------------------------------
1 | var1=some text
2 | var2=123
3 |
4 | [section1]
5 | var3=987.654
6 |
7 | [section2]
8 | var4=True
9 | var5=false
10 |
--------------------------------------------------------------------------------
/tests/files/encode_json_number.in:
--------------------------------------------------------------------------------
1 | var1: 123
2 | var2: -987
3 | var3: 123.456
4 | var4: -987.654
5 | var5: "123"
6 | var6: "-987"
7 | var7: "123.456"
8 | var8: "-987.654"
9 |
--------------------------------------------------------------------------------
/tests/files/encode_lua_number.in:
--------------------------------------------------------------------------------
1 | var1: 123
2 | var2: -987
3 | var3: 123.456
4 | var4: -987.654
5 | var5: "123"
6 | var6: "-987"
7 | var7: "123.456"
8 | var8: "-987.654"
9 |
--------------------------------------------------------------------------------
/tests/files/encode_toml_number.in:
--------------------------------------------------------------------------------
1 | var1: 123
2 | var2: -987
3 | var3: 123.456
4 | var4: -987.654
5 | var5: "123"
6 | var6: "-987"
7 | var7: "123.456"
8 | var8: "-987.654"
9 |
--------------------------------------------------------------------------------
/tests/files/encode_yaml_number.in:
--------------------------------------------------------------------------------
1 | var1: 123
2 | var2: -987
3 | var3: 123.456
4 | var4: -987.654
5 | var5: "123"
6 | var6: "-987"
7 | var7: "123.456"
8 | var8: "-987.654"
9 |
--------------------------------------------------------------------------------
/tests/files/encode_apache_number.in:
--------------------------------------------------------------------------------
1 | content:
2 | - options:
3 | - option1: 123
4 | - option2: 987.654
5 | - option3: "123"
6 | - option4: "987.654"
7 |
--------------------------------------------------------------------------------
/tests/files/encode_ini_mixed_quote.out:
--------------------------------------------------------------------------------
1 | var1="some text"
2 | var2="123"
3 |
4 | [section1]
5 | var3="987.654"
6 |
7 | [section2]
8 | var4="True"
9 | var5="false"
10 |
--------------------------------------------------------------------------------
/tests/files/encode_yaml_number.out:
--------------------------------------------------------------------------------
1 | var1: 123
2 | var2: -987
3 | var3: 123.456
4 | var4: -987.654
5 | var5: "123"
6 | var6: "-987"
7 | var7: "123.456"
8 | var8: "-987.654"
9 |
--------------------------------------------------------------------------------
/tests/files/encode_yaml_number_convert.out:
--------------------------------------------------------------------------------
1 | var1: 123
2 | var2: -987
3 | var3: 123.456
4 | var4: -987.654
5 | var5: 123
6 | var6: -987
7 | var7: 123.456
8 | var8: -987.654
9 |
--------------------------------------------------------------------------------
/tests/files/encode_ini_mixed_delimiter.out:
--------------------------------------------------------------------------------
1 | var1 = some text
2 | var2 = 123
3 |
4 | [section1]
5 | var3 = 987.654
6 |
7 | [section2]
8 | var4 = True
9 | var5 = false
10 |
--------------------------------------------------------------------------------
/tests/files/encode_toml_number.out:
--------------------------------------------------------------------------------
1 | var1 = 123
2 | var2 = -987
3 | var3 = 123.456
4 | var4 = -987.654
5 | var5 = "123"
6 | var6 = "-987"
7 | var7 = "123.456"
8 | var8 = "-987.654"
9 |
--------------------------------------------------------------------------------
/tests/files/encode_toml_number_convert.out:
--------------------------------------------------------------------------------
1 | var1 = 123
2 | var2 = -987
3 | var3 = 123.456
4 | var4 = -987.654
5 | var5 = 123
6 | var6 = -987
7 | var7 = 123.456
8 | var8 = -987.654
9 |
--------------------------------------------------------------------------------
/tests/files/encode_erlang_string.out:
--------------------------------------------------------------------------------
1 | [
2 | {var1, "some_text"},
3 | {var2, "some text"},
4 | {var3, "some 'text'"},
5 | {var4, "some \"text\""},
6 | {var5, "some \"text\""}
7 | ].
8 |
--------------------------------------------------------------------------------
/tests/files/encode_lua_number.out:
--------------------------------------------------------------------------------
1 | var1 = 123;
2 | var2 = -987;
3 | var3 = 123.456;
4 | var4 = -987.654;
5 | var5 = "123";
6 | var6 = "-987";
7 | var7 = "123.456";
8 | var8 = "-987.654";
9 |
--------------------------------------------------------------------------------
/tests/files/encode_lua_number_convert.out:
--------------------------------------------------------------------------------
1 | var1 = 123;
2 | var2 = -987;
3 | var3 = 123.456;
4 | var4 = -987.654;
5 | var5 = 123;
6 | var6 = -987;
7 | var7 = 123.456;
8 | var8 = -987.654;
9 |
--------------------------------------------------------------------------------
/tests/files/encode_lua_list.in:
--------------------------------------------------------------------------------
1 | var1:
2 | - aaa
3 | - 123
4 |
5 | var2:
6 | - aaa
7 | - bbb:
8 | ccc: ddd
9 | eee:
10 | - fff
11 | - ggg: hhh
12 | - bool: yes
13 |
--------------------------------------------------------------------------------
/tests/files/encode_json_list.in:
--------------------------------------------------------------------------------
1 | var1:
2 | - aaa
3 | - 123
4 |
5 | var2:
6 | - aaa
7 | - bbb:
8 | ccc: ddd
9 | eee:
10 | - fff
11 | - ggg: hhh
12 | - bool: yes
13 |
--------------------------------------------------------------------------------
/tests/files/encode_toml_table.out:
--------------------------------------------------------------------------------
1 | [dog."tater.man"]
2 | type = "pug"
3 |
4 | [table-1]
5 | key1 = "some string"
6 | key2 = 123
7 |
8 | [x.y]
9 | aaa = "bbb"
10 |
11 | [x.y.z.w]
12 | name = "deep table"
13 |
--------------------------------------------------------------------------------
/tests/files/encode_yaml_list.in:
--------------------------------------------------------------------------------
1 | var1:
2 | - aaa
3 | - 123
4 |
5 | var2:
6 | - aaa
7 | - bbb:
8 | ccc: ddd
9 | eee:
10 | - fff
11 | - ggg: hhh
12 | - bool: yes
13 |
--------------------------------------------------------------------------------
/tests/files/encode_yaml_list.out:
--------------------------------------------------------------------------------
1 | var1:
2 | - "aaa"
3 | - 123
4 | var2:
5 | - "aaa"
6 | - bbb:
7 | ccc: "ddd"
8 | eee:
9 | - "fff"
10 | - ggg: "hhh"
11 | - bool: true
12 |
--------------------------------------------------------------------------------
/tests/files/encode_json_dict.in:
--------------------------------------------------------------------------------
1 | var1:
2 | aaa: bbb
3 | ccc: 123
4 | ddd: yes
5 | eee:
6 | fff: ggg
7 | hhh:
8 | iii: jjj
9 | kkk: yes
10 | lll:
11 | - 987.654
12 | - mmm
13 |
--------------------------------------------------------------------------------
/tests/files/encode_lua_dict.in:
--------------------------------------------------------------------------------
1 | var1:
2 | aaa: bbb
3 | ccc: 123
4 | ddd: yes
5 | eee:
6 | fff: ggg
7 | hhh:
8 | iii: jjj
9 | kkk: yes
10 | lll:
11 | - 987.654
12 | - mmm
13 |
--------------------------------------------------------------------------------
/tests/files/encode_yaml_dict.in:
--------------------------------------------------------------------------------
1 | var1:
2 | aaa: bbb
3 | ccc: 123
4 | ddd: yes
5 | eee:
6 | fff: ggg
7 | hhh:
8 | iii: jjj
9 | kkk: yes
10 | lll:
11 | - 987.654
12 | - mmm
13 |
--------------------------------------------------------------------------------
/tests/files/encode_apache_string.in:
--------------------------------------------------------------------------------
1 | content:
2 | - options:
3 | - option1: some_text
4 | - option2: some text
5 | - option3: "some 'text'"
6 | - option4: 'some "text"'
7 | - option5: "some \"text\""
8 |
--------------------------------------------------------------------------------
/tests/files/encode_json_number.out:
--------------------------------------------------------------------------------
1 | {
2 | "var1": 123,
3 | "var2": -987,
4 | "var3": 123.456,
5 | "var4": -987.654,
6 | "var5": "123",
7 | "var6": "-987",
8 | "var7": "123.456",
9 | "var8": "-987.654"
10 | }
11 |
--------------------------------------------------------------------------------
/tests/files/encode_json_number_convert.out:
--------------------------------------------------------------------------------
1 | {
2 | "var1": 123,
3 | "var2": -987,
4 | "var3": 123.456,
5 | "var4": -987.654,
6 | "var5": 123,
7 | "var6": -987,
8 | "var7": 123.456,
9 | "var8": -987.654
10 | }
11 |
--------------------------------------------------------------------------------
/tests/files/encode_json_boolean.in:
--------------------------------------------------------------------------------
1 | var1: yes
2 | var2: no
3 | var3: true
4 | var4: false
5 | var5: True
6 | var6: False
7 | var7: "yes"
8 | var8: "no"
9 | var9: "true"
10 | var10: "false"
11 | var11: "True"
12 | var12: "False"
13 |
--------------------------------------------------------------------------------
/tests/files/encode_lua_boolean.in:
--------------------------------------------------------------------------------
1 | var1: yes
2 | var2: no
3 | var3: true
4 | var4: false
5 | var5: True
6 | var6: False
7 | var7: "yes"
8 | var8: "no"
9 | var9: "true"
10 | var10: "false"
11 | var11: "True"
12 | var12: "False"
13 |
--------------------------------------------------------------------------------
/tests/files/encode_toml_boolean.in:
--------------------------------------------------------------------------------
1 | var1: yes
2 | var2: no
3 | var3: true
4 | var4: false
5 | var5: True
6 | var6: False
7 | var7: "yes"
8 | var8: "no"
9 | var9: "true"
10 | var10: "false"
11 | var11: "True"
12 | var12: "False"
13 |
--------------------------------------------------------------------------------
/tests/files/encode_toml_table.in:
--------------------------------------------------------------------------------
1 | table-1:
2 | key1: some string
3 | key2: 123
4 |
5 | dog:
6 | tater.man:
7 | type: pug
8 |
9 | x:
10 | y:
11 | aaa: bbb
12 | z:
13 | w:
14 | name: deep table
15 |
--------------------------------------------------------------------------------
/tests/files/encode_yaml_boolean.in:
--------------------------------------------------------------------------------
1 | var1: yes
2 | var2: no
3 | var3: true
4 | var4: false
5 | var5: True
6 | var6: False
7 | var7: "yes"
8 | var8: "no"
9 | var9: "true"
10 | var10: "false"
11 | var11: "True"
12 | var12: "False"
13 |
--------------------------------------------------------------------------------
/tests/files/encode_yaml_boolean.out:
--------------------------------------------------------------------------------
1 | var1: true
2 | var10: "false"
3 | var11: "True"
4 | var12: "False"
5 | var2: false
6 | var3: true
7 | var4: false
8 | var5: true
9 | var6: false
10 | var7: "yes"
11 | var8: "no"
12 | var9: "true"
13 |
--------------------------------------------------------------------------------
/tests/files/encode_yaml_boolean_convert.out:
--------------------------------------------------------------------------------
1 | var1: true
2 | var10: false
3 | var11: true
4 | var12: false
5 | var2: false
6 | var3: true
7 | var4: false
8 | var5: true
9 | var6: false
10 | var7: "yes"
11 | var8: "no"
12 | var9: true
13 |
--------------------------------------------------------------------------------
/tests/files/encode_yaml_dict.out:
--------------------------------------------------------------------------------
1 | var1:
2 | aaa: "bbb"
3 | ccc: 123
4 | ddd: true
5 | eee:
6 | fff: "ggg"
7 | hhh:
8 | iii: "jjj"
9 | kkk: true
10 | lll:
11 | - 987.654
12 | - "mmm"
13 |
--------------------------------------------------------------------------------
/tests/files/encode_toml_boolean.out:
--------------------------------------------------------------------------------
1 | var1 = true
2 | var10 = "false"
3 | var11 = "True"
4 | var12 = "False"
5 | var2 = false
6 | var3 = true
7 | var4 = false
8 | var5 = true
9 | var6 = false
10 | var7 = "yes"
11 | var8 = "no"
12 | var9 = "true"
13 |
--------------------------------------------------------------------------------
/tests/files/encode_toml_boolean_convert.out:
--------------------------------------------------------------------------------
1 | var1 = true
2 | var10 = false
3 | var11 = true
4 | var12 = false
5 | var2 = false
6 | var3 = true
7 | var4 = false
8 | var5 = true
9 | var6 = false
10 | var7 = "yes"
11 | var8 = "no"
12 | var9 = true
13 |
--------------------------------------------------------------------------------
/tests/files/encode_lua_boolean.out:
--------------------------------------------------------------------------------
1 | var1 = true;
2 | var10 = "false";
3 | var11 = "True";
4 | var12 = "False";
5 | var2 = false;
6 | var3 = true;
7 | var4 = false;
8 | var5 = true;
9 | var6 = false;
10 | var7 = "yes";
11 | var8 = "no";
12 | var9 = "true";
13 |
--------------------------------------------------------------------------------
/tests/files/encode_lua_boolean_convert.out:
--------------------------------------------------------------------------------
1 | var1 = true;
2 | var10 = false;
3 | var11 = true;
4 | var12 = false;
5 | var2 = false;
6 | var3 = true;
7 | var4 = false;
8 | var5 = true;
9 | var6 = false;
10 | var7 = "yes";
11 | var8 = "no";
12 | var9 = true;
13 |
--------------------------------------------------------------------------------
/tests/files/encode_yaml_list_indent.out:
--------------------------------------------------------------------------------
1 | var1:
2 | - "aaa"
3 | - 123
4 | var2:
5 | - "aaa"
6 | - bbb:
7 | ccc: "ddd"
8 | eee:
9 | - "fff"
10 | - ggg: "hhh"
11 | - bool: true
12 |
--------------------------------------------------------------------------------
/tests/files/encode_apache_vhost.out:
--------------------------------------------------------------------------------
1 |
2 | DocumentRoot /www/example1
3 | ServerName www.example.com
4 | ErrorLog /var/log/httpd/www.example.com-error_log
5 | CustomLog /var/log/httpd/www.example.com-access_log common
6 | # "Other directives here ..."
7 |
8 |
--------------------------------------------------------------------------------
/tests/files/encode_json_boolean.out:
--------------------------------------------------------------------------------
1 | {
2 | "var1": true,
3 | "var10": "false",
4 | "var11": "True",
5 | "var12": "False",
6 | "var2": false,
7 | "var3": true,
8 | "var4": false,
9 | "var5": true,
10 | "var6": false,
11 | "var7": "yes",
12 | "var8": "no",
13 | "var9": "true"
14 | }
15 |
--------------------------------------------------------------------------------
/tests/files/encode_json_boolean_convert.out:
--------------------------------------------------------------------------------
1 | {
2 | "var1": true,
3 | "var10": false,
4 | "var11": true,
5 | "var12": false,
6 | "var2": false,
7 | "var3": true,
8 | "var4": false,
9 | "var5": true,
10 | "var6": false,
11 | "var7": "yes",
12 | "var8": "no",
13 | "var9": true
14 | }
15 |
--------------------------------------------------------------------------------
/tests/files/encode_json_dict.out:
--------------------------------------------------------------------------------
1 | {
2 | "var1": {
3 | "aaa": "bbb",
4 | "ccc": 123,
5 | "ddd": true,
6 | "eee": {
7 | "fff": "ggg",
8 | "hhh": {
9 | "iii": "jjj",
10 | "kkk": true,
11 | "lll": [
12 | 987.654,
13 | "mmm"
14 | ]
15 | }
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/tests/files/encode_lua_list_indent.out:
--------------------------------------------------------------------------------
1 | var1 = {
2 | "aaa";
3 | 123;
4 | }
5 | var2 = {
6 | "aaa";
7 | {
8 | bbb = {
9 | ccc = "ddd";
10 | eee = {
11 | "fff";
12 | {
13 | ggg = "hhh";
14 | };
15 | {
16 | bool = true;
17 | };
18 | };
19 | };
20 | };
21 | }
22 |
--------------------------------------------------------------------------------
/tests/files/encode_lua_dict.out:
--------------------------------------------------------------------------------
1 | var1 = {
2 | aaa = "bbb";
3 | ccc = 123;
4 | ddd = true;
5 | eee = {
6 | fff = "ggg";
7 | hhh = {
8 | iii = "jjj";
9 | kkk = true;
10 | lll = {
11 | 987.654;
12 | "mmm";
13 | };
14 | };
15 | };
16 | }
17 |
--------------------------------------------------------------------------------
/tests/files/encode_json_list.out:
--------------------------------------------------------------------------------
1 | {
2 | "var1": [
3 | "aaa",
4 | 123
5 | ],
6 | "var2": [
7 | "aaa",
8 | {
9 | "bbb": {
10 | "ccc": "ddd",
11 | "eee": [
12 | "fff",
13 | {
14 | "ggg": "hhh"
15 | },
16 | {
17 | "bool": true
18 | }
19 | ]
20 | }
21 | }
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/tests/files/encode_toml_table_array.in:
--------------------------------------------------------------------------------
1 | products:
2 | - name: Hammer
3 | sku: 738594937
4 | - {}
5 | - name: Nail
6 | sku: 284758393
7 | color: gray
8 |
9 | fruit:
10 | - name: apple
11 | physical:
12 | color: red
13 | shape: round
14 | variety:
15 | - name: red delicious
16 | - name: granny smith
17 | - name: banana
18 | variety:
19 | - name: plantain
20 |
--------------------------------------------------------------------------------
/tests/files/encode_lua_list.out:
--------------------------------------------------------------------------------
1 | var1 = {
2 | "aaa";
3 | 123;
4 | }
5 | var2 = {
6 | "aaa";
7 | {
8 | bbb = {
9 | ccc = "ddd";
10 | eee = {
11 | "fff";
12 | {
13 | ggg = "hhh";
14 | };
15 | {
16 | bool = true;
17 | };
18 | };
19 | };
20 | };
21 | }
22 |
--------------------------------------------------------------------------------
/tests/files/encode_apache_vhost.in:
--------------------------------------------------------------------------------
1 | content:
2 | - sections:
3 | - name: VirtualHost
4 | param: "*:80"
5 | content:
6 | - options:
7 | - DocumentRoot: /www/example1
8 | - ServerName: www.example.com
9 | - ErrorLog: /var/log/httpd/www.example.com-error_log
10 | - CustomLog:
11 | - /var/log/httpd/www.example.com-access_log
12 | - common
13 | - "#": Other directives here ...
14 |
--------------------------------------------------------------------------------
/tests/files/encode_erlang_mixed.in:
--------------------------------------------------------------------------------
1 | - rabbit:
2 | - tcp_listeners:
3 | - '"127.0.0.1"': 5672
4 | - ssl_listeners:
5 | - 5671
6 | - ssl_options:
7 | - cacertfile: /path/to/testca/cacert.pem
8 | - certfile: /path/to/server/cert.pem
9 | - keyfile: /path/to/server/key.pem
10 | - verify: :verify_peer
11 | - fail_if_no_peer_cert: true
12 | - cluster_nodes:
13 | ::
14 | -
15 | - node1
16 | - node2
17 | - :disc
18 |
--------------------------------------------------------------------------------
/tests/files/encode_toml_table_array.out:
--------------------------------------------------------------------------------
1 | [[fruit]]
2 | name = "apple"
3 |
4 | [fruit.physical]
5 | color = "red"
6 | shape = "round"
7 |
8 | [[fruit.variety]]
9 | name = "red delicious"
10 |
11 | [[fruit.variety]]
12 | name = "granny smith"
13 |
14 | [[fruit]]
15 | name = "banana"
16 |
17 | [[fruit.variety]]
18 | name = "plantain"
19 |
20 | [[products]]
21 | name = "Hammer"
22 | sku = 738594937
23 |
24 | [[products]]
25 |
26 | [[products]]
27 | color = "gray"
28 | name = "Nail"
29 | sku = 284758393
30 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | language: python
4 | dist: focal
5 |
6 | python:
7 | - 2.7
8 | - 3.5
9 | - 3.6
10 | - 3.7
11 | - 3.8
12 | - 3.9
13 |
14 | matrix:
15 | fast_finish: true
16 |
17 | env:
18 | matrix:
19 | - ANSIBLE=2.4
20 | - ANSIBLE=2.5
21 | - ANSIBLE=2.6
22 | - ANSIBLE=2.7
23 | - ANSIBLE=2.8
24 | - ANSIBLE=2.9
25 | - ANSIBLE=2.10
26 | - ANSIBLE=2.11
27 |
28 | install:
29 | - pip install tox-travis
30 |
31 | script:
32 | - tox
33 |
34 | notifications:
35 | email: false
36 |
--------------------------------------------------------------------------------
/tests/files/encode_json_list_indent.out:
--------------------------------------------------------------------------------
1 | {
2 | "var1": [
3 | "aaa",
4 | 123
5 | ],
6 | "var2": [
7 | "aaa",
8 | {
9 | "bbb": {
10 | "ccc": "ddd",
11 | "eee": [
12 | "fff",
13 | {
14 | "ggg": "hhh"
15 | },
16 | {
17 | "bool": true
18 | }
19 | ]
20 | }
21 | }
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/tests/files/encode_erlang_mixed.out:
--------------------------------------------------------------------------------
1 | [
2 | {rabbit, [
3 | {tcp_listeners, [
4 | {"127.0.0.1", 5672}
5 | ]},
6 | {ssl_listeners, [
7 | 5671
8 | ]},
9 | {ssl_options, [
10 | {cacertfile, "/path/to/testca/cacert.pem"},
11 | {certfile, "/path/to/server/cert.pem"},
12 | {keyfile, "/path/to/server/key.pem"},
13 | {verify, verify_peer},
14 | {fail_if_no_peer_cert, true}
15 | ]},
16 | {cluster_nodes,
17 | {[
18 | "node1",
19 | "node2"
20 | ], disc}}
21 | ]}
22 | ].
23 |
--------------------------------------------------------------------------------
/tests/files/encode_toml_table_grafana.in:
--------------------------------------------------------------------------------
1 | verbose_logging: false
2 |
3 | servers:
4 | - host: 127.0.0.1
5 | port: 389
6 | use_ssl: no
7 | start_tls: no
8 | ssl_skip_verify: no
9 | bind_dn: cn=admin,dc=grafana,dc=org
10 | bind_password: grafana
11 | search_filter: (cn=%s)
12 | search_base_dns:
13 | - dc=grafana,dc=org
14 | - attributes:
15 | name: givenName
16 | surname: sn
17 | username: cn
18 | member_of: memberOf
19 | email: email
20 | - group_mappings:
21 | - group_dn: cn=admins,dc=grafana,dc=org
22 | org_role: Admin
23 | - group_dn: cn=users,dc=grafana,dc=org
24 | org_role: Editor
25 | - group_dn: "*"
26 | org_role: Viewer
27 |
--------------------------------------------------------------------------------
/tests/files/encode_toml_table_grafana.out:
--------------------------------------------------------------------------------
1 | verbose_logging = false
2 |
3 | [[servers]]
4 | bind_dn = "cn=admin,dc=grafana,dc=org"
5 | bind_password = "grafana"
6 | host = "127.0.0.1"
7 | port = 389
8 | search_base_dns = ["dc=grafana,dc=org"]
9 | search_filter = "(cn=%s)"
10 | ssl_skip_verify = false
11 | start_tls = false
12 | use_ssl = false
13 |
14 | [servers.attributes]
15 | email = "email"
16 | member_of = "memberOf"
17 | name = "givenName"
18 | surname = "sn"
19 | username = "cn"
20 |
21 | [[servers.group_mappings]]
22 | group_dn = "cn=admins,dc=grafana,dc=org"
23 | org_role = "Admin"
24 |
25 | [[servers.group_mappings]]
26 | group_dn = "cn=users,dc=grafana,dc=org"
27 | org_role = "Editor"
28 |
29 | [[servers.group_mappings]]
30 | group_dn = "*"
31 | org_role = "Viewer"
32 |
--------------------------------------------------------------------------------
/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | minversion = 1.8
3 | envlist = py{27}-ansible{24,25,26,27,28,29,210,211},py{35,36,37,38,39}-ansible{25,26,27,28,29,210,211}
4 | skipsdist = true
5 |
6 | [travis:env]
7 | ANSIBLE=
8 | 2.4: ansible24
9 | 2.5: ansible25
10 | 2.6: ansible26
11 | 2.7: ansible27
12 | 2.8: ansible28
13 | 2.9: ansible29
14 | 2.10: ansible210
15 | 2.11: ansible211
16 |
17 | [testenv]
18 | passenv = *
19 | deps =
20 | -rtest-requirements.txt
21 | ansible24: ansible<2.5
22 | ansible25: ansible<2.6
23 | ansible26: ansible<2.7
24 | ansible27: ansible<2.8
25 | ansible28: ansible<2.9
26 | ansible29: ansible<2.10
27 | ansible210: ansible<2.11
28 | ansible211: ansible<2.12
29 | commands =
30 | flake8
31 | {posargs:python -m unittest -v tests.test_config_encoders}
32 |
--------------------------------------------------------------------------------
/meta/main.yaml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | galaxy_info:
4 | author: Jiri Tyr
5 | description: Config Encoder Filters
6 | license: GPLv3
7 | min_ansible_version: 2.4
8 | platforms:
9 | - name: Amazon
10 | versions:
11 | - all
12 | - name: Debian
13 | versions:
14 | - all
15 | - name: EL
16 | versions:
17 | - all
18 | - name: eos
19 | versions:
20 | - all
21 | - name: Fedora
22 | versions:
23 | - all
24 | - name: FreeBSD
25 | versions:
26 | - all
27 | - name: GenericBSD
28 | versions:
29 | - all
30 | - name: GenericLinux
31 | versions:
32 | - all
33 | - name: GenericUNIX
34 | versions:
35 | - all
36 | - name: IOS
37 | versions:
38 | - all
39 | - name: Junos
40 | versions:
41 | - all
42 | - name: NXOS
43 | versions:
44 | - all
45 | - name: opensuse
46 | versions:
47 | - all
48 | - name: SLES
49 | versions:
50 | - all
51 | - name: SmartOS
52 | versions:
53 | - all
54 | - name: Solaris
55 | versions:
56 | - all
57 | - name: Ubuntu
58 | versions:
59 | - all
60 | galaxy_tags:
61 | - filter
62 | dependencies: []
63 |
--------------------------------------------------------------------------------
/tests/test_config_encoders.include:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | - name: Load input file for {{ item.encoder }}_{{ item.out | default(item.in) }}
4 | include_vars:
5 | file: files/{{ item.encoder }}_{{ item.in }}.in
6 | name: myinput
7 |
8 | - name: Comparison for {{ item.encoder }}_{{ item.out | default(item.in) }}
9 | assert:
10 | that: >
11 | "{{ (
12 | myinput | encode_apache(**item.params | default({}))
13 | if item.encoder == 'encode_apache' else
14 | myinput | encode_erlang(**item.params | default({}))
15 | if item.encoder == 'encode_erlang' else
16 | myinput | encode_haproxy(**item.params | default({}))
17 | if item.encoder == 'encode_haproxy' else
18 | myinput | encode_ini(**item.params | default({}))
19 | if item.encoder == 'encode_ini' else
20 | myinput | encode_json(**item.params | default({}))
21 | if item.encoder == 'encode_json' else
22 | myinput | encode_logstash(**item.params | default({}))
23 | if item.encoder == 'encode_logstash' else
24 | myinput | encode_lua(**item.params | default({}))
25 | if item.encoder == 'encode_lua' else
26 | myinput | encode_nginx(**item.params | default({}))
27 | if item.encoder == 'encode_nginx' else
28 | myinput | encode_pam(**item.params | default({}))
29 | if item.encoder == 'encode_pam' else
30 | myinput | encode_toml(**item.params | default({}))
31 | if item.encoder == 'encode_toml' else
32 | myinput | encode_xml(**item.params | default({}))
33 | if item.encoder == 'encode_xml' else
34 | myinput | encode_yaml(**item.params | default({}))
35 | ) | trim | hash('md5') }}" == "{{ lookup('file', 'files/{{ item.encoder }}_{{ item.out | default(item.in) }}.out') | trim | hash('md5') }}"
36 |
--------------------------------------------------------------------------------
/tests/test_config_encoders.yaml:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | - hosts: all
4 | connection: local
5 | gather_facts: no
6 | vars:
7 | tests:
8 | # Apache
9 | - encoder: encode_apache
10 | in: boolean
11 | - encoder: encode_apache
12 | in: boolean
13 | out: boolean_convert
14 | params:
15 | convert_bools: yes
16 | - encoder: encode_apache
17 | in: boolean
18 | out: boolean_convert
19 | params:
20 | convert_bools: yes
21 | - encoder: encode_apache
22 | in: number
23 | - encoder: encode_apache
24 | in: number
25 | out: number_convert
26 | params:
27 | convert_nums: yes
28 | - encoder: encode_apache
29 | in: number
30 | out: number_quote
31 | params:
32 | quote_all_nums: yes
33 | - encoder: encode_apache
34 | in: string
35 | - encoder: encode_apache
36 | in: string
37 | out: string_quote
38 | params:
39 | quote_all_strings: yes
40 | - encoder: encode_apache
41 | in: vhost
42 |
43 | # JSON
44 | - encoder: encode_json
45 | in: boolean
46 | - encoder: encode_json
47 | in: boolean
48 | out: boolean_convert
49 | params:
50 | convert_bools: yes
51 | - encoder: encode_json
52 | in: string
53 | - encoder: encode_json
54 | in: number
55 | - encoder: encode_json
56 | in: number
57 | out: number_convert
58 | params:
59 | convert_nums: yes
60 | - encoder: encode_json
61 | in: list
62 | - encoder: encode_json
63 | in: list
64 | out: list_indent
65 | params:
66 | indent: " "
67 | - encoder: encode_json
68 | in: dict
69 |
70 | # YAML
71 | - encoder: encode_yaml
72 | in: boolean
73 | - encoder: encode_yaml
74 | in: boolean
75 | out: boolean_convert
76 | params:
77 | convert_bools: yes
78 | - encoder: encode_yaml
79 | in: string
80 | - encoder: encode_yaml
81 | in: number
82 | - encoder: encode_yaml
83 | in: number
84 | out: number_convert
85 | params:
86 | convert_nums: yes
87 | - encoder: encode_yaml
88 | in: list
89 | - encoder: encode_yaml
90 | in: list
91 | out: list_indent
92 | params:
93 | indent: " "
94 | - encoder: encode_yaml
95 | in: dict
96 | roles:
97 | - config_encoder_filters
98 | tasks:
99 | - include: test_config_encoders.include
100 | with_items: "{{ tests }}"
101 |
--------------------------------------------------------------------------------
/tests/test_config_encoders.py:
--------------------------------------------------------------------------------
1 | import filter_plugins.config_encoders as CE
2 | import os
3 | import unittest
4 | import yaml
5 |
6 |
7 | class MyTestCase(unittest.TestCase):
8 | def _load_file(self, kind, encoder, test):
9 | dir_path = os.path.dirname(os.path.realpath(__file__))
10 |
11 | f = open('%s/files/%s_%s.%s' % (dir_path, encoder, test, kind), 'r')
12 |
13 | if kind == 'in':
14 | ret = yaml.load(f)
15 | else:
16 | ret = f.read()
17 |
18 | f.close()
19 |
20 | return ret
21 |
22 | def _load_input(self, test):
23 | return self._load_file('in', self._encoder, test)
24 |
25 | def _load_output(self, test):
26 | return self._load_file('out', self._encoder, test)
27 |
28 | def _test(self, test, **params):
29 | test_in = test
30 | test_out = test
31 |
32 | if isinstance(test, list):
33 | test_in = test[0]
34 | test_out = test[1]
35 |
36 | my_in = self._load_input(test_in)
37 | my_out = self._load_output(test_out)
38 |
39 | encoder = getattr(CE, self._encoder)
40 |
41 | self.assertEqual(encoder(my_in, **params), my_out)
42 |
43 |
44 | class TestApache(MyTestCase):
45 | _encoder = 'encode_apache'
46 |
47 | def test_boolean(self):
48 | self._test('boolean')
49 |
50 | def test_boolean_convert(self):
51 | self._test(['boolean', 'boolean_convert'], convert_bools=True)
52 |
53 | def test_number(self):
54 | self._test('number')
55 |
56 | def test_number_convert(self):
57 | self._test(['number', 'number_convert'], convert_nums=True)
58 |
59 | def test_number_quote(self):
60 | self._test(['number', 'number_quote'], quote_all_nums=True)
61 |
62 | def test_string(self):
63 | self._test('string')
64 |
65 | def test_string_quote(self):
66 | self._test(['string', 'string_quote'], quote_all_strings=True)
67 |
68 | def test_vhost(self):
69 | self._test('vhost')
70 |
71 |
72 | class TestErlang(MyTestCase):
73 | _encoder = 'encode_erlang'
74 |
75 | def test_atom(self):
76 | self._test('atom')
77 |
78 | def test_boolean(self):
79 | self._test('boolean')
80 |
81 | def test_boolean_convert(self):
82 | self._test(['boolean', 'boolean_convert'], convert_bools=True)
83 |
84 | def test_number(self):
85 | self._test('number')
86 |
87 | def test_number_convert(self):
88 | self._test(['number', 'number_convert'], convert_nums=True)
89 |
90 | def test_string(self):
91 | self._test('string')
92 |
93 | def test_mixed(self):
94 | self._test('mixed')
95 |
96 |
97 | class TestIni(MyTestCase):
98 | _encoder = 'encode_ini'
99 |
100 | def test_general(self):
101 | self._test('general')
102 |
103 | def test_section(self):
104 | self._test('section')
105 |
106 | def test_mixed(self):
107 | self._test('mixed')
108 |
109 | def test_mixed_comment(self):
110 | self._test(['mixed', 'mixed_comment'], section_is_comment=True)
111 |
112 | def test_mixed_delimiter(self):
113 | self._test(['mixed', 'mixed_delimiter'], delimiter=" = ")
114 |
115 | def test_mixed_indent(self):
116 | self._test(['mixed', 'mixed_indent'], indent="\t")
117 |
118 | def test_mixed_quote(self):
119 | self._test(['mixed', 'mixed_quote'], quote='"')
120 |
121 | def test_mixed_ucase(self):
122 | self._test(['mixed', 'mixed_ucase'], ucase_prop=True)
123 |
124 | def test_null(self):
125 | self._test('null')
126 |
127 |
128 | class TestJson(MyTestCase):
129 | _encoder = 'encode_json'
130 |
131 | def test_boolean(self):
132 | self._test('boolean')
133 |
134 | def test_boolean_convert(self):
135 | self._test(['boolean', 'boolean_convert'], convert_bools=True)
136 |
137 | def test_string(self):
138 | self._test('string')
139 |
140 | def test_number(self):
141 | self._test('number')
142 |
143 | def test_number_convert(self):
144 | self._test(['number', 'number_convert'], convert_nums=True)
145 |
146 | def test_list(self):
147 | self._test('list')
148 |
149 | def test_list_indent(self):
150 | self._test(['list', 'list_indent'], indent=" ")
151 |
152 | def test_dict(self):
153 | self._test('dict')
154 |
155 |
156 | class TestLua(MyTestCase):
157 | _encoder = "encode_lua"
158 |
159 | def test_boolean(self):
160 | self._test('boolean')
161 |
162 | def test_boolean_convert(self):
163 | self._test(['boolean', 'boolean_convert'], convert_bools=True)
164 |
165 | def test_string(self):
166 | self._test('string')
167 |
168 | def test_number(self):
169 | self._test('number')
170 |
171 | def test_number_convert(self):
172 | self._test(['number', 'number_convert'], convert_nums=True)
173 |
174 | def test_list(self):
175 | self._test('list')
176 |
177 | def test_list_indent(self):
178 | self._test(['list', 'list_indent'], indent=" ")
179 |
180 | def test_dict(self):
181 | self._test('dict')
182 |
183 |
184 | class TestToml(MyTestCase):
185 | _encoder = 'encode_toml'
186 |
187 | def test_boolean(self):
188 | self._test('boolean')
189 |
190 | def test_boolean_convert(self):
191 | self._test(['boolean', 'boolean_convert'], convert_bools=True)
192 |
193 | def test_string(self):
194 | self._test('string')
195 |
196 | def test_string_quote(self):
197 | self._test(['string', 'string_quote'], quote="'")
198 |
199 | def test_number(self):
200 | self._test('number')
201 |
202 | def test_number_convert(self):
203 | self._test(['number', 'number_convert'], convert_nums=True)
204 |
205 | def test_array(self):
206 | self._test('array')
207 |
208 | def test_table(self):
209 | self._test('table')
210 |
211 | def test_table_array(self):
212 | self._test('table_array')
213 |
214 | def test_table_grafana(self):
215 | self._test('table_grafana')
216 |
217 |
218 | class TestXml(MyTestCase):
219 | _encoder = 'encode_xml'
220 |
221 | def test_element(self):
222 | self._test('element')
223 |
224 | def test_attribute(self):
225 | self._test('attribute')
226 |
227 |
228 | class TestYaml(MyTestCase):
229 | _encoder = 'encode_yaml'
230 |
231 | def test_boolean(self):
232 | self._test('boolean')
233 |
234 | def test_boolean_convert(self):
235 | self._test(['boolean', 'boolean_convert'], convert_bools=True)
236 |
237 | def test_string(self):
238 | self._test('string')
239 |
240 | def test_string_quote(self):
241 | self._test(['string', 'string_quote'], quote='')
242 |
243 | def test_number(self):
244 | self._test('number')
245 |
246 | def test_number_convert(self):
247 | self._test(['number', 'number_convert'], convert_nums=True)
248 |
249 | def test_list(self):
250 | self._test('list')
251 |
252 | def test_list_indent(self):
253 | self._test(['list', 'list_indent'], indent=" ")
254 |
255 | def test_dict(self):
256 | self._test('dict')
257 |
258 | def test_block(self):
259 | self._test('block')
260 |
261 | def test_null(self):
262 | self._test('null')
263 |
264 |
265 | if __name__ == '__main__':
266 | unittest.main()
267 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
676 |
--------------------------------------------------------------------------------
/filter_plugins/config_encoders.py:
--------------------------------------------------------------------------------
1 | # (c) 2016, Jiri Tyr
2 | #
3 | # This file is part of Config Encoder Filters (CEF)
4 | #
5 | # CEF is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # CEF is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with CEF. If not, see .
17 |
18 | """
19 | Config Encoder Filters
20 |
21 | More information: https://github.com/jtyr/ansible-config_encoder_filters
22 | """
23 |
24 | from __future__ import (absolute_import, division, print_function)
25 | from ansible.module_utils.six import string_types
26 | from ansible import errors
27 | from copy import copy
28 | import re
29 |
30 |
31 | def _str_is_bool(data):
32 | """Verify if data is boolean."""
33 |
34 | return re.match(r"^(true|false)$", str(data), flags=re.IGNORECASE)
35 |
36 |
37 | def _str_is_int(data):
38 | """Verify if data is integer."""
39 |
40 | return re.match(r"^[-+]?(0|[1-9][0-9]*)$", str(data))
41 |
42 |
43 | def _str_is_float(data):
44 | """Verify if data is float."""
45 |
46 | return re.match(
47 | r"^[-+]?(0|[1-9][0-9]*)(\.[0-9]*)?(e[-+]?[0-9]+)?$",
48 | str(data), flags=re.IGNORECASE)
49 |
50 |
51 | def _str_is_num(data):
52 | """Verify if data is either integer or float."""
53 |
54 | return _str_is_int(data) or _str_is_float(data)
55 |
56 |
57 | def _is_num(data):
58 | """Verify if data is either int or float.
59 |
60 | Could be replaced by:
61 |
62 | from numbers import Number as number
63 | isinstance(data, number)
64 |
65 | but that requires Python v2.6+.
66 | """
67 |
68 | return isinstance(data, int) or isinstance(data, float)
69 |
70 |
71 | def _escape(data, quote='"', format=None):
72 | """Escape special characters in a string."""
73 |
74 | if format == 'xml':
75 | return (
76 | str(data).
77 | replace('&', '&').
78 | replace('<', '<').
79 | replace('>', '>'))
80 | elif format == 'control':
81 | return (
82 | str(data).
83 | replace('\b', '\\b').
84 | replace('\f', '\\f').
85 | replace('\n', '\\n').
86 | replace('\r', '\\r').
87 | replace('\t', '\\t'))
88 | elif quote is not None and len(quote):
89 | return str(data).replace('\\', '\\\\').replace(quote, "\\%s" % quote)
90 | else:
91 | return data
92 |
93 |
94 | def encode_apache(
95 | data, block_type='sections', convert_bools=False, convert_nums=False,
96 | indent=" ", level=0, quote_all_nums=False, quote_all_strings=False):
97 | """Convert Python data structure to Apache format."""
98 |
99 | # Return value
100 | rv = ""
101 |
102 | if block_type == 'sections':
103 | for c in data['content']:
104 | # First check if this section has options
105 | if 'options' in c:
106 | rv += encode_apache(
107 | c['options'],
108 | convert_bools=convert_bools,
109 | convert_nums=convert_nums,
110 | indent=indent,
111 | level=level+1,
112 | quote_all_nums=quote_all_nums,
113 | quote_all_strings=quote_all_strings,
114 | block_type='options')
115 |
116 | is_empty = False
117 |
118 | # Check if this section has some sub-sections
119 | if 'sections' in c:
120 | for s in c['sections']:
121 | # Check for empty sub-sections
122 | for i in s['content']:
123 | if (
124 | ('options' in i and len(i['options']) > 0) or
125 | ('sections' in i and len(i['sections']) > 0)):
126 | is_empty = True
127 |
128 | if is_empty:
129 | rv += "%s<%s" % (indent * level, s['name'])
130 |
131 | if 'operator' in s:
132 | rv += " %s" % s['operator']
133 |
134 | if 'param' in s:
135 | rv += ' '
136 | rv += encode_apache(
137 | s['param'],
138 | convert_bools=convert_bools,
139 | convert_nums=convert_nums,
140 | indent=indent,
141 | level=level+1,
142 | quote_all_nums=quote_all_nums,
143 | quote_all_strings=quote_all_strings,
144 | block_type='value')
145 |
146 | rv += ">\n"
147 | rv += encode_apache(
148 | s,
149 | convert_bools=convert_bools,
150 | convert_nums=convert_nums,
151 | indent=indent,
152 | level=level+1,
153 | quote_all_nums=quote_all_nums,
154 | quote_all_strings=quote_all_strings,
155 | block_type='sections')
156 | rv += "%s%s>\n" % (indent * level, s['name'])
157 |
158 | # If not last item of the loop
159 | if c['sections'][-1] != s:
160 | rv += "\n"
161 |
162 | if (
163 | data['content'][-1] != c and (
164 | 'options' in c and len(c['options']) > 0 or (
165 | 'sections' in c and
166 | len(c['sections']) > 0 and
167 | is_empty))):
168 | rv += "\n"
169 |
170 | elif block_type == 'options':
171 | for o in data:
172 | for key, val in sorted(o.items()):
173 | rv += "%s%s " % (indent * (level-1), key)
174 | rv += encode_apache(
175 | val,
176 | convert_bools=convert_bools,
177 | convert_nums=convert_nums,
178 | indent=indent,
179 | level=level+1,
180 | quote_all_nums=quote_all_nums,
181 | quote_all_strings=quote_all_strings,
182 | block_type='value')
183 | rv += "\n"
184 |
185 | elif block_type == 'value':
186 | if isinstance(data, bool) or convert_bools and _str_is_bool(data):
187 | # Value is a boolean
188 |
189 | rv += str(data).lower()
190 |
191 | elif (
192 | _is_num(data) or
193 | (convert_nums and _str_is_num(data))):
194 | # Value is a number
195 |
196 | if quote_all_nums:
197 | rv += '"%s"' % data
198 | else:
199 | rv += str(data)
200 |
201 | elif isinstance(data, string_types):
202 | # Value is a string
203 | if (
204 | quote_all_strings or
205 | " " in data or
206 | "\t" in data or
207 | "\n" in data or
208 | "\r" in data or
209 | data == ""):
210 |
211 | rv += '"%s"' % _escape(data)
212 | else:
213 | rv += data
214 |
215 | elif isinstance(data, list):
216 | # Value is a list
217 | for v in data:
218 | rv += encode_apache(
219 | v,
220 | convert_bools=convert_bools,
221 | convert_nums=convert_nums,
222 | indent=indent,
223 | level=level+1,
224 | quote_all_nums=quote_all_nums,
225 | quote_all_strings=quote_all_strings,
226 | block_type='value')
227 |
228 | # If not last item of the loop
229 | if data[-1] != v:
230 | rv += " "
231 |
232 | return rv
233 |
234 |
235 | def encode_erlang(
236 | data, atom_value_indicator=":", convert_bools=False,
237 | convert_nums=False, indent=" ", level=0, ordered_tuple_indicator=":"):
238 | """Convert Python data structure to Erlang format."""
239 |
240 | # Return value
241 | rv = ""
242 |
243 | if isinstance(data, dict):
244 | # It's a dict
245 |
246 | rv += "\n"
247 |
248 | for key, val in sorted(data.items()):
249 | if key == ordered_tuple_indicator:
250 | rv += "%s{" % (indent*level)
251 |
252 | if isinstance(val, list):
253 | for i, v in enumerate(val):
254 | rv += encode_erlang(
255 | v,
256 | atom_value_indicator=atom_value_indicator,
257 | convert_bools=convert_bools,
258 | convert_nums=convert_nums,
259 | indent=indent,
260 | level=level+1,
261 | ordered_tuple_indicator=ordered_tuple_indicator)
262 |
263 | if i+1 < len(val):
264 | rv += ", "
265 | else:
266 | rv += "%s{%s," % (indent*level, key)
267 |
268 | if not isinstance(val, dict):
269 | rv += " "
270 |
271 | rv += encode_erlang(
272 | val,
273 | atom_value_indicator=atom_value_indicator,
274 | convert_bools=convert_bools,
275 | convert_nums=convert_nums,
276 | indent=indent,
277 | level=level+1,
278 | ordered_tuple_indicator=ordered_tuple_indicator)
279 |
280 | rv += "}"
281 | elif (
282 | data == "null" or
283 | _is_num(data) or
284 | isinstance(data, bool) or
285 | (convert_nums and _str_is_num(data)) or
286 | (convert_bools and _str_is_bool(data))):
287 | # It's null, number or boolean
288 |
289 | rv += str(data).lower()
290 |
291 | elif isinstance(data, string_types):
292 | # It's a string
293 |
294 | atom_len = len(atom_value_indicator)
295 |
296 | if (
297 | len(data) > atom_len and
298 | data[0:atom_len] == atom_value_indicator):
299 |
300 | # Atom configuration value
301 | rv += data[atom_len:]
302 | else:
303 | rv += '"%s"' % _escape(data)
304 |
305 | else:
306 | # It's a list
307 |
308 | rv += "["
309 |
310 | for val in data:
311 | if (
312 | isinstance(val, string_types) or
313 | _is_num(val)):
314 | rv += "\n%s" % (indent*level)
315 |
316 | rv += encode_erlang(
317 | val,
318 | atom_value_indicator=atom_value_indicator,
319 | convert_bools=convert_bools,
320 | convert_nums=convert_nums,
321 | indent=indent,
322 | level=level+1,
323 | ordered_tuple_indicator=ordered_tuple_indicator)
324 |
325 | if data[-1] == val:
326 | # Last item of the loop
327 | rv += "\n"
328 | else:
329 | rv += ","
330 |
331 | if len(data) > 0:
332 | rv += "%s]" % (indent * (level-1))
333 | else:
334 | rv += "]"
335 |
336 | if level == 0:
337 | rv += ".\n"
338 |
339 | return rv
340 |
341 |
342 | def encode_haproxy(data, indent=" "):
343 | """Convert Python data structure to HAProxy format."""
344 |
345 | # Return value
346 | rv = ""
347 | # Indicates first loop
348 | first = True
349 | # Indicates whether the previous section was a comment
350 | prev_comment = False
351 |
352 | for section in data:
353 | if first:
354 | first = False
355 | elif prev_comment:
356 | prev_comment = False
357 | else:
358 | # Print empty line between sections
359 | rv += "\n"
360 |
361 | if isinstance(section, dict):
362 | # It's a section
363 | rv += "%s\n" % list(section.keys())[0]
364 |
365 | # Process all parameters of the section
366 | for param in list(section.values())[0]:
367 | if isinstance(param, dict):
368 | for p_val in list(param.values())[0]:
369 | if len(p_val) > 0:
370 | rv += "%s%s %s\n" % (
371 | indent, list(param.keys())[0], p_val)
372 | else:
373 | if len(param) > 0:
374 | rv += "%s%s\n" % (indent, param)
375 | else:
376 | # It's a comment of a parameter
377 | rv += "%s\n" % section
378 | prev_comment = True
379 |
380 | return rv
381 |
382 |
383 | def encode_ini(
384 | data, comment="#", delimiter="=", indent="", quote="",
385 | section_is_comment=False, ucase_prop=False):
386 | """Convert Python data structure to INI format."""
387 |
388 | # Return value
389 | rv = ""
390 |
391 | # First process all standalone properties
392 | for prop, val in sorted(data.items()):
393 | if ucase_prop:
394 | prop = prop.upper()
395 |
396 | vals = []
397 |
398 | if isinstance(val, list):
399 | vals = val
400 | elif not isinstance(val, dict):
401 | vals = [val]
402 |
403 | for item in vals:
404 | if (
405 | len(quote) == 0 and
406 | isinstance(item, string_types) and
407 | len(item) == 0):
408 | item = '""'
409 |
410 | if item is not None:
411 | if item == "!!!null":
412 | rv += "%s%s\n" % (indent, prop)
413 | else:
414 | rv += "%s%s%s%s%s%s\n" % (
415 | indent, prop, delimiter, quote, _escape(item, quote),
416 | quote)
417 |
418 | # Then process all sections
419 | for section, props in sorted(data.items()):
420 | if isinstance(props, dict):
421 | if rv != "":
422 | rv += "\n"
423 |
424 | if section_is_comment:
425 | rv += "%s %s\n" % (comment, section)
426 | else:
427 | rv += "[%s]\n" % (section)
428 |
429 | # Let process all section options as standalone properties
430 | rv += encode_ini(
431 | props,
432 | delimiter=delimiter,
433 | indent=indent,
434 | quote=quote,
435 | section_is_comment=section_is_comment,
436 | ucase_prop=ucase_prop)
437 |
438 | return rv
439 |
440 |
441 | def encode_json(
442 | data, convert_bools=False, convert_nums=False, indent=" ", level=0):
443 | """Convert Python data structure to JSON format."""
444 |
445 | # Return value
446 | rv = ""
447 |
448 | if isinstance(data, dict):
449 | # It's a dict
450 |
451 | rv += "{"
452 |
453 | if len(data) > 0:
454 | rv += "\n"
455 |
456 | items = sorted(data.items())
457 |
458 | for key, val in items:
459 | rv += '%s"%s": ' % (indent * (level+1), key)
460 | rv += encode_json(
461 | val,
462 | convert_bools=convert_bools,
463 | convert_nums=convert_nums,
464 | indent=indent,
465 | level=level+1)
466 |
467 | # Last item of the loop
468 | if items[-1] == (key, val):
469 | rv += "\n"
470 | else:
471 | rv += ",\n"
472 |
473 | if len(data) > 0:
474 | rv += "%s}" % (indent * level)
475 | else:
476 | rv += "}"
477 |
478 | if level == 0:
479 | rv += "\n"
480 |
481 | elif (
482 | data == "null" or
483 | _is_num(data) or
484 | (convert_nums and _str_is_num(data)) or
485 | (convert_bools and _str_is_bool(data))):
486 | # It's a number, null or boolean
487 |
488 | rv += str(data).lower()
489 |
490 | elif isinstance(data, string_types):
491 | # It's a string
492 |
493 | rv += '"%s"' % _escape(_escape(data), format='control')
494 |
495 | else:
496 | # It's a list
497 |
498 | rv += "["
499 |
500 | if len(data) > 0:
501 | rv += "\n"
502 |
503 | for val in data:
504 | rv += indent * (level+1)
505 | rv += encode_json(
506 | val,
507 | convert_bools=convert_bools,
508 | convert_nums=convert_nums,
509 | indent=indent,
510 | level=level+1)
511 |
512 | # Last item of the loop
513 | if data[-1] == val:
514 | rv += "\n"
515 | else:
516 | rv += ",\n"
517 |
518 | if len(data) > 0:
519 | rv += "%s]" % (indent * level)
520 | else:
521 | rv += "]"
522 |
523 | return rv
524 |
525 |
526 | def encode_logstash(
527 | data, backslash_ignore_prefix='@@@', convert_bools=False,
528 | convert_nums=False, indent=" ", level=0, prevtype="",
529 | section_prefix=":"):
530 | """Convert Python data structure to Logstash format."""
531 |
532 | # Return value
533 | rv = ""
534 |
535 | if isinstance(data, dict):
536 | # The item is a dict
537 |
538 | if prevtype in ('value', 'value_hash', 'array'):
539 | rv += "{\n"
540 |
541 | items = sorted(data.items())
542 |
543 | for key, val in items:
544 | if key[0] == section_prefix:
545 | rv += "%s%s {\n" % (indent * level, key[1:])
546 | rv += encode_logstash(
547 | val,
548 | convert_bools=convert_bools,
549 | convert_nums=convert_nums,
550 | indent=indent,
551 | level=level+1,
552 | prevtype='block')
553 |
554 | # Last item of the loop
555 | if items[-1] == (key, val):
556 | if (
557 | isinstance(val, string_types) or
558 | _is_num(val) or
559 | isinstance(val, bool) or (
560 | isinstance(val, dict) and
561 | val and
562 | list(val.keys())[0][0] != section_prefix)):
563 | rv += "\n%s}\n" % (indent * level)
564 | else:
565 | rv += "%s}\n" % (indent * level)
566 | else:
567 | rv += indent * level
568 |
569 | if prevtype == 'value_hash':
570 | rv += '"%s" => ' % key
571 | else:
572 | rv += "%s => " % key
573 |
574 | rv += encode_logstash(
575 | val,
576 | convert_bools=convert_bools,
577 | convert_nums=convert_nums,
578 | indent=indent,
579 | level=level+1,
580 | prevtype=(
581 | 'value_hash' if isinstance(val, dict) else 'value'))
582 |
583 | if (
584 | items[-1] != (key, val) and (
585 | isinstance(val, string_types) or
586 | _is_num(val) or
587 | isinstance(val, bool))):
588 | rv += "\n"
589 |
590 | if prevtype in ('value', 'value_hash', 'array'):
591 | rv += "\n%s}" % (indent * (level-1))
592 |
593 | if prevtype in ('value', 'value_array'):
594 | rv += "\n"
595 |
596 | elif (
597 | _is_num(data) or
598 | isinstance(data, bool) or
599 | (convert_nums and _str_is_num(data)) or
600 | (convert_bools and _str_is_bool(data))):
601 | # It's number or boolean
602 |
603 | rv += str(data).lower()
604 |
605 | elif isinstance(data, string_types):
606 | # It's a string
607 |
608 | if data.startswith(backslash_ignore_prefix):
609 | rv += "%s" % data[len(backslash_ignore_prefix):]
610 | else:
611 | rv += '"%s"' % _escape(data)
612 |
613 | else:
614 | # It's a list
615 |
616 | for val in data:
617 | if isinstance(val, dict) and list(
618 | val.keys())[0][0] == section_prefix:
619 | # Value is a block
620 |
621 | rv += encode_logstash(
622 | val,
623 | convert_bools=convert_bools,
624 | convert_nums=convert_nums,
625 | indent=indent,
626 | level=level,
627 | prevtype='block')
628 | else:
629 | # First item of the loop
630 | if data[0] == val:
631 | rv += "[\n"
632 |
633 | rv += indent * level
634 | rv += encode_logstash(
635 | val,
636 | convert_bools=convert_bools,
637 | convert_nums=convert_nums,
638 | indent=indent,
639 | level=level+1,
640 | prevtype='array')
641 |
642 | # Last item of the loop
643 | if data[-1] == val:
644 | rv += "\n%s]" % (indent * (level-1))
645 | else:
646 | rv += ",\n"
647 |
648 | return rv
649 |
650 |
651 | def encode_lua(
652 | data, convert_bools=False, convert_nums=False,
653 | indent=' ', level=0, sort_keys=True):
654 | """Convert Python data structure to Lua format."""
655 |
656 | # Return value
657 | rv = ""
658 |
659 | if (
660 | _is_num(data) or
661 | (convert_nums and _str_is_num(data)) or
662 | (convert_bools and _str_is_bool(data))):
663 | # It's a number or boolean
664 | rv += str(data).lower() + ";"
665 |
666 | elif isinstance(data, string_types):
667 | if data == 'null':
668 | rv += "nil;"
669 | else:
670 | rv += '"%s";' % _escape(_escape(data), format="control")
671 |
672 | elif isinstance(data, list):
673 | rv += "{\n"
674 |
675 | for val in data:
676 | val = encode_lua(
677 | val,
678 | convert_bools=convert_bools,
679 | convert_nums=convert_nums,
680 | sort_keys=sort_keys,
681 | indent=indent,
682 | level=level + 1)
683 | rv += "%s%s\n" % (indent*level, val)
684 |
685 | rv += "%s}" % (indent*(level-1))
686 |
687 | if level > 1:
688 | rv += ";"
689 |
690 | elif isinstance(data, dict):
691 | if level > 0:
692 | rv += "{\n"
693 |
694 | if sort_keys:
695 | items = sorted(data.items())
696 | else:
697 | items = data.items()
698 | for key, val in items:
699 | val = encode_lua(
700 | val,
701 | convert_bools=convert_bools,
702 | convert_nums=convert_nums,
703 | sort_keys=sort_keys,
704 | indent=indent,
705 | level=level + 1)
706 | rv += "%s%s = %s\n" % (indent*level, key, val)
707 |
708 | if level > 0:
709 | rv += indent * (level - 1) + "}"
710 |
711 | if level > 1:
712 | rv += ";"
713 | else:
714 | raise errors.AnsibleFilterError(
715 | "Unexpected data type: %s" % (type(data)))
716 |
717 | return rv
718 |
719 |
720 | def encode_nginx(
721 | data, block_semicolon=False, indent=" ", level=0, semicolon=';',
722 | semicolon_ignore_postfix='!;'):
723 | """Convert Python data structure to Nginx format."""
724 |
725 | # Return value
726 | rv = ""
727 | # Indicates the item type [section|line]
728 | item_type = ""
729 |
730 | for item in data:
731 | if isinstance(item, dict):
732 | # Section
733 | if item_type in ('section', 'line'):
734 | rv += "\n"
735 |
736 | rv += "%s%s {\n" % (level*indent, list(item.keys())[0])
737 | rv += encode_nginx(
738 | list(item.values())[0],
739 | level=level+1,
740 | block_semicolon=block_semicolon,
741 | semicolon=semicolon,
742 | semicolon_ignore_postfix=semicolon_ignore_postfix)
743 | rv += "%s}%s\n" % (
744 | level*indent, semicolon if block_semicolon else '')
745 |
746 | item_type = 'section'
747 |
748 | elif isinstance(item, string_types):
749 | # Normal line
750 | if item_type == 'section':
751 | rv += "\n"
752 |
753 | item_type = 'line'
754 | ignore_semicolon = False
755 |
756 | if item.endswith(semicolon_ignore_postfix):
757 | item = item[:-len(semicolon_ignore_postfix)]
758 | ignore_semicolon = True
759 |
760 | rv += "%s%s" % (level*indent, item)
761 |
762 | # Do not finish comments with semicolon
763 | if item.startswith("# ") or ignore_semicolon:
764 | rv += "\n"
765 | else:
766 | rv += "%s\n" % (semicolon)
767 |
768 | else:
769 | raise errors.AnsibleFilterError(
770 | "Unexpected data type: %s" % (type(item)))
771 |
772 | return rv
773 |
774 |
775 | def encode_pam(
776 | data, print_label=False, separate_types=True, separator=" "):
777 | """Convert Python data structure to PAM format."""
778 |
779 | # Return value
780 | rv = ""
781 | # Remember previous type to make newline between type blocks
782 | prev_type = None
783 |
784 | for label, rule in sorted(data.items()):
785 | if separate_types:
786 | # Add extra newline to separate blocks of the same type
787 | if prev_type is not None and prev_type != rule['type']:
788 | rv += "\n"
789 |
790 | prev_type = rule['type']
791 |
792 | if print_label:
793 | rv += "# %s\n" % label
794 |
795 | if 'service' in rule:
796 | rv += "%s%s" % (rule['service'], separator)
797 |
798 | if 'silent' in rule and rule['silent']:
799 | rv += '-'
800 |
801 | rv += "%s%s" % (rule['type'], separator)
802 |
803 | if isinstance(rule['control'], list):
804 | rv += "[%s]%s" % (
805 | " ".join(
806 | map(
807 | lambda k: "=".join(map(str, k)),
808 | map(lambda x: list(x.items())[0], rule['control']))),
809 | separator)
810 | else:
811 | rv += "%s%s" % (rule['control'], separator)
812 |
813 | rv += rule['path']
814 |
815 | if 'args' in rule and rule['args']:
816 | rv += separator
817 |
818 | for i, arg in enumerate(rule['args']):
819 | if i > 0:
820 | rv += ' '
821 |
822 | if isinstance(arg, dict):
823 | rv += "=".join(map(str, list(arg.items())[0]))
824 | else:
825 | rv += arg
826 |
827 | rv += "\n"
828 |
829 | return rv
830 |
831 |
832 | def encode_toml(
833 | data, convert_bools=False, convert_nums=False, first=True, quote='"',
834 | table_name="", table_type=None):
835 | """Convert Python data structure to TOML format."""
836 |
837 | # Return value
838 | rv = ""
839 |
840 | if isinstance(data, dict):
841 | # It's a dict
842 |
843 | tn = table_name
844 |
845 | # First process all keys with elementar value (num/str/bool/array)
846 | for k, v in sorted(data.items()):
847 |
848 | if not (isinstance(v, dict) or isinstance(v, list)):
849 | if tn:
850 | if not first:
851 | rv += "\n"
852 |
853 | if table_type == 'table':
854 | rv += "[%s]\n" % tn
855 | else:
856 | rv += "[[%s]]\n" % tn
857 |
858 | rv += "%s = %s\n" % (
859 | k,
860 | encode_toml(
861 | v,
862 | convert_bools=convert_bools,
863 | convert_nums=convert_nums,
864 | first=first,
865 | quote=quote))
866 |
867 | first = False
868 | tn = ''
869 | elif isinstance(v, list) and (not v or not isinstance(v[0], dict)):
870 | if tn:
871 | if not first:
872 | rv += "\n"
873 |
874 | if table_type == 'table':
875 | rv += "[%s]\n" % tn
876 | else:
877 | rv += "[[%s]]\n" % tn
878 |
879 | rv += "%s = %s\n" % (
880 | k,
881 | encode_toml(
882 | v,
883 | convert_bools=convert_bools,
884 | convert_nums=convert_nums,
885 | first=first,
886 | quote=quote))
887 |
888 | first = False
889 | tn = ''
890 |
891 | if not data and table_type is not None:
892 | if not first:
893 | rv += "\n"
894 |
895 | if table_type == 'table':
896 | rv += "[%s]\n" % tn
897 | else:
898 | rv += "[[%s]]\n" % tn
899 |
900 | # Then process tables and arrays of tables
901 | for k, v in sorted(data.items()):
902 | tn = table_name
903 |
904 | if isinstance(v, dict):
905 | # Table
906 | tk = k
907 |
908 | if '.' in k:
909 | tk = "%s%s%s" % (quote, _escape(k, quote), quote)
910 |
911 | if tn:
912 | tn += ".%s" % tk
913 | else:
914 | tn += "%s" % tk
915 |
916 | rv += encode_toml(
917 | v,
918 | convert_bools=convert_bools,
919 | convert_nums=convert_nums,
920 | first=first,
921 | quote=quote,
922 | table_name=tn,
923 | table_type='table')
924 |
925 | first = False
926 | elif isinstance(v, list) and (not v or isinstance(v[0], dict)):
927 | # Array of tables
928 | tk = k
929 |
930 | if '.' in k:
931 | tk = "%s%s%s" % (quote, _escape(k, quote), quote)
932 |
933 | if tn:
934 | tn += ".%s" % tk
935 | else:
936 | tn += "%s" % tk
937 |
938 | for t in v:
939 | rv += encode_toml(
940 | t,
941 | convert_bools=convert_bools,
942 | convert_nums=convert_nums,
943 | first=first,
944 | quote=quote,
945 | table_name=tn,
946 | table_type='table_array')
947 |
948 | first = False
949 |
950 | elif isinstance(data, list):
951 |
952 | # Check if all values are elementar (num/str/bool/array)
953 | def is_elem(a):
954 | all_elementar = True
955 |
956 | for lv in a:
957 | if (
958 | isinstance(lv, dict) or (
959 | isinstance(lv, list) and
960 | not is_elem(lv))):
961 | all_elementar = False
962 | break
963 |
964 | return all_elementar
965 |
966 | if is_elem(data):
967 | v_len = len(data)
968 |
969 | array = ''
970 |
971 | for i, lv in enumerate(data):
972 | array += "%s" % encode_toml(
973 | lv,
974 | convert_bools=convert_bools,
975 | convert_nums=convert_nums,
976 | first=first,
977 | quote=quote)
978 |
979 | if i+1 < v_len:
980 | array += ', '
981 |
982 | rv += "[%s]" % (array)
983 |
984 | elif (
985 | _is_num(data) or
986 | isinstance(data, bool) or
987 | (convert_nums and _str_is_num(data)) or
988 | (convert_bools and _str_is_bool(data))):
989 | # It's number or boolean
990 |
991 | rv += str(data).lower()
992 |
993 | elif isinstance(data, string_types):
994 | # It's a string
995 |
996 | rv += "%s%s%s" % (quote, _escape(data, quote), quote)
997 |
998 | return rv
999 |
1000 |
1001 | def encode_xml(
1002 | data, attribute_sign="^", escape_xml=True, indent=" ", level=0):
1003 | """Convert Python data structure to XML format."""
1004 |
1005 | # Return value
1006 | rv = ""
1007 |
1008 | if isinstance(data, list):
1009 | # Pocess anything what's not attribute
1010 | for item in data:
1011 | if (
1012 | not (
1013 | isinstance(item, dict) and
1014 | list(item.keys())[0].startswith(attribute_sign))):
1015 | rv += encode_xml(
1016 | item,
1017 | attribute_sign=attribute_sign,
1018 | indent=indent,
1019 | level=level,
1020 | escape_xml=escape_xml)
1021 | elif isinstance(data, dict):
1022 | # It's eiher an attribute or an element
1023 |
1024 | key, val = list(data.items())[0]
1025 |
1026 | if key.startswith(attribute_sign):
1027 | # Process attribute
1028 | rv += ' %s="%s"' % (key[1:], _escape(val))
1029 | else:
1030 | # Process element
1031 | rv = '%s<%s' % (level*indent, key)
1032 |
1033 | # Check if there are any attributes
1034 | if isinstance(val, list):
1035 | num_attrs = 0
1036 |
1037 | for item in val:
1038 | if (
1039 | isinstance(item, dict) and
1040 | list(item.keys())[0].startswith(attribute_sign)):
1041 | num_attrs += 1
1042 | rv += encode_xml(
1043 | item,
1044 | attribute_sign=attribute_sign,
1045 | indent=indent,
1046 | level=level)
1047 |
1048 | if val == '' or (isinstance(val, list) and num_attrs == len(val)):
1049 | # Close the element as empty
1050 | rv += " />\n"
1051 | else:
1052 | # Close the element as normal
1053 | rv += ">"
1054 |
1055 | # Check if the value is text
1056 | val_not_text = False
1057 |
1058 | if isinstance(val, list):
1059 | # Check if it contains only attributes and a text value
1060 | for item in val:
1061 | if (isinstance(item, dict) and not list(
1062 | item.keys())[0].startswith(attribute_sign)):
1063 | val_not_text = True
1064 | break
1065 | elif isinstance(val, dict):
1066 | val_not_text = True
1067 |
1068 | if val_not_text:
1069 | rv += "\n"
1070 |
1071 | # Process inner content of the element
1072 | rv += encode_xml(
1073 | val,
1074 | attribute_sign=attribute_sign,
1075 | indent=indent,
1076 | level=level+1,
1077 | escape_xml=escape_xml)
1078 |
1079 | if val_not_text:
1080 | rv += level*indent
1081 |
1082 | rv += "%s>\n" % key
1083 | else:
1084 | # It's a string
1085 |
1086 | rv += "%s" % _escape(data, format=('xml' if escape_xml else None))
1087 |
1088 | return rv
1089 |
1090 |
1091 | def encode_yaml(
1092 | data, block_prefix=';;;', convert_bools=False, convert_nums=False,
1093 | indent=" ", level=0, quote='"', skip_indent=False):
1094 | """Convert Python data structure to YAML format."""
1095 |
1096 | # Return value
1097 | rv = ""
1098 |
1099 | if isinstance(data, dict):
1100 | # It's a dictionary
1101 |
1102 | if len(data.keys()) == 0:
1103 | rv += "{}\n"
1104 | else:
1105 | for i, (key, val) in enumerate(sorted(data.items())):
1106 | # Skip indentation only for the first pair
1107 | rv += "%s%s:" % (
1108 | "" if i == 0 and skip_indent else level*indent, key)
1109 |
1110 | if isinstance(val, dict) and len(val.keys()) == 0:
1111 | rv += " {}\n"
1112 | else:
1113 | if (
1114 | isinstance(val, dict) or (
1115 | isinstance(val, list) and
1116 | len(val) != 0)):
1117 | rv += "\n"
1118 | else:
1119 | rv += " "
1120 |
1121 | rv += encode_yaml(
1122 | val,
1123 | block_prefix=block_prefix,
1124 | convert_bools=convert_bools,
1125 | convert_nums=convert_nums,
1126 | indent=indent,
1127 | level=level+1,
1128 | quote=quote)
1129 |
1130 | elif isinstance(data, list):
1131 | # It's a list
1132 |
1133 | if len(data) == 0:
1134 | rv += "[]\n"
1135 | else:
1136 | for item in data:
1137 | if isinstance(item, list):
1138 | list_indent = "%s-\n" % (level*indent)
1139 | else:
1140 | list_indent = "%s- " % (level*indent)
1141 |
1142 | rv += "%s%s" % (list_indent, encode_yaml(
1143 | item,
1144 | block_prefix=block_prefix,
1145 | convert_bools=convert_bools,
1146 | convert_nums=convert_nums,
1147 | indent=indent,
1148 | level=level+1,
1149 | quote=quote,
1150 | skip_indent=True))
1151 |
1152 | elif (
1153 | data == "null" or
1154 | isinstance(data, bool) or
1155 | (convert_bools and _str_is_bool(data))):
1156 | # It's a boolean
1157 |
1158 | rv += "%s\n" % str(data).lower()
1159 |
1160 | elif (
1161 | _is_num(data) or
1162 | (convert_nums and _str_is_num(data))):
1163 | # It's a number
1164 |
1165 | rv += "%s\n" % str(data)
1166 |
1167 | else:
1168 | # It's a string
1169 |
1170 | if data is None:
1171 | rv += "null\n"
1172 | elif data.startswith(block_prefix):
1173 | rv += "%s\n" % data[len(block_prefix):].replace(
1174 | "\n", "\n%s" % (level*indent))
1175 | else:
1176 | rv += "%s%s%s\n" % (quote, _escape(data, quote), quote)
1177 |
1178 | return rv
1179 |
1180 |
1181 | def __eval_replace(match):
1182 | """Evaluate the real value of the variable specified as a string."""
1183 |
1184 | ret = '__item'
1185 | ret += ''.join(match.groups()[1:])
1186 |
1187 | # Try to evaluate the value of the special string
1188 | try:
1189 | ret = eval(ret)
1190 | except Exception:
1191 | # Return empty string if something went wrong
1192 | ret = ''
1193 |
1194 | return str(ret)
1195 |
1196 |
1197 | def template_replace(data, replacement):
1198 | """Replace special template decorated variable with its real value."""
1199 |
1200 | # Make the replacement variable visible for the __eval_replace function
1201 | global __item
1202 | __item = replacement
1203 |
1204 | # Clone the data to keep the original untouched
1205 | local_data = copy(data)
1206 |
1207 | # Walk through the data structure and try to replace all special strings
1208 | if isinstance(local_data, list):
1209 | local_data = map(
1210 | lambda x: template_replace(x, replacement), local_data)
1211 | elif isinstance(local_data, dict):
1212 | for key, val in local_data.items():
1213 | local_data[key] = template_replace(val, replacement)
1214 | elif isinstance(local_data, string_types):
1215 | # Replace the special string by it's evaluated value
1216 | p = re.compile(r'\{\[\{\s*(\w+)([^}\s]+|)\s*\}\]\}')
1217 | local_data = p.sub(__eval_replace, local_data)
1218 |
1219 | return local_data
1220 |
1221 |
1222 | class FilterModule(object):
1223 | """Ansible encoder Jinja2 filters."""
1224 |
1225 | def filters(self):
1226 | """Expose filters to ansible."""
1227 |
1228 | return {
1229 | 'encode_apache': encode_apache,
1230 | 'encode_erlang': encode_erlang,
1231 | 'encode_haproxy': encode_haproxy,
1232 | 'encode_ini': encode_ini,
1233 | 'encode_json': encode_json,
1234 | 'encode_logstash': encode_logstash,
1235 | 'encode_lua': encode_lua,
1236 | 'encode_nginx': encode_nginx,
1237 | 'encode_pam': encode_pam,
1238 | 'encode_toml': encode_toml,
1239 | 'encode_xml': encode_xml,
1240 | 'encode_yaml': encode_yaml,
1241 | 'template_replace': template_replace,
1242 | }
1243 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | Config Encoder Filters
2 | ======================
3 |
4 | This is an Ansible role used to deliver the Config Encoder Filters as
5 | a dependency of another Ansible role.
6 |
7 | .. image:: https://travis-ci.org/jtyr/ansible-config_encoder_filters.svg?branch=master
8 | :target: https://travis-ci.org/jtyr/ansible-config_encoder_filters
9 |
10 |
11 | Table of Contents
12 | -----------------
13 |
14 | - Motivation_
15 | - Example_
16 | - Usage_
17 | - Installation_
18 | - `Supported encoders`_
19 | - encode_apache_
20 | - encode_erlang_
21 | - encode_haproxy_
22 | - encode_ini_
23 | - encode_json_
24 | - encode_logstash_
25 | - encode_lua_
26 | - encode_nginx_
27 | - encode_pam_
28 | - encode_toml_
29 | - encode_xml_
30 | - encode_yaml_
31 | - Utilities_
32 | - template_replace_
33 | - Testing_
34 | - License_
35 | - Author_
36 |
37 | ----
38 |
39 |
40 | .. _Motivation:
41 |
42 | Motivation
43 | ----------
44 |
45 | Ansible Galaxy contains a lot of useful roles. Some of them exist in
46 | many variations which differ only by their parameterization. The
47 | parameterization is often used mainly in templates which generate the
48 | configuration file. A good example such issues are roles for Nginx of
49 | which you can find almost 200 in the Ansible Galaxy.
50 |
51 | Nginx is possible to configure in infinite number of ways and therefore
52 | is almost impossible to create an Ansible template file which would
53 | capture all possible variations of the configuration. Even if a suitable
54 | roles is found, users often want to customize even more. This is where
55 | people normally clone the role and add parameters they are missing. Some
56 | people try to get the change back into the original role by creating a
57 | pull request (PR) but sometimes such change is not accepted by the
58 | maintainer of the original role and the user ends up maintaining his/her
59 | own clone forever.
60 |
61 | This is why the Config Encoder filters were developed to facilitate the
62 | creation of Ansible roles with universal configuration. The structure of
63 | the configuration file is described as a YAML data structure stored in a
64 | variable. The variable together with he Config Encoder filter is then
65 | used in the template file which is used to generate the final
66 | configuration file. This approach allows to shift the paradigm of
67 | thinking about configuration files as templates to thinking about them as
68 | data structures. The data structure can be dynamically generated which
69 | allows to create truly universal configuration.
70 |
71 |
72 | .. _Example:
73 |
74 | Example
75 | -------
76 |
77 | Imagine the following INI file:
78 |
79 | .. code:: ini
80 |
81 | [section1]
82 | option11=value11
83 | option12=value12
84 |
85 | Such configuration file can be described as a YAML data structure:
86 |
87 | .. code:: yaml
88 |
89 | myapp_config:
90 | section1:
91 | option11: value11
92 | option12: value12
93 |
94 | The variable is then used together with the ``encode_ini`` Config Encoder
95 | filter in the template file ``myapp.cfg.j2`` like this:
96 |
97 | .. code:: jinja2
98 |
99 | {{ myapp_config | encode_ini }}
100 |
101 | And finally, the template file is used in a task like this:
102 |
103 | .. code:: yaml
104 |
105 | - name: Create config file
106 | template:
107 | src: myapp.cfg.j2
108 | dest: /etc/myapp/myapp.cfg
109 |
110 | When the task is executed, it creates exactly the same file as the
111 | original INI file.
112 |
113 | So we can describe the configuration as a data structure which is then
114 | converted into the final configuration file format with the Config
115 | Encoder filter.
116 |
117 | In order to change the above configuration, we would have to overwrite
118 | the ``myapp_config`` which is not very practical. Therefore we break the
119 | monolithic variable into a set of variables which will allow us to change
120 | any part of the configuration without the need to overwrite the whole
121 | data structure:
122 |
123 | .. code:: yaml
124 |
125 | myapp_config_section1_option11: value11
126 | myapp_config_section1_option12: value12
127 |
128 | myapp_config_section1__default:
129 | option11: "{{ myapp_config_section1_option11 }}"
130 | option12: "{{ myapp_config_section1_option12 }}"
131 |
132 | myapp_config_section1__custom: {}
133 |
134 | myapp_config_default:
135 | section1: "{{
136 | myapp_config_section1__default | combine(
137 | myapp_config_section1__custom) }}"
138 |
139 | myapp_config__custom: {}
140 |
141 | myapp_config: "{{
142 | myapp_config__default | combine(
143 | myapp_config__custom) }}"
144 |
145 | Like this, if we want to change the value of the ``option11``, we only
146 | override the variable ``myapp_config_section1_option11``:
147 |
148 | .. code:: yaml
149 |
150 | myapp_config_section1_option11: My new value
151 |
152 | If we want to add a new option into the ``section1``, we add it into the
153 | variable ``myapp_config_section1__custom`` which is then merged with the
154 | default list of options:
155 |
156 | .. code:: yaml
157 |
158 | myapp_config_section1__custom:
159 | section13: value13
160 |
161 | And if we want to add a new section, we add it into the variable
162 | ``myapp_config__custom`` which is then merged with the default list of
163 | sections:
164 |
165 | .. code:: yaml
166 |
167 | myapp_config__custom:
168 | section2:
169 | option21: value21
170 |
171 | The above is showing an example for INI configuration files only but the
172 | same principle is possible to use for all the supported Config Encoders
173 | listed bellow.
174 |
175 |
176 | .. _Usage:
177 |
178 | Usage
179 | -----
180 |
181 | Config Encoder filters can be used in any Ansible role by adding the
182 | ``config_encoder_filters`` role into the list of dependencies in the
183 | ``meta/main.yml`` file:
184 |
185 | .. code:: yaml
186 |
187 | dependencies:
188 | - config_encoder_filters
189 |
190 | The usage directy from a Playbook requires to add the
191 | ``config_encoder_filters`` into the list of roles:
192 |
193 | .. code:: yaml
194 |
195 | - name: My test Play
196 | hosts: all
197 | roles:
198 | - config_encoder_filters
199 | tasks:
200 | - name: Create config file
201 | template:
202 | src: my.conf.j2
203 | dest: /tmp/my.conf
204 |
205 |
206 | .. _Installation:
207 |
208 | Installation
209 | ------------
210 |
211 | The role can be downloaded either via Ansible Galaxy command:
212 |
213 | .. code:: shell
214 |
215 | $ ansible-galaxy install jtyr.config_encoder_filters,master,config_encoder_filters
216 |
217 | or via Ansible Gallaxy requirements file:
218 |
219 | .. code:: shell
220 |
221 | $ cat ./requirements.yaml
222 | ---
223 |
224 | - src: https://github.com/jtyr/ansible-config_encoder_filters.git
225 | name: config_encoder_filters
226 | $ ansible-galaxy -r ./requirements.yaml
227 |
228 | or via Git:
229 |
230 | .. code:: shell
231 |
232 | $ git clone https://github.com/jtyr/ansible-config_encoder_filters.git config_encoder_filters
233 |
234 |
235 | .. _`Supported encoders`:
236 |
237 | Supported encoders
238 | ------------------
239 |
240 | The following is the list of supported Config Encoder filters. Each
241 | filter requires special data structure as its input. Each filter also has
242 | a set of parameters which can modify the behaviour of the filter.
243 |
244 |
245 | .. _encode-apache:
246 |
247 | encode_apache
248 | ^^^^^^^^^^^^^
249 |
250 | This filter helps to create configuration in the format used by Apache
251 | web server. The expected data structure is the following:
252 |
253 | .. code:: yaml
254 |
255 | my_apache_vhost:
256 | content:
257 | - sections:
258 | - name: VirtualHost
259 | param: "*:80"
260 | content:
261 | - options:
262 | - DocumentRoot: /www/example1
263 | - ServerName: www.example.com
264 | - ErrorLog: /var/log/httpd/www.example.com-error_log
265 | - CustomLog:
266 | - /var/log/httpd/www.example.com-access_log
267 | - common
268 | - "#": Other directives here ...
269 |
270 | The variable starts with ``content`` which can contain list of
271 | ``sections`` or ``options``. ``sections`` then contain list of individual
272 | sections which has the ``name``, ``param`` and ``content`` parameter. The
273 | ``content`` can again contain a list of `sections`` or ``options``.
274 |
275 | The above variable can be used in the template file like this:
276 |
277 | .. code:: jinja2
278 |
279 | {{ my_apache_vhost | encode_apache }}
280 |
281 | The output of such template would be:
282 |
283 | .. code:: apache
284 |
285 |
286 | DocumentRoot /www/example1
287 | ServerName www.example.com
288 | ErrorLog /var/log/httpd/www.example.com-error_log
289 | CustomLog /var/log/httpd/www.example.com-access_log common
290 | # "Other directives here ..."
291 |
292 |
293 | The filter can have the following parameters:
294 |
295 | - ``convert_bools=false``
296 |
297 | Indicates whether Boolean values presented as a string should be
298 | converted to a real Boolean value. For example ``var1: 'True'`` would
299 | be represented as a string but by using the ``convert_bools=true`` it
300 | will be converted into Boolean like it would be defined like ``var1:
301 | true``.
302 |
303 | - ``convert_nums=false``
304 |
305 | Indicates whether number presented as a string should be converted to
306 | number. For example ``var1: '123'`` would be represented as a string
307 | but by using the ``convert_nums=true`` it will be converted it to a
308 | number like it would be defined like ``var1: 123``. It's also possible
309 | to use the YAML type casting to convert string to number (e.g. ``!!int
310 | "1234"``, ``!!float "3.14"``).
311 |
312 | - ``indent=" "``
313 |
314 | Defines the indentation unit.
315 |
316 | - ``level=0``
317 |
318 | Indicates the initial level of the indentation. Value ``0`` starts
319 | indenting from the beginning of the line. Setting the value to higher
320 | than ``0`` indents the content by ``indent * level``.
321 |
322 | - ``quote_all_nums=false``
323 |
324 | Number values are not quoted by default. This parameter will force to
325 | quote all numbers.
326 |
327 | - ``quote_all_strings=false``
328 |
329 | String values are quoted only if they contain a space. This parameter
330 | will force to quote all strings regardless if the they contain the
331 | space or not.
332 |
333 |
334 | .. _encode-erlang:
335 |
336 | encode_erlang
337 | ^^^^^^^^^^^^^
338 |
339 | This filter helps to create configuration in the Erlang format. The
340 | expected data structure is the following:
341 |
342 | .. code:: yaml
343 |
344 | my_rabbitmq_config:
345 | - rabbit:
346 | - tcp_listeners:
347 | - '"127.0.0.1"': 5672
348 | - ssl_listeners:
349 | - 5671
350 | - ssl_options:
351 | - cacertfile: /path/to/testca/cacert.pem
352 | - certfile: /path/to/server/cert.pem
353 | - keyfile: /path/to/server/key.pem
354 | - verify: :verify_peer
355 | - fail_if_no_peer_cert: true
356 | - cluster_nodes:
357 | ::
358 | -
359 | - :"'rabbit@node1'"
360 | - :"'rabbit@node2'"
361 | - :disc
362 |
363 | The variable consists of a lists of dictionaries. The value of the key-value
364 | pair can be another list or simple value like a string or a number. Erlang
365 | tuples can be enforced by prepending the value with the special character
366 | specified in the ``atom_value_indicator``. Order in tuple can be achieved by
367 | using special construction as shown for the ``cluste_nodes`` tuple from the
368 | above example. The indicator starting this special construction can be set with
369 | the parameter ``ordered_tuple_indicator``.
370 |
371 | The above variable can be used in the template file like this:
372 |
373 | .. code:: jinja2
374 |
375 | {{ my_rabbitmq_config | encode_erlang }}
376 |
377 | The output of such template would be:
378 |
379 | .. code:: erlang
380 |
381 | [
382 | {rabbit, [
383 | {tcp_listeners, [
384 | {"127.0.0.1", 5672}
385 | ]},
386 | {ssl_listeners, [
387 | 5671
388 | ]},
389 | {ssl_options, [
390 | {cacertfile, "/path/to/testca/cacert.pem"},
391 | {certfile, "/path/to/server/cert.pem"},
392 | {keyfile, "/path/to/server/key.pem"},
393 | {verify, verify_peer},
394 | {fail_if_no_peer_cert, true}
395 | ]},
396 | {cluster_nodes,
397 | {[
398 | 'rabbit@node1',
399 | 'rabbit@node2'
400 | ], disc}}
401 | ]}
402 | ].
403 |
404 | The filter can have the following parameters:
405 |
406 | - ``atom_value_indicator=":"``
407 |
408 | The value of this parameter indicates the string which must be
409 | prepended to a string value to treat it as an atom value.
410 |
411 | - ``convert_bools=false``
412 |
413 | Indicates whether Boolean values presented as a string should be
414 | converted to a real Boolean value. For example ``var1: 'True'`` would
415 | be represented as a string but by using the ``convert_bools=true`` it
416 | will be converted into Boolean like it would be defined like ``var1:
417 | true``.
418 |
419 | - ``convert_nums=false``
420 |
421 | Indicates whether number presented as a string should be converted to
422 | number. For example ``var1: '123'`` would be represented as a string
423 | but by using the ``convert_nums=true`` it will be converted it to a
424 | number like it would be defined like ``var1: 123``. It's also possible
425 | to use the YAML type casting to convert string to number (e.g. ``!!int
426 | "1234"``, ``!!float "3.14"``).
427 |
428 | - ``indent=" "``
429 |
430 | Defines the indentation unit.
431 |
432 | - ``level=0``
433 |
434 | Indicates the initial level of the indentation. Value ``0`` starts
435 | indenting from the beginning of the line. Setting the value to higher
436 | than ``0`` indents the content by ``indent * level``.
437 |
438 | - ``ordered_tuple_indicator=":"``
439 |
440 | Indicator used to start the special construction with ordered tuple.
441 |
442 |
443 | .. _encode-haproxy:
444 |
445 | encode_haproxy
446 | ^^^^^^^^^^^^^^
447 |
448 | This filter helps to create configuration in the format used in Haproxy.
449 | The expected data structure is the following:
450 |
451 | .. code:: yaml
452 |
453 | my_haproxy_config:
454 | - global:
455 | - daemon
456 | - maxconn 256
457 | - "# This is the default section"
458 | - defaults:
459 | - mode http
460 | - timeout:
461 | - connect 5000ms
462 | - client 50000ms
463 | - server 50000ms
464 | - frontend http-in:
465 | - "# This is the bind address/port"
466 | - bind *:80
467 | - default_backend servers
468 | - backend servers
469 | - server server1 127.0.0.1:8000 maxconn 32
470 |
471 | The variable is a list which can contain a simple string value or a dictionary
472 | which indicates a section.
473 |
474 | The above variable can be used in the template file like this:
475 |
476 | .. code:: jinja2
477 |
478 | {{ my_haproxy_config | encode_haproxy }}
479 |
480 | The output of such template would be:
481 |
482 | .. code:: haproxy
483 |
484 | global
485 | daemon
486 | maxconn 256
487 |
488 | # This is the default section
489 | defaults
490 | mode http
491 | timeout connect 5000ms
492 | timeout client 50000ms
493 | timeout server 50000ms
494 |
495 | frontend http-in
496 | # This is the bind address/port
497 | bind *:80
498 | default_backend servers
499 | backend servers
500 | server server1 127.0.0.1:8000 maxconn 32
501 |
502 | The filter can have the following parameters:
503 |
504 | - ``indent=" "``
505 |
506 | Defines the indentation unit.
507 |
508 |
509 | .. _encode-ini:
510 |
511 | encode_ini
512 | ^^^^^^^^^^
513 |
514 | This filter helps to create configuration in the INI format. The expected
515 | data structure is the following:
516 |
517 | .. code:: yaml
518 |
519 | my_rsyncd_config:
520 | uid: nobody
521 | gid: nobody
522 | use chroot: no
523 | max connections: 4
524 | syslog facility: local5
525 | pid file: /run/rsyncd.pid
526 | ftp:
527 | path: /srv/ftp
528 | comment: ftp area
529 |
530 | The variable consist of dictionaries which can be nested. If the value of the
531 | key-value pair on the first level is of a simple type (string, number, boolean),
532 | such pair is considered to be global and gets processed first. If the value of
533 | the key-value pair on the first level is another dictionary, the key is
534 | considered to be the name of the section and the inner dictionary as properties
535 | of the section.
536 |
537 | The above variable can be used in the template file like this:
538 |
539 | .. code:: jinja2
540 |
541 | {{ my_rsyncd_config | encode_ini }}
542 |
543 | The output of such template would be:
544 |
545 | .. code:: ini
546 |
547 | gid=nobody
548 | max connections=4
549 | pid file=/run/rsyncd.pid
550 | syslog facility=local5
551 | uid=nobody
552 | use chroot=False
553 |
554 | [ftp]
555 | comment=ftp area
556 | path=/srv/ftp
557 |
558 | The special value :code:`!!!null` can be used, to create a key without any value.
559 | This must be wrapped in quotes, to avoid being evaluated as a YAML tag.
560 |
561 | Take the following data structure:
562 |
563 | .. code:: yaml
564 |
565 | config:
566 | myconf:
567 | key: value
568 | keyWithoutValue: '!!!null'
569 |
570 | This would become:
571 |
572 | .. code:: ini
573 |
574 | [myconf]
575 | key=value
576 | keyWithoutValue
577 |
578 | The filter can have the following parameters:
579 |
580 | - ``comment="#"``
581 |
582 | Sign used to comment out lines when `section_is_comment=true`.
583 |
584 | - ``delimiter="="``
585 |
586 | Sign separating the *property* and the *value*. By default it's set to
587 | ``'='`` but it can also be set for example to ``' = '``.
588 |
589 | - ``indent=""``
590 |
591 | Indent the keys with the specified string. E.g. ``indent="\t"``.
592 |
593 | - ``quote=""``
594 |
595 | Sets the quoting of the value. Use ``quote="'"`` or ``quote='"'``.
596 |
597 | - ``section_is_comment=false``
598 |
599 | If this parameter is set to ``true``, the section value will be used as
600 | a comment for the following properties of the section.
601 |
602 | - ``ucase_prop=false``
603 |
604 | Indicates whether the *property* should be made upper case.
605 |
606 |
607 | .. _encode-json:
608 |
609 | encode_json
610 | ^^^^^^^^^^^
611 |
612 | This filter helps to create configuration in the JSON format. The
613 | expected data structure is the following:
614 |
615 | .. code:: yaml
616 |
617 | my_sensu_client_config:
618 | client:
619 | name: localhost
620 | address: 127.0.0.1
621 | subscriptions:
622 | - test
623 |
624 | Because JSON is very similar to YAML, the variable consists of
625 | dictionaries of which value can be either an simple type (number, string,
626 | boolean), list or another dictionary. All can be nested in any number of
627 | levels.
628 |
629 | The above variable can be used in the template file like this:
630 |
631 | .. code:: jinja2
632 |
633 | {{ my_sensu_client_config | encode_json }}
634 |
635 | The output of such template would be:
636 |
637 | .. code:: json
638 |
639 | {
640 | "client": {
641 | "address": "127.0.0.1",
642 | "name": "localhost",
643 | "subscriptions": [
644 | "test"
645 | ]
646 | }
647 | }
648 |
649 | The filter can have the following parameters:
650 |
651 | - ``convert_bools=false``
652 |
653 | Indicates whether Boolean values presented as a string should be
654 | converted to a real Boolean value. For example ``var1: 'True'`` would
655 | be represented as a string but by using the ``convert_bools=true`` it
656 | will be converted into Boolean like it would be defined like ``var1:
657 | true``.
658 |
659 | - ``convert_nums=false``
660 |
661 | Indicates whether number presented as a string should be converted to
662 | number. For example ``var1: '123'`` would be represented as a string
663 | but by using the ``convert_nums=true`` it will be converted it to a
664 | number like it would be defined like ``var1: 123``. It's also possible
665 | to use the YAML type casting to convert string to number (e.g. ``!!int
666 | "1234"``, ``!!float "3.14"``).
667 |
668 | - ``indent=" "``
669 |
670 | Defines the indentation unit.
671 |
672 | - ``level=0``
673 |
674 | Indicates the initial level of the indentation. Value ``0`` starts
675 | indenting from the beginning of the line. Setting the value to higher
676 | than ``0`` indents the content by ``indent * level``.
677 |
678 |
679 | .. _encode-logstash:
680 |
681 | encode_logstash
682 | ^^^^^^^^^^^^^^^
683 |
684 | This filter helps to create configuration in the format used by Logstash.
685 | The expected data structure is the following:
686 |
687 | .. code:: yaml
688 |
689 | my_logstash_config:
690 | - :input:
691 | - :file:
692 | path: /var/log/httpd/access_log
693 | start_position: beginning
694 | - :filter:
695 | - ':if [path] =~ "access"':
696 | - :mutate:
697 | replace:
698 | type: apache_access
699 | - :grok:
700 | match:
701 | message: "%{COMBINEDAPACHELOG}"
702 | - :date:
703 | match:
704 | - timestamp
705 | - dd/MMM/yyyy:HH:mm:ss Z
706 | - ':else if [path] =~ "error"':
707 | - :mutate:
708 | replace:
709 | type: "apache_error"
710 | - :else:
711 | - :mutate:
712 | replace:
713 | type: "random_logs"
714 | - :output:
715 | - :elasticsearch:
716 | hosts:
717 | - localhost:9200
718 | - :stdout:
719 | codec: rubydebug
720 |
721 | The variable consists of a list of sections where each section is
722 | prefixed by a special character specified by the ``section_prefix``
723 | (``:`` by default). The value of the top level sections can be either
724 | another section or a dictionary. The value of the dictionary can be a
725 | simple value, list or another dictionary.
726 |
727 | The above variable can be used in the template file like this:
728 |
729 | .. code:: jinja2
730 |
731 | {{ my_logstash_config | encode_logstash }}
732 |
733 | The output of such template would be:
734 |
735 | .. code:: logstash
736 |
737 | input {
738 | file {
739 | path => "/var/log/httpd/access_log"
740 | start_position => "beginning"
741 | }
742 | }
743 | filter {
744 | if [path] =~ "access" {
745 | mutate {
746 | replace => {
747 | "type" => "apache_access"
748 | }
749 | }
750 | grok {
751 | match => {
752 | "message" => "%{COMBINEDAPACHELOG}"
753 | }
754 | }
755 | date {
756 | match => [
757 | "timestamp",
758 | "dd/MMM/yyyy:HH:mm:ss Z"
759 | ]
760 | }
761 | }
762 | else if [path] =~ "error" {
763 | mutate {
764 | replace => {
765 | "type" => "apache_error"
766 | }
767 | }
768 | }
769 | else {
770 | mutate {
771 | replace => {
772 | "type" => "random_logs"
773 | }
774 | }
775 | }
776 | }
777 | output {
778 | elasticsearch {
779 | hosts => [
780 | "localhost:9200"
781 | ]
782 | }
783 | stdout {
784 | codec => "rubydebug"
785 | }
786 | }
787 |
788 | The filter can have the following parameters:
789 |
790 | - ``backslash_ignore_prefix='@@@'``
791 |
792 | This parameter defines a sets of characters than can be prepended to a string
793 | to prevent backslahes from being escaped in the resulting configuration (e.g.
794 | ``"@@@sshd(?:\[%{POSINT:[system][auth][pid]}\])?:"`` will turn to
795 | ``"sshd(?:\[%{POSINT:[system][auth][pid]}\])?:"`` instead of to
796 | ``"sshd(?:\\[%{POSINT:[system][auth][pid]}\\])?:"``).
797 |
798 | - ``convert_bools=false``
799 |
800 | Indicates whether Boolean values presented as a string should be
801 | converted to a real Boolean value. For example ``var1: 'True'`` would
802 | be represented as a string but by using the ``convert_bools=true`` it
803 | will be converted into Boolean like it would be defined like ``var1:
804 | true``.
805 |
806 | - ``convert_nums=false``
807 |
808 | Indicates whether number presented as a string should be converted to
809 | number. For example ``var1: '123'`` would be represented as a string
810 | but by using the ``convert_nums=true`` it will be converted it to a
811 | number like it would be defined like ``var1: 123``. It's also possible
812 | to use the YAML type casting to convert string to number (e.g. ``!!int
813 | "1234"``, ``!!float "3.14"``).
814 |
815 | - ``indent=" "``
816 |
817 | Defines the indentation unit.
818 |
819 | - ``level=0``
820 |
821 | Indicates the initial level of the indentation. Value ``0`` starts
822 | indenting from the beginning of the line. Setting the value to higher
823 | than ``0`` indents the content by ``indent * level``.
824 |
825 | - ``section_prefix=":"``
826 |
827 | This parameter specifies which character will be used to identify the
828 | Logstash section.
829 |
830 |
831 | .. _encode-lua:
832 |
833 | encode_lua
834 | ^^^^^^^^^^
835 |
836 | This filter helps to create configuration in a Lua friendly format.
837 | The expected data structure is the following:
838 |
839 | .. code:: yaml
840 |
841 | my_lua_config:
842 | fork: false
843 | external_addresses:
844 | - 1.2.3.4
845 | - 5.6.7.8
846 | admins:
847 | - admin@example.com
848 | contact_info:
849 | abuse: abuse@example.com
850 | admin: admin@example.com
851 |
852 | Lua is a small scripting language, often embedded into C/C++ applications.
853 | This encoder does a best effort to match configuration files seen in the wild,
854 | while allowing the user to further customize how the final output is rendered.
855 |
856 | The above variable can be used in the template file like this:
857 |
858 | .. code:: jinja2
859 |
860 | {{ my_lua_config | encode_lua }}
861 |
862 | The output of such a template would be:
863 |
864 | .. code:: lua
865 |
866 | fork = false;
867 | external_addresses = {
868 | "1.2.3.4";
869 | "5.6.7.8";
870 | }
871 | admins = {
872 | "admin@example.com";
873 | }
874 | contact_info = {
875 | abuse = "abuse@example.com";
876 | admin = "admin@example.com";
877 | }
878 |
879 | The filter can have the following parameters:
880 |
881 | - ``convert_bools=false``
882 |
883 | Indicates whether Boolean values presented as a string should be
884 | converted to a real Boolean value. For example ``var1: 'True'`` would
885 | be represented as a string but by using the ``convert_bools=true`` it
886 | will be converted into Boolean like it would be defined like ``var1:
887 | true``.
888 |
889 | - ``convert_nums=false``
890 |
891 | Indicates whether number presented as a string should be converted to
892 | number. For example ``var1: '123'`` would be represented as a string
893 | but by using the ``convert_nums=true`` it will be converted it to a
894 | number like it would be defined like ``var1: 123``. It's also possible
895 | to use the YAML type casting to convert string to number (e.g. ``!!int
896 | "1234"``, ``!!float "3.14"``).
897 |
898 | - ``sort_keys=false``
899 |
900 | Indicates whether the keys should be sorted when the output is rendered,
901 | or left to python's implicit handling of dict ordering.
902 |
903 | - ``indent=" "``
904 |
905 | Defines the indentation unit.
906 |
907 | - ``level=0``
908 |
909 | Indicates the initial level of the indentation. Value ``0`` starts
910 | indenting from the beginning of the line. Setting the value to higher
911 | than ``0`` indents the content by ``indent * level``.
912 |
913 |
914 | .. _encode-nginx:
915 |
916 | encode_nginx
917 | ^^^^^^^^^^^^
918 |
919 | This filter helps to create configuration in the format used by Nginx
920 | web server. The expected data structure is the following:
921 |
922 | .. code:: yaml
923 |
924 | my_nginx_vhost_config:
925 | - server:
926 | - listen 80
927 | - server_name $hostname
928 | - "location /":
929 | - root /srv/www/myapp
930 | - index index.html
931 |
932 | As Nginx configuration is order sensitive, the all configuration is
933 | defined as a nested list. As it would be difficult to recognize how many
934 | elements each configuration definition has, the list item value is no
935 | further separated into key/value dictionary. Every line of the
936 | configuration is treated either as a key indicating another nested list
937 | or simply as a string.
938 |
939 | The above variable can be used in the template file like this:
940 |
941 | .. code:: jinja2
942 |
943 | {{ my_nginx_vhost | encode_nginx }}
944 |
945 | The output of such template would be:
946 |
947 | .. code:: nginx
948 |
949 | server {
950 | listen 80;
951 | server_name $hostname;
952 |
953 | location / {
954 | root /srv/www/myapp;
955 | index index.html;
956 | }
957 | }
958 |
959 | The filter can have the following parameters:
960 |
961 | - ``block_semicolon=false``
962 |
963 | Allows to add a semicolon to the end of each block.
964 |
965 | - ``indent=" "``
966 |
967 | Defines the indentation unit.
968 |
969 | - ``level=0``
970 |
971 | Indicates the initial level of the indentation. Value ``0`` starts
972 | indenting from the beginning of the line. Setting the value to higher
973 | than ``0`` indents the content by ``indent * level``.
974 |
975 | - ``semicolon=';'``
976 |
977 | Semicolon character. Set this to empty string to ignore all semicolons.
978 |
979 | - ``semicolon_ignore_postfix='!;'``
980 |
981 | If the line ends with ``!;`` then don't add the final semicolon.
982 |
983 |
984 | .. _encode-pam:
985 |
986 | encode_pam
987 | ^^^^^^^^^^
988 |
989 | This filter helps to create configuration in the format user by Linux
990 | Pluggable Authentication Modules (PAM). The expected data structure is
991 | the following:
992 |
993 | .. code:: yaml
994 |
995 | my_system_auth_config:
996 | aa:
997 | type: auth
998 | control: required
999 | path: pam_unix.so
1000 | args:
1001 | - try_first_pass
1002 | - nullok
1003 | bb:
1004 | type: auth
1005 | control: optional
1006 | path: pam_permit.so
1007 | cc:
1008 | type: auth
1009 | control: required
1010 | path: pam_env.so
1011 | dd:
1012 | type: account
1013 | control: required
1014 | path: pam_unix.so
1015 | ee:
1016 | type: account
1017 | control: optional
1018 | path: pam_permit.so
1019 | ff:
1020 | type: account
1021 | control: required
1022 | path: pam_time.so
1023 | gg:
1024 | type: password
1025 | control: required
1026 | path: pam_unix.so
1027 | args:
1028 | - try_first_pass
1029 | - nullok
1030 | - sha512
1031 | - shadow
1032 | hh:
1033 | type: password
1034 | control: optional
1035 | path: pam_permit.so
1036 | args:
1037 | ii:
1038 | type: session
1039 | control: required
1040 | path: pam_limits.so
1041 | jj:
1042 | type: session
1043 | control: required
1044 | path: pam_unix.so
1045 | kk:
1046 | type: session
1047 | control: optional
1048 | path: pam_permit.so
1049 |
1050 | The variable is a dictionary of which the key is a labels and the value
1051 | is the PAM rule. The label is used to order the PAM rules. Using labels
1052 | with even number of characters allows to insert another rule in between
1053 | of any two rules.
1054 |
1055 | The above variable can be used in the template file like this:
1056 |
1057 | .. code:: jinja2
1058 |
1059 | {{ my_system_auth_config | encode_pam }}
1060 |
1061 | The output of such template would be:
1062 |
1063 | .. code:: pam
1064 |
1065 | auth required pam_unix.so try_first_pass nullok
1066 | auth optional pam_permit.so
1067 | auth required pam_env.so
1068 |
1069 | account required pam_unix.so
1070 | account optional pam_permit.so
1071 | account required pam_time.so
1072 |
1073 | password required pam_unix.so try_first_pass nullok sha512 shadow
1074 | password optional pam_permit.so
1075 |
1076 | session required pam_limits.so
1077 | session required pam_unix.so
1078 | session optional pam_permit.so
1079 |
1080 | The filter can have the following parameters:
1081 |
1082 | - ``print_label=false``
1083 |
1084 | Print labels as a comment in the output.
1085 |
1086 | - ``separate_types=true``
1087 |
1088 | Add a newline between the groups of types.
1089 |
1090 | - ``separator=" "``
1091 |
1092 | Separator between the collection of tokens.
1093 |
1094 |
1095 | .. _encode-toml:
1096 |
1097 | encode_toml
1098 | ^^^^^^^^^^^
1099 |
1100 | This filter helps to create configuration in the TOML format. The
1101 | expected data structure is the following:
1102 |
1103 | .. code:: yaml
1104 |
1105 | my_grafana_ldap_config:
1106 | verbose_logging: false
1107 | servers:
1108 | - host: 127.0.0.1
1109 | port: 389
1110 | use_ssl: no
1111 | start_tls: no
1112 | ssl_skip_verify: no
1113 | bind_dn: cn=admin,dc=grafana,dc=org
1114 | bind_password: grafana
1115 | search_filter: (cn=%s)
1116 | search_base_dns:
1117 | - dc=grafana,dc=org
1118 | - attributes:
1119 | name: givenName
1120 | surname: sn
1121 | username: cn
1122 | member_of: memberOf
1123 | email: email
1124 | - group_mappings:
1125 | - group_dn: cn=admins,dc=grafana,dc=org
1126 | org_role: Admin
1127 | - group_dn: cn=users,dc=grafana,dc=org
1128 | org_role: Editor
1129 | - group_dn: "*"
1130 | org_role: Viewer
1131 |
1132 | The variable is a dictionary of which value can be either a simple type
1133 | (number, string, boolean), list or another dictionary. The dictionaries
1134 | and lists can be nested.
1135 |
1136 | The above variable can be used in the template file like this:
1137 |
1138 | .. code:: jinja2
1139 |
1140 | {{ my_grafana_ldap_config | encode_toml }}
1141 |
1142 | The output of such template would be:
1143 |
1144 | .. code:: toml
1145 |
1146 | verbose_logging = false
1147 |
1148 | [[servers]]
1149 | host = "127.0.0.1"
1150 | port = 389
1151 | use_ssl = false
1152 | start_tls = false
1153 | ssl_skip_verify = false
1154 | bind_dn = "cn=admin,dc=grafana,dc=org"
1155 | bind_password = 'grafana'
1156 | search_filter = "(cn=%s)"
1157 | search_base_dns = ["dc=grafana,dc=org"]
1158 |
1159 | [servers.attributes]
1160 | name = "givenName"
1161 | surname = "sn"
1162 | username = "cn"
1163 | member_of = "memberOf"
1164 | email = "email"
1165 |
1166 | [[servers.group_mappings]]
1167 | group_dn = "cn=admins,dc=grafana,dc=org"
1168 | org_role = "Admin"
1169 |
1170 | [[servers.group_mappings]]
1171 | group_dn = "cn=users,dc=grafana,dc=org"
1172 | org_role = "Editor"
1173 |
1174 | [[servers.group_mappings]]
1175 | group_dn = "*"
1176 | org_role = "Viewer"
1177 |
1178 | The filter can have the following parameters:
1179 |
1180 | - ``convert_bools=false``
1181 |
1182 | Indicates whether Boolean values presented as a string should be
1183 | converted to a real Boolean value. For example ``var1: 'True'`` would
1184 | be represented as a string but by using the ``convert_bools=true`` it
1185 | will be converted into Boolean like it would be defined like ``var1:
1186 | true``.
1187 |
1188 | - ``convert_nums=false``
1189 |
1190 | Indicates whether number presented as a string should be converted to
1191 | number. For example ``var1: '123'`` would be represented as a string
1192 | but by using the ``convert_nums=true`` it will be converted it to a
1193 | number like it would be defined like ``var1: 123``. It's also possible
1194 | to use the YAML type casting to convert string to number (e.g. ``!!int
1195 | "1234"``, ``!!float "3.14"``).
1196 |
1197 | - ``quote='"'``
1198 |
1199 | Sets the quoting of the value. Use ``quote="'"`` or ``quote='"'``.
1200 |
1201 |
1202 | .. _encode-xml:
1203 |
1204 | encode_xml
1205 | ^^^^^^^^^^
1206 |
1207 | This filter helps to create configuration in the XML format. The expected
1208 | data structure is the following:
1209 |
1210 | .. code:: yaml
1211 |
1212 | my_oddjob_config:
1213 | - oddjobconfig:
1214 | - service:
1215 | - ^name: com.redhat.oddjob
1216 | - object:
1217 | - ^name: /com/redhat/oddjob
1218 | - interface:
1219 | - ^name: com.redhat.oddjob
1220 | - method:
1221 | - ^name: listall
1222 | - allow:
1223 | - ^min_uid: 0
1224 | - ^max_uid: 0
1225 | - method:
1226 | - ^name: list
1227 | - allow: ''
1228 | - method:
1229 | - ^name: quit
1230 | - allow:
1231 | - ^user: root
1232 | - method:
1233 | - ^name: reload
1234 | - allow:
1235 | - ^user: root
1236 | - include:
1237 | - ^ignore_missing: "yes"
1238 | - /etc/oddjobd.conf.d/*.conf
1239 | - include:
1240 | - ^ignore_missing: "yes"
1241 | - /etc/oddjobd-local.conf
1242 |
1243 | The variable can be a list of dictionaries, lists or strings. This config
1244 | encoder does not handle mixed content very well so the safest way how to
1245 | include mixed content is to define it as a string and use the parameter
1246 | ``escape_xml=false``. This config encoder also produces no XML declaration.
1247 | Any XML declaration or DOCTYPE must be a part of the template file.
1248 |
1249 | The above variable can be used in the template file like this:
1250 |
1251 | .. code:: jinja2
1252 |
1253 | {{ my_oddjob_config | encode_xml }}
1254 |
1255 | The output of such template would be:
1256 |
1257 | .. code:: xml
1258 |
1259 |
1260 |
1261 |
1277 |
1278 | /etc/oddjobd.conf.d/*.conf
1279 | /etc/oddjobd-local.conf
1280 |
1281 |
1282 | The filter can have the following parameters:
1283 |
1284 | - ``attribute_sign="^"``
1285 |
1286 | XML attribute indicator.
1287 |
1288 | - ``indent=" "``
1289 |
1290 | Defines the indentation unit.
1291 |
1292 | - ``level=0``
1293 |
1294 | Indicates the initial level of the indentation. Value ``0`` starts
1295 | indenting from the beginning of the line. Setting the value to higher
1296 | than ``0`` indents the content by ``indent * level``.
1297 |
1298 |
1299 | .. _encode-yaml:
1300 |
1301 | encode_yaml
1302 | ^^^^^^^^^^^
1303 |
1304 | This filter helps to create configuration in the YAML format. The
1305 | expected data structure is the following:
1306 |
1307 | .. code:: yaml
1308 |
1309 | my_mongodb_config:
1310 | systemLog:
1311 | destination: file
1312 | logAppend: true
1313 | path: /var/log/mongodb/mongod.log
1314 | storage:
1315 | dbPath: /var/lib/mongo
1316 | journal:
1317 | enabled: true
1318 | processManagement:
1319 | fork: true
1320 | pidFilePath: /var/run/mongodb/mongod.pid
1321 | net:
1322 | port: 27017
1323 | bindIp: 127.0.0.1
1324 |
1325 | The variable is ordinary YAML. The only purpose of this encoder filter is
1326 | to be able to convert YAML data structure into the string in a template
1327 | file in unified way compatible with the other config encoders.
1328 |
1329 | The above variable can be used in the template file like this:
1330 |
1331 | .. code:: jinja2
1332 |
1333 | {{ my_mongodb_config | encode_yaml }}
1334 |
1335 | The output of such template would be:
1336 |
1337 | .. code:: yaml
1338 |
1339 | net:
1340 | bindIp: "127.0.0.1"
1341 | port: 27017
1342 | processManagement:
1343 | fork: true
1344 | pidFilePath: "/var/run/mongodb/mongod.pid"
1345 | storage:
1346 | dbPath: "/var/lib/mongo"
1347 | journal:
1348 | enabled: true
1349 | systemLog:
1350 | destination: "file"
1351 | logAppend: true
1352 | path: "/var/log/mongodb/mongod.log"
1353 |
1354 | The filter can have the following parameters:
1355 |
1356 | - ``block_prefix=';;;'``
1357 |
1358 | Allows to maintain block signs in the output.
1359 |
1360 | .. code:: yaml
1361 |
1362 | aaa: |-
1363 | ;;;|-
1364 | bbb = ccc
1365 | ddd = eee
1366 |
1367 | - ``convert_bools=false``
1368 |
1369 | Indicates whether Boolean values presented as a string should be
1370 | converted to a real Boolean value. For example ``var1: 'True'`` would
1371 | be represented as a string but by using the ``convert_bools=true`` it
1372 | will be converted into Boolean like it would be defined like ``var1:
1373 | true``.
1374 |
1375 | - ``convert_nums=false``
1376 |
1377 | Indicates whether number presented as a string should be converted to
1378 | number. For example ``var1: '123'`` would be represented as a string
1379 | but by using the ``convert_nums=true`` it will be converted it to a
1380 | number like it would be defined like ``var1: 123``. It's also possible
1381 | to use the YAML type casting to convert string to number (e.g. ``!!int
1382 | "1234"``, ``!!float "3.14"``).
1383 |
1384 | - ``indent=" "``
1385 |
1386 | Defines the indentation unit.
1387 |
1388 | - ``level=0``
1389 |
1390 | Indicates the initial level of the indentation. Value ``0`` starts
1391 | indenting from the beginning of the line. Setting the value to higher
1392 | than ``0`` indents the content by ``indent * level``.
1393 |
1394 | - ``quote='"'``
1395 |
1396 | Sets the quoting of the value. Use ``quote="'"`` or ``quote='"'``.
1397 |
1398 |
1399 | .. _Utilities:
1400 |
1401 | Utilities
1402 | ---------
1403 |
1404 | The followng is a list of utilities that can be used in conjunction with the
1405 | Config Encoder filters.
1406 |
1407 |
1408 | .. _template-replace:
1409 |
1410 | template_replace
1411 | ^^^^^^^^^^^^^^^^
1412 |
1413 | This filter allows to use extra templating layer which gets processed during
1414 | the template file processing. That can be useful if it's necessary to create
1415 | repetitive but slightly different definitions inside the template file.
1416 |
1417 | The extra templating layer is represented by a templating variable which
1418 | contains specially decorated variables which get replaced by its real value at
1419 | the time of template file processing. The template variable can be composed
1420 | dynamically which provides extra flexibility that would otherwise have to be
1421 | hardcoded in the template file.
1422 |
1423 | The filter expects the template variable containing the specially decorated
1424 | variables as its input. The filter has one parameter which is used to replaced
1425 | the specially decorated variables in the template variable.
1426 |
1427 | Let's have a look at an example of such usage:
1428 |
1429 | .. code:: yaml
1430 |
1431 | # The variable used as the replacement in the template variable
1432 | my_clients:
1433 | - host: myclient01
1434 | jobdefs: Default
1435 | password: Passw0rd1
1436 | file_retention: 30 days
1437 | - host: myclient02
1438 | jobdefs: HomeOnly
1439 | password: Passw0rd2
1440 | file_retention: 90 days
1441 |
1442 | # The actual template variable used in the template file
1443 | bacula_director_config_job_client:
1444 | # First template variable containing the specially decorated variables
1445 | - template:
1446 | - Job:
1447 | - Name = Job-{[{ item['jobdefs'] }]}-{[{ item['host'] }]}
1448 | - Client = {[{ item['host'] }]}-fd
1449 | - JobDefs = {[{ item['jobdefs'] }]}
1450 | # Variable used to replace the specially decorated variables
1451 | items: "{{ my_clients }}"
1452 | # Second template and its items
1453 | - template:
1454 | - Client:
1455 | - Name = {[{ item['host'] }]}-fd
1456 | - Address = {[{ item['host'] }]}
1457 | - FD Port = 9102
1458 | - Catalog = Default
1459 | - Password = {[{ item['password'] }]}
1460 | - File Retention = {[{ item['file_retention'] }]}
1461 | - Job Retention = 3 months
1462 | - AutoPrune = yes
1463 | items: "{{ my_clients }}"
1464 |
1465 | The above variable can be used together with the `template_replace` filter in
1466 | the template file (``bacula-dir.conf.j2``) like this:
1467 |
1468 | .. code:: jinja2
1469 |
1470 | {% for record in bacula_director_config_job_client %}
1471 | {%- for item in record['items'] -%}
1472 | {{ record['template'] | template_replace(item) | encode_nginx }}{{ "\n" }}
1473 | {%- endfor -%}
1474 | {% endfor %}
1475 |
1476 | The template file can be called from the playbook/role like this:
1477 |
1478 | .. code:: yaml
1479 |
1480 | - name: Configure Bacula Director
1481 | template:
1482 | src: bacula-dir.conf.j2
1483 | dest: /etc/bacula/bacula-dir.conf
1484 |
1485 | And the result of such usage is the following:
1486 |
1487 | .. code:: nginx
1488 |
1489 | Job {
1490 | Name = Job-Default-myclient01;
1491 | Client = myclient01-fd;
1492 | JobDefs = Default;
1493 | }
1494 |
1495 | Job {
1496 | Name = Job-HomeOnly-myclient02;
1497 | Client = myclient02-fd;
1498 | JobDefs = HomeOnly;
1499 | }
1500 |
1501 | Client {
1502 | Name = myclient01-fd;
1503 | Address = myclient01;
1504 | FD Port = 9102;
1505 | Catalog = Default;
1506 | Password = Passw0rd1;
1507 | File Retention = 30 days;
1508 | Job Retention = 3 months;
1509 | AutoPrune = yes;
1510 | }
1511 |
1512 | Client {
1513 | Name = myclient02-fd;
1514 | Address = myclient02;
1515 | FD Port = 9102;
1516 | Catalog = Default;
1517 | Password = Passw0rd2;
1518 | File Retention = 90 days;
1519 | Job Retention = 3 months;
1520 | AutoPrune = yes;
1521 | }
1522 |
1523 |
1524 | .. _Testing:
1525 |
1526 | Testing
1527 | -------
1528 |
1529 | All encoders have a set of unit tests automated through
1530 | `tox `_. Full test can be executed like this:
1531 |
1532 | .. code:: shell
1533 |
1534 | $ tox
1535 |
1536 | Individual encoder can be tested like this:
1537 |
1538 | .. code:: shell
1539 |
1540 | $ tox -- python -m unittest tests.test_config_encoders.TestYaml
1541 |
1542 | Individual tests can be executed like this:
1543 |
1544 | .. code:: shell
1545 |
1546 | $ tox -- python -m unittest tests.test_config_encoders.TestYaml.test_string
1547 |
1548 | Tests are great source of advanced examples of how to use each of the encoders.
1549 | Explore them in the directory ``tests/files``. The content of the ``.in`` files
1550 | must be assigned to a variable when using in Ansible. The output in the
1551 | ``.out`` files might depend on additional parameters used in the
1552 | ``tests/test_config_encoders.py`` file. Testing via Ansible can be executed
1553 | like this:
1554 |
1555 | .. code:: shell
1556 |
1557 | $ ansible-playbook -i localhost, tests/test_config_encoders.yaml
1558 |
1559 |
1560 | .. _License:
1561 |
1562 | License
1563 | -------
1564 |
1565 | GPLv3
1566 |
1567 |
1568 | .. _Author:
1569 |
1570 | Author
1571 | ------
1572 |
1573 | Jiri Tyr
1574 |
--------------------------------------------------------------------------------