openstack-ansible/rpc_deployment/library/swift
2014-08-26 18:08:15 -05:00

634 lines
19 KiB
Python

#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2014, Kevin Carter <kevin.carter@rackspace.com>
#
# 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: swift
version_added: "1.6.2"
short_description:
- Manage objects stored in swift
description:
- Manage objects stored in swift
options:
login_user:
description:
- login username
required: true
login_password:
description:
- Password of login user
required: true
login_tenant_name:
description:
- The tenant login_user belongs to
required: false
default: None
login_url:
description:
- Authentication URL
required: true
region:
description:
- The password to be assigned to the user
required: false
container:
description:
- Name of container
required: true
src:
description:
- path to object. Only used for in 'upload' & 'download' command
required: false
object:
description:
- Name of object
required: false
config_file:
description:
- Path to credential file
required: false
section:
description:
- Section within ``config_file`` to load
required: false
default: default
auth_version:
description:
- Swift authentication version
default: 2.0
required: false
snet:
description:
- Enable service Net. This may not be supported by all providers
set true or false
default: false
marker:
description:
- Set beginning marker. Only used in 'list' command.
default: false
end_marker:
description:
- Set ending marker. Only used in 'list' command.
default: false
limit:
description:
- Set limit. Only used in 'list' command.
default: false
prefix:
description:
- Set prefix filter. Only used in 'list' command.
default: false
command:
description:
- Indicate desired state of the resource
choices: ['upload', 'download', 'delete', 'create', 'list']
required: true
notes:
- Environment variables can be set for all auth credentials which allows
for seemless access. The available environment variables are,
OS_USERNAME, OS_PASSWORD, OS_TENANT_ID, OS_AUTH_URL
- A configuration file can be used to load credentials, use ``config_file``
to source the file. If you have multiple sections within the
configuration file use the ``section`` argument to define the section,
however the default is set to "default".
requirements: [ python-swiftclient ]
author: Kevin Carter
"""
EXAMPLES = """
# Create a new container
- swift: >
login_user="SomeUser"
login_password="SomePassword"
login_url="https://identity.somedomain.com/v2.0/"
command=create
container=MyNewContainer
# Upload a new object
- swift: >
login_user="SomeUser"
login_password="SomePassword"
login_url="https://identity.somedomain.com/v2.0/"
command=upload
container=MyNewContainer
src=/path/to/file
object=MyNewObjectName
# Download an object
- swift: >
login_user="SomeUser"
login_password="SomePassword"
login_url="https://identity.somedomain.com/v2.0/"
command=download
container=MyNewContainer
src=/path/to/file
object=MyOldObjectName
# list up-to 10K objects
- swift: >
login_user="SomeUser"
login_password="SomePassword"
login_url="https://identity.somedomain.com/v2.0/"
command=list
container=MyNewContainer
# Delete an Object
- swift: >
login_user="SomeUser"
login_password="SomePassword"
login_url="https://identity.somedomain.com/v2.0/"
command=delete
container=MyNewContainer
object=MyOldObjectName
# Delete a container
- swift: >
login_user="SomeUser"
login_password="SomePassword"
login_url="https://identity.somedomain.com/v2.0/"
command=delete
container=MyNewContainer
"""
COMMAND_MAP = {
'upload': {
'variables': [
'login_user',
'login_password',
'login_tenant_name',
'login_url',
'region',
'container',
'src',
'object',
'auth_version'
]
},
'download': {
'variables': [
'login_user',
'login_password',
'login_tenant_name',
'login_url',
'region',
'container',
'src',
'object',
'auth_version'
]
},
'delete': {
'variables': [
'login_user',
'login_password',
'login_tenant_name',
'login_url',
'region',
'container',
'object',
'auth_version'
]
},
'create': {
'variables': [
'login_user',
'login_password',
'login_tenant_name',
'login_url',
'region',
'container',
'auth_version'
]
},
'list': {
'variables': [
'login_user',
'login_password',
'login_tenant_name',
'login_url',
'region',
'container',
'auth_version',
'marker',
'limit',
'prefix',
'end_marker'
]
}
}
import ConfigParser
try:
from swiftclient import client
except ImportError:
swiftclient_found = False
else:
swiftclient_found = True
class ManageSwift(object):
def __init__(self, module):
"""Manage Swift via Ansible."""
self.state_change = False
self.swift = None
# Load AnsibleModule
self.module = module
def command_router(self):
"""Run the command as its provided to the module."""
command_name = self.module.params['command']
if command_name not in COMMAND_MAP:
self.failure(
error='No Command Found',
rc=2,
msg='Command [ %s ] was not found.' % command_name
)
action_command = COMMAND_MAP[command_name]
if hasattr(self, '_%s' % command_name):
action = getattr(self, '_%s' % command_name)
self._authenticate()
facts = action(variables=action_command['variables'])
if facts is None:
self.module.exit_json(changed=self.state_change)
else:
self.module.exit_json(
changed=self.state_change,
ansible_facts=facts
)
else:
self.failure(
error='Command not in ManageSwift class',
rc=2,
msg='Method [ %s ] was not found.' % command_name
)
@staticmethod
def _facts(facts):
"""Return a dict for our Ansible facts.
:param facts: ``dict`` Dict with data to return
"""
return {'swift_facts': facts}
def _get_vars(self, variables, required=None):
"""Return a dict of all variables as found within the module.
:param variables: ``list`` List of all variables that are available to
use within the Swift Command.
:param required: ``list`` Name of variables that are required.
"""
return_dict = {}
for variable in variables:
return_dict[variable] = self.module.params.get(variable)
else:
if isinstance(required, list):
for var_name in required:
check = return_dict.get(var_name)
if check is None:
self.failure(
error='Missing [ %s ] from Task or found a None'
' value' % var_name,
rc=000,
msg='variables %s - available params [ %s ]'
% (variables, self.module.params)
)
return return_dict
def failure(self, error, rc, msg):
"""Return a Failure when running an Ansible command.
:param error: ``str`` Error that occurred.
:param rc: ``int`` Return code while executing an Ansible command.
:param msg: ``str`` Message to report.
"""
self.module.fail_json(msg=msg, rc=rc, err=error)
def _env_vars(self, cred_file=None, section='default'):
"""Load environment or sourced credentials.
If the credentials are specified in either environment variables
or in a credential file the sourced variables will be loaded IF the
not set within the ``module.params``.
:param cred_file: ``str`` Path to credentials file.
:param section: ``str`` Section within creds file to load.
"""
if cred_file:
parser = ConfigParser.SafeConfigParser()
parser.optionxform = str
parser.read(os.path.expanduser(cred_file))
for name, value in parser.items(section):
if name == 'OS_AUTH_URL':
if not self.module.params.get('login_url'):
self.module.params['login_url'] = value
if name == 'OS_USERNAME':
if not self.module.params.get('login_user'):
self.module.params['login_user'] = value
if name == 'OS_PASSWORD':
if not self.module.params.get('login_password'):
self.module.params['login_password'] = value
if name == 'OS_TENANT_ID':
if not self.module.params.get('login_tenant_name'):
self.module.params['login_tenant_name'] = value
else:
if not self.module.params.get('login_url'):
authurl = os.getenv('OS_AUTH_URL')
self.module.params['login_url'] = authurl
if not self.module.params.get('login_user'):
username = os.getenv('OS_USERNAME')
self.module.params['login_user'] = username
if not self.module.params.get('login_password'):
password = os.getenv('OS_PASSWORD')
self.module.params['login_password'] = password
if not self.module.params.get('login_tenant_name'):
tenant = os.getenv('OS_TENANT_ID')
self.module.params['login_tenant_name'] = tenant
def _authenticate(self):
"""Return a swift client object."""
cred_file = self.module.params.pop('config_file', None)
section = self.module.params.pop('section')
self._env_vars(cred_file=cred_file, section=section)
required_vars = ['login_url', 'login_user', 'login_password']
variables = [
'login_url',
'login_user',
'login_password',
'login_tenant_name',
'region',
'auth_version',
'snet'
]
variables_dict = self._get_vars(variables, required=required_vars)
login_url = variables_dict.pop('login_url')
login_user = variables_dict.pop(
'login_user', os.getenv('OS_AUTH_URL')
)
login_password = variables_dict.pop(
'login_password', os.getenv('OS_AUTH_URL')
)
login_tenant_name = variables_dict.pop(
'login_tenant_name', os.getenv('OS_TENANT_ID')
)
region = variables_dict.pop('region', None)
auth_version = variables_dict.pop('auth_version')
snet = variables_dict.pop('snet', None)
if snet in BOOLEANS_TRUE:
snet = True
else:
snet = None
if login_password is None:
self.failure(
error='Missing Password',
rc=2,
msg='A Password is required for authentication. Try adding'
' [ login_password ] to the task'
)
if login_tenant_name is None:
login_tenant_name = ' '
creds_dict = {
'user': login_user,
'key': login_password,
'authurl': login_url,
'tenant_name': login_tenant_name,
'os_options': {
'region': region
},
'snet': snet,
'auth_version': auth_version
}
self.swift = client.Connection(**creds_dict)
def _upload(self, variables):
"""Upload an object to a swift object store.
:param variables: ``list`` List of all variables that are available to
use within the Keystone Command.
"""
required_vars = ['container', 'src', 'object']
variables_dict = self._get_vars(variables, required=required_vars)
container_name = variables_dict.pop('container')
object_name = variables_dict.pop('object')
src_path = variables_dict.pop('src')
self._create_container(container_name=container_name)
with open(src_path, 'rb') as f:
self.swift.put_object(container_name, object_name, contents=f)
object_data = self.swift.head_object(container_name, object_name)
self.state_change = True
return self._facts(facts=[object_data])
def _download(self, variables):
"""Upload an object to a swift object store.
:param variables: ``list`` List of all variables that are available to
use within the Keystone Command.
"""
required_vars = ['container', 'src', 'object']
variables_dict = self._get_vars(variables, required=required_vars)
container_name = variables_dict.pop('container')
object_name = variables_dict.pop('object')
src_path = variables_dict.pop('src')
with open(src_path, 'wb') as f:
f.write(
self.swift.get_object(
container_name, object_name, resp_chunk_size=204800
)
)
self.state_change = True
def _delete(self, variables):
"""Upload an object to a swift object store.
If the ``object`` variable is not used the container will be deleted.
This assumes that the container is empty.
:param variables: ``list`` List of all variables that are available to
use within the Keystone Command.
"""
required_vars = ['container']
variables_dict = self._get_vars(variables, required=required_vars)
container_name = variables_dict.pop('container')
object_name = variables_dict.pop('object', None)
if object_name:
self.swift.delete_object(container_name, object_name)
else:
self.swift.delete_container(container_name)
self.state_change = True
def _create_container(self, container_name):
"""Ensure a container exists. If it does not, it will be created.
:param container_name: ``str`` Name of the container.
"""
try:
container = self.swift.head_container(container_name)
except client.ClientException:
self.swift.put_container(container_name)
else:
return container
def _create(self, variables):
"""Create a new container in swift.
:param variables: ``list`` List of all variables that are available to
use within the Keystone Command.
"""
required_vars = ['container']
variables_dict = self._get_vars(variables, required=required_vars)
container_name = variables_dict.pop('container')
container_data = self._create_container(container_name=container_name)
if not container_data:
container_data = self.swift.head_container(container_name)
return self._facts(facts=[container_data])
def _list(self, variables):
"""Return a list of objects or containers.
If the ``container`` variable is not used this will return a list of
containers in the region.
:param variables: ``list`` List of all variables that are available to
use within the Keystone Command.
"""
variables_dict = self._get_vars(variables)
container_name = variables_dict.pop('container', None)
filters = {
'marker': variables_dict.pop('marker', None),
'limit': variables_dict.pop('limit', None),
'prefix': variables_dict.pop('prefix', None),
'end_marker': variables_dict.pop('end_marker', None)
}
if container_name:
list_data = self.swift.get_container(container_name, **filters)[1]
else:
list_data = self.swift.get_account(**filters)[1]
return self._facts(facts=list_data)
def main():
module = AnsibleModule(
argument_spec=dict(
login_user=dict(
required=False
),
login_password=dict(
required=False
),
login_tenant_name=dict(
required=False
),
login_url=dict(
required=False
),
config_file=dict(
required=False
),
section=dict(
required=False,
default='default'
),
command=dict(
required=True,
choices=COMMAND_MAP.keys()
),
region=dict(
required=False
),
container=dict(
required=False
),
src=dict(
required=False
),
object=dict(
required=False
),
marker=dict(
required=False
),
limit=dict(
required=False
),
prefix=dict(
required=False
),
end_marker=dict(
required=False
),
auth_version=dict(
required=False,
default='2.0'
),
snet=dict(
required=False,
default='false',
choices=BOOLEANS
)
),
supports_check_mode=False,
)
sm = ManageSwift(module=module)
if not swiftclient_found:
sm.failure(
error='python-swiftclient is missing',
rc=2,
msg='Swift client was not importable, is it installed?'
)
sm.command_router()
# import module snippets
from ansible.module_utils.basic import *
if __name__ == '__main__':
main()