Устойчивые имена сетевых интерфейсов

Разработчики systemd продолжают вести нас в светлое будущее. В udev уже давно реализован механизм Predictable Network Interface Names, который представляет из себя кусочек кода на C, заполняющий переменные ID_NET_NAME_* исходя из свойств расположения физического сетевого устройства.

Считается, что подобный подход дает больше информации, чем прибитие гвоздями имен интерфейсов eth? к MAC-адресам в 70-persistent-net.rules. Однако, не обходится без недостатков. Например, при запуске системы под гипервизором VMWare ESXi, единственный сетевой интерфейс у меня называется eno16777728. Устройства, определяемые в DTB для одноплатных компьютеров на базе ARM, не поддерживаются схемой именования и имеют обычные имена eth?. А при подключении USB устройств, типа мобильных телефонов или модемов, схема именования генерирует скорее неустойчивые имена, потому-что в имя входит расположение устройства на шине USB, которое изменится при следующем подключении, таким образом придется настраивать интерфейс заново, потому-что предыдущие сохраненные настройки будут относиться к интерфейсу с другим именем.

К счастью, udev заполняет переменную ID_NET_NAME_MAC, представляющую имя интерфейса, основанное исключительно на его MAC-адресе. По-умолчанию, эта переменная не используется, но можно поменять стандартное поведение для USB-устройств. Один способ - через настройку udev, второй - используя systemd.link, файл, используемый systemd, для конфигурации сетевых интерфейсов.

Создадим файл /etc/systemd/network/90-usb.link следуя инструкции:
[Match]
Path=*-usb-*
[Link]
NamePolicy=mac
Юнит-файл состоит из двух секций: [Match] для описания устройств к которым он относится и [Link] для описания того, что с ними делать. В примере выше мы просим systemd использовать политику именования основанную на MAC-адресах для всех устройств подключенных через USB. После выполнения systemctl daemon-reload можно подключить устройство и увидеть его интерфейс с именем enx112233445566. Достаточно длинное, но уникальное (в известных пределах) и не изменится при следующем подключении устройства.

Кроме изменения имени через systemd.link можно настроить WOL, MTU, ограничить скорость на интерфейсе, назначить устройству другой MAC-адрес.

Изменение размера раздела

Имеется диск с тремя разделами sda1 (/boot), sda2 (swap), sda3 (/). Необходимо увеличить размер раздела /boot за счет swap.

Сначала отмонтируем все разделы и проверим файловую систему.
# swapoff  /dev/sda2 
# umount /dev/sda1 
# e2fsck /dev/sda1 
e2fsck 1.42.6 (21-Sep-2012)
/dev/sda1: clean, 49/14056 files, 47157/56196 blocks

Используем parted для того, чтобы сначала уменьшить и передвинуть второй раздел, а затем расширить первый на освободившееся место. Так как второй раздел - swap, то его мы просто передвинем, не заботясь о содержимом. Иначе говоря, сначала мы совсем сломаем, а потом заново её разметим.

# parted /dev/sda
GNU Parted 2.4
Using /dev/sda
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) unit s                                                           
(parted) print                                                            
Model: ATA QEMU HARDDISK (scsi)
Disk /dev/sda: 120103200s
Sector size (logical/physical): 512B/512B
Partition Table: msdos

Number  Start     End         Size        Type     File system     Flags
 1      63s       112454s     112392s     primary  ext2            boot, type=83
 2      112455s   1686824s    1574370s    primary  linux-swap(v1)  type=82
 3      1686825s  120101939s  118415115s  primary  reiserfs        type=83

Для изменения раздела используется команда resize номер_раздела начало конец

(parted) resize 2 224973 1686824
WARNING: you are attempting to use parted to operate on (resize) a file system.
parted's file system manipulation code is not as robust as what you'll find in
dedicated, file-system-specific packages like e2fsprogs.  We recommend
you use parted only to manipulate partition tables, whenever possible.
Support for performing most operations on most types of file systems
will be removed in an upcoming release.
(parted) print
Model: ATA QEMU HARDDISK (scsi)
Disk /dev/sda: 120103200s
Sector size (logical/physical): 512B/512B
Partition Table: msdos

Number  Start     End         Size        Type     File system     Flags
 1      63s       112454s     112392s     primary  ext2            boot, type=83
 2      224973s   1686824s    1461852s    primary  linux-swap(v1)  type=82
 3      1686825s  120101939s  118415115s  primary  reiserfs        type=83

К сожалению в этот момент оно само подмонтировало всё назад, поэтому нужно снова отмонтировать первый раздел. Раздел swap в данный момент уже должен быть работоспособен, потому-что mkswap на нем выполнился сам автоматически. К сожалению, я не нашел способа отключить всю эту самодеятельность.

