Add files from GitHub.
This commit is contained in:
12
load_balancer/boinc/boinc_components/boinc_application.py
Normal file
12
load_balancer/boinc/boinc_components/boinc_application.py
Normal file
@@ -0,0 +1,12 @@
|
||||
# ======== Applications ========
|
||||
# 1) -----------
|
||||
# name: Theory
|
||||
# Project: LHC@home
|
||||
# 2) -----------
|
||||
# name: sixtrack
|
||||
# Project: LHC@home
|
||||
|
||||
class BoincApplication:
|
||||
def __init__(self, name, project):
|
||||
self.name = name
|
||||
self.project = project
|
||||
@@ -0,0 +1,35 @@
|
||||
# ======== Application versions ========
|
||||
# 1) -----------
|
||||
# project: LHC@home
|
||||
# application: Theory
|
||||
# platform: x86_64-pc-linux-gnu
|
||||
# plan class: native_theory
|
||||
# version: 300.08
|
||||
# estimated GFLOPS: 0.08
|
||||
# filename: wrapper_2019_03_02_x86_64-linux
|
||||
# 2) -----------
|
||||
# project: LHC@home
|
||||
# application: sixtrack
|
||||
# platform: x86_64-pc-linux-gnu
|
||||
# plan class: avx
|
||||
# version: 502.05
|
||||
# estimated GFLOPS: 1.28
|
||||
# filename: sixtrack_lin64_50205_avx.linux
|
||||
# 3) -----------
|
||||
# project: LHC@home
|
||||
# application: sixtrack
|
||||
# platform: x86_64-pc-linux-gnu
|
||||
# plan class: sse2
|
||||
# version: 502.05
|
||||
# estimated GFLOPS: 1.44
|
||||
# filename: sixtrack_lin64_50205_sse2.linux
|
||||
|
||||
class BoincApplicationVersion:
|
||||
def __init__(self, project, application, platform, plan_class, version, estimated, filename):
|
||||
self.project = project # string
|
||||
self.application = application # string
|
||||
self.platform = platform # string
|
||||
self.plan_class = plan_class # string
|
||||
self.version = version # string
|
||||
self.estimated = estimated # string
|
||||
self.filename = filename # string
|
||||
56
load_balancer/boinc/boinc_components/boinc_commands.py
Normal file
56
load_balancer/boinc/boinc_components/boinc_commands.py
Normal file
@@ -0,0 +1,56 @@
|
||||
# Commands:
|
||||
|
||||
# --acct_mgr attach URL name passwd attach to account manager
|
||||
ACCOUNT_MANAGER_INFO = "--acct_mgr info" # show current account manager info
|
||||
ACCOUNT_MANAGER_SYNCHRONIZE = "--acct_mgr sync" # synchronize with acct mgr
|
||||
ACCOUNT_MANAGER_DETACH = "--acct_mgr detach" # detach from acct mgr
|
||||
|
||||
CLIENT_VERSION = "--client_version" # show client version
|
||||
|
||||
# --create_account URL email passwd name
|
||||
|
||||
# --file_transfer URL filename op file transfer operation
|
||||
# op = retry | abort
|
||||
|
||||
# --get_app_config URL show app config for given project
|
||||
GET_CC_STATUS = "--get_cc_status"
|
||||
GET_NETWORK_TRAFFIC_HISTORY = "--get_daily_xfer_history" # show network traffic history
|
||||
GET_DISK_USAGE = "--get_disk_usage" # show disk usage
|
||||
GET_FILE_TRANSFER = "--get_file_transfers" # show file transfers
|
||||
GET_HOST_INFO = "--get_host_info"
|
||||
GET_MESSAGE_COUNT = "--get_message_count" # show largest message seqno
|
||||
# --get_messages [ seqno ] show messages > seqno
|
||||
# --get_notices [ seqno ] show notices > seqno
|
||||
# --get_project_config URL
|
||||
GET_PROJECTS_STATUS = "--get_project_status" # show status of all attached projects
|
||||
GET_PROXY_SETTINGS = "--get_proxy_settings"
|
||||
GET_SIMPLE_GUI_INFO = "--get_simple_gui_info" # show status of projects and active tasks
|
||||
GET_STATE = "--get_state" # show entire state
|
||||
GET_TASKS = "--get_tasks" # show tasks
|
||||
# --get_old_tasks show reported tasks from last 1 hour
|
||||
# --join_acct_mgr URL name passwd same as --acct_mgr attach
|
||||
# --lookup_account URL email passwd
|
||||
# --network_available retry deferred network communication
|
||||
# --project URL op project operation
|
||||
# op = reset | detach | update | suspend | resume | nomorework | allowmorework | detach_when_done | dont_detach_when_done
|
||||
# --project_attach URL auth attach to project
|
||||
# --quit tell client to exit
|
||||
# --quit_acct_mgr same as --acct_mgr detach
|
||||
# --read_cc_config
|
||||
# --read_global_prefs_override
|
||||
# --reset_host_info have client get mem size, #CPUs etc. again
|
||||
# --run_benchmarks
|
||||
# --run_graphics_app id op (Macintosh only) run, test or stop graphics app
|
||||
# op = run | runfullscreen | stop | test
|
||||
# id = slot # for run or runfullscreen, process ID for stop or test
|
||||
# id = -1 for default screensaver (boincscr)
|
||||
# --set_gpu_mode mode duration set GPU run mode for given duration
|
||||
# mode = always | auto | never
|
||||
# --set_host_info product_name
|
||||
# --set_network_mode mode duration set network mode for given duration
|
||||
# mode = always | auto | never
|
||||
# --set_proxy_settings
|
||||
# --set_run_mode mode duration set run mode for given duration
|
||||
# mode = always | auto | never
|
||||
# --task url task_name op task operation
|
||||
# op = suspend | resume | abort
|
||||
18
load_balancer/boinc/boinc_components/boinc_host.py
Normal file
18
load_balancer/boinc/boinc_components/boinc_host.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from boinc_components.boinc_project import BoincProject
|
||||
from boinc_components.boinc_project_gui_url import BoincProjectGuiUrl
|
||||
from boinc_components.boinc_application import BoincApplication
|
||||
from boinc_components.boinc_application_version import BoincApplicationVersion
|
||||
from boinc_components.boinc_workunit import BoincWorkunit
|
||||
from boinc_components.boinc_task import BoincTask
|
||||
from boinc_components.boinc_time_stats import BoincTimeStats
|
||||
|
||||
class BoincHost:
|
||||
def __init__(self, address, password, projects=None, applications=None, application_versions=None, workunits=None, tasks=None, time_stats=None):
|
||||
self.address = address # string
|
||||
self.password = password # string
|
||||
self.projects = projects # list of BoincProject
|
||||
self.applications = applications # list of BoincApplication
|
||||
self.application_versions = application_versions # list of BoincApplicationVersion
|
||||
self.workunits = workunits # list of BoincWorkunit
|
||||
self.tasks = tasks # list of BoincTask
|
||||
self.time_stats = time_stats # TimeStats
|
||||
74
load_balancer/boinc/boinc_components/boinc_project.py
Normal file
74
load_balancer/boinc/boinc_components/boinc_project.py
Normal file
@@ -0,0 +1,74 @@
|
||||
# ======== Projects ========
|
||||
# 1) -----------
|
||||
# name: LHC@home
|
||||
# master URL: https://lhcathome.cern.ch/lhcathome/
|
||||
# user_name: WickedJack
|
||||
# team_name:
|
||||
# resource share: 100.000000
|
||||
# user_total_credit: 412.990429
|
||||
# user_expavg_credit: 22.766969
|
||||
# host_total_credit: 412.990429
|
||||
# host_expavg_credit: 25.140667
|
||||
# nrpc_failures: 0
|
||||
# master_fetch_failures: 0
|
||||
# master fetch pending: no
|
||||
# scheduler RPC pending: no
|
||||
# trickle upload pending: no
|
||||
# attached via Account Manager: no
|
||||
# ended: no
|
||||
# suspended via GUI: no
|
||||
# don't request more work: no
|
||||
# disk usage: 42.67MB
|
||||
# last RPC: Tue Apr 23 11:23:53 2024
|
||||
# project files downloaded: 0.000000
|
||||
|
||||
from boinc_components.boinc_project_gui_url import BoincProjectGuiUrl
|
||||
|
||||
class BoincProject:
|
||||
def __init__(
|
||||
self,
|
||||
name,
|
||||
master_url,
|
||||
user_name,
|
||||
team_name,
|
||||
resource_share,
|
||||
user_total_credit,
|
||||
user_exponential_average_credit,
|
||||
host_total_credit,
|
||||
host_exponential_average_credit,
|
||||
nrpc_failures,
|
||||
master_fetch_failures,
|
||||
master_fetch_pending,
|
||||
scheduler_rpc_pending,
|
||||
trickle_upload_pending,
|
||||
attached_via_account_manager,
|
||||
ended,
|
||||
suspended_via_gui,
|
||||
dont_request_more_work,
|
||||
disk_usage,
|
||||
last_rpc,
|
||||
project_files_downloaded,
|
||||
gui_urls
|
||||
):
|
||||
self.name = name
|
||||
self.master_url = master_url
|
||||
self.user_name = user_name
|
||||
self.team_name = team_name
|
||||
self.resource_share = resource_share
|
||||
self.user_total_credit = user_total_credit
|
||||
self.user_exponential_average_credit = user_exponential_average_credit
|
||||
self.host_total_credit = host_total_credit
|
||||
self.host_exponential_average_credit = host_exponential_average_credit
|
||||
self.nrpc_failures = nrpc_failures
|
||||
self.master_fetch_failures = master_fetch_failures
|
||||
self.master_fetch_pending = master_fetch_pending
|
||||
self.scheduler_rpc_pending = scheduler_rpc_pending
|
||||
self.trickle_upload_pending = trickle_upload_pending
|
||||
self.attached_via_account_manager = attached_via_account_manager
|
||||
self.ended = ended
|
||||
self.suspended_via_gui = suspended_via_gui
|
||||
self.dont_request_more_work = dont_request_more_work
|
||||
self.disk_usage = disk_usage
|
||||
self.last_rpc = last_rpc
|
||||
self.project_files_downloaded = project_files_downloaded
|
||||
self.gui_urls = gui_urls
|
||||
@@ -0,0 +1,30 @@
|
||||
# GUI URL:
|
||||
# name: Message boards
|
||||
# description: Correspond with other users on the LHC@home message boards
|
||||
# URL: https://lhcathome.cern.ch/lhcathome/forum_index.php
|
||||
# GUI URL:
|
||||
# name: Your account
|
||||
# description: View your account information
|
||||
# URL: https://lhcathome.cern.ch/lhcathome/home.php
|
||||
# GUI URL:
|
||||
# name: Your tasks
|
||||
# description: View the last week or so of computational work
|
||||
# URL: https://lhcathome.cern.ch/lhcathome/results.php?userid=1062754
|
||||
# GUI URL:
|
||||
# name: FAQ
|
||||
# description: Frequently Asked Questions on LHC@home
|
||||
# URL: http://lhcathome.web.cern.ch/faq
|
||||
# jobs succeeded: 39
|
||||
# jobs failed: 492
|
||||
# elapsed time: 430959.162318
|
||||
# cross-project ID: 773913b4ca09b7fae8533c9c9c859d07
|
||||
|
||||
class BoincProjectGuiUrl:
|
||||
def __init__(self, name, description, url, jobs_succeeded, jobs_failed, elapsed_time, cross_project_id):
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.url = url
|
||||
self.jobs_succeeded = jobs_succeeded
|
||||
self.jobs_failed = jobs_failed
|
||||
self.elapsed_time = elapsed_time
|
||||
self.cross_project_id = cross_project_id
|
||||
32
load_balancer/boinc/boinc_components/boinc_task.py
Normal file
32
load_balancer/boinc/boinc_components/boinc_task.py
Normal file
@@ -0,0 +1,32 @@
|
||||
# ======== Tasks ========
|
||||
|
||||
# TODO find out if this information one task has is up to date.
|
||||
|
||||
class BoincTask:
|
||||
def __init__(
|
||||
self,
|
||||
name,
|
||||
workunit_name,
|
||||
project_url,
|
||||
received,
|
||||
report_deadline,
|
||||
ready_to_report,
|
||||
state,
|
||||
scheduler_state,
|
||||
active_task_state,
|
||||
app_version_num,
|
||||
resources,
|
||||
estimated_cpu_time_remaining
|
||||
):
|
||||
self.name = name
|
||||
self.workunit_name = workunit_name
|
||||
self.project_url = project_url
|
||||
self.received = received
|
||||
self.report_deadline = report_deadline
|
||||
self.ready_to_report = ready_to_report
|
||||
self.state = state
|
||||
self.scheduler_state = scheduler_state
|
||||
self.active_task_state = active_task_state
|
||||
self.app_version_num = app_version_num
|
||||
self.resources = resources
|
||||
self.estimated_cpu_time_remaining = estimated_cpu_time_remaining
|
||||
50
load_balancer/boinc/boinc_components/boinc_time_stats.py
Normal file
50
load_balancer/boinc/boinc_components/boinc_time_stats.py
Normal file
@@ -0,0 +1,50 @@
|
||||
# ======== Time stats ========
|
||||
# now: 1713866392.601657
|
||||
# on_frac: 0.999491
|
||||
# connected_frac: -1.000000
|
||||
# cpu_and_network_available_frac: 0.999992
|
||||
# active_frac: 0.999992
|
||||
# gpu_active_frac: 0.999992
|
||||
# client_start_time: Tue Apr 16 14:45:29 2024
|
||||
|
||||
# previous_uptime: 3884.003729
|
||||
# session_active_duration: 594842.232585
|
||||
# session_gpu_active_duration: 594842.232585
|
||||
# total_start_time: Mon Apr 15 22:52:40 2024
|
||||
|
||||
# total_duration: 651143.128246
|
||||
# total_active_duration: 651133.088290
|
||||
# total_gpu_active_duration: 651133.088290
|
||||
|
||||
class BoincTimeStats:
|
||||
def __init__(
|
||||
self,
|
||||
now,
|
||||
on_frac,
|
||||
connected_frac,
|
||||
cpu_and_network_available_frac,
|
||||
active_frac,
|
||||
gpu_active_frac,
|
||||
client_start_time,
|
||||
previous_uptime,
|
||||
session_active_duration,
|
||||
session_gpu_active_duration,
|
||||
total_start_time,
|
||||
total_duration,
|
||||
total_active_duration,
|
||||
total_gpu_active_duration
|
||||
):
|
||||
self.now = now
|
||||
self.on_frac = on_frac
|
||||
self.connected_frac = connected_frac
|
||||
self.cpu_and_network_available_frac = cpu_and_network_available_frac
|
||||
self.active_frac = active_frac
|
||||
self.gpu_active_frac = gpu_active_frac
|
||||
self.client_start_time = client_start_time
|
||||
self.previous_uptime = previous_uptime
|
||||
self.session_active_duration = session_active_duration
|
||||
self.session_gpu_active_duration = session_gpu_active_duration
|
||||
self.total_start_time = total_start_time
|
||||
self.total_duration = total_duration
|
||||
self.total_active_duration = total_active_duration
|
||||
self.total_gpu_active_duration = total_gpu_active_duration
|
||||
6
load_balancer/boinc/boinc_components/boinc_workunit.py
Normal file
6
load_balancer/boinc/boinc_components/boinc_workunit.py
Normal file
@@ -0,0 +1,6 @@
|
||||
# ======== Workunits ========
|
||||
|
||||
class BoincWorkunit:
|
||||
def __init__(self):
|
||||
self
|
||||
# TODO find out which information is contained in a workunit
|
||||
26
load_balancer/boinc/boinc_output_parser.py
Normal file
26
load_balancer/boinc/boinc_output_parser.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from boinc_components.boinc_project import BoincProject
|
||||
from boinc_components.boinc_project_gui_url import BoincProjectGuiUrl
|
||||
from boinc_components.boinc_application import BoincApplication
|
||||
from boinc_components.boinc_application_version import BoincApplicationVersion
|
||||
from boinc_components.boinc_workunit import BoincWorkunit
|
||||
from boinc_components.boinc_task import BoincTask
|
||||
from boinc_components.boinc_time_stats import BoincTimeStats
|
||||
from boinc_components.boinc_host import BoincHost
|
||||
|
||||
# TODO parse data from received output to host object
|
||||
# Create subfunctions for each param of BoincHost except address and password
|
||||
def parseOutputToHost(output, old_host):
|
||||
address = old_host.address
|
||||
password = old_host.password
|
||||
|
||||
projects = []
|
||||
applications = []
|
||||
application_versions = []
|
||||
workunits = []
|
||||
tasks = []
|
||||
time_stats = parseOutputToBoincTimeStats(output)
|
||||
|
||||
return BoincHost(address, password, projects, applications, application_versions, workunits, tasks, time_stats)
|
||||
|
||||
def parseOutputToBoincTimeStats(output):
|
||||
return BoincTimeStats()
|
||||
15
load_balancer/boinc/heater_instance.py
Normal file
15
load_balancer/boinc/heater_instance.py
Normal file
@@ -0,0 +1,15 @@
|
||||
import subprocess
|
||||
|
||||
class HeaterInstance:
|
||||
def __init__(self, host):
|
||||
self.host = host
|
||||
|
||||
def execute_command_and_return_output(self, command):
|
||||
# Define the command to execute
|
||||
command = "boinccmd --host " + self.host.address + " --passwd '" + self.host.password + "' " + command
|
||||
# Execute the command and capture the output
|
||||
output = subprocess.check_output(command, shell=True)
|
||||
# Decode the output if needed (for Python 3)
|
||||
output = output.decode("utf-8")
|
||||
# Print or process the output
|
||||
return output
|
||||
20
load_balancer/boinc/load_balancer_a5.py
Normal file
20
load_balancer/boinc/load_balancer_a5.py
Normal file
@@ -0,0 +1,20 @@
|
||||
import boinc_components.boinc_commands as BOINC_COMMAND
|
||||
|
||||
from load_balancer.boinc.heater_instance import HeaterInstance
|
||||
from boinc_components.boinc_host import BoincHost
|
||||
|
||||
def main():
|
||||
print("-------------------------------")
|
||||
print("Load Balancer has been started.")
|
||||
print("-------------------------------")
|
||||
|
||||
address = "494216f3-d1df-4ec0-ae1c-51f6643adc4a.fr.bw-cloud-instance.org"
|
||||
password = "&!dBgncRmwnjurJXyhK>#G%PTy$H]kt0"
|
||||
host = BoincHost(address, password)
|
||||
instance = HeaterInstance(host)
|
||||
output = instance.execute_command_and_return_output(BOINC_COMMAND.GET_STATE)
|
||||
|
||||
print(output)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
62
load_balancer/constants.py
Normal file
62
load_balancer/constants.py
Normal file
@@ -0,0 +1,62 @@
|
||||
# author Aaron Moser
|
||||
|
||||
from enum import Enum
|
||||
|
||||
# Comstants
|
||||
|
||||
# 96 kWh maximale Arbeit, die von einer Instanz verrichtet werden kann
|
||||
# 4kW volle Leistungslast, welche in einer Stunde moeglich ist
|
||||
# 4kW / 100 = 0.04kW = 1 Prozent
|
||||
MAX_WORKLOAD_IN_KW:int = 4
|
||||
MAX_WORKLOAD_IN_KW_ONE_PERCENT:float = MAX_WORKLOAD_IN_KW / 100
|
||||
|
||||
# 24h/d * 60min/h * 60s/min = 86400s/d = Anzahl Sekunden am Tag
|
||||
# Ein Tag um Faktor 1000 gekuerzt = 86400s/d / 1000 = 86.4s/d
|
||||
# 86,4s/d / 1440min/d = 0.06s/min
|
||||
# Ratio how many seconds simulate one minute of a day
|
||||
DAILY_MODE_MILLISECONDS_MINUTE_RATIO:int = 60
|
||||
|
||||
MINIMUM_NUMBER_CLIENTS = 1
|
||||
MAXIMUM_NUMBER_CLIENTS = 20
|
||||
|
||||
# Error code enums
|
||||
class ConnectErrorCode(Enum):
|
||||
CONNECTED = 1
|
||||
ALREADY_CONNECTED = 2
|
||||
UNVERIFIED_KEY = 3
|
||||
AUTHENTICATION_FAILED = 4
|
||||
SOCKET_ERROR = 5
|
||||
ALL_CONNECTION_ATTEMPTS_FAILED = 6
|
||||
SSH_EXCEPTION = 7
|
||||
|
||||
class State(Enum):
|
||||
Stopped = 1
|
||||
Running = 2
|
||||
|
||||
# Commands
|
||||
START_SIMULATION_COMMAND = "startsimulation"
|
||||
STOP_SIMULATION_COMMAND = "stopsimulation"
|
||||
SET_SIMULATION_MODE_COMMAND = "setsimulationmode"
|
||||
ADD_INSTANCE_COMMAND = "addinstance"
|
||||
REMOVE_INSTANCE_COMMAND = "removeinstance"
|
||||
SHOW_INSTANCES_COMMAND = "showinstances"
|
||||
DISPLAY_OUTCOME_COMMAND = "displayoutcome"
|
||||
HELP_COMMAND = "help"
|
||||
EXIT_COMMAND = "exit"
|
||||
|
||||
SET_SIMULATION_MODE_COMMAND_REALTIME = "realtime"
|
||||
SET_SIMULATION_MODE_COMMAND_DAILY = "daily"
|
||||
|
||||
COMMANDS_HELP = [
|
||||
"StartSimulation [daily, realtime] [start date] [end date] // Starts simulation with default mode or optionally with given mode, start and end date.",
|
||||
"StopSimulation // Stops running simulation by stopping threads of instances.",
|
||||
"SetSimulationMode // 'daily' or 'realtime', sets simulation mode depending on second argument.",
|
||||
"AddInstance <hostname> <username> <ssh_key_filename> <passphrase> // Adds new instance to load balancer.",
|
||||
"RemoveInstance <hostname> <username> // Removes existing instance.",
|
||||
"ShowInstances // Lists all existsing instances and their state.",
|
||||
"DisplayOutcome // Shows global outcome which consists of: count, tries and PI",
|
||||
"Help // Shows commands and descriptions.",
|
||||
"Exit // Stops load balancer and exits application."]
|
||||
|
||||
# Messages
|
||||
START_MESSAGE = "-------------------------------\nLoad Balancer has been started."
|
||||
252
load_balancer/database_interface.py
Normal file
252
load_balancer/database_interface.py
Normal file
@@ -0,0 +1,252 @@
|
||||
# author Aaron Moser, Theresa Herr
|
||||
|
||||
import sqlite3
|
||||
import json
|
||||
import threading
|
||||
from enum import Enum
|
||||
from typing import List
|
||||
from heater.host import Host
|
||||
from heater_instance_interface import HeaterInstanceInterface
|
||||
from tasks.task_outcome import TaskOutcome
|
||||
|
||||
db_lock = threading.Lock()
|
||||
|
||||
class DatabaseOperation(Enum):
|
||||
GET_INSTANCE = 1
|
||||
GET_INSTANCES = 2
|
||||
UPDATE_INSTANCE = 3
|
||||
REMOVE_INSTANCE = 4
|
||||
REMOVE_ALL_INSTANCES = 5
|
||||
|
||||
class DatabaseInterface:
|
||||
def __init__(self, db_path):
|
||||
self.__db_path = db_path
|
||||
self.__init_database()
|
||||
|
||||
def access_db(self, operation: DatabaseOperation, args) -> List[Host]:
|
||||
db_lock.acquire()
|
||||
try:
|
||||
if operation == DatabaseOperation.GET_INSTANCE:
|
||||
return self.__get_instance(args)
|
||||
elif operation == DatabaseOperation.GET_INSTANCES:
|
||||
return self.__get_all_instances()
|
||||
elif operation == DatabaseOperation.UPDATE_INSTANCE:
|
||||
self.__update_instance(args)
|
||||
elif operation == DatabaseOperation.REMOVE_INSTANCE:
|
||||
self.__remove_instance(args)
|
||||
elif operation == DatabaseOperation.REMOVE_ALL_INSTANCES:
|
||||
self.__remove_all_instances()
|
||||
else:
|
||||
print("Unknown database operation: " + operation)
|
||||
finally:
|
||||
db_lock.release()
|
||||
|
||||
def __init_database(self):
|
||||
conn = sqlite3.connect(self.__db_path)
|
||||
self.__init_table_instances(conn)
|
||||
self.__init_table_task_outcomes(conn)
|
||||
conn.close()
|
||||
|
||||
def __init_table_instances(self, conn: sqlite3.Connection):
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''
|
||||
SELECT name FROM sqlite_master WHERE type='table' AND name='instances';
|
||||
''')
|
||||
table_exists = cursor.fetchone()
|
||||
|
||||
if table_exists:
|
||||
print("SQLiteDB table 'instances' already exists, skipped initialization of table.")
|
||||
else:
|
||||
cursor.execute('''
|
||||
CREATE TABLE instances (
|
||||
id INTEGER PRIMARY KEY,
|
||||
owner TEXT,
|
||||
calculated_demand BLOB,
|
||||
number_of_persons_in_household INTEGER,
|
||||
fulfilled_demand BLOB,
|
||||
status TEXT,
|
||||
heater_busy_methods BLOB
|
||||
)
|
||||
''')
|
||||
conn.commit()
|
||||
|
||||
def __init_table_task_outcomes(self, conn: sqlite3.Connection):
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''
|
||||
SELECT name FROM sqlite_master WHERE type='table' AND name='task_outcomes';
|
||||
''')
|
||||
table_exists = cursor.fetchone()
|
||||
|
||||
if table_exists:
|
||||
print("SQLiteDB table 'task_outcomes' already exists, skipped initialization of table.")
|
||||
else:
|
||||
cursor.execute('''
|
||||
CREATE TABLE task_outcomes (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
required_workload_in_percent REAL,
|
||||
runtime INTEGER,
|
||||
tries INTEGER,
|
||||
count INTEGER,
|
||||
diff REAL,
|
||||
diff_factor REAL,
|
||||
actual_date TEXT,
|
||||
heat_demand_per_minute REAL,
|
||||
actual_heat_generated REAL,
|
||||
hostname TEXT,
|
||||
username TEXT
|
||||
)
|
||||
''')
|
||||
conn.commit()
|
||||
|
||||
def __get_instance(self, args) -> List[Host]:
|
||||
conn = sqlite3.connect(self.__db_path)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("SELECT * FROM instances WHERE id=?", (args,))
|
||||
result = cursor.fetchone()
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return result
|
||||
|
||||
def __get_all_instances(self) -> List[Host]:
|
||||
conn = sqlite3.connect(self.__db_path)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("SELECT * FROM instances")
|
||||
result = cursor.fetchall()
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return result
|
||||
|
||||
def __update_instance(self, args):
|
||||
conn = sqlite3.connect(self.__db_path)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''
|
||||
UPDATE instances
|
||||
SET owner=?, calculated_demand=?, number_of_persons_in_household=?, fulfilled_demand=?, status=?, heater_busy_methods=?
|
||||
WHERE id=?
|
||||
''', (args['owner'], args['calculated_demand'], args['number_of_persons_in_household'], args['fulfilled_demand'], args['status'], args['heater_busy_methods'], args['id']))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
def __remove_instance(self, args):
|
||||
conn = sqlite3.connect(self.__db_path)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("DELETE FROM instances WHERE id=?", (args,))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
def __remove_all_instances(self):
|
||||
conn = sqlite3.connect(self.__db_path)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("DELETE FROM instances")
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
def _get_instances(self) -> List[HeaterInstanceInterface]:
|
||||
conn = sqlite3.connect(self.__db_path)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("SELECT * FROM instances")
|
||||
potential_instances = cursor.fetchall()
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
# Transform potential instances to objects which application can communicate with
|
||||
instance_objects = []
|
||||
for instance in potential_instances:
|
||||
id, owner, calculated_demand, number_of_persons_in_household, fulfilled_demand, status, heater_busy_methods = instance
|
||||
calculated_demand_json = json.loads(calculated_demand)
|
||||
fulfilled_demand_json = json.loads(fulfilled_demand)
|
||||
heater_busy_methods_json = json.loads(heater_busy_methods)
|
||||
host = Host(hostname=id, username=owner, ssh_key_filename="", passphrase="")
|
||||
heater_instance_interface = HeaterInstanceInterface(host)
|
||||
instance_objects.append(heater_instance_interface)
|
||||
|
||||
return instance_objects
|
||||
|
||||
def insert_task_outcome_into_instances_table(self, heater_instance_interface: HeaterInstanceInterface, outcome_list: List[TaskOutcome]):
|
||||
conn = sqlite3.connect(self.__db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
energy_demands = []
|
||||
fulfilled_demands = []
|
||||
|
||||
for outcome in outcome_list:
|
||||
energy_demands.append({
|
||||
"date": outcome.get_actual_date().strftime("%d.%m.%Y"),
|
||||
"energy_demand_in_watts": outcome.get_heat_demand_per_minute() * 60 # assuming demand per minute
|
||||
})
|
||||
fulfilled_demands.append({
|
||||
"date": outcome.get_actual_date().strftime("%d.%m.%Y"),
|
||||
"scored_energy_demand_in_watts": outcome.get_actual_heat_generated() * 60, # assuming generated heat per minute
|
||||
"busy_method_used": ["hpc"] # example method used
|
||||
})
|
||||
|
||||
calculated_demand_json = {"energy_demands": energy_demands}
|
||||
fulfilled_demand_json = {"scored_demand": fulfilled_demands}
|
||||
calculated_demand_blob = json.dumps(calculated_demand_json).encode('utf-8')
|
||||
fulfilled_demand_blob = json.dumps(fulfilled_demand_json).encode('utf-8')
|
||||
heater_busy_methods_blob = json.dumps({"busy_methods_active": ["hpc"]}).encode('utf-8')
|
||||
|
||||
cursor.execute('''
|
||||
INSERT INTO instances (
|
||||
owner, calculated_demand, number_of_persons_in_household,
|
||||
fulfilled_demand, status, heater_busy_methods
|
||||
) VALUES (?, ?, ?, ?, ?, ?)
|
||||
''', (
|
||||
heater_instance_interface.get_host().username,
|
||||
calculated_demand_blob,
|
||||
3, # assuming a default value, should be dynamic
|
||||
fulfilled_demand_blob,
|
||||
'online',
|
||||
heater_busy_methods_blob
|
||||
))
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
def insert_task_outcome(self, outcome: TaskOutcome):
|
||||
conn = sqlite3.connect(self.__db_path)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('''
|
||||
INSERT INTO task_outcomes (
|
||||
required_workload_in_percent, runtime, tries, count, diff, diff_factor, actual_date, heat_demand_per_minute, actual_heat_generated, hostname, username
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
''', (
|
||||
outcome.get_required_workload_in_percent(),
|
||||
outcome.get_runtime(),
|
||||
outcome.get_tries(),
|
||||
outcome.get_count(),
|
||||
outcome.get_diff(),
|
||||
outcome.get_diff_factor(),
|
||||
outcome.get_actual_date().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
outcome.get_heat_demand_per_minute(),
|
||||
outcome.get_actual_heat_generated(),
|
||||
outcome.get_hostname(),
|
||||
outcome.get_username()
|
||||
))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
# def insert_data(self, owner: str, calculated_demand: List[dict], number_of_persons_in_household: int, fulfilled_demand: List[dict], status: str, heater_busy_methods: List[str]):
|
||||
# conn = sqlite3.connect(self.__db_path)
|
||||
# cursor = conn.cursor()
|
||||
|
||||
# calculated_demand_blob = json.dumps({"energy_demands": calculated_demand}).encode('utf-8')
|
||||
# fulfilled_demand_blob = json.dumps({"scored_demand": fulfilled_demand}).encode('utf-8')
|
||||
# heater_busy_methods_blob = json.dumps({"busy_methods_active": heater_busy_methods}).encode('utf-8')
|
||||
|
||||
# cursor.execute('''
|
||||
# INSERT INTO instances (
|
||||
# owner, calculated_demand, number_of_persons_in_household,
|
||||
# fulfilled_demand, status, heater_busy_methods
|
||||
# ) VALUES (?, ?, ?, ?, ?, ?)
|
||||
# ''', (
|
||||
# owner,
|
||||
# calculated_demand_blob,
|
||||
# number_of_persons_in_household,
|
||||
# fulfilled_demand_blob,
|
||||
# status,
|
||||
# heater_busy_methods_blob
|
||||
# ))
|
||||
|
||||
# conn.commit()
|
||||
# conn.close()
|
||||
24
load_balancer/global_outcome.py
Normal file
24
load_balancer/global_outcome.py
Normal file
@@ -0,0 +1,24 @@
|
||||
# author Aaron Moser
|
||||
|
||||
import threading
|
||||
|
||||
class GlobalOutcome:
|
||||
|
||||
def __init__(self):
|
||||
self.tries_count_lock = threading.Lock()
|
||||
self.tries = 0
|
||||
self.count = 0
|
||||
|
||||
def update_tries_count(self, new_tries:int, new_count:int):
|
||||
with self.tries_count_lock:
|
||||
self.tries += new_tries
|
||||
self.count += new_count
|
||||
|
||||
def get_tries_count(self):
|
||||
with self.tries_count_lock:
|
||||
return self.tries, self.count
|
||||
|
||||
def reset_tries_count(self):
|
||||
with self.tries_count_lock:
|
||||
tries = 0
|
||||
count = 0
|
||||
148
load_balancer/heat_demand_simulator.py
Normal file
148
load_balancer/heat_demand_simulator.py
Normal file
@@ -0,0 +1,148 @@
|
||||
"""
|
||||
HEATING DEMAND EXAMPLE
|
||||
"""
|
||||
|
||||
|
||||
import datetime
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.dates as mdates
|
||||
|
||||
# comment it if you dowloaded demod from GitHub
|
||||
import demod
|
||||
|
||||
from demod.simulators.base_simulators import SimLogger
|
||||
from demod.datasets.GermanTOU.loader import GTOU
|
||||
from demod.datasets.OpenPowerSystems.loader import OpenPowerSystemClimate
|
||||
from demod.datasets.Germany.loader import GermanDataHerus
|
||||
|
||||
from demod.simulators.sparse_simulators import SparseTransitStatesSimulator
|
||||
from demod.simulators.weather_simulators import RealClimate
|
||||
from demod.simulators.lighting_simulators import CrestLightingSimulator
|
||||
from demod.simulators.heating_simulators import FiveModulesHeatingSimulator
|
||||
from demod.simulators.appliance_simulators import SubgroupApplianceSimulator
|
||||
from demod.simulators.weather_simulators import RealInterpolatedClimate
|
||||
|
||||
|
||||
#%% INITIALIZATION
|
||||
|
||||
def heat_demand(seed, date):
|
||||
np.random.seed(seed)
|
||||
data = GermanDataHerus(version='v0.1')
|
||||
# time_ = datetime.datetime(2018, 1, 1, 0, 0, 0) # starting time of simulation
|
||||
time_ = date
|
||||
days = 1 # number of days to be simulated
|
||||
n_households = 1 # number of households to be simulated
|
||||
subgroup = {
|
||||
'n_residents': 2,
|
||||
'household_type': 2,
|
||||
} # n° residents should be consisten with the household type
|
||||
# Here the available household types:
|
||||
# 1 = One person household
|
||||
# 2 = Couple without kids
|
||||
# 3 = Single Parent with at least one kid under 18 and the other under 27
|
||||
# 4 = Couple with at least one kid under 18 and the other under 27
|
||||
# 5 = Others
|
||||
|
||||
# Occupancy
|
||||
occ_sim = SparseTransitStatesSimulator(
|
||||
n_households, subgroup, data,
|
||||
logger=SimLogger('get_active_occupancy', 'get_occupancy'),
|
||||
start_datetime=time_
|
||||
)
|
||||
|
||||
|
||||
# Climate
|
||||
climate_sim = RealInterpolatedClimate(
|
||||
data,
|
||||
start_datetime=time_,
|
||||
logger=SimLogger(
|
||||
'get_irradiance', 'get_outside_temperature'
|
||||
),
|
||||
# choose the one minute step size
|
||||
step_size = datetime.timedelta(minutes=1),
|
||||
)
|
||||
|
||||
|
||||
# Heating system
|
||||
# It integrates (1) heating demand, (2) thermostat, (3) building thermal
|
||||
# dynamics, (4) heating system control and (5) operation
|
||||
heating_sim = FiveModulesHeatingSimulator(
|
||||
n_households=n_households,
|
||||
initial_outside_temperature=climate_sim.get_outside_temperature(),
|
||||
# The algo that computes the heat demand like in CREST
|
||||
heatdemand_algo='heat_max_emmiters',
|
||||
logger=SimLogger(
|
||||
'get_temperatures',
|
||||
'get_dhw_heat_demand',
|
||||
'get_sh_heat_demand',
|
||||
'get_heat_outputs',
|
||||
'get_power_demand',
|
||||
)
|
||||
)
|
||||
|
||||
# Appliances, water fixtures
|
||||
app_sim = SubgroupApplianceSimulator(
|
||||
[subgroup], [n_households], data=data,
|
||||
initial_active_occupancy=occ_sim.get_active_occupancy(),
|
||||
start_datetime=time_,
|
||||
logger=SimLogger(
|
||||
'get_power_demand', 'get_dhw_heating_demand',
|
||||
'get_current_power_consumptions', aggregated=False )
|
||||
)
|
||||
|
||||
# Lighting
|
||||
lighting_sim = CrestLightingSimulator(
|
||||
n_households=n_households,
|
||||
data=data,
|
||||
logger=SimLogger('get_power_demand'),
|
||||
bulbs_sampling_algo='randn'
|
||||
)
|
||||
|
||||
#%% SIMULATION
|
||||
|
||||
appliance_usage = []
|
||||
|
||||
for _ in range(24*days*6):
|
||||
# every 10 minutes
|
||||
occ_sim.step()
|
||||
|
||||
for __ in range(10):
|
||||
# every 1 minute
|
||||
climate_sim.step()
|
||||
|
||||
app_sim.step(
|
||||
occ_sim.get_active_occupancy()
|
||||
)
|
||||
lighting_sim.step(
|
||||
occ_sim.get_active_occupancy(),
|
||||
climate_sim.get_irradiance()
|
||||
)
|
||||
heating_sim.step(
|
||||
climate_sim.get_outside_temperature(),
|
||||
climate_sim.get_irradiance(),
|
||||
app_sim.get_dhw_heating_demand(),
|
||||
occ_sim.get_thermal_gains(),
|
||||
lighting_sim.get_thermal_gains(),
|
||||
app_sim.get_thermal_gains(),
|
||||
external_target_temperature={'space_heating':20},
|
||||
)
|
||||
# you can pass different target temperature profiles
|
||||
# here it is considered a constant temperature of 20°C
|
||||
|
||||
# store appliance usage
|
||||
appliance_usage.append(app_sim.get_current_usage())
|
||||
# Wattzahlen in 1 Minuten Schritten: Summe in kWh
|
||||
power_demand_1_minute_steps = heating_sim.logger.get('get_power_demand')
|
||||
return power_demand_1_minute_steps
|
||||
|
||||
# Returns power demand in kWh as array where each element represents demand of 10 minutes
|
||||
def heat_demand_per_minute_in_kW(power_demand_1_minute_steps):
|
||||
# Maps array of elements to function, that divides each element by 1000
|
||||
# By dividing by 1000, W is transformed to kW
|
||||
return np.array(power_demand_1_minute_steps) / 1000
|
||||
|
||||
# date = datetime.datetime(2018,4,13)
|
||||
# seed =100005
|
||||
# bedarf = heat_requirement_daily(heat_requirement(seed, date))
|
||||
# print(f"Der Tageswärmebedarf am {date.day}. {date.month}. ist {bedarf:.2f} kWh")
|
||||
11
load_balancer/heater/README.md
Normal file
11
load_balancer/heater/README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Heater
|
||||
|
||||
## Kompilieren
|
||||
|
||||
### Ohne Multithreading
|
||||
|
||||
`g++ ./monteCarloPi.cpp -O3 -o monteCarloPi`
|
||||
|
||||
### Mit Multithreading
|
||||
|
||||
`g++ ./monteCarloPi.cpp -O3 -o monteCarloPi -fopenmp`
|
||||
112
load_balancer/heater/heater_instance.py
Normal file
112
load_balancer/heater/heater_instance.py
Normal file
@@ -0,0 +1,112 @@
|
||||
import paramiko
|
||||
import weakref
|
||||
import math
|
||||
import json
|
||||
|
||||
from heater.host import Host
|
||||
|
||||
|
||||
class HeaterInstance:
|
||||
def __init__(self, host: Host, iter=1000):
|
||||
"""Creates a heater instance
|
||||
|
||||
Args:
|
||||
host (Host): host to connect to
|
||||
iter (int, optional): Number of iterations to compute in one go. Defaults to 1000.
|
||||
|
||||
Raises:
|
||||
IOError: if there was an error reading the file
|
||||
paramiko.ssh_exception.PasswordRequiredException: if the private key file is encrypted, and password is None
|
||||
paramiko.ssh_exception.SSHException: if the key file is invalid
|
||||
"""
|
||||
self.host = host
|
||||
self.iter = iter
|
||||
self.__ssh = paramiko.SSHClient()
|
||||
self.__ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
if host.passphrase:
|
||||
self.__key = paramiko.RSAKey.from_private_key_file(host.ssh_key_filename, host.passphrase)
|
||||
else:
|
||||
self.__key = paramiko.RSAKey.from_private_key_file(host.ssh_key_filename)
|
||||
self.__finalizer = weakref.finalize(self, self.close)
|
||||
|
||||
def run(self, percent: float, milliseconds: int) -> tuple[int, int, float, float]:
|
||||
"""Runs heater for a time dependend on given parameters.
|
||||
Returns number of tries, number of hits, difference between wanted runtime and executed runtime, difference as a factor.
|
||||
Differnce is negative when was shorter than expected or positive when longer than expected.
|
||||
Difference factor is the percent of the expected time the program ran.
|
||||
|
||||
Args:
|
||||
percent (float): cpu usage percentage
|
||||
milliseconds (int): time to run
|
||||
|
||||
Returns:
|
||||
tuple[int, int, float, float]: tries, count, difference, difference as factor
|
||||
"""
|
||||
runtime = percent * milliseconds
|
||||
cmd = f'./monteCarloPi -d {math.floor(runtime)} -i {self.iter}'
|
||||
duration = 0.
|
||||
tries = 0
|
||||
count = 0
|
||||
|
||||
try:
|
||||
_, stdout, stderr = self.__ssh.exec_command(cmd)
|
||||
if stderr.readlines() == []:
|
||||
try:
|
||||
result_json = json.loads(stdout.read().decode().strip("\n"))
|
||||
tries = result_json["tries"]
|
||||
count = result_json["count"]
|
||||
duration = result_json["durationMilli"]
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
except paramiko.SSHException:
|
||||
pass
|
||||
diff = (duration - runtime) * (1 / percent)
|
||||
diffFactor = duration / runtime
|
||||
return tries, count, diff, diffFactor
|
||||
|
||||
def connect(self):
|
||||
"""Connect to heater per ssh.
|
||||
|
||||
Raises:
|
||||
paramiko.ssh_exception.BadHostKeyException: if the server's host key could not be verified.
|
||||
paramiko.ssh_exception.AuthenticationException: if authentication failed.
|
||||
paramiko.ssh_exception.UnableToAuthenticate: if authentication failed (when auth_strategy is non-None; and note that this is a subclass of AuthenticationException).
|
||||
socket.error: if a socket error (other than connection-refused or host-unreachable) occurred while connecting.
|
||||
paramiko.ssh_exception.NoValidConnectionsError: if all valid connection targets for the requested hostname (eg IPv4 and IPv6) yielded connection-refused or host-unreachable socket errors.
|
||||
paramiko.ssh_exception.SSHException: if there was any other error connecting or establishing an SSH session.
|
||||
"""
|
||||
self.__ssh.connect(hostname=self.host.hostname, username=self.host.username, pkey=self.__key)
|
||||
|
||||
@property
|
||||
def open(self) -> bool:
|
||||
"""Returns if ssh connection is open.
|
||||
|
||||
Returns:
|
||||
bool: is ssh connection open
|
||||
"""
|
||||
if self.__ssh.get_transport() is not None:
|
||||
return self.__ssh.get_transport().is_active()
|
||||
return False
|
||||
|
||||
def close(self):
|
||||
"""Closes ssh connection.
|
||||
"""
|
||||
if self.open:
|
||||
self.__ssh.close()
|
||||
|
||||
def cleanup(self):
|
||||
"""cleans heater instance up.
|
||||
Only call once.
|
||||
Don't use instance after cleanup.
|
||||
Is called automatically by garbage collection.
|
||||
"""
|
||||
self.__finalizer()
|
||||
|
||||
@property
|
||||
def cleandup(self) -> bool:
|
||||
"""Returns if object was alreade cleand up.
|
||||
|
||||
Returns:
|
||||
bool: was object cleand up
|
||||
"""
|
||||
return not self.__finalizer.alive
|
||||
17
load_balancer/heater/host.py
Normal file
17
load_balancer/heater/host.py
Normal file
@@ -0,0 +1,17 @@
|
||||
class Host:
|
||||
def __init__(self, hostname: str, username: str, ssh_key_filename: str, passphrase: str):
|
||||
"""Create host to connect to.
|
||||
|
||||
Args:
|
||||
hostname (str): hostname to connect to
|
||||
username (str): user to use for connection
|
||||
ssh_key_filename (str): path to ssh key file
|
||||
passphrase (str): passphrase for ssh key
|
||||
"""
|
||||
self.hostname = hostname
|
||||
self.username = username
|
||||
self.ssh_key_filename = ssh_key_filename
|
||||
self.passphrase = passphrase
|
||||
|
||||
def equals(self, other):
|
||||
return (self.hostname == other.hostname) and (self.username == other.username)
|
||||
146
load_balancer/heater/monteCarloPi.cpp
Normal file
146
load_balancer/heater/monteCarloPi.cpp
Normal file
@@ -0,0 +1,146 @@
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <random>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
#if defined(_OPENMP)
|
||||
#include <omp.h>
|
||||
#endif
|
||||
|
||||
class InputParser
|
||||
{
|
||||
public:
|
||||
InputParser(int &argc, char **argv)
|
||||
{
|
||||
for (int i = 1; i < argc; ++i)
|
||||
{
|
||||
this->tokens.push_back(std::string(argv[i]));
|
||||
}
|
||||
}
|
||||
|
||||
const std::string &getCmdOption(const std::string &option) const
|
||||
{
|
||||
std::vector<std::string>::const_iterator itr;
|
||||
itr = std::find(this->tokens.begin(), this->tokens.end(), option);
|
||||
if (itr != this->tokens.end() && ++itr != this->tokens.end())
|
||||
{
|
||||
return *itr;
|
||||
}
|
||||
static const std::string empty_string("");
|
||||
return empty_string;
|
||||
}
|
||||
|
||||
bool cmdOptionExists(const std::string &option) const
|
||||
{
|
||||
return std::find(this->tokens.begin(), this->tokens.end(), option) != this->tokens.end();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::string> tokens;
|
||||
};
|
||||
|
||||
struct MonteCarloResult
|
||||
{
|
||||
int64_t tries = 0;
|
||||
int64_t count = 0;
|
||||
double durationMilli = 0;
|
||||
};
|
||||
|
||||
const int defaultIterationsPerChunk = 1000;
|
||||
|
||||
MonteCarloResult computePI(int milliseconds, int iterationsPerChunk)
|
||||
{
|
||||
std::chrono::duration<double, std::milli> diff;
|
||||
auto start = std::chrono::system_clock::now();
|
||||
|
||||
int64_t tries = 0;
|
||||
int64_t count = 0;
|
||||
|
||||
#if defined(_OPENMP)
|
||||
#pragma omp parallel reduction(+ : count, tries)
|
||||
#endif
|
||||
{
|
||||
#if defined(_OPENMP)
|
||||
std::mt19937 rng(static_cast<unsigned>(omp_get_thread_num()));
|
||||
#else
|
||||
std::mt19937 rng(0);
|
||||
#endif
|
||||
std::uniform_real_distribution<double> dist(0., 1.);
|
||||
|
||||
do
|
||||
{
|
||||
#if defined(_OPENMP)
|
||||
#pragma omp for nowait
|
||||
#endif
|
||||
for (int64_t i = 0; i < iterationsPerChunk; ++i)
|
||||
{
|
||||
double x = dist(rng);
|
||||
double y = dist(rng);
|
||||
if ((x * x + y * y) < 1)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
tries++;
|
||||
}
|
||||
|
||||
#if defined(_OPENMP)
|
||||
#pragma omp master
|
||||
#endif
|
||||
{
|
||||
auto end = std::chrono::system_clock::now();
|
||||
diff = end - start;
|
||||
}
|
||||
|
||||
#if defined(_OPENMP)
|
||||
#pragma omp barrier
|
||||
#endif
|
||||
} while (diff.count() < milliseconds);
|
||||
}
|
||||
|
||||
auto end = std::chrono::system_clock::now();
|
||||
diff = end - start;
|
||||
MonteCarloResult result{tries, count, diff.count()};
|
||||
return result;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// parse parameters
|
||||
InputParser input(argc, argv);
|
||||
std::filesystem::path path(argv[0]);
|
||||
|
||||
// help
|
||||
if (input.cmdOptionExists("-h") || argc <= 1)
|
||||
{
|
||||
std::cout << "Usage: " << path.filename() << " -d <milliseconds>" << std::endl;
|
||||
std::cout << " Options:" << std::endl;
|
||||
std::cout << " -d <milliseconds>: Number of milliseconds to compute pi for" << std::endl;
|
||||
std::cout << " -i <iterations>: Number of iterations to compute in one go" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// error if -d parameter not set
|
||||
if (!input.cmdOptionExists("-d"))
|
||||
{
|
||||
std::cout << "Error: Option -d must be set" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// check if -i parameter is set and set iterations
|
||||
int iterationsPerChunk = defaultIterationsPerChunk;
|
||||
if (input.cmdOptionExists("-i"))
|
||||
{
|
||||
iterationsPerChunk = std::stoi(input.getCmdOption("-i"));
|
||||
}
|
||||
|
||||
// get duration
|
||||
const int duration = std::stoi(input.getCmdOption("-d"));
|
||||
|
||||
// run Monte Carlo
|
||||
MonteCarloResult result = computePI(duration, iterationsPerChunk);
|
||||
|
||||
// format result
|
||||
std::cout << "{\"tries\":" << result.tries << ", \"count\":" << result.count << ", \"durationMilli\":" << result.durationMilli << "}" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
32
load_balancer/heater/test_remote_pi.py
Normal file
32
load_balancer/heater/test_remote_pi.py
Normal file
@@ -0,0 +1,32 @@
|
||||
import paramiko
|
||||
from credentials import hostname, username, ssh_key_filename, passphrase
|
||||
from host import Host
|
||||
from heater_instance import HeaterInstance
|
||||
|
||||
|
||||
def test_ssh():
|
||||
ssh = paramiko.SSHClient()
|
||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
|
||||
key = paramiko.RSAKey.from_private_key_file(ssh_key_filename, passphrase)
|
||||
ssh.connect(hostname, username, pkey=key)
|
||||
|
||||
stdin, stdout, stderr = ssh.exec_command("ls")
|
||||
print(stdout.readlines())
|
||||
print(stderr.readlines())
|
||||
|
||||
ssh.close()
|
||||
|
||||
|
||||
def test_heater():
|
||||
host = Host(hostname, username, ssh_key_filename, passphrase)
|
||||
heater = HeaterInstance(host)
|
||||
heater.connect()
|
||||
tries, count, diff = heater.run(0.5, 10)
|
||||
print("diff = %s" % diff)
|
||||
print("pi = %f" % ((count / tries) * 4))
|
||||
heater.close()
|
||||
|
||||
|
||||
test_heater()
|
||||
# test_ssh()
|
||||
96
load_balancer/heater_instance_interface.py
Normal file
96
load_balancer/heater_instance_interface.py
Normal file
@@ -0,0 +1,96 @@
|
||||
# author Aaron Moser
|
||||
|
||||
import paramiko
|
||||
import socket
|
||||
import paramiko.ssh_exception
|
||||
|
||||
from enum import Enum
|
||||
|
||||
from heater.heater_instance import HeaterInstance
|
||||
from heater.host import Host
|
||||
from constants import ConnectErrorCode
|
||||
|
||||
class HeaterInstanceState(Enum):
|
||||
INITIALIZED = 1
|
||||
RUNNING = 2
|
||||
ERROR = 3
|
||||
FINISHED = 4
|
||||
|
||||
# Interface to class HeaterInstance
|
||||
class HeaterInstanceInterface:
|
||||
def __init__(self, host: Host):
|
||||
self.host = host
|
||||
self._heater_instance = HeaterInstance(self.host)
|
||||
self._seed = (abs(hash(self.host.hostname + self.host.username)) % 4294967295)
|
||||
self._state: HeaterInstanceState = HeaterInstanceState.INITIALIZED
|
||||
|
||||
# Tries to connect to instance via ssh, returns an error code if failing
|
||||
def connect(self) -> ConnectErrorCode:
|
||||
try:
|
||||
if (not self._heater_instance.open):
|
||||
self._heater_instance.connect()
|
||||
#print("Connected to the SSH server successfully.")
|
||||
return ConnectErrorCode.CONNECTED
|
||||
|
||||
else:
|
||||
#print("Already connected to SSH server.")
|
||||
return ConnectErrorCode.ALREADY_CONNECTED
|
||||
|
||||
except paramiko.ssh_exception.BadHostKeyException as e:
|
||||
print(f"BadHostKeyException: The server's host key could not be verified. {e}")
|
||||
self._state = HeaterInstanceState.ERROR
|
||||
return ConnectErrorCode.UNVERIFIED_KEY
|
||||
|
||||
except paramiko.ssh_exception.AuthenticationException as e:
|
||||
print(f"AuthenticationException: Authentication failed. {e}")
|
||||
self._state = HeaterInstanceState.ERROR
|
||||
return ConnectErrorCode.AUTHENTICATION_FAILED
|
||||
|
||||
except paramiko.ssh_exception.UnableToAuthenticate as e:
|
||||
print(f"UnableToAuthenticate: Authentication failed with the provided strategy. {e}")
|
||||
self._state = HeaterInstanceState.ERROR
|
||||
return ConnectErrorCode.AUTHENTICATION_FAILED
|
||||
|
||||
except socket.error as e:
|
||||
print(f"SocketError: A socket error occurred while connecting. {e}")
|
||||
self._state = HeaterInstanceState.ERROR
|
||||
return ConnectErrorCode.SOCKET_ERROR
|
||||
|
||||
except paramiko.ssh_exception.NoValidConnectionsError as e:
|
||||
print(f"NoValidConnectionsError: All connection attempts failed. {e}")
|
||||
self._state = HeaterInstanceState.ERROR
|
||||
return ConnectErrorCode.ALL_CONNECTION_ATTEMPTS_FAILED
|
||||
|
||||
except paramiko.ssh_exception.SSHException as e:
|
||||
print(f"SSHException: An error occurred while connecting or establishing an SSH session. {e}")
|
||||
self._state = HeaterInstanceState.ERROR
|
||||
return ConnectErrorCode.SSH_EXCEPTION
|
||||
|
||||
# Closes ssh connection to instance
|
||||
def close(self) -> None:
|
||||
self._heater_instance.close()
|
||||
|
||||
# Sends a task to an instance to produce the required heat
|
||||
def run(self, percent: float, seconds: float) -> tuple[int, int, float, float]:
|
||||
if (not self._heater_instance.open):
|
||||
self.connect()
|
||||
result = 0,0,0
|
||||
if self._state != HeaterInstanceState.ERROR:
|
||||
self._state = HeaterInstanceState.RUNNING
|
||||
try:
|
||||
result = self._heater_instance.run(percent, seconds)
|
||||
except Exception as e:
|
||||
print("Connection closed.")
|
||||
if self._state == HeaterInstanceState.RUNNING:
|
||||
self._state = HeaterInstanceState.FINISHED
|
||||
return result
|
||||
|
||||
# Returns host of instance
|
||||
def get_host(self) -> Host:
|
||||
return self.host
|
||||
|
||||
def get_seed(self) -> int:
|
||||
return self._seed
|
||||
|
||||
def get_state(self) -> HeaterInstanceState:
|
||||
return self._state
|
||||
108
load_balancer/heater_instance_interface_manager.py
Normal file
108
load_balancer/heater_instance_interface_manager.py
Normal file
@@ -0,0 +1,108 @@
|
||||
# author Aaron Moser
|
||||
|
||||
from typing import List
|
||||
from enum import Enum
|
||||
import constants
|
||||
|
||||
from heater_instance_interface import HeaterInstanceInterface
|
||||
from heater_instance_worker_thread import HeaterInstanceWorkerThread
|
||||
from database_interface import DatabaseInterface
|
||||
from global_outcome import GlobalOutcome
|
||||
from heater.host import Host
|
||||
from simulation import Simulation
|
||||
from datetime import datetime
|
||||
|
||||
class HeaterInstanceInterfaceManager:
|
||||
|
||||
class HeaterInstanceInterfaceManagerErrorCode(Enum):
|
||||
HEATER_INSTANCE_ALREADY_EXISTS = 1
|
||||
HEATER_INSTANCE_ADDED = 2
|
||||
HEATER_INSTANCE_ADDED_AND_STARTED = 3
|
||||
HEATER_INSTANCE_REMOVED = 4
|
||||
|
||||
def __init__(self, global_outcome: GlobalOutcome, mode: Simulation.Mode, start_date: datetime, end_date: datetime, database_interface: DatabaseInterface):
|
||||
self.global_outcome = global_outcome
|
||||
self.heater_instances: List[HeaterInstanceInterface] = []
|
||||
self.__worker_thread_pool: List[HeaterInstanceWorkerThread] = []
|
||||
self._mode: Simulation.Mode = mode
|
||||
self.database_interface = database_interface
|
||||
self.start_date = start_date
|
||||
self.end_date = end_date
|
||||
|
||||
# Instance management --------------------------------------------------------------------------------------
|
||||
|
||||
# Adds new heater instance to list of instances
|
||||
def add_heater_instance_and_start_thread_if_running(self, host: Host, simulation_state: constants.State) -> HeaterInstanceInterfaceManagerErrorCode:
|
||||
heater_instance_exists = False
|
||||
for heater_instance in self.heater_instances:
|
||||
if heater_instance.get_host().equals(host):
|
||||
heater_instance_exists = True
|
||||
return HeaterInstanceInterfaceManager.HeaterInstanceInterfaceManagerErrorCode.HEATER_INSTANCE_ALREADY_EXISTS
|
||||
if not heater_instance_exists:
|
||||
new_heater_instance_interface = HeaterInstanceInterface(host)
|
||||
self.heater_instances.append(new_heater_instance_interface)
|
||||
if simulation_state == constants.State.Running:
|
||||
new_thread = HeaterInstanceWorkerThread(self.global_outcome, self._mode, new_heater_instance_interface, self.start_date, self.end_date, self.database_interface)
|
||||
self.__worker_thread_pool.append(new_thread)
|
||||
new_thread.run()
|
||||
return HeaterInstanceInterfaceManager.HeaterInstanceInterfaceManagerErrorCode.HEATER_INSTANCE_ADDED_AND_STARTED
|
||||
else:
|
||||
return HeaterInstanceInterfaceManager.HeaterInstanceInterfaceManagerErrorCode.HEATER_INSTANCE_ADDED
|
||||
|
||||
# Removes heater instance from list of instances
|
||||
def remove_heater_instance(self, host) -> HeaterInstanceInterfaceManagerErrorCode:
|
||||
heater_instance_exists = False
|
||||
for heater_instance in self.heater_instances:
|
||||
if heater_instance.get_host().equals(host):
|
||||
heater_instance_exists = True
|
||||
if heater_instance_exists:
|
||||
self.heater_instances.remove(HeaterInstanceInterface(host))
|
||||
for thread in self.__worker_thread_pool:
|
||||
if thread.get_instance_interface().get_host().equals(host):
|
||||
thread.stop()
|
||||
return HeaterInstanceInterfaceManager.HeaterInstanceInterfaceManagerErrorCode.HEATER_INSTANCE_REMOVED
|
||||
|
||||
# Returns list of all heater instances
|
||||
def get_heater_instances(self):
|
||||
return self.heater_instances
|
||||
|
||||
def show_heater_instances(self):
|
||||
if len(self.heater_instances) == 0:
|
||||
print("No instances registered.")
|
||||
else:
|
||||
for heater_instance in self.heater_instances:
|
||||
print("Hostname: " + heater_instance.get_host().hostname + ", Username: " + heater_instance.get_host().username + ", Seed: " + str(heater_instance.get_seed()))
|
||||
|
||||
def get_heater_instances_count(self) -> int:
|
||||
return len(self.heater_instances)
|
||||
|
||||
def set_start_and_end_date(self, start_date: datetime, end_date: datetime):
|
||||
self.start_date = start_date
|
||||
self.end_date = end_date
|
||||
|
||||
# Threading-------------------------------------------------------------------------------------------------
|
||||
|
||||
# Changes simulation mode, by restarting all threads with new mode
|
||||
def change_simulation_mode(self, mode: Simulation.Mode):
|
||||
self._mode = mode
|
||||
self.stop_threads()
|
||||
self.instantiate_threads()
|
||||
self.start_threads()
|
||||
|
||||
# Stops all running threads
|
||||
def stop_threads(self):
|
||||
for worker_thread in self.__worker_thread_pool:
|
||||
if worker_thread.get_stop_event_state() == False:
|
||||
worker_thread.stop()
|
||||
|
||||
# Clears list of threads and creates new threads which are then appended to cleared list
|
||||
def instantiate_threads(self):
|
||||
self.__worker_thread_pool: List[HeaterInstanceWorkerThread] = []
|
||||
for heater_instance_interface in self.heater_instances:
|
||||
self.__worker_thread_pool.append(HeaterInstanceWorkerThread(self.global_outcome, self._mode, heater_instance_interface, self.start_date, self.end_date, self.database_interface))
|
||||
|
||||
# Starts threads in list of threads if state of instance is 'INITIALIZED'
|
||||
def start_threads(self):
|
||||
for worker_thread in self.__worker_thread_pool:
|
||||
if worker_thread.get_stop_event_state() == False:
|
||||
worker_thread.start()
|
||||
46
load_balancer/heater_instance_worker_thread.py
Normal file
46
load_balancer/heater_instance_worker_thread.py
Normal file
@@ -0,0 +1,46 @@
|
||||
# author Aaron Moser, Theresa Herr
|
||||
|
||||
import datetime
|
||||
import threading
|
||||
from heater_instance_interface import HeaterInstanceInterface
|
||||
from database_interface import DatabaseInterface
|
||||
from global_outcome import GlobalOutcome
|
||||
from simulation import Simulation
|
||||
import tasks.daily_task
|
||||
import tasks.realtime_task
|
||||
|
||||
class HeaterInstanceWorkerThread(threading.Thread):
|
||||
|
||||
def __init__(self, global_outcome: GlobalOutcome, mode: Simulation.Mode, heater_instance_interface: HeaterInstanceInterface, start_date: datetime.datetime, end_date: datetime.datetime, database_interface: DatabaseInterface):
|
||||
super().__init__()
|
||||
self.global_outcome: GlobalOutcome = global_outcome
|
||||
self.__mode: Simulation.Mode = mode
|
||||
self._stop_event: threading.Event = threading.Event()
|
||||
self.heater_instance_interface: HeaterInstanceInterface = heater_instance_interface
|
||||
self.start_date: datetime.datetime = start_date
|
||||
self.end_date: datetime.datetime = end_date
|
||||
self.db_interface: DatabaseInterface = database_interface
|
||||
|
||||
def run(self):
|
||||
while not self._stop_event.is_set():
|
||||
match self.__mode:
|
||||
case Simulation.Mode.REALTIME:
|
||||
tasks.realtime_task.realtime_task(self.global_outcome, self.heater_instance_interface, self.start_date, self.end_date, self.save_outcome)
|
||||
case Simulation.Mode.DAILY:
|
||||
tasks.daily_task.daily_task(self.global_outcome, self.heater_instance_interface, self.start_date, self.end_date, self.save_outcome)
|
||||
case _:
|
||||
print("Error unknown mode.")
|
||||
|
||||
def get_instance_interface(self) -> HeaterInstanceInterface:
|
||||
return self.heater_instance_interface
|
||||
|
||||
def stop(self):
|
||||
self.heater_instance_interface.close()
|
||||
self._stop_event.set()
|
||||
|
||||
def get_stop_event_state(self) -> bool:
|
||||
return self._stop_event.is_set()
|
||||
|
||||
def save_outcome(self, outcome_list: list):
|
||||
for outcome in outcome_list:
|
||||
self.db_interface.insert_task_outcome(outcome)
|
||||
383
load_balancer/load_balancer_impl.py
Normal file
383
load_balancer/load_balancer_impl.py
Normal file
@@ -0,0 +1,383 @@
|
||||
# author Aaron Moser
|
||||
|
||||
#---------------------------------------------------------------------------------------------------------------
|
||||
|
||||
import constants
|
||||
|
||||
from typing import List
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from heater_instance_interface_manager import HeaterInstanceInterfaceManager
|
||||
from global_outcome import GlobalOutcome
|
||||
from simulation import Simulation
|
||||
from heater.host import Host
|
||||
|
||||
#---------------------------------------------------------------------------------------------------------------
|
||||
|
||||
class LoadBalancer:
|
||||
"""Implementation of load balancer, allows to add instances and start the simulation.
|
||||
"""
|
||||
|
||||
#---------------------------------------------------------------------------------------------------------------
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
global_outcome,
|
||||
db_interface,
|
||||
simulation_mode: Simulation.Mode,
|
||||
start_date: datetime,
|
||||
end_date: datetime
|
||||
):
|
||||
"""Instantiates a LoadBalancer object.
|
||||
|
||||
Args:
|
||||
self (LoadBalancer): this current object, which is instantiated.
|
||||
db_interface (DatabaseInterface): interface class to database.
|
||||
simulation_mode (Simulation.Mode): current mode of simulation.
|
||||
"""
|
||||
self.state: constants.State = constants.State.Stopped
|
||||
self.global_outcome: GlobalOutcome = global_outcome
|
||||
self.db_interface = db_interface
|
||||
self.simulation = Simulation()
|
||||
self.simulation.set_mode(simulation_mode)
|
||||
self.running = False
|
||||
self.start_date = start_date
|
||||
self.end_date = end_date
|
||||
self.heater_instance_interface_manager: HeaterInstanceInterfaceManager = HeaterInstanceInterfaceManager(global_outcome, simulation_mode, start_date, end_date, db_interface)
|
||||
|
||||
#---------------------------------------------------------------------------------------------------------------
|
||||
|
||||
def run(self) -> None:
|
||||
"""Loop of asking user for input and processing entered command.
|
||||
|
||||
Args:
|
||||
self (LoadBalancer): this current object, on which method is called.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
self.print_start_message()
|
||||
self.running = True
|
||||
while self.running:
|
||||
self._process_first_level_command(self._get_command_args())
|
||||
|
||||
#---------------------------------------------------------------------------------------------------------------
|
||||
|
||||
def print_start_message(self) -> None:
|
||||
"""Prints message, load balancer started
|
||||
|
||||
Args:
|
||||
self (LoadBalancer): this current object, on which method is called.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
print(constants.START_MESSAGE)
|
||||
|
||||
#---------------------------------------------------------------------------------------------------------------
|
||||
|
||||
def _get_command_args(self) -> List[str]:
|
||||
"""Prompts user to enter command and returns command string array splitted by spaces
|
||||
|
||||
Args:
|
||||
self (LoadBalancer): this current object, on which method is called.
|
||||
|
||||
Returns:
|
||||
List[str]: Partstrings which are created by splitting the command entered by user at spaces.
|
||||
"""
|
||||
return input("-------------------------------\nPlease enter command (Enter 'help' to see all commands)\nCommand: ").lower().split(" ")
|
||||
|
||||
#---------------------------------------------------------------------------------------------------------------
|
||||
|
||||
def _process_first_level_command(self, command_args) -> None:
|
||||
"""Processes the first level (command_args[0]) command.
|
||||
|
||||
Args:
|
||||
self (LoadBalancer): this current object, on which method is called.
|
||||
command_args (List[str]): partstrings of command string split at spaces.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
match command_args[0]:
|
||||
|
||||
#
|
||||
case constants.START_SIMULATION_COMMAND:
|
||||
if self.heater_instance_interface_manager.get_heater_instances_count() >= constants.MINIMUM_NUMBER_CLIENTS:
|
||||
self.start_simulation(command_args)
|
||||
else:
|
||||
print("Please add more instances, before starting the simulation.")
|
||||
|
||||
case constants.STOP_SIMULATION_COMMAND:
|
||||
self.stop_simulation()
|
||||
|
||||
#
|
||||
case constants.SET_SIMULATION_MODE_COMMAND:
|
||||
if self.heater_instance_interface_manager.get_heater_instances_count() >= constants.MINIMUM_NUMBER_CLIENTS:
|
||||
self.set_simulation_mode(command_args)
|
||||
else:
|
||||
print("Please add more instances, before re-setting the simulation mode.")
|
||||
|
||||
#
|
||||
case constants.ADD_INSTANCE_COMMAND:
|
||||
if self.heater_instance_interface_manager.get_heater_instances_count() < constants.MAXIMUM_NUMBER_CLIENTS:
|
||||
self.add_instance(command_args)
|
||||
else:
|
||||
print("Maximum number of clients reached, you have to remove an instance before you can add another.")
|
||||
|
||||
#
|
||||
case constants.REMOVE_INSTANCE_COMMAND:
|
||||
self.remove_instance(command_args)
|
||||
|
||||
#
|
||||
case constants.SHOW_INSTANCES_COMMAND:
|
||||
self.show_instances()
|
||||
|
||||
case constants.DISPLAY_OUTCOME_COMMAND:
|
||||
self.display_outcome()
|
||||
|
||||
#
|
||||
case constants.HELP_COMMAND:
|
||||
self.print_help()
|
||||
|
||||
#
|
||||
case constants.EXIT_COMMAND:
|
||||
self.exit_application()
|
||||
|
||||
# If user enters command, which doesn't match any case above.
|
||||
case _:
|
||||
print("Unknown command: " + command_args[0])
|
||||
|
||||
#---------------------------------------------------------------------------------------------------------------
|
||||
|
||||
def start_simulation(self, command_args) -> None:
|
||||
"""Starts simulation.
|
||||
|
||||
Args:
|
||||
self (LoadBalancer): this current object, on which method is called.
|
||||
command_args (List[str]): partstrings of command string split at spaces.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
if len(command_args) > 1:
|
||||
match command_args[1]:
|
||||
case constants.SET_SIMULATION_MODE_COMMAND_REALTIME:
|
||||
if len(command_args) == 4:
|
||||
start_date = command_args[2]
|
||||
end_date = command_args[3]
|
||||
date_format = "%d.%m.%Y"
|
||||
self.start_date = datetime.strptime(start_date, date_format)
|
||||
self.end_date = datetime.strptime(end_date, date_format)
|
||||
self.start_simulation_in_mode(Simulation.Mode.REALTIME)
|
||||
|
||||
elif len(command_args) == 3:
|
||||
start_date = command_args[2]
|
||||
date_format = "%d.%m.%Y"
|
||||
self.start_date = datetime.strptime(start_date, date_format)
|
||||
one_day_difference = timedelta(days=1)
|
||||
self.end_date = start_date + one_day_difference
|
||||
self.start_simulation_in_mode(Simulation.Mode.REALTIME)
|
||||
|
||||
else:
|
||||
self.start_simulation_in_mode(Simulation.Mode.REALTIME)
|
||||
|
||||
case constants.SET_SIMULATION_MODE_COMMAND_DAILY:
|
||||
if len(command_args) == 4:
|
||||
start_date = command_args[2]
|
||||
end_date = command_args[3]
|
||||
date_format = "%d.%m.%Y"
|
||||
self.start_date = datetime.strptime(start_date, date_format)
|
||||
self.end_date = datetime.strptime(end_date, date_format)
|
||||
self.start_simulation_in_mode(Simulation.Mode.DAILY)
|
||||
|
||||
elif len(command_args) == 3:
|
||||
start_date = command_args[2]
|
||||
date_format = "%d.%m.%Y"
|
||||
self.start_date = datetime.strptime(start_date, date_format)
|
||||
one_day_difference = timedelta(days=1)
|
||||
self.end_date = start_date + one_day_difference
|
||||
self.start_simulation_in_mode(Simulation.Mode.DAILY)
|
||||
|
||||
else:
|
||||
self.start_simulation_in_mode(Simulation.Mode.DAILY)
|
||||
|
||||
case _:
|
||||
print("Unknown simulation command.")
|
||||
else:
|
||||
self.heater_instance_interface_manager.change_simulation_mode(self.simulation.get_mode())
|
||||
|
||||
def start_simulation_in_mode(self, simulation_mode: Simulation.Mode):
|
||||
self.heater_instance_interface_manager.set_start_and_end_date(self.start_date, self.end_date)
|
||||
self.heater_instance_interface_manager.change_simulation_mode(simulation_mode)
|
||||
self.simulation.set_mode(simulation_mode)
|
||||
self.state: constants.State = constants.State.Running
|
||||
match simulation_mode:
|
||||
case Simulation.Mode.DAILY:
|
||||
print("Simulation started in daily mode. With start date: " + str(self.start_date) + " and end date: " + str(self.end_date))
|
||||
case Simulation.Mode.REALTIME:
|
||||
print("Simulation started in realtime mode. With start date: " + str(self.start_date) + " and end date: " + str(self.end_date))
|
||||
|
||||
#---------------------------------------------------------------------------------------------------------------
|
||||
|
||||
def stop_simulation(self) -> None:
|
||||
if self.state == constants.State.Running:
|
||||
self.heater_instance_interface_manager.stop_threads()
|
||||
self.state = constants.State.Stopped
|
||||
print("Stopped simulation. To restart enter StartSimulation [mode] [start date] [end date].")
|
||||
else:
|
||||
print("Error: Simulation is not running. Can't stop if not running.")
|
||||
|
||||
#---------------------------------------------------------------------------------------------------------------
|
||||
|
||||
def set_simulation_mode(self, command_args) -> None:
|
||||
"""If simulation is running, a user has to use stop simulation and start again or just SetSimulationMode.
|
||||
Will stop currently running threads and restart them with new mode.
|
||||
|
||||
Args:
|
||||
self (LoadBalancer): this current object, on which method is called.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
if len(command_args) > 1:
|
||||
match command_args[1]:
|
||||
case constants.SET_SIMULATION_MODE_COMMAND_REALTIME:
|
||||
if self.get_approval():
|
||||
self.heater_instance_interface_manager.change_simulation_mode(Simulation.Mode.REALTIME)
|
||||
self.simulation.set_mode(Simulation.Mode.REALTIME)
|
||||
else:
|
||||
print("Continuing at current mode.")
|
||||
|
||||
case constants.SET_SIMULATION_MODE_COMMAND_DAILY:
|
||||
if self.get_approval():
|
||||
self.heater_instance_interface_manager.change_simulation_mode(Simulation.Mode.DAILY)
|
||||
self.simulation.set_mode(Simulation.Mode.DAILY)
|
||||
else:
|
||||
print("Continuing at current mode.")
|
||||
else:
|
||||
print("Error, simulation mode missing, please re-enter command.")
|
||||
|
||||
#---------------------------------------------------------------------------------------------------------------
|
||||
|
||||
def get_approval(self) -> bool:
|
||||
"""Prompts user if s/he really wants to stop the simulation.
|
||||
|
||||
Args:
|
||||
self (LoadBalancer): this current object, on which method is called.
|
||||
|
||||
Returns:
|
||||
bool: True if user enters 'Y|y', False otherwise.
|
||||
"""
|
||||
return input("Warning, current simulation will stop. Do you really want to stop? (Y)es (N)o\nCommand: ").lower() == "y"
|
||||
|
||||
#---------------------------------------------------------------------------------------------------------------
|
||||
|
||||
def add_instance(self, command_args) -> None:
|
||||
"""Add instance to instance interface manager and start worker thread.
|
||||
|
||||
Args:
|
||||
self (LoadBalancer): this current object, on which method is called.
|
||||
command_args (List[str]): partstrings of command string split at spaces.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
hostname = command_args[1]
|
||||
username = command_args[2]
|
||||
ssh_key_filename = command_args[3]
|
||||
passphrase = ""
|
||||
if len(command_args) >= 5:
|
||||
passphrase = command_args[4]
|
||||
match self.heater_instance_interface_manager.add_heater_instance_and_start_thread_if_running(Host(hostname, username, ssh_key_filename, passphrase), self.state):
|
||||
case HeaterInstanceInterfaceManager.HeaterInstanceInterfaceManagerErrorCode.HEATER_INSTANCE_ALREADY_EXISTS:
|
||||
print("Instance already exists.")
|
||||
case HeaterInstanceInterfaceManager.HeaterInstanceInterfaceManagerErrorCode.HEATER_INSTANCE_ADDED:
|
||||
print("Instance added.")
|
||||
case HeaterInstanceInterfaceManager.HeaterInstanceInterfaceManagerErrorCode.HEATER_INSTANCE_ADDED_AND_STARTED:
|
||||
print("Instance added and thread started.")
|
||||
case _:
|
||||
print("Unkown error code.")
|
||||
|
||||
#---------------------------------------------------------------------------------------------------------------
|
||||
|
||||
def remove_instance(self, command_args) -> None:
|
||||
"""Removes instance indicated by host- and username given in command_args and stops worker thread.
|
||||
|
||||
Args:
|
||||
self (LoadBalancer): this current object, on which method is called.
|
||||
command_args (List[str]): partstrings of command string split at spaces.
|
||||
|
||||
Returns:
|
||||
None.
|
||||
"""
|
||||
hostname = command_args[1]
|
||||
username = command_args[2]
|
||||
match self.heater_instance_interface_manager.remove_heater_instance(Host(hostname, username, None, None)):
|
||||
case HeaterInstanceInterfaceManager.HeaterInstanceInterfaceManagerErrorCode.HEATER_INSTANCE_REMOVED:
|
||||
print("Instance removed successfully.")
|
||||
case _:
|
||||
print("Unknown error code at remove_instance received.")
|
||||
|
||||
#---------------------------------------------------------------------------------------------------------------
|
||||
|
||||
def show_instances(self) -> None:
|
||||
"""Shows instances by printing them to console.
|
||||
|
||||
Args:
|
||||
self (LoadBalancer): this current object, on which method is called.
|
||||
|
||||
Returns:
|
||||
None.
|
||||
"""
|
||||
self.heater_instance_interface_manager.show_heater_instances()
|
||||
|
||||
#---------------------------------------------------------------------------------------------------------------
|
||||
|
||||
def display_outcome(self) -> None:
|
||||
"""Displays global outcome stats by printing them to console.
|
||||
|
||||
Args:
|
||||
self (LoadBalancer): this current object, on which method is called.
|
||||
|
||||
Returns:
|
||||
None.
|
||||
"""
|
||||
tries, count = self.global_outcome.get_tries_count()
|
||||
pi:float = 0.0
|
||||
if tries != 0:
|
||||
pi = float(count) / float(tries) * float(4)
|
||||
print("Count: " + str(count) + ", Tries: " + str(tries) + ", PI: " + str(pi))
|
||||
|
||||
#---------------------------------------------------------------------------------------------------------------
|
||||
|
||||
def print_help(self) -> None:
|
||||
"""Prints list of commands with description from constants.
|
||||
|
||||
Args:
|
||||
self (LoadBalancer): this current object, on which method is called.
|
||||
|
||||
Returns:
|
||||
None.
|
||||
"""
|
||||
for command_description in constants.COMMANDS_HELP:
|
||||
print(command_description)
|
||||
|
||||
#---------------------------------------------------------------------------------------------------------------
|
||||
|
||||
def exit_application(self) -> None:
|
||||
"""Exits application by stopping all threads and sets own state to stopped,
|
||||
then stops while loop and terminates.
|
||||
|
||||
Args:
|
||||
self (LoadBalancer): this current object, on which method is called.
|
||||
|
||||
Returns:
|
||||
None.
|
||||
"""
|
||||
self.heater_instance_interface_manager.stop_threads()
|
||||
self.simulation.set_state(Simulation.State.STOPPED)
|
||||
self.running = False
|
||||
|
||||
#---------------------------------------------------------------------------------------------------------------
|
||||
31
load_balancer/simulation.py
Normal file
31
load_balancer/simulation.py
Normal file
@@ -0,0 +1,31 @@
|
||||
# author Aaron Moser
|
||||
|
||||
from enum import Enum
|
||||
|
||||
class Simulation:
|
||||
|
||||
class State(Enum):
|
||||
INITIALIZED = 1
|
||||
RUNNING = 2
|
||||
PAUSED = 3
|
||||
STOPPED = 4
|
||||
|
||||
class Mode(Enum):
|
||||
REALTIME = 1
|
||||
DAILY = 2
|
||||
|
||||
def __init__(self):
|
||||
self.state = Simulation.State.INITIALIZED
|
||||
self.mode = Simulation.Mode.REALTIME
|
||||
|
||||
def set_state(self, state):
|
||||
self.state = state
|
||||
|
||||
def get_state(self):
|
||||
return self.state
|
||||
|
||||
def set_mode(self, mode):
|
||||
self.mode = mode
|
||||
|
||||
def get_mode(self):
|
||||
return self.mode
|
||||
22
load_balancer/start_load_balancer.py
Normal file
22
load_balancer/start_load_balancer.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# author Aaron Moser
|
||||
|
||||
import datetime
|
||||
from database_interface import DatabaseInterface
|
||||
from global_outcome import GlobalOutcome
|
||||
from load_balancer_impl import LoadBalancer
|
||||
from simulation import Simulation
|
||||
|
||||
def main():
|
||||
# Establish connection to db, if not possible, print error
|
||||
# If error suggests, that db couldn't be found, prompt user to enter location of db
|
||||
# If error suggests, that user / pw were wrong, prompt user to reenter creds
|
||||
# Retry to connect to db
|
||||
# Create instance of DBInterface class
|
||||
db_path = "sqlitedb.db"
|
||||
db_interface = DatabaseInterface(db_path)
|
||||
global_outcome = GlobalOutcome()
|
||||
|
||||
LoadBalancer(global_outcome, db_interface, Simulation.Mode.DAILY, datetime.datetime(2018,4,1), datetime.datetime(2018,4,2)).run()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
59
load_balancer/tasks/daily_task.py
Normal file
59
load_balancer/tasks/daily_task.py
Normal file
@@ -0,0 +1,59 @@
|
||||
import time
|
||||
import constants
|
||||
import heat_demand_simulator
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from typing import List
|
||||
from heater_instance_interface import HeaterInstanceInterface
|
||||
from global_outcome import GlobalOutcome
|
||||
from tasks.task_outcome import TaskOutcome
|
||||
|
||||
def daily_task(global_outcome: GlobalOutcome, heater_instance_interface: HeaterInstanceInterface, start_date: datetime, end_date: datetime, store_db_function):
|
||||
|
||||
one_minute_difference = timedelta(minutes=1)
|
||||
actual_date: datetime = start_date
|
||||
# Get difference in days between start and end date.
|
||||
difference: timedelta = end_date - start_date
|
||||
stopped = False
|
||||
|
||||
for i in range(difference.days):
|
||||
|
||||
outcome_list:List[TaskOutcome] = []
|
||||
|
||||
seed:int = heater_instance_interface.get_seed()
|
||||
heat_demand_per_minute_in_kW_day = heat_demand_simulator.heat_demand_per_minute_in_kW(heat_demand_simulator.heat_demand(seed, actual_date))
|
||||
|
||||
for heat_demand_per_minute in heat_demand_per_minute_in_kW_day:
|
||||
|
||||
required_workload_in_percent = diff = diff_factor = 0.0
|
||||
tries = count = 0
|
||||
|
||||
if heat_demand_per_minute > 0.0:
|
||||
# Calculate percentage of workload for cpu while runtime.
|
||||
# example: 1.5 / 0.04 / 100 = 0.375 = 37.5%
|
||||
required_workload_in_percent = heat_demand_per_minute / constants.MAX_WORKLOAD_IN_KW_ONE_PERCENT / 100
|
||||
try:
|
||||
start_time = time.time()
|
||||
tries, count, diff, diff_factor = heater_instance_interface.run(required_workload_in_percent, constants.DAILY_MODE_MILLISECONDS_MINUTE_RATIO)
|
||||
end_time = time.time()
|
||||
runtime = end_time - start_time
|
||||
sleep_time = 0.06 - runtime
|
||||
if sleep_time > 0:
|
||||
time.sleep(sleep_time)
|
||||
except:
|
||||
print()
|
||||
stopped = True
|
||||
break
|
||||
global_outcome.update_tries_count(tries, count)
|
||||
|
||||
else:
|
||||
time.sleep(0.06)
|
||||
|
||||
actual_heat_generated = diff_factor * heat_demand_per_minute
|
||||
outcome_list.append(TaskOutcome(required_workload_in_percent, constants.DAILY_MODE_MILLISECONDS_MINUTE_RATIO, tries, count, diff / 1000, diff_factor, actual_date, heat_demand_per_minute, actual_heat_generated, heater_instance_interface.get_host().hostname, heater_instance_interface.get_host().username))
|
||||
actual_date += one_minute_difference
|
||||
|
||||
if stopped:
|
||||
break
|
||||
|
||||
store_db_function(outcome_list)
|
||||
55
load_balancer/tasks/realtime_task.py
Normal file
55
load_balancer/tasks/realtime_task.py
Normal file
@@ -0,0 +1,55 @@
|
||||
import time
|
||||
import constants
|
||||
import heat_demand_simulator
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from typing import List
|
||||
from heater_instance_interface import HeaterInstanceInterface
|
||||
from global_outcome import GlobalOutcome
|
||||
from tasks.task_outcome import TaskOutcome
|
||||
|
||||
def realtime_task(global_outcome: GlobalOutcome, heater_instance_interface: HeaterInstanceInterface, start_date: datetime, end_date: datetime, store_db_function):
|
||||
|
||||
one_minute_difference = timedelta(minutes=1)
|
||||
actual_date: datetime = start_date
|
||||
# Get difference in days between start and end date.
|
||||
difference: timedelta = end_date - start_date
|
||||
stopped = False
|
||||
|
||||
for i in range(difference.days):
|
||||
|
||||
seed = heater_instance_interface.get_seed()
|
||||
heat_demand_per_minute_in_kW_day = heat_demand_simulator.heat_demand_per_minute_in_kW(heat_demand_simulator.heat_demand(seed, actual_date))
|
||||
|
||||
for heat_demand_per_minute in heat_demand_per_minute_in_kW_day:
|
||||
|
||||
required_workload_in_percent = diff = diff_factor = 0.0
|
||||
tries = count = 0
|
||||
|
||||
if heat_demand_per_minute > 0.0:
|
||||
# Calculate percentage of workload for cpu while runtime.
|
||||
# example: 1.5 / 0.04 / 100 = 0.375 = 37.5%
|
||||
required_workload_in_percent = heat_demand_per_minute / constants.MAX_WORKLOAD_IN_KW_ONE_PERCENT / 100
|
||||
try:
|
||||
start_time = time.time()
|
||||
tries, count, diff, diff_factor = heater_instance_interface.run(required_workload_in_percent, 60000)
|
||||
end_time = time.time()
|
||||
runtime = end_time - start_time
|
||||
sleep_time = 60 - runtime
|
||||
if sleep_time > 0:
|
||||
time.sleep(sleep_time)
|
||||
except:
|
||||
print()
|
||||
stopped = True
|
||||
break
|
||||
global_outcome.update_tries_count(tries, count)
|
||||
|
||||
else:
|
||||
# If heat demand is 0, just sleep 1 minute and put values of 0 into list, since no heating required
|
||||
time.sleep(60)
|
||||
actual_heat_generated = diff_factor * heat_demand_per_minute
|
||||
actual_date += one_minute_difference
|
||||
store_db_function([TaskOutcome(required_workload_in_percent * 100, 60000, tries, count, diff / 1000, diff_factor, actual_date, heat_demand_per_minute, actual_heat_generated, heater_instance_interface.get_host().hostname, heater_instance_interface.get_host().username)])
|
||||
|
||||
if stopped:
|
||||
break
|
||||
49
load_balancer/tasks/task_outcome.py
Normal file
49
load_balancer/tasks/task_outcome.py
Normal file
@@ -0,0 +1,49 @@
|
||||
from datetime import datetime
|
||||
|
||||
class TaskOutcome:
|
||||
def __init__(self, required_workload_in_percent, runtime, tries, count, diff, diff_factor, actual_date, heat_demand_per_minute, actual_heat_generated, hostname: str, username: str):
|
||||
self.required_workload_in_percent:float = required_workload_in_percent
|
||||
self.runtime: float = runtime
|
||||
self.tries: int = tries
|
||||
self.count: int = count
|
||||
self.diff: float = diff
|
||||
self.diff_factor: float = diff_factor
|
||||
self.actual_date: datetime = actual_date
|
||||
self.heat_demand_per_minute: float = heat_demand_per_minute
|
||||
self.actual_heat_generated: float = actual_heat_generated
|
||||
self.hostname: str = hostname
|
||||
self.username: str = username
|
||||
|
||||
def get_required_workload_in_percent(self) -> float:
|
||||
return self.required_workload_in_percent
|
||||
|
||||
def get_runtime(self) -> float:
|
||||
return self.runtime
|
||||
|
||||
def get_tries(self) -> int:
|
||||
return self.tries
|
||||
|
||||
def get_count(self) -> int:
|
||||
return self.count
|
||||
|
||||
def get_diff(self) -> float:
|
||||
return self.diff
|
||||
|
||||
def get_diff_factor(self) -> float:
|
||||
return self. diff_factor
|
||||
|
||||
def get_actual_date(self) -> datetime:
|
||||
return self.actual_date
|
||||
|
||||
def get_heat_demand_per_minute(self) -> float:
|
||||
return self.heat_demand_per_minute
|
||||
|
||||
def get_actual_heat_generated(self) -> float:
|
||||
return self.actual_heat_generated
|
||||
|
||||
def get_hostname(self) -> str:
|
||||
return self.hostname
|
||||
|
||||
def get_username(self) -> str:
|
||||
return self.username
|
||||
|
||||
Reference in New Issue
Block a user