{"id":2274,"date":"2025-05-27T22:45:00","date_gmt":"2025-05-28T02:45:00","guid":{"rendered":"https:\/\/enterpriseadmins.org\/blog\/?p=2274"},"modified":"2025-05-27T21:18:07","modified_gmt":"2025-05-28T01:18:07","slug":"centralized-startup-scripting-for-automated-vm-load-testing","status":"publish","type":"post","link":"https:\/\/enterpriseadmins.org\/blog\/scripting\/centralized-startup-scripting-for-automated-vm-load-testing\/","title":{"rendered":"Centralized Startup Scripting for Automated VM Load Testing"},"content":{"rendered":"\n<p>When building or troubleshooting infrastructure, it is often useful to simulate high CPU or memory usage withou deploying full production workloads.  To assist with this, I previously created a few purpose built, like <a href=\"https:\/\/github.com\/bwuch\/code-snips\/blob\/master\/cpubusy.sh\" data-type=\"link\" data-id=\"https:\/\/github.com\/bwuch\/code-snips\/blob\/master\/cpubusy.sh\">cpubusy.sh<\/a> and <a href=\"https:\/\/github.com\/bwuch\/code-snips\/blob\/master\/memfill.sh\" data-type=\"link\" data-id=\"https:\/\/github.com\/bwuch\/code-snips\/blob\/master\/memfill.sh\">memfill.sh<\/a>.<\/p>\n\n\n\n<p>Historically, I created multiple Tiny Core Linux templates, each designed for a specific purpose, a generic one for troubleshooting, one that would fill mem and a another to load up the CPU.  I&#8217;d then place these scripts in <code>\/opt\/bootlocal.sh<\/code>, allowing each VM to run its designated script automatically at startup.  I&#8217;d then control load by simply powering VMs on or off. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Problem<\/h2>\n\n\n\n<p>That setup works fine for simple use cases, but it doesn&#8217;t scale well. What if I want:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>One VM to simulate CPU load,<\/li>\n\n\n\n<li>Another to test download speeds,<\/li>\n\n\n\n<li>A third to run a custom test script\u2014all using the same base image?<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">The Common Control Script<\/h2>\n\n\n\n<p>This script runs at boot and checks for specific instructions to execute. The idea is simple: deploy a single generic VM template that decides what to do based on either:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Metadata (via guestinfo)<\/li>\n\n\n\n<li>Network identity (IP, MAC, hostname)<\/li>\n\n\n\n<li>Shared config (via GuestStore or a web server)<\/li>\n<\/ul>\n\n\n\n<p>This common control script can be found here: <a href=\"https:\/\/github.com\/bwuch\/code-snips\/blob\/master\/cc.sh\">code-snips\/cc.sh<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Where It Looks for Instructions<\/h2>\n\n\n\n<p>When a VM boots, the script checks for commands in the following order:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Web server:\n<ul class=\"wp-block-list\">\n<li><code>http:\/\/&lt;web>\/&lt;macaddress>.txt<\/code><\/li>\n\n\n\n<li><code>http:\/\/&lt;web>\/&lt;ipaddress>.txt<\/code><\/li>\n\n\n\n<li><code>http:\/\/&lt;web>\/&lt;hostname>.txt<\/code><\/li>\n\n\n\n<li><code>http:\/\/&lt;web>\/all.txt<\/code><\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>VM Guest Store (using VMware tools):\n<ul class=\"wp-block-list\">\n<li><code>guestinfo.ccScript<\/code> (specified via advanced VM setting)<\/li>\n\n\n\n<li><code>\/custom\/cc\/cc-all.txt<\/code><\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<p>This layered approach gives flexibility:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Set a global script via <code>all.txt<\/code><\/li>\n\n\n\n<li>Override per host via metadata or identifiers<\/li>\n\n\n\n<li>Or push custom scripts directly via GuestInfo<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Example: Setting the GuestInfo Property<\/h2>\n\n\n\n<p>Using PowerCLI, we can set the script filename per VM like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Get-VM h045-tc16-02 | New-AdvancedSetting -Name 'guestinfo.ccScript' -Value 'memfill.sh' -confirm:$false<\/code><\/pre>\n\n\n\n<p>We can also modify this via the vSphere Web Client.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/05\/image-2.png\"><img loading=\"lazy\" decoding=\"async\" width=\"876\" height=\"753\" src=\"https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/05\/image-2.png\" alt=\"\" class=\"wp-image-2281\" srcset=\"https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/05\/image-2.png 876w, https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/05\/image-2-300x258.png 300w, https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/05\/image-2-768x660.png 768w\" sizes=\"auto, (max-width: 876px) 100vw, 876px\" \/><\/a><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Demonstration<\/h2>\n\n\n\n<p>Here\u2019s a test case from my lab:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>I set guestinfo.ccScript to memfill.sh<\/li>\n\n\n\n<li>The all.txt file includes a simple command to print system time<\/li>\n<\/ul>\n\n\n\n<p>Upon boot, the VM fills 90% of available RAM using a memory-backed filesystem and prints the time, confirming that both script sources are active.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><a href=\"https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/05\/image-3.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"584\" src=\"https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/05\/image-3-1024x584.png\" alt=\"\" class=\"wp-image-2282\" style=\"aspect-ratio:1;width:670px;height:auto\" srcset=\"https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/05\/image-3-1024x584.png 1024w, https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/05\/image-3-300x171.png 300w, https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/05\/image-3-768x438.png 768w, https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/05\/image-3-1536x875.png 1536w, https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/05\/image-3.png 1716w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p>Later, I removed the <code>guestinfo.ccScript<\/code> setting and added a <code>&lt;hostname>.txt<\/code> script to download a file repeatedly from a test web server.  After reboot, the VM behaved differently, now acting as a network test client. No changes to the template required.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/05\/image-4.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"597\" src=\"https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/05\/image-4-1024x597.png\" alt=\"\" class=\"wp-image-2283\" srcset=\"https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/05\/image-4-1024x597.png 1024w, https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/05\/image-4-300x175.png 300w, https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/05\/image-4-768x448.png 768w, https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/05\/image-4-1536x896.png 1536w, https:\/\/enterpriseadmins.org\/blog\/wp-content\/uploads\/2025\/05\/image-4.png 1679w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Sample Scripts<\/h2>\n\n\n\n<p>Here are a few lightweight test scripts used in the demo:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/github.com\/bwuch\/code-snips\/blob\/master\/cpubusy.sh\">cpubusy.sh<\/a> &#8211; uses <code>sha1sum<\/code> to keep all the configured CPU cores busy<\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/bwuch\/code-snips\/blob\/master\/download.sh\">download.sh<\/a> &#8211; uses <code>wget<\/code> to get the same webserver file <code>x<\/code> times and save it to <code>\/dev\/null<\/code><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/bwuch\/code-snips\/blob\/master\/memfill.sh\">memfill.sh<\/a> &#8211; creates a memory backed filesystem using 90% of RAM, then uses <code>dd<\/code> to fill it<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>This &#8216;common config&#8217; approach provides template reuse, easier script management, and dynamic testing control, all without modifying the template.<\/p>\n\n\n\n<p>Whether testing CPU, memory, or network load across dozens of VMs, the common control script simplifies the process and reduces maintenance overhead.<\/p>\n\n\n\n<p>In future iterations, this setup could be extended to include conditional logic (based on boot time, VM tags, or other metadata), or integration with CI pipelines for even more powerful automation.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>When building or troubleshooting infrastructure, it is often useful to simulate high CPU or memory usage withou deploying full production workloads. To assist with this, I previously created a few purpose built, like cpubusy.sh and memfill.sh. Historically, I created multiple &hellip; <a href=\"https:\/\/enterpriseadmins.org\/blog\/scripting\/centralized-startup-scripting-for-automated-vm-load-testing\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":6,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[3,4],"tags":[],"class_list":["post-2274","post","type-post","status-publish","format-standard","hentry","category-scripting","category-virtualization"],"_links":{"self":[{"href":"https:\/\/enterpriseadmins.org\/blog\/wp-json\/wp\/v2\/posts\/2274","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/enterpriseadmins.org\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/enterpriseadmins.org\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/enterpriseadmins.org\/blog\/wp-json\/wp\/v2\/users\/6"}],"replies":[{"embeddable":true,"href":"https:\/\/enterpriseadmins.org\/blog\/wp-json\/wp\/v2\/comments?post=2274"}],"version-history":[{"count":7,"href":"https:\/\/enterpriseadmins.org\/blog\/wp-json\/wp\/v2\/posts\/2274\/revisions"}],"predecessor-version":[{"id":2286,"href":"https:\/\/enterpriseadmins.org\/blog\/wp-json\/wp\/v2\/posts\/2274\/revisions\/2286"}],"wp:attachment":[{"href":"https:\/\/enterpriseadmins.org\/blog\/wp-json\/wp\/v2\/media?parent=2274"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/enterpriseadmins.org\/blog\/wp-json\/wp\/v2\/categories?post=2274"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/enterpriseadmins.org\/blog\/wp-json\/wp\/v2\/tags?post=2274"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}