Другое

Программный анализ снимков qcow2: Полное руководство

Узнайте несколько методов извлечения информации о снимках qcow2 в форматах JSON, CSV и других машиночитаемых форматах для автоматизации. Полное руководство с примерами кода.

Как я могу программно извлечь информацию о снимках из файла qcow2 в машиночитаемом формате для целей автоматизации?

У меня есть файл qcow2, содержащий несколько снимков, и мне нужно программно перечислить теги снимков. Команда qemu-img snapshot -l предоставляет информацию о снимках, но выводит ее в человекочитаемом табличном формате:

$ qemu-img snapshot -l ./image.qcow2
Snapshot list:
ID      TAG               VM_SIZE                DATE        VM_CLOCK     ICOUNT
1       appack-init      3.72 GiB 2025-10-29 17:43:38  0000:01:04.537         --

Какие существуют методы для извлечения этой информации о снимках в формате, подходящем для автоматизации, таком как JSON, CSV или другие структурированные форматы данных?

Программное извлечение информации о снимках из qcow2 файлов

Для программного извлечения информации о снимках из qcow2 файлов в машиночитаемом формате можно использовать несколько подходов, включая парсинг вывода qemu-img, использование QMP (QEMU Machine Protocol) или использование libvirt для автоматизации более высокого уровня. Команда qemu-img предоставляет возможности перечисления снимков, но для получения форматов, удобных для автоматизации, требуется парсинг вывода.

Содержание

Использование qemu-img с парсингом вывода

Команда qemu-img snapshot -l является основой для извлечения информации о снимках, но её вывод по умолчанию имеет формат таблицы, удобный для чтения человеком. Для преобразования этого вывода в машиночитаемые форматы необходимо выполнить парсинг.

Базовая команда qemu-img

bash
$ qemu-img snapshot -l ./image.qcow2
Snapshot list:
ID      TAG               VM_SIZE                DATE        VM_CLOCK     ICOUNT
1       appack-init      3.72 GiB 2025-10-29 17:43:38  0000:01:04.537         --

Стратегии парсинга

  1. Текстовый парсинг с использованием регулярных выражений

    bash
    qemu-img snapshot -l image.qcow2 | grep -v "Snapshot list:" | grep -v "^ID" | awk '{print $1","$2","$3","$4","$5","$6}' > snapshots.csv
    
  2. Структурированный вывод с использованием jq

    bash
    qemu-img snapshot -l image.qcow2 | tail -n +3 | awk '{print "{\"id\": "$1", \"tag\": \""$2"\", \"size\": \""$3"\", \"date\": \""$4" "$5"\", \"vm_clock\": \""$6"\", \"icount\": \""$7"\"}"}' | jq -s '.'
    
  3. Python-скрипт для преобразования в JSON

    python
    import subprocess
    import json
    import re
    
    def get_snapshots_qcow2(image_path):
        result = subprocess.run(['qemu-img', 'snapshot', '-l', image_path], 
                              capture_output=True, text=True)
        
        lines = result.stdout.split('\n')
        snapshots = []
        
        for line in lines:
            if line.startswith('ID') or line.startswith('Snapshot list'):
                continue
            if not line.strip():
                continue
                
            parts = re.split(r'\s{2,}', line.strip())
            if len(parts) >= 4:
                snapshot = {
                    'id': int(parts[0]),
                    'tag': parts[1],
                    'size': parts[2],
                    'date': f"{parts[3]} {parts[4]}" if len(parts) > 4 else parts[3],
                    'vm_clock': parts[5] if len(parts) > 5 else '',
                    'icount': parts[6] if len(parts) > 6 else ''
                }
                snapshots.append(snapshot)
        
        return snapshots
    
    # Использование
    snapshots = get_snapshots_qcow2('./image.qcow2')
    print(json.dumps(snapshots, indent=2))
    

Подход с использованием QMP/QEMU Machine Protocol

QMP предоставляет интерфейс на основе JSON для программного управления экземплярами QEMU, включая операции с снимками.

Пример команды QMP для работы со снимками

Как показано в документации QEMU, команды QMP используют формат JSON:

json
{
  "execute": "blockdev-snapshot-sync",
  "arguments": {
    "device": "virtio0",
    "snapshot-file": "/some/place/my-image",
    "format": "qcow2"
  }
}

Использование QMP для перечисления снимков

Для перечисления снимков через QMP обычно выполняются следующие шаги:

  1. Запустить QEMU с включенным QMP
  2. Подключиться к сокету QMP
  3. Отправить соответствующие команды запроса
  4. Распарсить JSON-ответы
python
import socket
import json

def query_snapshots_via_qmp(qmp_socket_path):
    # Подключение к сокету QMP
    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    sock.connect(qmp_socket_path)
    
    # Отправка приветственного сообщения QMP
    greeting = json.loads(sock.recv(1024).decode())
    
    # Отправка согласования возможностей
    sock.send(json.dumps({"execute": "qmp_capabilities"}).encode() + b"\n")
    
    # Запрос информации о блочных устройствах
    sock.send(json.dumps({"execute": "query-block"}).encode() + b"\n")
    response = json.loads(sock.recv(1024).decode())
    
    # Закрытие соединения
    sock.close()
    
    return response

Интерфейс автоматизации Libvirt

Libvirt предоставляет API более высокого уровня для управления платформами виртуализации и поддерживает операции с снимками.

Использование команды virsh

bash
# Перечисление снимков для домена
virsh snapshot-list --domain vm-name --table

# Получение информации о снимке в формате XML
virsh snapshot-info --domain vm-name --snapshotname snapshot-name --xml

Пример с использованием Python и Libvirt

python
import libvirt

def get_snapshots_libvirt(conn_uri, domain_name):
    conn = libvirt.open(conn_uri)
    if conn is None:
        raise Exception('Не удалось открыть соединение с гипервизором')
    
    domain = conn.lookupByName(domain_name)
    snapshots = domain.snapshotListNames()
    
    snapshot_info = []
    for snap_name in snapshots:
        snapshot = domain.snapshotLookupByName(snap_name)
        info = {
            'name': snap_name,
            'description': snapshot.getDescription(),
            'creationTime': snapshot.getCreationTime(),
            'state': snapshot.getState()
        }
        snapshot_info.append(info)
    
    conn.close()
    return snapshot_info

Техники и инструменты парсинга

Обработка текста со стандартными инструментами

Использование awk для преобразования в CSV:

bash
qemu-img snapshot -l image.qcow2 | \
  tail -n +3 | \
  awk 'BEGIN {OFS=","} {print $1, $2, $3, $4" "$5, $6, $7}' > snapshots.csv

Использование sed для структурированного вывода:

bash
qemu-img snapshot -l image.qcow2 | \
  sed -n '3,$p' | \
  sed 's/\s\{2,\}/,/g' | \
  sed 's/^,//' > snapshots.csv

Продвинутый парсинг с использованием jq

bash
qemu-img snapshot -l image.qcow2 | \
  tail -n +3 | \
  awk '{print "{\"id\": "$1", \"tag\": \""$2"\", \"size\": \""$3"\", \"date\": \""$4" "$5"\", \"vm_clock\": \""$6"\", \"icount\": \""$7"\"}"}' | \
  jq -s '.' > snapshots.json

Пример для PowerShell в среде Windows

powershell
function Get-Qcow2Snapshots {
    param([string]$ImagePath)
    
    $output = qemu-img snapshot -l $ImagePath
    $snapshots = @()
    
    # Пропуск заголовочных строк
    $dataLines = $output | Select-Object -Skip 2
    
    foreach ($line in $dataLines) {
        if ($line.Trim()) {
            $parts = [regex]::Split($line.Trim(), '\s{2,}')
            if ($parts.Count -ge 4) {
                $snapshot = [PSCustomObject]@{
                    ID = [int]$parts[0]
                    Tag = $parts[1]
                    Size = $parts[2]
                    Date = "$($parts[3]) $($parts[4])"
                    VM_Clock = if ($parts.Count -gt 5) { $parts[5] } else { "" }
                    ICOUNT = if ($parts.Count -gt 6) { $parts[6] } else { "" }
                }
                $snapshots += $snapshot
            }
        }
    }
    
    return $snapshots | ConvertTo-Json -Depth 1
}

