Здесь размещено описание одного из функциональных блоков инструкции с примером управления виртуальными машинами "Qemu-KVM" через простейшие BASH-скрипты. Отдельно неприменимо.
Пример опций конфигурационного файла виртуальной машины:
# vi /usr/local/etc/kvm/conf.d/mashine0.cnf
....
# Явно указываем имя виртуального сетевого интерфейса, тип эмулируемой сетевой карты и MAC
# # adapter={rtl8139|e1000|virtio} (на практике rtl8139 - самый стабильный, хоть и медленный, зато распознаётся практически всеми системами без каких-либо телодвижений; e1000 (intel) - скоростной, но "глючный" до такой странной степени, что порой вообще перестаёт работать после очередного обновления виртуальной операционной системы).
network.virtif=tap0
network.tap0.adapter=virtio
network.tap0.mac=52:54:0c:b8:71:b0
network.tap0.connect=bridged
network.tap0.bridge=br0
network.virtif=tap2
network.tap2.adapter=virtio
network.tap2.mac=52:54:0c:b8:71:b2
network.tap2.connect=routed
network.tap2.ip=10.10.12.101
....
# Явно указываем имя виртуального сетевого интерфейса, тип эмулируемой сетевой карты и MAC
# # adapter={rtl8139|e1000|virtio} (на практике rtl8139 - самый стабильный, хоть и медленный, зато распознаётся практически всеми системами без каких-либо телодвижений; e1000 (intel) - скоростной, но "глючный" до такой странной степени, что порой вообще перестаёт работать после очередного обновления виртуальной операционной системы).
network.virtif=tap0
network.tap0.adapter=virtio
network.tap0.mac=52:54:0c:b8:71:b0
network.tap0.connect=bridged
network.tap0.bridge=br0
network.virtif=tap2
network.tap2.adapter=virtio
network.tap2.mac=52:54:0c:b8:71:b2
network.tap2.connect=routed
network.tap2.ip=10.10.12.101
....
Поддерживаются два способа связи сетей виртуальной и несущей машины, с разделением их на "bridged" (схема с ручной преднастройкой на несущем хосте "Public Bridge") и "routed". Первый подразумевает создание прямой прозрачной среды между виртуальным и реальным сетевым интерфейсами мимо сетевой среды несущей машины, а второй обеспечивает маршрутизацию трафика виртуального сетевого интерфейса через сетевую среду несущей виртуальной машины (разумеется, для этого из виртуальной машины нужно будет указать путь к ближайшему доступному шлюзу на несущей машине).
Первый, "прозрачный" режим, проще в настройке и реализации, но не позволяет использовать всю пропускную способность агрегированных сетевых интерфейсов несущей машины. Второй сложнее, но более гибкий в настройках и совместим с любым типом сетевого оборудования (например беспроводные сетевые карты и модемы почти всегда невозможно включить в "прозрачный мост").
Первый, "прозрачный" режим, проще в настройке и реализации, но не позволяет использовать всю пропускную способность агрегированных сетевых интерфейсов несущей машины. Второй сложнее, но более гибкий в настройках и совместим с любым типом сетевого оборудования (например беспроводные сетевые карты и модемы почти всегда невозможно включить в "прозрачный мост").
Фрагмент кода с функциями подготовки строки инициализации сетевых устройств виртуальной машины:
# vi /etc/kvm/fnc.d/5.network.fnc
#!/bin/bash
# This file contains the code snippet for the shell Bash v.4 (Bourne again shell)
# Файл содержит фрагмент кода для командного интерпретатора Bash v.4 (Bourne again shell)
# Функция инициализации виртуальных сетевых интерфейсов
function start-network() {
# Зачищаем исходящую строку описания от результатов работы предыдущей итерации
NSTRING=""
# Перебираем все строки именований виртуальных сетевых интерфейсов
for NIFS in `grep --ignore-case "^network.virtif=" "${CNF}" | uniq`
do
NIF=`echo ${NIFS} | awk -F = '{print $2}'`
# Проверяем наличие в сетевом окружении целевого виртуального сетевого интерфейса
STATE=`ifconfig -a | grep --count --ignore-case ${NIF}`
if [ ${STATE} -ne 0 ]; then
echo "${DATE}: Виртуальный сетевой интерфейс ${NIF} уже инициирован." | tee -a "${LOGT}"
else
# Инициируем виртуальный сетевой интерфейс
STATE=`tunctl -b -u ${USER} -t ${NIF}`
if [ ${?} -eq 0 -a "${STATE}" == "${NIF}" ]; then
ifconfig ${NIF} 0.0.0.0 up
else
echo "${DATE}: Ошибка инициализации виртуального интерфейса ${NIF}." | tee -a "${LOGT}"
return 1
fi
fi
# Выясняем тип сетевого подключения ("прозрачный мост" или "маршрутизация")
CTYPE=`grep --ignore-case --max-count=1 "^network.${NIF}.connect=" "${CNF}" | awk -F = '{print $2}'`
if [ "${CTYPE}" == "bridged" ] ; then
NBRIDGE=`grep --ignore-case --max-count=1 "^network.${NIF}.bridge=" "${CNF}" | awk -F = '{print $2}'`
# Проверяем наличие в сетевом окружении целевого "моста"
STATE=`brctl show | grep --count --ignore-case ${NBRIDGE}`
if [ ${STATE} -ne 0 ]; then
# Проверяем включение в какой либо "мост" целевого виртуального сетевого интерфейса
STATE=`brctl show | grep --count --ignore-case ${NIF}`
if [ ${STATE} -ne 0 ]; then
echo "${DATE}: Виртуальный сетевой интерфейс ${NIF} уже инициирован и подключен к мосту ${NBRIDGE}." | tee -a "${LOGT}"
else
# Включаем виртуальный интерфейс в "мост"
brctl addif ${NBRIDGE} ${NIF}
fi
else
echo "${DATE}: Еarget bridge interface ${NBRIDGE} is missing. Abort operations." | tee -a "${LOGT}"
return 1
fi
elif [ "${CTYPE}" == "routed" ] ; then
# Явно включаем подсистему маршрутизации пакетов IPv4
sysctl --write net.ipv4.ip_forward=1 >/dev/null
# Явно включаем подсистему обмена MAC-адресами между сетевыми интерфейсами (Proxy ARP)
sysctl --write net.ipv4.conf.all.proxy_arp=1 >/dev/null
# Перебираем все IPv4-адреса, объявленные за целевым виртуальным интерфейсом
for IPS in `grep --ignore-case "^network.${NIF}.ip=" "${CNF}"`
do
IP=`echo ${IPS} | awk -F = '{print $2}'`
# Добавляем в таблицу маршрутизации путь к адресу, объявленному за целевым виртуальным интерфейсом
route add -host ${IP} dev ${NIF} >/dev/null
done
else
echo "${DATE}: Ошибка подключения в сеть виртуального интерфейса ${NIF}: неопределённый тип сети." | tee -a "${LOGT}"
return 1
fi
# Дочитываем ряд параметров сетевых интерфейсов
NIC=`grep --ignore-case --max-count=1 "^network.${NIF}.adapter=" "${CNF}" | awk -F = '{print $2}'`
MAC=`grep --ignore-case --max-count=1 "^network.${NIF}.mac=" "${CNF}" | awk -F = '{print $2}'`
# Формируем строку описания сетевых интерфейсов виртуальной машины
if [ "${NIC}" != "" -a "${MAC}" != "" ]; then
NSTRING="${NSTRING} -net nic,macaddr=${MAC},model=${NIC} -net tap,ifname=${NIF},script=no,downscript=no"
else
echo "${DATE}: No full details of a network environment. Abort operations." | tee -a "${LOGT}"
return 1
fi
done
return 0
}
# Функция деактивации виртуальных сетевых интерфейсов
function stop-network() {
# Перебираем все строки именований виртуальных сетевых интерфейсов
for NIFS in `grep --ignore-case "^network.virtif=" "${CNF}" | uniq`
do
NIF=`echo ${NIFS} | awk -F = '{print $2}'`
# Выясняем тип сетевого подключения ("прозрачный мост" или "маршрутизация")
CTYPE=`grep --ignore-case --max-count=1 "^network.${NIF}.connect=" "${CNF}" | awk -F = '{print $2}'`
if [ "${CTYPE}" == "bridged" ] ; then
NBRIDGE=`grep --ignore-case --max-count=1 "^network.${NIF}.bridge=" "${CNF}" | awk -F = '{print $2}'`
# Проверяем наличие целевого виртуального сетевого интерфейса в составе "моста"
STATE=`brctl show ${NBRIDGE} | grep --count --ignore-case ${NIF}`
if [ ${STATE} -ne 0 ]; then
# Удаляем из "моста" целевой виртуальный сетевой интерфейс
brctl delif ${NBRIDGE} ${NIF}
fi
elif [ "${CTYPE}" == "routed" ] ; then
# Перебираем все IPv4-адреса, объявленные за целевым виртуальным интерфейсом
for IPS in `grep --ignore-case "^network.${NIF}.ip=" "${CNF}"`
do
IP=`echo ${IPS} | awk -F = '{print $2}'`
# Удаляем из таблицы маршрутизации путь к адресам, объявленным за целевым виртуальным интерфейсом
route del -host ${IP} dev ${NIF} >/dev/null
done
else
echo "${DATE}: Ошибка отключения от сети виртуального интерфейса ${NIF}: неопределённый тип сети." | tee -a "${LOGT}"
return 1
fi
# Проверяем наличие в сетевом окружении целевого виртуального сетевого интерфейса
STATE=`ifconfig -a | grep --count --ignore-case ${NIF}`
if [ ${STATE} -ne 0 ]; then
# Деактивируем виртуальный сетевой интерфейс
ifconfig ${NIF} 0.0.0.0 down
STATE=`tunctl -d ${NIF}`
if [ ${?} -eq 0 ]; then
echo "${DATE}: Виртуальный сетевой интерфейс ${NIF} успешно деактивирован." | tee -a "${LOGT}"
fi
fi
done
return 0
}
# This file contains the code snippet for the shell Bash v.4 (Bourne again shell)
# Файл содержит фрагмент кода для командного интерпретатора Bash v.4 (Bourne again shell)
# Функция инициализации виртуальных сетевых интерфейсов
function start-network() {
# Зачищаем исходящую строку описания от результатов работы предыдущей итерации
NSTRING=""
# Перебираем все строки именований виртуальных сетевых интерфейсов
for NIFS in `grep --ignore-case "^network.virtif=" "${CNF}" | uniq`
do
NIF=`echo ${NIFS} | awk -F = '{print $2}'`
# Проверяем наличие в сетевом окружении целевого виртуального сетевого интерфейса
STATE=`ifconfig -a | grep --count --ignore-case ${NIF}`
if [ ${STATE} -ne 0 ]; then
echo "${DATE}: Виртуальный сетевой интерфейс ${NIF} уже инициирован." | tee -a "${LOGT}"
else
# Инициируем виртуальный сетевой интерфейс
STATE=`tunctl -b -u ${USER} -t ${NIF}`
if [ ${?} -eq 0 -a "${STATE}" == "${NIF}" ]; then
ifconfig ${NIF} 0.0.0.0 up
else
echo "${DATE}: Ошибка инициализации виртуального интерфейса ${NIF}." | tee -a "${LOGT}"
return 1
fi
fi
# Выясняем тип сетевого подключения ("прозрачный мост" или "маршрутизация")
CTYPE=`grep --ignore-case --max-count=1 "^network.${NIF}.connect=" "${CNF}" | awk -F = '{print $2}'`
if [ "${CTYPE}" == "bridged" ] ; then
NBRIDGE=`grep --ignore-case --max-count=1 "^network.${NIF}.bridge=" "${CNF}" | awk -F = '{print $2}'`
# Проверяем наличие в сетевом окружении целевого "моста"
STATE=`brctl show | grep --count --ignore-case ${NBRIDGE}`
if [ ${STATE} -ne 0 ]; then
# Проверяем включение в какой либо "мост" целевого виртуального сетевого интерфейса
STATE=`brctl show | grep --count --ignore-case ${NIF}`
if [ ${STATE} -ne 0 ]; then
echo "${DATE}: Виртуальный сетевой интерфейс ${NIF} уже инициирован и подключен к мосту ${NBRIDGE}." | tee -a "${LOGT}"
else
# Включаем виртуальный интерфейс в "мост"
brctl addif ${NBRIDGE} ${NIF}
fi
else
echo "${DATE}: Еarget bridge interface ${NBRIDGE} is missing. Abort operations." | tee -a "${LOGT}"
return 1
fi
elif [ "${CTYPE}" == "routed" ] ; then
# Явно включаем подсистему маршрутизации пакетов IPv4
sysctl --write net.ipv4.ip_forward=1 >/dev/null
# Явно включаем подсистему обмена MAC-адресами между сетевыми интерфейсами (Proxy ARP)
sysctl --write net.ipv4.conf.all.proxy_arp=1 >/dev/null
# Перебираем все IPv4-адреса, объявленные за целевым виртуальным интерфейсом
for IPS in `grep --ignore-case "^network.${NIF}.ip=" "${CNF}"`
do
IP=`echo ${IPS} | awk -F = '{print $2}'`
# Добавляем в таблицу маршрутизации путь к адресу, объявленному за целевым виртуальным интерфейсом
route add -host ${IP} dev ${NIF} >/dev/null
done
else
echo "${DATE}: Ошибка подключения в сеть виртуального интерфейса ${NIF}: неопределённый тип сети." | tee -a "${LOGT}"
return 1
fi
# Дочитываем ряд параметров сетевых интерфейсов
NIC=`grep --ignore-case --max-count=1 "^network.${NIF}.adapter=" "${CNF}" | awk -F = '{print $2}'`
MAC=`grep --ignore-case --max-count=1 "^network.${NIF}.mac=" "${CNF}" | awk -F = '{print $2}'`
# Формируем строку описания сетевых интерфейсов виртуальной машины
if [ "${NIC}" != "" -a "${MAC}" != "" ]; then
NSTRING="${NSTRING} -net nic,macaddr=${MAC},model=${NIC} -net tap,ifname=${NIF},script=no,downscript=no"
else
echo "${DATE}: No full details of a network environment. Abort operations." | tee -a "${LOGT}"
return 1
fi
done
return 0
}
# Функция деактивации виртуальных сетевых интерфейсов
function stop-network() {
# Перебираем все строки именований виртуальных сетевых интерфейсов
for NIFS in `grep --ignore-case "^network.virtif=" "${CNF}" | uniq`
do
NIF=`echo ${NIFS} | awk -F = '{print $2}'`
# Выясняем тип сетевого подключения ("прозрачный мост" или "маршрутизация")
CTYPE=`grep --ignore-case --max-count=1 "^network.${NIF}.connect=" "${CNF}" | awk -F = '{print $2}'`
if [ "${CTYPE}" == "bridged" ] ; then
NBRIDGE=`grep --ignore-case --max-count=1 "^network.${NIF}.bridge=" "${CNF}" | awk -F = '{print $2}'`
# Проверяем наличие целевого виртуального сетевого интерфейса в составе "моста"
STATE=`brctl show ${NBRIDGE} | grep --count --ignore-case ${NIF}`
if [ ${STATE} -ne 0 ]; then
# Удаляем из "моста" целевой виртуальный сетевой интерфейс
brctl delif ${NBRIDGE} ${NIF}
fi
elif [ "${CTYPE}" == "routed" ] ; then
# Перебираем все IPv4-адреса, объявленные за целевым виртуальным интерфейсом
for IPS in `grep --ignore-case "^network.${NIF}.ip=" "${CNF}"`
do
IP=`echo ${IPS} | awk -F = '{print $2}'`
# Удаляем из таблицы маршрутизации путь к адресам, объявленным за целевым виртуальным интерфейсом
route del -host ${IP} dev ${NIF} >/dev/null
done
else
echo "${DATE}: Ошибка отключения от сети виртуального интерфейса ${NIF}: неопределённый тип сети." | tee -a "${LOGT}"
return 1
fi
# Проверяем наличие в сетевом окружении целевого виртуального сетевого интерфейса
STATE=`ifconfig -a | grep --count --ignore-case ${NIF}`
if [ ${STATE} -ne 0 ]; then
# Деактивируем виртуальный сетевой интерфейс
ifconfig ${NIF} 0.0.0.0 down
STATE=`tunctl -d ${NIF}`
if [ ${?} -eq 0 ]; then
echo "${DATE}: Виртуальный сетевой интерфейс ${NIF} успешно деактивирован." | tee -a "${LOGT}"
fi
fi
done
return 0
}