-
Notifications
You must be signed in to change notification settings - Fork 306
/
guest_ops.py
302 lines (259 loc) · 12.9 KB
/
guest_ops.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
#!/usr/bin/env python
"""
* *******************************************************
* Copyright (c) VMware, Inc. 2020. All Rights Reserved.
* SPDX-License-Identifier: MIT
* *******************************************************
*
* DISCLAIMER. THIS PROGRAM IS PROVIDED TO YOU "AS IS" WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, WHETHER ORAL OR WRITTEN,
* EXPRESS OR IMPLIED. THE AUTHOR SPECIFICALLY DISCLAIMS ANY IMPLIED
* WARRANTIES OR CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY,
* NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE.
"""
__author__ = 'VMware Inc.'
__vcenter_version__ = 'VCenter 7.0 U2'
import os
import ssl
import time
from com.vmware.vcenter.vm.guest.filesystem_client import Transfers
from com.vmware.vcenter.vm.guest_client import Credentials
from com.vmware.vcenter.vm.guest_client import Processes
from com.vmware.vcenter_client import VM
from samples.vsphere.common import sample_cli
from samples.vsphere.common import sample_util
from samples.vsphere.common.ssl_helper import get_unverified_session
from vmware.vapi.vsphere.client import create_vsphere_client
try:
# Python 3
from urllib.parse import urlparse
import http.client as httpclient
except ImportError:
# Python 2
from urlparse import urlparse
import httplib as httpclient
class GuestOps(object):
"""
Demonstrate the vAPI Guest Operations.
Show the basic procedure to start a program or process on a Linux
guest VM and collect any output:
- create a temporary directory and temporary files for the process
stdout and stderr.
- upload a script to the guest.
- execute the script and collect the output.
Prerequisites:
- vCenter
- ESX host
- running guest 'Photon-3.2-64-EFI-Open-VM-Tools-for-GuestOps-SDK'
with open-vm-tools installed and running. The testbed created by
samples/vsphere/vcenter/setup/main.py does not contain a guest
with a runnable Linux or Windows OS.
"""
# Create the Process.CreateSpec for initiating processes in the guest
def _process_create_spec(self, path, args=None, dir=None, env={}):
return Processes.CreateSpec(path=path,
arguments=args,
working_directory=dir,
environment_variables=env)
# Create the Transfer.CreateSpec for the file transfer to/from the guest
def _create_transfer_spec(self,
path,
attributes=None):
return Transfers.CreateSpec(attributes=attributes,
path=path)
# Create a FileAttributeCreateSpec for a generic (non-OS specific) guest
def _fileAttributeCreateSpec_Plain(self,
size,
overwrite=None,
last_modified=None,
last_accessed=None):
return Transfers.FileCreationAttributes(size,
overwrite=overwrite,
last_modified=last_modified,
last_accessed=last_accessed)
# Create a FileAttributeCreateSpec for a linux (Posix) guest
def _fileAttributeCreateSpec_Linux(self,
size,
overwrite=None,
last_modified=None,
last_accessed=None,
owner_id=None,
group_id=None,
permissions=None):
posix = Transfers.PosixFileAttributesCreateSpec(owner_id=owner_id,
group_id=group_id,
permissions=permissions)
return Transfers.FileCreationAttributes(size,
overwrite=overwrite,
last_modified=last_modified,
last_accessed=last_accessed,
posix=posix)
def _download(self,
url,
expectedLen=None):
urloptions = urlparse(url)
# Skip server cert verification.
# This is not recommended in production code.
conn = httpclient.HTTPSConnection(urloptions.netloc,
context=ssl._create_unverified_context())
conn.request("GET", urloptions.path + "?" + urloptions.query)
res = conn.getresponse()
if res.status != 200:
print("GET request failed with errorcode : %s" % res.status)
raise HTTPError(res.status, res.reason)
body = res.read().decode()
return body
def _upload(self, url, body):
urloptions = urlparse(url)
conn = httpclient.HTTPSConnection(urloptions.netloc,
context=ssl._create_unverified_context())
headers = {"Content-Length": len(body)}
# Skip server cert verification.
# This is not recommended in production code.
conn.request("PUT", urloptions.path + "?" + urloptions.query,
body,
headers)
res = conn.getresponse()
if res.status != 200:
print("PUT request failed with errorcode : %s" % res.status)
raise HTTPError(res.status, res.reason)
return res
def __init__(self):
# Create argument parser for standard inputs:
# server, username, password, cleanup and skipverification
parser = sample_cli.build_arg_parser()
# Add your custom input arguments
parser.add_argument('--vm_name',
action='store',
help='Name of the testing vm')
parser.add_argument('--root_user',
action='store',
help='Administrator account user name')
parser.add_argument('--root_passwd',
action='store',
help='Administrator account password')
args = sample_util.process_cli_args(parser.parse_args())
self.vm_name = args.vm_name
self.root_user = args.root_user
self.root_passwd = args.root_passwd
self.cleardata = args.cleardata
# Skip server cert verification if needed.
# This is not recommended in production code.
session = get_unverified_session() if args.skipverification else None
# Connect to vSphere client
self.client = create_vsphere_client(server=args.server,
username=args.username,
password=args.password,
session=session)
def run(self):
# Using vAPI to find VM.
filter_spec = VM.FilterSpec(names=set([self.vm_name]))
vms = self.client.vcenter.VM.list(filter_spec)
if len(vms) != 1:
raise Exception('Could not locate the required VM with name ' +
self.vm_name + '. Please create the vm first.')
if vms[0].power_state != 'POWERED_ON':
raise Exception('VM is not powered on: ' + vms[0].power_state)
vm_id = vms[0].vm
# Check that vmtools svc (non-interactive user) is running.
info = self.client.vcenter.vm.guest.Operations.get(vm_id)
if info.guest_operations_ready is not True:
raise Exception('VMware Tools/open-vm-tools is not running as required.')
# Establish the user credentials that will be needed for all Guest Ops
# APIs.
creds = Credentials(interactive_session=False,
user_name=self.root_user,
password=self.root_passwd,
type=Credentials.Type.USERNAME_PASSWORD)
# Step 2 - Create a temporary directory from which to run the command
# and capture any output
tempDir = self.client.vcenter.vm.guest.filesystem.Directories.create_temporary(
vm_id, creds, '', '', parent_path=None)
# Step 3 - Create temproary files to reveive stdout and stderr
# as needed.
stdout = self.client.vcenter.vm.guest.filesystem.Files.create_temporary(
vm_id, creds, '', '.stdout', parent_path=tempDir)
stderr = self.client.vcenter.vm.guest.filesystem.Files.create_temporary(
vm_id, creds, '', '.stderr', parent_path=tempDir)
# Step 4 - (Optional) copy in the script to be run.
# While optional, using this step to demo tranfer of a
# file to a guest.
scriptPath = self.client.vcenter.vm.guest.filesystem.Files.create_temporary(
vm_id, creds, '', '.sh', tempDir)
# Create script contents and transfer to the guest.
# TODO: Need generic pick up of script content
baseFN = os.path.basename(scriptPath)
script = ('#! /bin/bash\n'
'# ' +
baseFN + '\n'
'\n'
'sleep 5 # Adding a little length to the process.\n'
'ps -ef\n'
'echo\n'
'rpm -qa | sort\n'
'\n')
print(script)
attr = self._fileAttributeCreateSpec_Linux(size=len(script),
overwrite=True,
permissions='0755')
spec = self._create_transfer_spec(path=scriptPath,
attributes=attr)
toURL = self.client.vcenter.vm.guest.filesystem.Transfers.create(vm_id,
creds,
spec)
res = self._upload(toURL, script)
# Check that the uploaded file size is correct.
info = self.client.vcenter.vm.guest.filesystem.Files.get(vm_id,
creds,
scriptPath)
if info.size != len(script):
raise Exception('Uploaded file size not as epected.')
# Step 5 - Start the program on the guest, capturing stdout and stderr
# in the separate temp files obtained earlier.
options = (" > " + stdout + " 2> " + stderr)
spec = self._process_create_spec(scriptPath,
args=options,
dir=tempDir)
pid = self.client.vcenter.vm.guest.Processes.create(vm_id, creds, spec)
print('process created with pid: %s\n' % pid)
# Step 6
# Need a loop to wait for the process to finish to handle longer
# running processes.
while True:
time.sleep(1.0)
try:
# List the single process for pid.
result = self.client.vcenter.vm.guest.Processes.get(vm_id,
creds,
pid)
if result.exit_code is not None:
print('Command: ' + result.command)
print('Exit code: %s\n' % result.exit_code)
break
if result.finished is None:
print('Process with pid %s is still running.' % pid)
continue
except Exception as e:
raise e
# Step 7 Copy out the results (stdout).
spec = self._create_transfer_spec(path=stdout)
# create the download URL
fromURL = self.client.vcenter.vm.guest.filesystem.Transfers.create(vm_id,
creds,
spec)
body = self._download(fromURL, expectedLen=info.size)
print("----------- stdout ------------------")
print(body)
print("---------------------------------------")
# Optionally the contents of "stderr" could be downloaded.
# And finally, clean up the temporary files and directories on the
# Linux guest. Deleting the temporary diretory and its contents.
self.client.vcenter.vm.guest.filesystem.Directories.delete(vm_id,
creds,
tempDir,
recursive=True)
def main():
sample = GuestOps()
sample.run()
if __name__ == '__main__':
main()