# umount /dev/sda1
# e2fsck -p /dev/sda1

Снова идем в parted:

# parted
GNU Parted 2.4
Using /dev/sda
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) unit s                                                           
(parted) print                                                            
Model: ATA QEMU HARDDISK (scsi)
Disk /dev/sda: 120103200s
Sector size (logical/physical): 512B/512B
Partition Table: msdos

Number  Start     End         Size        Type     File system     Flags
 1      63s       112454s     112392s     primary  ext2            boot, type=83
 2      224973s   1686824s    1461852s    primary  linux-swap(v1)  type=82
 3      1686825s  120101939s  118415115s  primary  reiserfs        type=83

(parted) resize 1 63 224972
WARNING: you are attempting to use parted to operate on (resize) a file system.
parted's file system manipulation code is not as robust as what you'll find in
dedicated, file-system-specific packages like e2fsprogs.  We recommend
you use parted only to manipulate partition tables, whenever possible.
Support for performing most operations on most types of file systems
will be removed in an upcoming release.
(parted) quit                                                             
Warning: You should reinstall your boot loader before rebooting.  Read section 4 of the Parted User documentation for more
information.
Information: You may need to update /etc/fstab.   

Готово, parted не только изменил размер раздела, но еще и молча расширил для нас файловую систему, а теперь предупреждает о необходимости обновить загрузчик.

snapper: настройка снимков подтомов

Показать список подтомов можно командой btrfs subvolume list /. Выберем какой-нибудь, например /var/lib/pgsql.
Создадим новую конфигурацию (профиль) для этого подтома: snapper -c pgsql create-config /var/lib/pgsql, здесь pgsql произвольное имя:
# snapper list-configs
Config | Subvolume     
-------+---------------
root   | /             
pgsql  | /var/lib/pgsql

Далее, все команды нужно выполнять с ключом -c pgsql, чтобы оперировать нужным профилем.

# snapper -c pgsql get-config
...      
NUMBER_CLEANUP         | yes           
NUMBER_LIMIT           | 50            
NUMBER_LIMIT_IMPORTANT | 10            
NUMBER_MIN_AGE         | 1800          

Здесь NUMBER_CLEANUP разрешает удалять старые снимки если их число превышает NUMBER_LIMIT и одновременно возраст (в секундах) больше NUMBER_MIN_AGE.

TIMELINE_CLEANUP       | yes           
TIMELINE_CREATE        | yes           
TIMELINE_LIMIT_DAILY   | 10            
TIMELINE_LIMIT_HOURLY  | 10            
TIMELINE_LIMIT_MONTHLY | 10            
TIMELINE_LIMIT_YEARLY  | 10            
TIMELINE_MIN_AGE       | 1800

Здесь соответствующая настройка разрешает автоматическое создание снимков раз в час. Команда TIMELINE_CLEANUP удаляет старые снимки, используя следующие правила: удаляется снимок только если он старше TIMELINE_MIN_AGE секунд и одновременно после удаления останутся TIMELINE_LIMIT_* снимков с соответствующими интервалами.
Таким образом, в вышеописанном стандартном случае всегда будут доступны снимки: 10 с интервалом в час с текущего момента, 10 с интервалом в один день с текущего момента, 10 с интервалом в один месяц и так далее.

Конвертация CVS в Mercurial

В системе контроля версий CVS есть так называемые модули с амперсандом, представляющие собой поддиректорию общую для нескольких модулей. Концепция похожая на сабмодули в git или сабрепозитории в mercurial.

Поскольку CVS немного устарел, нужно куда-то переехать. К сожалению, правильную работу с моделями с амперсандом не поддерживает ни одна из найденных утилит миграции. Об инкрементальной миграции (когда существуют два параллельных репозитория (разных систем контроля), которые каким-то чудом остаются друг с другом синхронизированы) тоже придется забыть.

Краткая схема действий. Сначала все модули из CVS конвертируются в формат fastimport, далее полученные дампы преобразуются и конвертируются в mercurial. Mercurial имеет в данной ситуации преимущество над git в том, что сабрепозитории управляются просто служебными файлами в дереве репозитория.

Все CVS модули можно разделить на две категории: те, которые содержат в себе модули с амперсандом, и те, которые нет (сами являются ими). Второй набор конвертируется просто. Для преобразования первого набора нужно пойти в cvsroot и вставить символические ссылки в те места, где должны были быть директории, соответствующие модулям с амперсандом. После преобразования каждый модуль из первого набора потеряет связь со своими вложенными модулями, и файлы с путями, соответствующими им, окажутся преобразованы несколько раз — один раз в репозитории относящимся к вложенному модулю и по разу в каждом репозитории модуля первого типа. Перед импортом в mercurial данная связь будет восстановлена.

