f827d8d015
This patch ensures that all python files (including the Ansible modules) are included in the flake8 test. This patch also fixes the current flake8 failures found in the Ansible modules we're carrying. Change-Id: I9ebef6ef7bbd2e424ad1be26d593471fd6fd3056 Closes-Bug: #1473002
222 lines
6.6 KiB
Python
222 lines
6.6 KiB
Python
#!/usr/bin/python
|
|
# (c) 2015, Sudarshan Acharya <sudarshan.acharya@gmail.com>
|
|
# Daniel Curran <daniel.curran@rackspace.com>
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
|
|
import ast
|
|
import base64
|
|
import collections
|
|
import os
|
|
|
|
DOCUMENTATION = """
|
|
---
|
|
module: copy_updates
|
|
short_description: Copies source file (or content) with updates to dest
|
|
location.
|
|
description:
|
|
- The M(copy_updates) module copies a source file or content, applies
|
|
somes updates and copies it to dest location.
|
|
options:
|
|
src:
|
|
description:
|
|
- Local path to a source file; can be absolute or relative.
|
|
required: false
|
|
content:
|
|
description:
|
|
- When used instead of src, sets the contents of the file to specified
|
|
value.
|
|
required: false
|
|
updates:
|
|
description:
|
|
- List of key-values to update src file with.
|
|
required: false
|
|
dest:
|
|
description:
|
|
- Dest absolute path where the file and updates should be copied to.
|
|
required: true
|
|
backup:
|
|
description:
|
|
- Create a backup file of the dest file so you can get the original file
|
|
back just in case.
|
|
required: false
|
|
choices: [ "yes", "no" ]
|
|
default: "no"
|
|
"""
|
|
|
|
EXAMPLES = """
|
|
# Get the src, apply the updates and copy to dest if it differs from copied
|
|
version
|
|
- copy_updates:
|
|
src=/src/config.json
|
|
updates={"key1": "val1"}
|
|
dest=/dest/config.json
|
|
owner=foo
|
|
group=foo
|
|
mode=0644
|
|
|
|
# Get the content, apply the updates and copy to dest if it differs from
|
|
copied version
|
|
- copy_updates:
|
|
content={"key1": "val1"}
|
|
updates={"key1": "val2"}
|
|
dest=/dest/config.json
|
|
owner=foo
|
|
group=foo
|
|
mode=0644
|
|
|
|
# Back up the original dest and copy the updated src to dest if it differs
|
|
from copied version
|
|
- copy_updates:
|
|
src=/src/config.json
|
|
dest=/etc/ntp.conf
|
|
owner=root
|
|
group=root
|
|
mode=644
|
|
backup=yes
|
|
"""
|
|
|
|
|
|
def get_json_from_file(module, file_path):
|
|
check_file(module, file_path)
|
|
try:
|
|
with open(file_path) as infile:
|
|
json_obj = json.loads(infile.read(),
|
|
object_pairs_hook=collections.OrderedDict)
|
|
return json_obj
|
|
except Exception as e:
|
|
module.fail_json(
|
|
msg="Error reading from file %s: %s" % (file_path, str(e)))
|
|
|
|
|
|
def dump_json_to_file(module, file_path, json_obj):
|
|
try:
|
|
with open(file_path, 'w') as outfile:
|
|
json.dump(json_obj, outfile, indent=4)
|
|
except Exception as e:
|
|
module.fail_json(
|
|
msg="Error writing to file %s: %s" % (file_path, str(e)))
|
|
|
|
|
|
def check_file(module, file_path):
|
|
if not os.access(file_path, os.R_OK):
|
|
module.fail_json(msg="%s not readable" % (file_path))
|
|
elif not os.path.isfile(file_path):
|
|
module.fail_json(msg="%s is not a file" % (file_path))
|
|
|
|
|
|
def str_to_json(module, json_str):
|
|
try:
|
|
try:
|
|
return json.loads(json_str,
|
|
object_pairs_hook=collections.OrderedDict)
|
|
except Exception:
|
|
return ast.literal_eval(json_str)
|
|
except Exception:
|
|
module.fail_json(msg="JSON format expected for: %s " % (json_str))
|
|
|
|
|
|
def main():
|
|
|
|
module = AnsibleModule(
|
|
argument_spec=dict(
|
|
src=dict(required=False),
|
|
content=dict(required=False, no_log=True),
|
|
content_type=dict(default="json", choices=['json'], type="str"),
|
|
updates=dict(required=False),
|
|
dest=dict(required=True),
|
|
backup=dict(default=False, type='bool'),
|
|
force=dict(default=True, aliases=['thirsty'], type='bool'),
|
|
),
|
|
add_file_common_args=True,
|
|
)
|
|
src = None
|
|
if module.params['src'] is not None:
|
|
src = os.path.expanduser(module.params['src'])
|
|
|
|
content = module.params['content']
|
|
content_type = module.params['content_type']
|
|
dest = os.path.expanduser(module.params['dest'])
|
|
updates = module.params['updates']
|
|
backup = module.params['backup']
|
|
|
|
if (src is None and content is None) or dest is None:
|
|
module.fail_json(msg="src (or content) and dest are required")
|
|
|
|
if (src is not None and content is not None):
|
|
module.fail_json(msg="provide src or content")
|
|
|
|
if content_type != "json":
|
|
module.fail_json(msg="content_type %s not supported" % (content_type))
|
|
|
|
# Working around an Ansible bug by using binary content for json
|
|
# https://github.com/ansible/ansible/issues/10659
|
|
if content_type == "json":
|
|
content = base64.b64decode(content)
|
|
|
|
src_json = None
|
|
if src and os.path.exists(src):
|
|
src_json = get_json_from_file(module, src)
|
|
elif(content is not None):
|
|
src_json = str_to_json(module, content)
|
|
else:
|
|
module.fail_json(msg="Source %s failed to transfer" % (src))
|
|
|
|
if updates:
|
|
# Read src and replace src vals with new vals from updates
|
|
updates = str_to_json(module, updates)
|
|
for key, val in updates.iteritems():
|
|
src_json[key] = val
|
|
|
|
changed = False
|
|
backup_file = None
|
|
if os.path.exists(dest):
|
|
# Check if the src and dest are different and replace the dest.
|
|
dest_json = get_json_from_file(module, dest)
|
|
|
|
# Check if the src and dest and replace the dest.
|
|
if src_json != dest_json:
|
|
if backup:
|
|
backup_file = module.backup_local(dest)
|
|
dump_json_to_file(module, dest, src_json)
|
|
changed = True
|
|
else:
|
|
changed = False
|
|
else:
|
|
# Create the dest if it doens't exist already
|
|
dest_file_loc = os.path.split(dest)[0]
|
|
if not os.path.exists(dest_file_loc):
|
|
os.makedirs(dest_file_loc)
|
|
dump_json_to_file(module, dest, src_json)
|
|
changed = True
|
|
|
|
if src:
|
|
os.remove(src)
|
|
|
|
res_args = dict(
|
|
dest=dest, src=src, changed=changed
|
|
)
|
|
if backup_file:
|
|
res_args['backup_file'] = backup_file
|
|
|
|
module.params['dest'] = dest
|
|
file_args = module.load_file_common_arguments(module.params)
|
|
res_args['changed'] = module.set_fs_attributes_if_different(
|
|
file_args, res_args['changed'])
|
|
|
|
module.exit_json(**res_args)
|
|
|
|
from ansible.module_utils.basic import *
|
|
main()
|