2022-07-18 23:59:14 +00:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
# -*- coding: utf-8 -*-
|
2023-03-09 21:50:32 +00:00
|
|
|
import logging
|
|
|
|
from sqlalchemy.orm import sessionmaker, scoped_session
|
|
|
|
from sqlalchemy import MetaData, Table
|
|
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
2023-03-22 15:51:24 +00:00
|
|
|
from sqlalchemy.exc import IllegalStateChangeError, NoInspectionAvailable
|
2023-03-09 21:50:32 +00:00
|
|
|
import asyncio
|
2023-03-05 00:00:20 +00:00
|
|
|
|
2022-07-18 23:59:14 +00:00
|
|
|
|
2020-06-19 13:20:22 +00:00
|
|
|
class database:
|
2023-03-09 21:50:32 +00:00
|
|
|
def __init__(self, db_engine):
|
2023-03-12 02:25:23 +00:00
|
|
|
self.CredentialsTable = None
|
2023-03-09 21:50:32 +00:00
|
|
|
self.HostsTable = None
|
|
|
|
|
|
|
|
self.db_engine = db_engine
|
|
|
|
self.metadata = MetaData()
|
|
|
|
asyncio.run(self.reflect_tables())
|
|
|
|
session_factory = sessionmaker(bind=self.db_engine, expire_on_commit=True, class_=AsyncSession)
|
2023-03-09 22:00:51 +00:00
|
|
|
|
2023-03-09 21:50:32 +00:00
|
|
|
Session = scoped_session(session_factory)
|
2023-03-05 00:00:20 +00:00
|
|
|
# this is still named "conn" when it is the session object; TODO: rename
|
2023-03-09 21:50:32 +00:00
|
|
|
self.conn = Session()
|
2020-06-19 13:20:22 +00:00
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def db_schema(db_conn):
|
|
|
|
db_conn.execute('''CREATE TABLE "credentials" (
|
|
|
|
"id" integer PRIMARY KEY,
|
|
|
|
"username" text,
|
|
|
|
"password" text
|
|
|
|
)''')
|
|
|
|
|
|
|
|
db_conn.execute('''CREATE TABLE "hosts" (
|
|
|
|
"id" integer PRIMARY KEY,
|
|
|
|
"ip" text,
|
|
|
|
"hostname" text,
|
|
|
|
"port" integer
|
|
|
|
)''')
|
2023-03-04 16:12:29 +00:00
|
|
|
|
2023-03-09 21:50:32 +00:00
|
|
|
async def shutdown_db(self):
|
|
|
|
try:
|
|
|
|
await asyncio.shield(self.conn.close())
|
|
|
|
# due to the async nature of CME, sometimes session state is a bit messy and this will throw:
|
|
|
|
# Method 'close()' can't be called here; method '_connection_for_bind()' is already in progress and
|
|
|
|
# this would cause an unexpected state change to <SessionTransactionState.CLOSED: 5>
|
|
|
|
except IllegalStateChangeError as e:
|
|
|
|
logging.debug(f"Error while closing session db object: {e}")
|
|
|
|
|
|
|
|
async def reflect_tables(self):
|
|
|
|
async with self.db_engine.connect() as conn:
|
2023-03-22 15:51:24 +00:00
|
|
|
try:
|
|
|
|
await conn.run_sync(self.metadata.reflect)
|
2023-03-09 21:50:32 +00:00
|
|
|
|
2023-03-22 15:51:24 +00:00
|
|
|
self.CredentialsTable = Table("credentials", self.metadata, autoload_with=self.db_engine)
|
|
|
|
self.HostsTable = Table("hosts", self.metadata, autoload_with=self.db_engine)
|
|
|
|
except NoInspectionAvailable:
|
|
|
|
print(
|
|
|
|
"[-] Error reflecting tables - this means there is a DB schema mismatch \n"
|
|
|
|
"[-] This is probably because a newer version of CME is being ran on an old DB schema\n"
|
2023-03-22 15:58:49 +00:00
|
|
|
"[-] If you wish to save the old DB data, copy it to a new location (`cp -r ~/.cme/workspaces/ ~/old_cme_workspaces/`)\n"
|
|
|
|
"[-] Then remove the CME DB folders (`rm -rf ~/.cme/workspaces/`) and rerun CME to initialize the new DB schema"
|
2023-03-22 15:51:24 +00:00
|
|
|
)
|
|
|
|
exit()
|
2023-03-09 21:50:32 +00:00
|
|
|
|
2023-03-04 16:12:29 +00:00
|
|
|
def clear_database(self):
|
2023-03-09 21:50:32 +00:00
|
|
|
for table in self.metadata.sorted_tables:
|
|
|
|
asyncio.run(self.conn.execute(table.delete()))
|