~ubuntu-branches/debian/sid/waagent/sid

« back to all changes in this revision

Viewing changes to bin/waagent2.0

  • Committer: Package Import Robot
  • Author(s): Bastian Blank
  • Date: 2016-02-11 16:40:04 UTC
  • mfrom: (1.2.3)
  • Revision ID: package-import@ubuntu.com-20160211164004-m9chank2yihdviil
Tags: 2.1.3-1
* New upstream version.
* Only create /var/lib/waagent on initial installation

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
#!/usr/bin/env python
2
2
#
3
 
# Microsoft Azure Linux Agent
 
3
# Azure Linux Agent
4
4
#
5
5
# Copyright 2015 Microsoft Corporation
6
6
#
23
23
# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx
24
24
#
25
25
 
 
26
import crypt
 
27
import random
26
28
import array
27
29
import base64
28
30
import httplib
50
52
import json
51
53
import datetime
52
54
import xml.sax.saxutils
 
55
from distutils.version import LooseVersion
53
56
 
54
57
if not hasattr(subprocess,'check_output'):
55
58
    def check_output(*popenargs, **kwargs):
79
82
    subprocess.CalledProcessError=CalledProcessError
80
83
    
81
84
GuestAgentName = "WALinuxAgent"
82
 
GuestAgentLongName = "Microsoft Azure Linux Agent"
83
 
GuestAgentVersion = "WALinuxAgent-2.0.15-pre"
 
85
GuestAgentLongName = "Azure Linux Agent"
 
86
GuestAgentVersion = "WALinuxAgent-2.0.16"
84
87
ProtocolVersion = "2012-11-30" #WARNING this value is used to confirm the correct fabric protocol.
85
88
 
86
89
Config = None
106
109
 
107
110
WaagentConf = """\
108
111
#
109
 
# Microsoft Azure Linux Agent Configuration
 
112
# Azure Linux Agent Configuration
110
113
#
111
114
 
112
115
Role.StateConsumer=None                 # Specified program is invoked with the argument "Ready" when we report ready status
126
129
ResourceDisk.EnableSwap=n               # Create and use swapfile on resource disk.
127
130
ResourceDisk.SwapSizeMB=0               # Size of the swapfile.
128
131
 
129
 
LBProbeResponder=y                      # Respond to load balancer probes if requested by Microsoft Azure.
 
132
LBProbeResponder=y                      # Respond to load balancer probes if requested by Azure.
130
133
 
131
134
Logs.Verbose=n                          # Enable verbose logs
132
135
 
173
176
        self.ssh_config_file='/etc/ssh/sshd_config'
174
177
        self.hostname_file_path='/etc/hostname'
175
178
        self.dhcp_client_name='dhclient'
176
 
        self.requiredDeps = [ 'route', 'shutdown', 'ssh-keygen', 'useradd', 
177
 
                              'openssl', 'sfdisk', 'fdisk', 'mkfs', 'chpasswd', 
 
179
        self.requiredDeps = [ 'route', 'shutdown', 'ssh-keygen', 'useradd', 'usermod',
 
180
                              'openssl', 'sfdisk', 'fdisk', 'mkfs', 
178
181
                              'sed', 'grep', 'sudo', 'parted' ]
179
182
        self.init_script_file='/etc/init.d/waagent'
180
183
        self.agent_package_name='WALinuxAgent'
187
190
        self.sudoers_dir_base = '/etc'
188
191
        self.waagent_conf_file = WaagentConf
189
192
        self.shadow_file_mode=0600
 
193
        self.shadow_file_path="/etc/shadow"
190
194
        self.dhcp_enabled = False
191
195
        
192
196
    def isSelinuxSystem(self):
341
345
        return 0
342
346
    
343
347
    def changePass(self,user,password):
344
 
        return RunSendStdin("chpasswd",(user + ":" + password + "\n"))
 
348
        Log("Change user password")
 
349
        crypt_id = Config.get("Provisioning.PasswordCryptId")
 
350
        if crypt_id is None:
 
351
            crypt_id = "6"
 
352
 
 
353
        salt_len = Config.get("Provisioning.PasswordCryptSaltLength")
 
354
        try:
 
355
            salt_len = int(salt_len)
 
356
            if salt_len < 0 or salt_len > 10:
 
357
                salt_len = 10
 
358
        except (ValueError, TypeError):
 
359
            salt_len = 10
 
360
 
 
361
        return self.chpasswd(user, password, crypt_id=crypt_id, 
 
362
                             salt_len=salt_len)
345
363
    
 
364
    def chpasswd(self, username, password, crypt_id=6, salt_len=10):
 
365
        passwd_hash = self.gen_password_hash(password, crypt_id, salt_len)
 
366
        cmd = "usermod -p '{0}' {1}".format(passwd_hash, username)
 
367
        ret, output = RunGetOutput(cmd, log_cmd=False)
 
368
        if ret != 0:
 
369
            return "Failed to set password for {0}: {1}".format(username, output)
 
370
 
 
371
    def gen_password_hash(self, password, crypt_id, salt_len):
 
372
        collection = string.ascii_letters + string.digits
 
373
        salt = ''.join(random.choice(collection) for _ in range(salt_len))
 
374
        salt = "${0}${1}".format(crypt_id, salt)
 
375
        return crypt.crypt(password, salt)
 
376
 
346
377
    def load_ata_piix(self):
347
378
        return WaAgent.TryLoadAtapiix()
348
379
 
438
469
    def GetInterfaceName(self):
439
470
        return GetFirstActiveNetworkInterfaceNonLoopback()[0]
440
471
 
441
 
    def RestartInterface(self, iface):
442
 
        Run("ifdown " + iface + " && ifup " + iface)
 
472
    def RestartInterface(self, iface, max_retry=3):
 
473
        for retry in range(1, max_retry + 1):
 
474
            ret = Run("ifdown " + iface + " && ifup " + iface)
 
475
            if ret == 0:
 
476
                return
 
477
            Log("Failed to restart interface: {0}, ret={1}".format(iface, ret))
 
478
            if retry < max_retry:
 
479
                Log("Retry restart interface in 5 seconds")
 
480
                time.sleep(5)
443
481
 
444
482
    def CreateAccount(self,user, password, expiration, thumbprint):
445
483
        return CreateAccount(user, password, expiration, thumbprint)
522
560
        if not os.path.isfile(mountpoint + "/swapfile"):
523
561
            Run("dd if=/dev/zero of=" + mountpoint + "/swapfile bs=1024 count=" + str(sizeKB))
524
562
            Run("mkswap " + mountpoint + "/swapfile")
 
563
        Run("chmod 600 " + mountpoint + "/swapfile")
525
564
        if not Run("swapon " + mountpoint + "/swapfile"):
526
565
            Log("Enabled " + str(sizeKB) + " KB of swap at " + mountpoint + "/swapfile")
527
566
        else:
645
684
        if ret != 0:
646
685
            raise Exception("Failed to config ipv4 for {0}: {1}".format(ifName, 
647
686
                                                                        output))
 
687
    def setDefaultGateway(self, gateway):
 
688
        Run("/sbin/route add default gw" + gateway, chk_err=False)
 
689
 
 
690
    def routeAdd(self, net, mask, gateway):
 
691
        Run("/sbin/route add -net " + net + " netmask " + mask + " gw " + gateway,
 
692
            chk_err=False)
 
693
 
648
694
 
649
695
############################################################
650
696
#       GentooDistro
656
702
pidfile=/var/run/waagent.pid
657
703
command_args=-daemon
658
704
command_background=true
659
 
name="Microsoft Azure Linux Agent"
 
705
name="Azure Linux Agent"
660
706
 
661
707
depend()
662
708
{
725
771
suse_init_file = """\
726
772
#! /bin/sh
727
773
#
728
 
