blob: 3040a26e638fec337826d94e10d8caae15ec300d [file] [log] [blame]
Sapan Bhatiafe16ae42016-01-14 11:44:43 -05001#!/usr/bin/env python
Sapan Bhatiaabb8ee72017-03-03 07:06:35 +01002
Sapan Bhatiafe16ae42016-01-14 11:44:43 -05003import jinja2
4import tempfile
5import os
6import json
7import pdb
8import string
9import random
10import re
11import traceback
12import subprocess
Sapan Bhatiaabb8ee72017-03-03 07:06:35 +010013import threading
Sapan Bhatiafe16ae42016-01-14 11:44:43 -050014from xos.config import Config, XOS_DIR
Sapan Bhatiabb4b5362017-02-04 09:05:32 -080015from xos.logger import observer_logger as logger
Sapan Bhatiac1783572017-02-23 10:39:42 +010016from multiprocessing import Process, Queue
17
Sapan Bhatiafe16ae42016-01-14 11:44:43 -050018
Scott Bakerc48f00f2016-08-16 16:45:00 -070019step_dir = Config().observer_steps_dir
20sys_dir = Config().observer_sys_dir
Sapan Bhatiafe16ae42016-01-14 11:44:43 -050021
Srikanth Vavilapallia3993152016-11-17 03:19:00 +000022os_template_loader = jinja2.FileSystemLoader( searchpath=[step_dir, "/opt/xos/synchronizers/shared_templates"])
Sapan Bhatiafe16ae42016-01-14 11:44:43 -050023os_template_env = jinja2.Environment(loader=os_template_loader)
24
Sapan Bhatiafe16ae42016-01-14 11:44:43 -050025def id_generator(size=6, chars=string.ascii_uppercase + string.digits):
26 return ''.join(random.choice(chars) for _ in range(size))
27
28def shellquote(s):
29 return "'" + s.replace("'", "'\\''") + "'"
30
31def get_playbook_fn(opts, path):
32 if not opts.get("ansible_tag", None):
33 # if no ansible_tag is in the options, then generate a unique one
34 objname= id_generator()
35 opts = opts.copy()
36 opts["ansible_tag"] = objname
37
38 objname = opts["ansible_tag"]
39
Zack Williamsa1775572016-03-07 20:30:14 -070040 pathed_sys_dir = os.path.join(sys_dir, path)
41 if not os.path.isdir(pathed_sys_dir):
42 os.makedirs(pathed_sys_dir)
43
44 # symlink steps/roles into sys/roles so that playbooks can access roles
45 roledir = os.path.join(step_dir,"roles")
46 rolelink = os.path.join(pathed_sys_dir, "roles")
47 if os.path.isdir(roledir) and not os.path.islink(rolelink):
48 os.symlink(roledir,rolelink)
49
50 return (opts, os.path.join(pathed_sys_dir,objname))
Sapan Bhatiafe16ae42016-01-14 11:44:43 -050051
Sapan Bhatiaabb8ee72017-03-03 07:06:35 +010052def run_playbook(ansible_hosts, ansible_config, fqp, opts):#, q):
Sapan Bhatiacedc6952017-03-01 14:39:00 +010053 try:
54 if ansible_config:
55 os.environ["ANSIBLE_CONFIG"] = ansible_config
56 else:
57 try:
58 del os.environ["ANSIBLE_CONFIG"]
59 except KeyError:
60 pass
Sapan Bhatiac1783572017-02-23 10:39:42 +010061
Sapan Bhatiacedc6952017-03-01 14:39:00 +010062 if ansible_hosts:
63 os.environ["ANSIBLE_HOSTS"] = ansible_hosts
64 else:
65 try:
66 del os.environ["ANSIBLE_HOSTS"]
67 except KeyError:
68 pass
Sapan Bhatiac1783572017-02-23 10:39:42 +010069
Sapan Bhatiacedc6952017-03-01 14:39:00 +010070 import ansible_runner
71 reload(ansible_runner)
Sapan Bhatiac1783572017-02-23 10:39:42 +010072
Sapan Bhatiacedc6952017-03-01 14:39:00 +010073 # Dropped support for observer_pretend - to be redone
74 runner = ansible_runner.Runner(
75 playbook=fqp,
76 run_data=opts,
77 host_file=ansible_hosts)
Sapan Bhatiac1783572017-02-23 10:39:42 +010078
Sapan Bhatiacedc6952017-03-01 14:39:00 +010079 stats,aresults = runner.run()
80 except Exception, e:
81 logger.log_exc("Exception executing playbook",extra={'exception':str(e)})
82 stats = None
83 aresults = None
84
Sapan Bhatiaabb8ee72017-03-03 07:06:35 +010085 #q.put([stats,aresults])
86 return (stats,aresults)
Sapan Bhatiac1783572017-02-23 10:39:42 +010087
Sapan Bhatiabb4b5362017-02-04 09:05:32 -080088def run_template(name, opts, path='', expected_num=None, ansible_config=None, ansible_hosts=None, run_ansible_script=None, object=None):
Sapan Bhatiaabb8ee72017-03-03 07:06:35 +010089 global uglylock
90 try:
91 if (uglylock):
92 pass
93 except NameError:
94 uglylock = threading.Lock()
95
96 uglylock.acquire()
97
Sapan Bhatiafe16ae42016-01-14 11:44:43 -050098 template = os_template_env.get_template(name)
99 buffer = template.render(opts)
100
101 (opts, fqp) = get_playbook_fn(opts, path)
102
103 f = open(fqp,'w')
104 f.write(buffer)
105 f.flush()
Sapan Bhatiac1783572017-02-23 10:39:42 +0100106
Sapan Bhatiaabb8ee72017-03-03 07:06:35 +0100107 """
Sapan Bhatiac1783572017-02-23 10:39:42 +0100108 q = Queue()
109 p = Process(target=run_playbook, args=(ansible_hosts, ansible_config, fqp, opts, q,))
110 p.start()
111 stats,aresults = q.get()
112 p.join()
Sapan Bhatiaabb8ee72017-03-03 07:06:35 +0100113 """
114 stats,aresults = run_playbook(ansible_hosts,ansible_config,fqp,opts)
Sapan Bhatiafe16ae42016-01-14 11:44:43 -0500115
Sapan Bhatiaabb8ee72017-03-03 07:06:35 +0100116 uglylock.release()
117
Sapan Bhatiad0275bd2017-02-27 21:06:34 +0100118 output_file = fqp + '.out'
Sapan Bhatiafe16ae42016-01-14 11:44:43 -0500119 try:
Sapan Bhatiacedc6952017-03-01 14:39:00 +0100120 if (aresults is None):
121 raise ValueError("Error executing playbook %s"%fqp)
122
Sapan Bhatiabb4b5362017-02-04 09:05:32 -0800123 ok_results = []
124 total_unreachable = 0
125 failed = 0
126
127 error_msg = []
Sapan Bhatiad0275bd2017-02-27 21:06:34 +0100128
129 ofile = open(output_file, 'w')
130
Sapan Bhatiabb4b5362017-02-04 09:05:32 -0800131 for x in aresults:
132 if not x.is_failed() and not x.is_unreachable() and not x.is_skipped():
133 ok_results.append(x)
134 elif x.is_unreachable():
Sapan Bhatiacb53df72017-02-06 16:13:06 -0800135 failed+=1
Sapan Bhatiabb4b5362017-02-04 09:05:32 -0800136 total_unreachable+=1
137 try:
138 error_msg.append(x._result['msg'])
139 except:
140 pass
141 elif x.is_failed():
142 failed+=1
143 try:
144 error_msg.append(x._result['msg'])
145 except:
146 pass
147
Zack Williamse48e6e72017-02-18 23:16:54 -0700148 # FIXME (zdw, 2017-02-19) - may not be needed with new callback logging
Sapan Bhatiad0275bd2017-02-27 21:06:34 +0100149
150 ofile.write('%s: %s\n'%(x._task, str(x._result)))
151
Sapan Bhatia81996092017-02-14 22:25:42 -0800152 if (object):
153 oprops = object.tologdict()
154 ansible = x._result
Zack Williamse48e6e72017-02-18 23:16:54 -0700155 oprops['xos_type']='ansible'
156 oprops['ansible_result']=json.dumps(ansible)
Sapan Bhatia81996092017-02-14 22:25:42 -0800157
Zack Williamse48e6e72017-02-18 23:16:54 -0700158 if failed == 0:
159 oprops['ansible_status']='OK'
160 else:
161 oprops['ansible_status']='FAILED'
162
Sapan Bhatiad0275bd2017-02-27 21:06:34 +0100163 logger.info(x._task, extra=oprops)
Sapan Bhatia81996092017-02-14 22:25:42 -0800164
Sapan Bhatiabb4b5362017-02-04 09:05:32 -0800165
Sapan Bhatiad0275bd2017-02-27 21:06:34 +0100166 ofile.close()
167
Sapan Bhatiafe16ae42016-01-14 11:44:43 -0500168 if (expected_num is not None) and (len(ok_results) != expected_num):
169 raise ValueError('Unexpected num %s!=%d' % (str(expected_num), len(ok_results)) )
170
Sapan Bhatiabb4b5362017-02-04 09:05:32 -0800171 #total_unreachable = stats.unreachable
172
Sapan Bhatiafe16ae42016-01-14 11:44:43 -0500173 if (failed):
174 raise ValueError('Ansible playbook failed.')
175
Sapan Bhatiafe16ae42016-01-14 11:44:43 -0500176 except ValueError,e:
Sapan Bhatiafe16ae42016-01-14 11:44:43 -0500177 try:
Sapan Bhatiabb4b5362017-02-04 09:05:32 -0800178 error = ' // '.join(error_msg)
Sapan Bhatiafe16ae42016-01-14 11:44:43 -0500179 except:
180 pass
181 raise Exception(error)
182
Sapan Bhatia81996092017-02-14 22:25:42 -0800183
Sapan Bhatiabb4b5362017-02-04 09:05:32 -0800184
185 processed_results = map(lambda x:x._result, ok_results)
186 return processed_results[1:] # 0 is setup
Sapan Bhatiafe16ae42016-01-14 11:44:43 -0500187
Sapan Bhatiabb4b5362017-02-04 09:05:32 -0800188def run_template_ssh(name, opts, path='', expected_num=None, object=None):
Sapan Bhatiafe16ae42016-01-14 11:44:43 -0500189 instance_name = opts["instance_name"]
190 hostname = opts["hostname"]
191 private_key = opts["private_key"]
192 baremetal_ssh = opts.get("baremetal_ssh",False)
193 if baremetal_ssh:
Scott Bakercb757432016-02-10 15:25:08 -0800194 # no instance_id or ssh_ip for baremetal
Sapan Bhatiafe16ae42016-01-14 11:44:43 -0500195 # we never proxy to baremetal
196 proxy_ssh = False
197 else:
198 instance_id = opts["instance_id"]
Scott Bakercb757432016-02-10 15:25:08 -0800199 ssh_ip = opts["ssh_ip"]
Sapan Bhatiafe16ae42016-01-14 11:44:43 -0500200 try:
201 proxy_ssh = Config().observer_proxy_ssh
202 except:
203 proxy_ssh = True
204
Sapan Bhatiac1783572017-02-23 10:39:42 +0100205 if (not ssh_ip):
206 raise Exception('IP of ssh proxy not available. Synchronization deferred')
Sapan Bhatia90ae2b92017-02-07 17:45:09 -0800207
Sapan Bhatiafe16ae42016-01-14 11:44:43 -0500208 (opts, fqp) = get_playbook_fn(opts, path)
209 private_key_pathname = fqp + ".key"
210 config_pathname = fqp + ".config"
211 hosts_pathname = fqp + ".hosts"
212
213 f = open(private_key_pathname, "w")
214 f.write(private_key)
215 f.close()
216
217 f = open(config_pathname, "w")
218 f.write("[ssh_connection]\n")
219 if proxy_ssh:
Scott Baker81365782016-02-10 17:25:07 -0800220 proxy_ssh_key = getattr(Config(), "observer_proxy_ssh_key", None)
221 proxy_ssh_user = getattr(Config(), "observer_proxy_ssh_user", "root")
222 if proxy_ssh_key:
223 # If proxy_ssh_key is known, then we can proxy into the compute
224 # node without needing to have the OpenCloud sshd machinery in
225 # place.
226 proxy_command = "ProxyCommand ssh -q -i %s -o StrictHostKeyChecking=no %s@%s nc %s 22" % (proxy_ssh_key, proxy_ssh_user, hostname, ssh_ip)
227 else:
228 proxy_command = "ProxyCommand ssh -q -i %s -o StrictHostKeyChecking=no %s@%s" % (private_key_pathname, instance_id, hostname)
Sapan Bhatiafe16ae42016-01-14 11:44:43 -0500229 f.write('ssh_args = -o "%s"\n' % proxy_command)
230 f.write('scp_if_ssh = True\n')
231 f.write('pipelining = True\n')
232 f.write('\n[defaults]\n')
233 f.write('host_key_checking = False\n')
Zack Williams3c5a85f2016-04-19 15:53:54 -0700234 f.write('timeout = 30\n')
Sapan Bhatiafe16ae42016-01-14 11:44:43 -0500235 f.close()
236
237 f = open(hosts_pathname, "w")
238 f.write("[%s]\n" % instance_name)
239 if proxy_ssh or baremetal_ssh:
240 f.write("%s ansible_ssh_private_key_file=%s\n" % (hostname, private_key_pathname))
241 else:
242 # acb: Login user is hardcoded, this is not great
Scott Bakercb757432016-02-10 15:25:08 -0800243 f.write("%s ansible_ssh_private_key_file=%s ansible_ssh_user=ubuntu\n" % (ssh_ip, private_key_pathname))
Sapan Bhatiafe16ae42016-01-14 11:44:43 -0500244 f.close()
245
246 # SSH will complain if private key is world or group readable
247 os.chmod(private_key_pathname, 0600)
248
249 print "ANSIBLE_CONFIG=%s" % config_pathname
250 print "ANSIBLE_HOSTS=%s" % hosts_pathname
251
Sapan Bhatiabb4b5362017-02-04 09:05:32 -0800252 return run_template(name, opts, path, ansible_config = config_pathname, ansible_hosts = hosts_pathname, run_ansible_script="/opt/xos/synchronizers/base/run_ansible_verbose", object=object)
Sapan Bhatiafe16ae42016-01-14 11:44:43 -0500253
254
255
256def main():
257 run_template('ansible/sync_user_deployments.yaml',{ "endpoint" : "http://172.31.38.128:5000/v2.0/",
258 "name" : "Sapan Bhatia",
259 "email": "gwsapan@gmail.com",
260 "password": "foobar",
261 "admin_user":"admin",
262 "admin_password":"6a789bf69dd647e2",
263 "admin_tenant":"admin",
264 "tenant":"demo",
265 "roles":['user','admin'] })