Skip to content

Commit 9569da6

Browse files
Merge pull request #236 from catherineliuchina/private/catherineli/CA-426487
CA-426487 Fix gen_vm_template.py encounter error with unrepacked Rocky
2 parents 6096f77 + 1d36448 commit 9569da6

3 files changed

Lines changed: 93 additions & 17 deletions

File tree

autocertkit/gen_network_conf.py

100644100755
File mode changed.

autocertkit/gen_vm_template.py

Lines changed: 90 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,10 @@ def main():
132132
vm_pass = DEFAULT_PASSWORD
133133

134134
# Step 1: Pre-checks
135-
print("[1/6] Pre-checks")
135+
print("[1/7] Pre-checks")
136+
if os.path.exists(XVA_PATH):
137+
print("ERROR: %s already exists. Please remove it first." % XVA_PATH)
138+
sys.exit(1)
136139
if not os.path.isdir(SETUP_SCRIPTS_SRC):
137140
raise RuntimeError("Cannot find setup scripts dir: %s" % SETUP_SCRIPTS_SRC)
138141

@@ -141,38 +144,110 @@ def main():
141144
except RuntimeError as e:
142145
print("\n%s" % str(e))
143146
print("Troubleshooting tips:")
144-
print(" 1. Check if VM is powered on: xe vm-list | grep <vm_name>")
147+
print(" 1. Check if VM is powered on: xe vm-list")
145148
print(" 2. Verify correct IP address for the VM")
146149
print(" 3. Check network/firewall on VM host")
147150
print(" 4. Make sure VM has network configured (DHCP or static IP)")
148151
sys.exit(1)
149152

153+
result = ssh_command(vm_ip, DEFAULT_USERNAME, vm_pass,
154+
"rpm -q xe-guest-utilities xe-guest-utilities-xenstore",
155+
attempts=1, timeout=60)
156+
if result.get('returncode') != 0:
157+
print("ERROR: VM prerequisite RPMs are missing: xe-guest-utilities and/or xe-guest-utilities-xenstore")
158+
if result.get('stdout'):
159+
print("stdout: %s" % result.get('stdout'))
160+
if result.get('stderr'):
161+
print("stderr: %s" % result.get('stderr'))
162+
sys.exit(1)
163+
150164
# Step 2: Copy setup-scripts into VM
151-
print("[2/6] Copy setup-scripts into VM")
165+
print("[2/7] Copy setup-scripts into VM")
152166
channel = SecureChannel(vm_ip, DEFAULT_USERNAME, vm_pass, timeout=300)
153167
channel.run_cmd("rm -rf %s" % REMOTE_SETUP_DIR)
154168
scp_cmd = channel._wrap_cmd(
155169
"%s -r %s %s@%s:/root/" % (SCP, SETUP_SCRIPTS_SRC, DEFAULT_USERNAME, vm_ip)
156170
)
157-
result = make_local_call(scp_cmd, shell=True, timeout=300)
171+
result = make_local_call(scp_cmd, shell=True, timeout=1800)
158172
if result['returncode'] != 0:
159173
print("Error copying setup scripts: %s" % result['stderr'])
160174
sys.exit(1)
161175

162176
# Step 3: Run Rocky init-run.sh inside VM
163-
print("[3/6] Run Rocky init-run.sh inside VM")
164-
channel.run_cmd("command -v semanage || dnf install -y policycoreutils-python-utils")
165-
channel.run_cmd("chmod +x %s/init-run.sh && bash %s/init-run.sh" % (REMOTE_SETUP_DIR, REMOTE_SETUP_DIR))
166-
print("init-run.sh completed")
167-
177+
print("[3/7] Run Rocky init-run.sh inside VM")
178+
# Use longer timeout for init-run.sh (dnf update can take 10-15 minutes)
179+
channel = SecureChannel(vm_ip, DEFAULT_USERNAME, vm_pass, timeout=1800)
180+
channel.run_cmd_ext("command -v semanage || dnf install -y policycoreutils-python-utils")
181+
# Convert CRLF to LF in case scripts were edited on Windows
182+
channel.run_cmd_ext("sed -i 's/\\r$//' %s/*.sh" % REMOTE_SETUP_DIR)
183+
result = channel.run_cmd_ext("chmod +x %s/init-run.sh && bash %s/init-run.sh" % (REMOTE_SETUP_DIR, REMOTE_SETUP_DIR))
184+
if result.get('returncode', 1) == 0:
185+
print("init-run.sh completed successfully")
186+
else:
187+
print("init-run.sh finished with exit code: %s" % result.get('returncode'))
188+
if result.get('stderr'):
189+
print("stderr: %s" % result.get('stderr'))
168190
# Step 4: Reboot VM and wait for SSH
169-
print("[4/6] Reboot VM and wait for SSH back")
170-
channel.run_cmd("reboot")
191+
print("[4/7] Reboot VM and wait for SSH back")
192+
# Use nohup + background to let SSH exit cleanly before reboot kicks in
193+
channel.run_cmd("nohup sh -c 'sleep 2; reboot' >/dev/null 2>&1 &")
171194
time.sleep(10)
172195
wait_for_ssh(vm_ip, vm_pass)
173196

