Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add self-commands request to server #5

Merged
merged 8 commits into from
Oct 8, 2024
Merged
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 100 additions & 32 deletions src/bt_auto_dumper/_v2/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,22 @@ def main(apiver: str | None = None):
apiver = apiver or pathlib.Path(__file__).parent.name
parser = argparse.ArgumentParser(description=f"BT Auto Dumper CLI {apiver}")
parser.add_argument("--note", help="Comment or note for the operation", type=str, default="")
parser.add_argument("subnet_identifier", help="Subnet Identifier", type=str)
parser.add_argument("autovalidator_address", help="AutoValidator Address", type=str)
parser.add_argument("subnet_identifier", help="Subnet Identifier", type=str, default="")
parser.add_argument("autovalidator_address", help="AutoValidator Address", type=str, default="")
parser.add_argument(
"subnet_realm",
help="Subnet Realm",
type=str,
choices=["testnet", "mainnet", "devnet"],
default="mainnet",
)
parser.add_argument("--set-autovalidator-address", help="Set a new autovalidator address", type=str)
parser.add_argument("--set-codename", help="Set a new Subnet Identifier codename", type=str)
parser.add_argument("--set-autovalidator-address", help="Set a new autovalidator address", type=str, default="")
parser.add_argument("--set-codename", help="Set a new Subnet Identifier codename", type=str, default="")
drunest marked this conversation as resolved.
Show resolved Hide resolved

args = parser.parse_args()

# Get configuration directory from env variable.
config_base_dir = os.getenv("CONFIG_DIR")
config_base_dir = os.getenv("CONFIG_DIR", default="")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
config_base_dir = os.getenv("CONFIG_DIR", default="")
config_base_dir = os.getenv("CONFIG_DIR", default=`~/.config/bt-auto-dumper`)


# Check if the CONFIG_DIR environment variable is set
if not config_base_dir:
Expand All @@ -57,49 +57,50 @@ def main(apiver: str | None = None):

if not (subnet_identifier := args.subnet_identifier) or not (autovalidator_address := args.autovalidator_address):
autovalidator_address, subnet_identifier = load_config(config_path=config_path)
dump_and_upload(subnet_identifier, args.subnet_realm, autovalidator_address, args.note)

wallet = bt.wallet(name="validator", hotkey="validator-hotkey", path="~/.bittensor/wallets")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these should all be taken from the config, but should have defaults

normalized_subnet_identifier = get_normalized_codename_from_server(
subnet_identifier, args.subnet_realm, wallet, autovalidator_address
)
if not normalized_subnet_identifier:
raise ValueError("Failed to normalize the codename.")
dump_and_upload(normalized_subnet_identifier, args.subnet_realm, wallet, autovalidator_address, args.note)
drunest marked this conversation as resolved.
Show resolved Hide resolved


def dump_and_upload(subnet_identifier: str, subnet_realm: str, autovalidator_address: str, note: str):
def dump_and_upload(
subnet_identifier: str, subnet_realm: str, wallet: bt.wallet, autovalidator_address: str, note: str
):
"""
Dump and upload the output of the commands to the AutoValidator
Args:
subnet_identifier: Subnet Identifier
subnet_realm: Subnet Realm
wallet: Bittensor wallet object
autovalidator_address: AutoValidator Address
note: Comment or note for the operation
Example:
dump_and_upload("computehorde", "mainnet", "http://localhost:8000", "Test")
"""
subnets = {
"computehorde": ["echo 'Mainnet Command 1'", "echo 'Mainnet Command 2'"],
"omron": ["echo 'Mainnet Command 1'", "echo 'Mainnet Command 2'"],
}

wallet = bt.wallet(name="validator", hotkey="validator-hotkey")
normalized_subnet_identifier = re.sub(r"[_\-.]", "", str.lower(subnet_identifier))
commands = {}
if normalized_subnet_identifier in subnets:
commands = {normalized_subnet_identifier: subnets[normalized_subnet_identifier]}
commands = get_commands_from_server(subnet_identifier, subnet_realm, wallet, autovalidator_address)

if not commands:
print(f"Subnet identifier {subnet_identifier} not found.")
return
output_files = []
for subnet_id, cmds in commands.items():
for i, command in enumerate(cmds, start=1):
output_file = f"{subnet_id}_{i}.txt"
with open(output_file, "w") as f:
f.write(f"Command: {command}\n")
result = subprocess.run(command, shell=True, capture_output=True, text=True)
f.write(result.stdout)
output_files.append(output_file)

