Refactor pillar model, add Environment and State models and add some basic API endpoints
This commit is contained in:
parent
b37f78b961
commit
c47794ecda
@ -0,0 +1,62 @@
|
|||||||
|
"""pillars are directly assigned to the hosts
|
||||||
|
|
||||||
|
Revision ID: 7eb66922e256
|
||||||
|
Revises: 54537e95fc4d
|
||||||
|
Create Date: 2026-02-08 20:34:16.291415
|
||||||
|
|
||||||
|
"""
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = '7eb66922e256'
|
||||||
|
down_revision: Union[str, Sequence[str], None] = '54537e95fc4d'
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
"""Upgrade schema."""
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_table('pillar_tool_pillar_value')
|
||||||
|
op.add_column('pillar_tool_pillar', sa.Column('pillar_name', sa.String(), nullable=False))
|
||||||
|
op.add_column('pillar_tool_pillar', sa.Column('host_id', sa.UUID(), nullable=True))
|
||||||
|
op.add_column('pillar_tool_pillar', sa.Column('type', sa.String(), nullable=False))
|
||||||
|
op.add_column('pillar_tool_pillar', sa.Column('value', sa.String(), nullable=False))
|
||||||
|
op.drop_constraint(op.f('pillar_parent_unique_name_parent'), 'pillar_tool_pillar', type_='unique')
|
||||||
|
op.create_unique_constraint('pillar_unique_pillar_name', 'pillar_tool_pillar', ['pillar_name', 'host_id'])
|
||||||
|
op.drop_constraint(op.f('pillar_tool_pillar_parent_id_fkey'), 'pillar_tool_pillar', type_='foreignkey')
|
||||||
|
op.create_foreign_key(None, 'pillar_tool_pillar', 'pillar_tool_host', ['host_id'], ['id'])
|
||||||
|
op.drop_column('pillar_tool_pillar', 'name')
|
||||||
|
op.drop_column('pillar_tool_pillar', 'parent_id')
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
"""Downgrade schema."""
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.add_column('pillar_tool_pillar', sa.Column('parent_id', sa.UUID(), autoincrement=False, nullable=True))
|
||||||
|
op.add_column('pillar_tool_pillar', sa.Column('name', sa.VARCHAR(), autoincrement=False, nullable=False))
|
||||||
|
op.drop_constraint(None, 'pillar_tool_pillar', type_='foreignkey')
|
||||||
|
op.create_foreign_key(op.f('pillar_tool_pillar_parent_id_fkey'), 'pillar_tool_pillar', 'pillar_tool_pillar', ['parent_id'], ['id'])
|
||||||
|
op.drop_constraint('pillar_unique_pillar_name', 'pillar_tool_pillar', type_='unique')
|
||||||
|
op.create_unique_constraint(op.f('pillar_parent_unique_name_parent'), 'pillar_tool_pillar', ['parent_id', 'name'], postgresql_nulls_not_distinct=False)
|
||||||
|
op.drop_column('pillar_tool_pillar', 'value')
|
||||||
|
op.drop_column('pillar_tool_pillar', 'type')
|
||||||
|
op.drop_column('pillar_tool_pillar', 'host_id')
|
||||||
|
op.drop_column('pillar_tool_pillar', 'pillar_name')
|
||||||
|
op.create_table('pillar_tool_pillar_value',
|
||||||
|
sa.Column('id', sa.UUID(), autoincrement=False, nullable=False),
|
||||||
|
sa.Column('pillar_id', sa.UUID(), autoincrement=False, nullable=False),
|
||||||
|
sa.Column('host_id', sa.UUID(), autoincrement=False, nullable=True),
|
||||||
|
sa.Column('type', sa.VARCHAR(), autoincrement=False, nullable=False),
|
||||||
|
sa.Column('value', sa.VARCHAR(), autoincrement=False, nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(['host_id'], ['pillar_tool_host.id'], name=op.f('pillar_tool_pillar_value_host_id_fkey')),
|
||||||
|
sa.ForeignKeyConstraint(['pillar_id'], ['pillar_tool_pillar.id'], name=op.f('pillar_tool_pillar_value_pillar_id_fkey')),
|
||||||
|
sa.PrimaryKeyConstraint('id', name=op.f('pillar_tool_pillar_value_pkey')),
|
||||||
|
sa.UniqueConstraint('pillar_id', 'host_id', name=op.f('pillar_value_unique_pillar_value'), postgresql_include=[], postgresql_nulls_not_distinct=False)
|
||||||
|
)
|
||||||
|
# ### end Alembic commands ###
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
"""added environments and states to the db schema
|
||||||
|
|
||||||
|
Revision ID: 0a912926be8b
|
||||||
|
Revises: 7eb66922e256
|
||||||
|
Create Date: 2026-02-08 20:51:10.862477
|
||||||
|
|
||||||
|
"""
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = '0a912926be8b'
|
||||||
|
down_revision: Union[str, Sequence[str], None] = '7eb66922e256'
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
"""Upgrade schema."""
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
pass
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
"""Downgrade schema."""
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
pass
|
||||||
|
# ### end Alembic commands ###
|
||||||
@ -5,32 +5,17 @@ from sqlalchemy import Column, UUID, String, ForeignKey, UniqueConstraint, Boole
|
|||||||
|
|
||||||
class Pillar(Base):
|
class Pillar(Base):
|
||||||
"""
|
"""
|
||||||
Describes a pillar by its name and parent. A parent equal to NULL mains that the pillar is a top level pillar.
|
A value for a given pillar on a given host.
|
||||||
Note that this is not a value, as the value is bound to the host.
|
|
||||||
"""
|
"""
|
||||||
__tablename__ = 'pillar_tool_pillar'
|
__tablename__ = 'pillar_tool_pillar'
|
||||||
id = Column(UUID, primary_key=True)
|
id = Column(UUID, primary_key=True)
|
||||||
name = Column(String, nullable=False)
|
pillar_name = Column(String, nullable=False)
|
||||||
parent_id = Column(UUID, ForeignKey('pillar_tool_pillar.id'), nullable=True)
|
|
||||||
|
|
||||||
__table_args__ = (
|
|
||||||
UniqueConstraint('parent_id', 'name', name="pillar_parent_unique_name_parent"),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class PillarValue(Base):
|
|
||||||
"""
|
|
||||||
A value for a given pillar on a given host.
|
|
||||||
"""
|
|
||||||
__tablename__ = 'pillar_tool_pillar_value'
|
|
||||||
id = Column(UUID, primary_key=True)
|
|
||||||
pillar_id = Column(UUID, ForeignKey('pillar_tool_pillar.id'), nullable=False)
|
|
||||||
host_id = Column(UUID, ForeignKey('pillar_tool_host.id'), nullable=True)
|
host_id = Column(UUID, ForeignKey('pillar_tool_host.id'), nullable=True)
|
||||||
type = Column(String, nullable=False)
|
type = Column(String, nullable=False)
|
||||||
value = Column(String, nullable=False)
|
value = Column(String, nullable=False)
|
||||||
|
|
||||||
__table_args__ = (
|
__table_args__ = (
|
||||||
UniqueConstraint('pillar_id', 'host_id', name='pillar_value_unique_pillar_value'),
|
UniqueConstraint('pillar_name', 'host_id', name='pillar_unique_pillar_name'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
39
pillar_tool/db/models/top_data.py
Normal file
39
pillar_tool/db/models/top_data.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
from pillar_tool.db.base_model import Base
|
||||||
|
|
||||||
|
from sqlalchemy import Column, UUID, String, ForeignKey, UniqueConstraint, Boolean
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
class Environment(Base):
|
||||||
|
__tablename__ = "pillar_tool_environment"
|
||||||
|
|
||||||
|
id = Column(UUID, primary_key=True, default=uuid.uuid4)
|
||||||
|
name = Column(String, nullable=False)
|
||||||
|
|
||||||
|
__table_args__ = (
|
||||||
|
UniqueConstraint('name', name="pillar_tool_unique_environment_unique_name")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class State(Base):
|
||||||
|
__tablename__ = "pillar_tool_state"
|
||||||
|
|
||||||
|
id = Column(UUID, primary_key=True, default=uuid.uuid4)
|
||||||
|
name = Column(String, nullable=False)
|
||||||
|
|
||||||
|
__table_args__ = (
|
||||||
|
UniqueConstraint('name', name="pillar_tool_unique_state_unique_name")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class StateAssignment(Base):
|
||||||
|
__tablename__ = "pillar_tool_state_assignment"
|
||||||
|
|
||||||
|
id = Column(UUID, primary_key=True, default=uuid.uuid4)
|
||||||
|
environment_id = Column(UUID, ForeignKey("pillar_tool_environment.id"), nullable=False)
|
||||||
|
state_id = Column(UUID, ForeignKey("pillar_tool_state.id"), nullable=False)
|
||||||
|
host_id = Column(UUID, ForeignKey("pillar_tool_host.id"), nullable=False)
|
||||||
|
|
||||||
|
__table_args__ = (
|
||||||
|
UniqueConstraint('environment_id', 'state_id', 'host_id', name="pillar_tool_state_assignment_unique_env_state_host"),
|
||||||
|
)
|
||||||
@ -23,7 +23,7 @@ def generate_host_hierarchy(db: Session, labels: list[str]) -> list[Host]:
|
|||||||
# NOTE: this is an assertion because the schema should enforce this
|
# NOTE: this is an assertion because the schema should enforce this
|
||||||
assert len(result) == 1
|
assert len(result) == 1
|
||||||
instance = Host(result[0])
|
instance = Host(result[0])
|
||||||
print(instance.id)
|
last_parent_id = instance.id
|
||||||
out.append(instance)
|
out.append(instance)
|
||||||
|
|
||||||
return out
|
return out
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
# load config so everything else can work
|
# load config so everything else can work
|
||||||
|
from pillar_tool.util import load_config, config
|
||||||
|
load_config()
|
||||||
|
|
||||||
from http.server import BaseHTTPRequestHandler
|
from http.server import BaseHTTPRequestHandler
|
||||||
|
|
||||||
from pillar_tool.db.base_model import as_dict
|
from pillar_tool.db.base_model import as_dict
|
||||||
from pillar_tool.middleware.logging import request_logging_middleware
|
from pillar_tool.middleware.logging import request_logging_middleware
|
||||||
from pillar_tool.schemas import HostCreateParams
|
from pillar_tool.schemas import HostCreateParams
|
||||||
from pillar_tool.util import load_config, config
|
|
||||||
load_config()
|
|
||||||
|
|
||||||
from starlette.middleware.base import BaseHTTPMiddleware
|
from starlette.middleware.base import BaseHTTPMiddleware
|
||||||
|
|
||||||
@ -84,17 +85,42 @@ async def pillar_get(req: Request, host: str):
|
|||||||
|
|
||||||
@app.post("/pillar/{host}")
|
@app.post("/pillar/{host}")
|
||||||
async def pillar_set(request: Request, host: str, value: str):
|
async def pillar_set(request: Request, host: str, value: str):
|
||||||
print(request.headers)
|
return JSONResponse({
|
||||||
|
"captain.linvogel.internal": {
|
||||||
|
"states": ["state1", "state2"],
|
||||||
|
"test": {
|
||||||
|
"pillar": "value"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
@app.get("/hosts")
|
@app.get("/hosts")
|
||||||
async def host_list(request: Request):
|
async def host_list(request: Request):
|
||||||
all_hosts = list_all_hosts(request.state.db)
|
all_hosts = list_all_hosts(request.state.db)
|
||||||
return JSONResponse([as_dict(x) for x in all_hosts])
|
return JSONResponse([x.name for x in all_hosts if x.parent_id is None])
|
||||||
|
|
||||||
|
@app.get("/hostgroups")
|
||||||
|
async def hostgroup_list(request: Request):
|
||||||
|
all_hosts = list_all_hosts(request.state.db)
|
||||||
|
return JSONResponse([x.name for x in all_hosts if x.parent_id is not None])
|
||||||
|
|
||||||
@app.post("/host/{fqdn}")
|
@app.post("/host/{fqdn}")
|
||||||
async def host_add(request: Request, fqdn: str, params: HostCreateParams):
|
async def host_add(request: Request, fqdn: str, params: HostCreateParams):
|
||||||
new_host = create_host(request.state.db, fqdn, params.parent)
|
new_host = create_host(request.state.db, fqdn, params.parent)
|
||||||
|
|
||||||
|
output = {
|
||||||
|
"message": "Host created",
|
||||||
|
"host": new_host,
|
||||||
|
}
|
||||||
if params.parent:
|
if params.parent:
|
||||||
print(f"Created new host: {new_host} with parent: {params.parent}")
|
output.update({
|
||||||
else:
|
"parent": params.parent
|
||||||
print(f"Created new host: {new_host}")
|
})
|
||||||
|
|
||||||
|
return JSONResponse(output)
|
||||||
|
|
||||||
|
@app.get("/top/{fqdn}")
|
||||||
|
async def host_top(request: Request, fqdn: str):
|
||||||
|
# TODO: implement
|
||||||
|
return JSONResponse({})
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user