# Microsoft Azure Linux Agent sysV init script
 
774
# Azure Linux Agent sysV init script
729
775
#
730
776
# Copyright 2013 Microsoft Corporation
731
777
# Copyright SUSE LLC
751
797
# System startup script for the waagent
752
798
#
753
799
### BEGIN INIT INFO
754
 
# Provides: MicrosoftAzureLinuxAgent
 
800
# Provides: AzureLinuxAgent
755
801
# Required-Start: $network sshd
756
802
# Required-Stop: $network sshd
757
803
# Default-Start: 3 5
758
804
# Default-Stop: 0 1 2 6
759
 
# Description: Start the MicrosoftAzureLinuxAgent
 
805
# Description: Start the AzureLinuxAgent
760
806
### END INIT INFO
761
807
 
762
808
PYTHON=/usr/bin/python
789
835
 
790
836
case "$1" in
791
837
    start)
792
 
        echo -n "Starting MicrosoftAzureLinuxAgent"
 
838
        echo -n "Starting AzureLinuxAgent"
793
839
        ## Start daemon with startproc(8). If this fails
794
840
        ## the echo return value is set appropriate.
795
841
        startproc -f ${PYTHON} ${WAZD_BIN} -daemon
796
842
        rc_status -v
797
843
        ;;
798
844
    stop)
799
 
        echo -n "Shutting down MicrosoftAzureLinuxAgent"
 
845
        echo -n "Shutting down AzureLinuxAgent"
800
846
        ## Stop daemon with killproc(8) and if this fails
801
847
        ## set echo the echo return value.
802
848
        killproc -p ${WAZD_PIDFILE} ${PYTHON} ${WAZD_BIN}
820
866
        rc_status
821
867
        ;;
822
868
    status)
823
 
        echo -n "Checking for service MicrosoftAzureLinuxAgent "
 
869
        echo -n "Checking for service AzureLinuxAgent "
824
870
        ## Check status with checkproc(8), if process is running
825
871
        ## checkproc will return with exit status 0.
826
872
 
902
948
redhat_init_file= """\
903
949
#!/bin/bash
904
950
#
905
 
# Init file for MicrosoftAzureLinuxAgent.
 
951
# Init file for AzureLinuxAgent.
906
952
#
907
953
# chkconfig: 2345 60 80
908
 
# description: MicrosoftAzureLinuxAgent
 
954
# description: AzureLinuxAgent
909
955
#
910
956
 
911
957
# source function library
912
958
. /etc/rc.d/init.d/functions
913
959
 
914
960
RETVAL=0
915
 
FriendlyName="MicrosoftAzureLinuxAgent"
 
961
FriendlyName="AzureLinuxAgent"
916
962
WAZD_BIN=/usr/sbin/waagent
917
963
 
918
964
start()
1014
1060
        else:
1015
1061
            return 0
1016
1062
 
1017
 
 
 
1063
    def checkDependencies(self):
 
1064
        """
 