zip_filename = f"{normalized_subnet_identifier}-output.zip"
for i, command in enumerate(commands, start=1):
output_file = f"{subnet_identifier}_{i}.txt"
with open(output_file, "w") as f:
f.write(f"Command: {command}\n")
result = subprocess.run(command, shell=True, capture_output=True, text=True)
f.write(result.stdout)
output_files.append(output_file)

zip_filename = f"{subnet_identifier}-output.zip"
with zipfile.ZipFile(zip_filename, "w") as zipf:
for file in output_files:
zipf.write(file)
send_to_autovalidator(zip_filename, wallet, autovalidator_address, note, normalized_subnet_identifier, subnet_realm)
send_to_autovalidator(zip_filename, wallet, autovalidator_address, note, subnet_identifier, subnet_realm)


def make_signed_request(
Expand Down Expand Up @@ -127,12 +128,15 @@ def make_signed_request(
headers["Nonce"] = str(time.time())
headers["Hotkey"] = wallet.hotkey.ss58_address
headers["Realm"] = subnet_realm
files = {"file": open(file_path, "rb")}
file = files.get("file")
file_content = b""
if isinstance(file, BufferedReader):
file_content = file.read()
file.seek(0)
files = None
if file_path:
files = {"file": open(file_path, "rb")}
file = files.get("file")

if isinstance(file, BufferedReader):
file_content = file.read()
file.seek(0)
headers_str = json.dumps(headers, sort_keys=True)
data_to_sign = f"{method}{url}{headers_str}{file_content.decode(errors='ignore')}".encode()
signature = wallet.hotkey.sign(
Expand Down Expand Up @@ -250,5 +254,69 @@ def update_confg(config_path: str, new_autovalidator_address: str, new_codename:
raise RuntimeError(f"Failed to write to the configuration file: {config_path}.\n Error: {e}")


def get_commands_from_server(
subnet_identifier: str, subnet_realm: str, wallet: bt.wallet, autovalidator_address: str
) -> list:
"""
Request commands to the server
drunest marked this conversation as resolved.
Show resolved Hide resolved
Args:
subnet_identifier: Subnet Identifier
subnet_realm: Subnet Realm
wallet: Bittensor wallet object
autovalidator_address: AutoValidator Address
Returns:
list: List of commands
Example:
request_commands_to_server("computehorde", "mainnet", wallet, "http://localhost:8000")
"""
url = f"{autovalidator_address}/api/v1/commands/"
headers = {
"Note": "",
"SubnetID": subnet_identifier,
}
response = make_signed_request("GET", url, headers, "", wallet, subnet_realm)
if response.status_code == 200:
data = response.json()
print(data)
print("Commands successfully retrieved.")
return data
else:
print(f"Failed to get commands. Status code: {response.status_code}")
print(response.text)
return []


def get_normalized_codename_from_server(
subnet_identifier: str, subnet_realm: str, wallet: bt.wallet, autovalidator_address: str
) -> str:
"""
Get the codename from the server
drunest marked this conversation as resolved.
Show resolved Hide resolved
Args:
subnet_identifier: Subnet Identifier
subnet_realm: Subnet Realm
wallet: Bittensor wallet object
autovalidator_address: AutoValidator Address
Returns:
str: Codename
drunest marked this conversation as resolved.
Show resolved Hide resolved
Example:
get_codename_from_server("12", "mainnet", wallet, "http://localhost:8000")
drunest marked this conversation as resolved.
Show resolved Hide resolved
"""
filtered_codename = re.sub(r"[_\-.]", "", str.lower(subnet_identifier))
url = f"{autovalidator_address}/api/v1/codename/"
headers = {
"Note": "",
"SubnetID": filtered_codename,
}
response = make_signed_request("GET", url, headers, "", wallet, subnet_realm)
if response.status_code == 200:
data = response.json()
print(f"Successfully retrieved codename map for {filtered_codename}.")
drunest marked this conversation as resolved.
Show resolved Hide resolved
return data
else:
print(f"Failed to get codename map. Status code: {response.status_code}")
print(response.text)
return ""
drunest marked this conversation as resolved.
Show resolved Hide resolved


if __name__ == "__main__":
main()