@@ -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 ))
0 commit comments