1065
        Generic dependency check.
 
1066
        Return 1 unless all dependencies are satisfied.
 
1067
        """
 
1068
        if DistInfo()[1] < '7.0' and self.checkPackageInstalled('NetworkManager'):
 
1069
            Error(GuestAgentLongName + " is not compatible with network-manager.")
 
1070
            return 1
 
1071
        try:
 
1072
            m= __import__('pyasn1')
 
1073
        except ImportError:
 
1074
            Error(GuestAgentLongName + " requires python-pyasn1 for your Linux distribution.")
 
1075
            return 1
 
1076
        for a in self.requiredDeps:
 
1077
            if Run("which " + a + " > /dev/null 2>&1",chk_err=False):
 
1078
                Error("Missing required dependency: " + a)
 
1079
                return 1
 
1080
        return 0        
1018
1081
 
1019
1082
############################################################    
1020
1083
#       centosDistro
1028
1091
    def __init__(self):
1029
1092
        super(centosDistro,self).__init__()
1030
1093
 
 
1094
############################################################    
 
1095
#       oracleDistro
 
1096
############################################################    
 
1097
 
 
1098
class oracleDistro(redhatDistro):
 
1099
    """
 
1100
    Oracle Distro concrete class
 
1101
    Put Oracle specific behavior here...
 
1102
    """
 
1103
    def __init__(self):
 
1104
        super(oracleDistro, self).__init__()
 
1105
 
 
1106
 
1031
1107
 
1032
1108
############################################################    
1033
1109
#       asianuxDistro
1150
1226
        else:
1151
1227
            Log("CreateAccount: " + user + " already exists. Will update password.")
1152
1228
        if password != None:
1153
 
            RunSendStdin("chpasswd", user + ":" + password + "\n")
 
1229
            self.changePass(user, password)
1154
1230
        try:
1155
1231
            if password == None:
1156
1232
                SetFileContents("/etc/sudoers.d/waagent", user + " ALL = (ALL) NOPASSWD: ALL\n")
1194
1270
debian_init_file = """\
1195
1271
#!/bin/sh
1196
1272
### BEGIN INIT INFO
1197
 
# Provides:          MicrosoftAzureLinuxAgent
 
1273
# Provides:          AzureLinuxAgent
1198
1274
# Required-Start:    $network $syslog
1199
1275
# Required-Stop:     $network $syslog
1200
1276
# Should-Start:      $network $syslog
1201
1277
# Should-Stop:       $network $syslog
1202
1278
# Default-Start:     2 3 4 5
1203
1279
# Default-Stop:      0 1 6
1204
 
# Short-Description: MicrosoftAzureLinuxAgent
1205
 
# Description:       MicrosoftAzureLinuxAgent
 
1280
# Short-Description: AzureLinuxAgent
 
1281
# Description:       AzureLinuxAgent
1206
1282
### END INIT INFO
1207
1283
 
1208
1284
. /lib/lsb/init-functions
1213
1289
 
1214
1290
case "$1" in
1215
1291
    start)
1216
 
        log_begin_msg "Starting MicrosoftAzureLinuxAgent..."
 
1292
        log_begin_msg "Starting AzureLinuxAgent..."
1217
1293
        pid=$( pidofproc $WAZD_BIN )
1218
1294
        if [ -n "$pid" ] ; then
1219
1295
              log_begin_msg "Already running."
1225
1301
        ;;
1226
1302
 
1227
1303
    stop)
1228
 
        log_begin_msg "Stopping MicrosoftAzureLinuxAgent..."
 
1304
        log_begin_msg "Stopping AzureLinuxAgent..."
1229
1305
        start-stop-daemon --stop --quiet --oknodo --pidfile $WAZD_PID
1230
1306
        ret=$?
1231
1307
        rm -f $WAZD_PID
1350
1426
#       UbuntuDistro
1351
1427
############################################################    
1352
1428
ubuntu_upstart_file = """\
1353
 
#walinuxagent - start Microsoft Azure agent
 
1429
#walinuxagent - start Azure agent
1354
1430
 
1355
1431
description "walinuxagent"
1356
1432
author "Ben Howard <ben.howard@canonical.com>"
1474
1550
############################################################    
1475
1551
fedora_systemd_service = """\
1476
1552
[Unit]
1477
 
Description=Microsoft Azure Linux Agent
 
