# -*- coding: utf-8 -*-


from ansible.inventory.group   import Group
from ansible.inventory.host    import Host
from ansible.inventory         import Inventory
from ansible.runner            import Runner
from ansible.playbook          import PlayBook


class AnsibleError(StandardError):
    """
    the base AnsibleError which contains error(required),
    data(optional) and message(optional).
    存储所有Ansible 异常对象
    """
    def __init__(self, error, data='', message=''):
        super(AnsibleError, self).__init__(message)
        self.error = error
        self.data = data
        self.message = message


class CommandValueError(AnsibleError):
    """
    indicate the input value has error or invalid. 
    the data specifies the error field of input form.
    输入不合法 异常对象
    """
    def __init__(self, field, message=''):
        super(CommandValueError, self).__init__('value:invalid', field, message)


class MyInventory(object):
    """
    this is my ansible inventory object.
    """
    def __init__(self, resource):
        """
        resource :
                 必须是一个字典列表，比如
                 [{"hostname": "10.10.10.10", "port": "22", 
                   "username": "test", "password": "mypass"}, ...] 
        """
        self.resource = resource
        self._gen_inventory()


    def _gen_inventory(self):
        """
        add hosts to inventory.
        """
        my_group = Group(name='my_group')

        for host in self.resource:
            hostname = host.get("hostname")
            hostport = host.get("hostport")
            username = host.get("username")
            password = host.get("password") 
            my_host = Host(name=hostname, port=hostport)
            my_host.set_variable('ansible_ssh_host', hostname)
            my_host.set_variable('ansible_ssh_port', hostport)
            my_host.set_variable('ansible_ssh_user', username) 
            my_host.set_variable('ansible_ssh_pass', password)
            my_group.add_host(my_host)

        my_inventory = Inventory()
        my_inventory.add_group(my_group)
        my_inventory.subset('my_group')

        self.inventory = my_inventory


class Command(MyInventory):
    """
    this is a command object for parallel execute command.
    """
    def __init__(self, *args, **kwargs):
        super(Command, self).__init__(*args, **kwargs)
   

    def run(self, command, module_name="command", timeout=5, forks=10):
        """
        run command from andible ad-hoc.
        command  : 必须是一个需要执行的命令字符串， 比如 
                 'uname -a'
        """
        if module_name not in ["raw", "command", "shell"]:
            raise  CommandValueError("module_name",
                                     "module_name must be of the 'raw, command, shell'")
        hoc = Runner(module_name=module_name,
                     module_args=command,
                     timeout=timeout,
                     inventory=self.inventory,
                     subset='my_group',
                     forks=forks
                     )
        
        self.results = hoc.run()
        return self.stdout


    @property
    def raw_results(self):
        """
        get the ansible raw results.
        """
        return self.results


    @property
    def exec_time(self):
        """
        get the command execute time.
        """
        result = {}
        all = self.results.get("contacted")
        for key, value in all.iteritems():
            result[key] = {
                    "start": value.get("start"),
                    "end"  : value.get("end"),
                    "delta": value.get("delta"),}
        return result
            

    @property
    def stdout(self):
        """
        get the comamnd standard output.
        """
        result = {}
        all = self.results.get("contacted")
        for key, value in all.iteritems():
            result[key] =  value.get("stdout")
        return result
    

    @property
    def stderr(self):
        """
        get the command standard error.
        """
        result = {}
        all = self.results.get("contacted")
        for key, value in all.iteritems():
            result[key] = {
                    "stderr": value.get("stderr"),
                    "warnings": value.get("warnings"),}
        return result

    @property
    def dark(self):
        """
        get the dark results.
        """
        return self.results.get("dark")


class Tasks(Command):
    """
    this is a tasks object for include the common command.
    """
    def __init__(self, *args, **kwargs):
        super(Tasks, self).__init__(*args, **kwargs)


    def __run(self, module_args, module_name="command", timeout=5, forks=10):
        """
        run command from andible ad-hoc.
        command  : 必须是一个需要执行的命令字符串， 比如 
                 'uname -a'
        """
        hoc = Runner(module_name=module_name,
                     module_args=module_args,
                     timeout=timeout,
                     inventory=self.inventory,
                     subset='my_group',
                     forks=forks
                     )
        
        self.results = hoc.run()


    def push_key(self, user, key_path):
        """
        push the ssh authorized key to target.
        """
        module_args = 'user="%s" key="{{ lookup("file", "%s") }}"' % (user, key_path)
        self.__run(module_args, "authorized_key")

        msg = {}
        for result in ["contacted", "dark"]:
            all = self.results.get(result)
            for key, value in all.iteritems():
                if value.get("msg"):
                    msg[key] = value.get("msg")

        if not msg:
            return {"status": "ok"}
        else:
            return {"status": "failed","msg": msg}


    def add_user(self, user):
        """
        add a host user.
        """
        pass


    def del_user(self, user):
        """
        delete a host user.
        """
        pass

        
        


class MyPlaybook(MyInventory):
    """
    this is my playbook object for execute playbook.
    """
    def __init__(self, *args, **kwargs):
        super(MyPlaybook, self).__init__(*args, **kwargs)


    def deploy(self):
        """
        use ansible playbook to deploy a application.
        """
        pass


class App(MyPlaybook):
    """
    this is a app object for inclue the common playbook.
    """
    def __init__(self, *args, **kwargs):
        super(App, self).__init__(*args, **kwargs)

        

if __name__ == "__main__":
   resource =  [ {"hostname": "192.168.10.128", "port": "22", "username": "root", "password": "xxx"}]
   task = Tasks(resource)
   print task.push_key('root', '/root/.ssh/id_rsa.pub')









