summaryrefslogtreecommitdiff
path: root/bin
diff options
context:
space:
mode:
Diffstat (limited to 'bin')
-rw-r--r--[-rwxr-xr-x]bin/waagent2.0394
1 files changed, 296 insertions, 98 deletions
diff --git a/bin/waagent2.0 b/bin/waagent2.0
index 94c8ac8..673a04c 100755..100644
--- a/bin/waagent2.0
+++ b/bin/waagent2.0
@@ -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()