def wait_for_lock(): if hasattr(env, 'deploy_user'): lock_user = env.deploy_user else: lock_user = env.user LOCK_ID = 'u:{user} h:{host} pid:{pid}'.format(user=lock_user, host=socket.gethostname(), pid=str(os.getpid())) sleep_time = 0.1 timeout = 120 start_time = time.time() with settings(warn_only=True): while True: wait_time = time.time() - start_time # break if the lockfile is removed or if it belongs to this pid # if it exists lock_status will have the file's contents with hide('running', 'stdout', 'stderr', 'warnings'): lock_status = run("test ! -f {lfile} || " "(cat {lfile} && " 'grep -q "{lid}" {lfile})'.format( lfile=LOCK_FILE, lid=LOCK_ID)) if lock_status.succeeded: noopable(sudo)('echo "{0}" > {1}'.format( LOCK_ID, LOCK_FILE)) notify("Took lock") break elif wait_time >= timeout: abort("Timeout expired, giving up") lock_create_time = run("stat -c %Y {0}".format(LOCK_FILE)) delta = time.time() - float(lock_create_time) (dhour, dsec) = divmod(delta, 3600) notify(""" !! Deploy lockfile already exists ({lockfile}) !! Waiting: {wait}s Lockfile info: [ {owner} ] Lock created: {dhour}h{dmin}m ago """.format( lockfile=LOCK_FILE, wait=int(timeout - wait_time), owner=lock_status, dhour=int(dhour), dmin=int(dsec / 60), )) time.sleep(sleep_time) sleep_time *= 2 if sleep_time > MAX_SLEEP_TIME: sleep_time = MAX_SLEEP_TIME
def wrapper(*args, **kwargs): elb = boto.connect_elb() elbs = elb.get_all_load_balancers() execute('locks.wait_for_all_locks') inst_id = instance_id() tags = ['task:' + func.__name__] + instance_tags(inst_id) active_lbs = sorted(lb for lb in elbs if inst_id in [info.id for info in lb.instances]) timer = partial(dog_stats_api.timer, tags=tags) # Remove this node from the LB for lb in active_lbs: notify("Removing {id} from {lb}".format(id=inst_id, lb=lb)) with timer('rolling.deregister_instance'): noopable(lb.deregister_instances)([inst_id]) noopable(await_elb_instance_state)(lb, inst_id, "OutOfService") # Execute the operation func(*args, **kwargs) # Add this node back to the LBs for lb in active_lbs: notify("Adding {id} to {lb}".format(id=inst_id, lb=lb)) with timer('rolling.register_instance'): noopable(lb.register_instances)([inst_id]) with timer('rolling.wait_for_start'): # Wait for the node to come online in the LBs for lb in active_lbs: noopable(await_elb_instance_state)(lb, inst_id, "InService")
def status(): """ Drops {0} which is a json formatted file that contains a status message that will be displayed to all users on the on the courseware for a single course or for all courses if 'global' is set. Message(s) are entered or removed interactively on the console. Example usage: $ fab groups:prod_edx status """.format(status_file) with hide('running', 'stdout', 'stderr', 'warnings'): env_json = sudo("cat /opt/wwc/lms-xml.env.json") course_listings = json.loads(env_json)['COURSE_LISTINGS'] course_ids = [course_id for course_list in course_listings.itervalues() for course_id in course_list] course_ids = ['global'] + course_ids with no_ts(): course_status = None with settings(warn_only=True): cur_status = noopable(sudo)('cat {0}'.format(status_file)) try: course_status = json.loads(cur_status) # add empty entries for courses not in the list empty_entries = set(course_ids) - set(course_status.keys()) course_status.update({entry: '' for entry in list(empty_entries)}) except ValueError: fastprint(red("Not a valid json file, overwritting\n")) if course_status is None: course_status = {course: '' for course in course_ids} new_status = multi_choose_with_input( 'Set the status message, blank to disable:', course_status) if new_status is not None: # remove empty entries new_status = {entry: new_status[entry] for entry in new_status if len(new_status[entry]) > 1} with unsquelched(): if not console.confirm( 'Setting new status message:\n{0}'.format( blue(str(new_status), bold=True)), default=False): abort('Operation cancelled by user') with tempfile.NamedTemporaryFile(delete=True) as f: f.write(json.dumps(new_status)) f.flush() execute(update_status, f.name) else: abort('Operation cancelled by user')
def wrapper(*args, **kwargs): elb = boto.connect_elb() elbs = elb.get_all_load_balancers() execute('locks.wait_for_all_locks') inst_id = instance_id() tags = ['task:' + func.__name__] + instance_tags(inst_id) active_lbs = sorted( lb for lb in elbs if inst_id in [info.id for info in lb.instances] ) timer = partial(dog_stats_api.timer, tags=tags) # Remove this node from the LB for lb in active_lbs: notify("Removing {id} from {lb}".format(id=inst_id, lb=lb)) with timer('rolling.deregister_instance'): noopable(lb.deregister_instances)([inst_id]) noopable(await_elb_instance_state)(lb, inst_id, "OutOfService") # Execute the operation func(*args, **kwargs) # Add this node back to the LBs for lb in active_lbs: notify("Adding {id} to {lb}".format(id=inst_id, lb=lb)) with timer('rolling.register_instance'): noopable(lb.register_instances)([inst_id]) with timer('rolling.wait_for_start'): # Wait for the node to come online in the LBs for lb in active_lbs: noopable(await_elb_instance_state)(lb, inst_id, "InService")
def unmaintain_service(service): """ Removes a specified edxapp service from maintenance mode by replacing the appropriate link in /etc/nginx/sites-enabled. """ if service not in services: raise Exception("Provided service not in the service inventory. " "Acceptable values are {services}".format( services=services )) noopable(sudo)("rm -f /etc/nginx/sites-enabled/{service}-maintenance".format( service=service)) noopable(sudo)("ln -s /etc/nginx/sites-available/{service}" " /etc/nginx/sites-enabled/{service}".format( service=service)) noopable(sudo)("service nginx reload")
def maintain_service(service): """ Puts a specified edxapp service into maintenance mode by replacing its nginx sites-enabled link with a link to the maintenance vhost. """ if service not in services: raise Exception("Provided service not in the service inventory. " "Acceptable values are {services}".format( services=services )) noopable(sudo)("rm -f /etc/nginx/sites-enabled/{service}".format( service=service)) noopable(sudo)("ln -s /etc/nginx/sites-available/{service}-maintenance" " /etc/nginx/sites-enabled/{service}-maintenance".format( service=service)) noopable(sudo)("service nginx reload")
def remove_lock(): noopable(sudo)("test ! -f {0} || rm {0}".format(LOCK_FILE))
def remove_status(): noopable(sudo)('rm -f {0}'.format(status_file))
def update_status(fjson): print status_file noopable(put)(fjson, status_file, use_sudo=True)
def apt_get_clean(): """ Runs apt-get clean on a remote server """ noopable(sudo)('apt-get clean')
def mako_template_cache(): noopable(sudo)('service gunicorn stop') noopable(sudo)('rm -rf /tmp/tmp*mako') noopable(sudo)('service gunicorn start')
def status(): """ Drops {0} which is a json formatted file that contains a status message that will be displayed to all users on the on the courseware for a single course or for all courses if 'global' is set. Message(s) are entered or removed interactively on the console. Example usage: $ fab groups:prod_edx status """.format(status_file) with hide('running', 'stdout', 'stderr', 'warnings'): env_json = sudo("cat /opt/wwc/lms-xml.env.json") course_listings = json.loads(env_json)['COURSE_LISTINGS'] course_ids = [ course_id for course_list in course_listings.itervalues() for course_id in course_list ] course_ids = ['global'] + course_ids with no_ts(): course_status = None with settings(warn_only=True): cur_status = noopable(sudo)('cat {0}'.format(status_file)) try: course_status = json.loads(cur_status) # add empty entries for courses not in the list empty_entries = set(course_ids) - set(course_status.keys()) course_status.update({entry: '' for entry in list(empty_entries)}) except ValueError: fastprint(red("Not a valid json file, overwritting\n")) if course_status is None: course_status = {course: '' for course in course_ids} new_status = multi_choose_with_input( 'Set the status message, blank to disable:', course_status) if new_status is not None: # remove empty entries new_status = { entry: new_status[entry] for entry in new_status if len(new_status[entry]) > 1 } with unsquelched(): if not console.confirm( 'Setting new status message:\n{0}'.format( blue(str(new_status), bold=True)), default=False): abort('Operation cancelled by user') with tempfile.NamedTemporaryFile(delete=True) as f: f.write(json.dumps(new_status)) f.flush() execute(update_status, f.name) else: abort('Operation cancelled by user')
def set_maintenance(value): noopable(put)(StringIO(json.dumps({'maintenance': value})), '/etc/facter/facts.d/mitx_maintenance.json', use_sudo=True)