Полные примеры автоматизации

Bash-скрипт для вывода в формате CSV и JSON

bash
#!/bin/bash

# qcow2_snapshot_extractor.sh
# Использование: ./qcow2_snapshot_extractor.sh <image.qcow2> [format]

IMAGE_PATH="$1"
FORMAT="${2:-json}"

if [[ ! -f "$IMAGE_PATH" ]]; then
    echo "Ошибка: Файл образа не найден: $IMAGE_PATH"
    exit 1
fi

# Получение данных о снимках
SNAPSHOT_DATA=$(qemu-img snapshot -l "$IMAGE_PATH")

# Пропуск заголовочных строк и обработка данных
PROCESSED_DATA=$(echo "$SNAPSHOT_DATA" | tail -n +3 | grep -v '^$')

if [[ "$FORMAT" == "csv" ]]; then
    echo "ID,TAG,SIZE,DATE,VM_CLOCK,ICOUNT"
    echo "$PROCESSED_DATA" | awk '{print $1","$2","$3","$4" "$5","$6","$7}'
elif [[ "$FORMAT" == "json" ]]; then
    JSON_OUTPUT='['
    FIRST=true
    
    while read -r line; do
        if [[ $FIRST == true ]]; then
            FIRST=false
        else
            JSON_OUTPUT+=','
        fi
        
        parts=($(echo "$line" | tr -s ' '))
        
        if [[ ${#parts[@]} -ge 4 ]]; then
            date_part="${parts[2]} ${parts[3]}"
            vm_clock_part="${parts[4]:-}"
            icount_part="${parts[5]:-}"
            
            JSON_OUTPUT+="{\"id\":${parts[0]},\"tag\":\"${parts[1]}\",\"size\":\"${parts[2]}\",\"date\":\"$date_part\",\"vm_clock\":\"$vm_clock_part\",\"icount\":\"$icount_part\"}"
        fi
    done <<< "$PROCESSED_DATA"
    
    JSON_OUTPUT+=']'
    echo "$JSON_OUTPUT" | jq .
else
    echo "Неподдерживаемый формат: $FORMAT. Используйте 'csv' или 'json'"
    exit 1
fi

Python-класс для управления снимками

python
import subprocess
import json
import re
from datetime import datetime

class Qcow2SnapshotManager:
    def __init__(self, image_path):
        self.image_path = image_path
        self.snapshot_pattern = re.compile(r'\s{2,}')
        
    def get_snapshots(self, output_format='json'):
        """Получение информации о снимках в указанном формате"""
        try:
            result = subprocess.run(
                ['qemu-img', 'snapshot', '-l', self.image_path],
                capture_output=True,
                text=True,
                check=True
            )
            
            snapshots = self._parse_snapshot_output(result.stdout)
            
            if output_format == 'json':
                return json.dumps(snapshots, indent=2)
            elif output_format == 'csv':
                return self._format_csv(snapshots)
            else:
                raise ValueError(f"Неподдерживаемый формат вывода: {output_format}")
                
        except subprocess.CalledProcessError as e:
            raise Exception(f"Не удалось получить информацию о снимках: {e.stderr}")
    
    def _parse_snapshot_output(self, output):
        """Парсинг вывода команды qemu-img snapshot"""
        lines = output.split('\n')
        snapshots = []
        
        for line in lines:
            if line.startswith('ID') or line.startswith('Snapshot list'):
                continue
            if not line.strip():
                continue
                
            parts = self.snapshot_pattern.split(line.strip())
            if len(parts) >= 4:
                snapshot = {
                    'id': int(parts[0]),
                    'tag': parts[1],
                    'size': parts[2],
                    'date': self._parse_date(parts[3], parts[4] if len(parts) > 4 else None),
                    'vm_clock': parts[5] if len(parts) > 5 else '',
                    'icount': parts[6] if len(parts) > 6 else ''
                }
                snapshots.append(snapshot)
        
        return snapshots
    
    def _parse_date(self, date_str, time_str=None):
        """Парсинг строк с датой и временем"""
        if time_str:
            return f"{date_str} {time_str}"
        return date_str
    
    def _format_csv(self, snapshots):
        """Форматирование снимков в CSV"""
        csv_lines = ["ID,TAG,SIZE,DATE,VM_CLOCK,ICOUNT"]
        for snap in snapshots:
            csv_lines.append(f"{snap['id']},{snap['tag']},{snap['size']},"
                           f"\"{snap['date']}\",\"{snap['vm_clock']}\","
                           f"\"{snap['icount']}\"")
        return '\n'.join(csv_lines)
    
    def get_snapshot_tags(self):
        """Получение только списка тегов снимков"""
        snapshots = self.get_snapshots_json()
        return [snap['tag'] for snap in snapshots]
    
    def get_snapshots_json(self):
        """Получение снимков в виде объектов Python"""
        result = subprocess.run(
            ['qemu-img', 'snapshot', '-l', self.image_path],
            capture_output=True,
            text=True,
            check=True
        )
        return self._parse_snapshot_output(result.stdout)

# Пример использования
if __name__ == "__main__":
    manager = Qcow2SnapshotManager('./image.qcow2')
    
    # Получение вывода в формате JSON
    print("Вывод в формате JSON:")
    print(manager.get_snapshots('json'))
    
    # Получение вывода в формате CSV
    print("\nВывод в формате CSV:")
    print(manager.get_snapshots('csv'))
    
    # Получение только тегов снимков
    print("\nТеги снимков:")
    print(manager.get_snapshot_tags())

Интеграция с CI/CD конвейерами

yaml
# Пример рабочего процесса GitHub Actions для анализа снимков
name: Анализ снимков Qcow2

on:
  workflow_dispatch:
    inputs:
      image_path:
        description: 'Путь к файлу образа qcow2'
        required: true
        default: './image.qcow2'
      output_format:
        description: 'Формат вывода (json/csv)'
        required: true
        default: 'json'

jobs:
  snapshot-analysis:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout кода
      uses: actions/checkout@v3
      
    - name: Установка QEMU
      run: sudo apt-get update && sudo apt-get install -y qemu-utils
      
    - name: Извлечение информации о снимках
      run: |
        chmod +x scripts/qcow2_snapshot_extractor.sh
        ./scripts/qcow2_snapshot_extractor.sh ${{ inputs.image_path }} ${{ inputs.output_format }}
        
    - name: Загрузка данных о снимках
      uses: actions/upload-artifact@v3
      with:
        name: snapshot-data
        path: snapshot_data.*

Источники

  1. Stack Overflow - Программное перечисление снимков qcow2 файла
  2. QEMU Wiki - Features/Snapshots
  3. Programster’s Blog - Qemu-img Cheatsheet
  4. Azer Technology - KVM-QEMU, QCOW2, QEMU-IMG и снимки
  5. Kashyap Chamarthy - Создание снимков с libvirt для qcow2 образов
  6. Server Fault - KVM снимки. Как увидеть содержимое qcow2 файла?
  7. Proxmox Support Forum - Расположение файла снимка qcow2

Заключение

Для программного извлечения информации о снимках из qcow2 файлов с целью автоматизации существуют несколько эффективных подходов:

  1. Парсить вывод qemu-img с помощью инструментов обработки текста, таких как awk, sed или пользовательских скриптов, для преобразования формата таблицы в JSON или CSV
  2. Использовать QMP/QEMU Machine Protocol для прямого JSON-взаимодействия с QEMU для более сложных сценариев автоматизации
  3. Воспользоваться libvirt для управления виртуализацией более высокого уровня с встроенной поддержкой снимков
  4. Создавать пользовательские скрипты автоматизации на Python, bash или других языках для обработки парсинга и форматирования в соответствии с требованиями

Наиболее практичный подход для большинства случаев использования - парсинг вывода qemu-img snapshot -l, так как он не требует запуска экземпляров QEMU и работает с существующими файлами qcow2. Приведенные примеры демонстрируют, как создавать надежные решения автоматизации, которые могут интегрироваться в CI/CD конвейеры, системы резервного копирования или инструменты управления виртуализацией.

Авторы
Проверено модерацией
Модерация