diff --git a/.gitignore b/.gitignore index faaf506..0567bf4 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,10 @@ tests/status_blob_url.py build dist *.egg-info +.vs/WALinuxAgent/v14/.suo +WALinuxAgent.pyproj +WALinuxAgent.pyproj.user +WALinuxAgent.sln .coverage .ropeproject diff --git a/Changelog b/Changelog index 62bf4fc..919bd2d 100644 --- a/Changelog +++ b/Changelog @@ -1,5 +1,8 @@ WALinuxAgent Changelog ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +6 Jun 2016, WALinuxAgent 2.0.16 + . add the support for the rdma driver installing/updating. +||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| 5 Jun 2016, WALinuxAgent 2.0.16 . Handle 410 when reporting health . Add timeout to http request diff --git a/config/waagent.conf b/config/waagent.conf index 081579b..2cc697b 100644 --- a/config/waagent.conf +++ b/config/waagent.conf @@ -84,3 +84,7 @@ OS.OpensslPath=None #HttpProxy.Host=None #HttpProxy.Port=None +# If set, agent will try to update or check the rdma driver +OS.UpdateRdmaDriver=y +OS.CheckRdmaDriver=y +OS.RdmaRepository=https://drivers.suse.com/microsoft/Microsoft-LIS-RDMA/sle-12/updates diff --git a/waagent b/waagent index 673a04c..4d2a95c 100644 --- a/waagent +++ b/waagent @@ -80,7 +80,7 @@ if not hasattr(subprocess,'check_output'): subprocess.check_output=check_output subprocess.CalledProcessError=CalledProcessError - + GuestAgentName = "WALinuxAgent" GuestAgentLongName = "Azure Linux Agent" GuestAgentVersion = "WALinuxAgent-2.0.16" @@ -192,7 +192,7 @@ class AbstractDistro(object): self.shadow_file_mode=0600 self.shadow_file_path="/etc/shadow" self.dhcp_enabled = False - + def isSelinuxSystem(self): """ Checks and sets self.selinux = True if SELinux is available on system. @@ -203,7 +203,7 @@ class AbstractDistro(object): else: self.selinux = True return self.selinux - + def isSelinuxRunning(self): """ Calls shell command 'getenforce' and returns True if 'Enforcing'. @@ -212,7 +212,7 @@ class AbstractDistro(object): return RunGetOutput("getenforce")[1].startswith("Enforcing") else: return False - + def setSelinuxEnforce(self,state): """ Calls shell command 'setenforce' with 'state' and returns resulting exit code. @@ -229,14 +229,14 @@ class AbstractDistro(object): """ if self.isSelinuxSystem(): return Run('chcon ' + cn + ' ' + path) - + def setHostname(self,name): """ Shell call to hostname. Returns resulting exit code. """ return Run('hostname ' + name) - + def publishHostname(self,name): """ Set the contents of the hostname file to 'name'. @@ -251,7 +251,7 @@ class AbstractDistro(object): except: return 1 return r - + def installAgentServiceScriptFiles(self): """ Create the waagent support files for service installation. @@ -267,7 +267,7 @@ class AbstractDistro(object): Abstract Virtual Function. Over-ridden in concrete Distro classes. """ pass - + def uninstallAgentService(self): """ Call service subsystem to remove waagent script. @@ -281,19 +281,19 @@ class AbstractDistro(object): """ self.stopAgentService() self.uninstallAgentService() - + def startAgentService(self): """ Service call to start the Agent service """ return Run(self.service_cmd + ' ' + self.agent_service_name + ' start') - + def stopAgentService(self): """ Service call to stop the Agent service """ return Run(self.service_cmd + ' ' + self.agent_service_name + ' stop',False) - + def restartSshService(self): """ Service call to re(start) the SSH service @@ -316,7 +316,7 @@ class AbstractDistro(object): Error("Failed: " + fprint + ".crt -> " + path) error = 1 return error - + def checkPackageInstalled(self,p): """ Query package database for prescence of an installed package. @@ -343,7 +343,7 @@ class AbstractDistro(object): self.setSelinuxContext(filepath,'system_u:object_r:shadow_t:s0') Log("Root password deleted.") return 0 - + def changePass(self,user,password): Log("Change user password") crypt_id = Config.get("Provisioning.PasswordCryptId") @@ -360,7 +360,7 @@ class AbstractDistro(object): 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) @@ -382,7 +382,7 @@ class AbstractDistro(object): Generic function to remove ata_piix.ko. """ return WaAgent.TryUnloadAtapiix() - + def deprovisionWarnUser(self): """ Generic user warnings used at deprovision. @@ -404,7 +404,7 @@ class AbstractDistro(object): except: pass return 0 - + def uninstallDeleteFiles(self): """ Files to delete when agent is uninstalled. @@ -415,7 +415,7 @@ class AbstractDistro(object): except: pass return 0 - + def checkDependencies(self): """ Generic dependency check. @@ -575,7 +575,7 @@ class AbstractDistro(object): if Run("LC_ALL=C fdisk -l " + dsk + " | grep Disk"): return False return True - + def mountDVD(self,dvd,location): return RunGetOutput(self.mount_dvd_cmd + ' ' + dvd + ' ' + location) @@ -652,7 +652,7 @@ class AbstractDistro(object): def getConfigurationPath(self): return "/etc/waagent.conf" - + def getProcessorCores(self): return int(RunGetOutput("grep 'processor.*:' /proc/cpuinfo |wc -l")[1]) @@ -691,6 +691,38 @@ class AbstractDistro(object): Run("/sbin/route add -net " + net + " netmask " + mask + " gw " + gateway, chk_err=False) + def getNdDriverVersion(self): + """ + if error happens, raise a RdmaError + """ + try: + with open("/var/lib/hyperv/.kvp_pool_0", "r") as f: + lines = f.read() + r = re.search("NdDriverVersion\0+(\d\d\d\.\d)", lines) + if r is not None: + NdDriverVersion = r.groups()[0] + return NdDriverVersion #e.g. NdDriverVersion = 142.0 + else : + Log("Error: NdDriverVersion not found.") + return None + except Exception as e: + errMsg = 'Cannot update status: Failed to enable the extension with error: %s, stack trace: %s' % (str(e), traceback.format_exc()) + Log(errMsg) + raise RdmaError(RdmaConfig.nd_driver_detect_error) + + def checkInstallHyperV(self): + return None + + def getRdmaPackageVersion(self): + return None + + def rdmaUpdate(self,updateRdmaRepository=None): + Log("rdmaUpdate in base class") + pass + + def checkRDMA(self): + Log("checkRDMA in base class") + pass ############################################################ # GentooDistro @@ -712,6 +744,7 @@ depend() } """ + class gentooDistro(AbstractDistro): """ Gentoo distro concrete class @@ -889,21 +922,34 @@ class SuSEDistro(AbstractDistro): """ def __init__(self): super(SuSEDistro,self).__init__() - self.service_cmd='/sbin/service' - self.ssh_service_name='sshd' - self.kernel_boot_options_file='/boot/grub/menu.lst' - self.hostname_file_path='/etc/HOSTNAME' - self.requiredDeps += [ "/sbin/insserv" ] - self.init_file=suse_init_file - self.dhcp_client_name='dhcpcd' - if ((DistInfo(fullname=1)[0] == 'SUSE Linux Enterprise Server' and DistInfo()[1] >= '12') or \ - (DistInfo(fullname=1)[0] == 'openSUSE' and DistInfo()[1] >= '13.2')): - self.dhcp_client_name='wickedd-dhcp4' + dist_info = DistInfo() + dist_info_fullname = DistInfo(fullname=1) + + self.dhcp_client_name = 'dhcpcd' + if ((dist_info_fullname[0] == 'SUSE Linux Enterprise Server' and dist_info[1] >= '12') or \ + (dist_info_fullname[0] == 'openSUSE' and dist_info[1] >= '13.2')): + self.dhcp_client_name = 'wickedd-dhcp4' + self.dhcp_enabled = True self.grubKernelBootOptionsFile = '/boot/grub/menu.lst' self.grubKernelBootOptionsLine = 'kernel' - self.getpidcmd='pidof ' - self.dhcp_enabled=True - + self.getpidcmd = 'pidof ' + self.hostname_file_path = '/etc/HOSTNAME' + self.init_file = suse_init_file + self.kernel_boot_options_file = '/boot/grub/menu.lst' + self.modprobe_path = '/usr/bin/modprobe' + + self.requiredDeps += [ "/sbin/insserv" ] + self.reboot_path = '/sbin/reboot' + self.rpm_path = '/bin/rpm' + self.service_cmd = '/sbin/service' + self.ssh_service_name ='sshd' + if(dist_info[1] == "11"): + self.ps_path = '/bin/ps' + else: + self.ps_path = '/usr/bin/ps' + + self.zypper_path = '/usr/bin/zypper' + def checkPackageInstalled(self,p): if Run("rpm -q " + p,chk_err=False): return 0 @@ -915,7 +961,6 @@ class SuSEDistro(AbstractDistro): return 1 else: return 0 - def installAgentServiceScriptFiles(self): try: @@ -923,7 +968,7 @@ class SuSEDistro(AbstractDistro): os.chmod(self.init_script_file, 0744) except: pass - + def registerAgentService(self): self.installAgentServiceScriptFiles() return Run('insserv ' + self.agent_service_name) @@ -940,7 +985,137 @@ class SuSEDistro(AbstractDistro): def stopDHCP(self): Run("service " + self.dhcp_client_name + " stop", chk_err=False) - + + def getRdmaPackageVersion(self): + """ + """ + error, output = RunGetOutput(self.zypper_path + " info " + RdmaConfig.rmda_package_name) + if(error == RdmaConfig.process_success): + r = re.search("Version: (\S+)", output) + if r is not None: + package_version = r.groups()[0] # e.g. package_version is "20150707.140.0_k3.12.28_4-3.1." + return package_version + else: + return None + else: + return None + + def checkInstallHyperV(self): + error, output = RunGetOutput(self.ps_path + " -ef") + if(error != RdmaConfig.process_success): + return RdmaConfig.common_failed + else: + hv_kvp_daemon_service_process_name = "hv_kvp_daemon" + hv_kvp_daemon_service_name = "hv_kvp_daemon" + r = re.search(hv_kvp_daemon_service_process_name, output) + if r is None : + # if the + Log("hv kvp daemon is not running.") + error,output = RunGetOutput(self.rpm_path + " -q hyper-v", chk_err=False,log_cmd=False) + if(error == RdmaConfig.process_success): + Log("the hyper-v package is installed, but hv_kvp_daemon not started") + return RdmaConfig.hv_kvp_daemon_not_started + else: + error,output = RunGetOutput(self.zypper_path + " -n install --force hyper-v") + Log("install hyper-v return code: " + str(error) + " output:" + str(output)) + if(error != RdmaConfig.process_success): + return RdmaConfig.common_failed + else: + self.rebootMachine() + return RdmaConfig.process_success + else : + Log("KVP daemon is running") + return RdmaConfig.process_success + + def rdmaUpdate(self,updateRdmaRepository=None): + # give some time for the hv_hvp_daemon to start up. + time.sleep(10) + check_install_result = self.checkInstallHyperV() + if(check_install_result == RdmaConfig.process_success): + # wait for sometime the RDMA Driver not passed in by KVP in time. + time.sleep(10) + + nd_driver_version = self.getNdDriverVersion() + if(nd_driver_version is None): + raise RdmaError(RdmaConfig.driver_version_not_found) + else: + check_result = self.checkRDMA(nd_driver_version=nd_driver_version) + Log("RDMA version check result is " + str(check_result)) + if(check_result == RdmaConfig.UpToDate): + return + elif(check_result == RdmaConfig.OutOfDate): + update_rdma_driver_result = self.rdmaUpdatePackage(host_version=nd_driver_version,updateRdmaRepository=updateRdmaRepository) + elif(check_result == RdmaConfig.DriverVersionNotFound): + raise RdmaError(RdmaConfig.driver_version_not_found) + elif(check_result == RdmaConfig.Unknown): + raise RdmaError(RdmaConfig.unknown_error) + else: + raise RdmaError(RdmaConfig.check_install_hv_utils_failed) + + def rdmaUpdatePackage(self, host_version, updateRdmaRepository = None): + # check the repository first + if(updateRdmaRepository is not None): + error,output = RunGetOutput(self.zypper_path + " lr -u") + rdma_pack_repository_name = "msft-rdma-pack" + rdma_pack_result = re.search(rdma_pack_repository_name, output) + if rdma_pack_result is None : + Log("rdma_pack_result is None") + error, output = RunGetOutput(self.zypper_path + " ar " + str(updateRdmaRepository) + " " + rdma_pack_repository_name) + #wait for the cache build. + time.sleep(20) + Log("error result is " + str(error) + " output is : " + str(output)) + else: + Log("output is: " + str(output)) + Log("msft-rdma-pack found") + + returnCode, message = RunGetOutput(self.zypper_path + " --no-gpg-checks refresh") + Log("refresh repo return code is " + str(returnCode) + " output is: " + str(message)) + #install the wrapper package, that will put the driver RPM packages under /opt/microsoft/rdma + returnCode, message = RunGetOutput(self.zypper_path + " -n remove " + RdmaConfig.wrapper_package_name) + Log("remove wrapper package return code is " + str(returnCode) + " output is: " + str(message)) + returnCode, message = RunGetOutput(self.zypper_path + " --non-interactive install --force " + RdmaConfig.wrapper_package_name) + Log("install wrapper package return code is " + str(returnCode) + " output is: " + str(message)) + r = os.listdir("/opt/microsoft/rdma") + if r is not None : + for filename in r : + if re.match(RdmaConfig.rmda_package_name + "-\d{8}\.(%s).+" % host_version, filename) : + error, output = RunGetOutput(self.zypper_path + " --non-interactive remove " + RdmaConfig.rmda_package_name) + Log("remove rdma package result is " + str(error) + " output is: " + str(output)) + Log("Installing RPM /opt/microsoft/rdma/" + filename) + error, output = RunGetOutput(self.zypper_path + " --non-interactive install --force /opt/microsoft/rdma/%s" % filename) + Log("Install rdma package result is " + str(error) + " output is: " + str(output)) + if(error == RdmaConfig.process_success): + self.rebootMachine() + else: + raise RdmaError(RdmaConfig.package_install_failed) + else: + Log("RDMA drivers not found in /opt/microsoft/rdma") + raise RdmaError(RdmaConfig.package_not_found) + + def checkRDMA(self, nd_driver_version = None): + if(nd_driver_version is None): + nd_driver_version = self.getNdDriverVersion() + if(nd_driver_version is None or nd_driver_version == ""): + return RdmaConfig.DriverVersionNotFound + package_version = self.getRdmaPackageVersion() + if(package_version is None or package_version == ""): + return RdmaConfig.OutOfDate + else: + # package_version would be like this :20150707_k3.12.28_4-3.1 20150707.140.0_k3.12.28_4-1.1 + # nd_driver_version 140.0 + Log("nd_driver_version is " + str(nd_driver_version) + " package_version is " + str(package_version)) + if(nd_driver_version is not None): + r = re.match("^[0-9]+[.](%s).+" % nd_driver_version, package_version)# NdDriverVersion should be at the end of package version + if not r : #host ND version is the same as the package version, do an update + return RdmaConfig.OutOfDate + else: + return RdmaConfig.UpToDate + return RdmaConfig.Unknown + + def rebootMachine(self): + Log("rebooting the machine") + RunGetOutput(self.reboot_path) + ############################################################ # redhatDistro ############################################################ @@ -1001,7 +1176,6 @@ case "$1" in esac exit $RETVAL """ - class redhatDistro(AbstractDistro): """ Redhat Distro concrete class @@ -1040,14 +1214,14 @@ class redhatDistro(AbstractDistro): def registerAgentService(self): self.installAgentServiceScriptFiles() return Run('chkconfig --add waagent') - + def uninstallAgentService(self): return Run('chkconfig --del ' + self.agent_service_name) def unregisterAgentService(self): self.stopAgentService() return self.uninstallAgentService() - + def checkPackageInstalled(self,p): if Run("yum list installed " + p,chk_err=False): return 0 @@ -1091,6 +1265,12 @@ class centosDistro(redhatDistro): def __init__(self): super(centosDistro,self).__init__() + def rdmaUpdate(self,updateRdmaRepository=None): + pass + + def checkRDMA(self): + pass + ############################################################ # oracleDistro ############################################################ @@ -1103,8 +1283,6 @@ class oracleDistro(redhatDistro): def __init__(self): super(oracleDistro, self).__init__() - - ############################################################ # asianuxDistro ############################################################ @@ -1117,7 +1295,6 @@ class asianuxDistro(redhatDistro): def __init__(self): super(asianuxDistro,self).__init__() - ############################################################ # CoreOSDistro ############################################################ @@ -1325,7 +1502,6 @@ esac exit 0 """ - class debianDistro(AbstractDistro): """ debian Distro concrete class @@ -1352,7 +1528,7 @@ class debianDistro(AbstractDistro): return 1 else: return 0 - + def checkDependencies(self): """ Debian dependency check. python-pyasn1 is NOT needed. @@ -1373,7 +1549,7 @@ class debianDistro(AbstractDistro): return 1 else: return 0 - + def installAgentServiceScriptFiles(self): """ If we are packaged - the service name is walinuxagent, do nothing. @@ -1387,20 +1563,20 @@ class debianDistro(AbstractDistro): ErrorWithPrefix('installAgentServiceScriptFiles','Exception: '+str(e)+' occured creating ' + self.init_script_file) return 1 return 0 - + def registerAgentService(self): if self.installAgentServiceScriptFiles() == 0: return Run('update-rc.d waagent defaults') else : return 1 - + def uninstallAgentService(self): return Run('update-rc.d -f ' + self.agent_service_name + ' remove') def unregisterAgentService(self): self.stopAgentService() return self.uninstallAgentService() - + def sshDeployPublicKey(self,fprint,path): """ We support PKCS8. @@ -1409,7 +1585,7 @@ class debianDistro(AbstractDistro): return 1 else : return 0 - + ############################################################ # KaliDistro - WIP # Functioning on Kali 1.1.0a so far @@ -1452,7 +1628,6 @@ end script exec /usr/sbin/waagent -daemon """ - class UbuntuDistro(debianDistro): """ Ubuntu Distro concrete class @@ -1468,7 +1643,7 @@ class UbuntuDistro(debianDistro): def registerAgentService(self): return self.installAgentServiceScriptFiles() - + def uninstallAgentService(self): """ If we are packaged - the service name is walinuxagent, do nothing. @@ -1532,7 +1707,6 @@ class UbuntuDistro(debianDistro): Error("Can't find host key: {0}".format(path)) return False - ############################################################ # LinuxMintDistro ############################################################ @@ -1803,7 +1977,7 @@ class FreeBSDDistro(AbstractDistro): self.mount_dvd_cmd = 'dd bs=2048 count=33 skip=295 if=' # custom data max len is 64k self.sudoers_dir_base = '/usr/local/etc' self.waagent_conf_file = FreeBSDWaagentConf - + def installAgentServiceScriptFiles(self): SetFileContents(self.init_script_file, self.init_file) os.chmod(self.init_script_file, 0777) @@ -1814,7 +1988,6 @@ class FreeBSDDistro(AbstractDistro): self.installAgentServiceScriptFiles() return Run("services_mkdb " + self.init_script_file) - def sshDeployPublicKey(self,fprint,path): """ We support PKCS8. @@ -2001,7 +2174,7 @@ class FreeBSDDistro(AbstractDistro): ChangeOwner(dir + "/authorized_keys", user) Log("Created user account: " + user) return None - + def DeleteAccount(self,user): """ Delete the 'user'. @@ -2091,12 +2264,12 @@ class FreeBSDDistro(AbstractDistro): Log("Configured SSH client probing to keep connections alive.") #ApplyVNUMAWorkaround() return 0 - + def mediaHasFilesystem(self,dsk): if Run('LC_ALL=C fdisk -p ' + dsk + ' | grep "invalid fdisk partition table found" ',False): return False return True - + def mountDVD(self,dvd,location): #At this point we cannot read a joliet option udf DVD in freebsd10 - so we 'dd' it into our location retcode,out = RunGetOutput(self.mount_dvd_cmd + dvd + ' of=' + location + '/ovf-env.xml') @@ -2132,10 +2305,10 @@ class FreeBSDDistro(AbstractDistro): def getProcessorCores(self): return int(RunGetOutput("sysctl hw.ncpu | awk '{print $2}'")[1]) - + 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) @@ -2270,7 +2443,7 @@ def RunGetOutput(cmd, chk_err=True, log_cmd=True): """ if log_cmd: LogIfVerbose(cmd) - try: + try: output=subprocess.check_output(cmd,stderr=subprocess.STDOUT,shell=True) except subprocess.CalledProcessError,e : if chk_err and log_cmd: @@ -2520,13 +2693,13 @@ class Logger(object): self.file_path=filepath self.con_path=conpath self.verbose=verbose - + def ThrottleLog(self,counter): """ Log everything up to 10, every 10 up to 100, then every 100. """ return (counter < 10) or ((counter < 100) and ((counter % 10) == 0)) or ((counter % 100) == 0) - + def LogToFile(self,message): """ Write 'message' to logfile. @@ -2539,7 +2712,7 @@ class Logger(object): except IOError, e: print e pass - + def LogToCon(self,message): """ Write 'message' to /dev/console. @@ -2553,14 +2726,14 @@ class Logger(object): C.write(message.encode('ascii','ignore') + "\n") except IOError, e: pass - + def Log(self,message): """ Standard Log function. Logs to self.file_path, and con_path """ self.LogWithPrefix("", message) - + def LogWithPrefix(self,prefix, message): """ Prefix each line of 'message' with current time+'prefix'. @@ -2572,19 +2745,19 @@ class Logger(object): line = t + line self.LogToFile(line) self.LogToCon(line) - + def NoLog(self,message): """ Don't Log. """ pass - + def LogIfVerbose(self,message): """ Only log 'message' if global Verbose is True. """ self.LogWithPrefixIfVerbose('',message) - + def LogWithPrefixIfVerbose(self,prefix, message): """ Only log 'message' if global Verbose is True. @@ -2598,26 +2771,26 @@ class Logger(object): line = t + line self.LogToFile(line) self.LogToCon(line) - + def Warn(self,message): """ Prepend the text "WARNING:" to the prefix for each line in 'message'. """ self.LogWithPrefix("WARNING:", message) - + def Error(self,message): """ Call ErrorWithPrefix(message). """ ErrorWithPrefix("", message) - + def ErrorWithPrefix(self,prefix, message): """ Prepend the text "ERROR:" to the prefix for each line in 'message'. Errors written to logfile, and /dev/console """ self.LogWithPrefix("ERROR:", message) - + def LoggerInit(log_file_path,log_con_path,verbose=False): """ Create log object and export its methods to global scope. @@ -2764,7 +2937,7 @@ def DoInstallRHUIRPM(): return Log("install RHUI RPM completed") - + class Util(object): """ Http communication class. @@ -3108,7 +3281,7 @@ class TCPHandler(SocketServer.BaseRequestHandler): log("Received LB probe # " + strCounter) self.request.recv(1024) self.request.send("HTTP/1.1 200 OK\r\nContent-Length: 2\r\nContent-Type: text/html\r\nDate: " + self.GetHttpDateTimeNow() + "\r\n\r\nOK") - + class LoadBalancerProbeServer(object): """ Threaded object to receive and send LB probe messages. @@ -3140,9 +3313,6 @@ class ConfigurationProvider(object): """ def __init__(self, walaConfigFile): self.values = dict() - if 'MyDistro' not in globals(): - global MyDistro - MyDistro = GetMyDistro() if walaConfigFile is None: walaConfigFile = MyDistro.getConfigurationPath() if os.path.isfile(walaConfigFile) == False: @@ -3164,6 +3334,20 @@ class ConfigurationProvider(object): def get(self, key): return self.values.get(key) + def yes(self,key): + configValue = self.get(key) + if(configValue is not None and configValue.lower().startswith("y")): + return True + else: + return False + + def no(self,key): + configValue = self.get(key) + if(configValue is not None and configValue.lower().startswith("n")): + return True + else: + return False + class EnvMonitor(object): """ Montor changes to dhcp and hostname. @@ -3440,8 +3624,35 @@ class SharedConfig(object): rdma_configured = False +class RdmaConfig(object): + """ + configurations + """ + wrapper_package_name = 'msft-rdma-drivers' + rmda_package_name = 'msft-lis-rdma-kmp-default' + """ + error code definitions + """ + process_success = 0 + common_failed = 1 + check_install_hv_utils_failed = 2 + nd_driver_detect_error = 3 + driver_version_not_found = 4 + unknown_error = 5 + package_not_found = 6 + package_install_failed = 7 + hv_kvp_daemon_not_started = 8 + """ + check_rdma_result + """ + UpToDate = 0 + OutOfDate = 1 + DriverVersionNotFound = 3 + Unknown = -1 + class RdmaError(Exception): - pass + def __init__(self, error_code = RdmaConfig.process_success): + self.error_code = error_code class RdmaHandler(object): """ @@ -4139,7 +4350,6 @@ class ExtensionsConfig(object): continue return str(seq_no) - def GenerateAggStatus(self, name, version, reportHeartbeat = False): """ Generate the status which Azure can understand by the status and heartbeat reported by extension @@ -4198,7 +4408,6 @@ class ExtensionsConfig(object): agg_status_string = json.dumps(agg_status_obj) LogIfVerbose("Handler Aggregated Status:" + agg_status_string) return agg_status_string - def SetHandlerState(self, handler, state=''): zip_dir=LibDir+"/" + handler @@ -4222,7 +4431,6 @@ class ExtensionsConfig(object): else: return 'NotInstalled' - class HostingEnvironmentConfig(object): """ Parse Hosting enviromnet config and store in @@ -4509,7 +4717,7 @@ class GoalState(Util): LogIfVerbose("Process goalstate") self.HostingEnvironmentConfig.Process() self.SharedConfig.Process() - + class OvfEnv(object): """ Read, and process provisioning info from provisioning file OvfEnv.xml @@ -4780,13 +4988,10 @@ class OvfEnv(object): MyDistro.restartSshService() return error - -class WALAEvent(object): +class WALAEvent(object): def __init__(self): - self.providerId="" self.eventId=1 - self.OpcodeName="" self.KeywordName="" self.TaskName="" @@ -4815,7 +5020,7 @@ class WALAEvent(object): for attName in self.__dict__: if attName in ["eventId","filedCount","providerId"]: continue - + attValue = self.__dict__[attName] if type(attValue) is int: strEventsData+=strRecordFormat.format(attName,attValue,strMtUInt64) @@ -4834,7 +5039,7 @@ class WALAEvent(object): if type(attValue) is float: strEventsData+=strRecordFormat.format(attName,attValue,strMtFloat) continue - + Log("Warning: property "+attName+":"+str(type(attValue))+":type"+str(type(attValue))+"Can't convert to events data:"+":type not supported") return u"{0}{1}{2}".format(strProviderid,strEventid,strEventsData) @@ -4846,13 +5051,12 @@ class WALAEvent(object): os.chmod(eventfolder,0700) if len(os.listdir(eventfolder)) > 1000: raise Exception("WriteToFolder:Too many file under "+eventfolder+" exit") - + filename = os.path.join(eventfolder,str(int(time.time()*1000000))) with open(filename+".tmp",'wb+') as hfile: hfile.write(self.ToXml().encode("utf-8")) os.rename(filename+".tmp",filename+".tld") - class WALAEventOperation: HeartBeat="HeartBeat" Provision = "Provision" @@ -4862,7 +5066,7 @@ class WALAEventOperation: Enable = "Enable" Download = "Download" Upgrade = "Upgrade" - Update = "Update" + Update = "Update" def AddExtensionEvent(name,op,isSuccess,duration=0,version="1.0",message="",type="",isInternal=False): event = ExtensionEvent() @@ -4878,8 +5082,7 @@ def AddExtensionEvent(name,op,isSuccess,duration=0,version="1.0",message="",type event.Save() except: Error("Error "+traceback.format_exc()) - - + class ExtensionEvent(WALAEvent): def __init__(self): @@ -4894,8 +5097,7 @@ class ExtensionEvent(WALAEvent): self.ExtensionType="" self.Message="" self.Duration=0 - - + class WALAEventMonitor(WALAEvent): def __init__(self,postMethod): WALAEvent.__init__(self) @@ -4908,7 +5110,7 @@ class WALAEventMonitor(WALAEvent): eventThread = threading.Thread(target = self.EventsLoop) eventThread.setDaemon(True) eventThread.start() - + def EventsLoop(self): LastReportHeartBeatTime = datetime.datetime.min try: @@ -4921,7 +5123,7 @@ class WALAEventMonitor(WALAEvent): time.sleep(60) except: Error("Exception in events loop:"+traceback.format_exc()) - + def SendEvent(self,providerid,events): dataFormat = u'{1}'\ '' @@ -4983,7 +5185,6 @@ class WALAEventMonitor(WALAEvent): self.SendEvent(key,events[key]) if eventSendNumber%3 == 0: time.sleep(15) - def AddSystemInfo(self,eventData): if not self.issysteminfoinitilized: @@ -5011,8 +5212,7 @@ class WALAEventMonitor(WALAEvent): if self.sysInfo.get(name): node.setAttribute("Value",xml.sax.saxutils.escape(str(self.sysInfo[name]))) - return eventObject.toxml() - + return eventObject.toxml() class Agent(Util): """ @@ -5529,7 +5729,7 @@ class Agent(Util): return 1 Log("Loaded " + krn_pth + " driver for ATAPI CD-ROM") - + # we have succeeded loading the ata_piix mod if it can be done. def SearchForVMMStartup(self): @@ -5579,7 +5779,7 @@ class Agent(Util): Log("VMM Init script not found. Provisioning for Azure") return - + def Provision(self): """ Responible for: @@ -5716,7 +5916,6 @@ class Agent(Util): self.Endpoint = self.DoDhcpWork() while self.Endpoint == None: - Log("Azure environment not detected.") Log("Retry environment detection in 60 seconds") time.sleep(60) self.Endpoint = self.DoDhcpWork() @@ -5732,25 +5931,44 @@ class Agent(Util): MyDistro.initScsiDiskTimeout() global provisioned global provisionError - + global Openssl Openssl = Config.get("OS.OpensslPath") if Openssl == None: Openssl = "openssl" self.TransportCert = self.GenerateTransportCert() - + eventMonitor = None incarnation = None # goalStateIncarnationFromHealthReport currentPort = None # loadBalancerProbePort goalState = None # self.GoalState, instance of GoalState provisioned = os.path.exists(LibDir + "/provisioned") program = Config.get("Role.StateConsumer") - provisionError = None + provisionError = None lbProbeResponder = True - setting = Config.get("LBProbeResponder") - if setting != None and setting.lower().startswith("n"): + + lbProbeResponderNo = Config.no("LBProbeResponder") + if lbProbeResponderNo: lbProbeResponder = False + + try: + updateRdmaDriverConfigured = Config.yes("OS.UpdateRdmaDriver") + updateRdmaRepository = Config.get("OS.RdmaRepository") + if(updateRdmaDriverConfigured): + MyDistro.rdmaUpdate(updateRdmaRepository) + else: + Log("OS.UpdateRdmaDriver configured to "+str(updateRdmaDriverConfigured)+" so skip the rdma update.") + checkRdmaDriverConfigured = Config.yes("OS.CheckRdmaDriver") + if(checkRdmaDriverConfigured): + checkRdmaResult = MyDistro.checkRDMA() + Log("Rdma check result is " + str(checkRdmaResult)) + else: + Log("OS.CheckRdmaDriver configured to "+str(checkRdmaDriverConfigured)+" so skip the rdma check.") + except Exception as e: + errMsg = 'check or update Rdma driver failed with error: %s, stack trace: %s' % (str(e), traceback.format_exc()) + Error(errMsg) + while True: if (goalState == None) or (incarnation == None) or (goalState.Incarnation != incarnation): try: @@ -5848,18 +6066,16 @@ class Agent(Util): #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() time.sleep(25 - sleepToReduceAccessDenied) - WaagentLogrotate = """\ /var/log/waagent.log { monthly @@ -5979,7 +6195,7 @@ def ApplyVNUMAWorkaround(): Log("Your kernel version " + platform.release() + " has a NUMA-related bug: NUMA has been disabled.") else : "Error adding 'numa=off'. NUMA has not been disabled." - + def RevertVNUMAWorkaround(): """ Remove 'numa=off' from kernel boot options. @@ -6167,8 +6383,6 @@ def Usage(): print("usage: " + sys.argv[0] + " [-verbose] [-force] [-help|-install|-uninstall|-deprovision[+user]|-version|-serialconsole|-daemon]") return 0 - - def main(): """ Instantiate MyDistro, exit if distro class is not defined. @@ -6186,9 +6400,9 @@ def main(): LoggerInit('/var/log/waagent.log','/dev/console') global LinuxDistro LinuxDistro=DistInfo()[0] - + global MyDistro - MyDistro=GetMyDistro() + MyDistro = GetMyDistro() if MyDistro == None : sys.exit(1) args = [] @@ -6246,7 +6460,7 @@ def main(): sys.exit(Usage()) global modloaded modloaded = False - + while True: try: SwitchCwd() @@ -6261,6 +6475,5 @@ def main(): Error("Exception: " + str(e)) Log("Restart agent in 15 seconds") time.sleep(15) - if __name__ == '__main__' : main()