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

Provide functionality to work with Rackspace's public cloud #2537

Merged
merged 3 commits into from
Apr 5, 2013
Merged
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
265 changes: 265 additions & 0 deletions library/rax
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
#!/usr/bin/env python -tt
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.

DOCUMENTATION = '''
---
module: rax
short_description: create an instance in Rackspace Public Cloud, return instanceid
description:
- creates Rackspace Public Cloud instances and optionally waits for it to be 'running'.
version_added: "1.2"
options:
service:
description:
- Cloud service to interact with
required: false
choices: ['cloudservers', 'cloudfiles', 'cloud_databases', 'cloud_loadbalancers']
default: cloudservers
state:
description:
- Indicate desired state of the resource
required: false
choices: ['present', 'active', 'absent', 'deleted']
default: present
creds_file:
description:
- File to find the Rackspace Public Cloud credentials in
required: false
default: null
name:
description:
- Name to give the instance
required: false
default: null
flavor:
description:
- flavor to use for the instance
required: false
default: null
image:
description:
- image to use for the instance
required: false
default: null
meta:
description:
- A hash of metadata to associate with the instance
default: null
key_name:
description:
- key pair to use on the instance
required: false
default: null
aliases: ['keypair']
files:
description:
- Files to insert into the instance. remotefilename:localcontent
default: null
region:
description:
- Region to create an instance in
required: false
default: null
wait:
description:
- wait for the instance to be in state 'running' before returning
required: false
default: "no"
choices: [ "yes", "no" ]
wait_timeout:
description:
- how long before wait gives up, in seconds
default: 300
examples:
- code: 'local_action: rax creds_file=~/.raxpub service=cloudservers name=rax-test1 flavor=5 image=b11d9567-e412-4255-96b9-bd63ab23bcfe wait=yes state=present'
description: "Examples from Ansible Playbooks"
requirements: [ "pyrax" ]
author: Jesse Keating
notes:
- Two environment variables can be used, RAX_CREDS and RAX_REGION.
- RAX_CREDS points to a credentials file appropriate for pyrax
- RAX_REGION defines a Rackspace Public Cloud region (DFW, ORD, LON, ...)
'''

import sys
import time
import os

try:
import pyrax
except ImportError:
print("failed=True msg='pyrax required for this module'")
sys.exit(1)

SUPPORTEDSERVICES = ['cloudservers', 'cloudfiles', 'cloud_blockstorage',
'cloud_databases', 'cloud_loadbalancers']

def cloudservers(module, state, name, flavor, image, meta, key_name, files,
wait, wait_timeout):
# Check our args (this could be done better)
for arg in (state, name, flavor, image):
if not arg:
module.fail_json(msg='%s is required for cloudservers' % arg)

instances = []
changed = False
servers = []
# See if we can find servers that match our options
for server in pyrax.cloudservers.list():
if name != server.name:
continue
if flavor != server.flavor['id']:
continue
if image != server.image['id']:
continue
if meta != server.metadata:
continue
# Nothing else ruled us not a match, so consider it a winner
servers.append(server)

# act on the state
if state in ('active', 'present'):
if not servers:
# Handle the file contents
for rpath in files.keys():
lpath = os.path.expanduser(files[rpath])
try:
fileobj = open(lpath, 'r')
files[rpath] = fileobj
except Exception, e:
module.fail_json(msg = 'Failed to load %s' % lpath)
try:
servers = [pyrax.cloudservers.servers.create(name=name,
image=image,
flavor=flavor,
key_name=key_name,
meta=meta,
files=files)]
changed = True
except Exception, e:
module.fail_json(msg = '%s' % e.message)

for server in servers:
# wait here until the instances are up
wait_timeout = time.time() + wait_timeout
while wait and wait_timeout > time.time():
# refresh the server details
server.get()
if server.status in ('ACTIVE', 'ERROR'):
break
time.sleep(5)
if wait and wait_timeout <= time.time():
# waiting took too long
module.fail_json(msg = 'Timeout waiting on %s' % server.id)
# Get a fresh copy of the server details
server.get()
if server.status == 'ERROR':
module.fail_json(msg = '%s failed to build' % server.id)
instance = {'id': server.id,
'accessIPv4': server.accessIPv4,
'name': server.name,
'status': server.status}
instances.append(instance)

elif state in ('absent', 'deleted'):
deleted = []
# See if we can find a server that matches our credentials
for server in servers:
if server.name == name:
if server.flavor['id'] == flavor and \
server.image['id'] == image and \
server.metadata == meta:
try:
server.delete()
deleted.append(server)
except Exception, e:
module.fail_json(msg = e.message)
instance = {'id': server.id,
'accessIPv4': server.accessIPv4,
'name': server.name,
'status': 'DELETING'}
instances.append(instance)
changed = True

module.exit_json(changed=changed, instances=instances)

def main():
module = AnsibleModule(
argument_spec = dict(
service = dict(default='cloudservers', choices=SUPPORTEDSERVICES),
state = dict(default='present', choices=['active', 'present',
'deleted', 'absent']),
creds_file = dict(),
name = dict(),
flavor = dict(),
image = dict(),
meta = dict(type='dict', default={}),
key_name = dict(aliases = ['keypair']),
files = dict(type='dict', default={}),
region = dict(),
wait = dict(type='bool', choices=BOOLEANS),
wait_timeout = dict(default=300),
)
)

service = module.params.get('service')
state = module.params.get('state')
creds_file = module.params.get('creds_file')
name = module.params.get('name')
flavor = module.params.get('flavor')
image = module.params.get('image')
meta = module.params.get('meta')
key_name = module.params.get('key_name')
files = module.params.get('files')
region = module.params.get('region')
wait = module.params.get('wait')
wait_timeout = int(module.params.get('wait_timeout'))

# Setup the credentials file
if not creds_file:
try:
creds_file = os.environ['RAX_CREDS_FILE']
except KeyError, e:
module.fail_json(msg = 'Unable to load %s' % e.message)

# Define the region
if not region:
try:
region = os.environ['RAX_REGION']
except KeyError, e:
module.fail_json(msg = 'Unable to load %s' % e.message)

# setup the auth
sys.stderr.write('region is %s' % region)
try:
pyrax.set_credential_file(creds_file, region=region)
except Exception, e:
module.fail_json(msg = '%s' % e.message)

# Act based on service
if service == 'cloudservers':
cloudservers(module, state, name, flavor, image, meta, key_name, files,
wait, wait_timeout)
elif service in ['cloudfiles', 'cloud_blockstorage',
'cloud_databases', 'cloud_loadbalancers']:
module.fail_json(msg = 'Service %s is not supported at this time' %
service)


# this is magic, see lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>

main()