#!/usr/bin/env python

# Copyright 2018 99cloud
#
# 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 json
import re
import subprocess  # nosec


DOCUMENTATION = '''
---
module: kolla_ceph_keyring
short_description: >
  Module for update ceph client keyring caps in kolla.
description:
  - A module used to update ceph client keyring caps in kolla.
options:
  name:
    description:
      - the client name in ceph
    required: True
    type: str
  container_name:
    description:
      - the ceph mon container name
    required: True
    default: ceph_mon
    type: str
  caps:
    description:
      - the ceph auth caps
    required: True
    type: dict
author: Jeffrey Zhang
'''

EXAMPLES = '''
- name: configure admin client caps
  kolla_ceph_keyring:
    name: client.admin
    container_name: ceph_mon
    caps:
      mds: 'allow *'
      mon: 'allow *'
      osd: 'allow *'
      mgr: 'allow *'
'''


enoent_re = re.compile(r"\bENOENT\b")


class CephKeyring(object):
    def __init__(self, name, caps, container_name='ceph_mon'):
        self.name = name
        self.caps = caps
        self.container_name = container_name
        self.changed = False
        self.message = None

    def _run(self, cmd):
        _prefix = ['docker', 'exec', self.container_name]
        cmd = _prefix + cmd
        proc = subprocess.Popen(cmd,  # nosec
                                stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE)
        stdout, stderr = proc.communicate()
        retcode = proc.poll()
        if retcode != 0:
            output = 'stdout: "%s", stderr: "%s"' % (stdout, stderr)
            raise subprocess.CalledProcessError(retcode, cmd, output)
        return stdout

    def _format_caps(self):
        caps = []
        for obj in sorted(self.caps):
            caps.extend([obj, self.caps[obj]])
        return caps

    def parse_stdout(self, stdout):
        keyring = json.loads(stdout)
        # there should be only one element
        return keyring[0]

    def ensure_keyring(self):
        try:
            stdout = self.get_keyring()
        except subprocess.CalledProcessError as e:
            if e.returncode != 2 or not enoent_re.search(e.output):
                # this is not a missing keyring case
                raise
            # keyring doesn't exsit, try to create it
            stdout = self.create_keyring()
            self.changed = True
            self.message = 'ceph keyring for %s is created' % self.name
        keyring = self.parse_stdout(stdout)
        if keyring['caps'] != self.caps:
            self.update_caps()
            stdout = self.get_keyring()
            keyring = self.parse_stdout(stdout)
            self.changed = True
            self.message = 'ceph keyring for %s is updated' % self.name
        self.keyring = keyring
        return self.keyring

    def get_keyring(self):
        ceph_cmd = ['ceph', '--format', 'json', 'auth', 'get', self.name]
        return self._run(ceph_cmd)

    def update_caps(self):
        ceph_cmd = ['ceph', '--format', 'json', 'auth', 'caps', self.name]
        caps = self._format_caps()
        ceph_cmd.extend(caps)
        self._run(ceph_cmd)

    def create_keyring(self):
        ceph_cmd = ['ceph', '--format', 'json', 'auth',
                    'get-or-create', self.name]
        caps = self._format_caps()
        ceph_cmd.extend(caps)
        return self._run(ceph_cmd)


def main():
    specs = dict(
        name=dict(type='str', required=True),
        container_name=dict(type='str', default='ceph_mon'),
        caps=dict(type='dict', required=True)
    )
    module = AnsibleModule(argument_spec=specs)  # noqa
    params = module.params
    ceph_keyring = CephKeyring(params['name'],
                               params['caps'],
                               params['container_name'])
    try:
        keyring = ceph_keyring.ensure_keyring()
        module.exit_json(changed=ceph_keyring.changed,
                         keyring=keyring,
                         message=ceph_keyring.message)
    except subprocess.CalledProcessError as ex:
        msg = ('Failed to call command: %s returncode: %s output: %s' %
               (ex.cmd, ex.returncode, ex.output))
        module.fail_json(msg=msg)


from ansible.module_utils.basic import *  # noqa
if __name__ == "__main__":
    main()
