blob: f65a2317838f09a9814984e63cfbe3d27a3c31c5 [file] [log] [blame]
Scott Baker35a99532015-08-31 16:18:08 -07001import os
Scott Baker714d5242015-08-05 08:20:12 -07002import pdb
Scott Baker35a99532015-08-31 16:18:08 -07003import json
Scott Bakerf65591f2015-09-28 16:01:21 -07004import subprocess
Scott Bakerccf785f2016-05-03 15:55:16 -07005import sys
Scott Baker714d5242015-08-05 08:20:12 -07006
Scott Baker07c71fc2015-09-24 15:37:16 -07007from core.models import User
8
Scott Baker3841b372015-08-03 14:20:31 -07009class XOSResource(object):
10 xos_base_class = "XOSResource"
Scott Baker9fdb39f2015-08-04 16:44:18 -070011 xos_model = None
Scott Baker2142bd72015-08-10 17:08:02 -070012 name_field = "name"
13 copyin_props = []
Scott Baker3841b372015-08-03 14:20:31 -070014 provides = None
15
Scott Baker5d432142015-08-07 17:06:47 -070016 def __init__(self, user, nodetemplate, engine):
Scott Baker9fce62e2015-08-03 15:43:54 -070017 self.dirty = False
Scott Baker404c46d2015-08-24 15:50:03 -070018 self.deferred_sync = []
Scott Baker3841b372015-08-03 14:20:31 -070019 self.user = user
20 self.nodetemplate = nodetemplate
Scott Baker5d432142015-08-07 17:06:47 -070021 self.engine = engine
Scott Baker3841b372015-08-03 14:20:31 -070022
Scott Baker642dbb52016-04-25 14:55:22 -070023 @property
24 def full_name(self):
25 return self.nodetemplate.name
26
27 @property
28 def obj_name(self):
29 if "#" in self.nodetemplate.name:
Scott Baker38265222016-04-25 15:19:03 -070030 return self.nodetemplate.name.split("#",1)[1]
Scott Baker642dbb52016-04-25 14:55:22 -070031 else:
32 return self.nodetemplate.name
33
Scott Baker32795252015-08-04 23:53:07 -070034 def get_all_required_node_names(self):
35 results = []
36 for reqs in self.nodetemplate.requirements:
37 for (k,v) in reqs.items():
38 results.append(v["node"])
39 return results
40
Scott Baker14152942015-08-04 10:59:29 -070041 def get_requirements(self, relationship_name, throw_exception=False):
Scott Baker9fce62e2015-08-03 15:43:54 -070042 """ helper to search the list of requirements for a particular relationship
43 type.
44 """
Scott Baker14152942015-08-04 10:59:29 -070045
46 results = []
Scott Baker9fce62e2015-08-03 15:43:54 -070047 for reqs in self.nodetemplate.requirements:
48 for (k,v) in reqs.items():
49 if (v["relationship"] == relationship_name):
Scott Baker14152942015-08-04 10:59:29 -070050 results.append(v["node"])
Scott Baker9fce62e2015-08-03 15:43:54 -070051
Scott Baker14152942015-08-04 10:59:29 -070052 if (not results) and throw_exception:
Scott Baker642dbb52016-04-25 14:55:22 -070053 raise Exception("Failed to find requirement in %s using relationship %s" % (self.full_name, relationship_name))
Scott Baker9fce62e2015-08-03 15:43:54 -070054
Scott Baker14152942015-08-04 10:59:29 -070055 return results
56
57 def get_requirement(self, relationship_name, throw_exception=False):
58 reqs = self.get_requirements(relationship_name, throw_exception)
59 if not reqs:
60 return None
61 return reqs[0]
Scott Baker9fce62e2015-08-03 15:43:54 -070062
Scott Baker714d5242015-08-05 08:20:12 -070063 def get_scalable(self):
Scott Baker502400a2015-08-05 10:41:51 -070064 scalable = self.nodetemplate.get_capabilities().get("scalable", None)
65 if scalable:
66 return {"min_instances": scalable.get_property_value("min_instances"),
67 "max_instances": scalable.get_property_value("max_instances"),
68 "default_instances": scalable.get_property_value("default_instances")}
Scott Baker714d5242015-08-05 08:20:12 -070069 else:
70 return {}
71
Scott Baker35a99532015-08-31 16:18:08 -070072 def get_property(self, name):
73 return self.nodetemplate.get_property_value(name)
Scott Baker068b1182015-08-05 18:34:23 -070074
Scott Baker33142872015-10-08 16:51:34 -070075 def get_property_default(self, name, default=None):
76 props = self.nodetemplate.get_properties()
Sapan Bhatia16be1432016-01-14 11:41:38 -050077 if props and name in props.keys():
Scott Baker33142872015-10-08 16:51:34 -070078 return props[name].value
79 return default
80
Scott Baker5fcce042015-08-24 16:25:46 -070081 def get_xos_object(self, cls, throw_exception=True, **kwargs):
Scott Baker3dd46f82016-04-28 08:41:31 -070082 # do the same parsing that we do for objname
83 for (k,v) in kwargs.items():
84 if (k=="name") and ("#" in v):
85 kwargs[k] = v.split("#",1)[1]
86
Scott Baker9fce62e2015-08-03 15:43:54 -070087 objs = cls.objects.filter(**kwargs)
88 if not objs:
Scott Baker5fcce042015-08-24 16:25:46 -070089 if throw_exception:
90 raise Exception("Failed to find %s filtered by %s" % (cls.__name__, str(kwargs)))
91 return None
Scott Baker9fce62e2015-08-03 15:43:54 -070092 return objs[0]
93
Scott Bakerccf785f2016-05-03 15:55:16 -070094 def get_replaces_objs(self):
95 replaces = self.get_property_default("replaces", None)
96 if replaces:
97 return self.xos_model.objects.filter(**{self.name_field: replaces})
98 else:
99 return []
100
Scott Baker9fdb39f2015-08-04 16:44:18 -0700101 def get_existing_objs(self):
Scott Baker642dbb52016-04-25 14:55:22 -0700102 return self.xos_model.objects.filter(**{self.name_field: self.obj_name})
Scott Baker9fdb39f2015-08-04 16:44:18 -0700103
Scott Bakerc0581502015-09-01 22:57:46 -0700104 def get_model_class_name(self):
105 return self.xos_model.__name__
106
Scott Baker9fdb39f2015-08-04 16:44:18 -0700107 def create_or_update(self):
Scott Bakerccf785f2016-05-03 15:55:16 -0700108 replaces_objs = self.get_replaces_objs()
Scott Baker9fdb39f2015-08-04 16:44:18 -0700109 existing_objs = self.get_existing_objs()
Scott Bakerccf785f2016-05-03 15:55:16 -0700110
111 if (replaces_objs and existing_objs):
112 ro = replaces_objs[0]
113 self.info("deleting %s:%s" % (self.get_model_class_name(), getattr(ro,self.name_field)))
114 ro.delete()
115
116 # in case we wanted to throw an error instead...
117 #self.error("CRITICAL ERROR: Both %s and %s exist!" % (getattr(ro,self.name_field), self.obj_name))
118 #sys.exit(-1)
119
120 if (replaces_objs and not existing_objs):
121 ro = replaces_objs[0]
122 self.info("renaming %s:%s to %s" % (self.get_model_class_name(), getattr(ro,self.name_field), self.obj_name))
123 setattr(ro, self.name_field, self.obj_name)
124 ro.save()
125 existing_objs = self.get_existing_objs()
126
Scott Baker9fdb39f2015-08-04 16:44:18 -0700127 if existing_objs:
Scott Baker33142872015-10-08 16:51:34 -0700128 if self.get_property_default("no-update", False):
Scott Baker642dbb52016-04-25 14:55:22 -0700129 self.info("%s:%s (%s) already exists. Skipping update due to 'no-update' property" % (self.get_model_class_name(), self.obj_name, self.full_name))
Scott Baker33142872015-10-08 16:51:34 -0700130 else:
Scott Baker642dbb52016-04-25 14:55:22 -0700131 self.info("%s:%s (%s) already exists" % (self.get_model_class_name(), self.obj_name, self.full_name))
Scott Baker33142872015-10-08 16:51:34 -0700132 self.update(existing_objs[0])
Scott Baker9fdb39f2015-08-04 16:44:18 -0700133 else:
Scott Baker33142872015-10-08 16:51:34 -0700134 if self.get_property_default("no-create", False):
Scott Baker642dbb52016-04-25 14:55:22 -0700135 self.info("%s:%s (%s) does not exist, but 'no-create' is specified" % (self.get_model_class_name(), self.obj_name, self.full_name))
Scott Baker33142872015-10-08 16:51:34 -0700136 else:
137 self.create()
Scott Baker9fdb39f2015-08-04 16:44:18 -0700138
Scott Baker61c43112015-08-12 19:06:16 -0700139 def can_delete(self, obj):
Scott Baker33142872015-10-08 16:51:34 -0700140 if self.get_property_default("no-delete",False):
Scott Baker642dbb52016-04-25 14:55:22 -0700141 self.info("%s:%s %s is marked 'no-delete'. Skipping delete." % (self.get_model_class_name(), self.obj_name, self.full_name))
Scott Baker33142872015-10-08 16:51:34 -0700142 return False
Scott Baker61c43112015-08-12 19:06:16 -0700143 return True
Scott Baker2142bd72015-08-10 17:08:02 -0700144
Scott Baker07c71fc2015-09-24 15:37:16 -0700145 def postprocess_privileges(self, roleclass, privclass, rolemap, obj, toFieldName):
Scott Baker92362bc2015-09-24 15:22:46 -0700146 for (rel, role) in rolemap:
147 for email in self.get_requirements(rel):
Scott Baker688c4dd2016-01-27 19:00:06 -0800148 role_obj = self.get_xos_object(roleclass, throw_exception=False, role=role)
149 if not role_obj:
150 # if the role doesn't exist, make it
151 self.info("Creating %s %s" % (roleclass.__name__, role))
152 role_obj = roleclass(role=role)
153 role_obj.save()
154
Scott Baker92362bc2015-09-24 15:22:46 -0700155 user = self.get_xos_object(User, email=email)
Scott Baker688c4dd2016-01-27 19:00:06 -0800156 if not privclass.objects.filter(user=user, role=role_obj, **{toFieldName: obj}):
157 sp = privclass(user=user, role=role_obj, **{toFieldName: obj})
Scott Baker92362bc2015-09-24 15:22:46 -0700158 sp.save()
159 self.info("Added privilege on %s role %s for %s" % (str(obj), str(role), str(user)))
160
Scott Baker2142bd72015-08-10 17:08:02 -0700161 def postprocess(self, obj):
162 pass
163
Scott Baker35a99532015-08-31 16:18:08 -0700164 def intrinsic_get_artifact(self, obj=None, name=None, method=None):
165 if obj!="SELF":
166 raise Exception("only SELF is supported for get_artifact first arg")
167 if method!="LOCAL_FILE":
168 raise Exception("only LOCAL_FILE is supported for get_artifact third arg")
169
170 for (k,v) in self.nodetemplate.entity_tpl.get("artifacts", {}).items():
171 if k == name:
172 if not os.path.exists(v):
173 raise Exception("Artifact local file %s for artifact %s does not exist" % (v, k))
174 return open(v).read()
175
176 raise Exception("artifact %s not found" % name)
177
Scott Bakerf65591f2015-09-28 16:01:21 -0700178 def intrinsic_get_script_env(self, obj=None, name=None, varname=None, method=None):
179 if obj!="SELF":
180 raise Exception("only SELF is supported for get_artifact first arg")
181 if method!="LOCAL_FILE":
182 raise Exception("only LOCAL_FILE is supported for get_artifact fourth arg")
183
184 for (k,v) in self.nodetemplate.entity_tpl.get("artifacts", {}).items():
185 if k == name:
186 if not os.path.exists(v):
187 raise Exception("Artifact local file %s for artifact %s does not exist" % (v, k))
188 return subprocess.Popen('/bin/bash -c "source %s &> /dev/null; echo \\$%s"' % (v, varname), shell=True, stdout=subprocess.PIPE).stdout.read().strip()
189
190 raise Exception("artifact %s not found" % name)
191
Scott Baker18cd4622016-06-03 15:49:16 -0700192 def intrinsic_path_join(self, obj=None, name=None, varname=None, method=None):
193 if obj!="SELF":
194 raise Exception("only SELF is supported for get_artifact first arg")
195 if method!="ENV_VAR":
196 raise Exception("only ENV_VAR is supported for get_artifact fourth arg")
197
198 if not (name in os.environ):
199 raise Exception("environment variable %s not found" % name)
200
201 return os.path.join(os.environ[name], varname)
202
Scott Baker35a99532015-08-31 16:18:08 -0700203 def try_intrinsic_function(self, v):
204 try:
205 jsv = v.replace("'", '"')
206 jsv = json.loads(jsv)
207 except:
208 #import traceback
209 #traceback.print_exc()
210 return v
211
212 if type(jsv)!=dict:
213 return v
214
215 if "get_artifact" in jsv:
216 return self.intrinsic_get_artifact(*jsv["get_artifact"])
Scott Bakerf65591f2015-09-28 16:01:21 -0700217 elif "get_script_env" in jsv:
218 return self.intrinsic_get_script_env(*jsv["get_script_env"])
Scott Baker18cd4622016-06-03 15:49:16 -0700219 elif "path_join" in jsv:
220 return self.intrinsic_path_join(*jsv["path_join"])
Scott Baker35a99532015-08-31 16:18:08 -0700221
222 return v
223
Scott Baker2142bd72015-08-10 17:08:02 -0700224 def get_xos_args(self):
225 args = {}
226
227 if self.name_field:
Scott Baker642dbb52016-04-25 14:55:22 -0700228 args[self.name_field] = self.obj_name
Scott Baker2142bd72015-08-10 17:08:02 -0700229
230 # copy simple string properties from the template into the arguments
231 for prop in self.copyin_props:
232 v = self.get_property(prop)
Scott Baker35a99532015-08-31 16:18:08 -0700233
234 v = self.try_intrinsic_function(v)
235
Scott Baker744f9a12015-09-02 16:29:16 -0700236 if v is not None:
Scott Baker2142bd72015-08-10 17:08:02 -0700237 args[prop] = v
238
239 return args
240
Scott Baker9fdb39f2015-08-04 16:44:18 -0700241 def create(self):
Scott Baker2142bd72015-08-10 17:08:02 -0700242 xos_args = self.get_xos_args()
243 xos_obj = self.xos_model(**xos_args)
Scott Baker4ce7d4f2016-04-22 09:26:18 -0700244 if self.user:
245 xos_obj.caller = self.user
Scott Baker2142bd72015-08-10 17:08:02 -0700246 xos_obj.save()
247
Scott Baker2142bd72015-08-10 17:08:02 -0700248 self.info("Created %s '%s'" % (self.xos_model.__name__,str(xos_obj)))
Scott Baker9fdb39f2015-08-04 16:44:18 -0700249
Scott Baker37937722015-08-14 12:41:18 -0700250 self.postprocess(xos_obj)
251
Scott Baker9fdb39f2015-08-04 16:44:18 -0700252 def update(self, obj):
Scott Baker744f9a12015-09-02 16:29:16 -0700253 xos_args = self.get_xos_args()
Scott Baker41916c92015-09-03 12:15:50 -0700254 for (k,v) in xos_args.items():
255 setattr(obj, k, v)
Scott Bakereb2aa3c2015-09-03 14:52:22 -0700256 self.postprocess(obj)
Scott Baker744f9a12015-09-02 16:29:16 -0700257 obj.save()
Scott Baker3841b372015-08-03 14:20:31 -0700258
Scott Baker13399c02015-08-05 16:35:09 -0700259 def delete(self, obj):
Scott Baker61c43112015-08-12 19:06:16 -0700260 if (self.can_delete(obj)):
261 self.info("destroying object %s" % str(obj))
262 obj.delete(purge=True) # XXX TODO: turn off purge before production
Scott Baker13399c02015-08-05 16:35:09 -0700263
Scott Baker9fce62e2015-08-03 15:43:54 -0700264 def info(self, s):
Scott Bakere08b4562015-08-11 17:23:52 -0700265 self.engine.log(s)
Scott Baker9fce62e2015-08-03 15:43:54 -0700266