diff options
author | Ben Howard <ben.howard@ubuntu.com> | 2014-02-14 11:55:14 -0700 |
---|---|---|
committer | usd-importer <ubuntu-server@lists.ubuntu.com> | 2014-02-17 18:03:31 +0000 |
commit | 3e535e3f3b9b1e44ed9e2394821a4f4799c8245b (patch) | |
tree | 83c66978db367943a2f8fe17b6921ad894c40b1f /tests | |
parent | b10340ae4082fc4904a41f90d4b4e6d2179ef2ff (diff) | |
download | vyos-walinuxagent-3e535e3f3b9b1e44ed9e2394821a4f4799c8245b.tar.gz vyos-walinuxagent-3e535e3f3b9b1e44ed9e2394821a4f4799c8245b.zip |
Import patches-unapplied version 2.0.3-0ubuntu1 to ubuntu/trusty-proposed
Imported using git-ubuntu import.
Changelog parent: b10340ae4082fc4904a41f90d4b4e6d2179ef2ff
New changelog entries:
* Update to latest upstream version 2.0.3 (LP: #1249052).
- use python-setuptools to do installation, dropping the {pre,post}insts
- dropped the walinuxagent-datasaver packages as no longer needed.
- use packaged default configuration file by default.
* Include patches from 1.3.x series
- debian/patches/disable_provisioning.patch: disable provisioning
features infavor of WALinuxAgent cloud-init support.
- debian/patches/disable-udev-rules.patch: disable UDEV rule mangling
* Include default Cloud-init configuration
- configures Cloud-init to use the default Azure Datasource
Diffstat (limited to 'tests')
-rwxr-xr-x | tests/azure_test.py | 687 | ||||
-rwxr-xr-x | tests/test_waagent.py | 386 |
2 files changed, 1073 insertions, 0 deletions
diff --git a/tests/azure_test.py b/tests/azure_test.py new file mode 100755 index 0000000..fd152b7 --- /dev/null +++ b/tests/azure_test.py @@ -0,0 +1,687 @@ +#!/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/test_waagent.py b/tests/test_waagent.py new file mode 100755 index 0000000..11510b6 --- /dev/null +++ b/tests/test_waagent.py @@ -0,0 +1,386 @@ +#!/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') + |