From a226701efe836e3c288a1624dfd820928dcd0c16 Mon Sep 17 00:00:00 2001 From: Monty Taylor Date: Tue, 31 Mar 2015 16:37:07 -0400 Subject: [PATCH 1/6] Add OpenStack Keypair module Also deprecate old nova_keypair module. --- .../{nova_keypair.py => _nova_keypair.py} | 1 + cloud/openstack/os_keypair.py | 140 ++++++++++++++++++ 2 files changed, 141 insertions(+) rename cloud/openstack/{nova_keypair.py => _nova_keypair.py} (99%) create mode 100644 cloud/openstack/os_keypair.py diff --git a/cloud/openstack/nova_keypair.py b/cloud/openstack/_nova_keypair.py similarity index 99% rename from cloud/openstack/nova_keypair.py rename to cloud/openstack/_nova_keypair.py index b2e38ff7db9..68df0c5a2c4 100644 --- a/cloud/openstack/nova_keypair.py +++ b/cloud/openstack/_nova_keypair.py @@ -32,6 +32,7 @@ version_added: "1.2" author: - "Benno Joy (@bennojoy)" - "Michael DeHaan" +deprecated: Deprecated in 2.0. Use os_keypair instead short_description: Add/Delete key pair from nova description: - Add or Remove key pair from nova . diff --git a/cloud/openstack/os_keypair.py b/cloud/openstack/os_keypair.py new file mode 100644 index 00000000000..c4725552725 --- /dev/null +++ b/cloud/openstack/os_keypair.py @@ -0,0 +1,140 @@ +#!/usr/bin/python + +# Copyright (c) 2014 Hewlett-Packard Development Company, L.P. +# Copyright (c) 2013, Benno Joy +# Copyright (c) 2013, John Dewey +# +# This module 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. +# +# This software 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 this software. If not, see . + + +try: + import shade + HAS_SHADE = True +except ImportError: + HAS_SHADE = False + + +DOCUMENTATION = ''' +--- +module: os_keypair +short_description: Add/Delete a keypair from OpenStack +extends_documentation_fragment: openstack +version_added: "2.0" +description: + - Add or Remove key pair from OpenStack +options: + name: + description: + - Name that has to be given to the key pair + required: true + default: None + public_key: + description: + - The public key that would be uploaded to nova and injected to vm's upon creation + required: false + default: None + public_key_file: + description: + - Path to local file containing ssh public key. Mutually exclusive with public_key + required: false + default: None + state: + description: + - Should the resource be present or absent. + choices: [present, absent] + default: present +requirements: ["shade"] +''' + +EXAMPLES = ''' +# Creates a key pair with the running users public key +- os_keypair: + cloud: mordred + state: present + name: ansible_key + public_key: "{{ lookup('file','~/.ssh/id_rsa.pub') }}" + +# Creates a new key pair and the private key returned after the run. +- os_keypair: + cloud: rax-dfw + state: present + name: ansible_key +''' + + +def main(): + argument_spec = openstack_full_argument_spec( + name = dict(required=True), + public_key = dict(default=None), + public_key_file = dict(default=None), + state = dict(default='present', choices=['absent', 'present']), + ) + module_kwargs = openstack_module_kwargs( + mutually_exclusive=[['public_key', 'public_key_file']]) + module = AnsibleModule(argument_spec, **module_kwargs) + + if module.params['public_key_file']: + public_key = open(module.params['public_key_file']).read() + else: + public_key = module.params['public_key'] + + if not HAS_SHADE: + module.fail_json(msg='shade is required for this module') + + state = module.params['state'] + name = module.params['name'] + public_key = module.params['public_key'] + + try: + cloud = shade.openstack_cloud(**module.params) + + if state == 'present': + for key in cloud.list_keypairs(): + if key.name == name: + if public_key and (public_key != key.public_key): + module.fail_json( + msg="Key name %s present but key hash not the same" + " as offered. Delete key first." % key.name + ) + else: + module.exit_json(changed=False, result="Key present") + try: + key = cloud.create_keypair(name, public_key) + except Exception, e: + module.exit_json( + msg="Error in creating the keypair: %s" % e.message + ) + if not public_key: + module.exit_json(changed=True, key=key.private_key) + module.exit_json(changed=True, key=None) + + elif state == 'absent': + for key in cloud.list_keypairs(): + if key.name == name: + try: + cloud.delete_keypair(name) + except Exception, e: + module.fail_json( + msg="Keypair deletion has failed: %s" % e.message + ) + module.exit_json(changed=True, result="deleted") + module.exit_json(changed=False, result="not present") + + except shade.OpenStackCloudException as e: + module.fail_json(msg=e.message) + +# this is magic, see lib/ansible/module_common.py +from ansible.module_utils.basic import * +from ansible.module_utils.openstack import * +main() From 82dc5c4394ab88e055debed6b0d7d397f11638d7 Mon Sep 17 00:00:00 2001 From: Davide Guerri Date: Thu, 4 Jun 2015 19:30:34 +0100 Subject: [PATCH 2/6] Avoind using lookup() in documentation lookup() is currently broken (current Ansible devel branch), so better to avoid it in our examples. --- cloud/openstack/os_keypair.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloud/openstack/os_keypair.py b/cloud/openstack/os_keypair.py index c4725552725..c6794b47826 100644 --- a/cloud/openstack/os_keypair.py +++ b/cloud/openstack/os_keypair.py @@ -63,7 +63,7 @@ EXAMPLES = ''' cloud: mordred state: present name: ansible_key - public_key: "{{ lookup('file','~/.ssh/id_rsa.pub') }}" + public_key_file: ~/.ssh/id_rsa.pub # Creates a new key pair and the private key returned after the run. - os_keypair: From 02d0a73906bcd6e1c8805825a23b49df027c65a9 Mon Sep 17 00:00:00 2001 From: Monty Taylor Date: Thu, 18 Jun 2015 07:59:32 -0400 Subject: [PATCH 3/6] Move the order of argument processing --- cloud/openstack/os_keypair.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/cloud/openstack/os_keypair.py b/cloud/openstack/os_keypair.py index c6794b47826..b404e6cc02a 100644 --- a/cloud/openstack/os_keypair.py +++ b/cloud/openstack/os_keypair.py @@ -84,18 +84,16 @@ def main(): mutually_exclusive=[['public_key', 'public_key_file']]) module = AnsibleModule(argument_spec, **module_kwargs) + state = module.params['state'] + name = module.params['name'] + public_key = module.params['public_key'] + if module.params['public_key_file']: public_key = open(module.params['public_key_file']).read() - else: - public_key = module.params['public_key'] if not HAS_SHADE: module.fail_json(msg='shade is required for this module') - state = module.params['state'] - name = module.params['name'] - public_key = module.params['public_key'] - try: cloud = shade.openstack_cloud(**module.params) From bed420cd531c30c0865bf331c74035494b612a1e Mon Sep 17 00:00:00 2001 From: David Shrewsbury Date: Thu, 25 Jun 2015 12:19:20 -0400 Subject: [PATCH 4/6] Update os_keypair for latest shade Uses the latest version of shade for cleaner code. Also, always return the key dict whether we create the key, or it already exists. The example using public_key_file is corrected to use a full path since ~ is not converted for us. --- cloud/openstack/os_keypair.py | 80 +++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 36 deletions(-) diff --git a/cloud/openstack/os_keypair.py b/cloud/openstack/os_keypair.py index b404e6cc02a..a9c2640628f 100644 --- a/cloud/openstack/os_keypair.py +++ b/cloud/openstack/os_keypair.py @@ -41,12 +41,14 @@ options: default: None public_key: description: - - The public key that would be uploaded to nova and injected to vm's upon creation + - The public key that would be uploaded to nova and injected into VMs + upon creation. required: false default: None public_key_file: description: - - Path to local file containing ssh public key. Mutually exclusive with public_key + - Path to local file containing ssh public key. Mutually exclusive + with public_key. required: false default: None state: @@ -63,7 +65,7 @@ EXAMPLES = ''' cloud: mordred state: present name: ansible_key - public_key_file: ~/.ssh/id_rsa.pub + public_key_file: /home/me/.ssh/id_rsa.pub # Creates a new key pair and the private key returned after the run. - os_keypair: @@ -73,16 +75,33 @@ EXAMPLES = ''' ''' +def _system_state_change(module, keypair): + state = module.params['state'] + if state == 'present' and not keypair: + return True + if state == 'absent' and keypair: + return True + return False + + def main(): argument_spec = openstack_full_argument_spec( name = dict(required=True), public_key = dict(default=None), public_key_file = dict(default=None), - state = dict(default='present', choices=['absent', 'present']), + state = dict(default='present', + choices=['absent', 'present']), ) + module_kwargs = openstack_module_kwargs( mutually_exclusive=[['public_key', 'public_key_file']]) - module = AnsibleModule(argument_spec, **module_kwargs) + + module = AnsibleModule(argument_spec, + supports_check_mode=True, + **module_kwargs) + + if not HAS_SHADE: + module.fail_json(msg='shade is required for this module') state = module.params['state'] name = module.params['name'] @@ -90,44 +109,33 @@ def main(): if module.params['public_key_file']: public_key = open(module.params['public_key_file']).read() - - if not HAS_SHADE: - module.fail_json(msg='shade is required for this module') + public_key = public_key.rstrip() try: cloud = shade.openstack_cloud(**module.params) + keypair = cloud.get_keypair(name) + + if module.check_mode: + module.exit_json(changed=_system_state_change(module, keypair)) if state == 'present': - for key in cloud.list_keypairs(): - if key.name == name: - if public_key and (public_key != key.public_key): - module.fail_json( - msg="Key name %s present but key hash not the same" - " as offered. Delete key first." % key.name - ) - else: - module.exit_json(changed=False, result="Key present") - try: - key = cloud.create_keypair(name, public_key) - except Exception, e: - module.exit_json( - msg="Error in creating the keypair: %s" % e.message - ) - if not public_key: - module.exit_json(changed=True, key=key.private_key) - module.exit_json(changed=True, key=None) + if keypair and keypair['name'] == name: + if public_key and (public_key != keypair['public_key']): + module.fail_json( + msg="Key name %s present but key hash not the same" + " as offered. Delete key first." % name + ) + else: + module.exit_json(changed=False, key=keypair) + + new_key = cloud.create_keypair(name, public_key) + module.exit_json(changed=True, key=new_key) elif state == 'absent': - for key in cloud.list_keypairs(): - if key.name == name: - try: - cloud.delete_keypair(name) - except Exception, e: - module.fail_json( - msg="Keypair deletion has failed: %s" % e.message - ) - module.exit_json(changed=True, result="deleted") - module.exit_json(changed=False, result="not present") + if keypair: + cloud.delete_keypair(name) + module.exit_json(changed=True) + module.exit_json(changed=False) except shade.OpenStackCloudException as e: module.fail_json(msg=e.message) From e6fc129013b0dfd2873fad648a867cc87dc76cc6 Mon Sep 17 00:00:00 2001 From: David Shrewsbury Date: Mon, 29 Jun 2015 14:49:13 -0400 Subject: [PATCH 5/6] Add a note about the return value. --- cloud/openstack/os_keypair.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cloud/openstack/os_keypair.py b/cloud/openstack/os_keypair.py index a9c2640628f..f485d7fd2fc 100644 --- a/cloud/openstack/os_keypair.py +++ b/cloud/openstack/os_keypair.py @@ -33,6 +33,10 @@ extends_documentation_fragment: openstack version_added: "2.0" description: - Add or Remove key pair from OpenStack +notes: + - The module returns a dictionary describing the keypair, with + keys including: id, name, public_key. A private_key entry may + also be included if a keypair was generated for you. options: name: description: From 7970924bd56e2bbd53f6588b023ca3497afc6ebb Mon Sep 17 00:00:00 2001 From: David Shrewsbury Date: Mon, 29 Jun 2015 15:55:15 -0400 Subject: [PATCH 6/6] Use newest documentation style for return value. --- cloud/openstack/os_keypair.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/cloud/openstack/os_keypair.py b/cloud/openstack/os_keypair.py index f485d7fd2fc..7a0c1ca47a0 100644 --- a/cloud/openstack/os_keypair.py +++ b/cloud/openstack/os_keypair.py @@ -33,10 +33,6 @@ extends_documentation_fragment: openstack version_added: "2.0" description: - Add or Remove key pair from OpenStack -notes: - - The module returns a dictionary describing the keypair, with - keys including: id, name, public_key. A private_key entry may - also be included if a keypair was generated for you. options: name: description: @@ -78,6 +74,26 @@ EXAMPLES = ''' name: ansible_key ''' +RETURN = ''' +id: + description: Unique UUID. + returned: success + type: string +name: + description: Name given to the keypair. + returned: success + type: string +public_key: + description: The public key value for the keypair. + returned: success + type: string +private_key: + description: The private key value for the keypair. + returned: Only when a keypair is generated for the user (e.g., when creating one + and a public key is not specified). + type: string +''' + def _system_state_change(module, keypair): state = module.params['state']