Использование cvs2git из пакета cvs2svn не требует особой внимательности, кроме параметра COMMIT_THRESHOLD который склеивает коммиты. Дело в том, что в CVS каждый файл имеет свою историю, а в формате fast-import (как в git и mercurial) историю имеет весь репозиторий, поэтому отдельные изменения файлов надо слепить в прообразы будущих коммитов. По-умолчанию COMMIT_THRESHOLD равен 5 минутам, это значит, что изменения всех файлов попадающих в пятиминутное окно будут слеплены в один коммит.
Кроме того, можно задать отображение тегов, отображение авторов и другие параметры.

Для преобразования из формата fastimport в репозиторий mercurial используется расширение hg-fastimport. Отметим, что изначальный автор забросил этот проект и считает его неудачным, а более правильным подходом к преобразованию называет другой свой проект. Видимо поэтому расширение очень странным образом обрабатывает TAG.FIXUP ветви (в CVS можно было накладывать теги на те ревизии файлов, которые во времени друг с другом вообще никогда не встречались, поэтому для отображения таких тегов создается отдельная специальная ветка), что обычно требует подправления кода.

Формат fastimport представляет из себя файл (или несколько), задающий дерево репозитория в топологическом порядке. Каждый узел дерева, который затем станет коммитом, помечается уникальным текстовым идентификатором, например ":1000000003". После преобразования модулей второго типа в соответствующих новых репозиториях будет лежать файл .hg/shamap, с отображением коммитов mercurial в идентификаторы fastimport. Кроме того, каждый узел дерева в текстовом виде содержит список измененных файлов, например
M 100644 :30 sub/Makefile
здесь :30 — маркер блоба, т.е. фактически содержания файла. Используя путь файла можно идентифицировать все файлы занесенные к нам из вложенных модулей. Нам бы хотелось преобразовать fastimport дамп таким образом чтобы в этих местах возникли ссылки на внешние репозитории, например так
M 160000 2d1eb0523f244f7bb1d58f016583a25ba3d40426 sub

Для этого предлагается следующих алгоритм. У нас есть два дерева в виде дампов fastimport: дерево вложенного репозитория и дерево основного. Кроме того, есть отображение маркеров узлов вложенного репозитория на хэш коммита. Обойдя каждое из деревьев для каждого узла, мы можем полностью восстановить состояние, т.е. набор всех существовавших файлов и их содержимое. Кроме того, для основного репозитория возможно определить состояние вложенной директории (будущего сабрепозитория). На рисунке схематично изображены узлы основного дерева (пронумерованы арабскими цифрами) и узлы дерева сабрепозитория (пронумерованы латинскими буквами). Черными стрелками показана очередность коммитов, стрелка идет от более нового коммита к его предку.
Далее, сравнивая состояния вложенного репозитория и соответствующие под-состояния основного репозитория, можно построить отображение узлов основного дерева на вложенное (красные стрелки на рисунке).

Возможны ситуации когда связь многозначная. Вообразим, что в предложенном примере G откатывает F, поэтому имеет такое же содержимое что и E, кроме того, в другой ветке есть C с таким же содержимым. Сообщения коммитов могут отсутствовать, время коммитов может отличаеться. В данной ситуации предлагаются следующие критерии разрешения конфликта.

Так как основное дерево обходится в топологическом порядке, то к моменту когда мы оказывается в узле 5 узел 4 уже обработан и мы знаем что ему соответствует узел D. Для всех возможных кандидатов C, E, G рассчитаем расстояние между ними и узлом D. Узел D не достижим из C, поэтому C отбрасывается. Оставшийся выбор производится на основании сообщения коммита и близости времени. Либо можно представить простой алгоритм, который за не полиномиальное время назначит соответствие, однако без информации о времени и сообщениях коммитов все-равно не обойтись.

Таким образом, у нас есть отображение меток дерева основного репозитория на метки (или хэши) вложенного репозитория. Этой информации достаточно, чтобы заменить все нужные вхождения об изменениях файлов в дампе. После этого, дамп можно скормить в hg-fastimport.

Наиболее удобным способом расположения иерархии репозиториев на сервере оказался следующий. Каждый вложенный репозиторий кладется в корень иерархии, кроме того, в каждый проект. Причем этим вторичным клонам мы запретим запись. А на первичный репозиторий навесим хуки, которые будут дергать pull во всех клонах.
[extensions]
hgext.acl=

[hooks]
pretxnchangegroup.acl = python:hgext.acl.hook

[acl]
sources = serve push bundle

[acl.deny]
** = *

[paths]
default = /repos/subrepo

OBS: локальная сборка под ARM

Когда требуется локально пересобрать пакет под одну из архитектур ARM, например для тестирования сборки, можно воспользоваться следующим медленным подходом.

osc build --clean --alternative-project openSUSE:Factory:ARM qemu armv7l name.spec

