forked from br0ziliy/err-ansible
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathansible.py
174 lines (153 loc) · 7.04 KB
/
ansible.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# -*- coding: utf8 -*-
from errbot import BotPlugin, arg_botcmd
from os import path
from lib import utils, tasks
from itertools import chain
import argparse
CONFIG_TEMPLATE = {'INVENTORY_DIR': u"/etc/ansible/inventory",
'PLAYBOOK_DIR': u"/etc/ansible/playbooks",
'ANSIBLE_SSH_KEY': u"/root/.ssh/id_rsa.pub",
'ANSIBLE_REMOTE_USER': u"root"}
class Ansible(BotPlugin):
"""
Err plugin to run Ansible commands/playbooks
"""
def activate(self):
"""
Plugin "constructor", triggers on plugin activation
"""
# array of task UUIDs for tasks.task_poller() to watch
super(Ansible, self).activate()
self.start_poller(5, self.task_poller)
def configure(self, configuration):
"""
Creates a Python dictionary object which contains all the values from our
CONFIG_TEMPLATE and then updates that dictionary with the configuration
received when calling the "!plugin config Ansible" command.
"""
if configuration is not None and configuration != {}:
config = dict(chain(CONFIG_TEMPLATE.items(),
configuration.items()))
else:
config = CONFIG_TEMPLATE
super(Ansible, self).configure(config)
def get_configuration_template(self):
"""
Defines the configuration structure this plugin supports
"""
return CONFIG_TEMPLATE
def check_configuration(self, configuration):
"""
Triggers when the configuration is checked, shortly before activation
"""
self.log.debug("Checking plugin configuration: {}".format(configuration))
if not configuration['INVENTORY_DIR'].endswith('/'):
configuration['INVENTORY_DIR'] = \
"".join([configuration['INVENTORY_DIR'], '/'])
if not configuration['PLAYBOOK_DIR'].endswith('/'):
configuration['PLAYBOOK_DIR'] = \
"".join([configuration['PLAYBOOK_DIR'], '/'])
super(Ansible, self).check_configuration(configuration)
@arg_botcmd('variables', type=str, nargs=argparse.REMAINDER, default=None,
help="optional playbook variables")
@arg_botcmd('inventory', type=str,
help="filename of the inventory file")
@arg_botcmd('playbook', type=str,
help="filename of the playbook file")
def ansible(self, mess, inventory=None, playbook=None, variables=None):
"""
Runs specified Ansible playbook on the specific inventory
"""
_from = mess.frm
inventory_file = "".join([self.config['INVENTORY_DIR'], inventory])
playbook_file = "".join([self.config['PLAYBOOK_DIR'], playbook])
ssh_key = self.config['ANSIBLE_SSH_KEY']
remote_user = self.config['ANSIBLE_REMOTE_USER']
# path come from "os" module
if not path.isfile(inventory_file) or not path.isfile(playbook_file):
return "*ERROR*: inventory/playbook file not found (was looking for \
{} {})".format(inventory_file, playbook_file)
ansible_cmd = ['ansible-playbook', '-u', remote_user, '--private-key', ssh_key,
'-v', '-D', '-i', inventory_file, playbook_file]
if variables:
ansible_cmd.extend(['-e', " ".join(variables)])
raw_result = tasks.run_task(self, ansible_cmd, _from)
return raw_result
@arg_botcmd('objects', type=str, default='all', nargs='?',
help="objects to list; choises are: playbooks, inventories, all (default)",
template='list_objects')
def ansible_list(self, mess=None, objects=None):
"""
Lists available playbooks/inventory files
"""
playbooks = []
inventories = []
if objects is 'playbooks' or objects is 'all':
playbooks = utils.myreaddir(self.config['PLAYBOOK_DIR'])
if objects is 'inventories' or objects is 'all':
inventories = utils.myreaddir(self.config['INVENTORY_DIR'])
return {'playbooks': playbooks, 'inventories': inventories}
@arg_botcmd('command', type=str, nargs=argparse.REMAINDER,
help="command to run on the host(s), or one of: ping, facts")
@arg_botcmd('inventory', type=str,
help="filename of the inventory file")
@arg_botcmd('host', type=str,
help="host pattern or group name from the inventory to run the command on")
def ansible_cmd(self, mess, inventory=None, host=None, command=None):
"""
Runs commands on remote servers using Ansible `command` module,
with "ping" and "facts" having special meaning.
"""
self.log.debug("Got command: {} for Host: {} "
"in Inventory: {}".format(command, host, inventory))
_from = mess.frm
command = " ".join(command)
ssh_key = self.config['ANSIBLE_SSH_KEY']
remote_user = self.config['ANSIBLE_REMOTE_USER']
inventory_file = "".join([self.config['INVENTORY_DIR'], inventory])
# path come from "os" module
if not path.isfile(inventory_file):
return "*ERROR*: inventory file not found (was looking for \
{})".format(inventory_file)
ansible_cmd = ['ansible', host, '-u', remote_user, '--private-key', ssh_key,
'-v', '-i', inventory_file, '-m']
if command == 'ping':
ansible_cmd.extend(['ping'])
elif command == 'facts':
ansible_cmd.extend(['setup'])
else:
ansible_cmd.extend(['command', '-a', command])
raw_result = tasks.run_task(self, ansible_cmd, _from)
return raw_result
@arg_botcmd('uuid', type=str, nargs='?',
help="Task UUID")
def task_info(self, mess, uuid=None):
"""
Obtains various types of information about queued tasks
"""
if not uuid:
return "Listing all jobs not implemented yet, please specify UUID of a job"
(result, status) = tasks.get_task_info(uuid)
if result:
return "Task {} status: {}\n\n{}".format(uuid, status, result)
else: return "Task {} status: {}".format(uuid, status)
def task_poller(self):
"""
Polls for in-progress tasks to notify users about task completion
"""
self.log.debug("Polling for completed tasks...")
if 'tasks' not in self:
self['tasks'] = {}
self.log.debug("Task list: {}".format(self['tasks']))
tasklist = self['tasks']
for uuid in tasklist.keys():
author = tasklist[uuid]
(result, status) = tasks.get_task_info(uuid)
self.log.debug("Processing task: {}; status: {},"
"result:\n{}".format(uuid, status, result))
if status in ['finished', 'failed']:
self.send(self.build_identifier(author),
"Task {} status: {}\n\n{}".format(
uuid, status, result))
del tasklist[uuid]
self['tasks'] = tasklist