mirror of
https://github.com/vmware/vsphere-automation-sdk-python.git
synced 2024-11-25 02:39:59 -05:00
303 lines
13 KiB
Python
303 lines
13 KiB
Python
|
#!/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()
|