mirror of https://github.com/ceph/ceph-ansible.git
572 lines
20 KiB
Python
572 lines
20 KiB
Python
|
#!/usr/bin/python
|
||
|
|
||
|
# Copyright 2018 Daniel Pivonka <dpivonka@redhat.com>
|
||
|
# Copyright 2018 Red Hat, Inc.
|
||
|
#
|
||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||
|
|
||
|
ANSIBLE_METADATA = {
|
||
|
'metadata_version': '1.1',
|
||
|
'status': ['preview'],
|
||
|
'supported_by': 'community'
|
||
|
}
|
||
|
|
||
|
DOCUMENTATION = '''
|
||
|
---
|
||
|
module: ceph_add_users_buckets
|
||
|
short_description: bulk create user and buckets
|
||
|
description:
|
||
|
- Bulk create Ceph Object Storage users and buckets
|
||
|
|
||
|
option:
|
||
|
rgw_host:
|
||
|
description:
|
||
|
- a radosgw host in the ceph cluster
|
||
|
required: true
|
||
|
port:
|
||
|
description:
|
||
|
- tcp port of the radosgw host
|
||
|
required: true
|
||
|
is_secure:
|
||
|
description:
|
||
|
- boolean indicating whether the instance is running over https
|
||
|
required: false
|
||
|
default: false
|
||
|
admin_access_key:
|
||
|
description:
|
||
|
- radosgw admin user's access key
|
||
|
required: true
|
||
|
admin_secret_key:
|
||
|
description:
|
||
|
- radosgw admin user's secret key
|
||
|
required: true
|
||
|
users:
|
||
|
description:
|
||
|
- list of users to be created containing sub options
|
||
|
required: false
|
||
|
sub_options:
|
||
|
username:
|
||
|
description:
|
||
|
- username for new user
|
||
|
required: true
|
||
|
fullname:
|
||
|
description:
|
||
|
- fullname for new user
|
||
|
required: true
|
||
|
email:
|
||
|
description:
|
||
|
- email for new user
|
||
|
required: false
|
||
|
maxbucket:
|
||
|
description:
|
||
|
- max bucket for new user
|
||
|
required: false
|
||
|
default: 1000
|
||
|
suspend:
|
||
|
description:
|
||
|
- suspend a new user apon creation
|
||
|
required: false
|
||
|
default: false
|
||
|
autogenkey:
|
||
|
description:
|
||
|
- auto generate keys for new user
|
||
|
required: false
|
||
|
default: true
|
||
|
accesskey:
|
||
|
description:
|
||
|
- access key for new user
|
||
|
required: false
|
||
|
secretkey:
|
||
|
description:
|
||
|
- secret key for new user
|
||
|
required: false
|
||
|
userquota:
|
||
|
description:
|
||
|
- enable/disable user quota for new user
|
||
|
required: false
|
||
|
default: false
|
||
|
usermaxsize:
|
||
|
description:
|
||
|
- with user quota enabled specify quota size in kb
|
||
|
required: false
|
||
|
default: unlimited
|
||
|
usermaxobjects:
|
||
|
description:
|
||
|
- with user quota enabled specify maximum number of objects
|
||
|
required: false
|
||
|
default: unlimited
|
||
|
bucketquota:
|
||
|
description:
|
||
|
- enable/disable bucket quota for new user
|
||
|
required: false
|
||
|
default: false
|
||
|
bucketmaxsize:
|
||
|
description:
|
||
|
- with bucket quota enabled specify bucket size in kb
|
||
|
required: false
|
||
|
default: unlimited
|
||
|
bucketmaxobjects:
|
||
|
description:
|
||
|
- with bucket quota enabled specify maximum number of objects
|
||
|
required: false
|
||
|
default: unlimited
|
||
|
buckets:
|
||
|
description:
|
||
|
- list of buckets to be created containing sub options
|
||
|
required: false
|
||
|
sub_options:
|
||
|
bucket:
|
||
|
description:
|
||
|
- name for new bucket
|
||
|
required: true
|
||
|
user:
|
||
|
description:
|
||
|
- user new bucket will be linked too
|
||
|
required: true
|
||
|
|
||
|
|
||
|
requirements: ['radosgw', 'boto']
|
||
|
|
||
|
author:
|
||
|
- 'Daniel Pivonka'
|
||
|
|
||
|
'''
|
||
|
|
||
|
EXAMPLES = '''
|
||
|
# single basic user
|
||
|
- name: single basic user
|
||
|
ceph_add_users_buckets:
|
||
|
rgw_host: '172.16.0.12'
|
||
|
port: 8080
|
||
|
admin_access_key: 'N61I8625V4XTWGDTLBLL'
|
||
|
admin_secret_key: 'HZrkuHHO9usUurDWBQHTeLIjO325bIULaC7DxcoV'
|
||
|
users:
|
||
|
- username: 'test1'
|
||
|
fullname: 'tester'
|
||
|
|
||
|
|
||
|
# single complex user
|
||
|
- name: single complex user
|
||
|
ceph_add_users_buckets:
|
||
|
rgw_host: '172.16.0.12'
|
||
|
port: 8080
|
||
|
admin_access_key: 'N61I8625V4XTWGDTLBLL'
|
||
|
admin_secret_key: 'HZrkuHHO9usUurDWBQHTeLIjO325bIULaC7DxcoV'
|
||
|
users:
|
||
|
- username: 'test1'
|
||
|
fullname: 'tester'
|
||
|
email: 'dan@email.com'
|
||
|
maxbucket: 666
|
||
|
suspend: true
|
||
|
autogenkey: true
|
||
|
accesskey: 'B3AR4Q33L59YV56A9A2F'
|
||
|
secretkey: 'd84BRnMysnVGSyZiRlYUMduVgIarQWiNMdKzrF76'
|
||
|
userquota: true
|
||
|
usermaxsize: '1000'
|
||
|
usermaxobjects: 3
|
||
|
bucketquota: true
|
||
|
bucketmaxsize: '1000'
|
||
|
bucketmaxobjects: 3
|
||
|
|
||
|
# multi user
|
||
|
- name: multi user
|
||
|
ceph_add_users_buckets:
|
||
|
rgw_host: '172.16.0.12'
|
||
|
port: 8080
|
||
|
admin_access_key: 'N61I8625V4XTWGDTLBLL'
|
||
|
admin_secret_key: 'HZrkuHHO9usUurDWBQHTeLIjO325bIULaC7DxcoV'
|
||
|
users:
|
||
|
- username: 'test1'
|
||
|
fullname: 'tester'
|
||
|
email: 'dan@email.com'
|
||
|
maxbucket: 666
|
||
|
suspend: true
|
||
|
autogenkey: true
|
||
|
accesskey: 'B3AR4Q33L59YV56A9A2F'
|
||
|
secretkey: 'd84BRnMysnVGSyZiRlYUMduVgIarQWiNMdKzrF76'
|
||
|
userquota: true
|
||
|
usermaxsize: '1000K'
|
||
|
usermaxobjects: 3
|
||
|
bucketquota: true
|
||
|
bucketmaxsize: '1000K'
|
||
|
bucketmaxobjects: 3
|
||
|
- username: 'test2'
|
||
|
fullname: 'tester'
|
||
|
|
||
|
# single bucket
|
||
|
- name: single basic user
|
||
|
ceph_add_users_buckets:
|
||
|
rgw_host: '172.16.0.12'
|
||
|
port: 8080
|
||
|
admin_access_key: 'N61I8625V4XTWGDTLBLL'
|
||
|
admin_secret_key: 'HZrkuHHO9usUurDWBQHTeLIjO325bIULaC7DxcoV'
|
||
|
buckets:
|
||
|
- bucket: 'heyimabucket1'
|
||
|
user: 'test1'
|
||
|
|
||
|
# multi bucket
|
||
|
- name: single basic user
|
||
|
ceph_add_users_buckets:
|
||
|
rgw_host: '172.16.0.12'
|
||
|
port: 8080
|
||
|
admin_access_key: 'N61I8625V4XTWGDTLBLL'
|
||
|
admin_secret_key: 'HZrkuHHO9usUurDWBQHTeLIjO325bIULaC7DxcoV'
|
||
|
buckets:
|
||
|
- bucket: 'heyimabucket1'
|
||
|
user: 'test1'
|
||
|
- bucket: 'heyimabucket2'
|
||
|
user: 'test2'
|
||
|
- bucket: 'heyimabucket3'
|
||
|
user: 'test2'
|
||
|
|
||
|
# buckets and users
|
||
|
- name: single basic user
|
||
|
ceph_add_users_buckets:
|
||
|
rgw_host: '172.16.0.12'
|
||
|
port: 8080
|
||
|
admin_access_key: 'N61I8625V4XTWGDTLBLL'
|
||
|
admin_secret_key: 'HZrkuHHO9usUurDWBQHTeLIjO325bIULaC7DxcoV'
|
||
|
users:
|
||
|
- username: 'test1'
|
||
|
fullname: 'tester'
|
||
|
email: 'dan@email.com'
|
||
|
maxbucket: 666
|
||
|
- username: 'test2'
|
||
|
fullname: 'tester'
|
||
|
email: 'dan1@email.com'
|
||
|
accesskey: 'B3AR4Q33L59YV56A9A2F'
|
||
|
secretkey: 'd84BRnMysnVGSyZiRlYUMduVgIarQWiNMdKzrF76'
|
||
|
userquota: true
|
||
|
usermaxsize: '1000'
|
||
|
usermaxobjects: 3
|
||
|
bucketquota: true
|
||
|
bucketmaxsize: '1000'
|
||
|
bucketmaxobjects: 3
|
||
|
buckets:
|
||
|
- bucket: 'heyimabucket1'
|
||
|
user: 'test1'
|
||
|
- bucket: 'heyimabucket2'
|
||
|
user: 'test2'
|
||
|
- bucket: 'heyimabucket3'
|
||
|
user: 'test2'
|
||
|
|
||
|
'''
|
||
|
|
||
|
RETURN = '''
|
||
|
error_messages:
|
||
|
description: error for failed user or bucket.
|
||
|
returned: always
|
||
|
type: list
|
||
|
sample: [
|
||
|
"test2: could not modify user: unable to modify user, cannot add duplicate email\n"
|
||
|
]
|
||
|
|
||
|
failed_users:
|
||
|
description: users that were not created.
|
||
|
returned: always
|
||
|
type: str
|
||
|
sample: "test2"
|
||
|
|
||
|
added_users:
|
||
|
description: users that were created.
|
||
|
returned: always
|
||
|
type: str
|
||
|
sample: "test1"
|
||
|
|
||
|
failed_buckets:
|
||
|
description: buckets that were not created.
|
||
|
returned: always
|
||
|
type: str
|
||
|
sample: "heyimabucket3"
|
||
|
|
||
|
added_buckets:
|
||
|
description: buckets that were created.
|
||
|
returned: always
|
||
|
type: str
|
||
|
sample: "heyimabucket1, heyimabucket2"
|
||
|
|
||
|
'''
|
||
|
|
||
|
from ansible.module_utils.basic import AnsibleModule
|
||
|
from socket import error as socket_error
|
||
|
import boto
|
||
|
import radosgw
|
||
|
|
||
|
|
||
|
def create_users(rgw, users, result):
|
||
|
|
||
|
added_users = []
|
||
|
failed_users = []
|
||
|
|
||
|
for user in users:
|
||
|
|
||
|
# get info
|
||
|
username = user['username']
|
||
|
fullname = user['fullname']
|
||
|
email = user['email']
|
||
|
maxbucket = user['maxbucket']
|
||
|
suspend = user['suspend']
|
||
|
autogenkey = user['autogenkey']
|
||
|
accesskey = user['accesskey']
|
||
|
secretkey = user['secretkey']
|
||
|
userquota = user['userquota']
|
||
|
usermaxsize = user['usermaxsize']
|
||
|
usermaxobjects = user['usermaxobjects']
|
||
|
bucketquota = user['bucketquota']
|
||
|
bucketmaxsize = user['bucketmaxsize']
|
||
|
bucketmaxobjects = user['bucketmaxobjects']
|
||
|
|
||
|
fail_flag = False
|
||
|
|
||
|
# check if user exists
|
||
|
try:
|
||
|
user_info = rgw.get_user(uid=username)
|
||
|
except radosgw.exception.RadosGWAdminError as e:
|
||
|
# it doesnt exist
|
||
|
user_info = None
|
||
|
|
||
|
# user exists can not create
|
||
|
if user_info:
|
||
|
result['error_messages'].append(username + ' UserExists')
|
||
|
failed_users.append(username)
|
||
|
else:
|
||
|
# user doesnt exist create it
|
||
|
if email:
|
||
|
if autogenkey:
|
||
|
try:
|
||
|
rgw.create_user(username, fullname, email=email, key_type='s3',
|
||
|
generate_key=autogenkey,
|
||
|
max_buckets=maxbucket, suspended=suspend)
|
||
|
except radosgw.exception.RadosGWAdminError as e:
|
||
|
result['error_messages'].append(username + ' ' + e.get_code())
|
||
|
fail_flag = True
|
||
|
else:
|
||
|
try:
|
||
|
rgw.create_user(username, fullname, email=email, key_type='s3',
|
||
|
access_key=accesskey, secret_key=secretkey,
|
||
|
max_buckets=maxbucket, suspended=suspend)
|
||
|
except radosgw.exception.RadosGWAdminError as e:
|
||
|
result['error_messages'].append(username + ' ' + e.get_code())
|
||
|
fail_flag = True
|
||
|
else:
|
||
|
if autogenkey:
|
||
|
try:
|
||
|
rgw.create_user(username, fullname, key_type='s3',
|
||
|
generate_key=autogenkey,
|
||
|
max_buckets=maxbucket, suspended=suspend)
|
||
|
except radosgw.exception.RadosGWAdminError as e:
|
||
|
result['error_messages'].append(username + ' ' + e.get_code())
|
||
|
fail_flag = True
|
||
|
else:
|
||
|
try:
|
||
|
rgw.create_user(username, fullname, key_type='s3',
|
||
|
access_key=accesskey, secret_key=secretkey,
|
||
|
max_buckets=maxbucket, suspended=suspend)
|
||
|
except radosgw.exception.RadosGWAdminError as e:
|
||
|
result['error_messages'].append(username + ' ' + e.get_code())
|
||
|
fail_flag = True
|
||
|
|
||
|
if not fail_flag and userquota:
|
||
|
try:
|
||
|
rgw.set_quota(username, 'user', max_objects=usermaxobjects,
|
||
|
max_size_kb=usermaxsize, enabled=True)
|
||
|
except radosgw.exception.RadosGWAdminError as e:
|
||
|
result['error_messages'].append(username + ' ' + e.get_code())
|
||
|
fail_flag = True
|
||
|
|
||
|
if not fail_flag and bucketquota:
|
||
|
try:
|
||
|
rgw.set_quota(username, 'bucket', max_objects=bucketmaxobjects,
|
||
|
max_size_kb=bucketmaxsize, enabled=True)
|
||
|
except radosgw.exception.RadosGWAdminError as e:
|
||
|
result['error_messages'].append(username + ' ' + e.get_code())
|
||
|
fail_flag = True
|
||
|
|
||
|
if fail_flag:
|
||
|
try:
|
||
|
rgw.delete_user(username)
|
||
|
except radosgw.exception.RadosGWAdminError as e:
|
||
|
pass
|
||
|
failed_users.append(username)
|
||
|
else:
|
||
|
added_users.append(username)
|
||
|
|
||
|
result['added_users'] = ", ".join(added_users)
|
||
|
result['failed_users'] = ", ".join(failed_users)
|
||
|
|
||
|
|
||
|
def create_buckets(rgw, buckets, result):
|
||
|
|
||
|
added_buckets = []
|
||
|
failed_buckets = []
|
||
|
|
||
|
for bucket_info in buckets:
|
||
|
bucket = bucket_info['bucket']
|
||
|
user = bucket_info['user']
|
||
|
|
||
|
# check if bucket exists
|
||
|
try:
|
||
|
bucket_info = rgw.get_bucket(bucket_name=bucket)
|
||
|
except TypeError:
|
||
|
# it doesnt exist
|
||
|
bucket_info = None
|
||
|
|
||
|
# if it exists add to failed list
|
||
|
if bucket_info:
|
||
|
failed_buckets.append(bucket)
|
||
|
result['error_messages'].append(bucket + ' BucketExists')
|
||
|
else:
|
||
|
# bucket doesn't exist, so we need to create it
|
||
|
bucket_info = create_bucket(rgw, bucket)
|
||
|
if bucket_info:
|
||
|
# bucket created ok, link to user
|
||
|
|
||
|
# check if user exists
|
||
|
try:
|
||
|
user_info = rgw.get_user(uid=user)
|
||
|
except radosgw.exception.RadosGWAdminError as e:
|
||
|
# it doesnt exist
|
||
|
user_info = None
|
||
|
|
||
|
# user exists, link
|
||
|
if user_info:
|
||
|
try:
|
||
|
rgw.link_bucket(bucket_name=bucket,
|
||
|
bucket_id=bucket_info.id,
|
||
|
uid=user)
|
||
|
added_buckets.append(bucket)
|
||
|
except radosgw.exception.RadosGWAdminError as e:
|
||
|
result['error_messages'].append(bucket + e.get_code())
|
||
|
try:
|
||
|
rgw.delete_bucket(bucket, purge_objects=True)
|
||
|
except radosgw.exception.RadosGWAdminError as e:
|
||
|
pass
|
||
|
failed_buckets.append(bucket)
|
||
|
|
||
|
else:
|
||
|
# user doesnt exist cant be link delete bucket
|
||
|
try:
|
||
|
rgw.delete_bucket(bucket, purge_objects=True)
|
||
|
except radosgw.exception.RadosGWAdminError as e:
|
||
|
pass
|
||
|
failed_buckets.append(bucket)
|
||
|
result['error_messages'].append(bucket + ' could not be linked' + ', NoSuchUser ' + user)
|
||
|
|
||
|
else:
|
||
|
# something went wrong
|
||
|
failed_buckets.append(bucket)
|
||
|
result['error_messages'].append(bucket + ' could not be created')
|
||
|
|
||
|
result['added_buckets'] = ", ".join(added_buckets)
|
||
|
result['failed_buckets'] = ", ".join(failed_buckets)
|
||
|
|
||
|
|
||
|
def create_bucket(rgw, bucket):
|
||
|
conn = boto.connect_s3(aws_access_key_id=rgw.provider._access_key,
|
||
|
aws_secret_access_key=rgw.provider._secret_key,
|
||
|
host=rgw._connection[0],
|
||
|
port=rgw.port,
|
||
|
is_secure=rgw.is_secure,
|
||
|
calling_format=boto.s3.connection.OrdinaryCallingFormat(),
|
||
|
)
|
||
|
|
||
|
try:
|
||
|
conn.create_bucket(bucket_name=bucket)
|
||
|
bucket_info = rgw.get_bucket(bucket_name=bucket)
|
||
|
except boto.exception.S3ResponseError:
|
||
|
return None
|
||
|
else:
|
||
|
return bucket_info
|
||
|
|
||
|
|
||
|
def main():
|
||
|
# arguments/parameters that a user can pass to the module
|
||
|
fields = dict(rgw_host=dict(type='str', required=True),
|
||
|
port=dict(type='int', required=True),
|
||
|
is_secure=dict(type='bool',
|
||
|
required=False,
|
||
|
default=False),
|
||
|
admin_access_key=dict(type='str', required=True),
|
||
|
admin_secret_key=dict(type='str', required=True),
|
||
|
buckets=dict(type='list', required=False, elements='dict',
|
||
|
options=dict(bucket=dict(type='str', required=True),
|
||
|
user=dict(type='str', required=True))),
|
||
|
users=dict(type='list', required=False, elements='dict',
|
||
|
options=dict(username=dict(type='str', required=True),
|
||
|
fullname=dict(type='str', required=True),
|
||
|
email=dict(type='str', required=False),
|
||
|
maxbucket=dict(type='int', required=False, default=1000),
|
||
|
suspend=dict(type='bool', required=False, default=False),
|
||
|
autogenkey=dict(type='bool', required=False, default=True),
|
||
|
accesskey=dict(type='str', required=False),
|
||
|
secretkey=dict(type='str', required=False),
|
||
|
userquota=dict(type='bool', required=False, default=False),
|
||
|
usermaxsize=dict(type='str', required=False, default='-1'),
|
||
|
usermaxobjects=dict(type='int', required=False, default=-1),
|
||
|
bucketquota=dict(type='bool', required=False, default=False),
|
||
|
bucketmaxsize=dict(type='str', required=False, default='-1'),
|
||
|
bucketmaxobjects=dict(type='int', required=False, default=-1))))
|
||
|
|
||
|
# the AnsibleModule object
|
||
|
module = AnsibleModule(argument_spec=fields,
|
||
|
supports_check_mode=False)
|
||
|
|
||
|
# get vars
|
||
|
rgw_host = module.params.get('rgw_host')
|
||
|
port = module.params.get('port')
|
||
|
is_secure = module.params.get('is_secure')
|
||
|
admin_access_key = module.params.get('admin_access_key')
|
||
|
admin_secret_key = module.params.get('admin_secret_key')
|
||
|
users = module.params['users']
|
||
|
buckets = module.params.get('buckets')
|
||
|
|
||
|
# seed the result dict in the object
|
||
|
result = dict(
|
||
|
changed=False,
|
||
|
error_messages=[],
|
||
|
added_users='',
|
||
|
failed_users='',
|
||
|
added_buckets='',
|
||
|
failed_buckets='',
|
||
|
)
|
||
|
|
||
|
# radosgw connection
|
||
|
rgw = radosgw.connection.RadosGWAdminConnection(host=rgw_host,
|
||
|
port=port,
|
||
|
access_key=admin_access_key,
|
||
|
secret_key=admin_secret_key,
|
||
|
aws_signature='AWS4',
|
||
|
is_secure=is_secure)
|
||
|
|
||
|
# test connection
|
||
|
connected = True
|
||
|
try:
|
||
|
rgw.get_usage()
|
||
|
except radosgw.exception.RadosGWAdminError as e:
|
||
|
connected = False
|
||
|
result['error_messages'] = e.get_code()
|
||
|
except socket_error as e:
|
||
|
connected = False
|
||
|
result['error_messages'] = str(e)
|
||
|
|
||
|
if connected and users:
|
||
|
create_users(rgw, users, result)
|
||
|
|
||
|
if connected and buckets:
|
||
|
create_buckets(rgw, buckets, result)
|
||
|
|
||
|
if result['added_users'] != '' or result['added_buckets'] != '':
|
||
|
result['changed'] = True
|
||
|
|
||
|
# conditional state caused a failure
|
||
|
if result['added_users'] == '' and result['added_buckets'] == '':
|
||
|
module.fail_json(msg='No users or buckets were added successfully',
|
||
|
**result)
|
||
|
|
||
|
# EXIT
|
||
|
module.exit_json(**result)
|
||
|
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
main()
|