Хочу разобрать для себя, как посредством инструмента единой конфигурации Ansible приспособить его для взаимодействия с телефонными аппаратами Yealink, у меня в парке устройств встречаются: Yealink T21_E2, T21P_E2, T31G.

К примеру, если телефонные аппараты настраиваются через AutoProvision или вручную кому как нравится, то последующие действия можно явно для каждого аппарата с учетом тегирования задач выполнять средствами Ansible.

Ниже я покажу, каким образом я приспособил Ubuntu 22.04 + Ansible к управлению аппаратами Yealink, опробовано на моем парке устройств.

Вот об этом будет текущая пошаговая заметка.

Шаг №1: Опираясь на заметку "Через Ansible с доменными Windows системами на Ubuntu 22.04 Server" устанавливаю инструмент Ansible.

Шаг №2: Опираюсь на заметку "Удаленное управление Yealink аппаратами" указываю IP адрес системы с которой можно будет взаимодействовать с телефонным аппаратом Yealink через Action URI. Так проделываем для каждого аппарата.

Шаг №3: Проверяю, что с хоста Ansible я могу взаимодействовать с аппаратом (ами) Yealink, к примеру, включить DND режим на аппарате, а затем выключить DND режим:

ekzorchik@srv-ansible:~$ curl -X POST --ciphers 'DEFAULT:!DH' -d "" --user admin:Aa1234567aA https://192.168.10.10/cgi-bin/ConfigManApp.com?key=DNDOn -k -s -v
*   Trying 192.168.10.10:443...
* Connected to 192.168.10.10 (192.168.10.10) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: DEFAULT:!DH
* TLSv1.0 (OUT), TLS header, Certificate Status (22):
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (OUT), TLS header, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS header, Finished (20):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: C=CN; ST=Fujian; L=Xiamen; O=Yealink Network Technology Co.,Ltd.; OU=Yealink Equipment; CN=249ad847fe20; serialNumber=ffffffff-ffff-ffff-fff0-249ad847fe20; emailAddress=support@yealink.com
*  start date: Mar 20 00:31:35 2023 GMT
*  expire date: Mar 17 00:31:35 2033 GMT
*  issuer: C=CN; ST=Fujian; L=Xiamen; O=Yealink Network Technology Co.,Ltd.; OU=yealink.com; CN=Yealink Manufacturing CA; emailAddress=support@yealink.com
*  SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
* Server auth using Basic with user 'admin'
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
> POST /cgi-bin/ConfigManApp.com?key=DNDOn HTTP/1.1
> Host: 192.168.10.10
> Authorization: Basic YWRtaW46a2s1NTQ1NGx0
> User-Agent: curl/7.81.0
> Accept: */*
> Content-Length: 0
> Content-Type: application/x-www-form-urlencoded
>
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< X-Frame-Options: SAMEORIGIN
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Strict-Transport-Security: max-age=31536000; includeSubDomains
< Cache-Control: private, max-age=0, no-cache
< Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; font-src 'self' data:; img-src * data:; frame-ancestors 'self'
< Content-Type: text/html
< Content-Length: 58
< Date: Thu, 07 Sep 2023 05:32:13 GMT
<
* TLSv1.2 (IN), TLS header, Supplemental data (23):
<html>
<body>
        <div id="_RES_INFO_"></div>
</body>
* Connection #0 to host 192.168.10.10 left intact
</html>ekzorchik@srv-ansible:~$

Команда выше отработала успешно.

ekzorchik@srv-ansible:~$ curl -X POST --ciphers 'DEFAULT:!DH' -d "" --user admin:Aa1234567aA https://192.168.10.10/cgi-bin/ConfigManApp.com?key=DNDOff -k -s -v

Команда выше отработала успешно.

Шаг №4: А теперь разберем, как сделать Playbook и через него взаимодействовать с аппаратом(ами) Yealink, но из общего Playbook мне нужно будет запускать конкретный Task (Задачу):

ekzorchik@srv-ansible:~$ sudo nano /etc/ansible/playbook/voipyealink.yml
---
- name: Enable/disable DND on Yealink phones via rest API
  hosts: localhost
  gather_facts: false
  vars:
    # set enable_dnd to 1 to enable it on the phone and set it to 0 to disable it on the phone
    #enable_dnd: '0'
    url_u:  admin
    url_p: Aa1234567aA
    phone_ip: 192.168.10.10

  tasks:
  - name: Enable DND
    delegate_to: localhost
    #when: "enable_dnd == '1'"
    uri:
      url: "https://{{ phone_ip }}/cgi-bin/ConfigManApp.com?key=DNDOn"
      url_username: "{{ url_u }}"
      url_password: "{{ url_p }}"
      method: POST
      body: ""
      force_basic_auth: yes
      status_code: 200
      validate_certs: No
    register: _result
    #Для запуска конкретной задачи используем tag (тег)
    tags:
      - dndon

  - name: Disable DND
    delegate_to: localhost
    #when: "enable_dnd != '1'"
    uri:
      url: "https://{{ phone_ip }}/cgi-bin/ConfigManApp.com?key=DNDOff"
      url_username: "{{ url_u }}"
      url_password: "{{ url_p }}"
      method: POST
      body: ""
      force_basic_auth: yes
      status_code: 200
      validate_certs: No
    register: _result
    #Для запуска конкретной задачи используем tag (тег)
    tags:
      - dndoff

На заметку: Во многих примерах имена переменных я видел что заключают значение в {{ }}, но я так понял что на моей версии Ansible данное не работает поэтому никак не обрамлял переменные: имя пользователя, пароль, IP-адрес; у меня:

ekzorchik@srv-ansible:~$ ansible --version
ansible [core 2.15.2]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/ekzorchik/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3/dist-packages/ansible
  ansible collection location = /home/ekzorchik/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/bin/ansible
  python version = 3.10.12 (main, Jun 11 2023, 05:26:28) [GCC 11.4.0] (/usr/bin/python3)
  jinja version = 3.0.3
  libyaml = True
ekzorchik@srv-ansible:~$

Шаг №5: Отобразить какие задачи описаны в Playbook:

ekzorchik@srv-ansible:~$ sudo ansible-playbook  /etc/ansible/playbook/voipyealink.yml --list-tags
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match
'all'

playbook: /etc/ansible/playbook/voipyealink.yml

  play #1 (localhost): Enable/disable DND on Yealink phones via rest API        TAGS: []
      TASK TAGS: [dndoff, dndon]
ekzorchik@srv-ansible:~$

Шаг №6: Запускаю Playbook где из него выполняю только конкретную задачу, для этого опираюсь на указанный атрибут в виде тега:

(включаю DND режим) — отрабатывает успешно.

ekzorchik@srv-ansible:~$ sudo ansible-playbook   /etc/ansible/playbook/voipyealink.yml --tags="dndon"
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match
'all'

PLAY [Enable/disable DND on Yealink phones via rest API] ***************************************************************

TASK [Enable DND] ******************************************************************************************************
ok: [localhost]

PLAY RECAP *************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

(выключаю DND режим) — отрабатывает успешно.

ekzorchik@srv-ansible:~$ sudo ansible-playbook   /etc/ansible/playbook/voipyealink.yml --tags="dndoff"
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match
'all'

PLAY [Enable/disable DND on Yealink phones via rest API] ***************************************************************

TASK [Disable DND] *****************************************************************************************************
ok: [localhost]

PLAY RECAP *************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

ekzorchik@srv-ansible:~$

Шаг №7: Дополняю Playbook следующими задачами: перезагрузка:

ekzorchik@srv-ansible:~$ sudo nano /etc/ansible/playbook/voipyealink.yml
  - name: Reboot
    delegate_to: localhost
    #when: "enable_dnd != '1'"
    uri:
      url: "https://{{ phone_ip }}/cgi-bin/ConfigManApp.com?key=Reboot"
      url_username: "{{ url_u }}"
      url_password: "{{ url_p }}"
      method: POST
      body: ""
      force_basic_auth: yes
      status_code: 200
      validate_certs: No
    register: _result
    #Для запуска конкретной задачи используем tag (тег)
    tags:
      - reboot
ekzorchik@srv-ansible:~$ sudo ansible-playbook   /etc/ansible/playbook/voipyealink.yml --tags="reboot"

По аналогии делаем для доступных команд строки в Playbook, к примеру:

  • key=MUTE – включить режим без звука
  • key=X – завершить текущий вызов
  • key=OK/ENTER - отобразить на экране если выбрать меню "Статус"

какие команды еще доступы можно посмотреть также у меня в заметке: "Удаленное управление Yealink аппаратами"

В таком случае, не о какой предварительной настройки речь не идет, получается, что используем AutoProvision, а для выполнения задач: DNDON & DNDOFF & Reboot инструмент Ansible.

Ну пусть будет так. На этом я прощаюсь, буду в работе замечания, замечания будут отражены в виде пошаговых заметок на блоге, с уважением автор блога Олло Александр aka ekzorchik.