|
18 | 18 | """ |
19 | 19 | # Import Local Modules |
20 | 20 | from marvin.cloudstackTestCase import cloudstackTestCase |
21 | | -from marvin.cloudstackAPI import (stopSystemVm, |
| 21 | +from marvin.cloudstackAPI import (getDiagnosticsData, stopSystemVm, |
22 | 22 | rebootSystemVm, |
23 | 23 | destroySystemVm, updateConfiguration) |
24 | 24 | from marvin.lib.utils import (cleanup_resources, |
25 | 25 | get_process_status, |
26 | 26 | get_host_credentials, |
27 | 27 | wait_until) |
28 | | -from marvin.lib.base import (PhysicalNetwork, |
29 | | - NetScaler, ImageStore) |
| 28 | +from marvin.lib.base import (PhysicalNetwork, NetScaler, ImageStore, UserData) |
30 | 29 | from marvin.lib.common import (get_zone, |
31 | 30 | list_hosts, |
32 | 31 | list_ssvms, |
|
35 | 34 | from nose.plugins.attrib import attr |
36 | 35 | import telnetlib |
37 | 36 | import logging |
| 37 | +import base64 |
| 38 | +import os |
| 39 | +import urllib |
| 40 | +import zipfile |
38 | 41 |
|
39 | 42 | # Import System modules |
40 | 43 | import time |
@@ -1399,3 +1402,139 @@ def test_13_ss_nfs_version_on_ssvm(self): |
1399 | 1402 | int(nfs_version), |
1400 | 1403 | "Check mounted NFS version to be the same as provided" |
1401 | 1404 | ) |
| 1405 | + |
| 1406 | + @attr( |
| 1407 | + tags=[ |
| 1408 | + "advanced", |
| 1409 | + "advancedns", |
| 1410 | + "smoke", |
| 1411 | + "basic", "vj", |
| 1412 | + "sg"], |
| 1413 | + required_hardware="true") |
| 1414 | + def test_14_userdata_on_ssvm(self): |
| 1415 | + """Test user data functionality on SSVM |
| 1416 | + """ |
| 1417 | + # 1) Register userdata for the default admin user |
| 1418 | + # 2) Update global settings to enable userdata and add userdata for SSVM |
| 1419 | + # 3) Delete the SSVM if already present and wait for it to come up again |
| 1420 | + # 4) Wait for the SSVM to be ready |
| 1421 | + # 5) Download the file created by userdata script using the getDiagnosticsData command |
| 1422 | + # 6) Verify the file contains the expected content |
| 1423 | + |
| 1424 | + userdata_script = """#!/bin/bash |
| 1425 | +echo "User data script ran successfully" > /tmp/userdata.txt |
| 1426 | +""" |
| 1427 | + userdata_file_path = "/tmp/userdata.txt" |
| 1428 | + expected_userdata_content = "User data script ran successfully" |
| 1429 | + b64_encoded_userdata = base64.b64encode(userdata_script.encode('utf-8')).decode('utf-8') |
| 1430 | + # Create a userdata entry |
| 1431 | + try: |
| 1432 | + self.userdata = UserData.register( |
| 1433 | + self.apiclient, |
| 1434 | + name="ssvm_userdata", |
| 1435 | + userdata=b64_encoded_userdata |
| 1436 | + ) |
| 1437 | + except Exception as e: |
| 1438 | + if "already exists" in str(e): |
| 1439 | + self.debug("Userdata already exists, getting it") |
| 1440 | + self.userdata = UserData.list(self.apiclient, name="ssvm_userdata", listall=True)[0] |
| 1441 | + else: |
| 1442 | + self.fail("Failed to register userdata: %s" % e) |
| 1443 | + |
| 1444 | + #Enable user data and set the script to be run on SSVM |
| 1445 | + cmd = updateConfiguration.updateConfigurationCmd() |
| 1446 | + cmd.name = "systemvm.userdata.enabled" |
| 1447 | + cmd.value = "true" |
| 1448 | + self.apiclient.updateConfiguration(cmd) |
| 1449 | + |
| 1450 | + cmd = updateConfiguration.updateConfigurationCmd() |
| 1451 | + cmd.name = "secstorage.userdata" |
| 1452 | + cmd.value = self.userdata.id |
| 1453 | + self.apiclient.updateConfiguration(cmd) |
| 1454 | + |
| 1455 | + list_ssvm_response = list_ssvms(self.apiclient, systemvmtype='secondarystoragevm', state='Running', zoneid=self.zone.id) |
| 1456 | + self.assertEqual( |
| 1457 | + isinstance(list_ssvm_response, list), |
| 1458 | + True, |
| 1459 | + "Check list response returns a valid list" |
| 1460 | + ) |
| 1461 | + |
| 1462 | + ssvm = list_ssvm_response[0] |
| 1463 | + self.debug("Destroying SSVM: %s" % ssvm.id) |
| 1464 | + cmd = destroySystemVm.destroySystemVmCmd() |
| 1465 | + cmd.id = ssvm.id |
| 1466 | + self.apiclient.destroySystemVm(cmd) |
| 1467 | + |
| 1468 | + ssvm_response = self.checkForRunningSystemVM(ssvm, 'secondarystoragevm') |
| 1469 | + self.debug("SSVM state after debug: %s" % ssvm_response.state) |
| 1470 | + self.assertEqual( |
| 1471 | + ssvm_response.state, |
| 1472 | + 'Running', |
| 1473 | + "Check whether SSVM is running or not" |
| 1474 | + ) |
| 1475 | + # Wait for the agent to be up |
| 1476 | + self.waitForSystemVMAgent(ssvm_response.name) |
| 1477 | + |
| 1478 | + # 5) Download the file created by userdata script using the getDiagnosticsData command |
| 1479 | + cmd = getDiagnosticsData.getDiagnosticsDataCmd() |
| 1480 | + cmd.targetid = ssvm_response.id |
| 1481 | + cmd.files = userdata_file_path |
| 1482 | + |
| 1483 | + # getDiagnosticsData command takes some time to work after a SSVM is started |
| 1484 | + retries = 4 |
| 1485 | + response = None |
| 1486 | + while retries > -1: |
| 1487 | + try: |
| 1488 | + response = self.apiclient.getDiagnosticsData(cmd) |
| 1489 | + break # Success, exit retry loop |
| 1490 | + except Exception as e: |
| 1491 | + if retries >= 0: |
| 1492 | + retries = retries - 1 |
| 1493 | + self.debug("getDiagnosticsData failed (retries left: %d): %s" % (retries + 1, e)) |
| 1494 | + if retries > -1: |
| 1495 | + time.sleep(30) |
| 1496 | + continue |
| 1497 | + # If all retries exhausted, re-raise the exception |
| 1498 | + self.fail("Failed to get diagnostics data after retries: %s" % e) |
| 1499 | + |
| 1500 | + # Download response.url file to /tmp/ directory and extract it |
| 1501 | + self.debug("Downloading userdata file from SSVM") |
| 1502 | + try: |
| 1503 | + urllib.request.urlretrieve( |
| 1504 | + response.url, |
| 1505 | + "/tmp/userdata.zip" |
| 1506 | + ) |
| 1507 | + except Exception as e: |
| 1508 | + self.fail("Failed to download userdata file from SSVM: %s" % e) |
| 1509 | + self.debug("Downloaded userdata file from SSVM: %s" % |
| 1510 | + "/tmp/userdata.zip") |
| 1511 | + try: |
| 1512 | + with zipfile.ZipFile("/tmp/userdata.zip", 'r') as zip_ref: |
| 1513 | + zip_ref.extractall("/tmp/") |
| 1514 | + except zipfile.BadZipFile as e: |
| 1515 | + self.fail("Downloaded userdata file is not a zip file: %s" % e) |
| 1516 | + self.debug("Extracted userdata file from zip: %s" % "/tmp/userdata.txt") |
| 1517 | + |
| 1518 | + # 7) Verify the file contains the expected content |
| 1519 | + try: |
| 1520 | + with open("/tmp/userdata.txt", 'r') as f: |
| 1521 | + content = f.read().strip() |
| 1522 | + self.debug("Userdata file content: %s" % content) |
| 1523 | + self.assertEqual( |
| 1524 | + expected_userdata_content in content, |
| 1525 | + True, |
| 1526 | + f"Check that userdata file contains expected content: '{expected_userdata_content}'" |
| 1527 | + ) |
| 1528 | + except FileNotFoundError: |
| 1529 | + self.fail("Userdata file not found in extracted zip") |
| 1530 | + except Exception as e: |
| 1531 | + self.fail("Failed to read userdata file: %s" % e) |
| 1532 | + finally: |
| 1533 | + # Clean up temporary files |
| 1534 | + try: |
| 1535 | + if os.path.exists("/tmp/userdata.zip"): |
| 1536 | + os.remove("/tmp/userdata.zip") |
| 1537 | + if os.path.exists("/tmp/userdata.txt"): |
| 1538 | + os.remove("/tmp/userdata.txt") |
| 1539 | + except Exception as e: |
| 1540 | + self.debug("Failed to clean up temporary files: %s" % e) |
0 commit comments