~fnux/machines.sr.ht

4618e030a22c47611316693b64e04528cac6c0b9 — Timothée Floure a month ago 561c651
Add inital database structure / test db wiring with hosts
A machines/alembic/README => machines/alembic/README +1 -0
@@ 0,0 1,1 @@
Generic single-database configuration.
\ No newline at end of file

A machines/alembic/script.py.mako => machines/alembic/script.py.mako +24 -0
@@ 0,0 1,24 @@
"""${message}

Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}

"""
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}

# revision identifiers, used by Alembic.
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
branch_labels = ${repr(branch_labels)}
depends_on = ${repr(depends_on)}


def upgrade():
    ${upgrades if upgrades else "pass"}


def downgrade():
    ${downgrades if downgrades else "pass"}

D machines/alembic/versions/.keep => machines/alembic/versions/.keep +0 -0

A machines/alembic/versions/598f0a752e89_initial_host_machine_volume_structure.py => machines/alembic/versions/598f0a752e89_initial_host_machine_volume_structure.py +82 -0
@@ 0,0 1,82 @@
"""Initial Host/Machine/Volume structure

Revision ID: 598f0a752e89
Revises: 
Create Date: 2020-06-21 11:31:43.065605

"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = '598f0a752e89'
down_revision = None
branch_labels = None
depends_on = None


def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table('host',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('name', sa.String(), nullable=False),
    sa.Column('address', sa.String(), nullable=False),
    sa.Column('created', sa.DateTime(), nullable=False),
    sa.Column('updated', sa.DateTime(), nullable=False),
    sa.Column('cores', sa.Integer(), nullable=False),
    sa.Column('memory', sa.Integer(), nullable=False),
    sa.Column('status', sqlalchemy_utils.types.choice.ChoiceType(), nullable=False),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_table('user',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('created', sa.DateTime(), nullable=False),
    sa.Column('updated', sa.DateTime(), nullable=False),
    sa.Column('username', sa.Unicode(length=256), nullable=True),
    sa.Column('email', sa.String(length=256), nullable=False),
    sa.Column('user_type', sqlalchemy_utils.types.choice.ChoiceType(), nullable=False),
    sa.Column('url', sa.String(length=256), nullable=True),
    sa.Column('location', sa.Unicode(length=256), nullable=True),
    sa.Column('bio', sa.Unicode(length=4096), nullable=True),
    sa.Column('suspension_notice', sa.Unicode(length=4096), nullable=True),
    sa.Column('oauth_token', sa.String(length=256), nullable=True),
    sa.Column('oauth_token_expires', sa.DateTime(), nullable=True),
    sa.Column('oauth_token_scopes', sa.String(), nullable=True),
    sa.Column('oauth_revocation_token', sa.String(length=256), nullable=True),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_index(op.f('ix_user_username'), 'user', ['username'], unique=True)
    op.create_table('machine',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('name', sa.String(), nullable=False),
    sa.Column('created', sa.DateTime(), nullable=False),
    sa.Column('updated', sa.DateTime(), nullable=False),
    sa.Column('cores', sa.Integer(), nullable=False),
    sa.Column('memory', sa.Integer(), nullable=False),
    sa.Column('status', sqlalchemy_utils.types.choice.ChoiceType(), nullable=False),
    sa.Column('host_id', sa.Integer(), nullable=False),
    sa.ForeignKeyConstraint(['host_id'], ['host.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_table('volume',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('type', sa.String(), nullable=False),
    sa.Column('created', sa.DateTime(), nullable=False),
    sa.Column('updated', sa.DateTime(), nullable=False),
    sa.Column('size', sa.Integer(), nullable=False),
    sa.Column('machine_id', sa.Integer(), nullable=False),
    sa.ForeignKeyConstraint(['machine_id'], ['machine.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    # ### end Alembic commands ###


def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_table('volume')
    op.drop_table('machine')
    op.drop_index(op.f('ix_user_username'), table_name='user')
    op.drop_table('user')
    op.drop_table('host')
    # ### end Alembic commands ###

M machines/blueprints/web.py => machines/blueprints/web.py +31 -1
@@ 1,5 1,8 @@
from flask import Blueprint, render_template
from flask import Blueprint, render_template, request, redirect
from srht.validation import Validation
from srht.oauth import current_user, loginrequired
from srht.database import db
from machines.types import Host, HostStatus

web = Blueprint("machines.web", __name__)



@@ 15,6 18,33 @@ def index():
def usage():
    return render_template("usage.html")

@web.route("/hosts")
@loginrequired
def hosts():
    return render_template("hosts.html", hosts=Host.query.all())

@web.route("/host/create", methods=["GET"])
@loginrequired
def create_host_GET():
    return render_template("host-create.html")

@web.route("/host/create", methods=["POST"])
@loginrequired
def create_host_POST():
    valid = Validation(request)
    name = valid.require("name", friendly_name="Name")
    address = valid.require("address", friendly_name="address")
    if not valid.ok:
        return render_template("host-create.html", **valid.kwargs), 400

    host = Host(name=name, address=address,
            cores=0, memory=0, status=HostStatus.unknown)
    db.session.add(host)
    db.session.flush()
    db.session.commit()

    return redirect("/hosts")

@web.route("/allocate")
@loginrequired
def allocate():

M machines/templates/dashboard.html => machines/templates/dashboard.html +4 -0
@@ 14,6 14,10 @@
        class="btn btn-primary btn-block"
      >Allocate new machine {{icon("caret-right")}}</a>
      <a
        href="/hosts"
        class="btn btn-secondary btn-block"
      >Hosts {{icon("caret-right")}}</a>
      <a
        href="/usage"
        class="btn btn-secondary btn-block"
      >Consult usage {{icon("caret-right")}}</a>

A machines/templates/host-create.html => machines/templates/host-create.html +46 -0
@@ 0,0 1,46 @@
{% extends "layout.html" %}
{% block content %}
<div class="row">
	<div class="col-md-4">
		<p>
		Create host.
		</p>
	</div>
	<div class="col-md-8">
		<h2>Connect new host</h2>
		<form method="POST" action="/host/create">
			{{csrf_token()}}
			<div class="form-group">
				<label for="name">Name</label>
				<input
					type="text"
					name="name"
					id="name"
					class="form-control {{valid.cls("name")}}"
					value="{{ name or "" }}"
					aria-describedby="name-help" />
				{{valid.summary("name")}}
			</div>
			<div class="form-group">
				<label for="description">Address</label>
				<input
					name="address"
					id="address"
					class="form-control {{valid.cls("address")}}"
					value="{{ address or "" }}"
					rows="5"
					aria-describedby="address-help" />
				{{valid.summary("address")}}
			</div>
			{{valid.summary()}}
			<button
				type="submit"
				class="btn btn-primary"
				name="create"
				>
				Connect host {{icon("caret-right")}}
			</button>
		</form>
	</div>
</div>
{% endblock %}

A machines/templates/hosts.html => machines/templates/hosts.html +25 -0
@@ 0,0 1,25 @@
{% extends "layout.html" %}
{% block content %}
<div class="row">
	<div class="col-md-4">
		<p>
		This page list hosts.
		</p>
		<a
			href="/host/create"
			class="btn btn-secondary btn-block"
			>Connect host {{icon("caret-right")}}</a>
	</div>
	<div class="col-md-8">
		<table>
			{% for host in hosts %}
			<tr>
				<td>{{ host.id }}</td>
				<td>{{ host.name }}</td>
			</tr>
			{% endfor %}
		</table>
		</div>
	</div>
</div>
{% endblock %}

M machines/types/__init__.py => machines/types/__init__.py +4 -0
@@ 3,3 3,7 @@ from srht.oauth import ExternalUserMixin

class User(Base, ExternalUserMixin):
    pass

from .host import Host, HostStatus
from .machine import Machine, MachineStatus
from .volume import Volume

A machines/types/host.py => machines/types/host.py +24 -0
@@ 0,0 1,24 @@
import sqlalchemy as sa
import sqlalchemy_utils as sau
from srht.database import Base
from enum import Enum

class HostStatus(Enum):
    enabled = 'enabled'
    disabled = 'disabled'
    off = 'off'
    unknown = 'unknown'

class Host(Base):
    __tablename__ = 'host'
    id = sa.Column(sa.Integer, primary_key=True)
    name = sa.Column(sa.String, nullable=False)
    address= sa.Column(sa.String, nullable=False)
    created = sa.Column(sa.DateTime, nullable=False)
    updated = sa.Column(sa.DateTime, nullable=False)
    cores = sa.Column(sa.Integer, nullable=False)
    memory = sa.Column(sa.Integer, nullable=False)
    status = sa.Column(
            sau.ChoiceType(HostStatus, impl=sa.String()),
            nullable=False,
            default=HostStatus.unknown)

A machines/types/machine.py => machines/types/machine.py +24 -0
@@ 0,0 1,24 @@
import sqlalchemy as sa
import sqlalchemy_utils as sau
from srht.database import Base
from enum import Enum

class MachineStatus(Enum):
    pending = 'pending'
    unknown = 'unknown'

class Machine(Base):
    __tablename__ = 'machine'
    id = sa.Column(sa.Integer, primary_key=True)
    name = sa.Column(sa.String, nullable=False)
    created = sa.Column(sa.DateTime, nullable=False)
    updated = sa.Column(sa.DateTime, nullable=False)
    cores = sa.Column(sa.Integer, nullable=False)
    memory = sa.Column(sa.Integer, nullable=False)
    status = sa.Column(
            sau.ChoiceType(MachineStatus, impl=sa.String()),
            nullable=False,
            default=MachineStatus.pending)

    host_id = sa.Column(sa.Integer, sa.ForeignKey("host.id"), nullable=False)
    host = sa.orm.relationship("Host", backref=sa.orm.backref("machines"))

A machines/types/volume.py => machines/types/volume.py +14 -0
@@ 0,0 1,14 @@
import sqlalchemy as sa
import sqlalchemy_utils as sau
from srht.database import Base

class Volume(Base):
    __tablename__ = 'volume'
    id = sa.Column(sa.Integer, primary_key=True)
    type = sa.Column(sa.String, nullable=False)
    created = sa.Column(sa.DateTime, nullable=False)
    updated = sa.Column(sa.DateTime, nullable=False)
    size = sa.Column(sa.Integer, nullable=False)

    machine_id = sa.Column(sa.Integer, sa.ForeignKey("machine.id"), nullable=False)
    machine = sa.orm.relationship("Machine", backref=sa.orm.backref("volumes"))