blob: 012f8143125a9edec96f9e5acb64a002a1108967 [file] [log] [blame]
import os
import pdb
import json
import subprocess
import sys
from core.models import User
class XOSResource(object):
xos_base_class = "XOSResource"
xos_model = None
name_field = "name"
copyin_props = []
provides = None
def __init__(self, user, nodetemplate, engine):
self.dirty = False
self.deferred_sync = []
self.user = user
self.nodetemplate = nodetemplate
self.engine = engine
@property
def full_name(self):
return self.nodetemplate.name
@property
def obj_name(self):
if "#" in self.nodetemplate.name:
return self.nodetemplate.name.split("#",1)[1]
else:
return self.nodetemplate.name
def get_all_required_node_names(self):
results = []
for reqs in self.nodetemplate.requirements:
for (k,v) in reqs.items():
results.append(v["node"])
return results
def get_requirements(self, relationship_name, throw_exception=False):
""" helper to search the list of requirements for a particular relationship
type.
"""
results = []
for reqs in self.nodetemplate.requirements:
for (k,v) in reqs.items():
if (v["relationship"] == relationship_name):
results.append(v["node"])
if (not results) and throw_exception:
raise Exception("Failed to find requirement in %s using relationship %s" % (self.full_name, relationship_name))
return results
def get_requirement(self, relationship_name, throw_exception=False):
reqs = self.get_requirements(relationship_name, throw_exception)
if not reqs:
return None
return reqs[0]
def get_scalable(self):
scalable = self.nodetemplate.get_capabilities().get("scalable", None)
if scalable:
return {"min_instances": scalable.get_property_value("min_instances"),
"max_instances": scalable.get_property_value("max_instances"),
"default_instances": scalable.get_property_value("default_instances")}
else:
return {}
def get_property(self, name):
return self.nodetemplate.get_property_value(name)
def get_property_default(self, name, default=None):
props = self.nodetemplate.get_properties()
if props and name in props.keys():
return props[name].value
return default
def get_xos_object(self, cls, throw_exception=True, **kwargs):
# do the same parsing that we do for objname
for (k,v) in kwargs.items():
if (k=="name") and ("#" in v):
kwargs[k] = v.split("#",1)[1]
objs = cls.objects.filter(**kwargs)
if not objs:
if throw_exception:
raise Exception("Failed to find %s filtered by %s" % (cls.__name__, str(kwargs)))
return None
return objs[0]
def get_replaces_objs(self):
replaces = self.get_property_default("replaces", None)
if replaces:
return self.xos_model.objects.filter(**{self.name_field: replaces})
else:
return []
def get_existing_objs(self):
return self.xos_model.objects.filter(**{self.name_field: self.obj_name})
def get_model_class_name(self):
return self.xos_model.__name__
def create_or_update(self):
replaces_objs = self.get_replaces_objs()
existing_objs = self.get_existing_objs()
if (replaces_objs and existing_objs):
ro = replaces_objs[0]
self.info("deleting %s:%s" % (self.get_model_class_name(), getattr(ro,self.name_field)))
ro.delete()
# in case we wanted to throw an error instead...
#self.error("CRITICAL ERROR: Both %s and %s exist!" % (getattr(ro,self.name_field), self.obj_name))
#sys.exit(-1)
if (replaces_objs and not existing_objs):
ro = replaces_objs[0]
self.info("renaming %s:%s to %s" % (self.get_model_class_name(), getattr(ro,self.name_field), self.obj_name))
setattr(ro, self.name_field, self.obj_name)
ro.save()
existing_objs = self.get_existing_objs()
if existing_objs:
if self.get_property_default("no-update", False):
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))
else:
self.info("%s:%s (%s) already exists" % (self.get_model_class_name(), self.obj_name, self.full_name))
self.update(existing_objs[0])
else:
if self.get_property_default("no-create", False):
self.info("%s:%s (%s) does not exist, but 'no-create' is specified" % (self.get_model_class_name(), self.obj_name, self.full_name))
else:
self.create()
def can_delete(self, obj):
if self.get_property_default("no-delete",False):
self.info("%s:%s %s is marked 'no-delete'. Skipping delete." % (self.get_model_class_name(), self.obj_name, self.full_name))
return False
return True
def postprocess_privileges(self, roleclass, privclass, rolemap, obj, toFieldName):
for (rel, role) in rolemap:
for email in self.get_requirements(rel):
role_obj = self.get_xos_object(roleclass, throw_exception=False, role=role)
if not role_obj:
# if the role doesn't exist, make it
self.info("Creating %s %s" % (roleclass.__name__, role))
role_obj = roleclass(role=role)
role_obj.save()
user = self.get_xos_object(User, email=email)
if not privclass.objects.filter(user=user, role=role_obj, **{toFieldName: obj}):
sp = privclass(user=user, role=role_obj, **{toFieldName: obj})
sp.save()
self.info("Added privilege on %s role %s for %s" % (str(obj), str(role), str(user)))
def postprocess(self, obj):
pass
def intrinsic_get_artifact(self, obj=None, name=None, method=None):
if obj!="SELF":
raise Exception("only SELF is supported for get_artifact first arg")
if method!="LOCAL_FILE":
raise Exception("only LOCAL_FILE is supported for get_artifact third arg")
for (k,v) in self.nodetemplate.entity_tpl.get("artifacts", {}).items():
if k == name:
if not os.path.exists(v):
raise Exception("Artifact local file %s for artifact %s does not exist" % (v, k))
return open(v).read()
raise Exception("artifact %s not found" % name)
def intrinsic_get_script_env(self, obj=None, name=None, varname=None, method=None):
if obj!="SELF":
raise Exception("only SELF is supported for get_artifact first arg")
if method!="LOCAL_FILE":
raise Exception("only LOCAL_FILE is supported for get_artifact fourth arg")
for (k,v) in self.nodetemplate.entity_tpl.get("artifacts", {}).items():
if k == name:
if not os.path.exists(v):
raise Exception("Artifact local file %s for artifact %s does not exist" % (v, k))
return subprocess.Popen('/bin/bash -c "source %s &> /dev/null; echo \\$%s"' % (v, varname), shell=True, stdout=subprocess.PIPE).stdout.read().strip()
raise Exception("artifact %s not found" % name)
def try_intrinsic_function(self, v):
try:
jsv = v.replace("'", '"')
jsv = json.loads(jsv)
except:
#import traceback
#traceback.print_exc()
return v
if type(jsv)!=dict:
return v
if "get_artifact" in jsv:
return self.intrinsic_get_artifact(*jsv["get_artifact"])
elif "get_script_env" in jsv:
return self.intrinsic_get_script_env(*jsv["get_script_env"])
return v
def get_xos_args(self):
args = {}
if self.name_field:
args[self.name_field] = self.obj_name
# copy simple string properties from the template into the arguments
for prop in self.copyin_props:
v = self.get_property(prop)
v = self.try_intrinsic_function(v)
if v is not None:
args[prop] = v
return args
def create(self):
xos_args = self.get_xos_args()
xos_obj = self.xos_model(**xos_args)
if self.user:
xos_obj.caller = self.user
xos_obj.save()
self.info("Created %s '%s'" % (self.xos_model.__name__,str(xos_obj)))
self.postprocess(xos_obj)
def update(self, obj):
xos_args = self.get_xos_args()
for (k,v) in xos_args.items():
setattr(obj, k, v)
self.postprocess(obj)
obj.save()
def delete(self, obj):
if (self.can_delete(obj)):
self.info("destroying object %s" % str(obj))
obj.delete(purge=True) # XXX TODO: turn off purge before production
def info(self, s):
self.engine.log(s)