Здесь указывается, что используется альтернативный проект для сборки — openSUSE:Factory:ARM (версию подставить по желанию), используется репозиторий qemu, который есть в этом проекте. Поэтому и указывается этот альтернативный проект.

Несмотря на то, что в данном примере указана архитектура armv7l, технически, это x86_64. В среду сборки будет установлен бинарный транслятор qemu, работающий через подсистему ядра binfmt. При инициализации ядро настраивается таким образом, что при виде сигнатуры ELF для архитектуры ARM вызывает бинарик как qemu-arm /usr/bin/..., подобно тому, как при виде символов "#!" в начале исполняемого файла вызывается указанный интерпретатор. Работает поэтому достаточно медленно.

К сожалению, с пакетами kiwi такое не проходит.

Сетевой стек на ATSAMD20

В предыдущих выпусках рассматривалась демонстрационная плата на базе 32-битного микроконтроллера ATSAMD20. Далее рассмотрим как прикрутить к нему сетевое приложение. Так как сам микроконтроллер не снабжен интерфейсом Ethernet, возьмем какое-нибудь готовое решение на базе ENC28J60 и прикрутим его через SPI.

Нам понадобится:
  • Atmel Software Framework — распространяемая под странным образом модифицированной BSD лицензией библиотека, реализующая удобный уровень индирекции при работе с переферией микроконтроллеров Atmel. Мы ей воспользуемся для работы через SPI. Внутри библиотеки весьма красивый код, понятный API и гора документации, исполненной в doxygen. Поэтому, основываясь на чувстве прекрасного, будем её использовать.
  • ec28j60driver — драйвер для ENC28J60, реализует высокоуровневые функции работы с Ethernet путем обмена через SPI. Возьмем этот, потому-что написан он не левой пяткой и был достаточно легко портирован для работы с Atmel Software Framework. (см. ветку asf)
  • lwIP — легковесный стек TCP/IP. Он отсылает или принимает Ethernet-пакеты через драйвер устройства и дальше разбирается что-же там внутри пришло. На сайте присутствует куча примеров.

Настройки для lwIP следующие:

lwoptions.h:
#define MEM_LIBC_MALLOC 1
#define MEMP_MEM_MALLOC 1
#define MEM_ALIGNMENT 4

cc.h:
#define BYTE_ORDER  LITTLE_ENDIAN

Стиль программирования приложений, определяемый использованием RAW API библиотеки lwIP, через 5 минут создает полное ощущение, что ты пишешь gen_server на erlang, но последний почему-то кривой, неудобный и недоделанный.

Далее в таблице приводится размер кода прошивки, получаемый при различных вариантах конфигурации:
config -O3 -Os
TCP/IP + DHCP + DEBUG(printf) 96k 82k
TCP/IP + DHCP 54k 40k
TCP/IP 45k 36k
UDP/IP + SNMP 54k 43k
UDP/IP 28k 24k

Как-то так оно и работает.

openSUSE 13.1 на BeagleBone Black

BeagleBone Black — одноплатный компьютер на основе процессора TI AM3358 (Cortex-A8), выпущенный весной прошлого года:

На нем вполне успешно может загрузиться и даже работать openSUSE 13.1. Готовые образы для нанесения на microSD карточку находятся здесь. Эти же самые образы должны в теории работать и на BeagleBone. Конечно, пока все на этапе интеграции и поиска ошибок, поэтому имеется подробная инструкция по запуску:
  1. Скачать, распаковать и сделать dd на карточку образ системы *.raw.xz
  2. Подключить отладочный TTL-RS232 порт (можно использовать, например, такой, но важно помнить, что разработчики BeagleBone Black не поставили на свою плату преобразователь уровней и сигнал с отладочного RS232 идет в виде 3.3V. Подключение через обычный COM-порт на большом компьютере обречено на провал.), руководствуясь распиновкой. Используя screen /dev/ttyUSB0 115200, во-первых, можно будет легко видеть все этапы начиная от работы u-boot. Во-вторых, запустится Yast2 firstboot и попросит вас задать root пароль, завести пользователя, установить временную зону и системное время (аналогично тому, как это происходит на соответствующем этапе установки с DVD), а для этого потребуется интерактивное взаимодействие.
  3. Помните, что для загрузки с внешней карты памяти при подаче питания на устройство нужно удерживать кнопку S2 (она же boot select switch). Подсказка — загрузившись с SD карты можно подмонтировать раздел встроенной MMC памяти и переименовать или удалить файл загрузчика MLO, после этого загрузка будет всегда начинаться с SD, но загрузиться с MMC больше не получится.

Технические подробности (тем, кто хочет продолжить ковыряния) изложены в списке рассылки: http://lists.opensuse.org/opensuse-arm/2014-01/msg00001.html.