implemented more state and topfile logic
This commit is contained in:
parent
045049cfa4
commit
4b5b9cfe6a
13
pillar_tool.service
Normal file
13
pillar_tool.service
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=The PillarTool backend Service
|
||||||
|
Documentation=
|
||||||
|
After=network.target postgresql.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=pillar_tool
|
||||||
|
Group=pillar_tool
|
||||||
|
EnvironmentFile=/usr/local/homelab/apps/PillarTool/.venv/bin/activate
|
||||||
|
WorkingDirectory=/usr/local/homelab/apps/PillarTool
|
||||||
|
ExecStart=/usr/bin/uv run --project . --module hypercorn pillar_tool.main:app --workers 2
|
||||||
|
|
||||||
@ -1,3 +1,5 @@
|
|||||||
|
from multiprocessing.connection import default_family
|
||||||
|
|
||||||
import click
|
import click
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
@ -47,10 +49,14 @@ def state_show(name: str):
|
|||||||
|
|
||||||
@state.command("create")
|
@state.command("create")
|
||||||
@click.argument("name")
|
@click.argument("name")
|
||||||
def state_create(name: str):
|
@click.option("--addenv", nargs=1, default=None, required=False, multiple=True)
|
||||||
|
def state_create(name: str, addenv: list[str] | None):
|
||||||
click.echo(f"Creating state '{name}'...")
|
click.echo(f"Creating state '{name}'...")
|
||||||
try:
|
try:
|
||||||
data = StateParams()
|
data = StateParams(
|
||||||
|
addenv=addenv,
|
||||||
|
delenv=None
|
||||||
|
)
|
||||||
response = requests.post(f'{base_url()}/state/{name}', headers=auth_header(), json=data.model_dump())
|
response = requests.post(f'{base_url()}/state/{name}', headers=auth_header(), json=data.model_dump())
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
|
|
||||||
@ -70,5 +76,25 @@ def state_delete(name: str):
|
|||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
|
|
||||||
click.echo("State deleted successfully.")
|
click.echo("State deleted successfully.")
|
||||||
|
except requests.exceptions.HTTPError:
|
||||||
|
raise click.ClickException(f"Failed to delete state:\n{response.text}")
|
||||||
|
|
||||||
|
|
||||||
|
@state.command("update")
|
||||||
|
@click.argument("state")
|
||||||
|
@click.option("--addenv", nargs=1, default=None, required=False, multiple=True)
|
||||||
|
@click.option("--delenv", nargs=1, default=None, required=False, multiple=True)
|
||||||
|
def state_update(state: str, addenv: list[str], delenv: list[str]):
|
||||||
|
click.echo(f"Updating state '{state}'...")
|
||||||
|
|
||||||
|
try:
|
||||||
|
params = StateParams(
|
||||||
|
addenv=list(addenv),
|
||||||
|
delenv=list(delenv)
|
||||||
|
)
|
||||||
|
response = requests.patch(f'{base_url()}/state/{state}', headers=auth_header(), json=params.model_dump())
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
click.echo("State updated successfully.")
|
||||||
except requests.exceptions.HTTPError as e:
|
except requests.exceptions.HTTPError as e:
|
||||||
raise click.ClickException(f"Failed to delete state:\n{e}")
|
raise click.ClickException(f"Failed to update state:\n{e}")
|
||||||
@ -51,3 +51,18 @@ def top_assign(host: str, state: str):
|
|||||||
click.echo("Assigned state")
|
click.echo("Assigned state")
|
||||||
except requests.exceptions.HTTPError as e:
|
except requests.exceptions.HTTPError as e:
|
||||||
raise click.ClickException(f"Failed to assign state:\n{e}")
|
raise click.ClickException(f"Failed to assign state:\n{e}")
|
||||||
|
|
||||||
|
@top.command("unassign")
|
||||||
|
@click.argument("host")
|
||||||
|
@click.argument("state")
|
||||||
|
def top_assign(host: str, state: str):
|
||||||
|
click.echo("Assigning state to host...")
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.delete(f'{base_url()}/top/assign/{host}/{state}', headers=auth_header())
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
click.echo("Assigned state")
|
||||||
|
except requests.exceptions.HTTPError as e:
|
||||||
|
click.echo(f"Failed to assign state:\n{response.text}")
|
||||||
|
|
||||||
|
|||||||
@ -1,13 +1,15 @@
|
|||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from sqlalchemy import select, insert, delete
|
from sqlalchemy import select, insert, delete, bindparam, and_
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from starlette.exceptions import HTTPException
|
from starlette.exceptions import HTTPException
|
||||||
from starlette.requests import Request
|
from starlette.requests import Request
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
from starlette.responses import JSONResponse
|
from starlette.responses import JSONResponse
|
||||||
|
|
||||||
from pillar_tool.db.models.top_data import State, StateAssignment
|
from pillar_tool.db import TopFile, Host
|
||||||
|
from pillar_tool.db.models.top_data import State, StateAssignment, Environment
|
||||||
|
from pillar_tool.schemas import StateParams
|
||||||
from pillar_tool.util.validation import validate_state_name
|
from pillar_tool.util.validation import validate_state_name
|
||||||
|
|
||||||
router = APIRouter(
|
router = APIRouter(
|
||||||
@ -79,7 +81,7 @@ def state_get(req: Request, name: str):
|
|||||||
|
|
||||||
|
|
||||||
@router.post("/{name}")
|
@router.post("/{name}")
|
||||||
def state_create(req: Request, name: str):
|
def state_create(req: Request, name: str, patch_params: StateParams):
|
||||||
"""
|
"""
|
||||||
Create a new state.
|
Create a new state.
|
||||||
|
|
||||||
@ -110,6 +112,16 @@ def state_create(req: Request, name: str):
|
|||||||
new_id = uuid.uuid4()
|
new_id = uuid.uuid4()
|
||||||
db.execute(insert(State).values(id=new_id, name=name))
|
db.execute(insert(State).values(id=new_id, name=name))
|
||||||
|
|
||||||
|
stmt_set_env = insert(StateAssignment).values(state_id=new_id, environment_id=bindparam('env_id'))
|
||||||
|
stmt_get_env_id = select(Environment).where(Environment.name == bindparam('env_name'))
|
||||||
|
for env in patch_params.addenv:
|
||||||
|
env_id_res = db.execute(stmt_get_env_id, {'env_name': env}).fetchall()
|
||||||
|
if len(env_id_res) < 1:
|
||||||
|
raise HTTPException(status_code=404, detail="No such environment exists")
|
||||||
|
env_id = env_id_res[0][0].id
|
||||||
|
|
||||||
|
db.execute(stmt_set_env, {'env_id': env_id})
|
||||||
|
|
||||||
return JSONResponse(status_code=201, content={
|
return JSONResponse(status_code=201, content={
|
||||||
'id': str(new_id),
|
'id': str(new_id),
|
||||||
'name': name
|
'name': name
|
||||||
@ -149,20 +161,38 @@ def state_delete(req: Request, name: str):
|
|||||||
|
|
||||||
state: State = result[0][0]
|
state: State = result[0][0]
|
||||||
|
|
||||||
# Check for assigned hosts before deleting
|
# Check for assigned environments before deleting
|
||||||
assignments_stmt = select(StateAssignment).where(
|
assignments_stmt = select(StateAssignment).where(
|
||||||
StateAssignment.state_id == state.id
|
StateAssignment.state_id == state.id
|
||||||
)
|
)
|
||||||
assignments = db.execute(assignments_stmt).fetchall()
|
assignments = db.execute(assignments_stmt).fetchall()
|
||||||
|
|
||||||
if len(assignments) > 0:
|
if len(assignments) > 0:
|
||||||
host_ids_stmt = select(StateAssignment.host_id).where(
|
env_ids_stmt = select(StateAssignment.environment_id).where(
|
||||||
StateAssignment.state_id == state.id
|
StateAssignment.state_id == state.id
|
||||||
)
|
)
|
||||||
|
env_ids = [row[0] for row in db.execute(env_ids_stmt).fetchall()]
|
||||||
|
|
||||||
|
# Get host names (could optimize this)
|
||||||
|
envs_stmt = select(Environment).where(Environment.id.in_(env_ids))
|
||||||
|
envs: list[Environment] = list(map(lambda x: x[0], db.execute(envs_stmt).fetchall()))
|
||||||
|
|
||||||
|
return JSONResponse(status_code=409, content={
|
||||||
|
'message': "Cannot delete a state that still has environment assignments",
|
||||||
|
'assigned_envs': [e.name for e in envs]
|
||||||
|
})
|
||||||
|
|
||||||
|
# Check for assigned top files before deleting
|
||||||
|
top_stmt = select(TopFile).where(TopFile.state_id == state.id)
|
||||||
|
top = db.execute(top_stmt).fetchall()
|
||||||
|
|
||||||
|
if len(top) > 0:
|
||||||
|
host_ids_stmt = select(TopFile.host_id).where(
|
||||||
|
TopFile.state_id == state.id
|
||||||
|
)
|
||||||
host_ids = [row[0] for row in db.execute(host_ids_stmt).fetchall()]
|
host_ids = [row[0] for row in db.execute(host_ids_stmt).fetchall()]
|
||||||
|
|
||||||
# Get host names (could optimize this)
|
# Get host names (could optimize this)
|
||||||
from pillar_tool.db import Host
|
|
||||||
hosts_stmt = select(Host).where(Host.id.in_(host_ids))
|
hosts_stmt = select(Host).where(Host.id.in_(host_ids))
|
||||||
hosts: list[Host] = list(map(lambda x: x[0], db.execute(hosts_stmt).fetchall()))
|
hosts: list[Host] = list(map(lambda x: x[0], db.execute(hosts_stmt).fetchall()))
|
||||||
|
|
||||||
@ -175,3 +205,38 @@ def state_delete(req: Request, name: str):
|
|||||||
db.execute(delete(State).where(State.id == state.id))
|
db.execute(delete(State).where(State.id == state.id))
|
||||||
|
|
||||||
return JSONResponse(status_code=204, content={})
|
return JSONResponse(status_code=204, content={})
|
||||||
|
|
||||||
|
@router.patch("/{name}")
|
||||||
|
def state_patch(req: Request, name: str, patch_params: StateParams):
|
||||||
|
|
||||||
|
db: Session = req.state.db
|
||||||
|
|
||||||
|
stmt_state_id = select(State).where(State.name == name)
|
||||||
|
selected_state_res = db.execute(stmt_state_id).fetchall()
|
||||||
|
if len(selected_state_res) != 1:
|
||||||
|
raise HTTPException(status_code=404, detail="No such state exists")
|
||||||
|
state: State = selected_state_res[0][0]
|
||||||
|
|
||||||
|
# Statement for getting the
|
||||||
|
stmt_get_env_id = select(Environment).where(Environment.name == bindparam('env_name'))
|
||||||
|
|
||||||
|
# add any requested environments to the state in question
|
||||||
|
stmt_set_env = insert(StateAssignment).values(state_id=state.id, environment_id=bindparam('env_id'))
|
||||||
|
for env in patch_params.addenv:
|
||||||
|
env_id_res = db.execute(stmt_get_env_id, {'env_name': env}).fetchall()
|
||||||
|
if len(env_id_res) < 1:
|
||||||
|
raise HTTPException(status_code=404, detail="No such environment exists")
|
||||||
|
env_id = env_id_res[0][0].id
|
||||||
|
|
||||||
|
db.execute(stmt_set_env, {'env_id': env_id})
|
||||||
|
|
||||||
|
stmt_del_env = delete(StateAssignment).where(and_(StateAssignment.state_id == state.id, StateAssignment.environment_id == bindparam('env_id')))
|
||||||
|
for env in patch_params.delenv:
|
||||||
|
env_id_res = db.execute(stmt_get_env_id, {'env_name': env}).fetchall()
|
||||||
|
if len(env_id_res) < 1:
|
||||||
|
raise HTTPException(status_code=404, detail="No such environment exists")
|
||||||
|
env_id = env_id_res[0][0].id
|
||||||
|
|
||||||
|
db.execute(stmt_del_env, {'env_id': env_id})
|
||||||
|
|
||||||
|
return JSONResponse(status_code=204, content={})
|
||||||
@ -111,8 +111,6 @@ def top_setenv(req: Request, host: str, environment: str):
|
|||||||
def top_state_assign(req: Request, host_name: str, state_name: str):
|
def top_state_assign(req: Request, host_name: str, state_name: str):
|
||||||
db: Session = req.state.db
|
db: Session = req.state.db
|
||||||
|
|
||||||
print(f"Assigning {state_name} to {host_name}")
|
|
||||||
|
|
||||||
# get the host in question
|
# get the host in question
|
||||||
host_stmt = select(Host).where(Host.name == host_name)
|
host_stmt = select(Host).where(Host.name == host_name)
|
||||||
host_res = db.execute(host_stmt).fetchall()
|
host_res = db.execute(host_stmt).fetchall()
|
||||||
@ -120,7 +118,6 @@ def top_state_assign(req: Request, host_name: str, state_name: str):
|
|||||||
return JSONResponse(status_code=404, content={"error": f"Host '{host_name} not found"})
|
return JSONResponse(status_code=404, content={"error": f"Host '{host_name} not found"})
|
||||||
|
|
||||||
host: Host = host_res[0][0]
|
host: Host = host_res[0][0]
|
||||||
print(f"Found host: {host.id}")
|
|
||||||
|
|
||||||
parent_stmt = select(Host).where(Host.id == bindparam("parent_id"))
|
parent_stmt = select(Host).where(Host.id == bindparam("parent_id"))
|
||||||
parents: list[Host] = []
|
parents: list[Host] = []
|
||||||
@ -141,10 +138,8 @@ def top_state_assign(req: Request, host_name: str, state_name: str):
|
|||||||
env_assign: EnvironmentAssignment | None = None
|
env_assign: EnvironmentAssignment | None = None
|
||||||
for current_host in parents:
|
for current_host in parents:
|
||||||
env_res = db.execute(env_assign_stmt, {'host_id': current_host.id}).fetchall()
|
env_res = db.execute(env_assign_stmt, {'host_id': current_host.id}).fetchall()
|
||||||
print(f"Looking at host: {current_host.name}")
|
|
||||||
if len(env_res) == 1:
|
if len(env_res) == 1:
|
||||||
env_assign: EnvironmentAssignment = env_res[0][0]
|
env_assign: EnvironmentAssignment = env_res[0][0]
|
||||||
print(f"Found host with assigned environment: {current_host.name} with environment: {env_assign.environment_id}")
|
|
||||||
break
|
break
|
||||||
|
|
||||||
env_stmt = select(Environment).where(Environment.id == env_assign.environment_id)
|
env_stmt = select(Environment).where(Environment.id == env_assign.environment_id)
|
||||||
@ -152,23 +147,74 @@ def top_state_assign(req: Request, host_name: str, state_name: str):
|
|||||||
if len(env_res) != 1:
|
if len(env_res) != 1:
|
||||||
return JSONResponse(status_code=404, content={"error": f"Host '{host_name}' has no environment assigned"})
|
return JSONResponse(status_code=404, content={"error": f"Host '{host_name}' has no environment assigned"})
|
||||||
env: Environment = env_res[0][0]
|
env: Environment = env_res[0][0]
|
||||||
print(f"Environment found: {env.name if env else 'None'}")
|
|
||||||
|
|
||||||
# get the state in question
|
# get the state in question
|
||||||
state_stmt = (select(State).join(StateAssignment, State.id == StateAssignment.state_id)
|
state_stmt = (select(State).join(StateAssignment, State.id == StateAssignment.state_id)
|
||||||
.where(and_(State.name == state_name, StateAssignment.environment_id == env.id))
|
.where(and_(State.name == state_name, StateAssignment.environment_id == env.id))
|
||||||
)
|
)
|
||||||
print(f"Check 1: {state_stmt}")
|
|
||||||
state_res = db.execute(state_stmt).fetchall()
|
state_res = db.execute(state_stmt).fetchall()
|
||||||
print("Check 2")
|
|
||||||
if len(state_res) != 1:
|
if len(state_res) != 1:
|
||||||
print(f"Check 3: State '{state_name}' not found in environment '{env.name}'")
|
|
||||||
return JSONResponse(status_code=404, content={"error": f"No state '{state_name}' found in environment '{env.name}'"})
|
return JSONResponse(status_code=404, content={"error": f"No state '{state_name}' found in environment '{env.name}'"})
|
||||||
state: State = state_res[0][0]
|
state: State = state_res[0][0]
|
||||||
print("Check 4")
|
|
||||||
|
|
||||||
# insert the relation into the database
|
# insert the relation into the database
|
||||||
db.execute(insert(TopFile).on_conflict_do_nothing('pillar_tool_top_file_unique_state_host').values(state_id=state.id, host_id=host.id))
|
db.execute(insert(TopFile).on_conflict_do_nothing('pillar_tool_top_file_unique_state_host').values(state_id=state.id, host_id=host.id))
|
||||||
print("Check 5")
|
|
||||||
|
|
||||||
return JSONResponse(status_code=200, content={})
|
return JSONResponse(status_code=200, content={})
|
||||||
|
|
||||||
|
|
||||||
|
@router.delete("/assign/{host_name}/{state_name}")
|
||||||
|
def top_state_unassign(req: Request, host_name: str, state_name: str):
|
||||||
|
db: Session = req.state.db
|
||||||
|
|
||||||
|
# get the host in question
|
||||||
|
host_stmt = select(Host).where(Host.name == host_name)
|
||||||
|
host_res = db.execute(host_stmt).fetchall()
|
||||||
|
if len(host_res) != 1:
|
||||||
|
return JSONResponse(status_code=404, content={"error": f"Host '{host_name} not found"})
|
||||||
|
|
||||||
|
host: Host = host_res[0][0]
|
||||||
|
|
||||||
|
parent_stmt = select(Host).where(Host.id == bindparam("parent_id"))
|
||||||
|
parents: list[Host] = []
|
||||||
|
current: Host = host
|
||||||
|
while current is not None:
|
||||||
|
parents.append(current)
|
||||||
|
if current.parent_id is None:
|
||||||
|
current = None
|
||||||
|
else:
|
||||||
|
parent = db.execute(parent_stmt, {'parent_id': current.parent_id}).fetchall()
|
||||||
|
if len(parent) == 0:
|
||||||
|
return JSONResponse(status_code=500, content={"error": f"Host Hierarchy seems broken: parent_id '{current.parent_id}' does not exist"})
|
||||||
|
# Note: more than one result is impossible, since the id is a primary key
|
||||||
|
current: Host = parent[0][0]
|
||||||
|
|
||||||
|
# get the hosts environment
|
||||||
|
env_assign_stmt = select(EnvironmentAssignment).where(EnvironmentAssignment.host_id == bindparam("host_id"))
|
||||||
|
env_assign: EnvironmentAssignment | None = None
|
||||||
|
for current_host in parents:
|
||||||
|
env_res = db.execute(env_assign_stmt, {'host_id': current_host.id}).fetchall()
|
||||||
|
if len(env_res) == 1:
|
||||||
|
env_assign: EnvironmentAssignment = env_res[0][0]
|
||||||
|
break
|
||||||
|
|
||||||
|
env_stmt = select(Environment).where(Environment.id == env_assign.environment_id)
|
||||||
|
env_res = db.execute(env_stmt).fetchall()
|
||||||
|
if len(env_res) != 1:
|
||||||
|
return JSONResponse(status_code=404, content={"error": f"Host '{host_name}' has no environment assigned"})
|
||||||
|
env: Environment = env_res[0][0]
|
||||||
|
|
||||||
|
# get the state in question
|
||||||
|
state_stmt = (select(State).join(StateAssignment, State.id == StateAssignment.state_id)
|
||||||
|
.where(and_(State.name == state_name, StateAssignment.environment_id == env.id))
|
||||||
|
)
|
||||||
|
state_res = db.execute(state_stmt).fetchall()
|
||||||
|
if len(state_res) != 1:
|
||||||
|
return JSONResponse(status_code=404, content={"error": f"No state '{state_name}' found in environment '{env.name}'"})
|
||||||
|
state: State = state_res[0][0]
|
||||||
|
|
||||||
|
# delete the relation from the database
|
||||||
|
db.execute(delete(TopFile).where(and_(TopFile.state_id == state.id, TopFile.host_id == host.id)))
|
||||||
|
|
||||||
|
return JSONResponse(status_code=200, content={})
|
||||||
|
|
||||||
|
|||||||
@ -48,7 +48,8 @@ class HostgroupParams(BaseModel):
|
|||||||
|
|
||||||
# State operations
|
# State operations
|
||||||
class StateParams(BaseModel):
|
class StateParams(BaseModel):
|
||||||
pass # No parameters needed for state operations currently
|
addenv: list[str] | None
|
||||||
|
delenv: list[str] | None
|
||||||
|
|
||||||
# Pillar operations
|
# Pillar operations
|
||||||
class PillarParams(BaseModel):
|
class PillarParams(BaseModel):
|
||||||
|
|||||||
@ -11,5 +11,7 @@ Requires-Dist: jinja2>=3.1.6
|
|||||||
Requires-Dist: psycopg2>=2.9.11
|
Requires-Dist: psycopg2>=2.9.11
|
||||||
Requires-Dist: pycryptodome>=3.23.0
|
Requires-Dist: pycryptodome>=3.23.0
|
||||||
Requires-Dist: pydantic>=2.12.5
|
Requires-Dist: pydantic>=2.12.5
|
||||||
|
Requires-Dist: pygit2>=1.19.2
|
||||||
|
Requires-Dist: pyyaml>=6.0.3
|
||||||
Requires-Dist: requests>=2.32.5
|
Requires-Dist: requests>=2.32.5
|
||||||
Requires-Dist: sqlalchemy>=2.0.45
|
Requires-Dist: sqlalchemy>=2.0.45
|
||||||
|
|||||||
@ -18,6 +18,11 @@ pillar_tool/db/migrations/versions/2026_02_08_2034-7eb66922e256_pillars_are_dire
|
|||||||
pillar_tool/db/migrations/versions/2026_02_08_2051-0a912926be8b_added_environments_and_states_to_the_db_.py
|
pillar_tool/db/migrations/versions/2026_02_08_2051-0a912926be8b_added_environments_and_states_to_the_db_.py
|
||||||
pillar_tool/db/migrations/versions/2026_02_08_2220-e33744090598_mark_hosts_as_hostgroups.py
|
pillar_tool/db/migrations/versions/2026_02_08_2220-e33744090598_mark_hosts_as_hostgroups.py
|
||||||
pillar_tool/db/migrations/versions/2026_02_10_2147-dd573f631ee4_added_environment_assignments_to_.py
|
pillar_tool/db/migrations/versions/2026_02_10_2147-dd573f631ee4_added_environment_assignments_to_.py
|
||||||
|
pillar_tool/db/migrations/versions/2026_02_14_2335-a5848dcca950_fixed_errors_in_database_schema.py
|
||||||
|
pillar_tool/db/migrations/versions/2026_02_14_2337-58c2a8e7c302_import_top_data.py
|
||||||
|
pillar_tool/db/migrations/versions/2026_02_21_2338-ec7c818f92b5_renamed_bad_parameter.py
|
||||||
|
pillar_tool/db/migrations/versions/2026_04_25_0004-9ebc4cadee1c_separate_top_file_for_environments_and_.py
|
||||||
|
pillar_tool/db/migrations/versions/2026_04_25_1133-d3406fae0ecf_remove_id_from_env_assign.py
|
||||||
pillar_tool/db/models/__init__.py
|
pillar_tool/db/models/__init__.py
|
||||||
pillar_tool/db/models/pillar_data.py
|
pillar_tool/db/models/pillar_data.py
|
||||||
pillar_tool/db/models/top_data.py
|
pillar_tool/db/models/top_data.py
|
||||||
@ -26,6 +31,8 @@ pillar_tool/db/queries/__init__.py
|
|||||||
pillar_tool/db/queries/auth_queries.py
|
pillar_tool/db/queries/auth_queries.py
|
||||||
pillar_tool/db/queries/host_queries.py
|
pillar_tool/db/queries/host_queries.py
|
||||||
pillar_tool/db/queries/pillar_queries.py
|
pillar_tool/db/queries/pillar_queries.py
|
||||||
|
pillar_tool/git/__init__.py
|
||||||
|
pillar_tool/git/repository.py
|
||||||
pillar_tool/middleware/__init__.py
|
pillar_tool/middleware/__init__.py
|
||||||
pillar_tool/middleware/basicauth_backend.py
|
pillar_tool/middleware/basicauth_backend.py
|
||||||
pillar_tool/middleware/db_connection.py
|
pillar_tool/middleware/db_connection.py
|
||||||
@ -40,14 +47,18 @@ pillar_tool/ptcli/cli/hostgroup.py
|
|||||||
pillar_tool/ptcli/cli/pillar.py
|
pillar_tool/ptcli/cli/pillar.py
|
||||||
pillar_tool/ptcli/cli/query.py
|
pillar_tool/ptcli/cli/query.py
|
||||||
pillar_tool/ptcli/cli/state.py
|
pillar_tool/ptcli/cli/state.py
|
||||||
|
pillar_tool/ptcli/cli/top.py
|
||||||
pillar_tool/routers/__init__.py
|
pillar_tool/routers/__init__.py
|
||||||
pillar_tool/routers/environment.py
|
pillar_tool/routers/environment.py
|
||||||
pillar_tool/routers/host.py
|
pillar_tool/routers/host.py
|
||||||
pillar_tool/routers/hostgroup.py
|
pillar_tool/routers/hostgroup.py
|
||||||
pillar_tool/routers/pillar.py
|
pillar_tool/routers/pillar.py
|
||||||
pillar_tool/routers/state.py
|
pillar_tool/routers/state.py
|
||||||
|
pillar_tool/routers/top.py
|
||||||
pillar_tool/util/__init__.py
|
pillar_tool/util/__init__.py
|
||||||
pillar_tool/util/config.py
|
pillar_tool/util/config.py
|
||||||
|
pillar_tool/util/files.py
|
||||||
|
pillar_tool/util/pillar_utilities.py
|
||||||
pillar_tool/util/validation.py
|
pillar_tool/util/validation.py
|
||||||
pillartool.egg-info/PKG-INFO
|
pillartool.egg-info/PKG-INFO
|
||||||
pillartool.egg-info/SOURCES.txt
|
pillartool.egg-info/SOURCES.txt
|
||||||
|
|||||||
@ -6,5 +6,7 @@ jinja2>=3.1.6
|
|||||||
psycopg2>=2.9.11
|
psycopg2>=2.9.11
|
||||||
pycryptodome>=3.23.0
|
pycryptodome>=3.23.0
|
||||||
pydantic>=2.12.5
|
pydantic>=2.12.5
|
||||||
|
pygit2>=1.19.2
|
||||||
|
pyyaml>=6.0.3
|
||||||
requests>=2.32.5
|
requests>=2.32.5
|
||||||
sqlalchemy>=2.0.45
|
sqlalchemy>=2.0.45
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user