1553
Description=Azure Linux Agent
1478
1554
After=network.target
1479
1555
After=sshd.service
1480
1556
ConditionFileIsExecutable=/usr/sbin/waagent
1599
1675
############################################################    
1600
1676
FreeBSDWaagentConf = """\
1601
1677
#
1602
 
# Microsoft Azure Linux Agent Configuration
 
1678
# Azure Linux Agent Configuration
1603
1679
#
1604
1680
 
1605
1681
Role.StateConsumer=None                 # Specified program is invoked with the argument "Ready" when we report ready status
1619
1695
ResourceDisk.EnableSwap=n               # Create and use swapfile on resource disk.
1620
1696
ResourceDisk.SwapSizeMB=0               # Size of the swapfile.
1621
1697
 
1622
 
LBProbeResponder=y                      # Respond to load balancer probes if requested by Microsoft Azure.
 
1698
LBProbeResponder=y                      # Respond to load balancer probes if requested by Azure.
1623
1699
 
1624
1700
Logs.Verbose=n                          # Enable verbose logs
1625
1701
 
1661
1737
waagent=imp.load_source('waagent','/tmp/waagent') 
1662
1738
waagent.LoggerInit('/var/log/waagent.log','/dev/console')
1663
1739
from waagent import RunGetOutput,Run
1664
 
Config=waagent.ConfigurationProvider()
 
1740
Config=waagent.ConfigurationProvider(None)
1665
1741
format = Config.get("ResourceDisk.Format")
1666
1742
if format == None or format.lower().startswith("n"):
1667
1743
    sys.exit(0)
1764
1840
        return 0
1765
1841
 
1766
1842
    def changePass(self,user,password):
1767
 
        return RunSendStdin("pw usermod " + user + " -h 0 ",password)
 
1843
        return RunSendStdin("pw usermod " + user + " -h 0 ",password, log_cmd=False)
1768
1844
    
1769
1845
    def load_ata_piix(self):
1770
1846
        return 0
2059
2135
    
2060
2136
    def getTotalMemory(self):
2061
2137
        return int(RunGetOutput("sysctl hw.realmem | awk '{print $2}'")[1])/1024
2062
 
        
 
2138
    
 
2139
    def setDefaultGateway(self, gateway):
 
2140
        Run("/sbin/route add default " + gateway, chk_err=False)
 
2141
 
 
2142
    def routeAdd(self, net, mask, gateway):
 
2143
        Run("/sbin/route add -net " + net + " " + mask + " " + gateway, chk_err=False)
 
2144
 
2063
2145
############################################################
2064
2146
# END DISTRO CLASS DEFS
2065
2147
############################################################  
2180
2262
    retcode,out=RunGetOutput(cmd,chk_err)
2181
2263
    return retcode
2182
2264
 
2183
 
def RunGetOutput(cmd,chk_err=True):
 
2265
def RunGetOutput(cmd, chk_err=True, log_cmd=True):
2184
2266
    """
2185
2267
    Wrapper for subprocess.check_output.
2186
2268
    Execute 'cmd'.  Returns return code and STDOUT, trapping expected exceptions.
2187
2269
    Reports exceptions to Error if chk_err parameter is True
2188
2270
    """
2189
 
    LogIfVerbose(cmd)
 
2271
    if log_cmd:
 
2272
        LogIfVerbose(cmd)
2190
2273
    try:                                     
2191
2274
        output=subprocess.check_output(cmd,stderr=subprocess.STDOUT,shell=True)
2192
2275
    except subprocess.CalledProcessError,e :
2193
 
        if chk_err :
 
2276
        if chk_err and log_cmd:
2194
2277
            Error('CalledProcessError.  Error Code is ' + str(e.returncode)  )
2195
2278
            Error('CalledProcessError.  Command string was ' + e.cmd  )
2196
2279
            Error('CalledProcessError.  Command result was ' + (e.output[:-1]).decode('latin-1'))
2197
2280
        return e.returncode,e.output.decode('latin-1')
2198
2281
    return 0,output.decode('latin-1')
2199
2282
 
2200
 
def RunSendStdin(cmd,input,chk_err=True):
 
2283
def RunSendStdin(cmd, input, chk_err=True, log_cmd=True):
2201
2284
    """
2202
2285
    Wrapper for subprocess.Popen.
2203
2286
    Execute 'cmd', sending 'input' to STDIN of 'cmd'.
2204
2287
    Returns return code and STDOUT, trapping expected exceptions.
2205
2288
    Reports exceptions to Error if chk_err parameter is True
2206
2289
    """
2207
 
    LogIfVerbose(cmd+input)
 
2290
    if log_cmd:
 
2291
        LogIfVerbose(cmd+input)
2208
2292
    try:                                     
2209
2293
        me=subprocess.Popen([cmd], shell=True, stdin=subprocess.PIPE,stderr=subprocess.STDOUT,stdout=subprocess.PIPE)
2210
2294
        output=me.communicate(input)
2211
2295
    except OSError , e :
2212
 
        if chk_err :
 
2296
        if chk_err and log_cmd:
2213
2297
            Error('CalledProcessError.  Error Code is ' + str(me.returncode)  )
2214
2298
            Error('CalledProcessError.  Command string was ' + cmd  )
2215
2299
            Error('CalledProcessError.  Command result was ' + output[0].decode('latin-1'))
2216
2300
            return 1,output[0].decode('latin-1')
2217
 
    if me.returncode is not 0 and chk_err is True:
 
2301
    if me.returncode is not 0 and chk_err is True and log_cmd:
2218
2302
            Error('CalledProcessError.  Error Code is ' + str(me.returncode)  )
2219
2303
            Error('CalledProcessError.  Command string was ' + cmd  )
2220
2304
            Error('CalledProcessError.  Command result was ' + output[0].decode('latin-1'))
2296
2380
    else:
2297
2381
        Log("CreateAccount: " + user + " already exists. Will update password.")
2298
2382
    if password != None:
2299
 
        RunSendStdin("chpasswd",(user + ":" + password + "\n"))
 
2383
        MyDistro.changePass(user, password)
2300
2384
    try:
2301
2385
        # for older distros create sudoers.d
2302
2386
        if not os.path.isdir('/etc/sudoers.d/'):
2468
2552
                    message = filter(lambda x : x in string.printable, message)
2469
2553
                    C.write(message.encode('ascii','ignore') + "\n")
2470
2554
            except IOError, e:
2471
 
                print e
2472
2555
                pass
2473
2556
                
2474
2557
    def Log(self,message):
2635
2718
class HttpResourceGoneError(Exception):
2636
2719
    pass
2637
2720
 
 
2721
def DoInstallRHUIRPM():
 
2722
    """
 
