blob: 56019ce7a6151ada5ea7c4aaf89da1d26ea16876 [file] [log] [blame]
Scott Baker7581c252016-05-27 13:12:47 -07001import os
2import base64
Scott Bakeraab8a292016-06-03 16:32:45 -07003import jinja2
Scott Baker7581c252016-05-27 13:12:47 -07004import string
5import sys
Scott Bakerb0eb23e2016-06-01 16:08:04 -07006import urllib2
7import urlparse
Scott Baker7581c252016-05-27 13:12:47 -07008import xmlrpclib
9
Scott Baker7581c252016-05-27 13:12:47 -070010from xos.config import Config
Scott Bakeraab8a292016-06-03 16:32:45 -070011from core.models import Service, ServiceController, ServiceControllerResource, XOS
Scott Baker7581c252016-05-27 13:12:47 -070012from xos.logger import Logger, logging
13
14logger = Logger(level=logging.INFO)
15
16class XOSBuilder(object):
Scott Baker7806f622016-06-07 17:45:04 -070017 UI_KINDS=["models", "admin", "django_library", "rest_service", "rest_tenant", "tosca_custom_types", "tosca_resource","public_key"]
Scott Baker18c89172016-06-02 16:40:25 -070018 SYNC_CONTROLLER_KINDS=["synchronizer", "private_key", "public_key"]
Scott Baker513ea452016-06-02 16:03:26 -070019 SYNC_ALLCONTROLLER_KINDS=["models", "django_library"]
Scott Bakerb0eb23e2016-06-01 16:08:04 -070020
Scott Baker7581c252016-05-27 13:12:47 -070021 def __init__(self):
Scott Bakerb0eb23e2016-06-01 16:08:04 -070022 self.source_ui_image = "xosproject/xos"
23 self.source_sync_image = "xosproject/xos-synchronizer-openstack"
24 self.build_dir = "/opt/xos/BUILD/"
25
26 # stuff that has to do with downloading
27
28 def get_dest_dir(self, scr):
29 xos_base = "opt/xos"
30 service_name = scr.service_controller.name
31 base_dirs = {"models": "%s/services/%s/" % (xos_base, service_name),
32 "admin": "%s/services/%s/" % (xos_base, service_name),
Scott Bakere360ff82016-06-13 10:55:23 -070033 "admin_template": "%s/services/%s/templates/" % (xos_base, service_name),
Scott Bakerb0eb23e2016-06-01 16:08:04 -070034 "django_library": "%s/services/%s/" % (xos_base, service_name),
35 "synchronizer": "%s/synchronizers/%s/" % (xos_base, service_name),
36 "tosca_custom_types": "%s/tosca/custom_types/" % (xos_base),
Scott Baker18c89172016-06-02 16:40:25 -070037 "tosca_resource": "%s/tosca/resources/" % (xos_base),
Scott Baker16cfb9c2016-06-07 15:37:03 -070038 "rest_service": "%s/api/service/" % (xos_base),
Scott Baker7806f622016-06-07 17:45:04 -070039 "rest_tenant": "%s/api/tenant/" % (xos_base),
Scott Baker18c89172016-06-02 16:40:25 -070040 "private_key": "%s/services/%s/keys" % (xos_base, service_name),
41 "public_key": "%s/services/%s/keys/" % (xos_base, service_name)}
Scott Baker260a21c2016-06-13 10:42:49 -070042 dest_dir = base_dirs[scr.kind]
43
44 if scr.subdirectory:
45 dest_dir = os.path.join(dest_dir, scr.subdirectory)
46
47 return dest_dir
Scott Bakerb0eb23e2016-06-01 16:08:04 -070048
49 def get_build_fn(self, scr):
50 dest_dir = self.get_dest_dir(scr)
51 dest_fn = os.path.split(urlparse.urlsplit(scr.full_url).path)[-1]
52 return os.path.join(dest_dir, dest_fn)
53
54 def get_download_fn(self, scr):
55 dest_fn = self.get_build_fn(scr)
56 return os.path.join(self.build_dir, dest_fn)
57
58 def read_manifest(self, scr, fn):
59 manifest = []
60 manifest_lines = file(fn).readlines()
61 manifest_lines = [x.strip() for x in manifest_lines]
Scott Bakerc2a2fe92016-06-02 13:19:10 -070062 manifest_lines = [x for x in manifest_lines if x]
Scott Bakerb0eb23e2016-06-01 16:08:04 -070063 for line in manifest_lines:
Scott Bakerc2a2fe92016-06-02 13:19:10 -070064 url_parts = urlparse.urlsplit(scr.full_url)
65 new_path = os.path.join(os.path.join(*os.path.split(url_parts.path)[:-1]),line)
66 url = urlparse.urlunsplit( (url_parts.scheme, url_parts.netloc, new_path, url_parts.query, url_parts.fragment) )
Scott Bakerb0eb23e2016-06-01 16:08:04 -070067
68 build_fn = os.path.join(self.get_dest_dir(scr), line)
Scott Bakerc2a2fe92016-06-02 13:19:10 -070069 download_fn = os.path.join(self.build_dir, build_fn)
Scott Bakerb0eb23e2016-06-01 16:08:04 -070070
71 manifest.append( (url, download_fn, build_fn) )
72 return manifest
73
74 def download_file(self, url, dest_fn):
75 logger.info("Download %s to %s" % (url, dest_fn))
76 if not os.path.exists(os.path.dirname(dest_fn)):
77 os.makedirs(os.path.dirname(dest_fn))
78 obj = urllib2.urlopen(url)
79 file(dest_fn,"w").write(obj.read())
80
Scott Bakerc2a2fe92016-06-02 13:19:10 -070081 # make python files executable
82 if dest_fn.endswith(".py"): # and contents.startswith("#!"):
83 os.chmod(dest_fn, 0755)
84
Scott Bakerb0eb23e2016-06-01 16:08:04 -070085 def download_resource(self, scr):
86 if scr.format == "manifest":
87 manifest_fn = self.get_download_fn(scr)
88 self.download_file(scr.full_url, manifest_fn)
Scott Bakerc2a2fe92016-06-02 13:19:10 -070089 manifest = self.read_manifest(scr, manifest_fn)
Scott Bakerb0eb23e2016-06-01 16:08:04 -070090 for (url, download_fn, build_fn) in manifest:
91 self.download_file(url, download_fn)
92 else:
93 self.download_file(scr.full_url, self.get_download_fn(scr))
94
95 def get_docker_lines(self, scr):
96 if scr.format == "manifest":
97 manifest_fn = self.get_download_fn(scr)
98 manifest = self.read_manifest(scr, manifest_fn)
99 lines = []
100 for (url, download_fn, build_fn) in manifest:
101 lines.append("ADD %s /%s" % (build_fn, build_fn))
102 return lines
103 else:
104 build_fn = self.get_build_fn(scr)
105 return ["ADD %s /%s" % (build_fn, build_fn)]
106
107 def get_controller_docker_lines(self, controller, kinds):
Scott Baker7806f622016-06-07 17:45:04 -0700108 need_service_init_py = False
Scott Bakerb0eb23e2016-06-01 16:08:04 -0700109 dockerfile=[]
110 for scr in controller.service_controller_resources.all():
111 if scr.kind in kinds:
112 lines = self.get_docker_lines(scr)
113 dockerfile = dockerfile + lines
Scott Baker7806f622016-06-07 17:45:04 -0700114 if scr.kind in ["admin", "models"]:
115 need_service_init_py = True
116
117 if need_service_init_py:
118 file(os.path.join(self.build_dir, "opt/xos/empty__init__.py"),"w").write("")
119 dockerfile.append("ADD opt/xos/empty__init__.py /opt/xos/services/%s/__init__.py" % controller.name)
120
Scott Bakerb0eb23e2016-06-01 16:08:04 -0700121 return dockerfile
122
Scott Baker7806f622016-06-07 17:45:04 -0700123 def check_controller_unready(self, controller):
124 unready_resources=[]
125 for scr in controller.service_controller_resources.all():
126 if (not scr.backend_status) or (not scr.backend_status.startswith("1")):
127 unready_resources.append(scr)
128
129 return unready_resources
130
Scott Bakerb0eb23e2016-06-01 16:08:04 -0700131 # stuff that has to do with building
132
Scott Baker513ea452016-06-02 16:03:26 -0700133 def create_xos_app_data(self, name, dockerfile, app_list, migration_list):
Scott Bakerae196c32016-06-01 23:29:22 -0700134 if not os.path.exists(os.path.join(self.build_dir,"opt/xos/xos")):
135 os.makedirs(os.path.join(self.build_dir,"opt/xos/xos"))
136
137 if app_list:
Scott Baker513ea452016-06-02 16:03:26 -0700138 dockerfile.append("COPY opt/xos/xos/%s_xosbuilder_app_list /opt/xos/xos/xosbuilder_app_list" % name)
139 file(os.path.join(self.build_dir, "opt/xos/xos/%s_xosbuilder_app_list") % name, "w").write("\n".join(app_list)+"\n")
Scott Bakerae196c32016-06-01 23:29:22 -0700140
141 if migration_list:
Scott Baker513ea452016-06-02 16:03:26 -0700142 dockerfile.append("COPY opt/xos/xos/%s_xosbuilder_migration_list /opt/xos/xos/xosbuilder_migration_list" % name)
143 file(os.path.join(self.build_dir, "opt/xos/xos/%s_xosbuilder_migration_list") % name, "w").write("\n".join(migration_list)+"\n")
Scott Bakerae196c32016-06-01 23:29:22 -0700144
Scott Bakerb0eb23e2016-06-01 16:08:04 -0700145 def create_ui_dockerfile(self):
146 dockerfile_fn = "Dockerfile.UI"
147
Scott Bakerae196c32016-06-01 23:29:22 -0700148 app_list = []
149 migration_list = []
150
Scott Bakerb0eb23e2016-06-01 16:08:04 -0700151 dockerfile = ["FROM %s" % self.source_ui_image]
152 for controller in ServiceController.objects.all():
Scott Baker7806f622016-06-07 17:45:04 -0700153 if self.check_controller_unready(controller):
154 logger.warning("Controller %s has unready resources" % str(controller))
155 continue
156
Scott Bakerb0eb23e2016-06-01 16:08:04 -0700157 dockerfile = dockerfile + self.get_controller_docker_lines(controller, self.UI_KINDS)
Scott Bakerae196c32016-06-01 23:29:22 -0700158 if controller.service_controller_resources.filter(kind="models").exists():
159 app_list.append("services." + controller.name)
160 migration_list.append(controller.name)
161
Scott Baker513ea452016-06-02 16:03:26 -0700162 self.create_xos_app_data("ui", dockerfile, app_list, migration_list)
Scott Bakerb0eb23e2016-06-01 16:08:04 -0700163
164 file(os.path.join(self.build_dir, dockerfile_fn), "w").write("\n".join(dockerfile)+"\n")
165
Scott Baker8d252b62016-06-01 23:08:04 -0700166 return {"dockerfile_fn": dockerfile_fn,
167 "docker_image_name": "xosproject/xos-ui"}
Scott Bakerb0eb23e2016-06-01 16:08:04 -0700168
169 def create_synchronizer_dockerfile(self, controller):
Scott Baker513ea452016-06-02 16:03:26 -0700170 # bake in the synchronizer from this controller
171 sync_lines = self.get_controller_docker_lines(controller, self.SYNC_CONTROLLER_KINDS)
172 if not sync_lines:
Scott Bakerb0eb23e2016-06-01 16:08:04 -0700173 return []
174
175 dockerfile_fn = "Dockerfile.%s" % controller.name
176 dockerfile = ["FROM %s" % self.source_sync_image]
Scott Baker513ea452016-06-02 16:03:26 -0700177
178 # Now bake in models from this controller as well as the others
179 # It's important to bake all services in, because some services'
180 # synchronizers may depend on models from another service.
181 app_list = []
182 for c in ServiceController.objects.all():
183 dockerfile = dockerfile + self.get_controller_docker_lines(c, self.SYNC_ALLCONTROLLER_KINDS)
184 if controller.service_controller_resources.filter(kind="models").exists():
185 app_list.append("services." + controller.name)
186
187 self.create_xos_app_data(controller.name, dockerfile, app_list, None)
188
189 dockerfile = dockerfile + sync_lines
Scott Bakerc2a2fe92016-06-02 13:19:10 -0700190 file(os.path.join(self.build_dir, dockerfile_fn), "w").write("\n".join(dockerfile)+"\n")
Scott Bakerb0eb23e2016-06-01 16:08:04 -0700191
Scott Baker8d252b62016-06-01 23:08:04 -0700192 return {"dockerfile_fn": dockerfile_fn,
193 "docker_image_name": "xosproject/xos-synchronizer-%s" % controller.name}
Scott Bakerb0eb23e2016-06-01 16:08:04 -0700194
Scott Bakeraab8a292016-06-03 16:32:45 -0700195 def create_docker_compose(self):
196 xos = XOS.objects.all()[0]
197
Scott Baker29b677b2016-06-07 10:20:00 -0700198 volume_list = []
199 for volume in xos.volumes.all():
200 volume_list.append({"host_path": volume.host_path,
201 "container_path": volume.container_path,
202 "read_only": volume.read_only})
Scott Bakeraab8a292016-06-03 16:32:45 -0700203
204 containers = {}
205
206 containers["xos_db"] = \
207 {"image": "xosproject/xos-postgres",
208 "expose": [5432]}
209
Scott Baker584cd892016-06-09 16:10:31 -0700210 db_container_name = xos.docker_project_name + "_xos_db_1"
211
Scott Bakeraab8a292016-06-03 16:32:45 -0700212 containers["xos_ui"] = \
213 {"image": "xosproject/xos-ui",
214 "command": "python /opt/xos/manage.py runserver 0.0.0.0:%d --insecure --makemigrations" % xos.ui_port,
215 "ports": {"%d"%xos.ui_port : "%d"%xos.ui_port},
216 "links": ["xos_db"],
Scott Baker584cd892016-06-09 16:10:31 -0700217 #"external_links": [db_container_name],
Scott Bakeraab8a292016-06-03 16:32:45 -0700218 "volumes": volume_list}
219
Scott Baker584cd892016-06-09 16:10:31 -0700220# containers["xos_bootstrap_ui"] = {"image": "xosproject/xos",
221# "command": "python /opt/xos/manage.py runserver 0.0.0.0:%d --insecure --makemigrations" % xos.bootstrap_ui_port,
222# "ports": {"%d"%xos.bootstrap_ui_port : "%d"%xos.bootstrap_ui_port},
223# #"external_links": [db_container_name],
224# "links": ["xos_db"],
225# "volumes": volume_list}
Scott Bakerbe41a122016-06-06 10:40:40 -0700226
Scott Bakeraab8a292016-06-03 16:32:45 -0700227 for c in ServiceController.objects.all():
Scott Baker7806f622016-06-07 17:45:04 -0700228 if self.check_controller_unready(c):
229 logger.warning("Controller %s has unready resources" % str(c))
230 continue
231
Scott Bakeraab8a292016-06-03 16:32:45 -0700232 containers["xos_synchronizer_%s" % c.name] = \
Scott Baker7806f622016-06-07 17:45:04 -0700233 {"image": "xosproject/xos-synchronizer-%s" % c.name,
Scott Bakerff903a92016-06-08 15:23:34 -0700234 "command": 'bash -c "sleep 120; cd /opt/xos/synchronizers/%s; bash ./run.sh"' % c.name,
Scott Baker584cd892016-06-09 16:10:31 -0700235 #"external_links": [db_container_name],
Scott Bakeraab8a292016-06-03 16:32:45 -0700236 "links": ["xos_db"],
237 "volumes": volume_list}
238
239 vars = { "containers": containers }
240
241 template_loader = jinja2.FileSystemLoader( "/opt/xos/synchronizers/onboarding/templates/" )
242 template_env = jinja2.Environment(loader=template_loader)
243 template = template_env.get_template("docker-compose.yml.j2")
244 buffer = template.render(vars)
245
246 if not os.path.exists("/opt/xos/synchronizers/onboarding/docker-compose"):
247 os.makedirs("/opt/xos/synchronizers/onboarding/docker-compose")
248 file("/opt/xos/synchronizers/onboarding/docker-compose/docker-compose.yml", "w").write(buffer)
249
Scott Baker8d252b62016-06-01 23:08:04 -0700250# def build_xos(self):
251# dockerfiles=[]
252# dockerfiles.append(self.create_ui_dockerfile())
253#
254# for controller in ServiceController.objects.all():
255# dockerfiles.append(self.create_synchronizer_dockerfile(controller))
Scott Bakerb0eb23e2016-06-01 16:08:04 -0700256
257
258