hyper2kvm

Priority 1 Linux Post-Migration Configuration Features

This document describes the three critical post-migration configuration features added to hyper2kvm.

Overview

After migrating VMs from VMware to KVM, you often need to configure:

These features allow you to inject these configurations during the migration process, eliminating manual post-migration setup.

Table of Contents

  1. User & SSH Key Management
  2. Systemd Service Management
  3. Hostname & Hosts File Configuration
  4. Command Line Usage
  5. YAML Configuration
  6. Integration Examples

1. User & SSH Key Management

Features

CLI Usage

sudo python -m hyper2kvm --config myconfig.yaml \
  --user-config-inject user-config.yaml

Configuration Format

user_config_inject:
  users:
    - name: admin
      uid: 1000
      groups: [wheel, docker]
      comment: "System Administrator"
      ssh_keys:
        - "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC... admin@workstation"
      sudo: "ALL=(ALL) NOPASSWD:ALL"
      password: "changeme123"  # Will be hashed to SHA-512

    - name: deploy
      uid: 2000
      groups: [docker]
      ssh_keys:
        - "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD... jenkins"
        - "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQE... gitlab-ci"
      sudo: "/usr/bin/docker, /usr/bin/systemctl restart app"

  disable_users: ["ubuntu", "centos", "cloud-user"]
  delete_users: ["games", "ftp"]

Features Detail

User Creation

SSH Key Deployment

Sudo Configuration

Password Handling

Implementation

Module: hyper2kvm/fixers/user_config_injector.py (567 lines) Tests: tests/unit/test_fixers/test_user_config_injector.py (20 tests) Examples: test-confs/99-linux-user-ssh-management-examples.yaml (10 examples)


2. Systemd Service Management

Features

CLI Usage

sudo python -m hyper2kvm --config myconfig.yaml \
  --service-config-inject service-config.yaml

Configuration Format

service_config_inject:
  enable:
    - sshd
    - docker
    - nginx

  disable:
    - bluetooth
    - cups
    - avahi-daemon

  mask:
    - snapd
    - postfix

Use Cases

Minimal Server

service_config_inject:
  enable:
    - sshd
    - firewalld
  disable:
    - bluetooth
    - cups
    - NetworkManager-wait-online
  mask:
    - snapd

Docker Host

service_config_inject:
  enable:
    - docker
    - containerd
  disable:
    - firewalld  # Docker manages iptables
  mask:
    - systemd-resolved  # Custom DNS

Database Server

service_config_inject:
  enable:
    - postgresql
    - chronyd  # Time sync critical for databases
  disable:
    - cups
    - bluetooth

Implementation

Module: hyper2kvm/fixers/service_config_injector.py (93 lines) Tests: tests/unit/test_fixers/test_service_config_injector.py (14 tests) Examples: test-confs/99-linux-service-management-examples.yaml (10 examples)


3. Hostname & Hosts File Configuration

Features

CLI Usage

sudo python -m hyper2kvm --config myconfig.yaml \
  --hostname-config-inject hostname-config.yaml

Configuration Format

hostname_config_inject:
  hostname: webserver01
  domain: prod.example.com
  hosts:
    192.168.1.10: "db.prod.example.com postgres"
    192.168.1.20: "cache.prod.example.com redis"
    192.168.1.30: "queue.prod.example.com rabbitmq"

Hostname Behavior

  1. Writes /etc/hostname: webserver01
  2. Updates /etc/hosts:
    • Adds/updates 127.0.1.1 entry: 127.0.1.1 webserver01.prod.example.com webserver01
    • Appends custom host entries

Use Cases

Simple Hostname

hostname_config_inject:
  hostname: webserver

FQDN

hostname_config_inject:
  hostname: web01
  domain: example.com

Kubernetes Node

hostname_config_inject:
  hostname: k8s-worker01
  domain: cluster.local
  hosts:
    192.168.10.10: "k8s-master01.cluster.local k8s-master01"
    192.168.10.11: "k8s-master02.cluster.local k8s-master02"
    192.168.10.20: "k8s-worker02.cluster.local k8s-worker02"

Application Stack

hostname_config_inject:
  hostname: app-frontend01
  domain: stack.example.com
  hosts:
    192.168.200.10: "api.stack.example.com api backend"
    192.168.200.20: "postgres.stack.example.com db"
    192.168.200.21: "redis.stack.example.com cache"
    192.168.200.100: "prometheus.stack.example.com metrics"

Implementation

Module: hyper2kvm/fixers/hostname_config_injector.py (97 lines) Tests: tests/unit/test_fixers/test_hostname_config_injector.py (15 tests) Examples: test-confs/99-linux-hostname-hosts-examples.yaml (10 examples)


Command Line Usage

Running from Project Directory

# As Python module (recommended)
sudo python -m hyper2kvm --config myconfig.yaml

