diff options
Diffstat (limited to 'tests')
42 files changed, 2400 insertions, 1568 deletions
diff --git a/tests/azure_test.py b/tests/azure_test.py deleted file mode 100755 index fd152b7..0000000 --- a/tests/azure_test.py +++ /dev/null @@ -1,687 +0,0 @@ -#!/usr/bin/env python - -import os -import sys -import imp -import json -import time -import pwd - -# waagent has no '.py' therefore create waagent module import manually. -waagent=imp.load_source('waagent','waagent') - -from waagent import RunGetOutput, Run, LoggerInit - -""" -Test waagent in azure using azure-cli -Usage: - -./azure_test.py --stable_vm_image b4590d9e3ed742e4a1d46e5424aa335e__openSUSE-12.3-v120 --source_disk "http://mystorage.blob.core.windows.net/vhds/my-suse2.vhd" --acct "<storage acct key>" --testname my-osuse --mount_point /mnt/disk --agent_path ../waagent --stable_vm_acct_name MyUserName --stable_vm_acct_pass 'myp455wd' --test_vm_acct_name MyUserName --test_vm_acct_pass 'myp455wd' --azure_location "East US" --part_num 1 --retries 20 --fstype scsi --test_vm_acct_cert /root/.ssh/myCert.pem --stable_vm_acct_cert /root/.ssh/myCert.pem --keep_test_vm_vhd no --teardown_test_vm always --prompt no - -azure_test --vm <stable vm name> --testname <testname> [--acct <storage account>] -[--disk <vhd url to use as initial disk image>] -If --disk is specified, use this vhd as starting point, -otherwise use a copy of the stable vm as the starting vhd. - -Starting VHD is attached to stable vm and the sources are copied to it. -Spin up a new VM using the VHD. -Loop waiting for provisioned. -If not provisioned: - Destroy vm and attach the disk to the stable vm. - Copy the logs to the localhost. - Destroy all created objects except the starting vhd. - Exit(1) -If Provosioned: - Copy the logs to the local host. - Exit(0) - -EXAMPLE: - -sudo ./azure_test.py --vm my-stable-vm-name --disk "http://mystorageaccount.blob.core.windows.net/myvhds/my-new-os.vhd" --acct mylong-unquoted-starage-account-id --testname my-vm-test --mount_point /my-stablevm-mountpoint --agent_path ../waagent --vm_acct_name root --testvm_acct_name azureuser --testvm_acct_pass 'azureuserpassword' --location "East US" --part_num 2 --retry 20 --fstype bsd --testvm_acct_cert /home/azureuser/.ssh/myCert.pem --keep_vhd "once" --teardown "always" --prompt "no" -""" - -def makeDiskImage(di_name,vhd_url,location,copy=False): - """ - Create new data disk image of the VHD. If 'copy' - is set to True, then create a new VHD in the form of myvhd-di.vhd - based on the VHD source name. If 'copy is set to False, re-use the - vhd. Returns return code and diskimageVHD path. Code is 0 on - success or the azure-cli error code upon error. - """ - if copy : - target = os.path.dirname(vhd_url) - target = target+ '/' + di_name + '.vhd' - else : - target = vhd_url - cmd='azure vm disk create --json ' + di_name + ' --blob-url ' + target + ' ' + vhd_url - print cmd - waagent.Log( cmd) - code,output=RunGetOutput(cmd,False) - print output,code - waagent.Log(output) - waagent.Log(str(code)) - return target,code - -def makeVMImage(vmi_name,vhd_url,copy=False): - """ - Create new VM Image based on Disk Image. - Returns 0 on success or error code upon error. - """ - if copy : - target = os.path.dirname(vhd_url) - target = target+ '/' + vmi_name + '.vhd' - else : - target = vhd_url - cmd='azure vm image create --json ' + vmi_name + ' --base-vhd ' + target + ' --os Linux --blob-url ' + vhd_url - print cmd - waagent.Log( cmd) - code,output=RunGetOutput(cmd,False) - print output,code - waagent.Log(str(code)) - waagent.Log(output) - return code - -def makeVM(vm_name,vmi_name,vhd_url,test_name,location,vmuser_name,vmuser_pass,vmuser_cert,copy=False): - """ - Create new VM from the VM Image. - Returns 0 on success or error code upon error. - """ - target=os.path.dirname(vhd_url) - target = target + '/' + test_name + '.vhd' - cmd='azure vm create --json ' - if copy : - cmd += ' --blob-url "' + target + '"' - else : - target=vhd_url - cmd += ' --location "' + location + '"' - if os.path.exists(vmuser_cert): - cmd += ' -t "' + vmuser_cert + '"' - cmd += ' -e 22 ' + vm_name + ' ' + vmi_name + ' ' + vmuser_name + ' \'' +vmuser_pass + '\'' - print cmd - waagent.Log( cmd) - code,output=RunGetOutput(cmd,False) - print output,code - waagent.Log(str(code)) - waagent.Log(output) - retry=3 - while code !=0 and retry > 0 : - time.sleep(5) - code,output=RunGetOutput(cmd,False) - retry -=1 - return target - -def flushDiskImage(di_name,dele=True): - """ - Delete the VM Image. - On error we asume the VM disk image is deleted - """ - cmd='azure vm disk delete --json ' - if dele : - cmd += '--blob-delete ' - cmd+= di_name - print cmd - waagent.Log( cmd) - code,output=RunGetOutput(cmd,False) - print output,code - waagent.Log( str(code)) - waagent.Log(output) - return output,code - -def flushVMImage(vmi_name): - """ - Delete the VM Image. - Always delete the underlying blob. - On error we asume the VM image is deleted. - """ - cmd='azure vm image delete --blob-delete --json ' + vmi_name - print cmd - waagent.Log( cmd) - code,output=RunGetOutput(cmd,False) - print output,code - waagent.Log( str(code)) - waagent.Log(output) - return output,code - -def flushVM(vm_name,dele=False): - """ - Delete the VM. - On error we asume the VM is deleted - """ - cmd='azure vm delete --json ' - if dele : - cmd += ' --blob-delete ' - cmd += vm_name - print cmd - waagent.Log( cmd) - code,output=RunGetOutput(cmd,False) - print output,code - waagent.Log( str(code)) - waagent.Log(output) - return output,code - - -def createStableVMFromVMImage(vm_image,vhd_url): - """ - Create a new stable vm, provisioned with acct and certificate from - the VMImage, using the basepath of vhd_url for the new VM's vhd. - """ - stableVM=testname+'-stable-vm' - return makeVM(stableVM,vm_image,vhd_url,testname+'-stable',location,stableVMaccount,stableVMpass,stableVMCert,copy=True) - -def createStableVMFromVHD(vmi_name,vhd_url): - """ - Create a new stable vm, provisioned with acct and certificate from - the VHD, using the basepath of vhd_url for the new VM's vhd. - """ - makeVMImage(vmi_name,vhd_url,False) - return createStableVMFromVMImage(vmi_name,vhd_url) - -def createDiskImageFromStableVMDisk(vm_name): - """ - Determine the media link for the os disk of the stable vm. - Create vhd disk image copy. <vm_name>-<testname>-di - Return new disk_image_media_path or None on error. - """ - cmd='azure vm disk list --json' - print cmd - waagent.Log( cmd) - code,output=RunGetOutput(cmd,False) - print output,code - waagent.Log( str(code)) - waagent.Log(output) - if code: - print 'Error is ' + str(code) - waagent.Log( 'Error is ' + str(code)) - return None - j=json.loads(output) - source_media_link=None - for i in j : - if i.has_key('AttachedTo'): - if i['AttachedTo']['RoleName'] == vm_name: - source_media_link=i['MediaLink'] - break - if not source_media_link: - print 'Unable to locate OS disk for ' + vm_name - waagent.Log( 'Unable to locate OS disk for ' + vm_name ) - return None - target_name= testname + '-di' - makeDiskImage(target_name,source_media_link,location,copy=True) - target_media_link=os.path.dirname(source_media_link) + '/' + target_name + '.vhd' - return target_media_link - -def addDiskImageToVM(vm_name,di_name): - """ - Attach the disk image to the 'stableVM'. - Returns the LUN if successful otherwise returns None - NOTE: azure vm show may return json matching the disk image - name yet missing the LUN. When this occurs, the LUN is '0'. - """ - cmd='azure vm disk attach --json ' + vm_name + ' ' + di_name - print cmd - waagent.Log( cmd) - code,output=RunGetOutput(cmd,False) - print output,code - waagent.Log(str(code)) - waagent.Log(output) - cmd='azure vm show --json ' + vm_name - print cmd - waagent.Log( cmd) - code,output=RunGetOutput(cmd,False) - print output,code - waagent.Log( str(code)) - waagent.Log(output) - retries=3 - while code != 0 and retries: - retries-=1 - print cmd - waagent.Log( cmd) - code,output=RunGetOutput(cmd,False) - - if code == 0: - jsn=json.loads(output) - for i in jsn['DataDisks']: - if i['DiskName'] == di_name: - if 'Lun' in i : - return i['Lun'] - else : - return u'0' - return None - - -def dropDiskImageFromVM(vm_name,lun): - """ - Detach the disk image from the 'stableVM'. - On Error we assume the disk is no longer attached. - """ - cmd='azure vm disk detach --json ' + vm_name + ' ' + lun - print cmd - waagent.Log( cmd) - code,output=RunGetOutput(cmd,False) - print output,code - waagent.Log( str(code)) - waagent.Log(output) - return output,code - -def checkVMProvisioned(vm_name): - cmd='azure vm show --json ' + vm_name - print cmd - waagent.Log( cmd) - code,output=RunGetOutput(cmd,False) - print output,code - waagent.Log( str(code)) - waagent.Log(output) - if code ==0 : - j=json.loads(output) - print vm_name+' instance status: ', j['InstanceStatus'] - waagent.Log( vm_name+' instance status: ' + j['InstanceStatus']) - if j['InstanceStatus'] == 'ReadyRole': - return True, j['InstanceStatus'] - else : - print 'Error: ' + output , code - waagent.Log( 'Error: ' + output + str(code)) - return False, j['InstanceStatus'] - -def updateAgent(agent_path,vm_name,account,cert,disk_mountpoint,mnt_opts,lun,partnum,provisioned_account): - """ - Copy the agent specified in 'agent' to the Disk - using the 'stableVM'. - """ - retries=30 - retry=0 - cmd='uptime' - while ssh_command(vm_name,account,cmd)[1] != 0 and retry < retries : - time.sleep(10) - retry+=1 - #setup sudo NOPASSWD - pss=stableVMpass.replace('$','\$') - cmd='echo \'' + pss + '\' > /home/' + account + '/pswd ' - ssh_command(vm_name,account,cmd) - cmd='echo \'#!/bin/bash\ncat /home/' + account + '/pswd\n\' > /home/' + account + '/pw.sh' - ssh_command(vm_name,account,cmd) - cmd='chmod +x /home/' + account + '/pw.sh' - ssh_command(vm_name,account,cmd) - cmd='export SUDO_ASKPASS=./pw.sh && sudo -A mkdir -p ' + disk_mountpoint - ssh_command(vm_name,account,cmd) - retries=3 - # TODO retires here for the mount - #mount - cmd='export SUDO_ASKPASS=./pw.sh && sudo -A mount ' +mnt_opts + ' ' + lunToDiskName(lun,partnum) + ' ' +disk_mountpoint - waagent.Log( cmd) - retry=0 - while ssh_command(vm_name,account,cmd)[1] not in (0,32) and retry < retries : - if retry == 0: - if 'bsd' in fstype: - fcmd = "export SUDO_ASKPASS=./pw.sh && sudo -A fsck_ffs -y " - else : - fcmd = "export SUDO_ASKPASS=./pw.sh && sudo -A fsck -y " - fcmd += lunToDiskName(lun,partnum) - ssh_command(vm_name,account,fcmd) - time.sleep(2) - retry+=1 - - # remove packaged agent service if present. - cmd='export SUDO_ASKPASS=./pw.sh && sudo -A chroot '+ disk_mountpoint+' dpkg -r walinuxagent' - ssh_command(vm_name,account,cmd) # remove Ubuntu walinuxagent agent service if present. - cmd='export SUDO_ASKPASS=./pw.sh && sudo -A rm '+ disk_mountpoint+'/etc/default/walinuxagent' - ssh_command(vm_name,account,cmd) # remove Ubuntu walinuxagent agent service if present. - cmd='export SUDO_ASKPASS=./pw.sh && sudo -A chroot '+ disk_mountpoint+' rpm -e WALinuxAgent' - ssh_command(vm_name,account,cmd) - #copy agent - remote_path='/tmp' - print 'scp ' + agent_path + ' to ' + vm_name + ' ' + account + ':' + remote_path - waagent.Log( 'scp ' + agent_path + ' to ' + vm_name + ' ' + account + ':' + remote_path) - retry=0 - while scp_to_host_command(account,vm_name,remote_path,agent_path)[1] != 0 and retry < retries : - time.sleep(2) - retry+=1 - # move agent to /usr/sbin - cmd= 'export SUDO_ASKPASS=./pw.sh && sudo -A cp ' + remote_path +'/waagent '+ disk_mountpoint+'/usr/sbin/waagent' - ssh_command(vm_name,account,cmd) - cmd= 'export SUDO_ASKPASS=./pw.sh && sudo -A chmod 755 '+ disk_mountpoint+'/usr/sbin/waagent' - ssh_command(vm_name,account,cmd) - # Fix the password file - if 'bsd' in fstype: - cmd='export SUDO_ASKPASS=./pw.sh && sudo -A cp /etc/master.passwd ' + disk_mountpoint + '/etc/master.passwd' - ssh_command(vm_name,account,cmd) - else : - cmd='export SUDO_ASKPASS=./pw.sh && sudo -A cp /etc/passwd ' + disk_mountpoint + '/etc/passwd' - ssh_command(vm_name,account,cmd) - cmd='export SUDO_ASKPASS=./pw.sh && sudo -A cp /etc/shadow ' + disk_mountpoint + '/etc/shadow' - ssh_command(vm_name,account,cmd) - #remove /var/lib/waagent - cmd='export SUDO_ASKPASS=./pw.sh && sudo -A rm -rf ' + disk_mountpoint + '/var/lib/waagent' - ssh_command(vm_name,account,cmd) - #remove /var/log/waagent* - cmd='export SUDO_ASKPASS=./pw.sh && sudo -A rm -rf ' + disk_mountpoint + '/var/log/waagent*' - ssh_command(vm_name,account,cmd) - #delete the provisioning user - if 'bsd' in fstype: - cmd='export SUDO_ASKPASS=./pw.sh && sudo -A chroot '+ disk_mountpoint+' rmuser -y ' + provisioned_account - ssh_command(vm_name,account,cmd) - else : - cmd='export SUDO_ASKPASS=./pw.sh && sudo -A chroot '+ disk_mountpoint+' userdel -f ' + provisioned_account - ssh_command(vm_name,account,cmd) - cmd='export SUDO_ASKPASS=./pw.sh && sudo -A chroot '+ disk_mountpoint+' groupdel ' + provisioned_account - ssh_command(vm_name,account,cmd) - cmd='export SUDO_ASKPASS=./pw.sh && sudo -A rm -rf ' + disk_mountpoint + '/home/' + provisioned_account - ssh_command(vm_name,account,cmd) - # install agent - cmd='export SUDO_ASKPASS=./pw.sh && sudo -A chroot '+ disk_mountpoint+' /usr/sbin/waagent verbose install ' - ssh_command(vm_name,account,cmd) - cmd="export SUDO_ASKPASS=./pw.sh && sudo -A sed -i 's/Verbose=n/Verbose=y/' " + disk_mountpoint+"/etc/waagent.conf" - ssh_command(vm_name,account,cmd) - #umount - cmd='export SUDO_ASKPASS=./pw.sh && sudo -A umount ' + lunToDiskName(lun,partnum) - ssh_command(vm_name,account,cmd) - -def gatherAgentInfo(localpath,vm_name,account,cert,disk_mountpoint,mnt_opts,lun,partnum): - """ - Copy the /var/lib/waagent, and /var/log directories to - localhost:localpath. - """ - retries=30 - retry=0 - cmd='uptime' - while ssh_command(vm_name,account,cmd)[1] != 0 and retry < retries : - time.sleep(10) - retry+=1 - #mount - cmd='export SUDO_ASKPASS=./pw.sh && sudo -A mount ' +mnt_opts + ' ' + lunToDiskName(lun,partnum) + ' ' +disk_mountpoint - print cmd - waagent.Log( cmd) - ssh_command(vm_name,account,cmd) - #copy info - Run("mkdir -p "+ localpath) - cmd='export SUDO_ASKPASS=./pw.sh && sudo -A mkdir -p /tmp/results' - ssh_command(vm_name,account,cmd) - cmd='export SUDO_ASKPASS=./pw.sh && sudo -A cp -r ' + disk_mountpoint + '/var/log /tmp/results/' - ssh_command(vm_name,account,cmd) - cmd='export SUDO_ASKPASS=./pw.sh && sudo -A cp -r ' + disk_mountpoint + '/var/lib/waagent /tmp/results/' - ssh_command(vm_name,account,cmd) - cmd='export SUDO_ASKPASS=./pw.sh && sudo -A chmod -R 777 /tmp/results' - ssh_command(vm_name,account,cmd) - cmd='export SUDO_ASKPASS=./pw.sh && sudo -A chown -R ' + account + ' /tmp/results' - ssh_command(vm_name,account,cmd) - scp_from_host_command(account,vm_name,'/tmp/results/*',localpath) - #umount - cmd='export SUDO_ASKPASS=./pw.sh && sudo -A umount ' + lunToDiskName(lun,partnum) - print cmd - waagent.Log( cmd) - ssh_command(vm_name,account,cmd) - -def lunToDiskName(lun,partnum): - if 'bsd' in fstype : - return lunToFreeBSDDiskName(lun,partnum) - else : - return lunToScsiDiskName(lun,partnum) - -def lunToScsiDiskName(lun,partnum): - """ - Convert lun to '/dev/sd[chr(ord('c')+lun)]partnum' - """ - return str('/dev/sd'+chr( (ord('c')+int(lun)) ) +str(partnum)) - -def lunToFreeBSDDiskName(lun,partnum): - """ - Convert lun to '/dev/da' + str(lun) + 'p' + partnum - """ - return '/dev/da'+ str(int(lun)) + 'p' + str(partnum) - -def ssh_command(host,account,cmd): - """ - Wrapper for an ssh operation. - """ - if stableVMCert == None: - if not os.path.exists('./pw.sh'): - with open('./pw.sh','w') as F: - F.write('#!/bin/bash\ncat ./pswd\n') - os.system('chmod +x ./pw.sh') - with open('./pswd','w') as F: - F.write(stableVMpass) - req = "export SSH_ASKPASS=./pw.sh && setsid ssh -T -o StrictHostKeyChecking='no' " + account + "@" + host.lower() + ".cloudapp.net \"" + cmd + "\"" - else : - req = "ssh -t -o StrictHostKeyChecking='no' " + account + "@" + host.lower() + ".cloudapp.net \"" + cmd + "\"" - print req - waagent.Log(req) - code,output=RunGetOutput(req,False) - print output,code - waagent.Log(str(code)) - waagent.Log(output.encode('ascii','ignore')) - return output,code - -def scp_to_host_command(account,host,remote_path,local_path): - """ - Wrapper for an scp operation. Always uses -r. - Requires key authentication configured. - """ - req="scp -o StrictHostKeyChecking='no' -r " + local_path + " " + account + "@" + host.lower() + ".cloudapp.net:" + remote_path - print req - waagent.Log( req) - code,output=RunGetOutput(req,False) - print output,code - waagent.Log( str(code)) - waagent.Log(output) - return output,code - -def scp_from_host_command(account,host,remote_path,local_path): - """ - Wrapper for an scp operation. Always uses -r. - Requires key authentication configured. - """ - req="scp -r " + account + "@" + host.lower() + ".cloudapp.net:" + remote_path + " " + local_path - print req - waagent.Log( req) - code,output=RunGetOutput(req,False) - print output,code - waagent.Log( str(code)) - waagent.Log(output) - return output,code - -def teardown(name): - diskImageName=os.path.splitext(os.path.basename(sourceVHD))[0]+'-di' - while makeDiskImage(diskImageName,sourceVHD,location,True)[1] !=0 : - time.sleep(20) - lun=addDiskImageToVM(stableVM,diskImageName) - while lun == None : - time.sleep(2) - lun=addDiskImageToVM(stableVM,diskImageName) - out,code=flushVM(vmName,True) - if code != 0 : - vmDisk=out[out.find('disk with name ')+len('disk with name '):out.find(' is currently in use')] - while flushDiskImage(vmDisk,True)[1] != 0 : - time.sleep(5) - out,code=flushVMImage(vmImageName) - if code != 0 : - vmDisk=out[out.find('disk with name ')+len('disk with name '):out.find(' is currently in use')] - while flushDiskImage(vmDisk,True)[1] != 0 : - time.sleep(5) - gatherAgentInfo(localInfo+'/'+name,stableVM,stableVMaccount,stableVMCert,stableVMMountpoint,mountOptions,lun,partNum) - print 'Logs for ' + vmName + ' copied to ' + localInfo + '/' + name - waagent.Log( 'Logs for ' + vmName + ' copied to ' + localInfo + '/' + name) - # detach and delete the disk image - while dropDiskImageFromVM(stableVM,lun)[1] != 0 : - time.sleep(2) - while flushDiskImage(diskImageName,('no' in keep_vhd))[1] != 0 : - time.sleep(2) - out,code=flushVM(stableVM,True) - if code != 0 : - stableVMDisk=out[out.find('disk with name ')+len('disk with name '):out.find(' is currently in use')] - while flushDiskImage(stableVMDisk,True)[1] != 0 : - time.sleep(5) - if stableVMImageName: - while flushVMImage(stableVMImageName,True)[1] != 0 : - time.sleep(2) - -def doPrompt() : - if prompt in ('yes','on'): - raw_input('Press enter to continue:') - -if __name__ == '__main__' : - """ - Create a disk image and attach it to StableVM. - Copy the current sources to it. - Detach and delete the disk image container - Delete the vm image container and the VM. - Create a vm image container, and the VM. - Check the VM for provision succedded: - if so, exit - if provisioning failed: - delete the vm, delete the vm image, create a disk image - from the VM's vhd, attach the disk to the stable VM, copy - /var/log and /var/lib/waagent to the localhost. - """ - stableVM='' - mountOptions='' - stableVMMountpoint='/mnt/disk' # need to ensure this is created if not existing. - sourceVHD='' - location="" - localAgent='/home/ericg/Public/git_repos/private/WALinuxAgent-Private/waagent_freebsd' - localInfo='./logs' - stableVMaccount='' # Need to ensure root permissions for this to work - stableVMpass='' - stableVMCert='' - provisionedVMaccount='' - provisionedVMpass='' - provisionedVMCert='' - account='' - testname='azuretest' - partNum=1 - provision_retries=1 - fstype='scsi' - keep_vhd='no' - teardown_test_vm='fail' - teardown_stable_vm='fail' - prompt = 'yes' - stable_vm_vhd=None - stableVMImageName=None - stable_vm_image=None - logfile='azure_test.log' - """ - We need to create a disk image container and attach it to a stable - vm in order to copy the current sources to it. Then we detach it, - delete the disk image container, create a vm image container, and - the VM. - """ - - for i in range(len(sys.argv)) : - if '--stable_vm' == sys.argv[i] : stableVM=sys.argv[i+1] - elif '--source_disk' == sys.argv[i]: sourceVHD=sys.argv[i+1] - elif '--storage_acct' == sys.argv[i]: account=sys.argv[i+1] - elif '--testname' == sys.argv[i] : testname=sys.argv[i+1] - elif '--stable_vm_mount_point' == sys.argv[i] : stableVMMountpoint=sys.argv[i+1] - elif '--agent_path' == sys.argv[i] : localAgent=sys.argv[i+1] - elif '--stable_vm_acct_name' == sys.argv[i] : stableVMaccount=sys.argv[i+1] - elif '--stable_vm_acct_pass' == sys.argv[i] : stableVMpass=sys.argv[i+1] - elif '--stable_vm_acct_cert' == sys.argv[i] : stableVMCert=sys.argv[i+1] - elif '--test_vm_acct_name' == sys.argv[i] : provisionedVMaccount=sys.argv[i+1] - elif '--test_vm_acct_pass' == sys.argv[i] : provisionedVMpass=sys.argv[i+1] - elif '--test_vm_acct_cert' == sys.argv[i] : provisionedVMCert=sys.argv[i+1] - elif '--azure_location' == sys.argv[i] : location=sys.argv[i+1] - elif '--mount_opts' == sys.argv[i] : mountOptions=sys.argv[i+1] - elif '--part_num' == sys.argv[i] : partNum=sys.argv[i+1] - elif '--retries' == sys.argv[i] : provision_retries=int(sys.argv[i+1]) - elif '--fs_type' == sys.argv[i] : fstype=sys.argv[i+1] - elif '--keep_test_vm_vhd' == sys.argv[i] : keep_vhd=sys.argv[i+1] - elif '--teardown_test_vm' == sys.argv[i] : teardown_test_vm=sys.argv[i+1] - elif '--teardown_stable_vm' == sys.argv[i] : teardown_stable_vm=sys.argv[i+1] - elif '--prompt' == sys.argv[i] : prompt=sys.argv[i+1] - elif '--stable_vm_image' == sys.argv[i] : stable_vm_image=sys.argv[i+1] - elif '--stable_vm_vhd' == sys.argv[i] : stable_vm_vhd=sys.argv[i+1] - elif '--logfile' == sys.argv[i] : logfile=sys.argv[i+1] - - LoggerInit(logfile,'') - waagent.Log("User: "+ pwd.getpwuid(os.geteuid()).pw_name +" Running Command :\n" + reduce(lambda x, y: x+' '+y,sys.argv)) - - if len(stableVM) == 0 and not ( stable_vm_image or stable_vm_vhd ): - print '--vm <stable vm> must be provided unless --stable_vm_image or --stable_vm_vhd' - waagent.Log( '--vm <stable vm> must be provided!') - sys.exit(1) - else: - if stable_vm_image: - sourceVHD=createStableVMFromVMImage(stable_vm_image,sourceVHD) - stableVM=testname+'-stable-vm' - elif stable_vm_vhd: - stableVMImageName=testname+'-stable-vi' - sourceVHD=createStableVMFromVHD(stableVMImageName,stable_vm_vhd) - stableVM=testname+'-stable-vm' - p = False - retries = provision_retries - while not p and retries > 0: - p,out = checkVMProvisioned(stableVM) - if not p: - if 'Failed' in out or 'Timeout' in out : - break - print stableVM + ' Not Provisioned - sleeping on retry:' + str( provision_retries - retries ) - waagent.Log( stableVM + ' Not Provisioned - sleeping on retry:' + str( provision_retries - retries ) ) - time.sleep(30) - retries -= 1 - else : - print stableVM + ' Provision SUCCEEDED.' - waagent.Log( stableVM + ' Provision SUCCEEDED.') - # done creating the stable vm - vmImageName=os.path.splitext(os.path.basename(sourceVHD))[0]+'-vi' - #flushVMImage(vmImageName) - - # if no disk image name is provided we want to clone the stable vm disk. - if not sourceVHD: - sourceVHD=createDiskImageFromStableVMDisk(stableVM) - if not sourceVHD: - print 'Errors - unable to create disk image - assuming created' - waagent.Log( 'Errors - unable to create disk image - assuming created') - diskImageName=os.path.splitext(os.path.basename(sourceVHD))[0] - else : - diskImageName=os.path.splitext(os.path.basename(sourceVHD))[0]+'-di' - diskImageVHD,code=makeDiskImage(diskImageName,sourceVHD,location,True) - if code: - print 'Error - unable to make ' + diskImageName - waagent.Log( 'Error - unable to make ' + diskImageName) - - lun=addDiskImageToVM(stableVM,diskImageName) - while lun == None : - time.sleep(2) - lun=addDiskImageToVM(stableVM,diskImageName) - - doPrompt() - updateAgent(localAgent,stableVM,stableVMaccount,stableVMCert,stableVMMountpoint,mountOptions,lun,partNum,provisionedVMaccount) - doPrompt() - #reboot to prevent stale mount bugs - cmd= 'export SUDO_ASKPASS=./pw.sh && sudo -A reboot' - ssh_command(stableVM,stableVMaccount,cmd) - - while dropDiskImageFromVM(stableVM,lun)[1] != 0 : - time.sleep(2) - while flushDiskImage(diskImageName,False)[1] != 0 : - time.sleep(2) - vmImageName=os.path.splitext(os.path.basename(sourceVHD))[0]+'-vi' - flushVMImage(vmImageName) - vmName=testname+'-vm' - flushVM(vmName) - makeVMImage(vmImageName,diskImageVHD,True) - sourceVHD=makeVM(vmName,vmImageName,sourceVHD,testname,location,provisionedVMaccount,provisionedVMpass,provisionedVMCert,True) - print 'The new source vhd is ' + sourceVHD - waagent.Log( 'The new source vhd is ' + sourceVHD) - p = False - retries = provision_retries - while not p and retries > 0: - p,out = checkVMProvisioned(vmName) - if not p: - if 'Failed' in out or 'Timeout' in out : - break - print vmName + ' Not Provisioned - sleeping on retry:' + str( provision_retries - retries ) - waagent.Log( vmName + ' Not Provisioned - sleeping on retry:' + str( provision_retries - retries ) ) - time.sleep(30) - else : - print vmName + ' Provision SUCCEEDED.' - waagent.Log( vmName + ' Provision SUCCEEDED.') - doPrompt() - if teardown_test_vm in ('success','always'): - teardown(testname+'_pass') - sys.exit(0) - retries -= 1 - - print vmName + ' Provision FAILED.' - waagent.Log( vmName + ' Provision FAILED.') - doPrompt() - if teardown_test_vm in ('fail','always'): - teardown(testname+'_fail') - sys.exit(1) diff --git a/tests/dhcp b/tests/dhcp Binary files differnew file mode 100644 index 0000000..8c9d127 --- /dev/null +++ b/tests/dhcp diff --git a/tests/env.py b/tests/env.py index 513df7f..5bc6eb8 100644 --- a/tests/env.py +++ b/tests/env.py @@ -12,14 +12,15 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# Requires Python 2.4+ and Openssl 1.0+ +# +# Implements parts of RFC 2131, 1541, 1497 and +# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx +# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx -import imp import os import sys -project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -waagent = imp.load_source('waagent', os.path.join(project_root, 'waagent')) +test_root = os.path.dirname(os.path.abspath(__file__)) +project_root = os.path.dirname(test_root) sys.path.insert(0, project_root) - -waagent.LoggerInit('/dev/stdout', '/dev/null') - diff --git a/tests/run_all.sh b/tests/run_all.sh new file mode 100755 index 0000000..9e88c46 --- /dev/null +++ b/tests/run_all.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# +# This script is used to set up a test env for extensions +# +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +script=$(dirname $0) +root=$script +cd $root +root=`pwd` + +echo "Run unit test:" +tests=`ls test_*.py | sed -e 's/\.py//'` +for test in $tests ; do + echo $test +done + +if [ "$1" == "-c" ] ; then + echo $tests | xargs coverage run -m unittest +else + echo $tests | xargs python -m unittest +fi diff --git a/tests/sshd_config b/tests/sshd_config new file mode 100644 index 0000000..77fb290 --- /dev/null +++ b/tests/sshd_config @@ -0,0 +1,90 @@ +# Package generated configuration file +# See the sshd_config(5) manpage for details + +# What ports, IPs and protocols we listen for +Port 22 +# Use these options to restrict which interfaces/protocols sshd will bind to +#ListenAddress :: +#ListenAddress 0.0.0.0 +Protocol 2 +# HostKeys for protocol version 2 +HostKey /etc/ssh/ssh_host_rsa_key +HostKey /etc/ssh/ssh_host_dsa_key +HostKey /etc/ssh/ssh_host_ecdsa_key +HostKey /etc/ssh/ssh_host_ed25519_key +#Privilege Separation is turned on for security +UsePrivilegeSeparation yes + +# Lifetime and size of ephemeral version 1 server key +KeyRegenerationInterval 3600 +ServerKeyBits 1024 + +# Logging +SyslogFacility AUTH +LogLevel INFO + +# Authentication: +LoginGraceTime 120 +PermitRootLogin without-password +StrictModes yes + +RSAAuthentication yes +PubkeyAuthentication yes +#AuthorizedKeysFile %h/.ssh/authorized_keys + +# Don't read the user's ~/.rhosts and ~/.shosts files +IgnoreRhosts yes +# For this to work you will also need host keys in /etc/ssh_known_hosts +RhostsRSAAuthentication no +# similar for protocol version 2 +HostbasedAuthentication no +# Uncomment if you don't trust ~/.ssh/known_hosts for RhostsRSAAuthentication +#IgnoreUserKnownHosts yes + +# To enable empty passwords, change to yes (NOT RECOMMENDED) +PermitEmptyPasswords no + +# Change to yes to enable challenge-response passwords (beware issues with +# some PAM modules and threads) +ChallengeResponseAuthentication no + +# Change to no to disable tunnelled clear text passwords +#PasswordAuthentication yes + +# Kerberos options +#KerberosAuthentication no +#KerberosGetAFSToken no +#KerberosOrLocalPasswd yes +#KerberosTicketCleanup yes + +# GSSAPI options +#GSSAPIAuthentication no +#GSSAPICleanupCredentials yes + +X11Forwarding yes +X11DisplayOffset 10 +PrintMotd no +PrintLastLog yes +TCPKeepAlive yes +#UseLogin no + +#MaxStartups 10:30:60 +#Banner /etc/issue.net + +# Allow client to pass locale environment variables +AcceptEnv LANG LC_* + +Subsystem sftp /usr/lib/openssh/sftp-server + +# Set this to 'yes' to enable PAM authentication, account processing, +# and session processing. If this is enabled, PAM authentication will +# be allowed through the ChallengeResponseAuthentication and +# PasswordAuthentication. Depending on your PAM configuration, +# PAM authentication via ChallengeResponseAuthentication may bypass +# the setting of "PermitRootLogin without-password". +# If you just want the PAM account and session checks to run without +# PAM authentication, then enable this but set PasswordAuthentication +# and ChallengeResponseAuthentication to 'no'. +UsePAM yes + +Match group root diff --git a/tests/test.crt b/tests/test.crt new file mode 100644 index 0000000..e87c5f9 --- /dev/null +++ b/tests/test.crt @@ -0,0 +1,17 @@ +Bag Attributes: <Empty Attributes> +subject=/C=ab/ST=ab/L=ab/O=ab/OU=ab/CN=ab/emailAddress=ab +issuer=/C=ab/ST=ab/L=ab/O=ab/OU=ab/CN=ab/emailAddress=ab +-----BEGIN CERTIFICATE----- +MIICOTCCAaICCQD7F0nb+GtpcTANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQGEwJh +YjELMAkGA1UECAwCYWIxCzAJBgNVBAcMAmFiMQswCQYDVQQKDAJhYjELMAkGA1UE +CwwCYWIxCzAJBgNVBAMMAmFiMREwDwYJKoZIhvcNAQkBFgJhYjAeFw0xNDA4MDUw +ODIwNDZaFw0xNTA4MDUwODIwNDZaMGExCzAJBgNVBAYTAmFiMQswCQYDVQQIDAJh +YjELMAkGA1UEBwwCYWIxCzAJBgNVBAoMAmFiMQswCQYDVQQLDAJhYjELMAkGA1UE +AwwCYWIxETAPBgkqhkiG9w0BCQEWAmFiMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB +iQKBgQC4Vugyj4uAKGYHW/D1eAg1DmLAv01e+9I0zIi8HzJxP87MXmS8EdG5SEzR +N6tfQQie76JBSTYI4ngTaVCKx5dVT93LiWxLV193Q3vs/HtwwH1fLq0rAKUhREQ6 ++CsRGNyeVfJkNsxAvNvQkectnYuOtcDxX5n/25eWAofobxVbSQIDAQABMA0GCSqG +SIb3DQEBCwUAA4GBAF20gkq/DeUSXkZA+jjmmbCPioB3KL63GpoTXfP65d6yU4xZ +TlMoLkqGKe3WoXmhjaTOssulgDAGA24IeWy/u7luH+oHdZEmEufFhj4M7tQ1pAhN +CT8JCL2dI3F76HD6ZutTOkwRar3PYk5q7RsSJdAemtnwVpgp+RBMtbmct7MQ +-----END CERTIFICATE----- diff --git a/tests/test.prv b/tests/test.prv new file mode 100644 index 0000000..9d6ab87 --- /dev/null +++ b/tests/test.prv @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQC4Vugyj4uAKGYHW/D1eAg1DmLAv01e+9I0zIi8HzJxP87MXmS8 +EdG5SEzRN6tfQQie76JBSTYI4ngTaVCKx5dVT93LiWxLV193Q3vs/HtwwH1fLq0r +AKUhREQ6+CsRGNyeVfJkNsxAvNvQkectnYuOtcDxX5n/25eWAofobxVbSQIDAQAB +AoGAIakE506c238E+m0Id9o+LWn+EFIeT6zN+oQqp6dOr61GFr1ZyZm7YQjZtg5j +RZZ7e4Iob6Fts3ufD3RYl67QbBzRwsKwI7sAmzdCmqkopY2H6xv421cEGjkqZIJV +2Xyp9Idji6GfUB6+t1SZDOssbZx3SUkyim0hixK2HCJT4u0CQQDw6rNLZwEmwuhY +z1jSERyeTtIcRJ47+Y79tX2xmkyKxZ2Kf28V3Fw/6biCIlmuvxHNhlLijimOME7/ +rkqDiscnAkEAw+FpkM96xLlDCqNL2AcNxVnmNyO0Boxw0AKrogfcnDh6S3rD5tZQ +IdcIAsEYNjhEJ+/hVCByIUArC885PTzQDwJBAMaDfm3ZWHeKD05uvG+MLhq8NCGa +4Q/mWU7xZ7sau4t1vpTK4MwQoesAOUrx5xg41QCXeGC6Z7+ESvQft8Kgbe0CQAkS +OExPf3T6y2MDuvBvKzEXf7TP/3dKK7NGXGJtkMbfSrKSJd5b0GwwxBs0jAV+x5E9 +56Z4tjBaA2RRnWn7lfsCQA5SWuDMtlOzyWir09fparnnRL1JFvOwDAHTE0iwS8dO +UFHIIw4nqqUYuHb+r/eyRzVtokJ9bSPZOjtTWSVL4W4= +-----END RSA PRIVATE KEY----- diff --git a/tests/test_agent.py b/tests/test_agent.py new file mode 100644 index 0000000..721b9f0 --- /dev/null +++ b/tests/test_agent.py @@ -0,0 +1,45 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# +# Implements parts of RFC 2131, 1541, 1497 and +# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx +# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx + +import tests.env +import tests.tools as tools +import uuid +import unittest +import os +import json +import azurelinuxagent.utils.fileutil as fileutil +import azurelinuxagent.agent as agent + +class TestAgent(unittest.TestCase): + def test_parse_args(self): + cmd, force, verbose = agent.parse_args(["deprovision+user", + "-force", + "/verbose"]) + self.assertEquals("deprovision+user", cmd) + self.assertTrue(force) + self.assertTrue(verbose) + + cmd, force, verbose = agent.parse_args(["wrong cmd"]) + self.assertEquals("help", cmd) + self.assertFalse(force) + self.assertFalse(verbose) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_certificates.py b/tests/test_certificates.py new file mode 100644 index 0000000..2950c6b --- /dev/null +++ b/tests/test_certificates.py @@ -0,0 +1,198 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# +# Implements parts of RFC 2131, 1541, 1497 and +# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx +# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx + +import tests.env +from .tools import * +import uuid +import unittest +import os +import json +import azurelinuxagent.utils.fileutil as fileutil +import azurelinuxagent.protocol.v1 as v1 + +certs_sample=u"""\ +<?xml version="1.0" encoding="utf-8"?> +<CertificateFile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="certificates10.xsd"> + <Version>2012-11-30</Version> + <Incarnation>12</Incarnation> + <Format>Pkcs7BlobWithPfxContents</Format> + <Data>MIINswYJKoZIhvcNAQcDoIINpDCCDaACAQIxggEwMIIBLAIBAoAUvyL+x6GkZXog +QNfsXRZAdD9lc7IwDQYJKoZIhvcNAQEBBQAEggEArhMPepD/RqwdPcHEVqvrdZid +72vXrOCuacRBhwlCGrNlg8oI+vbqmT6CSv6thDpet31ALUzsI4uQHq1EVfV1+pXy +NlYD1CKhBCoJxs2fSPU4rc8fv0qs5JAjnbtW7lhnrqFrXYcyBYjpURKfa9qMYBmj +NdijN+1T4E5qjxPr7zK5Dalp7Cgp9P2diH4Nax2nixotfek3MrEFBaiiegDd+7tE +ux685GWYPqB5Fn4OsDkkYOdb0OE2qzLRrnlCIiBCt8VubWH3kMEmSCxBwSJupmQ8 +sxCWk+sBPQ9gJSt2sIqfx/61F8Lpu6WzP+ZOnMLTUn2wLU/d1FN85HXmnQALzTCC +DGUGCSqGSIb3DQEHATAUBggqhkiG9w0DBwQIbEcBfddWPv+AggxAAOAt/kCXiffe +GeJG0P2K9Q18XZS6Rz7Xcz+Kp2PVgqHKRpPjjmB2ufsRO0pM4z/qkHTOdpfacB4h +gz912D9U04hC8mt0fqGNTvRNAFVFLsmo7KXc/a8vfZNrGWEnYn7y1WfP52pqA/Ei +SNFf0NVtMyqg5Gx+hZ/NpWAE5vcmRRdoYyWeg13lhlW96QUxf/W7vY/D5KpAGACI +ok79/XI4eJkbq3Dps0oO/difNcvdkE74EU/GPuL68yR0CdzzafbLxzV+B43TBRgP +jH1hCdRqaspjAaZL5LGfp1QUM8HZIKHuTze/+4dWzS1XR3/ix9q/2QFI7YCuXpuE +un3AFYXE4QX/6kcPklZwh9FqjSie3I5HtC1vczqYVjqT4oHrs8ktkZ7oAzeXaXTF +k6+JQNNa/IyJw24I1MR77q7HlHSSfhXX5cFjVCd/+SiA4HJQjJgeIuXZ+dXmSPdL +9xLbDbtppifFyNaXdlSzcsvepKy0WLF49RmbL7Bnd46ce/gdQ6Midwi2MTnUtapu +tHmu/iJtaUpwXXC0B93PHfAk7Y3SgeY4tl/gKzn9/x5SPAcHiNRtOsNBU8ZThzos +Wh41xMLZavmX8Yfm/XWtl4eU6xfhcRAbJQx7E1ymGEt7xGqyPV7hjqhoB9i3oR5N +itxHgf1+jw/cr7hob+Trd1hFqZO6ePMyWpqUg97G2ThJvWx6cv+KRtTlVA6/r/UH +gRGBArJKBlLpXO6dAHFztT3Y6DFThrus4RItcfA8rltfQcRm8d0nPb4lCa5kRbCx +iudq3djWtTIe64sfk8jsc6ahWYSovM+NmhbpxEUbZVWLVEcHAYOeMbKgXSu5sxNO +JZNeFdzZqDRRY9fGjYNS7DdNOmrMmWKH+KXuMCItpNZsZS/3W7QxAo3ugYLdUylU +Zg8H/BjUGZCGn1rEBAuQX78m0SZ1xHlgHSwJIOmxOJUDHLPHtThfbELY9ec14yi5 +so1aQwhhfhPvF+xuXBrVeTAfhFNYkf2uxcEp7+tgFAc5W0QfT9SBn5vSvIxv+dT4 +7B2Pg1l/zjdsM74g58lmRJeDoz4psAq+Uk7n3ImBhIku9qX632Q1hanjC8D4xM4W +sI/W0ADCuAbY7LmwMpAMdrGg//SJUnBftlom7C9VA3EVf8Eo+OZH9hze+gIgUq+E +iEUL5M4vOHK2ttsYrSkAt8MZzjQiTlDr1yzcg8fDIrqEAi5arjTPz0n2s0NFptNW +lRD+Xz6pCXrnRgR8YSWpxvq3EWSJbZkSEk/eOmah22sFnnBZpDqn9+UArAznXrRi +nYK9w38aMGPKM39ymG8kcbY7jmDZlRgGs2ab0Fdj1jl3CRo5IUatkOJwCEMd/tkB +eXLQ8hspJhpFnVNReX0oithVZir+j36epk9Yn8d1l+YlKmuynjunKl9fhmoq5Q6i +DFzdYpqBV+x9nVhnmPfGyrOkXvGL0X6vmXAEif/4JoOW4IZpyXjgn+VoCJUoae5J +Djl45Bcc2Phrn4HW4Gg/+pIwTFqqZZ2jFrznNdgeIxTGjBrVsyJUeO3BHI0mVLaq +jtjhTshYCI7mXOis9W3ic0RwE8rgdDXOYKHhLVw9c4094P/43utSVXE7UzbEhhLE +Ngb4H5UGrQmPTNbq40tMUMUCej3zIKuVOvamzeE0IwLhkjNrvKhCG1EUhX4uoJKu +DQ++3KVIVeYSv3+78Jfw9F3usAXxX1ICU74/La5DUNjU7DVodLDvCAy5y1jxP3Ic +If6m7aBYVjFSQAcD8PZPeIEl9W4ZnbwyBfSDd11P2a8JcZ7N99GiiH3yS1QgJnAO +g9XAgjT4Gcn7k4lHPHLULgijfiDSvt94Ga4/hse0F0akeZslVN/bygyib7x7Lzmq +JkepRianrvKHbatuxvcajt/d+dxCnr32Q1qCEc5fcgDsjvviRL2tKR0qhuYjn1zR +Vk/fRtYOmlaGBVzUXcjLRAg3gC9+Gy8KvXIDrnHxD+9Ob+DUP9fgbKqMeOzKcCK8 +NSfSQ+tQjBYD5Ku4zAPUQJoRGgx43vXzcl2Z2i3E2otpoH82Kx8S9WlVEUlTtBjQ +QIGM5aR0QUNt8z34t2KWRA8SpP54VzBmEPdwLnzna+PkrGKsKiHVn4K+HfjDp1uW +xyO8VjrolAOYosTPXMpNp2u/FoFxaAPTa/TvmKc0kQ3ED9/sGLS2twDnEccvHP+9 +zzrnzzN3T2CWuXveDpuyuAty3EoAid1nuC86WakSaAZoa8H2QoRgsrkkBCq+K/yl +4FO9wuP+ksZoVq3mEDQ9qv6H4JJEWurfkws3OqrA5gENcLmSUkZie4oqAxeOD4Hh +Zx4ckG5egQYr0PnOd2r7ZbIizv3MKT4RBrfOzrE6cvm9bJEzNWXdDyIxZ/kuoLA6 +zX7gGLdGhg7dqzKqnGtopLAsyM1b/utRtWxOTGO9K9lRxyX82oCVT9Yw0DwwA+cH +Gutg1w7JHrIAYEtY0ezHgxhqMGuuTyJMX9Vr0D+9DdMeBK7hVOeSnxkaQ0f9HvF6 +0XI/2OTIoBSCBpUXjpgsYt7m7n2rFJGJmtqgLAosCAkacHnHLwX0EnzBw3sdDU6Q +jFXUWIDd5xUsNkFDCbspLMFs22hjNI6f/GREwd23Q4ujF8pUIcxcfbs2myjbK45s +tsn/jrkxmKRgwCIeN/H7CM+4GXSkEGLWbiGCxWzWt9wW1F4M7NW9nho3D1Pi2LBL +1ByTmjfo/9u9haWrp53enDLJJbcaslfe+zvo3J70Nnzu3m3oJ3dmUxgJIstG10g3 +lhpUm1ynvx04IFkYJ3kr/QHG/xGS+yh/pMZlwcUSpjEgYFmjFHU4A1Ng4LGI4lnw +5wisay4J884xmDgGfK0sdVQyW5rExIg63yYXp2GskRdDdwvWlFUzPzGgCNXQU96A +ljZfjs2u4IiVCC3uVsNbGqCeSdAl9HC5xKuPNbw5yTxPkeRL1ouSdkBy7rvdFaFf +dMPw6sBRNW8ZFInlgOncR3+xT/rZxru87LCq+3hRN3kw3hvFldrW2QzZSksO759b +pJEP+4fxuG96Wq25fRmzHzE0bdJ+2qF3fp/hy4oRi+eVPa0vHdtkymE4OUFWftb6 ++P++JVOzZ4ZxYA8zyUoJb0YCaxL+Jp/QqiUiH8WZVmYZmswqR48sUUKr7TIvpNbY +6jEH6F7KiZCoWfKH12tUC69iRYx3UT/4Bmsgi3S4yUxfieYRMIwihtpP4i0O+OjB +/DPbb13qj8ZSfXJ+jmF2SRFfFG+2T7NJqm09JvT9UcslVd+vpUySNe9UAlpcvNGZ +2+j180ZU7YAgpwdVwdvqiJxkeVtAsIeqAvIXMFm1PDe7FJB0BiSVZdihB6cjnKBI +dv7Lc1tI2sQe7QSfk+gtionLrEnto+aXF5uVM5LMKi3gLElz7oXEIhn54OeEciB1 +cEmyX3Kb4HMRDMHyJxqJXwxm88RgC6RekoPvstu+AfX/NgSpRj5beaj9XkweJT3H +rKWhkjq4Ghsn1LoodxluMMHd61m47JyoqIP9PBKoW+Na0VUKIVHw9e9YeW0nY1Zi +5qFA/pHPAt9AbEilRay6NEm8P7TTlNo216amc8byPXanoNrqBYZQHhZ93A4yl6jy +RdpYskMivT+Sh1nhZAioKqqTZ3HiFR8hFGspAt5gJc4WLYevmxSicGa6AMyhrkvG +rvOSdjY6JY/NkxtcgeycBX5MLF7uDbhUeqittvmlcrVN6+V+2HIbCCrvtow9pcX9 +EkaaNttj5M0RzjQxogCG+S5TkhCy04YvKIkaGJFi8xO3icdlxgOrKD8lhtbf4UpR +cDuytl70JD95mSUWL53UYjeRf9OsLRJMHQOpS02japkMwCb/ngMCQuUXA8hGkBZL +Xw7RwwPuM1Lx8edMXn5C0E8UK5e0QmI/dVIl2aglXk2oBMBJbnyrbfUPm462SG6u +ke4gQKFmVy2rKICqSkh2DMr0NzeYEUjZ6KbmQcV7sKiFxQ0/ROk8eqkYYxGWUWJv +ylPF1OTLH0AIbGlFPLQO4lMPh05yznZTac4tmowADSHY9RCxad1BjBeine2pj48D +u36OnnuQIsedxt5YC+h1bs+mIvwMVsnMLidse38M/RayCDitEBvL0KeG3vWYzaAL +h0FCZGOW0ilVk8tTF5+XWtsQEp1PpclvkcBMkU3DtBUnlmPSKNfJT0iRr2T0sVW1 +h+249Wj0Bw== +</Data> +</CertificateFile> +""" + +transport_cert=u"""\ +-----BEGIN CERTIFICATE----- +MIIDBzCCAe+gAwIBAgIJANujJuVt5eC8MA0GCSqGSIb3DQEBCwUAMBkxFzAVBgNV +BAMMDkxpbnV4VHJhbnNwb3J0MCAXDTE0MTAyNDA3MjgwN1oYDzIxMDQwNzEyMDcy +ODA3WjAZMRcwFQYDVQQDDA5MaW51eFRyYW5zcG9ydDCCASIwDQYJKoZIhvcNAQEB +BQADggEPADCCAQoCggEBANPcJAkd6V5NeogSKjIeTXOWC5xzKTyuJPt4YZMVSosU +0lI6a0wHp+g2fP22zrVswW+QJz6AVWojIEqLQup3WyCXZTv8RUblHnIjkvX/+J/G +aLmz0G5JzZIpELL2C8IfQLH2IiPlK9LOQH00W74WFcK3QqcJ6Kw8GcVaeSXT1r7X +QcGMqEjcWJkpKLoMJv3LMufE+JMdbXDUGY+Ps7Zicu8KXvBPaKVsc6H2jrqBS8et +jXbzLyrezTUDz45rmyRJzCO5Sk2pohuYg73wUykAUPVxd7L8WnSyqz1v4zrObqnw +BAyor67JR/hjTBfjFOvd8qFGonfiv2Vnz9XsYFTZsXECAwEAAaNQME4wHQYDVR0O +BBYEFL8i/sehpGV6IEDX7F0WQHQ/ZXOyMB8GA1UdIwQYMBaAFL8i/sehpGV6IEDX +7F0WQHQ/ZXOyMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAMPLrimT +Gptu5pLRHPT8OFRN+skNSkepYaUaJuq6cSKxLumSYkD8++rohu+1+a7t1YNjjNSJ +8ohRAynRJ7aRqwBmyX2OPLRpOfyRZwR0rcFfAMORm/jOE6WBdqgYD2L2b+tZplGt +/QqgQzebaekXh/032FK4c74Zg5r3R3tfNSUMG6nLauWzYHbQ5SCdkuQwV0ehGqh5 +VF1AOdmz4CC2237BNznDFQhkeU0LrqqAoE/hv5ih7klJKZdS88rOYEnVJsFFJb0g +qaycXjOm5Khgl4hKrd+DBD/qj4IVVzsmdpFli72k6WLBHGOXusUGo/3isci2iAIt +DsfY6XGSEIhZnA4= +-----END CERTIFICATE----- +""" + +transport_private=u"""\ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDT3CQJHeleTXqI +EioyHk1zlguccyk8riT7eGGTFUqLFNJSOmtMB6foNnz9ts61bMFvkCc+gFVqIyBK +i0Lqd1sgl2U7/EVG5R5yI5L1//ifxmi5s9BuSc2SKRCy9gvCH0Cx9iIj5SvSzkB9 +NFu+FhXCt0KnCeisPBnFWnkl09a+10HBjKhI3FiZKSi6DCb9yzLnxPiTHW1w1BmP +j7O2YnLvCl7wT2ilbHOh9o66gUvHrY128y8q3s01A8+Oa5skScwjuUpNqaIbmIO9 +8FMpAFD1cXey/Fp0sqs9b+M6zm6p8AQMqK+uyUf4Y0wX4xTr3fKhRqJ34r9lZ8/V +7GBU2bFxAgMBAAECggEBAM4hsfog3VAAyIieS+npq+gbhH6bWfMNaTQ3g5CNNbMu +9hhFeOJHzKnWYjSlamgBQhAfTN+2E+Up+iAtcVUZ/lMumrQLlwgMo1vgmvu5Kxmh +/YE5oEG+k0JzrCjD1trwd4zvc3ZDYyk/vmVTzTOc311N248UyArUiyqHBbq1a4rP +tJhCLn2c4S7flXGF0MDVGZyV9V7J8N8leq/dRGMB027Li21T+B4mPHXa6b8tpRPL +4vc8sHoUJDa2/+mFDJ2XbZfmlgd3MmIPlRn1VWoW7mxgT/AObsPl7LuQx7+t80Wx +hIMjuKUHRACQSLwHxJ3SQRFWp4xbztnXSRXYuHTscLUCgYEA//Uu0qIm/FgC45yG +nXtoax4+7UXhxrsWDEkbtL6RQ0TSTiwaaI6RSQcjrKDVSo/xo4ZySTYcRgp5GKlI +CrWyNM+UnIzTNbZOtvSIAfjxYxMsq1vwpTlOB5/g+cMukeGg39yUlrjVNoFpv4i6 +9t4yYuEaF4Vww0FDd2nNKhhW648CgYEA0+UYH6TKu03zDXqFpwf4DP2VoSo8OgfQ +eN93lpFNyjrfzvxDZkGF+7M/ebyYuI6hFplVMu6BpgpFP7UVJpW0Hn/sXkTq7F1Q +rTJTtkTp2+uxQVP/PzSOqK0Twi5ifkfoEOkPkNNtTiXzwCW6Qmmcvln2u893pyR5 +gqo5BHR7Ev8CgYAb7bXpN9ZHLJdMHLU3k9Kl9YvqOfjTxXA3cPa79xtEmsrTys4q +4HuL22KSII6Fb0VvkWkBAg19uwDRpw78VC0YxBm0J02Yi8b1AaOhi3dTVzFFlWeh +r6oK/PAAcMKxGkyCgMAZ3hstsltGkfXMoBwhW+yL6nyOYZ2p9vpzAGrjkwKBgQDF +0huzbyXVt/AxpTEhv07U0enfjI6tnp4COp5q8zyskEph8yD5VjK/yZh5DpmFs6Kw +dnYUFpbzbKM51tToMNr3nnYNjEnGYVfwWgvNHok1x9S0KLcjSu3ki7DmmGdbfcYq +A2uEyd5CFyx5Nr+tQOwUyeiPbiFG6caHNmQExLoiAQKBgFPy9H8///xsadYmZ18k +r77R2CvU7ArxlLfp9dr19aGYKvHvnpsY6EuChkWfy8Xjqn3ogzgrHz/rn3mlGUpK +vbtwtsknAHtTbotXJwfaBZv2RGgGRr3DzNo6ll2Aez0lNblZFXq132h7+y5iLvar +4euORaD/fuM4UPlR5mN+bypU +-----END PRIVATE KEY----- +""" + +def MockGetOpensslCmd(): + return 'openssl' + +class TestCertificates(unittest.TestCase): + + def test_certificates(self): + crt1 = '/tmp/33B0ABCE4673538650971C10F7D7397E71561F35.crt' + crt2 = '/tmp/4037FBF5F1F3014F99B5D6C7799E9B20E6871CB3.crt' + prv2 = '/tmp/4037FBF5F1F3014F99B5D6C7799E9B20E6871CB3.prv' + os.chdir('/tmp') + if os.path.isfile(crt1): + os.remove(crt1) + if os.path.isfile(crt2): + os.remove(crt2) + if os.path.isfile(prv2): + os.remove(prv2) + fileutil.write_file(os.path.join('/tmp', "TransportCert.pem"), + transport_cert) + fileutil.write_file(os.path.join('/tmp', "TransportPrivate.pem"), + transport_private) + config = v1.Certificates(certs_sample) + self.assertNotEquals(None, config) + self.assertTrue(os.path.isfile(crt1)) + self.assertTrue(os.path.isfile(crt2)) + self.assertTrue(os.path.isfile(prv2)) + + self.assertNotEquals(0, len(config.cert_list.certificates)) + cert = config.cert_list.certificates[0] + self.assertNotEquals(None, cert.thumbprint) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_conf.py b/tests/test_conf.py new file mode 100644 index 0000000..204e4f5 --- /dev/null +++ b/tests/test_conf.py @@ -0,0 +1,69 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# +# Implements parts of RFC 2131, 1541, 1497 and +# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx +# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx + +import os +import tests.env +import uuid +import unittest +import tests.tools as tools +import azurelinuxagent.utils.fileutil as fileutil +import azurelinuxagent.conf as conf +from azurelinuxagent.exception import * + +TestConf="""\ +# +# This is comment +# +foo.bar.switch=y +foo.bar.switch2=n +foo.bar.str=foobar +foo.bar.int=300 + +""" + +class TestConfiguration(unittest.TestCase): + def test_parse_conf(self): + config = conf.ConfigurationProvider() + config.load(TestConf) + self.assertEquals(True, config.get_switch("foo.bar.switch")) + self.assertEquals(False, config.get_switch("foo.bar.switch2")) + self.assertEquals(False, config.get_switch("foo.bar.switch3")) + self.assertEquals(True, config.get_switch("foo.bar.switch4", True)) + self.assertEquals("foobar", config.get("foo.bar.str")) + self.assertEquals("foobar1", config.get("foo.bar.str1", "foobar1")) + self.assertEquals(300, config.get_int("foo.bar.int")) + self.assertEquals(-1, config.get_int("foo.bar.int2")) + self.assertEquals(-1, config.get_int("foo.bar.str")) + + def test_parse_malformed_conf(self): + config = conf.ConfigurationProvider() + self.assertRaises(AgentConfigError, config.load, None) + + def test_load_conf_file(self): + with open('/tmp/test_conf', 'w') as F: + F.write(TestConf) + F.close() + + config = conf.ConfigurationProvider() + conf.load_conf('/tmp/test_conf', conf=config) + self.assertEquals(True, config.get_switch("foo.bar.switch"), False) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_deprovision.py b/tests/test_deprovision.py new file mode 100644 index 0000000..8bad6b9 --- /dev/null +++ b/tests/test_deprovision.py @@ -0,0 +1,54 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# +# Implements parts of RFC 2131, 1541, 1497 and +# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx +# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx + +import tests.env +from tests.tools import * +import unittest +import azurelinuxagent.distro.default.deprovision as deprovision_handler + +def MockAction(param): + #print param + pass + +def MockSetup(self, deluser): + warnings = ["Print warning to console"] + actions = [ + deprovision_handler.DeprovisionAction(MockAction, ['Take action']) + ] + return warnings, actions + +class TestDeprovisionHandler(unittest.TestCase): + def test_setup(self): + handler = deprovision_handler.DeprovisionHandler() + warnings, actions = handler.setup(False) + self.assertNotEquals(None, warnings) + self.assertNotEquals(0, len(warnings)) + self.assertNotEquals(None, actions) + self.assertNotEquals(0, len(actions)) + self.assertEquals(deprovision_handler.DeprovisionAction, type(actions[0])) + + + @mock(deprovision_handler.DeprovisionHandler, 'setup', MockSetup) + def test_deprovision(self): + handler = deprovision_handler.DeprovisionHandler() + handler.deprovision(force=True) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_dhcp.py b/tests/test_dhcp.py new file mode 100644 index 0000000..2206325 --- /dev/null +++ b/tests/test_dhcp.py @@ -0,0 +1,68 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# +# Implements parts of RFC 2131, 1541, 1497 and +# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx +# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx + +import tests.env as env +from tests.tools import * +import uuid +import unittest +import os +import json +import azurelinuxagent.utils.fileutil as fileutil +import azurelinuxagent.distro.default.dhcp as dhcp_handler + +SampleDhcpResponse = None +with open(os.path.join(env.test_root, "dhcp"), 'rb') as F: + SampleDhcpResponse = F.read() + +mock_socket_send = MockFunc('socket_send', SampleDhcpResponse) +mock_gen_trans_id = MockFunc('gen_trans_id', "\xC6\xAA\xD1\x5D") +mock_get_mac_addr = MockFunc('get_mac_addr', "\x00\x15\x5D\x38\xAA\x38") +mock_send_dhcp_failed = MockFunc(retval=None) + +class TestdhcpHandler(unittest.TestCase): + + def test_build_dhcp_req(self): + req = dhcp_handler.build_dhcp_request(mock_get_mac_addr()) + self.assertNotEquals(None, req) + + @mock(dhcp_handler, "gen_trans_id", mock_gen_trans_id) + @mock(dhcp_handler, "socket_send", mock_socket_send) + def test_send_dhcp_req(self): + req = dhcp_handler.build_dhcp_request(mock_get_mac_addr()) + resp = dhcp_handler.send_dhcp_request(req) + self.assertNotEquals(None, resp) + + @mock(dhcp_handler, "send_dhcp_request", mock_send_dhcp_failed) + def test_send_dhcp_failed(self): + dhcp = dhcp_handler.DhcpHandler() + dhcp.probe() + + @mock(dhcp_handler, "socket_send", mock_socket_send) + @mock(dhcp_handler, "gen_trans_id", mock_gen_trans_id) + @mock(dhcp_handler.OSUTIL, "get_mac_addr", mock_get_mac_addr) + @mock(dhcp_handler.fileutil, "write_file", MockFunc()) + def test_handle_dhcp(self): + dh = dhcp_handler.DhcpHandler() + dh.probe() + self.assertEquals("10.62.144.1", dh.gateway) + self.assertEquals("10.62.144.140", dh.endpoint) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_distroLoader.py b/tests/test_distroLoader.py new file mode 100644 index 0000000..16987c5 --- /dev/null +++ b/tests/test_distroLoader.py @@ -0,0 +1,42 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# +# Implements parts of RFC 2131, 1541, 1497 and +# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx +# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx + +import tests.env +from tests.tools import * +import unittest +from azurelinuxagent.utils.osutil import OSUTIL, OSUtilError +from azurelinuxagent.handler import HANDLERS +import azurelinuxagent.distro.default.osutil as osutil + +class TestDistroLoader(unittest.TestCase): + def test_loader(self): + self.assertNotEquals(osutil.DefaultOSUtil, type(OSUTIL)) + self.assertNotEquals(None, HANDLERS.init_handler) + self.assertNotEquals(None, HANDLERS.main_handler) + self.assertNotEquals(None, HANDLERS.scvmm_handler) + self.assertNotEquals(None, HANDLERS.dhcp_handler) + self.assertNotEquals(None, HANDLERS.env_handler) + self.assertNotEquals(None, HANDLERS.provision_handler) + self.assertNotEquals(None, HANDLERS.resource_disk_handler) + self.assertNotEquals(None, HANDLERS.env_handler) + self.assertNotEquals(None, HANDLERS.deprovision_handler) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_envmon.py b/tests/test_envmon.py new file mode 100644 index 0000000..74b61ee --- /dev/null +++ b/tests/test_envmon.py @@ -0,0 +1,52 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# +# Implements parts of RFC 2131, 1541, 1497 and +# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx +# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx + +import tests.env +from tests.tools import * +import unittest +import time +from azurelinuxagent.future import text +from azurelinuxagent.utils.osutil import OSUTIL +from azurelinuxagent.distro.default.env import EnvMonitor + +class MockDhcpHandler(object): + def conf_routes(self): + pass + +def mock_get_dhcp_pid(): + return "1234" + +def mock_dhcp_pid_change(): + return text(time.time()) + +class TestEnvMonitor(unittest.TestCase): + + @mock(OSUTIL, 'get_dhcp_pid', mock_get_dhcp_pid) + def test_dhcp_pid_not_change(self): + monitor = EnvMonitor(MockDhcpHandler()) + monitor.handle_dhclient_restart() + + @mock(OSUTIL, 'get_dhcp_pid', mock_dhcp_pid_change) + def test_dhcp_pid_change(self): + monitor = EnvMonitor(MockDhcpHandler()) + monitor.handle_dhclient_restart() + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_event.py b/tests/test_event.py new file mode 100644 index 0000000..fcf67c9 --- /dev/null +++ b/tests/test_event.py @@ -0,0 +1,53 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# +# Implements parts of RFC 2131, 1541, 1497 and +# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx +# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx + +import tests.env +from .tools import * +import uuid +import unittest +import os +import shutil +import azurelinuxagent.utils.fileutil as fileutil +import azurelinuxagent.event as evt +import azurelinuxagent.protocol as prot + +class MockProtocol(object): + def get_vminfo(self): + return prot.VMInfo(subscriptionId='foo', vmName='bar') + def report_event(self, data): pass + +class TestEvent(unittest.TestCase): + def test_save(self): + if not os.path.exists("/tmp/events"): + os.mkdir("/tmp/events") + evt.add_event("Test", "Test", True) + eventsFile = os.listdir("/tmp/events") + self.assertNotEquals(0, len(eventsFile)) + shutil.rmtree("/tmp/events") + + @mock(evt.prot.FACTORY, 'get_default_protocol', + MockFunc(retval=MockProtocol())) + def test_init_sys_info(self): + monitor = evt.EventMonitor() + monitor.init_sysinfo() + self.assertNotEquals(0, len(monitor.sysinfo)) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_ext.py b/tests/test_ext.py new file mode 100644 index 0000000..126c0ec --- /dev/null +++ b/tests/test_ext.py @@ -0,0 +1,178 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# +# Implements parts of RFC 2131, 1541, 1497 and +# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx +# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx + +import tests.env +from tests.tools import * +import uuid +import unittest +import os +import json +import azurelinuxagent.logger as logger +from azurelinuxagent.utils.osutil import OSUTIL +import azurelinuxagent.utils.fileutil as fileutil +import azurelinuxagent.protocol as prot +import azurelinuxagent.distro.default.extension as ext + +ext_sample_json = { + "name":"TestExt", + "properties":{ + "version":"2.0", + "state":"enabled", + "upgradePolicy":"auto", + "extensions":[{ + "sequenceNumber": 0, + "publicSettings": "", + "protectedSettings": "", + "certificateThumbprint": "" + }], + "versionUris":[{ + "version":"2.1", + "uris":["http://foo.bar"] + },{ + "version":"2.0", + "uris":["http://foo.bar"] + }] + } +} +ext_sample = prot.Extension() +prot.set_properties(ext_sample, ext_sample_json) + +pkd_list_sample_str={ + "versions": [{ + "version": "2.0", + "uris":[{ + "uri":"http://foo.bar" + }] + },{ + "version": "2.1", + "uris":[{ + "uri":"http://foo.bar" + }] + }] +} +pkg_list_sample = prot.ExtensionPackageList() +prot.set_properties(pkg_list_sample, pkd_list_sample_str) + +manifest_sample_str = { + "handlerManifest":{ + "installCommand": "echo 'install'", + "uninstallCommand": "echo 'uninstall'", + "updateCommand": "echo 'update'", + "enableCommand": "echo 'enable'", + "disableCommand": "echo 'disable'", + } +} +manifest_sample = ext.HandlerManifest(manifest_sample_str) + +def mock_load_manifest(self): + return manifest_sample + +mock_launch_command = MockFunc() +mock_set_handler_status = MockFunc() + +def mock_download(self): + fileutil.mkdir(self.get_base_dir()) + fileutil.write_file(self.get_manifest_file(), json.dumps(manifest_sample_str)) + +#logger.LoggerInit("/dev/null", "/dev/stdout") +class TestExtensions(unittest.TestCase): + + def test_load_ext(self): + libDir = OSUTIL.get_lib_dir() + test_ext1 = os.path.join(libDir, 'TestExt-1.0') + test_ext2 = os.path.join(libDir, 'TestExt-2.0') + test_ext2 = os.path.join(libDir, 'TestExt-2.1') + for path in [test_ext1, test_ext2]: + if not os.path.isdir(path): + os.mkdir(path) + test_ext = ext.get_installed_version('TestExt') + self.assertEqual('2.1', test_ext) + + def test_getters(self): + test_ext = ext.ExtensionInstance(ext_sample, pkg_list_sample, + ext_sample.properties.version, False) + self.assertEqual("/tmp/TestExt-2.0", test_ext.get_base_dir()) + self.assertEqual("/tmp/TestExt-2.0/status", test_ext.get_status_dir()) + self.assertEqual("/tmp/TestExt-2.0/status/0.status", + test_ext.get_status_file()) + self.assertEqual("/tmp/TestExt-2.0/config/HandlerState", + test_ext.get_handler_state_file()) + self.assertEqual("/tmp/TestExt-2.0/config", test_ext.get_conf_dir()) + self.assertEqual("/tmp/TestExt-2.0/config/0.settings", + test_ext.get_settings_file()) + self.assertEqual("/tmp/TestExt-2.0/heartbeat.log", + test_ext.get_heartbeat_file()) + self.assertEqual("/tmp/TestExt-2.0/HandlerManifest.json", + test_ext.get_manifest_file()) + self.assertEqual("/tmp/TestExt-2.0/HandlerEnvironment.json", + test_ext.get_env_file()) + self.assertEqual("/tmp/log/TestExt/2.0", test_ext.get_log_dir()) + + test_ext = ext.ExtensionInstance(ext_sample, pkg_list_sample, "2.1", False) + self.assertEqual("/tmp/TestExt-2.1", test_ext.get_base_dir()) + self.assertEqual("2.1", test_ext.get_target_version()) + + @mock(ext.ExtensionInstance, 'load_manifest', mock_load_manifest) + @mock(ext.ExtensionInstance, 'launch_command', mock_launch_command) + @mock(ext.ExtensionInstance, 'set_handler_status', mock_set_handler_status) + def test_handle_uninstall(self): + mock_launch_command.args = None + mock_set_handler_status.args = None + test_ext = ext.ExtensionInstance(ext_sample, pkg_list_sample, + ext_sample.properties.version, False) + test_ext.handle_uninstall() + self.assertEqual(None, mock_launch_command.args) + self.assertEqual(None, mock_set_handler_status.args) + self.assertEqual(None, test_ext.get_curr_op()) + + test_ext = ext.ExtensionInstance(ext_sample, pkg_list_sample, + ext_sample.properties.version, True) + test_ext.handle_uninstall() + self.assertEqual(manifest_sample.get_uninstall_command(), mock_launch_command.args[0]) + self.assertEqual("UnInstall", test_ext.get_curr_op()) + self.assertEqual("NotReady", mock_set_handler_status.args[0]) + + @mock(ext.ExtensionInstance, 'load_manifest', mock_load_manifest) + @mock(ext.ExtensionInstance, 'launch_command', mock_launch_command) + @mock(ext.ExtensionInstance, 'download', mock_download) + @mock(ext.ExtensionInstance, 'get_handler_status', MockFunc(retval="enabled")) + @mock(ext.ExtensionInstance, 'set_handler_status', mock_set_handler_status) + def test_handle(self): + #Test enable + test_ext = ext.ExtensionInstance(ext_sample, pkg_list_sample, + ext_sample.properties.version, False) + test_ext.init_logger() + self.assertEqual(1, len(test_ext.logger.appenders) - len(logger.DEFAULT_LOGGER.appenders)) + test_ext.handle() + + #Test upgrade + test_ext = ext.ExtensionInstance(ext_sample, pkg_list_sample, + ext_sample.properties.version, False) + test_ext.init_logger() + self.assertEqual(1, len(test_ext.logger.appenders) - len(logger.DEFAULT_LOGGER.appenders)) + test_ext.handle() + + def test_status_convert(self): + ext_status = json.loads('[{"status": {"status": "success", "formattedMessage": {"lang": "en-US", "message": "Script is finished"}, "operation": "Enable", "code": "0", "name": "Microsoft.OSTCExtensions.CustomScriptForLinux"}, "version": "1.0", "timestampUTC": "2015-06-27T08:34:50Z"}]') + ext.ext_status_to_v2(ext_status[0], 0) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_extensionsconfig.py b/tests/test_extensionsconfig.py new file mode 100644 index 0000000..4dbf30d --- /dev/null +++ b/tests/test_extensionsconfig.py @@ -0,0 +1,159 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# +# Implements parts of RFC 2131, 1541, 1497 and +# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx +# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx + +import tests.env +import tests.tools as tools +import uuid +import unittest +import os +import json +import azurelinuxagent.protocol.v1 as v1 + +ext_conf_sample=u"""\ +<Extensions version="1.0.0.0" goalStateIncarnation="9"><GuestAgentExtension xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> + <GAFamilies> + <GAFamily> + <Name>Win8</Name> + <Uris> + <Uri>http://rdfepirv2hknprdstr03.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri> + <Uri>http://rdfepirv2hknprdstr04.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri> + <Uri>http://rdfepirv2hknprdstr05.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri> + <Uri>http://rdfepirv2hknprdstr06.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri> + <Uri>http://rdfepirv2hknprdstr07.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri> + <Uri>http://rdfepirv2hknprdstr08.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri> + <Uri>http://rdfepirv2hknprdstr09.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri> + <Uri>http://rdfepirv2hknprdstr10.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri> + <Uri>http://rdfepirv2hknprdstr11.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri> + <Uri>http://rdfepirv2hknprdstr12.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri> + <Uri>http://zrdfepirv2hk2prdstr01.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win8_asiaeast_manifest.xml</Uri> + </Uris> + </GAFamily> + <GAFamily> + <Name>Win7</Name> + <Uris> + <Uri>http://rdfepirv2hknprdstr03.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri> + <Uri>http://rdfepirv2hknprdstr04.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri> + <Uri>http://rdfepirv2hknprdstr05.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri> + <Uri>http://rdfepirv2hknprdstr06.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri> + <Uri>http://rdfepirv2hknprdstr07.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri> + <Uri>http://rdfepirv2hknprdstr08.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri> + <Uri>http://rdfepirv2hknprdstr09.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri> + <Uri>http://rdfepirv2hknprdstr10.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri> + <Uri>http://rdfepirv2hknprdstr11.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri> + <Uri>http://rdfepirv2hknprdstr12.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri> + <Uri>http://zrdfepirv2hk2prdstr01.blob.core.windows.net/bfd5c281a7dc4e4b84381eb0b47e3aaf/Microsoft.WindowsAzure.GuestAgent_Win7_asiaeast_manifest.xml</Uri> + </Uris> + </GAFamily> + </GAFamilies> +</GuestAgentExtension> +<Plugins> + <Plugin name="OSTCExtensions.ExampleHandlerLinux" version="1.4" location="http://rdfepirv2hknprdstr03.blob.core.windows.net/b01058962be54ceca550a390fa5ff064/Microsoft.OSTCExtensions_CustomScriptForLinuxTest_asiaeast_manifest.xml" config="" state="enabled" autoUpgrade="true" failoverlocation="http://rdfepirv2hknprdstr04.blob.core.windows.net/b01058962be54ceca550a390fa5ff064/Microsoft.OSTCExtensions_CustomScriptForLinuxTest_asiaeast_manifest.xml" runAsStartupTask="false" isJson="true" /> +</Plugins> +<PluginSettings> + <Plugin name="OSTCExtensions.ExampleHandlerLinux" version="1.4"> + <RuntimeSettings seqNo="6">{"runtimeSettings":[{"handlerSettings":{"protectedSettingsCertThumbprint":"4037FBF5F1F3014F99B5D6C7799E9B20E6871CB3","protectedSettings":"MIICWgYJK","publicSettings":{"foo":"bar"}}}]}</RuntimeSettings> + </Plugin> +</PluginSettings> +<StatusUploadBlob>https://yuezhatest.blob.core.windows.net/vhds/test-cs12.test-cs12.test-cs12.status?sr=b&sp=rw&se=9999-01-01&sk=key1&sv=2014-02-14&sig=hfRh7gzUE7sUtYwke78IOlZOrTRCYvkec4hGZ9zZzXo%3D</StatusUploadBlob></Extensions> +""" + +manifest_sample=u"""\ +<?xml version="1.0" encoding="utf-8"?> +<PluginVersionManifest xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> + <Plugins> + <Plugin> + <Version>1.0</Version> + <Uris> + <Uri>http://blahblah</Uri> + </Uris> + </Plugin> + <Plugin> + <Version>1.1</Version> + <Uris> + <Uri>http://blahblah</Uri> + </Uris> + </Plugin> + </Plugins> + <InternalPlugins> + <Plugin> + <Version>1.2</Version> + <Uris> + <Uri>http://blahblah</Uri> + </Uris> + </Plugin> +</InternalPlugins> +</PluginVersionManifest> +""" + +EmptySettings=u"""\ +<Extensions> + <Plugins> + <Plugin name="OSTCExtensions.ExampleHandlerLinux" version="1.4" location="http://rdfepirv2hknprdstr03.blob.core.windows.net/b01058962be54ceca550a390fa5ff064/Microsoft.OSTCExtensions_CustomScriptForLinuxTest_asiaeast_manifest.xml" config="" state="enabled" autoUpgrade="true" failoverlocation="http://rdfepirv2hknprdstr04.blob.core.windows.net/b01058962be54ceca550a390fa5ff064/Microsoft.OSTCExtensions_CustomScriptForLinuxTest_asiaeast_manifest.xml" runAsStartupTask="false" isJson="true" /> + </Plugins> + <StatusUploadBlob>https://yuezhatest.blob.core.windows.net/vhds/test-cs12.test-cs12.test-cs12.status?sr=b&sp=rw&se=9999-01-01&sk=key1&sv=2014-02-14&sig=hfRh7gzUE7sUtYwke78IOlZOrTRCYvkec4hGZ9zZzXo%3D</StatusUploadBlob> +</Extensions> +""" + +EmptyPublicSettings=u"""\ +<Extensions> + <Plugins> + <Plugin name="OSTCExtensions.ExampleHandlerLinux" version="1.4" location="http://rdfepirv2hknprdstr03.blob.core.windows.net/b01058962be54ceca550a390fa5ff064/Microsoft.OSTCExtensions_CustomScriptForLinuxTest_asiaeast_manifest.xml" config="" state="enabled" autoUpgrade="true" failoverlocation="http://rdfepirv2hknprdstr04.blob.core.windows.net/b01058962be54ceca550a390fa5ff064/Microsoft.OSTCExtensions_CustomScriptForLinuxTest_asiaeast_manifest.xml" runAsStartupTask="false" isJson="true" /> + </Plugins> + <PluginSettings> + <Plugin name="OSTCExtensions.ExampleHandlerLinux" version="1.4"> + <RuntimeSettings seqNo="6">{"runtimeSettings":[{"handlerSettings":{"protectedSettingsCertThumbprint":"4037FBF5F1F3014F99B5D6C7799E9B20E6871CB3","protectedSettings":"MIICWgYJK"}}]}</RuntimeSettings> + </Plugin> + </PluginSettings> + <StatusUploadBlob>https://yuezhatest.blob.core.windows.net/vhds/test-cs12.test-cs12.test-cs12.status?sr=b&sp=rw&se=9999-01-01&sk=key1&sv=2014-02-14&sig=hfRh7gzUE7sUtYwke78IOlZOrTRCYvkec4hGZ9zZzXo%3D</StatusUploadBlob> +</Extensions> +""" + +class TestExtensionsConfig(unittest.TestCase): + def test_extensions_config(self): + config = v1.ExtensionsConfig(ext_conf_sample) + extensions = config.ext_list.extensions + self.assertNotEquals(None, extensions) + self.assertEquals(1, len(extensions)) + self.assertNotEquals(None, extensions[0]) + extension = extensions[0] + self.assertEquals("OSTCExtensions.ExampleHandlerLinux", + extension.name) + self.assertEquals("1.4", extension.properties.version) + self.assertEquals('auto', extension.properties.upgradePolicy) + self.assertEquals("enabled", extension.properties.state) + settings = extension.properties.extensions[0] + self.assertEquals("4037FBF5F1F3014F99B5D6C7799E9B20E6871CB3", + settings.certificateThumbprint) + self.assertEquals("MIICWgYJK", settings.privateSettings) + self.assertEquals(json.loads('{"foo":"bar"}'), + settings.publicSettings) + + man = v1.ExtensionManifest(manifest_sample) + self.assertNotEquals(None, man.pkg_list) + self.assertEquals(3, len(man.pkg_list.versions)) + + def test_empty_settings(self): + config = v1.ExtensionsConfig(EmptySettings) + + def test_empty_public_settings(self): + config = v1.ExtensionsConfig(EmptyPublicSettings) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_file_util.py b/tests/test_file_util.py new file mode 100644 index 0000000..1eeb784 --- /dev/null +++ b/tests/test_file_util.py @@ -0,0 +1,75 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# +# Implements parts of RFC 2131, 1541, 1497 and +# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx +# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx + +import tests.env +import tests.tools as tools +import uuid +import unittest +import os +import sys +from azurelinuxagent.future import text +import azurelinuxagent.utils.fileutil as fileutil + +class TestFileOperations(unittest.TestCase): + def test_read_write_file(self): + test_file='/tmp/test_file' + content = text(uuid.uuid4()) + fileutil.write_file(test_file, content) + self.assertTrue(tools.simple_file_grep(test_file, content)) + + content_read = fileutil.read_file('/tmp/test_file') + self.assertEquals(content, content_read) + os.remove(test_file) + + def test_rw_utf8_file(self): + test_file='/tmp/test_file3' + content = "\u6211" + fileutil.write_file(test_file, content, encoding="utf-8") + self.assertTrue(tools.simple_file_grep(test_file, content)) + + content_read = fileutil.read_file('/tmp/test_file3') + self.assertEquals(content, content_read) + os.remove(test_file) + + def test_remove_bom(self): + test_file= '/tmp/test_file4' + data = b'\xef\xbb\xbfhehe' + fileutil.write_file(test_file, data, asbin=True) + data = fileutil.read_file(test_file, remove_bom=True) + self.assertNotEquals(0xbb, ord(data[0])) + + def test_append_file(self): + test_file='/tmp/test_file2' + content = text(uuid.uuid4()) + fileutil.append_file(test_file, content) + self.assertTrue(tools.simple_file_grep(test_file, content)) + os.remove(test_file) + + def test_get_last_path_element(self): + filepath = '/tmp/abc.def' + filename = fileutil.base_name(filepath) + self.assertEquals('abc.def', filename) + + filepath = '/tmp/abc' + filename = fileutil.base_name(filepath) + self.assertEquals('abc', filename) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/part-gpt.py b/tests/test_future.py index 373e1c6..20edc93 100644 --- a/tests/part-gpt.py +++ b/tests/test_future.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python -# # Copyright 2014 Microsoft Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,18 +12,26 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# Requires Python 2.4+ and Openssl 1.0+ +# # Implements parts of RFC 2131, 1541, 1497 and # http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx # http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx -# -import subprocess +import tests.env +from tests.tools import * +import uuid +import unittest +import os +import shutil +import time +import azurelinuxagent.future as future + +class TestFuture(unittest.TestCase): + def test_future_pkgs(self): + future.httpclient + future.urlparse + future.text(b"asdf", encoding="utf-8") if __name__ == '__main__': - subprocess.call(['umount', '/mnt/resource']) - subprocess.call(['umount', '/mnt']) - subprocess.call(['parted', '/dev/sdb', 'print']) - subprocess.call(['parted', '/dev/sdb', 'rm', '1']) - subprocess.call(['parted', '/dev/sdb', 'mklabel', 'gpt']) - subprocess.call(['parted', '/dev/sdb', 'mkpart', 'primary', '0%', '50%']) - subprocess.call(['parted', '/dev/sdb', 'mkpart', 'primary', '50%', '100%']) + unittest.main() diff --git a/tests/test_goalstate.py b/tests/test_goalstate.py new file mode 100644 index 0000000..a18ce8d --- /dev/null +++ b/tests/test_goalstate.py @@ -0,0 +1,71 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# +# Implements parts of RFC 2131, 1541, 1497 and +# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx +# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx + +import tests.env +import tests.tools as tools +import uuid +import unittest +import os +import test +import azurelinuxagent.protocol.v1 as v1 + +goal_state_sample=u"""\ +<?xml version="1.0" encoding="utf-8"?> +<GoalState xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="goalstate10.xsd"> + <Version>2010-12-15</Version> + <Incarnation>1</Incarnation> + <Machine> + <ExpectedState>Started</ExpectedState> + <LBProbePorts> + <Port>16001</Port> + </LBProbePorts> + </Machine> + <Container> + <ContainerId>c6d5526c-5ac2-4200-b6e2-56f2b70c5ab2</ContainerId> + <RoleInstanceList> + <RoleInstance> + <InstanceId>MachineRole_IN_0</InstanceId> + <State>Started</State> + <Configuration> + <HostingEnvironmentConfig>http://hostingenvuri/</HostingEnvironmentConfig> + <SharedConfig>http://sharedconfiguri/</SharedConfig> + <ExtensionsConfig>http://extensionsconfiguri/</ExtensionsConfig> + <FullConfig>http://fullconfiguri/</FullConfig> + </Configuration> + </RoleInstance> + </RoleInstanceList> + </Container> + </GoalState> +""" + +class TestGoalState(unittest.TestCase): + def test_goal_state(self): + goal_state = v1.GoalState(goal_state_sample) + self.assertEquals('1', goal_state.incarnation) + self.assertNotEquals(None, goal_state.expected_state) + self.assertNotEquals(None, goal_state.hosting_env_uri) + self.assertNotEquals(None, goal_state.shared_conf_uri) + self.assertEquals(None, goal_state.certs_uri) + self.assertNotEquals(None, goal_state.ext_uri) + self.assertNotEquals(None, goal_state.role_instance_id) + self.assertNotEquals(None, goal_state.container_id) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_hostingenv.py b/tests/test_hostingenv.py new file mode 100644 index 0000000..3d2ce73 --- /dev/null +++ b/tests/test_hostingenv.py @@ -0,0 +1,66 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# +# Implements parts of RFC 2131, 1541, 1497 and +# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx +# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx + +import tests.env +import tests.tools as tools +import uuid +import unittest +import os +import azurelinuxagent.protocol.v1 as v1 + +hosting_env_sample=u""" + <HostingEnvironmentConfig version="1.0.0.0" goalStateIncarnation="1"> + <StoredCertificates> + <StoredCertificate name="Stored0Microsoft.WindowsAzure.Plugins.RemoteAccess.PasswordEncryption" certificateId="sha1:C093FA5CD3AAE057CB7C4E04532B2E16E07C26CA" storeName="My" configurationLevel="System" /> + </StoredCertificates> + <Deployment name="db00a7755a5e4e8a8fe4b19bc3b330c3" guid="{ce5a036f-5c93-40e7-8adf-2613631008ab}" incarnation="2"> + <Service name="MyVMRoleService" guid="{00000000-0000-0000-0000-000000000000}" /> + <ServiceInstance name="db00a7755a5e4e8a8fe4b19bc3b330c3.1" guid="{d113f4d7-9ead-4e73-b715-b724b5b7842c}" /> + </Deployment> + <Incarnation number="1" instance="MachineRole_IN_0" guid="{a0faca35-52e5-4ec7-8fd1-63d2bc107d9b}" /> + <Role guid="{73d95f1c-6472-e58e-7a1a-523554e11d46}" name="MachineRole" hostingEnvironmentVersion="1" software="" softwareType="ApplicationPackage" entryPoint="" parameters="" settleTimeSeconds="10" /> + <HostingEnvironmentSettings name="full" runtime="rd_fabric_stable.110217-1402.runtimePackage_1.0.0.8.zip"> + <CAS mode="full" /> + <PrivilegeLevel mode="max" /> + <AdditionalProperties><CgiHandlers></CgiHandlers></AdditionalProperties> + </HostingEnvironmentSettings> + <ApplicationSettings> + <Setting name="__ModelData" value="<m role="MachineRole" xmlns="urn:azure:m:v1"><r name="MachineRole"><e name="a" /><e name="b" /><e name="Microsoft.WindowsAzure.Plugins.RemoteAccess.Rdp" /><e name="Microsoft.WindowsAzure.Plugins.RemoteForwarder.RdpInput" /></r></m>" /> + <Setting name="Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString" value="DefaultEndpointsProtocol=http;AccountName=osimages;AccountKey=DNZQ..." /> + <Setting name="Microsoft.WindowsAzure.Plugins.RemoteForwarder.Enabled" value="true" /> + </ApplicationSettings> + <ResourceReferences> + <Resource name="DiagnosticStore" type="directory" request="Microsoft.Cis.Fabric.Controller.Descriptions.ServiceDescription.Data.Policy" sticky="true" size="1" path="db00a7755a5e4e8a8fe4b19bc3b330c3.MachineRole.DiagnosticStore\" disableQuota="false" /> + </ResourceReferences> + </HostingEnvironmentConfig> +""" + +class TestHostingEvn(unittest.TestCase): + def test_hosting_env(self): + hosting_env = v1.HostingEnv(hosting_env_sample) + self.assertNotEquals(None, hosting_env) + self.assertEquals("MachineRole_IN_0", hosting_env.vm_name) + self.assertEquals("MachineRole", hosting_env.role_name) + self.assertEquals("db00a7755a5e4e8a8fe4b19bc3b330c3", + hosting_env.deployment_name) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_http.py b/tests/test_http.py deleted file mode 100644 index eab1382..0000000 --- a/tests/test_http.py +++ /dev/null @@ -1,147 +0,0 @@ -# Copyright 2014 Microsoft Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import unittest -from env import waagent -import sys -from tests.tools import * - -class MockHTTPResponse(object): - def __init__(self, status=200): - self.status = status - self.reason = "foo" - - def getheaders(*args, **kwargs): - return {"hehe" : "haha"} - - def read(*args, **kwargs): - return "bar" - -class MockOldHTTPConnection(object): - MockHost=None - MockPort=None - MockUrl=None - MockCallCount=0 - - def __init__(self, host, port): - self.__class__.MockHost = host - self.__class__.MockPort = port - - def request(self, method, url, data, headers = None): - self.__class__.MockUrl = url - self.__class__.MockCallCount += 1 - - def getresponse(*args, **kwargs): - return MockHTTPResponse() - -class MockHTTPConnection(MockOldHTTPConnection): - def set_tunnel(*args, **kwargs): - pass - -class MockBadHTTPConnection(MockHTTPConnection): - def getresponse(*args, **kwargs): - return MockHTTPResponse(500) - -class MockHttpLib(object): - def __init__(self): - self.HTTPConnection = MockHTTPConnection - self.OK = 200 - -MockOSEnv = { - "http_proxy":"http://httpproxy:8888", - "https_proxy":"https://httpsproxy:8888" -} - -class TestHttp(unittest.TestCase): - - def test_parseurl(self): - httputil = waagent.Util() - host, port, secure, path = httputil._ParseUrl("http://foo:8/bar?hehe") - self.assertEquals("foo", host) - self.assertEquals(8, port) - self.assertEquals(False, secure) - self.assertEquals("/bar?hehe", path) - - host, port, secure, path = httputil._ParseUrl("http://foo.bar/") - self.assertEquals("foo.bar", host) - self.assertEquals(80, port) - self.assertEquals(False, secure) - self.assertEquals("/", path) - - host, port, secure, path= httputil._ParseUrl("https://foo.bar/") - self.assertEquals("foo.bar", host) - self.assertEquals(80, port) - self.assertEquals(True, secure) - self.assertEquals("/", path) - - self.assertRaises(ValueError, httputil._ParseUrl, - "https://a:b@foo.bar/") - - host, port, secure, path = httputil._ParseUrl("https://foo.bar") - self.assertEquals("foo.bar", host) - self.assertEquals(80, port) - self.assertEquals(True, secure) - self.assertEquals("/", path) - - host, port, secure, path = httputil._ParseUrl("http://a:b@foo.bar:8888") - self.assertEquals("a:b@foo.bar", host) - self.assertEquals(8888, port) - self.assertEquals(False, secure) - self.assertEquals("/", path) - - @Mockup(waagent.httplib, "HTTPConnection", MockHTTPConnection) - @Mockup(waagent.os, "environ", MockOSEnv) - def test_http_request(self): - httputil = waagent.Util() - - #If chkProxy is on, host and port should point to proxy server - httputil.HttpRequest("GET", "http://foo.bar/get", chkProxy=True) - self.assertEquals("httpproxy", MockHTTPConnection.MockHost) - self.assertEquals(8888, MockHTTPConnection.MockPort) - self.assertEquals("http://foo.bar:80/get", MockHTTPConnection.MockUrl) - - #If chkProxy is off, ignore proxy - httputil.HttpRequest("GET", "http://foo.bar/get", chkProxy=False) - self.assertEquals("foo.bar", MockHTTPConnection.MockHost) - self.assertEquals(80, MockHTTPConnection.MockPort) - self.assertEquals("/get", MockHTTPConnection.MockUrl) - - @Mockup(waagent, "httplib" , MockHttpLib()) - def test_https_fallback(self): - httputil = waagent.Util() - print "The bellowing warning log is expected:" - httputil.HttpRequest("GET", "https://foo.bar/get") - self.assertEquals("/get", MockHTTPConnection.MockUrl) - - @Mockup(waagent.httplib, "HTTPConnection", MockOldHTTPConnection) - @Mockup(waagent.httplib, "HTTPSConnection", MockOldHTTPConnection) - @Mockup(waagent.os, "environ", MockOSEnv) - def test_https_fallback2(self): - httputil = waagent.Util() - print "The bellowing warning log is expected:" - httputil.HttpRequest("GET", "https://foo.bar/get", chkProxy=True) - self.assertEquals("http://foo.bar:80/get", MockOldHTTPConnection.MockUrl) - - @Mockup(waagent.Util, "RetryWaitingInterval", 0) - @Mockup(waagent.httplib, "HTTPConnection", MockBadHTTPConnection) - def test_retry(self): - httputil = waagent.Util() - MockBadHTTPConnection.MockCallCount=0 - print "The bellowing error log is expected:" - httputil.HttpRequest("GET", "http://foo.bar", chkProxy=False, maxRetry=1) - self.assertEquals(2, MockBadHTTPConnection.MockCallCount) - -if __name__ == '__main__': - unittest.main() diff --git a/tests/upload_status_blob.py b/tests/test_import_waagent.py index 59cb732..ec3f923 100644 --- a/tests/upload_status_blob.py +++ b/tests/test_import_waagent.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python -# # Copyright 2014 Microsoft Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,30 +12,29 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# Requires Python 2.4+ and Openssl 1.0+ +# # Implements parts of RFC 2131, 1541, 1497 and # http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx # http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx -# +import tests.env +import tests.tools as tools import os -from env import waagent -""" -To run the test, you need to create a file under the same directory called: - status_blob_url.py -and defined the following 2 variables like: - blockBlobUrl="<sas link to a block blob with w/r access>" - pageBlobUrl="<sas link to a page blob with w/r access>" -""" -from status_blob_url import blockBlobUrl, pageBlobUrl - -class MockConfig(object): - def get(self, keyName): - return None +import imp +import sys +import uuid +import unittest -waagent.Config = MockConfig() +class TestImportWAAgent(unittest.TestCase): + def test_import_waagent(self): + agent_path = os.path.join(tools.parent, 'bin/waagent') + if sys.version_info[0] == 2: + waagent = imp.load_source('waagent', agent_path) + self.assertNotEquals(None, waagent.LoggerInit) + else: + self.assertRaises(ImportError, imp.load_source, 'waagent', + agent_path) if __name__ == '__main__': - waagent.LoggerInit('/dev/stdout', '/dev/null', verbose=True) - status = "a" * 512 - waagent.UploadStatusBlob(blockBlobUrl, status.encode("utf-8")) - #waagent.UploadStatusBlob(pageBlobUrl, status.encode("utf-8")) + unittest.main() diff --git a/tests/test_logger.py b/tests/test_logger.py index d188277..121a8fe 100644 --- a/tests/test_logger.py +++ b/tests/test_logger.py @@ -12,24 +12,91 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# Requires Python 2.4+ and Openssl 1.0+ +# +# Implements parts of RFC 2131, 1541, 1497 and +# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx +# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx +import tests.env +import tests.tools as tools +import uuid import unittest -from env import waagent +import azurelinuxagent.logger as logger +from azurelinuxagent.future import text + +class TestLogger(unittest.TestCase): + + def test_no_appender(self): + #The logger won't throw exception even if no appender. + _logger = logger.Logger() + _logger.verbose("Assert no exception") + _logger.info("Assert no exception") + _logger.warn("Assert no exception") + _logger.error("Assert no exception") + + def test_logger_format(self): + _logger = logger.Logger() + _logger.info("This is an exception {0}", Exception("Test")) + _logger.info("This is an number {0}", 0) + _logger.info("This is an boolean {0}", True) + _logger.verbose("{0}") + _logger.verbose("{0} {1}", 0, 1) + _logger.info("{0} {1}", 0, 1) + _logger.warn("{0} {1}", 0, 1) + _logger.error("{0} {1}", 0, 1) + _logger.info("this is a unicode {0}", '\u6211') + _logger.info("this is a utf-8 {0}", '\u6211'.encode('utf-8')) + _logger.info("this is a gbk {0}", 0xff ) + + def test_file_appender(self): + _logger = logger.Logger() + _logger.add_appender(logger.AppenderType.FILE, + logger.LogLevel.INFO, + '/tmp/testlog') + + msg = text(uuid.uuid4()) + _logger.info("Test logger: {0}", msg) + self.assertTrue(tools.simple_file_grep('/tmp/testlog', msg)) + + msg = text(uuid.uuid4()) + _logger.verbose("Verbose should not be logged: {0}", msg) + self.assertFalse(tools.simple_file_grep('/tmp/testlog', msg)) + + _logger.info("this is a unicode {0}", '\u6211') + _logger.info("this is a utf-8 {0}", '\u6211'.encode('utf-8')) + _logger.info("this is a gbk {0}", 0xff) + + def test_concole_appender(self): + _logger = logger.Logger() + _logger.add_appender(logger.AppenderType.CONSOLE, + logger.LogLevel.VERBOSE, + '/tmp/testlog') + + msg = text(uuid.uuid4()) + _logger.info("Test logger: {0}", msg) + self.assertTrue(tools.simple_file_grep('/tmp/testlog', msg)) + + msg = text(uuid.uuid4()) + _logger.verbose("Test logger: {0}", msg) + self.assertFalse(tools.simple_file_grep('/tmp/testlog', msg)) -class TestWAAgentLogger(unittest.TestCase): - def test_log_to_non_exists_dev(self): - logger = waagent.Logger('/tmp/testlog', '/dev/nonexists') - logger.Log("something") + _logger = logger.Logger() + _logger.add_appender(logger.AppenderType.CONSOLE, + logger.LogLevel.INFO, + '/dev/nonexists') + _logger.info("something") def test_log_to_non_exists_file(self): - logger = waagent.Logger('/tmp/nonexists/testlog', '/tmp/testconsole') - logger.Log("something") + _logger = logger.Logger() + _logger.add_appender(logger.AppenderType.FILE, + logger.LogLevel.INFO, + '/tmp/nonexists') + _logger.info("something") + - def test_log_unicode(self): - logger = waagent.Logger('/tmp/testlog', '/tmp/testconsole') - logger.Log(u"anything\u6211\u7231\u5201\u831C".encode("utf-8")) if __name__ == '__main__': unittest.main() diff --git a/tests/test_metadata.py b/tests/test_metadata.py new file mode 100644 index 0000000..8c34acc --- /dev/null +++ b/tests/test_metadata.py @@ -0,0 +1,36 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# +# Implements parts of RFC 2131, 1541, 1497 and +# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx +# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx + +import tests.env +from tests.tools import * +import unittest +from azurelinuxagent.metadata import AGENT_NAME, AGENT_VERSION, \ + DISTRO_NAME, DISTRO_VERSION, DISTRO_CODE_NAME, \ + DISTRO_FULL_NAME + +class TestOSInfo(unittest.TestCase): + def test_curr_os_info(self): + self.assertNotEquals(None, DISTRO_NAME) + self.assertNotEquals(None, DISTRO_VERSION) + self.assertNotEquals(None, DISTRO_CODE_NAME) + self.assertNotEquals(None, DISTRO_FULL_NAME) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_osutil.py b/tests/test_osutil.py new file mode 100644 index 0000000..95b8e17 --- /dev/null +++ b/tests/test_osutil.py @@ -0,0 +1,174 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# +# Implements parts of RFC 2131, 1541, 1497 and +# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx +# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx + +import tests.env as env +from tests.tools import * +import uuid +import unittest +import os +import shutil +import time +import azurelinuxagent.utils.fileutil as fileutil +import azurelinuxagent.utils.shellutil as shellutil +import azurelinuxagent.conf as conf +from azurelinuxagent.utils.osutil import OSUTIL, OSUtilError +import test + +class TestOSUtil(unittest.TestCase): + def test_current_distro(self): + self.assertNotEquals(None, OSUTIL) + +mount_list_sample="""\ +/dev/sda1 on / type ext4 (rw) +proc on /proc type proc (rw) +sysfs on /sys type sysfs (rw) +devpts on /dev/pts type devpts (rw,gid=5,mode=620) +tmpfs on /dev/shm type tmpfs (rw,rootcontext="system_u:object_r:tmpfs_t:s0") +none on /proc/sys/fs/binfmt_misc type binfmt_misc (rw) +/dev/sdb1 on /mnt/resource type ext4 (rw) +""" + +class TestCurrOS(unittest.TestCase): +#class TestCurrOS(object): + def test_get_paths(self): + self.assertNotEquals(None, OSUTIL.get_home()) + self.assertNotEquals(None, OSUTIL.get_lib_dir()) + self.assertNotEquals(None, OSUTIL.get_agent_pid_file_path()) + self.assertNotEquals(None, OSUTIL.get_conf_file_path()) + self.assertNotEquals(None, OSUTIL.get_dvd_mount_point()) + self.assertNotEquals(None, OSUTIL.get_ovf_env_file_path_on_dvd()) + + @mock(fileutil, 'write_file', MockFunc()) + @mock(fileutil, 'append_file', MockFunc()) + @mock(fileutil, 'chmod', MockFunc()) + @mock(fileutil, 'read_file', MockFunc(retval='')) + @mock(shellutil, 'run', MockFunc()) + @mock(shellutil, 'run_get_output', MockFunc(retval=[0, ''])) + def test_update_user_account(self): + OSUTIL.useradd('foo') + OSUTIL.chpasswd('foo', 'bar') + OSUTIL.del_account('foo') + + @mock(fileutil, 'read_file', MockFunc(retval='root::::')) + @mock(fileutil, 'write_file', MockFunc()) + def test_delete_root_password(self): + OSUTIL.del_root_password() + self.assertEquals('root:*LOCK*:14600::::::', + fileutil.write_file.args[1]) + + def test_cert_operation(self): + if os.path.isfile('/tmp/test.prv'): + os.remove('/tmp/test.prv') + shutil.copyfile(os.path.join(env.test_root, 'test.prv'), + '/tmp/test.prv') + if os.path.isfile('/tmp/test.crt'): + os.remove('/tmp/test.crt') + shutil.copyfile(os.path.join(env.test_root, 'test.crt'), + '/tmp/test.crt') + pub1 = OSUTIL.get_pubkey_from_prv('/tmp/test.prv') + pub2 = OSUTIL.get_pubkey_from_crt('/tmp/test.crt') + self.assertEquals(pub1, pub2) + thumbprint = OSUTIL.get_thumbprint_from_crt('/tmp/test.crt') + self.assertEquals('33B0ABCE4673538650971C10F7D7397E71561F35', thumbprint) + + def test_selinux(self): + if not OSUTIL.is_selinux_system(): + return + isrunning = OSUTIL.is_selinux_enforcing() + if not OSUTIL.is_selinux_enforcing(): + OSUTIL.set_selinux_enforce(0) + self.assertEquals(False, OSUTIL.is_selinux_enforcing()) + OSUTIL.set_selinux_enforce(1) + self.assertEquals(True, OSUTIL.is_selinux_enforcing()) + if os.path.isfile('/tmp/abc'): + os.remove('/tmp/abc') + fileutil.write_file('/tmp/abc', '') + OSUTIL.set_selinux_context('/tmp/abc','unconfined_u:object_r:ssh_home_t:s') + OSUTIL.set_selinux_enforce(1 if isrunning else 0) + + @mock(shellutil, 'run_get_output', MockFunc(retval=[0, ''])) + @mock(fileutil, 'write_file', MockFunc()) + def test_network_operation(self): + OSUTIL.start_network() + OSUTIL.allow_dhcp_broadcast() + OSUTIL.gen_transport_cert() + mac = OSUTIL.get_mac_addr() + self.assertNotEquals(None, mac) + OSUTIL.is_missing_default_route() + OSUTIL.set_route_for_dhcp_broadcast('api') + OSUTIL.remove_route_for_dhcp_broadcast('api') + OSUTIL.route_add('', '', '') + OSUTIL.get_dhcp_pid() + OSUTIL.set_hostname('api') + OSUTIL.publish_hostname('api') + + @mock(OSUTIL, 'get_home', MockFunc(retval='/tmp/home')) + @mock(OSUTIL, 'get_pubkey_from_prv', MockFunc(retval='')) + @mock(fileutil, 'chowner', MockFunc()) + def test_deploy_key(self): + if os.path.isdir('/tmp/home'): + shutil.rmtree('/tmp/home') + fileutil.write_file('/tmp/foo.prv', '') + OSUTIL.deploy_ssh_keypair("foo", ('$HOME/.ssh/id_rsa', 'foo')) + OSUTIL.deploy_ssh_pubkey("foo", ('$HOME/.ssh/authorized_keys', None, + 'ssh-rsa asdf')) + OSUTIL.deploy_ssh_pubkey("foo", ('$HOME/.ssh/authorized_keys', 'foo', + 'ssh-rsa asdf')) + self.assertRaises(OSUtilError, OSUTIL.deploy_ssh_pubkey, "foo", + ('$HOME/.ssh/authorized_keys', 'foo','hehe-rsa asdf')) + self.assertTrue(os.path.isfile('/tmp/home/.ssh/id_rsa')) + self.assertTrue(os.path.isfile('/tmp/home/.ssh/id_rsa.pub')) + self.assertTrue(os.path.isfile('/tmp/home/.ssh/authorized_keys')) + + @mock(shellutil, 'run_get_output', MockFunc(retval=[0, ''])) + @mock(OSUTIL, 'get_sshd_conf_file_path', MockFunc(retval='/tmp/sshd_config')) + def test_ssh_operation(self): + shellutil.run_get_output.retval=[0, + '2048 f1:fe:14:66:9d:46:9a:60:8b:8c:' + '80:43:39:1c:20:9e root@api (RSA)'] + sshd_conf = OSUTIL.get_sshd_conf_file_path() + self.assertEquals('/tmp/sshd_config', sshd_conf) + if os.path.isfile(sshd_conf): + os.remove(sshd_conf) + shutil.copyfile(os.path.join(env.test_root, 'sshd_config'), sshd_conf) + OSUTIL.set_ssh_client_alive_interval() + OSUTIL.conf_sshd(True) + self.assertTrue(simple_file_grep(sshd_conf, + 'PasswordAuthentication no')) + self.assertTrue(simple_file_grep(sshd_conf, + 'ChallengeResponseAuthentication no')) + self.assertTrue(simple_file_grep(sshd_conf, + 'ClientAliveInterval 180')) + + @mock(shellutil, 'run_get_output', MockFunc(retval=[0, ''])) + @mock(OSUTIL, 'get_dvd_device', MockFunc(retval=[0, 'abc'])) + @mock(OSUTIL, 'get_mount_point', MockFunc(retval='/tmp/cdrom')) + def test_mount(self): + OSUTIL.mount_dvd() + OSUTIL.umount_dvd() + mount_point = OSUTIL.get_mount_point(mount_list_sample, '/dev/sda') + self.assertNotEquals(None, mount_point) + + def test_getdvd(self): + fileutil.write_file("/tmp/sr0", '') + OSUTIL.get_dvd_device(dev_dir='/tmp') + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_ovfxml.py b/tests/test_ovfxml.py new file mode 100644 index 0000000..7b6990b --- /dev/null +++ b/tests/test_ovfxml.py @@ -0,0 +1,81 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# +# Implements parts of RFC 2131, 1541, 1497 and +# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx +# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx + +import tests.env +import tests.tools as tools +import uuid +import unittest +import os +import json +from azurelinuxagent.future import text +import azurelinuxagent.protocol.ovfenv as ovfenv + +ExtensionsConfigSample="""\ +<?xml version="1.0" encoding="utf-8"?> + <Environment xmlns="http://schemas.dmtf.org/ovf/environment/1" xmlns:oe="http://schemas.dmtf.org/ovf/environment/1" xmlns:wa="http://schemas.microsoft.com/windowsazure" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <wa:ProvisioningSection> + <wa:Version>1.0</wa:Version> + <LinuxProvisioningConfigurationSet xmlns="http://schemas.microsoft.com/windowsazure" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> + <ConfigurationSetType>LinuxProvisioningConfiguration</ConfigurationSetType> + <HostName>HostName</HostName> + <UserName>UserName</UserName> + <UserPassword>UserPassword</UserPassword> + <DisableSshPasswordAuthentication>false</DisableSshPasswordAuthentication> + <SSH> + <PublicKeys> + <PublicKey> + <Fingerprint>EB0C0AB4B2D5FC35F2F0658D19F44C8283E2DD62</Fingerprint> + <Path>$HOME/UserName/.ssh/authorized_keys</Path> + <Value>ssh-rsa AAAANOTAREALKEY== foo@bar.local</Value> + </PublicKey> + </PublicKeys> + <KeyPairs> + <KeyPair> + <Fingerprint>EB0C0AB4B2D5FC35F2F0658D19F44C8283E2DD62</Fingerprint> + <Path>$HOME/UserName/.ssh/id_rsa</Path> + </KeyPair> + </KeyPairs> + </SSH> + <CustomData>CustomData</CustomData> + </LinuxProvisioningConfigurationSet> + </wa:ProvisioningSection> + </Environment> +""" + +class TestOvf(unittest.TestCase): + def test_ovf(self): + config = ovfenv.OvfEnv(ExtensionsConfigSample) + self.assertEquals("HostName", config.hostname) + self.assertEquals("UserName", config.username) + self.assertEquals("UserPassword", config.user_password) + self.assertEquals(False, config.disable_ssh_password_auth) + self.assertEquals("CustomData", config.customdata) + self.assertNotEquals(None, config.ssh_pubkeys) + self.assertEquals(1, len(config.ssh_pubkeys)) + pubkey = config.ssh_pubkeys[0] + path, fingerprint, value = pubkey + self.assertEquals(path, "$HOME/UserName/.ssh/authorized_keys") + self.assertEquals(fingerprint, "EB0C0AB4B2D5FC35F2F0658D19F44C8283E2DD62"), + self.assertEquals(value, "ssh-rsa AAAANOTAREALKEY== foo@bar.local") + self.assertNotEquals(None, config.ssh_keypairs) + self.assertEquals(1, len(config.ssh_keypairs)) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_protocol.py b/tests/test_protocol.py new file mode 100644 index 0000000..3eff197 --- /dev/null +++ b/tests/test_protocol.py @@ -0,0 +1,88 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# +# Implements parts of RFC 2131, 1541, 1497 and +# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx +# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx + +import tests.env +import tests.tools as tools +from .tools import * +import uuid +import unittest +import os +import time +import json +from azurelinuxagent.protocol.common import * + +extensionDataStr = """ +{ + "vmAgent": { + "agentVersion": "2.4.1198.689", + "status": "Ready", + "message": "GuestAgent is running and accepting new configurations." + }, + "extensionHandlers": [{ + "handlerName": "Microsoft.Compute.CustomScript", + "handlerVersion": "1.0.0.0", + "status": "Ready", + "message": "Plugin enabled (name: Microsoft.Compute.CustomScript, version: 1.0.0.0).", + "extensionStatusList": [{ + "name": "MyDomainJoinScript", + "configurationAppliedTime": "2014-08-12T19:20:18Z", + "operation": "CommandExecutionFinished", + "status": "Success", + "sequenceNumber": "0", + "substatusList": [{ + "name": "StdOut", + "status": "Info", + "code": "0", + "message": "Joiningdomainfoo" + }] + }] + } + + ] +} +""" + +class TestProtocolContract(unittest.TestCase): + def test_get_properties(self): + data = get_properties(VMInfo()) + data = get_properties(Cert()) + data = get_properties(ExtensionPackageList()) + data = get_properties(InstanceMetadata()) + data = get_properties(VMStatus()) + data = get_properties(TelemetryEventList()) + data = get_properties(Extension(name="hehe")) + self.assertTrue("name" in data) + self.assertTrue("properties" in data) + self.assertEquals(dict, type(data["properties"])) + self.assertTrue("versionUris" not in data) + + def test_set_properties(self): + data = json.loads(extensionDataStr) + obj = VMStatus() + set_properties(obj, data) + self.assertNotEquals(None, obj.vmAgent) + self.assertEquals(VMAgentStatus, type(obj.vmAgent)) + self.assertNotEquals(None, obj.vmAgent.status) + self.assertNotEquals(None, obj.extensionHandlers) + self.assertEquals(DataContractList, type(obj.extensionHandlers)) + +if __name__ == '__main__': + unittest.main() + diff --git a/tests/test_protocolFactory.py b/tests/test_protocolFactory.py new file mode 100644 index 0000000..9928b88 --- /dev/null +++ b/tests/test_protocolFactory.py @@ -0,0 +1,37 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# +# Implements parts of RFC 2131, 1541, 1497 and +# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx +# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx + +import tests.env +from tests.tools import * +import uuid +import unittest +import os +import azurelinuxagent.protocol as protocol +import azurelinuxagent.protocol.protocolFactory as protocolFactory + +class TestWireProtocolEndpoint(unittest.TestCase): + def test_get_available_protocols(self): + mockGetV1 = MockFunc(retval="Mock protocol") + protocols = protocolFactory.get_available_protocols([mockGetV1]) + self.assertNotEquals(None, protocols) + self.assertNotEquals(0, len(protocols)) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_redhat.py b/tests/test_redhat.py new file mode 100644 index 0000000..d9ea4ec --- /dev/null +++ b/tests/test_redhat.py @@ -0,0 +1,49 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# +# Implements parts of RFC 2131, 1541, 1497 and +# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx +# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx + +import tests.env +from tests.tools import * +import unittest +from azurelinuxagent.distro.redhat.osutil import RedhatOSUtil + +test_pubkey="""\ +-----BEGIN PUBLIC KEY----- +MIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0AMIIBCAKCAQEA2wo22vf1N8NWE+5lLfit +T7uzkfwqdw0IAoHZ0l2BtP0ajy6f835HCR3w3zLWw5ut7Xvyo26x1OMOzjo5lqtM +h8iyQwfHtWf6Cekxfkf+6Pca99bNuDgwRopOTOyoVgwDzJB0+slpn/sJjeGbhxJl +ToT8tNPLrBmnnpaMZLMIANcPQtTRCQcV/ycv+/omKXFB+zULYkN8v22o5mysoCuQ +fzXiJP3Mlnf+V2XMl1WAJylhOJif04K8j+G8oF5ECBIQiph4ZLQS1yTYlozPXU8k +8vB6A5+UiOGxBnOQYnp42cS5d4qSQ8LORCRGXrCj4DCP+lvkUDLUHx2WN+1ivZkO +fQIBIw== +-----END PUBLIC KEY----- +""" + +expected_ssh_rsa_pubkey="""\ +ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA2wo22vf1N8NWE+5lLfitT7uzkfwqdw0IAoHZ0l2BtP0ajy6f835HCR3w3zLWw5ut7Xvyo26x1OMOzjo5lqtMh8iyQwfHtWf6Cekxfkf+6Pca99bNuDgwRopOTOyoVgwDzJB0+slpn/sJjeGbhxJlToT8tNPLrBmnnpaMZLMIANcPQtTRCQcV/ycv+/omKXFB+zULYkN8v22o5mysoCuQfzXiJP3Mlnf+V2XMl1WAJylhOJif04K8j+G8oF5ECBIQiph4ZLQS1yTYlozPXU8k8vB6A5+UiOGxBnOQYnp42cS5d4qSQ8LORCRGXrCj4DCP+lvkUDLUHx2WN+1ivZkOfQ== +""" + +class TestRedhat(unittest.TestCase): + def test_RsaPublicKeyToSshRsa(self): + OSUtil = RedhatOSUtil() + ssh_rsa_pubkey = OSUtil.asn1_to_ssh_rsa(test_pubkey) + self.assertEquals(expected_ssh_rsa_pubkey, ssh_rsa_pubkey) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_resourcedisk.py b/tests/test_resourcedisk.py new file mode 100644 index 0000000..de54fd3 --- /dev/null +++ b/tests/test_resourcedisk.py @@ -0,0 +1,63 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# +# Implements parts of RFC 2131, 1541, 1497 and +# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx +# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx + +import tests.env +from tests.tools import * +import unittest +import azurelinuxagent.distro.default.resourceDisk as rdh +import azurelinuxagent.logger as logger +from azurelinuxagent.utils.osutil import OSUTIL + +#logger.LoggerInit("/dev/null", "/dev/stdout") + +gpt_output_sample=""" +Model: Msft Virtual Disk (scsi) +Disk /dev/sda: 32.2GB +Sector size (logical/physical): 512B/4096B +Partition Table: gpt + +Number Start End Size Type File system Flags + 1 2097kB 29.4GB 29.4GB primary ext4 boot + 2 2097kB 29.4GB 29.4GB primary ext4 boot +""" + +class TestResourceDisk(unittest.TestCase): + + @mock(rdh.OSUTIL, 'device_for_ide_port', MockFunc(retval='foo')) + @mock(rdh.shellutil, 'run_get_output', MockFunc(retval=(0, gpt_output_sample))) + @mock(rdh.shellutil, 'run', MockFunc(retval=0)) + def test_mountGPT(self): + handler = rdh.ResourceDiskHandler() + handler.mount_resource_disk('/tmp/foo', 'ext4') + + @mock(rdh.OSUTIL, 'device_for_ide_port', MockFunc(retval='foo')) + @mock(rdh.shellutil, 'run_get_output', MockFunc(retval=(0, ""))) + @mock(rdh.shellutil, 'run', MockFunc(retval=0)) + def test_mountMBR(self): + handler = rdh.ResourceDiskHandler() + handler.mount_resource_disk('/tmp/foo', 'ext4') + + @mock(rdh.shellutil, 'run', MockFunc(retval=0)) + def test_createSwapSpace(self): + handler = rdh.ResourceDiskHandler() + handler.create_swap_space('/tmp/foo', 512) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_rest_util.py b/tests/test_rest_util.py new file mode 100644 index 0000000..d07a1df --- /dev/null +++ b/tests/test_rest_util.py @@ -0,0 +1,63 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# +# Implements parts of RFC 2131, 1541, 1497 and +# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx +# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx + +import tests.env +from tests.tools import * +import uuid +import unittest +import os +import azurelinuxagent.utils.restutil as restutil +from azurelinuxagent.future import text +import test +import socket +import azurelinuxagent.logger as logger + +class MockResponse(object): + def __init__(self, status=restutil.httpclient.OK): + self.status = status + + def getheaders(self): + pass + +class TestHttpOperations(unittest.TestCase): + + def test_parse_url(self): + host, port, secure, rel_uri = restutil._parse_url("http://abc.def/ghi#hash?jkl=mn") + self.assertEquals("abc.def", host) + self.assertEquals("/ghi#hash?jkl=mn", rel_uri) + + host, port, secure, rel_uri = restutil._parse_url("http://abc.def/") + self.assertEquals("abc.def", host) + self.assertEquals("/", rel_uri) + self.assertEquals(False, secure) + + host, port, secure, rel_uri = restutil._parse_url("https://abc.def/ghi?jkl=mn") + self.assertEquals(True, secure) + + host, port, secure, rel_uri = restutil._parse_url("http://abc.def:80/") + self.assertEquals("abc.def", host) + + @mock(restutil.httpclient.HTTPConnection, 'request', MockFunc()) + @mock(restutil.httpclient.HTTPConnection, 'getresponse', MockFunc(retval=MockResponse())) + def test_http_request(self): + restutil.http_get("https://httpbin.org/get") + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_shared_config.py b/tests/test_shared_config.py deleted file mode 100644 index 8252b3b..0000000 --- a/tests/test_shared_config.py +++ /dev/null @@ -1,148 +0,0 @@ -# Copyright 2014 Microsoft Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import os -import re -import unittest -from env import waagent - -class MockDistro(object): - def getInterfaceNameByMac(self, mac): - pass - - def configIpV4(self, ifName, addr): - pass - -class TestSharedConfig(unittest.TestCase): - - def test_reg(self): - mac = "00:15:5D:34:00:08" - output = Ifconfig_Out - output = output.replace('\n', '') - reg = r"(eth\d).*(HWaddr|ether) {0}".format(mac) - match = re.search(reg, output, re.IGNORECASE) - output = match.group(0) - eths = re.findall(r"eth\d", output) - self.assertNotEquals(0, len(eths)) - - def test_parse_shared_config(self): - conf = waagent.SharedConfig().Parse(SharedConfigText) - self.assertNotEquals(None, conf) - self.assertNotEquals(None, conf.RdmaMacAddress) - self.assertNotEquals(None, conf.RdmaIPv4Address) - self.assertEquals("00:15:5D:34:00:44", conf.RdmaMacAddress) - return conf - - def test_config_rdma(self): - waagent.MyDistro= MockDistro() - waagent.LibDir="/tmp" - - test_dev = "/tmp/hvnd_rdma" - test_dat_conf_files = ["/tmp/dat.conf"] - if os.path.isfile("/tmp/rdmaconfiged"): - os.remove("/tmp/rdmaconfiged") - waagent.SetFileContents(test_dev, "") - old = ("ofa-v2-ib0 u2.0 nonthreadsafe default libdaplofa.so.2 " - "dapl.2.0 \"oldip 0\"") - waagent.SetFileContents(test_dat_conf_files[0], old) - conf = self.test_parse_shared_config() - handler = waagent.RdmaHandler(conf.RdmaMacAddress, conf.RdmaIPv4Address, - test_dev, test_dat_conf_files) - handler.set_dat_conf() - handler.set_rdma_dev() - - rdma_conf = waagent.GetFileContents(test_dev) - self.assertNotEquals(None, rdma_conf) - self.assertNotEquals(0, rdma_conf.count(conf.RdmaIPv4Address)) - self.assertNotEquals(0, rdma_conf.count(conf.RdmaMacAddress)) - - dat_conf = waagent.GetFileContents(test_dat_conf_files[0]) - self.assertNotEquals(None, dat_conf) - self.assertNotEquals(0, dat_conf.count(conf.RdmaIPv4Address)) - self.assertEquals(0, dat_conf.count("oldip")) - -SharedConfigText="""\ -<?xml version="1.0" encoding="utf-8"?> -<SharedConfig version="1.0.0.0" goalStateIncarnation="1"> - <Deployment name="698f959e434c41cc9d72a2c67c044463" guid="{ba92e945-0302-4030-9710-257c03c07e22}" incarnation="0" isNonCancellableTopologyChangeEnabled="false"> - <Service name="test-rdms" guid="{00000000-0000-0000-0000-000000000000}" /> - <ServiceInstance name="698f959e434c41cc9d72a2c67c044463.0" guid="{6f157bcb-b6ac-4fdd-9789-2ca466220e17}" /> - </Deployment> - <Incarnation number="1" instance="test-rdms" guid="{33d19bb6-f34d-4dfb-966c-2bade1714cc5}" /> - <Role guid="{dad0becc-5d1d-3c55-3285-0136e9933bbe}" name="test-rdms" settleTimeSeconds="0" /> - <LoadBalancerSettings timeoutSeconds="0" waitLoadBalancerProbeCount="8"> - <Probes> - <Probe name="D41D8CD98F00B204E9800998ECF8427E" /> - <Probe name="423A4BBA20CEBE79BA641B20A03ED6F9" /> - </Probes> - </LoadBalancerSettings> - <OutputEndpoints> - <Endpoint name="test-rdms:openInternalEndpoint" type="SFS"> - <Target instance="test-rdms" endpoint="openInternalEndpoint" /> - </Endpoint> - </OutputEndpoints> - <Instances> - <Instance id="test-rdms" address="100.74.58.20" primaryMacAddress="000D3A101ED4" rdmaMacAddress="00155D340044" rdmaIPv4Address="172.16.2.59"> - <FaultDomains randomId="0" updateId="0" updateCount="0" /> - <InputEndpoints> - <Endpoint name="openInternalEndpoint" address="100.74.58.20" protocol="any" isPublic="false" enableDirectServerReturn="false" isDirectAddress="false" disableStealthMode="false"> - <LocalPorts> - <LocalPortSelfManaged /> - </LocalPorts> - </Endpoint> - <Endpoint name="SSH" address="100.74.58.20:22" protocol="tcp" hostName="test-rdmsContractContract" isPublic="true" loadBalancedPublicAddress="104.45.128.35:22" enableDirectServerReturn="false" isDirectAddress="false" disableStealthMode="false"> - <LocalPorts> - <LocalPortRange from="22" to="22" /> - </LocalPorts> - </Endpoint> - <Endpoint name="test-rdms_A9_Infiniband" address="100.74.58.20" protocol="any" isPublic="false" enableDirectServerReturn="false" isDirectAddress="false" disableStealthMode="false"> - <LocalPorts> - <LocalPortSelfManaged /> - </LocalPorts> - </Endpoint> - </InputEndpoints> - </Instance> - </Instances> -</SharedConfig> -""" -Ifconfig_Out="""\ -eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 -inet 100.74.52.8 netmask 255.255.255.0 broadcast 100.74.52.255 -inet6 fe80::20d:3aff:fe10:672f prefixlen 64 scopeid 0x20<link> -ether 00:0d:3a:10:67:2f txqueuelen 1000 (Ethernet) -RX packets 9911 bytes 4451278 (4.2 MiB) -RX errors 0 dropped 0 overruns 0 frame 0 -TX packets 10505 bytes 1643251 (1.5 MiB) -TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 - -eth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 -inet6 fe80::215:5dff:fe34:8 prefixlen 64 scopeid 0x20<link> -ether 00:15:5d:34:00:08 txqueuelen 1000 (Ethernet) -RX packets 16 bytes 672 (672.0 B) -RX errors 0 dropped 0 overruns 0 frame 0 -TX packets 16 bytes 2544 (2.4 KiB) -TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 - -lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536 -inet 127.0.0.1 netmask 255.0.0.0 -inet6 ::1 prefixlen 128 scopeid 0x10<host> -loop txqueuelen 0 (Local Loopback) -RX packets 0 bytes 0 (0.0 B) -RX errors 0 dropped 0 overruns 0 frame 0 -TX packets 0 bytes 0 (0.0 B) -""" - -if __name__ == '__main__': - unittest.main() diff --git a/tests/test_sharedconfig.py b/tests/test_sharedconfig.py new file mode 100644 index 0000000..f480253 --- /dev/null +++ b/tests/test_sharedconfig.py @@ -0,0 +1,79 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# +# Implements parts of RFC 2131, 1541, 1497 and +# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx +# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx + +import tests.env +import tests.tools as tools +import uuid +import unittest +import os +import azurelinuxagent.protocol.v1 as v1 + +shared_config_sample=u""" + + <SharedConfig version="1.0.0.0" goalStateIncarnation="1"> + <Deployment name="db00a7755a5e4e8a8fe4b19bc3b330c3" guid="{ce5a036f-5c93-40e7-8adf-2613631008ab}" incarnation="2"> + <Service name="MyVMRoleService" guid="{00000000-0000-0000-0000-000000000000}" /> + <ServiceInstance name="db00a7755a5e4e8a8fe4b19bc3b330c3.1" guid="{d113f4d7-9ead-4e73-b715-b724b5b7842c}" /> + </Deployment> + <Incarnation number="1" instance="MachineRole_IN_0" guid="{a0faca35-52e5-4ec7-8fd1-63d2bc107d9b}" /> + <Role guid="{73d95f1c-6472-e58e-7a1a-523554e11d46}" name="MachineRole" settleTimeSeconds="10" /> + <LoadBalancerSettings timeoutSeconds="0" waitLoadBalancerProbeCount="8"> + <Probes> + <Probe name="MachineRole" /> + <Probe name="55B17C5E41A1E1E8FA991CF80FAC8E55" /> + <Probe name="3EA4DBC19418F0A766A4C19D431FA45F" /> + </Probes> + </LoadBalancerSettings> + <OutputEndpoints> + <Endpoint name="MachineRole:Microsoft.WindowsAzure.Plugins.RemoteAccess.Rdp" type="SFS"> + <Target instance="MachineRole_IN_0" endpoint="Microsoft.WindowsAzure.Plugins.RemoteAccess.Rdp" /> + </Endpoint> + </OutputEndpoints> + <Instances> + <Instance id="MachineRole_IN_0" address="10.115.153.75"> + <FaultDomains randomId="0" updateId="0" updateCount="0" /> + <InputEndpoints> + <Endpoint name="a" address="10.115.153.75:80" protocol="http" isPublic="true" loadBalancedPublicAddress="70.37.106.197:80" enableDirectServerReturn="false" isDirectAddress="false" disableStealthMode="false"> + <LocalPorts> + <LocalPortRange from="80" to="80" /> + </LocalPorts> + </Endpoint> + <Endpoint name="Microsoft.WindowsAzure.Plugins.RemoteAccess.Rdp" address="10.115.153.75:3389" protocol="tcp" isPublic="false" enableDirectServerReturn="false" isDirectAddress="false" disableStealthMode="false"> + <LocalPorts> + <LocalPortRange from="3389" to="3389" /> + </LocalPorts> + </Endpoint> + <Endpoint name="Microsoft.WindowsAzure.Plugins.RemoteForwarder.RdpInput" address="10.115.153.75:20000" protocol="tcp" isPublic="true" loadBalancedPublicAddress="70.37.106.197:3389" enableDirectServerReturn="false" isDirectAddress="false" disableStealthMode="false"> + <LocalPorts> + <LocalPortRange from="20000" to="20000" /> + </LocalPorts> + </Endpoint> + </InputEndpoints> + </Instance> + </Instances> + </SharedConfig> +""" + +class TestSharedConfig(unittest.TestCase): + def test_sharedconfig(self): + shared_conf = v1.SharedConfig(shared_config_sample) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_shell_util.py b/tests/test_shell_util.py new file mode 100644 index 0000000..9862745 --- /dev/null +++ b/tests/test_shell_util.py @@ -0,0 +1,39 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# +# Implements parts of RFC 2131, 1541, 1497 and +# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx +# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx + +import tests.env +import tests.tools as tools +import uuid +import unittest +import os +import azurelinuxagent.utils.shellutil as shellutil +import test + +class TestrunCmd(unittest.TestCase): + def test_run_get_output(self): + output = shellutil.run_get_output("ls /") + self.assertNotEquals(None, output) + self.assertEquals(0, output[0]) + + err = shellutil.run_get_output("ls /not-exists") + self.assertNotEquals(0, err[0]) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_text_util.py b/tests/test_text_util.py new file mode 100644 index 0000000..b29beff --- /dev/null +++ b/tests/test_text_util.py @@ -0,0 +1,47 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# +# Implements parts of RFC 2131, 1541, 1497 and +# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx +# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx + +import tests.env +from tests.tools import * +import uuid +import unittest +import os +from azurelinuxagent.future import text +import azurelinuxagent.utils.textutil as textutil + +class TestTextUtil(unittest.TestCase): + def test_get_password_hash(self): + password_hash = textutil.gen_password_hash("asdf", True, 6, 10) + self.assertNotEquals(None, password_hash) + + def test_remove_bom(self): + #Test bom could be removed + data = text(b'\xef\xbb\xbfhehe', encoding='utf-8') + data = textutil.remove_bom(data) + self.assertNotEquals(0xbb, data[0]) + + #Test string without BOM is not affected + data = u"hehe" + data = textutil.remove_bom(data) + self.assertEquals(u"h", data[0]) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_util.py b/tests/test_util.py deleted file mode 100644 index 6e3ff27..0000000 --- a/tests/test_util.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright 2014 Microsoft Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import unittest -from env import waagent -import sys -from tests.tools import * - -SampleInterfaceInfo="""\ -eth0 Link encap:Ethernet HWaddr ff:ff:ff:ff:ff:ff - inet addr:10.94.20.249 Bcast:10.94.23.255 Mask:255.255.252.0 - inet6 addr: fe80::215:5dff:fe5f:bf03/64 Scope:Link - UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 - RX packets:3789880 errors:0 dropped:0 overruns:0 frame:0 - TX packets:80973 errors:0 dropped:0 overruns:0 carrier:0 - collisions:0 txqueuelen:1000 - RX bytes:388563383 (388.5 MB) TX bytes:21484571 (21.4 MB) - -eth1 Link encap:Ethernet HWaddr 00:00:00:00:00:00 - inet addr:192.168.1.1 Bcast:192.168.1.255 Mask:255.255.255.0 - inet6 addr: fe80::215:5dff:fe5f:bf08/64 Scope:Link - UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 - RX packets:386614 errors:0 dropped:0 overruns:0 frame:0 - TX packets:201356 errors:0 dropped:0 overruns:0 carrier:0 - collisions:0 txqueuelen:1000 - RX bytes:32507619 (32.5 MB) TX bytes:78342503 (78.3 MB) - -lo Link encap:Local Loopback - inet addr:127.0.0.1 Mask:255.0.0.0 - inet6 addr: ::1/128 Scope:Host - UP LOOPBACK RUNNING MTU:65536 Metric:1 - RX packets:2561 errors:0 dropped:0 overruns:0 frame:0 - TX packets:2561 errors:0 dropped:0 overruns:0 carrier:0 - collisions:0 txqueuelen:0 -""" - -class TestUtil(unittest.TestCase): - - @Mockup(waagent, "RunGetOutput", MockFunc('', (0, SampleInterfaceInfo))) - def test_getInterfaceNameByMac(self): - distro = waagent.AbstractDistro() - ifName = distro.getInterfaceNameByMac("ff:ff:ff:ff:ff:ff") - self.assertEquals("eth0", ifName) - ifName = distro.getInterfaceNameByMac("00:00:00:00:00:00") - self.assertEquals("eth1", ifName) - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/test_utils.py b/tests/test_utils.py deleted file mode 100644 index e4c1c45..0000000 --- a/tests/test_utils.py +++ /dev/null @@ -1,83 +0,0 @@ -# Copyright 2014 Microsoft Corporation -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import unittest -import tempfile -import os -from env import waagent - -sample_mount_list = """\ -/dev/sda1 on / type ext4 (rw) -proc on /proc type proc (rw) -sysfs on /sys type sysfs (rw) -devpts on /dev/pts type devpts (rw,gid=5,mode=620) -tmpfs on /dev/shm type tmpfs (rw,rootcontext="system_u:object_r:tmpfs_t:s0") -none on /proc/sys/fs/binfmt_misc type binfmt_misc (rw) -/dev/sdb1 on /mnt/resource type ext4 (rw) -""" - -device_name="/dev/sdb" - -class TestWAAgentUtils(unittest.TestCase): - - def test_get_mount_point(self): - normal = sample_mount_list - mp = waagent.GetMountPoint(normal, device_name) - self.assertEqual(mp, '/mnt/resource') - - null = None - mp = waagent.GetMountPoint(null, device_name) - self.assertEqual(mp, None) - - malformed = 'asdfasdfasdfa aasdf' - mp = waagent.GetMountPoint(malformed, device_name) - self.assertEqual(mp, None) - - def test_replace_in_file_found(self): - tmpfilename = tempfile.mkstemp('', 'tmp', None, True)[1] - try: - tmpfile = open(tmpfilename, 'w') - tmpfile.write('Replace Me') - tmpfile.close() - - result = waagent.ReplaceStringInFile(tmpfilename, r'c. ', 'ced ') - - tmpfile = open(tmpfilename, 'r') - newcontents = tmpfile.read(); - tmpfile.close() - - self.assertEqual('Replaced Me', str(newcontents)) - finally: - os.remove(tmpfilename) - - def test_replace_in_file_not_found(self): - tmpfilename = tempfile.mkstemp('', 'tmp', None, True)[1] - try: - tmpfile = open(tmpfilename, 'w') - tmpfile.write('Replace Me') - tmpfile.close() - - result = waagent.ReplaceStringInFile(tmpfilename, r'not here ', 'ced ') - - tmpfile = open(tmpfilename, 'r') - newcontents = tmpfile.read(); - tmpfile.close() - - self.assertEqual('Replace Me', str(newcontents)) - finally: - os.remove(tmpfilename) - -if __name__ == '__main__': - unittest.main() diff --git a/tests/test_v1.py b/tests/test_v1.py new file mode 100644 index 0000000..02225af --- /dev/null +++ b/tests/test_v1.py @@ -0,0 +1,173 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# +# Implements parts of RFC 2131, 1541, 1497 and +# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx +# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx + +import tests.env +import tests.tools as tools +from tests.tools import * +import uuid +import unittest +import os +import time +from azurelinuxagent.utils.restutil import httpclient +import azurelinuxagent.logger as logger +import azurelinuxagent.protocol.v1 as v1 +from tests.test_version import VersionInfoSample +from tests.test_goalstate import goal_state_sample +from tests.test_hostingenv import hosting_env_sample +from tests.test_sharedconfig import shared_config_sample +from tests.test_certificates import certs_sample, transport_cert +from tests.test_extensionsconfig import ext_conf_sample, manifest_sample + +def mock_fetch_uri(url, headers=None, chk_proxy=False): + content = None + if "versions" in url: + content = VersionInfoSample + elif "goalstate" in url: + content = goal_state_sample + elif "hostingenvuri" in url: + content = hosting_env_sample + elif "sharedconfiguri" in url: + content = shared_config_sample + elif "certificatesuri" in url: + content = certs_sample + elif "extensionsconfiguri" in url: + content = ext_conf_sample + elif "manifest.xml" in url: + content = manifest_sample + else: + raise Exception("Bad url {0}".format(url)) + return content + +def mock_fetch_manifest(uris): + return manifest_sample + +def mock_fetch_cache(file_path): + content = None + if "Incarnation" in file_path: + content = 1 + elif "GoalState" in file_path: + content = goal_state_sample + elif "HostingEnvironmentConfig" in file_path: + content = hosting_env_sample + elif "SharedConfig" in file_path: + content = shared_config_sample + elif "Certificates" in file_path: + content = certs_sample + elif "TransportCert" in file_path: + content = transport_cert + elif "ExtensionsConfig" in file_path: + content = ext_conf_sample + elif "manifest" in file_path: + content = manifest_sample + else: + raise Exception("Bad filepath {0}".format(file_path)) + return content + +data_with_bom = b'\xef\xbb\xbfhehe' + +class MockResp(object): + def __init__(self, status=v1.httpclient.OK, data=None): + self.status = status + self.data = data + + def read(self): + return self.data + +class TestWireClint(unittest.TestCase): + + @mock(v1.restutil, 'http_get', MockFunc(retval=MockResp(data=data_with_bom))) + def test_fetch_uri_with_bom(self): + v1._fetch_uri("http://foo.bar", None) + + @mock(v1, '_fetch_cache', mock_fetch_cache) + def test_get(self): + os.chdir('/tmp') + client = v1.WireClient("foobar") + goalState = client.get_goal_state() + self.assertNotEquals(None, goalState) + hostingEnv = client.get_hosting_env() + self.assertNotEquals(None, hostingEnv) + sharedConfig = client.get_shared_conf() + self.assertNotEquals(None, sharedConfig) + extensionsConfig = client.get_ext_conf() + self.assertNotEquals(None, extensionsConfig) + + + @mock(v1, '_fetch_cache', mock_fetch_cache) + def test_get_head_for_cert(self): + client = v1.WireClient("foobar") + header = client.get_header_for_cert() + self.assertNotEquals(None, header) + + @mock(v1.WireClient, 'get_header_for_cert', MockFunc()) + @mock(v1, '_fetch_uri', mock_fetch_uri) + @mock(v1.fileutil, 'write_file', MockFunc()) + def test_update_goal_state(self): + client = v1.WireClient("foobar") + client.update_goal_state() + goal_state = client.get_goal_state() + self.assertNotEquals(None, goal_state) + hosting_env = client.get_hosting_env() + self.assertNotEquals(None, hosting_env) + shared_config = client.get_shared_conf() + self.assertNotEquals(None, shared_config) + ext_conf = client.get_ext_conf() + self.assertNotEquals(None, ext_conf) + +class TestStatusBlob(unittest.TestCase): + def testToJson(self): + vm_status = v1.VMStatus() + status_blob = v1.StatusBlob(vm_status) + self.assertNotEquals(None, status_blob.to_json()) + + @mock(v1.restutil, 'http_put', MockFunc(retval=MockResp(httpclient.CREATED))) + @mock(v1.restutil, 'http_head', MockFunc(retval=MockResp(httpclient.OK))) + def test_put_page_blob(self): + vm_status = v1.VMStatus() + status_blob = v1.StatusBlob(vm_status) + data = 'a' * 100 + status_blob.put_page_blob("http://foo.bar", data) + +class TestConvert(unittest.TestCase): + def test_status(self): + vm_status = v1.VMStatus() + handler_status = v1.ExtensionHandlerStatus() + substatus = v1.ExtensionSubStatus() + ext_status = v1.ExtensionStatus() + + vm_status.extensionHandlers.append(handler_status) + v1.vm_status_to_v1(vm_status) + + handler_status.extensionStatusList.append(ext_status) + v1.vm_status_to_v1(vm_status) + + ext_status.substatusList.append(substatus) + v1.vm_status_to_v1(vm_status) + + def test_param(self): + param = v1.TelemetryEventParam() + event = v1.TelemetryEvent() + event.parameters.append(param) + + v1.event_to_v1(event) + +if __name__ == '__main__': + unittest.main() + diff --git a/tests/test_version.py b/tests/test_version.py new file mode 100644 index 0000000..72d9599 --- /dev/null +++ b/tests/test_version.py @@ -0,0 +1,53 @@ +# Copyright 2014 Microsoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Requires Python 2.4+ and Openssl 1.0+ +# +# Implements parts of RFC 2131, 1541, 1497 and +# http://msdn.microsoft.com/en-us/library/cc227282%28PROT.10%29.aspx +# http://msdn.microsoft.com/en-us/library/cc227259%28PROT.13%29.aspx + +import tests.env +import tests.tools as tools +import uuid +import unittest +import os +import json +import azurelinuxagent.protocol.v1 as v1 +from azurelinuxagent.future import text + +VersionInfoSample=u"""\ +<?xml version="1.0" encoding="utf-8"?> +<Versions> + <Preferred> + <Version>2012-11-30</Version> + </Preferred> + <Supported> + <Version>2010-12-15</Version> + <Version>2010-28-10</Version> + </Supported> +</Versions> +""" + +class TestVersionInfo(unittest.TestCase): + def test_version_info(self): + config = v1.VersionInfo(VersionInfoSample) + self.assertEquals("2012-11-30", config.get_preferred()) + self.assertNotEquals(None, config.get_supported()) + self.assertEquals(2, len(config.get_supported())) + self.assertEquals("2010-12-15", config.get_supported()[0]) + self.assertEquals("2010-28-10", config.get_supported()[1]) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_waagent.py b/tests/test_waagent.py deleted file mode 100755 index 11510b6..0000000 --- a/tests/test_waagent.py +++ /dev/null @@ -1,386 +0,0 @@ -#!/usr/bin/python - -import os -import sys -import platform -import socket -import fcntl -import struct -import array -import re -import tempfile -import unittest -import random -import string -import threading -from time import ctime, sleep -import imp - -# waagent has no '.py' therefore create waagent module import manually. -waagent=imp.load_source('waagent','../waagent') - -TestingVersion = "$CommitBranch:future$|$LastCommitDate:2013-04-16 15:52:17 -0700$|$LastCommitHash:7ad7c643b2adbac40b1ea4a5b6eb19f0fe971623$" - - -class WaagentTestCases(unittest.TestCase): - """ - Test cases for waagent - """ - def setUp(self): - """ - Check for root permissions. - Check Distro is supported. - Create a waagent.conf file. - """ - waagent.LoggerInit('/var/log/waagent.log','/dev/console') - if not self.AmIRoot(): - raise Exception('I need to run as root') - DistroName=platform.dist()[0] - self.failUnless(hasattr(waagent,DistroName+'Distro') == True,DistroName+' is not a supported linux distribution.') - waagent.MyDistro=getattr(waagent,DistroName+'Distro')() - # set up /etc/waagent.conf - with open('/etc/waagent.conf','wb') as f: - f.write(waagent.WaagentConf) - f.close() - - def tearDown(self): - """ - Remove test resources. - This is a stub - """ - pass - - def AmIRoot(self): - """ - Check that our uid is root. - """ - return 'root' in waagent.RunGetOutput('id')[1] - - def writetothelog(self,id): - """ - Convienence function. - Used by testTwoLogWritingThreads() - Write 'start', sleep for id seconds and - write 'end' to the logfile. - """ - waagent.Log(str(id)+' start ') - sleep(id) - waagent.Log(str(id)+' end ') - - def noop(self,arg2): - """ - Set a method to noop() to prevent its operation. - """ - pass - -##############TESTCASES########################## - -###############Astract Distro - Concrete Distro Tests############## - - def testMyDistroMemberVariables(self): - """ - Ensure that required Distro properties are not None. - """ - assert waagent.MyDistro.agent_service_name is not None , 'MyDistro.agent_service_name must not be None' - assert waagent.MyDistro.selinux is not None , 'MyDistro.selinux must not be None' - assert waagent.MyDistro.ssh_service_name is not None , 'MyDistro.ssh_service_name must not be None' - assert waagent.MyDistro.ssh_config_file is not None , 'MyDistro.ssh_config_file must not be None' - assert waagent.MyDistro.hostname_file_path is not None , 'MyDistro.hostname_file_path must not be None' - assert waagent.MyDistro.dhcp_client_name is not None , 'MyDistro.dhcp_client_name must not be None' - assert waagent.MyDistro.requiredDeps is not None , 'MyDistro.requiredDeps must not be None' - assert waagent.MyDistro.init_script_file is not None , 'MyDistro.init_script_file must not be None' - assert waagent.MyDistro.agent_package_name is not None , 'MyDistro.agent_package_name must not be None' - assert waagent.MyDistro.fileBlackList is not None , 'MyDistro.fileBlackList must not be None' - assert waagent.MyDistro.agent_files_to_uninstall is not None , 'MyDistro.agent_files_to_uninstall must not be None' - assert waagent.MyDistro.grubKernelBootOptionsFile is not None , 'MyDistro.grubKernelBootOptionsFile must not be None' - assert waagent.MyDistro.grubKernelBootOptionsLine is not None , 'MyDistro.grubKernelBootOptionsLine must not be None' - - - def testMyDistro_restartSshService(self): - """ - Test MyDistro.restartSshService() - """ - cmd = 'service '+ waagent.MyDistro.ssh_service_name + ' status' - sshpid=string.rsplit(waagent.RunGetOutput(cmd)[1],' ',1) - waagent.MyDistro.restartSshService() - assert sshpid is not string.rsplit(waagent.RunGetOutput(cmd)[1],' ',1),'ssh server pid is unchanged.' - -# def testMyDistro_checkPackageInstalled(self): -# """MyDistro can check if WaLinuxAgent package is installed""" -# assert waagent.MyDistro.checkPackageInstalled(waagent.MyDistro.agent_package_name) != 0, waagent.MyDistro.agent_package_name+' is Not Installed.' - -# def testMyDistro_checkPackageUpdateable(self): -# """MyDistro can check if WaLinuxAgent package is updateable to new version.""" -# assert waagent.MyDistro.checkPackageUpdateable(waagent.MyDistro.agent_package_name) == 0 , waagent.MyDistro.agent_package_name+' is not updateable.' - - - def testMyDistro_isSelinuxSystem(self): - """ - MyDistro can perform Selinux operations. - Test MyDistro.isSelinuxSystem, if true then also test: - MyDistro.isSelinuxRunning - MyDistro.setSelinuxEnforce - MyDistro.setSelinuxContext - """ - selinux=waagent.MyDistro.isSelinuxSystem() - if selinux: - assert waagent.MyDistro.isSelinuxRunning(), 'Selinux not running.' - assert waagent.MyDistro.setSelinuxEnforce(0), 'Unable to call setenforce(0).' - assert waagent.MyDistro.setSelinuxContext('./test_waagent.py','unconfined_u:object_r:ssh_home_t:s0'), 'Unable to set Selinux context.' - assert waagent.MyDistro.setSelinuxEnforce(0), 'Unable to call setenforce(1).' - else: - print 'Selinux not installed. - skipping Selinux tests' - - def testMyDistro_load_unload_ata_piix(self): - """ - Attempt to insert and remove ata_piix.ko - by calling MyDistro.load_ata_piix - and MyDistro.unload_ata_piix. - """ - assert waagent.MyDistro.load_ata_piix() == 0, 'Unable to load ata_piix.ko.' - assert waagent.MyDistro.unload_ata_piix() == 0, 'Unable to unload ata_piix.ko.' - - def testMyDistro_publishHostname(self): - """ - Test MyDistro.publishHostname - on success, the distro dependent config - contains the hostname, but currently - this test suceeds if the config files were written - without error. - """ - assert waagent.MyDistro.publishHostname('LENG') == 0, 'Error setting hostname to LENG.' - -# def testMyDistro_registerAgentService(self): -# assert waagent.MyDistro.registerAgentService() == 0, 'Unable to register agent as service.' - - def testMyDistro_setHostname(self): - """ - Test MyDistro.setHostname. - Successfull if hostname is changed. - Reset hostname when finished. - """ - code,oldname = waagent.RunGetOutput('hostname') - waagent.MyDistro.setHostname('HOSTNAMETEST') - code,newname = waagent.RunGetOutput('hostname') - assert 'HOSTNAMETEST' == newname.strip(), 'Unable to set hostname.' - waagent.MyDistro.setHostname(oldname) - - def testMyDistro_checkDependencies(self): - """ - Test MyDistro.checkDependencies succeeds - """ - assert waagent.MyDistro.checkDependencies() == 0 , 'Dependency Check failed.' - - def testMyDistro_startAgentService(self): - """ - Test MyDistro.startAgentService. - """ - assert waagent.MyDistro.startAgentService() == 0, 'Unable to start ' + waagent.MyDistro.agent_service_name - - def testMyDistro_stopAgentService(self): - """ - Test MyDistro.stopAgentService. - """ - assert waagent.MyDistro.stopAgentService() == 0, 'Unable to stop ' + waagent.MyDistro.agent_service_name - - def testMyDistro_deleteRootPassword(self): - """ - Test MyDistro.deleteRootPassword. - Restore the shadow file to previous state when finished. - """ - #copy the shadow - waagent.Run('cp /etc/shadow /etc/shadow.keep') - waagent.MyDistro.deleteRootPassword() - assert waagent.Run('grep LOCK /etc/shadow') == 0 , 'Error removing root password.' - # put shadow back - waagent.Run('mv /etc/shadow.keep /etc/shadow') - - def testFindIn_AppendTo_RemoveFrom_LinuxKernelCmdline(self): - """ - Test LinuxKernelCmdline operations. - Search for 'splish=splash' in the kernel boot options, expect fail. - Add 'splish=splash'. Search for splish=splash expect success. - Remove 'splish=splash', confirm splish=splash absent - """ - m=waagent.FindInLinuxKernelCmdline('splish=splash') - assert not m, '"splish=splash" was found before i put it there!!! edit it to remove "splish=splash" please.' - - waagent.AppendToLinuxKernelCmdline('splish=splash') - m=waagent.FindInLinuxKernelCmdline('splish=splash') - assert m, 'AppendToLinuxKernelCmdline failed, "splish=splash" still not found.' - - waagent.RemoveFromLinuxKernelCmdline('splish=splash') - m=waagent.FindInLinuxKernelCmdline('splish=splash') - assert not m, 'RemoveFromLinuxKernelCmdline failed, "splish=splash" still found.' - -###############Generic waagent tests############## - - def testLogFile(self): - """ - Write a random number with waagent.Log() and read it back. - """ - rnds=str(random.random()) - waagent.Log('testLogFile: '+rnds) - found = rnds in (open('/var/log/waagent.log','rb').read()) - assert found,'Unable to find '+rnds+' in /var/log/waagent.log' - - def testFindReplaceStringInFile(self): - """ - Test file/string operations using - string literals and regular expressions. - Tests: - FindStringInFile - ReplaceStringInFile - - """ - fn='/tmp/junk' - if os.path.exists(fn): - os.remove(fn) - sp='splish splash' - yb='yabba dabba do' - open(fn,'wb').write(sp+' I was taking a bath.') - m=waagent.FindStringInFile(fn,sp) - assert m is not None,'waagent.FindStringInFile() Failed: '+sp+' not found in ' + fn + '.' - src=r'^(.*)('+sp+')(.*)$' - rpl=r'\1 '+sp+'\2 '+yb+' \3' - waagent.ReplaceStringInFile(fn,src,rpl) - m=waagent.FindStringInFile(fn,yb) - assert m is not None,'waagent.ReplaceStringInFile() Failed: '+yb+' not found in ' + fn + '.' - - def testGetFirstActiveNetworkInterfaceNonLoopback(self): - """ - Test GetFirstActiveNetworkInterfaceNonLoopback. - Fail if iface is 'lo' - """ - addr='null' - iface=waagent.GetFirstActiveNetworkInterfaceNonLoopback()[0] - addr=waagent.GetFirstActiveNetworkInterfaceNonLoopback()[1] - assert len(iface)>1,'Interface name too short' - assert iface is not 'lo','Loopback Interface was returned' - print 'iface=' + iface + ' addr=' + addr - - def testTwoLogWritingThreads(self): - """ - Test that two threads writing to the same log - function do not block or scramble messages. - TODO - there is no check for success !!! - """ - for j in range(5): - t1=threading.Thread(target=self.writetothelog,args=(4,)) - t2=threading.Thread(target=self.writetothelog,args=(2,)) - t1.start() - t2.start() - t1.join() - t2.join() - - def testCertificatesParse(self): - """ - TODO - need cert xml from test... - """ - pass - - def testSharedConfigParse(self): - - """ - Test SharedConfig().Parse returns without error. - """ - assert waagent.SharedConfig().Parse(SHAREDCONFIG), 'Error parsing SharedConfig.xml' - - - def testOvfEnvParse(self): - """ - Test OvfEnv().Parse returns without error. - """ - assert waagent.OvfEnv().Parse(OVFXML) is not None , 'Failed to Parse ovfxml' - - def testOvfEnvProcess(self): - """ - We expect the /var/lib/waagent/Certificates.p7m file exists. - Test ovfenv.Process() return other than None. - """ - assert os.path.exists('/var/lib/waagent/Certificates.p7m') , 'We expect the /var/lib/waagent/Certificates.p7m file exists.' - waagent.WaAgent = waagent.Agent() - ovfenv=waagent.OvfEnv().Parse(OVFXML) - waagent.WaAgent.EnvMonitor = waagent.EnvMonitor() - assert ovfenv.Process() is None , 'Failed to Process ovfxml' - waagent.Run("userdel -f -r myUserName") - - def testAgentProvision(self): - """ - TODO - Test Provision in non-fabric environment - """ - waagent.verbose = True - waagent.WaAgent = waagent.Agent() - waagent.WaAgent.EnvMonitor = waagent.EnvMonitor() - waagent.Config = waagent.ConfigurationProvider() - # we cant report our role unless we have one. - waagent.WaAgent.ReportRoleProperties=self.noop - err=waagent.WaAgent.Provision() - assert err == None, 'Provision Failed error ' + str(err) - - -######################################## - - - -OVFXML="""<?xml version="1.0" encoding="utf-8"?> -<Environment xmlns="http://schemas.dmtf.org/ovf/environment/1" xmlns:oe="http://schemas.dmtf.org/ovf/environment/1" xmlns:wa="http://schemas.microsoft.com/windowsazure" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> - - <wa:ProvisioningSection><wa:Version>1.0</wa:Version><LinuxProvisioningConfigurationSet xmlns="http://schemas.microsoft.com/windowsazure" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><ConfigurationSetType>LinuxProvisioningConfiguration</ConfigurationSetType><HostName>egub13-vm</HostName><UserName>myUserName</UserName><UserPassword>mypassword</UserPassword><DisableSshPasswordAuthentication>false</DisableSshPasswordAuthentication><SSH><PublicKeys><PublicKey><Fingerprint>2D97B25D49B98ECC90BF1600D66D68799CFB361E</Fingerprint><Path>/home/myUserName/.ssh/authorized_keys</Path></PublicKey></PublicKeys></SSH></LinuxProvisioningConfigurationSet></wa:ProvisioningSection> - - <wa:PlatformSettingsSection><wa:Version>1.0</wa:Version><PlatformSettings xmlns="http://schemas.microsoft.com/windowsazure" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><KmsServerHostname>kms.core.windows.net</KmsServerHostname></PlatformSettings></wa:PlatformSettingsSection> -</Environment> -""" - -SHAREDCONFIG=""" -<SharedConfig version="1.0.0.0" goalStateIncarnation="1"> - <Deployment name="db00a7755a5e4e8a8fe4b19bc3b330c3" guid="{ce5a036f-5c93-40e7-8adf-2613631008ab}" incarnation="2"> - <Service name="MyVMRoleService" guid="{00000000-0000-0000-0000-000000000000}" /> - <ServiceInstance name="db00a7755a5e4e8a8fe4b19bc3b330c3.1" guid="{d113f4d7-9ead-4e73-b715-b724b5b7842c}" /> - </Deployment> - <Incarnation number="1" instance="MachineRole_IN_0" guid="{a0faca35-52e5-4ec7-8fd1-63d2bc107d9b}" /> - <Role guid="{73d95f1c-6472-e58e-7a1a-523554e11d46}" name="MachineRole" settleTimeSeconds="10" /> - <LoadBalancerSettings timeoutSeconds="0" waitLoadBalancerProbeCount="8"> - <Probes> - <Probe name="MachineRole" /> - <Probe name="55B17C5E41A1E1E8FA991CF80FAC8E55" /> - <Probe name="3EA4DBC19418F0A766A4C19D431FA45F" /> - </Probes> - </LoadBalancerSettings> - <OutputEndpoints> - <Endpoint name="MachineRole:Microsoft.WindowsAzure.Plugins.RemoteAccess.Rdp" type="SFS"> - <Target instance="MachineRole_IN_0" endpoint="Microsoft.WindowsAzure.Plugins.RemoteAccess.Rdp" /> - </Endpoint> - </OutputEndpoints> - <Instances> - <Instance id="MachineRole_IN_0" address="10.115.153.75"> - <FaultDomains randomId="0" updateId="0" updateCount="0" /> - <InputEndpoints> - <Endpoint name="a" address="10.115.153.75:80" protocol="http" isPublic="true" loadBalancedPublicAddress="70.37.106.197:80" enableDirectServerReturn="false" isDirectAddress="false" disableStealthMode="false"> - <LocalPorts> - <LocalPortRange from="80" to="80" /> - </LocalPorts> - </Endpoint> - <Endpoint name="Microsoft.WindowsAzure.Plugins.RemoteAccess.Rdp" address="10.115.153.75:3389" protocol="tcp" isPublic="false" enableDirectServerReturn="false" isDirectAddress="false" disableStealthMode="false"> - <LocalPorts> - <LocalPortRange from="3389" to="3389" /> - </LocalPorts> - </Endpoint> - <Endpoint name="Microsoft.WindowsAzure.Plugins.RemoteForwarder.RdpInput" address="10.115.153.75:20000" protocol="tcp" isPublic="true" loadBalancedPublicAddress="70.37.106.197:3389" enableDirectServerReturn="false" isDirectAddress="false" disableStealthMode="false"> - <LocalPorts> - <LocalPortRange from="20000" to="20000" /> - </LocalPorts> - </Endpoint> - </InputEndpoints> - </Instance> - </Instances> -</SharedConfig> -""" - - -if __name__ == '__main__': - s=unittest.TestLoader().loadTestsFromTestCase(WaagentTestCases) - unittest.TextTestRunner(verbosity=2).run(s) - # import cProfile - # cProfile.run('unittest.TextTestRunner(verbosity=2).run(s)','profile.out') - diff --git a/tests/tools.py b/tests/tools.py index 27e16d3..392f395 100644 --- a/tests/tools.py +++ b/tests/tools.py @@ -21,6 +21,8 @@ import os import sys +from functools import wraps +from azurelinuxagent.utils.osutil import OSUTIL parent = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(parent) @@ -30,9 +32,10 @@ def simple_file_grep(file_path, search_str): if search_str in line: return line -def Mockup(target, name, mock): - def Decorator(func): - def Wrapper(*args, **kwargs): +def mock(target, name, mock): + def decorator(func): + @wraps(func) + def wrapper(*args, **kwargs): origin = getattr(target, name) setattr(target, name, mock) try: @@ -42,10 +45,10 @@ def Mockup(target, name, mock): finally: setattr(target, name, origin) return result - return Wrapper - return Decorator + return wrapper + return decorator -class MockFunc(): +class MockFunc(object): def __init__(self, name='', retval=None): self.name = name self.retval = retval @@ -56,6 +59,7 @@ class MockFunc(): self.kwargs = kwargs return self.retval -def Dummy(): - pass +#Mock osutil so that the test of other part will be os unrelated +OSUTIL.get_lib_dir = MockFunc(retval='/tmp') +OSUTIL.get_ext_log_dir = MockFunc(retval='/tmp/log') |