Giới thiệu về Ngôn ngữ Mẫu

Jinja là ngôn ngữ mẫu linh hoạt cho Python có thể được sử dụng để tạo bất kỳ định dạng dựa trên văn bản nào như HTML, XML và YAML. Các ngôn ngữ mẫu như Jinja cho phép bạn chèn dữ liệu vào định dạng có cấu trúc. Bạn cũng có thể nhúng các câu lệnh logic hoặc luồng điều khiển vào các mẫu để có khả năng tái sử dụng và tính mô-đun cao hơn. Công cụ mẫu của Jinja chịu trách nhiệm xử lý mã trong các mẫu và tạo đầu ra cho tài liệu dựa trên văn bản cuối cùng.

Ngôn ngữ mẫu được biết đến rộng rãi trong bối cảnh tạo trang web trong kiến ​​trúc Model View Controller . Trong trường hợp này, công cụ mẫu xử lý dữ liệu nguồn, như dữ liệu tìm thấy trong cơ sở dữ liệu và mẫu web bao gồm hỗn hợp HTML và ngôn ngữ mẫu. Hai phần này sau đó được sử dụng để tạo trang web cuối cùng để người dùng sử dụng. Tuy nhiên, ngôn ngữ mẫu không giới hạn ở các trang web. Salt, một phần mềm quản lý cấu hình phổ biến dựa trên Python, hỗ trợ Jinja để cho phép trừu tượng hóa và tái sử dụng trong các tệp trạng thái Salt và các tệp thông thường.

Hướng dẫn này sẽ cung cấp tổng quan về ngôn ngữ mẫu Jinja được sử dụng chủ yếu trong Salt. Nếu bạn chưa quen với các khái niệm Salt, hãy xem lại Hướng dẫn dành cho người mới bắt đầu về Salt trước khi tiếp tục. Mặc dù bạn sẽ không tạo trạng thái Salt của riêng mình trong hướng dẫn này, nhưng cũng hữu ích khi xem lại hướng dẫn Bắt đầu với Salt – Cài đặt và Thiết lập cơ bản .

Kiến thức cơ bản về Jinja

Phần này cung cấp mô tả giới thiệu về cú pháp và khái niệm Jinja cùng với các ví dụ về trạng thái Jinja và Salt. Để tìm hiểu sâu hơn về Jinja, hãy tham khảo Tài liệu chính thức của Jinja Template Designer .

Các ứng dụng như Salt có thể xác định hành vi mặc định cho công cụ tạo mẫu Jinja. Tất cả các ví dụ trong hướng dẫn này đều sử dụng các tùy chọn môi trường Jinja mặc định của Salt. Các thiết lập này có thể được thay đổi trong tệp cấu hình chính Salt:

# Default Jinja environment options for all templates except sls templates
#jinja_env:
#  block_start_string: '{%'
#  block_end_string: '%}'
#  variable_start_string: '{{'
#  variable_end_string: '}}'
#  comment_start_string: '{#'
#  comment_end_string: '#}'
#  line_statement_prefix:
#  line_comment_prefix:
#  trim_blocks: False
#  lstrip_blocks: False
#  newline_sequence: '\n'
#  keep_trailing_newline: False

# Jinja environment options for sls templates
#jinja_sls_env:
#  block_start_string: '{%'
#  block_end_string: '%}'
#  variable_start_string: '{{'
#  variable_end_string: '}}'
#  comment_start_string: '{#'
#  comment_end_string: '#}'
#  line_statement_prefix:
#  line_comment_prefix:
#  trim_blocks: False
#  lstrip_blocks: False

Ghi chú: Trước khi đưa Jinja vào trạng thái Salt của bạn, hãy đảm bảo xem lại phần Thực hành tốt nhất về Salt và Jinja của hướng dẫn này để đảm bảo rằng bạn đang tạo ra các trạng thái Salt có thể bảo trì và đọc được. Có thể sử dụng các công cụ và khái niệm Salt nâng cao hơn để cải thiện tính mô-đun và khả năng tái sử dụng của một số ví dụ về trạng thái Jinja và Salt được sử dụng trong hướng dẫn này.

Dấu phân cách

Các dấu phân cách ngôn ngữ mẫu được sử dụng để biểu thị ranh giới giữa ngôn ngữ mẫu và một loại định dạng dữ liệu khác như HTML hoặc YAML. Jinja sử dụng các dấu phân cách sau:

Cú pháp phân cáchCách sử dụng
{% ... %}Cấu trúc điều khiển
{{ ... }}Các biểu thức được đánh giá sẽ in ra đầu ra mẫu
{# ... #}Các bình luận sẽ bị bỏ qua bởi công cụ tạo mẫu
# ... ##Các câu lệnh dòng

Trong ví dụ về tệp trạng thái Salt này, bạn có thể phân biệt cú pháp Jinja với YAML nhờ các {% ... %}dấu phân cách bao quanh các điều kiện if/else:

{% if grains['group'] == 'admin' %}
    America/Denver:
        timezone.system:
{% else %}
    Europe/Minsk:
        timezone.system:
{% endif %}

Xem phần cấu trúc điều khiển để biết thêm thông tin về điều kiện.

Biến mẫu

Biến mẫu có sẵn thông qua từ điển ngữ cảnh của mẫu. Từ điển ngữ cảnh của mẫu được tạo tự động trong các giai đoạn khác nhau của quá trình đánh giá mẫu. Các biến này có thể được truy cập bằng ký hiệu dấu chấm:

{{ foo.bar }}

Hoặc chúng có thể được truy cập bằng cú pháp chỉ số dưới:

{{ foo['bar'] }}

Salt cung cấp một số biến ngữ cảnh có sẵn theo mặc định cho bất kỳ tệp trạng thái Salt hoặc mẫu tệp nào:

Salt : saltBiến này cung cấp một tập hợp các hàm thư viện Salt mạnh mẽ .

{{ salt['pw_user.list_groups']('jdoe') }} 

Bạn có thể chạy salt '*' sys.doctừ Salt master để xem danh sách tất cả các chức năng có sẵn.

Opts : Biến là một từ điển cung cấp quyền truy cập vào nội dung của tệp cấu hìnhopts minion Salt :

{{ opts['log_file'] }} 

Vị trí lưu trữ tệp cấu hình của minion là /etc/salt/minion.

Trụ cột : Biến là một từ điển được sử dụng để truy cập dữ liệu trụ cộtpillar của Salt :

{{ pillar['my_key'] }}

Mặc dù bạn có thể truy cập trực tiếp các khóa và giá trị của cột, nhưng bạn nên sử dụng pillar.gethàm thư viện biến của Salt, vì nó cho phép bạn xác định giá trị mặc định. Điều này hữu ích khi giá trị không tồn tại trong cột:

{{ salt['pillar.get']('my_key', 'default_value') }}

Grains : grainsBiến này là một từ điển và cung cấp quyền truy cập vào dữ liệu hạt của minion :

{{ grains['shell'] }}

grains.getBạn cũng có thể sử dụng hàm thư viện biến của Salt để truy cập dữ liệu hạt:

{{ salt['grains.get']('shell') }}

Saltenv : Bạn có thể định nghĩa nhiều môi trường salt cho minion trong tệp top của Salt master, chẳng hạn như baseproddevvà testsaltenvBiến này cung cấp một cách để truy cập môi trường Salt hiện tại trong tệp trạng thái Salt. Biến này chỉ khả dụng trong các tệp trạng thái Salt.

{{ saltenv }}

SLS : Với slsbiến này, bạn có thể lấy được giá trị tham chiếu cho tệp trạng thái hiện tại (ví dụ: apachewebserver, v.v.). Đây là giá trị tương tự được sử dụng trong tệp top để ánh xạ minion vào tệp trạng thái hoặc thông qua includetùy chọn trong tệp trạng thái:

{{ sls }}

Slspath : Biến này cung cấp đường dẫn đến tệp trạng thái hiện tại:

{{ slspath }}

Gán biến

Bạn có thể gán giá trị cho một biến bằng cách sử dụng thẻ setcùng với dấu phân cách và cú pháp sau:

{% set var_name = myvalue %}

Thực hiện theo quy ước đặt tên Python khi tạo tên biến. Nếu biến được gán ở cấp cao nhất của mẫu, phép gán sẽ được xuất và có thể được nhập bởi các mẫu khác.

Bất kỳ giá trị nào được tạo ra bởi hàm thư viện biến mẫu Salt đều có thể được gán cho một biến mới.

{% set username = salt['user.info']('username') %}

Bộ lọc

Bộ lọc có thể được áp dụng cho bất kỳ biến mẫu nào thông qua một |ký tự. Bộ lọc có thể nối chuỗi và chấp nhận các đối số tùy chọn trong dấu ngoặc đơn. Khi nối chuỗi bộ lọc, đầu ra của một bộ lọc sẽ trở thành đầu vào của bộ lọc tiếp theo.

{{ '/etc/salt/' | list_files | join('\n') }}

Các bộ lọc được nối này sẽ trả về danh sách đệ quy của tất cả các tệp trong thư /etc/salt/mục. Mỗi mục danh sách sẽ được nối bằng một dòng mới.

  /etc/salt/master
  /etc/salt/proxy
  /etc/salt/minion
  /etc/salt/pillar/top.sls
  /etc/salt/pillar/device1.sls
  

Để biết danh sách đầy đủ tất cả các bộ lọc Jinja tích hợp, hãy tham khảo tài liệu Thiết kế mẫu Jinja . Tài liệu chính thức của Salt bao gồm danh sách các bộ lọc Jinja tùy chỉnh .

Macro

Macro là các mẫu nhỏ, có thể tái sử dụng giúp bạn giảm thiểu sự lặp lại khi tạo trạng thái. Xác định macro trong các mẫu Jinja để biểu diễn các cấu trúc thường dùng và sau đó tái sử dụng các macro trong các tệp trạng thái.

{% macro mysql_privs(user, grant=select, database, host=localhost) %}
{{ user }}_exampledb:
   mysql_grants.present:
    - grant: {{ grant }}
    - database: {{ database }}
    - user: {{user}}
    - host: {{ host }}
{% endmacro %}
{% import "/srv/salt/mysql/db_macro.sls" as db -%}

db.mysql_privs('jane','exampledb.*','select,insert,update')

Macro mysql_privs()được định nghĩa trong db_macro.slstệp. Sau đó, mẫu được nhập vào dbbiến trong db_privs.slstệp trạng thái và được sử dụng để tạo trạng thái MySQL grantscho một người dùng cụ thể.

Tham khảo phần Nhập và Bao gồm để biết thêm thông tin về việc nhập mẫu và biến.

Nhập khẩu và Bao gồm

Nhập khẩu

Nhập trong Jinja tương tự như nhập trong Python. Bạn có thể nhập toàn bộ mẫu, trạng thái cụ thể hoặc macro được xác định trong tệp.

{% import '/srv/salt/users.sls' as users %}

Ví dụ này sẽ nhập tệp trạng thái users.slsvào biến users. Tất cả các trạng thái và macro được xác định trong mẫu sẽ có sẵn bằng cách sử dụng ký hiệu dấu chấm.

Bạn cũng có thể nhập trạng thái hoặc macro cụ thể từ một tệp.

{% from '/srv/salt/user.sls' import mysql_privs as grants %}

Lệnh nhập này nhắm vào macro mysql_privsđược xác định trong user.slstệp trạng thái và được cung cấp cho mẫu hiện tại với grantsbiến đó.

Bao gồm

Thẻ này {% include %}hiển thị đầu ra của một mẫu khác vào vị trí mà thẻ include được khai báo. Khi sử dụng thẻ, {% include %}ngữ cảnh của mẫu include được chuyển đến mẫu đang gọi.

include:
  - groups

{% include 'users.sls' %}

Ghi chú: Một tệp được tham chiếu bởi thẻ Jinja includecần được chỉ định theo đường dẫn tuyệt đối của nó từ thiết lập của Saltfile_roots ; sử dụng đường dẫn tương đối từ tệp trạng thái hiện tại sẽ tạo ra lỗi. Để bao gồm một tệp trong cùng thư mục với tệp trạng thái hiện tại:

{% include slspath + "/users.sls" %}

Ngoài ra, lưu ý rằng Salt có khai báo gốc riênginclude , độc lập với . của Jinja include.

Nhập khẩu hành vi bối cảnh

Theo mặc định, một lần nhập sẽ không bao gồm ngữ cảnh của mẫu đã nhập, vì các lần nhập được lưu trong bộ nhớ đệm. Điều này có thể được ghi đè bằng cách thêm with contextvào các câu lệnh nhập của bạn.

{% from '/srv/salt/user.sls' import mysql_privs with context %}

Tương tự như vậy, nếu bạn muốn xóa ngữ cảnh khỏi {% include %}, hãy thêm without context:

{% include 'users.sls' without context %}

Kiểm soát khoảng trắng

Jinja cung cấp một số cơ chế để kiểm soát khoảng trắng của đầu ra được kết xuất. Theo mặc định, Jinja xóa các dòng mới theo sau và giữ nguyên mọi thứ khác, ví dụ như tab, khoảng trắng và nhiều dòng mới. Bạn có thể tùy chỉnh cách công cụ mẫu Jinja của Salt xử lý khoảng trắng trong tệp cấu hình chính của Salt . Một số tùy chọn môi trường có sẵn để kiểm soát khoảng trắng là:

  • trim_blocks: Khi được đặt thành True, dòng mới đầu tiên sau thẻ mẫu sẽ tự động bị xóa. FalseTheo mặc định, tùy chọn này được đặt thành trong Salt.
  • lstrip_blocks: Khi được đặt thành True, Jinja sẽ xóa các tab và khoảng trắng từ đầu dòng đến đầu khối. Nếu có các ký tự khác trước khi bắt đầu khối, sẽ không có ký tự nào bị xóa. FalseTheo mặc định, tùy chọn này được đặt thành trong Salt.
  • keep_trailing_newline: Khi được đặt thành True, Jinja sẽ giữ nguyên các dòng mới theo sau. FalseTheo mặc định, tùy chọn này được đặt thành trong Salt.

Để tránh gặp phải lỗi cú pháp YAML, hãy đảm bảo rằng bạn cân nhắc đến hành vi hiển thị khoảng trắng của Jinja khi chèn đánh dấu mẫu vào trạng thái Salt. Hãy nhớ rằng, Jinja phải tạo ra YAML hợp lệ. Khi sử dụng cấu trúc điều khiển hoặc macro, có thể cần phải xóa khoảng trắng khỏi khối mẫu để hiển thị YAML hợp lệ một cách phù hợp.

Để giữ nguyên khoảng trắng của nội dung trong các khối mẫu, bạn có thể đặt cả tùy chọn trim_blocksvà trong tệp cấu hình chính. Bạn cũng có thể bật và tắt thủ công các tùy chọn môi trường khoảng trắng trong mỗi khối mẫu. Một ký tự sẽ đặt hành vi của và thành và một ký tự sẽ đặt các tùy chọn này cho khối:lstrip_blockTrue-trim_blockslstrip_blocksFalse+True

Ví dụ, để xóa khoảng trắng sau phần đầu của cấu trúc điều khiển, hãy thêm một -ký tự trước phần đóng %}:

{% for item in [1,2,3,4,5] -%}
    {{ item }}
{% endfor %}

Điều này sẽ xuất ra các số 12345mà không có khoảng trắng ở đầu. Nếu không có -ký tự, đầu ra sẽ giữ nguyên khoảng cách được xác định trong khối.

Cấu trúc điều khiển

Jinja cung cấp các cấu trúc điều khiển phổ biến cho nhiều ngôn ngữ lập trình như vòng lặp, điều kiện, macro và khối. Việc sử dụng các cấu trúc điều khiển trong trạng thái Salt cho phép kiểm soát chi tiết luồng thực thi trạng thái.

Vòng lặp For

Vòng lặp For cho phép bạn lặp qua danh sách các mục và thực thi cùng một mã hoặc cấu hình cho từng mục trong danh sách. Vòng lặp cung cấp một cách để giảm sự lặp lại trong các trạng thái Salt.

{% set groups = ['sudo','wheel', 'admins'] %}
include:
  - groups

jane:
  user.present:
    - fullname: Jane Doe
    - shell: /bin/zsh
    - createhome: True
    - home: /home/jane
    - uid: 4001
    - groups:
    {%- for group in groups %}
      - {{ group }}
    {%- endfor -%}

Vòng lặp for trước đó sẽ gán người dùng janevào tất cả các nhóm trong groupsdanh sách được đặt ở đầu users.slstệp.