2723
    Install RHUI RPM according to VM region
 
2724
    """
 
2725
    rhuiRPMinstalled = os.path.exists(LibDir + "/rhuirpminstalled")
 
2726
    if rhuiRPMinstalled:
 
2727
        return
 
2728
    else:
 
2729
        SetFileContents(LibDir + "/rhuirpminstalled", "")
 
2730
 
 
2731
    Log("Begin to install RHUI RPM")
 
2732
    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"
 
2733
 
 
2734
    retcode,out = RunGetOutput(cmd, True)
 
2735
    region = out.rstrip("\n")
 
2736
 
 
2737
    #try a few times at most to get the region info
 
2738
    retry = 0
 
2739
    for i in range(0, 8):
 
2740
        if (region != ""):
 
2741
            break
 
2742
        Log("region info is empty, now wait 15 seconds...")
 
2743
        time.sleep(15)
 
2744
        retcode,out = RunGetOutput(cmd, True)
 
2745
        region = out.rstrip("\n")
 
2746
 
 
2747
    if region == "":
 
2748
        Log("could not detect region info, now use the default region: eastus2")
 
2749
        region = "eastus2"
 
2750
 
 
2751
    scriptFilePath = "/tmp/install-rhui-rpm.sh"
 
2752
 
 
2753
    if not os.path.exists(scriptFilePath):
 
2754
        Error(scriptFilePath + " does not exist, now quit RHUI RPM installation.");
 
2755
        return
 
2756
    #chmod a+x script file
 
2757
    os.chmod(scriptFilePath, 0100)
 
2758
    Log("begin to run " + scriptFilePath)
 
2759
 
 
2760
    #execute the downloaded script file
 
2761
    retcode,out = RunGetOutput(scriptFilePath, True)
 
2762
    if retcode != 0:
 
2763
        Error("execute script " + scriptFilePath + " failed, return code: " + str(retcode) + ", now exit RHUI RPM installation.");
 
2764
        return
 
2765
 
 
2766
    Log("install RHUI RPM completed")
 
2767
    
2638
2768
class Util(object):
2639
2769
    """
2640
2770
    Http communication class.
2697
2827
            if secure:
2698
2828
                port = 443 if port is None else port
2699
2829
                if proxyHost is not None and proxyPort is not None:
2700
 
                    conn = httplib.HTTPSConnection(proxyHost, proxyPort)
 
2830
                    conn = httplib.HTTPSConnection(proxyHost, proxyPort, timeout=10)
2701
2831
                    conn.set_tunnel(host, port)
2702
2832
                    #If proxy is used, full url is needed.
2703
2833
                    path = "https://{0}:{1}{2}".format(host, port, path)
2704
2834
                else:
2705
 
                    conn = httplib.HTTPSConnection(host, port)
 
2835
                    conn = httplib.HTTPSConnection(host, port, timeout=10)
2706
2836
            else:
2707
2837
                port = 80 if port is None else port
2708
2838
                if proxyHost is not None and proxyPort is not None:
2709
 
                    conn = httplib.HTTPConnection(proxyHost, proxyPort)
 
2839
                    conn = httplib.HTTPConnection(proxyHost, proxyPort, timeout=10)
2710
2840
                    #If proxy is used, full url is needed.
2711
2841
                    path = "http://{0}:{1}{2}".format(host, port, path)
2712
2842
                else:
2713
 
                    conn = httplib.HTTPConnection(host, port)
 
2843
                    conn = httplib.HTTPConnection(host, port, timeout=10)
2714
2844
            if headers == None:
2715
2845
                conn.request(method, path, data)
2716
2846
            else:
2849
2979
            "Content-Type": "text/xml; charset=utf-8",
2850
2980
            "x-ms-version": ProtocolVersion
2851
2981
        }
2852
 
        return self.HttpPost(url, data=data, headers=headers, 
2853
 
                             maxRetry=maxRetry, chkProxy=chkProxy)
 
2982
        try:
 
2983
            return self.HttpPost(url, data=data, headers=headers, 
 
2984
                                 maxRetry=maxRetry, chkProxy=chkProxy)
 
2985
        except HttpResourceGoneError as e:
 
2986
            Error("Failed to post: {0} {1}".format(url, e))
 
2987
            return None
2854
2988
 
2855
2989
__StorageVersion="2014-02-14"
2856
2990
 
2883
3017
    }, chkProxy=True)
2884
3018
    if ret is None:
2885
3019
        Error("Failed to upload block blob for status.")
 
3020
        return -1
 
3021
    return 0
2886
3022
 
