blob: 6cf990d71bd1b89005d9e15e2d48b41faa15903c [file] [log] [blame]
Sapan Bhatiafe16ae42016-01-14 11:44:43 -05001#!/usr/bin/env python
2import jinja2
3import tempfile
4import os
5import json
6import pdb
7import string
8import random
9import re
10import traceback
11import subprocess
12from xos.config import Config, XOS_DIR
Sapan Bhatiabb4b5362017-02-04 09:05:32 -080013from xos.logger import observer_logger as logger
Sapan Bhatiafe16ae42016-01-14 11:44:43 -050014
Scott Bakerc48f00f2016-08-16 16:45:00 -070015step_dir = Config().observer_steps_dir
16sys_dir = Config().observer_sys_dir
Sapan Bhatiafe16ae42016-01-14 11:44:43 -050017
Srikanth Vavilapallia3993152016-11-17 03:19:00 +000018os_template_loader = jinja2.FileSystemLoader( searchpath=[step_dir, "/opt/xos/synchronizers/shared_templates"])
Sapan Bhatiafe16ae42016-01-14 11:44:43 -050019os_template_env = jinja2.Environment(loader=os_template_loader)
20
Sapan Bhatiafe16ae42016-01-14 11:44:43 -050021def id_generator(size=6, chars=string.ascii_uppercase + string.digits):
22 return ''.join(random.choice(chars) for _ in range(size))
23
24def shellquote(s):
25 return "'" + s.replace("'", "'\\''") + "'"
26
27def get_playbook_fn(opts, path):
28 if not opts.get("ansible_tag", None):
29 # if no ansible_tag is in the options, then generate a unique one
30 objname= id_generator()
31 opts = opts.copy()
32 opts["ansible_tag"] = objname
33
34 objname = opts["ansible_tag"]
35
Zack Williamsa1775572016-03-07 20:30:14 -070036 pathed_sys_dir = os.path.join(sys_dir, path)
37 if not os.path.isdir(pathed_sys_dir):
38 os.makedirs(pathed_sys_dir)
39
40 # symlink steps/roles into sys/roles so that playbooks can access roles
41 roledir = os.path.join(step_dir,"roles")
42 rolelink = os.path.join(pathed_sys_dir, "roles")
43 if os.path.isdir(roledir) and not os.path.islink(rolelink):
44 os.symlink(roledir,rolelink)
45
46 return (opts, os.path.join(pathed_sys_dir,objname))
Sapan Bhatiafe16ae42016-01-14 11:44:43 -050047
Sapan Bhatiabb4b5362017-02-04 09:05:32 -080048def run_template(name, opts, path='', expected_num=None, ansible_config=None, ansible_hosts=None, run_ansible_script=None, object=None):
Sapan Bhatiafe16ae42016-01-14 11:44:43 -050049 template = os_template_env.get_template(name)
50 buffer = template.render(opts)
51
52 (opts, fqp) = get_playbook_fn(opts, path)
53
54 f = open(fqp,'w')
55 f.write(buffer)
56 f.flush()
57
Sapan Bhatiafe16ae42016-01-14 11:44:43 -050058 if ansible_config:
Sapan Bhatiaa89ee512017-02-05 20:42:14 -080059 os.environ["ANSIBLE_CONFIG"] = ansible_config
Sapan Bhatiafe16ae42016-01-14 11:44:43 -050060 if ansible_hosts:
Sapan Bhatiaa89ee512017-02-05 20:42:14 -080061 os.environ["ANSIBLE_HOSTS"] = ansible_hosts
62
63 # This import needs to be here, otherwise ANSIBLE_CONFIG does not take effect
64 from ansible_runner import Runner
65
Sapan Bhatiafe16ae42016-01-14 11:44:43 -050066
Sapan Bhatiabb4b5362017-02-04 09:05:32 -080067 # Dropped support for observer_pretend - to be redone
68 runner = Runner(
69 playbook=fqp,
Sapan Bhatiaa89ee512017-02-05 20:42:14 -080070 run_data=opts,
71 host_file=ansible_hosts)
Sapan Bhatiafe16ae42016-01-14 11:44:43 -050072
Sapan Bhatiabb4b5362017-02-04 09:05:32 -080073 stats,aresults = runner.run()
Sapan Bhatiafe16ae42016-01-14 11:44:43 -050074
75 try:
Sapan Bhatiabb4b5362017-02-04 09:05:32 -080076 ok_results = []
77 total_unreachable = 0
78 failed = 0
79
80 error_msg = []
81 for x in aresults:
82 if not x.is_failed() and not x.is_unreachable() and not x.is_skipped():
83 ok_results.append(x)
84 elif x.is_unreachable():
Sapan Bhatiacb53df72017-02-06 16:13:06 -080085 failed+=1
Sapan Bhatiabb4b5362017-02-04 09:05:32 -080086 total_unreachable+=1
87 try:
88 error_msg.append(x._result['msg'])
89 except:
90 pass
91 elif x.is_failed():
92 failed+=1
93 try:
94 error_msg.append(x._result['msg'])
95 except:
96 pass
97
98
Sapan Bhatiafe16ae42016-01-14 11:44:43 -050099 if (expected_num is not None) and (len(ok_results) != expected_num):
100 raise ValueError('Unexpected num %s!=%d' % (str(expected_num), len(ok_results)) )
101
Sapan Bhatiabb4b5362017-02-04 09:05:32 -0800102 #total_unreachable = stats.unreachable
103
Sapan Bhatiafe16ae42016-01-14 11:44:43 -0500104 if (failed):
105 raise ValueError('Ansible playbook failed.')
106
Sapan Bhatiafe16ae42016-01-14 11:44:43 -0500107 except ValueError,e:
Sapan Bhatiafe16ae42016-01-14 11:44:43 -0500108 try:
Sapan Bhatiabb4b5362017-02-04 09:05:32 -0800109 error = ' // '.join(error_msg)
Sapan Bhatiafe16ae42016-01-14 11:44:43 -0500110 except:
111 pass
112 raise Exception(error)
113
Sapan Bhatiabb4b5362017-02-04 09:05:32 -0800114 if (object):
115 oprops = object.tologdict()
116 for i in ok_results:
117 ansible = i._result
118 ansible['ansible'] = 1
119 c = dict(oprops.items() + ansible.items())
120 logger.info(i._task, extra=c)
121
122 processed_results = map(lambda x:x._result, ok_results)
123 return processed_results[1:] # 0 is setup
Sapan Bhatiafe16ae42016-01-14 11:44:43 -0500124
Sapan Bhatiabb4b5362017-02-04 09:05:32 -0800125def run_template_ssh(name, opts, path='', expected_num=None, object=None):
Sapan Bhatiafe16ae42016-01-14 11:44:43 -0500126 instance_name = opts["instance_name"]
127 hostname = opts["hostname"]
128 private_key = opts["private_key"]
129 baremetal_ssh = opts.get("baremetal_ssh",False)
130 if baremetal_ssh:
Scott Bakercb757432016-02-10 15:25:08 -0800131 # no instance_id or ssh_ip for baremetal
Sapan Bhatiafe16ae42016-01-14 11:44:43 -0500132 # we never proxy to baremetal
133 proxy_ssh = False
134 else:
135 instance_id = opts["instance_id"]
Scott Bakercb757432016-02-10 15:25:08 -0800136 ssh_ip = opts["ssh_ip"]
Sapan Bhatiafe16ae42016-01-14 11:44:43 -0500137 try:
138 proxy_ssh = Config().observer_proxy_ssh
139 except:
140 proxy_ssh = True
141
Sapan Bhatia90ae2b92017-02-07 17:45:09 -0800142 if (not ssh_ip):
143 raise Exception('IP of ssh proxy not available. Synchronization deferred')
144
Sapan Bhatiafe16ae42016-01-14 11:44:43 -0500145 (opts, fqp) = get_playbook_fn(opts, path)
146 private_key_pathname = fqp + ".key"
147 config_pathname = fqp + ".config"
148 hosts_pathname = fqp + ".hosts"
149
150 f = open(private_key_pathname, "w")
151 f.write(private_key)
152 f.close()
153
154 f = open(config_pathname, "w")
155 f.write("[ssh_connection]\n")
156 if proxy_ssh:
Scott Baker81365782016-02-10 17:25:07 -0800157 proxy_ssh_key = getattr(Config(), "observer_proxy_ssh_key", None)
158 proxy_ssh_user = getattr(Config(), "observer_proxy_ssh_user", "root")
159 if proxy_ssh_key:
160 # If proxy_ssh_key is known, then we can proxy into the compute
161 # node without needing to have the OpenCloud sshd machinery in
162 # place.
163 proxy_command = "ProxyCommand ssh -q -i %s -o StrictHostKeyChecking=no %s@%s nc %s 22" % (proxy_ssh_key, proxy_ssh_user, hostname, ssh_ip)
164 else:
165 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 -0500166 f.write('ssh_args = -o "%s"\n' % proxy_command)
167 f.write('scp_if_ssh = True\n')
168 f.write('pipelining = True\n')
169 f.write('\n[defaults]\n')
170 f.write('host_key_checking = False\n')
Zack Williams3c5a85f2016-04-19 15:53:54 -0700171 f.write('timeout = 30\n')
Sapan Bhatiafe16ae42016-01-14 11:44:43 -0500172 f.close()
173
174 f = open(hosts_pathname, "w")
175 f.write("[%s]\n" % instance_name)
176 if proxy_ssh or baremetal_ssh:
177 f.write("%s ansible_ssh_private_key_file=%s\n" % (hostname, private_key_pathname))
178 else:
179 # acb: Login user is hardcoded, this is not great
Scott Bakercb757432016-02-10 15:25:08 -0800180 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 -0500181 f.close()
182
183 # SSH will complain if private key is world or group readable
184 os.chmod(private_key_pathname, 0600)
185
186 print "ANSIBLE_CONFIG=%s" % config_pathname
187 print "ANSIBLE_HOSTS=%s" % hosts_pathname
188
Sapan Bhatiabb4b5362017-02-04 09:05:32 -0800189 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 -0500190
191
192
193def main():
194 run_template('ansible/sync_user_deployments.yaml',{ "endpoint" : "http://172.31.38.128:5000/v2.0/",
195 "name" : "Sapan Bhatia",
196 "email": "gwsapan@gmail.com",
197 "password": "foobar",
198 "admin_user":"admin",
199 "admin_password":"6a789bf69dd647e2",
200 "admin_tenant":"admin",
201 "tenant":"demo",
202 "roles":['user','admin'] })