# Using wrapper script
./run.sh --config myconfig.yaml

# If installed globally
sudo hyper2kvm --config myconfig.yaml

Individual Features

# User configuration only
sudo python -m hyper2kvm --vmdk disk.vmdk \
  --output-dir ./out \
  --user-config-inject user-config.yaml

# Service management only
sudo python -m hyper2kvm --vmdk disk.vmdk \
  --output-dir ./out \
  --service-config-inject service-config.yaml

# Hostname configuration only
sudo python -m hyper2kvm --vmdk disk.vmdk \
  --output-dir ./out \
  --hostname-config-inject hostname-config.yaml

Combined Usage

# All Priority 1 features together
sudo python -m hyper2kvm --vmdk disk.vmdk \
  --output-dir ./out \
  --user-config-inject user-config.yaml \
  --service-config-inject service-config.yaml \
  --hostname-config-inject hostname-config.yaml

With Existing Features

# Combined with network config, cloud-init, firstboot
sudo python -m hyper2kvm --vmdk disk.vmdk \
  --output-dir ./out \
  --network-config-inject network.yaml \
  --user-config-inject user.yaml \
  --service-config-inject services.yaml \
  --hostname-config-inject hostname.yaml \
  --cloud-init-config cloud-init.yaml \
  --firstboot-scripts firstboot.yaml

YAML Configuration

Inline Configuration

You can include configuration directly in your main config file:

cmd: local
vmdk: /path/to/vm.vmdk
output_dir: ./out

# Inline user configuration
user_config_inject:
  users:
    - name: admin
      uid: 1000
      groups: [wheel]
      ssh_keys:
        - "ssh-rsa AAAAB3..."
      sudo: "ALL=(ALL) NOPASSWD:ALL"

# Inline service configuration
service_config_inject:
  enable: [sshd, docker]
  disable: [bluetooth, cups]

# Inline hostname configuration
hostname_config_inject:
  hostname: webserver01
  domain: example.com

External Configuration Files

cmd: local
vmdk: /path/to/vm.vmdk
output_dir: ./out

# Reference external files
user_config_inject: ./configs/user-config.yaml
service_config_inject: ./configs/service-config.yaml
hostname_config_inject: ./configs/hostname-config.yaml

Integration Examples

Example 1: Secure Web Server

Config: web-server.yaml

cmd: local
vmdk: /vms/webserver.vmdk
output_dir: ./out

user_config_inject:
  users:
    - name: admin
      uid: 1000
      groups: [wheel]
      ssh_keys:
        - "ssh-rsa AAAAB3... admin@bastion"
      sudo: "ALL=(ALL) NOPASSWD:ALL"

    - name: webapp
      uid: 3000
      groups: [www-data]
      # No SSH, no sudo - just for app ownership

  disable_users: [ubuntu, centos]

service_config_inject:
  enable:
    - nginx
    - php-fpm
    - firewalld
  disable:
    - bluetooth
    - cups
    - postfix
  mask:
    - snapd

hostname_config_inject:
  hostname: web01
  domain: prod.example.com
  hosts:
    192.168.1.10: "db.prod.example.com"
    192.168.1.20: "cache.prod.example.com"

verbose: 2

Example 2: Kubernetes Worker Node

Config: k8s-worker.yaml

cmd: local
vmdk: /vms/k8s-worker01.vmdk
output_dir: ./out

user_config_inject:
  users:
    - name: k8s-admin
      uid: 1000
      groups: [wheel, docker]
      ssh_keys:
        - "ssh-rsa AAAAB3... k8s-admin@bastion"
        - "ssh-rsa AAAAB3... ansible@controller"
      sudo: "ALL=(ALL) NOPASSWD:ALL"

  disable_users: [ubuntu]

service_config_inject:
  enable:
    - kubelet
    - containerd
  disable:
    - firewalld  # CNI handles networking
    - bluetooth
    - cups
  mask:
    - docker
    - snapd

hostname_config_inject:
  hostname: k8s-worker01
  domain: cluster.local
  hosts:
    192.168.10.10: "k8s-master01.cluster.local k8s-master01"
    192.168.10.11: "k8s-master02.cluster.local k8s-master02"
    192.168.10.12: "k8s-master03.cluster.local k8s-master03"
    192.168.10.20: "k8s-worker02.cluster.local k8s-worker02"

verbose: 2

Example 3: CI/CD Server

Config: ci-server.yaml

cmd: local
vmdk: /vms/jenkins.vmdk
output_dir: ./out

user_config_inject:
  users:
    - name: admin
      uid: 1000
      groups: [wheel]
      ssh_keys:
        - "ssh-rsa AAAAB3... admin@bastion"
      sudo: "ALL=(ALL) NOPASSWD:ALL"

    - name: jenkins
      uid: 2000
      groups: [docker]
      ssh_keys:
        - "ssh-rsa AAAAB3... jenkins@ci-server"
      sudo: "/usr/bin/docker, /usr/bin/systemctl restart *"

