forked from vitalif/vitastor
Compare commits
1 Commits
master
...
fsync-feed
Author | SHA1 | Date |
---|---|---|
Vitaliy Filippov | a39e8acc88 |
|
@ -395,7 +395,7 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- name: Run test
|
- name: Run test
|
||||||
id: test
|
id: test
|
||||||
timeout-minutes: 6
|
timeout-minutes: 3
|
||||||
run: SCHEME=ec /root/vitastor/tests/test_snapshot_chain.sh
|
run: SCHEME=ec /root/vitastor/tests/test_snapshot_chain.sh
|
||||||
- name: Print logs
|
- name: Print logs
|
||||||
if: always() && steps.test.outcome == 'failure'
|
if: always() && steps.test.outcome == 'failure'
|
||||||
|
@ -532,24 +532,6 @@ jobs:
|
||||||
echo ""
|
echo ""
|
||||||
done
|
done
|
||||||
|
|
||||||
test_switch_primary:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: build
|
|
||||||
container: ${{env.TEST_IMAGE}}:${{github.sha}}
|
|
||||||
steps:
|
|
||||||
- name: Run test
|
|
||||||
id: test
|
|
||||||
timeout-minutes: 3
|
|
||||||
run: /root/vitastor/tests/test_switch_primary.sh
|
|
||||||
- name: Print logs
|
|
||||||
if: always() && steps.test.outcome == 'failure'
|
|
||||||
run: |
|
|
||||||
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
|
|
||||||
echo "-------- $i --------"
|
|
||||||
cat $i
|
|
||||||
echo ""
|
|
||||||
done
|
|
||||||
|
|
||||||
test_write:
|
test_write:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: build
|
needs: build
|
||||||
|
|
|
@ -39,10 +39,6 @@ for my $line (<>)
|
||||||
$test_name .= '_'.lc($1).'_'.$2;
|
$test_name .= '_'.lc($1).'_'.$2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($test_name eq 'test_snapshot_chain_ec')
|
|
||||||
{
|
|
||||||
$timeout = 6;
|
|
||||||
}
|
|
||||||
$line =~ s!\./test_!/root/vitastor/tests/test_!;
|
$line =~ s!\./test_!/root/vitastor/tests/test_!;
|
||||||
# Gitea CI doesn't support artifacts yet, lol
|
# Gitea CI doesn't support artifacts yet, lol
|
||||||
#- name: Upload results
|
#- name: Upload results
|
||||||
|
|
115
CLA-en.md
115
CLA-en.md
|
@ -1,115 +0,0 @@
|
||||||
## Contributor License Agreement
|
|
||||||
|
|
||||||
> This Agreement is made in the Russian and English languages. **The English
|
|
||||||
text of Agreement is for informational purposes only** and is not binding
|
|
||||||
for the Parties.
|
|
||||||
>
|
|
||||||
> In the event of a conflict between the provisions of the Russian and
|
|
||||||
English versions of this Agreement, the **Russian version shall prevail**.
|
|
||||||
>
|
|
||||||
> Russian version is published at https://git.yourcmc.ru/vitalif/vitastor/src/branch/master/CLA-ru.md
|
|
||||||
|
|
||||||
This document represents the offer of Filippov Vitaliy Vladimirovich
|
|
||||||
("Author"), author and copyright holder of Vitastor software ("Program"),
|
|
||||||
acknowledged by a certificate of Federal Service for Intellectual
|
|
||||||
Property of Russian Federation (Rospatent) # 2021617829 dated 20 May 2021,
|
|
||||||
to "Contributors" to conclude this license agreement as follows
|
|
||||||
("Agreement" or "Offer").
|
|
||||||
|
|
||||||
In accordance with Art. 435, Art. 438 of the Civil Code of the Russian
|
|
||||||
Federation, this Agreement is an offer and in case of acceptance of the
|
|
||||||
offer, an agreement is considered concluded on the conditions specified
|
|
||||||
in the offer.
|
|
||||||
|
|
||||||
1. Applicable Terms. \
|
|
||||||
1.1. "Official Repository" shall mean the computer storage, operated by
|
|
||||||
the Author, containing all prior and future versions of the Source
|
|
||||||
Code of the Program, at Internet addresses https://git.yourcmc.ru/vitalif/vitastor/
|
|
||||||
or https://github.com/vitalif/vitastor/. \
|
|
||||||
1.2. "Contributions" shall mean results of intellectual activity
|
|
||||||
(including, but not limited to, source code, libraries, components,
|
|
||||||
texts, documentation) which can be software or elements of the software
|
|
||||||
and which are provided by Contributors to the Author for inclusion
|
|
||||||
in the Program. \
|
|
||||||
1.3. "Contributor" shall mean a person who provides Contributions to
|
|
||||||
the Author and agrees with all provisions of this Agreement.
|
|
||||||
A Сontributor can be: 1) an individual; or 2) a legal entity or an
|
|
||||||
individual entrepreneur in case when an individual provides Contributions
|
|
||||||
on behalf of third parties, including on behalf of his employer.
|
|
||||||
|
|
||||||
2. Subject of the Agreement. \
|
|
||||||
2.1. Subject of the Agreement shall be the Contributions sent to the Author by Contributors. \
|
|
||||||
2.2. The Contributor grants to the Author the right to use Contributions at his own
|
|
||||||
discretion and without any necessity to get a prior approval from Contributor or
|
|
||||||
any other third party in any way, under a simple (non-exclusive), royalty-free,
|
|
||||||
irrevocable license throughout the world by all means not contrary to law, in whole
|
|
||||||
or as a part of the Program, or other open-source or closed-source computer programs,
|
|
||||||
products or services (hereinafter -- the "License"), including, but not limited to: \
|
|
||||||
2.2.1. to execute Contributions and use them for any tasks; \
|
|
||||||
2.2.2. to publish and distribute Contributions in modified or unmodified form and/or to rent them; \
|
|
||||||
2.2.3. to modify Contributions, add comments, illustrations or any explanations to Contributions while using them; \
|
|
||||||
2.2.4. to create other results of intellectual activity based on Contributions, including derivative works and composite works; \
|
|
||||||
2.2.5. to translate Contributions into other languages, including other programming languages; \
|
|
||||||
2.2.6. to carry out rental and public display of Contributions; \
|
|
||||||
2.2.7. to use Contributions under the trade name and/or any trademark or any other label, or without it, as the Author thinks fit; \
|
|
||||||
2.3. The Contributor grants to the Author the right to sublicense any of the aforementioned
|
|
||||||
rights to third parties on any terms at the Author's discretion. \
|
|
||||||
2.4. The License is provided for the entire duration of Contributor's
|
|
||||||
exclusive intellectual property rights to the Contributions. \
|
|
||||||
2.5. The Contributor grants to the Author the right to decide how and where to mention,
|
|
||||||
or to not mention at all, the fact of his authorship, name, nickname and/or company
|
|
||||||
details when including Contributions into the Program or in any other computer
|
|
||||||
programs, products or services.
|
|
||||||
|
|
||||||
3. Acceptance of the Offer \
|
|
||||||
3.1. The Contributor may provide Contributions to the Author in the form of
|
|
||||||
a "Pull Request" in an Official Repository of the Program or by any
|
|
||||||
other electronic means of communication, including, but not limited to,
|
|
||||||
E-mail or messenger applications. \
|
|
||||||
3.2. The acceptance of the Offer shall be the fact of provision of Contributions
|
|
||||||
to the Author by the Contributor by any means with the following remark:
|
|
||||||
“I accept Vitastor CLA agreement: https://git.yourcmc.ru/vitalif/vitastor/src/branch/master/CLA-en.md”
|
|
||||||
or “Я принимаю соглашение Vitastor CLA: https://git.yourcmc.ru/vitalif/vitastor/src/branch/master/CLA-ru.md”. \
|
|
||||||
3.3. Date of acceptance of the Offer shall be the date of such provision.
|
|
||||||
|
|
||||||
4. Rights and obligations of the parties. \
|
|
||||||
4.1. The Contributor reserves the right to use Contributions by any lawful means
|
|
||||||
not contrary to this Agreement. \
|
|
||||||
4.2. The Author has the right to refuse to include Contributions into the Program
|
|
||||||
at any moment with no explanation to the Contributor.
|
|
||||||
|
|
||||||
5. Representations and Warranties. \
|
|
||||||
5.1. The person providing Contributions for the purpose of their inclusion
|
|
||||||
in the Program represents and warrants that he is the Contributor
|
|
||||||
or legally acts on the Contributor's behalf. Name or company details
|
|
||||||
of the Contributor shall be provided with the Contribution at the moment
|
|
||||||
of their provision to the Author. \
|
|
||||||
5.2. The Contributor represents and warrants that he legally owns exclusive
|
|
||||||
intellectual property rights to the Contributions. \
|
|
||||||
5.3. The Contributor represents and warrants that any further use of
|
|
||||||
Contributions by the Author as provided by Contributor under the terms
|
|
||||||
of the Agreement does not infringe on intellectual and other rights and
|
|
||||||
legitimate interests of third parties. \
|
|
||||||
5.4. The Contributor represents and warrants that he has all rights and legal
|
|
||||||
capacity needed to accept this Offer; \
|
|
||||||
5.5. The Contributor represents and warrants that Contributions don't
|
|
||||||
contain malware or any information considered illegal under the law
|
|
||||||
of Russian Federation.
|
|
||||||
|
|
||||||
6. Termination of the Agreement \
|
|
||||||
6.1. The Agreement may be terminated at will of both Author and Contributor,
|
|
||||||
formalised in the written form or if the Agreement is terminated on
|
|
||||||
reasons prescribed by the law of Russian Federation.
|
|
||||||
|
|
||||||
7. Final Clauses \
|
|
||||||
7.1. The Contributor may optionally sign the Agreement in the written form. \
|
|
||||||
7.2. The Agreement is deemed to become effective from the Date of signing of
|
|
||||||
the Agreement and until the expiration of Contributor's exclusive
|
|
||||||
intellectual property rights to the Contributions. \
|
|
||||||
7.3. The Author may unilaterally alter the Agreement without informing Contributors.
|
|
||||||
The new version of the document shall come into effect 3 (three) days after
|
|
||||||
being published in the Official Repository of the Program at Internet address
|
|
||||||
[https://git.yourcmc.ru/vitalif/vitastor/src/branch/master/CLA-en.md](https://git.yourcmc.ru/vitalif/vitastor/src/branch/master/CLA-en.md).
|
|
||||||
Contributors should keep informed about the actual version of the Agreement themselves. \
|
|
||||||
7.4. If the Author and the Contributor fail to agree on disputable issues,
|
|
||||||
disputes shall be referred to the Moscow Arbitration court.
|
|
108
CLA-ru.md
108
CLA-ru.md
|
@ -1,108 +0,0 @@
|
||||||
## Лицензионное соглашение с участником
|
|
||||||
|
|
||||||
> Данная Оферта написана в Русской и Английской версиях. **Версия на английском
|
|
||||||
языке предоставляется в информационных целях** и не связывает стороны договора.
|
|
||||||
>
|
|
||||||
> В случае несоответствий между положениями Русской и Английской версий Договора,
|
|
||||||
**Русская версия имеет приоритет**.
|
|
||||||
>
|
|
||||||
> Английская версия опубликована по адресу https://git.yourcmc.ru/vitalif/vitastor/src/branch/master/CLA-en.md
|
|
||||||
|
|
||||||
Настоящий договор-оферта (далее по тексту – Оферта, Договор) адресована физическим
|
|
||||||
и юридическим лицам (далее – Участникам) и является официальным публичным предложением
|
|
||||||
Филиппова Виталия Владимировича (далее – Автора) программного обеспечения Vitastor,
|
|
||||||
свидетельство Федеральной службы по интеллектуальной собственности (Роспатент) № 2021617829
|
|
||||||
от 20 мая 2021 г. (далее – Программа) о нижеследующем:
|
|
||||||
|
|
||||||
1. Термины и определения \
|
|
||||||
1.1. Репозиторий – электронное хранилище, содержащее исходный код Программы. \
|
|
||||||
1.2. Доработка – результат интеллектуальной деятельности Участника, включающий
|
|
||||||
в себя изменения или дополнения к исходному коду Программы, которые Участник
|
|
||||||
желает включить в состав Программы для дальнейшего использования и распространения
|
|
||||||
Автором и для этого направляет их Автору. \
|
|
||||||
1.3. Участник – физическое или юридическое лицо, вносящее Доработки в код Программы. \
|
|
||||||
1.4. ГК РФ – Гражданский кодекс Российской Федерации.
|
|
||||||
|
|
||||||
2. Предмет оферты \
|
|
||||||
2.1. Предметом настоящей оферты являются Доработки, отправляемые Участником Автору. \
|
|
||||||
2.2. Участник предоставляет Автору право использовать Доработки по собственному усмотрению
|
|
||||||
и без необходимости предварительного согласования с Участником или иным третьим лицом
|
|
||||||
на условиях простой (неисключительной) безвозмездной безотзывной лицензии, полностью
|
|
||||||
или фрагментарно, в составе Программы или других программ, продуктов или сервисов
|
|
||||||
как с открытым, так и с закрытым исходным кодом, любыми способами, не противоречащими
|
|
||||||
закону, включая, но не ограничиваясь следующими: \
|
|
||||||
2.2.1. Запускать и использовать Доработки для выполнения любых задач; \
|
|
||||||
2.2.2. Распространять, импортировать и доводить Доработки до всеобщего сведения; \
|
|
||||||
2.2.3. Вносить в Доработки изменения, сокращения и дополнения, снабжать Доработки
|
|
||||||
при их использовании комментариями, иллюстрациями или пояснениями; \
|
|
||||||
2.2.4. Создавать на основе Доработок иные результаты интеллектуальной деятельности,
|
|
||||||
в том числе производные и составные произведения; \
|
|
||||||
2.2.5. Переводить Доработки на другие языки, в том числе на другие языки программирования; \
|
|
||||||
2.2.6. Осуществлять прокат и публичный показ Доработок; \
|
|
||||||
2.2.7. Использовать Доработки под любым фирменным наименованием, товарным знаком
|
|
||||||
(знаком обслуживания) или иным обозначением, или без такового. \
|
|
||||||
2.3. Участник предоставляет Автору право сублицензировать полученные права на Доработки
|
|
||||||
третьим лицам на любых условиях на усмотрение Автора. \
|
|
||||||
2.4. Участник предоставляет Автору права на Доработки на территории всего мира. \
|
|
||||||
2.5. Участник предоставляет Автору права на весь срок действия исключительного права
|
|
||||||
Участника на Доработки. \
|
|
||||||
2.6. Участник предоставляет Автору права на Доработки на безвозмездной основе. \
|
|
||||||
2.7. Участник разрешает Автору самостоятельно определять порядок, способ и
|
|
||||||
место указания его имени, реквизитов и/или псевдонима при включении
|
|
||||||
Доработок в состав Программы или других программ, продуктов или сервисов.
|
|
||||||
|
|
||||||
3. Акцепт Оферты \
|
|
||||||
3.1. Участник может передавать Доработки в адрес Автора через зеркала официального
|
|
||||||
Репозитория Программы по адресам https://git.yourcmc.ru/vitalif/vitastor/ или
|
|
||||||
https://github.com/vitalif/vitastor/ в виде “запроса на слияние” (pull request),
|
|
||||||
либо в письменном виде или с помощью любых других электронных средств коммуникации,
|
|
||||||
например, электронной почты или мессенджеров. \
|
|
||||||
3.2. Факт передачи Участником Доработок в адрес Автора любым способом с одной из пометок
|
|
||||||
“I accept Vitastor CLA agreement: https://git.yourcmc.ru/vitalif/vitastor/src/branch/master/CLA-en.md”
|
|
||||||
или “Я принимаю соглашение Vitastor CLA: https://git.yourcmc.ru/vitalif/vitastor/src/branch/master/CLA-ru.md”
|
|
||||||
является полным и безоговорочным акцептом (принятием) Участником условий настоящей
|
|
||||||
Оферты, т.е. Участник считается ознакомившимся с настоящим публичным договором и
|
|
||||||
в соответствии с ГК РФ признается лицом, вступившим с Автором в договорные отношения
|
|
||||||
на основании настоящей Оферты. \
|
|
||||||
3.3. Датой акцептирования настоящей Оферты считается дата такой передачи.
|
|
||||||
|
|
||||||
4. Права и обязанности Сторон \
|
|
||||||
4.1. Участник сохраняет за собой право использовать Доработки любым законным
|
|
||||||
способом, не противоречащим настоящему Договору. \
|
|
||||||
4.2. Автор вправе отказать Участнику во включении Доработок в состав
|
|
||||||
Программы без объяснения причин в любой момент по своему усмотрению.
|
|
||||||
|
|
||||||
5. Гарантии и заверения \
|
|
||||||
5.1. Лицо, направляющее Доработки для целей их включения в состав Программы,
|
|
||||||
гарантирует, что является Участником или представителем Участника. Имя или реквизиты
|
|
||||||
Участника должны быть указаны при их передаче в адрес Автора Программы. \
|
|
||||||
5.2. Участник гарантирует, что является законным обладателем исключительных прав
|
|
||||||
на Доработки. \
|
|
||||||
5.3. Участник гарантирует, что на момент акцептирования настоящей Оферты ему
|
|
||||||
ничего не известно (и не могло быть известно) о правах третьих лиц на
|
|
||||||
передаваемые Автору Доработки или их часть, которые могут быть нарушены
|
|
||||||
в связи с передачей Доработок по настоящему Договору. \
|
|
||||||
5.4. Участник гарантирует, что является дееспособным лицом и обладает всеми
|
|
||||||
необходимыми правами для заключения Договора. \
|
|
||||||
5.5. Участник гарантирует, что Доработки не содержат вредоносного ПО, а также
|
|
||||||
любой другой информации, запрещённой к распространению по законам Российской
|
|
||||||
Федерации.
|
|
||||||
|
|
||||||
6. Прекращение действия оферты \
|
|
||||||
6.1. Действие настоящего договора может быть прекращено по соглашению сторон,
|
|
||||||
оформленному в письменном виде, а также вследствие его расторжения по основаниям,
|
|
||||||
предусмотренным законом.
|
|
||||||
|
|
||||||
7. Заключительные положения \
|
|
||||||
7.1. Участник вправе по желанию подписать настоящий Договор в письменном виде. \
|
|
||||||
7.2. Настоящий договор действует с момента его заключения и до истечения срока
|
|
||||||
действия исключительных прав Участника на Доработки. \
|
|
||||||
7.3. Автор имеет право в одностороннем порядке вносить изменения и дополнения в договор
|
|
||||||
без специального уведомления об этом Участников. Новая редакция документа вступает
|
|
||||||
в силу через 3 (Три) календарных дня со дня опубликования в официальном Репозитории
|
|
||||||
Программы по адресу в сети Интернет
|
|
||||||
[https://git.yourcmc.ru/vitalif/vitastor/src/branch/master/CLA-ru.md](https://git.yourcmc.ru/vitalif/vitastor/src/branch/master/CLA-ru.md).
|
|
||||||
Участники самостоятельно отслеживают действующие условия Оферты. \
|
|
||||||
7.4. Все споры, возникающие между сторонами в процессе их взаимодействия по настоящему
|
|
||||||
договору, решаются путём переговоров. В случае невозможности урегулирования споров
|
|
||||||
переговорным порядком стороны разрешают их в Арбитражном суде г.Москвы.
|
|
|
@ -2,6 +2,6 @@ cmake_minimum_required(VERSION 2.8.12)
|
||||||
|
|
||||||
project(vitastor)
|
project(vitastor)
|
||||||
|
|
||||||
set(VERSION "1.4.4")
|
set(VERSION "1.0.0")
|
||||||
|
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
|
|
|
@ -50,7 +50,6 @@ Vitastor поддерживает QEMU-драйвер, протоколы NBD и
|
||||||
- Параметры
|
- Параметры
|
||||||
- [Общие](docs/config/common.ru.md)
|
- [Общие](docs/config/common.ru.md)
|
||||||
- [Сетевые](docs/config/network.ru.md)
|
- [Сетевые](docs/config/network.ru.md)
|
||||||
- [Клиентский код](docs/config/client.en.md)
|
|
||||||
- [Глобальные дисковые параметры](docs/config/layout-cluster.ru.md)
|
- [Глобальные дисковые параметры](docs/config/layout-cluster.ru.md)
|
||||||
- [Дисковые параметры OSD](docs/config/layout-osd.ru.md)
|
- [Дисковые параметры OSD](docs/config/layout-osd.ru.md)
|
||||||
- [Прочие параметры OSD](docs/config/osd.ru.md)
|
- [Прочие параметры OSD](docs/config/osd.ru.md)
|
||||||
|
|
|
@ -50,7 +50,6 @@ Read more details below in the documentation.
|
||||||
- Parameter Reference
|
- Parameter Reference
|
||||||
- [Common](docs/config/common.en.md)
|
- [Common](docs/config/common.en.md)
|
||||||
- [Network](docs/config/network.en.md)
|
- [Network](docs/config/network.en.md)
|
||||||
- [Client](docs/config/client.en.md)
|
|
||||||
- [Global Disk Layout](docs/config/layout-cluster.en.md)
|
- [Global Disk Layout](docs/config/layout-cluster.en.md)
|
||||||
- [OSD Disk Layout](docs/config/layout-osd.en.md)
|
- [OSD Disk Layout](docs/config/layout-osd.en.md)
|
||||||
- [OSD Runtime Parameters](docs/config/osd.en.md)
|
- [OSD Runtime Parameters](docs/config/osd.en.md)
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 8de8b467acbca50cfd8835c20e0e379110f3b32b
|
Subproject commit 45e6d1f13196a0824e2089a586c53b9de0283f17
|
|
@ -1,15 +1,14 @@
|
||||||
# Compile stage
|
# Compile stage
|
||||||
FROM golang:bookworm AS build
|
FROM golang:buster AS build
|
||||||
|
|
||||||
ADD go.sum go.mod /app/
|
ADD go.sum go.mod /app/
|
||||||
RUN cd /app; CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go mod download -x
|
RUN cd /app; CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go mod download -x
|
||||||
ADD . /app
|
ADD . /app
|
||||||
RUN perl -i -e '$/ = undef; while(<>) { s/\n\s*(\{\s*\n)/$1\n/g; s/\}(\s*\n\s*)else\b/$1} else/g; print; }' `find /app -name '*.go'` && \
|
RUN perl -i -e '$/ = undef; while(<>) { s/\n\s*(\{\s*\n)/$1\n/g; s/\}(\s*\n\s*)else\b/$1} else/g; print; }' `find /app -name '*.go'`
|
||||||
cd /app && \
|
RUN cd /app; CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -o vitastor-csi
|
||||||
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -o vitastor-csi
|
|
||||||
|
|
||||||
# Final stage
|
# Final stage
|
||||||
FROM debian:bookworm
|
FROM debian:buster
|
||||||
|
|
||||||
LABEL maintainers="Vitaliy Filippov <vitalif@yourcmc.ru>"
|
LABEL maintainers="Vitaliy Filippov <vitalif@yourcmc.ru>"
|
||||||
LABEL description="Vitastor CSI Driver"
|
LABEL description="Vitastor CSI Driver"
|
||||||
|
@ -19,30 +18,19 @@ ENV CSI_ENDPOINT=""
|
||||||
|
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y wget && \
|
apt-get install -y wget && \
|
||||||
|
(echo deb http://deb.debian.org/debian buster-backports main > /etc/apt/sources.list.d/backports.list) && \
|
||||||
(echo "APT::Install-Recommends false;" > /etc/apt/apt.conf) && \
|
(echo "APT::Install-Recommends false;" > /etc/apt/apt.conf) && \
|
||||||
apt-get update && \
|
apt-get update && \
|
||||||
apt-get install -y e2fsprogs xfsprogs kmod iproute2 \
|
apt-get install -y e2fsprogs xfsprogs kmod && \
|
||||||
# dependencies of qemu-storage-daemon
|
|
||||||
libnuma1 liburing2 libglib2.0-0 libfuse3-3 libaio1 libzstd1 libnettle8 \
|
|
||||||
libgmp10 libhogweed6 libp11-kit0 libidn2-0 libunistring2 libtasn1-6 libpcre2-8-0 libffi8 && \
|
|
||||||
apt-get clean && \
|
apt-get clean && \
|
||||||
(echo options nbd nbds_max=128 > /etc/modprobe.d/nbd.conf)
|
(echo options nbd nbds_max=128 > /etc/modprobe.d/nbd.conf)
|
||||||
|
|
||||||
COPY --from=build /app/vitastor-csi /bin/
|
COPY --from=build /app/vitastor-csi /bin/
|
||||||
|
|
||||||
RUN (echo deb http://vitastor.io/debian bookworm main > /etc/apt/sources.list.d/vitastor.list) && \
|
RUN (echo deb http://vitastor.io/debian buster main > /etc/apt/sources.list.d/vitastor.list) && \
|
||||||
((echo 'Package: *'; echo 'Pin: origin "vitastor.io"'; echo 'Pin-Priority: 1000') > /etc/apt/preferences.d/vitastor.pref) && \
|
|
||||||
wget -q -O /etc/apt/trusted.gpg.d/vitastor.gpg https://vitastor.io/debian/pubkey.gpg && \
|
wget -q -O /etc/apt/trusted.gpg.d/vitastor.gpg https://vitastor.io/debian/pubkey.gpg && \
|
||||||
apt-get update && \
|
apt-get update && \
|
||||||
apt-get install -y vitastor-client && \
|
apt-get install -y vitastor-client && \
|
||||||
wget https://vitastor.io/archive/qemu/qemu-bookworm-8.1.2%2Bds-1%2Bvitastor1/qemu-utils_8.1.2%2Bds-1%2Bvitastor1_amd64.deb && \
|
|
||||||
wget https://vitastor.io/archive/qemu/qemu-bookworm-8.1.2%2Bds-1%2Bvitastor1/qemu-block-extra_8.1.2%2Bds-1%2Bvitastor1_amd64.deb && \
|
|
||||||
dpkg -x qemu-utils*.deb tmp1 && \
|
|
||||||
dpkg -x qemu-block-extra*.deb tmp1 && \
|
|
||||||
cp -a tmp1/usr/bin/qemu-storage-daemon /usr/bin/ && \
|
|
||||||
mkdir -p /usr/lib/x86_64-linux-gnu/qemu && \
|
|
||||||
cp -a tmp1/usr/lib/x86_64-linux-gnu/qemu/block-vitastor.so /usr/lib/x86_64-linux-gnu/qemu/ && \
|
|
||||||
rm -rf tmp1 *.deb && \
|
|
||||||
apt-get clean
|
apt-get clean
|
||||||
|
|
||||||
ENTRYPOINT ["/bin/vitastor-csi"]
|
ENTRYPOINT ["/bin/vitastor-csi"]
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
VERSION ?= v1.4.4
|
VERSION ?= v1.0.0
|
||||||
|
|
||||||
all: build push
|
all: build push
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
data:
|
data:
|
||||||
# You can add multiple configuration files here to use a multi-cluster setup
|
|
||||||
vitastor.conf: |-
|
vitastor.conf: |-
|
||||||
{"etcd_address":"http://192.168.7.2:2379","etcd_prefix":"/vitastor"}
|
{"etcd_address":"http://192.168.7.2:2379","etcd_prefix":"/vitastor"}
|
||||||
metadata:
|
metadata:
|
||||||
|
|
|
@ -49,7 +49,7 @@ spec:
|
||||||
capabilities:
|
capabilities:
|
||||||
add: ["SYS_ADMIN"]
|
add: ["SYS_ADMIN"]
|
||||||
allowPrivilegeEscalation: true
|
allowPrivilegeEscalation: true
|
||||||
image: vitalif/vitastor-csi:v1.4.4
|
image: vitalif/vitastor-csi:v1.0.0
|
||||||
args:
|
args:
|
||||||
- "--node=$(NODE_ID)"
|
- "--node=$(NODE_ID)"
|
||||||
- "--endpoint=$(CSI_ENDPOINT)"
|
- "--endpoint=$(CSI_ENDPOINT)"
|
||||||
|
@ -82,8 +82,6 @@ spec:
|
||||||
name: host-sys
|
name: host-sys
|
||||||
- mountPath: /run/mount
|
- mountPath: /run/mount
|
||||||
name: host-mount
|
name: host-mount
|
||||||
- mountPath: /run/vitastor-csi
|
|
||||||
name: run-vitastor-csi
|
|
||||||
- mountPath: /lib/modules
|
- mountPath: /lib/modules
|
||||||
name: lib-modules
|
name: lib-modules
|
||||||
readOnly: true
|
readOnly: true
|
||||||
|
@ -134,9 +132,6 @@ spec:
|
||||||
- name: host-mount
|
- name: host-mount
|
||||||
hostPath:
|
hostPath:
|
||||||
path: /run/mount
|
path: /run/mount
|
||||||
- name: run-vitastor-csi
|
|
||||||
hostPath:
|
|
||||||
path: /run/vitastor-csi
|
|
||||||
- name: lib-modules
|
- name: lib-modules
|
||||||
hostPath:
|
hostPath:
|
||||||
path: /lib/modules
|
path: /lib/modules
|
||||||
|
|
|
@ -35,13 +35,10 @@ rules:
|
||||||
verbs: ["get", "list", "watch"]
|
verbs: ["get", "list", "watch"]
|
||||||
- apiGroups: ["snapshot.storage.k8s.io"]
|
- apiGroups: ["snapshot.storage.k8s.io"]
|
||||||
resources: ["volumesnapshots"]
|
resources: ["volumesnapshots"]
|
||||||
verbs: ["get", "list", "patch"]
|
verbs: ["get", "list"]
|
||||||
- apiGroups: ["snapshot.storage.k8s.io"]
|
|
||||||
resources: ["volumesnapshots/status"]
|
|
||||||
verbs: ["get", "list", "patch"]
|
|
||||||
- apiGroups: ["snapshot.storage.k8s.io"]
|
- apiGroups: ["snapshot.storage.k8s.io"]
|
||||||
resources: ["volumesnapshotcontents"]
|
resources: ["volumesnapshotcontents"]
|
||||||
verbs: ["create", "get", "list", "watch", "update", "delete", "patch"]
|
verbs: ["create", "get", "list", "watch", "update", "delete"]
|
||||||
- apiGroups: ["snapshot.storage.k8s.io"]
|
- apiGroups: ["snapshot.storage.k8s.io"]
|
||||||
resources: ["volumesnapshotclasses"]
|
resources: ["volumesnapshotclasses"]
|
||||||
verbs: ["get", "list", "watch"]
|
verbs: ["get", "list", "watch"]
|
||||||
|
@ -56,7 +53,7 @@ rules:
|
||||||
verbs: ["get", "list", "watch"]
|
verbs: ["get", "list", "watch"]
|
||||||
- apiGroups: ["snapshot.storage.k8s.io"]
|
- apiGroups: ["snapshot.storage.k8s.io"]
|
||||||
resources: ["volumesnapshotcontents/status"]
|
resources: ["volumesnapshotcontents/status"]
|
||||||
verbs: ["update", "patch"]
|
verbs: ["update"]
|
||||||
- apiGroups: [""]
|
- apiGroups: [""]
|
||||||
resources: ["configmaps"]
|
resources: ["configmaps"]
|
||||||
verbs: ["get"]
|
verbs: ["get"]
|
||||||
|
|
|
@ -23,11 +23,6 @@ metadata:
|
||||||
name: csi-vitastor-provisioner
|
name: csi-vitastor-provisioner
|
||||||
spec:
|
spec:
|
||||||
replicas: 3
|
replicas: 3
|
||||||
strategy:
|
|
||||||
type: RollingUpdate
|
|
||||||
rollingUpdate:
|
|
||||||
maxUnavailable: 1
|
|
||||||
maxSurge: 0
|
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app: csi-vitastor-provisioner
|
app: csi-vitastor-provisioner
|
||||||
|
@ -51,7 +46,7 @@ spec:
|
||||||
priorityClassName: system-cluster-critical
|
priorityClassName: system-cluster-critical
|
||||||
containers:
|
containers:
|
||||||
- name: csi-provisioner
|
- name: csi-provisioner
|
||||||
image: k8s.gcr.io/sig-storage/csi-provisioner:v3.0.0
|
image: k8s.gcr.io/sig-storage/csi-provisioner:v2.2.0
|
||||||
args:
|
args:
|
||||||
- "--csi-address=$(ADDRESS)"
|
- "--csi-address=$(ADDRESS)"
|
||||||
- "--v=5"
|
- "--v=5"
|
||||||
|
@ -121,7 +116,7 @@ spec:
|
||||||
privileged: true
|
privileged: true
|
||||||
capabilities:
|
capabilities:
|
||||||
add: ["SYS_ADMIN"]
|
add: ["SYS_ADMIN"]
|
||||||
image: vitalif/vitastor-csi:v1.4.4
|
image: vitalif/vitastor-csi:v1.0.0
|
||||||
args:
|
args:
|
||||||
- "--node=$(NODE_ID)"
|
- "--node=$(NODE_ID)"
|
||||||
- "--endpoint=$(CSI_ENDPOINT)"
|
- "--endpoint=$(CSI_ENDPOINT)"
|
||||||
|
|
|
@ -12,6 +12,8 @@ parameters:
|
||||||
etcdVolumePrefix: ""
|
etcdVolumePrefix: ""
|
||||||
poolId: "1"
|
poolId: "1"
|
||||||
# you can choose other configuration file if you have it in the config map
|
# you can choose other configuration file if you have it in the config map
|
||||||
# different etcd URLs and prefixes should also be put in the config
|
|
||||||
#configPath: "/etc/vitastor/vitastor.conf"
|
#configPath: "/etc/vitastor/vitastor.conf"
|
||||||
allowVolumeExpansion: true
|
# you can also specify etcdUrl here, maybe to connect to another Vitastor cluster
|
||||||
|
# multiple etcdUrls may be specified, delimited by comma
|
||||||
|
#etcdUrl: "http://192.168.7.2:2379"
|
||||||
|
#etcdPrefix: "/vitastor"
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
apiVersion: snapshot.storage.k8s.io/v1
|
|
||||||
kind: VolumeSnapshotClass
|
|
||||||
metadata:
|
|
||||||
name: vitastor-snapclass
|
|
||||||
driver: csi.vitastor.io
|
|
||||||
deletionPolicy: Delete
|
|
||||||
parameters:
|
|
|
@ -1,16 +0,0 @@
|
||||||
---
|
|
||||||
apiVersion: v1
|
|
||||||
kind: PersistentVolumeClaim
|
|
||||||
metadata:
|
|
||||||
name: test-vitastor-clone
|
|
||||||
spec:
|
|
||||||
storageClassName: vitastor
|
|
||||||
dataSource:
|
|
||||||
name: snap1
|
|
||||||
kind: VolumeSnapshot
|
|
||||||
apiGroup: snapshot.storage.k8s.io
|
|
||||||
accessModes:
|
|
||||||
- ReadWriteOnce
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
storage: 10Gi
|
|
|
@ -1,8 +0,0 @@
|
||||||
apiVersion: snapshot.storage.k8s.io/v1
|
|
||||||
kind: VolumeSnapshot
|
|
||||||
metadata:
|
|
||||||
name: snap1
|
|
||||||
spec:
|
|
||||||
volumeSnapshotClassName: vitastor-snapclass
|
|
||||||
source:
|
|
||||||
persistentVolumeClaimName: test-vitastor-pvc
|
|
|
@ -9,7 +9,6 @@ require (
|
||||||
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb
|
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||||
google.golang.org/grpc v1.33.1
|
google.golang.org/grpc v1.33.1
|
||||||
google.golang.org/protobuf v1.24.0
|
|
||||||
k8s.io/klog v1.0.0
|
k8s.io/klog v1.0.0
|
||||||
k8s.io/utils v0.0.0-20210305010621-2afb4311ab10
|
k8s.io/utils v0.0.0-20210305010621-2afb4311ab10
|
||||||
)
|
)
|
||||||
|
|
|
@ -5,7 +5,7 @@ package vitastor
|
||||||
|
|
||||||
const (
|
const (
|
||||||
vitastorCSIDriverName = "csi.vitastor.io"
|
vitastorCSIDriverName = "csi.vitastor.io"
|
||||||
vitastorCSIDriverVersion = "1.4.4"
|
vitastorCSIDriverVersion = "1.0.0"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config struct fills the parameters of request or user input
|
// Config struct fills the parameters of request or user input
|
||||||
|
|
|
@ -20,7 +20,6 @@ import (
|
||||||
|
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
|
||||||
|
|
||||||
"github.com/container-storage-interface/spec/lib/go/csi"
|
"github.com/container-storage-interface/spec/lib/go/csi"
|
||||||
)
|
)
|
||||||
|
@ -46,7 +45,6 @@ type InodeConfig struct
|
||||||
ParentPool uint64 `json:"parent_pool,omitempty"`
|
ParentPool uint64 `json:"parent_pool,omitempty"`
|
||||||
ParentId uint64 `json:"parent_id,omitempty"`
|
ParentId uint64 `json:"parent_id,omitempty"`
|
||||||
Readonly bool `json:"readonly,omitempty"`
|
Readonly bool `json:"readonly,omitempty"`
|
||||||
CreateTs uint64 `json:"create_ts,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ControllerServer struct
|
type ControllerServer struct
|
||||||
|
@ -62,7 +60,7 @@ func NewControllerServer(driver *Driver) *ControllerServer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetConnectionParams(params map[string]string) (map[string]string, error)
|
func GetConnectionParams(params map[string]string) (map[string]string, []string, string)
|
||||||
{
|
{
|
||||||
ctxVars := make(map[string]string)
|
ctxVars := make(map[string]string)
|
||||||
configPath := params["configPath"]
|
configPath := params["configPath"]
|
||||||
|
@ -75,69 +73,71 @@ func GetConnectionParams(params map[string]string) (map[string]string, error)
|
||||||
ctxVars["configPath"] = configPath
|
ctxVars["configPath"] = configPath
|
||||||
}
|
}
|
||||||
config := make(map[string]interface{})
|
config := make(map[string]interface{})
|
||||||
configFD, err := os.Open(configPath)
|
if configFD, err := os.Open(configPath); err == nil
|
||||||
if (err != nil)
|
|
||||||
{
|
{
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer configFD.Close()
|
defer configFD.Close()
|
||||||
data, _ := ioutil.ReadAll(configFD)
|
data, _ := ioutil.ReadAll(configFD)
|
||||||
json.Unmarshal(data, &config)
|
json.Unmarshal(data, &config)
|
||||||
// Check etcd URL in the config, but do not use the explicit etcdUrl
|
}
|
||||||
// parameter for CLI calls, otherwise users won't be able to later
|
// Try to load prefix & etcd URL from the config
|
||||||
// change them - storage class parameters are saved in volume IDs
|
|
||||||
var etcdUrl []string
|
var etcdUrl []string
|
||||||
switch config["etcd_address"].(type)
|
if (params["etcdUrl"] != "")
|
||||||
{
|
{
|
||||||
case string:
|
ctxVars["etcdUrl"] = params["etcdUrl"]
|
||||||
url := strings.TrimSpace(config["etcd_address"].(string))
|
etcdUrl = strings.Split(params["etcdUrl"], ",")
|
||||||
if (url != "")
|
|
||||||
{
|
|
||||||
etcdUrl = strings.Split(url, ",")
|
|
||||||
}
|
|
||||||
case []string:
|
|
||||||
etcdUrl = config["etcd_address"].([]string)
|
|
||||||
case []interface{}:
|
|
||||||
for _, url := range config["etcd_address"].([]interface{})
|
|
||||||
{
|
|
||||||
s, ok := url.(string)
|
|
||||||
if (ok)
|
|
||||||
{
|
|
||||||
etcdUrl = append(etcdUrl, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (len(etcdUrl) == 0)
|
if (len(etcdUrl) == 0)
|
||||||
{
|
{
|
||||||
return nil, status.Error(codes.InvalidArgument, "etcd_address is missing in "+configPath)
|
switch config["etcd_address"].(type)
|
||||||
}
|
|
||||||
return ctxVars, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func system(program string, args ...string) ([]byte, []byte, error)
|
|
||||||
{
|
|
||||||
klog.Infof("Running "+program+" "+strings.Join(args, " "))
|
|
||||||
c := exec.Command(program, args...)
|
|
||||||
var stdout, stderr bytes.Buffer
|
|
||||||
c.Stdout, c.Stderr = &stdout, &stderr
|
|
||||||
err := c.Run()
|
|
||||||
if (err != nil)
|
|
||||||
{
|
{
|
||||||
stdoutStr, stderrStr := string(stdout.Bytes()), string(stderr.Bytes())
|
case string:
|
||||||
klog.Errorf(program+" "+strings.Join(args, " ")+" failed: %s, status %s\n", stdoutStr+stderrStr, err)
|
etcdUrl = strings.Split(config["etcd_address"].(string), ",")
|
||||||
return nil, nil, status.Error(codes.Internal, stdoutStr+stderrStr+" (status "+err.Error()+")")
|
case []string:
|
||||||
|
etcdUrl = config["etcd_address"].([]string)
|
||||||
}
|
}
|
||||||
return stdout.Bytes(), stderr.Bytes(), nil
|
}
|
||||||
|
etcdPrefix := params["etcdPrefix"]
|
||||||
|
if (etcdPrefix == "")
|
||||||
|
{
|
||||||
|
etcdPrefix, _ = config["etcd_prefix"].(string)
|
||||||
|
if (etcdPrefix == "")
|
||||||
|
{
|
||||||
|
etcdPrefix = "/vitastor"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ctxVars["etcdPrefix"] = etcdPrefix
|
||||||
|
}
|
||||||
|
return ctxVars, etcdUrl, etcdPrefix
|
||||||
}
|
}
|
||||||
|
|
||||||
func invokeCLI(ctxVars map[string]string, args []string) ([]byte, error)
|
func invokeCLI(ctxVars map[string]string, args []string) ([]byte, error)
|
||||||
{
|
{
|
||||||
|
if (ctxVars["etcdUrl"] != "")
|
||||||
|
{
|
||||||
|
args = append(args, "--etcd_address", ctxVars["etcdUrl"])
|
||||||
|
}
|
||||||
|
if (ctxVars["etcdPrefix"] != "")
|
||||||
|
{
|
||||||
|
args = append(args, "--etcd_prefix", ctxVars["etcdPrefix"])
|
||||||
|
}
|
||||||
if (ctxVars["configPath"] != "")
|
if (ctxVars["configPath"] != "")
|
||||||
{
|
{
|
||||||
args = append(args, "--config_path", ctxVars["configPath"])
|
args = append(args, "--config_path", ctxVars["configPath"])
|
||||||
}
|
}
|
||||||
stdout, _, err := system("/usr/bin/vitastor-cli", args...)
|
c := exec.Command("/usr/bin/vitastor-cli", args...)
|
||||||
return stdout, err
|
var stdout, stderr bytes.Buffer
|
||||||
|
c.Stdout = &stdout
|
||||||
|
c.Stderr = &stderr
|
||||||
|
err := c.Run()
|
||||||
|
stderrStr := string(stderr.Bytes())
|
||||||
|
if (err != nil)
|
||||||
|
{
|
||||||
|
klog.Errorf("vitastor-cli %s failed: %s, status %s\n", strings.Join(args, " "), stderrStr, err)
|
||||||
|
return nil, status.Error(codes.Internal, stderrStr+" (status "+err.Error()+")")
|
||||||
|
}
|
||||||
|
return stdout.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the volume
|
// Create the volume
|
||||||
|
@ -172,49 +172,33 @@ func (cs *ControllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
|
||||||
volSize = ((capRange.GetRequiredBytes() + MB - 1) / MB) * MB
|
volSize = ((capRange.GetRequiredBytes() + MB - 1) / MB) * MB
|
||||||
}
|
}
|
||||||
|
|
||||||
ctxVars, err := GetConnectionParams(req.Parameters)
|
ctxVars, etcdUrl, _ := GetConnectionParams(req.Parameters)
|
||||||
if (err != nil)
|
if (len(etcdUrl) == 0)
|
||||||
{
|
{
|
||||||
return nil, err
|
return nil, status.Error(codes.InvalidArgument, "no etcdUrl in storage class configuration and no etcd_address in vitastor.conf")
|
||||||
}
|
|
||||||
|
|
||||||
args := []string{ "create", volName, "-s", fmt.Sprintf("%v", volSize), "--pool", fmt.Sprintf("%v", poolId) }
|
|
||||||
|
|
||||||
// Support creation from snapshot
|
|
||||||
var src *csi.VolumeContentSource
|
|
||||||
if (req.VolumeContentSource.GetSnapshot() != nil)
|
|
||||||
{
|
|
||||||
snapId := req.VolumeContentSource.GetSnapshot().GetSnapshotId()
|
|
||||||
if (snapId != "")
|
|
||||||
{
|
|
||||||
snapVars := make(map[string]string)
|
|
||||||
err := json.Unmarshal([]byte(snapId), &snapVars)
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
return nil, status.Error(codes.Internal, "volume ID not in JSON format")
|
|
||||||
}
|
|
||||||
args = append(args, "--parent", snapVars["name"]+"@"+snapVars["snapshot"])
|
|
||||||
src = &csi.VolumeContentSource{
|
|
||||||
Type: &csi.VolumeContentSource_Snapshot{
|
|
||||||
Snapshot: &csi.VolumeContentSource_SnapshotSource{
|
|
||||||
SnapshotId: snapId,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create image using vitastor-cli
|
// Create image using vitastor-cli
|
||||||
_, err = invokeCLI(ctxVars, args)
|
_, err := invokeCLI(ctxVars, []string{ "create", volName, "-s", fmt.Sprintf("%v", volSize), "--pool", fmt.Sprintf("%v", poolId) })
|
||||||
if (err != nil)
|
if (err != nil)
|
||||||
{
|
{
|
||||||
if (strings.Index(err.Error(), "already exists") > 0)
|
if (strings.Index(err.Error(), "already exists") > 0)
|
||||||
{
|
{
|
||||||
inodeCfg, err := invokeList(ctxVars, volName, true)
|
stat, err := invokeCLI(ctxVars, []string{ "ls", "--json", volName })
|
||||||
if (err != nil)
|
if (err != nil)
|
||||||
{
|
{
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
var inodeCfg []InodeConfig
|
||||||
|
err = json.Unmarshal(stat, &inodeCfg)
|
||||||
|
if (err != nil)
|
||||||
|
{
|
||||||
|
return nil, status.Error(codes.Internal, "Invalid JSON in vitastor-cli ls: "+err.Error())
|
||||||
|
}
|
||||||
|
if (len(inodeCfg) == 0)
|
||||||
|
{
|
||||||
|
return nil, status.Error(codes.Internal, "vitastor-cli create said that image already exists, but ls can't find it")
|
||||||
|
}
|
||||||
if (inodeCfg[0].Size < uint64(volSize))
|
if (inodeCfg[0].Size < uint64(volSize))
|
||||||
{
|
{
|
||||||
return nil, status.Error(codes.Internal, "image "+volName+" is already created, but size is less than expected")
|
return nil, status.Error(codes.Internal, "image "+volName+" is already created, but size is less than expected")
|
||||||
|
@ -233,7 +217,6 @@ func (cs *ControllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
|
||||||
// Ugly, but VolumeContext isn't passed to DeleteVolume :-(
|
// Ugly, but VolumeContext isn't passed to DeleteVolume :-(
|
||||||
VolumeId: string(volumeIdJson),
|
VolumeId: string(volumeIdJson),
|
||||||
CapacityBytes: volSize,
|
CapacityBytes: volSize,
|
||||||
ContentSource: src,
|
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -247,19 +230,15 @@ func (cs *ControllerServer) DeleteVolume(ctx context.Context, req *csi.DeleteVol
|
||||||
return nil, status.Error(codes.InvalidArgument, "request cannot be empty")
|
return nil, status.Error(codes.InvalidArgument, "request cannot be empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
volVars := make(map[string]string)
|
ctxVars := make(map[string]string)
|
||||||
err := json.Unmarshal([]byte(req.VolumeId), &volVars)
|
err := json.Unmarshal([]byte(req.VolumeId), &ctxVars)
|
||||||
if (err != nil)
|
if (err != nil)
|
||||||
{
|
{
|
||||||
return nil, status.Error(codes.Internal, "volume ID not in JSON format")
|
return nil, status.Error(codes.Internal, "volume ID not in JSON format")
|
||||||
}
|
}
|
||||||
volName := volVars["name"]
|
volName := ctxVars["name"]
|
||||||
|
|
||||||
ctxVars, err := GetConnectionParams(volVars)
|
ctxVars, _, _ = GetConnectionParams(ctxVars)
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = invokeCLI(ctxVars, []string{ "rm", volName })
|
_, err = invokeCLI(ctxVars, []string{ "rm", volName })
|
||||||
if (err != nil)
|
if (err != nil)
|
||||||
|
@ -365,8 +344,6 @@ func (cs *ControllerServer) ControllerGetCapabilities(ctx context.Context, req *
|
||||||
csi.ControllerServiceCapability_RPC_LIST_VOLUMES,
|
csi.ControllerServiceCapability_RPC_LIST_VOLUMES,
|
||||||
csi.ControllerServiceCapability_RPC_EXPAND_VOLUME,
|
csi.ControllerServiceCapability_RPC_EXPAND_VOLUME,
|
||||||
csi.ControllerServiceCapability_RPC_CREATE_DELETE_SNAPSHOT,
|
csi.ControllerServiceCapability_RPC_CREATE_DELETE_SNAPSHOT,
|
||||||
csi.ControllerServiceCapability_RPC_LIST_SNAPSHOTS,
|
|
||||||
// TODO: csi.ControllerServiceCapability_RPC_CLONE_VOLUME,
|
|
||||||
} {
|
} {
|
||||||
controllerServerCapabilities = append(controllerServerCapabilities, functionControllerServerCapabilities(capability))
|
controllerServerCapabilities = append(controllerServerCapabilities, functionControllerServerCapabilities(capability))
|
||||||
}
|
}
|
||||||
|
@ -376,226 +353,28 @@ func (cs *ControllerServer) ControllerGetCapabilities(ctx context.Context, req *
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func invokeList(ctxVars map[string]string, pattern string, expectExist bool) ([]InodeConfig, error)
|
|
||||||
{
|
|
||||||
stat, err := invokeCLI(ctxVars, []string{ "ls", "--json", pattern })
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var inodeCfg []InodeConfig
|
|
||||||
err = json.Unmarshal(stat, &inodeCfg)
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
return nil, status.Error(codes.Internal, "Invalid JSON in vitastor-cli ls: "+err.Error())
|
|
||||||
}
|
|
||||||
if (expectExist && len(inodeCfg) == 0)
|
|
||||||
{
|
|
||||||
return nil, status.Error(codes.Internal, "Can't find expected image "+pattern+" via vitastor-cli ls")
|
|
||||||
}
|
|
||||||
return inodeCfg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateSnapshot create snapshot of an existing PV
|
// CreateSnapshot create snapshot of an existing PV
|
||||||
func (cs *ControllerServer) CreateSnapshot(ctx context.Context, req *csi.CreateSnapshotRequest) (*csi.CreateSnapshotResponse, error)
|
func (cs *ControllerServer) CreateSnapshot(ctx context.Context, req *csi.CreateSnapshotRequest) (*csi.CreateSnapshotResponse, error)
|
||||||
{
|
{
|
||||||
klog.Infof("received controller create snapshot request %+v", protosanitizer.StripSecrets(req))
|
return nil, status.Error(codes.Unimplemented, "")
|
||||||
if (req == nil)
|
|
||||||
{
|
|
||||||
return nil, status.Errorf(codes.InvalidArgument, "request cannot be empty")
|
|
||||||
}
|
|
||||||
if (req.SourceVolumeId == "" || req.Name == "")
|
|
||||||
{
|
|
||||||
return nil, status.Error(codes.InvalidArgument, "source volume ID and snapshot name are required fields")
|
|
||||||
}
|
|
||||||
|
|
||||||
// snapshot name
|
|
||||||
snapName := req.Name
|
|
||||||
|
|
||||||
// req.VolumeId is an ugly json string in our case :)
|
|
||||||
ctxVars := make(map[string]string)
|
|
||||||
err := json.Unmarshal([]byte(req.SourceVolumeId), &ctxVars)
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
return nil, status.Error(codes.Internal, "volume ID not in JSON format")
|
|
||||||
}
|
|
||||||
volName := ctxVars["name"]
|
|
||||||
|
|
||||||
// Create image using vitastor-cli
|
|
||||||
_, err = invokeCLI(ctxVars, []string{ "create", "--snapshot", snapName, volName })
|
|
||||||
if (err != nil && strings.Index(err.Error(), "already exists") <= 0)
|
|
||||||
{
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check created snapshot
|
|
||||||
inodeCfg, err := invokeList(ctxVars, volName+"@"+snapName, true)
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use ugly JSON snapshot ID again, DeleteSnapshot doesn't have context :-(
|
|
||||||
ctxVars["snapshot"] = snapName
|
|
||||||
snapIdJson, _ := json.Marshal(ctxVars)
|
|
||||||
return &csi.CreateSnapshotResponse{
|
|
||||||
Snapshot: &csi.Snapshot{
|
|
||||||
SizeBytes: int64(inodeCfg[0].Size),
|
|
||||||
SnapshotId: string(snapIdJson),
|
|
||||||
SourceVolumeId: req.SourceVolumeId,
|
|
||||||
CreationTime: ×tamppb.Timestamp{ Seconds: int64(inodeCfg[0].CreateTs) },
|
|
||||||
ReadyToUse: true,
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteSnapshot delete provided snapshot of a PV
|
// DeleteSnapshot delete provided snapshot of a PV
|
||||||
func (cs *ControllerServer) DeleteSnapshot(ctx context.Context, req *csi.DeleteSnapshotRequest) (*csi.DeleteSnapshotResponse, error)
|
func (cs *ControllerServer) DeleteSnapshot(ctx context.Context, req *csi.DeleteSnapshotRequest) (*csi.DeleteSnapshotResponse, error)
|
||||||
{
|
{
|
||||||
klog.Infof("received controller delete snapshot request %+v", protosanitizer.StripSecrets(req))
|
return nil, status.Error(codes.Unimplemented, "")
|
||||||
if (req == nil)
|
|
||||||
{
|
|
||||||
return nil, status.Errorf(codes.InvalidArgument, "request cannot be empty")
|
|
||||||
}
|
|
||||||
if (req.SnapshotId == "")
|
|
||||||
{
|
|
||||||
return nil, status.Error(codes.InvalidArgument, "snapshot ID is a required field")
|
|
||||||
}
|
|
||||||
|
|
||||||
volVars := make(map[string]string)
|
|
||||||
err := json.Unmarshal([]byte(req.SnapshotId), &volVars)
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
return nil, status.Error(codes.Internal, "snapshot ID not in JSON format")
|
|
||||||
}
|
|
||||||
volName := volVars["name"]
|
|
||||||
snapName := volVars["snapshot"]
|
|
||||||
|
|
||||||
ctxVars, err := GetConnectionParams(volVars)
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = invokeCLI(ctxVars, []string{ "rm", volName+"@"+snapName })
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &csi.DeleteSnapshotResponse{}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListSnapshots list the snapshots of a PV
|
// ListSnapshots list the snapshots of a PV
|
||||||
func (cs *ControllerServer) ListSnapshots(ctx context.Context, req *csi.ListSnapshotsRequest) (*csi.ListSnapshotsResponse, error)
|
func (cs *ControllerServer) ListSnapshots(ctx context.Context, req *csi.ListSnapshotsRequest) (*csi.ListSnapshotsResponse, error)
|
||||||
{
|
{
|
||||||
klog.Infof("received controller list snapshots request %+v", protosanitizer.StripSecrets(req))
|
return nil, status.Error(codes.Unimplemented, "")
|
||||||
if (req == nil)
|
|
||||||
{
|
|
||||||
return nil, status.Error(codes.InvalidArgument, "request cannot be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
volVars := make(map[string]string)
|
|
||||||
err := json.Unmarshal([]byte(req.SourceVolumeId), &volVars)
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
return nil, status.Error(codes.Internal, "volume ID not in JSON format")
|
|
||||||
}
|
|
||||||
volName := volVars["name"]
|
|
||||||
ctxVars, err := GetConnectionParams(volVars)
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
inodeCfg, err := invokeList(ctxVars, volName+"@*", false)
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
resp := &csi.ListSnapshotsResponse{}
|
|
||||||
for _, ino := range inodeCfg
|
|
||||||
{
|
|
||||||
snapName := ino.Name[len(volName)+1:]
|
|
||||||
if (len(req.StartingToken) > 0 && snapName < req.StartingToken)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
else if (req.MaxEntries == 0 || len(resp.Entries) < int(req.MaxEntries))
|
|
||||||
{
|
|
||||||
volVars["snapshot"] = snapName
|
|
||||||
snapIdJson, _ := json.Marshal(volVars)
|
|
||||||
resp.Entries = append(resp.Entries, &csi.ListSnapshotsResponse_Entry{
|
|
||||||
Snapshot: &csi.Snapshot{
|
|
||||||
SizeBytes: int64(ino.Size),
|
|
||||||
SnapshotId: string(snapIdJson),
|
|
||||||
SourceVolumeId: req.SourceVolumeId,
|
|
||||||
CreationTime: ×tamppb.Timestamp{ Seconds: int64(ino.CreateTs) },
|
|
||||||
ReadyToUse: true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
resp.NextToken = snapName
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return resp, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ControllerExpandVolume increases the size of a volume
|
// ControllerExpandVolume resizes a volume
|
||||||
func (cs *ControllerServer) ControllerExpandVolume(ctx context.Context, req *csi.ControllerExpandVolumeRequest) (*csi.ControllerExpandVolumeResponse, error)
|
func (cs *ControllerServer) ControllerExpandVolume(ctx context.Context, req *csi.ControllerExpandVolumeRequest) (*csi.ControllerExpandVolumeResponse, error)
|
||||||
{
|
{
|
||||||
klog.Infof("received controller expand volume request %+v", protosanitizer.StripSecrets(req))
|
return nil, status.Error(codes.Unimplemented, "")
|
||||||
if (req == nil)
|
|
||||||
{
|
|
||||||
return nil, status.Error(codes.InvalidArgument, "request cannot be empty")
|
|
||||||
}
|
|
||||||
if (req.VolumeId == "" || req.CapacityRange == nil || req.CapacityRange.RequiredBytes == 0)
|
|
||||||
{
|
|
||||||
return nil, status.Error(codes.InvalidArgument, "VolumeId, CapacityRange and RequiredBytes are required fields")
|
|
||||||
}
|
|
||||||
|
|
||||||
volVars := make(map[string]string)
|
|
||||||
err := json.Unmarshal([]byte(req.VolumeId), &volVars)
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
return nil, status.Error(codes.Internal, "volume ID not in JSON format")
|
|
||||||
}
|
|
||||||
volName := volVars["name"]
|
|
||||||
ctxVars, err := GetConnectionParams(volVars)
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
inodeCfg, err := invokeList(ctxVars, volName, true)
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if (req.CapacityRange.RequiredBytes > 0 && inodeCfg[0].Size < uint64(req.CapacityRange.RequiredBytes))
|
|
||||||
{
|
|
||||||
sz := ((req.CapacityRange.RequiredBytes+4095)/4096)*4096
|
|
||||||
_, err := invokeCLI(ctxVars, []string{ "modify", "--inc_size", "1", "--resize", fmt.Sprintf("%d", sz), volName })
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
inodeCfg, err = invokeList(ctxVars, volName, true)
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &csi.ControllerExpandVolumeResponse{
|
|
||||||
CapacityBytes: int64(inodeCfg[0].Size),
|
|
||||||
NodeExpansionRequired: false,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ControllerGetVolume get volume info
|
// ControllerGetVolume get volume info
|
||||||
|
|
|
@ -49,13 +49,6 @@ func (is *IdentityServer) GetPluginCapabilities(ctx context.Context, req *csi.Ge
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Type: &csi.PluginCapability_VolumeExpansion_{
|
|
||||||
VolumeExpansion: &csi.PluginCapability_VolumeExpansion{
|
|
||||||
Type: csi.PluginCapability_VolumeExpansion_OFFLINE,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,16 +5,11 @@ package vitastor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"encoding/json"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"bytes"
|
||||||
"time"
|
|
||||||
|
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
@ -30,102 +25,16 @@ import (
|
||||||
type NodeServer struct
|
type NodeServer struct
|
||||||
{
|
{
|
||||||
*Driver
|
*Driver
|
||||||
useVduse bool
|
|
||||||
stateDir string
|
|
||||||
mounter mount.Interface
|
mounter mount.Interface
|
||||||
restartInterval time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
type DeviceState struct
|
|
||||||
{
|
|
||||||
ConfigPath string `json:"configPath"`
|
|
||||||
VdpaId string `json:"vdpaId"`
|
|
||||||
Image string `json:"image"`
|
|
||||||
Blockdev string `json:"blockdev"`
|
|
||||||
Readonly bool `json:"readonly"`
|
|
||||||
PidFile string `json:"pidFile"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewNodeServer create new instance node
|
// NewNodeServer create new instance node
|
||||||
func NewNodeServer(driver *Driver) *NodeServer
|
func NewNodeServer(driver *Driver) *NodeServer
|
||||||
{
|
{
|
||||||
stateDir := os.Getenv("STATE_DIR")
|
return &NodeServer{
|
||||||
if (stateDir == "")
|
|
||||||
{
|
|
||||||
stateDir = "/run/vitastor-csi"
|
|
||||||
}
|
|
||||||
if (stateDir[len(stateDir)-1] != '/')
|
|
||||||
{
|
|
||||||
stateDir += "/"
|
|
||||||
}
|
|
||||||
ns := &NodeServer{
|
|
||||||
Driver: driver,
|
Driver: driver,
|
||||||
useVduse: checkVduseSupport(),
|
|
||||||
stateDir: stateDir,
|
|
||||||
mounter: mount.New(""),
|
mounter: mount.New(""),
|
||||||
}
|
}
|
||||||
if (ns.useVduse)
|
|
||||||
{
|
|
||||||
ns.restoreVduseDaemons()
|
|
||||||
dur, err := time.ParseDuration(os.Getenv("RESTART_INTERVAL"))
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
dur = 10 * time.Second
|
|
||||||
}
|
|
||||||
ns.restartInterval = dur
|
|
||||||
if (ns.restartInterval != time.Duration(0))
|
|
||||||
{
|
|
||||||
go ns.restarter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ns
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkVduseSupport() bool
|
|
||||||
{
|
|
||||||
// Check VDUSE support (vdpa, vduse, virtio-vdpa kernel modules)
|
|
||||||
vduse := true
|
|
||||||
for _, mod := range []string{"vdpa", "vduse", "virtio-vdpa"}
|
|
||||||
{
|
|
||||||
_, err := os.Stat("/sys/module/"+mod)
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
if (!errors.Is(err, os.ErrNotExist))
|
|
||||||
{
|
|
||||||
klog.Errorf("failed to check /sys/module/%s: %v", mod, err)
|
|
||||||
}
|
|
||||||
c := exec.Command("/sbin/modprobe", mod)
|
|
||||||
c.Stdout = os.Stderr
|
|
||||||
c.Stderr = os.Stderr
|
|
||||||
err := c.Run()
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
klog.Errorf("/sbin/modprobe %s failed: %v", mod, err)
|
|
||||||
vduse = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Check that vdpa tool functions
|
|
||||||
if (vduse)
|
|
||||||
{
|
|
||||||
c := exec.Command("/sbin/vdpa", "-j", "dev")
|
|
||||||
c.Stderr = os.Stderr
|
|
||||||
err := c.Run()
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
klog.Errorf("/sbin/vdpa -j dev failed: %v", err)
|
|
||||||
vduse = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!vduse)
|
|
||||||
{
|
|
||||||
klog.Errorf(
|
|
||||||
"Your host apparently has no VDUSE support. VDUSE support disabled, NBD will be used to map devices."+
|
|
||||||
" For VDUSE you need at least Linux 5.15 and the following kernel modules: vdpa, virtio-vdpa, vduse.",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return vduse
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NodeStageVolume mounts the volume to a staging path on the node.
|
// NodeStageVolume mounts the volume to a staging path on the node.
|
||||||
|
@ -152,318 +61,6 @@ func Contains(list []string, s string) bool
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *NodeServer) mapNbd(volName string, ctxVars map[string]string, readonly bool) (string, error)
|
|
||||||
{
|
|
||||||
// Map NBD device
|
|
||||||
// FIXME: Check if already mapped
|
|
||||||
args := []string{
|
|
||||||
"map", "--image", volName,
|
|
||||||
}
|
|
||||||
if (ctxVars["configPath"] != "")
|
|
||||||
{
|
|
||||||
args = append(args, "--config_path", ctxVars["configPath"])
|
|
||||||
}
|
|
||||||
if (readonly)
|
|
||||||
{
|
|
||||||
args = append(args, "--readonly", "1")
|
|
||||||
}
|
|
||||||
stdout, stderr, err := system("/usr/bin/vitastor-nbd", args...)
|
|
||||||
dev := strings.TrimSpace(string(stdout))
|
|
||||||
if (dev == "")
|
|
||||||
{
|
|
||||||
return "", fmt.Errorf("vitastor-nbd did not return the name of NBD device. output: %s", stderr)
|
|
||||||
}
|
|
||||||
return dev, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *NodeServer) unmapNbd(devicePath string)
|
|
||||||
{
|
|
||||||
// unmap NBD device
|
|
||||||
unmapOut, unmapErr := exec.Command("/usr/bin/vitastor-nbd", "unmap", devicePath).CombinedOutput()
|
|
||||||
if (unmapErr != nil)
|
|
||||||
{
|
|
||||||
klog.Errorf("failed to unmap NBD device %s: %s, error: %v", devicePath, unmapOut, unmapErr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func findByPidFile(pidFile string) (*os.Process, error)
|
|
||||||
{
|
|
||||||
pidBuf, err := os.ReadFile(pidFile)
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
pid, err := strconv.ParseInt(strings.TrimSpace(string(pidBuf)), 0, 64)
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
proc, err := os.FindProcess(int(pid))
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return proc, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func killByPidFile(pidFile string) error
|
|
||||||
{
|
|
||||||
klog.Infof("killing process with PID from file %s", pidFile)
|
|
||||||
proc, err := findByPidFile(pidFile)
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return proc.Signal(syscall.SIGTERM)
|
|
||||||
}
|
|
||||||
|
|
||||||
func startStorageDaemon(vdpaId, volName, pidFile, configPath string, readonly bool) error
|
|
||||||
{
|
|
||||||
// Start qemu-storage-daemon
|
|
||||||
blockSpec := map[string]interface{}{
|
|
||||||
"node-name": "disk1",
|
|
||||||
"driver": "vitastor",
|
|
||||||
"image": volName,
|
|
||||||
"cache": map[string]bool{
|
|
||||||
"direct": true,
|
|
||||||
"no-flush": false,
|
|
||||||
},
|
|
||||||
"discard": "unmap",
|
|
||||||
}
|
|
||||||
if (configPath != "")
|
|
||||||
{
|
|
||||||
blockSpec["config-path"] = configPath
|
|
||||||
}
|
|
||||||
blockSpecJson, _ := json.Marshal(blockSpec)
|
|
||||||
writable := "true"
|
|
||||||
if (readonly)
|
|
||||||
{
|
|
||||||
writable = "false"
|
|
||||||
}
|
|
||||||
_, _, err := system(
|
|
||||||
"/usr/bin/qemu-storage-daemon", "--daemonize", "--pidfile", pidFile, "--blockdev", string(blockSpecJson),
|
|
||||||
"--export", "vduse-blk,id="+vdpaId+",node-name=disk1,name="+vdpaId+",num-queues=16,queue-size=128,writable="+writable,
|
|
||||||
)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *NodeServer) mapVduse(volName string, ctxVars map[string]string, readonly bool) (string, string, error)
|
|
||||||
{
|
|
||||||
// Generate state file
|
|
||||||
stateFd, err := os.CreateTemp(ns.stateDir, "vitastor-vduse-*.json")
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
stateFile := stateFd.Name()
|
|
||||||
stateFd.Close()
|
|
||||||
vdpaId := filepath.Base(stateFile)
|
|
||||||
vdpaId = vdpaId[0:len(vdpaId)-5] // remove ".json"
|
|
||||||
pidFile := ns.stateDir + vdpaId + ".pid"
|
|
||||||
// Map VDUSE device via qemu-storage-daemon
|
|
||||||
err = startStorageDaemon(vdpaId, volName, pidFile, ctxVars["configPath"], readonly)
|
|
||||||
if (err == nil)
|
|
||||||
{
|
|
||||||
// Add device to VDPA bus
|
|
||||||
_, _, err = system("/sbin/vdpa", "-j", "dev", "add", "name", vdpaId, "mgmtdev", "vduse")
|
|
||||||
if (err == nil)
|
|
||||||
{
|
|
||||||
// Find block device name
|
|
||||||
var matches []string
|
|
||||||
matches, err = filepath.Glob("/sys/bus/vdpa/devices/"+vdpaId+"/virtio*/block/*")
|
|
||||||
if (err == nil && len(matches) == 0)
|
|
||||||
{
|
|
||||||
err = errors.New("/sys/bus/vdpa/devices/"+vdpaId+"/virtio*/block/* is not found")
|
|
||||||
}
|
|
||||||
if (err == nil)
|
|
||||||
{
|
|
||||||
blockdev := "/dev/"+filepath.Base(matches[0])
|
|
||||||
_, err = os.Stat(blockdev)
|
|
||||||
if (err == nil)
|
|
||||||
{
|
|
||||||
// Generate state file
|
|
||||||
stateJSON, _ := json.Marshal(&DeviceState{
|
|
||||||
ConfigPath: ctxVars["configPath"],
|
|
||||||
VdpaId: vdpaId,
|
|
||||||
Image: volName,
|
|
||||||
Blockdev: blockdev,
|
|
||||||
Readonly: readonly,
|
|
||||||
PidFile: pidFile,
|
|
||||||
})
|
|
||||||
err = os.WriteFile(stateFile, stateJSON, 0600)
|
|
||||||
if (err == nil)
|
|
||||||
{
|
|
||||||
return blockdev, vdpaId, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
killErr := killByPidFile(pidFile)
|
|
||||||
if (killErr != nil)
|
|
||||||
{
|
|
||||||
klog.Errorf("Failed to kill started qemu-storage-daemon: %v", killErr)
|
|
||||||
}
|
|
||||||
os.Remove(stateFile)
|
|
||||||
os.Remove(pidFile)
|
|
||||||
}
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *NodeServer) unmapVduse(devicePath string)
|
|
||||||
{
|
|
||||||
if (len(devicePath) < 6 || devicePath[0:6] != "/dev/v")
|
|
||||||
{
|
|
||||||
klog.Errorf("%s does not start with /dev/v", devicePath)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
vduseDev, err := os.Readlink("/sys/block/"+devicePath[5:])
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
klog.Errorf("%s is not a symbolic link to VDUSE device (../devices/virtual/vduse/xxx): %v", devicePath, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
vdpaId := ""
|
|
||||||
p := strings.Index(vduseDev, "/vduse/")
|
|
||||||
if (p >= 0)
|
|
||||||
{
|
|
||||||
vduseDev = vduseDev[p+7:]
|
|
||||||
p = strings.Index(vduseDev, "/")
|
|
||||||
if (p >= 0)
|
|
||||||
{
|
|
||||||
vdpaId = vduseDev[0:p]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (vdpaId == "")
|
|
||||||
{
|
|
||||||
klog.Errorf("%s is not a symbolic link to VDUSE device (../devices/virtual/vduse/xxx), but is %v", devicePath, vduseDev)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ns.unmapVduseById(vdpaId)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *NodeServer) unmapVduseById(vdpaId string)
|
|
||||||
{
|
|
||||||
_, err := os.Stat("/sys/bus/vdpa/devices/"+vdpaId)
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
klog.Errorf("failed to stat /sys/bus/vdpa/devices/"+vdpaId+": %v", err)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_, _, _ = system("/sbin/vdpa", "-j", "dev", "del", vdpaId)
|
|
||||||
}
|
|
||||||
stateFile := ns.stateDir + vdpaId + ".json"
|
|
||||||
os.Remove(stateFile)
|
|
||||||
pidFile := ns.stateDir + vdpaId + ".pid"
|
|
||||||
_, err = os.Stat(pidFile)
|
|
||||||
if (os.IsNotExist(err))
|
|
||||||
{
|
|
||||||
// ok, already killed
|
|
||||||
}
|
|
||||||
else if (err != nil)
|
|
||||||
{
|
|
||||||
klog.Errorf("Failed to stat %v: %v", pidFile, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
err = killByPidFile(pidFile)
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
klog.Errorf("Failed to kill started qemu-storage-daemon: %v", err)
|
|
||||||
}
|
|
||||||
os.Remove(pidFile)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *NodeServer) restarter()
|
|
||||||
{
|
|
||||||
// Restart dead VDUSE daemons at regular intervals
|
|
||||||
// Otherwise volume I/O may hang in case of a qemu-storage-daemon crash
|
|
||||||
// Moreover, it may lead to a kernel panic of the kernel is configured to
|
|
||||||
// panic on hung tasks
|
|
||||||
ticker := time.NewTicker(ns.restartInterval)
|
|
||||||
defer ticker.Stop()
|
|
||||||
for
|
|
||||||
{
|
|
||||||
<-ticker.C
|
|
||||||
ns.restoreVduseDaemons()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *NodeServer) restoreVduseDaemons()
|
|
||||||
{
|
|
||||||
pattern := ns.stateDir+"vitastor-vduse-*.json"
|
|
||||||
matches, err := filepath.Glob(pattern)
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
klog.Errorf("failed to list %s: %v", pattern, err)
|
|
||||||
}
|
|
||||||
if (len(matches) == 0)
|
|
||||||
{
|
|
||||||
return
|
|
||||||
}
|
|
||||||
devList := make(map[string]interface{})
|
|
||||||
// example output: {"dev":{"test1":{"type":"block","mgmtdev":"vduse","vendor_id":0,"max_vqs":16,"max_vq_size":128}}}
|
|
||||||
devListJSON, _, err := system("/sbin/vdpa", "-j", "dev", "list")
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(devListJSON, &devList)
|
|
||||||
devs, ok := devList["dev"].(map[string]interface{})
|
|
||||||
if (err != nil || !ok)
|
|
||||||
{
|
|
||||||
klog.Errorf("/sbin/vdpa -j dev list returned bad JSON (error %v): %v", err, string(devListJSON))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, stateFile := range matches
|
|
||||||
{
|
|
||||||
vdpaId := filepath.Base(stateFile)
|
|
||||||
vdpaId = vdpaId[0:len(vdpaId)-5]
|
|
||||||
// Check if VDPA device is still added to the bus
|
|
||||||
if (devs[vdpaId] != nil)
|
|
||||||
{
|
|
||||||
// Check if the storage daemon is still active
|
|
||||||
pidFile := ns.stateDir + vdpaId + ".pid"
|
|
||||||
exists := false
|
|
||||||
proc, err := findByPidFile(pidFile)
|
|
||||||
if (err == nil)
|
|
||||||
{
|
|
||||||
exists = proc.Signal(syscall.Signal(0)) == nil
|
|
||||||
}
|
|
||||||
if (!exists)
|
|
||||||
{
|
|
||||||
// Restart daemon
|
|
||||||
stateJSON, err := os.ReadFile(stateFile)
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
klog.Warningf("error reading state file %v: %v", stateFile, err)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var state DeviceState
|
|
||||||
err := json.Unmarshal(stateJSON, &state)
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
klog.Warningf("state file %v contains invalid JSON (error %v): %v", stateFile, err, string(stateJSON))
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
klog.Warningf("restarting storage daemon for volume %v (VDPA ID %v)", state.Image, vdpaId)
|
|
||||||
_ = startStorageDaemon(vdpaId, state.Image, pidFile, state.ConfigPath, state.Readonly)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Unused, clean it up
|
|
||||||
ns.unmapVduseById(vdpaId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NodePublishVolume mounts the volume mounted to the staging path to the target path
|
// NodePublishVolume mounts the volume mounted to the staging path to the target path
|
||||||
func (ns *NodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolumeRequest) (*csi.NodePublishVolumeResponse, error)
|
func (ns *NodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolumeRequest) (*csi.NodePublishVolumeResponse, error)
|
||||||
{
|
{
|
||||||
|
@ -473,10 +70,10 @@ func (ns *NodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublis
|
||||||
isBlock := req.GetVolumeCapability().GetBlock() != nil
|
isBlock := req.GetVolumeCapability().GetBlock() != nil
|
||||||
|
|
||||||
// Check that it's not already mounted
|
// Check that it's not already mounted
|
||||||
_, err := mount.IsNotMountPoint(ns.mounter, targetPath)
|
_, error := mount.IsNotMountPoint(ns.mounter, targetPath)
|
||||||
if (err != nil)
|
if (error != nil)
|
||||||
{
|
{
|
||||||
if (os.IsNotExist(err))
|
if (os.IsNotExist(error))
|
||||||
{
|
{
|
||||||
if (isBlock)
|
if (isBlock)
|
||||||
{
|
{
|
||||||
|
@ -484,13 +81,13 @@ func (ns *NodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublis
|
||||||
if (err != nil)
|
if (err != nil)
|
||||||
{
|
{
|
||||||
klog.Errorf("failed to create block device mount target %s with error: %v", targetPath, err)
|
klog.Errorf("failed to create block device mount target %s with error: %v", targetPath, err)
|
||||||
return nil, err
|
return nil, status.Error(codes.Internal, err.Error())
|
||||||
}
|
}
|
||||||
err = pathFile.Close()
|
err = pathFile.Close()
|
||||||
if (err != nil)
|
if (err != nil)
|
||||||
{
|
{
|
||||||
klog.Errorf("failed to close %s with error: %v", targetPath, err)
|
klog.Errorf("failed to close %s with error: %v", targetPath, err)
|
||||||
return nil, err
|
return nil, status.Error(codes.Internal, err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -499,57 +96,70 @@ func (ns *NodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublis
|
||||||
if (err != nil)
|
if (err != nil)
|
||||||
{
|
{
|
||||||
klog.Errorf("failed to create fs mount target %s with error: %v", targetPath, err)
|
klog.Errorf("failed to create fs mount target %s with error: %v", targetPath, err)
|
||||||
return nil, err
|
return nil, status.Error(codes.Internal, err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return nil, err
|
return nil, status.Error(codes.Internal, error.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctxVars := make(map[string]string)
|
ctxVars := make(map[string]string)
|
||||||
err = json.Unmarshal([]byte(req.VolumeId), &ctxVars)
|
err := json.Unmarshal([]byte(req.VolumeId), &ctxVars)
|
||||||
if (err != nil)
|
if (err != nil)
|
||||||
{
|
{
|
||||||
return nil, status.Error(codes.Internal, "volume ID not in JSON format")
|
return nil, status.Error(codes.Internal, "volume ID not in JSON format")
|
||||||
}
|
}
|
||||||
volName := ctxVars["name"]
|
volName := ctxVars["name"]
|
||||||
|
|
||||||
_, err = GetConnectionParams(ctxVars)
|
_, etcdUrl, etcdPrefix := GetConnectionParams(ctxVars)
|
||||||
if (err != nil)
|
if (len(etcdUrl) == 0)
|
||||||
{
|
{
|
||||||
return nil, err
|
return nil, status.Error(codes.InvalidArgument, "no etcdUrl in storage class configuration and no etcd_address in vitastor.conf")
|
||||||
}
|
}
|
||||||
|
|
||||||
var devicePath, vdpaId string
|
// Map NBD device
|
||||||
if (!ns.useVduse)
|
// FIXME: Check if already mapped
|
||||||
|
args := []string{
|
||||||
|
"map", "--etcd_address", strings.Join(etcdUrl, ","),
|
||||||
|
"--etcd_prefix", etcdPrefix,
|
||||||
|
"--image", volName,
|
||||||
|
};
|
||||||
|
if (ctxVars["configPath"] != "")
|
||||||
{
|
{
|
||||||
devicePath, err = ns.mapNbd(volName, ctxVars, req.GetReadonly())
|
args = append(args, "--config_path", ctxVars["configPath"])
|
||||||
}
|
}
|
||||||
else
|
if (req.GetReadonly())
|
||||||
{
|
{
|
||||||
devicePath, vdpaId, err = ns.mapVduse(volName, ctxVars, req.GetReadonly())
|
args = append(args, "--readonly", "1")
|
||||||
}
|
}
|
||||||
|
c := exec.Command("/usr/bin/vitastor-nbd", args...)
|
||||||
|
var stdout, stderr bytes.Buffer
|
||||||
|
c.Stdout, c.Stderr = &stdout, &stderr
|
||||||
|
err = c.Run()
|
||||||
|
stdoutStr, stderrStr := string(stdout.Bytes()), string(stderr.Bytes())
|
||||||
if (err != nil)
|
if (err != nil)
|
||||||
{
|
{
|
||||||
return nil, err
|
klog.Errorf("vitastor-nbd map failed: %s, status %s\n", stdoutStr+stderrStr, err)
|
||||||
|
return nil, status.Error(codes.Internal, stdoutStr+stderrStr+" (status "+err.Error()+")")
|
||||||
}
|
}
|
||||||
|
devicePath := strings.TrimSpace(stdoutStr)
|
||||||
|
|
||||||
diskMounter := &mount.SafeFormatAndMount{Interface: ns.mounter, Exec: utilexec.New()}
|
|
||||||
if (isBlock)
|
|
||||||
{
|
|
||||||
err = diskMounter.Mount(devicePath, targetPath, "", []string{"bind"})
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Check existing format
|
// Check existing format
|
||||||
|
diskMounter := &mount.SafeFormatAndMount{Interface: ns.mounter, Exec: utilexec.New()}
|
||||||
existingFormat, err := diskMounter.GetDiskFormat(devicePath)
|
existingFormat, err := diskMounter.GetDiskFormat(devicePath)
|
||||||
if (err != nil)
|
if (err != nil)
|
||||||
{
|
{
|
||||||
klog.Errorf("failed to get disk format for path %s, error: %v", err)
|
klog.Errorf("failed to get disk format for path %s, error: %v", err)
|
||||||
goto unmap
|
// unmap NBD device
|
||||||
|
unmapOut, unmapErr := exec.Command("/usr/bin/vitastor-nbd", "unmap", devicePath).CombinedOutput()
|
||||||
|
if (unmapErr != nil)
|
||||||
|
{
|
||||||
|
klog.Errorf("failed to unmap NBD device %s: %s, error: %v", devicePath, unmapOut, unmapErr)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format the device (ext4 or xfs)
|
// Format the device (ext4 or xfs)
|
||||||
|
@ -569,42 +179,38 @@ func (ns *NodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublis
|
||||||
readOnly := Contains(opt, "ro")
|
readOnly := Contains(opt, "ro")
|
||||||
if (existingFormat == "" && !readOnly)
|
if (existingFormat == "" && !readOnly)
|
||||||
{
|
{
|
||||||
var cmdOut []byte
|
args := []string{}
|
||||||
switch fsType
|
switch fsType
|
||||||
{
|
{
|
||||||
case "ext4":
|
case "ext4":
|
||||||
args := []string{"-m0", "-Enodiscard,lazy_itable_init=1,lazy_journal_init=1", devicePath}
|
args = []string{"-m0", "-Enodiscard,lazy_itable_init=1,lazy_journal_init=1", devicePath}
|
||||||
cmdOut, err = diskMounter.Exec.Command("mkfs.ext4", args...).CombinedOutput()
|
|
||||||
case "xfs":
|
case "xfs":
|
||||||
cmdOut, err = diskMounter.Exec.Command("mkfs.xfs", "-K", devicePath).CombinedOutput()
|
args = []string{"-K", devicePath}
|
||||||
}
|
}
|
||||||
if (err != nil)
|
if (len(args) > 0)
|
||||||
{
|
{
|
||||||
klog.Errorf("failed to run mkfs error: %v, output: %v", err, string(cmdOut))
|
cmdOut, cmdErr := diskMounter.Exec.Command("mkfs."+fsType, args...).CombinedOutput()
|
||||||
goto unmap
|
if (cmdErr != nil)
|
||||||
|
{
|
||||||
|
klog.Errorf("failed to run mkfs error: %v, output: %v", cmdErr, string(cmdOut))
|
||||||
|
// unmap NBD device
|
||||||
|
unmapOut, unmapErr := exec.Command("/usr/bin/vitastor-nbd", "unmap", devicePath).CombinedOutput()
|
||||||
|
if (unmapErr != nil)
|
||||||
|
{
|
||||||
|
klog.Errorf("failed to unmap NBD device %s: %s, error: %v", devicePath, unmapOut, unmapErr)
|
||||||
|
}
|
||||||
|
return nil, status.Error(codes.Internal, cmdErr.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (isBlock)
|
||||||
|
{
|
||||||
|
opt = append(opt, "bind")
|
||||||
|
err = diskMounter.Mount(devicePath, targetPath, fsType, opt)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
err = diskMounter.FormatAndMount(devicePath, targetPath, fsType, opt)
|
err = diskMounter.FormatAndMount(devicePath, targetPath, fsType, opt)
|
||||||
|
|
||||||
// Try to run online resize on mount.
|
|
||||||
// FIXME: Implement online resize. It requires online resize support in vitastor-nbd.
|
|
||||||
if (err == nil && existingFormat != "" && !readOnly)
|
|
||||||
{
|
|
||||||
var cmdOut []byte
|
|
||||||
switch (fsType)
|
|
||||||
{
|
|
||||||
case "ext4":
|
|
||||||
cmdOut, err = diskMounter.Exec.Command("resize2fs", devicePath).CombinedOutput()
|
|
||||||
case "xfs":
|
|
||||||
cmdOut, err = diskMounter.Exec.Command("xfs_growfs", devicePath).CombinedOutput()
|
|
||||||
}
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
klog.Errorf("failed to run resizefs error: %v, output: %v", err, string(cmdOut))
|
|
||||||
goto unmap
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (err != nil)
|
if (err != nil)
|
||||||
{
|
{
|
||||||
|
@ -612,20 +218,15 @@ func (ns *NodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublis
|
||||||
"failed to mount device path (%s) to path (%s) for volume (%s) error: %s",
|
"failed to mount device path (%s) to path (%s) for volume (%s) error: %s",
|
||||||
devicePath, targetPath, volName, err,
|
devicePath, targetPath, volName, err,
|
||||||
)
|
)
|
||||||
goto unmap
|
// unmap NBD device
|
||||||
|
unmapOut, unmapErr := exec.Command("/usr/bin/vitastor-nbd", "unmap", devicePath).CombinedOutput()
|
||||||
|
if (unmapErr != nil)
|
||||||
|
{
|
||||||
|
klog.Errorf("failed to unmap NBD device %s: %s, error: %v", devicePath, unmapOut, unmapErr)
|
||||||
|
}
|
||||||
|
return nil, status.Error(codes.Internal, err.Error())
|
||||||
}
|
}
|
||||||
return &csi.NodePublishVolumeResponse{}, nil
|
return &csi.NodePublishVolumeResponse{}, nil
|
||||||
|
|
||||||
unmap:
|
|
||||||
if (!ns.useVduse || len(devicePath) >= 8 && devicePath[0:8] == "/dev/nbd")
|
|
||||||
{
|
|
||||||
ns.unmapNbd(devicePath)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ns.unmapVduseById(vdpaId)
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NodeUnpublishVolume unmounts the volume from the target path
|
// NodeUnpublishVolume unmounts the volume from the target path
|
||||||
|
@ -640,31 +241,25 @@ func (ns *NodeServer) NodeUnpublishVolume(ctx context.Context, req *csi.NodeUnpu
|
||||||
{
|
{
|
||||||
return nil, status.Error(codes.NotFound, "Target path not found")
|
return nil, status.Error(codes.NotFound, "Target path not found")
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, status.Error(codes.Internal, err.Error())
|
||||||
}
|
}
|
||||||
if (devicePath == "")
|
if (devicePath == "")
|
||||||
{
|
{
|
||||||
// volume not mounted
|
return nil, status.Error(codes.NotFound, "Volume not mounted")
|
||||||
klog.Warningf("%s is not a mountpoint, deleting", targetPath)
|
|
||||||
os.Remove(targetPath)
|
|
||||||
return &csi.NodeUnpublishVolumeResponse{}, nil
|
|
||||||
}
|
}
|
||||||
// unmount
|
// unmount
|
||||||
err = mount.CleanupMountPoint(targetPath, ns.mounter, false)
|
err = mount.CleanupMountPoint(targetPath, ns.mounter, false)
|
||||||
if (err != nil)
|
if (err != nil)
|
||||||
{
|
{
|
||||||
return nil, err
|
return nil, status.Error(codes.Internal, err.Error())
|
||||||
}
|
}
|
||||||
// unmap NBD device
|
// unmap NBD device
|
||||||
if (refCount == 1)
|
if (refCount == 1)
|
||||||
{
|
{
|
||||||
if (!ns.useVduse)
|
unmapOut, unmapErr := exec.Command("/usr/bin/vitastor-nbd", "unmap", devicePath).CombinedOutput()
|
||||||
|
if (unmapErr != nil)
|
||||||
{
|
{
|
||||||
ns.unmapNbd(devicePath)
|
klog.Errorf("failed to unmap NBD device %s: %s, error: %v", devicePath, unmapOut, unmapErr)
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ns.unmapVduse(devicePath)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &csi.NodeUnpublishVolumeResponse{}, nil
|
return &csi.NodeUnpublishVolumeResponse{}, nil
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
vitastor (1.4.4-1) unstable; urgency=medium
|
vitastor (1.0.0-1) unstable; urgency=medium
|
||||||
|
|
||||||
* Bugfixes
|
* Bugfixes
|
||||||
|
|
||||||
-- Vitaliy Filippov <vitalif@yourcmc.ru> Fri, 03 Jun 2022 02:09:44 +0300
|
-- Vitaliy Filippov <vitalif@yourcmc.ru> Fri, 03 Jun 2022 02:09:44 +0300
|
||||||
|
|
||||||
vitastor (0.7.0-1) unstable; urgency=medium
|
vitastor (1.0.0-1) unstable; urgency=medium
|
||||||
|
|
||||||
* Implement NFS proxy
|
* Implement NFS proxy
|
||||||
* Add documentation
|
* Add documentation
|
||||||
|
|
|
@ -2,7 +2,7 @@ Source: vitastor
|
||||||
Section: admin
|
Section: admin
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Maintainer: Vitaliy Filippov <vitalif@yourcmc.ru>
|
Maintainer: Vitaliy Filippov <vitalif@yourcmc.ru>
|
||||||
Build-Depends: debhelper, liburing-dev (>= 0.6), g++ (>= 8), libstdc++6 (>= 8), linux-libc-dev, libgoogle-perftools-dev, libjerasure-dev, libgf-complete-dev, libibverbs-dev, libisal-dev, cmake, pkg-config
|
Build-Depends: debhelper, liburing-dev (>= 0.6), g++ (>= 8), libstdc++6 (>= 8), linux-libc-dev, libgoogle-perftools-dev, libjerasure-dev, libgf-complete-dev, libibverbs-dev, libisal-dev
|
||||||
Standards-Version: 4.5.0
|
Standards-Version: 4.5.0
|
||||||
Homepage: https://vitastor.io/
|
Homepage: https://vitastor.io/
|
||||||
Rules-Requires-Root: no
|
Rules-Requires-Root: no
|
||||||
|
|
|
@ -7,7 +7,7 @@ ARG REL=
|
||||||
|
|
||||||
WORKDIR /root
|
WORKDIR /root
|
||||||
|
|
||||||
RUN if [ "$REL" = "buster" -o "$REL" = "bullseye" -o "$REL" = "bookworm" ]; then \
|
RUN if [ "$REL" = "buster" -o "$REL" = "bullseye" ]; then \
|
||||||
echo "deb http://deb.debian.org/debian $REL-backports main" >> /etc/apt/sources.list; \
|
echo "deb http://deb.debian.org/debian $REL-backports main" >> /etc/apt/sources.list; \
|
||||||
echo >> /etc/apt/preferences; \
|
echo >> /etc/apt/preferences; \
|
||||||
echo 'Package: *' >> /etc/apt/preferences; \
|
echo 'Package: *' >> /etc/apt/preferences; \
|
||||||
|
@ -45,7 +45,7 @@ RUN set -e; \
|
||||||
rm -rf /root/packages/qemu-$REL/*; \
|
rm -rf /root/packages/qemu-$REL/*; \
|
||||||
cd /root/packages/qemu-$REL; \
|
cd /root/packages/qemu-$REL; \
|
||||||
dpkg-source -x /root/qemu*.dsc; \
|
dpkg-source -x /root/qemu*.dsc; \
|
||||||
QEMU_VER=$(ls -d qemu*/ | perl -pe 's!^.*?(\d+\.\d+).*!$1!'); \
|
QEMU_VER=$(ls -d qemu*/ | perl -pe 's!^.*(\d+\.\d+).*!$1!'); \
|
||||||
D=$(ls -d qemu*/); \
|
D=$(ls -d qemu*/); \
|
||||||
cp /root/vitastor/patches/qemu-$QEMU_VER-vitastor.patch ./qemu-*/debian/patches; \
|
cp /root/vitastor/patches/qemu-$QEMU_VER-vitastor.patch ./qemu-*/debian/patches; \
|
||||||
echo qemu-$QEMU_VER-vitastor.patch >> $D/debian/patches/series; \
|
echo qemu-$QEMU_VER-vitastor.patch >> $D/debian/patches/series; \
|
||||||
|
@ -54,8 +54,7 @@ RUN set -e; \
|
||||||
quilt add block/vitastor.c; \
|
quilt add block/vitastor.c; \
|
||||||
cp /root/vitastor/src/qemu_driver.c block/vitastor.c; \
|
cp /root/vitastor/src/qemu_driver.c block/vitastor.c; \
|
||||||
quilt refresh; \
|
quilt refresh; \
|
||||||
V=$(head -n1 debian/changelog | perl -pe 's/5\.2\+dfsg-9/5.2+dfsg-11/; s/^.*\((.*?)(~bpo[\d\+]*)?\).*$/$1/')+vitastor4; \
|
V=$(head -n1 debian/changelog | perl -pe 's/^.*\((.*?)(~bpo[\d\+]*)?\).*$/$1/')+vitastor3; \
|
||||||
if [ "$REL" = bullseye ]; then V=${V}bullseye; fi; \
|
|
||||||
DEBEMAIL="Vitaliy Filippov <vitalif@yourcmc.ru>" dch -D $REL -v $V 'Plug Vitastor block driver'; \
|
DEBEMAIL="Vitaliy Filippov <vitalif@yourcmc.ru>" dch -D $REL -v $V 'Plug Vitastor block driver'; \
|
||||||
DEB_BUILD_OPTIONS=nocheck dpkg-buildpackage --jobs=auto -sa; \
|
DEB_BUILD_OPTIONS=nocheck dpkg-buildpackage --jobs=auto -sa; \
|
||||||
rm -rf /root/packages/qemu-$REL/qemu-*/
|
rm -rf /root/packages/qemu-$REL/qemu-*/
|
||||||
|
|
|
@ -35,8 +35,8 @@ RUN set -e -x; \
|
||||||
mkdir -p /root/packages/vitastor-$REL; \
|
mkdir -p /root/packages/vitastor-$REL; \
|
||||||
rm -rf /root/packages/vitastor-$REL/*; \
|
rm -rf /root/packages/vitastor-$REL/*; \
|
||||||
cd /root/packages/vitastor-$REL; \
|
cd /root/packages/vitastor-$REL; \
|
||||||
cp -r /root/vitastor vitastor-1.4.4; \
|
cp -r /root/vitastor vitastor-1.0.0; \
|
||||||
cd vitastor-1.4.4; \
|
cd vitastor-1.0.0; \
|
||||||
ln -s /root/fio-build/fio-*/ ./fio; \
|
ln -s /root/fio-build/fio-*/ ./fio; \
|
||||||
FIO=$(head -n1 fio/debian/changelog | perl -pe 's/^.*\((.*?)\).*$/$1/'); \
|
FIO=$(head -n1 fio/debian/changelog | perl -pe 's/^.*\((.*?)\).*$/$1/'); \
|
||||||
ls /usr/include/linux/raw.h || cp ./debian/raw.h /usr/include/linux/raw.h; \
|
ls /usr/include/linux/raw.h || cp ./debian/raw.h /usr/include/linux/raw.h; \
|
||||||
|
@ -49,8 +49,8 @@ RUN set -e -x; \
|
||||||
rm -rf a b; \
|
rm -rf a b; \
|
||||||
echo "dep:fio=$FIO" > debian/fio_version; \
|
echo "dep:fio=$FIO" > debian/fio_version; \
|
||||||
cd /root/packages/vitastor-$REL; \
|
cd /root/packages/vitastor-$REL; \
|
||||||
tar --sort=name --mtime='2020-01-01' --owner=0 --group=0 --exclude=debian -cJf vitastor_1.4.4.orig.tar.xz vitastor-1.4.4; \
|
tar --sort=name --mtime='2020-01-01' --owner=0 --group=0 --exclude=debian -cJf vitastor_1.0.0.orig.tar.xz vitastor-1.0.0; \
|
||||||
cd vitastor-1.4.4; \
|
cd vitastor-1.0.0; \
|
||||||
V=$(head -n1 debian/changelog | perl -pe 's/^.*\((.*?)\).*$/$1/'); \
|
V=$(head -n1 debian/changelog | perl -pe 's/^.*\((.*?)\).*$/$1/'); \
|
||||||
DEBFULLNAME="Vitaliy Filippov <vitalif@yourcmc.ru>" dch -D $REL -v "$V""$REL" "Rebuild for $REL"; \
|
DEBFULLNAME="Vitaliy Filippov <vitalif@yourcmc.ru>" dch -D $REL -v "$V""$REL" "Rebuild for $REL"; \
|
||||||
DEB_BUILD_OPTIONS=nocheck dpkg-buildpackage --jobs=auto -sa; \
|
DEB_BUILD_OPTIONS=nocheck dpkg-buildpackage --jobs=auto -sa; \
|
||||||
|
|
|
@ -33,7 +33,6 @@ In the future, additional configuration methods may be added:
|
||||||
|
|
||||||
- [Common](config/common.en.md)
|
- [Common](config/common.en.md)
|
||||||
- [Network](config/network.en.md)
|
- [Network](config/network.en.md)
|
||||||
- [Client](config/client.en.md)
|
|
||||||
- [Global Disk Layout](config/layout-cluster.en.md)
|
- [Global Disk Layout](config/layout-cluster.en.md)
|
||||||
- [OSD Disk Layout](config/layout-osd.en.md)
|
- [OSD Disk Layout](config/layout-osd.en.md)
|
||||||
- [OSD Runtime Parameters](config/osd.en.md)
|
- [OSD Runtime Parameters](config/osd.en.md)
|
||||||
|
|
|
@ -36,7 +36,6 @@
|
||||||
|
|
||||||
- [Общие](config/common.ru.md)
|
- [Общие](config/common.ru.md)
|
||||||
- [Сеть](config/network.ru.md)
|
- [Сеть](config/network.ru.md)
|
||||||
- [Клиентский код](config/client.ru.md)
|
|
||||||
- [Глобальные дисковые параметры](config/layout-cluster.ru.md)
|
- [Глобальные дисковые параметры](config/layout-cluster.ru.md)
|
||||||
- [Дисковые параметры OSD](config/layout-osd.ru.md)
|
- [Дисковые параметры OSD](config/layout-osd.ru.md)
|
||||||
- [Прочие параметры OSD](config/osd.ru.md)
|
- [Прочие параметры OSD](config/osd.ru.md)
|
||||||
|
|
|
@ -1,137 +0,0 @@
|
||||||
[Documentation](../../README.md#documentation) → [Configuration](../config.en.md) → Client Parameters
|
|
||||||
|
|
||||||
-----
|
|
||||||
|
|
||||||
[Читать на русском](client.ru.md)
|
|
||||||
|
|
||||||
# Client Parameters
|
|
||||||
|
|
||||||
These parameters apply only to Vitastor clients (QEMU, fio, NBD and so on) and
|
|
||||||
affect their interaction with the cluster.
|
|
||||||
|
|
||||||
- [client_max_dirty_bytes](#client_max_dirty_bytes)
|
|
||||||
- [client_max_dirty_ops](#client_max_dirty_ops)
|
|
||||||
- [client_enable_writeback](#client_enable_writeback)
|
|
||||||
- [client_max_buffered_bytes](#client_max_buffered_bytes)
|
|
||||||
- [client_max_buffered_ops](#client_max_buffered_ops)
|
|
||||||
- [client_max_writeback_iodepth](#client_max_writeback_iodepth)
|
|
||||||
- [nbd_timeout](#nbd_timeout)
|
|
||||||
- [nbd_max_devices](#nbd_max_devices)
|
|
||||||
- [nbd_max_part](#nbd_max_part)
|
|
||||||
|
|
||||||
## client_max_dirty_bytes
|
|
||||||
|
|
||||||
- Type: integer
|
|
||||||
- Default: 33554432
|
|
||||||
- Can be changed online: yes
|
|
||||||
|
|
||||||
Without [immediate_commit](layout-cluster.en.md#immediate_commit)=all this parameter sets the limit of "dirty"
|
|
||||||
(not committed by fsync) data allowed by the client before forcing an
|
|
||||||
additional fsync and committing the data. Also note that the client always
|
|
||||||
holds a copy of uncommitted data in memory so this setting also affects
|
|
||||||
RAM usage of clients.
|
|
||||||
|
|
||||||
## client_max_dirty_ops
|
|
||||||
|
|
||||||
- Type: integer
|
|
||||||
- Default: 1024
|
|
||||||
- Can be changed online: yes
|
|
||||||
|
|
||||||
Same as client_max_dirty_bytes, but instead of total size, limits the number
|
|
||||||
of uncommitted write operations.
|
|
||||||
|
|
||||||
## client_enable_writeback
|
|
||||||
|
|
||||||
- Type: boolean
|
|
||||||
- Default: false
|
|
||||||
- Can be changed online: yes
|
|
||||||
|
|
||||||
This parameter enables client-side write buffering. This means that write
|
|
||||||
requests are accumulated in memory for a short time before being sent to
|
|
||||||
a Vitastor cluster which allows to send them in parallel and increase
|
|
||||||
performance of some applications. Writes are buffered until client forces
|
|
||||||
a flush with fsync() or until the amount of buffered writes exceeds the
|
|
||||||
limit.
|
|
||||||
|
|
||||||
Write buffering significantly increases performance of some applications,
|
|
||||||
for example, CrystalDiskMark under Windows (LOL :-D), but also any other
|
|
||||||
applications if they do writes in one of two non-optimal ways: either if
|
|
||||||
they do a lot of small (4 kb or so) sequential writes, or if they do a lot
|
|
||||||
of small random writes, but without any parallelism or asynchrony, and also
|
|
||||||
without calling fsync().
|
|
||||||
|
|
||||||
With write buffering enabled, you can expect around 22000 T1Q1 random write
|
|
||||||
iops in QEMU more or less regardless of the quality of your SSDs, and this
|
|
||||||
number is in fact bound by QEMU itself rather than Vitastor (check it
|
|
||||||
yourself by adding a "driver=null-co" disk in QEMU). Without write
|
|
||||||
buffering, the current record is 9900 iops, but the number is usually
|
|
||||||
even lower with non-ideal hardware, for example, it may be 5000 iops.
|
|
||||||
|
|
||||||
Even when this parameter is enabled, write buffering isn't enabled until
|
|
||||||
the client explicitly allows it, because enabling it without the client
|
|
||||||
being aware of the fact that his writes may be buffered may lead to data
|
|
||||||
loss. Because of this, older versions of clients don't support write
|
|
||||||
buffering at all, newer versions of the QEMU driver allow write buffering
|
|
||||||
only if it's enabled in disk settings with `-blockdev cache.direct=false`,
|
|
||||||
and newer versions of FIO only allow write buffering if you don't specify
|
|
||||||
`-direct=1`. NBD and NFS drivers allow write buffering by default.
|
|
||||||
|
|
||||||
You can overcome this restriction too with the `client_writeback_allowed`
|
|
||||||
parameter, but you shouldn't do that unless you **really** know what you
|
|
||||||
are doing.
|
|
||||||
|
|
||||||
## client_max_buffered_bytes
|
|
||||||
|
|
||||||
- Type: integer
|
|
||||||
- Default: 33554432
|
|
||||||
- Can be changed online: yes
|
|
||||||
|
|
||||||
Maximum total size of buffered writes which triggers write-back when reached.
|
|
||||||
|
|
||||||
## client_max_buffered_ops
|
|
||||||
|
|
||||||
- Type: integer
|
|
||||||
- Default: 1024
|
|
||||||
- Can be changed online: yes
|
|
||||||
|
|
||||||
Maximum number of buffered writes which triggers write-back when reached.
|
|
||||||
Multiple consecutive modified data regions are counted as 1 write here.
|
|
||||||
|
|
||||||
## client_max_writeback_iodepth
|
|
||||||
|
|
||||||
- Type: integer
|
|
||||||
- Default: 256
|
|
||||||
- Can be changed online: yes
|
|
||||||
|
|
||||||
Maximum number of parallel writes when flushing buffered data to the server.
|
|
||||||
|
|
||||||
## nbd_timeout
|
|
||||||
|
|
||||||
- Type: seconds
|
|
||||||
- Default: 300
|
|
||||||
|
|
||||||
Timeout for I/O operations for [NBD](../usage/nbd.en.md). If an operation
|
|
||||||
executes for longer than this timeout, including when your cluster is just
|
|
||||||
temporarily down for more than timeout, the NBD device will detach by itself
|
|
||||||
(and possibly break the mounted file system).
|
|
||||||
|
|
||||||
You can set timeout to 0 to never detach, but in that case you won't be
|
|
||||||
able to remove the kernel device at all if the NBD process dies - you'll have
|
|
||||||
to reboot the host.
|
|
||||||
|
|
||||||
## nbd_max_devices
|
|
||||||
|
|
||||||
- Type: integer
|
|
||||||
- Default: 64
|
|
||||||
|
|
||||||
Maximum number of NBD devices in the system. This value is passed as
|
|
||||||
`nbds_max` parameter for the nbd kernel module when vitastor-nbd autoloads it.
|
|
||||||
|
|
||||||
## nbd_max_part
|
|
||||||
|
|
||||||
- Type: integer
|
|
||||||
- Default: 3
|
|
||||||
|
|
||||||
Maximum number of partitions per NBD device. This value is passed as
|
|
||||||
`max_part` parameter for the nbd kernel module when vitastor-nbd autoloads it.
|
|
||||||
Note that (nbds_max)*(1+max_part) usually can't exceed 256.
|
|
|
@ -1,137 +0,0 @@
|
||||||
[Документация](../../README-ru.md#документация) → [Конфигурация](../config.ru.md) → Параметры клиентского кода
|
|
||||||
|
|
||||||
-----
|
|
||||||
|
|
||||||
[Read in English](client.en.md)
|
|
||||||
|
|
||||||
# Параметры клиентского кода
|
|
||||||
|
|
||||||
Данные параметры применяются только к клиентам Vitastor (QEMU, fio, NBD и т.п.) и
|
|
||||||
затрагивают логику их работы с кластером.
|
|
||||||
|
|
||||||
- [client_max_dirty_bytes](#client_max_dirty_bytes)
|
|
||||||
- [client_max_dirty_ops](#client_max_dirty_ops)
|
|
||||||
- [client_enable_writeback](#client_enable_writeback)
|
|
||||||
- [client_max_buffered_bytes](#client_max_buffered_bytes)
|
|
||||||
- [client_max_buffered_ops](#client_max_buffered_ops)
|
|
||||||
- [client_max_writeback_iodepth](#client_max_writeback_iodepth)
|
|
||||||
- [nbd_timeout](#nbd_timeout)
|
|
||||||
- [nbd_max_devices](#nbd_max_devices)
|
|
||||||
- [nbd_max_part](#nbd_max_part)
|
|
||||||
|
|
||||||
## client_max_dirty_bytes
|
|
||||||
|
|
||||||
- Тип: целое число
|
|
||||||
- Значение по умолчанию: 33554432
|
|
||||||
- Можно менять на лету: да
|
|
||||||
|
|
||||||
При работе без [immediate_commit](layout-cluster.ru.md#immediate_commit)=all - это лимит объёма "грязных" (не
|
|
||||||
зафиксированных fsync-ом) данных, при достижении которого клиент будет
|
|
||||||
принудительно вызывать fsync и фиксировать данные. Также стоит иметь в виду,
|
|
||||||
что в этом случае до момента fsync клиент хранит копию незафиксированных
|
|
||||||
данных в памяти, то есть, настройка влияет на потребление памяти клиентами.
|
|
||||||
|
|
||||||
## client_max_dirty_ops
|
|
||||||
|
|
||||||
- Тип: целое число
|
|
||||||
- Значение по умолчанию: 1024
|
|
||||||
- Можно менять на лету: да
|
|
||||||
|
|
||||||
Аналогично client_max_dirty_bytes, но ограничивает количество
|
|
||||||
незафиксированных операций записи вместо их общего объёма.
|
|
||||||
|
|
||||||
## client_enable_writeback
|
|
||||||
|
|
||||||
- Тип: булево (да/нет)
|
|
||||||
- Значение по умолчанию: false
|
|
||||||
- Можно менять на лету: да
|
|
||||||
|
|
||||||
Данный параметр разрешает включать буферизацию записи в памяти. Буферизация
|
|
||||||
означает, что операции записи отправляются на кластер Vitastor не сразу, а
|
|
||||||
могут небольшое время накапливаться в памяти и сбрасываться сразу пакетами,
|
|
||||||
до тех пор, пока либо не будет превышен лимит неотправленных записей, либо
|
|
||||||
пока клиент не вызовет fsync.
|
|
||||||
|
|
||||||
Буферизация значительно повышает производительность некоторых приложений,
|
|
||||||
например, CrystalDiskMark в Windows (ха-ха :-D), но также и любых других,
|
|
||||||
которые пишут на диск неоптимально: либо последовательно, но мелкими блоками
|
|
||||||
(например, по 4 кб), либо случайно, но без параллелизма и без fsync - то
|
|
||||||
есть, например, отправляя 128 операций записи в разные места диска, но не
|
|
||||||
все сразу с помощью асинхронного I/O, а по одной.
|
|
||||||
|
|
||||||
В QEMU с буферизацией записи можно ожидать показателя примерно 22000
|
|
||||||
операций случайной записи в секунду в 1 поток и с глубиной очереди 1 (T1Q1)
|
|
||||||
без fsync, почти вне зависимости от того, насколько хороши ваши диски - эта
|
|
||||||
цифра упирается в сам QEMU. Без буферизации рекорд пока что - 9900 операций
|
|
||||||
в секунду, но на железе похуже может быть и поменьше, например, 5000 операций
|
|
||||||
в секунду.
|
|
||||||
|
|
||||||
При этом, даже если данный параметр включён, буферизация не включается, если
|
|
||||||
явно не разрешена клиентом, т.к. если клиент не знает, что запросы записи
|
|
||||||
буферизуются, это может приводить к потере данных. Поэтому в старых версиях
|
|
||||||
клиентских драйверов буферизация записи не включается вообще, в новых
|
|
||||||
версиях QEMU-драйвера включается, только если разрешена опцией диска
|
|
||||||
`-blockdev cache.direct=false`, а в fio - только если нет опции `-direct=1`.
|
|
||||||
В NBD и NFS драйверах буферизация записи разрешена по умолчанию.
|
|
||||||
|
|
||||||
Можно обойти и это ограничение с помощью параметра `client_writeback_allowed`,
|
|
||||||
но делать так не надо, если только вы не уверены в том, что делаете, на все
|
|
||||||
100%. :-)
|
|
||||||
|
|
||||||
## client_max_buffered_bytes
|
|
||||||
|
|
||||||
- Тип: целое число
|
|
||||||
- Значение по умолчанию: 33554432
|
|
||||||
- Можно менять на лету: да
|
|
||||||
|
|
||||||
Максимальный общий размер буферизованных записей, при достижении которого
|
|
||||||
начинается процесс сброса данных на сервер.
|
|
||||||
|
|
||||||
## client_max_buffered_ops
|
|
||||||
|
|
||||||
- Тип: целое число
|
|
||||||
- Значение по умолчанию: 1024
|
|
||||||
- Можно менять на лету: да
|
|
||||||
|
|
||||||
Максимальное количество буферизованных записей, при достижении которого
|
|
||||||
начинается процесс сброса данных на сервер. При этом несколько
|
|
||||||
последовательных изменённых областей здесь считаются 1 записью.
|
|
||||||
|
|
||||||
## client_max_writeback_iodepth
|
|
||||||
|
|
||||||
- Тип: целое число
|
|
||||||
- Значение по умолчанию: 256
|
|
||||||
- Можно менять на лету: да
|
|
||||||
|
|
||||||
Максимальное число параллельных операций записи при сбросе буферов на сервер.
|
|
||||||
|
|
||||||
## nbd_timeout
|
|
||||||
|
|
||||||
- Тип: секунды
|
|
||||||
- Значение по умолчанию: 300
|
|
||||||
|
|
||||||
Таймаут для операций чтения/записи через [NBD](../usage/nbd.ru.md). Если
|
|
||||||
операция выполняется дольше таймаута, включая временную недоступность
|
|
||||||
кластера на время, большее таймаута, NBD-устройство отключится само собой
|
|
||||||
(и, возможно, сломает примонтированную ФС).
|
|
||||||
|
|
||||||
Вы можете установить таймаут в 0, чтобы никогда не отключать устройство по
|
|
||||||
таймауту, но в этом случае вы вообще не сможете удалить устройство, если
|
|
||||||
процесс NBD умрёт - вам придётся перезагружать сервер.
|
|
||||||
|
|
||||||
## nbd_max_devices
|
|
||||||
|
|
||||||
- Тип: целое число
|
|
||||||
- Значение по умолчанию: 64
|
|
||||||
|
|
||||||
Максимальное число NBD-устройств в системе. Данное значение передаётся
|
|
||||||
модулю ядра nbd как параметр `nbds_max`, когда его загружает vitastor-nbd.
|
|
||||||
|
|
||||||
## nbd_max_part
|
|
||||||
|
|
||||||
- Тип: целое число
|
|
||||||
- Значение по умолчанию: 3
|
|
||||||
|
|
||||||
Максимальное число разделов на одном NBD-устройстве. Данное значение передаётся
|
|
||||||
модулю ядра nbd как параметр `max_part`, когда его загружает vitastor-nbd.
|
|
||||||
Имейте в виду, что (nbds_max)*(1+max_part) обычно не может превышать 256.
|
|
|
@ -19,8 +19,8 @@ These parameters only apply to Monitors.
|
||||||
## etcd_mon_ttl
|
## etcd_mon_ttl
|
||||||
|
|
||||||
- Type: seconds
|
- Type: seconds
|
||||||
- Default: 1
|
- Default: 30
|
||||||
- Minimum: 5
|
- Minimum: 10
|
||||||
|
|
||||||
Monitor etcd lease refresh interval in seconds
|
Monitor etcd lease refresh interval in seconds
|
||||||
|
|
||||||
|
|
|
@ -19,8 +19,8 @@
|
||||||
## etcd_mon_ttl
|
## etcd_mon_ttl
|
||||||
|
|
||||||
- Тип: секунды
|
- Тип: секунды
|
||||||
- Значение по умолчанию: 1
|
- Значение по умолчанию: 30
|
||||||
- Минимальное значение: 5
|
- Минимальное значение: 10
|
||||||
|
|
||||||
Интервал обновления etcd резервации (lease) монитором
|
Интервал обновления etcd резервации (lease) монитором
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,6 @@ between clients, OSDs and etcd.
|
||||||
- [rdma_max_msg](#rdma_max_msg)
|
- [rdma_max_msg](#rdma_max_msg)
|
||||||
- [rdma_max_recv](#rdma_max_recv)
|
- [rdma_max_recv](#rdma_max_recv)
|
||||||
- [rdma_max_send](#rdma_max_send)
|
- [rdma_max_send](#rdma_max_send)
|
||||||
- [rdma_odp](#rdma_odp)
|
|
||||||
- [peer_connect_interval](#peer_connect_interval)
|
- [peer_connect_interval](#peer_connect_interval)
|
||||||
- [peer_connect_timeout](#peer_connect_timeout)
|
- [peer_connect_timeout](#peer_connect_timeout)
|
||||||
- [osd_idle_timeout](#osd_idle_timeout)
|
- [osd_idle_timeout](#osd_idle_timeout)
|
||||||
|
@ -31,6 +30,7 @@ between clients, OSDs and etcd.
|
||||||
- [etcd_slow_timeout](#etcd_slow_timeout)
|
- [etcd_slow_timeout](#etcd_slow_timeout)
|
||||||
- [etcd_keepalive_timeout](#etcd_keepalive_timeout)
|
- [etcd_keepalive_timeout](#etcd_keepalive_timeout)
|
||||||
- [etcd_ws_keepalive_timeout](#etcd_ws_keepalive_timeout)
|
- [etcd_ws_keepalive_timeout](#etcd_ws_keepalive_timeout)
|
||||||
|
- [client_dirty_limit](#client_dirty_limit)
|
||||||
|
|
||||||
## tcp_header_buffer_size
|
## tcp_header_buffer_size
|
||||||
|
|
||||||
|
@ -69,14 +69,11 @@ but they are not connected to the cluster.
|
||||||
- Type: string
|
- Type: string
|
||||||
|
|
||||||
RDMA device name to use for Vitastor OSD communications (for example,
|
RDMA device name to use for Vitastor OSD communications (for example,
|
||||||
"rocep5s0f0"). Now Vitastor supports all adapters, even ones without
|
"rocep5s0f0"). Please note that Vitastor RDMA requires Implicit On-Demand
|
||||||
ODP support, like Mellanox ConnectX-3 and non-Mellanox cards.
|
Paging (Implicit ODP) and Scatter/Gather (SG) support from the RDMA device
|
||||||
|
to work. For example, Mellanox ConnectX-3 and older adapters don't have
|
||||||
Versions up to Vitastor 1.2.0 required ODP which is only present in
|
Implicit ODP, so they're unsupported by Vitastor. Run `ibv_devinfo -v` as
|
||||||
Mellanox ConnectX >= 4. See also [rdma_odp](#rdma_odp).
|
root to list available RDMA devices and their features.
|
||||||
|
|
||||||
Run `ibv_devinfo -v` as root to list available RDMA devices and their
|
|
||||||
features.
|
|
||||||
|
|
||||||
Remember that you also have to configure your network switches if you use
|
Remember that you also have to configure your network switches if you use
|
||||||
RoCE/RoCEv2, otherwise you may experience unstable performance. Refer to
|
RoCE/RoCEv2, otherwise you may experience unstable performance. Refer to
|
||||||
|
@ -151,28 +148,6 @@ less than `rdma_max_recv` so the receiving side doesn't run out of buffers.
|
||||||
Doesn't affect memory usage - additional memory isn't allocated for send
|
Doesn't affect memory usage - additional memory isn't allocated for send
|
||||||
operations.
|
operations.
|
||||||
|
|
||||||
## rdma_odp
|
|
||||||
|
|
||||||
- Type: boolean
|
|
||||||
- Default: false
|
|
||||||
|
|
||||||
Use RDMA with On-Demand Paging. ODP is currently only available on Mellanox
|
|
||||||
ConnectX-4 and newer adapters. ODP allows to not register memory explicitly
|
|
||||||
for RDMA adapter to be able to use it. This, in turn, allows to skip memory
|
|
||||||
copying during sending. One would think this should improve performance, but
|
|
||||||
**in reality** RDMA performance with ODP is **drastically** worse. Example
|
|
||||||
3-node cluster with 8 NVMe in each node and 2*25 GBit/s ConnectX-6 RDMA network
|
|
||||||
without ODP pushes 3950000 read iops, but only 239000 iops with ODP...
|
|
||||||
|
|
||||||
This happens because Mellanox ODP implementation seems to be based on
|
|
||||||
message retransmissions when the adapter doesn't know about the buffer yet -
|
|
||||||
it likely uses standard "RNR retransmissions" (RNR = receiver not ready)
|
|
||||||
which is generally slow in RDMA/RoCE networks. Here's a presentation about
|
|
||||||
it from ISPASS-2021 conference: https://tkygtr6.github.io/pub/ISPASS21_slides.pdf
|
|
||||||
|
|
||||||
ODP support is retained in the code just in case a good ODP implementation
|
|
||||||
appears one day.
|
|
||||||
|
|
||||||
## peer_connect_interval
|
## peer_connect_interval
|
||||||
|
|
||||||
- Type: seconds
|
- Type: seconds
|
||||||
|
@ -215,8 +190,8 @@ is scheduled.
|
||||||
## up_wait_retry_interval
|
## up_wait_retry_interval
|
||||||
|
|
||||||
- Type: milliseconds
|
- Type: milliseconds
|
||||||
- Default: 50
|
- Default: 500
|
||||||
- Minimum: 10
|
- Minimum: 50
|
||||||
- Can be changed online: yes
|
- Can be changed online: yes
|
||||||
|
|
||||||
OSDs respond to clients with a special error code when they receive I/O
|
OSDs respond to clients with a special error code when they receive I/O
|
||||||
|
@ -265,3 +240,17 @@ etcd_report_interval to guarantee that keepalive actually works.
|
||||||
|
|
||||||
etcd websocket ping interval required to keep the connection alive and
|
etcd websocket ping interval required to keep the connection alive and
|
||||||
detect disconnections quickly.
|
detect disconnections quickly.
|
||||||
|
|
||||||
|
## client_dirty_limit
|
||||||
|
|
||||||
|
- Type: integer
|
||||||
|
- Default: 33554432
|
||||||
|
- Can be changed online: yes
|
||||||
|
|
||||||
|
Without immediate_commit=all this parameter sets the limit of "dirty"
|
||||||
|
(not committed by fsync) data allowed by the client before forcing an
|
||||||
|
additional fsync and committing the data. Also note that the client always
|
||||||
|
holds a copy of uncommitted data in memory so this setting also affects
|
||||||
|
RAM usage of clients.
|
||||||
|
|
||||||
|
This parameter doesn't affect OSDs themselves.
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
- [rdma_max_msg](#rdma_max_msg)
|
- [rdma_max_msg](#rdma_max_msg)
|
||||||
- [rdma_max_recv](#rdma_max_recv)
|
- [rdma_max_recv](#rdma_max_recv)
|
||||||
- [rdma_max_send](#rdma_max_send)
|
- [rdma_max_send](#rdma_max_send)
|
||||||
- [rdma_odp](#rdma_odp)
|
|
||||||
- [peer_connect_interval](#peer_connect_interval)
|
- [peer_connect_interval](#peer_connect_interval)
|
||||||
- [peer_connect_timeout](#peer_connect_timeout)
|
- [peer_connect_timeout](#peer_connect_timeout)
|
||||||
- [osd_idle_timeout](#osd_idle_timeout)
|
- [osd_idle_timeout](#osd_idle_timeout)
|
||||||
|
@ -31,6 +30,7 @@
|
||||||
- [etcd_slow_timeout](#etcd_slow_timeout)
|
- [etcd_slow_timeout](#etcd_slow_timeout)
|
||||||
- [etcd_keepalive_timeout](#etcd_keepalive_timeout)
|
- [etcd_keepalive_timeout](#etcd_keepalive_timeout)
|
||||||
- [etcd_ws_keepalive_timeout](#etcd_ws_keepalive_timeout)
|
- [etcd_ws_keepalive_timeout](#etcd_ws_keepalive_timeout)
|
||||||
|
- [client_dirty_limit](#client_dirty_limit)
|
||||||
|
|
||||||
## tcp_header_buffer_size
|
## tcp_header_buffer_size
|
||||||
|
|
||||||
|
@ -72,15 +72,12 @@ RDMA может быть нужно только если у клиентов е
|
||||||
- Тип: строка
|
- Тип: строка
|
||||||
|
|
||||||
Название RDMA-устройства для связи с Vitastor OSD (например, "rocep5s0f0").
|
Название RDMA-устройства для связи с Vitastor OSD (например, "rocep5s0f0").
|
||||||
Сейчас Vitastor поддерживает все модели адаптеров, включая те, у которых
|
Имейте в виду, что поддержка RDMA в Vitastor требует функций устройства
|
||||||
нет поддержки ODP, то есть вы можете использовать RDMA с ConnectX-3 и
|
Implicit On-Demand Paging (Implicit ODP) и Scatter/Gather (SG). Например,
|
||||||
картами производства не Mellanox.
|
адаптеры Mellanox ConnectX-3 и более старые не поддерживают Implicit ODP и
|
||||||
|
потому не поддерживаются в Vitastor. Запустите `ibv_devinfo -v` от имени
|
||||||
Версии Vitastor до 1.2.0 включительно требовали ODP, который есть только
|
суперпользователя, чтобы посмотреть список доступных RDMA-устройств, их
|
||||||
на Mellanox ConnectX 4 и более новых. См. также [rdma_odp](#rdma_odp).
|
параметры и возможности.
|
||||||
|
|
||||||
Запустите `ibv_devinfo -v` от имени суперпользователя, чтобы посмотреть
|
|
||||||
список доступных RDMA-устройств, их параметры и возможности.
|
|
||||||
|
|
||||||
Обратите внимание, что если вы используете RoCE/RoCEv2, вам также необходимо
|
Обратите внимание, что если вы используете RoCE/RoCEv2, вам также необходимо
|
||||||
правильно настроить для него коммутаторы, иначе вы можете столкнуться с
|
правильно настроить для него коммутаторы, иначе вы можете столкнуться с
|
||||||
|
@ -159,29 +156,6 @@ OSD в любом случае согласовывают реальное зн
|
||||||
Не влияет на потребление памяти - дополнительная память на операции отправки
|
Не влияет на потребление памяти - дополнительная память на операции отправки
|
||||||
не выделяется.
|
не выделяется.
|
||||||
|
|
||||||
## rdma_odp
|
|
||||||
|
|
||||||
- Тип: булево (да/нет)
|
|
||||||
- Значение по умолчанию: false
|
|
||||||
|
|
||||||
Использовать RDMA с On-Demand Paging. ODP - функция, доступная пока что
|
|
||||||
исключительно на адаптерах Mellanox ConnectX-4 и более новых. ODP позволяет
|
|
||||||
не регистрировать память для её использования RDMA-картой. Благодаря этому
|
|
||||||
можно не копировать данные при отправке их в сеть и, казалось бы, это должно
|
|
||||||
улучшать производительность - но **по факту** получается так, что
|
|
||||||
производительность только ухудшается, причём сильно. Пример - на 3-узловом
|
|
||||||
кластере с 8 NVMe в каждом узле и сетью 2*25 Гбит/с на чтение с RDMA без ODP
|
|
||||||
удаётся снять 3950000 iops, а с ODP - всего 239000 iops...
|
|
||||||
|
|
||||||
Это происходит из-за того, что реализация ODP у Mellanox неоптимальная и
|
|
||||||
основана на повторной передаче сообщений, когда карте не известен буфер -
|
|
||||||
вероятно, на стандартных "RNR retransmission" (RNR = receiver not ready).
|
|
||||||
А данные повторные передачи в RDMA/RoCE - всегда очень медленная штука.
|
|
||||||
Презентация на эту тему с конференции ISPASS-2021: https://tkygtr6.github.io/pub/ISPASS21_slides.pdf
|
|
||||||
|
|
||||||
Возможность использования ODP сохранена в коде на случай, если вдруг в один
|
|
||||||
прекрасный день появится хорошая реализация ODP.
|
|
||||||
|
|
||||||
## peer_connect_interval
|
## peer_connect_interval
|
||||||
|
|
||||||
- Тип: секунды
|
- Тип: секунды
|
||||||
|
@ -224,8 +198,8 @@ OSD в любом случае согласовывают реальное зн
|
||||||
## up_wait_retry_interval
|
## up_wait_retry_interval
|
||||||
|
|
||||||
- Тип: миллисекунды
|
- Тип: миллисекунды
|
||||||
- Значение по умолчанию: 50
|
- Значение по умолчанию: 500
|
||||||
- Минимальное значение: 10
|
- Минимальное значение: 50
|
||||||
- Можно менять на лету: да
|
- Можно менять на лету: да
|
||||||
|
|
||||||
Когда OSD получают от клиентов запросы ввода-вывода, относящиеся к не
|
Когда OSD получают от клиентов запросы ввода-вывода, относящиеся к не
|
||||||
|
@ -277,3 +251,17 @@ etcd_report_interval, чтобы keepalive гарантированно рабо
|
||||||
- Можно менять на лету: да
|
- Можно менять на лету: да
|
||||||
|
|
||||||
Интервал проверки живости вебсокет-подключений к etcd.
|
Интервал проверки живости вебсокет-подключений к etcd.
|
||||||
|
|
||||||
|
## client_dirty_limit
|
||||||
|
|
||||||
|
- Тип: целое число
|
||||||
|
- Значение по умолчанию: 33554432
|
||||||
|
- Можно менять на лету: да
|
||||||
|
|
||||||
|
При работе без immediate_commit=all - это лимит объёма "грязных" (не
|
||||||
|
зафиксированных fsync-ом) данных, при достижении которого клиент будет
|
||||||
|
принудительно вызывать fsync и фиксировать данные. Также стоит иметь в виду,
|
||||||
|
что в этом случае до момента fsync клиент хранит копию незафиксированных
|
||||||
|
данных в памяти, то есть, настройка влияет на потребление памяти клиентами.
|
||||||
|
|
||||||
|
Параметр не влияет на сами OSD.
|
||||||
|
|
|
@ -11,7 +11,6 @@ initialization and can be changed - either with an OSD restart or, for some of
|
||||||
them, even without restarting by updating configuration in etcd.
|
them, even without restarting by updating configuration in etcd.
|
||||||
|
|
||||||
- [etcd_report_interval](#etcd_report_interval)
|
- [etcd_report_interval](#etcd_report_interval)
|
||||||
- [etcd_stats_interval](#etcd_stats_interval)
|
|
||||||
- [run_primary](#run_primary)
|
- [run_primary](#run_primary)
|
||||||
- [osd_network](#osd_network)
|
- [osd_network](#osd_network)
|
||||||
- [bind_address](#bind_address)
|
- [bind_address](#bind_address)
|
||||||
|
@ -19,7 +18,6 @@ them, even without restarting by updating configuration in etcd.
|
||||||
- [autosync_interval](#autosync_interval)
|
- [autosync_interval](#autosync_interval)
|
||||||
- [autosync_writes](#autosync_writes)
|
- [autosync_writes](#autosync_writes)
|
||||||
- [recovery_queue_depth](#recovery_queue_depth)
|
- [recovery_queue_depth](#recovery_queue_depth)
|
||||||
- [recovery_sleep_us](#recovery_sleep_us)
|
|
||||||
- [recovery_pg_switch](#recovery_pg_switch)
|
- [recovery_pg_switch](#recovery_pg_switch)
|
||||||
- [recovery_sync_batch](#recovery_sync_batch)
|
- [recovery_sync_batch](#recovery_sync_batch)
|
||||||
- [readonly](#readonly)
|
- [readonly](#readonly)
|
||||||
|
@ -52,35 +50,17 @@ them, even without restarting by updating configuration in etcd.
|
||||||
- [scrub_list_limit](#scrub_list_limit)
|
- [scrub_list_limit](#scrub_list_limit)
|
||||||
- [scrub_find_best](#scrub_find_best)
|
- [scrub_find_best](#scrub_find_best)
|
||||||
- [scrub_ec_max_bruteforce](#scrub_ec_max_bruteforce)
|
- [scrub_ec_max_bruteforce](#scrub_ec_max_bruteforce)
|
||||||
- [recovery_tune_interval](#recovery_tune_interval)
|
|
||||||
- [recovery_tune_util_low](#recovery_tune_util_low)
|
|
||||||
- [recovery_tune_util_high](#recovery_tune_util_high)
|
|
||||||
- [recovery_tune_client_util_low](#recovery_tune_client_util_low)
|
|
||||||
- [recovery_tune_client_util_high](#recovery_tune_client_util_high)
|
|
||||||
- [recovery_tune_agg_interval](#recovery_tune_agg_interval)
|
|
||||||
- [recovery_tune_sleep_min_us](#recovery_tune_sleep_min_us)
|
|
||||||
- [recovery_tune_sleep_cutoff_us](#recovery_tune_sleep_cutoff_us)
|
|
||||||
|
|
||||||
## etcd_report_interval
|
## etcd_report_interval
|
||||||
|
|
||||||
- Type: seconds
|
- Type: seconds
|
||||||
- Default: 5
|
- Default: 5
|
||||||
|
|
||||||
Interval at which OSDs report their liveness to etcd. Affects OSD lease time
|
Interval at which OSDs report their state to etcd. Affects OSD lease time
|
||||||
and thus the failover speed. Lease time is equal to this parameter value
|
and thus the failover speed. Lease time is equal to this parameter value
|
||||||
plus max_etcd_attempts * etcd_quick_timeout because it should be guaranteed
|
plus max_etcd_attempts * etcd_quick_timeout because it should be guaranteed
|
||||||
that every OSD always refreshes its lease in time.
|
that every OSD always refreshes its lease in time.
|
||||||
|
|
||||||
## etcd_stats_interval
|
|
||||||
|
|
||||||
- Type: seconds
|
|
||||||
- Default: 30
|
|
||||||
|
|
||||||
Interval at which OSDs report their statistics to etcd. Highly affects the
|
|
||||||
imposed load on etcd, because statistics include a key for every OSD and
|
|
||||||
for every PG. At the same time, low statistic intervals make `vitastor-cli`
|
|
||||||
statistics more responsive.
|
|
||||||
|
|
||||||
## run_primary
|
## run_primary
|
||||||
|
|
||||||
- Type: boolean
|
- Type: boolean
|
||||||
|
@ -144,24 +124,12 @@ operations before issuing an fsync operation internally.
|
||||||
## recovery_queue_depth
|
## recovery_queue_depth
|
||||||
|
|
||||||
- Type: integer
|
- Type: integer
|
||||||
- Default: 1
|
- Default: 4
|
||||||
- Can be changed online: yes
|
- Can be changed online: yes
|
||||||
|
|
||||||
Maximum recovery and rebalance operations initiated by each OSD in parallel.
|
Maximum recovery operations per one primary OSD at any given moment of time.
|
||||||
Note that each OSD talks to a lot of other OSDs so actual number of parallel
|
Currently it's the only parameter available to tune the speed or recovery
|
||||||
recovery operations per each OSD is greater than just recovery_queue_depth.
|
and rebalancing, but it's planned to implement more.
|
||||||
Increasing this parameter can speedup recovery if [auto-tuning](#recovery_tune_interval)
|
|
||||||
allows it or if it is disabled.
|
|
||||||
|
|
||||||
## recovery_sleep_us
|
|
||||||
|
|
||||||
- Type: microseconds
|
|
||||||
- Default: 0
|
|
||||||
- Can be changed online: yes
|
|
||||||
|
|
||||||
Delay for all recovery- and rebalance- related operations. If non-zero,
|
|
||||||
such operations are artificially slowed down to reduce the impact on
|
|
||||||
client I/O.
|
|
||||||
|
|
||||||
## recovery_pg_switch
|
## recovery_pg_switch
|
||||||
|
|
||||||
|
@ -529,90 +497,3 @@ the variant with most available equal copies is correct. For example, if
|
||||||
you have 3 replicas and 1 of them differs, this one is considered to be
|
you have 3 replicas and 1 of them differs, this one is considered to be
|
||||||
corrupted. But if there is no "best" version with more copies than all
|
corrupted. But if there is no "best" version with more copies than all
|
||||||
others have then the object is also marked as inconsistent.
|
others have then the object is also marked as inconsistent.
|
||||||
|
|
||||||
## recovery_tune_interval
|
|
||||||
|
|
||||||
- Type: seconds
|
|
||||||
- Default: 1
|
|
||||||
- Can be changed online: yes
|
|
||||||
|
|
||||||
Interval at which OSD re-considers client and recovery load and automatically
|
|
||||||
adjusts [recovery_sleep_us](#recovery_sleep_us). Recovery auto-tuning is
|
|
||||||
disabled if recovery_tune_interval is set to 0.
|
|
||||||
|
|
||||||
Auto-tuning targets utilization. Utilization is a measure of load and is
|
|
||||||
equal to the product of iops and average latency (so it may be greater
|
|
||||||
than 1). You set "low" and "high" client utilization thresholds and two
|
|
||||||
corresponding target recovery utilization levels. OSD calculates desired
|
|
||||||
recovery utilization from client utilization using linear interpolation
|
|
||||||
and auto-tunes recovery operation delay to make actual recovery utilization
|
|
||||||
match desired.
|
|
||||||
|
|
||||||
This allows to reduce recovery/rebalance impact on client operations. It is
|
|
||||||
of course impossible to remove it completely, but it should become adequate.
|
|
||||||
In some tests rebalance could earlier drop client write speed from 1.5 GB/s
|
|
||||||
to 50-100 MB/s, with default auto-tuning settings it now only reduces
|
|
||||||
to ~1 GB/s.
|
|
||||||
|
|
||||||
## recovery_tune_util_low
|
|
||||||
|
|
||||||
- Type: number
|
|
||||||
- Default: 0.1
|
|
||||||
- Can be changed online: yes
|
|
||||||
|
|
||||||
Desired recovery/rebalance utilization when client load is high, i.e. when
|
|
||||||
it is at or above recovery_tune_client_util_high.
|
|
||||||
|
|
||||||
## recovery_tune_util_high
|
|
||||||
|
|
||||||
- Type: number
|
|
||||||
- Default: 1
|
|
||||||
- Can be changed online: yes
|
|
||||||
|
|
||||||
Desired recovery/rebalance utilization when client load is low, i.e. when
|
|
||||||
it is at or below recovery_tune_client_util_low.
|
|
||||||
|
|
||||||
## recovery_tune_client_util_low
|
|
||||||
|
|
||||||
- Type: number
|
|
||||||
- Default: 0
|
|
||||||
- Can be changed online: yes
|
|
||||||
|
|
||||||
Client utilization considered "low".
|
|
||||||
|
|
||||||
## recovery_tune_client_util_high
|
|
||||||
|
|
||||||
- Type: number
|
|
||||||
- Default: 0.5
|
|
||||||
- Can be changed online: yes
|
|
||||||
|
|
||||||
Client utilization considered "high".
|
|
||||||
|
|
||||||
## recovery_tune_agg_interval
|
|
||||||
|
|
||||||
- Type: integer
|
|
||||||
- Default: 10
|
|
||||||
- Can be changed online: yes
|
|
||||||
|
|
||||||
The number of last auto-tuning iterations to use for calculating the
|
|
||||||
delay as average. Lower values result in quicker response to client
|
|
||||||
load change, higher values result in more stable delay. Default value of 10
|
|
||||||
is usually fine.
|
|
||||||
|
|
||||||
## recovery_tune_sleep_min_us
|
|
||||||
|
|
||||||
- Type: microseconds
|
|
||||||
- Default: 10
|
|
||||||
- Can be changed online: yes
|
|
||||||
|
|
||||||
Minimum possible value for auto-tuned recovery_sleep_us. Lower values
|
|
||||||
are changed to 0.
|
|
||||||
|
|
||||||
## recovery_tune_sleep_cutoff_us
|
|
||||||
|
|
||||||
- Type: microseconds
|
|
||||||
- Default: 10000000
|
|
||||||
- Can be changed online: yes
|
|
||||||
|
|
||||||
Maximum possible value for auto-tuned recovery_sleep_us. Higher values
|
|
||||||
are treated as outliers and ignored in aggregation.
|
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
изменения конфигурации в etcd.
|
изменения конфигурации в etcd.
|
||||||
|
|
||||||
- [etcd_report_interval](#etcd_report_interval)
|
- [etcd_report_interval](#etcd_report_interval)
|
||||||
- [etcd_stats_interval](#etcd_stats_interval)
|
|
||||||
- [run_primary](#run_primary)
|
- [run_primary](#run_primary)
|
||||||
- [osd_network](#osd_network)
|
- [osd_network](#osd_network)
|
||||||
- [bind_address](#bind_address)
|
- [bind_address](#bind_address)
|
||||||
|
@ -20,7 +19,6 @@
|
||||||
- [autosync_interval](#autosync_interval)
|
- [autosync_interval](#autosync_interval)
|
||||||
- [autosync_writes](#autosync_writes)
|
- [autosync_writes](#autosync_writes)
|
||||||
- [recovery_queue_depth](#recovery_queue_depth)
|
- [recovery_queue_depth](#recovery_queue_depth)
|
||||||
- [recovery_sleep_us](#recovery_sleep_us)
|
|
||||||
- [recovery_pg_switch](#recovery_pg_switch)
|
- [recovery_pg_switch](#recovery_pg_switch)
|
||||||
- [recovery_sync_batch](#recovery_sync_batch)
|
- [recovery_sync_batch](#recovery_sync_batch)
|
||||||
- [readonly](#readonly)
|
- [readonly](#readonly)
|
||||||
|
@ -53,35 +51,17 @@
|
||||||
- [scrub_list_limit](#scrub_list_limit)
|
- [scrub_list_limit](#scrub_list_limit)
|
||||||
- [scrub_find_best](#scrub_find_best)
|
- [scrub_find_best](#scrub_find_best)
|
||||||
- [scrub_ec_max_bruteforce](#scrub_ec_max_bruteforce)
|
- [scrub_ec_max_bruteforce](#scrub_ec_max_bruteforce)
|
||||||
- [recovery_tune_interval](#recovery_tune_interval)
|
|
||||||
- [recovery_tune_util_low](#recovery_tune_util_low)
|
|
||||||
- [recovery_tune_util_high](#recovery_tune_util_high)
|
|
||||||
- [recovery_tune_client_util_low](#recovery_tune_client_util_low)
|
|
||||||
- [recovery_tune_client_util_high](#recovery_tune_client_util_high)
|
|
||||||
- [recovery_tune_agg_interval](#recovery_tune_agg_interval)
|
|
||||||
- [recovery_tune_sleep_min_us](#recovery_tune_sleep_min_us)
|
|
||||||
- [recovery_tune_sleep_cutoff_us](#recovery_tune_sleep_cutoff_us)
|
|
||||||
|
|
||||||
## etcd_report_interval
|
## etcd_report_interval
|
||||||
|
|
||||||
- Тип: секунды
|
- Тип: секунды
|
||||||
- Значение по умолчанию: 5
|
- Значение по умолчанию: 5
|
||||||
|
|
||||||
Интервал, с которым OSD сообщает о том, что жив, в etcd. Значение параметра
|
Интервал, с которым OSD обновляет своё состояние в etcd. Значение параметра
|
||||||
влияет на время резервации (lease) OSD и поэтому - на скорость переключения
|
влияет на время резервации (lease) OSD и поэтому на скорость переключения
|
||||||
при падении OSD. Время lease равняется значению этого параметра плюс
|
при падении OSD. Время lease равняется значению этого параметра плюс
|
||||||
max_etcd_attempts * etcd_quick_timeout.
|
max_etcd_attempts * etcd_quick_timeout.
|
||||||
|
|
||||||
## etcd_stats_interval
|
|
||||||
|
|
||||||
- Тип: секунды
|
|
||||||
- Значение по умолчанию: 30
|
|
||||||
|
|
||||||
Интервал, с которым OSD обновляет свою статистику в etcd. Сильно влияет на
|
|
||||||
создаваемую нагрузку на etcd, потому что статистика содержит по ключу на
|
|
||||||
каждый OSD и на каждую PG. В то же время низкий интервал делает
|
|
||||||
статистику, печатаемую `vitastor-cli`, отзывчивей.
|
|
||||||
|
|
||||||
## run_primary
|
## run_primary
|
||||||
|
|
||||||
- Тип: булево (да/нет)
|
- Тип: булево (да/нет)
|
||||||
|
@ -147,25 +127,13 @@ OSD, чтобы успевать очищать журнал - без них OSD
|
||||||
## recovery_queue_depth
|
## recovery_queue_depth
|
||||||
|
|
||||||
- Тип: целое число
|
- Тип: целое число
|
||||||
- Значение по умолчанию: 1
|
- Значение по умолчанию: 4
|
||||||
- Можно менять на лету: да
|
- Можно менять на лету: да
|
||||||
|
|
||||||
Максимальное число параллельных операций восстановления, инициируемых одним
|
Максимальное число операций восстановления на одном первичном OSD в любой
|
||||||
OSD в любой момент времени. Имейте в виду, что каждый OSD обычно работает с
|
момент времени. На данный момент единственный параметр, который можно менять
|
||||||
многими другими OSD, так что на практике параллелизм восстановления больше,
|
для ускорения или замедления восстановления и перебалансировки данных, но
|
||||||
чем просто recovery_queue_depth. Увеличение значения этого параметра может
|
в планах реализация других параметров.
|
||||||
ускорить восстановление если [автотюнинг скорости](#recovery_tune_interval)
|
|
||||||
разрешает это или если он отключён.
|
|
||||||
|
|
||||||
## recovery_sleep_us
|
|
||||||
|
|
||||||
- Тип: микросекунды
|
|
||||||
- Значение по умолчанию: 0
|
|
||||||
- Можно менять на лету: да
|
|
||||||
|
|
||||||
Delay for all recovery- and rebalance- related operations. If non-zero,
|
|
||||||
such operations are artificially slowed down to reduce the impact on
|
|
||||||
client I/O.
|
|
||||||
|
|
||||||
## recovery_pg_switch
|
## recovery_pg_switch
|
||||||
|
|
||||||
|
@ -556,93 +524,3 @@ EC (кодов коррекции ошибок) с более, чем 1 диск
|
||||||
считается некорректной. Однако, если "лучшую" версию с числом доступных
|
считается некорректной. Однако, если "лучшую" версию с числом доступных
|
||||||
копий большим, чем у всех других версий, найти невозможно, то объект тоже
|
копий большим, чем у всех других версий, найти невозможно, то объект тоже
|
||||||
маркируется неконсистентным.
|
маркируется неконсистентным.
|
||||||
|
|
||||||
## recovery_tune_interval
|
|
||||||
|
|
||||||
- Тип: секунды
|
|
||||||
- Значение по умолчанию: 1
|
|
||||||
- Можно менять на лету: да
|
|
||||||
|
|
||||||
Интервал, с которым OSD пересматривает клиентскую нагрузку и нагрузку
|
|
||||||
восстановления и автоматически подстраивает [recovery_sleep_us](#recovery_sleep_us).
|
|
||||||
Автотюнинг (автоподстройка) отключается, если recovery_tune_interval
|
|
||||||
устанавливается в значение 0.
|
|
||||||
|
|
||||||
Автотюнинг регулирует утилизацию. Утилизация является мерой нагрузки
|
|
||||||
и равна произведению числа операций в секунду и средней задержки
|
|
||||||
(то есть, она может быть выше 1). Вы задаёте два уровня клиентской
|
|
||||||
утилизации - "низкий" и "высокий" (low и high) и два соответствующих
|
|
||||||
целевых уровня утилизации операциями восстановления. OSD рассчитывает
|
|
||||||
желаемый уровень утилизации восстановления линейной интерполяцией от
|
|
||||||
клиентской утилизации и подстраивает задержку операций восстановления
|
|
||||||
так, чтобы фактическая утилизация восстановления совпадала с желаемой.
|
|
||||||
|
|
||||||
Это позволяет снизить влияние восстановления и ребаланса на клиентские
|
|
||||||
операции. Конечно, невозможно исключить такое влияние полностью, но оно
|
|
||||||
должно становиться адекватнее. В некоторых тестах перебалансировка могла
|
|
||||||
снижать клиентскую скорость записи с 1.5 ГБ/с до 50-100 МБ/с, а теперь, с
|
|
||||||
настройками автотюнинга по умолчанию, она снижается только до ~1 ГБ/с.
|
|
||||||
|
|
||||||
## recovery_tune_util_low
|
|
||||||
|
|
||||||
- Тип: число
|
|
||||||
- Значение по умолчанию: 0.1
|
|
||||||
- Можно менять на лету: да
|
|
||||||
|
|
||||||
Желаемая утилизация восстановления в моменты, когда клиентская нагрузка
|
|
||||||
высокая, то есть, находится на уровне или выше recovery_tune_client_util_high.
|
|
||||||
|
|
||||||
## recovery_tune_util_high
|
|
||||||
|
|
||||||
- Тип: число
|
|
||||||
- Значение по умолчанию: 1
|
|
||||||
- Можно менять на лету: да
|
|
||||||
|
|
||||||
Желаемая утилизация восстановления в моменты, когда клиентская нагрузка
|
|
||||||
низкая, то есть, находится на уровне или ниже recovery_tune_client_util_low.
|
|
||||||
|
|
||||||
## recovery_tune_client_util_low
|
|
||||||
|
|
||||||
- Тип: число
|
|
||||||
- Значение по умолчанию: 0
|
|
||||||
- Можно менять на лету: да
|
|
||||||
|
|
||||||
Клиентская утилизация, которая считается "низкой".
|
|
||||||
|
|
||||||
## recovery_tune_client_util_high
|
|
||||||
|
|
||||||
- Тип: число
|
|
||||||
- Значение по умолчанию: 0.5
|
|
||||||
- Можно менять на лету: да
|
|
||||||
|
|
||||||
Клиентская утилизация, которая считается "высокой".
|
|
||||||
|
|
||||||
## recovery_tune_agg_interval
|
|
||||||
|
|
||||||
- Тип: целое число
|
|
||||||
- Значение по умолчанию: 10
|
|
||||||
- Можно менять на лету: да
|
|
||||||
|
|
||||||
Число последних итераций автоподстройки для расчёта задержки как среднего
|
|
||||||
значения. Меньшие значения параметра ускоряют отклик на изменение нагрузки,
|
|
||||||
большие значения делают задержку стабильнее. Значение по умолчанию 10
|
|
||||||
обычно нормальное и не требует изменений.
|
|
||||||
|
|
||||||
## recovery_tune_sleep_min_us
|
|
||||||
|
|
||||||
- Тип: микросекунды
|
|
||||||
- Значение по умолчанию: 10
|
|
||||||
- Можно менять на лету: да
|
|
||||||
|
|
||||||
Минимальное возможное значение авто-подстроенного recovery_sleep_us.
|
|
||||||
Меньшие значения заменяются на 0.
|
|
||||||
|
|
||||||
## recovery_tune_sleep_cutoff_us
|
|
||||||
|
|
||||||
- Тип: микросекунды
|
|
||||||
- Значение по умолчанию: 10000000
|
|
||||||
- Можно менять на лету: да
|
|
||||||
|
|
||||||
Максимальное возможное значение авто-подстроенного recovery_sleep_us.
|
|
||||||
Большие значения считаются случайными выбросами и игнорируются в
|
|
||||||
усреднении.
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
# Client Parameters
|
|
||||||
|
|
||||||
These parameters apply only to Vitastor clients (QEMU, fio, NBD and so on) and
|
|
||||||
affect their interaction with the cluster.
|
|
|
@ -1,4 +0,0 @@
|
||||||
# Параметры клиентского кода
|
|
||||||
|
|
||||||
Данные параметры применяются только к клиентам Vitastor (QEMU, fio, NBD и т.п.) и
|
|
||||||
затрагивают логику их работы с кластером.
|
|
|
@ -1,168 +0,0 @@
|
||||||
- name: client_max_dirty_bytes
|
|
||||||
type: int
|
|
||||||
default: 33554432
|
|
||||||
online: true
|
|
||||||
info: |
|
|
||||||
Without [immediate_commit](layout-cluster.en.md#immediate_commit)=all this parameter sets the limit of "dirty"
|
|
||||||
(not committed by fsync) data allowed by the client before forcing an
|
|
||||||
additional fsync and committing the data. Also note that the client always
|
|
||||||
holds a copy of uncommitted data in memory so this setting also affects
|
|
||||||
RAM usage of clients.
|
|
||||||
info_ru: |
|
|
||||||
При работе без [immediate_commit](layout-cluster.ru.md#immediate_commit)=all - это лимит объёма "грязных" (не
|
|
||||||
зафиксированных fsync-ом) данных, при достижении которого клиент будет
|
|
||||||
принудительно вызывать fsync и фиксировать данные. Также стоит иметь в виду,
|
|
||||||
что в этом случае до момента fsync клиент хранит копию незафиксированных
|
|
||||||
данных в памяти, то есть, настройка влияет на потребление памяти клиентами.
|
|
||||||
- name: client_max_dirty_ops
|
|
||||||
type: int
|
|
||||||
default: 1024
|
|
||||||
online: true
|
|
||||||
info: |
|
|
||||||
Same as client_max_dirty_bytes, but instead of total size, limits the number
|
|
||||||
of uncommitted write operations.
|
|
||||||
info_ru: |
|
|
||||||
Аналогично client_max_dirty_bytes, но ограничивает количество
|
|
||||||
незафиксированных операций записи вместо их общего объёма.
|
|
||||||
- name: client_enable_writeback
|
|
||||||
type: bool
|
|
||||||
default: false
|
|
||||||
online: true
|
|
||||||
info: |
|
|
||||||
This parameter enables client-side write buffering. This means that write
|
|
||||||
requests are accumulated in memory for a short time before being sent to
|
|
||||||
a Vitastor cluster which allows to send them in parallel and increase
|
|
||||||
performance of some applications. Writes are buffered until client forces
|
|
||||||
a flush with fsync() or until the amount of buffered writes exceeds the
|
|
||||||
limit.
|
|
||||||
|
|
||||||
Write buffering significantly increases performance of some applications,
|
|
||||||
for example, CrystalDiskMark under Windows (LOL :-D), but also any other
|
|
||||||
applications if they do writes in one of two non-optimal ways: either if
|
|
||||||
they do a lot of small (4 kb or so) sequential writes, or if they do a lot
|
|
||||||
of small random writes, but without any parallelism or asynchrony, and also
|
|
||||||
without calling fsync().
|
|
||||||
|
|
||||||
With write buffering enabled, you can expect around 22000 T1Q1 random write
|
|
||||||
iops in QEMU more or less regardless of the quality of your SSDs, and this
|
|
||||||
number is in fact bound by QEMU itself rather than Vitastor (check it
|
|
||||||
yourself by adding a "driver=null-co" disk in QEMU). Without write
|
|
||||||
buffering, the current record is 9900 iops, but the number is usually
|
|
||||||
even lower with non-ideal hardware, for example, it may be 5000 iops.
|
|
||||||
|
|
||||||
Even when this parameter is enabled, write buffering isn't enabled until
|
|
||||||
the client explicitly allows it, because enabling it without the client
|
|
||||||
being aware of the fact that his writes may be buffered may lead to data
|
|
||||||
loss. Because of this, older versions of clients don't support write
|
|
||||||
buffering at all, newer versions of the QEMU driver allow write buffering
|
|
||||||
only if it's enabled in disk settings with `-blockdev cache.direct=false`,
|
|
||||||
and newer versions of FIO only allow write buffering if you don't specify
|
|
||||||
`-direct=1`. NBD and NFS drivers allow write buffering by default.
|
|
||||||
|
|
||||||
You can overcome this restriction too with the `client_writeback_allowed`
|
|
||||||
parameter, but you shouldn't do that unless you **really** know what you
|
|
||||||
are doing.
|
|
||||||
info_ru: |
|
|
||||||
Данный параметр разрешает включать буферизацию записи в памяти. Буферизация
|
|
||||||
означает, что операции записи отправляются на кластер Vitastor не сразу, а
|
|
||||||
могут небольшое время накапливаться в памяти и сбрасываться сразу пакетами,
|
|
||||||
до тех пор, пока либо не будет превышен лимит неотправленных записей, либо
|
|
||||||
пока клиент не вызовет fsync.
|
|
||||||
|
|
||||||
Буферизация значительно повышает производительность некоторых приложений,
|
|
||||||
например, CrystalDiskMark в Windows (ха-ха :-D), но также и любых других,
|
|
||||||
которые пишут на диск неоптимально: либо последовательно, но мелкими блоками
|
|
||||||
(например, по 4 кб), либо случайно, но без параллелизма и без fsync - то
|
|
||||||
есть, например, отправляя 128 операций записи в разные места диска, но не
|
|
||||||
все сразу с помощью асинхронного I/O, а по одной.
|
|
||||||
|
|
||||||
В QEMU с буферизацией записи можно ожидать показателя примерно 22000
|
|
||||||
операций случайной записи в секунду в 1 поток и с глубиной очереди 1 (T1Q1)
|
|
||||||
без fsync, почти вне зависимости от того, насколько хороши ваши диски - эта
|
|
||||||
цифра упирается в сам QEMU. Без буферизации рекорд пока что - 9900 операций
|
|
||||||
в секунду, но на железе похуже может быть и поменьше, например, 5000 операций
|
|
||||||
в секунду.
|
|
||||||
|
|
||||||
При этом, даже если данный параметр включён, буферизация не включается, если
|
|
||||||
явно не разрешена клиентом, т.к. если клиент не знает, что запросы записи
|
|
||||||
буферизуются, это может приводить к потере данных. Поэтому в старых версиях
|
|
||||||
клиентских драйверов буферизация записи не включается вообще, в новых
|
|
||||||
версиях QEMU-драйвера включается, только если разрешена опцией диска
|
|
||||||
`-blockdev cache.direct=false`, а в fio - только если нет опции `-direct=1`.
|
|
||||||
В NBD и NFS драйверах буферизация записи разрешена по умолчанию.
|
|
||||||
|
|
||||||
Можно обойти и это ограничение с помощью параметра `client_writeback_allowed`,
|
|
||||||
но делать так не надо, если только вы не уверены в том, что делаете, на все
|
|
||||||
100%. :-)
|
|
||||||
- name: client_max_buffered_bytes
|
|
||||||
type: int
|
|
||||||
default: 33554432
|
|
||||||
online: true
|
|
||||||
info: |
|
|
||||||
Maximum total size of buffered writes which triggers write-back when reached.
|
|
||||||
info_ru: |
|
|
||||||
Максимальный общий размер буферизованных записей, при достижении которого
|
|
||||||
начинается процесс сброса данных на сервер.
|
|
||||||
- name: client_max_buffered_ops
|
|
||||||
type: int
|
|
||||||
default: 1024
|
|
||||||
online: true
|
|
||||||
info: |
|
|
||||||
Maximum number of buffered writes which triggers write-back when reached.
|
|
||||||
Multiple consecutive modified data regions are counted as 1 write here.
|
|
||||||
info_ru: |
|
|
||||||
Максимальное количество буферизованных записей, при достижении которого
|
|
||||||
начинается процесс сброса данных на сервер. При этом несколько
|
|
||||||
последовательных изменённых областей здесь считаются 1 записью.
|
|
||||||
- name: client_max_writeback_iodepth
|
|
||||||
type: int
|
|
||||||
default: 256
|
|
||||||
online: true
|
|
||||||
info: |
|
|
||||||
Maximum number of parallel writes when flushing buffered data to the server.
|
|
||||||
info_ru: |
|
|
||||||
Максимальное число параллельных операций записи при сбросе буферов на сервер.
|
|
||||||
- name: nbd_timeout
|
|
||||||
type: sec
|
|
||||||
default: 300
|
|
||||||
online: false
|
|
||||||
info: |
|
|
||||||
Timeout for I/O operations for [NBD](../usage/nbd.en.md). If an operation
|
|
||||||
executes for longer than this timeout, including when your cluster is just
|
|
||||||
temporarily down for more than timeout, the NBD device will detach by itself
|
|
||||||
(and possibly break the mounted file system).
|
|
||||||
|
|
||||||
You can set timeout to 0 to never detach, but in that case you won't be
|
|
||||||
able to remove the kernel device at all if the NBD process dies - you'll have
|
|
||||||
to reboot the host.
|
|
||||||
info_ru: |
|
|
||||||
Таймаут для операций чтения/записи через [NBD](../usage/nbd.ru.md). Если
|
|
||||||
операция выполняется дольше таймаута, включая временную недоступность
|
|
||||||
кластера на время, большее таймаута, NBD-устройство отключится само собой
|
|
||||||
(и, возможно, сломает примонтированную ФС).
|
|
||||||
|
|
||||||
Вы можете установить таймаут в 0, чтобы никогда не отключать устройство по
|
|
||||||
таймауту, но в этом случае вы вообще не сможете удалить устройство, если
|
|
||||||
процесс NBD умрёт - вам придётся перезагружать сервер.
|
|
||||||
- name: nbd_max_devices
|
|
||||||
type: int
|
|
||||||
default: 64
|
|
||||||
online: false
|
|
||||||
info: |
|
|
||||||
Maximum number of NBD devices in the system. This value is passed as
|
|
||||||
`nbds_max` parameter for the nbd kernel module when vitastor-nbd autoloads it.
|
|
||||||
info_ru: |
|
|
||||||
Максимальное число NBD-устройств в системе. Данное значение передаётся
|
|
||||||
модулю ядра nbd как параметр `nbds_max`, когда его загружает vitastor-nbd.
|
|
||||||
- name: nbd_max_part
|
|
||||||
type: int
|
|
||||||
default: 3
|
|
||||||
online: false
|
|
||||||
info: |
|
|
||||||
Maximum number of partitions per NBD device. This value is passed as
|
|
||||||
`max_part` parameter for the nbd kernel module when vitastor-nbd autoloads it.
|
|
||||||
Note that (nbds_max)*(1+max_part) usually can't exceed 256.
|
|
||||||
info_ru: |
|
|
||||||
Максимальное число разделов на одном NBD-устройстве. Данное значение передаётся
|
|
||||||
модулю ядра nbd как параметр `max_part`, когда его загружает vitastor-nbd.
|
|
||||||
Имейте в виду, что (nbds_max)*(1+max_part) обычно не может превышать 256.
|
|
|
@ -28,8 +28,6 @@
|
||||||
|
|
||||||
{{../../config/network.en.md|indent=2}}
|
{{../../config/network.en.md|indent=2}}
|
||||||
|
|
||||||
{{../../config/client.en.md|indent=2}}
|
|
||||||
|
|
||||||
{{../../config/layout-cluster.en.md|indent=2}}
|
{{../../config/layout-cluster.en.md|indent=2}}
|
||||||
|
|
||||||
{{../../config/layout-osd.en.md|indent=2}}
|
{{../../config/layout-osd.en.md|indent=2}}
|
||||||
|
|
|
@ -28,8 +28,6 @@
|
||||||
|
|
||||||
{{../../config/network.ru.md|indent=2}}
|
{{../../config/network.ru.md|indent=2}}
|
||||||
|
|
||||||
{{../../config/client.ru.md|indent=2}}
|
|
||||||
|
|
||||||
{{../../config/layout-cluster.ru.md|indent=2}}
|
{{../../config/layout-cluster.ru.md|indent=2}}
|
||||||
|
|
||||||
{{../../config/layout-osd.ru.md|indent=2}}
|
{{../../config/layout-osd.ru.md|indent=2}}
|
||||||
|
|
|
@ -38,7 +38,6 @@ const types = {
|
||||||
bool: 'boolean',
|
bool: 'boolean',
|
||||||
int: 'integer',
|
int: 'integer',
|
||||||
sec: 'seconds',
|
sec: 'seconds',
|
||||||
float: 'number',
|
|
||||||
ms: 'milliseconds',
|
ms: 'milliseconds',
|
||||||
us: 'microseconds',
|
us: 'microseconds',
|
||||||
},
|
},
|
||||||
|
@ -47,7 +46,6 @@ const types = {
|
||||||
bool: 'булево (да/нет)',
|
bool: 'булево (да/нет)',
|
||||||
int: 'целое число',
|
int: 'целое число',
|
||||||
sec: 'секунды',
|
sec: 'секунды',
|
||||||
float: 'число',
|
|
||||||
ms: 'миллисекунды',
|
ms: 'миллисекунды',
|
||||||
us: 'микросекунды',
|
us: 'микросекунды',
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
- name: etcd_mon_ttl
|
- name: etcd_mon_ttl
|
||||||
type: sec
|
type: sec
|
||||||
min: 5
|
min: 10
|
||||||
default: 1
|
default: 30
|
||||||
info: Monitor etcd lease refresh interval in seconds
|
info: Monitor etcd lease refresh interval in seconds
|
||||||
info_ru: Интервал обновления etcd резервации (lease) монитором
|
info_ru: Интервал обновления etcd резервации (lease) монитором
|
||||||
- name: etcd_mon_timeout
|
- name: etcd_mon_timeout
|
||||||
|
|
|
@ -48,14 +48,11 @@
|
||||||
type: string
|
type: string
|
||||||
info: |
|
info: |
|
||||||
RDMA device name to use for Vitastor OSD communications (for example,
|
RDMA device name to use for Vitastor OSD communications (for example,
|
||||||
"rocep5s0f0"). Now Vitastor supports all adapters, even ones without
|
"rocep5s0f0"). Please note that Vitastor RDMA requires Implicit On-Demand
|
||||||
ODP support, like Mellanox ConnectX-3 and non-Mellanox cards.
|
Paging (Implicit ODP) and Scatter/Gather (SG) support from the RDMA device
|
||||||
|
to work. For example, Mellanox ConnectX-3 and older adapters don't have
|
||||||
Versions up to Vitastor 1.2.0 required ODP which is only present in
|
Implicit ODP, so they're unsupported by Vitastor. Run `ibv_devinfo -v` as
|
||||||
Mellanox ConnectX >= 4. See also [rdma_odp](#rdma_odp).
|
root to list available RDMA devices and their features.
|
||||||
|
|
||||||
Run `ibv_devinfo -v` as root to list available RDMA devices and their
|
|
||||||
features.
|
|
||||||
|
|
||||||
Remember that you also have to configure your network switches if you use
|
Remember that you also have to configure your network switches if you use
|
||||||
RoCE/RoCEv2, otherwise you may experience unstable performance. Refer to
|
RoCE/RoCEv2, otherwise you may experience unstable performance. Refer to
|
||||||
|
@ -64,15 +61,12 @@
|
||||||
PFC (Priority Flow Control) and ECN (Explicit Congestion Notification).
|
PFC (Priority Flow Control) and ECN (Explicit Congestion Notification).
|
||||||
info_ru: |
|
info_ru: |
|
||||||
Название RDMA-устройства для связи с Vitastor OSD (например, "rocep5s0f0").
|
Название RDMA-устройства для связи с Vitastor OSD (например, "rocep5s0f0").
|
||||||
Сейчас Vitastor поддерживает все модели адаптеров, включая те, у которых
|
Имейте в виду, что поддержка RDMA в Vitastor требует функций устройства
|
||||||
нет поддержки ODP, то есть вы можете использовать RDMA с ConnectX-3 и
|
Implicit On-Demand Paging (Implicit ODP) и Scatter/Gather (SG). Например,
|
||||||
картами производства не Mellanox.
|
адаптеры Mellanox ConnectX-3 и более старые не поддерживают Implicit ODP и
|
||||||
|
потому не поддерживаются в Vitastor. Запустите `ibv_devinfo -v` от имени
|
||||||
Версии Vitastor до 1.2.0 включительно требовали ODP, который есть только
|
суперпользователя, чтобы посмотреть список доступных RDMA-устройств, их
|
||||||
на Mellanox ConnectX 4 и более новых. См. также [rdma_odp](#rdma_odp).
|
параметры и возможности.
|
||||||
|
|
||||||
Запустите `ibv_devinfo -v` от имени суперпользователя, чтобы посмотреть
|
|
||||||
список доступных RDMA-устройств, их параметры и возможности.
|
|
||||||
|
|
||||||
Обратите внимание, что если вы используете RoCE/RoCEv2, вам также необходимо
|
Обратите внимание, что если вы используете RoCE/RoCEv2, вам также необходимо
|
||||||
правильно настроить для него коммутаторы, иначе вы можете столкнуться с
|
правильно настроить для него коммутаторы, иначе вы можете столкнуться с
|
||||||
|
@ -166,45 +160,6 @@
|
||||||
у принимающей стороны в процессе работы не заканчивались буферы на приём.
|
у принимающей стороны в процессе работы не заканчивались буферы на приём.
|
||||||
Не влияет на потребление памяти - дополнительная память на операции отправки
|
Не влияет на потребление памяти - дополнительная память на операции отправки
|
||||||
не выделяется.
|
не выделяется.
|
||||||
- name: rdma_odp
|
|
||||||
type: bool
|
|
||||||
default: false
|
|
||||||
online: false
|
|
||||||
info: |
|
|
||||||
Use RDMA with On-Demand Paging. ODP is currently only available on Mellanox
|
|
||||||
ConnectX-4 and newer adapters. ODP allows to not register memory explicitly
|
|
||||||
for RDMA adapter to be able to use it. This, in turn, allows to skip memory
|
|
||||||
copying during sending. One would think this should improve performance, but
|
|
||||||
**in reality** RDMA performance with ODP is **drastically** worse. Example
|
|
||||||
3-node cluster with 8 NVMe in each node and 2*25 GBit/s ConnectX-6 RDMA network
|
|
||||||
without ODP pushes 3950000 read iops, but only 239000 iops with ODP...
|
|
||||||
|
|
||||||
This happens because Mellanox ODP implementation seems to be based on
|
|
||||||
message retransmissions when the adapter doesn't know about the buffer yet -
|
|
||||||
it likely uses standard "RNR retransmissions" (RNR = receiver not ready)
|
|
||||||
which is generally slow in RDMA/RoCE networks. Here's a presentation about
|
|
||||||
it from ISPASS-2021 conference: https://tkygtr6.github.io/pub/ISPASS21_slides.pdf
|
|
||||||
|
|
||||||
ODP support is retained in the code just in case a good ODP implementation
|
|
||||||
appears one day.
|
|
||||||
info_ru: |
|
|
||||||
Использовать RDMA с On-Demand Paging. ODP - функция, доступная пока что
|
|
||||||
исключительно на адаптерах Mellanox ConnectX-4 и более новых. ODP позволяет
|
|
||||||
не регистрировать память для её использования RDMA-картой. Благодаря этому
|
|
||||||
можно не копировать данные при отправке их в сеть и, казалось бы, это должно
|
|
||||||
улучшать производительность - но **по факту** получается так, что
|
|
||||||
производительность только ухудшается, причём сильно. Пример - на 3-узловом
|
|
||||||
кластере с 8 NVMe в каждом узле и сетью 2*25 Гбит/с на чтение с RDMA без ODP
|
|
||||||
удаётся снять 3950000 iops, а с ODP - всего 239000 iops...
|
|
||||||
|
|
||||||
Это происходит из-за того, что реализация ODP у Mellanox неоптимальная и
|
|
||||||
основана на повторной передаче сообщений, когда карте не известен буфер -
|
|
||||||
вероятно, на стандартных "RNR retransmission" (RNR = receiver not ready).
|
|
||||||
А данные повторные передачи в RDMA/RoCE - всегда очень медленная штука.
|
|
||||||
Презентация на эту тему с конференции ISPASS-2021: https://tkygtr6.github.io/pub/ISPASS21_slides.pdf
|
|
||||||
|
|
||||||
Возможность использования ODP сохранена в коде на случай, если вдруг в один
|
|
||||||
прекрасный день появится хорошая реализация ODP.
|
|
||||||
- name: peer_connect_interval
|
- name: peer_connect_interval
|
||||||
type: sec
|
type: sec
|
||||||
min: 1
|
min: 1
|
||||||
|
@ -245,8 +200,8 @@
|
||||||
повторная попытка соединения.
|
повторная попытка соединения.
|
||||||
- name: up_wait_retry_interval
|
- name: up_wait_retry_interval
|
||||||
type: ms
|
type: ms
|
||||||
min: 10
|
min: 50
|
||||||
default: 50
|
default: 500
|
||||||
online: true
|
online: true
|
||||||
info: |
|
info: |
|
||||||
OSDs respond to clients with a special error code when they receive I/O
|
OSDs respond to clients with a special error code when they receive I/O
|
||||||
|
@ -304,3 +259,23 @@
|
||||||
detect disconnections quickly.
|
detect disconnections quickly.
|
||||||
info_ru: |
|
info_ru: |
|
||||||
Интервал проверки живости вебсокет-подключений к etcd.
|
Интервал проверки живости вебсокет-подключений к etcd.
|
||||||
|
- name: client_dirty_limit
|
||||||
|
type: int
|
||||||
|
default: 33554432
|
||||||
|
online: true
|
||||||
|
info: |
|
||||||
|
Without immediate_commit=all this parameter sets the limit of "dirty"
|
||||||
|
(not committed by fsync) data allowed by the client before forcing an
|
||||||
|
additional fsync and committing the data. Also note that the client always
|
||||||
|
holds a copy of uncommitted data in memory so this setting also affects
|
||||||
|
RAM usage of clients.
|
||||||
|
|
||||||
|
This parameter doesn't affect OSDs themselves.
|
||||||
|
info_ru: |
|
||||||
|
При работе без immediate_commit=all - это лимит объёма "грязных" (не
|
||||||
|
зафиксированных fsync-ом) данных, при достижении которого клиент будет
|
||||||
|
принудительно вызывать fsync и фиксировать данные. Также стоит иметь в виду,
|
||||||
|
что в этом случае до момента fsync клиент хранит копию незафиксированных
|
||||||
|
данных в памяти, то есть, настройка влияет на потребление памяти клиентами.
|
||||||
|
|
||||||
|
Параметр не влияет на сами OSD.
|
||||||
|
|
|
@ -2,28 +2,15 @@
|
||||||
type: sec
|
type: sec
|
||||||
default: 5
|
default: 5
|
||||||
info: |
|
info: |
|
||||||
Interval at which OSDs report their liveness to etcd. Affects OSD lease time
|
Interval at which OSDs report their state to etcd. Affects OSD lease time
|
||||||
and thus the failover speed. Lease time is equal to this parameter value
|
and thus the failover speed. Lease time is equal to this parameter value
|
||||||
plus max_etcd_attempts * etcd_quick_timeout because it should be guaranteed
|
plus max_etcd_attempts * etcd_quick_timeout because it should be guaranteed
|
||||||
that every OSD always refreshes its lease in time.
|
that every OSD always refreshes its lease in time.
|
||||||
info_ru: |
|
info_ru: |
|
||||||
Интервал, с которым OSD сообщает о том, что жив, в etcd. Значение параметра
|
Интервал, с которым OSD обновляет своё состояние в etcd. Значение параметра
|
||||||
влияет на время резервации (lease) OSD и поэтому - на скорость переключения
|
влияет на время резервации (lease) OSD и поэтому на скорость переключения
|
||||||
при падении OSD. Время lease равняется значению этого параметра плюс
|
при падении OSD. Время lease равняется значению этого параметра плюс
|
||||||
max_etcd_attempts * etcd_quick_timeout.
|
max_etcd_attempts * etcd_quick_timeout.
|
||||||
- name: etcd_stats_interval
|
|
||||||
type: sec
|
|
||||||
default: 30
|
|
||||||
info: |
|
|
||||||
Interval at which OSDs report their statistics to etcd. Highly affects the
|
|
||||||
imposed load on etcd, because statistics include a key for every OSD and
|
|
||||||
for every PG. At the same time, low statistic intervals make `vitastor-cli`
|
|
||||||
statistics more responsive.
|
|
||||||
info_ru: |
|
|
||||||
Интервал, с которым OSD обновляет свою статистику в etcd. Сильно влияет на
|
|
||||||
создаваемую нагрузку на etcd, потому что статистика содержит по ключу на
|
|
||||||
каждый OSD и на каждую PG. В то же время низкий интервал делает
|
|
||||||
статистику, печатаемую `vitastor-cli`, отзывчивей.
|
|
||||||
- name: run_primary
|
- name: run_primary
|
||||||
type: bool
|
type: bool
|
||||||
default: true
|
default: true
|
||||||
|
@ -107,29 +94,17 @@
|
||||||
принудительной отправкой fsync-а.
|
принудительной отправкой fsync-а.
|
||||||
- name: recovery_queue_depth
|
- name: recovery_queue_depth
|
||||||
type: int
|
type: int
|
||||||
default: 1
|
default: 4
|
||||||
online: true
|
online: true
|
||||||
info: |
|
info: |
|
||||||
Maximum recovery and rebalance operations initiated by each OSD in parallel.
|
Maximum recovery operations per one primary OSD at any given moment of time.
|
||||||
Note that each OSD talks to a lot of other OSDs so actual number of parallel
|
Currently it's the only parameter available to tune the speed or recovery
|
||||||
recovery operations per each OSD is greater than just recovery_queue_depth.
|
and rebalancing, but it's planned to implement more.
|
||||||
Increasing this parameter can speedup recovery if [auto-tuning](#recovery_tune_interval)
|
|
||||||
allows it or if it is disabled.
|
|
||||||
info_ru: |
|
info_ru: |
|
||||||
Максимальное число параллельных операций восстановления, инициируемых одним
|
Максимальное число операций восстановления на одном первичном OSD в любой
|
||||||
OSD в любой момент времени. Имейте в виду, что каждый OSD обычно работает с
|
момент времени. На данный момент единственный параметр, который можно менять
|
||||||
многими другими OSD, так что на практике параллелизм восстановления больше,
|
для ускорения или замедления восстановления и перебалансировки данных, но
|
||||||
чем просто recovery_queue_depth. Увеличение значения этого параметра может
|
в планах реализация других параметров.
|
||||||
ускорить восстановление если [автотюнинг скорости](#recovery_tune_interval)
|
|
||||||
разрешает это или если он отключён.
|
|
||||||
- name: recovery_sleep_us
|
|
||||||
type: us
|
|
||||||
default: 0
|
|
||||||
online: true
|
|
||||||
info: |
|
|
||||||
Delay for all recovery- and rebalance- related operations. If non-zero,
|
|
||||||
such operations are artificially slowed down to reduce the impact on
|
|
||||||
client I/O.
|
|
||||||
- name: recovery_pg_switch
|
- name: recovery_pg_switch
|
||||||
type: int
|
type: int
|
||||||
default: 128
|
default: 128
|
||||||
|
@ -638,112 +613,3 @@
|
||||||
считается некорректной. Однако, если "лучшую" версию с числом доступных
|
считается некорректной. Однако, если "лучшую" версию с числом доступных
|
||||||
копий большим, чем у всех других версий, найти невозможно, то объект тоже
|
копий большим, чем у всех других версий, найти невозможно, то объект тоже
|
||||||
маркируется неконсистентным.
|
маркируется неконсистентным.
|
||||||
- name: recovery_tune_interval
|
|
||||||
type: sec
|
|
||||||
default: 1
|
|
||||||
online: true
|
|
||||||
info: |
|
|
||||||
Interval at which OSD re-considers client and recovery load and automatically
|
|
||||||
adjusts [recovery_sleep_us](#recovery_sleep_us). Recovery auto-tuning is
|
|
||||||
disabled if recovery_tune_interval is set to 0.
|
|
||||||
|
|
||||||
Auto-tuning targets utilization. Utilization is a measure of load and is
|
|
||||||
equal to the product of iops and average latency (so it may be greater
|
|
||||||
than 1). You set "low" and "high" client utilization thresholds and two
|
|
||||||
corresponding target recovery utilization levels. OSD calculates desired
|
|
||||||
recovery utilization from client utilization using linear interpolation
|
|
||||||
and auto-tunes recovery operation delay to make actual recovery utilization
|
|
||||||
match desired.
|
|
||||||
|
|
||||||
This allows to reduce recovery/rebalance impact on client operations. It is
|
|
||||||
of course impossible to remove it completely, but it should become adequate.
|
|
||||||
In some tests rebalance could earlier drop client write speed from 1.5 GB/s
|
|
||||||
to 50-100 MB/s, with default auto-tuning settings it now only reduces
|
|
||||||
to ~1 GB/s.
|
|
||||||
info_ru: |
|
|
||||||
Интервал, с которым OSD пересматривает клиентскую нагрузку и нагрузку
|
|
||||||
восстановления и автоматически подстраивает [recovery_sleep_us](#recovery_sleep_us).
|
|
||||||
Автотюнинг (автоподстройка) отключается, если recovery_tune_interval
|
|
||||||
устанавливается в значение 0.
|
|
||||||
|
|
||||||
Автотюнинг регулирует утилизацию. Утилизация является мерой нагрузки
|
|
||||||
и равна произведению числа операций в секунду и средней задержки
|
|
||||||
(то есть, она может быть выше 1). Вы задаёте два уровня клиентской
|
|
||||||
утилизации - "низкий" и "высокий" (low и high) и два соответствующих
|
|
||||||
целевых уровня утилизации операциями восстановления. OSD рассчитывает
|
|
||||||
желаемый уровень утилизации восстановления линейной интерполяцией от
|
|
||||||
клиентской утилизации и подстраивает задержку операций восстановления
|
|
||||||
так, чтобы фактическая утилизация восстановления совпадала с желаемой.
|
|
||||||
|
|
||||||
Это позволяет снизить влияние восстановления и ребаланса на клиентские
|
|
||||||
операции. Конечно, невозможно исключить такое влияние полностью, но оно
|
|
||||||
должно становиться адекватнее. В некоторых тестах перебалансировка могла
|
|
||||||
снижать клиентскую скорость записи с 1.5 ГБ/с до 50-100 МБ/с, а теперь, с
|
|
||||||
настройками автотюнинга по умолчанию, она снижается только до ~1 ГБ/с.
|
|
||||||
- name: recovery_tune_util_low
|
|
||||||
type: float
|
|
||||||
default: 0.1
|
|
||||||
online: true
|
|
||||||
info: |
|
|
||||||
Desired recovery/rebalance utilization when client load is high, i.e. when
|
|
||||||
it is at or above recovery_tune_client_util_high.
|
|
||||||
info_ru: |
|
|
||||||
Желаемая утилизация восстановления в моменты, когда клиентская нагрузка
|
|
||||||
высокая, то есть, находится на уровне или выше recovery_tune_client_util_high.
|
|
||||||
- name: recovery_tune_util_high
|
|
||||||
type: float
|
|
||||||
default: 1
|
|
||||||
online: true
|
|
||||||
info: |
|
|
||||||
Desired recovery/rebalance utilization when client load is low, i.e. when
|
|
||||||
it is at or below recovery_tune_client_util_low.
|
|
||||||
info_ru: |
|
|
||||||
Желаемая утилизация восстановления в моменты, когда клиентская нагрузка
|
|
||||||
низкая, то есть, находится на уровне или ниже recovery_tune_client_util_low.
|
|
||||||
- name: recovery_tune_client_util_low
|
|
||||||
type: float
|
|
||||||
default: 0
|
|
||||||
online: true
|
|
||||||
info: Client utilization considered "low".
|
|
||||||
info_ru: Клиентская утилизация, которая считается "низкой".
|
|
||||||
- name: recovery_tune_client_util_high
|
|
||||||
type: float
|
|
||||||
default: 0.5
|
|
||||||
online: true
|
|
||||||
info: Client utilization considered "high".
|
|
||||||
info_ru: Клиентская утилизация, которая считается "высокой".
|
|
||||||
- name: recovery_tune_agg_interval
|
|
||||||
type: int
|
|
||||||
default: 10
|
|
||||||
online: true
|
|
||||||
info: |
|
|
||||||
The number of last auto-tuning iterations to use for calculating the
|
|
||||||
delay as average. Lower values result in quicker response to client
|
|
||||||
load change, higher values result in more stable delay. Default value of 10
|
|
||||||
is usually fine.
|
|
||||||
info_ru: |
|
|
||||||
Число последних итераций автоподстройки для расчёта задержки как среднего
|
|
||||||
значения. Меньшие значения параметра ускоряют отклик на изменение нагрузки,
|
|
||||||
большие значения делают задержку стабильнее. Значение по умолчанию 10
|
|
||||||
обычно нормальное и не требует изменений.
|
|
||||||
- name: recovery_tune_sleep_min_us
|
|
||||||
type: us
|
|
||||||
default: 10
|
|
||||||
online: true
|
|
||||||
info: |
|
|
||||||
Minimum possible value for auto-tuned recovery_sleep_us. Lower values
|
|
||||||
are changed to 0.
|
|
||||||
info_ru: |
|
|
||||||
Минимальное возможное значение авто-подстроенного recovery_sleep_us.
|
|
||||||
Меньшие значения заменяются на 0.
|
|
||||||
- name: recovery_tune_sleep_cutoff_us
|
|
||||||
type: us
|
|
||||||
default: 10000000
|
|
||||||
online: true
|
|
||||||
info: |
|
|
||||||
Maximum possible value for auto-tuned recovery_sleep_us. Higher values
|
|
||||||
are treated as outliers and ignored in aggregation.
|
|
||||||
info_ru: |
|
|
||||||
Максимальное возможное значение авто-подстроенного recovery_sleep_us.
|
|
||||||
Большие значения считаются случайными выбросами и игнорируются в
|
|
||||||
усреднении.
|
|
||||||
|
|
|
@ -17,27 +17,4 @@ and apply all `NNN-*.yaml` manifests to your Kubernetes installation:
|
||||||
for i in ./???-*.yaml; do kubectl apply -f $i; done
|
for i in ./???-*.yaml; do kubectl apply -f $i; done
|
||||||
```
|
```
|
||||||
|
|
||||||
After that you'll be able to create PersistentVolumes.
|
After that you'll be able to create PersistentVolumes. See example in [csi/deploy/example-pvc.yaml](../../csi/deploy/example-pvc.yaml).
|
||||||
|
|
||||||
**Important:** For best experience, use Linux kernel at least 5.15 with [VDUSE](../usage/qemu.en.md#vduse)
|
|
||||||
kernel modules enabled (vdpa, vduse, virtio-vdpa). If your distribution doesn't
|
|
||||||
have them pre-built - build them yourself ([instructions](../usage/qemu.en.md#vduse)),
|
|
||||||
I promise it's worth it :-). When VDUSE is unavailable, CSI driver uses [NBD](../usage/nbd.en.md)
|
|
||||||
to map Vitastor devices. NBD is slower and prone to timeout issues: if Vitastor
|
|
||||||
cluster becomes unresponsible for more than [nbd_timeout](../config/client.en.md#nbd_timeout),
|
|
||||||
the NBD device detaches and breaks pods using it.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
Vitastor CSI supports:
|
|
||||||
- Kubernetes starting with 1.20 (or 1.17 for older vitastor-csi <= 1.1.0)
|
|
||||||
- Filesystem RWO (ReadWriteOnce) volumes. Example: [PVC](../../csi/deploy/example-pvc.yaml), [pod](../../csi/deploy/example-test-pod.yaml)
|
|
||||||
- Raw block RWX (ReadWriteMany) volumes. Example: [PVC](../../csi/deploy/example-pvc-block.yaml), [pod](../../csi/deploy/example-test-pod-block.yaml)
|
|
||||||
- Volume expansion
|
|
||||||
- Volume snapshots. Example: [snapshot class](../../csi/deploy/example-snapshot-class.yaml), [snapshot](../../csi/deploy/example-snapshot.yaml), [clone](../../csi/deploy/example-snapshot-clone.yaml)
|
|
||||||
- [VDUSE](../usage/qemu.en.md#vduse) (preferred) and [NBD](../usage/nbd.en.md) device mapping methods
|
|
||||||
- Upgrades with VDUSE - new handler processes are restarted when CSI pods are restarted themselves
|
|
||||||
- VDUSE daemon auto-restart - handler processes are automatically restarted if they crash due to a bug in Vitastor client code
|
|
||||||
- Multiple clusters by using multiple configuration files in ConfigMap.
|
|
||||||
|
|
||||||
Remember that to use snapshots with CSI you also have to install [Snapshot Controller and CRDs](https://kubernetes-csi.github.io/docs/snapshot-controller.html#deployment).
|
|
||||||
|
|
|
@ -17,27 +17,4 @@
|
||||||
for i in ./???-*.yaml; do kubectl apply -f $i; done
|
for i in ./???-*.yaml; do kubectl apply -f $i; done
|
||||||
```
|
```
|
||||||
|
|
||||||
После этого вы сможете создавать PersistentVolume.
|
После этого вы сможете создавать PersistentVolume. Пример смотрите в файле [csi/deploy/example-pvc.yaml](../../csi/deploy/example-pvc.yaml).
|
||||||
|
|
||||||
**Важно:** Лучше всего использовать ядро Linux версии не менее 5.15 с включёнными модулями
|
|
||||||
[VDUSE](../usage/qemu.ru.md#vduse) (vdpa, vduse, virtio-vdpa). Если в вашем дистрибутиве
|
|
||||||
они не собраны из коробки - соберите их сами, обещаю, что это стоит того ([инструкция](../usage/qemu.ru.md#vduse)) :-).
|
|
||||||
Когда VDUSE недоступно, CSI-плагин использует [NBD](../usage/nbd.ru.md) для подключения
|
|
||||||
дисков, а NBD медленнее и имеет проблему таймаута - если кластер остаётся недоступным
|
|
||||||
дольше, чем [nbd_timeout](../config/client.ru.md#nbd_timeout), NBD-устройство отключается
|
|
||||||
и ломает поды, использующие его.
|
|
||||||
|
|
||||||
## Возможности
|
|
||||||
|
|
||||||
CSI-плагин Vitastor поддерживает:
|
|
||||||
- Версии Kubernetes, начиная с 1.20 (или с 1.17 для более старых vitastor-csi <= 1.1.0)
|
|
||||||
- Файловые RWO (ReadWriteOnce) тома. Пример: [PVC](../../csi/deploy/example-pvc.yaml), [под](../../csi/deploy/example-test-pod.yaml)
|
|
||||||
- Сырые блочные RWX (ReadWriteMany) тома. Пример: [PVC](../../csi/deploy/example-pvc-block.yaml), [под](../../csi/deploy/example-test-pod-block.yaml)
|
|
||||||
- Расширение размера томов
|
|
||||||
- Снимки томов. Пример: [класс снимков](../../csi/deploy/example-snapshot-class.yaml), [снимок](../../csi/deploy/example-snapshot.yaml), [клон снимка](../../csi/deploy/example-snapshot-clone.yaml)
|
|
||||||
- Способы подключения устройств [VDUSE](../usage/qemu.ru.md#vduse) (предпочитаемый) и [NBD](../usage/nbd.ru.md)
|
|
||||||
- Обновление при использовании VDUSE - новые процессы-обработчики устройств успешно перезапускаются вместе с самими подами CSI
|
|
||||||
- Автоперезауск демонов VDUSE - процесс-обработчик автоматически перезапустится, если он внезапно упадёт из-за бага в коде клиента Vitastor
|
|
||||||
- Несколько кластеров через задание нескольких файлов конфигурации в ConfigMap.
|
|
||||||
|
|
||||||
Не забывайте, что для использования снимков нужно сначала установить [контроллер снимков и CRD](https://kubernetes-csi.github.io/docs/snapshot-controller.html#deployment).
|
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
stable version from 0.9.x branch instead of 1.x
|
stable version from 0.9.x branch instead of 1.x
|
||||||
- For Debian 10 (Buster) also enable backports repository:
|
- For Debian 10 (Buster) also enable backports repository:
|
||||||
`deb http://deb.debian.org/debian buster-backports main`
|
`deb http://deb.debian.org/debian buster-backports main`
|
||||||
- Install packages: `apt update; apt install vitastor lp-solve etcd linux-image-amd64 qemu-system-x86`
|
- Install packages: `apt update; apt install vitastor lp-solve etcd linux-image-amd64 qemu`
|
||||||
|
|
||||||
## CentOS
|
## CentOS
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
установить последнюю стабильную версию из ветки 0.9.x вместо 1.x
|
установить последнюю стабильную версию из ветки 0.9.x вместо 1.x
|
||||||
- Для Debian 10 (Buster) также включите репозиторий backports:
|
- Для Debian 10 (Buster) также включите репозиторий backports:
|
||||||
`deb http://deb.debian.org/debian buster-backports main`
|
`deb http://deb.debian.org/debian buster-backports main`
|
||||||
- Установите пакеты: `apt update; apt install vitastor lp-solve etcd linux-image-amd64 qemu-system-x86`
|
- Установите пакеты: `apt update; apt install vitastor lp-solve etcd linux-image-amd64 qemu`
|
||||||
|
|
||||||
## CentOS
|
## CentOS
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,10 @@
|
||||||
|
|
||||||
# Proxmox VE
|
# Proxmox VE
|
||||||
|
|
||||||
To enable Vitastor support in Proxmox Virtual Environment (6.4-8.1 are supported):
|
To enable Vitastor support in Proxmox Virtual Environment (6.4-8.0 are supported):
|
||||||
|
|
||||||
- Add the corresponding Vitastor Debian repository into sources.list on Proxmox hosts:
|
- Add the corresponding Vitastor Debian repository into sources.list on Proxmox hosts:
|
||||||
bookworm for 8.1, pve8.0 for 8.0, bullseye for 7.4, pve7.3 for 7.3, pve7.2 for 7.2, pve7.1 for 7.1, buster for 6.4
|
bookworm for 8.0, bullseye for 7.4, pve7.3 for 7.3, pve7.2 for 7.2, pve7.1 for 7.1, buster for 6.4
|
||||||
- Install vitastor-client, pve-qemu-kvm, pve-storage-vitastor (* or see note) packages from Vitastor repository
|
- Install vitastor-client, pve-qemu-kvm, pve-storage-vitastor (* or see note) packages from Vitastor repository
|
||||||
- Define storage in `/etc/pve/storage.cfg` (see below)
|
- Define storage in `/etc/pve/storage.cfg` (see below)
|
||||||
- Block network access from VMs to Vitastor network (to OSDs and etcd),
|
- Block network access from VMs to Vitastor network (to OSDs and etcd),
|
||||||
|
@ -25,7 +25,7 @@ vitastor: vitastor
|
||||||
vitastor_pool testpool
|
vitastor_pool testpool
|
||||||
# path to the configuration file
|
# path to the configuration file
|
||||||
vitastor_config_path /etc/vitastor/vitastor.conf
|
vitastor_config_path /etc/vitastor/vitastor.conf
|
||||||
# etcd address(es), OPTIONAL, required only if missing in the configuration file
|
# etcd address(es), required only if missing in the configuration file
|
||||||
vitastor_etcd_address 192.168.7.2:2379/v3
|
vitastor_etcd_address 192.168.7.2:2379/v3
|
||||||
# prefix for keys in etcd
|
# prefix for keys in etcd
|
||||||
vitastor_etcd_prefix /vitastor
|
vitastor_etcd_prefix /vitastor
|
||||||
|
|
|
@ -6,10 +6,10 @@
|
||||||
|
|
||||||
# Proxmox VE
|
# Proxmox VE
|
||||||
|
|
||||||
Чтобы подключить Vitastor к Proxmox Virtual Environment (поддерживаются версии 6.4-8.1):
|
Чтобы подключить Vitastor к Proxmox Virtual Environment (поддерживаются версии 6.4-8.0):
|
||||||
|
|
||||||
- Добавьте соответствующий Debian-репозиторий Vitastor в sources.list на хостах Proxmox:
|
- Добавьте соответствующий Debian-репозиторий Vitastor в sources.list на хостах Proxmox:
|
||||||
bookworm для 8.1, pve8.0 для 8.0, bullseye для 7.4, pve7.3 для 7.3, pve7.2 для 7.2, pve7.1 для 7.1, buster для 6.4
|
bookworm для 8.0, bullseye для 7.4, pve7.3 для 7.3, pve7.2 для 7.2, pve7.1 для 7.1, buster для 6.4
|
||||||
- Установите пакеты vitastor-client, pve-qemu-kvm, pve-storage-vitastor (* или см. сноску) из репозитория Vitastor
|
- Установите пакеты vitastor-client, pve-qemu-kvm, pve-storage-vitastor (* или см. сноску) из репозитория Vitastor
|
||||||
- Определите тип хранилища в `/etc/pve/storage.cfg` (см. ниже)
|
- Определите тип хранилища в `/etc/pve/storage.cfg` (см. ниже)
|
||||||
- Обязательно заблокируйте доступ от виртуальных машин к сети Vitastor (OSD и etcd), т.к. Vitastor (пока) не поддерживает аутентификацию
|
- Обязательно заблокируйте доступ от виртуальных машин к сети Vitastor (OSD и etcd), т.к. Vitastor (пока) не поддерживает аутентификацию
|
||||||
|
@ -24,7 +24,7 @@ vitastor: vitastor
|
||||||
vitastor_pool testpool
|
vitastor_pool testpool
|
||||||
# Путь к файлу конфигурации
|
# Путь к файлу конфигурации
|
||||||
vitastor_config_path /etc/vitastor/vitastor.conf
|
vitastor_config_path /etc/vitastor/vitastor.conf
|
||||||
# Адрес(а) etcd, ОПЦИОНАЛЬНЫ, нужны, только если не указаны в vitastor.conf
|
# Адрес(а) etcd, нужны, только если не указаны в vitastor.conf
|
||||||
vitastor_etcd_address 192.168.7.2:2379/v3
|
vitastor_etcd_address 192.168.7.2:2379/v3
|
||||||
# Префикс ключей метаданных в etcd
|
# Префикс ключей метаданных в etcd
|
||||||
vitastor_etcd_prefix /vitastor
|
vitastor_etcd_prefix /vitastor
|
||||||
|
|
|
@ -54,8 +54,7 @@
|
||||||
виртуальные диски, их снимки и клоны.
|
виртуальные диски, их снимки и клоны.
|
||||||
- **Драйвер QEMU** — подключаемый модуль QEMU, позволяющий QEMU/KVM виртуальным машинам работать
|
- **Драйвер QEMU** — подключаемый модуль QEMU, позволяющий QEMU/KVM виртуальным машинам работать
|
||||||
с виртуальными дисками Vitastor напрямую из пространства пользователя с помощью клиентской
|
с виртуальными дисками Vitastor напрямую из пространства пользователя с помощью клиентской
|
||||||
библиотеки, без необходимости отображения дисков в виде блочных устройств. Тот же драйвер
|
библиотеки, без необходимости отображения дисков в виде блочных устройств.
|
||||||
позволяет подключать диски в систему через [VDUSE](../usage/qemu.ru.md#vduse).
|
|
||||||
- **vitastor-nbd** — утилита, позволяющая монтировать образы Vitastor в виде блочных устройств
|
- **vitastor-nbd** — утилита, позволяющая монтировать образы Vitastor в виде блочных устройств
|
||||||
с помощью NBD (Network Block Device), на самом деле скорее работающего как "BUSE"
|
с помощью NBD (Network Block Device), на самом деле скорее работающего как "BUSE"
|
||||||
(Block Device In Userspace). Модуля ядра Linux для выполнения той же задачи в Vitastor нет
|
(Block Device In Userspace). Модуля ядра Linux для выполнения той же задачи в Vitastor нет
|
||||||
|
|
|
@ -31,8 +31,6 @@
|
||||||
- [RDMA/RoCEv2 support via libibverbs](../config/network.en.md#rdma_device)
|
- [RDMA/RoCEv2 support via libibverbs](../config/network.en.md#rdma_device)
|
||||||
- [Scrubbing](../config/osd.en.md#auto_scrub) (verification of copies)
|
- [Scrubbing](../config/osd.en.md#auto_scrub) (verification of copies)
|
||||||
- [Checksums](../config/layout-osd.en.md#data_csum_type)
|
- [Checksums](../config/layout-osd.en.md#data_csum_type)
|
||||||
- [Client write-back cache](../config/client.en.md#client_enable_writeback)
|
|
||||||
- [Intelligent recovery auto-tuning](../config/osd.en.md#recovery_tune_interval)
|
|
||||||
|
|
||||||
## Plugins and tools
|
## Plugins and tools
|
||||||
|
|
||||||
|
@ -52,15 +50,13 @@
|
||||||
|
|
||||||
The following features are planned for the future:
|
The following features are planned for the future:
|
||||||
|
|
||||||
- File system
|
|
||||||
- Control plane optimisation
|
|
||||||
- Other administrative tools
|
- Other administrative tools
|
||||||
- Web GUI
|
- Web GUI
|
||||||
- OpenNebula plugin
|
- OpenNebula plugin
|
||||||
- iSCSI and NVMeoF gateways
|
- iSCSI proxy
|
||||||
- Multi-threaded client
|
- Multi-threaded client
|
||||||
- Faster failover
|
- Faster failover
|
||||||
- S3
|
|
||||||
- Tiered storage (SSD caching)
|
- Tiered storage (SSD caching)
|
||||||
- NVDIMM support
|
- NVDIMM support
|
||||||
- Compression (possibly)
|
- Compression (possibly)
|
||||||
|
- Read caching using system page cache (possibly)
|
||||||
|
|
|
@ -33,8 +33,6 @@
|
||||||
- [Поддержка RDMA/RoCEv2 через libibverbs](../config/network.ru.md#rdma_device)
|
- [Поддержка RDMA/RoCEv2 через libibverbs](../config/network.ru.md#rdma_device)
|
||||||
- [Фоновая проверка целостности](../config/osd.ru.md#auto_scrub) (сверка копий)
|
- [Фоновая проверка целостности](../config/osd.ru.md#auto_scrub) (сверка копий)
|
||||||
- [Контрольные суммы](../config/layout-osd.ru.md#data_csum_type)
|
- [Контрольные суммы](../config/layout-osd.ru.md#data_csum_type)
|
||||||
- [Буферизация записи на стороне клиента](../config/client.ru.md#client_enable_writeback)
|
|
||||||
- [Интеллектуальная автоподстройка скорости восстановления](../config/osd.ru.md#recovery_tune_interval)
|
|
||||||
|
|
||||||
## Драйверы и инструменты
|
## Драйверы и инструменты
|
||||||
|
|
||||||
|
@ -52,15 +50,12 @@
|
||||||
|
|
||||||
## Планы развития
|
## Планы развития
|
||||||
|
|
||||||
- Файловая система
|
|
||||||
- Оптимизация слоя управления
|
|
||||||
- Другие инструменты администрирования
|
- Другие инструменты администрирования
|
||||||
- Web-интерфейс
|
- Web-интерфейс
|
||||||
- Плагин для OpenNebula
|
- Плагин для OpenNebula
|
||||||
- iSCSI и NVMeoF прокси
|
- iSCSI-прокси
|
||||||
- Многопоточный клиент
|
- Многопоточный клиент
|
||||||
- Более быстрое переключение при отказах
|
- Более быстрое переключение при отказах
|
||||||
- S3
|
|
||||||
- Поддержка SSD-кэширования (tiered storage)
|
- Поддержка SSD-кэширования (tiered storage)
|
||||||
- Поддержка NVDIMM
|
- Поддержка NVDIMM
|
||||||
- Возможно, сжатие
|
- Возможно, сжатие
|
||||||
|
|
|
@ -11,26 +11,19 @@ Replicated setups:
|
||||||
- Single-threaded write+fsync latency:
|
- Single-threaded write+fsync latency:
|
||||||
- With immediate commit: 2 network roundtrips + 1 disk write.
|
- With immediate commit: 2 network roundtrips + 1 disk write.
|
||||||
- With lazy commit: 4 network roundtrips + 1 disk write + 1 disk flush.
|
- With lazy commit: 4 network roundtrips + 1 disk write + 1 disk flush.
|
||||||
- Linear read: `min(total network bandwidth, sum(disk read MB/s))`.
|
- Saturated parallel read iops: min(network bandwidth, sum(disk read iops)).
|
||||||
- Linear write: `min(total network bandwidth, sum(disk write MB/s / number of replicas))`.
|
- Saturated parallel write iops: min(network bandwidth, sum(disk write iops / number of replicas / write amplification)).
|
||||||
- Saturated parallel read iops: `min(total network bandwidth, sum(disk read iops))`.
|
|
||||||
- Saturated parallel write iops: `min(total network bandwidth / number of replicas, sum(disk write iops / number of replicas / (write amplification = 4)))`.
|
|
||||||
|
|
||||||
EC/XOR setups (EC N+K):
|
EC/XOR setups:
|
||||||
- Single-threaded (T1Q1) read latency: 1.5 network roundtrips + 1 disk read.
|
- Single-threaded (T1Q1) read latency: 1.5 network roundtrips + 1 disk read.
|
||||||
- Single-threaded write+fsync latency:
|
- Single-threaded write+fsync latency:
|
||||||
- With immediate commit: 3.5 network roundtrips + 1 disk read + 2 disk writes.
|
- With immediate commit: 3.5 network roundtrips + 1 disk read + 2 disk writes.
|
||||||
- With lazy commit: 5.5 network roundtrips + 1 disk read + 2 disk writes + 2 disk fsyncs.
|
- With lazy commit: 5.5 network roundtrips + 1 disk read + 2 disk writes + 2 disk fsyncs.
|
||||||
- 0.5 in actually `(N-1)/N` which means that an additional roundtrip doesn't happen when
|
- 0.5 in actually (k-1)/k which means that an additional roundtrip doesn't happen when
|
||||||
the read sub-operation can be served locally.
|
the read sub-operation can be served locally.
|
||||||
- Linear read: `min(total network bandwidth, sum(disk read MB/s))`.
|
- Saturated parallel read iops: min(network bandwidth, sum(disk read iops)).
|
||||||
- Linear write: `min(total network bandwidth, sum(disk write MB/s * N/(N+K)))`.
|
- Saturated parallel write iops: min(network bandwidth, sum(disk write iops * number of data drives / (number of data + parity drives) / write amplification)).
|
||||||
- Saturated parallel read iops: `min(total network bandwidth, sum(disk read iops))`.
|
In fact, you should put disk write iops under the condition of ~10% reads / ~90% writes in this formula.
|
||||||
- Saturated parallel write iops: roughly `total iops / (N+K) / WA`. More exactly,
|
|
||||||
`min(total network bandwidth * N/(N+K), sum(disk randrw iops / (N*4 + K*5 + 1)))` with
|
|
||||||
random read/write mix corresponding to `(N-1)/(N*4 + K*5 + 1)*100 % reads`.
|
|
||||||
- For example, with EC 2+1 it is: `(7% randrw iops) / 14`.
|
|
||||||
- With EC 6+3 it is: `(12.5% randrw iops) / 40`.
|
|
||||||
|
|
||||||
Write amplification for 4 KB blocks is usually 3-5 in Vitastor:
|
Write amplification for 4 KB blocks is usually 3-5 in Vitastor:
|
||||||
1. Journal block write
|
1. Journal block write
|
||||||
|
|
|
@ -11,27 +11,20 @@
|
||||||
- Запись+fsync в 1 поток:
|
- Запись+fsync в 1 поток:
|
||||||
- С мгновенным сбросом: 2 RTT + 1 запись.
|
- С мгновенным сбросом: 2 RTT + 1 запись.
|
||||||
- С отложенным ("ленивым") сбросом: 4 RTT + 1 запись + 1 fsync.
|
- С отложенным ("ленивым") сбросом: 4 RTT + 1 запись + 1 fsync.
|
||||||
- Линейное чтение: сумма МБ/с чтения всех дисков, либо общая производительность сети (сумма пропускной способности сети всех нод), если в сеть упрётся раньше.
|
- Параллельное чтение: сумма IOPS всех дисков либо производительность сети, если в сеть упрётся раньше.
|
||||||
- Линейная запись: сумма МБ/с записи всех дисков / число реплик, либо производительность сети / число реплик, если в сеть упрётся раньше.
|
- Параллельная запись: сумма IOPS всех дисков / число реплик / WA либо производительность сети, если в сеть упрётся раньше.
|
||||||
- Параллельное случайное мелкое чтение: сумма IOPS чтения всех дисков, либо производительность сети, если в сеть упрётся раньше.
|
|
||||||
- Параллельная случайная мелкая запись: сумма IOPS записи всех дисков / число реплик / WA, либо производительность сети / число реплик, если в сеть упрётся раньше.
|
|
||||||
|
|
||||||
При использовании кодов коррекции ошибок (EC N+K):
|
При использовании кодов коррекции ошибок (EC):
|
||||||
- Задержка чтения в 1 поток (T1Q1): 1.5 RTT + 1 чтение.
|
- Задержка чтения в 1 поток (T1Q1): 1.5 RTT + 1 чтение.
|
||||||
- Запись+fsync в 1 поток:
|
- Запись+fsync в 1 поток:
|
||||||
- С мгновенным сбросом: 3.5 RTT + 1 чтение + 2 записи.
|
- С мгновенным сбросом: 3.5 RTT + 1 чтение + 2 записи.
|
||||||
- С отложенным ("ленивым") сбросом: 5.5 RTT + 1 чтение + 2 записи + 2 fsync.
|
- С отложенным ("ленивым") сбросом: 5.5 RTT + 1 чтение + 2 записи + 2 fsync.
|
||||||
- Под 0.5 на самом деле подразумевается (N-1)/N, где N - число дисков данных,
|
- Под 0.5 на самом деле подразумевается (k-1)/k, где k - число дисков данных,
|
||||||
что означает, что дополнительное обращение по сети не нужно, когда операция
|
что означает, что дополнительное обращение по сети не нужно, когда операция
|
||||||
чтения обслуживается локально.
|
чтения обслуживается локально.
|
||||||
- Линейное чтение: сумма МБ/с чтения всех дисков, либо общая производительность сети, если в сеть упрётся раньше.
|
- Параллельное чтение: сумма IOPS всех дисков либо производительность сети, если в сеть упрётся раньше.
|
||||||
- Линейная запись: сумма МБ/с записи всех дисков * N/(N+K), либо производительность сети * N / (N+K), если в сеть упрётся раньше.
|
- Параллельная запись: сумма IOPS всех дисков / общее число дисков данных и чётности / WA либо производительность сети, если в сеть упрётся раньше.
|
||||||
- Параллельное случайное мелкое чтение: сумма IOPS чтения всех дисков либо производительность сети, если в сеть упрётся раньше.
|
Примечание: IOPS дисков в данном случае надо брать в смешанном режиме чтения/записи в пропорции, аналогичной формулам выше.
|
||||||
- Параллельная случайная мелкая запись: грубо `(сумма IOPS / (N+K) / WA)`. Если точнее, то:
|
|
||||||
сумма смешанного IOPS всех дисков при `(N-1)/(N*4 + K*5 + 1)*100 %` чтения, делённая на `(N*4 + K*5 + 1)`.
|
|
||||||
Либо, производительность сети * N/(N+K), если в сеть упрётся раньше.
|
|
||||||
- Например, при EC 2+1 это: `(сумма IOPS при 7% чтения) / 14`.
|
|
||||||
- При EC 6+3 это: `(сумма IOPS при 12.5% чтения) / 40`.
|
|
||||||
|
|
||||||
WA (мультипликатор записи) для 4 КБ блоков в Vitastor обычно составляет 3-5:
|
WA (мультипликатор записи) для 4 КБ блоков в Vitastor обычно составляет 3-5:
|
||||||
1. Запись метаданных в журнал
|
1. Запись метаданных в журнал
|
||||||
|
|
|
@ -28,8 +28,7 @@ It supports the following commands:
|
||||||
Global options:
|
Global options:
|
||||||
|
|
||||||
```
|
```
|
||||||
--config_file FILE Path to Vitastor configuration file
|
--etcd_address ADDR Etcd connection address
|
||||||
--etcd_address URL Etcd connection address
|
|
||||||
--iodepth N Send N operations in parallel to each OSD when possible (default 32)
|
--iodepth N Send N operations in parallel to each OSD when possible (default 32)
|
||||||
--parallel_osds M Work with M osds in parallel when possible (default 4)
|
--parallel_osds M Work with M osds in parallel when possible (default 4)
|
||||||
--progress 1|0 Report progress (default 1)
|
--progress 1|0 Report progress (default 1)
|
||||||
|
|
|
@ -27,8 +27,7 @@ vitastor-cli - интерфейс командной строки для адм
|
||||||
Глобальные опции:
|
Глобальные опции:
|
||||||
|
|
||||||
```
|
```
|
||||||
--config_file FILE Путь к файлу конфигурации Vitastor
|
--etcd_address ADDR Адрес соединения с etcd
|
||||||
--etcd_address URL Адрес соединения с etcd
|
|
||||||
--iodepth N Отправлять параллельно N операций на каждый OSD (по умолчанию 32)
|
--iodepth N Отправлять параллельно N операций на каждый OSD (по умолчанию 32)
|
||||||
--parallel_osds M Работать параллельно с M OSD (по умолчанию 4)
|
--parallel_osds M Работать параллельно с M OSD (по умолчанию 4)
|
||||||
--progress 1|0 Печатать прогресс выполнения (по умолчанию 1)
|
--progress 1|0 Печатать прогресс выполнения (по умолчанию 1)
|
||||||
|
|
|
@ -17,7 +17,6 @@ It supports the following commands:
|
||||||
- [purge](#purge)
|
- [purge](#purge)
|
||||||
- [read-sb](#read-sb)
|
- [read-sb](#read-sb)
|
||||||
- [write-sb](#write-sb)
|
- [write-sb](#write-sb)
|
||||||
- [update-sb](#update-sb)
|
|
||||||
- [udev](#udev)
|
- [udev](#udev)
|
||||||
- [exec-osd](#exec-osd)
|
- [exec-osd](#exec-osd)
|
||||||
- [pre-exec](#pre-exec)
|
- [pre-exec](#pre-exec)
|
||||||
|
@ -183,14 +182,6 @@ Try to read Vitastor OSD superblock from `<device>` and print it in JSON format.
|
||||||
|
|
||||||
Read JSON from STDIN and write it into Vitastor OSD superblock on `<device>`.
|
Read JSON from STDIN and write it into Vitastor OSD superblock on `<device>`.
|
||||||
|
|
||||||
## update-sb
|
|
||||||
|
|
||||||
`vitastor-disk update-sb <device> [--force] [--<parameter> <value>] [...]`
|
|
||||||
|
|
||||||
Read Vitastor OSD superblock from <device>, update parameters in it and write it back.
|
|
||||||
|
|
||||||
`--force` allows to ignore validation errors.
|
|
||||||
|
|
||||||
## udev
|
## udev
|
||||||
|
|
||||||
`vitastor-disk udev <device>`
|
`vitastor-disk udev <device>`
|
||||||
|
|
|
@ -17,7 +17,6 @@ vitastor-disk - инструмент командной строки для уп
|
||||||
- [purge](#purge)
|
- [purge](#purge)
|
||||||
- [read-sb](#read-sb)
|
- [read-sb](#read-sb)
|
||||||
- [write-sb](#write-sb)
|
- [write-sb](#write-sb)
|
||||||
- [update-sb](#update-sb)
|
|
||||||
- [udev](#udev)
|
- [udev](#udev)
|
||||||
- [exec-osd](#exec-osd)
|
- [exec-osd](#exec-osd)
|
||||||
- [pre-exec](#pre-exec)
|
- [pre-exec](#pre-exec)
|
||||||
|
@ -188,15 +187,6 @@ throttle_target_mbs, throttle_target_parallelism, throttle_threshold_us.
|
||||||
|
|
||||||
Прочитать JSON со стандартного ввода и записать его в суперблок OSD на диск `<device>`.
|
Прочитать JSON со стандартного ввода и записать его в суперблок OSD на диск `<device>`.
|
||||||
|
|
||||||
## update-sb
|
|
||||||
|
|
||||||
`vitastor-disk update-sb <device> [--force] [--<параметр> <значение>] [...]`
|
|
||||||
|
|
||||||
Прочитать суперблок OSD с диска `<device>`, изменить в нём заданные параметры и записать обратно.
|
|
||||||
|
|
||||||
Опция `--force` позволяет читать суперблок, даже если он считается некорректным
|
|
||||||
из-за ошибок валидации.
|
|
||||||
|
|
||||||
## udev
|
## udev
|
||||||
|
|
||||||
`vitastor-disk udev <device>`
|
`vitastor-disk udev <device>`
|
||||||
|
|
|
@ -14,13 +14,10 @@ Vitastor has a fio driver which can be installed from the package vitastor-fio.
|
||||||
Use the following command as an example to run tests with fio against a Vitastor cluster:
|
Use the following command as an example to run tests with fio against a Vitastor cluster:
|
||||||
|
|
||||||
```
|
```
|
||||||
fio -thread -ioengine=libfio_vitastor.so -name=test -bs=4M -direct=1 -iodepth=16 -rw=write -image=testimg
|
fio -thread -ioengine=libfio_vitastor.so -name=test -bs=4M -direct=1 -iodepth=16 -rw=write -etcd=10.115.0.10:2379/v3 -image=testimg
|
||||||
```
|
```
|
||||||
|
|
||||||
If you don't want to access your image by name, you can specify pool number, inode number and size
|
If you don't want to access your image by name, you can specify pool number, inode number and size
|
||||||
(`-pool=1 -inode=1 -size=400G`) instead of the image name (`-image=testimg`).
|
(`-pool=1 -inode=1 -size=400G`) instead of the image name (`-image=testimg`).
|
||||||
|
|
||||||
You can also specify etcd address(es) explicitly by adding `-etcd=10.115.0.10:2379/v3`, or you
|
See exact fio commands to use for benchmarking [here](../performance/understanding.en.md#команды-fio).
|
||||||
can override configuration file path by adding `-conf=/etc/vitastor/vitastor.conf`.
|
|
||||||
|
|
||||||
See exact fio commands to use for benchmarking [here](../performance/understanding.en.md#fio-commands).
|
|
||||||
|
|
|
@ -14,13 +14,10 @@
|
||||||
Используйте следующую команду как пример для запуска тестов кластера Vitastor через fio:
|
Используйте следующую команду как пример для запуска тестов кластера Vitastor через fio:
|
||||||
|
|
||||||
```
|
```
|
||||||
fio -thread -ioengine=libfio_vitastor.so -name=test -bs=4M -direct=1 -iodepth=16 -rw=write -image=testimg
|
fio -thread -ioengine=libfio_vitastor.so -name=test -bs=4M -direct=1 -iodepth=16 -rw=write -etcd=10.115.0.10:2379/v3 -image=testimg
|
||||||
```
|
```
|
||||||
|
|
||||||
Вместо обращения к образу по имени (`-image=testimg`) можно указать номер пула, номер инода и размер:
|
Вместо обращения к образу по имени (`-image=testimg`) можно указать номер пула, номер инода и размер:
|
||||||
`-pool=1 -inode=1 -size=400G`.
|
`-pool=1 -inode=1 -size=400G`.
|
||||||
|
|
||||||
Вы также можете задать адрес(а) подключения к etcd явно, добавив `-etcd=10.115.0.10:2379/v3`,
|
|
||||||
или переопределить путь к файлу конфигурации, добавив `-conf=/etc/vitastor/vitastor.conf`.
|
|
||||||
|
|
||||||
Конкретные команды fio для тестирования производительности можно посмотреть [здесь](../performance/understanding.ru.md#команды-fio).
|
Конкретные команды fio для тестирования производительности можно посмотреть [здесь](../performance/understanding.ru.md#команды-fio).
|
||||||
|
|
|
@ -11,25 +11,25 @@ NBD stands for "Network Block Device", but in fact it also functions as "BUSE"
|
||||||
NBD slighly lowers the performance due to additional overhead, but performance still
|
NBD slighly lowers the performance due to additional overhead, but performance still
|
||||||
remains decent (see an example [here](../performance/comparison1.en.md#vitastor-0-4-0-nbd)).
|
remains decent (see an example [here](../performance/comparison1.en.md#vitastor-0-4-0-nbd)).
|
||||||
|
|
||||||
See also [VDUSE](qemu.en.md#vduse) as a better alternative to NBD.
|
Vitastor Kubernetes CSI driver is based on NBD.
|
||||||
|
|
||||||
Vitastor Kubernetes CSI driver uses NBD when VDUSE is unavailable.
|
See also [VDUSE](qemu.en.md#vduse).
|
||||||
|
|
||||||
## Map image
|
## Map image
|
||||||
|
|
||||||
To create a local block device for a Vitastor image run:
|
To create a local block device for a Vitastor image run:
|
||||||
|
|
||||||
```
|
```
|
||||||
vitastor-nbd map --image testimg
|
vitastor-nbd map --etcd_address 10.115.0.10:2379/v3 --image testimg
|
||||||
```
|
```
|
||||||
|
|
||||||
It will output a block device name like /dev/nbd0 which you can then use as a normal disk.
|
It will output a block device name like /dev/nbd0 which you can then use as a normal disk.
|
||||||
|
|
||||||
You can also use `--pool <POOL> --inode <INODE> --size <SIZE>` instead of `--image <IMAGE>` if you want.
|
You can also use `--pool <POOL> --inode <INODE> --size <SIZE>` instead of `--image <IMAGE>` if you want.
|
||||||
|
|
||||||
vitastor-nbd supports all usual Vitastor configuration options like `--config_file <path_to_config>` plus NBD-specific:
|
Additional options for map command:
|
||||||
|
|
||||||
* `--nbd_timeout 300` \
|
* `--nbd_timeout 30` \
|
||||||
Timeout for I/O operations in seconds after exceeding which the kernel stops
|
Timeout for I/O operations in seconds after exceeding which the kernel stops
|
||||||
the device. You can set it to 0 to disable the timeout, but beware that you
|
the device. You can set it to 0 to disable the timeout, but beware that you
|
||||||
won't be able to stop the device at all if vitastor-nbd process dies.
|
won't be able to stop the device at all if vitastor-nbd process dies.
|
||||||
|
@ -44,9 +44,6 @@ vitastor-nbd supports all usual Vitastor configuration options like `--config_fi
|
||||||
* `--foreground 1` \
|
* `--foreground 1` \
|
||||||
Stay in foreground, do not daemonize.
|
Stay in foreground, do not daemonize.
|
||||||
|
|
||||||
Note that `nbd_timeout`, `nbd_max_devices` and `nbd_max_part` options may also be specified
|
|
||||||
in `/etc/vitastor/vitastor.conf` or in other configuration file specified with `--config_file`.
|
|
||||||
|
|
||||||
## Unmap image
|
## Unmap image
|
||||||
|
|
||||||
To unmap the device run:
|
To unmap the device run:
|
||||||
|
|
|
@ -14,16 +14,16 @@ NBD на данный момент необходимо, чтобы монтир
|
||||||
NBD немного снижает производительность из-за дополнительных копирований памяти,
|
NBD немного снижает производительность из-за дополнительных копирований памяти,
|
||||||
но она всё равно остаётся на неплохом уровне (см. для примера [тест](../performance/comparison1.ru.md#vitastor-0-4-0-nbd)).
|
но она всё равно остаётся на неплохом уровне (см. для примера [тест](../performance/comparison1.ru.md#vitastor-0-4-0-nbd)).
|
||||||
|
|
||||||
Смотрите также [VDUSE](qemu.ru.md#vduse), как лучшую альтернативу NBD.
|
CSI-драйвер Kubernetes Vitastor основан на NBD.
|
||||||
|
|
||||||
CSI-драйвер Kubernetes Vitastor использует NBD, когда VDUSE недоступен.
|
Смотрите также [VDUSE](qemu.ru.md#vduse).
|
||||||
|
|
||||||
## Подключить устройство
|
## Подключить устройство
|
||||||
|
|
||||||
Чтобы создать локальное блочное устройство для образа, выполните команду:
|
Чтобы создать локальное блочное устройство для образа, выполните команду:
|
||||||
|
|
||||||
```
|
```
|
||||||
vitastor-nbd map --image testimg
|
vitastor-nbd map --etcd_address 10.115.0.10:2379/v3 --image testimg
|
||||||
```
|
```
|
||||||
|
|
||||||
Команда напечатает название блочного устройства вида /dev/nbd0, которое потом можно
|
Команда напечатает название блочного устройства вида /dev/nbd0, которое потом можно
|
||||||
|
@ -32,8 +32,7 @@ vitastor-nbd map --image testimg
|
||||||
Для обращения по номеру инода, аналогично другим командам, можно использовать опции
|
Для обращения по номеру инода, аналогично другим командам, можно использовать опции
|
||||||
`--pool <POOL> --inode <INODE> --size <SIZE>` вместо `--image testimg`.
|
`--pool <POOL> --inode <INODE> --size <SIZE>` вместо `--image testimg`.
|
||||||
|
|
||||||
vitastor-nbd поддерживает все обычные опции Vitastor, например, `--config_file <path_to_config>`,
|
Дополнительные опции для команды подключения NBD-устройства:
|
||||||
плюс специфичные для NBD:
|
|
||||||
|
|
||||||
* `--nbd_timeout 30` \
|
* `--nbd_timeout 30` \
|
||||||
Максимальное время выполнения любой операции чтения/записи в секундах, при
|
Максимальное время выполнения любой операции чтения/записи в секундах, при
|
||||||
|
@ -54,10 +53,6 @@ vitastor-nbd поддерживает все обычные опции Vitastor,
|
||||||
* `--foreground 1` \
|
* `--foreground 1` \
|
||||||
Не уводить процесс в фоновый режим.
|
Не уводить процесс в фоновый режим.
|
||||||
|
|
||||||
Обратите внимание, что опции `nbd_timeout`, `nbd_max_devices` и `nbd_max_part` можно
|
|
||||||
также задавать в `/etc/vitastor/vitastor.conf` или в другом файле конфигурации,
|
|
||||||
заданном опцией `--config_file`.
|
|
||||||
|
|
||||||
## Отключить устройство
|
## Отключить устройство
|
||||||
|
|
||||||
Для отключения устройства выполните:
|
Для отключения устройства выполните:
|
||||||
|
|
|
@ -23,7 +23,7 @@ balancer or any failover method you want to in that case.
|
||||||
vitastor-nfs usage:
|
vitastor-nfs usage:
|
||||||
|
|
||||||
```
|
```
|
||||||
vitastor-nfs [STANDARD OPTIONS] [OTHER OPTIONS]
|
vitastor-nfs [--etcd_address ADDR] [OTHER OPTIONS]
|
||||||
|
|
||||||
--subdir <DIR> export images prefixed <DIR>/ (default empty - export all images)
|
--subdir <DIR> export images prefixed <DIR>/ (default empty - export all images)
|
||||||
--portmap 0 do not listen on port 111 (portmap/rpcbind, requires root)
|
--portmap 0 do not listen on port 111 (portmap/rpcbind, requires root)
|
||||||
|
@ -34,7 +34,7 @@ vitastor-nfs [STANDARD OPTIONS] [OTHER OPTIONS]
|
||||||
--foreground 1 stay in foreground, do not daemonize
|
--foreground 1 stay in foreground, do not daemonize
|
||||||
```
|
```
|
||||||
|
|
||||||
Example start and mount commands (etcd_address is optional):
|
Example start and mount commands:
|
||||||
|
|
||||||
```
|
```
|
||||||
vitastor-nfs --etcd_address 192.168.5.10:2379 --portmap 0 --port 2050 --pool testpool
|
vitastor-nfs --etcd_address 192.168.5.10:2379 --portmap 0 --port 2050 --pool testpool
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
Использование vitastor-nfs:
|
Использование vitastor-nfs:
|
||||||
|
|
||||||
```
|
```
|
||||||
vitastor-nfs [СТАНДАРТНЫЕ ОПЦИИ] [ДРУГИЕ ОПЦИИ]
|
vitastor-nfs [--etcd_address ADDR] [ДРУГИЕ ОПЦИИ]
|
||||||
|
|
||||||
--subdir <DIR> экспортировать "поддиректорию" - образы с префиксом имени <DIR>/ (по умолчанию пусто - экспортировать все образы)
|
--subdir <DIR> экспортировать "поддиректорию" - образы с префиксом имени <DIR>/ (по умолчанию пусто - экспортировать все образы)
|
||||||
--portmap 0 отключить сервис portmap/rpcbind на порту 111 (по умолчанию включён и требует root привилегий)
|
--portmap 0 отключить сервис portmap/rpcbind на порту 111 (по умолчанию включён и требует root привилегий)
|
||||||
|
@ -33,7 +33,7 @@ vitastor-nfs [СТАНДАРТНЫЕ ОПЦИИ] [ДРУГИЕ ОПЦИИ]
|
||||||
--foreground 1 не уходить в фон после запуска
|
--foreground 1 не уходить в фон после запуска
|
||||||
```
|
```
|
||||||
|
|
||||||
Пример монтирования Vitastor через NFS (etcd_address необязателен):
|
Пример монтирования Vitastor через NFS:
|
||||||
|
|
||||||
```
|
```
|
||||||
vitastor-nfs --etcd_address 192.168.5.10:2379 --portmap 0 --port 2050 --pool testpool
|
vitastor-nfs --etcd_address 192.168.5.10:2379 --portmap 0 --port 2050 --pool testpool
|
||||||
|
|
|
@ -16,16 +16,13 @@ Old syntax (-drive):
|
||||||
|
|
||||||
```
|
```
|
||||||
qemu-system-x86_64 -enable-kvm -m 1024 \
|
qemu-system-x86_64 -enable-kvm -m 1024 \
|
||||||
-drive 'file=vitastor:image=debian9',
|
-drive 'file=vitastor:etcd_host=192.168.7.2\:2379/v3:image=debian9',
|
||||||
format=raw,if=none,id=drive-virtio-disk0,cache=none \
|
format=raw,if=none,id=drive-virtio-disk0,cache=none \
|
||||||
-device 'virtio-blk-pci,scsi=off,bus=pci.0,addr=0x5,drive=drive-virtio-disk0,
|
-device 'virtio-blk-pci,scsi=off,bus=pci.0,addr=0x5,drive=drive-virtio-disk0,
|
||||||
id=virtio-disk0,bootindex=1,write-cache=off' \
|
id=virtio-disk0,bootindex=1,write-cache=off' \
|
||||||
-vnc 0.0.0.0:0
|
-vnc 0.0.0.0:0
|
||||||
```
|
```
|
||||||
|
|
||||||
Etcd address may be specified explicitly by adding `:etcd_host=192.168.7.2\:2379/v3` to `file=`.
|
|
||||||
Configuration file path may be overriden by adding `:config_path=/etc/vitastor/vitastor.conf`.
|
|
||||||
|
|
||||||
New syntax (-blockdev):
|
New syntax (-blockdev):
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -37,28 +34,14 @@ qemu-system-x86_64 -enable-kvm -m 1024 \
|
||||||
-vnc 0.0.0.0:0
|
-vnc 0.0.0.0:0
|
||||||
```
|
```
|
||||||
|
|
||||||
With a separate I/O thread:
|
|
||||||
|
|
||||||
```
|
|
||||||
qemu-system-x86_64 -enable-kvm -m 1024 \
|
|
||||||
-object iothread,id=vitastor1 \
|
|
||||||
-blockdev '{"node-name":"drive-virtio-disk0","driver":"vitastor","image":"debian9",
|
|
||||||
"cache":{"direct":true,"no-flush":false},"auto-read-only":true,"discard":"unmap"}' \
|
|
||||||
-device 'virtio-blk-pci,iothread=vitastor1,scsi=off,bus=pci.0,addr=0x5,drive=drive-virtio-disk0,
|
|
||||||
id=virtio-disk0,bootindex=1,write-cache=off' \
|
|
||||||
-vnc 0.0.0.0:0
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also specify inode ID, pool and size manually instead of `:image=<IMAGE>` option: `:pool=<POOL>:inode=<INODE>:size=<SIZE>`.
|
|
||||||
|
|
||||||
## qemu-img
|
## qemu-img
|
||||||
|
|
||||||
For qemu-img, you should use `vitastor:image=<IMAGE>[:etcd_host=<HOST>]` as filename.
|
For qemu-img, you should use `vitastor:etcd_host=<HOST>:image=<IMAGE>` as filename.
|
||||||
|
|
||||||
For example, to upload a VM image into Vitastor, run:
|
For example, to upload a VM image into Vitastor, run:
|
||||||
|
|
||||||
```
|
```
|
||||||
qemu-img convert -f qcow2 debian10.qcow2 -p -O raw 'vitastor:image=debian10'
|
qemu-img convert -f qcow2 debian10.qcow2 -p -O raw 'vitastor:etcd_host=192.168.7.2\:2379/v3:image=debian10'
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also specify `:pool=<POOL>:inode=<INODE>:size=<SIZE>` instead of `:image=<IMAGE>`
|
You can also specify `:pool=<POOL>:inode=<INODE>:size=<SIZE>` instead of `:image=<IMAGE>`
|
||||||
|
@ -75,10 +58,10 @@ the snapshot separately using the following commands (key points are using `skip
|
||||||
`-B backing_file` option):
|
`-B backing_file` option):
|
||||||
|
|
||||||
```
|
```
|
||||||
qemu-img convert -f raw 'vitastor:image=testimg@0' \
|
qemu-img convert -f raw 'vitastor:etcd_host=192.168.7.2\:2379/v3:image=testimg@0' \
|
||||||
-O qcow2 testimg_0.qcow2
|
-O qcow2 testimg_0.qcow2
|
||||||
|
|
||||||
qemu-img convert -f raw 'vitastor:image=testimg:skip-parents=1' \
|
qemu-img convert -f raw 'vitastor:etcd_host=192.168.7.2\:2379/v3:image=testimg:skip-parents=1' \
|
||||||
-O qcow2 -o 'cluster_size=4k' -B testimg_0.qcow2 testimg.qcow2
|
-O qcow2 -o 'cluster_size=4k' -B testimg_0.qcow2 testimg.qcow2
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -101,75 +84,25 @@ This can be used for backups. Just note that exporting an image that is currentl
|
||||||
is of course unsafe and doesn't produce a consistent result, so only export snapshots if you do this
|
is of course unsafe and doesn't produce a consistent result, so only export snapshots if you do this
|
||||||
on a live VM.
|
on a live VM.
|
||||||
|
|
||||||
## vhost-user-blk
|
|
||||||
|
|
||||||
QEMU, starting with 6.0, includes support for attaching disks via a separate
|
|
||||||
userspace worker process, called `vhost-user-blk`. It usually has slightly (20-30 us)
|
|
||||||
lower latency.
|
|
||||||
|
|
||||||
Example commands to use it with Vitastor:
|
|
||||||
|
|
||||||
```
|
|
||||||
qemu-storage-daemon \
|
|
||||||
--daemonize \
|
|
||||||
--blockdev '{"node-name":"drive-virtio-disk1","driver":"vitastor","image":"testosd1","cache":{"direct":true,"no-flush":false},"auto-read-only":true,"discard":"unmap"}' \
|
|
||||||
--export type=vhost-user-blk,id=vitastor1,node-name=drive-virtio-disk1,addr.type=unix,addr.path=/run/vitastor1-user-blk.sock,writable=on,num-queues=1
|
|
||||||
|
|
||||||
qemu-system-x86_64 -enable-kvm -m 2048 -M accel=kvm,memory-backend=mem \
|
|
||||||
-object memory-backend-memfd,id=mem,size=2G,share=on \
|
|
||||||
-chardev socket,id=vitastor1,reconnect=1,path=/run/vitastor1-user-blk.sock \
|
|
||||||
-device vhost-user-blk-pci,chardev=vitastor1,num-queues=1,config-wce=off \
|
|
||||||
-vnc 0.0.0.0:0
|
|
||||||
```
|
|
||||||
|
|
||||||
memfd memory-backend is crucial, vhost-user-blk does not work without it.
|
|
||||||
|
|
||||||
## VDUSE
|
## VDUSE
|
||||||
|
|
||||||
Linux kernel, starting with version 5.15, supports a new interface for attaching virtual disks
|
Linux kernel, starting with version 5.15, supports a new interface for attaching virtual disks
|
||||||
to the host - VDUSE (vDPA Device in Userspace). QEMU, starting with 7.2, has support for
|
to the host - VDUSE (vDPA Device in Userspace). QEMU, starting with 7.2, has support for
|
||||||
exporting QEMU block devices over this protocol using qemu-storage-daemon.
|
exporting QEMU block devices over this protocol using qemu-storage-daemon.
|
||||||
|
|
||||||
VDUSE is currently the best interface to attach Vitastor disks as kernel devices because:
|
VDUSE has the same problem as other FUSE-like interfaces in Linux: if a userspace process hangs,
|
||||||
- It avoids data copies and thus achieves much better performance than [NBD](nbd.en.md)
|
for example, if it loses connectivity with Vitastor cluster - active processes doing I/O may
|
||||||
- It doesn't have NBD timeout problem - the device doesn't die if an operation executes for too long
|
hang in the D state (uninterruptible sleep) and you won't be able to kill them even with kill -9.
|
||||||
- It doesn't have hung device problem - if the userspace process dies it can be restarted (!)
|
In this case reboot will be the only way to remove VDUSE devices from system.
|
||||||
and block device will continue operation
|
|
||||||
- It doesn't seem to have the device number limit
|
|
||||||
|
|
||||||
Example performance comparison:
|
On the other hand, VDUSE is faster than [NBD](nbd.en.md), so you may prefer to use it if
|
||||||
|
performance is important for you. Approximate performance numbers:
|
||||||
| | direct fio | NBD | VDUSE |
|
direct fio benchmark - 115000 iops, NBD - 60000 iops, VDUSE - 90000 iops.
|
||||||
|----------------------|-------------|-------------|-------------|
|
|
||||||
| linear write | 3.85 GB/s | 1.12 GB/s | 3.85 GB/s |
|
|
||||||
| 4k random write Q128 | 240000 iops | 120000 iops | 178000 iops |
|
|
||||||
| 4k random write Q1 | 9500 iops | 7620 iops | 7640 iops |
|
|
||||||
| linear read | 4.3 GB/s | 1.8 GB/s | 2.85 GB/s |
|
|
||||||
| 4k random read Q128 | 287000 iops | 140000 iops | 189000 iops |
|
|
||||||
| 4k random read Q1 | 9600 iops | 7640 iops | 7780 iops |
|
|
||||||
|
|
||||||
To try VDUSE you need at least Linux 5.15, built with VDUSE support
|
To try VDUSE you need at least Linux 5.15, built with VDUSE support
|
||||||
(CONFIG_VDPA=m, CONFIG_VDPA_USER=m, CONFIG_VIRTIO_VDPA=m).
|
(CONFIG_VIRTIO_VDPA=m and CONFIG_VDPA_USER=m). Debian Linux kernels have these options
|
||||||
|
disabled by now, so if you want to try it on Debian, use a kernel from Ubuntu
|
||||||
Debian Linux kernels have these options disabled by now, so if you want to try it on Debian,
|
[kernel-ppa/mainline](https://kernel.ubuntu.com/~kernel-ppa/mainline/) or Proxmox.
|
||||||
use a kernel from Ubuntu [kernel-ppa/mainline](https://kernel.ubuntu.com/~kernel-ppa/mainline/), Proxmox,
|
|
||||||
or build modules for Debian kernel manually:
|
|
||||||
|
|
||||||
```
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
apt-get install linux-headers-`uname -r`
|
|
||||||
apt-get build-dep linux-image-`uname -r`-unsigned
|
|
||||||
apt-get source linux-image-`uname -r`-unsigned
|
|
||||||
cd linux*/drivers/vdpa
|
|
||||||
make -C /lib/modules/`uname -r`/build M=$PWD CONFIG_VDPA=m CONFIG_VDPA_USER=m CONFIG_VIRTIO_VDPA=m -j8 modules modules_install
|
|
||||||
cat Module.symvers >> /lib/modules/`uname -r`/build/Module.symvers
|
|
||||||
cd ../virtio
|
|
||||||
make -C /lib/modules/`uname -r`/build M=$PWD CONFIG_VDPA=m CONFIG_VDPA_USER=m CONFIG_VIRTIO_VDPA=m -j8 modules modules_install
|
|
||||||
depmod -a
|
|
||||||
```
|
|
||||||
|
|
||||||
You also need `vdpa` tool from the `iproute2` package.
|
|
||||||
|
|
||||||
Commands to attach Vitastor image as a VDUSE device:
|
Commands to attach Vitastor image as a VDUSE device:
|
||||||
|
|
||||||
|
@ -182,7 +115,7 @@ qemu-storage-daemon --daemonize --blockdev '{"node-name":"test1","driver":"vitas
|
||||||
vdpa dev add name test1 mgmtdev vduse
|
vdpa dev add name test1 mgmtdev vduse
|
||||||
```
|
```
|
||||||
|
|
||||||
After running these commands, `/dev/vda` device will appear in the system and you'll be able to
|
After running these commands /dev/vda device will appear in the system and you'll be able to
|
||||||
use it as a normal disk.
|
use it as a normal disk.
|
||||||
|
|
||||||
To remove the device:
|
To remove the device:
|
||||||
|
|
|
@ -18,16 +18,13 @@
|
||||||
|
|
||||||
```
|
```
|
||||||
qemu-system-x86_64 -enable-kvm -m 1024 \
|
qemu-system-x86_64 -enable-kvm -m 1024 \
|
||||||
-drive 'file=vitastor:image=debian9',
|
-drive 'file=vitastor:etcd_host=192.168.7.2\:2379/v3:image=debian9',
|
||||||
format=raw,if=none,id=drive-virtio-disk0,cache=none \
|
format=raw,if=none,id=drive-virtio-disk0,cache=none \
|
||||||
-device 'virtio-blk-pci,scsi=off,bus=pci.0,addr=0x5,drive=drive-virtio-disk0,
|
-device 'virtio-blk-pci,scsi=off,bus=pci.0,addr=0x5,drive=drive-virtio-disk0,
|
||||||
id=virtio-disk0,bootindex=1,write-cache=off' \
|
id=virtio-disk0,bootindex=1,write-cache=off' \
|
||||||
-vnc 0.0.0.0:0
|
-vnc 0.0.0.0:0
|
||||||
```
|
```
|
||||||
|
|
||||||
Адрес подключения etcd можно задать явно, если добавить `:etcd_host=192.168.7.2\:2379/v3` к `file=`.
|
|
||||||
Путь к файлу конфигурации можно переопределить, добавив `:config_path=/etc/vitastor/vitastor.conf`.
|
|
||||||
|
|
||||||
Новый синтаксис (-blockdev):
|
Новый синтаксис (-blockdev):
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -39,28 +36,16 @@ qemu-system-x86_64 -enable-kvm -m 1024 \
|
||||||
-vnc 0.0.0.0:0
|
-vnc 0.0.0.0:0
|
||||||
```
|
```
|
||||||
|
|
||||||
С отдельным потоком ввода-вывода:
|
|
||||||
|
|
||||||
```
|
|
||||||
qemu-system-x86_64 -enable-kvm -m 1024 \
|
|
||||||
-object iothread,id=vitastor1 \
|
|
||||||
-blockdev '{"node-name":"drive-virtio-disk0","driver":"vitastor","image":"debian9",
|
|
||||||
"cache":{"direct":true,"no-flush":false},"auto-read-only":true,"discard":"unmap"}' \
|
|
||||||
-device 'virtio-blk-pci,iothread=vitastor1,scsi=off,bus=pci.0,addr=0x5,drive=drive-virtio-disk0,
|
|
||||||
id=virtio-disk0,bootindex=1,write-cache=off' \
|
|
||||||
-vnc 0.0.0.0:0
|
|
||||||
```
|
|
||||||
|
|
||||||
Вместо `:image=<IMAGE>` также можно указывать номер инода, пул и размер: `:pool=<POOL>:inode=<INODE>:size=<SIZE>`.
|
Вместо `:image=<IMAGE>` также можно указывать номер инода, пул и размер: `:pool=<POOL>:inode=<INODE>:size=<SIZE>`.
|
||||||
|
|
||||||
## qemu-img
|
## qemu-img
|
||||||
|
|
||||||
Для qemu-img используйте строку `vitastor:image=<IMAGE>[:etcd_host=<HOST>]` в качестве имени файла диска.
|
Для qemu-img используйте строку `vitastor:etcd_host=<HOST>:image=<IMAGE>` в качестве имени файла диска.
|
||||||
|
|
||||||
Например, чтобы загрузить образ диска в Vitastor:
|
Например, чтобы загрузить образ диска в Vitastor:
|
||||||
|
|
||||||
```
|
```
|
||||||
qemu-img convert -f qcow2 debian10.qcow2 -p -O raw 'vitastor:image=testimg'
|
qemu-img convert -f qcow2 debian10.qcow2 -p -O raw 'vitastor:etcd_host=10.115.0.10\:2379/v3:image=testimg'
|
||||||
```
|
```
|
||||||
|
|
||||||
Если вы не хотите обращаться к образу по имени, вместо `:image=<IMAGE>` можно указать номер пула, номер инода и размер:
|
Если вы не хотите обращаться к образу по имени, вместо `:image=<IMAGE>` можно указать номер пула, номер инода и размер:
|
||||||
|
@ -76,10 +61,10 @@ qemu-img convert -f qcow2 debian10.qcow2 -p -O raw 'vitastor:image=testimg'
|
||||||
с помощью следующих команд (ключевые моменты - использование `skip-parents=1` и опции `-B backing_file.qcow2`):
|
с помощью следующих команд (ключевые моменты - использование `skip-parents=1` и опции `-B backing_file.qcow2`):
|
||||||
|
|
||||||
```
|
```
|
||||||
qemu-img convert -f raw 'vitastor:image=testimg@0' \
|
qemu-img convert -f raw 'vitastor:etcd_host=192.168.7.2\:2379/v3:image=testimg@0' \
|
||||||
-O qcow2 testimg_0.qcow2
|
-O qcow2 testimg_0.qcow2
|
||||||
|
|
||||||
qemu-img convert -f raw 'vitastor:image=testimg:skip-parents=1' \
|
qemu-img convert -f raw 'vitastor:etcd_host=192.168.7.2\:2379/v3:image=testimg:skip-parents=1' \
|
||||||
-O qcow2 -o 'cluster_size=4k' -B testimg_0.qcow2 testimg.qcow2
|
-O qcow2 -o 'cluster_size=4k' -B testimg_0.qcow2 testimg.qcow2
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -103,76 +88,25 @@ qemu-img rebase -u -b '' testimg.qcow2
|
||||||
в то же время идёт запись, небезопасно - результат чтения не будет целостным. Так что если вы работаете
|
в то же время идёт запись, небезопасно - результат чтения не будет целостным. Так что если вы работаете
|
||||||
с активными виртуальными машинами, экспортируйте только их снимки, но не сам образ.
|
с активными виртуальными машинами, экспортируйте только их снимки, но не сам образ.
|
||||||
|
|
||||||
## vhost-user-blk
|
|
||||||
|
|
||||||
QEMU, начиная с 6.0, позволяет подключать диски через отдельный рабочий процесс.
|
|
||||||
Этот метод подключения называется `vhost-user-blk` и обычно имеет чуть меньшую
|
|
||||||
задержку (ниже на 20-30 микросекунд, чем при обычном методе).
|
|
||||||
|
|
||||||
Пример команд для использования vhost-user-blk с Vitastor:
|
|
||||||
|
|
||||||
```
|
|
||||||
qemu-storage-daemon \
|
|
||||||
--daemonize \
|
|
||||||
--blockdev '{"node-name":"drive-virtio-disk1","driver":"vitastor","image":"testosd1","cache":{"direct":true,"no-flush":false},"auto-read-only":true,"discard":"unmap"}' \
|
|
||||||
--export type=vhost-user-blk,id=vitastor1,node-name=drive-virtio-disk1,addr.type=unix,addr.path=/run/vitastor1-user-blk.sock,writable=on,num-queues=1
|
|
||||||
|
|
||||||
qemu-system-x86_64 -enable-kvm -m 2048 -M accel=kvm,memory-backend=mem \
|
|
||||||
-object memory-backend-memfd,id=mem,size=2G,share=on \
|
|
||||||
-chardev socket,id=vitastor1,reconnect=1,path=/run/vitastor1-user-blk.sock \
|
|
||||||
-device vhost-user-blk-pci,chardev=vitastor1,num-queues=1,config-wce=off \
|
|
||||||
-vnc 0.0.0.0:0
|
|
||||||
```
|
|
||||||
|
|
||||||
Здесь критична опция memory-backend-memfd, vhost-user-blk без неё не работает.
|
|
||||||
|
|
||||||
## VDUSE
|
## VDUSE
|
||||||
|
|
||||||
В Linux, начиная с версии ядра 5.15, доступен новый интерфейс для подключения виртуальных дисков
|
В Linux, начиная с версии ядра 5.15, доступен новый интерфейс для подключения виртуальных дисков
|
||||||
к системе - VDUSE (vDPA Device in Userspace), а в QEMU, начиная с версии 7.2, есть поддержка
|
к системе - VDUSE (vDPA Device in Userspace), а в QEMU, начиная с версии 7.2, есть поддержка
|
||||||
экспорта блочных устройств QEMU по этому протоколу через qemu-storage-daemon.
|
экспорта блочных устройств QEMU по этому протоколу через qemu-storage-daemon.
|
||||||
|
|
||||||
VDUSE - на данный момент лучший интерфейс для подключения дисков Vitastor в виде блочных
|
VDUSE страдает общей проблемой FUSE-подобных интерфейсов в Linux: если пользовательский процесс
|
||||||
устройств на уровне ядра, ибо:
|
подвиснет, например, если будет потеряна связь с кластером Vitastor - читающие/пишущие в кластер
|
||||||
- VDUSE не копирует данные и поэтому достигает значительно лучшей производительности, чем [NBD](nbd.ru.md)
|
процессы могут "залипнуть" в состоянии D (непрерываемый сон) и их будет невозможно убить даже
|
||||||
- Также оно не имеет проблемы NBD-таймаута - устройство не умирает, если операция выполняется слишком долго
|
через kill -9. В этом случае удалить из системы устройство можно только перезагрузившись.
|
||||||
- Также оно не имеет проблемы подвисающих устройств - если процесс-обработчик умирает, его можно
|
|
||||||
перезапустить (!) и блочное устройство продолжит работать
|
|
||||||
- По-видимому, у него нет предела числа подключаемых в систему устройств
|
|
||||||
|
|
||||||
Пример сравнения производительности:
|
С другой стороны, VDUSE быстрее по сравнению с [NBD](nbd.ru.md), поэтому его может
|
||||||
|
быть предпочтительно использовать там, где производительность важнее. Порядок показателей:
|
||||||
|
прямое тестирование через fio - 115000 iops, NBD - 60000 iops, VDUSE - 90000 iops.
|
||||||
|
|
||||||
| | Прямой fio | NBD | VDUSE |
|
Чтобы использовать VDUSE, вам нужно ядро Linux версии хотя бы 5.15, собранное с поддержкой
|
||||||
|--------------------------|-------------|-------------|-------------|
|
VDUSE (CONFIG_VIRTIO_VDPA=m и CONFIG_VDPA_USER=m). В ядрах в Debian Linux поддержка пока
|
||||||
| линейная запись | 3.85 GB/s | 1.12 GB/s | 3.85 GB/s |
|
отключена - если хотите попробовать эту функцию на Debian, поставьте ядро из Ubuntu
|
||||||
| 4k случайная запись Q128 | 240000 iops | 120000 iops | 178000 iops |
|
[kernel-ppa/mainline](https://kernel.ubuntu.com/~kernel-ppa/mainline/) или из Proxmox.
|
||||||
| 4k случайная запись Q1 | 9500 iops | 7620 iops | 7640 iops |
|
|
||||||
| линейное чтение | 4.3 GB/s | 1.8 GB/s | 2.85 GB/s |
|
|
||||||
| 4k случайное чтение Q128 | 287000 iops | 140000 iops | 189000 iops |
|
|
||||||
| 4k случайное чтение Q1 | 9600 iops | 7640 iops | 7780 iops |
|
|
||||||
|
|
||||||
Чтобы попробовать VDUSE, вам нужно ядро Linux как минимум версии 5.15, собранное с поддержкой
|
|
||||||
VDUSE (CONFIG_VDPA=m, CONFIG_VDPA_USER=m, CONFIG_VIRTIO_VDPA=m).
|
|
||||||
|
|
||||||
В ядрах в Debian Linux поддержка пока отключена по умолчанию, так что чтобы попробовать VDUSE
|
|
||||||
на Debian, поставьте ядро из Ubuntu [kernel-ppa/mainline](https://kernel.ubuntu.com/~kernel-ppa/mainline/),
|
|
||||||
из Proxmox или соберите модули для ядра Debian вручную:
|
|
||||||
|
|
||||||
```
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
apt-get install linux-headers-`uname -r`
|
|
||||||
apt-get build-dep linux-image-`uname -r`-unsigned
|
|
||||||
apt-get source linux-image-`uname -r`-unsigned
|
|
||||||
cd linux*/drivers/vdpa
|
|
||||||
make -C /lib/modules/`uname -r`/build M=$PWD CONFIG_VDPA=m CONFIG_VDPA_USER=m CONFIG_VIRTIO_VDPA=m -j8 modules modules_install
|
|
||||||
cat Module.symvers >> /lib/modules/`uname -r`/build/Module.symvers
|
|
||||||
cd ../virtio
|
|
||||||
make -C /lib/modules/`uname -r`/build M=$PWD CONFIG_VDPA=m CONFIG_VDPA_USER=m CONFIG_VIRTIO_VDPA=m -j8 modules modules_install
|
|
||||||
depmod -a
|
|
||||||
```
|
|
||||||
|
|
||||||
Также вам понадобится консольная утилита `vdpa` из пакета `iproute2`.
|
|
||||||
|
|
||||||
Команды для подключения виртуального диска через VDUSE:
|
Команды для подключения виртуального диска через VDUSE:
|
||||||
|
|
||||||
|
@ -185,7 +119,7 @@ qemu-storage-daemon --daemonize --blockdev '{"node-name":"test1","driver":"vitas
|
||||||
vdpa dev add name test1 mgmtdev vduse
|
vdpa dev add name test1 mgmtdev vduse
|
||||||
```
|
```
|
||||||
|
|
||||||
После этого в системе появится устройство `/dev/vda`, которое можно будет использовать как
|
После этого в системе появится устройство /dev/vda, которое можно будет использовать как
|
||||||
обычный диск.
|
обычный диск.
|
||||||
|
|
||||||
Для удаления устройства из системы:
|
Для удаления устройства из системы:
|
||||||
|
|
|
@ -3,5 +3,5 @@ SUBSYSTEM=="block", ENV{ID_PART_ENTRY_TYPE}=="e7009fac-a5a1-4d72-af72-53de130599
|
||||||
IMPORT{program}="/usr/bin/vitastor-disk udev $devnode", \
|
IMPORT{program}="/usr/bin/vitastor-disk udev $devnode", \
|
||||||
SYMLINK+="vitastor/$env{VITASTOR_ALIAS}"
|
SYMLINK+="vitastor/$env{VITASTOR_ALIAS}"
|
||||||
|
|
||||||
ENV{VITASTOR_OSD_NUM}!="", ACTION=="add", RUN{program}+="/usr/bin/systemctl enable --now --no-block vitastor-osd@$env{VITASTOR_OSD_NUM}"
|
ENV{VITASTOR_OSD_NUM}!="", ACTION=="add", RUN{program}+="/usr/bin/systemctl enable --now vitastor-osd@$env{VITASTOR_OSD_NUM}"
|
||||||
ENV{VITASTOR_OSD_NUM}!="", ACTION=="remove", RUN{program}+="/usr/bin/systemctl disable --now --no-block vitastor-osd@$env{VITASTOR_OSD_NUM}"
|
ENV{VITASTOR_OSD_NUM}!="", ACTION=="remove", RUN{program}+="/usr/bin/systemctl disable --now vitastor-osd@$env{VITASTOR_OSD_NUM}"
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
scale_pg_count,
|
scale_pg_count,
|
||||||
scale_pg_history,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function add_pg_history(new_pg_history, new_pg, prev_pgs, prev_pg_history, old_pg)
|
function add_pg_history(new_pg_history, new_pg, prev_pgs, prev_pg_history, old_pg)
|
||||||
|
@ -44,18 +43,16 @@ function finish_pg_history(merged_history)
|
||||||
merged_history.all_peers = Object.values(merged_history.all_peers);
|
merged_history.all_peers = Object.values(merged_history.all_peers);
|
||||||
}
|
}
|
||||||
|
|
||||||
function scale_pg_history(prev_pg_history, prev_pgs, new_pgs)
|
function scale_pg_count(prev_pgs, real_prev_pgs, prev_pg_history, new_pg_history, new_pg_count)
|
||||||
{
|
{
|
||||||
const new_pg_history = [];
|
const old_pg_count = real_prev_pgs.length;
|
||||||
const old_pg_count = prev_pgs.length;
|
|
||||||
const new_pg_count = new_pgs.length;
|
|
||||||
// Add all possibly intersecting PGs to the history of new PGs
|
// Add all possibly intersecting PGs to the history of new PGs
|
||||||
if (!(new_pg_count % old_pg_count))
|
if (!(new_pg_count % old_pg_count))
|
||||||
{
|
{
|
||||||
// New PG count is a multiple of old PG count
|
// New PG count is a multiple of old PG count
|
||||||
for (let i = 0; i < new_pg_count; i++)
|
for (let i = 0; i < new_pg_count; i++)
|
||||||
{
|
{
|
||||||
add_pg_history(new_pg_history, i, prev_pgs, prev_pg_history, i % old_pg_count);
|
add_pg_history(new_pg_history, i, real_prev_pgs, prev_pg_history, i % old_pg_count);
|
||||||
finish_pg_history(new_pg_history[i]);
|
finish_pg_history(new_pg_history[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,7 +64,7 @@ function scale_pg_history(prev_pg_history, prev_pgs, new_pgs)
|
||||||
{
|
{
|
||||||
for (let j = 0; j < mul; j++)
|
for (let j = 0; j < mul; j++)
|
||||||
{
|
{
|
||||||
add_pg_history(new_pg_history, i, prev_pgs, prev_pg_history, i+j*new_pg_count);
|
add_pg_history(new_pg_history, i, real_prev_pgs, prev_pg_history, i+j*new_pg_count);
|
||||||
}
|
}
|
||||||
finish_pg_history(new_pg_history[i]);
|
finish_pg_history(new_pg_history[i]);
|
||||||
}
|
}
|
||||||
|
@ -79,7 +76,7 @@ function scale_pg_history(prev_pg_history, prev_pgs, new_pgs)
|
||||||
let merged_history = {};
|
let merged_history = {};
|
||||||
for (let i = 0; i < old_pg_count; i++)
|
for (let i = 0; i < old_pg_count; i++)
|
||||||
{
|
{
|
||||||
add_pg_history(merged_history, 1, prev_pgs, prev_pg_history, i);
|
add_pg_history(merged_history, 1, real_prev_pgs, prev_pg_history, i);
|
||||||
}
|
}
|
||||||
finish_pg_history(merged_history[1]);
|
finish_pg_history(merged_history[1]);
|
||||||
for (let i = 0; i < new_pg_count; i++)
|
for (let i = 0; i < new_pg_count; i++)
|
||||||
|
@ -92,12 +89,6 @@ function scale_pg_history(prev_pg_history, prev_pgs, new_pgs)
|
||||||
{
|
{
|
||||||
new_pg_history[i] = null;
|
new_pg_history[i] = null;
|
||||||
}
|
}
|
||||||
return new_pg_history;
|
|
||||||
}
|
|
||||||
|
|
||||||
function scale_pg_count(prev_pgs, new_pg_count)
|
|
||||||
{
|
|
||||||
const old_pg_count = prev_pgs.length;
|
|
||||||
// Just for the lp_solve optimizer - pick a "previous" PG for each "new" one
|
// Just for the lp_solve optimizer - pick a "previous" PG for each "new" one
|
||||||
if (prev_pgs.length < new_pg_count)
|
if (prev_pgs.length < new_pg_count)
|
||||||
{
|
{
|
||||||
|
|
477
mon/mon.js
477
mon/mon.js
|
@ -55,11 +55,10 @@ const etcd_tree = {
|
||||||
// etcd connection - configurable online
|
// etcd connection - configurable online
|
||||||
etcd_address: "10.0.115.10:2379/v3",
|
etcd_address: "10.0.115.10:2379/v3",
|
||||||
// mon
|
// mon
|
||||||
etcd_mon_ttl: 5, // min: 1
|
etcd_mon_ttl: 30, // min: 10
|
||||||
etcd_mon_timeout: 1000, // ms. min: 0
|
etcd_mon_timeout: 1000, // ms. min: 0
|
||||||
etcd_mon_retries: 5, // min: 0
|
etcd_mon_retries: 5, // min: 0
|
||||||
mon_change_timeout: 1000, // ms. min: 100
|
mon_change_timeout: 1000, // ms. min: 100
|
||||||
mon_retry_change_timeout: 50, // ms. min: 10
|
|
||||||
mon_stats_timeout: 1000, // ms. min: 100
|
mon_stats_timeout: 1000, // ms. min: 100
|
||||||
osd_out_time: 600, // seconds. min: 0
|
osd_out_time: 600, // seconds. min: 0
|
||||||
placement_levels: { datacenter: 1, rack: 2, host: 3, osd: 4, ... },
|
placement_levels: { datacenter: 1, rack: 2, host: 3, osd: 4, ... },
|
||||||
|
@ -92,7 +91,7 @@ const etcd_tree = {
|
||||||
peer_connect_timeout: 5, // seconds. min: 1
|
peer_connect_timeout: 5, // seconds. min: 1
|
||||||
osd_idle_timeout: 5, // seconds. min: 1
|
osd_idle_timeout: 5, // seconds. min: 1
|
||||||
osd_ping_timeout: 5, // seconds. min: 1
|
osd_ping_timeout: 5, // seconds. min: 1
|
||||||
up_wait_retry_interval: 50, // ms. min: 10
|
up_wait_retry_interval: 500, // ms. min: 50
|
||||||
max_etcd_attempts: 5,
|
max_etcd_attempts: 5,
|
||||||
etcd_quick_timeout: 1000, // ms
|
etcd_quick_timeout: 1000, // ms
|
||||||
etcd_slow_timeout: 5000, // ms
|
etcd_slow_timeout: 5000, // ms
|
||||||
|
@ -100,7 +99,6 @@ const etcd_tree = {
|
||||||
etcd_ws_keepalive_interval: 30, // seconds
|
etcd_ws_keepalive_interval: 30, // seconds
|
||||||
// osd
|
// osd
|
||||||
etcd_report_interval: 5, // seconds
|
etcd_report_interval: 5, // seconds
|
||||||
etcd_stats_interval: 30, // seconds
|
|
||||||
run_primary: true,
|
run_primary: true,
|
||||||
osd_network: null, // "192.168.7.0/24" or an array of masks
|
osd_network: null, // "192.168.7.0/24" or an array of masks
|
||||||
bind_address: "0.0.0.0",
|
bind_address: "0.0.0.0",
|
||||||
|
@ -111,15 +109,7 @@ const etcd_tree = {
|
||||||
autosync_interval: 5,
|
autosync_interval: 5,
|
||||||
autosync_writes: 128,
|
autosync_writes: 128,
|
||||||
client_queue_depth: 128, // unused
|
client_queue_depth: 128, // unused
|
||||||
recovery_queue_depth: 1,
|
recovery_queue_depth: 4,
|
||||||
recovery_sleep_us: 0,
|
|
||||||
recovery_tune_util_low: 0.1,
|
|
||||||
recovery_tune_client_util_low: 0,
|
|
||||||
recovery_tune_util_high: 1.0,
|
|
||||||
recovery_tune_client_util_high: 0.5,
|
|
||||||
recovery_tune_interval: 1,
|
|
||||||
recovery_tune_agg_interval: 10, // 10 times recovery_tune_interval
|
|
||||||
recovery_tune_sleep_min_us: 10, // 10 microseconds
|
|
||||||
recovery_pg_switch: 128,
|
recovery_pg_switch: 128,
|
||||||
recovery_sync_batch: 16,
|
recovery_sync_batch: 16,
|
||||||
no_recovery: false,
|
no_recovery: false,
|
||||||
|
@ -390,8 +380,7 @@ class Mon
|
||||||
{
|
{
|
||||||
constructor(config)
|
constructor(config)
|
||||||
{
|
{
|
||||||
this.failconnect = (e) => this._die(e, 2);
|
this.die = (e) => this._die(e);
|
||||||
this.die = (e) => this._die(e, 1);
|
|
||||||
if (fs.existsSync(config.config_path||'/etc/vitastor/vitastor.conf'))
|
if (fs.existsSync(config.config_path||'/etc/vitastor/vitastor.conf'))
|
||||||
{
|
{
|
||||||
config = {
|
config = {
|
||||||
|
@ -402,18 +391,17 @@ class Mon
|
||||||
this.parse_etcd_addresses(config.etcd_address||config.etcd_url);
|
this.parse_etcd_addresses(config.etcd_address||config.etcd_url);
|
||||||
this.verbose = config.verbose || 0;
|
this.verbose = config.verbose || 0;
|
||||||
this.initConfig = config;
|
this.initConfig = config;
|
||||||
this.config = { ...config };
|
this.config = {};
|
||||||
this.etcd_prefix = config.etcd_prefix || '/vitastor';
|
this.etcd_prefix = config.etcd_prefix || '/vitastor';
|
||||||
this.etcd_prefix = this.etcd_prefix.replace(/\/\/+/g, '/').replace(/^\/?(.*[^\/])\/?$/, '/$1');
|
this.etcd_prefix = this.etcd_prefix.replace(/\/\/+/g, '/').replace(/^\/?(.*[^\/])\/?$/, '/$1');
|
||||||
this.etcd_start_timeout = (config.etcd_start_timeout || 5) * 1000;
|
this.etcd_start_timeout = (config.etcd_start_timeout || 5) * 1000;
|
||||||
this.state = JSON.parse(JSON.stringify(this.constructor.etcd_tree));
|
this.state = JSON.parse(JSON.stringify(this.constructor.etcd_tree));
|
||||||
this.prev_stats = { osd_stats: {}, osd_diff: {} };
|
|
||||||
this.signals_set = false;
|
this.signals_set = false;
|
||||||
|
this.stat_time = Date.now();
|
||||||
this.ws = null;
|
this.ws = null;
|
||||||
this.ws_alive = false;
|
this.ws_alive = false;
|
||||||
this.ws_keepalive_timer = null;
|
this.ws_keepalive_timer = null;
|
||||||
this.on_stop_cb = () => this.on_stop(0).catch(console.error);
|
this.on_stop_cb = () => this.on_stop(0).catch(console.error);
|
||||||
this.recheck_pgs_active = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_etcd_addresses(addrs)
|
parse_etcd_addresses(addrs)
|
||||||
|
@ -480,10 +468,10 @@ class Mon
|
||||||
|
|
||||||
check_config()
|
check_config()
|
||||||
{
|
{
|
||||||
this.config.etcd_mon_ttl = Number(this.config.etcd_mon_ttl) || 5;
|
this.config.etcd_mon_ttl = Number(this.config.etcd_mon_ttl) || 30;
|
||||||
if (this.config.etcd_mon_ttl < 1)
|
if (this.config.etcd_mon_ttl < 10)
|
||||||
{
|
{
|
||||||
this.config.etcd_mon_ttl = 1;
|
this.config.etcd_mon_ttl = 10;
|
||||||
}
|
}
|
||||||
this.config.etcd_mon_timeout = Number(this.config.etcd_mon_timeout) || 0;
|
this.config.etcd_mon_timeout = Number(this.config.etcd_mon_timeout) || 0;
|
||||||
if (this.config.etcd_mon_timeout <= 0)
|
if (this.config.etcd_mon_timeout <= 0)
|
||||||
|
@ -500,11 +488,6 @@ class Mon
|
||||||
{
|
{
|
||||||
this.config.mon_change_timeout = 100;
|
this.config.mon_change_timeout = 100;
|
||||||
}
|
}
|
||||||
this.config.mon_retry_change_timeout = Number(this.config.mon_retry_change_timeout) || 50;
|
|
||||||
if (this.config.mon_retry_change_timeout < 50)
|
|
||||||
{
|
|
||||||
this.config.mon_retry_change_timeout = 50;
|
|
||||||
}
|
|
||||||
this.config.mon_stats_timeout = Number(this.config.mon_stats_timeout) || 1000;
|
this.config.mon_stats_timeout = Number(this.config.mon_stats_timeout) || 1000;
|
||||||
if (this.config.mon_stats_timeout < 100)
|
if (this.config.mon_stats_timeout < 100)
|
||||||
{
|
{
|
||||||
|
@ -568,9 +551,9 @@ class Mon
|
||||||
const cur_addr = this.pick_next_etcd();
|
const cur_addr = this.pick_next_etcd();
|
||||||
const base = 'ws'+cur_addr.substr(4);
|
const base = 'ws'+cur_addr.substr(4);
|
||||||
let now = Date.now();
|
let now = Date.now();
|
||||||
if (tried[base] && now-tried[base] < this.etcd_start_timeout)
|
if (tried[base] && now-tried[base] < timeout)
|
||||||
{
|
{
|
||||||
await new Promise(ok => setTimeout(ok, this.etcd_start_timeout-(now-tried[base])));
|
await new Promise(ok => setTimeout(ok, timeout-(now-tried[base])));
|
||||||
now = Date.now();
|
now = Date.now();
|
||||||
}
|
}
|
||||||
tried[base] = now;
|
tried[base] = now;
|
||||||
|
@ -605,7 +588,7 @@ class Mon
|
||||||
}
|
}
|
||||||
if (!this.ws)
|
if (!this.ws)
|
||||||
{
|
{
|
||||||
this.failconnect('Failed to open etcd watch websocket');
|
this.die('Failed to open etcd watch websocket');
|
||||||
}
|
}
|
||||||
const cur_addr = this.selected_etcd_url;
|
const cur_addr = this.selected_etcd_url;
|
||||||
this.ws_alive = true;
|
this.ws_alive = true;
|
||||||
|
@ -621,7 +604,7 @@ class Mon
|
||||||
console.log('etcd websocket timed out, restarting it');
|
console.log('etcd websocket timed out, restarting it');
|
||||||
this.restart_watcher(cur_addr);
|
this.restart_watcher(cur_addr);
|
||||||
}
|
}
|
||||||
}, (Number(this.config.etcd_ws_keepalive_interval) || 30)*1000);
|
}, (Number(this.config.etcd_keepalive_interval) || 30)*1000);
|
||||||
this.ws.on('error', () => this.restart_watcher(cur_addr));
|
this.ws.on('error', () => this.restart_watcher(cur_addr));
|
||||||
this.ws.send(JSON.stringify({
|
this.ws.send(JSON.stringify({
|
||||||
create_request: {
|
create_request: {
|
||||||
|
@ -675,12 +658,7 @@ class Mon
|
||||||
{
|
{
|
||||||
this.parse_kv(e.kv);
|
this.parse_kv(e.kv);
|
||||||
const key = e.kv.key.substr(this.etcd_prefix.length);
|
const key = e.kv.key.substr(this.etcd_prefix.length);
|
||||||
if (key.substr(0, 11) == '/osd/state/')
|
if (key.substr(0, 11) == '/osd/stats/' || key.substr(0, 10) == '/pg/stats/' || key.substr(0, 16) == '/osd/inodestats/')
|
||||||
{
|
|
||||||
stats_changed = true;
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
else if (key.substr(0, 11) == '/osd/stats/' || key.substr(0, 10) == '/pg/stats/' || key.substr(0, 16) == '/osd/inodestats/')
|
|
||||||
{
|
{
|
||||||
stats_changed = true;
|
stats_changed = true;
|
||||||
}
|
}
|
||||||
|
@ -713,27 +691,8 @@ class Mon
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Schedule save_last_clean() to to run after a small timeout (1s) (to not spam etcd)
|
|
||||||
schedule_save_last_clean()
|
|
||||||
{
|
|
||||||
if (!this.save_last_clean_timer)
|
|
||||||
{
|
|
||||||
this.save_last_clean_timer = setTimeout(() =>
|
|
||||||
{
|
|
||||||
this.save_last_clean_timer = null;
|
|
||||||
this.save_last_clean().catch(this.die);
|
|
||||||
}, this.config.mon_change_timeout || 1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async save_last_clean()
|
async save_last_clean()
|
||||||
{
|
{
|
||||||
if (this.save_last_clean_running)
|
|
||||||
{
|
|
||||||
this.schedule_save_last_clean();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.save_last_clean_running = true;
|
|
||||||
// last_clean_pgs is used to avoid extra data move when observing a series of changes in the cluster
|
// last_clean_pgs is used to avoid extra data move when observing a series of changes in the cluster
|
||||||
const new_clean_pgs = { items: {} };
|
const new_clean_pgs = { items: {} };
|
||||||
next_pool:
|
next_pool:
|
||||||
|
@ -770,7 +729,6 @@ class Mon
|
||||||
value: b64(JSON.stringify(this.state.history.last_clean_pgs))
|
value: b64(JSON.stringify(this.state.history.last_clean_pgs))
|
||||||
} } ],
|
} } ],
|
||||||
}, this.etcd_start_timeout, 0);
|
}, this.etcd_start_timeout, 0);
|
||||||
this.save_last_clean_running = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get_mon_state()
|
get_mon_state()
|
||||||
|
@ -797,9 +755,9 @@ class Mon
|
||||||
const res = await this.etcd_call('/lease/keepalive', { ID: this.etcd_lease_id }, this.config.etcd_mon_timeout, this.config.etcd_mon_retries);
|
const res = await this.etcd_call('/lease/keepalive', { ID: this.etcd_lease_id }, this.config.etcd_mon_timeout, this.config.etcd_mon_retries);
|
||||||
if (!res.result.TTL)
|
if (!res.result.TTL)
|
||||||
{
|
{
|
||||||
this.failconnect('Lease expired');
|
this.die('Lease expired');
|
||||||
}
|
}
|
||||||
}, this.config.etcd_mon_ttl*1000);
|
}, this.config.etcd_mon_timeout);
|
||||||
if (!this.signals_set)
|
if (!this.signals_set)
|
||||||
{
|
{
|
||||||
process.on('SIGINT', this.on_stop_cb);
|
process.on('SIGINT', this.on_stop_cb);
|
||||||
|
@ -1242,12 +1200,50 @@ class Mon
|
||||||
return aff_osds;
|
return aff_osds;
|
||||||
}
|
}
|
||||||
|
|
||||||
async generate_pool_pgs(pool_id, osd_tree, levels)
|
async recheck_pgs()
|
||||||
|
{
|
||||||
|
// Take configuration and state, check it against the stored configuration hash
|
||||||
|
// Recalculate PGs and save them to etcd if the configuration is changed
|
||||||
|
// FIXME: Do not change anything if the distribution is good and random enough and no PGs are degraded
|
||||||
|
const { up_osds, levels, osd_tree } = this.get_osd_tree();
|
||||||
|
const tree_cfg = {
|
||||||
|
osd_tree,
|
||||||
|
pools: this.state.config.pools,
|
||||||
|
};
|
||||||
|
const tree_hash = sha1hex(stableStringify(tree_cfg));
|
||||||
|
if (this.state.config.pgs.hash != tree_hash)
|
||||||
|
{
|
||||||
|
// Something has changed
|
||||||
|
const new_config_pgs = JSON.parse(JSON.stringify(this.state.config.pgs));
|
||||||
|
const etcd_request = { compare: [], success: [] };
|
||||||
|
for (const pool_id in (this.state.config.pgs||{}).items||{})
|
||||||
|
{
|
||||||
|
if (!this.state.config.pools[pool_id])
|
||||||
|
{
|
||||||
|
// Pool deleted. Delete all PGs, but first stop them.
|
||||||
|
if (!await this.stop_all_pgs(pool_id))
|
||||||
|
{
|
||||||
|
this.schedule_recheck();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const prev_pgs = [];
|
||||||
|
for (const pg in this.state.config.pgs.items[pool_id]||{})
|
||||||
|
{
|
||||||
|
prev_pgs[pg-1] = this.state.config.pgs.items[pool_id][pg].osd_set;
|
||||||
|
}
|
||||||
|
// Also delete pool statistics
|
||||||
|
etcd_request.success.push({ requestDeleteRange: {
|
||||||
|
key: b64(this.etcd_prefix+'/pool/stats/'+pool_id),
|
||||||
|
} });
|
||||||
|
this.save_new_pgs_txn(new_config_pgs, etcd_request, pool_id, up_osds, osd_tree, prev_pgs, [], []);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const pool_id in this.state.config.pools)
|
||||||
{
|
{
|
||||||
const pool_cfg = this.state.config.pools[pool_id];
|
const pool_cfg = this.state.config.pools[pool_id];
|
||||||
if (!this.validate_pool_cfg(pool_id, pool_cfg, false))
|
if (!this.validate_pool_cfg(pool_id, pool_cfg, false))
|
||||||
{
|
{
|
||||||
return null;
|
continue;
|
||||||
}
|
}
|
||||||
let pool_tree = osd_tree[pool_cfg.root_node || ''];
|
let pool_tree = osd_tree[pool_cfg.root_node || ''];
|
||||||
pool_tree = pool_tree ? pool_tree.children : [];
|
pool_tree = pool_tree ? pool_tree.children : [];
|
||||||
|
@ -1259,21 +1255,26 @@ class Mon
|
||||||
pool_cfg.bitmap_granularity || this.config.bitmap_granularity || 4096,
|
pool_cfg.bitmap_granularity || this.config.bitmap_granularity || 4096,
|
||||||
pool_cfg.immediate_commit || this.config.immediate_commit || 'none'
|
pool_cfg.immediate_commit || this.config.immediate_commit || 'none'
|
||||||
);
|
);
|
||||||
// First try last_clean_pgs to minimize data movement
|
// These are for the purpose of building history.osd_sets
|
||||||
|
const real_prev_pgs = [];
|
||||||
|
let pg_history = [];
|
||||||
|
for (const pg in ((this.state.config.pgs.items||{})[pool_id]||{}))
|
||||||
|
{
|
||||||
|
real_prev_pgs[pg-1] = this.state.config.pgs.items[pool_id][pg].osd_set;
|
||||||
|
if (this.state.pg.history[pool_id] &&
|
||||||
|
this.state.pg.history[pool_id][pg])
|
||||||
|
{
|
||||||
|
pg_history[pg-1] = this.state.pg.history[pool_id][pg];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// And these are for the purpose of minimizing data movement
|
||||||
let prev_pgs = [];
|
let prev_pgs = [];
|
||||||
for (const pg in ((this.state.history.last_clean_pgs.items||{})[pool_id]||{}))
|
for (const pg in ((this.state.history.last_clean_pgs.items||{})[pool_id]||{}))
|
||||||
{
|
{
|
||||||
prev_pgs[pg-1] = [ ...this.state.history.last_clean_pgs.items[pool_id][pg].osd_set ];
|
prev_pgs[pg-1] = this.state.history.last_clean_pgs.items[pool_id][pg].osd_set;
|
||||||
}
|
}
|
||||||
if (!prev_pgs.length)
|
prev_pgs = JSON.parse(JSON.stringify(prev_pgs.length ? prev_pgs : real_prev_pgs));
|
||||||
{
|
const old_pg_count = real_prev_pgs.length;
|
||||||
// Fall back to config/pgs if it's empty
|
|
||||||
for (const pg in ((this.state.config.pgs.items||{})[pool_id]||{}))
|
|
||||||
{
|
|
||||||
prev_pgs[pg-1] = [ ...this.state.config.pgs.items[pool_id][pg].osd_set ];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const old_pg_count = prev_pgs.length;
|
|
||||||
const optimize_cfg = {
|
const optimize_cfg = {
|
||||||
osd_tree: pool_tree,
|
osd_tree: pool_tree,
|
||||||
pg_count: pool_cfg.pg_count,
|
pg_count: pool_cfg.pg_count,
|
||||||
|
@ -1283,15 +1284,19 @@ class Mon
|
||||||
ordered: pool_cfg.scheme != 'replicated',
|
ordered: pool_cfg.scheme != 'replicated',
|
||||||
};
|
};
|
||||||
let optimize_result;
|
let optimize_result;
|
||||||
// Re-shuffle PGs if config/pgs.hash is empty
|
if (old_pg_count > 0)
|
||||||
if (old_pg_count > 0 && this.state.config.pgs.hash)
|
|
||||||
{
|
{
|
||||||
if (prev_pgs.length != pool_cfg.pg_count)
|
if (old_pg_count != pool_cfg.pg_count)
|
||||||
{
|
{
|
||||||
// Scale PG count
|
// PG count changed. Need to bring all PGs down.
|
||||||
// Do it even if old_pg_count is already equal to pool_cfg.pg_count,
|
if (!await this.stop_all_pgs(pool_id))
|
||||||
// because last_clean_pgs may still contain the old number of PGs
|
{
|
||||||
PGUtil.scale_pg_count(prev_pgs, pool_cfg.pg_count);
|
this.schedule_recheck();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const new_pg_history = [];
|
||||||
|
PGUtil.scale_pg_count(prev_pgs, real_prev_pgs, pg_history, new_pg_history, pool_cfg.pg_count);
|
||||||
|
pg_history = new_pg_history;
|
||||||
}
|
}
|
||||||
for (const pg of prev_pgs)
|
for (const pg of prev_pgs)
|
||||||
{
|
{
|
||||||
|
@ -1300,86 +1305,53 @@ class Mon
|
||||||
pg.push(0);
|
pg.push(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!this.state.config.pgs.hash)
|
||||||
|
{
|
||||||
|
// Re-shuffle PGs
|
||||||
|
optimize_result = await LPOptimizer.optimize_initial(optimize_cfg);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
optimize_result = await LPOptimizer.optimize_change({
|
optimize_result = await LPOptimizer.optimize_change({
|
||||||
prev_pgs,
|
prev_pgs,
|
||||||
...optimize_cfg,
|
...optimize_cfg,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
optimize_result = await LPOptimizer.optimize_initial(optimize_cfg);
|
optimize_result = await LPOptimizer.optimize_initial(optimize_cfg);
|
||||||
}
|
}
|
||||||
console.log(`Pool ${pool_id} (${pool_cfg.name || 'unnamed'}):`);
|
if (old_pg_count != optimize_result.int_pgs.length)
|
||||||
|
{
|
||||||
|
console.log(
|
||||||
|
`PG count for pool ${pool_id} (${pool_cfg.name || 'unnamed'})`+
|
||||||
|
` changed from: ${old_pg_count} to ${optimize_result.int_pgs.length}`
|
||||||
|
);
|
||||||
|
// Drop stats
|
||||||
|
etcd_request.success.push({ requestDeleteRange: {
|
||||||
|
key: b64(this.etcd_prefix+'/pg/stats/'+pool_id+'/'),
|
||||||
|
range_end: b64(this.etcd_prefix+'/pg/stats/'+pool_id+'0'),
|
||||||
|
} });
|
||||||
|
}
|
||||||
LPOptimizer.print_change_stats(optimize_result);
|
LPOptimizer.print_change_stats(optimize_result);
|
||||||
const pg_effsize = Math.min(pool_cfg.pg_size, Object.keys(pool_tree).length);
|
const pg_effsize = Math.min(pool_cfg.pg_size, Object.keys(pool_tree).length);
|
||||||
return {
|
this.state.pool.stats[pool_id] = {
|
||||||
pool_id,
|
used_raw_tb: (this.state.pool.stats[pool_id]||{}).used_raw_tb || 0,
|
||||||
pgs: optimize_result.int_pgs,
|
|
||||||
stats: {
|
|
||||||
total_raw_tb: optimize_result.space,
|
total_raw_tb: optimize_result.space,
|
||||||
pg_real_size: pg_effsize || pool_cfg.pg_size,
|
pg_real_size: pg_effsize || pool_cfg.pg_size,
|
||||||
raw_to_usable: (pg_effsize || pool_cfg.pg_size) / (pool_cfg.scheme === 'replicated'
|
raw_to_usable: (pg_effsize || pool_cfg.pg_size) / (pool_cfg.scheme === 'replicated'
|
||||||
? 1 : (pool_cfg.pg_size - (pool_cfg.parity_chunks||0))),
|
? 1 : (pool_cfg.pg_size - (pool_cfg.parity_chunks||0))),
|
||||||
space_efficiency: optimize_result.space/(optimize_result.total_space||1),
|
space_efficiency: optimize_result.space/(optimize_result.total_space||1),
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
etcd_request.success.push({ requestPut: {
|
||||||
|
key: b64(this.etcd_prefix+'/pool/stats/'+pool_id),
|
||||||
|
value: b64(JSON.stringify(this.state.pool.stats[pool_id])),
|
||||||
|
} });
|
||||||
|
this.save_new_pgs_txn(new_config_pgs, etcd_request, pool_id, up_osds, osd_tree, real_prev_pgs, optimize_result.int_pgs, pg_history);
|
||||||
}
|
}
|
||||||
|
new_config_pgs.hash = tree_hash;
|
||||||
async recheck_pgs()
|
await this.save_pg_config(new_config_pgs, etcd_request);
|
||||||
{
|
|
||||||
if (this.recheck_pgs_active)
|
|
||||||
{
|
|
||||||
this.schedule_recheck();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.recheck_pgs_active = true;
|
|
||||||
// Take configuration and state, check it against the stored configuration hash
|
|
||||||
// Recalculate PGs and save them to etcd if the configuration is changed
|
|
||||||
// FIXME: Do not change anything if the distribution is good and random enough and no PGs are degraded
|
|
||||||
const { up_osds, levels, osd_tree } = this.get_osd_tree();
|
|
||||||
const tree_cfg = {
|
|
||||||
osd_tree,
|
|
||||||
levels,
|
|
||||||
pools: this.state.config.pools,
|
|
||||||
};
|
|
||||||
const tree_hash = sha1hex(stableStringify(tree_cfg));
|
|
||||||
if (this.state.config.pgs.hash != tree_hash)
|
|
||||||
{
|
|
||||||
// Something has changed
|
|
||||||
console.log('Pool configuration or OSD tree changed, re-optimizing');
|
|
||||||
// First re-optimize PGs, but don't look at history yet
|
|
||||||
const optimize_results = await Promise.all(Object.keys(this.state.config.pools)
|
|
||||||
.map(pool_id => this.generate_pool_pgs(pool_id, osd_tree, levels)));
|
|
||||||
// Then apply the modification in the form of an optimistic transaction,
|
|
||||||
// each time considering new pg/history modifications (OSDs modify it during rebalance)
|
|
||||||
while (!await this.apply_pool_pgs(optimize_results, up_osds, osd_tree, tree_hash))
|
|
||||||
{
|
|
||||||
console.log(
|
|
||||||
'Someone changed PG configuration while we also tried to change it.'+
|
|
||||||
' Retrying in '+this.config.mon_retry_change_timeout+' ms'
|
|
||||||
);
|
|
||||||
// Failed to apply - parallel change detected. Wait a bit and retry
|
|
||||||
const old_rev = this.etcd_watch_revision;
|
|
||||||
while (this.etcd_watch_revision === old_rev)
|
|
||||||
{
|
|
||||||
await new Promise(ok => setTimeout(ok, this.config.mon_retry_change_timeout));
|
|
||||||
}
|
|
||||||
const new_ot = this.get_osd_tree();
|
|
||||||
const new_tcfg = {
|
|
||||||
osd_tree: new_ot.osd_tree,
|
|
||||||
levels: new_ot.levels,
|
|
||||||
pools: this.state.config.pools,
|
|
||||||
};
|
|
||||||
if (sha1hex(stableStringify(new_tcfg)) !== tree_hash)
|
|
||||||
{
|
|
||||||
// Configuration actually changed, restart from the beginning
|
|
||||||
this.recheck_pgs_active = false;
|
|
||||||
setImmediate(() => this.recheck_pgs().catch(this.die));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Configuration didn't change, PG history probably changed, so just retry
|
|
||||||
}
|
|
||||||
console.log('PG configuration successfully changed');
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1420,96 +1392,10 @@ class Mon
|
||||||
}
|
}
|
||||||
if (changed)
|
if (changed)
|
||||||
{
|
{
|
||||||
const ok = await this.save_pg_config(new_config_pgs);
|
await this.save_pg_config(new_config_pgs);
|
||||||
if (ok)
|
|
||||||
console.log('PG configuration successfully changed');
|
|
||||||
else
|
|
||||||
{
|
|
||||||
console.log('Someone changed PG configuration while we also tried to change it. Retrying in '+this.config.mon_change_timeout+' ms');
|
|
||||||
this.schedule_recheck();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.recheck_pgs_active = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async apply_pool_pgs(results, up_osds, osd_tree, tree_hash)
|
|
||||||
{
|
|
||||||
for (const pool_id in (this.state.config.pgs||{}).items||{})
|
|
||||||
{
|
|
||||||
// We should stop all PGs when deleting a pool or changing its PG count
|
|
||||||
if (!this.state.config.pools[pool_id] ||
|
|
||||||
this.state.config.pgs.items[pool_id] && this.state.config.pools[pool_id].pg_count !=
|
|
||||||
Object.keys(this.state.config.pgs.items[pool_id]).reduce((a, c) => (a < (0|c) ? (0|c) : a), 0))
|
|
||||||
{
|
|
||||||
if (!await this.stop_all_pgs(pool_id))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const new_config_pgs = JSON.parse(JSON.stringify(this.state.config.pgs));
|
|
||||||
const etcd_request = { compare: [], success: [] };
|
|
||||||
for (const pool_id in (new_config_pgs||{}).items||{})
|
|
||||||
{
|
|
||||||
if (!this.state.config.pools[pool_id])
|
|
||||||
{
|
|
||||||
const prev_pgs = [];
|
|
||||||
for (const pg in new_config_pgs.items[pool_id]||{})
|
|
||||||
{
|
|
||||||
prev_pgs[pg-1] = new_config_pgs.items[pool_id][pg].osd_set;
|
|
||||||
}
|
|
||||||
// Also delete pool statistics
|
|
||||||
etcd_request.success.push({ requestDeleteRange: {
|
|
||||||
key: b64(this.etcd_prefix+'/pool/stats/'+pool_id),
|
|
||||||
} });
|
|
||||||
this.save_new_pgs_txn(new_config_pgs, etcd_request, pool_id, up_osds, osd_tree, prev_pgs, [], []);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const pool_res of results)
|
|
||||||
{
|
|
||||||
const pool_id = pool_res.pool_id;
|
|
||||||
const pool_cfg = this.state.config.pools[pool_id];
|
|
||||||
let pg_history = [];
|
|
||||||
for (const pg in ((this.state.config.pgs.items||{})[pool_id]||{}))
|
|
||||||
{
|
|
||||||
if (this.state.pg.history[pool_id] &&
|
|
||||||
this.state.pg.history[pool_id][pg])
|
|
||||||
{
|
|
||||||
pg_history[pg-1] = this.state.pg.history[pool_id][pg];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const real_prev_pgs = [];
|
|
||||||
for (const pg in ((this.state.config.pgs.items||{})[pool_id]||{}))
|
|
||||||
{
|
|
||||||
real_prev_pgs[pg-1] = [ ...this.state.config.pgs.items[pool_id][pg].osd_set ];
|
|
||||||
}
|
|
||||||
if (real_prev_pgs.length > 0 && real_prev_pgs.length != pool_res.pgs.length)
|
|
||||||
{
|
|
||||||
console.log(
|
|
||||||
`Changing PG count for pool ${pool_id} (${pool_cfg.name || 'unnamed'})`+
|
|
||||||
` from: ${real_prev_pgs.length} to ${pool_res.pgs.length}`
|
|
||||||
);
|
|
||||||
pg_history = PGUtil.scale_pg_history(pg_history, real_prev_pgs, pool_res.pgs);
|
|
||||||
// Drop stats
|
|
||||||
etcd_request.success.push({ requestDeleteRange: {
|
|
||||||
key: b64(this.etcd_prefix+'/pg/stats/'+pool_id+'/'),
|
|
||||||
range_end: b64(this.etcd_prefix+'/pg/stats/'+pool_id+'0'),
|
|
||||||
} });
|
|
||||||
}
|
|
||||||
const stats = {
|
|
||||||
used_raw_tb: (this.state.pool.stats[pool_id]||{}).used_raw_tb || 0,
|
|
||||||
...pool_res.stats,
|
|
||||||
};
|
|
||||||
etcd_request.success.push({ requestPut: {
|
|
||||||
key: b64(this.etcd_prefix+'/pool/stats/'+pool_id),
|
|
||||||
value: b64(JSON.stringify(stats)),
|
|
||||||
} });
|
|
||||||
this.save_new_pgs_txn(new_config_pgs, etcd_request, pool_id, up_osds, osd_tree, real_prev_pgs, pool_res.pgs, pg_history);
|
|
||||||
}
|
|
||||||
new_config_pgs.hash = tree_hash;
|
|
||||||
return await this.save_pg_config(new_config_pgs, etcd_request);
|
|
||||||
}
|
|
||||||
|
|
||||||
async save_pg_config(new_config_pgs, etcd_request = { compare: [], success: [] })
|
async save_pg_config(new_config_pgs, etcd_request = { compare: [], success: [] })
|
||||||
{
|
{
|
||||||
|
@ -1520,8 +1406,14 @@ class Mon
|
||||||
etcd_request.success.push(
|
etcd_request.success.push(
|
||||||
{ requestPut: { key: b64(this.etcd_prefix+'/config/pgs'), value: b64(JSON.stringify(new_config_pgs)) } },
|
{ requestPut: { key: b64(this.etcd_prefix+'/config/pgs'), value: b64(JSON.stringify(new_config_pgs)) } },
|
||||||
);
|
);
|
||||||
const txn_res = await this.etcd_call('/kv/txn', etcd_request, this.config.etcd_mon_timeout, 0);
|
const res = await this.etcd_call('/kv/txn', etcd_request, this.config.etcd_mon_timeout, 0);
|
||||||
return txn_res.succeeded;
|
if (!res.succeeded)
|
||||||
|
{
|
||||||
|
console.log('Someone changed PG configuration while we also tried to change it. Retrying in '+this.config.mon_change_timeout+' ms');
|
||||||
|
this.schedule_recheck();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log('PG configuration successfully changed');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Schedule next recheck at least at <unixtime>
|
// Schedule next recheck at least at <unixtime>
|
||||||
|
@ -1552,6 +1444,7 @@ class Mon
|
||||||
}
|
}
|
||||||
|
|
||||||
// Schedule a recheck to run after a small timeout (1s)
|
// Schedule a recheck to run after a small timeout (1s)
|
||||||
|
// If already scheduled, cancel previous timer and schedule it again
|
||||||
// This is required for multiple change events to trigger at most 1 recheck in 1s
|
// This is required for multiple change events to trigger at most 1 recheck in 1s
|
||||||
schedule_recheck()
|
schedule_recheck()
|
||||||
{
|
{
|
||||||
|
@ -1565,15 +1458,15 @@ class Mon
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
derive_osd_stats(st, prev, prev_diff)
|
derive_osd_stats(st, prev)
|
||||||
{
|
{
|
||||||
const zero_stats = { op: { bps: 0n, iops: 0n, lat: 0n }, subop: { iops: 0n, lat: 0n }, recovery: { bps: 0n, iops: 0n } };
|
const zero_stats = { op: { bps: 0n, iops: 0n, lat: 0n }, subop: { iops: 0n, lat: 0n }, recovery: { bps: 0n, iops: 0n } };
|
||||||
const diff = { op_stats: {}, subop_stats: {}, recovery_stats: {}, inode_stats: {} };
|
const diff = { op_stats: {}, subop_stats: {}, recovery_stats: {} };
|
||||||
if (!st || !st.time || !prev || !prev.time || prev.time >= st.time)
|
if (!st || !st.time || prev && (prev.time || this.stat_time/1000) >= st.time)
|
||||||
{
|
{
|
||||||
return prev_diff || diff;
|
return diff;
|
||||||
}
|
}
|
||||||
const timediff = BigInt(st.time*1000 - prev.time*1000);
|
const timediff = BigInt(st.time*1000 - (prev && prev.time*1000 || this.stat_time));
|
||||||
for (const op in st.op_stats||{})
|
for (const op in st.op_stats||{})
|
||||||
{
|
{
|
||||||
const pr = prev && prev.op_stats && prev.op_stats[op];
|
const pr = prev && prev.op_stats && prev.op_stats[op];
|
||||||
|
@ -1605,51 +1498,25 @@ class Mon
|
||||||
if (n > 0)
|
if (n > 0)
|
||||||
diff.recovery_stats[op] = { ...c, bps: b*1000n/timediff, iops: n*1000n/timediff };
|
diff.recovery_stats[op] = { ...c, bps: b*1000n/timediff, iops: n*1000n/timediff };
|
||||||
}
|
}
|
||||||
for (const pool_id in st.inode_stats||{})
|
|
||||||
{
|
|
||||||
const pool_diff = diff.inode_stats[pool_id] = {};
|
|
||||||
for (const inode_num in st.inode_stats[pool_id])
|
|
||||||
{
|
|
||||||
const inode_diff = diff.inode_stats[pool_id][inode_num] = {};
|
|
||||||
for (const op of [ 'read', 'write', 'delete' ])
|
|
||||||
{
|
|
||||||
const c = st.inode_stats[pool_id][inode_num][op];
|
|
||||||
const pr = prev && prev.inode_stats && prev.inode_stats[pool_id] &&
|
|
||||||
prev.inode_stats[pool_id][inode_num] && prev.inode_stats[pool_id][inode_num][op];
|
|
||||||
const n = BigInt(c.count||0) - BigInt(pr && pr.count||0);
|
|
||||||
inode_diff[op] = {
|
|
||||||
bps: (BigInt(c.bytes||0) - BigInt(pr && pr.bytes||0))*1000n/timediff,
|
|
||||||
iops: n*1000n/timediff,
|
|
||||||
lat: (BigInt(c.usec||0) - BigInt(pr && pr.usec||0))/(n || 1n),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return diff;
|
return diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
sum_op_stats()
|
sum_op_stats(timestamp, prev_stats)
|
||||||
{
|
{
|
||||||
|
const sum_diff = { op_stats: {}, subop_stats: {}, recovery_stats: {} };
|
||||||
|
if (!prev_stats || prev_stats.timestamp >= timestamp)
|
||||||
|
{
|
||||||
|
return sum_diff;
|
||||||
|
}
|
||||||
|
const tm = BigInt(timestamp - (prev_stats.timestamp || 0));
|
||||||
|
// Sum derived values instead of deriving summed
|
||||||
for (const osd in this.state.osd.stats)
|
for (const osd in this.state.osd.stats)
|
||||||
{
|
{
|
||||||
const cur = { ...this.state.osd.stats[osd], inode_stats: this.state.osd.inodestats[osd]||{} };
|
const derived = this.derive_osd_stats(this.state.osd.stats[osd],
|
||||||
this.prev_stats.osd_diff[osd] = this.derive_osd_stats(
|
this.prev_stats && this.prev_stats.osd_stats && this.prev_stats.osd_stats[osd]);
|
||||||
cur, this.prev_stats.osd_stats[osd], this.prev_stats.osd_diff[osd]
|
for (const type in derived)
|
||||||
);
|
|
||||||
this.prev_stats.osd_stats[osd] = cur;
|
|
||||||
}
|
|
||||||
const sum_diff = { op_stats: {}, subop_stats: {}, recovery_stats: {} };
|
|
||||||
// Sum derived values instead of deriving summed
|
|
||||||
for (const osd in this.state.osd.state)
|
|
||||||
{
|
{
|
||||||
const derived = this.prev_stats.osd_diff[osd];
|
for (const op in derived[type])
|
||||||
if (!this.state.osd.state[osd] || !derived)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (const type in sum_diff)
|
|
||||||
{
|
|
||||||
for (const op in derived[type]||{})
|
|
||||||
{
|
{
|
||||||
for (const k in derived[type][op])
|
for (const k in derived[type][op])
|
||||||
{
|
{
|
||||||
|
@ -1706,14 +1573,14 @@ class Mon
|
||||||
return { object_counts, object_bytes };
|
return { object_counts, object_bytes };
|
||||||
}
|
}
|
||||||
|
|
||||||
sum_inode_stats()
|
sum_inode_stats(prev_stats, timestamp, prev_timestamp)
|
||||||
{
|
{
|
||||||
const inode_stats = {};
|
const inode_stats = {};
|
||||||
const inode_stub = () => ({
|
const inode_stub = () => ({
|
||||||
raw_used: 0n,
|
raw_used: 0n,
|
||||||
read: { count: 0n, usec: 0n, bytes: 0n, bps: 0n, iops: 0n, lat: 0n },
|
read: { count: 0n, usec: 0n, bytes: 0n },
|
||||||
write: { count: 0n, usec: 0n, bytes: 0n, bps: 0n, iops: 0n, lat: 0n },
|
write: { count: 0n, usec: 0n, bytes: 0n },
|
||||||
delete: { count: 0n, usec: 0n, bytes: 0n, bps: 0n, iops: 0n, lat: 0n },
|
delete: { count: 0n, usec: 0n, bytes: 0n },
|
||||||
});
|
});
|
||||||
const seen_pools = {};
|
const seen_pools = {};
|
||||||
for (const pool_id in this.state.config.pools)
|
for (const pool_id in this.state.config.pools)
|
||||||
|
@ -1747,13 +1614,9 @@ class Mon
|
||||||
const used = this.state.pool.stats[pool_id].used_raw_tb;
|
const used = this.state.pool.stats[pool_id].used_raw_tb;
|
||||||
this.state.pool.stats[pool_id].used_raw_tb = Number(used)/1024/1024/1024/1024;
|
this.state.pool.stats[pool_id].used_raw_tb = Number(used)/1024/1024/1024/1024;
|
||||||
}
|
}
|
||||||
for (const osd_num in this.state.osd.state)
|
for (const osd_num in this.state.osd.inodestats)
|
||||||
{
|
{
|
||||||
const ist = this.state.osd.inodestats[osd_num];
|
const ist = this.state.osd.inodestats[osd_num];
|
||||||
if (!ist || !this.state.osd.state[osd_num])
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (const pool_id in ist)
|
for (const pool_id in ist)
|
||||||
{
|
{
|
||||||
inode_stats[pool_id] = inode_stats[pool_id] || {};
|
inode_stats[pool_id] = inode_stats[pool_id] || {};
|
||||||
|
@ -1769,30 +1632,11 @@ class Mon
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const osd in this.state.osd.state)
|
if (prev_stats && prev_timestamp >= timestamp)
|
||||||
{
|
{
|
||||||
const osd_diff = this.prev_stats.osd_diff[osd];
|
prev_stats = null;
|
||||||
if (!osd_diff || !this.state.osd.state[osd])
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (const pool_id in osd_diff.inode_stats)
|
|
||||||
{
|
|
||||||
for (const inode_num in this.prev_stats.osd_diff[osd].inode_stats[pool_id])
|
|
||||||
{
|
|
||||||
inode_stats[pool_id][inode_num] = inode_stats[pool_id][inode_num] || inode_stub();
|
|
||||||
for (const op of [ 'read', 'write', 'delete' ])
|
|
||||||
{
|
|
||||||
const op_diff = this.prev_stats.osd_diff[osd].inode_stats[pool_id][inode_num][op] || {};
|
|
||||||
const op_st = inode_stats[pool_id][inode_num][op];
|
|
||||||
op_st.bps += op_diff.bps;
|
|
||||||
op_st.iops += op_diff.iops;
|
|
||||||
op_st.lat += op_diff.lat;
|
|
||||||
op_st.n_osd = (op_st.n_osd || 0) + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
const tm = prev_stats ? BigInt(timestamp - prev_timestamp) : 0;
|
||||||
for (const pool_id in inode_stats)
|
for (const pool_id in inode_stats)
|
||||||
{
|
{
|
||||||
for (const inode_num in inode_stats[pool_id])
|
for (const inode_num in inode_stats[pool_id])
|
||||||
|
@ -1801,12 +1645,11 @@ class Mon
|
||||||
for (const op of [ 'read', 'write', 'delete' ])
|
for (const op of [ 'read', 'write', 'delete' ])
|
||||||
{
|
{
|
||||||
const op_st = inode_stats[pool_id][inode_num][op];
|
const op_st = inode_stats[pool_id][inode_num][op];
|
||||||
if (op_st.n_osd)
|
const prev_st = prev_stats && prev_stats[pool_id] && prev_stats[pool_id][inode_num] && prev_stats[pool_id][inode_num][op];
|
||||||
{
|
op_st.bps = prev_st ? (op_st.bytes - prev_st.bytes) * 1000n / tm : 0;
|
||||||
op_st.lat /= BigInt(op_st.n_osd);
|
op_st.iops = prev_st ? (op_st.count - prev_st.count) * 1000n / tm : 0;
|
||||||
delete op_st.n_osd;
|
op_st.lat = prev_st ? (op_st.usec - prev_st.usec) / ((op_st.count - prev_st.count) || 1n) : 0;
|
||||||
}
|
if (op_st.bps > 0 || op_st.iops > 0 || op_st.lat > 0)
|
||||||
if (op_st.bps > 0 || op_st.iops > 0)
|
|
||||||
nonzero = true;
|
nonzero = true;
|
||||||
}
|
}
|
||||||
if (!nonzero && (!this.state.config.inode[pool_id] || !this.state.config.inode[pool_id][inode_num]))
|
if (!nonzero && (!this.state.config.inode[pool_id] || !this.state.config.inode[pool_id][inode_num]))
|
||||||
|
@ -1839,9 +1682,15 @@ class Mon
|
||||||
async update_total_stats()
|
async update_total_stats()
|
||||||
{
|
{
|
||||||
const txn = [];
|
const txn = [];
|
||||||
|
const timestamp = Date.now();
|
||||||
const { object_counts, object_bytes } = this.sum_object_counts();
|
const { object_counts, object_bytes } = this.sum_object_counts();
|
||||||
let stats = this.sum_op_stats();
|
let stats = this.sum_op_stats(timestamp, this.prev_stats);
|
||||||
let { inode_stats, seen_pools } = this.sum_inode_stats();
|
let { inode_stats, seen_pools } = this.sum_inode_stats(
|
||||||
|
this.prev_stats ? this.prev_stats.inode_stats : null,
|
||||||
|
timestamp, this.prev_stats ? this.prev_stats.timestamp : null
|
||||||
|
);
|
||||||
|
this.prev_stats = { timestamp, inode_stats, osd_stats: { ...this.state.osd.stats } };
|
||||||
|
this.stat_time = Date.now();
|
||||||
stats.object_counts = object_counts;
|
stats.object_counts = object_counts;
|
||||||
stats.object_bytes = object_bytes;
|
stats.object_bytes = object_bytes;
|
||||||
stats = this.serialize_bigints(stats);
|
stats = this.serialize_bigints(stats);
|
||||||
|
@ -2016,14 +1865,14 @@ class Mon
|
||||||
return res.json;
|
return res.json;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.failconnect();
|
this.die();
|
||||||
}
|
}
|
||||||
|
|
||||||
_die(err, code)
|
_die(err)
|
||||||
{
|
{
|
||||||
// In fact we can just try to rejoin
|
// In fact we can just try to rejoin
|
||||||
console.error(new Error(err || 'Cluster connection failed'));
|
console.error(new Error(err || 'Cluster connection failed'));
|
||||||
process.exit(code || 2);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
local_ips(all)
|
local_ips(all)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "vitastor-mon",
|
"name": "vitastor-mon",
|
||||||
"version": "1.4.4",
|
"version": "1.0.0",
|
||||||
"description": "Vitastor SDS monitor service",
|
"description": "Vitastor SDS monitor service",
|
||||||
"main": "mon-main.js",
|
"main": "mon-main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
@ -8,9 +8,7 @@ PartOf=vitastor.target
|
||||||
LimitNOFILE=1048576
|
LimitNOFILE=1048576
|
||||||
LimitNPROC=1048576
|
LimitNPROC=1048576
|
||||||
LimitMEMLOCK=infinity
|
LimitMEMLOCK=infinity
|
||||||
# Use the following for direct logs to files
|
ExecStart=bash -c 'exec vitastor-disk exec-osd /dev/vitastor/osd%i-data >>/var/log/vitastor/osd%i.log 2>&1'
|
||||||
#ExecStart=bash -c 'exec vitastor-disk exec-osd /dev/vitastor/osd%i-data >>/var/log/vitastor/osd%i.log 2>&1'
|
|
||||||
ExecStart=vitastor-disk exec-osd /dev/vitastor/osd%i-data
|
|
||||||
ExecStartPre=+vitastor-disk pre-exec /dev/vitastor/osd%i-data
|
ExecStartPre=+vitastor-disk pre-exec /dev/vitastor/osd%i-data
|
||||||
WorkingDirectory=/
|
WorkingDirectory=/
|
||||||
User=vitastor
|
User=vitastor
|
||||||
|
|
|
@ -110,6 +110,7 @@ sub properties
|
||||||
vitastor_etcd_address => {
|
vitastor_etcd_address => {
|
||||||
description => 'IP address(es) of etcd.',
|
description => 'IP address(es) of etcd.',
|
||||||
type => 'string',
|
type => 'string',
|
||||||
|
format => 'pve-storage-portal-dns-list',
|
||||||
},
|
},
|
||||||
vitastor_etcd_prefix => {
|
vitastor_etcd_prefix => {
|
||||||
description => 'Prefix for Vitastor etcd metadata',
|
description => 'Prefix for Vitastor etcd metadata',
|
||||||
|
|
|
@ -50,7 +50,7 @@ from cinder.volume import configuration
|
||||||
from cinder.volume import driver
|
from cinder.volume import driver
|
||||||
from cinder.volume import volume_utils
|
from cinder.volume import volume_utils
|
||||||
|
|
||||||
VERSION = '1.4.4'
|
VERSION = '1.0.0'
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
|
@ -1,643 +0,0 @@
|
||||||
commit c1cd026e211e94b120028e7c98a6e4ce5afe9846
|
|
||||||
Author: Vitaliy Filippov <vitalif@yourcmc.ru>
|
|
||||||
Date: Wed Jan 24 22:04:50 2024 +0300
|
|
||||||
|
|
||||||
Add Vitastor support
|
|
||||||
|
|
||||||
diff --git a/include/libvirt/libvirt-storage.h b/include/libvirt/libvirt-storage.h
|
|
||||||
index aaad4a3da1..5f5daa8341 100644
|
|
||||||
--- a/include/libvirt/libvirt-storage.h
|
|
||||||
+++ b/include/libvirt/libvirt-storage.h
|
|
||||||
@@ -326,6 +326,7 @@ typedef enum {
|
|
||||||
VIR_CONNECT_LIST_STORAGE_POOLS_ZFS = 1 << 17, /* (Since: 1.2.8) */
|
|
||||||
VIR_CONNECT_LIST_STORAGE_POOLS_VSTORAGE = 1 << 18, /* (Since: 3.1.0) */
|
|
||||||
VIR_CONNECT_LIST_STORAGE_POOLS_ISCSI_DIRECT = 1 << 19, /* (Since: 5.6.0) */
|
|
||||||
+ VIR_CONNECT_LIST_STORAGE_POOLS_VITASTOR = 1 << 20, /* (Since: 5.0.0) */
|
|
||||||
} virConnectListAllStoragePoolsFlags;
|
|
||||||
|
|
||||||
int virConnectListAllStoragePools(virConnectPtr conn,
|
|
||||||
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
|
|
||||||
index 22ad43e1d7..56c81d6852 100644
|
|
||||||
--- a/src/conf/domain_conf.c
|
|
||||||
+++ b/src/conf/domain_conf.c
|
|
||||||
@@ -7185,7 +7185,8 @@ virDomainDiskSourceNetworkParse(xmlNodePtr node,
|
|
||||||
src->configFile = virXPathString("string(./config/@file)", ctxt);
|
|
||||||
|
|
||||||
if (src->protocol == VIR_STORAGE_NET_PROTOCOL_HTTP ||
|
|
||||||
- src->protocol == VIR_STORAGE_NET_PROTOCOL_HTTPS)
|
|
||||||
+ src->protocol == VIR_STORAGE_NET_PROTOCOL_HTTPS ||
|
|
||||||
+ src->protocol == VIR_STORAGE_NET_PROTOCOL_VITASTOR)
|
|
||||||
src->query = virXMLPropString(node, "query");
|
|
||||||
|
|
||||||
if (virDomainStorageNetworkParseHosts(node, ctxt, &src->hosts, &src->nhosts) < 0)
|
|
||||||
@@ -30618,6 +30619,7 @@ virDomainStorageSourceTranslateSourcePool(virStorageSource *src,
|
|
||||||
|
|
||||||
case VIR_STORAGE_POOL_MPATH:
|
|
||||||
case VIR_STORAGE_POOL_RBD:
|
|
||||||
+ case VIR_STORAGE_POOL_VITASTOR:
|
|
||||||
case VIR_STORAGE_POOL_SHEEPDOG:
|
|
||||||
case VIR_STORAGE_POOL_GLUSTER:
|
|
||||||
case VIR_STORAGE_POOL_LAST:
|
|
||||||
diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c
|
|
||||||
index c72108886e..c739ed6c43 100644
|
|
||||||
--- a/src/conf/domain_validate.c
|
|
||||||
+++ b/src/conf/domain_validate.c
|
|
||||||
@@ -495,6 +495,7 @@ virDomainDiskDefValidateSourceChainOne(const virStorageSource *src)
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_RBD:
|
|
||||||
break;
|
|
||||||
|
|
||||||
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_NBD:
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
|
|
||||||
@@ -541,7 +542,7 @@ virDomainDiskDefValidateSourceChainOne(const virStorageSource *src)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- /* internal snapshots and config files are currently supported only with rbd: */
|
|
||||||
+ /* internal snapshots are currently supported only with rbd: */
|
|
||||||
if (virStorageSourceGetActualType(src) != VIR_STORAGE_TYPE_NETWORK &&
|
|
||||||
src->protocol != VIR_STORAGE_NET_PROTOCOL_RBD) {
|
|
||||||
if (src->snapshot) {
|
|
||||||
@@ -549,10 +550,15 @@ virDomainDiskDefValidateSourceChainOne(const virStorageSource *src)
|
|
||||||
_("<snapshot> element is currently supported only with 'rbd' disks"));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
+ }
|
|
||||||
|
|
||||||
+ /* config files are currently supported only with rbd and vitastor: */
|
|
||||||
+ if (virStorageSourceGetActualType(src) != VIR_STORAGE_TYPE_NETWORK &&
|
|
||||||
+ src->protocol != VIR_STORAGE_NET_PROTOCOL_RBD &&
|
|
||||||
+ src->protocol != VIR_STORAGE_NET_PROTOCOL_VITASTOR) {
|
|
||||||
if (src->configFile) {
|
|
||||||
virReportError(VIR_ERR_XML_ERROR, "%s",
|
|
||||||
- _("<config> element is currently supported only with 'rbd' disks"));
|
|
||||||
+ _("<config> element is currently supported only with 'rbd' and 'vitastor' disks"));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng
|
|
||||||
index b98a2ae602..7d7a872e01 100644
|
|
||||||
--- a/src/conf/schemas/domaincommon.rng
|
|
||||||
+++ b/src/conf/schemas/domaincommon.rng
|
|
||||||
@@ -1997,6 +1997,35 @@
|
|
||||||
</element>
|
|
||||||
</define>
|
|
||||||
|
|
||||||
+ <define name="diskSourceNetworkProtocolVitastor">
|
|
||||||
+ <element name="source">
|
|
||||||
+ <interleave>
|
|
||||||
+ <attribute name="protocol">
|
|
||||||
+ <value>vitastor</value>
|
|
||||||
+ </attribute>
|
|
||||||
+ <ref name="diskSourceCommon"/>
|
|
||||||
+ <optional>
|
|
||||||
+ <attribute name="name"/>
|
|
||||||
+ </optional>
|
|
||||||
+ <optional>
|
|
||||||
+ <attribute name="query"/>
|
|
||||||
+ </optional>
|
|
||||||
+ <zeroOrMore>
|
|
||||||
+ <ref name="diskSourceNetworkHost"/>
|
|
||||||
+ </zeroOrMore>
|
|
||||||
+ <optional>
|
|
||||||
+ <element name="config">
|
|
||||||
+ <attribute name="file">
|
|
||||||
+ <ref name="absFilePath"/>
|
|
||||||
+ </attribute>
|
|
||||||
+ <empty/>
|
|
||||||
+ </element>
|
|
||||||
+ </optional>
|
|
||||||
+ <empty/>
|
|
||||||
+ </interleave>
|
|
||||||
+ </element>
|
|
||||||
+ </define>
|
|
||||||
+
|
|
||||||
<define name="diskSourceNetworkProtocolISCSI">
|
|
||||||
<element name="source">
|
|
||||||
<attribute name="protocol">
|
|
||||||
@@ -2347,6 +2376,7 @@
|
|
||||||
<ref name="diskSourceNetworkProtocolSimple"/>
|
|
||||||
<ref name="diskSourceNetworkProtocolVxHS"/>
|
|
||||||
<ref name="diskSourceNetworkProtocolNFS"/>
|
|
||||||
+ <ref name="diskSourceNetworkProtocolVitastor"/>
|
|
||||||
</choice>
|
|
||||||
</define>
|
|
||||||
|
|
||||||
diff --git a/src/conf/storage_conf.c b/src/conf/storage_conf.c
|
|
||||||
index 68842004b7..1d69a788b6 100644
|
|
||||||
--- a/src/conf/storage_conf.c
|
|
||||||
+++ b/src/conf/storage_conf.c
|
|
||||||
@@ -56,7 +56,7 @@ VIR_ENUM_IMPL(virStoragePool,
|
|
||||||
"logical", "disk", "iscsi",
|
|
||||||
"iscsi-direct", "scsi", "mpath",
|
|
||||||
"rbd", "sheepdog", "gluster",
|
|
||||||
- "zfs", "vstorage",
|
|
||||||
+ "zfs", "vstorage", "vitastor",
|
|
||||||
);
|
|
||||||
|
|
||||||
VIR_ENUM_IMPL(virStoragePoolFormatFileSystem,
|
|
||||||
@@ -242,6 +242,18 @@ static virStoragePoolTypeInfo poolTypeInfo[] = {
|
|
||||||
.formatToString = virStorageFileFormatTypeToString,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
+ {.poolType = VIR_STORAGE_POOL_VITASTOR,
|
|
||||||
+ .poolOptions = {
|
|
||||||
+ .flags = (VIR_STORAGE_POOL_SOURCE_HOST |
|
|
||||||
+ VIR_STORAGE_POOL_SOURCE_NETWORK |
|
|
||||||
+ VIR_STORAGE_POOL_SOURCE_NAME),
|
|
||||||
+ },
|
|
||||||
+ .volOptions = {
|
|
||||||
+ .defaultFormat = VIR_STORAGE_FILE_RAW,
|
|
||||||
+ .formatFromString = virStorageVolumeFormatFromString,
|
|
||||||
+ .formatToString = virStorageFileFormatTypeToString,
|
|
||||||
+ }
|
|
||||||
+ },
|
|
||||||
{.poolType = VIR_STORAGE_POOL_SHEEPDOG,
|
|
||||||
.poolOptions = {
|
|
||||||
.flags = (VIR_STORAGE_POOL_SOURCE_HOST |
|
|
||||||
@@ -538,6 +550,11 @@ virStoragePoolDefParseSource(xmlXPathContextPtr ctxt,
|
|
||||||
_("element 'name' is mandatory for RBD pool"));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
+ if (pool_type == VIR_STORAGE_POOL_VITASTOR && source->name == NULL) {
|
|
||||||
+ virReportError(VIR_ERR_XML_ERROR, "%s",
|
|
||||||
+ _("element 'name' is mandatory for Vitastor pool"));
|
|
||||||
+ return -1;
|
|
||||||
+ }
|
|
||||||
|
|
||||||
if (options->formatFromString) {
|
|
||||||
g_autofree char *format = NULL;
|
|
||||||
@@ -1127,6 +1144,7 @@ virStoragePoolDefFormatBuf(virBuffer *buf,
|
|
||||||
/* RBD, Sheepdog, Gluster and Iscsi-direct devices are not local block devs nor
|
|
||||||
* files, so they don't have a target */
|
|
||||||
if (def->type != VIR_STORAGE_POOL_RBD &&
|
|
||||||
+ def->type != VIR_STORAGE_POOL_VITASTOR &&
|
|
||||||
def->type != VIR_STORAGE_POOL_SHEEPDOG &&
|
|
||||||
def->type != VIR_STORAGE_POOL_GLUSTER &&
|
|
||||||
def->type != VIR_STORAGE_POOL_ISCSI_DIRECT) {
|
|
||||||
diff --git a/src/conf/storage_conf.h b/src/conf/storage_conf.h
|
|
||||||
index fc67957cfe..720c07ef74 100644
|
|
||||||
--- a/src/conf/storage_conf.h
|
|
||||||
+++ b/src/conf/storage_conf.h
|
|
||||||
@@ -103,6 +103,7 @@ typedef enum {
|
|
||||||
VIR_STORAGE_POOL_GLUSTER, /* Gluster device */
|
|
||||||
VIR_STORAGE_POOL_ZFS, /* ZFS */
|
|
||||||
VIR_STORAGE_POOL_VSTORAGE, /* Virtuozzo Storage */
|
|
||||||
+ VIR_STORAGE_POOL_VITASTOR, /* Vitastor */
|
|
||||||
|
|
||||||
VIR_STORAGE_POOL_LAST,
|
|
||||||
} virStoragePoolType;
|
|
||||||
@@ -454,6 +455,7 @@ VIR_ENUM_DECL(virStoragePartedFs);
|
|
||||||
VIR_CONNECT_LIST_STORAGE_POOLS_SCSI | \
|
|
||||||
VIR_CONNECT_LIST_STORAGE_POOLS_MPATH | \
|
|
||||||
VIR_CONNECT_LIST_STORAGE_POOLS_RBD | \
|
|
||||||
+ VIR_CONNECT_LIST_STORAGE_POOLS_VITASTOR | \
|
|
||||||
VIR_CONNECT_LIST_STORAGE_POOLS_SHEEPDOG | \
|
|
||||||
VIR_CONNECT_LIST_STORAGE_POOLS_GLUSTER | \
|
|
||||||
VIR_CONNECT_LIST_STORAGE_POOLS_ZFS | \
|
|
||||||
diff --git a/src/conf/storage_source_conf.c b/src/conf/storage_source_conf.c
|
|
||||||
index f974a521b1..cd394d0a9f 100644
|
|
||||||
--- a/src/conf/storage_source_conf.c
|
|
||||||
+++ b/src/conf/storage_source_conf.c
|
|
||||||
@@ -88,6 +88,7 @@ VIR_ENUM_IMPL(virStorageNetProtocol,
|
|
||||||
"ssh",
|
|
||||||
"vxhs",
|
|
||||||
"nfs",
|
|
||||||
+ "vitastor",
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1301,6 +1302,7 @@ virStorageSourceNetworkDefaultPort(virStorageNetProtocol protocol)
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
|
|
||||||
return 24007;
|
|
||||||
|
|
||||||
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_RBD:
|
|
||||||
/* we don't provide a default for RBD */
|
|
||||||
return 0;
|
|
||||||
diff --git a/src/conf/storage_source_conf.h b/src/conf/storage_source_conf.h
|
|
||||||
index 5e7d127453..283709eeb3 100644
|
|
||||||
--- a/src/conf/storage_source_conf.h
|
|
||||||
+++ b/src/conf/storage_source_conf.h
|
|
||||||
@@ -129,6 +129,7 @@ typedef enum {
|
|
||||||
VIR_STORAGE_NET_PROTOCOL_SSH,
|
|
||||||
VIR_STORAGE_NET_PROTOCOL_VXHS,
|
|
||||||
VIR_STORAGE_NET_PROTOCOL_NFS,
|
|
||||||
+ VIR_STORAGE_NET_PROTOCOL_VITASTOR,
|
|
||||||
|
|
||||||
VIR_STORAGE_NET_PROTOCOL_LAST
|
|
||||||
} virStorageNetProtocol;
|
|
||||||
diff --git a/src/conf/virstorageobj.c b/src/conf/virstorageobj.c
|
|
||||||
index 59fa5da372..4739167f5f 100644
|
|
||||||
--- a/src/conf/virstorageobj.c
|
|
||||||
+++ b/src/conf/virstorageobj.c
|
|
||||||
@@ -1438,6 +1438,7 @@ virStoragePoolObjSourceFindDuplicateCb(const void *payload,
|
|
||||||
return 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
+ case VIR_STORAGE_POOL_VITASTOR:
|
|
||||||
case VIR_STORAGE_POOL_ISCSI_DIRECT:
|
|
||||||
case VIR_STORAGE_POOL_RBD:
|
|
||||||
case VIR_STORAGE_POOL_LAST:
|
|
||||||
@@ -1921,6 +1922,8 @@ virStoragePoolObjMatch(virStoragePoolObj *obj,
|
|
||||||
(obj->def->type == VIR_STORAGE_POOL_MPATH)) ||
|
|
||||||
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_RBD) &&
|
|
||||||
(obj->def->type == VIR_STORAGE_POOL_RBD)) ||
|
|
||||||
+ (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_VITASTOR) &&
|
|
||||||
+ (obj->def->type == VIR_STORAGE_POOL_VITASTOR)) ||
|
|
||||||
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_SHEEPDOG) &&
|
|
||||||
(obj->def->type == VIR_STORAGE_POOL_SHEEPDOG)) ||
|
|
||||||
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_GLUSTER) &&
|
|
||||||
diff --git a/src/libvirt-storage.c b/src/libvirt-storage.c
|
|
||||||
index db7660aac4..561df34709 100644
|
|
||||||
--- a/src/libvirt-storage.c
|
|
||||||
+++ b/src/libvirt-storage.c
|
|
||||||
@@ -94,6 +94,7 @@ virStoragePoolGetConnect(virStoragePoolPtr pool)
|
|
||||||
* VIR_CONNECT_LIST_STORAGE_POOLS_SCSI
|
|
||||||
* VIR_CONNECT_LIST_STORAGE_POOLS_MPATH
|
|
||||||
* VIR_CONNECT_LIST_STORAGE_POOLS_RBD
|
|
||||||
+ * VIR_CONNECT_LIST_STORAGE_POOLS_VITASTOR
|
|
||||||
* VIR_CONNECT_LIST_STORAGE_POOLS_SHEEPDOG
|
|
||||||
* VIR_CONNECT_LIST_STORAGE_POOLS_GLUSTER
|
|
||||||
* VIR_CONNECT_LIST_STORAGE_POOLS_ZFS
|
|
||||||
diff --git a/src/libxl/libxl_conf.c b/src/libxl/libxl_conf.c
|
|
||||||
index 62e1be6672..71a1d42896 100644
|
|
||||||
--- a/src/libxl/libxl_conf.c
|
|
||||||
+++ b/src/libxl/libxl_conf.c
|
|
||||||
@@ -979,6 +979,7 @@ libxlMakeNetworkDiskSrcStr(virStorageSource *src,
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_SSH:
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_VXHS:
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_NFS:
|
|
||||||
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_LAST:
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_NONE:
|
|
||||||
virReportError(VIR_ERR_NO_SUPPORT,
|
|
||||||
diff --git a/src/libxl/xen_xl.c b/src/libxl/xen_xl.c
|
|
||||||
index f175359307..8efcf4c329 100644
|
|
||||||
--- a/src/libxl/xen_xl.c
|
|
||||||
+++ b/src/libxl/xen_xl.c
|
|
||||||
@@ -1456,6 +1456,7 @@ xenFormatXLDiskSrcNet(virStorageSource *src)
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_SSH:
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_VXHS:
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_NFS:
|
|
||||||
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_LAST:
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_NONE:
|
|
||||||
virReportError(VIR_ERR_NO_SUPPORT,
|
|
||||||
diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c
|
|
||||||
index 7e9daf0bdc..825b4a3006 100644
|
|
||||||
--- a/src/qemu/qemu_block.c
|
|
||||||
+++ b/src/qemu/qemu_block.c
|
|
||||||
@@ -758,6 +758,38 @@ qemuBlockStorageSourceGetRBDProps(virStorageSource *src,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
+static virJSONValue *
|
|
||||||
+qemuBlockStorageSourceGetVitastorProps(virStorageSource *src)
|
|
||||||
+{
|
|
||||||
+ virJSONValue *ret = NULL;
|
|
||||||
+ virStorageNetHostDef *host;
|
|
||||||
+ size_t i;
|
|
||||||
+ g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
|
||||||
+ g_autofree char *etcd = NULL;
|
|
||||||
+
|
|
||||||
+ for (i = 0; i < src->nhosts; i++) {
|
|
||||||
+ host = src->hosts + i;
|
|
||||||
+ if ((virStorageNetHostTransport)host->transport != VIR_STORAGE_NET_HOST_TRANS_TCP) {
|
|
||||||
+ return NULL;
|
|
||||||
+ }
|
|
||||||
+ virBufferAsprintf(&buf, i > 0 ? ",%s:%u" : "%s:%u", host->name, host->port);
|
|
||||||
+ }
|
|
||||||
+ if (src->nhosts > 0) {
|
|
||||||
+ etcd = virBufferContentAndReset(&buf);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (virJSONValueObjectAdd(&ret,
|
|
||||||
+ "S:etcd-host", etcd,
|
|
||||||
+ "S:etcd-prefix", src->query,
|
|
||||||
+ "S:config-path", src->configFile,
|
|
||||||
+ "s:image", src->path,
|
|
||||||
+ NULL) < 0)
|
|
||||||
+ return NULL;
|
|
||||||
+
|
|
||||||
+ return ret;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+
|
|
||||||
static virJSONValue *
|
|
||||||
qemuBlockStorageSourceGetSheepdogProps(virStorageSource *src)
|
|
||||||
{
|
|
||||||
@@ -1140,6 +1172,12 @@ qemuBlockStorageSourceGetBackendProps(virStorageSource *src,
|
|
||||||
return NULL;
|
|
||||||
break;
|
|
||||||
|
|
||||||
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
|
||||||
+ driver = "vitastor";
|
|
||||||
+ if (!(fileprops = qemuBlockStorageSourceGetVitastorProps(src)))
|
|
||||||
+ return NULL;
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
|
|
||||||
driver = "sheepdog";
|
|
||||||
if (!(fileprops = qemuBlockStorageSourceGetSheepdogProps(src)))
|
|
||||||
@@ -2032,6 +2070,7 @@ qemuBlockGetBackingStoreString(virStorageSource *src,
|
|
||||||
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_RBD:
|
|
||||||
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_VXHS:
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_NFS:
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_SSH:
|
|
||||||
@@ -2415,6 +2454,12 @@ qemuBlockStorageSourceCreateGetStorageProps(virStorageSource *src,
|
|
||||||
return -1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
|
||||||
+ driver = "vitastor";
|
|
||||||
+ if (!(location = qemuBlockStorageSourceGetVitastorProps(src)))
|
|
||||||
+ return -1;
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
|
|
||||||
driver = "sheepdog";
|
|
||||||
if (!(location = qemuBlockStorageSourceGetSheepdogProps(src)))
|
|
||||||
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
|
|
||||||
index 953808fcfe..62860283d8 100644
|
|
||||||
--- a/src/qemu/qemu_domain.c
|
|
||||||
+++ b/src/qemu/qemu_domain.c
|
|
||||||
@@ -5215,7 +5215,8 @@ qemuDomainValidateStorageSource(virStorageSource *src,
|
|
||||||
if (src->query &&
|
|
||||||
(actualType != VIR_STORAGE_TYPE_NETWORK ||
|
|
||||||
(src->protocol != VIR_STORAGE_NET_PROTOCOL_HTTPS &&
|
|
||||||
- src->protocol != VIR_STORAGE_NET_PROTOCOL_HTTP))) {
|
|
||||||
+ src->protocol != VIR_STORAGE_NET_PROTOCOL_HTTP &&
|
|
||||||
+ src->protocol != VIR_STORAGE_NET_PROTOCOL_VITASTOR))) {
|
|
||||||
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
||||||
_("query is supported only with HTTP(S) protocols"));
|
|
||||||
return -1;
|
|
||||||
@@ -10340,6 +10341,7 @@ qemuDomainPrepareStorageSourceTLS(virStorageSource *src,
|
|
||||||
break;
|
|
||||||
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_RBD:
|
|
||||||
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_ISCSI:
|
|
||||||
diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c
|
|
||||||
index 73ff533827..e9c799ca8f 100644
|
|
||||||
--- a/src/qemu/qemu_snapshot.c
|
|
||||||
+++ b/src/qemu/qemu_snapshot.c
|
|
||||||
@@ -423,6 +423,7 @@ qemuSnapshotPrepareDiskExternalInactive(virDomainSnapshotDiskDef *snapdisk,
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_NONE:
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_NBD:
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_RBD:
|
|
||||||
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_ISCSI:
|
|
||||||
@@ -648,6 +649,7 @@ qemuSnapshotPrepareDiskInternal(virDomainDiskDef *disk,
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_NONE:
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_NBD:
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_RBD:
|
|
||||||
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_ISCSI:
|
|
||||||
diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c
|
|
||||||
index 314fe930e0..fb615a8b4e 100644
|
|
||||||
--- a/src/storage/storage_driver.c
|
|
||||||
+++ b/src/storage/storage_driver.c
|
|
||||||
@@ -1626,6 +1626,7 @@ storageVolLookupByPathCallback(virStoragePoolObj *obj,
|
|
||||||
|
|
||||||
case VIR_STORAGE_POOL_GLUSTER:
|
|
||||||
case VIR_STORAGE_POOL_RBD:
|
|
||||||
+ case VIR_STORAGE_POOL_VITASTOR:
|
|
||||||
case VIR_STORAGE_POOL_SHEEPDOG:
|
|
||||||
case VIR_STORAGE_POOL_ZFS:
|
|
||||||
case VIR_STORAGE_POOL_LAST:
|
|
||||||
diff --git a/src/storage_file/storage_source_backingstore.c b/src/storage_file/storage_source_backingstore.c
|
|
||||||
index 80681924ea..8a3ade9ec0 100644
|
|
||||||
--- a/src/storage_file/storage_source_backingstore.c
|
|
||||||
+++ b/src/storage_file/storage_source_backingstore.c
|
|
||||||
@@ -287,6 +287,75 @@ virStorageSourceParseRBDColonString(const char *rbdstr,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
+static int
|
|
||||||
+virStorageSourceParseVitastorColonString(const char *colonstr,
|
|
||||||
+ virStorageSource *src)
|
|
||||||
+{
|
|
||||||
+ char *p, *e, *next;
|
|
||||||
+ g_autofree char *options = NULL;
|
|
||||||
+
|
|
||||||
+ /* optionally skip the "vitastor:" prefix if provided */
|
|
||||||
+ if (STRPREFIX(colonstr, "vitastor:"))
|
|
||||||
+ colonstr += strlen("vitastor:");
|
|
||||||
+
|
|
||||||
+ options = g_strdup(colonstr);
|
|
||||||
+
|
|
||||||
+ p = options;
|
|
||||||
+ while (*p) {
|
|
||||||
+ /* find : delimiter or end of string */
|
|
||||||
+ for (e = p; *e && *e != ':'; ++e) {
|
|
||||||
+ if (*e == '\\') {
|
|
||||||
+ e++;
|
|
||||||
+ if (*e == '\0')
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ if (*e == '\0') {
|
|
||||||
+ next = e; /* last kv pair */
|
|
||||||
+ } else {
|
|
||||||
+ next = e + 1;
|
|
||||||
+ *e = '\0';
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (STRPREFIX(p, "image=")) {
|
|
||||||
+ src->path = g_strdup(p + strlen("image="));
|
|
||||||
+ } else if (STRPREFIX(p, "etcd-prefix=")) {
|
|
||||||
+ src->query = g_strdup(p + strlen("etcd-prefix="));
|
|
||||||
+ } else if (STRPREFIX(p, "config-path=")) {
|
|
||||||
+ src->configFile = g_strdup(p + strlen("config-path="));
|
|
||||||
+ } else if (STRPREFIX(p, "etcd-host=")) {
|
|
||||||
+ char *h, *sep;
|
|
||||||
+
|
|
||||||
+ h = p + strlen("etcd-host=");
|
|
||||||
+ while (h < e) {
|
|
||||||
+ for (sep = h; sep < e; ++sep) {
|
|
||||||
+ if (*sep == '\\' && (sep[1] == ',' ||
|
|
||||||
+ sep[1] == ';' ||
|
|
||||||
+ sep[1] == ' ')) {
|
|
||||||
+ *sep = '\0';
|
|
||||||
+ sep += 2;
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (virStorageSourceRBDAddHost(src, h) < 0)
|
|
||||||
+ return -1;
|
|
||||||
+
|
|
||||||
+ h = sep;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ p = next;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (!src->path) {
|
|
||||||
+ return -1;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+
|
|
||||||
static int
|
|
||||||
virStorageSourceParseNBDColonString(const char *nbdstr,
|
|
||||||
virStorageSource *src)
|
|
||||||
@@ -399,6 +468,11 @@ virStorageSourceParseBackingColon(virStorageSource *src,
|
|
||||||
return -1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
|
||||||
+ if (virStorageSourceParseVitastorColonString(path, src) < 0)
|
|
||||||
+ return -1;
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_LAST:
|
|
||||||
case VIR_STORAGE_NET_PROTOCOL_NONE:
|
|
||||||
@@ -975,6 +1049,54 @@ virStorageSourceParseBackingJSONRBD(virStorageSource *src,
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
+static int
|
|
||||||
+virStorageSourceParseBackingJSONVitastor(virStorageSource *src,
|
|
||||||
+ virJSONValue *json,
|
|
||||||
+ const char *jsonstr G_GNUC_UNUSED,
|
|
||||||
+ int opaque G_GNUC_UNUSED)
|
|
||||||
+{
|
|
||||||
+ const char *filename;
|
|
||||||
+ const char *image = virJSONValueObjectGetString(json, "image");
|
|
||||||
+ const char *conf = virJSONValueObjectGetString(json, "config-path");
|
|
||||||
+ const char *etcd_prefix = virJSONValueObjectGetString(json, "etcd-prefix");
|
|
||||||
+ virJSONValue *servers = virJSONValueObjectGetArray(json, "server");
|
|
||||||
+ size_t nservers;
|
|
||||||
+ size_t i;
|
|
||||||
+
|
|
||||||
+ src->type = VIR_STORAGE_TYPE_NETWORK;
|
|
||||||
+ src->protocol = VIR_STORAGE_NET_PROTOCOL_VITASTOR;
|
|
||||||
+
|
|
||||||
+ /* legacy syntax passed via 'filename' option */
|
|
||||||
+ if ((filename = virJSONValueObjectGetString(json, "filename")))
|
|
||||||
+ return virStorageSourceParseVitastorColonString(filename, src);
|
|
||||||
+
|
|
||||||
+ if (!image) {
|
|
||||||
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
|
|
||||||
+ _("missing image name in Vitastor backing volume "
|
|
||||||
+ "JSON specification"));
|
|
||||||
+ return -1;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ src->path = g_strdup(image);
|
|
||||||
+ src->configFile = g_strdup(conf);
|
|
||||||
+ src->query = g_strdup(etcd_prefix);
|
|
||||||
+
|
|
||||||
+ if (servers) {
|
|
||||||
+ nservers = virJSONValueArraySize(servers);
|
|
||||||
+
|
|
||||||
+ src->hosts = g_new0(virStorageNetHostDef, nservers);
|
|
||||||
+ src->nhosts = nservers;
|
|
||||||
+
|
|
||||||
+ for (i = 0; i < nservers; i++) {
|
|
||||||
+ if (virStorageSourceParseBackingJSONInetSocketAddress(src->hosts + i,
|
|
||||||
+ virJSONValueArrayGet(servers, i)) < 0)
|
|
||||||
+ return -1;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static int
|
|
||||||
virStorageSourceParseBackingJSONRaw(virStorageSource *src,
|
|
||||||
virJSONValue *json,
|
|
||||||
@@ -1152,6 +1274,7 @@ static const struct virStorageSourceJSONDriverParser jsonParsers[] = {
|
|
||||||
{"sheepdog", false, virStorageSourceParseBackingJSONSheepdog, 0},
|
|
||||||
{"ssh", false, virStorageSourceParseBackingJSONSSH, 0},
|
|
||||||
{"rbd", false, virStorageSourceParseBackingJSONRBD, 0},
|
|
||||||
+ {"vitastor", false, virStorageSourceParseBackingJSONVitastor, 0},
|
|
||||||
{"raw", true, virStorageSourceParseBackingJSONRaw, 0},
|
|
||||||
{"nfs", false, virStorageSourceParseBackingJSONNFS, 0},
|
|
||||||
{"vxhs", false, virStorageSourceParseBackingJSONVxHS, 0},
|
|
||||||
diff --git a/src/test/test_driver.c b/src/test/test_driver.c
|
|
||||||
index e87d7cfd44..ccc05d7aae 100644
|
|
||||||
--- a/src/test/test_driver.c
|
|
||||||
+++ b/src/test/test_driver.c
|
|
||||||
@@ -7335,6 +7335,7 @@ testStorageVolumeTypeForPool(int pooltype)
|
|
||||||
case VIR_STORAGE_POOL_ISCSI_DIRECT:
|
|
||||||
case VIR_STORAGE_POOL_GLUSTER:
|
|
||||||
case VIR_STORAGE_POOL_RBD:
|
|
||||||
+ case VIR_STORAGE_POOL_VITASTOR:
|
|
||||||
return VIR_STORAGE_VOL_NETWORK;
|
|
||||||
case VIR_STORAGE_POOL_LOGICAL:
|
|
||||||
case VIR_STORAGE_POOL_DISK:
|
|
||||||
diff --git a/tests/storagepoolcapsschemadata/poolcaps-fs.xml b/tests/storagepoolcapsschemadata/poolcaps-fs.xml
|
|
||||||
index eee75af746..8bd0a57bdd 100644
|
|
||||||
--- a/tests/storagepoolcapsschemadata/poolcaps-fs.xml
|
|
||||||
+++ b/tests/storagepoolcapsschemadata/poolcaps-fs.xml
|
|
||||||
@@ -204,4 +204,11 @@
|
|
||||||
</enum>
|
|
||||||
</volOptions>
|
|
||||||
</pool>
|
|
||||||
+ <pool type='vitastor' supported='no'>
|
|
||||||
+ <volOptions>
|
|
||||||
+ <defaultFormat type='raw'/>
|
|
||||||
+ <enum name='targetFormatType'>
|
|
||||||
+ </enum>
|
|
||||||
+ </volOptions>
|
|
||||||
+ </pool>
|
|
||||||
</storagepoolCapabilities>
|
|
||||||
diff --git a/tests/storagepoolcapsschemadata/poolcaps-full.xml b/tests/storagepoolcapsschemadata/poolcaps-full.xml
|
|
||||||
index 805950a937..852df0de16 100644
|
|
||||||
--- a/tests/storagepoolcapsschemadata/poolcaps-full.xml
|
|
||||||
+++ b/tests/storagepoolcapsschemadata/poolcaps-full.xml
|
|
||||||
@@ -204,4 +204,11 @@
|
|
||||||
</enum>
|
|
||||||
</volOptions>
|
|
||||||
</pool>
|
|
||||||
+ <pool type='vitastor' supported='yes'>
|
|
||||||
+ <volOptions>
|
|
||||||
+ <defaultFormat type='raw'/>
|
|
||||||
+ <enum name='targetFormatType'>
|
|
||||||
+ </enum>
|
|
||||||
+ </volOptions>
|
|
||||||
+ </pool>
|
|
||||||
</storagepoolCapabilities>
|
|
||||||
diff --git a/tests/storagepoolxml2argvtest.c b/tests/storagepoolxml2argvtest.c
|
|
||||||
index e8e40d695e..db55fe5f3a 100644
|
|
||||||
--- a/tests/storagepoolxml2argvtest.c
|
|
||||||
+++ b/tests/storagepoolxml2argvtest.c
|
|
||||||
@@ -65,6 +65,7 @@ testCompareXMLToArgvFiles(bool shouldFail,
|
|
||||||
case VIR_STORAGE_POOL_GLUSTER:
|
|
||||||
case VIR_STORAGE_POOL_ZFS:
|
|
||||||
case VIR_STORAGE_POOL_VSTORAGE:
|
|
||||||
+ case VIR_STORAGE_POOL_VITASTOR:
|
|
||||||
case VIR_STORAGE_POOL_LAST:
|
|
||||||
default:
|
|
||||||
VIR_TEST_DEBUG("pool type '%s' has no xml2argv test", defTypeStr);
|
|
||||||
diff --git a/tools/virsh-pool.c b/tools/virsh-pool.c
|
|
||||||
index 36f00cf643..5f5bd3464e 100644
|
|
||||||
--- a/tools/virsh-pool.c
|
|
||||||
+++ b/tools/virsh-pool.c
|
|
||||||
@@ -1223,6 +1223,9 @@ cmdPoolList(vshControl *ctl, const vshCmd *cmd G_GNUC_UNUSED)
|
|
||||||
case VIR_STORAGE_POOL_VSTORAGE:
|
|
||||||
flags |= VIR_CONNECT_LIST_STORAGE_POOLS_VSTORAGE;
|
|
||||||
break;
|
|
||||||
+ case VIR_STORAGE_POOL_VITASTOR:
|
|
||||||
+ flags |= VIR_CONNECT_LIST_STORAGE_POOLS_VITASTOR;
|
|
||||||
+ break;
|
|
||||||
case VIR_STORAGE_POOL_LAST:
|
|
||||||
break;
|
|
||||||
}
|
|
|
@ -1,190 +0,0 @@
|
||||||
Index: pve-qemu-kvm-8.1.2/block/meson.build
|
|
||||||
===================================================================
|
|
||||||
--- pve-qemu-kvm-8.1.2.orig/block/meson.build
|
|
||||||
+++ pve-qemu-kvm-8.1.2/block/meson.build
|
|
||||||
@@ -123,6 +123,7 @@ foreach m : [
|
|
||||||
[libnfs, 'nfs', files('nfs.c')],
|
|
||||||
[libssh, 'ssh', files('ssh.c')],
|
|
||||||
[rbd, 'rbd', files('rbd.c')],
|
|
||||||
+ [vitastor, 'vitastor', files('vitastor.c')],
|
|
||||||
]
|
|
||||||
if m[0].found()
|
|
||||||
module_ss = ss.source_set()
|
|
||||||
Index: pve-qemu-kvm-8.1.2/meson.build
|
|
||||||
===================================================================
|
|
||||||
--- pve-qemu-kvm-8.1.2.orig/meson.build
|
|
||||||
+++ pve-qemu-kvm-8.1.2/meson.build
|
|
||||||
@@ -1303,6 +1303,26 @@ if not get_option('rbd').auto() or have_
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
+vitastor = not_found
|
|
||||||
+if not get_option('vitastor').auto() or have_block
|
|
||||||
+ libvitastor_client = cc.find_library('vitastor_client', has_headers: ['vitastor_c.h'],
|
|
||||||
+ required: get_option('vitastor'))
|
|
||||||
+ if libvitastor_client.found()
|
|
||||||
+ if cc.links('''
|
|
||||||
+ #include <vitastor_c.h>
|
|
||||||
+ int main(void) {
|
|
||||||
+ vitastor_c_create_qemu(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
||||||
+ return 0;
|
|
||||||
+ }''', dependencies: libvitastor_client)
|
|
||||||
+ vitastor = declare_dependency(dependencies: libvitastor_client)
|
|
||||||
+ elif get_option('vitastor').enabled()
|
|
||||||
+ error('could not link libvitastor_client')
|
|
||||||
+ else
|
|
||||||
+ warning('could not link libvitastor_client, disabling')
|
|
||||||
+ endif
|
|
||||||
+ endif
|
|
||||||
+endif
|
|
||||||
+
|
|
||||||
glusterfs = not_found
|
|
||||||
glusterfs_ftruncate_has_stat = false
|
|
||||||
glusterfs_iocb_has_stat = false
|
|
||||||
@@ -2123,6 +2143,7 @@ if numa.found()
|
|
||||||
endif
|
|
||||||
config_host_data.set('CONFIG_OPENGL', opengl.found())
|
|
||||||
config_host_data.set('CONFIG_RBD', rbd.found())
|
|
||||||
+config_host_data.set('CONFIG_VITASTOR', vitastor.found())
|
|
||||||
config_host_data.set('CONFIG_RDMA', rdma.found())
|
|
||||||
config_host_data.set('CONFIG_SAFESTACK', get_option('safe_stack'))
|
|
||||||
config_host_data.set('CONFIG_SDL', sdl.found())
|
|
||||||
@@ -4298,6 +4319,7 @@ summary_info += {'fdt support': fd
|
|
||||||
summary_info += {'libcap-ng support': libcap_ng}
|
|
||||||
summary_info += {'bpf support': libbpf}
|
|
||||||
summary_info += {'rbd support': rbd}
|
|
||||||
+summary_info += {'vitastor support': vitastor}
|
|
||||||
summary_info += {'smartcard support': cacard}
|
|
||||||
summary_info += {'U2F support': u2f}
|
|
||||||
summary_info += {'libusb': libusb}
|
|
||||||
Index: pve-qemu-kvm-8.1.2/meson_options.txt
|
|
||||||
===================================================================
|
|
||||||
--- pve-qemu-kvm-8.1.2.orig/meson_options.txt
|
|
||||||
+++ pve-qemu-kvm-8.1.2/meson_options.txt
|
|
||||||
@@ -186,6 +186,8 @@ option('lzo', type : 'feature', value :
|
|
||||||
description: 'lzo compression support')
|
|
||||||
option('rbd', type : 'feature', value : 'auto',
|
|
||||||
description: 'Ceph block device driver')
|
|
||||||
+option('vitastor', type : 'feature', value : 'auto',
|
|
||||||
+ description: 'Vitastor block device driver')
|
|
||||||
option('opengl', type : 'feature', value : 'auto',
|
|
||||||
description: 'OpenGL support')
|
|
||||||
option('rdma', type : 'feature', value : 'auto',
|
|
||||||
Index: pve-qemu-kvm-8.1.2/qapi/block-core.json
|
|
||||||
===================================================================
|
|
||||||
--- pve-qemu-kvm-8.1.2.orig/qapi/block-core.json
|
|
||||||
+++ pve-qemu-kvm-8.1.2/qapi/block-core.json
|
|
||||||
@@ -3403,7 +3403,7 @@
|
|
||||||
'raw', 'rbd',
|
|
||||||
{ 'name': 'replication', 'if': 'CONFIG_REPLICATION' },
|
|
||||||
'pbs',
|
|
||||||
- 'ssh', 'throttle', 'vdi', 'vhdx',
|
|
||||||
+ 'ssh', 'throttle', 'vdi', 'vhdx', 'vitastor',
|
|
||||||
{ 'name': 'virtio-blk-vfio-pci', 'if': 'CONFIG_BLKIO' },
|
|
||||||
{ 'name': 'virtio-blk-vhost-user', 'if': 'CONFIG_BLKIO' },
|
|
||||||
{ 'name': 'virtio-blk-vhost-vdpa', 'if': 'CONFIG_BLKIO' },
|
|
||||||
@@ -4465,6 +4465,28 @@
|
|
||||||
'*server': ['InetSocketAddressBase'] } }
|
|
||||||
|
|
||||||
##
|
|
||||||
+# @BlockdevOptionsVitastor:
|
|
||||||
+#
|
|
||||||
+# Driver specific block device options for vitastor
|
|
||||||
+#
|
|
||||||
+# @image: Image name
|
|
||||||
+# @inode: Inode number
|
|
||||||
+# @pool: Pool ID
|
|
||||||
+# @size: Desired image size in bytes
|
|
||||||
+# @config-path: Path to Vitastor configuration
|
|
||||||
+# @etcd-host: etcd connection address(es)
|
|
||||||
+# @etcd-prefix: etcd key/value prefix
|
|
||||||
+##
|
|
||||||
+{ 'struct': 'BlockdevOptionsVitastor',
|
|
||||||
+ 'data': { '*inode': 'uint64',
|
|
||||||
+ '*pool': 'uint64',
|
|
||||||
+ '*size': 'uint64',
|
|
||||||
+ '*image': 'str',
|
|
||||||
+ '*config-path': 'str',
|
|
||||||
+ '*etcd-host': 'str',
|
|
||||||
+ '*etcd-prefix': 'str' } }
|
|
||||||
+
|
|
||||||
+##
|
|
||||||
# @ReplicationMode:
|
|
||||||
#
|
|
||||||
# An enumeration of replication modes.
|
|
||||||
@@ -4923,6 +4945,7 @@
|
|
||||||
'throttle': 'BlockdevOptionsThrottle',
|
|
||||||
'vdi': 'BlockdevOptionsGenericFormat',
|
|
||||||
'vhdx': 'BlockdevOptionsGenericFormat',
|
|
||||||
+ 'vitastor': 'BlockdevOptionsVitastor',
|
|
||||||
'virtio-blk-vfio-pci':
|
|
||||||
{ 'type': 'BlockdevOptionsVirtioBlkVfioPci',
|
|
||||||
'if': 'CONFIG_BLKIO' },
|
|
||||||
@@ -5360,6 +5383,17 @@
|
|
||||||
'*encrypt' : 'RbdEncryptionCreateOptions' } }
|
|
||||||
|
|
||||||
##
|
|
||||||
+# @BlockdevCreateOptionsVitastor:
|
|
||||||
+#
|
|
||||||
+# Driver specific image creation options for Vitastor.
|
|
||||||
+#
|
|
||||||
+# @size: Size of the virtual disk in bytes
|
|
||||||
+##
|
|
||||||
+{ 'struct': 'BlockdevCreateOptionsVitastor',
|
|
||||||
+ 'data': { 'location': 'BlockdevOptionsVitastor',
|
|
||||||
+ 'size': 'size' } }
|
|
||||||
+
|
|
||||||
+##
|
|
||||||
# @BlockdevVmdkSubformat:
|
|
||||||
#
|
|
||||||
# Subformat options for VMDK images
|
|
||||||
@@ -5581,6 +5615,7 @@
|
|
||||||
'ssh': 'BlockdevCreateOptionsSsh',
|
|
||||||
'vdi': 'BlockdevCreateOptionsVdi',
|
|
||||||
'vhdx': 'BlockdevCreateOptionsVhdx',
|
|
||||||
+ 'vitastor': 'BlockdevCreateOptionsVitastor',
|
|
||||||
'vmdk': 'BlockdevCreateOptionsVmdk',
|
|
||||||
'vpc': 'BlockdevCreateOptionsVpc'
|
|
||||||
} }
|
|
||||||
Index: pve-qemu-kvm-8.1.2/scripts/ci/org.centos/stream/8/x86_64/configure
|
|
||||||
===================================================================
|
|
||||||
--- pve-qemu-kvm-8.1.2.orig/scripts/ci/org.centos/stream/8/x86_64/configure
|
|
||||||
+++ pve-qemu-kvm-8.1.2/scripts/ci/org.centos/stream/8/x86_64/configure
|
|
||||||
@@ -30,7 +30,7 @@
|
|
||||||
--with-suffix="qemu-kvm" \
|
|
||||||
--firmwarepath=/usr/share/qemu-firmware \
|
|
||||||
--target-list="x86_64-softmmu" \
|
|
||||||
---block-drv-rw-whitelist="qcow2,raw,file,host_device,nbd,iscsi,rbd,blkdebug,luks,null-co,nvme,copy-on-read,throttle,gluster" \
|
|
||||||
+--block-drv-rw-whitelist="qcow2,raw,file,host_device,nbd,iscsi,rbd,vitastor,blkdebug,luks,null-co,nvme,copy-on-read,throttle,gluster" \
|
|
||||||
--audio-drv-list="" \
|
|
||||||
--block-drv-ro-whitelist="vmdk,vhdx,vpc,https,ssh" \
|
|
||||||
--with-coroutine=ucontext \
|
|
||||||
@@ -176,6 +176,7 @@
|
|
||||||
--enable-opengl \
|
|
||||||
--enable-pie \
|
|
||||||
--enable-rbd \
|
|
||||||
+--enable-vitastor \
|
|
||||||
--enable-rdma \
|
|
||||||
--enable-seccomp \
|
|
||||||
--enable-snappy \
|
|
||||||
Index: pve-qemu-kvm-8.1.2/scripts/meson-buildoptions.sh
|
|
||||||
===================================================================
|
|
||||||
--- pve-qemu-kvm-8.1.2.orig/scripts/meson-buildoptions.sh
|
|
||||||
+++ pve-qemu-kvm-8.1.2/scripts/meson-buildoptions.sh
|
|
||||||
@@ -153,6 +153,7 @@ meson_options_help() {
|
|
||||||
printf "%s\n" ' qed qed image format support'
|
|
||||||
printf "%s\n" ' qga-vss build QGA VSS support (broken with MinGW)'
|
|
||||||
printf "%s\n" ' rbd Ceph block device driver'
|
|
||||||
+ printf "%s\n" ' vitastor Vitastor block device driver'
|
|
||||||
printf "%s\n" ' rdma Enable RDMA-based migration'
|
|
||||||
printf "%s\n" ' replication replication support'
|
|
||||||
printf "%s\n" ' sdl SDL user interface'
|
|
||||||
@@ -416,6 +417,8 @@ _meson_option_parse() {
|
|
||||||
--disable-qom-cast-debug) printf "%s" -Dqom_cast_debug=false ;;
|
|
||||||
--enable-rbd) printf "%s" -Drbd=enabled ;;
|
|
||||||
--disable-rbd) printf "%s" -Drbd=disabled ;;
|
|
||||||
+ --enable-vitastor) printf "%s" -Dvitastor=enabled ;;
|
|
||||||
+ --disable-vitastor) printf "%s" -Dvitastor=disabled ;;
|
|
||||||
--enable-rdma) printf "%s" -Drdma=enabled ;;
|
|
||||||
--disable-rdma) printf "%s" -Drdma=disabled ;;
|
|
||||||
--enable-replication) printf "%s" -Dreplication=enabled ;;
|
|
|
@ -1,190 +0,0 @@
|
||||||
diff --git a/block/meson.build b/block/meson.build
|
|
||||||
index 529fc172c6..d542dc0609 100644
|
|
||||||
--- a/block/meson.build
|
|
||||||
+++ b/block/meson.build
|
|
||||||
@@ -110,6 +110,7 @@ foreach m : [
|
|
||||||
[libnfs, 'nfs', files('nfs.c')],
|
|
||||||
[libssh, 'ssh', files('ssh.c')],
|
|
||||||
[rbd, 'rbd', files('rbd.c')],
|
|
||||||
+ [vitastor, 'vitastor', files('vitastor.c')],
|
|
||||||
]
|
|
||||||
if m[0].found()
|
|
||||||
module_ss = ss.source_set()
|
|
||||||
diff --git a/meson.build b/meson.build
|
|
||||||
index a9c4f28247..8496cf13f1 100644
|
|
||||||
--- a/meson.build
|
|
||||||
+++ b/meson.build
|
|
||||||
@@ -1303,6 +1303,26 @@ if not get_option('rbd').auto() or have_block
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
+vitastor = not_found
|
|
||||||
+if not get_option('vitastor').auto() or have_block
|
|
||||||
+ libvitastor_client = cc.find_library('vitastor_client', has_headers: ['vitastor_c.h'],
|
|
||||||
+ required: get_option('vitastor'))
|
|
||||||
+ if libvitastor_client.found()
|
|
||||||
+ if cc.links('''
|
|
||||||
+ #include <vitastor_c.h>
|
|
||||||
+ int main(void) {
|
|
||||||
+ vitastor_c_create_qemu(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
||||||
+ return 0;
|
|
||||||
+ }''', dependencies: libvitastor_client)
|
|
||||||
+ vitastor = declare_dependency(dependencies: libvitastor_client)
|
|
||||||
+ elif get_option('vitastor').enabled()
|
|
||||||
+ error('could not link libvitastor_client')
|
|
||||||
+ else
|
|
||||||
+ warning('could not link libvitastor_client, disabling')
|
|
||||||
+ endif
|
|
||||||
+ endif
|
|
||||||
+endif
|
|
||||||
+
|
|
||||||
glusterfs = not_found
|
|
||||||
glusterfs_ftruncate_has_stat = false
|
|
||||||
glusterfs_iocb_has_stat = false
|
|
||||||
@@ -2119,6 +2139,7 @@ if numa.found()
|
|
||||||
endif
|
|
||||||
config_host_data.set('CONFIG_OPENGL', opengl.found())
|
|
||||||
config_host_data.set('CONFIG_RBD', rbd.found())
|
|
||||||
+config_host_data.set('CONFIG_VITASTOR', vitastor.found())
|
|
||||||
config_host_data.set('CONFIG_RDMA', rdma.found())
|
|
||||||
config_host_data.set('CONFIG_SAFESTACK', get_option('safe_stack'))
|
|
||||||
config_host_data.set('CONFIG_SDL', sdl.found())
|
|
||||||
@@ -4286,6 +4307,7 @@ summary_info += {'fdt support': fdt_opt == 'disabled' ? false : fdt_opt}
|
|
||||||
summary_info += {'libcap-ng support': libcap_ng}
|
|
||||||
summary_info += {'bpf support': libbpf}
|
|
||||||
summary_info += {'rbd support': rbd}
|
|
||||||
+summary_info += {'vitastor support': vitastor}
|
|
||||||
summary_info += {'smartcard support': cacard}
|
|
||||||
summary_info += {'U2F support': u2f}
|
|
||||||
summary_info += {'libusb': libusb}
|
|
||||||
diff --git a/meson_options.txt b/meson_options.txt
|
|
||||||
index ae6d8f469d..e3d9f8404d 100644
|
|
||||||
--- a/meson_options.txt
|
|
||||||
+++ b/meson_options.txt
|
|
||||||
@@ -186,6 +186,8 @@ option('lzo', type : 'feature', value : 'auto',
|
|
||||||
description: 'lzo compression support')
|
|
||||||
option('rbd', type : 'feature', value : 'auto',
|
|
||||||
description: 'Ceph block device driver')
|
|
||||||
+option('vitastor', type : 'feature', value : 'auto',
|
|
||||||
+ description: 'Vitastor block device driver')
|
|
||||||
option('opengl', type : 'feature', value : 'auto',
|
|
||||||
description: 'OpenGL support')
|
|
||||||
option('rdma', type : 'feature', value : 'auto',
|
|
||||||
diff --git a/qapi/block-core.json b/qapi/block-core.json
|
|
||||||
index 2b1d493d6e..90673fdbdc 100644
|
|
||||||
--- a/qapi/block-core.json
|
|
||||||
+++ b/qapi/block-core.json
|
|
||||||
@@ -3146,7 +3146,7 @@
|
|
||||||
'parallels', 'preallocate', 'qcow', 'qcow2', 'qed', 'quorum',
|
|
||||||
'raw', 'rbd',
|
|
||||||
{ 'name': 'replication', 'if': 'CONFIG_REPLICATION' },
|
|
||||||
- 'ssh', 'throttle', 'vdi', 'vhdx',
|
|
||||||
+ 'ssh', 'throttle', 'vdi', 'vhdx', 'vitastor',
|
|
||||||
{ 'name': 'virtio-blk-vfio-pci', 'if': 'CONFIG_BLKIO' },
|
|
||||||
{ 'name': 'virtio-blk-vhost-user', 'if': 'CONFIG_BLKIO' },
|
|
||||||
{ 'name': 'virtio-blk-vhost-vdpa', 'if': 'CONFIG_BLKIO' },
|
|
||||||
@@ -4196,6 +4196,28 @@
|
|
||||||
'*key-secret': 'str',
|
|
||||||
'*server': ['InetSocketAddressBase'] } }
|
|
||||||
|
|
||||||
+##
|
|
||||||
+# @BlockdevOptionsVitastor:
|
|
||||||
+#
|
|
||||||
+# Driver specific block device options for vitastor
|
|
||||||
+#
|
|
||||||
+# @image: Image name
|
|
||||||
+# @inode: Inode number
|
|
||||||
+# @pool: Pool ID
|
|
||||||
+# @size: Desired image size in bytes
|
|
||||||
+# @config-path: Path to Vitastor configuration
|
|
||||||
+# @etcd-host: etcd connection address(es)
|
|
||||||
+# @etcd-prefix: etcd key/value prefix
|
|
||||||
+##
|
|
||||||
+{ 'struct': 'BlockdevOptionsVitastor',
|
|
||||||
+ 'data': { '*inode': 'uint64',
|
|
||||||
+ '*pool': 'uint64',
|
|
||||||
+ '*size': 'uint64',
|
|
||||||
+ '*image': 'str',
|
|
||||||
+ '*config-path': 'str',
|
|
||||||
+ '*etcd-host': 'str',
|
|
||||||
+ '*etcd-prefix': 'str' } }
|
|
||||||
+
|
|
||||||
##
|
|
||||||
# @ReplicationMode:
|
|
||||||
#
|
|
||||||
@@ -4654,6 +4676,7 @@
|
|
||||||
'throttle': 'BlockdevOptionsThrottle',
|
|
||||||
'vdi': 'BlockdevOptionsGenericFormat',
|
|
||||||
'vhdx': 'BlockdevOptionsGenericFormat',
|
|
||||||
+ 'vitastor': 'BlockdevOptionsVitastor',
|
|
||||||
'virtio-blk-vfio-pci':
|
|
||||||
{ 'type': 'BlockdevOptionsVirtioBlkVfioPci',
|
|
||||||
'if': 'CONFIG_BLKIO' },
|
|
||||||
@@ -5089,6 +5112,17 @@
|
|
||||||
'*cluster-size' : 'size',
|
|
||||||
'*encrypt' : 'RbdEncryptionCreateOptions' } }
|
|
||||||
|
|
||||||
+##
|
|
||||||
+# @BlockdevCreateOptionsVitastor:
|
|
||||||
+#
|
|
||||||
+# Driver specific image creation options for Vitastor.
|
|
||||||
+#
|
|
||||||
+# @size: Size of the virtual disk in bytes
|
|
||||||
+##
|
|
||||||
+{ 'struct': 'BlockdevCreateOptionsVitastor',
|
|
||||||
+ 'data': { 'location': 'BlockdevOptionsVitastor',
|
|
||||||
+ 'size': 'size' } }
|
|
||||||
+
|
|
||||||
##
|
|
||||||
# @BlockdevVmdkSubformat:
|
|
||||||
#
|
|
||||||
@@ -5311,6 +5345,7 @@
|
|
||||||
'ssh': 'BlockdevCreateOptionsSsh',
|
|
||||||
'vdi': 'BlockdevCreateOptionsVdi',
|
|
||||||
'vhdx': 'BlockdevCreateOptionsVhdx',
|
|
||||||
+ 'vitastor': 'BlockdevCreateOptionsVitastor',
|
|
||||||
'vmdk': 'BlockdevCreateOptionsVmdk',
|
|
||||||
'vpc': 'BlockdevCreateOptionsVpc'
|
|
||||||
} }
|
|
||||||
diff --git a/scripts/ci/org.centos/stream/8/x86_64/configure b/scripts/ci/org.centos/stream/8/x86_64/configure
|
|
||||||
index d02b09a4b9..f0b5fbfef3 100755
|
|
||||||
--- a/scripts/ci/org.centos/stream/8/x86_64/configure
|
|
||||||
+++ b/scripts/ci/org.centos/stream/8/x86_64/configure
|
|
||||||
@@ -30,7 +30,7 @@
|
|
||||||
--with-suffix="qemu-kvm" \
|
|
||||||
--firmwarepath=/usr/share/qemu-firmware \
|
|
||||||
--target-list="x86_64-softmmu" \
|
|
||||||
---block-drv-rw-whitelist="qcow2,raw,file,host_device,nbd,iscsi,rbd,blkdebug,luks,null-co,nvme,copy-on-read,throttle,gluster" \
|
|
||||||
+--block-drv-rw-whitelist="qcow2,raw,file,host_device,nbd,iscsi,rbd,vitastor,blkdebug,luks,null-co,nvme,copy-on-read,throttle,gluster" \
|
|
||||||
--audio-drv-list="" \
|
|
||||||
--block-drv-ro-whitelist="vmdk,vhdx,vpc,https,ssh" \
|
|
||||||
--with-coroutine=ucontext \
|
|
||||||
@@ -176,6 +176,7 @@
|
|
||||||
--enable-opengl \
|
|
||||||
--enable-pie \
|
|
||||||
--enable-rbd \
|
|
||||||
+--enable-vitastor \
|
|
||||||
--enable-rdma \
|
|
||||||
--enable-seccomp \
|
|
||||||
--enable-snappy \
|
|
||||||
diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh
|
|
||||||
index d7020af175..94958eb6fa 100644
|
|
||||||
--- a/scripts/meson-buildoptions.sh
|
|
||||||
+++ b/scripts/meson-buildoptions.sh
|
|
||||||
@@ -153,6 +153,7 @@ meson_options_help() {
|
|
||||||
printf "%s\n" ' qed qed image format support'
|
|
||||||
printf "%s\n" ' qga-vss build QGA VSS support (broken with MinGW)'
|
|
||||||
printf "%s\n" ' rbd Ceph block device driver'
|
|
||||||
+ printf "%s\n" ' vitastor Vitastor block device driver'
|
|
||||||
printf "%s\n" ' rdma Enable RDMA-based migration'
|
|
||||||
printf "%s\n" ' replication replication support'
|
|
||||||
printf "%s\n" ' sdl SDL user interface'
|
|
||||||
@@ -416,6 +417,8 @@ _meson_option_parse() {
|
|
||||||
--disable-qom-cast-debug) printf "%s" -Dqom_cast_debug=false ;;
|
|
||||||
--enable-rbd) printf "%s" -Drbd=enabled ;;
|
|
||||||
--disable-rbd) printf "%s" -Drbd=disabled ;;
|
|
||||||
+ --enable-vitastor) printf "%s" -Dvitastor=enabled ;;
|
|
||||||
+ --disable-vitastor) printf "%s" -Dvitastor=disabled ;;
|
|
||||||
--enable-rdma) printf "%s" -Drdma=enabled ;;
|
|
||||||
--disable-rdma) printf "%s" -Drdma=disabled ;;
|
|
||||||
--enable-replication) printf "%s" -Dreplication=enabled ;;
|
|
|
@ -1,28 +0,0 @@
|
||||||
name: Pull Request
|
|
||||||
about: Submit a pull request
|
|
||||||
body:
|
|
||||||
- type: textarea
|
|
||||||
id: description
|
|
||||||
attributes:
|
|
||||||
label: Description
|
|
||||||
description: Describe your pull request
|
|
||||||
placeholder: ""
|
|
||||||
value: ""
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: input
|
|
||||||
id: author
|
|
||||||
attributes:
|
|
||||||
label: Contributor Name
|
|
||||||
description: Contributor Name or Company Details if the Contributor is a company
|
|
||||||
placeholder: ""
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: checkboxes
|
|
||||||
id: terms
|
|
||||||
attributes:
|
|
||||||
label: CLA
|
|
||||||
description: By submitting this pull request, I accept [Vitastor CLA](https://git.yourcmc.ru/vitalif/vitastor/src/branch/master/CLA-en.md)
|
|
||||||
options:
|
|
||||||
- label: "I accept Vitastor CLA agreement: https://git.yourcmc.ru/vitalif/vitastor/src/branch/master/CLA-en.md"
|
|
||||||
required: true
|
|
|
@ -24,4 +24,4 @@ rm fio
|
||||||
mv fio-copy fio
|
mv fio-copy fio
|
||||||
FIO=`rpm -qi fio | perl -e 'while(<>) { /^Epoch[\s:]+(\S+)/ && print "$1:"; /^Version[\s:]+(\S+)/ && print $1; /^Release[\s:]+(\S+)/ && print "-$1"; }'`
|
FIO=`rpm -qi fio | perl -e 'while(<>) { /^Epoch[\s:]+(\S+)/ && print "$1:"; /^Version[\s:]+(\S+)/ && print $1; /^Release[\s:]+(\S+)/ && print "-$1"; }'`
|
||||||
perl -i -pe 's/(Requires:\s*fio)([^\n]+)?/$1 = '$FIO'/' $VITASTOR/rpm/vitastor-el$EL.spec
|
perl -i -pe 's/(Requires:\s*fio)([^\n]+)?/$1 = '$FIO'/' $VITASTOR/rpm/vitastor-el$EL.spec
|
||||||
tar --transform 's#^#vitastor-1.4.4/#' --exclude 'rpm/*.rpm' -czf $VITASTOR/../vitastor-1.4.4$(rpm --eval '%dist').tar.gz *
|
tar --transform 's#^#vitastor-1.0.0/#' --exclude 'rpm/*.rpm' -czf $VITASTOR/../vitastor-1.0.0$(rpm --eval '%dist').tar.gz *
|
||||||
|
|
|
@ -15,7 +15,6 @@ RUN yumdownloader --disablerepo=centos-sclo-rh --source fio
|
||||||
RUN rpm --nomd5 -i fio*.src.rpm
|
RUN rpm --nomd5 -i fio*.src.rpm
|
||||||
RUN rm -f /etc/yum.repos.d/CentOS-Media.repo
|
RUN rm -f /etc/yum.repos.d/CentOS-Media.repo
|
||||||
RUN cd ~/rpmbuild/SPECS && yum-builddep -y fio.spec
|
RUN cd ~/rpmbuild/SPECS && yum-builddep -y fio.spec
|
||||||
RUN yum -y install cmake3
|
|
||||||
|
|
||||||
ADD https://vitastor.io/rpms/liburing-el7/liburing-0.7-2.el7.src.rpm /root
|
ADD https://vitastor.io/rpms/liburing-el7/liburing-0.7-2.el7.src.rpm /root
|
||||||
|
|
||||||
|
@ -36,7 +35,7 @@ ADD . /root/vitastor
|
||||||
RUN set -e; \
|
RUN set -e; \
|
||||||
cd /root/vitastor/rpm; \
|
cd /root/vitastor/rpm; \
|
||||||
sh build-tarball.sh; \
|
sh build-tarball.sh; \
|
||||||
cp /root/vitastor-1.4.4.el7.tar.gz ~/rpmbuild/SOURCES; \
|
cp /root/vitastor-1.0.0.el7.tar.gz ~/rpmbuild/SOURCES; \
|
||||||
cp vitastor-el7.spec ~/rpmbuild/SPECS/vitastor.spec; \
|
cp vitastor-el7.spec ~/rpmbuild/SPECS/vitastor.spec; \
|
||||||
cd ~/rpmbuild/SPECS/; \
|
cd ~/rpmbuild/SPECS/; \
|
||||||
rpmbuild -ba vitastor.spec; \
|
rpmbuild -ba vitastor.spec; \
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
Name: vitastor
|
Name: vitastor
|
||||||
Version: 1.4.4
|
Version: 1.0.0
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: Vitastor, a fast software-defined clustered block storage
|
Summary: Vitastor, a fast software-defined clustered block storage
|
||||||
|
|
||||||
License: Vitastor Network Public License 1.1
|
License: Vitastor Network Public License 1.1
|
||||||
URL: https://vitastor.io/
|
URL: https://vitastor.io/
|
||||||
Source0: vitastor-1.4.4.el7.tar.gz
|
Source0: vitastor-1.0.0.el7.tar.gz
|
||||||
|
|
||||||
BuildRequires: liburing-devel >= 0.6
|
BuildRequires: liburing-devel >= 0.6
|
||||||
BuildRequires: gperftools-devel
|
BuildRequires: gperftools-devel
|
||||||
|
@ -16,7 +16,7 @@ BuildRequires: jerasure-devel
|
||||||
BuildRequires: libisa-l-devel
|
BuildRequires: libisa-l-devel
|
||||||
BuildRequires: gf-complete-devel
|
BuildRequires: gf-complete-devel
|
||||||
BuildRequires: libibverbs-devel
|
BuildRequires: libibverbs-devel
|
||||||
BuildRequires: cmake3
|
BuildRequires: cmake
|
||||||
Requires: vitastor-osd = %{version}-%{release}
|
Requires: vitastor-osd = %{version}-%{release}
|
||||||
Requires: vitastor-mon = %{version}-%{release}
|
Requires: vitastor-mon = %{version}-%{release}
|
||||||
Requires: vitastor-client = %{version}-%{release}
|
Requires: vitastor-client = %{version}-%{release}
|
||||||
|
@ -94,7 +94,7 @@ Vitastor fio drivers for benchmarking.
|
||||||
|
|
||||||
%build
|
%build
|
||||||
. /opt/rh/devtoolset-9/enable
|
. /opt/rh/devtoolset-9/enable
|
||||||
%cmake3 .
|
%cmake .
|
||||||
%make_build
|
%make_build
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ ADD . /root/vitastor
|
||||||
RUN set -e; \
|
RUN set -e; \
|
||||||
cd /root/vitastor/rpm; \
|
cd /root/vitastor/rpm; \
|
||||||
sh build-tarball.sh; \
|
sh build-tarball.sh; \
|
||||||
cp /root/vitastor-1.4.4.el8.tar.gz ~/rpmbuild/SOURCES; \
|
cp /root/vitastor-1.0.0.el8.tar.gz ~/rpmbuild/SOURCES; \
|
||||||
cp vitastor-el8.spec ~/rpmbuild/SPECS/vitastor.spec; \
|
cp vitastor-el8.spec ~/rpmbuild/SPECS/vitastor.spec; \
|
||||||
cd ~/rpmbuild/SPECS/; \
|
cd ~/rpmbuild/SPECS/; \
|
||||||
rpmbuild -ba vitastor.spec; \
|
rpmbuild -ba vitastor.spec; \
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
Name: vitastor
|
Name: vitastor
|
||||||
Version: 1.4.4
|
Version: 1.0.0
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: Vitastor, a fast software-defined clustered block storage
|
Summary: Vitastor, a fast software-defined clustered block storage
|
||||||
|
|
||||||
License: Vitastor Network Public License 1.1
|
License: Vitastor Network Public License 1.1
|
||||||
URL: https://vitastor.io/
|
URL: https://vitastor.io/
|
||||||
Source0: vitastor-1.4.4.el8.tar.gz
|
Source0: vitastor-1.0.0.el8.tar.gz
|
||||||
|
|
||||||
BuildRequires: liburing-devel >= 0.6
|
BuildRequires: liburing-devel >= 0.6
|
||||||
BuildRequires: gperftools-devel
|
BuildRequires: gperftools-devel
|
||||||
|
|
|
@ -18,7 +18,7 @@ ADD . /root/vitastor
|
||||||
RUN set -e; \
|
RUN set -e; \
|
||||||
cd /root/vitastor/rpm; \
|
cd /root/vitastor/rpm; \
|
||||||
sh build-tarball.sh; \
|
sh build-tarball.sh; \
|
||||||
cp /root/vitastor-1.4.4.el9.tar.gz ~/rpmbuild/SOURCES; \
|
cp /root/vitastor-1.0.0.el9.tar.gz ~/rpmbuild/SOURCES; \
|
||||||
cp vitastor-el9.spec ~/rpmbuild/SPECS/vitastor.spec; \
|
cp vitastor-el9.spec ~/rpmbuild/SPECS/vitastor.spec; \
|
||||||
cd ~/rpmbuild/SPECS/; \
|
cd ~/rpmbuild/SPECS/; \
|
||||||
rpmbuild -ba vitastor.spec; \
|
rpmbuild -ba vitastor.spec; \
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
Name: vitastor
|
Name: vitastor
|
||||||
Version: 1.4.4
|
Version: 1.0.0
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: Vitastor, a fast software-defined clustered block storage
|
Summary: Vitastor, a fast software-defined clustered block storage
|
||||||
|
|
||||||
License: Vitastor Network Public License 1.1
|
License: Vitastor Network Public License 1.1
|
||||||
URL: https://vitastor.io/
|
URL: https://vitastor.io/
|
||||||
Source0: vitastor-1.4.4.el9.tar.gz
|
Source0: vitastor-1.0.0.el9.tar.gz
|
||||||
|
|
||||||
BuildRequires: liburing-devel >= 0.6
|
BuildRequires: liburing-devel >= 0.6
|
||||||
BuildRequires: gperftools-devel
|
BuildRequires: gperftools-devel
|
||||||
|
|
|
@ -16,11 +16,10 @@ if("${CMAKE_INSTALL_PREFIX}" MATCHES "^/usr/local/?$")
|
||||||
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
|
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_definitions(-DVERSION="1.4.4")
|
add_definitions(-DVERSION="1.0.0")
|
||||||
add_definitions(-Wall -Wno-sign-compare -Wno-comment -Wno-parentheses -Wno-pointer-arith -fdiagnostics-color=always -fno-omit-frame-pointer -I ${CMAKE_SOURCE_DIR}/src)
|
add_definitions(-Wall -Wno-sign-compare -Wno-comment -Wno-parentheses -Wno-pointer-arith -fdiagnostics-color=always -I ${CMAKE_SOURCE_DIR}/src)
|
||||||
add_link_options(-fno-omit-frame-pointer)
|
|
||||||
if (${WITH_ASAN})
|
if (${WITH_ASAN})
|
||||||
add_definitions(-fsanitize=address)
|
add_definitions(-fsanitize=address -fno-omit-frame-pointer)
|
||||||
add_link_options(-fsanitize=address -fno-omit-frame-pointer)
|
add_link_options(-fsanitize=address -fno-omit-frame-pointer)
|
||||||
endif (${WITH_ASAN})
|
endif (${WITH_ASAN})
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <set>
|
|
||||||
|
|
||||||
#include "addr_util.h"
|
#include "addr_util.h"
|
||||||
|
|
||||||
|
@ -136,7 +135,7 @@ std::vector<std::string> getifaddr_list(std::vector<std::string> mask_cfg, bool
|
||||||
throw std::runtime_error((include_v6 ? "Invalid IPv4 address mask: " : "Invalid IP address mask: ") + mask);
|
throw std::runtime_error((include_v6 ? "Invalid IPv4 address mask: " : "Invalid IP address mask: ") + mask);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::set<std::string> addresses;
|
std::vector<std::string> addresses;
|
||||||
ifaddrs *list, *ifa;
|
ifaddrs *list, *ifa;
|
||||||
if (getifaddrs(&list) == -1)
|
if (getifaddrs(&list) == -1)
|
||||||
{
|
{
|
||||||
|
@ -150,8 +149,7 @@ std::vector<std::string> getifaddr_list(std::vector<std::string> mask_cfg, bool
|
||||||
}
|
}
|
||||||
int family = ifa->ifa_addr->sa_family;
|
int family = ifa->ifa_addr->sa_family;
|
||||||
if ((family == AF_INET || family == AF_INET6 && include_v6) &&
|
if ((family == AF_INET || family == AF_INET6 && include_v6) &&
|
||||||
// Do not skip loopback addresses if the address filter is specified
|
(ifa->ifa_flags & (IFF_UP | IFF_RUNNING | IFF_LOOPBACK)) == (IFF_UP | IFF_RUNNING))
|
||||||
(ifa->ifa_flags & (IFF_UP | IFF_RUNNING | (masks.size() ? 0 : IFF_LOOPBACK))) == (IFF_UP | IFF_RUNNING))
|
|
||||||
{
|
{
|
||||||
void *addr_ptr;
|
void *addr_ptr;
|
||||||
if (family == AF_INET)
|
if (family == AF_INET)
|
||||||
|
@ -184,11 +182,11 @@ std::vector<std::string> getifaddr_list(std::vector<std::string> mask_cfg, bool
|
||||||
{
|
{
|
||||||
throw std::runtime_error(std::string("inet_ntop: ") + strerror(errno));
|
throw std::runtime_error(std::string("inet_ntop: ") + strerror(errno));
|
||||||
}
|
}
|
||||||
addresses.insert(std::string(addr));
|
addresses.push_back(std::string(addr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
freeifaddrs(list);
|
freeifaddrs(list);
|
||||||
return std::vector<std::string>(addresses.begin(), addresses.end());
|
return addresses;
|
||||||
}
|
}
|
||||||
|
|
||||||
int create_and_bind_socket(std::string bind_address, int bind_port, int listen_backlog, int *listening_port)
|
int create_and_bind_socket(std::string bind_address, int bind_port, int listen_backlog, int *listening_port)
|
||||||
|
|
|
@ -82,3 +82,8 @@ uint32_t blockstore_t::get_bitmap_granularity()
|
||||||
{
|
{
|
||||||
return impl->get_bitmap_granularity();
|
return impl->get_bitmap_granularity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool blockstore_t::wants_fsync()
|
||||||
|
{
|
||||||
|
return impl->wants_fsync();
|
||||||
|
}
|
||||||
|
|
|
@ -226,4 +226,7 @@ public:
|
||||||
uint64_t get_journal_size();
|
uint64_t get_journal_size();
|
||||||
|
|
||||||
uint32_t get_bitmap_granularity();
|
uint32_t get_bitmap_granularity();
|
||||||
|
|
||||||
|
// Returns true if writing can stall due to a lack of fsync
|
||||||
|
bool wants_fsync();
|
||||||
};
|
};
|
||||||
|
|
|
@ -184,7 +184,8 @@ void journal_flusher_t::mark_trim_possible()
|
||||||
if (trim_wanted > 0)
|
if (trim_wanted > 0)
|
||||||
{
|
{
|
||||||
dequeuing = true;
|
dequeuing = true;
|
||||||
journal_trim_counter = 0;
|
if (!journal_trim_counter)
|
||||||
|
journal_trim_counter = journal_trim_interval;
|
||||||
bs->ringloop->wakeup();
|
bs->ringloop->wakeup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -365,7 +366,7 @@ resume_0:
|
||||||
!flusher->flush_queue.size() || !flusher->dequeuing)
|
!flusher->flush_queue.size() || !flusher->dequeuing)
|
||||||
{
|
{
|
||||||
stop_flusher:
|
stop_flusher:
|
||||||
if (flusher->trim_wanted > 0 && !flusher->journal_trim_counter)
|
if (flusher->trim_wanted > 0 && flusher->journal_trim_counter > 0)
|
||||||
{
|
{
|
||||||
// Attempt forced trim
|
// Attempt forced trim
|
||||||
flusher->active_flushers++;
|
flusher->active_flushers++;
|
||||||
|
@ -1345,6 +1346,7 @@ bool journal_flusher_co::trim_journal(int wait_base)
|
||||||
else if (wait_state == wait_base+2) goto resume_2;
|
else if (wait_state == wait_base+2) goto resume_2;
|
||||||
else if (wait_state == wait_base+3) goto resume_3;
|
else if (wait_state == wait_base+3) goto resume_3;
|
||||||
else if (wait_state == wait_base+4) goto resume_4;
|
else if (wait_state == wait_base+4) goto resume_4;
|
||||||
|
flusher->journal_trim_counter = 0;
|
||||||
new_trim_pos = bs->journal.get_trim_pos();
|
new_trim_pos = bs->journal.get_trim_pos();
|
||||||
if (new_trim_pos != bs->journal.used_start)
|
if (new_trim_pos != bs->journal.used_start)
|
||||||
{
|
{
|
||||||
|
@ -1370,8 +1372,7 @@ bool journal_flusher_co::trim_journal(int wait_base)
|
||||||
? (uint32_t)JE_START_V1_SIZE : (uint32_t)JE_START_V2_SIZE),
|
? (uint32_t)JE_START_V1_SIZE : (uint32_t)JE_START_V2_SIZE),
|
||||||
.reserved = 0,
|
.reserved = 0,
|
||||||
.journal_start = new_trim_pos,
|
.journal_start = new_trim_pos,
|
||||||
.version = (uint64_t)(!bs->dsk.data_csum_type && ((journal_entry_start*)flusher->journal_superblock)->version == JOURNAL_VERSION_V1
|
.version = JOURNAL_VERSION_V2,
|
||||||
? JOURNAL_VERSION_V1 : JOURNAL_VERSION_V2),
|
|
||||||
.data_csum_type = bs->dsk.data_csum_type,
|
.data_csum_type = bs->dsk.data_csum_type,
|
||||||
.csum_block_size = bs->dsk.csum_block_size,
|
.csum_block_size = bs->dsk.csum_block_size,
|
||||||
};
|
};
|
||||||
|
@ -1417,7 +1418,6 @@ bool journal_flusher_co::trim_journal(int wait_base)
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
flusher->journal_trim_counter = 0;
|
|
||||||
flusher->trimming = false;
|
flusher->trimming = false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -163,10 +163,20 @@ void blockstore_impl_t::loop()
|
||||||
}
|
}
|
||||||
else if (op->opcode == BS_OP_SYNC)
|
else if (op->opcode == BS_OP_SYNC)
|
||||||
{
|
{
|
||||||
// sync only completed writes?
|
// wait for all small writes to be submitted
|
||||||
|
// wait for all big writes to complete, submit data device fsync
|
||||||
// wait for the data device fsync to complete, then submit journal writes for big writes
|
// wait for the data device fsync to complete, then submit journal writes for big writes
|
||||||
// then submit an fsync operation
|
// then submit an fsync operation
|
||||||
|
if (0 && has_writes)
|
||||||
|
{
|
||||||
|
// Can't submit SYNC before previous writes
|
||||||
|
continue;
|
||||||
|
}
|
||||||
wr_st = continue_sync(op);
|
wr_st = continue_sync(op);
|
||||||
|
if (wr_st != 2)
|
||||||
|
{
|
||||||
|
has_writes = wr_st > 0 ? 1 : 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (op->opcode == BS_OP_STABLE)
|
else if (op->opcode == BS_OP_STABLE)
|
||||||
{
|
{
|
||||||
|
@ -195,10 +205,6 @@ void blockstore_impl_t::loop()
|
||||||
// ring is full, stop submission
|
// ring is full, stop submission
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (PRIV(op)->wait_for == WAIT_JOURNAL)
|
|
||||||
{
|
|
||||||
PRIV(op)->wait_detail2 = (unstable_writes.size()+unstable_unsynced);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (op_idx != new_idx)
|
if (op_idx != new_idx)
|
||||||
|
@ -277,8 +283,7 @@ void blockstore_impl_t::check_wait(blockstore_op_t *op)
|
||||||
}
|
}
|
||||||
else if (PRIV(op)->wait_for == WAIT_JOURNAL)
|
else if (PRIV(op)->wait_for == WAIT_JOURNAL)
|
||||||
{
|
{
|
||||||
if (journal.used_start == PRIV(op)->wait_detail &&
|
if (journal.used_start == PRIV(op)->wait_detail)
|
||||||
(unstable_writes.size()+unstable_unsynced) == PRIV(op)->wait_detail2)
|
|
||||||
{
|
{
|
||||||
// do not submit
|
// do not submit
|
||||||
#ifdef BLOCKSTORE_DEBUG
|
#ifdef BLOCKSTORE_DEBUG
|
||||||
|
@ -553,14 +558,13 @@ void blockstore_impl_t::process_list(blockstore_op_t *op)
|
||||||
if (stable_count >= stable_alloc)
|
if (stable_count >= stable_alloc)
|
||||||
{
|
{
|
||||||
stable_alloc *= 2;
|
stable_alloc *= 2;
|
||||||
obj_ver_id* nst = (obj_ver_id*)realloc(stable, sizeof(obj_ver_id) * stable_alloc);
|
stable = (obj_ver_id*)realloc(stable, sizeof(obj_ver_id) * stable_alloc);
|
||||||
if (!nst)
|
if (!stable)
|
||||||
{
|
{
|
||||||
op->retval = -ENOMEM;
|
op->retval = -ENOMEM;
|
||||||
FINISH_OP(op);
|
FINISH_OP(op);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
stable = nst;
|
|
||||||
}
|
}
|
||||||
stable[stable_count++] = {
|
stable[stable_count++] = {
|
||||||
.oid = clean_it->first,
|
.oid = clean_it->first,
|
||||||
|
@ -638,8 +642,8 @@ void blockstore_impl_t::process_list(blockstore_op_t *op)
|
||||||
if (stable_count >= stable_alloc)
|
if (stable_count >= stable_alloc)
|
||||||
{
|
{
|
||||||
stable_alloc += 32768;
|
stable_alloc += 32768;
|
||||||
obj_ver_id *nst = (obj_ver_id*)realloc(stable, sizeof(obj_ver_id) * stable_alloc);
|
stable = (obj_ver_id*)realloc(stable, sizeof(obj_ver_id) * stable_alloc);
|
||||||
if (!nst)
|
if (!stable)
|
||||||
{
|
{
|
||||||
if (unstable)
|
if (unstable)
|
||||||
free(unstable);
|
free(unstable);
|
||||||
|
@ -647,7 +651,6 @@ void blockstore_impl_t::process_list(blockstore_op_t *op)
|
||||||
FINISH_OP(op);
|
FINISH_OP(op);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
stable = nst;
|
|
||||||
}
|
}
|
||||||
stable[stable_count++] = dirty_it->first;
|
stable[stable_count++] = dirty_it->first;
|
||||||
}
|
}
|
||||||
|
@ -663,8 +666,8 @@ void blockstore_impl_t::process_list(blockstore_op_t *op)
|
||||||
if (unstable_count >= unstable_alloc)
|
if (unstable_count >= unstable_alloc)
|
||||||
{
|
{
|
||||||
unstable_alloc += 32768;
|
unstable_alloc += 32768;
|
||||||
obj_ver_id *nst = (obj_ver_id*)realloc(unstable, sizeof(obj_ver_id) * unstable_alloc);
|
unstable = (obj_ver_id*)realloc(unstable, sizeof(obj_ver_id) * unstable_alloc);
|
||||||
if (!nst)
|
if (!unstable)
|
||||||
{
|
{
|
||||||
if (stable)
|
if (stable)
|
||||||
free(stable);
|
free(stable);
|
||||||
|
@ -672,7 +675,6 @@ void blockstore_impl_t::process_list(blockstore_op_t *op)
|
||||||
FINISH_OP(op);
|
FINISH_OP(op);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
unstable = nst;
|
|
||||||
}
|
}
|
||||||
unstable[unstable_count++] = dirty_it->first;
|
unstable[unstable_count++] = dirty_it->first;
|
||||||
}
|
}
|
||||||
|
@ -692,8 +694,8 @@ void blockstore_impl_t::process_list(blockstore_op_t *op)
|
||||||
if (stable_count+unstable_count > stable_alloc)
|
if (stable_count+unstable_count > stable_alloc)
|
||||||
{
|
{
|
||||||
stable_alloc = stable_count+unstable_count;
|
stable_alloc = stable_count+unstable_count;
|
||||||
obj_ver_id *nst = (obj_ver_id*)realloc(stable, sizeof(obj_ver_id) * stable_alloc);
|
stable = (obj_ver_id*)realloc(stable, sizeof(obj_ver_id) * stable_alloc);
|
||||||
if (!nst)
|
if (!stable)
|
||||||
{
|
{
|
||||||
if (unstable)
|
if (unstable)
|
||||||
free(unstable);
|
free(unstable);
|
||||||
|
@ -701,7 +703,6 @@ void blockstore_impl_t::process_list(blockstore_op_t *op)
|
||||||
FINISH_OP(op);
|
FINISH_OP(op);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
stable = nst;
|
|
||||||
}
|
}
|
||||||
// Copy unstable entries
|
// Copy unstable entries
|
||||||
for (int i = 0; i < unstable_count; i++)
|
for (int i = 0; i < unstable_count; i++)
|
||||||
|
@ -733,3 +734,15 @@ void blockstore_impl_t::disk_error_abort(const char *op, int retval, int expecte
|
||||||
fprintf(stderr, "Disk %s failed: result is %d, expected %d. Can't continue, sorry :-(\n", op, retval, expected);
|
fprintf(stderr, "Disk %s failed: result is %d, expected %d. Can't continue, sorry :-(\n", op, retval, expected);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool blockstore_impl_t::wants_fsync()
|
||||||
|
{
|
||||||
|
if (!unstable_writes.size())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint64_t journal_free_space = journal.next_free < journal.used_start
|
||||||
|
? (journal.used_start - journal.next_free)
|
||||||
|
: (journal.len - journal.next_free + journal.used_start - journal.block_size);
|
||||||
|
return journal_fsync_feedback_limit > 0 && journal.len-journal_free_space >= journal_fsync_feedback_limit;
|
||||||
|
}
|
||||||
|
|
|
@ -55,7 +55,6 @@
|
||||||
#define IS_JOURNAL(st) (((st) & 0x0F) == BS_ST_SMALL_WRITE)
|
#define IS_JOURNAL(st) (((st) & 0x0F) == BS_ST_SMALL_WRITE)
|
||||||
#define IS_BIG_WRITE(st) (((st) & 0x0F) == BS_ST_BIG_WRITE)
|
#define IS_BIG_WRITE(st) (((st) & 0x0F) == BS_ST_BIG_WRITE)
|
||||||
#define IS_DELETE(st) (((st) & 0x0F) == BS_ST_DELETE)
|
#define IS_DELETE(st) (((st) & 0x0F) == BS_ST_DELETE)
|
||||||
#define IS_INSTANT(st) (((st) & BS_ST_TYPE_MASK) == BS_ST_DELETE || ((st) & BS_ST_INSTANT))
|
|
||||||
|
|
||||||
#define BS_SUBMIT_CHECK_SQES(n) \
|
#define BS_SUBMIT_CHECK_SQES(n) \
|
||||||
if (ringloop->sqes_left() < (n))\
|
if (ringloop->sqes_left() < (n))\
|
||||||
|
@ -202,7 +201,7 @@ struct blockstore_op_private_t
|
||||||
{
|
{
|
||||||
// Wait status
|
// Wait status
|
||||||
int wait_for;
|
int wait_for;
|
||||||
uint64_t wait_detail, wait_detail2;
|
uint64_t wait_detail;
|
||||||
int pending_ops;
|
int pending_ops;
|
||||||
int op_state;
|
int op_state;
|
||||||
|
|
||||||
|
@ -265,6 +264,8 @@ class blockstore_impl_t
|
||||||
int throttle_threshold_us = 50;
|
int throttle_threshold_us = 50;
|
||||||
// Maximum writes between automatically added fsync operations
|
// Maximum writes between automatically added fsync operations
|
||||||
uint64_t autosync_writes = 128;
|
uint64_t autosync_writes = 128;
|
||||||
|
// Maximum free space in the journal in bytes to start sending fsync feedback to primary OSDs
|
||||||
|
uint64_t journal_fsync_feedback_limit = 0;
|
||||||
/******* END OF OPTIONS *******/
|
/******* END OF OPTIONS *******/
|
||||||
|
|
||||||
struct ring_consumer_t ring_consumer;
|
struct ring_consumer_t ring_consumer;
|
||||||
|
@ -275,10 +276,9 @@ class blockstore_impl_t
|
||||||
blockstore_dirty_db_t dirty_db;
|
blockstore_dirty_db_t dirty_db;
|
||||||
std::vector<blockstore_op_t*> submit_queue;
|
std::vector<blockstore_op_t*> submit_queue;
|
||||||
std::vector<obj_ver_id> unsynced_big_writes, unsynced_small_writes;
|
std::vector<obj_ver_id> unsynced_big_writes, unsynced_small_writes;
|
||||||
int unsynced_big_write_count = 0, unstable_unsynced = 0;
|
int unsynced_big_write_count = 0;
|
||||||
int unsynced_queued_ops = 0;
|
int unsynced_queued_ops = 0;
|
||||||
allocator *data_alloc = NULL;
|
allocator *data_alloc = NULL;
|
||||||
uint64_t used_blocks = 0;
|
|
||||||
uint8_t *zero_object;
|
uint8_t *zero_object;
|
||||||
|
|
||||||
void *metadata_buffer = NULL;
|
void *metadata_buffer = NULL;
|
||||||
|
@ -378,7 +378,7 @@ class blockstore_impl_t
|
||||||
// Stabilize
|
// Stabilize
|
||||||
int dequeue_stable(blockstore_op_t *op);
|
int dequeue_stable(blockstore_op_t *op);
|
||||||
int continue_stable(blockstore_op_t *op);
|
int continue_stable(blockstore_op_t *op);
|
||||||
void mark_stable(obj_ver_id ov, bool forget_dirty = false);
|
void mark_stable(const obj_ver_id & ov, bool forget_dirty = false);
|
||||||
void stabilize_object(object_id oid, uint64_t max_ver);
|
void stabilize_object(object_id oid, uint64_t max_ver);
|
||||||
blockstore_op_t* selective_sync(blockstore_op_t *op);
|
blockstore_op_t* selective_sync(blockstore_op_t *op);
|
||||||
int split_stab_op(blockstore_op_t *op, std::function<int(obj_ver_id v)> decider);
|
int split_stab_op(blockstore_op_t *op, std::function<int(obj_ver_id v)> decider);
|
||||||
|
@ -432,7 +432,9 @@ public:
|
||||||
|
|
||||||
inline uint32_t get_block_size() { return dsk.data_block_size; }
|
inline uint32_t get_block_size() { return dsk.data_block_size; }
|
||||||
inline uint64_t get_block_count() { return dsk.block_count; }
|
inline uint64_t get_block_count() { return dsk.block_count; }
|
||||||
inline uint64_t get_free_block_count() { return dsk.block_count - used_blocks; }
|
inline uint64_t get_free_block_count() { return data_alloc->get_free_count(); }
|
||||||
inline uint32_t get_bitmap_granularity() { return dsk.disk_alignment; }
|
inline uint32_t get_bitmap_granularity() { return dsk.disk_alignment; }
|
||||||
inline uint64_t get_journal_size() { return dsk.journal_len; }
|
inline uint64_t get_journal_size() { return dsk.journal_len; }
|
||||||
|
|
||||||
|
bool wants_fsync();
|
||||||
};
|
};
|
||||||
|
|
|
@ -376,7 +376,6 @@ bool blockstore_init_meta::handle_meta_block(uint8_t *buf, uint64_t entries_per_
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bs->inode_space_stats[entry->oid.inode] += bs->dsk.data_block_size;
|
bs->inode_space_stats[entry->oid.inode] += bs->dsk.data_block_size;
|
||||||
bs->used_blocks++;
|
|
||||||
}
|
}
|
||||||
entries_loaded++;
|
entries_loaded++;
|
||||||
#ifdef BLOCKSTORE_DEBUG
|
#ifdef BLOCKSTORE_DEBUG
|
||||||
|
@ -554,7 +553,7 @@ resume_1:
|
||||||
}
|
}
|
||||||
if (je_start->size == JE_START_V0_SIZE ||
|
if (je_start->size == JE_START_V0_SIZE ||
|
||||||
(je_start->version != JOURNAL_VERSION_V1 || je_start->size != JE_START_V1_SIZE) &&
|
(je_start->version != JOURNAL_VERSION_V1 || je_start->size != JE_START_V1_SIZE) &&
|
||||||
(je_start->version != JOURNAL_VERSION_V2 || je_start->size != JE_START_V2_SIZE && je_start->size != JE_START_V1_SIZE))
|
(je_start->version != JOURNAL_VERSION_V2 || je_start->size != JE_START_V2_SIZE))
|
||||||
{
|
{
|
||||||
fprintf(
|
fprintf(
|
||||||
stderr, "The code only supports journal versions 2 and 1, but it is %lu on disk."
|
stderr, "The code only supports journal versions 2 and 1, but it is %lu on disk."
|
||||||
|
@ -563,8 +562,7 @@ resume_1:
|
||||||
);
|
);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
if (je_start->version == JOURNAL_VERSION_V1 ||
|
if (je_start->version == JOURNAL_VERSION_V1)
|
||||||
je_start->version == JOURNAL_VERSION_V2 && je_start->size == JE_START_V1_SIZE)
|
|
||||||
{
|
{
|
||||||
je_start->data_csum_type = 0;
|
je_start->data_csum_type = 0;
|
||||||
je_start->csum_block_size = 0;
|
je_start->csum_block_size = 0;
|
||||||
|
@ -733,9 +731,8 @@ int blockstore_init_journal::handle_journal_part(void *buf, uint64_t done_pos, u
|
||||||
resume:
|
resume:
|
||||||
while (pos < bs->journal.block_size)
|
while (pos < bs->journal.block_size)
|
||||||
{
|
{
|
||||||
auto buf_pos = proc_pos - done_pos + pos;
|
journal_entry *je = (journal_entry*)((uint8_t*)buf + proc_pos - done_pos + pos);
|
||||||
journal_entry *je = (journal_entry*)((uint8_t*)buf + buf_pos);
|
if (je->magic != JOURNAL_MAGIC || je_crc32(je) != je->crc32 ||
|
||||||
if (je->magic != JOURNAL_MAGIC || buf_pos+je->size > len || je_crc32(je) != je->crc32 ||
|
|
||||||
je->type < JE_MIN || je->type > JE_MAX || started && je->crc32_prev != crc32_last)
|
je->type < JE_MIN || je->type > JE_MAX || started && je->crc32_prev != crc32_last)
|
||||||
{
|
{
|
||||||
if (pos == 0)
|
if (pos == 0)
|
||||||
|
@ -1182,7 +1179,6 @@ void blockstore_init_journal::erase_dirty_object(blockstore_dirty_db_t::iterator
|
||||||
sp -= bs->dsk.data_block_size;
|
sp -= bs->dsk.data_block_size;
|
||||||
else
|
else
|
||||||
bs->inode_space_stats.erase(oid.inode);
|
bs->inode_space_stats.erase(oid.inode);
|
||||||
bs->used_blocks--;
|
|
||||||
}
|
}
|
||||||
bs->erase_dirty(dirty_it, dirty_end, clean_loc);
|
bs->erase_dirty(dirty_it, dirty_end, clean_loc);
|
||||||
// Remove it from the flusher's queue, too
|
// Remove it from the flusher's queue, too
|
||||||
|
|
|
@ -144,10 +144,7 @@ journal_entry* prefill_single_journal_entry(journal_t & journal, uint16_t type,
|
||||||
journal.sector_info[journal.cur_sector].written = false;
|
journal.sector_info[journal.cur_sector].written = false;
|
||||||
journal.sector_info[journal.cur_sector].offset = journal.next_free;
|
journal.sector_info[journal.cur_sector].offset = journal.next_free;
|
||||||
journal.in_sector_pos = 0;
|
journal.in_sector_pos = 0;
|
||||||
auto next_next_free = (journal.next_free+journal.block_size) < journal.len ? journal.next_free + journal.block_size : journal.block_size;
|
journal.next_free = (journal.next_free+journal.block_size) < journal.len ? journal.next_free + journal.block_size : journal.block_size;
|
||||||
// double check that next_free doesn't cross used_start from the left
|
|
||||||
assert(journal.next_free >= journal.used_start || next_next_free < journal.used_start);
|
|
||||||
journal.next_free = next_next_free;
|
|
||||||
memset(journal.inmemory
|
memset(journal.inmemory
|
||||||
? (uint8_t*)journal.buffer + journal.sector_info[journal.cur_sector].offset
|
? (uint8_t*)journal.buffer + journal.sector_info[journal.cur_sector].offset
|
||||||
: (uint8_t*)journal.sector_buf + journal.block_size*journal.cur_sector, 0, journal.block_size);
|
: (uint8_t*)journal.sector_buf + journal.block_size*journal.cur_sector, 0, journal.block_size);
|
||||||
|
|
|
@ -13,6 +13,12 @@
|
||||||
#define JOURNAL_BUFFER_SIZE 4*1024*1024
|
#define JOURNAL_BUFFER_SIZE 4*1024*1024
|
||||||
#define JOURNAL_ENTRY_HEADER_SIZE 16
|
#define JOURNAL_ENTRY_HEADER_SIZE 16
|
||||||
|
|
||||||
|
// We reserve some extra space for future stabilize requests during writes
|
||||||
|
// FIXME: This value should be dynamic i.e. Blockstore ideally shouldn't allow
|
||||||
|
// writing more than can be stabilized afterwards
|
||||||
|
#define JOURNAL_STABILIZE_RESERVATION 65536
|
||||||
|
#define JOURNAL_INSTANT_RESERVATION 131072
|
||||||
|
|
||||||
// Journal entries
|
// Journal entries
|
||||||
// Journal entries are linked to each other by their crc32 value
|
// Journal entries are linked to each other by their crc32 value
|
||||||
// The journal is almost a blockchain, because object versions constantly increase
|
// The journal is almost a blockchain, because object versions constantly increase
|
||||||
|
|
|
@ -4,6 +4,25 @@
|
||||||
#include <sys/file.h>
|
#include <sys/file.h>
|
||||||
#include "blockstore_impl.h"
|
#include "blockstore_impl.h"
|
||||||
|
|
||||||
|
static uint64_t parse_fsync_feedback(blockstore_config_t & config, uint64_t journal_len)
|
||||||
|
{
|
||||||
|
uint64_t journal_fsync_feedback_limit = 0;
|
||||||
|
if (config.find("journal_min_free_bytes") == config.end() &&
|
||||||
|
config.find("journal_min_free_percent") == config.end())
|
||||||
|
{
|
||||||
|
journal_fsync_feedback_limit = 90 * journal_len / 100;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
journal_fsync_feedback_limit = strtoull(config["journal_min_free_bytes"].c_str(), NULL, 10);
|
||||||
|
if (!journal_fsync_feedback_limit)
|
||||||
|
{
|
||||||
|
journal_fsync_feedback_limit = strtoull(config["journal_min_free_percent"].c_str(), NULL, 10) * journal_len / 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return journal_fsync_feedback_limit;
|
||||||
|
}
|
||||||
|
|
||||||
void blockstore_impl_t::parse_config(blockstore_config_t & config, bool init)
|
void blockstore_impl_t::parse_config(blockstore_config_t & config, bool init)
|
||||||
{
|
{
|
||||||
// Online-configurable options:
|
// Online-configurable options:
|
||||||
|
@ -19,7 +38,7 @@ void blockstore_impl_t::parse_config(blockstore_config_t & config, bool init)
|
||||||
throttle_target_mbs = strtoull(config["throttle_target_mbs"].c_str(), NULL, 10);
|
throttle_target_mbs = strtoull(config["throttle_target_mbs"].c_str(), NULL, 10);
|
||||||
throttle_target_parallelism = strtoull(config["throttle_target_parallelism"].c_str(), NULL, 10);
|
throttle_target_parallelism = strtoull(config["throttle_target_parallelism"].c_str(), NULL, 10);
|
||||||
throttle_threshold_us = strtoull(config["throttle_threshold_us"].c_str(), NULL, 10);
|
throttle_threshold_us = strtoull(config["throttle_threshold_us"].c_str(), NULL, 10);
|
||||||
if (config["autosync_writes"] != "")
|
if (config.find("autosync_writes") != config.end())
|
||||||
{
|
{
|
||||||
autosync_writes = strtoull(config["autosync_writes"].c_str(), NULL, 10);
|
autosync_writes = strtoull(config["autosync_writes"].c_str(), NULL, 10);
|
||||||
}
|
}
|
||||||
|
@ -53,6 +72,8 @@ void blockstore_impl_t::parse_config(blockstore_config_t & config, bool init)
|
||||||
}
|
}
|
||||||
if (!init)
|
if (!init)
|
||||||
{
|
{
|
||||||
|
// has to be parsed after dsk.parse_config(), thus repeated here for online update
|
||||||
|
journal_fsync_feedback_limit = parse_fsync_feedback(config, journal.len);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Offline-configurable options:
|
// Offline-configurable options:
|
||||||
|
@ -96,6 +117,7 @@ void blockstore_impl_t::parse_config(blockstore_config_t & config, bool init)
|
||||||
config["journal_no_same_sector_overwrites"] == "1" || config["journal_no_same_sector_overwrites"] == "yes";
|
config["journal_no_same_sector_overwrites"] == "1" || config["journal_no_same_sector_overwrites"] == "yes";
|
||||||
journal.inmemory = config["inmemory_journal"] != "false" && config["inmemory_journal"] != "0" &&
|
journal.inmemory = config["inmemory_journal"] != "false" && config["inmemory_journal"] != "0" &&
|
||||||
config["inmemory_journal"] != "no";
|
config["inmemory_journal"] != "no";
|
||||||
|
journal_fsync_feedback_limit = parse_fsync_feedback(config, journal.len);
|
||||||
// Validate
|
// Validate
|
||||||
if (journal.sector_count < 2)
|
if (journal.sector_count < 2)
|
||||||
{
|
{
|
||||||
|
|
|
@ -412,40 +412,11 @@ resume_4:
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
void blockstore_impl_t::mark_stable(obj_ver_id v, bool forget_dirty)
|
void blockstore_impl_t::mark_stable(const obj_ver_id & v, bool forget_dirty)
|
||||||
{
|
{
|
||||||
auto dirty_it = dirty_db.find(v);
|
auto dirty_it = dirty_db.find(v);
|
||||||
if (dirty_it != dirty_db.end())
|
if (dirty_it != dirty_db.end())
|
||||||
{
|
{
|
||||||
if (IS_INSTANT(dirty_it->second.state))
|
|
||||||
{
|
|
||||||
// 'Instant' (non-EC) operations may complete and try to become stable out of order. Prevent it.
|
|
||||||
auto back_it = dirty_it;
|
|
||||||
while (back_it != dirty_db.begin())
|
|
||||||
{
|
|
||||||
back_it--;
|
|
||||||
if (back_it->first.oid != v.oid)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!IS_STABLE(back_it->second.state))
|
|
||||||
{
|
|
||||||
// There are preceding unstable versions, can't flush <v>
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
dirty_it++;
|
|
||||||
if (dirty_it == dirty_db.end() || dirty_it->first.oid != v.oid ||
|
|
||||||
!IS_SYNCED(dirty_it->second.state))
|
|
||||||
{
|
|
||||||
dirty_it--;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
v.version = dirty_it->first.version;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
bool was_stable = IS_STABLE(dirty_it->second.state);
|
bool was_stable = IS_STABLE(dirty_it->second.state);
|
||||||
|
@ -474,7 +445,6 @@ void blockstore_impl_t::mark_stable(obj_ver_id v, bool forget_dirty)
|
||||||
if (!exists)
|
if (!exists)
|
||||||
{
|
{
|
||||||
inode_space_stats[dirty_it->first.oid.inode] += dsk.data_block_size;
|
inode_space_stats[dirty_it->first.oid.inode] += dsk.data_block_size;
|
||||||
used_blocks++;
|
|
||||||
}
|
}
|
||||||
big_to_flush++;
|
big_to_flush++;
|
||||||
}
|
}
|
||||||
|
@ -485,7 +455,6 @@ void blockstore_impl_t::mark_stable(obj_ver_id v, bool forget_dirty)
|
||||||
sp -= dsk.data_block_size;
|
sp -= dsk.data_block_size;
|
||||||
else
|
else
|
||||||
inode_space_stats.erase(dirty_it->first.oid.inode);
|
inode_space_stats.erase(dirty_it->first.oid.inode);
|
||||||
used_blocks--;
|
|
||||||
big_to_flush++;
|
big_to_flush++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@ int blockstore_impl_t::continue_sync(blockstore_op_t *op)
|
||||||
{
|
{
|
||||||
if (immediate_commit == IMMEDIATE_ALL)
|
if (immediate_commit == IMMEDIATE_ALL)
|
||||||
{
|
{
|
||||||
// We can return immediately because sync is only dequeued after all previous writes
|
|
||||||
op->retval = 0;
|
op->retval = 0;
|
||||||
FINISH_OP(op);
|
FINISH_OP(op);
|
||||||
return 2;
|
return 2;
|
||||||
|
@ -76,7 +75,6 @@ int blockstore_impl_t::continue_sync(blockstore_op_t *op)
|
||||||
// 2nd step: Data device is synced, prepare & write journal entries
|
// 2nd step: Data device is synced, prepare & write journal entries
|
||||||
// Check space in the journal and journal memory buffers
|
// Check space in the journal and journal memory buffers
|
||||||
blockstore_journal_check_t space_check(this);
|
blockstore_journal_check_t space_check(this);
|
||||||
auto reservation = (unstable_writes.size()+unstable_unsynced+PRIV(op)->sync_big_writes.size())*journal.block_size;
|
|
||||||
if (dsk.csum_block_size)
|
if (dsk.csum_block_size)
|
||||||
{
|
{
|
||||||
// More complex check because all journal entries have different lengths
|
// More complex check because all journal entries have different lengths
|
||||||
|
@ -86,14 +84,15 @@ int blockstore_impl_t::continue_sync(blockstore_op_t *op)
|
||||||
left--;
|
left--;
|
||||||
auto & dirty_entry = dirty_db.at(sbw);
|
auto & dirty_entry = dirty_db.at(sbw);
|
||||||
uint64_t dyn_size = dsk.dirty_dyn_size(dirty_entry.offset, dirty_entry.len);
|
uint64_t dyn_size = dsk.dirty_dyn_size(dirty_entry.offset, dirty_entry.len);
|
||||||
if (!space_check.check_available(op, 1, sizeof(journal_entry_big_write) + dyn_size, left ? 0 : reservation))
|
if (!space_check.check_available(op, 1, sizeof(journal_entry_big_write) + dyn_size,
|
||||||
|
left == 0 ? JOURNAL_STABILIZE_RESERVATION : 0))
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!space_check.check_available(op, PRIV(op)->sync_big_writes.size(),
|
else if (!space_check.check_available(op, PRIV(op)->sync_big_writes.size(),
|
||||||
sizeof(journal_entry_big_write) + dsk.clean_entry_bitmap_size, reservation))
|
sizeof(journal_entry_big_write) + dsk.clean_entry_bitmap_size, JOURNAL_STABILIZE_RESERVATION))
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -184,11 +183,6 @@ void blockstore_impl_t::ack_sync(blockstore_op_t *op)
|
||||||
{
|
{
|
||||||
mark_stable(dirty_it->first);
|
mark_stable(dirty_it->first);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
unstable_unsynced--;
|
|
||||||
assert(unstable_unsynced >= 0);
|
|
||||||
}
|
|
||||||
dirty_it++;
|
dirty_it++;
|
||||||
while (dirty_it != dirty_db.end() && dirty_it->first.oid == it->oid)
|
while (dirty_it != dirty_db.end() && dirty_it->first.oid == it->oid)
|
||||||
{
|
{
|
||||||
|
@ -219,11 +213,6 @@ void blockstore_impl_t::ack_sync(blockstore_op_t *op)
|
||||||
{
|
{
|
||||||
mark_stable(*it);
|
mark_stable(*it);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
unstable_unsynced--;
|
|
||||||
assert(unstable_unsynced >= 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
op->retval = 0;
|
op->retval = 0;
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue