]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph-ansible.git/commitdiff
rgw users/buckets module
authorDaniel-Pivonka <dpivonka@redhat.com>
Thu, 29 Nov 2018 15:17:40 +0000 (10:17 -0500)
committerSébastien Han <seb@redhat.com>
Thu, 20 Dec 2018 13:23:25 +0000 (14:23 +0100)
ansible module to bulk create rgw users and buckets

Signed-off-by: Daniel-Pivonka <dpivonka@redhat.com>
library/ceph_add_users_buckets.py [new file with mode: 0644]

diff --git a/library/ceph_add_users_buckets.py b/library/ceph_add_users_buckets.py
new file mode 100644 (file)
index 0000000..346cec8
--- /dev/null
@@ -0,0 +1,571 @@
+#!/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()