File: vm-copy-bridge

package info (click to toggle)
cockpit 355-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 311,568 kB
  • sloc: javascript: 774,787; python: 40,655; ansic: 35,157; cpp: 11,141; sh: 3,512; makefile: 580; xml: 261
file content (145 lines) | stat: -rwxr-xr-x 4,799 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#!/usr/bin/env python3

# This file is part of Cockpit.
#
# Copyright (C) 2026 Red Hat, Inc.
# SPDX-License-Identifier: LGPL-2.1-or-later

"""
Fast iteration of src/cockpit/ against existing running VMs.

Instead of the slow image-prepare way (building packages), this script:
1. Uploads the current src/cockpit code to /var/tmp on the VM
2. Detects the package manager (dpkg/rpm/pacman) or container
3. Bind-mounts the new code over the installed package path
4. For containers, rebuilds the cockpit/ws container image

Usage:
    test/vm-copy-bridge PORT
"""

import argparse
import os
import subprocess
import sys

# Add bots to path
BOTS_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "bots")
sys.path.insert(0, BOTS_DIR)

from lib.constants import DEFAULT_IDENTITY_FILE  # noqa: E402
from machine.machine_core.ssh_connection import SSHConnection  # noqa: E402


def find_cockpit_path(file_list: str) -> str:
    """Find the cockpit module path from a list of file paths.

    Handles package manager output formats:
    - dpkg/rpm: one filepath per line
    - pacman: "package-name filepath" per line
    """
    for line in file_list.splitlines():
        line = line.strip()
        if line.endswith('/cockpit/__init__.py'):
            # Extract the path (for pacman, take last field to skip package name)
            path = line.split()[-1]
            return os.path.dirname(path)

    sys.exit("Could not find cockpit module path in package file list: " + file_list)


def setup_bind_mount(conn: SSHConnection, cockpit_path: str, port: int) -> None:
    """Set up bind mount from /var/tmp/cockpit to the package path."""
    conn.execute(f"if mountpoint -q {cockpit_path}; then umount {cockpit_path}; fi")
    conn.execute(f"mount --bind /var/tmp/cockpit {cockpit_path}")
    print(f"Mounted /var/tmp/cockpit over {cockpit_path}")


def rebuild_container(conn: SSHConnection) -> None:
    """Rebuild the cockpit/ws container with the new bridge code."""
    print("Rebuilding cockpit/ws container...")

    # Find the cockpit module path inside the container
    cockpit_path_in_container = conn.execute(
        "podman run --rm localhost/cockpit/ws python3 -c "
        "'import cockpit, os; print(os.path.dirname(cockpit.__file__))'"
    ).strip()

    # Build Containerfile with stdin - copy uploaded code into container
    # Note: COPY source is relative to build context (.)
    containerfile = f"""FROM localhost/cockpit/ws:latest
COPY cockpit {cockpit_path_in_container}
"""

    # Build the new container using stdin input
    conn.execute(
        "cd /var/tmp && podman build -t localhost/cockpit/ws:latest -f - .",
        input=containerfile,
        timeout=30
    )
    print("cockpit/ws container rebuilt successfully!")


#
# main
#

parser = argparse.ArgumentParser(description="Copy bridge code to a running VM")
parser.add_argument("port", type=int, help="SSH port of the running VM")
parser.add_argument("-v", "--verbose", action="store_true", help="Enable verbose output")
args = parser.parse_args()

# Connection parameters matching ~/.ssh/config "Host c" paragraph
conn = SSHConnection(
    user="root",
    address="127.0.0.2",
    ssh_port=args.port,
    identity_file=DEFAULT_IDENTITY_FILE,
    verbose=args.verbose
)

# Check if we can connect
if not conn.wait_execute(timeout_sec=5):
    sys.exit(f"Cannot connect to VM on port {args.port}")

print(f"Connected to VM on port {args.port}")

# Get the local src/cockpit directory
src_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
cockpit_src = os.path.join(src_dir, "src", "cockpit")

if not os.path.isdir(cockpit_src):
    sys.exit(f"{cockpit_src} does not exist")

# Upload the cockpit module to /var/tmp
print(f"Uploading {cockpit_src} to VM...")
conn.execute("rm -rf /var/tmp/cockpit")
conn.upload(["src/cockpit"], "/var/tmp/", relative_dir=src_dir)

# Known package managers
pkg_managers = [
    ("dpkg -L cockpit-bridge", "Debian/Ubuntu"),
    ("rpm -ql cockpit-bridge", "RPM-based"),
    ("pacman -Ql cockpit", "Arch Linux"),
]

for cmd, system_type in pkg_managers:
    try:
        output = conn.execute(cmd)
    except subprocess.CalledProcessError:
        continue
    else:
        cockpit_path = find_cockpit_path(output)
        print(f"Detected {system_type} system, cockpit at: {cockpit_path}")
        setup_bind_mount(conn, cockpit_path, args.port)
        break
else:
    # No package manager found, try container
    try:
        conn.execute("podman image exists localhost/cockpit/ws")
        print("Detected container-based system (localhost/cockpit/ws)")
        rebuild_container(conn)
    except subprocess.CalledProcessError:
        sys.exit("Could not detect package type (tried dpkg, rpm, pacman, podman)")

conn.disconnect()