16.6.4 Запуск транзакции

После инициализации сета транзакции, запуск операций осуществляется вызовом run. Требуется два параметра:

ts.run(callback, client_data)

Параметр callback должен быть функцией Python. Параметр client_data - любые данные, которые вы хотите поместить в обратный вызов. Поскольку в сете транзакции может быть много пакетов, это не должны быть данные, относящиеся к определенному пакету. В качестве client_data не может быть передано значение None.

16.6.4.1 Обратные вызовы метода run
Callback, который был передан в run, необходим. Обратный вызов должен сработать правильно, иначе транзакция завершится с ошибкой. Вы должны предоставить callback.

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

Базовый синтаксис обратного вызова для сета транзакции:

def runCallback(reason, amount, total, key, client_data):

# Do your stuff...

key - это данные, предоставленные вами для метода addInstall. client_data - данные, которые будут передаваться в run.

Всякий раз, когда происходит обратный вызов, сет транзакции будет задавать флаг reason. Возможные значения флага показаны в таблице.

Значение

На что указывает

rpm.RPMCALLBACK_UNKNOWN

Неизвестная ошибка

rpm.RPMCALLBACK_INST_PROGRESS

Прогресс установки

rpm.RPMCALLBACK_INST_START

Старт установки

rpm.RPMCALLBACK_INST_OPEN_FILE

Callback должен открыть файл пакета

rpm.RPMCALLBACK_INST_CLOSE_FILE

Callback должен закрыть файл пакета

rpm.RPMCALLBACK_TRANS_PROGRESS

Прогресс транзакции

rpm.RPMCALLBACK_TRANS_START

Старт транзакции

rpm.RPMCALLBACK_TRANS_STOP

Стоп транзакции

rpm.RPMCALLBACK_UNINST_PROGRESS

Прогресс удаления

rpm.RPMCALLBACK_UNINST_START

Старт удаления

rpm.RPMCALLBACK_UNINST_STOP

Стоп удаления

rpm.RPMCALLBACK_REPACKAGE_PROGRESS

Прогресс пересборки

rpm.RPMCALLBACK_REPACKAGE_START

Старт пересборки

rpm.RPMCALLBACK_REPACKAGE_STOP

Стоп пересборки

rpm.RPMCALLBACK_UNPACK_ERROR

Ошибка распаковки

rpm.RPMCALLBACK_CPIO_ERROR

Ошибка cpio при попытке разархивировать нагрузку

Ваш callback должен обрабатывать по меньшей мере два случая: rpm.RPMCALLBACK_INST_OPEN_FILE и rpm.RPMCALLBACK_INST_CLOSE_FILE.

С reason, равным rpm.RPMCALLBACK_INST_OPEN_FILE, необходимо открыть файл rpm-пакета и вернуть дескриптор. Этот дескриптор необходимо хранить в глобально-доступной переменной, или по крайней мере в зоне видимости, так как потребуется файл закрыть, когда reason будет rpm.RPMCALLBACK_INST_CLOSE_FILE.

16.6.4.2 Кодируем пример callback-функции
Код ниже показывает работающий пример callback для установки или обновления пакетов.

# Global file descriptor for the callback.

rpmtsCallback_fd = None

def runCallback(reason, amount, total, key, client_data):

global rpmtsCallback_fd

if reason == rpm.RPMCALLBACK_INST_OPEN_FILE:

print "Opening file. ", reason, amount, total, key, client_data

rpmtsCallback_fd = os.open(client_data, os.O_RDONLY)

return rpmtsCallback_fd

elif reason == rpm.RPMCALLBACK_INST_START:

print "Closing file. ", reason, amount, total, key, client_data

os.close(rpmtsCallback_fd)

Этот callback подразумевает, что в вызов addInstall передается в качестве пользовательских данных имя файла rpm-пакета. client_data в метод run игнорируется, но это хорошее место для передачи объекта. Можно, например, использовать эту возможность для того, чтобы избежать хранения дескриптора файла в глобальной переменной.

16.6.4.3 Обновление пакета
Пример ниже содержит скрипт (rpmupgrade.py) для обновления или установки пакета.

#!/usr/bin/python

# Upgrades packages passed on the command line.

# Usage:

