{"id":20445819,"url":"https://github.com/rapid7/vm-automation","last_synced_at":"2025-04-04T10:03:41.259Z","repository":{"id":52221156,"uuid":"86614263","full_name":"rapid7/vm-automation","owner":"rapid7","description":"Created to simplify interactions with virtual machines","archived":false,"fork":false,"pushed_at":"2025-03-03T08:43:46.000Z","size":146,"stargazers_count":79,"open_issues_count":7,"forks_count":39,"subscribers_count":55,"default_branch":"master","last_synced_at":"2025-03-28T09:03:34.984Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rapid7.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-03-29T18:09:34.000Z","updated_at":"2025-03-07T18:20:42.000Z","dependencies_parsed_at":"2022-08-24T04:51:08.671Z","dependency_job_id":"8bf5c02d-805a-4134-874f-7ce27c709bfb","html_url":"https://github.com/rapid7/vm-automation","commit_stats":{"total_commits":93,"total_committers":5,"mean_commits":18.6,"dds":0.4301075268817204,"last_synced_commit":"b198d79f6fabf1a175252f297e2e764726a0e34f"},"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rapid7%2Fvm-automation","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rapid7%2Fvm-automation/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rapid7%2Fvm-automation/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rapid7%2Fvm-automation/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rapid7","download_url":"https://codeload.github.com/rapid7/vm-automation/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247153088,"owners_count":20892602,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-15T10:14:22.338Z","updated_at":"2025-04-04T10:03:41.236Z","avatar_url":"https://github.com/rapid7.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# vm-automation\n## Introduction\nThe vm-automation repo was created to simplify interactions with virtual\nmachines.  Specifically, this was built to support automated testing by \nsimplifying interaction with VMs.  Currently, it supports VMWare \nWorkstation through the vmrun.exe command-line application and ESXi \nthrough encapsulation of pyvmomi functions.  My testing has used python \n2.7.\n\n#### Note: \n* This is a dependency for an in-development internal R7 tool.  Some functions\nmight be modified in the near term with regard to parameters and return values \nto support that internal project.\n* VMWare workstation support may be a bit behnd ESXi\n* Currently, I have logging to the screen turned on to help with \ndebugging.  vm-automation logs to a file passed into the intialization\nfunction for the server.  The default name is `defaultLogfile.log`\n### Why bother to encapsulate pyvmomi and vmrun.exe?\nI'm no big fan of re-inventing the wheel, and I conceed that is a bit of\nwhat I did here, but I did it for some very good reasons:\n* Using this library allows me to seamlessly manage VMware workstation \nVMs and VMWare ESXi VMs because each server type has a class, and I \noverloaded the management functions to work with both classes.\u003cBR\u003e\n* Pyvmomi is not particularly simple.  If you do not believe me, see \nthe `uploadFileToGuest` function.  It contains the pyvmomi calls to\nupload a file to a guest OS.  It's about 40 lines.  Even worse is the\ncode to get a list of VM snapshots, which requires a recurive search.\nVm-automation encapsulates the pyvmomi code and adds error handling as\nwell.\n\n### Can I use it as a stand-alone solution?\nCertainly.  I created the library as a separate entity specifically\nbecause I planned to reuse this library to support other projects.  \nRight now, it has all the functions that I need to run automated payload\ntesting, but I'm all for adding on to support more projects.\n\n### How do I use it?\n* If you don't have python (2.7), crawl out from under the rock and install\nit.\n* Install [pyvmomi](https://pypi.python.org/pypi/pyvmomi) on your machine:\n`pip install --upgrade pyvmomi`\n* Git this repo:\n`git clone git@github.com:rapid7/vm-automation.git`\n\n### What Hypervisors are supported?\nRight now, just VMWare Workstation and ESXi (vSphere).  I was sad to find out that the API calls this repo uses are only available on the paid version of ESXi, but we all gotta' eat, so I can't be too mad.  Hopefully, in the future, we can get support up and running for something like Virtualbox.\n\n### How can I get started?\nThe fastest way to get started is to instantiate a server (it can be \neither VMWare ESXi or Workstation).  I need to add some documentation for Workstation, because I've been focused on ESXi so far.\n\n```\ntmoose@ubuntu:~/rapid7/vm-automation$ python\n\u003e\u003e\u003e import vm_automation\n\u003e\u003e\u003e myserver = vm_automation.esxiServer(\"xxx.xxx.xxx.xxx\", \"user\", \"password\", \"443\", \"example.log\")\n\u003e\u003e\u003e print myserver.connect()\nTrue\n```\n\nAlternatively, to make automation easier, you can create a hypervisor configuration file containing the same information:\n```\n{\n\t\"HYPERVISOR_TYPE\":\t\t\"ESXI\",\n\t\"HYPERVISOR_HOST\":\t\t\"xxx.xxx.xxx.xxx\",\n\t\"HYPERVISOR_USERNAME\":\t\t\"my_username\",\n\t\"HYPERVISOR_PASSWORD\":\t\t\"secret_password\",\n\t\"HYPERVISOR_LISTENING_PORT\":\t443\n}\n```\nOK, yeah; I probably should not have made the port number a string in the constructor, but a number in the config file....\nAnyway, that `connect` got us a connection to the server which is stored\nin the class and reused when we need to talk to the server.  It also \nregistered the connection for removal when our process exits.\nLet's query the type of Server:\n```\n\u003e\u003e\u003e print myserver.getVersion()\nVMware ESXi 6.5.0 build-4564106\n```\nBy default, nothing happens other than establishing the session.\nIf we wanted to get a list of VMs on the server, we need to populate\nthat list, first:\n```\n\u003e\u003e\u003e myserver.enumerateVms()\n```\nNow, we can print it:\n```\n\u003e\u003e\u003e for vm in myserver.vmList:\n...     print vm.vmName\n... \n[APT] Windows XP Pro\n[APT] Windows 7 x64\n[APT] Ubuntu 16x64\n[APT] Generic Dev Ubuntu\n[APT] Windows 8 x64\n[APT] Windows 10x64 Pro\n```\nThese VMs are of a custom vmObject type defined in the esxiVm.py file\nAs they are a class, you just get an object which is kind of a pain to \nreference.  It might be easier to create a dictionary with the names and\nobjects as pairs:\n```\n\u003e\u003e\u003e vmDic = {}\n\u003e\u003e\u003e for vm in myserver.vmList:\n...     vmDic[vm.vmName] = vm\n... \n```\nNow, we can play with a given vm more easily:\n```\n\u003e\u003e\u003e print vmDic['[APT] Windows 10x64 Pro'].isPoweredOn()\nFalse\n\u003e\u003e\u003e print vmDic['[APT] Windows 10x64 Pro'].powerOn()\nserverlog:[2017-04-04 15:32:30.638260] POWERING ON [APT] Windows 10x64 Pro\nserverlog:[2017-04-04 15:32:35.667396] DONE\nTrue\n\u003e\u003e\u003e print vmDic['[APT] Windows 10x64 Pro'].isPoweredOn()\nTrue\n\u003e\u003e\u003e print vmDic['[APT] Windows 10x64 Pro'].powerOff()\nserverlog:[2017-04-04 15:32:50.859867] POWERING OFF [APT] Windows 10x64 Pro\nserverlog:[2017-04-04 15:32:55.887729] DONE\nTrue\n\u003e\u003e\u003e \n```\nMore advanced features require you to authenticate with the VM itself:\n```\n\u003e\u003e\u003e vmDic['[APT] Windows 10x64 Pro'].setUsername('username')\n\u003e\u003e\u003e vmDic['[APT] Windows 10x64 Pro'].setPassword('password')\n```\nI suggest you use the function `waitForVmsToBoot` before calling any\ninteractive VM functions.  VMware tools gets in odd states during the \nboot process, and that function will wait for VMWare tools to stabilize\nand be ready to handle requests:\n```\n\u003e\u003e\u003e myserver.waitForVmsToBoot([vmDic['[APT] Windows 10x64 Pro']])\nserverlog:[2017-04-04 16:02:31.691551] WAITING FOR VMS TO BE READY; THIS COULD TAKE A FEW MINUTES\nserverlog:[2017-04-04 16:04:40.216568] VMS APPEAR TO BE READY; PULLING IP ADDRESSES TO VERIFY\nserverlog:[2017-04-04 16:04:45.237939] IP ADDRESS FOR [APT] Windows 10x64 Pro = xxx.xxx.xxx.xxx\nTrue\n```\nLet's grab a process list:\n```\n\u003e\u003e\u003e vmDic['[APT] Windows 10x64 Pro'].updateProcList()\nTrue\n\u003e\u003e\u003e for proc in vmDic['[APT] Windows 10x64 Pro'].procList:\n...     print proc\n... \n0\t\t[System Process]\t\t[System Process]\t\t\n4\t\tSystem\t\tSystem\t\tNT AUTHORITY\\SYSTEM\n264\t\tsmss.exe\t\tsmss.exe\t\tNT AUTHORITY\\SYSTEM\n356\t\tcsrss.exe\t\tcsrss.exe\t\tNT AUTHORITY\\SYSTEM\n424\t\twininit.exe\t\twininit.exe\t\tNT AUTHORITY\\SYSTEM\n432\t\tcsrss.exe\t\tcsrss.exe\t\tNT AUTHORITY\\SYSTEM\n500\t\twinlogon.exe\t\twinlogon.exe\t\tNT AUTHORITY\\SYSTEM\n552\t\tservices.exe\t\tservices.exe\t\tNT AUTHORITY\\SYSTEM\n560\t\tlsass.exe\t\tlsass.exe\t\tNT AUTHORITY\\SYSTEM\n644\t\tsvchost.exe\t\tsvchost.exe\t\tNT AUTHORITY\\SYSTEM\n708\t\tsvchost.exe\t\tsvchost.exe\t\tNT AUTHORITY\\NETWORK SERVICE\n808\t\tLogonUI.exe\t\tLogonUI.exe\t\tNT AUTHORITY\\SYSTEM\n816\t\tdwm.exe\t\tdwm.exe\t\tWindow Manager\\DWM-1\n840\t\tsvchost.exe\t\tsvchost.exe\t\tNT AUTHORITY\\SYSTEM\n956\t\tsvchost.exe\t\tsvchost.exe\t\tNT AUTHORITY\\SYSTEM\n972\t\tsvchost.exe\t\tsvchost.exe\t\tNT AUTHORITY\\LOCAL SERVICE\n984\t\tsvchost.exe\t\tsvchost.exe\t\tNT AUTHORITY\\LOCAL SERVICE\n620\t\tsvchost.exe\t\tsvchost.exe\t\tNT AUTHORITY\\LOCAL SERVICE\n1080\t\tsvchost.exe\t\tsvchost.exe\t\tNT AUTHORITY\\NETWORK SERVICE\n1196\t\tsvchost.exe\t\tsvchost.exe\t\tNT AUTHORITY\\LOCAL SERVICE\n1276\t\tsvchost.exe\t\tsvchost.exe\t\tNT AUTHORITY\\LOCAL SERVICE\n1372\t\tspoolsv.exe\t\tspoolsv.exe\t\tNT AUTHORITY\\SYSTEM\n1456\t\tsvchost.exe\t\tsvchost.exe\t\tNT AUTHORITY\\SYSTEM\n1648\t\tsvchost.exe\t\tsvchost.exe\t\tNT AUTHORITY\\NETWORK SERVICE\n1744\t\tsvchost.exe\t\tsvchost.exe\t\tNT AUTHORITY\\SYSTEM\n1812\t\tVGAuthService.exe\t\tVGAuthService.exe\t\tNT AUTHORITY\\SYSTEM\n1840\t\tsvchost.exe\t\tsvchost.exe\t\tNT AUTHORITY\\SYSTEM\n1848\t\tvmtoolsd.exe\t\tvmtoolsd.exe\t\tNT AUTHORITY\\SYSTEM\n1872\t\tMsMpEng.exe\t\tMsMpEng.exe\t\tNT AUTHORITY\\SYSTEM\n2028\t\tMemory Compression\t\tMemory Compression\t\tNT AUTHORITY\\SYSTEM\n2292\t\tWmiPrvSE.exe\t\tWmiPrvSE.exe\t\tNT AUTHORITY\\NETWORK SERVICE\n2356\t\tdllhost.exe\t\tdllhost.exe\t\tNT AUTHORITY\\SYSTEM\n2452\t\tdllhost.exe\t\tdllhost.exe\t\tNT AUTHORITY\\SYSTEM\n2592\t\tmsdtc.exe\t\tmsdtc.exe\t\tNT AUTHORITY\\NETWORK SERVICE\n2760\t\tVSSVC.exe\t\tVSSVC.exe\t\tNT AUTHORITY\\SYSTEM\n3024\t\tWmiPrvSE.exe\t\tWmiPrvSE.exe\t\tNT AUTHORITY\\SYSTEM\n2424\t\tsppsvc.exe\t\tsppsvc.exe\t\tNT AUTHORITY\\NETWORK SERVICE\n```\nThere are also a couple of functions that support querying, resetting,\ncreating, and deleting snapshots:\n```\n\u003e\u003e\u003e vmDic['[APT] Windows 10x64 Pro'].getSnapshots()\nserverlog:[2017-04-04 16:06:16.775557] FINDING SNAPSHOTS FOR [APT] Windows 10x64 Pro\n\u003e\u003e\u003e for snapshot in vmDic['[APT] Windows 10x64 Pro'].snapshotList:\n...     print snapshot\n... \n((vim.vm.SnapshotTree) {\n   dynamicType = \u003cunset\u003e,\n   dynamicProperty = (vmodl.DynamicProperty) [],\n   snapshot = 'vim.vm.Snapshot:9-snapshot-3',\n   vm = 'vim.VirtualMachine:9',\n   name = 'TURNED_OFF',\n   description = '',\n   id = 3,\n   createTime = 2017-02-26T22:08:42.981401Z,\n   state = 'poweredOff',\n   quiesced = false,\n   backupManifest = \u003cunset\u003e,\n   childSnapshotList = (vim.vm.SnapshotTree) [\n      (vim.vm.SnapshotTree) {\n         dynamicType = \u003cunset\u003e,\n         dynamicProperty = (vmodl.DynamicProperty) [],\n         snapshot = 'vim.vm.Snapshot:9-snapshot-6',\n         vm = 'vim.VirtualMachine:9',\n         name = 'TESTING_BASE',\n         description = '',\n         id = 6,\n         createTime = 2017-03-17T15:11:29.883673Z,\n         state = 'poweredOn',\n         quiesced = false,\n         backupManifest = \u003cunset\u003e,\n         childSnapshotList = (vim.vm.SnapshotTree) [],\n         replaySupported = false\n      }\n   ],\n   replaySupported = false\n}, 'TURNED_OFF')\n((vim.vm.SnapshotTree) {\n   dynamicType = \u003cunset\u003e,\n   dynamicProperty = (vmodl.DynamicProperty) [],\n   snapshot = 'vim.vm.Snapshot:9-snapshot-6',\n   vm = 'vim.VirtualMachine:9',\n   name = 'TESTING_BASE',\n   description = '',\n   id = 6,\n   createTime = 2017-03-17T15:11:29.883673Z,\n   state = 'poweredOn',\n   quiesced = false,\n   backupManifest = \u003cunset\u003e,\n   childSnapshotList = (vim.vm.SnapshotTree) [],\n   replaySupported = false\n}, 'TURNED_OFF/TESTING_BASE')\n```\nI admit that was less than helpful because the snapshots are stored as \npyvmomi snapshot objects, but you can get the snapshot names:\n```\n\u003e\u003e\u003e for snapshot in vmDic['[APT] Windows 10x64 Pro'].snapshotList:\n...     print snapshot[0].name\n... \nTURNED_OFF\nTESTING_BASE\n```\nEven then, it is not much of an issue, as most of the snapshot functions\nuse snapshot names rather than the pyvmomi class variables.\nFor example, let's look at creating a snapshot.  (Turning off the VM\nfirst makes it faster).  There are lots of optional parameters for these\nfunctions, but I assumed most common use-cases as the default values.\n```\n\u003e\u003e\u003e vmDic['[APT] Windows 10x64 Pro'].powerOff()\nserverlog:[2017-04-04 16:16:13.734495] POWERING OFF [APT] Windows 10x64 Pro\nserverlog:[2017-04-04 16:16:18.773459] DONE\nTrue\n\u003e\u003e\u003e vmDic['[APT] Windows 10x64 Pro'].takeSnapshot('new_snapshot')\nserverlog:[2017-04-04 16:17:02.679320] TAKING SNAPSHOT new_snapshot ON [APT] Windows 10x64 Pro\n\u003e\u003e\u003e vmDic['[APT] Windows 10x64 Pro'].getSnapshots()\nserverlog:[2017-04-04 16:18:04.798396] FINDING SNAPSHOTS FOR [APT] Windows 10x64 Pro\n\u003e\u003e\u003e for snapshot in vmDic['[APT] Windows 10x64 Pro'].snapshotList:\n...     print snapshot[0].name\n... \nTURNED_OFF\nTESTING_BASE\nnew_snapshot\n```\nGreat; we created a snapshot, but now let's delete it:\n```\n\u003e\u003e\u003e vmDic['[APT] Windows 10x64 Pro'].deleteSnapshot('new_snapshot')\nserverlog:[2017-04-04 16:18:35.777414] FINDING SNAPSHOTS FOR [APT] Windows 10x64 Pro\nserverlog:[2017-04-04 16:18:35.787257] DELETING SNAPSHOT new_snapshot FROM [APT] Windows 10x64 Pro\nserverlog:[2017-04-04 16:18:40.829540] DONE\nTrue\n\u003e\u003e\u003e vmDic['[APT] Windows 10x64 Pro'].getSnapshots()\nserverlog:[2017-04-04 16:18:50.773661] FINDING SNAPSHOTS FOR [APT] Windows 10x64 Pro\n\u003e\u003e\u003e for snapshot in vmDic['[APT] Windows 10x64 Pro'].snapshotList:\n...     print snapshot[0].name\n... \nTURNED_OFF\nTESTING_BASE\n\u003e\u003e\u003e \n```\n\n\n\nAs a first-use, I implemented this library to support payload testing,\nso the following methods are supported now:\nserver class:\n`connect`\n`enumerateVms`\n`getVersion`\n`waitForVmsToBoot`\n\nVm Class:\n* `checkTools`\n* `deleteSnapshot`\n* `enumerateSnapshotsRecursively`\n* `getArch`\n* `getFileFromGuest`\n* `getSnapshots`\n* `getVmIp`\n* `getUsername`\n* `isPoweredOff`\n* `isPoweredOn`\n* `makeDirOnGuest`\n* `powerOn`\n* `powerOff`\n* `revertToSnapshot`\n* `runCmdOnGuest`\n* `setPassword`\n* `setUsername`\n* `setVmIp`\n* `takeSnapshot`\n* `updateProcList`\n* `uploadAndRun`\n* `uploadFileToGuest`\n* `waitForTask`\n\nThese are less useful in general, but very useful to automated testing.\nIn time, they may get moved:\n* `prepVm`\n* `revertToTestingBase`\n* `revertDevVm`\n* `setTestVm`\n* `takeTempSnapshot`\n\n### This is kind of cool; how can I help?\nThere are several ways to contribute:\n* If there's something you'd like to be able to do with a virtual machine that's not supported, go for it!\n* If there's a hypervisor you'd like supported, please feel free to start a library to support it; just please make sure you match the function names!  I'd love to add support for virtualbox because it is free, but I do not know when I will get time, and I have not looked to how hard it would be.\n* If you want to add Unit testing to make sure that the functions are supported correctly across classes, you would be doing God's work.\n* If you want to go back and catch the VMWare workstation library up to the ESXi library, that would help a lot, and it would just be making sure function names and return values are the same.\n* If you run through the examples above and one is wrong, correct it for quick contributing karma!\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frapid7%2Fvm-automation","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frapid7%2Fvm-automation","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frapid7%2Fvm-automation/lists"}