Điều kiện

Biểu thức điều kiện đánh giá thành hoặc Truehoặc Falsevà điều khiển luồng của chương trình dựa trên kết quả của biểu thức boolean được đánh giá. Biểu thức điều kiện của Jinja được thêm tiền tố là ifelifelsevà được đặt trong {% ... %}dấu phân cách.

{% set users = ['anna','juan','genaro','mirza'] %}
{% set admin_users = ['genaro','mirza'] %}
{% set admin_groups = ['sudo','wheel', 'admins'] %}
{% set org_groups = ['games', 'webserver'] %}


include:
  - groups

{% for user in users %}
{{ user }}:
  user.present:
    - shell: /bin/zsh
    - createhome: True
    - home: /home/{{ user }}
    - groups:
{% if user in admin_users %}
    {%- for admin_group in admin_groups %}
      - {{ admin_group }}
    {%- endfor -%}
{% else %}
    {%- for org_group in org_groups %}
      - {{ org_group }}
    {% endfor %}
{%- endif -%}
{% endfor %}

Trong ví dụ này, sự hiện diện của người dùng trong admin_usersdanh sách sẽ xác định nhóm nào được đặt cho người dùng đó trong trạng thái. Tham khảo phần Thực hành tốt nhất của Salt để biết thêm thông tin về việc sử dụng các điều kiện và câu lệnh điều khiển luồng trong tệp trạng thái.

