blob: bf847d61779fcdd9088d977b2cb97c4a228d4fe0 [file] [log] [blame]
Adam Tengler12b4bfc2017-06-02 19:01:18 +00001from __future__ import absolute_import
2# Let's not allow PyLint complain about string substitution
3# pylint: disable=W1321,E1321
4
5# Import python libs
6import logging
7
8# Import Salt libs
9import salt.returners
10
11# Import third party libs
12try:
13 import psycopg2
14 import psycopg2.extras
15 HAS_POSTGRES = True
16except ImportError:
17 HAS_POSTGRES = False
18
Adam Tengler8455c252017-09-12 14:51:47 +000019__virtualname__ = 'saltresource'
Adam Tengler12b4bfc2017-06-02 19:01:18 +000020LOG = logging.getLogger(__name__)
21
22
23def __virtual__():
24 if not HAS_POSTGRES:
25 return False, 'Could not import saltresource module; psycopg2 is not installed.'
Adam Tengler8455c252017-09-12 14:51:47 +000026 return __virtualname__
Adam Tengler12b4bfc2017-06-02 19:01:18 +000027
28
29def _get_options(ret=None):
30 '''
31 Get the postgres options from salt.
32 '''
Adam Tengler8455c252017-09-12 14:51:47 +000033 defaults = {'host': '127.0.0.1',
34 'user': 'salt',
35 'passwd': 'salt',
36 'db': 'salt',
37 'port': '5432'}
Adam Tengler12b4bfc2017-06-02 19:01:18 +000038
Adam Tengler8455c252017-09-12 14:51:47 +000039 _options = {}
40 for key, default in defaults.items():
41 _options[key] = __salt__['config.get']('%s.%s' % (__virtualname__, key), default)
42
Adam Tengler12b4bfc2017-06-02 19:01:18 +000043 return _options
44
45
46def _get_conn(ret=None):
47 '''
48 Return a postgres connection.
49 '''
50 _options = _get_options(ret)
51
52 host = _options.get('host')
53 user = _options.get('user')
54 passwd = _options.get('passwd')
55 datab = _options.get('db')
56 port = _options.get('port')
57
58 return psycopg2.connect(
59 host=host,
60 user=user,
61 password=passwd,
62 database=datab,
63 port=port)
64
65
66def _close_conn(conn):
67 '''
68 Close the Postgres connection
69 '''
70 conn.commit()
71 conn.close()
72
73
74def graph_data(*args, **kwargs):
75 '''
76 Returns graph data for visualization app
77
78 CLI Examples:
79
80 .. code-block:: bash
81
82 salt '*' saltresource.graph_data
83
84 '''
85 conn = _get_conn()
86 cur_dict = conn.cursor(cursor_factory=psycopg2.extras.DictCursor)
87
88 cur_dict.execute('SELECT host, service, status FROM salt_resources')
89 resources_db = [dict(res) for res in cur_dict]
90 db_dict = {}
91
92 for resource in resources_db:
93 host = resource.get('host')
94 service = '.'.join(resource.get('service').split('.')[:2])
95 status = resource.get('status')
96
97 if db_dict.get(host, None):
98 if db_dict[host].get(service, None):
99 service_data = db_dict[host][service]
100 service_data.append(status)
101 else:
102 db_dict[host][service] = [status]
103 else:
104 db_dict[host] = {service: []}
105
106 graph = []
107 for host, services in db_dict.items():
108 for service, statuses in services.items():
109 status = 'unknown'
110 if 'failed' in statuses:
111 status = 'failed'
112 elif 'success' in statuses and not ('failed' in statuses or 'unknown' in statuses):
113 status = 'success'
114 datum = {'host': host, 'service': service, 'status': status}
115 graph.append(datum)
116
117 _close_conn(conn)
118
119 return {'graph': graph}
120
121
122def host_data(host, **kwargs):
123 '''
124 Returns data describing single host
125
126 CLI Examples:
127
128 .. code-block:: bash
129
130 salt-call saltresource.host_data '<minion_id>'
131
132 '''
133 conn = _get_conn()
134 cur_dict = conn.cursor(cursor_factory=psycopg2.extras.DictCursor)
135
136 sql = 'SELECT host, service, resource_id, last_ret, status FROM salt_resources WHERE host=%s'
137 cur_dict.execute(sql, (host,))
138 resources_db = [dict(res) for res in cur_dict]
139 db_dict = {}
140
141 for resource in resources_db:
142 host = resource.get('host')
143 service = '.'.join(resource.get('service').split('.')[:2])
144 status = resource.get('status')
145
146 if db_dict.get(host, None):
147 if db_dict[host].get(service, None):
148 service_data = db_dict[host][service]
149 service_data.append(status)
150 else:
151 db_dict[host][service] = [status]
152 else:
153 db_dict[host] = {service: []}
154
155 graph = []
156
157 for host, services in db_dict.items():
158 for service, statuses in services.items():
159 status = 'unknown'
160 if 'failed' in statuses:
161 status = 'failed'
162 elif 'success' in statuses and not ('failed' in statuses or 'unknown' in statuses):
163 status = 'success'
164 resources = [{'service': r.get('service', ''), 'resource_id': r.get('resource_id', ''), 'last_ret': r.get('last_ret', None), 'status': r.get('status', '')}
165 for r
166 in resources_db
167 if r.get('service', '').startswith(service)]
168 datum = {'host': host, 'service': service, 'status': status, 'resources': resources}
169 graph.append(datum)
170
171 _close_conn(conn)
172
173 return {'graph': graph}
174
175
176def sync_db(*args, **kwargs):
177 conn = _get_conn()
178 cur = conn.cursor()
179
180 resources_sql = '''
181 CREATE TABLE IF NOT EXISTS salt_resources (
182 id varchar(255) NOT NULL UNIQUE,
183 resource_id varchar(255) NOT NULL,
184 host varchar(255) NOT NULL,
185 service varchar(255) NOT NULL,
186 module varchar(50) NOT NULL,
187 fun varchar(50) NOT NULL,
188 status varchar(50) NOT NULL,
189 options json NULL,
190 last_ret text NULL,
191 alter_time TIMESTAMP WITH TIME ZONE DEFAULT now()
192 );
193 '''
194 cur.execute(resources_sql)
195 conn.commit()
196
197 resources_meta_sql = '''
198 CREATE TABLE IF NOT EXISTS salt_resources_meta (
199 id varchar(255) NOT NULL UNIQUE,
200 options json NULL,
201 alter_time TIMESTAMP WITH TIME ZONE DEFAULT now()
202 );
203 '''
204 cur.execute(resources_meta_sql)
205 _close_conn(conn)
206
207 return True
208
209
210def flush_db(*args, **kwargs):
211 conn = _get_conn()
212 cur = conn.cursor()
213 result = True
214
215 resources_sql = 'DELETE FROM salt_resources'
216 try:
217 cur.execute(resources_sql)
218 conn.commit()
219 except Exception as e:
220 LOG.warning(repr(e))
221 result = False
222
223 resources_meta_sql = 'DELETE FROM salt_resources_meta'
224 try:
225 cur.execute(resources_meta_sql)
226 _close_conn(conn)
227 except Exception as e:
228 LOG.warning(repr(e))
229 result = False
230
231 return result
232
233
234def destroy_db(*args, **kwargs):
235 conn = _get_conn()
236 cur = conn.cursor()
237
238 resources_sql = 'DROP TABLE IF EXISTS salt_resources;'
239 cur.execute(resources_sql)
240 conn.commit()
241
242 resources_meta_sql = 'DROP TABLE IF EXISTS salt_resources_meta;'
243 cur.execute(resources_meta_sql)
244 _close_conn(conn)
245
246 return True
247