2887
3023
def PutPageBlob(url, data):
2888
3024
    restutil = Util()
2899
3035
    }, chkProxy=True)
2900
3036
    if ret is None:
2901
3037
        Error("Failed to clean up page blob for status")
2902
 
        return
 
3038
        return -1
2903
3039
        
2904
3040
    if url.index('?') < 0:
2905
3041
        url = "{0}?comp=page".format(url)
2927
3063
        }, chkProxy=True)
2928
3064
        if ret is None:
2929
3065
            Error("Failed to upload page blob for status")
2930
 
            return
 
3066
            return -1
2931
3067
        start = end
 
3068
    return 0
2932
3069
 
2933
3070
def UploadStatusBlob(url, data):
2934
3071
    LogIfVerbose("Upload status blob")
2936
3073
    blobType = GetBlobType(url) 
2937
3074
 
2938
3075
    if blobType == "BlockBlob":
2939
 
        PutBlockBlob(url, data)    
 
3076
        return PutBlockBlob(url, data)    
2940
3077
    elif blobType == "PageBlob":
2941
 
        PutPageBlob(url, data)    
 
3078
        return PutPageBlob(url, data)    
2942
3079
    else:
2943
3080
        Error("Unknown blob type: {0}".format(blobType))
2944
 
        return None
 
3081
        return -1
2945
3082
 
2946
3083
class TCPHandler(SocketServer.BaseRequestHandler):
2947
3084
    """
3529
3666
            # if the same plugin exists and the version is newer or
3530
3667
            # does not exist then download and unzip the new plugin
3531
3668
            plg_dir=None
3532
 
            for root, dirs, files in os.walk(LibDir):
3533
 
                for d in dirs:
3534
 
                    if name in d:
3535
 
                        plg_dir=os.path.join(root,d)
3536
 
                    if plg_dir != None:
3537
 
                        break
3538
 
            if plg_dir != None :
3539
 
                previous_version=plg_dir.rsplit('-')[-1]
3540
 
            if plg_dir == None or version > previous_version :
 
3669
 
 
3670
            latest_version_installed = LooseVersion("0.0")
 
3671
            for item in os.listdir(LibDir):
 
3672
                itemPath = os.path.join(LibDir, item)
 
3673
                if os.path.isdir(itemPath) and name in item:
 
3674
                    try:
 
3675
                        #Split plugin dir name with '-' to get intalled plugin name and version
 
3676
                        sperator = item.rfind('-')
 
3677
                        if sperator < 0:
 
3678
                            continue
 
3679
                        installed_plg_name = item[0:sperator]
 
3680
                        installed_plg_version = LooseVersion(item[sperator + 1:])
 
3681
 
 
3682
                        #Check installed plugin name and compare installed version to get the latest version installed
 
3683
                        if installed_plg_name == name and installed_plg_version > latest_version_installed:
 
3684
                            plg_dir = itemPath
 
3685
                            previous_version = str(installed_plg_version)
 
3686
                            latest_version_installed = installed_plg_version
 
3687
                    except Exception as e:
 
3688
                        Warn("Invalid plugin dir name: {0} {1}".format(item, e))
 
3689
                        continue
 
3690
 
 
3691
            if plg_dir == None or LooseVersion(version) > LooseVersion(previous_version) :
3541
3692
                location=p.getAttribute("location")
3542
3693
                Log("Downloading plugin manifest: " + name + " from " + location)
3543
3694
                SimpleLog(p.plugin_log,"Downloading plugin manifest: " + name + " from " + location)
3653
3804
 
3654
3805
                cmd = ''
3655
3806
                getcmd='installCommand'
3656
 
                if plg_dir != None and previous_version != None and version > previous_version :
 
3807
                if plg_dir != None and previous_version != None and LooseVersion(version) > LooseVersion(previous_version):
3657
3808
                    previous_handler=name+'-'+previous_version
3658
3809
                    if self.GetHandlerState(previous_handler) != 'NotInstalled':
3659
3810
                        getcmd='updateCommand'
3666
3817
                            self.SetHandlerState(previous_handler, 'Disabled')
3667
3818
                            Log(name+' version ' + previous_version + ' is disabled')
3668
3819
                            SimpleLog(p.plugin_log,name+' version ' + previous_version + ' is disabled')
 
3820
                    
 
3821
                        try:
 
3822
                            Log("Copy status file from old plugin dir to new")
 
3823
                            old_plg_dir = plg_dir 
 
3824
                            new_plg_dir = os.path.join(LibDir, "{0}-{1}".format(name, version))
 
3825
                            old_ext_status_dir = os.path.join(old_plg_dir, "status")
 
3826
                            new_ext_status_dir = os.path.join(new_plg_dir, "status")
 
3827
                            if os.path.isdir(old_ext_status_dir):
 
3828
                                for status_file in os.listdir(old_ext_status_dir):
 
3829
                                    status_file_path = os.path.join(old_ext_status_dir, status_file)
 
3830
                                    if os.path.isfile(status_file_path):
 
3831
                                        shutil.copy2(status_file_path, new_ext_status_dir)
 
3832
                            mrseq_file = os.path.join(old_plg_dir, "mrseq")
 
3833
                            if os.path.isfile(mrseq_file):
 
3834
                                shutil.copy(mrseq_file, new_plg_dir)
 
3835
                        except Exception as e:
 
3836
                            Error("Failed to copy status file.")
3669
3837
 
3670
3838
                isupgradeSuccess = True
3671
3839
                if getcmd=='updateCommand':
3687
3855
                        self.SetHandlerState(previous_handler, 'NotInstalled')
3688
3856
                        Log('Uninstall complete'+ previous_handler )
3689
3857
                        SimpleLog(p.plugin_log,'Uninstall complete'+ name +'-' + previous_version)
 
3858
                    
 
3859
                    try:
 
3860
                        #rm old plugin dir
 
3861
                        if os.path.isdir(plg_dir):
 
3862
                            shutil.rmtree(plg_dir)
 
3863
                            Log(name +'-'+ previous_version + ' extension files deleted.')
 
3864
                            SimpleLog(p.plugin_log,name +'-'+ previous_version + ' extension files deleted.')
 
3865
                    except Exception as e:
 
3866
                        Error("Failed to remove old plugin directory")
 
3867
 
3690
3868
                    AddExtensionEvent(name,WALAEventOperation.Upgrade,isupgradeSuccess,0,previous_version)
3691
3869
                else :  # run install
3692
3870
                    if self.launchCommand(p.plugin_log,name,version,getcmd) == None :
3902
4080
            incarnation=self.Extensions[0].getAttribute("goalStateIncarnation")
3903
4081
        except:
3904
4082
            Error('Error parsing ExtensionsConfig.  Unable to send status reports')
3905
 
            return None
 
4083
            return -1
3906
4084
        status=''
3907
4085
        statuses=''
3908
4086
        for p in self.Plugins:
3940
4118
            uri=GetNodeTextData(self.Extensions[0].getElementsByTagName("StatusUploadBlob")[0]).replace('&amp;','&')
3941
4119
        except:
3942
4120
            Error('Error parsing ExtensionsConfig.  Unable to send status reports')
3943
 
            return None
 
4121
            return -1
3944
4122
 
3945
 
        UploadStatusBlob(uri, status.encode("utf-8"))
3946
4123
        LogIfVerbose('Status report '+status+' sent to ' + uri)
3947
 
        return True
 
4124
        return UploadStatusBlob(uri, status.encode("utf-8"))
3948
4125
 
3949
4126
    def GetCurrentSequenceNumber(self, plugin_base_dir):
3950
4127
        """