# python rpmupgrade.py rpm_file1.rpm rpm_file2.rpm ...

#

import rpm, os, sys

# Global file descriptor for the callback.

rpmtsCallback_fd = None

def runCallback(reason, amount, total, key, client_data):

global rpmtsCallback_fd

if reason == rpm.RPMCALLBACK_INST_OPEN_FILE:

print "Opening file. ", reason, amount, total, key, client_data

rpmtsCallback_fd = os.open(key, os.O_RDONLY)

return rpmtsCallback_fd

elif reason == rpm.RPMCALLBACK_INST_START:

print "Closing file. ", reason, amount, total, key, client_data

os.close(rpmtsCallback_fd)

def checkCallback(ts, TagN, N, EVR, Flags):

if TagN == rpm.RPMTAG_REQUIRENAME:

prev = ""

Nh = None

if N[0] == '/':

dbitag = 'basenames'

else:

dbitag = 'providename'

# What do you need to do.

if EVR:

print "Must find package [", N, "-", EVR, "]"

else:

print "Must find file [", N, "]"

if resolved:

# ts.addIntall(h, h, 'i')

return -1

return 1

def readRpmHeader(ts, filename):

""" Read an rpm header. """

fd = os.open(filename, os.O_RDONLY)

h = ts.hdrFromFdno(fd)

os.close(fd)

return h

ts = rpm.TransactionSet()

# Set to not verify DSA signatures.

ts.setVSFlags(-1)

for filename in sys.argv[1:]:

h = readRpmHeader(ts, filename)

print "Upgrading %s-%s-%s" % (h['name'], h['version'], h['release'])

ts.addInstall(h, filename, 'u')

unresolved_dependencies = ts.check(checkCallback)

if not unresolved_dependencies:

ts.order()

print "This upgrade will install:"

for te in ts:

print "%s-%s-%s" % (te.N(), te.V(), te.R())

print "Running transaction (final step)..."

ts.run(runCallback, 1)

else:

print "Error: Unresolved dependencies, transaction failed."

print unresolved_dependencies

Этот скрипт ожидает, что имя файла rpm-пакета будет передано в параметре командной строки, получив имя он выполняет обновление пакета (а если это новый пакет - то установку).

При запуске скрипта увидим следующее:

# rpm -q jikes

jikes-1.17-1

# python rpmupgrade.py jikes-1.18-1.i386.rpm

Upgrading jikes-1.18-1

This upgrade will install:

jikes-1.18-1

jikes-1.17-1

Running transaction (final step)...

Opening file. 4 0 0 jikes-1.18-1.i386.rpm 1

Closing file. 2 0 2854204 jikes-1.18-1.i386.rpm 1

# rpm -q jikes

jikes-1.18-1

Если запустить скрипт без прав суперпользователя, увидим сообщение об ошибке типа следующего:

$ python rpmupgrade.py jikes-1.18-1.i386.rpm

Upgrading jikes-1.18-1

This upgrade will install:

jikes-1.18-1

jikes-1.17-1

Running transaction (final step)...

error: cannot get exclusive lock on /var/lib/rpm/Packages

error: cannot open Packages index using db3 - Operation not permitted (1)

error: cannot open Packages database in /var/lib/rpm

Если пакет имеет зависимости от файла, например, разделяемой библиотеки, вывод будет таким:

# python rpmupgrade.py jikes-1.17-glibc2.2-1.i386.rpm jpilot-0_97-1_i386.rpm
Upgrading jikes-1.17-1

Upgrading jpilot-0.97-1

Must find file [ libpisock.so.3 ]

Error: Unresolved dependencies, transaction failed.

(('jpilot', '0.97', '1'), ('libpisock.so.3', None), 0, None, 0)

Если имеется неразрешаемая зависимость от другого пакета, вывод будет таким:

# python rpmupgrade.py eruby-devel-0.9.8-2.i386.rpm

Upgrading eruby-devel-0.9.8-2

Must find package [ eruby-libs - 0.9.8 ]

Error: Unresolved dependencies, transaction failed.

(('eruby-devel', '0.9.8', '2'), ('eruby-libs', '0.9.8'), 8, None, 0)

Далее - Раздел 17. Программирование RPM на Perl
Назад - Проверка и переопределение порядка элементов транзакции
Содержание