class ReplicaCatalog(CatalogMixin, db.Model): __tablename__ = 'replica_catalog' __table_args__ = (db.UniqueConstraint('user_id', 'name'), { 'mysql_engine': 'InnoDB' }) __catalog_type__ = 'replica' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100), nullable=False) format = db.Column(db.Enum(*RC_FORMATS), nullable=False) created = db.Column(db.DateTime, nullable=False) updated = db.Column(db.DateTime, nullable=False) user_id = db.Column(db.Integer, db.ForeignKey('user.id')) user = db.relationship("User") def __init__(self, user_id, name, format): self.user_id = user_id self.set_name(name) self.set_format(format) self.set_created() self.set_updated()
class Ensemble(db.Model, EnsembleMixin): __tablename__ = 'ensemble' __table_args__ = ( db.UniqueConstraint('user_id', 'name'), {'mysql_engine': 'InnoDB'} ) id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100), nullable=False) created = db.Column(db.DateTime, nullable=False) updated = db.Column(db.DateTime, nullable=False) state = db.Column(db.Enum(*EnsembleStates), nullable=False) max_running = db.Column(db.Integer, nullable=False) max_planning = db.Column(db.Integer, nullable=False) user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) user = db.relationship("User") def __init__(self, user_id, name): self.user_id = user_id self.set_name(name) self.set_created() self.set_updated() self.state = EnsembleStates.ACTIVE self.set_max_running(1) self.set_max_planning(1) def set_state(self, state): state = state.upper() if state not in EnsembleStates: raise APIError("Invalid ensemble state: %s" % state) self.state = state def set_max_running(self, max_running): try: x = int(max_running) if max_running < 1: raise APIError("Value for max_running must be >= 1: %s" % max_running) self.max_running = x except ValueError: raise APIError("Invalid value for max_running: %s" % max_running) def set_max_planning(self, max_planning): try: x = int(max_planning) if max_planning < 1: raise APIError("Value for max_planning must be >= 1: %s" % max_planning) self.max_planning = x except ValueError: raise APIError("Invalid value for max_planning: %s" % max_planning) def get_dir(self): return os.path.join(self.user.get_userdata_dir(), "ensembles", self.name) def get_object(self): return { "id": self.id, "name": self.name, "created": self.created, "updated": self.updated, "state": self.state, "max_running": self.max_running, "max_planning": self.max_planning, "workflows": url_for("route_list_ensemble_workflows", name=self.name, _external=True), "href": url_for("route_get_ensemble", name=self.name, _external=True) }
class EnsembleWorkflow(db.Model, EnsembleMixin): __tablename__ = 'ensemble_workflow' __table_args__ = ( db.UniqueConstraint('ensemble_id', 'name'), {'mysql_engine': 'InnoDB'} ) id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100), nullable=False) created = db.Column(db.DateTime, nullable=False) updated = db.Column(db.DateTime, nullable=False) state = db.Column(db.Enum(*EnsembleWorkflowStates), nullable=False) priority = db.Column(db.Integer, nullable=False) wf_uuid = db.Column(db.String(36)) submitdir = db.Column(db.String(512)) ensemble_id = db.Column(db.Integer, db.ForeignKey('ensemble.id'), nullable=False) ensemble = db.relationship("Ensemble", backref="workflows") def __init__(self, ensemble_id, name): self.ensemble_id = ensemble_id self.set_name(name) self.set_created() self.set_updated() self.state = EnsembleWorkflowStates.READY self.set_priority(0) self.set_wf_uuid(None) self.set_submitdir(None) def set_state(self, state): state = state.upper() if state not in EnsembleWorkflowStates: raise APIError("Invalid ensemble workflow state: %s" % state) self.state = state def change_state(self, state): # The allowed transitions are: # PLAN_FAILED -> READY # RUN_FAILED -> QUEUED # RUN_FAILED -> READY # FAILED -> QUEUED # FAILED -> READY if self.state == EnsembleWorkflowStates.PLAN_FAILED: if state != EnsembleWorkflowStates.READY: raise APIError("Can only replan workflows in PLAN_FAILED state") elif self.state == EnsembleWorkflowStates.RUN_FAILED: if state not in (EnsembleWorkflowStates.READY, EnsembleWorkflowStates.QUEUED): raise APIError("Can only replan or rerun workflows in RUN_FAILED state") elif self.state == EnsembleWorkflowStates.FAILED: if state not in (EnsembleWorkflowStates.READY, EnsembleWorkflowStates.QUEUED): raise APIError("Can only replan or rerun workflows in FAILED state") else: raise APIError("Invalid state change: %s -> %s" % (self.state, state)) self.set_state(state) def set_priority(self, priority): self.priority = validate_priority(priority) def set_wf_uuid(self, wf_uuid): if wf_uuid is not None and len(wf_uuid) != 36: raise APIError("Invalid wf_uuid") self.wf_uuid = wf_uuid def set_submitdir(self, submitdir): self.submitdir = submitdir def get_dir(self): ensembledir = self.ensemble.get_dir() return os.path.join(ensembledir, "workflows", self.name) def get_object(self): return { "id": self.id, "name": self.name, "created": self.created, "updated": self.updated, "state": self.state, "priority": self.priority, "wf_uuid": self.wf_uuid, "href": url_for("route_get_ensemble_workflow", ensemble=self.ensemble.name, workflow=self.name, _external=True) } def get_detail_object(self): def myurl_for(filename): return url_for("route_get_ensemble_workflow_file", ensemble=self.ensemble.name, workflow=self.name, filename=filename, _external=True) o = self.get_object() o["dax"] = myurl_for("dax.xml") o["replicas"] = myurl_for("rc.txt") o["transformations"] = myurl_for("tc.txt") o["sites"] = myurl_for("sites.xml") o["conf"] = myurl_for("pegasus.properties") o["plan_script"] = myurl_for("plan.sh") return o