|
| 1 | +import time |
| 2 | +import os |
| 3 | + |
| 4 | +from fabric.contrib.project import rsync_project |
| 5 | +from fabric.decorators import task |
| 6 | +from fabric.operations import sudo, run, put, get |
| 7 | +from fabric.context_managers import cd, shell_env, settings, prefix |
| 8 | +from fabric.tasks import Task |
| 9 | +from saws import AwsManager |
| 10 | +from saws.tasks import ebs_mount |
| 11 | +from helpers import set_rwx_permissions, set_rx_permisions, fcmd, parser |
| 12 | + |
| 13 | + |
| 14 | +@task |
| 15 | +def deploy_test_local_copy(dest): |
| 16 | + from ocgis.test.base import TestBase |
| 17 | + |
| 18 | + dest = os.path.expanduser(dest) |
| 19 | + if not os.path.exists(dest): |
| 20 | + os.path.makedirs(dest) |
| 21 | + tdata = TestBase.get_tst_data() |
| 22 | + tdata.copy_files(dest, verbose=True) |
| 23 | + |
| 24 | + |
| 25 | +@task() |
| 26 | +def deploy_test_rsync(): |
| 27 | + remote_dir = parser.get('server', 'dir_data') |
| 28 | + local_dir = os.getenv('OCGIS_DIR_TEST_DATA') |
| 29 | + |
| 30 | + # we want to create the local test directory on the remote server: |
| 31 | + # http://docs.fabfile.org/en/latest/api/contrib/project.html#fabric.contrib.project.rsync_project |
| 32 | + assert not local_dir.endswith('/') |
| 33 | + |
| 34 | + # update permissions so files may be copied |
| 35 | + local_dir_name = os.path.split(local_dir)[1] |
| 36 | + test_data_path = os.path.join(remote_dir, local_dir_name) |
| 37 | + set_rwx_permissions(test_data_path) |
| 38 | + try: |
| 39 | + # synchronize the project |
| 40 | + rsync_project(remote_dir, local_dir=local_dir) |
| 41 | + finally: |
| 42 | + # remove write permissions on the files/directories |
| 43 | + set_rx_permisions(test_data_path) |
| 44 | + |
| 45 | + |
| 46 | +@task |
| 47 | +def ebs_mkfs(): |
| 48 | + """Make a file system on a newly attached device.""" |
| 49 | + |
| 50 | + cmd = ['mkfs', '-t', 'ext4', parser.get('aws', 'ebs_mount_name')] |
| 51 | + fcmd(sudo, cmd) |
| 52 | + |
| 53 | + |
| 54 | +@task |
| 55 | +def list_storage(): |
| 56 | + """List storage size of connected devices.""" |
| 57 | + |
| 58 | + fcmd(run, ['lsblk']) |
| 59 | + |
| 60 | +@task |
| 61 | +def put_file(local_path, remote_path): |
| 62 | + """ |
| 63 | + Put a file on the remote server: local_path,remote_path |
| 64 | + """ |
| 65 | + |
| 66 | + put(local_path=local_path, remote_path=remote_path) |
| 67 | + |
| 68 | + |
| 69 | +@task |
| 70 | +def remove_dir(path, use_sudo='false'): |
| 71 | + """Remove the source directory.""" |
| 72 | + |
| 73 | + cmd = ['rm', '-r', path] |
| 74 | + if use_sudo == 'true': |
| 75 | + fmeth = sudo |
| 76 | + elif use_sudo == 'false': |
| 77 | + fmeth = run |
| 78 | + else: |
| 79 | + raise NotImplementedError(use_sudo) |
| 80 | + |
| 81 | + fcmd(fmeth, cmd) |
| 82 | + |
| 83 | + |
| 84 | +class RunAwsTests(Task): |
| 85 | + """ |
| 86 | + Run tests on remote server and return the path to a local log file of tests results. |
| 87 | + """ |
| 88 | + name = 'run_aws_tests' |
| 89 | + |
| 90 | + def run(self, path_local_log=None, branch='next', sched='false', launch_pause='false'): |
| 91 | + """ |
| 92 | + :param str path_local_log: Path to the local log file copied from the remote server. If ``None``, do not copy |
| 93 | + remote log file. |
| 94 | + :param str branch: Target git branch to test. |
| 95 | + :param str sched: If ``'true'``, run tests only once. Otherwise, run tests at 23:00 hours daily. |
| 96 | + :param str launch_pause: If ``'true'``, pause at a breakpoint after launching the instance and mounting the data |
| 97 | + volume. Continuing from the breakpoint will terminate the instance and destroy the volume. |
| 98 | + """ |
| 99 | + |
| 100 | + import schedule |
| 101 | + from logbook import Logger |
| 102 | + |
| 103 | + self.log = Logger('nesii-testing') |
| 104 | + |
| 105 | + self.path_local_log = path_local_log |
| 106 | + self.branch = branch |
| 107 | + self.launch_pause = launch_pause |
| 108 | + |
| 109 | + if self.launch_pause == 'true': |
| 110 | + self.log.info('launching instance then pausing') |
| 111 | + self._run_tests_(should_email=False) |
| 112 | + else: |
| 113 | + if sched == 'true': |
| 114 | + self.log.info('begin continous loop') |
| 115 | + schedule.every().day.at("6:00").do(self._run_tests_, should_email=True) |
| 116 | + while True: |
| 117 | + schedule.run_pending() |
| 118 | + time.sleep(1) |
| 119 | + else: |
| 120 | + self.log.info('running tests once') |
| 121 | + self._run_tests_(should_email=True) |
| 122 | + |
| 123 | + def _run_tests_(self, should_email=False): |
| 124 | + aws_src = os.getenv('OCGIS_SIMPLEAWS_SRC') |
| 125 | + aws_conf = os.getenv('OCGIS_CONF_PATH') |
| 126 | + aws_testing_section = 'aws-testing' |
| 127 | + |
| 128 | + ebs_volumesize = int(parser.get(aws_testing_section, 'ebs_volumesize')) |
| 129 | + ebs_snapshot = parser.get(aws_testing_section, 'ebs_snapshot') |
| 130 | + ebs_mount_name = parser.get(aws_testing_section, 'ebs_mount_name') |
| 131 | + ebs_placement = parser.get(aws_testing_section, 'ebs_placement') |
| 132 | + test_results_path = parser.get(aws_testing_section, 'test_results_path') |
| 133 | + test_instance_name = parser.get(aws_testing_section, 'test_instance_name') |
| 134 | + test_instance_type = parser.get(aws_testing_section, 'test_instance_type') |
| 135 | + test_image_id = parser.get(aws_testing_section, 'test_image_id') |
| 136 | + dest_email = parser.get(aws_testing_section, 'dest_email') |
| 137 | + dir_clone = parser.get('server', 'dir_clone') |
| 138 | + key_name = parser.get('simple-aws', 'key_name') |
| 139 | + |
| 140 | + import sys |
| 141 | + sys.path.append(aws_src) |
| 142 | + import saws |
| 143 | + import ipdb |
| 144 | + |
| 145 | + am = saws.AwsManager(aws_conf) |
| 146 | + |
| 147 | + self.log.info('launching instance') |
| 148 | + instance = am.launch_new_instance(test_instance_name, image_id=test_image_id, instance_type=test_instance_type, |
| 149 | + placement=ebs_placement) |
| 150 | + |
| 151 | + with settings(host_string=instance.ip_address, disable_known_hosts=True, connection_attempts=10): |
| 152 | + try: |
| 153 | + self.log.info('creating volume') |
| 154 | + volume = am.conn.create_volume(ebs_volumesize, ebs_placement, snapshot=ebs_snapshot) |
| 155 | + am.wait_for_status(volume, 'available') |
| 156 | + try: |
| 157 | + self.log.info('attaching volume') |
| 158 | + am.conn.attach_volume(volume.id, instance.id, ebs_mount_name, dry_run=False) |
| 159 | + am.wait_for_status(volume, 'in-use') |
| 160 | + |
| 161 | + ebs_mount() |
| 162 | + |
| 163 | + if self.launch_pause == 'true': |
| 164 | + self.log.info('pausing. continue to terminate instance...') |
| 165 | + msg = 'ssh -i ~/.ssh/{0}.pem ubuntu@{1}'.format(key_name, instance.public_dns_name) |
| 166 | + self.log.info(msg) |
| 167 | + ipdb.set_trace() |
| 168 | + else: |
| 169 | + path = os.path.join(dir_clone, parser.get('git', 'name')) |
| 170 | + test_target = os.path.join(path, 'src', 'ocgis', 'test') |
| 171 | + # test_target = os.path.join(path, 'src', 'ocgis', 'test', 'test_simple') |
| 172 | + nose_runner = os.path.join(path, 'fabfile', 'nose_runner.py') |
| 173 | + path_src = os.path.join(path, 'src') |
| 174 | + with cd(path): |
| 175 | + fcmd(run, ['git', 'pull']) |
| 176 | + fcmd(run, ['git', 'checkout', self.branch]) |
| 177 | + fcmd(run, ['git', 'pull']) |
| 178 | + with cd(path_src): |
| 179 | + with shell_env(OCGIS_TEST_TARGET=test_target): |
| 180 | + fcmd(run, ['python', nose_runner]) |
| 181 | + if self.path_local_log is not None: |
| 182 | + get(test_results_path, local_path=self.path_local_log) |
| 183 | + |
| 184 | + ebs_umount() |
| 185 | + |
| 186 | + finally: |
| 187 | + self.log.info('detaching volume') |
| 188 | + volume.detach(force=True) |
| 189 | + am.wait_for_status(volume, 'available') |
| 190 | + self.log.info('deleting volume') |
| 191 | + volume.delete() |
| 192 | + finally: |
| 193 | + self.log.info('terminating instance') |
| 194 | + instance.terminate() |
| 195 | + |
| 196 | + if should_email and self.launch_pause == 'false' and self.path_local_log is not None: |
| 197 | + self.log.info('sending email') |
| 198 | + with open(self.path_local_log, 'r') as f: |
| 199 | + content = f.read() |
| 200 | + am.send_email(dest_email, dest_email, 'OCGIS_AWS', content) |
| 201 | + |
| 202 | + self.log.info('success') |
| 203 | + |
| 204 | + |
| 205 | +r = RunAwsTests() |
| 206 | + |
| 207 | + |
| 208 | +@task |
| 209 | +def test_node_launch(instance_type='m3.xlarge'): |
| 210 | + am = AwsManager() |
| 211 | + instance_name = 'ocgis-test-node' |
| 212 | + image_id = 'ami-d1878fe1' |
| 213 | + ebs_mount_dir = '~/data' |
| 214 | + ebs_mount_name = '/dev/xvdf' |
| 215 | + instance = am.launch_new_instance(instance_name, image_id=image_id, instance_type=instance_type) |
| 216 | + test_node_ebs_mount(instance_name=instance_name, ebs_mount_name=ebs_mount_name, ebs_mount_dir=ebs_mount_dir) |
| 217 | + print am.get_ssh_command(instance=instance) |
| 218 | + |
| 219 | + |
| 220 | +@task |
| 221 | +def test_node_ebs_mount(instance_name='ocgis-test-node', ebs_mount_name='/dev/xvdg', ebs_mount_dir='~/data'): |
| 222 | + am = AwsManager() |
| 223 | + instance = am.get_instance_by_name(instance_name) |
| 224 | + kwargs = {'mount_name': ebs_mount_name, 'mount_dir': ebs_mount_dir} |
| 225 | + am.do_task(ebs_mount, instance=instance, kwargs=kwargs) |
| 226 | + |
| 227 | + |
| 228 | +@task |
| 229 | +def test_node_run_tests(instance_name='ocgis-test-node', branch='next', failed='false'): |
| 230 | + am = AwsManager() |
| 231 | + instance = am.get_instance_by_name(instance_name) |
| 232 | + tcenv = 'test-ocgis' |
| 233 | + texclude = '!slow,!remote,!esmpy7' |
| 234 | + tgdal_data = '/home/ubuntu/anaconda/envs/{0}/share/gdal'.format(tcenv) |
| 235 | + tocgis_dir_shpcabinet = '/home/ubuntu/data/ocgis_test_data/shp' |
| 236 | + tocgis_dir_test_data = '/home/ubuntu/data/ocgis_test_data/' |
| 237 | + tsrc = '~/git/ocgis/src' |
| 238 | + |
| 239 | + def _run_(): |
| 240 | + senv = dict(OCGIS_DIR_SHPCABINET=tocgis_dir_shpcabinet, OCGIS_DIR_TEST_DATA=tocgis_dir_test_data, |
| 241 | + GDAL_DATA=tgdal_data) |
| 242 | + with shell_env(**senv): |
| 243 | + with prefix('source activate {0}'.format(tcenv)): |
| 244 | + with cd(tsrc): |
| 245 | + run('git checkout next') |
| 246 | + run('git pull') |
| 247 | + if failed == 'true': |
| 248 | + cmd = 'cp .noseids /tmp; git checkout {tbranch}; git pull; nosetests -vs --failed --with-id -a {texclude} ocgis/test' |
| 249 | + else: |
| 250 | + cmd = 'cp .noseids /tmp; rm .noseids; git checkout {tbranch}; git pull; nosetests -vs --with-id -a {texclude} ocgis/test' |
| 251 | + cmd = cmd.format(tbranch=branch, texclude=texclude) |
| 252 | + run(cmd) |
| 253 | + |
| 254 | + am.do_task(_run_, instance=instance) |
| 255 | + |
| 256 | + |
| 257 | +@task |
| 258 | +def test_node_terminate(): |
| 259 | + am = AwsManager() |
| 260 | + instance_name = 'ocgis-test-node' |
| 261 | + instance = am.get_instance_by_name(instance_name) |
| 262 | + instance.terminate() |
0 commit comments