Joshua M. Boniface bbfd587cd6 Initial commit of PVC Bootstrap system
Adds the PVC Bootstrap system, which allows the automated deployment of
one or more PVC clusters.
2021-12-30 01:21:17 -05:00

220 lines
6.6 KiB
Python
Executable File

#!/usr/bin/env python3
# db.py - PVC Cluster Auto-bootstrap database libraries
# Part of the Parallel Virtual Cluster (PVC) system
#
# Copyright (C) 2018-2021 Joshua M. Boniface <joshua@boniface.me>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
###############################################################################
import os
import sqlite3
import contextlib
import json
from time import sleep
from pvcbootstrapd.lib.dataclasses import Cluster, Node
#
# Database functions
#
@contextlib.contextmanager
def dbconn(db_path):
conn = sqlite3.connect(db_path)
conn.execute("PRAGMA foreign_keys = 1")
cur = conn.cursor()
yield cur
conn.commit()
conn.close()
def init_database(config):
db_path = config["database_path"]
if not os.path.isfile(db_path):
# Initializing the database
with dbconn(db_path) as cur:
# Table listing all clusters
cur.execute(
"""CREATE TABLE clusters
(id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT UNIQUE NOT NULL,
state TEXT NOT NULL)"""
)
# Table listing all nodes
# FK: cluster -> clusters.id
cur.execute(
"""CREATE TABLE nodes
(id INTEGER PRIMARY KEY AUTOINCREMENT,
cluster INTEGER NOT NULL,
state TEXT NOT NULL,
name TEXT UNIQUE NOT NULL,
nodeid INTEGER NOT NULL,
bmc_macaddr TEXT NOT NULL,
bmc_ipaddr TEXT NOT NULL,
host_macaddr TEXT NOT NULL,
host_ipaddr TEXT NOT NULL,
CONSTRAINT cluster_col FOREIGN KEY (cluster) REFERENCES clusters(id) ON DELETE CASCADE )"""
)
#
# Cluster functions
#
def get_cluster(config, cid=None, name=None):
if cid is None and name is None:
return None
elif cid is not None:
findfield = 'id'
datafield = cid
elif name is not None:
findfield = 'name'
datafield = name
with dbconn(config["database_path"]) as cur:
cur.execute(
f"""SELECT * FROM clusters WHERE {findfield} = ?""",
(datafield,)
)
rows = cur.fetchall()
if len(rows) > 0:
row = rows[0]
else:
return None
return Cluster(row[0], row[1], row[2])
def add_cluster(config, name, state):
with dbconn(config["database_path"]) as cur:
cur.execute(
"""INSERT INTO clusters
(name, state)
VALUES
(?, ?)""",
(name, state)
)
return get_cluster(config, name=name)
def update_cluster_state(config, name, state):
with dbconn(config["database_path"]) as cur:
cur.execute(
"""UPDATE clusters
SET state = ?
WHERE name = ?""",
(state, name)
)
return get_cluster(config, name=name)
#
# Node functions
#
def get_node(config, cluster_name, nid=None, name=None, bmc_macaddr=None):
cluster = get_cluster(config, name=cluster_name)
if nid is None and name is None and bmc_macaddr is None:
return None
elif nid is not None:
findfield = 'id'
datafield = nid
elif bmc_macaddr is not None:
findfield = 'bmc_macaddr'
datafield = bmc_macaddr
elif name is not None:
findfield = 'name'
datafield = name
with dbconn(config["database_path"]) as cur:
cur.execute(
f"""SELECT * FROM nodes WHERE {findfield} = ? AND cluster = ?""",
(datafield, cluster.id)
)
rows = cur.fetchall()
if len(rows) > 0:
row = rows[0]
else:
return None
return Node(row[0], cluster.name, row[2], row[3], row[4], row[5], row[6], row[7], row[8])
def get_nodes_in_cluster(config, cluster_name):
cluster = get_cluster(config, name=cluster_name)
with dbconn(config["database_path"]) as cur:
cur.execute(
"""SELECT * FROM nodes WHERE cluster = ?""",
(cluster.id, )
)
rows = cur.fetchall()
node_list = list()
for row in rows:
node_list.append(
Node(row[0], cluster.name, row[2], row[3], row[4], row[5], row[6], row[7], row[8])
)
return node_list
def add_node(config, cluster_name, state, name, nodeid, bmc_macaddr, bmc_ipaddr, host_macaddr, host_ipaddr):
cluster = get_cluster(config, name=cluster_name)
with dbconn(config["database_path"]) as cur:
cur.execute(
"""INSERT INTO nodes
(cluster, state, name, nodeid, bmc_macaddr, bmc_ipaddr, host_macaddr, host_ipaddr)
VALUES
(?, ?, ?, ?, ?, ?, ?, ?)""",
(cluster.id, state, name, nodeid, bmc_macaddr, bmc_ipaddr, host_macaddr, host_ipaddr)
)
return get_node(config, cluster_name, name=name)
def update_node_state(config, cluster_name, name, state):
cluster = get_cluster(config, name=cluster_name)
with dbconn(config["database_path"]) as cur:
cur.execute(
"""UPDATE nodes
SET state = ?
WHERE name = ? AND cluster = ?""",
(state, name, cluster.id)
)
return get_node(config, cluster_name, name=name)
def update_node_addresses(config, cluster_name, name, bmc_macaddr, bmc_ipaddr, host_macaddr, host_ipaddr):
cluster = get_cluster(config, name=cluster_name)
with dbconn(config["database_path"]) as cur:
cur.execute(
"""UPDATE nodes
SET bmc_macaddr = ?, bmc_ipaddr = ?, host_macaddr = ?, host_ipaddr = ?
WHERE name = ? AND cluster = ?""",
(bmc_macaddr, bmc_ipaddr, host_macaddr, host_ipaddr, name, cluster.id)
)
return get_node(config, cluster_name, name=name)