Kế thừa mẫu

Với kế thừa mẫu, bạn có thể định nghĩa một mẫu cơ sở có thể được sử dụng lại bởi các mẫu con. Mẫu con có thể ghi đè các khối được chỉ định bởi mẫu cơ sở.

Sử dụng {% block block_name %}thẻ có tên khối để xác định vùng của mẫu cơ sở có thể ghi đè.

{% block user %}jane{% endblock %}:
  user.present:
    - fullname: {% block fullname %}{% endblock %}
    - shell: /bin/zsh
    - createhome: True
    - home: /home/{% block home_dir %}
    - uid: 4000
    - groups:
      - sudo

Ví dụ này tạo ra một mẫu trạng thái người dùng cơ sở. Bất kỳ giá trị nào chứa thẻ đều {% block %}có thể được ghi đè bởi một mẫu con với giá trị riêng của nó.

Để sử dụng mẫu cơ sở trong mẫu con, hãy sử dụng thẻ {% extends "base.sls"%}có vị trí của tệp mẫu cơ sở.

{% extends "/srv/salt/users.jinja" %}

{% block fullname %}{{ salt['pillar.get']('jane:fullname', '') }}{% endblock %}
{% block home_dir %}{{ salt['pillar.get']('jane:home_dir', 'jane') }}{% endblock %}

Tệp webserver_users.slstrạng thái mở rộng users.jinjamẫu và xác định giá trị cho các khối fullnamevà home_dir. Các giá trị được tạo bằng cách sử dụng saltbiến ngữ cảnh và dữ liệu trụ cột. Phần còn lại của trạng thái sẽ được hiển thị theo user.jinjacách mẫu cha đã xác định.

Thực hành tốt nhất của muối và Jinja

Nếu Jinja bị sử dụng quá mức, sức mạnh và tính linh hoạt của nó có thể tạo ra các tệp trạng thái Salt không thể bảo trì và khó đọc. Sau đây là một số biện pháp tốt nhất để đảm bảo rằng bạn đang sử dụng Jinja hiệu quả:

  • Giới hạn số lượng Jinja bạn sử dụng trong các tệp trạng thái. Tốt nhất là tách dữ liệu khỏi trạng thái sẽ sử dụng dữ liệu. Điều này cho phép bạn cập nhật dữ liệu mà không cần phải thay đổi trạng thái của mình.
  • Không lạm dụng điều kiện và vòng lặp trong các tệp trạng thái. Lạm dụng sẽ khiến việc đọc, hiểu và duy trì trạng thái của bạn trở nên khó khăn.
  • Sử dụng từ điển biến và tuần tự hóa trực tiếp chúng thành YAML, thay vì cố gắng tạo YAML hợp lệ trong một mẫu. Bạn có thể đưa logic của mình vào từ điển và truy xuất biến cần thiết trong các trạng thái của mình.Thẻ {% load_yaml %}sẽ hủy tuần tự hóa các chuỗi và biến được truyền vào nó.
 {% load_yaml as example_yaml %}
       user: jane
       firstname: Jane
       lastname: Doe
   {% endload %}

   {{ example_yaml.user }}:
      user.present:
        - fullname: {{ example_yaml.firstname }} {{ example_yaml.lastname }}
        - shell: /bin/zsh
        - createhome: True
        - home: /home/{{ example_yaml.user }}
        - uid: 4001
        - groups:
          - games

Sử dụng {% import_yaml %}để nhập các tệp dữ liệu bên ngoài và làm cho dữ liệu có sẵn dưới dạng biến Jinja.

   {% import_yaml "users.yml" as users %}

Sử dụng Salt Pillars để lưu trữ dữ liệu chung hoặc nhạy cảm dưới dạng biến. Truy cập các biến này bên trong tệp trạng thái và tệp mẫu.

Thông tin thêm

Bạn có thể muốn tham khảo các nguồn sau để biết thêm thông tin về chủ đề này. Mặc dù chúng tôi cung cấp với hy vọng rằng chúng sẽ hữu ích, nhưng xin lưu ý rằng chúng tôi không thể đảm bảo tính chính xác hoặc tính kịp thời của các tài liệu được lưu trữ bên ngoài.

Nguồn: https://www.linode.com/docs/guides/introduction-to-jinja-templates-for-salt/