Содержание статьи
Linux поддерживает очень разнообразный набор средств управления. С одной стороны, это хорошо — у пользователей есть право выбора. Но, с другой стороны, именно это многообразие не совсем удобно для корпоративного сектора, где зачастую нужно управлять десятками и сотнями компьютеров. В последнее время появилось немало инструментов для облегчения данной задачи, один из которых мы и рассмотрим.
Введение
Прежде чем переходить к описанию собственно OpenLMI, стоит сделать историческое отступление. Для управления по сети в свое время был разработан протокол SNMP. И всем-то он был хорош, кроме одного: в самом стандарте были определены только базовые примитивы, остальное все отдавалось на откуп производителю/разработчику, и стандартизация чего-либо помимо этих примитивов отсутствовала. Да, сейчас существуют некоторые стандартные OID, но в самом стандарте их очень мало, остальные задаются ведущими вендорами.
В 1996 году организация DMTF, состоящая из ведущих производителей оборудования и ПО, предоставила набор стандартов WBEM — Web-based enterprise management, причем Web-based в то время и в том контексте означало вовсе не Web-UI, а использование протоколов и стандартов, таких как HTTP, SSL, XML. В отличие от SNMP, эти стандарты определяли не только протокол обмена, но и набор объектов, над которыми можно производить манипуляции, — CIM, Common Information Model и соответствующие языки запросов CQL/WQL, созданные, как это видно из названия, под влиянием SQL. Но, в отличие от SQL, данное подмножество не поддерживает модификацию или удаление каких-либо параметров.
Шло время. В проприетарных системах (Windows NT, Sun Solaris) данный набор стандартов начали реализовывать. Для опенсорса же, из-за его многообразия и отсутствия мало-мальской стандартизации, это оказалось затруднительным. Нет, базовая часть (CIM-сервер) существует уже довольно давно и даже в различных реализациях, но вот вся остальная инфраструктура отсутствовала. С недавнего времени, однако, ситуация начала изменяться: несколько ведущих игроков на рынке корпоративного Linux выпустили на базе различных реализаций CIM-серверов свои варианты инфраструктуры WBEM, одну из которых, разрабатываемую под эгидой Red Hat и называемую OpenLMI, мы и рассмотрим.
Установка и архитектура
Установка в RHEL/CentOS проста до невозможности — для этого достаточно набрать следующие команды:
# yum install tog-pegasus openlmi-*
# systemctl enable tog-pegasus.service
# systemctl start tog-pegasus.service
Но мало установить инструментарий, нужно иметь представление о его архитектуре, которая и будет описана далее.
Центральная часть OpenLMI — OpenPegasus, который является CIM-сервером (он же CIM Object Manager, CIMOM, он же брокер). Сам по себе OpenPegasus бесполезен, поэтому помимо него мы ставим еще и провайдеры, которые фактически представляют собой бэкенд, совершающий те или иные действия с конфигурационными файлами. Перечислю некоторые провайдеры:
- openlmi-account — управление пользователями;
- openlmi-logicalfile — чтение файлов и каталогов;
- openlmi-networking — управление сетью;
- openlmi-service — управление службами;
- openlmi-hardware — предоставление информации об оборудовании.
Часть провайдеров написана на Python, другая (большая) часть — на C. Помимо CIMOM и различных провайдеров-бэкендов, в OpenLMI есть еще и два варианта CLI. Для начала рассмотрю тот, который входит в базовый состав CentOS 7. CLI может быть запущен как для управления локальной системой, так и для управления удаленной. Стоит также сказать, что стандартный CLI фактически является интерпретатором Python с поддержкой WBEM.
Обмен между CIMOM и клиентом происходит посредством CIM-XML over HTTPS. Протокол HTTPS требует сертификата, соответственно, для удаленного управления нужно скопировать сертификат управляемой машины и, по необходимости, открыть TCP-порт 5989 на ней же. Предположим, управляемая машина имеет имя elephant. Тогда для копирования сертификата набираем следующие команды:
# scp root@elephant:/etc/Pegasus/server.pem /etc/pki/ca-trust/source/anchors/elephant-cert.pem
# update-ca-trust extract
Для локального управления можно обойтись и без этого (по умолчанию оно доступно лишь суперпользователю), для удаленного же нужно на управляемой машине задать пароль для пользователя pegasus.
Стандарт WBEM также поддерживает и уведомления о событиях — то, что в SNMP называется трапом. Поддерживают его, правда, отнюдь не все провайдеры.
Отмечу, что в OpenLMI нельзя ограничить доступ к отдельным пространствам имен — у пользователя может либо быть доступ ко всем установленным на управляемой системе CIM-провайдерам, либо не быть его вообще. И это более чем странно — в аналогичном решении от Oracle, применяемом в Solaris, такая возможность имеется. Более того, имея доступ ко всем пространствам имен, непривилегированный пользователь, можно сказать, автоматически получает права суперпользователя (пример будет приведен позже).
INFO
В Ubuntu тоже есть инфраструктура WBEM на базе брокера SBLIM SFCB.
Подключение и базовые примеры. YAWN
Но перейдем к подключению CLI к OpenPegasus. Для этого запусти lmishell и в появившемся приглашении введи следующее:
> c = connect("localhost", "root")
Чтобы проверить, создалось ли соединение, можно использовать оператор is с инвертированием:
> c is not None
Если соединение установлено, результатом будет True.
Затем, для упрощения дальнейшей работы, нужно установить пространство имен, в пределах которого мы и будем оперировать:
> ns = c.root.cimv2
Рассмотрим, наконец, самый примитивный скрипт для вывода списка пользователей:
> for user in ns.LMI_Account.instances():
... print user.Name
Мы создаем цикл, просматривающий все экземпляры, предоставляемые провайдером LMI_Account, который отвечает за пользователей, и затем выводим поле name каждого экземпляра.
То же самое можно сделать и с помощью запроса WQL:
> query = ns.wql('SELECT Name FROM LMI_Account')
> for result in query:
... print result.property_value("Name")
В результате получился несколько более длинный ввод, чем в предыдущем случае. Это связано с тем, что в результате запроса возвращаются не сами строки, а список экземпляров, из которого мы затем в цикле извлекаем все нужные нам свойства. Нужно сказать, что для данного запроса тип кавычек значения не имеет, но вот если использовать операторы сравнения, необходимо сам запрос окружать одинарными кавычками, а строку, с которой сравниваешь, — двойными (пример см. ниже).
Точно так же можно вывести и список устройств PCI:
> query = ns.wql('SELECT Name FROM LMI_PCIDevice')
> for result in query:
... print result.property_value("Name")
Перейдем к чуть более сложному запросу — модифицируем запрос на перечисление имен пользователей так, чтобы он выводил только имена псевдопользователей:
> query = ns.wql('SELECT Name FROM LMI_Account WHERE LoginShell = "/sbin/nologin" OR loginShell = "/bin/false"')
> for result in query:
... print result.property_value("Name")
Посмотрим, как можно вывести хеш пароля пользователя root:
> query = ns.wql('SELECT Name, UssrPassword FROM LMI_Account WHERE Name = "root"')
> for result in query:
... print result.property_value("UserPassword")
Но все это касается лишь выборки данных. Попробуем что-нибудь изменить — например, добавить нового пользователя. WQL для этого, по понятным причинам, не подходит, поэтому используем объектную нотацию:
> import crypt
> cs = ns.PG_ComputerSystem.first_instance()
> accmgr = ns.LMI_AccountManagementService.first_instance()
> print accmgr.CreateAccount(Name="testuser", Password=crypt.crypt('test', crypt.mksalt(crypt.METHOD_SHA512)), System=cs)
Разберемся, что делают эти команды. Первая импортирует модуль crypt Питона для хеширования пароля. Вторая создает объект класса PG_ComputerSystem, который указывает на локальную систему. Третья же создает объект accmgr, который и отвечает за управление аккаунтами. Ну а четвертая команда как раз и создает пользователя с заданными свойствами (в том числе и с хешированным паролем) на заданном компьютере.
Но у тебя может возникнуть вопрос, как можно ориентироваться в дереве CIM. Во-первых, все описания классов, свойств и методов хранятся в MOF-файлах, которые в случае OpenLMI находятся в каталоге /var/lib/openlmi-registration/mof, однако синтаксис у них достаточно запутанный. Во-вторых, возможно поставить один из браузеров CIM, что лично я и сделал — установил YAWN. К сожалению, в репозиториях CentOS его нет (более того, сейчас его нет даже на сайте его разработчиков), поэтому пакет нужно качать с какого-нибудь стороннего ресурса:
# wget "ftp://ftp.muug.mb.ca/mirror/fedora/linux/development/21/i386/os/Packages/y/yawn-0-0.18.20140318svn632.fc21.noarch.rpm"
# rpm -ivh yawn-0-0.18.20140318svn632.fc21.noarch.rpm
Отмечу, что для использования YAWN нужно иметь запущенный Apache и разрешение SELinux на установку им сетевых соединений. Для последнего набери следующую команду:
# setsebool -P httpd_can_network_connect 1
После этого в браузере можно вводить http://localhost/yawn и просматривать дерево классов.
В целом базовые принципы работы более-менее понятны, перейду к более сложным примерам.
Написание CIM-провайдеров для OpenLMI
Вкратце изложу, что необходимо для написания OpenLMI-провайдера:
- Написать NOF-файл, который содержит описание классов и методов.
- Написать провайдер. В случае с Python имена методов, к которым обращается CIMOM, будут примерно такими: enum_instances() — должен перечислять экземпляры класса CIM, get_instance() — получение того или иного экземпляра или свойства, set_instance() — соответственно, создание или изменение экземпляра/свойства, delete_instance() — удаление экземпляра, ну и, наконец, cim_method_имя_метода() — вызов какого-нибудь специфичного для данного класса метода.
- Зарегистрировать MOF-файл и написанный файл регистрации с помощью команды 'openlmi-mof-register provider.mof provider.reg' и скопировать каталог с провайдером в /usr/lib/python2.7/site-packages/lmi.
После этого можно его использовать.
Сложные примеры использования. Команда lmi
Поскольку LMIshell представляет собой интерпретатор Python, вместо ввода команд ему можно скармливать скрипты, чем мы и займемся. Напишем скрипт, который создает таблицу разделов GPT, создает сами разделы и форматирует их в указанные ФС. Схема разбиения, конечно, крайне упрощена, но для понимания ее будет достаточно. Итак, сам скрипт:
#!/usr/bin/lmishell
c = connect('localhost')
ns = c.root.cimv2
MEGABYTE = 1024*1024
def print_partition(partition_name):
partition = partition_name.to_instance()
print "Created partition", partition.DeviceID, \
"with", partition.NumberOfBlocks * partition.BlockSize, "bytes."
sdb = ns.LMI_StorageExtent.first_instance({"Name": "/dev/sdb"})
partmgr = ns.LMI_DiskPartitionConfigurationService.first_instance(
{"Name":"LMI_DiskPartitionConfigurationService"})
fsysmgr = ns.LMI_FileSystemConfigurationService.first_instance({"Name":"LMI_FileSystemConfigurationService"})
gpt_style = ns.LMI_DiskPartitionConfigurationCapabilities.first_instance({"InstanceID": "LMI:LMI_DiskPartitionConfigurationCapabilities:GPT"})
partmgr.SetPartitionStyle(Extent=sdb, PartitionStyle=gpt_style)
for i in range(2):
(ret, outparams, err) = partmgr.SyncLMI_CreateOrModifyPartition(Extent=sdb, Size = 200 * MEGABYTE)
print_partition(outparams['Partition'])
(ret, outparams, err) = partmgr.SyncLMI_CreateOrModifyPartition(Extent=sdb)
print_partition(outparams['Partition'])
sdb1 = ns.CIM_StorageExtent.first_instance({"Name": "/dev/sdb1"})
sdb2 = ns.CIM_StorageExtent.first_instance({"Name": "/dev/sdb2"})
sdb3 = ns.CIM_StorageExtent.first_instance({"Name": "/dev/sdb3"})
for part in sdb1, sdb2:
print fsysmgr.SyncLMI_CreateFileSystem(FileSystemType=fsysmgr.LMI_CreateFileSystem.FileSystemTypeValues.EXT3, InExtents=[part])
print fsysmgr.SyncLMI_CreateFileSystem(FileSystemType=fsysmgr.LMI_CreateFileSystem.FileSystemTypeValues.XFS, InExtents=[sdb3])
Как видишь, для того чтобы делать что-то серьезное, нужно писать довольно много. Но есть и альтернатива в виде более удобной обертки вокруг LMIshell под названием LMI. К сожалению, в базовый состав CentOS она не входит, поэтому поставим сперва бету репозитория EPEL7:
# wget "http://dl.fedoraproject.org/pub/epel/beta/7/x86_64/epel-release-7-0.2.noarch.rpm"
# rpm -ivh epel-release-7-0.2.noarch.rpm
Затем поставим нужные пакеты:
# yum install openlmi-scripts*
И можно запускать свежеустановленную оболочку. Рассмотрим, как с ее помощью сделать то же самое, что делал скрипт выше:
# lmi -h localhost
lmi> storage partition-table create --gpt /dev/sdb
lmi> storage partition create /dev/sdb 200m
lmi> storage partition create /dev/sdb 200m
lmi> storage partition create /dev/sdb
lmi> storage fs create ext3 /dev/sdb1
lmi> storage fs create ext3 /dev/sdb2
lmi> storage fs create xfs /dev/sdb3
Стоит отметить, что данная оболочка поддерживает также и пространства имен — то есть можно перейти, например, в пространство имен 'storage' и набирать команды уже относительно него. Для ориентирования в пространстве имен используются команды ':cd', ':pwd' и ':..' - последняя представляет собой алиас для ':cd ..'.
Рассмотрим, как с помощью данной оболочки установить пакет Shorewall и просмотреть файлы в составе его (предполагается, что EPEL уже подключен). Для начала узнаем, как точно называется данный пакет:
lmi> :cd sw
>sw> search shorewall
Нужный нам пакет называется shorewall-0:4.6.3-1.el7.noarch. Его и ставим:
>sw> install shorewall-0:4.6.3-1.el7.noarch
Затем узнаем, какие файлы входят в состав данного пакета:
>sw> list files shorewall-0:4.6.3-1.el7.noarch
Для удаления же пакета достаточно набрать следующую команду:
>sw> remove shorewall-0:4.6.3-1.el7.noarch
Посмотрим, что можно сделать с настройками сети. В данной оболочке поддерживаются следующие возможности сетевых настроек: добавление/удаление статических маршрутов, адресов и DNS-серверов, настройка интерфейсов в bridging- и bonding-режиме. Приведу пример добавления DNS-сервера для интерфейса enp0s3:
lmi> net dns add enp0s3 8.8.8.8
С точки зрения удобства для конечного пользователя данная оболочка, безусловно, выглядит более подходяще, нежели LMIshell, — не нужно запоминать кучу названий классов, методов и свойств. С другой стороны, если нужно сделать нечто чуть менее примитивное, придется браться за изучение рассмотренного выше, ибо команда 'lmi' для подобного не предназначена — в ней нет даже циклов.
Заключение
Впечатление об OpenLMI складывается двоякое. С одной стороны, подобной функциональности в Linux давно не хватало — синтаксис конфигурационных файлов и команд настолько разнообразный, что при наличии множества серверов в голове все держать невозможно. Не спорю, дерево классов CIM тоже достаточно запутанное, но оно подчиняется определенным правилам и в итоге позволяет абстрагироваться от тех или иных конфигов с командами.
С другой стороны, выглядит затея сыро. В поставке отсутствует браузер классов, что затрудняет их изучение. Удобной обработки событий тоже нет, все придется писать самому — то есть порог вхождения достаточно большой. Наконец, безопасность. Помилуйте, о чем можно говорить, если любой пользователь, подключившийся к CIM-серверу, может совершать абсолютно все действия, которые позволяют делать OpenLMI-агенты, — при этом, есть ли у данного пользователя такие привилегии на управляемой системе, значения не имеет. Выглядит крайне странным, что Red Hat вообще включила данную инфраструктуру в свой коммерческий дистрибутив.
Я бы рекомендовал рассматривать данную инфраструктуру как некое Technology Preview и вектор, куда будут двигаться средства управления в дальнейшем. Для промышленной эксплуатации применение чревато дырами в безопасности.