ГЛАВА 8
Что к чему
Пользуйтесь переводом существенно переработанной и дополенной 2й редакции (12-дек-2014),
находящейся теперь и в режиме постоянно обновляемой документации
(последняя пока доступна только на англ.яз.).
Начиная с этого раздела руководства, мы предполагаем, что у вас есть облако OpenStack и оно работает.
Данный раздел поможет вам настроить рабочую среду и использовать ее, чтобы осмотреть ваше облако.
Инструменты командной строки клиента
Мы рекомендуем использовать сочетание инструментальных средств интерфейса командной строки OpenStack (CLI)
и инструментальной панели OpenStack.
Некоторые пользователи с опытом работы в других облачных технологиях могут воспользоваться совместимым
API EC2, который использует несколько иные соглашения об именовании по сравнению с родным API.
Мы выделим эти отличия.
Мы настоятельно рекомендуем вам установить клиентов командной строки из
Python Package Index (PyPI) (https://pypi.python.org/)
вместо их установки из пакетов Ubuntu или Fedora.
Клиенты находятся в бурном развитии и очень вероятно, что в любой момент времени версия пакетов,
распространяемая вашим производителем операционной системы устареет.
Для управления установкой из архива PyPI используется утилита "pip", а также во многих
дистрибутивах доступен пакет "python-pip".
Каждый проект OpenStack имеет своего собственного клиента, поэтому в зависимости от работающих на вашем
сайте служб установите необходимые вам или все из следующих пакетов:
- python-novaclient (nova CLI)
- python-glanceclient (glance CLI)
- python-keystoneclient (keystone CLI)
- python-cinderclient (cinder CLI)
- python-swiftclient (swift CLI)
- python-quantumclient (quantum CLI)
Инструменты установки
Чтобы установить (обновить) пакет из архива PyPI с помощью команды pip с правами root:
# pip install [--upgrade]
Чтобы удалить пакет:
# pip uninstall
Если вам необходимы самые последние версии клиентов, pip может проводить установку непосредственно
из исходящего репозитория git с использованием флага -e.
Вы должны указать имя устанавливаемого яйца Python.
Например:
# pip install -e git+https://github.com/openstack/python-novaclient.git#egg=python-novaclient
Если вы поддерживаете в облаке EC2 API, вы также должны установить пакет "euca2ools"
или другой инструмент EC2 API, чтобы вы могли получить привычный вашим пользователям интерфейс.
Использование инструментов, основанных на EC2 API, в основном выходит за рамки данного руководства,
хотя мы и обсудим получение учетных данных для использования с данным API.
Инструменты командной строки для администрирования
Существует также ряд *-manage инструментов командной строки:
- nova-manage
- glance-manage
- keystone-manage
- cinder-manage
В отличие от инструментов, упомянутых выше, инструменты *-manage должны запускаться
в контроллере облака, причем с правами root, поскольку они должны иметь доступ по чтению к
файлам конфигурации, таким как /etc/nova/nova.conf и выполнять запросы непосредственно в базе данных,
а не в качестве оконечных узлов (endpoint) API OpenStack.
Наличие инструментов *-manage является наследуемой проблемой.
Именно миграция всей оставшейся в инструментах *-manage функциональности на обычные
инструменты клиента в конечном счете является целью OpenStack.
На сегодняшний день для выполнения некоторых операций по техническому обслуживанию, которые требуют
некоторые инструменты *-manage, вам необходим SSH на узле
контроллера облака.
Получение учетных данных
Если вы хотите использовать инструменты командной строки для осуществления запросов к своему
облаку OpenStack, вы должны иметь соответствующие полномочия.
По сравнению с использованием клиентов командной строки, использование инструментальной панели является
гораздо более простым способом получения авторизованных полномочий.
В верхней правой навигационной строке выберите ссылку Settings (Настройки), чтобы открыть страницу
настроек пользователя, где вы можете задать предпочитаемые язык и часовой пояс для представления
инструментальной панели (dashboard).
Что еще более важно, данное действие изменяем левую колонку навигации, включая ссылку OpenStack
API and EC2 Credentials (Учетные данные OpenStack API и EC2), что позволяет вам создать файлы,
которые вы сможете использовать в качестве исходных данных в вашем окружении для присвоения
данных переменным окружения, которые должны быть установлены, чтобы инструменты командной строки
имели информацию о местоположении терминальных служб, а также вашей аутентификации.
Для использования собственных утилит OpenStack проследуйте за ссылкой OpenStack API.
В верхней части перечислены URL- адреса ваших терминальных служб, а ниже располагается раздел,
озаглавленный Download OpenStack RC File (Загрузка RC файлов OpenStack).
Для обзора облака в качестве администратора, вы можете выбрать в ниспадающем меню admin.
В этом разделе выберите проект, для которого вы хотите получить учетные данные и кликните Download RC.
В результате будет создан файл с именем openrc.sh, который будет выглядеть примерно так:
#!/bin/bash
# With the addition of Keystone, to use an openstack cloud you should
# authenticate against keystone, which returns a **Token** and **Service
# Catalog**. The catalog contains the endpoint for all services the
# user/tenant has access to - including nova, glance, keystone, swift.
#
# *NOTE*: Using the 2.0 *auth api* does not mean that compute api is 2.0.
# We use the 1.1 *compute api*
export OS_AUTH_URL=http://203.0.113.10:5000/v2.0
# With the addition of Keystone we have standardized on the term **tenant**
# as the entity that owns the resources.
export OS_TENANT_ID=98333aba48e756fa8f629c83a818ad57
export OS_TENANT_NAME="test-project"
# In addition to the owning entity (tenant), openstack stores the entity
# performing the action as the **user**.
export OS_USERNAME=test-user
# With Keystone you pass the keystone password.
echo "Please enter your OpenStack Password: "
read -s OS_PASSWORD_INPUT
export OS_PASSWORD=$OS_PASSWORD_INPUT
Перевод на русский язык:
#!/bin/bash
# При добавлении Keystone, для использования облака openstack вы должны
# осуществить аутентификацию в keystone, которая вернет **Token** и **Service
# Catalog**. Каталог содержит терминалы для всех служб, к которым
# user/tenant имеет доступ - включая nova, glance, keystone, swift.
#
# *ПРИМЕЧАНИЕ*: Использование 2.0 *auth api* не означает, что используется compute api 2.0.
# Мы используем *compute api* 1.1
export OS_AUTH_URL=http://203.0.113.10:5000/v2.0
# При добавлении Keystone мы получаем стандартизацию понятия **tenant** (владелец)
# как лица (сущности), которому принадлежат ресурсы.
export OS_TENANT_ID=98333aba48e756fa8f629c83a818ad57
export OS_TENANT_NAME="test-project"
# В добавление к лицу- владельцу (tenant), openstack хранит сущности
# выполняющие действия в качестве **user**.
export OS_USERNAME=test-user
# используя Keystone, вы передаете пароль keystone.
echo "Введите, пожалуйста, пароль OpenStack: "
read -s OS_PASSWORD_INPUT
export OS_PASSWORD=$OS_PASSWORD_INPUT
Данные операции не сохраняют пароль в простом текстовом виде,
что является хорошим обстоятельством.
Однако, когда вы получаете или выполняете сценарий, он просит вас ввести пароль, который сохраняется в
переменной окружения OS_PASSWORD.
Важно отметить, что это не требует взаимодействия.
Если вам необходимо интерактивное взаимодействие, вы можете хранить значение непосредственно в
сценарии, но в этом случае вы должны быть предельно внимательны в вопросах безопасности и прав
доступа к этому файлу.
Совместимые с EC2 полномочия могут быть загружены со ссылки "EC2 Credentials" в левой панели
навигатора, затем выберите проект, для которого необходимы права и кликните "Download EC2 Credentials".
В результате будет создан zip файл с сертификатами сервера x509 и фрагментами сценария окружения.
Создайте новый каталог в безопасном месте, отличном от местоположения openrc по-умолчанию, поскольку
все эти данные представляют действующую информацию аутентификации, необходимую для доступа к вашей
идентификации в облаке.
Извлеките сюда все данные из архива.
У вас должны быть cacert.pem, cert.pem, ec2rc.sh и pk.pem.
Содержимое ec2rc.sh будет подобно следующему:
#!/bin/bash
NOVARC=$(readlink -f "${BASH_SOURCE:-${0}}" 2>/dev/null) ||\
NOVARC=$(python -c 'import os,sys; \
print os.path.abspath(os.path.realpath(sys.argv[1]))' "${BASH_SOURCE:-${0}}")
NOVA_KEY_DIR=${NOVARC%/*}
export EC2_ACCESS_KEY=df7f93ec47e84ef8a347bbb3d598449a
export EC2_SECRET_KEY=ead2fff9f8a344e489956deacd47e818
export EC2_URL=http://203.0.113.10:8773/services/Cloud
export EC2_USER_ID=42 # nova does not use user id, but bundling requires it
export EC2_PRIVATE_KEY=${NOVA_KEY_DIR}/pk.pem
export EC2_CERT=${NOVA_KEY_DIR}/cert.pem
export NOVA_CERT=${NOVA_KEY_DIR}/cacert.pem
export EUCALYPTUS_CERT=${NOVA_CERT} # euca-bundle-image seems to require this
alias ec2-bundle-image=""ec2-bundle-image --cert $EC2_CERT --privatekey \
$EC2_PRIVATE_KEY --user 42 --ec2cert $NOVA_CERT"
alias ec2-upload-bundle="ec2-upload-bundle -a $EC2_ACCESS_KEY -s \
$EC2_SECRET_KEY --url $S3_URL --ec2cert $NOVA_CERT"
Чтобы поместить полномочия EC2 для вашего окружения, получите файл ec2rc.sh
Трюки и капканы командной строки
Путем передачи флага --debug, инструменты командной строки могут быть сформированы
для показа вызовов OpenStack API, например:
# nova --debug list
Данный пример демонстрирует HTTP запрос клиента и ответы терминала, что может быть полезным при
создании инструментов, пишущихся под OpenStack API.
Поддержка Keyring (Брелока, https://wiki.openstack.org/wiki/KeyringSupport) может быть источником путаницы, к моменту написания этих строк существует сообщение об ошибке (https://bugs.launchpad.net/python-novaclient/+bug/1020238), которое было открыто, закрыто как неверное, а затем повторно открытое через несколько циклов.
Проблема в том, что при некоторых обстоятельствах инструменты командной строки пытаются использовать
брелок (keyring) Python в качестве кэша полномочий, однако, при некотором подмножестве условий, могут
возникнуть другие условия, при которых инструменты будут запрашивать ввод пароля брелока при каждом
использовании.
Если вы нашли себя в этом подмножестве невезения, добавьте флаг --no-caching или установите
значение переменной окружения OS_NO_CACHE=1 для исключения кэширования полномочий.
Это приведет к необходимости выполнения аутентификации инструментов
командной строки при каждом и повсеместном взаимодействии с облаком.
cURL
В основе использования командной строки лежит OpenStack API, который является RESTful API, работающим
поверх протокола HTTP.
Могут возникнуть случаи, когда вы захотите взаимодействовать с API непосредственно или у вас возникнет
такая необходимость из-за подозрения в наличии ошибки в инструментах CLI.
Лучшим способом выполнить эту задачу будет использование комбинации cURL
(http://curl.haxx.se/) и другого инструмента для разбора ответов JSON,
например, jq (
http://stedolan.github.com/jq/).
Первое, что вы должны сделать, это аутентифицироваться в облаке с помощью ваших учетных данных, чтобы
получить маркер (token) аутентификации.
Ваши учетные данные представляют собой сочетание имени пользователя, пароля и владельца (tenant или
проекта - project).
Вы можете получить эти данные из openrc.sh, как это обсуждалось ранее.
Маркер позволяет взаимодействовать с другими терминальными службами без необходимости повторной
аутентификации для каждого последующего запроса.
Маркеры, как правило, доступны на 24 часа и, по истечению срока действия маркера, вы получаете
предупреждение о несанкционированном ответе (401, Unauthorized) и вы можете запросить другой маркер.
- Просмотрите свой каталог служб OpenStack:
$ curl -s -X POST http://203.0.113.10:35357/v2.0/tokens \
-d '{"auth": {"passwordCredentials": {"username":"test-user",
"password":"test-password"}, "tenantName":"test-project"}}' \
-H "Content-type: application/json" | jq
- Пролистайте ответ JSON, чтобы понять как выложен каталог.
Для упрощения работы с последующими запросами сохраните маркер в переменной окружения.
$ TOKEN=`curl -s -X POST http://203.0.113.10:35357/v2.0/tokens \
-d '{"auth": {"passwordCredentials": {"username":"test-user",
"password":"test-password"}, "tenantName":"test-project"}}' \
-H "Content-type: application/json" | jq -r .access.token.id`
Теперь вы можете к вашему маркеру в командной строке с помощью имени переменной $TOKEN.
- Выберите терминальную службу из каталога услуг, скажем, compute, и попробуйте выполнить запрос,
например, выдать список экземпляров (серверов).
$ curl -s \
-H "X-Auth-Token: $TOKEN" \
http://203.0.113.10:8774/v2/98333aba48e756fa8f629c83a818ad57/servers | jq
Чтобы узнать как должны быть структурированы запросы, прочтите OpenStack
API Reference (http://api.openstack.org/api-ref.html).
Для пережевывания ответов с помощью jq, прочтите jq Manual
(http://stedolan.github.com/jq/manual/).
Флаг -s используется в приводимых выше командах cURL для предотвращения показа
индикатора исполнения.
Если у вас возникли проблемы с исполнением команд cURL, вы захотите удалить его.
Точно так же, для помощи в устранении трудностей с командами cURL вы можете включить флаг
-v для показа вам подробного вывода.
Существует еще много чрезвычайно полезных функций cURL, обратитесь к страницам руководства (man)
для ознакомления со всеми опциями.
Серверы и службы
Существует несколько способов составить вам для себя, как для администратора, представление о вашем облаке
OpenStack просто используя доступные инструменты OpenStack.
Данный раздел даст вам представление о том, как получить обзор вашего облака, его формы, размера
и текущего состояния.
Во- первых, вы можете определить какие серверы входят в состав вашего облака OpenStack выполнив:
$ nova-manage service list | sort
результат выглядит следующим образом:
Binary Host Zone Status State Updated_At
nova-cert cloud.example.com nova enabled :-) 2013-02-25 19:32:38
nova-compute c01.example.com nova enabled :-) 2013-02-25 19:32:35
nova-compute c02.example.com nova enabled :-) 2013-02-25 19:32:32
nova-compute c03.example.com nova enabled :-) 2013-02-25 19:32:36
nova-compute c04.example.com nova enabled :-) 2013-02-25 19:32:32
nova-compute c05.example.com nova enabled :-) 2013-02-25 19:32:41
nova-consoleauth cloud.example.com nova enabled :-) 2013-02-25 19:32:36
nova-network cloud.example.com nova enabled :-) 2013-02-25 19:32:32
nova-scheduler cloud.example.com nova enabled :-) 2013-02-25 19:32:33
Вывод показывает, что существует пять вычислительных узлов и один контроллер облака.
Вы видите смайлик подобный :-), который указывает, что услуги включены, работают
и функционируют.
Если служба не доступна, то :-) сменяется на XXX.
Это признак того, что вы должны определить причину недоступности сервиса.
Если вы используете Cinder, выполните следующую команду, чтобы увидеть подобный листинг:
$ cinder-manage host list | sort
host zone
c01.example.com nova
c02.example.com nova
c03.example.com nova
c04.example.com nova
c05.example.com nova
cloud.example.com nova
С помощью этих двух таблиц, у вас теперь есть хороший обзор того, какие серверы и службы создают
ваше облако.
Вы также можете использовать Службу идентификации (Keystone), чтобы увидеть, какие службы доступны
в облаке, а также какие терминальные службы настроены для этих служб.
Следующие команды нужны вам для того, чтобы у вас была оболочка, настроенная с соответствующими
административными переменными.
$ keystone service-list
+-----+----------+----------+----------------------------+
| id | name | type | description |
+-----+----------+----------+----------------------------+
| ... | cinder | volume | Cinder Service |
| ... | glance | image | OpenStack Image Service |
| ... | nova_ec2 | ec2 | EC2 Service |
| ... | keystone | identity | OpenStack Identity Service |
| ... | nova | compute | OpenStack Compute Service |
+-----+----------+----------+----------------------------+
Приведенный выше вывод показывает, что существует пять настроенных служб.
Чтобы увидеть терминал каждой службы выполните:
$ keystone endpoint-list
---+------------------------------------------+--
| publicurl |
---+------------------------------------------+--
| http://example.com:8774/v2/%(tenant_id)s |
| http://example.com:9292 |
| http://example.com:8000/v1 |
| http://example.com:5000/v2.0 |
---+------------------------------------------+--
---+------------------------------------------+--
| adminurl |
---+------------------------------------------+--
| http://example.com:8774/v2/%(tenant_id)s |
| http://example.com:9292 |
| http://example.com:8000/v1 |
| http://example.com:5000/v2.0 |
---+------------------------------------------+--
Этот пример показывает две колонки, вынутые из большего списка.
Должно существовать взаимнооднозначное отображение (один-к-одному) между службой и терминалом.
Обратите внимание на различные URL-адреса и порты между общественными URL и административными URL
для некоторых служб.
Вы можете определить версию установленного Compute с помощью команды nova-manag`e:
$ nova-manage version list
Диагностика ваших вычислительных узлов
Вы можете получить дополнительную информацию о запущенных виртуальных машинах: использование ими
процессоров, памяти, дискового ввода / вывода или сетевого ввода / вывода для каждого экземпляра с помощью
выполнения команды диагностики nova с ID:
$ nova diagnostics <serverID>
Вывод этой команды будет меняться в зависимости от используемого гипервизора.
Пример вывода при использовании гипервизора Xen:
+----------------+-----------------+
| Property | Value |
+----------------+-----------------+
| cpu0 | 4.3627 |
| memory | 1171088064.0000 |
| memory_target | 1171088064.0000 |
| vbd_xvda_read | 0.0 |
| vbd_xvda_write | 0.0 |
| vif_0_rx | 3223.6870 |
| vif_0_tx | 0.0 |
| vif_1_rx | 104.4955 |
| vif_1_tx | 0.0 |
+----------------+-----------------+
Хотя команда должна работать с любым гипервизором, который управляется с помощью libvirt
(например, KVM, QEMU, LXC), она была протестирована только с KVM.
Пример вывода, при гипервизоре KVM:
+------------------+------------+
| Property | Value |
+------------------+------------+
| cpu0_time | 2870000000 |
| memory | 524288 |
| vda_errors | -1 |
| vda_read | 262144 |
| vda_read_req | 112 |
| vda_write | 5606400 |
| vda_write_req | 376 |
| vnet0_rx | 63343 |
| vnet0_rx_drop | 0 |
| vnet0_rx_errors | 0 |
| vnet0_rx_packets | 431 |
| vnet0_tx | 4905 |
| vnet0_tx_drop | 0 |
| vnet0_tx_errors | 0 |
| vnet0_tx_packets | 45 |
+------------------+------------+
Сеть
Далее бросим взгляд на то, как настраиваются в облаке фиксированные (Fixed) IP.
Вы можете использовать клиенты командной строки nova для получения диапазонов IP.
+--------------------------------------+--------+--------------+
| ID | Label | Cidr |
+--------------------------------------+--------+--------------+
| 3df67919-9600-4ea8-952e-2a7be6f70774 | test01 | 10.1.0.0/24 |
| 8283efb2-e53d-46e1-a6bd-bb2bdef9cb9a | test02 | 10.1.1.0/24 |
+--------------------------------------+--------+--------------+
Инструмент nova-manage может снабдить вас некоторыми дополнительными деталями.
$ nova-manage network list
id IPv4 IPv6 start address DNS1 DNS2 VlanID project uuid
1 10.1.0.0/24 None 10.1.0.3 None None 300 2725bbd beacb3f2
2 10.1.1.0/24 None 10.1.1.3 None None 301 none d0b1a796
Этот вывод показывает, что настроены две сети, причем каждая сеть содержит 255 IP адресов (/24 подсети).
Первая сеть была назначена определенному проекту, в то время как вторая сеть по-прежнему открыта
для назначения.
Вы можете можете назначить эту сеть вручную или автоматически при первом запуске проекта.
Чтобы выяснить, какие плавающие (floating) IP- адреса доступны в вашем облаке, выполните:
$ nova-manage floating list
2725bbd458e2459a8c1bd36be859f43f 1.2.3.4 None
nova vlan20
None 1.2.3.5 48a415e7-6f07-4d33-ad00-814e60b010ff
nova vlan20
Здесь доступно два плавающих IP.
Первый был выделен проекту, а второй остается не распределенным.
Пользователи и проекты
Чтобы увидеть список проектов, которые были добавлены в облако, выполните:
$ keystone tenant-list
+-----+----------+---------+
| id | name | enabled |
+-----+----------+---------+
| ... | jtopjian | True |
| ... | alvaro | True |
| ... | everett | True |
| ... | admin | True |
| ... | services | True |
| ... | jonathan | True |
| ... | lorin | True |
| ... | anne | True |
| ... | rhulsker | True |
| ... | tom | True |
| ... | adam | True |
+-----+----------+---------+
Чтобы увидеть пользователей, выполните:
$ keystone user-list
+-----+----------+---------+------------------------------+
| id | name | enabled | email |
+-----+----------+---------+------------------------------+
| ... | everett | True | everett.towne@backspace.com |
| ... | jonathan | True | jon@sfcu.edu |
| ... | nova | True | nova@localhost |
| ... | rhulsker | True | ryan.hulkster@cyberalbert.ca |
| ... | lorin | True | lorinhoch@nsservices.com |
| ... | alvaro | True | Alvaro.Perry@cyberalbert.ca |
| ... | anne | True | anne.green@backspace.com |
| ... | admin | True | root@localhost |
| ... | cinder | True | cinder@localhost |
| ... | glance | True | glance@localhost |
| ... | jtopjian | True | joe.topjian@cyberalbert.com |
| ... | adam | True | adam@ossmanuals.net |
| ... | tom | True | fafield@univm.edu.au |
+-----+----------+---------+------------------------------+
Иногда пользователь и группа имеют взаимно однозначное (один-к-одному)
отображение.
Это происходит для стандартных системных учетных записей, таких как cinder, glance, nova и swift, или
когда только один пользователь является целой частью группы.
Выполнение экземпляров
Чтобы посмотреть список запущенных экземпляров, выполните:
$ nova list --all-tenants
+-----+------------------+--------+-------------------------------------------+
| ID | Name | Status | Networks |
+-----+------------------+--------+-------------------------------------------+
| ... | Windows | ACTIVE | novanetwork_1=10.1.1.3, 199.116.232.39 |
| ... | cloud controller | ACTIVE | novanetwork_0=10.1.0.6; jtopjian=10.1.2.3 |
| ... | compute node 1 | ACTIVE | novanetwork_0=10.1.0.4; jtopjian=10.1.2.4 |
| ... | devbox | ACTIVE | novanetwork_0=10.1.0.3 |
| ... | devstack | ACTIVE | novanetwork_0=10.1.0.5 |
| ... | initial | ACTIVE | nova_network=10.1.7.4, 10.1.8.4 |
| ... | lorin-head | ACTIVE | nova_network=10.1.7.3, 10.1.8.3 |
+-----+------------------+--------+-------------------------------------------+
К сожалению эта команда не рассказывает вам различных подробностей о запущенных экземплярах, например,
какие вычислительные узлы экземпляра запущены, какой экземпляр flavor, и так далее.
Вы можете использовать следующую команду, чтобы просмотреть сведения об отдельных экземплярах:
$ nova show <uuid>
Например:
# nova show 81db556b-8aa5-427d-a95c-2a9a6972f630
+-------------------------------------+-----------------------------------+
| Property | Value |
+-------------------------------------+-----------------------------------+
| OS-DCF:diskConfig | MANUAL |
| OS-EXT-SRV-ATTR:host | c02.example.com |
| OS-EXT-SRV-ATTR:hypervisor_hostname | c02.example.com |
| OS-EXT-SRV-ATTR:instance_name | instance-00000029 |
| OS-EXT-STS:power_state | 1 |
| OS-EXT-STS:task_state | None |
| OS-EXT-STS:vm_state | active |
| accessIPv4 | |
| accessIPv6 | |
| config_drive | |
| created | 2013-02-13T20:08:36Z |
| flavor | m1.small (6) |
| hostId | ... |
| id | ... |
| image | Ubuntu 12.04 cloudimg amd64 (...) |
| key_name | jtopjian-sandbox |
| metadata | {} |
| name | devstack |
| novanetwork_0 network | 10.1.0.5 |
| progress | 0 |
| security_groups | [{u'name': u'default'}] |
| status | ACTIVE |
| tenant_id | ... |
| updated | 2013-02-13T20:08:59Z |
| user_id | ... |
+-------------------------------------+-----------------------------------+
|