4430
4607
            if len(CDSection) > 0 :
4431
4608
                self.CustomData=GetNodeTextData(CDSection[0])
4432
4609
                if len(self.CustomData)>0:
4433
 
                    SetFileContents(LibDir + '/CustomData', MyDistro.translateCustomData(self.CustomData))
 
4610
                    SetFileContents(LibDir + '/CustomData', bytearray(MyDistro.translateCustomData(self.CustomData)))
4434
4611
                    Log('Wrote ' + LibDir + '/CustomData')
4435
4612
                else :
4436
4613
                    Error('<CustomData> contains no data!')
4999
5176
        net = self.IntegerToIpAddressV4String(net)
5000
5177
        mask = self.IntegerToIpAddressV4String(mask)
5001
5178
        gateway = self.IntegerToIpAddressV4String(gateway)
5002
 
        Run("/sbin/route add -net " + net + " netmask " + mask + " gw " + gateway,chk_err=False)
 
5179
        Log("Route add: net={0}, mask={1}, gateway={2}".format(net, mask, gateway))
 
5180
        MyDistro.routeAdd(net, mask, gateway)
 
5181
    
 
5182
    def SetDefaultGateway(self, gateway):
 
5183
        """
 
5184
        Set default gateway
 
5185
        """
 
5186
        gateway = self.IntegerToIpAddressV4String(gateway)
 
5187
        Log("Set default gateway: {0}".format(gateway))
 
5188
        MyDistro.setDefaultGateway(gateway)
5003
5189
 
5004
5190
    def HandleDhcpResponse(self, sendData, receiveBuffer):
5005
5191
        """
5082
5268
                    gateway = self.UnpackBigEndian(receiveBuffer, i + 2, 4)
5083
5269
                    IpAddress = self.IntegerToIpAddressV4String(gateway)
5084
5270
                    if option == 3:
5085
 
                        self.RouteAdd(0, 0, gateway)
 
5271
                        self.SetDefaultGateway(gateway)
5086
5272
                        name = "DefaultGateway"
5087
5273
                    else:
5088
5274
                        endpoint = IpAddress
5089
 
                        name = "Microsoft Azure wire protocol endpoint"
 
5275
                        name = "Azure wire protocol endpoint"
5090
5276
                    LogIfVerbose(name + ": " + IpAddress + " at " + hex(i))
5091
5277
                else:
5092
5278
                    Error("HandleDhcpResponse: Data too small for option " + str(option))
5098
5284
    def DoDhcpWork(self):
5099
5285
        """
5100
5286
        Discover the wire server via DHCP option 245.
5101
 
        And workaround incompatibility with Microsoft Azure DHCP servers.
 
5287
        And workaround incompatibility with Azure DHCP servers.
