Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

weblauncher #8

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,13 @@ My cluster is not done launching one (or several) of my nodes. What did I do wro
Nothing. EC2 and Rackspace do this from time to time. You can either continue on to do
basic testing, or terminate this cluster and try again. Using EC2 and Rackspace off it's
peak hours helps in this scenario, in general.

## weblauncher

the weblauncher directory contains a trivial web front end for the launcher. weblauncher/weblauncher.py --help will give details. If a results_directory is specified, all cluster launches are recorded in a format that the demoservice can pick up to automatically terminate the cluster after a specified TTL.

## demoservice

the scripts/demoservice can be copied to a crond job to be run every few minutes. If you specifiy the same results directory (defaults to /tmp/wl) used by the weblauncher (or from the command line) the demoservice script will terminate clusters whose TTL has expired.


67 changes: 40 additions & 27 deletions cassandralauncher/cassandralauncher.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import tempfile
import time
import urllib2
import uuid

import ec2
import common
Expand Down Expand Up @@ -359,26 +360,6 @@ def install_opsc_agents(user):

#################################

#################################
# Log code for private stats

def running_log(reservation, demotime):
"""Logs usage data for personal stats."""

loginfo = [
'Running' if config.get('Cassandra', 'demo') == 'True' else 'Ignore',
config.get('Shared', 'handle'),
str(demotime),
str(time.time()),
str(reservation.id)
]
logline = ",".join(loginfo) + '\n'

with open('running.log', 'a') as f:
f.write(logline)

#################################

#################################
# Argument parsing

Expand Down Expand Up @@ -466,8 +447,8 @@ def running_log(reservation, demotime):
},
'demotime': {
'Section': 'Cassandra',
'Prompt': 'Time (in hours) for the cluster to live',
'Help': 'For use with DemoService'
'Prompt': 'Time (in seconds) for the cluster to live',
'Help': 'For use with demoservice'
},
'instance_type': {
'Section': 'EC2',
Expand All @@ -490,7 +471,18 @@ def running_log(reservation, demotime):
'Prompt': 'QA',
'Action': 'store_true',
'Help': 'Upload QA scripts.'
},
'result_directory':{
'Section': 'CLI',
'Prompt': 'NoPrompts',
'Help': 'log results of launch to a file in the specified directory'
},
'launch_id':{
'Section': 'CLI',
'Prompt': 'NoPrompts',
'Help': 'use specified id in combination with result_directory'
}

}

def type_checker(option, read_option, type_check, passive=False):
Expand Down Expand Up @@ -670,11 +662,11 @@ def main():
print

# Included for the experimental DemoService that requires demoservice.py to always be running
demotime = -1
demotime = 0
if config.get('Cassandra', 'demo') == 'True':
print "Your configuration file is set to launch a demo cluster for a specified time."
demotime = check_cascading_options('demotime', float)
print "If the demo service is running, this cluster will live for %s hour(s)." % demotime
print "If demosercie is running, this cluster will live for %s seconds(s)." % demotime
print

if check_cascading_options('installopscenter', optional=True) == 'False':
Expand Down Expand Up @@ -709,12 +701,33 @@ def main():
private_ips, public_ips, reservation = clusterinfo

# Log clusterinfo
running_log(reservation, demotime)
if check_cascading_options('result_directory', optional=True):
result_directory = check_cascading_options('result_directory')
user_data += ' --result_directory %s' % result_directory

launch_id = str(uuid.uuid4())
if check_cascading_options('launch_id', optional=True):
launch_id = check_cascading_options('launch_id')
user_data += ' --launch_id %s' % launch_id

if not os.path.exists(result_directory):
os.mkdir(result_directory)

tmpfile = os.path.join(result_directory, "%s.tmp" % launch_id)
dstfile = os.path.join(result_directory, "%s.results" % launch_id)
with open(tmpfile, 'w') as f:
f.write("reservation_id=%s\n" % reservation.id)
f.write("ttl_seconds=%s\n" % demotime)
f.write("launch_time=%s\n" % time.time())
if check_cascading_options('installopscenter', optional=True) != 'False':
f.write("opsc_ip=%s\n" % public_ips[0])
f.write("opsc_port=%s\n" % opscenterinterface)
os.rename(tmpfile, dstfile)

if check_cascading_options('installopscenter', optional=True) != 'False':
# Print OpsCenter url
print "OpsCenter Address:"
print "http://%s:%s" % (public_ips[0], opscenterinterface)
url = "http://%s:%s" % (public_ips[0], opscenterinterface)
print "OpsCenter URL: %s" % url
print "Note: You must wait 60 seconds after Cassandra becomes active to access OpsCenter."
print

Expand Down
15 changes: 2 additions & 13 deletions cassandralauncher/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,8 @@ def header():
configfile = os.path.join(os.path.expanduser('~'), '.clusterlauncher.conf')
if not os.path.exists(configfile):
# Look for the configuration file in /etc/clusterlauncher
defaultfile = os.path.join('/etc', 'cassandralauncher', 'clusterlauncher.conf')
configfile = os.path.join(os.path.expanduser('~'), '.clusterlauncher.conf')
shutil.copyfile(defaultfile, configfile)

