Utils/ScriptUtil.py (100 lines of code) (raw):
# Script utilities
#
# Copyright 2014 Microsoft Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import os.path
import time
import subprocess
import traceback
import string
import shlex
import sys
from Utils import LogUtil
from Utils.WAAgentUtil import waagent
DefaultStdoutFile = "stdout"
DefaultErroutFile = "errout"
def run_command(hutil, args, cwd, operation, extension_short_name, version, exit_after_run=True, interval=30,
std_out_file_name=DefaultStdoutFile, std_err_file_name=DefaultErroutFile):
std_out_file = os.path.join(cwd, std_out_file_name)
err_out_file = os.path.join(cwd, std_err_file_name)
std_out = None
err_out = None
try:
std_out = open(std_out_file, "w")
err_out = open(err_out_file, "w")
start_time = time.time()
child = subprocess.Popen(args,
cwd=cwd,
stdout=std_out,
stderr=err_out)
time.sleep(1)
while child.poll() is None:
msg = "Command is running..."
msg_with_cmd_output = LogUtil.get_formatted_log(msg, LogUtil.tail(std_out_file), LogUtil.tail(err_out_file))
msg_without_cmd_output = msg + " Stdout/Stderr omitted from output."
hutil.log_to_file(msg_with_cmd_output)
hutil.log_to_console(msg_without_cmd_output)
hutil.do_status_report(operation, 'transitioning', '0', msg_without_cmd_output)
time.sleep(interval)
exit_code = child.returncode
if child.returncode and child.returncode != 0:
msg = "Command returned an error."
msg_with_cmd_output = LogUtil.get_formatted_log(msg, LogUtil.tail(std_out_file), LogUtil.tail(err_out_file))
msg_without_cmd_output = msg + " Stdout/Stderr omitted from output."
hutil.error(msg_without_cmd_output)
waagent.AddExtensionEvent(name=extension_short_name,
op=operation,
isSuccess=False,
version=version,
message="(01302)" + msg_without_cmd_output)
else:
msg = "Command is finished."
msg_with_cmd_output = LogUtil.get_formatted_log(msg, LogUtil.tail(std_out_file), LogUtil.tail(err_out_file))
msg_without_cmd_output = msg + " Stdout/Stderr omitted from output."
hutil.log_to_file(msg_with_cmd_output)
hutil.log_to_console(msg_without_cmd_output)
waagent.AddExtensionEvent(name=extension_short_name,
op=operation,
isSuccess=True,
version=version,
message="(01302)" + msg_without_cmd_output)
end_time = time.time()
waagent.AddExtensionEvent(name=extension_short_name,
op=operation,
isSuccess=True,
version=version,
message=("(01304)Command execution time: "
"{0}s").format(str(end_time - start_time)))
log_or_exit(hutil, exit_after_run, exit_code, operation, msg_with_cmd_output)
except Exception as e:
error_msg = ("Failed to launch command with error: {0},"
"stacktrace: {1}").format(e, traceback.format_exc())
hutil.error(error_msg)
waagent.AddExtensionEvent(name=extension_short_name,
op=operation,
isSuccess=False,
version=version,
message="(01101)" + error_msg)
exit_code = 1
msg = 'Launch command failed: {0}'.format(e)
log_or_exit(hutil, exit_after_run, exit_code, operation, msg)
finally:
if std_out:
std_out.close()
if err_out:
err_out.close()
return exit_code
# do_exit calls sys.exit which raises an exception so we do not call it from the finally block
def log_or_exit(hutil, exit_after_run, exit_code, operation, msg):
status = 'success' if exit_code == 0 else 'failed'
if exit_after_run:
hutil.do_exit(exit_code, operation, status, str(exit_code), msg)
else:
hutil.do_status_report(operation, status, str(exit_code), msg)
def parse_args(cmd):
cmd = filter(lambda x: x in string.printable, cmd)
# encoding works different for between interpreter version, we are keeping separate implementation to ensure
# backward compatibility
if sys.version_info[0] == 3:
cmd = ''.join(list(cmd)).encode('ascii', 'ignore').decode("ascii", "ignore")
elif sys.version_info[0] == 2:
cmd = cmd.decode("ascii", "ignore")
args = shlex.split(cmd)
# From python 2.6 to python 2.7.2, shlex.split output UCS-4 result like
# '\x00\x00a'. Temp workaround is to replace \x00
for idx, val in enumerate(args):
if '\x00' in args[idx]:
args[idx] = args[idx].replace('\x00', '')
return args