5102
5288
        """
5103
5289
        ShortSleep = False # Sleep 1 second before retrying DHCP queries.
5104
5290
        ifname=None
5212
5398
        if not goalStateXml:
5213
5399
            Error("UpdateGoalState failed.")
5214
5400
            return
5215
 
        Log("Retrieved GoalState from Microsoft Azure Fabric.")
 
5401
        Log("Retrieved GoalState from Azure Fabric.")
5216
5402
        self.GoalState = GoalState(self).Parse(goalStateXml)
5217
5403
        return self.GoalState
5218
5404
 
5503
5689
        """
5504
5690
        SetFileContents("/var/run/waagent.pid", str(os.getpid()) + "\n")
5505
5691
 
 
5692
        reportHandlerStatusCount = 0
 
5693
 
5506
5694
        # Determine if we are in VMM.  Spawn VMM_STARTUP_SCRIPT_NAME if found.
5507
5695
        self.SearchForVMMStartup()
5508
5696
        ipv4=''
5524
5712
        except:
5525
5713
            pass
5526
5714
 
5527
 
        Log("Probing for Microsoft Azure environment.")
 
5715
        Log("Probing for Azure environment.")
5528
5716
        self.Endpoint = self.DoDhcpWork()
5529
5717
 
5530
 
        if self.Endpoint == None:
5531
 
            Log("Microsoft Azure environment not detected.")
5532
 
            while True:
5533
 
                time.sleep(60)
 
5718
        while self.Endpoint == None:
 
5719
            Log("Azure environment not detected.")
 
5720
            Log("Retry environment detection in 60 seconds")
 
5721
            time.sleep(60)
 
5722
            self.Endpoint = self.DoDhcpWork()
5534
5723
 
5535
 
        Log("Discovered Microsoft Azure endpoint: " + self.Endpoint)
 
5724
        Log("Discovered Azure endpoint: " + self.Endpoint)
5536
5725
        if not self.CheckVersions():
5537
5726
            Error("Agent.CheckVersions failed")
5538
5727
            sys.exit(1)
5647
5836
                incarnation = self.ReportReady()
5648
5837
            # Process our extensions.
5649
5838
            if goalState.ExtensionsConfig == None and goalState.ExtensionsConfigXml != None :
 
5839
                reportHandlerStatusCount = 0 #Reset count when new goal state comes
5650
5840
                goalState.ExtensionsConfig = ExtensionsConfig().Parse(goalState.ExtensionsConfigXml)
5651
5841
 
5652
5842
            # report the status/heartbeat results of extension processing
5653
5843
            if goalState.ExtensionsConfig != None :
5654
 
                goalState.ExtensionsConfig.ReportHandlerStatus()
 
5844
                ret = goalState.ExtensionsConfig.ReportHandlerStatus()
 
5845
                if ret != 0:
 
5846
                    Error("Failed to report handler status")
 
5847
                elif reportHandlerStatusCount % 1000 == 0:
 
5848
                    #Agent report handler status every 25 seconds. Reduce the log entries by adding a count
 
5849
                    Log("Successfully reported handler status")
 
5850
                reportHandlerStatusCount += 1
5655
5851
            
 
5852
            global LinuxDistro
 
5853
            if LinuxDistro == "redhat":
 
5854
                DoInstallRHUIRPM()
 
5855
                
5656
5856
            if not eventMonitor:
5657
5857
                eventMonitor = WALAEventMonitor(self.HttpPostWithHeaders)
5658
5858
                eventMonitor.StartEventsLoop()
5987
6187
    global LinuxDistro
5988
6188
    LinuxDistro=DistInfo()[0]
5989
6189
    
5990
 
    #The platform.py lib has issue with detecting oracle linux distribution.
5991
 
    #Merge the following patch provided by oracle as a temparory fix.
5992
 
    if os.path.exists("/etc/oracle-release"): 
5993
 
        LinuxDistro="Oracle Linux" 
5994
 
 
5995
6190
    global MyDistro
5996
6191
    MyDistro=GetMyDistro()
5997
6192
    if MyDistro == None :
6051
6246
        sys.exit(Usage())
6052
6247
    global modloaded
6053
6248
    modloaded = False
6054
 
    try:
6055
 
        SwitchCwd()
6056
 
        Log(GuestAgentLongName + " Version: " + GuestAgentVersion)
6057
 
        if IsLinux():
6058
 
            Log("Linux Distribution Detected      : " + LinuxDistro)
6059
 
        global WaAgent
6060
 
        WaAgent = Agent()
6061
 
        WaAgent.Run()
6062
 
    except Exception, e:
6063
 
        Error(traceback.format_exc())
6064
 
        Error("Exception: " + str(e))
6065
 
        sys.exit(1)
 
6249
   
 
6250
    while True:
 
6251
        try:
 
6252
            SwitchCwd()
 
6253
            Log(GuestAgentLongName + " Version: " + GuestAgentVersion)
 
6254
            if IsLinux():
 
6255
                Log("Linux Distribution Detected      : " + LinuxDistro)
 
6256
            global WaAgent
 
6257
            WaAgent = Agent()
 
6258
            WaAgent.Run()
 
6259
        except Exception, e:
 
6260
            Error(traceback.format_exc())
 
6261
            Error("Exception: " + str(e))
 
6262
            Log("Restart agent in 15 seconds")
 
6263
            time.sleep(15)
6066
6264
    
6067
6265
if __name__ == '__main__' :
6068
6266
    main()