diff options
author | Hugo Hörnquist <hugo@lysator.liu.se> | 2022-08-08 04:41:11 +0200 |
---|---|---|
committer | Hugo Hörnquist <hugo@lysator.liu.se> | 2022-08-08 04:41:11 +0200 |
commit | ba1516c255f7167b696ca7f4c6db2846cf6bb046 (patch) | |
tree | c1794abe263034300efce07bb629cb10edc5ddd5 /pyenc/app/model.py | |
parent | work (diff) | |
download | puppet-classifier-ba1516c255f7167b696ca7f4c6db2846cf6bb046.tar.gz puppet-classifier-ba1516c255f7167b696ca7f4c6db2846cf6bb046.tar.xz |
work
Diffstat (limited to 'pyenc/app/model.py')
-rw-r--r-- | pyenc/app/model.py | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/pyenc/app/model.py b/pyenc/app/model.py new file mode 100644 index 0000000..f67cd10 --- /dev/null +++ b/pyenc/app/model.py @@ -0,0 +1,191 @@ +"""Database model for application.""" + +from flask_sqlalchemy import SQLAlchemy + +# db = SQLAlchemy(session_options={"autoflush": False}) +db = SQLAlchemy() + + +def init_app(app): + """Adds database bindings to a Flask App.""" + db.init_app(app) + import logging + # logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO) + + +host_classes = db.Table( + 'host_classes', + db.Column('host_id', db.ForeignKey('host.id'), primary_key=True), + db.Column('class_id', db.ForeignKey('puppet_class.id'), primary_key=True), +) + + +# NOTE this is non-final, and might get removed shortly +environment_classes = db.Table( + 'environment_classes', + db.Column('environment_id', db.ForeignKey('puppet_environment.id'), primary_key=True), + db.Column('class_id', db.ForeignKey('puppet_class.id'), primary_key=True), +) + + +class_files = db.Table( + 'class_files', + db.Column('class_id', db.ForeignKey('puppet_class.id'), primary_key=True), + db.Column('file_id', db.ForeignKey('puppet_file.id'), primary_key=True), + db.UniqueConstraint('class_id', 'file_id'), +) + + +# class HostClasses(db.Model): +# __tablename__ = 'host_classes' +# id = db.Column(db.Integer, primary_key=True) +# host_id = db.Column(db.Integer, db.ForeignKey('host.id'), nullable=False) +# class_id = db.Column(db.Integer, db.ForeignKey('puppet_class.id'), nullable=False) + + +class PuppetEnvironment(db.Model): + """ + A puppet environment. + + An enviromnet is a collection of modules, but here we only keep + the files of the modules, in PuppetFile. + """ + __tablename__ = 'puppet_environment' + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.Text, nullable=False, unique=True) + classes = db.relationship( + 'PuppetClass', + back_populates='environments', + secondary=environment_classes) + hosts = db.relationship( + 'Host', + back_populates='environment') + + +class Host(db.Model): + """ + Single computer. + + A computer has a name (machine.example.com.), an environment + (production) and a list of puppet classes. + + (TODO and direct values?) + """ + + __tablename__ = 'host' + id = db.Column(db.Integer, primary_key=True) + fqdn = db.Column(db.Text, nullable=False, unique=True) + environment_id = db.Column(db.Integer, db.ForeignKey(f'{PuppetEnvironment.__tablename__}.id')) + environment = db.relationship('PuppetEnvironment', back_populates='hosts') + # classes = db.relationship('HostClasses', backref='host', lazy='dynamic') + classes = db.relationship( + 'PuppetClass', + back_populates='hosts', + secondary=host_classes) + + def serialize(self): # pylint: disable=missing-function-docstring + return {column.name: self.__getattribute__(column.name) + for column in self.__table__.columns} + + +class PuppetFile(db.Model): + """ + Puppet source code file. + + Keeps track of known puppet files. Each file contains 0 to many + puppet classes. + + Each file is uniquely identified by the pair (path, environment). + """ + + __tablename__ = 'puppet_file' + id = db.Column(db.Integer, primary_key=True) + # Where we found the file (path inside environment) + # e.g. /etc/puppetlabs/code/environments/<environment name>/<this path> + path = db.Column(db.Text, nullable=False) + + # Puppet environment this file belongs in + environment = db.Column(db.Integer, + db.ForeignKey(f'{PuppetEnvironment.__tablename__}.id'), + nullable=False) + + # Checksum of the content, should be usable as a key in PuppetFileContent + checksum = db.Column(db.Text, nullable=False) + + # When we last read data into json + last_parse = db.Column(db.Float) + + classes = db.relationship('PuppetClass', + back_populates='files', + secondary=class_files) + content = db.relationship('PuppetFileContent', backref='file') + + __table_args__ = ( + db.UniqueConstraint('path', 'environment'), + ) + + +class PuppetFileContent(db.Model): + """ + (Parsed) contents of puppet source files. + + Separate from PuppetFile since many environments can share files, + and I don't want to store reduntand data. + """ + __tablename__ = 'puppet_file_content' + + id = db.Column(db.Integer, primary_key=True) + + file_id = db.Column(db.Integer, db.ForeignKey(f'{PuppetFile.__tablename__}.id')) + + # Checksum of the original file + checksum = db.Column(db.Text, nullable=False) + + # Output of 'puppet parser dump --format json <filename>' + json = db.Column(db.Text, nullable=False) + + +# TODO class environment mappings? +# - the same class can exist in multiple environmentns +# - the same class in multiple environments might be different +# - the class can come or go when the file is changed (??) +# - when a node changes environment it still has its classes, but they +# refer to something else now + +# Possibly: +# nodes holds its list of classes as a list of strings +# I have tables which maps class names to files per environment + +# What happens when two different environments have separate classes +# which share a name + +class PuppetClass(db.Model): + """ + A puppet class. + + The class itself only keeps track of its name here, and mostly + ensures that only existing classes can be added to a given node/host. + """ + __tablename__ = 'puppet_class' + id = db.Column(db.Integer, primary_key=True) + class_name = db.Column(db.Text, nullable=False, unique=True) + + hosts = db.relationship( + 'Host', + back_populates='classes', + secondary=host_classes) + environments = db.relationship( + 'PuppetEnvironment', + back_populates='classes', + secondary=environment_classes) + files = db.relationship( + 'PuppetFile', + back_populates='classes', + secondary=class_files) + + +class Misc(db.Model): + __tablename__ = 'misc' + id = db.Column(db.Integer, primary_key=True) + key = db.Column(db.Text, nullable=False) + value = db.Column(db.Text) |