# Exit the program to alert the user that the conf file must be properly set with authentications
# before continuing
sys.stderr.write("A copy of the default configuration file located at:\n")
sys.stderr.write(' %s\n' % defaultfile)
sys.stderr.write("was now copied to:\n")
sys.stderr.write(' %s\n' % configfile)
sys.stderr.write("Please ensure that all default settings are correct and filled in before continuing.\n")
sys.exit(1)

configfile = os.path.join('/etc', 'cassandralauncher', 'clusterlauncher.conf')
sys.stdout.write("currently using %s for config, copy to ~/.clusterlauncher.conf to override\n")
if not os.path.exists(configfile):
# Exit since we still have not found the configuration file
sys.stderr.write("Please setup your authentication configurations. Order of importance:\n")
Expand Down
42 changes: 42 additions & 0 deletions cassandralauncher/demoservice.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#!/usr/bin/env python

import os
import sys
import imp
import boto
import time
from glob import glob

def checkAndCloseExpiredInstances(results_directory):
#common = imp.load_module('common', *imp.find_module('common', [os.path.join(os.path.dirname(__file__), '..', 'cassandralauncher')]))
import common
config, KEY_PAIR, PEM_HOME, HOST_FILE, PEM_FILE = common.header()
conn = boto.connect_ec2(config.get('EC2', 'aws_access_key_id'), config.get('EC2', 'aws_secret_access_key'))
reservations = dict(map(lambda x: (x.id, x), conn.get_all_instances()))
result_files = glob(os.path.join(results_directory, '*.results'))

print 'checking %s' % result_files
for result_file in result_files:
results = None
with open(result_file, 'r') as f:
results = dict(map(lambda l: l.strip().split('='), f.readlines()))

if time.time() - float(results['launch_time']) > float(results['ttl_seconds']):
res = reservations.get(results['reservation_id'])
if res != None:
print 'killing %s' % res.id
instances = [i.id for i in res.instances if i.state != 'terminated']
if len(instances) > 0:
conn.terminate_instances(instances)
else:
os.rename(result_file, '%s.%s' % (result_file, 'done'))

def cli_main():
from optparse import OptionParser
parser = OptionParser()
parser.add_option('-r', '--results_directory', dest='results_directory', help='direcotry to search for running clusters', metavar='RESULTS_DIRECTORY', default='/tmp/wl')
(options, args) = parser.parse_args()
checkAndCloseExpiredInstances(options.results_directory)

if __name__ == '__main__':
cli_main()
3 changes: 2 additions & 1 deletion cassandralauncher/ec2.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ def create_cluster(aws_access_key_id, aws_secret_access_key, reservation_size, i

try:
# Create the EC2 cluster
print 'Launching cluster...'
print 'Launching cluster ...'
start_time = time.time()
try:
reservation = conn.run_instances(image,
Expand All @@ -180,6 +180,7 @@ def create_cluster(aws_access_key_id, aws_secret_access_key, reservation_size, i
key_name=key_pair,
placement=placement,
security_groups=['DataStax'], user_data=user_data)

except boto.exception.EC2ResponseError:
print_boto_error()

Expand Down
13 changes: 0 additions & 13 deletions demoservice/README.md

This file was deleted.

74 changes: 0 additions & 74 deletions demoservice/demoservice.py

This file was deleted.

4 changes: 4 additions & 0 deletions scripts/demoservice
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env bash

set -e
/path/to/demoservice.py -r /tmp/wl
72 changes: 72 additions & 0 deletions weblauncher/css/weblauncher.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
body{
font-family:"Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif;
font-size:12px;
}

p, h1, form, button{border:0; margin:0; padding:0;}

.spacer{clear:both; height:1px;}

.form{
margin:0 auto;
width:400px;
padding:14px;
}

#stylized{
border:solid 2px #b7ddf2;
background:#ebf4fb;
}

#stylized h1 {
font-size:14px;
font-weight:bold;
margin-bottom:8px;
}

#stylized p{
font-size:11px;
color:#666666;
margin-bottom:20px;
border-bottom:solid 1px #b7ddf2;
padding-bottom:10px;
}

#stylized label{
display:block;
font-weight:bold;
text-align:right;
width:140px;
float:left;
}

#stylized .small{
color:#666666;
display:block;
font-size:11px;
font-weight:normal;
text-align:right;
width:140px;
}

#stylized input{
float:left;
font-size:12px;
padding:4px 2px;
border:solid 1px #aacfe4;
width:200px;
margin:2px 0 20px 10px;
}

#stylized button{
clear:both;
margin-left:150px;
width:125px;
height:31px;
background:#666666;
text-align:center;
line-height:31px;
color:#FFFFFF;
font-size:11px;
font-weight:bold;
}