Update waagent2.0

This commit is contained in:
Yue Zhang
2016-01-12 17:10:57 +08:00
parent 2c949b3077
commit bb0678b821
Executable → Regular
+296 -98
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env python
#
# Microsoft Azure Linux Agent
# Azure Linux Agent
#
# Copyright 2015 Microsoft Corporation
#
@@ -23,6 +23,8 @@
# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
#
import crypt
import random
import array
import base64
import httplib
@@ -50,6 +52,7 @@ import zipfile
import json
import datetime
import xml.sax.saxutils
from distutils.version import LooseVersion
if not hasattr(subprocess,'check_output'):
def check_output(*popenargs, **kwargs):
@@ -79,8 +82,8 @@ if not hasattr(subprocess,'check_output'):
subprocess.CalledProcessError=CalledProcessError
GuestAgentName = "WALinuxAgent"
GuestAgentLongName = "Microsoft Azure Linux Agent"
GuestAgentVersion = "WALinuxAgent-2.0.15-pre"
GuestAgentLongName = "Azure Linux Agent"
GuestAgentVersion = "WALinuxAgent-2.0.16"
ProtocolVersion = "2012-11-30" #WARNING this value is used to confirm the correct fabric protocol.
Config = None
@@ -106,7 +109,7 @@ HandlerStatusToAggStatus = {"installed":"Installing", "enabled":"Ready", "uninta
WaagentConf = """\
#
# Microsoft Azure Linux Agent Configuration
# Azure Linux Agent Configuration
#
Role.StateConsumer=None # Specified program is invoked with the argument "Ready" when we report ready status
@@ -126,7 +129,7 @@ ResourceDisk.MountPoint=/mnt/resource #
ResourceDisk.EnableSwap=n # Create and use swapfile on resource disk.
ResourceDisk.SwapSizeMB=0 # Size of the swapfile.
LBProbeResponder=y # Respond to load balancer probes if requested by Microsoft Azure.
LBProbeResponder=y # Respond to load balancer probes if requested by Azure.
Logs.Verbose=n # Enable verbose logs
@@ -173,8 +176,8 @@ class AbstractDistro(object):
self.ssh_config_file='/etc/ssh/sshd_config'
self.hostname_file_path='/etc/hostname'
self.dhcp_client_name='dhclient'
self.requiredDeps = [ 'route', 'shutdown', 'ssh-keygen', 'useradd',
'openssl', 'sfdisk', 'fdisk', 'mkfs', 'chpasswd',
self.requiredDeps = [ 'route', 'shutdown', 'ssh-keygen', 'useradd', 'usermod',
'openssl', 'sfdisk', 'fdisk', 'mkfs',
'sed', 'grep', 'sudo', 'parted' ]
self.init_script_file='/etc/init.d/waagent'
self.agent_package_name='WALinuxAgent'
@@ -187,6 +190,7 @@ class AbstractDistro(object):
self.sudoers_dir_base = '/etc'
self.waagent_conf_file = WaagentConf
self.shadow_file_mode=0600
self.shadow_file_path="/etc/shadow"
self.dhcp_enabled = False
def isSelinuxSystem(self):
@@ -341,8 +345,35 @@ class AbstractDistro(object):
return 0
def changePass(self,user,password):
return RunSendStdin("chpasswd",(user + ":" + password + "\n"))
Log("Change user password")
crypt_id = Config.get("Provisioning.PasswordCryptId")
if crypt_id is None:
crypt_id = "6"
salt_len = Config.get("Provisioning.PasswordCryptSaltLength")
try:
salt_len = int(salt_len)
if salt_len < 0 or salt_len > 10:
salt_len = 10
except (ValueError, TypeError):
salt_len = 10
return self.chpasswd(user, password, crypt_id=crypt_id,
salt_len=salt_len)
def chpasswd(self, username, password, crypt_id=6, salt_len=10):
passwd_hash = self.gen_password_hash(password, crypt_id, salt_len)
cmd = "usermod -p '{0}' {1}".format(passwd_hash, username)
ret, output = RunGetOutput(cmd, log_cmd=False)
if ret != 0:
return "Failed to set password for {0}: {1}".format(username, output)
def gen_password_hash(self, password, crypt_id, salt_len):
collection = string.ascii_letters + string.digits
salt = ''.join(random.choice(collection) for _ in range(salt_len))
salt = "${0}${1}".format(crypt_id, salt)
return crypt.crypt(password, salt)
def load_ata_piix(self):
return WaAgent.TryLoadAtapiix()
@@ -438,8 +469,15 @@ class AbstractDistro(object):
def GetInterfaceName(self):
return GetFirstActiveNetworkInterfaceNonLoopback()[0]
def RestartInterface(self, iface):
Run("ifdown " + iface + " && ifup " + iface)
def RestartInterface(self, iface, max_retry=3):
for retry in range(1, max_retry + 1):
ret = Run("ifdown " + iface + " && ifup " + iface)
if ret == 0:
return
Log("Failed to restart interface: {0}, ret={1}".format(iface, ret))
if retry < max_retry:
Log("Retry restart interface in 5 seconds")
time.sleep(5)
def CreateAccount(self,user, password, expiration, thumbprint):
return CreateAccount(user, password, expiration, thumbprint)
@@ -522,6 +560,7 @@ class AbstractDistro(object):
if not os.path.isfile(mountpoint + "/swapfile"):
Run("dd if=/dev/zero of=" + mountpoint + "/swapfile bs=1024 count=" + str(sizeKB))
Run("mkswap " + mountpoint + "/swapfile")
Run("chmod 600 " + mountpoint + "/swapfile")
if not Run("swapon " + mountpoint + "/swapfile"):
Log("Enabled " + str(sizeKB) + " KB of swap at " + mountpoint + "/swapfile")
else:
@@ -645,6 +684,13 @@ class AbstractDistro(object):
if ret != 0:
raise Exception("Failed to config ipv4 for {0}: {1}".format(ifName,
output))
def setDefaultGateway(self, gateway):
Run("/sbin/route add default gw" + gateway, chk_err=False)
def routeAdd(self, net, mask, gateway):
Run("/sbin/route add -net " + net + " netmask " + mask + " gw " + gateway,
chk_err=False)
############################################################
# GentooDistro
@@ -656,7 +702,7 @@ command=/usr/sbin/waagent
pidfile=/var/run/waagent.pid
command_args=-daemon
command_background=true
name="Microsoft Azure Linux Agent"
name="Azure Linux Agent"
depend()
{
@@ -725,7 +771,7 @@ class gentooDistro(AbstractDistro):
suse_init_file = """\
#! /bin/sh
#
# Microsoft Azure Linux Agent sysV init script
# Azure Linux Agent sysV init script
#
# Copyright 2013 Microsoft Corporation
# Copyright SUSE LLC
@@ -751,12 +797,12 @@ suse_init_file = """\
# System startup script for the waagent
#
### BEGIN INIT INFO
# Provides: MicrosoftAzureLinuxAgent
# Provides: AzureLinuxAgent
# Required-Start: $network sshd
# Required-Stop: $network sshd
# Default-Start: 3 5
# Default-Stop: 0 1 2 6
# Description: Start the MicrosoftAzureLinuxAgent
# Description: Start the AzureLinuxAgent
### END INIT INFO
PYTHON=/usr/bin/python
@@ -789,14 +835,14 @@ rc_reset
case "$1" in
start)
echo -n "Starting MicrosoftAzureLinuxAgent"
echo -n "Starting AzureLinuxAgent"
## Start daemon with startproc(8). If this fails
## the echo return value is set appropriate.
startproc -f ${PYTHON} ${WAZD_BIN} -daemon
rc_status -v
;;
stop)
echo -n "Shutting down MicrosoftAzureLinuxAgent"
echo -n "Shutting down AzureLinuxAgent"
## Stop daemon with killproc(8) and if this fails
## set echo the echo return value.
killproc -p ${WAZD_PIDFILE} ${PYTHON} ${WAZD_BIN}
@@ -820,7 +866,7 @@ case "$1" in
rc_status
;;
status)
echo -n "Checking for service MicrosoftAzureLinuxAgent "
echo -n "Checking for service AzureLinuxAgent "
## Check status with checkproc(8), if process is running
## checkproc will return with exit status 0.
@@ -902,17 +948,17 @@ class SuSEDistro(AbstractDistro):
redhat_init_file= """\
#!/bin/bash
#
# Init file for MicrosoftAzureLinuxAgent.
# Init file for AzureLinuxAgent.
#
# chkconfig: 2345 60 80
# description: MicrosoftAzureLinuxAgent
# description: AzureLinuxAgent
#
# source function library
. /etc/rc.d/init.d/functions
RETVAL=0
FriendlyName="MicrosoftAzureLinuxAgent"
FriendlyName="AzureLinuxAgent"
WAZD_BIN=/usr/sbin/waagent
start()
@@ -1014,7 +1060,24 @@ class redhatDistro(AbstractDistro):
else:
return 0
def checkDependencies(self):
"""
Generic dependency check.
Return 1 unless all dependencies are satisfied.
"""
if DistInfo()[1] < '7.0' and self.checkPackageInstalled('NetworkManager'):
Error(GuestAgentLongName + " is not compatible with network-manager.")
return 1
try:
m= __import__('pyasn1')
except ImportError:
Error(GuestAgentLongName + " requires python-pyasn1 for your Linux distribution.")
return 1
for a in self.requiredDeps:
if Run("which " + a + " > /dev/null 2>&1",chk_err=False):
Error("Missing required dependency: " + a)
return 1
return 0
############################################################
# centosDistro
@@ -1028,6 +1091,19 @@ class centosDistro(redhatDistro):
def __init__(self):
super(centosDistro,self).__init__()
############################################################
# oracleDistro
############################################################
class oracleDistro(redhatDistro):
"""
Oracle Distro concrete class
Put Oracle specific behavior here...
"""
def __init__(self):
super(oracleDistro, self).__init__()
############################################################
# asianuxDistro
@@ -1150,7 +1226,7 @@ class CoreOSDistro(AbstractDistro):
else:
Log("CreateAccount: " + user + " already exists. Will update password.")
if password != None:
RunSendStdin("chpasswd", user + ":" + password + "\n")
self.changePass(user, password)
try:
if password == None:
SetFileContents("/etc/sudoers.d/waagent", user + " ALL = (ALL) NOPASSWD: ALL\n")
@@ -1194,15 +1270,15 @@ class CoreOSDistro(AbstractDistro):
debian_init_file = """\
#!/bin/sh
### BEGIN INIT INFO
# Provides: MicrosoftAzureLinuxAgent
# Provides: AzureLinuxAgent
# Required-Start: $network $syslog
# Required-Stop: $network $syslog
# Should-Start: $network $syslog
# Should-Stop: $network $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: MicrosoftAzureLinuxAgent
# Description: MicrosoftAzureLinuxAgent
# Short-Description: AzureLinuxAgent
# Description: AzureLinuxAgent
### END INIT INFO
. /lib/lsb/init-functions
@@ -1213,7 +1289,7 @@ WAZD_PID=/var/run/waagent.pid
case "$1" in
start)
log_begin_msg "Starting MicrosoftAzureLinuxAgent..."
log_begin_msg "Starting AzureLinuxAgent..."
pid=$( pidofproc $WAZD_BIN )
if [ -n "$pid" ] ; then
log_begin_msg "Already running."
@@ -1225,7 +1301,7 @@ case "$1" in
;;
stop)
log_begin_msg "Stopping MicrosoftAzureLinuxAgent..."
log_begin_msg "Stopping AzureLinuxAgent..."
start-stop-daemon --stop --quiet --oknodo --pidfile $WAZD_PID
ret=$?
rm -f $WAZD_PID
@@ -1350,7 +1426,7 @@ class KaliDistro(debianDistro):
# UbuntuDistro
############################################################
ubuntu_upstart_file = """\
#walinuxagent - start Microsoft Azure agent
#walinuxagent - start Azure agent
description "walinuxagent"
author "Ben Howard <ben.howard@canonical.com>"
@@ -1474,7 +1550,7 @@ class LinuxMintDistro(UbuntuDistro):
############################################################
fedora_systemd_service = """\
[Unit]
Description=Microsoft Azure Linux Agent
Description=Azure Linux Agent
After=network.target
After=sshd.service
ConditionFileIsExecutable=/usr/sbin/waagent
@@ -1599,7 +1675,7 @@ class fedoraDistro(redhatDistro):
############################################################
FreeBSDWaagentConf = """\
#
# Microsoft Azure Linux Agent Configuration
# Azure Linux Agent Configuration
#
Role.StateConsumer=None # Specified program is invoked with the argument "Ready" when we report ready status
@@ -1619,7 +1695,7 @@ ResourceDisk.MountPoint=/mnt/resource #
ResourceDisk.EnableSwap=n # Create and use swapfile on resource disk.
ResourceDisk.SwapSizeMB=0 # Size of the swapfile.
LBProbeResponder=y # Respond to load balancer probes if requested by Microsoft Azure.
LBProbeResponder=y # Respond to load balancer probes if requested by Azure.
Logs.Verbose=n # Enable verbose logs
@@ -1661,7 +1737,7 @@ __name__='setupmain' #prevent waagent.__main__ from executing
waagent=imp.load_source('waagent','/tmp/waagent')
waagent.LoggerInit('/var/log/waagent.log','/dev/console')
from waagent import RunGetOutput,Run
Config=waagent.ConfigurationProvider()
Config=waagent.ConfigurationProvider(None)
format = Config.get("ResourceDisk.Format")
if format == None or format.lower().startswith("n"):
sys.exit(0)
@@ -1764,7 +1840,7 @@ class FreeBSDDistro(AbstractDistro):
return 0
def changePass(self,user,password):
return RunSendStdin("pw usermod " + user + " -h 0 ",password)
return RunSendStdin("pw usermod " + user + " -h 0 ",password, log_cmd=False)
def load_ata_piix(self):
return 0
@@ -2059,7 +2135,13 @@ class FreeBSDDistro(AbstractDistro):
def getTotalMemory(self):
return int(RunGetOutput("sysctl hw.realmem | awk '{print $2}'")[1])/1024
def setDefaultGateway(self, gateway):
Run("/sbin/route add default " + gateway, chk_err=False)
def routeAdd(self, net, mask, gateway):
Run("/sbin/route add -net " + net + " " + mask + " " + gateway, chk_err=False)
############################################################
# END DISTRO CLASS DEFS
############################################################
@@ -2180,41 +2262,43 @@ def Run(cmd,chk_err=True):
retcode,out=RunGetOutput(cmd,chk_err)
return retcode
def RunGetOutput(cmd,chk_err=True):
def RunGetOutput(cmd, chk_err=True, log_cmd=True):
"""
Wrapper for subprocess.check_output.
Execute 'cmd'. Returns return code and STDOUT, trapping expected exceptions.
Reports exceptions to Error if chk_err parameter is True
"""
LogIfVerbose(cmd)
if log_cmd:
LogIfVerbose(cmd)
try:
output=subprocess.check_output(cmd,stderr=subprocess.STDOUT,shell=True)
except subprocess.CalledProcessError,e :
if chk_err :
if chk_err and log_cmd:
Error('CalledProcessError. Error Code is ' + str(e.returncode) )
Error('CalledProcessError. Command string was ' + e.cmd )
Error('CalledProcessError. Command result was ' + (e.output[:-1]).decode('latin-1'))
return e.returncode,e.output.decode('latin-1')
return 0,output.decode('latin-1')
def RunSendStdin(cmd,input,chk_err=True):
def RunSendStdin(cmd, input, chk_err=True, log_cmd=True):
"""
Wrapper for subprocess.Popen.
Execute 'cmd', sending 'input' to STDIN of 'cmd'.
Returns return code and STDOUT, trapping expected exceptions.
Reports exceptions to Error if chk_err parameter is True
"""
LogIfVerbose(cmd+input)
if log_cmd:
LogIfVerbose(cmd+input)
try:
me=subprocess.Popen([cmd], shell=True, stdin=subprocess.PIPE,stderr=subprocess.STDOUT,stdout=subprocess.PIPE)
output=me.communicate(input)
except OSError , e :
if chk_err :
if chk_err and log_cmd:
Error('CalledProcessError. Error Code is ' + str(me.returncode) )
Error('CalledProcessError. Command string was ' + cmd )
Error('CalledProcessError. Command result was ' + output[0].decode('latin-1'))
return 1,output[0].decode('latin-1')
if me.returncode is not 0 and chk_err is True:
if me.returncode is not 0 and chk_err is True and log_cmd:
Error('CalledProcessError. Error Code is ' + str(me.returncode) )
Error('CalledProcessError. Command string was ' + cmd )
Error('CalledProcessError. Command result was ' + output[0].decode('latin-1'))
@@ -2296,7 +2380,7 @@ def CreateAccount(user, password, expiration, thumbprint):
else:
Log("CreateAccount: " + user + " already exists. Will update password.")
if password != None:
RunSendStdin("chpasswd",(user + ":" + password + "\n"))
MyDistro.changePass(user, password)
try:
# for older distros create sudoers.d
if not os.path.isdir('/etc/sudoers.d/'):
@@ -2468,7 +2552,6 @@ class Logger(object):
message = filter(lambda x : x in string.printable, message)
C.write(message.encode('ascii','ignore') + "\n")
except IOError, e:
print e
pass
def Log(self,message):
@@ -2635,6 +2718,53 @@ def DeviceForIdePort(n):
class HttpResourceGoneError(Exception):
pass
def DoInstallRHUIRPM():
"""
Install RHUI RPM according to VM region
"""
rhuiRPMinstalled = os.path.exists(LibDir + "/rhuirpminstalled")
if rhuiRPMinstalled:
return
else:
SetFileContents(LibDir + "/rhuirpminstalled", "")
Log("Begin to install RHUI RPM")
cmd = "grep '<Location>' /var/lib/waagent/ExtensionsConfig* --no-filename | sed 's/<Location>//g' | sed 's/<\/Location>//g' | sed 's/ //g' | tr 'A-Z' 'a-z' | uniq"
retcode,out = RunGetOutput(cmd, True)
region = out.rstrip("\n")
#try a few times at most to get the region info
retry = 0
for i in range(0, 8):
if (region != ""):
break
Log("region info is empty, now wait 15 seconds...")
time.sleep(15)
retcode,out = RunGetOutput(cmd, True)
region = out.rstrip("\n")
if region == "":
Log("could not detect region info, now use the default region: eastus2")
region = "eastus2"
scriptFilePath = "/tmp/install-rhui-rpm.sh"
if not os.path.exists(scriptFilePath):
Error(scriptFilePath + " does not exist, now quit RHUI RPM installation.");
return
#chmod a+x script file
os.chmod(scriptFilePath, 0100)
Log("begin to run " + scriptFilePath)
#execute the downloaded script file
retcode,out = RunGetOutput(scriptFilePath, True)
if retcode != 0:
Error("execute script " + scriptFilePath + " failed, return code: " + str(retcode) + ", now exit RHUI RPM installation.");
return
Log("install RHUI RPM completed")
class Util(object):
"""
Http communication class.
@@ -2697,20 +2827,20 @@ class Util(object):
if secure:
port = 443 if port is None else port
if proxyHost is not None and proxyPort is not None:
conn = httplib.HTTPSConnection(proxyHost, proxyPort)
conn = httplib.HTTPSConnection(proxyHost, proxyPort, timeout=10)
conn.set_tunnel(host, port)
#If proxy is used, full url is needed.
path = "https://{0}:{1}{2}".format(host, port, path)
else:
conn = httplib.HTTPSConnection(host, port)
conn = httplib.HTTPSConnection(host, port, timeout=10)
else:
port = 80 if port is None else port
if proxyHost is not None and proxyPort is not None:
conn = httplib.HTTPConnection(proxyHost, proxyPort)
conn = httplib.HTTPConnection(proxyHost, proxyPort, timeout=10)
#If proxy is used, full url is needed.
path = "http://{0}:{1}{2}".format(host, port, path)
else:
conn = httplib.HTTPConnection(host, port)
conn = httplib.HTTPConnection(host, port, timeout=10)
if headers == None:
conn.request(method, path, data)
else:
@@ -2849,8 +2979,12 @@ class Util(object):
"Content-Type": "text/xml; charset=utf-8",
"x-ms-version": ProtocolVersion
}
return self.HttpPost(url, data=data, headers=headers,
maxRetry=maxRetry, chkProxy=chkProxy)
try:
return self.HttpPost(url, data=data, headers=headers,
maxRetry=maxRetry, chkProxy=chkProxy)
except HttpResourceGoneError as e:
Error("Failed to post: {0} {1}".format(url, e))
return None
__StorageVersion="2014-02-14"
@@ -2883,6 +3017,8 @@ def PutBlockBlob(url, data):
}, chkProxy=True)
if ret is None:
Error("Failed to upload block blob for status.")
return -1
return 0
def PutPageBlob(url, data):
restutil = Util()
@@ -2899,7 +3035,7 @@ def PutPageBlob(url, data):
}, chkProxy=True)
if ret is None:
Error("Failed to clean up page blob for status")
return
return -1
if url.index('?') < 0:
url = "{0}?comp=page".format(url)
@@ -2927,8 +3063,9 @@ def PutPageBlob(url, data):
}, chkProxy=True)
if ret is None:
Error("Failed to upload page blob for status")
return
return -1
start = end
return 0
def UploadStatusBlob(url, data):
LogIfVerbose("Upload status blob")
@@ -2936,12 +3073,12 @@ def UploadStatusBlob(url, data):
blobType = GetBlobType(url)
if blobType == "BlockBlob":
PutBlockBlob(url, data)
return PutBlockBlob(url, data)
elif blobType == "PageBlob":
PutPageBlob(url, data)
return PutPageBlob(url, data)
else:
Error("Unknown blob type: {0}".format(blobType))
return None
return -1
class TCPHandler(SocketServer.BaseRequestHandler):
"""
@@ -3529,15 +3666,29 @@ class ExtensionsConfig(object):
# if the same plugin exists and the version is newer or
# does not exist then download and unzip the new plugin
plg_dir=None
for root, dirs, files in os.walk(LibDir):
for d in dirs:
if name in d:
plg_dir=os.path.join(root,d)
if plg_dir != None:
break
if plg_dir != None :
previous_version=plg_dir.rsplit('-')[-1]
if plg_dir == None or version > previous_version :
latest_version_installed = LooseVersion("0.0")
for item in os.listdir(LibDir):
itemPath = os.path.join(LibDir, item)
if os.path.isdir(itemPath) and name in item:
try:
#Split plugin dir name with '-' to get intalled plugin name and version
sperator = item.rfind('-')
if sperator < 0:
continue
installed_plg_name = item[0:sperator]
installed_plg_version = LooseVersion(item[sperator + 1:])
#Check installed plugin name and compare installed version to get the latest version installed
if installed_plg_name == name and installed_plg_version > latest_version_installed:
plg_dir = itemPath
previous_version = str(installed_plg_version)
latest_version_installed = installed_plg_version
except Exception as e:
Warn("Invalid plugin dir name: {0} {1}".format(item, e))
continue
if plg_dir == None or LooseVersion(version) > LooseVersion(previous_version) :
location=p.getAttribute("location")
Log("Downloading plugin manifest: " + name + " from " + location)
SimpleLog(p.plugin_log,"Downloading plugin manifest: " + name + " from " + location)
@@ -3653,7 +3804,7 @@ class ExtensionsConfig(object):
cmd = ''
getcmd='installCommand'
if plg_dir != None and previous_version != None and version > previous_version :
if plg_dir != None and previous_version != None and LooseVersion(version) > LooseVersion(previous_version):
previous_handler=name+'-'+previous_version
if self.GetHandlerState(previous_handler) != 'NotInstalled':
getcmd='updateCommand'
@@ -3666,6 +3817,23 @@ class ExtensionsConfig(object):
self.SetHandlerState(previous_handler, 'Disabled')
Log(name+' version ' + previous_version + ' is disabled')
SimpleLog(p.plugin_log,name+' version ' + previous_version + ' is disabled')
try:
Log("Copy status file from old plugin dir to new")
old_plg_dir = plg_dir
new_plg_dir = os.path.join(LibDir, "{0}-{1}".format(name, version))
old_ext_status_dir = os.path.join(old_plg_dir, "status")
new_ext_status_dir = os.path.join(new_plg_dir, "status")
if os.path.isdir(old_ext_status_dir):
for status_file in os.listdir(old_ext_status_dir):
status_file_path = os.path.join(old_ext_status_dir, status_file)
if os.path.isfile(status_file_path):
shutil.copy2(status_file_path, new_ext_status_dir)
mrseq_file = os.path.join(old_plg_dir, "mrseq")
if os.path.isfile(mrseq_file):
shutil.copy(mrseq_file, new_plg_dir)
except Exception as e:
Error("Failed to copy status file.")
isupgradeSuccess = True
if getcmd=='updateCommand':
@@ -3687,6 +3855,16 @@ class ExtensionsConfig(object):
self.SetHandlerState(previous_handler, 'NotInstalled')
Log('Uninstall complete'+ previous_handler )
SimpleLog(p.plugin_log,'Uninstall complete'+ name +'-' + previous_version)
try:
#rm old plugin dir
if os.path.isdir(plg_dir):
shutil.rmtree(plg_dir)
Log(name +'-'+ previous_version + ' extension files deleted.')
SimpleLog(p.plugin_log,name +'-'+ previous_version + ' extension files deleted.')
except Exception as e:
Error("Failed to remove old plugin directory")
AddExtensionEvent(name,WALAEventOperation.Upgrade,isupgradeSuccess,0,previous_version)
else : # run install
if self.launchCommand(p.plugin_log,name,version,getcmd) == None :
@@ -3902,7 +4080,7 @@ class ExtensionsConfig(object):
incarnation=self.Extensions[0].getAttribute("goalStateIncarnation")
except:
Error('Error parsing ExtensionsConfig. Unable to send status reports')
return None
return -1
status=''
statuses=''
for p in self.Plugins:
@@ -3940,11 +4118,10 @@ class ExtensionsConfig(object):
uri=GetNodeTextData(self.Extensions[0].getElementsByTagName("StatusUploadBlob")[0]).replace('&amp;','&')
except:
Error('Error parsing ExtensionsConfig. Unable to send status reports')
return None
return -1
UploadStatusBlob(uri, status.encode("utf-8"))
LogIfVerbose('Status report '+status+' sent to ' + uri)
return True
return UploadStatusBlob(uri, status.encode("utf-8"))
def GetCurrentSequenceNumber(self, plugin_base_dir):
"""
@@ -4430,7 +4607,7 @@ class OvfEnv(object):
if len(CDSection) > 0 :
self.CustomData=GetNodeTextData(CDSection[0])
if len(self.CustomData)>0:
SetFileContents(LibDir + '/CustomData', MyDistro.translateCustomData(self.CustomData))
SetFileContents(LibDir + '/CustomData', bytearray(MyDistro.translateCustomData(self.CustomData)))
Log('Wrote ' + LibDir + '/CustomData')
else :
Error('<CustomData> contains no data!')
@@ -4999,7 +5176,16 @@ class Agent(Util):
net = self.IntegerToIpAddressV4String(net)
mask = self.IntegerToIpAddressV4String(mask)
gateway = self.IntegerToIpAddressV4String(gateway)
Run("/sbin/route add -net " + net + " netmask " + mask + " gw " + gateway,chk_err=False)
Log("Route add: net={0}, mask={1}, gateway={2}".format(net, mask, gateway))
MyDistro.routeAdd(net, mask, gateway)
def SetDefaultGateway(self, gateway):
"""
Set default gateway
"""
gateway = self.IntegerToIpAddressV4String(gateway)
Log("Set default gateway: {0}".format(gateway))
MyDistro.setDefaultGateway(gateway)
def HandleDhcpResponse(self, sendData, receiveBuffer):
"""
@@ -5082,11 +5268,11 @@ class Agent(Util):
gateway = self.UnpackBigEndian(receiveBuffer, i + 2, 4)
IpAddress = self.IntegerToIpAddressV4String(gateway)
if option == 3:
self.RouteAdd(0, 0, gateway)
self.SetDefaultGateway(gateway)
name = "DefaultGateway"
else:
endpoint = IpAddress
name = "Microsoft Azure wire protocol endpoint"
name = "Azure wire protocol endpoint"
LogIfVerbose(name + ": " + IpAddress + " at " + hex(i))
else:
Error("HandleDhcpResponse: Data too small for option " + str(option))
@@ -5098,7 +5284,7 @@ class Agent(Util):
def DoDhcpWork(self):
"""
Discover the wire server via DHCP option 245.
And workaround incompatibility with Microsoft Azure DHCP servers.
And workaround incompatibility with Azure DHCP servers.
"""
ShortSleep = False # Sleep 1 second before retrying DHCP queries.
ifname=None
@@ -5212,7 +5398,7 @@ class Agent(Util):
if not goalStateXml:
Error("UpdateGoalState failed.")
return
Log("Retrieved GoalState from Microsoft Azure Fabric.")
Log("Retrieved GoalState from Azure Fabric.")
self.GoalState = GoalState(self).Parse(goalStateXml)
return self.GoalState
@@ -5503,6 +5689,8 @@ class Agent(Util):
"""
SetFileContents("/var/run/waagent.pid", str(os.getpid()) + "\n")
reportHandlerStatusCount = 0
# Determine if we are in VMM. Spawn VMM_STARTUP_SCRIPT_NAME if found.
self.SearchForVMMStartup()
ipv4=''
@@ -5524,15 +5712,16 @@ class Agent(Util):
except:
pass
Log("Probing for Microsoft Azure environment.")
Log("Probing for Azure environment.")
self.Endpoint = self.DoDhcpWork()
if self.Endpoint == None:
Log("Microsoft Azure environment not detected.")
while True:
time.sleep(60)
while self.Endpoint == None:
Log("Azure environment not detected.")
Log("Retry environment detection in 60 seconds")
time.sleep(60)
self.Endpoint = self.DoDhcpWork()
Log("Discovered Microsoft Azure endpoint: " + self.Endpoint)
Log("Discovered Azure endpoint: " + self.Endpoint)
if not self.CheckVersions():
Error("Agent.CheckVersions failed")
sys.exit(1)
@@ -5647,12 +5836,23 @@ class Agent(Util):
incarnation = self.ReportReady()
# Process our extensions.
if goalState.ExtensionsConfig == None and goalState.ExtensionsConfigXml != None :
reportHandlerStatusCount = 0 #Reset count when new goal state comes
goalState.ExtensionsConfig = ExtensionsConfig().Parse(goalState.ExtensionsConfigXml)
# report the status/heartbeat results of extension processing
if goalState.ExtensionsConfig != None :
goalState.ExtensionsConfig.ReportHandlerStatus()
ret = goalState.ExtensionsConfig.ReportHandlerStatus()
if ret != 0:
Error("Failed to report handler status")
elif reportHandlerStatusCount % 1000 == 0:
#Agent report handler status every 25 seconds. Reduce the log entries by adding a count
Log("Successfully reported handler status")
reportHandlerStatusCount += 1
global LinuxDistro
if LinuxDistro == "redhat":
DoInstallRHUIRPM()
if not eventMonitor:
eventMonitor = WALAEventMonitor(self.HttpPostWithHeaders)
eventMonitor.StartEventsLoop()
@@ -5987,11 +6187,6 @@ def main():
global LinuxDistro
LinuxDistro=DistInfo()[0]
#The platform.py lib has issue with detecting oracle linux distribution.
#Merge the following patch provided by oracle as a temparory fix.
if os.path.exists("/etc/oracle-release"):
LinuxDistro="Oracle Linux"
global MyDistro
MyDistro=GetMyDistro()
if MyDistro == None :
@@ -6051,18 +6246,21 @@ def main():
sys.exit(Usage())
global modloaded
modloaded = False
try:
SwitchCwd()
Log(GuestAgentLongName + " Version: " + GuestAgentVersion)
if IsLinux():
Log("Linux Distribution Detected : " + LinuxDistro)
global WaAgent
WaAgent = Agent()
WaAgent.Run()
except Exception, e:
Error(traceback.format_exc())
Error("Exception: " + str(e))
sys.exit(1)
while True:
try:
SwitchCwd()
Log(GuestAgentLongName + " Version: " + GuestAgentVersion)
if IsLinux():
Log("Linux Distribution Detected : " + LinuxDistro)
global WaAgent
WaAgent = Agent()
WaAgent.Run()
except Exception, e:
Error(traceback.format_exc())
Error("Exception: " + str(e))
Log("Restart agent in 15 seconds")
time.sleep(15)
if __name__ == '__main__' :
main()