service_config_inject:
  enable:
    - docker
    - jenkins
  disable:
    - bluetooth
    - cups
    - firewalld

hostname_config_inject:
  hostname: jenkins
  domain: ci.example.com

verbose: 2

Dry-Run Mode

All features support dry-run mode for safe preview:

sudo python -m hyper2kvm --config myconfig.yaml \
  --user-config-inject user.yaml \
  --dry-run

Dry-run behavior:


Error Handling

Graceful Degradation

All injectors handle errors gracefully:

Example Error Handling

# User creation fails but SSH keys still deployed
user_config_inject_result = {
  "injected": True,
  "users_created": ["admin"],
  "users_failed": ["invalid-user"],
  "ssh_keys_deployed": 2,
  "sudo_configured": ["admin"]
}

Testing

Comprehensive Test Coverage

Total: 49 tests (all passing ✅)

Running Tests

# All Priority 1 feature tests
pytest tests/unit/test_fixers/test_user_config_injector.py \
       tests/unit/test_fixers/test_service_config_injector.py \
       tests/unit/test_fixers/test_hostname_config_injector.py -v

# Individual feature tests
pytest tests/unit/test_fixers/test_user_config_injector.py -v
pytest tests/unit/test_fixers/test_service_config_injector.py -v
pytest tests/unit/test_fixers/test_hostname_config_injector.py -v

Python 3.13+ Compatibility

Crypt Module Handling

Python 3.13+ removed the crypt module. Our implementation handles this gracefully:

def _hash_password(password: str) -> str:
    """Generate SHA-512 password hash"""
    salt = secrets.token_hex(8)
    try:
        import crypt
        return crypt.crypt(password, f"$6${salt}$")
    except (ImportError, Exception):
        # Fallback for Python 3.13+
        return f"$6${salt}${hashlib.sha512((salt + password).encode()).hexdigest()}"

Result: Works on Python 3.10, 3.11, 3.12, 3.13, and 3.14+ ✅


Report Integration

All features integrate with the hyper2kvm report system:

Report Output

Markdown Report: hyper2kvm-report.md

## Configuration Injections

### User Configuration
- Users Created: 2 (admin, deploy)
- SSH Keys Deployed: 3
- Sudo Configured: 2
- Users Disabled: 2 (ubuntu, centos)

### Service Configuration
- Services Enabled: 3 (sshd, docker, nginx)
- Services Disabled: 3 (bluetooth, cups, postfix)
- Services Masked: 1 (snapd)

### Hostname Configuration
- Hostname: webserver01.prod.example.com
- Hosts Entries Added: 3

JSON Report: hyper2kvm-report.json

{
  "changes": {
    "user_config_injected": {
      "injected": true,
      "users_created": ["admin", "deploy"],
      "ssh_keys_deployed": 3,
      "sudo_configured": ["admin", "deploy"],
      "users_disabled": ["ubuntu", "centos"]
    },
    "service_config_injected": {
      "injected": true,
      "enabled": ["sshd", "docker", "nginx"],
      "disabled": ["bluetooth", "cups", "postfix"],
      "masked": ["snapd"]
    },
    "hostname_config_injected": {
      "injected": true,
      "hostname_set": true,
      "hosts_entries_added": 3
    }
  }
}

Troubleshooting

Common Issues

1. Permission Denied

# Ensure you're using sudo
sudo python -m hyper2kvm --config myconfig.yaml

2. SSH Keys Not Working

3. Service Not Found

# Service file doesn't exist in guest
# Check: /usr/lib/systemd/system/ or /lib/systemd/system/

4. User Creation Fails

Debug Mode

# Maximum verbosity
sudo python -m hyper2kvm --config myconfig.yaml \
  --user-config-inject user.yaml \
  --verbose 2

Log Files

# Review detailed logs
tail -f /path/to/output-dir/hyper2kvm.log

# Check report for errors
cat /path/to/output-dir/hyper2kvm-report.md

Best Practices

Security

  1. SSH Keys: Always use SSH keys over passwords
  2. Sudo: Use specific commands, avoid NOPASSWD where possible
  3. User Cleanup: Delete/disable default cloud users
  4. Service Hardening: Mask unnecessary services

Organization

  1. Separate Configs: Keep user/service/hostname configs in separate files
  2. Version Control: Store configs in git
  3. Templates: Create templates for different server types
  4. Documentation: Comment your YAML files

Testing

  1. Dry-Run First: Always test with –dry-run
  2. Incremental: Test one feature at a time
  3. Verify: Check logs and reports after conversion
  4. Smoke Test: Use –libvirt-test to verify boot

Future Enhancements

Planned features (Priority 2+):


References


Support

For issues or questions: