|
#!/usr/bin/env python |
|
import os |
|
import time |
|
import re |
|
import argparse |
|
import errno |
|
from socket import error as socket_error |
|
from getpass import getpass |
|
import paramiko |
|
import pyrax |
|
|
|
nova = None |
|
|
|
|
|
def get_flavor(name): |
|
return nova.flavors.find(name=name) |
|
|
|
|
|
def get_image(name): |
|
return nova.images.find(name=name) |
|
|
|
|
|
def create_web_server(flavor=None, image=None, key_name=None): |
|
server = nova.servers.create(name='flasky', flavor=flavor.id, |
|
image=image.id, key_name=key_name) |
|
print 'Building, please wait...' |
|
|
|
# wait for server create to be complete |
|
while server.status == 'BUILD': |
|
time.sleep(5) |
|
server = nova.servers.get(server.id) # refresh server |
|
|
|
# check for errors |
|
if server.status != 'ACTIVE': |
|
raise RuntimeError('Server did not boot, status=' + server.status) |
|
|
|
# the server was assigned IPv4 and IPv6 addresses, locate the IPv4 address |
|
ip_address = None |
|
for network in server.networks['public']: |
|
if re.match('\d+\.\d+\.\d+\.\d+', network): |
|
ip_address = network |
|
break |
|
if ip_address is None: |
|
raise RuntimeError('No IP address assigned!') |
|
print 'Server is running at IP address ' + ip_address |
|
return ip_address |
|
|
|
|
|
def install_flasky(ip_address, gmail_username, gmail_password): |
|
print 'Installing Flasky...' |
|
|
|
# establish a SSH connection |
|
ssh = paramiko.SSHClient() |
|
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) |
|
ssh.load_system_host_keys() |
|
retries_left = 3 |
|
while True: |
|
try: |
|
ssh.connect(ip_address, username='root') |
|
break |
|
except socket_error as e: |
|
if e.errno != errno.ECONNREFUSED or retries_left <= 1: |
|
raise e |
|
time.sleep(10) # wait 10 seconds and retry |
|
retries_left -= 1 |
|
|
|
# upload deployment script |
|
ftp = ssh.open_sftp() |
|
ftp.put('install-flasky.sh', 'install-flasky.sh') |
|
ftp.chmod('install-flasky.sh', 0544) |
|
|
|
# deploy |
|
stdin, stdout, stderr = ssh.exec_command( |
|
'./install-flasky.sh "{0}" "{1}"'.format( |
|
gmail_username, gmail_password)) |
|
status = stdout.channel.recv_exit_status() |
|
open('stdout.log', 'wt').write(stdout.read()) |
|
open('stderr.log', 'wt').write(stderr.read()) |
|
if status != 0: |
|
raise RuntimeError( |
|
'Deployment script returned status {0}.'.format(status)) |
|
|
|
|
|
def process_args(): |
|
parser = argparse.ArgumentParser( |
|
description='Deploy Flasky to a Rackspace cloud server.') |
|
parser.add_argument( |
|
'--flavor', '-f', metavar='FLAVOR', default='512MB Standard Instance', |
|
help='Flavor to use for the instance.' |
|
'Default is 512MB Standard Instance.') |
|
parser.add_argument( |
|
'--image', '-i', metavar='IMAGE', |
|
default='Ubuntu 14.04 LTS (Trusty Tahr)', |
|
help='Image to use for the server. Default is Ubuntu 14.04.') |
|
parser.add_argument( |
|
'key_name', metavar='KEY_NAME', |
|
help='Keypair name to install on the server. ' |
|
'Must be uploaded to your cloud account in advance.') |
|
parser.add_argument( |
|
'gmail_username', metavar='GMAIL_USERNAME', |
|
help='The username of the gmail account that the web app ' |
|
'will use to send emails.') |
|
return parser.parse_args() |
|
|
|
|
|
def main(): |
|
args = process_args() |
|
gmail_password = getpass('gmail password: ') |
|
|
|
# instantiate the nova client |
|
global nova |
|
pyrax.set_setting('identity_type', os.environ['OS_AUTH_SYSTEM']) |
|
pyrax.set_default_region(os.environ['OS_REGION_NAME']) |
|
pyrax.set_credentials(os.environ['OS_USERNAME'], os.environ['OS_PASSWORD']) |
|
nova = pyrax.cloudservers |
|
|
|
flavor = get_flavor(args.flavor) |
|
image = get_image(args.image) |
|
|
|
ip_address = create_web_server(flavor, image, args.key_name) |
|
install_flasky(ip_address, args.gmail_username, gmail_password) |
|
print 'Flasky is now running at http://{0}.'.format(ip_address) |
|
|
|
|
|
if __name__ == '__main__': |
|
main() |