174-
# Step 5: Find VM UUID
175-
print("[5/6] Find VM UUID")
197+
# Step 5: Verify VM changes after reboot
198+
print("[5/7] Verify VM configuration after reboot")
199+
channel = SecureChannel(vm_ip, DEFAULT_USERNAME, vm_pass, timeout=300)
200+
201+
# Check startup-ip service
202+
result = channel.run_cmd_ext("systemctl is-enabled startup-ip.service")
203+
print(" startup-ip.service enabled: %s" % ("yes" if result.get('returncode') == 0 else "no"))
204+
if result.get('returncode') != 0:
205+
print("ERROR: startup-ip.service is not enabled")
206+
sys.exit(1)
207+
208+
# Check firewall state
209+
result = channel.run_cmd_ext("firewall-cmd --state")
210+
fw_state = result.get('stdout', '').strip()
211+
print(" firewall state: %s" % fw_state)
212+
if fw_state != "running":
213+
print("ERROR: firewall is not running")
214+
sys.exit(1)
215+
216+
# Check firewall ports
217+
result = channel.run_cmd_ext("firewall-cmd --list-ports")
218+
ports = result.get('stdout', '').strip()
219+
print(" firewall ports: %s" % ports)
220+
required_ports = ["4/tcp", "4/udp", "5001/tcp", "5001/udp"]
221+
for port in required_ports:
222+
if port not in ports:
223+
print("ERROR: Required firewall port %s is missing" % port)
224+
sys.exit(1)
225+
226+
# Check required packages
227+
result = channel.run_cmd_ext("rpm -q perl tcpdump")
228+
print(" packages installed: %s" % ("yes" if result.get('returncode') == 0 else "no"))
229+
if result.get('returncode') != 0:
230+
print("ERROR: Required packages (perl, tcpdump) are not installed")
231+
sys.exit(1)
232+
233+
# Check perl modules
234+
result = channel.run_cmd_ext("perl -e 'use List::MoreUtils; use Readonly; print \"perl modules OK\"'")
235+
print(" perl modules (List::MoreUtils, Readonly): %s" % ("yes" if result.get('returncode') == 0 else "no"))
236+
if result.get('returncode') != 0:
237+
print("ERROR: Required perl modules (List::MoreUtils, Readonly) are not installed")
238+
sys.exit(1)
239+
240+
# Set pm_freeze_timeout (runtime param, doesn't survive reboot)
241+
channel.run_cmd_ext("echo 300000 > /sys/power/pm_freeze_timeout")
242+
result = channel.run_cmd_ext("cat /sys/power/pm_freeze_timeout")
243+
pm_timeout = result.get('stdout', '').strip()
244+
print(" pm_freeze_timeout: %s" % pm_timeout)
245+
if pm_timeout != "300000":
246+
print("ERROR: pm_freeze_timeout is not 300000 (got: %s)" % pm_timeout)
247+
sys.exit(1)
248+
249+
# Step 6: Find VM UUID
250+
print("[6/7] Find VM UUID")
176251
vm_uuid = None
177252

178253
# Try to get VM UUID
@@ -191,8 +266,8 @@ def main():
191266
sys.exit(1)
192267
print("VM UUID: %s" % vm_uuid)
193268

194-
# Step 6: Shutdown and export VM
195-
print("[6/6] Shutdown and export VM as %s" % XVA_NAME)
269+
# Step 7: Shutdown and export VM
270+
print("[7/7] Shutdown and export VM as %s" % XVA_NAME)
196271
result = make_local_call(["xe", "vm-param-get", "uuid=%s" % vm_uuid, "param-name=name-label"], logging=False)
197272
vm_name = result['stdout'].strip() if result['returncode'] == 0 else "unknown"
198273
print("Shutting down VM %s (%s)..." % (vm_name, vm_uuid))

docs/xenserver-automated-certification-kit-guide.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<br>
66

77

8-
Published April 2026
8+
Published June 2026
99
V9.0.1 Edition
1010

1111
<br>
@@ -101,10 +101,11 @@ The certification kit requires a VM template (called Droid VM) to generate test
101101

102102
#### Prerequisites
103103

104-
- A Rocky Linux 8 or Rocky Linux 9 VM running on XenServer with:
104+
- A Rocky Linux 8.10 or 8 latest VM running on XenServer with:
105105
- XenServer VM Tools for Linux installed
106106
- Root password set to `xenserver`
107107
- Network connectivity to the XenServer host
108+
- No ISO mounted in the VM
108109

109110
#### Using the script
110111

0 commit comments

Comments
 (0)