Files
rocm-systems/src/utils/db_connector.py
T

242 lines
9.3 KiB
Python
Raw Normal View History

2024-01-30 10:43:30 -06:00
##############################################################################bl
# MIT License
#
2025-01-23 13:09:32 -06:00
# Copyright (c) 2021 - 2025 Advanced Micro Devices, Inc. All Rights Reserved.
2024-01-30 10:43:30 -06:00
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
##############################################################################el
2025-01-02 13:29:47 -08:00
import getpass
import os
2024-01-30 10:43:30 -06:00
from abc import ABC, abstractmethod
2025-01-02 13:29:47 -08:00
from pathlib import Path
import pandas as pd
from pymongo import MongoClient
from tqdm import tqdm
from utils.kernel_name_shortener import kernel_name_shortener
from utils.logger import (
2025-01-02 13:29:47 -08:00
console_debug,
2024-03-04 12:57:25 -06:00
console_error,
console_log,
console_warning,
2025-01-02 13:29:47 -08:00
demarcate,
2024-03-04 12:57:25 -06:00
)
from utils.utils import is_workload_empty
2024-01-30 10:43:30 -06:00
MAX_SERVER_SEL_DELAY = 5000 # 5 sec connection timeout
2024-02-16 15:34:28 -06:00
2024-01-30 10:43:30 -06:00
class DatabaseConnector:
def __init__(self, args):
self.args = args
self.cache = dict()
self.connection_info = {
"username": self.args.username,
"password": self.args.password,
"host": self.args.host,
"port": str(self.args.port),
"team": self.args.team,
"workload": self.args.workload,
2024-02-16 15:34:28 -06:00
"db": None,
2024-01-30 10:43:30 -06:00
}
2024-02-16 15:34:28 -06:00
self.interaction_type: str = (
None # set to 'import' or 'remove' based on user arguments
)
2024-01-30 10:43:30 -06:00
self.client: MongoClient = None
2024-02-16 15:34:28 -06:00
2024-01-30 10:43:30 -06:00
@demarcate
def prep_import(self):
2024-01-30 10:43:30 -06:00
# Extract SoC and workload name from sysinfo.csv
2025-01-02 13:29:47 -08:00
sys_info = str(Path(self.connection_info["workload"]).joinpath("sysinfo.csv"))
if Path(sys_info).is_file():
2024-01-30 10:43:30 -06:00
sys_info = pd.read_csv(sys_info)
2024-03-12 12:24:49 -05:00
try:
2024-03-12 15:54:52 -05:00
soc = sys_info["gpu_model"][0].strip()
name = sys_info["workload_name"][0].strip()
2024-03-12 12:24:49 -05:00
except KeyError as e:
console_error(
f"Outdated workload. Cannot find {e} field. Please reprofile to update."
)
2024-01-30 10:43:30 -06:00
else:
2024-03-04 12:57:25 -06:00
console_error(
2024-03-11 11:24:17 -05:00
"database", "Unable to parse SoC and/or workload name from sysinfo.csv"
2024-03-04 12:57:25 -06:00
)
2024-01-30 10:43:30 -06:00
2024-02-16 15:34:28 -06:00
self.connection_info["db"] = (
"rocprofiler-compute_"
+ str(self.args.team)
+ "_"
+ str(name)
+ "_"
+ str(soc)
2024-02-16 15:34:28 -06:00
)
2024-01-30 10:43:30 -06:00
@demarcate
def db_import(self):
self.prep_import()
i = 0
file = "blank"
for file in tqdm(os.listdir(self.connection_info["workload"])):
if file.endswith(".csv"):
2024-01-30 17:25:16 -06:00
console_log(
"database",
2024-03-04 12:57:25 -06:00
"Uploading: %s" % self.connection_info["workload"] + "/" + file,
2024-02-16 15:34:28 -06:00
)
2024-01-30 10:43:30 -06:00
try:
fileName = file[0 : file.find(".")]
2024-03-12 15:54:52 -05:00
data = pd.read_csv(self.connection_info["workload"] + "/" + file)
# Demangle original KernelNames
kernel_name_shortener(data, self.args.kernel_verbose)
data.reset_index(inplace=True)
data_dict = data.to_dict("records")
client = MongoClient(
"mongodb://{}:{}@{}:{}/{}?authSource=admin".format(
self.connection_info["username"],
self.connection_info["password"],
self.connection_info["host"],
self.connection_info["port"],
self.connection_info["db"],
)
2024-01-30 10:43:30 -06:00
)
2024-03-12 15:54:52 -05:00
db = client[self.connection_info["db"]]
collection = db[fileName]
collection.insert_many(data_dict)
2024-01-30 10:43:30 -06:00
i += 1
except pd.errors.EmptyDataError:
2024-03-11 11:25:15 -05:00
console_warning("database", "Skipping empty file: %s" % file)
2024-01-30 10:43:30 -06:00
2024-03-04 12:57:25 -06:00
console_log("database", "%s collections successfully added." % i)
2024-01-30 10:43:30 -06:00
mydb = self.client["workload_names"]
mycol = mydb["names"]
value = {"name": self.connection_info["db"]}
newValue = {"name": self.connection_info["db"]}
mycol.replace_one(value, newValue, upsert=True)
2024-03-04 12:57:25 -06:00
console_log("database", "Workload name uploaded.")
2024-01-30 10:43:30 -06:00
@demarcate
def db_remove(self):
2024-02-16 15:34:28 -06:00
db_to_remove = self.client[self.connection_info["workload"]]
2024-01-30 10:43:30 -06:00
# check the collection names on the database
col_list = db_to_remove.list_collection_names()
self.client.drop_database(db_to_remove)
db = self.client["workload_names"]
col = db["names"]
2024-03-04 12:57:25 -06:00
col.delete_many({"name": self.connection_info["workload"]})
2024-01-30 10:43:30 -06:00
2024-01-30 17:25:16 -06:00
console_log(
2024-03-04 12:57:25 -06:00
"database", "Successfully removed %s" % self.connection_info["workload"]
2024-02-16 15:34:28 -06:00
)
2024-01-30 10:43:30 -06:00
@abstractmethod
def pre_processing(self):
2024-03-04 12:57:25 -06:00
"""Perform any pre-processing steps prior to database conncetion."""
console_debug("database", "pre-processing database connection")
2024-01-30 10:43:30 -06:00
if not self.args.remove and not self.args.upload:
2024-03-04 12:57:25 -06:00
console_error(
"Either -i/--import or -r/--remove is required in database mode"
)
self.interaction_type = "import" if self.args.upload else "remove"
2024-01-30 10:43:30 -06:00
# Detect interaction type
2024-03-04 12:57:25 -06:00
if self.interaction_type == "remove":
console_debug("database", "validating arguments for --remove workflow")
2024-01-30 10:43:30 -06:00
is_full_workload_name = self.args.workload.count("_") >= 3
if not is_full_workload_name:
2024-03-04 12:57:25 -06:00
console_error(
"-w/--workload is not valid. Please use full workload name as seen in GUI when removing (i.e. rocprofiler-compute_asw_vcopy_mi200)"
2024-03-04 12:57:25 -06:00
)
if (
self.connection_info["host"] == None
or self.connection_info["username"] == None
):
console_error(
"-H/--host and -u/--username are required when interaction type is set to %s"
% self.interaction_type
)
if (
self.connection_info["workload"] == "admin"
or self.connection_info["workload"] == "local"
):
console_error(
"Cannot remove %s. Try again." % self.connection_info["workload"]
)
2024-01-30 10:43:30 -06:00
else:
2024-03-04 12:57:25 -06:00
console_debug("database", "validating arguments for --import workflow")
2024-01-30 10:43:30 -06:00
if (
2024-02-16 15:34:28 -06:00
self.connection_info["host"] == None
or self.connection_info["team"] == None
or self.connection_info["username"] == None
or self.connection_info["workload"] == None
2024-01-30 10:43:30 -06:00
):
2024-03-04 12:57:25 -06:00
console_error(
"-H/--host, -w/--workload, -u/--username, and -t/--team are all required when interaction type is set to %s"
% self.interaction_type
)
2024-01-30 10:43:30 -06:00
2025-01-02 13:29:47 -08:00
if Path(self.connection_info["workload"]).absolute().is_dir():
2024-02-16 15:34:28 -06:00
is_workload_empty(self.connection_info["workload"])
2024-01-30 10:43:30 -06:00
else:
2024-03-04 12:57:25 -06:00
console_error(
"--workload is invalid. Please pass path to a valid directory."
)
2024-01-30 10:43:30 -06:00
if len(self.args.team) > 13:
2024-01-30 17:25:16 -06:00
console_error("--team exceeds 13 character limit. Try again.")
2024-03-04 12:57:25 -06:00
2024-01-30 10:43:30 -06:00
# format path properly
2025-01-02 13:29:47 -08:00
self.connection_info["workload"] = str(
Path(self.connection_info["workload"]).absolute().resolve()
2024-02-16 15:34:28 -06:00
)
2024-01-30 10:43:30 -06:00
# Detect password
2024-02-16 15:34:28 -06:00
if self.connection_info["password"] == "":
2024-01-30 10:43:30 -06:00
try:
2024-02-16 15:34:28 -06:00
self.connection_info["password"] = getpass.getpass()
2024-01-30 10:43:30 -06:00
except Exception as e:
2024-03-04 12:57:25 -06:00
console_error("database", "PASSWORD ERROR %s" % e)
2024-01-30 10:43:30 -06:00
else:
console_log("database", "Password received")
2024-01-30 10:43:30 -06:00
else:
2024-02-16 15:34:28 -06:00
password = self.connection_info["password"]
2024-01-30 10:43:30 -06:00
# Establish client connection
connection_str = (
"mongodb://"
+ self.connection_info["username"]
+ ":"
+ self.connection_info["password"]
+ "@"
+ self.connection_info["host"]
+ ":"
+ self.connection_info["port"]
+ "/?authSource=admin"
)
2024-02-16 15:34:28 -06:00
self.client = MongoClient(
connection_str, serverSelectionTimeoutMS=MAX_SERVER_SEL_DELAY
)
2024-01-30 10:43:30 -06:00
try:
self.client.server_info()
except:
2024-03-04 12:57:25 -06:00
console_error("database", "Unable to connect to the DB server.")