|
1 |
| -#!/usr/bin/env python |
| 1 | + |
2 | 2 | # Author: William Lam
|
3 | 3 | # Website: www.williamlam.com
|
4 | 4 | # Product: VMware vSphere
|
5 |
| -# Description: vSphere SDK for Python program for creating tiny VMs (1vCPU/128MB) with random names using the Marvel Commics API |
| 5 | +# Description: vSphere SDK for Python (pyvmomi) program for creating tiny VMs (1vCPU/128MB) with random names using the Marvel Comics API |
6 | 6 | # Reference: http://www.williamlam.com/2014/02/having-some-fun-with-marvel-comics-api.html
|
7 | 7 |
|
8 | 8 | """
|
|
11 | 11 |
|
12 | 12 | from optparse import OptionParser, make_option
|
13 | 13 | from pyVim.connect import SmartConnect, Disconnect
|
| 14 | +from pyVmomi import vim, VmomiSupport |
| 15 | +from requests.packages.urllib3.exceptions import InsecureRequestWarning |
14 | 16 | from pyVmomi import vmodl
|
15 | 17 | from pyVmomi import vim
|
16 | 18 | from pprint import pprint
|
|
23 | 25 | import argparse
|
24 | 26 | import atexit
|
25 | 27 | import sys
|
| 28 | +import string |
| 29 | +import ssl |
26 | 30 |
|
27 | 31 | # Marvel API keys
|
28 | 32 | marvel_public_key = ''
|
29 | 33 | marvel_private_key = ''
|
30 | 34 |
|
31 | 35 | def GetArgs():
|
32 |
| - """ |
33 |
| - Supports the command-line arguments listed below. |
34 |
| - """ |
35 |
| - parser = argparse.ArgumentParser(description='Process args for retrieving all the Virtual Machines') |
36 |
| - parser.add_argument('-s', '--host', required=True, action='store', help='Remote host to connect to') |
37 |
| - parser.add_argument('-o', '--port', type=int, default=443, action='store', help='Port to connect on') |
38 |
| - parser.add_argument('-u', '--user', required=True, action='store', help='User name to use when connecting to host') |
39 |
| - parser.add_argument('-p', '--password', required=True, action='store', help='Password to use when connecting to host') |
40 |
| - parser.add_argument('-c', '--count', required=True, action='store', help='Number of VMs to create') |
41 |
| - parser.add_argument('-d', '--datastore', required=True, action='store', help='Name of Datastore to create VM in') |
42 |
| - args = parser.parse_args() |
43 |
| - return args |
| 36 | + """ |
| 37 | + Supports the command-line arguments listed below. |
| 38 | + """ |
| 39 | + parser = argparse.ArgumentParser( |
| 40 | + description='Process args for retrieving all the Virtual Machines') |
| 41 | + parser.add_argument('-s', '--host', required=True, |
| 42 | + action='store', help='Remote host to connect to') |
| 43 | + parser.add_argument('-o', '--port', type=int, default=443, |
| 44 | + action='store', help='Port to connect on') |
| 45 | + parser.add_argument('-u', '--user', required=True, action='store', |
| 46 | + help='User name to use when connecting to host') |
| 47 | + parser.add_argument('-p', '--password', required=True, |
| 48 | + action='store', help='Password to use when connecting to host') |
| 49 | + parser.add_argument('-c', '--count', required=True, |
| 50 | + action='store', help='Number of VMs to create') |
| 51 | + parser.add_argument('-d', '--datastore', required=True, |
| 52 | + action='store', help='Name of Datastore to create VM in') |
| 53 | + parser.add_argument('-e', '--datacenter', required=True, |
| 54 | + action='store', help='Name of Datastore to create VM in') |
| 55 | + parser.add_argument('-f', '--opid', default='create-marvel-vm', |
| 56 | + action='store', help='Name of Datastore to create VM in') |
| 57 | + args = parser.parse_args() |
| 58 | + return args |
| 59 | + |
| 60 | +def find_datacenter(service_instance, datacenter_name): |
| 61 | + content = service_instance.RetrieveContent() |
| 62 | + |
| 63 | + # Traverse the inventory hierarchy starting from the root folder |
| 64 | + for child_entity in content.rootFolder.childEntity: |
| 65 | + if isinstance(child_entity, vim.Datacenter): |
| 66 | + if child_entity.name == datacenter_name: |
| 67 | + return child_entity |
| 68 | + |
| 69 | + # Datacenter not found |
| 70 | + return None |
44 | 71 |
|
45 | 72 | def getMarvelCharacters(number_of_characters):
|
46 | 73 | timestamp = str(int(time.time()))
|
47 | 74 | # hash is required as part of request which is md5(timestamp + private + public key)
|
48 |
| - hash_value = hashlib.md5(timestamp + marvel_private_key + marvel_public_key).hexdigest() |
| 75 | + hash_value = hashlib.md5( |
| 76 | + (timestamp + marvel_private_key + marvel_public_key).encode('utf-8')).hexdigest() |
49 | 77 |
|
50 | 78 | characters = []
|
51 |
| - for x in xrange(number_of_characters): |
52 |
| - #randomly select one of the 1402 Marvel character |
53 |
| - offset = random.randrange(1,1402) |
| 79 | + for x in range(number_of_characters): |
| 80 | + # randomly select one of the 1402 Marvel character |
| 81 | + offset = random.randrange(1, 1402) |
54 | 82 | limit = '1'
|
55 | 83 |
|
56 | 84 | # GET /v1/public/characters
|
57 |
| - url = 'http://gateway.marvel.com:80/v1/public/characters?limit=' + limit + '&offset=' + str(offset) + '&apikey=' + marvel_public_key + '&ts=' + timestamp + '&hash=' + hash_value |
58 |
| - headers = {'content-type':'application/json'} |
| 85 | + url = 'http://gateway.marvel.com:80/v1/public/characters?limit=' + limit + '&offset=' + \ |
| 86 | + str(offset) + '&apikey=' + marvel_public_key + \ |
| 87 | + '&ts=' + timestamp + '&hash=' + hash_value |
| 88 | + headers = {'content-type': 'application/json'} |
59 | 89 | request = requests.get(url, headers=headers)
|
60 | 90 | data = json.loads(request.content)
|
61 | 91 | # retrieve character name & replace spaces with underscore so we don't have stupid spaces in our VM names
|
62 |
| - character = data['data']['results'][0]['name'].strip().replace(' ','_') |
63 |
| - characters.append(character) |
| 92 | + character = 'marvelvm-' + \ |
| 93 | + data['data']['results'][0]['name'].strip().replace(' ', '_') |
| 94 | + characters.append(character.lower()) |
64 | 95 | return characters
|
65 | 96 |
|
66 |
| -def CreateDummyVM(name,si,vmFolder,rp,datastore): |
67 |
| - vmName = 'MARVEL-' + name |
68 |
| - datastorePath = '[' + datastore + '] ' + vmName |
| 97 | +def CreateDummyVM(name, si, vmFolder, rp, datastore): |
| 98 | + datastorePath = f"[{datastore}]" |
69 | 99 |
|
70 |
| - # bare minimum VM shell, no disks. Feel free to edit |
71 |
| - file = vim.vm.FileInfo(logDirectory=None,snapshotDirectory=None,suspendDirectory=None,vmPathName=datastorePath) |
72 |
| - config = vim.vm.ConfigSpec(name=vmName, memoryMB=128, numCPUs=1, files=file, guestId='dosGuest', version='vmx-07') |
| 100 | + # bare minimum VM shell, no disks. Feel free to edit |
| 101 | + file = vim.vm.FileInfo(logDirectory=None, snapshotDirectory=None, |
| 102 | + suspendDirectory=None, vmPathName=datastorePath) |
| 103 | + config = vim.vm.ConfigSpec( |
| 104 | + name=name, memoryMB=128, numCPUs=1, files=file, guestId='dosGuest', version='vmx-13') |
73 | 105 |
|
74 |
| - print "Creating VM " + vmName + " ..." |
75 |
| - task = vmFolder.CreateVM_Task(config=config,pool=rp) |
76 |
| - WaitForTasks([task],si) |
| 106 | + print("Creating VM " + name + " ...") |
| 107 | + task = vmFolder.CreateVM_Task(config=config, pool=rp) |
| 108 | + WaitForTasks([task], si) |
77 | 109 |
|
78 | 110 | # borrowed from poweronvm.py sample
|
79 | 111 | def WaitForTasks(tasks, si):
|
80 |
| - """ |
81 |
| - Given the service instance si and tasks, it returns after all the |
82 |
| - tasks are complete |
83 |
| - """ |
84 |
| - |
85 |
| - pc = si.content.propertyCollector |
86 |
| - |
87 |
| - taskList = [str(task) for task in tasks] |
88 |
| - |
89 |
| - # Create filter |
90 |
| - objSpecs = [vmodl.query.PropertyCollector.ObjectSpec(obj=task) |
91 |
| - for task in tasks] |
92 |
| - propSpec = vmodl.query.PropertyCollector.PropertySpec(type=vim.Task, |
93 |
| - pathSet=[], all=True) |
94 |
| - filterSpec = vmodl.query.PropertyCollector.FilterSpec() |
95 |
| - filterSpec.objectSet = objSpecs |
96 |
| - filterSpec.propSet = [propSpec] |
97 |
| - filter = pc.CreateFilter(filterSpec, True) |
98 |
| - |
99 |
| - try: |
100 |
| - version, state = None, None |
101 |
| - |
102 |
| - # Loop looking for updates till the state moves to a completed state. |
103 |
| - while len(taskList): |
104 |
| - update = pc.WaitForUpdates(version) |
105 |
| - for filterSet in update.filterSet: |
106 |
| - for objSet in filterSet.objectSet: |
107 |
| - task = objSet.obj |
108 |
| - for change in objSet.changeSet: |
109 |
| - if change.name == 'info': |
110 |
| - state = change.val.state |
111 |
| - elif change.name == 'info.state': |
112 |
| - state = change.val |
113 |
| - else: |
114 |
| - continue |
115 |
| - |
116 |
| - if not str(task) in taskList: |
117 |
| - continue |
118 |
| - |
119 |
| - if state == vim.TaskInfo.State.success: |
120 |
| - # Remove task from taskList |
121 |
| - taskList.remove(str(task)) |
122 |
| - elif state == vim.TaskInfo.State.error: |
123 |
| - raise task.info.error |
124 |
| - # Move to next version |
125 |
| - version = update.version |
126 |
| - finally: |
127 |
| - if filter: |
128 |
| - filter.Destroy() |
| 112 | + """ |
| 113 | + Given the service instance si and tasks, it returns after all the |
| 114 | + tasks are complete |
| 115 | + """ |
| 116 | + |
| 117 | + pc = si.content.propertyCollector |
| 118 | + |
| 119 | + taskList = [str(task) for task in tasks] |
| 120 | + |
| 121 | + # Create filter |
| 122 | + objSpecs = [vmodl.query.PropertyCollector.ObjectSpec(obj=task) |
| 123 | + for task in tasks] |
| 124 | + propSpec = vmodl.query.PropertyCollector.PropertySpec(type=vim.Task, pathSet=[], all=True) |
| 125 | + filterSpec = vmodl.query.PropertyCollector.FilterSpec() |
| 126 | + filterSpec.objectSet = objSpecs |
| 127 | + filterSpec.propSet = [propSpec] |
| 128 | + filter = pc.CreateFilter(filterSpec, True) |
| 129 | + |
| 130 | + try: |
| 131 | + version, state = None, None |
| 132 | + |
| 133 | + # Loop looking for updates till the state moves to a completed state. |
| 134 | + while len(taskList): |
| 135 | + update = pc.WaitForUpdates(version) |
| 136 | + for filterSet in update.filterSet: |
| 137 | + for objSet in filterSet.objectSet: |
| 138 | + task = objSet.obj |
| 139 | + for change in objSet.changeSet: |
| 140 | + if change.name == 'info': |
| 141 | + state = change.val.state |
| 142 | + elif change.name == 'info.state': |
| 143 | + state = change.val |
| 144 | + else: |
| 145 | + continue |
| 146 | + |
| 147 | + if not str(task) in taskList: |
| 148 | + continue |
| 149 | + |
| 150 | + if state == vim.TaskInfo.State.success: |
| 151 | + # Remove task from taskList |
| 152 | + taskList.remove(str(task)) |
| 153 | + elif state == vim.TaskInfo.State.error: |
| 154 | + raise task.info.error |
| 155 | + # Move to next version |
| 156 | + version = update.version |
| 157 | + finally: |
| 158 | + if filter: |
| 159 | + filter.Destroy() |
129 | 160 |
|
130 | 161 | def main():
|
131 |
| - """ |
132 |
| - Simple command-line program for creating Dummy VM based on Marvel character names |
133 |
| - """ |
134 |
| - |
135 |
| - # Ensure user sets up Marvel API keys |
136 |
| - if marvel_public_key == '' or marvel_private_key == '': |
137 |
| - print "\nPlease configure your Marvel Public/Private API Key by setting marvel_public_key and marvel_private_key variable\n" |
138 |
| - return -1 |
139 |
| - |
140 |
| - args = GetArgs() |
141 |
| - try: |
142 |
| - si = None |
143 |
| - try: |
144 |
| - si = SmartConnect(host=args.host, |
145 |
| - user=args.user, |
146 |
| - pwd=args.password, |
147 |
| - port=int(args.port)) |
148 |
| - except IOError, e: |
149 |
| - pass |
150 |
| - if not si: |
151 |
| - print "Could not connect to the specified host using specified username and password" |
152 |
| - return -1 |
153 |
| - |
154 |
| - atexit.register(Disconnect, si) |
155 |
| - |
156 |
| - content = si.RetrieveContent() |
157 |
| - datacenter = content.rootFolder.childEntity[0] |
158 |
| - vmFolder = datacenter.vmFolder |
159 |
| - hosts = datacenter.hostFolder.childEntity |
160 |
| - rp = hosts[0].resourcePool |
161 |
| - |
162 |
| - print "Connecting to Marvel API and retrieving " + args.count + " random character(s) ..." |
163 |
| - characters = getMarvelCharacters(int(args.count)) |
164 |
| - |
165 |
| - for name in characters: |
166 |
| - CreateDummyVM(name,si,vmFolder,rp,args.datastore) |
167 |
| - |
168 |
| - except vmodl.MethodFault, e: |
169 |
| - print "Caught vmodl fault : " + e.msg |
170 |
| - return -1 |
171 |
| - except Exception, e: |
172 |
| - print "Caught exception : " + str(e) |
173 |
| - return -1 |
174 |
| - |
175 |
| - return 0 |
| 162 | + """ |
| 163 | + Simple command-line program for creating Dummy VM based on Marvel character names |
| 164 | + """ |
| 165 | + |
| 166 | + args = GetArgs() |
| 167 | + |
| 168 | + reqCtx = VmomiSupport.GetRequestContext() |
| 169 | + reqCtx["operationID"] = args.opid |
| 170 | + |
| 171 | + try: |
| 172 | + context = None |
| 173 | + try: |
| 174 | + if sys.version_info[:3] > (2, 7, 8): |
| 175 | + context = ssl.create_default_context() |
| 176 | + context.check_hostname = False |
| 177 | + context.verify_mode = ssl.CERT_NONE |
| 178 | + |
| 179 | + # Disabling the annoying InsecureRequestWarning message |
| 180 | + requests.packages.urllib3.disable_warnings(InsecureRequestWarning) |
| 181 | + |
| 182 | + si = SmartConnect(host=args.host, |
| 183 | + user=args.user, |
| 184 | + pwd=args.password, |
| 185 | + port=int(args.port), |
| 186 | + sslContext=context) |
| 187 | + except IOError as e: |
| 188 | + pass |
| 189 | + if not si: |
| 190 | + print( |
| 191 | + "Could not connect to the specified host using specified username and password") |
| 192 | + return -1 |
| 193 | + |
| 194 | + atexit.register(Disconnect, si) |
| 195 | + |
| 196 | + content = si.RetrieveContent() |
| 197 | + dc = find_datacenter(si, args.datacenter) |
| 198 | + vmFolder = dc.vmFolder |
| 199 | + hosts = dc.hostFolder.childEntity |
| 200 | + rp = hosts[0].resourcePool |
| 201 | + |
| 202 | + # Ensure user sets up Marvel API keys |
| 203 | + if marvel_public_key == '' or marvel_private_key == '': |
| 204 | + print("\nPlease configure your Marvel Public/Private API Key by setting marvel_public_key and marvel_private_key variable\n") |
| 205 | + print("Generating random VM name for now ...") |
| 206 | + characters = ['randomvm-' + ''.join(random.choice(string.ascii_letters) |
| 207 | + for _ in range(10)) for _ in range(int(args.count))] |
| 208 | + else: |
| 209 | + print("Connecting to Marvel API and retrieving " + args.count + " random character(s) ...") |
| 210 | + characters = getMarvelCharacters(int(args.count)) |
| 211 | + |
| 212 | + for name in characters: |
| 213 | + CreateDummyVM(name, si, vmFolder, rp, args.datastore) |
| 214 | + |
| 215 | + except vmodl.MethodFault as e: |
| 216 | + print("Caught vmodl fault : " + e.msg) |
| 217 | + return -1 |
| 218 | + except Exception as e: |
| 219 | + print("Caught exception : " + str(e)) |
| 220 | + return -1 |
| 221 | + |
| 222 | + return 0 |
176 | 223 |
|
177 | 224 | # Start program
|
178 | 225 | if __name__ == "__main__":
|
|
0 commit comments