From 017c9d07d2893963976befec4bb15ac0135613c0 Mon Sep 17 00:00:00 2001 From: Matt Martz Date: Tue, 19 Nov 2013 10:52:54 -0600 Subject: [PATCH 1/3] Initial commit of rax_keypair --- library/cloud/rax_keypair | 196 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 library/cloud/rax_keypair diff --git a/library/cloud/rax_keypair b/library/cloud/rax_keypair new file mode 100644 index 0000000000..ce46a8687f --- /dev/null +++ b/library/cloud/rax_keypair @@ -0,0 +1,196 @@ +#!/usr/bin/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 . + +DOCUMENTATION = ''' +--- +module: rax_keypair +short_description: Create a keypair for use with Rackspace Cloud Servers +description: + - Create a keypair for use with Rackspace Cloud Servers +version_added: 1.5 +options: + api_key: + description: + - Rackspace API key (overrides C(credentials)) + credentials: + description: + - File to find the Rackspace credentials in (ignored if C(api_key) and + C(username) are provided) + default: null + aliases: ['creds_file'] + name: + description: + - Name of keypair + required: true + public_key: + description: + - Public Key string to upload + default: null + region: + description: + - Region to create the load balancer in + default: DFW + username: + description: + - Rackspace username (overrides C(credentials)) + state: + description: + - Indicate desired state of the resource + choices: ['present', 'absent'] + default: present +requirements: [ "pyrax" ] +author: Matt Martz +notes: + - The following environment variables can be used, C(RAX_USERNAME), + C(RAX_API_KEY), C(RAX_CREDS_FILE), C(RAX_CREDENTIALS), C(RAX_REGION). + - C(RAX_CREDENTIALS) and C(RAX_CREDS_FILE) points to a credentials file + appropriate for pyrax. See U(https://github.com/rackspace/pyrax/blob/master/docs/getting_started.md#authenticating) + - C(RAX_USERNAME) and C(RAX_API_KEY) obviate the use of a credentials file + - C(RAX_REGION) defines a Rackspace Public Cloud region (DFW, ORD, LON, ...) + - Keypairs cannot be manipulated, only created and deleted. To "update" a + keypair you must first delete and then recreate. +''' + +EXAMPLES = ''' +- name: Create a keypair + hosts: local + gather_facts: False + tasks: + - name: keypair request + local_action: + module: rax_keypair + credentials: ~/.raxpub + name: my_keypair + region: DFW + register: keypair + - name: Create local public key + local_action: + module: copy + content: "{{ keypair.keypair.public_key }}" + dest: "{{ inventory_dir }}/{{ keypair.keypair.name }}.pub" + - name: Create local private key + local_action: + module: copy + content: "{{ keypair.keypair.private_key }}" + dest: "{{ inventory_dir }}/{{ keypair.keypair.name }}" +''' + +import sys +import os + +from types import NoneType + +try: + import pyrax +except ImportError: + print("failed=True msg='pyrax required for this module'") + sys.exit(1) + +from novaclient.exceptions import NotFound + +NON_CALLABLES = (basestring, bool, dict, int, list, NoneType) + + +def to_dict(obj): + instance = {} + for key in dir(obj): + value = getattr(obj, key) + if (isinstance(value, NON_CALLABLES) and not key.startswith('_')): + instance[key] = value + return instance + + +def rax_keypair(module, name, public_key, state): + changed = False + + cs = pyrax.cloudservers + keypair = {} + + if state == 'present': + try: + keypair = cs.keypairs.find(name=name) + except NotFound: + try: + keypair = cs.keypairs.create(name, public_key) + changed = True + except Exception, e: + module.fail_json(msg='%s' % e.message) + except Exception, e: + module.fail_json(msg='%s' % e.message) + + elif state == 'absent': + try: + keypair = cs.keypairs.find(name=name) + except: + pass + + if keypair: + try: + keypair.delete() + changed = True + except Exception, e: + module.fail_json(msg='%s' % e.message) + + module.exit_json(changed=changed, keypair=to_dict(keypair)) + + +def main(): + module = AnsibleModule( + argument_spec=dict( + api_key=dict(), + credentials=dict(aliases=['creds_file']), + name=dict(), + public_key=dict(), + region=dict(), + state=dict(default='present', choices=['absent', 'present']), + username=dict(), + ), + ) + + api_key = module.params.get('api_key') + credentials = module.params.get('credentials') + name = module.params.get('name') + public_key = module.params.get('public_key') + region = module.params.get('region') + state = module.params.get('state') + username = module.params.get('username') + + try: + username = username or os.environ.get('RAX_USERNAME') + api_key = api_key or os.environ.get('RAX_API_KEY') + credentials = (credentials or os.environ.get('RAX_CREDENTIALS') or + os.environ.get('RAX_CREDS_FILE')) + region = region or os.environ.get('RAX_REGION') + except KeyError, e: + module.fail_json(msg='Unable to load %s' % e.message) + + try: + pyrax.set_setting('identity_type', 'rackspace') + if api_key and username: + pyrax.set_credentials(username, api_key=api_key, region=region) + elif credentials: + credentials = os.path.expanduser(credentials) + pyrax.set_credential_file(credentials, region=region) + else: + raise Exception('No credentials supplied!') + except Exception, e: + module.fail_json(msg='%s' % e.message) + + rax_keypair(module, name, public_key, state) + +from ansible.module_utils.basic import * + +main() From b3744800fb26df650f359ad3bb5612b40f48be0d Mon Sep 17 00:00:00 2001 From: Matt Martz Date: Mon, 2 Dec 2013 14:03:10 -0600 Subject: [PATCH 2/3] Utilize ansible.module_utils.rax --- library/cloud/rax_keypair | 50 +++++++++++++-------------------------- 1 file changed, 16 insertions(+), 34 deletions(-) diff --git a/library/cloud/rax_keypair b/library/cloud/rax_keypair index ce46a8687f..4a09f64b6a 100644 --- a/library/cloud/rax_keypair +++ b/library/cloud/rax_keypair @@ -41,7 +41,7 @@ options: default: null region: description: - - Region to create the load balancer in + - Region to create the key pair in default: DFW username: description: @@ -89,7 +89,6 @@ EXAMPLES = ''' ''' import sys -import os from types import NoneType @@ -148,49 +147,32 @@ def rax_keypair(module, name, public_key, state): def main(): - module = AnsibleModule( - argument_spec=dict( - api_key=dict(), - credentials=dict(aliases=['creds_file']), + argument_spec = rax_argument_spec() + argument_spec.update( + dict( name=dict(), public_key=dict(), - region=dict(), state=dict(default='present', choices=['absent', 'present']), - username=dict(), - ), + ) + ) + + module = AnsibleModule( + argument_spec=argument_spec, + required_together=rax_required_together(), ) - api_key = module.params.get('api_key') - credentials = module.params.get('credentials') name = module.params.get('name') public_key = module.params.get('public_key') - region = module.params.get('region') state = module.params.get('state') - username = module.params.get('username') - try: - username = username or os.environ.get('RAX_USERNAME') - api_key = api_key or os.environ.get('RAX_API_KEY') - credentials = (credentials or os.environ.get('RAX_CREDENTIALS') or - os.environ.get('RAX_CREDS_FILE')) - region = region or os.environ.get('RAX_REGION') - except KeyError, e: - module.fail_json(msg='Unable to load %s' % e.message) - - try: - pyrax.set_setting('identity_type', 'rackspace') - if api_key and username: - pyrax.set_credentials(username, api_key=api_key, region=region) - elif credentials: - credentials = os.path.expanduser(credentials) - pyrax.set_credential_file(credentials, region=region) - else: - raise Exception('No credentials supplied!') - except Exception, e: - module.fail_json(msg='%s' % e.message) + setup_rax_module(module, pyrax) rax_keypair(module, name, public_key, state) -from ansible.module_utils.basic import * +# import module snippets +from ansible.module_utils.basic import * +from ansible.module_utils.rax import * + +### invoke the module main() From 925eb1dca769497a6820d119f25110599e38131f Mon Sep 17 00:00:00 2001 From: Matt Martz Date: Fri, 13 Dec 2013 19:34:33 -0600 Subject: [PATCH 3/3] Don't import novaclient, exceptions are available via cs.exceptions --- library/cloud/rax_keypair | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/library/cloud/rax_keypair b/library/cloud/rax_keypair index 4a09f64b6a..0df4ceb73a 100644 --- a/library/cloud/rax_keypair +++ b/library/cloud/rax_keypair @@ -98,8 +98,6 @@ except ImportError: print("failed=True msg='pyrax required for this module'") sys.exit(1) -from novaclient.exceptions import NotFound - NON_CALLABLES = (basestring, bool, dict, int, list, NoneType) @@ -121,7 +119,7 @@ def rax_keypair(module, name, public_key, state): if state == 'present': try: keypair = cs.keypairs.find(name=name) - except NotFound: + except cs.exceptions.NotFound: try: keypair = cs.keypairs.create(name, public_key) changed = True