From bcef2e7daf967dbd93c51ee2babcf5973b721d53 Mon Sep 17 00:00:00 2001 From: Paul Durivage Date: Thu, 23 Jan 2014 19:19:05 -0600 Subject: [PATCH 1/4] Doc updates; fix YAML problems --- cloud/rax_files | 379 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 379 insertions(+) create mode 100644 cloud/rax_files diff --git a/cloud/rax_files b/cloud/rax_files new file mode 100644 index 00000000000..1fdd1351052 --- /dev/null +++ b/cloud/rax_files @@ -0,0 +1,379 @@ +#!/usr/bin/python -tt + +# (c) 2013, Paul Durivage +# +# 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_files +short_description: Manipulate Rackspace Cloud Files Containers +description: + - Manipulate Rackspace Cloud Files Containers +version_added: "1.5" +options: + api_key: + description: + - Rackspace API key (overrides I(credentials)) + clear_meta: + description: + - Optionally clear existing metadata when applying metadata to existing containers. + Selecting this option is only appropriate when setting type=meta + choices: ["yes", "no"] + default: "no" + container: + description: + - The container to use for container or metadata operations. + required: true + credentials: + description: + - File to find the Rackspace credentials in (ignored if I(api_key) and + I(username) are provided) + default: null + aliases: ['creds_file'] + meta: + description: + - A hash of items to set as metadata values on a container + private: + description: + - Used to set a container as private, removing it from the CDN. B(Warning!) + Private containers, if previously made public, can have live objects + available until the TTL on cached objects expires + public: + description: + - Used to set a container as public, available via the Cloud Files CDN + region: + description: + - Region to create an instance in + default: DFW + ttl: + description: + - In seconds, set a container-wide TTL for all objects cached on CDN edge nodes. + Setting a TTL is only appropriate for containers that are public + type: + description: + - Type of object to do work on, i.e. metadata object or a container object + choices: ["file", "meta"] + default: "file" + username: + description: + - Rackspace username (overrides I(credentials)) + web_error: + description: + - Sets an object to be presented as the HTTP error page when accessed by the CDN URL + web_index: + description: + - Sets an object to be presented as the HTTP index page when accessed by the CDN URL +requirements: [ "pyrax" ] +author: Paul Durivage +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, ...) +''' + +EXAMPLES = ''' +- name: "Test Cloud Files Containers" + hosts: local + gather_facts: no + tasks: + - name: "List all containers" + rax_files: state=list + + - name: "Create container called 'mycontainer'" + rax_files: container=mycontainer + + - name: "Create container 'mycontainer2' with metadata" + rax_files: + container: mycontainer2 + meta: + key: value + file_for: paul.durivage@rackspace.com + + - name: "Set a container's web index page" + rax_files: container=mycontainer web_index=index.html + + - name: "Set a container's web error page" + rax_files: container=mycontainer web_error=error.html + + - name: "Make container public" + rax_files: container=mycontainer public=yes + + - name: "Make container public with a 24 hour TTL" + rax_files: container=mycontainer public=yes ttl=86400 + + - name: "Make container private" + rax_files: container=mycontainer private=yes + +- name: "Test Cloud Files Containers Metadata Storage" + hosts: local + gather_facts: no + tasks: + - name: "Get mycontainer2 metadata" + rax_files: + container: mycontainer2 + type: meta + + - name: "Set mycontainer2 metadata" + rax_files: + container: mycontainer2 + type: meta + meta: + uploaded_by: paul.durivage@rackspace.com + + - name: "Remove mycontainer2 metadata" + rax_files: + container: "mycontainer2" + type: meta + state: absent + meta: + key: "" + file_for: "" +''' + +from ansible import __version__ + +try: + import pyrax +except ImportError, e: + print("failed=True msg='pyrax is required for this module'") + sys.exit(1) + +EXIT_DICT = {'success': True} +META_PREFIX = 'x-container-meta-' +USER_AGENT = "Ansible/%s via pyrax" % __version__ + + +def _get_container(module, cf, container): + try: + return cf.get_container(container) + except pyrax.exc.NoSuchContainer, e: + module.fail_json(msg=e.message) + + +def _fetch_meta(module, container): + try: + EXIT_DICT['meta'] = { + k.split(META_PREFIX)[-1]: v for k, v in container.get_metadata().items() + } + except Exception, e: + module.fail_json(msg=e.message) + + +def meta(cf, module, container_, state, meta_, clear_meta): + c = _get_container(module, cf, container_) + + if meta_ and state == 'present': + try: + meta_set = c.set_metadata(meta_, clear=clear_meta) + except Exception, e: + module.fail_json(msg=e.message) + elif meta_ and state == 'absent': + remove_results = [] + for k, v in meta_.items(): + c.remove_metadata_key(k) + remove_results.append(k) + EXIT_DICT['deleted_meta_keys'] = remove_results + elif state == 'absent': + remove_results = [] + for k, v in c.get_metadata().items(): + c.remove_metadata_key(k) + remove_results.append(k) + EXIT_DICT['deleted_meta_keys'] = remove_results + + _fetch_meta(module, c) + _locals = locals().keys() + + EXIT_DICT['container'] = c.name + if 'meta_set' in _locals or 'remove_results' in _locals: + EXIT_DICT['changed'] = True + + module.exit_json(**EXIT_DICT) + + +def container(cf, module, container_, state, meta_, clear_meta, ttl, public, private, web_index, web_error): + if public and private: + module.fail_json(msg='container cannot be simultaneously ' + 'set to public and private') + + if state == 'absent' and (meta_ or clear_meta or public or private or web_index or web_error): + module.fail_json(msg='state cannot be omitted when setting/removing ' + 'attributes on a container') + + if state == 'list': + # We don't care if attributes are specified, let's list containers + EXIT_DICT['containers'] = cf.list_containers() + module.exit_json(**EXIT_DICT) + + try: + c = cf.get_container(container_) + except pyrax.exc.NoSuchContainer, e: + # Make the container if state=present, otherwise bomb out + if state == 'present': + try: + c = cf.create_container(container_) + except Exception, e: + module.fail_json(msg=e.message) + else: + EXIT_DICT['created'] = True + else: + module.fail_json(msg=e.message) + else: + # Successfully grabbed a container object + # Delete if state is absent + if state == 'absent': + try: + cont_deleted = c.delete() + except Exception, e: + module.fail_json(msg=e.message) + else: + EXIT_DICT['deleted'] = True + + if meta_: + try: + meta_set = c.set_metadata(meta_, clear=clear_meta) + except Exception, e: + module.fail_json(msg=e.message) + finally: + _fetch_meta(module, c) + + if ttl: + try: + c.cdn_ttl = ttl + except Exception, e: + module.fail_json(msg=e.message) + else: + EXIT_DICT['ttl'] = c.cdn_ttl + + if public: + try: + cont_public = c.make_public() + except Exception, e: + module.fail_json(msg=e.message) + else: + EXIT_DICT['container_urls'] = { + 'url': c.cdn_uri, + 'ssl_url': c.cdn_ssl_uri, + 'streaming_url': c.cdn_streaming_uri, + 'ios_uri': c.cdn_ios_uri + } + + if private: + try: + cont_private = c.make_private() + except Exception, e: + module.fail_json(msg=e.message) + else: + EXIT_DICT['set_private'] = True + + if web_index: + try: + cont_web_index = c.set_web_index_page(web_index) + except Exception, e: + module.fail_json(msg=e.message) + else: + EXIT_DICT['set_index'] = True + finally: + _fetch_meta(module, c) + + if web_error: + try: + cont_err_index = c.set_web_error_page(web_error) + except Exception, e: + module.fail_json(msg=e.message) + else: + EXIT_DICT['set_error'] = True + finally: + _fetch_meta(module, c) + + EXIT_DICT['container'] = c.name + EXIT_DICT['objs_in_container'] = c.object_count + EXIT_DICT['total_bytes'] = c.total_bytes + + _locals = locals().keys() + + if ('cont_created' in _locals + or 'cont_deleted' in _locals + or 'meta_set' in _locals + or 'cont_public' in _locals + or 'cont_private' in _locals + or 'cont_web_index' in _locals + or 'cont_err_index' in _locals): + EXIT_DICT['changed'] = True + + module.exit_json(**EXIT_DICT) + + +def cloudfiles(module, container_, state, meta_, clear_meta, typ, ttl, public, private, web_index, web_error): + """ Dispatch from here to work with metadata or file objects """ + cf = pyrax.cloudfiles + cf.user_agent = USER_AGENT + + if typ == "container": + container(cf, module, container_, state, meta_, clear_meta, ttl, public, private, web_index, web_error) + else: + meta(cf, module, container_, state, meta_, clear_meta) + + +def main(): + argument_spec = rax_argument_spec() + argument_spec.update( + dict( + container=dict(), + state=dict(choices=['present', 'absent', 'list'], default='present'), + meta=dict(type='dict', default={}), + clear_meta=dict(choices=BOOLEANS, default=False, type='bool'), + type=dict(choices=['container', 'meta'], default='container'), + ttl=dict(type='int'), + public=dict(choices=BOOLEANS, default=False, type='bool'), + private=dict(choices=BOOLEANS, default=False, type='bool'), + web_index=dict(), + web_error=dict() + ) + ) + + module = AnsibleModule( + argument_spec=argument_spec, + required_together=rax_required_together() + ) + + container_ = module.params.get('container') + state = module.params.get('state') + meta_ = module.params.get('meta') + clear_meta = module.params.get('clear_meta') + typ = module.params.get('type') + ttl = module.params.get('ttl') + public = module.params.get('public') + private = module.params.get('private') + web_index = module.params.get('web_index') + web_error = module.params.get('web_error') + + if state in ['present', 'absent'] and not container_: + module.fail_json(msg='please specify a container name') + if clear_meta and not typ == 'meta': + module.fail_json(msg='clear_meta can only be used when setting metadata') + + setup_rax_module(module, pyrax) + cloudfiles(module, container_, state, meta_, clear_meta, typ, ttl, public, private, web_index, web_error) + + +from ansible.module_utils.basic import * +from ansible.module_utils.rax import * + +main() From e2555bc7a770809e30c41160f5df9b14b6885aa0 Mon Sep 17 00:00:00 2001 From: Paul Durivage Date: Thu, 23 Jan 2014 20:31:44 -0600 Subject: [PATCH 2/4] Imperfect revert w/ artifacts. Revert "Revert "Merge pull request #5540 from angstwad/cf-container-module"" This reverts commit c3408f205e169c0afc08abf5ef44cf687250847d. Conflicts: library/cloud/rax_files --- cloud/rax_files | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/cloud/rax_files b/cloud/rax_files index 1fdd1351052..f5a518a8172 100644 --- a/cloud/rax_files +++ b/cloud/rax_files @@ -19,19 +19,34 @@ DOCUMENTATION = ''' --- +<<<<<<< HEAD module: rax_files short_description: Manipulate Rackspace Cloud Files Containers description: - Manipulate Rackspace Cloud Files Containers +======= +module: +short_description: create, fetch, and delete objects in Rackspace Cloud Files +description: + - Upload, download, and delete objects in Rackspace Cloud Files +>>>>>>> parent of c3408f2... Revert "Merge pull request #5540 from angstwad/cf-container-module" version_added: "1.5" options: api_key: description: +<<<<<<< HEAD - Rackspace API key (overrides I(credentials)) clear_meta: description: - Optionally clear existing metadata when applying metadata to existing containers. Selecting this option is only appropriate when setting type=meta +======= + - Rackspace API key (overrides I(credentials))requirements: [ "pyrax" ] + clear_meta: + description: + - Optionally clear existing metadata when applying metadata to existing containers. + - Selecting this option is only appropriate when setting type=meta +>>>>>>> parent of c3408f2... Revert "Merge pull request #5540 from angstwad/cf-container-module" choices: ["yes", "no"] default: "no" container: @@ -49,9 +64,14 @@ options: - A hash of items to set as metadata values on a container private: description: +<<<<<<< HEAD - Used to set a container as private, removing it from the CDN. B(Warning!) Private containers, if previously made public, can have live objects available until the TTL on cached objects expires +======= + - Used to set a container as private, removing it from the CDN. + - Warning: Private containers, if previously made public, can have live objects available until the TTL on cached objects expires +>>>>>>> parent of c3408f2... Revert "Merge pull request #5540 from angstwad/cf-container-module" public: description: - Used to set a container as public, available via the Cloud Files CDN @@ -61,11 +81,19 @@ options: default: DFW ttl: description: +<<<<<<< HEAD - In seconds, set a container-wide TTL for all objects cached on CDN edge nodes. Setting a TTL is only appropriate for containers that are public type: description: - Type of object to do work on, i.e. metadata object or a container object +======= + - In seconds, set a container-wide TTL for all objects cached on CDN edge nodes + - Setting a TTL is only appropriate for containers that are public + type: + description: + - Type of object to do work on: metadata object or a container object +>>>>>>> parent of c3408f2... Revert "Merge pull request #5540 from angstwad/cf-container-module" choices: ["file", "meta"] default: "file" username: @@ -89,6 +117,10 @@ notes: ''' EXAMPLES = ''' +<<<<<<< HEAD +======= +--- +>>>>>>> parent of c3408f2... Revert "Merge pull request #5540 from angstwad/cf-container-module" - name: "Test Cloud Files Containers" hosts: local gather_facts: no From 37f7f3dae867606a0965e28addeb9f9a82699f43 Mon Sep 17 00:00:00 2001 From: Paul Durivage Date: Thu, 23 Jan 2014 20:43:31 -0600 Subject: [PATCH 3/4] Resolve revert conflicts --- cloud/rax_files | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/cloud/rax_files b/cloud/rax_files index f5a518a8172..1fdd1351052 100644 --- a/cloud/rax_files +++ b/cloud/rax_files @@ -19,34 +19,19 @@ DOCUMENTATION = ''' --- -<<<<<<< HEAD module: rax_files short_description: Manipulate Rackspace Cloud Files Containers description: - Manipulate Rackspace Cloud Files Containers -======= -module: -short_description: create, fetch, and delete objects in Rackspace Cloud Files -description: - - Upload, download, and delete objects in Rackspace Cloud Files ->>>>>>> parent of c3408f2... Revert "Merge pull request #5540 from angstwad/cf-container-module" version_added: "1.5" options: api_key: description: -<<<<<<< HEAD - Rackspace API key (overrides I(credentials)) clear_meta: description: - Optionally clear existing metadata when applying metadata to existing containers. Selecting this option is only appropriate when setting type=meta -======= - - Rackspace API key (overrides I(credentials))requirements: [ "pyrax" ] - clear_meta: - description: - - Optionally clear existing metadata when applying metadata to existing containers. - - Selecting this option is only appropriate when setting type=meta ->>>>>>> parent of c3408f2... Revert "Merge pull request #5540 from angstwad/cf-container-module" choices: ["yes", "no"] default: "no" container: @@ -64,14 +49,9 @@ options: - A hash of items to set as metadata values on a container private: description: -<<<<<<< HEAD - Used to set a container as private, removing it from the CDN. B(Warning!) Private containers, if previously made public, can have live objects available until the TTL on cached objects expires -======= - - Used to set a container as private, removing it from the CDN. - - Warning: Private containers, if previously made public, can have live objects available until the TTL on cached objects expires ->>>>>>> parent of c3408f2... Revert "Merge pull request #5540 from angstwad/cf-container-module" public: description: - Used to set a container as public, available via the Cloud Files CDN @@ -81,19 +61,11 @@ options: default: DFW ttl: description: -<<<<<<< HEAD - In seconds, set a container-wide TTL for all objects cached on CDN edge nodes. Setting a TTL is only appropriate for containers that are public type: description: - Type of object to do work on, i.e. metadata object or a container object -======= - - In seconds, set a container-wide TTL for all objects cached on CDN edge nodes - - Setting a TTL is only appropriate for containers that are public - type: - description: - - Type of object to do work on: metadata object or a container object ->>>>>>> parent of c3408f2... Revert "Merge pull request #5540 from angstwad/cf-container-module" choices: ["file", "meta"] default: "file" username: @@ -117,10 +89,6 @@ notes: ''' EXAMPLES = ''' -<<<<<<< HEAD -======= ---- ->>>>>>> parent of c3408f2... Revert "Merge pull request #5540 from angstwad/cf-container-module" - name: "Test Cloud Files Containers" hosts: local gather_facts: no From ccec015b5b56f7d837536992f2ef6f7da067c7ac Mon Sep 17 00:00:00 2001 From: Paul Durivage Date: Thu, 23 Jan 2014 21:05:52 -0600 Subject: [PATCH 4/4] Remove dict comprehension; use dict constructor --- cloud/rax_files | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/cloud/rax_files b/cloud/rax_files index 1fdd1351052..33a347ccf8c 100644 --- a/cloud/rax_files +++ b/cloud/rax_files @@ -155,7 +155,7 @@ except ImportError, e: print("failed=True msg='pyrax is required for this module'") sys.exit(1) -EXIT_DICT = {'success': True} +EXIT_DICT = dict(success=True) META_PREFIX = 'x-container-meta-' USER_AGENT = "Ansible/%s via pyrax" % __version__ @@ -168,10 +168,11 @@ def _get_container(module, cf, container): def _fetch_meta(module, container): + EXIT_DICT['meta'] = dict() try: - EXIT_DICT['meta'] = { - k.split(META_PREFIX)[-1]: v for k, v in container.get_metadata().items() - } + for k, v in container.get_metadata().items(): + split_key = k.split(META_PREFIX)[-1] + EXIT_DICT['meta'][split_key] = v except Exception, e: module.fail_json(msg=e.message) @@ -267,12 +268,10 @@ def container(cf, module, container_, state, meta_, clear_meta, ttl, public, pri except Exception, e: module.fail_json(msg=e.message) else: - EXIT_DICT['container_urls'] = { - 'url': c.cdn_uri, - 'ssl_url': c.cdn_ssl_uri, - 'streaming_url': c.cdn_streaming_uri, - 'ios_uri': c.cdn_ios_uri - } + EXIT_DICT['container_urls'] = dict(url=c.cdn_uri, + ssl_url=c.cdn_ssl_uri, + streaming_url=c.cdn_streaming_uri, + ios_uri=c.cdn_ios_uri) if private: try: @@ -337,7 +336,7 @@ def main(): dict( container=dict(), state=dict(choices=['present', 'absent', 'list'], default='present'), - meta=dict(type='dict', default={}), + meta=dict(type='dict', default=dict()), clear_meta=dict(choices=BOOLEANS, default=False, type='bool'), type=dict(choices=['container', 'meta'], default='container'), ttl=dict(type='int'),