diff --git a/README.md b/README.md
index b4de276..2298393 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
- Chia Plot, Drive Manager, Coin Monitor & Auto Drive (V0.94 - August 8th, 2021)
+ Chia Plot, Drive Manager, Coin Monitor & Auto Drive (V0.95 - September 3rd, 2021)
Multi Server Chia Plot and Drive Management Solution
@@ -98,7 +98,7 @@ Beginning in V0.94, we now fully support portable plot management as well as old
After the transfer is complete, checks the exact file size of the plot on both systems as a basic verification
Once files sizes are verified, deletes the sent plot
Kills any lingering netcat connections on the selected Harvester/NAS
- Supports any number of harvesters and prioritizes sending plots to the harvester with the most space available.
+ Supports any number of harvesters and prioritizes sending plots to the harvester with the most space available (Empty plot space + old plots to replace = total space available.
If replacing old plots with new pool plots, above utilizes number of plot spaces available based on free space + number of old plots.
@@ -152,7 +152,7 @@ I am running on Python 3.8.5 and pretty much everything else came installed with
ConfigParser (5.0.2) - Used for reading and updating config files
py-SMART (0.3) - Used for reading drive information
Natsort (7.1.1) - Used for natural sorting of drive numbers
- Sysstat - Used to monitor Disk I/O Stats
+ Sysstat - Used to monitor Disk and Network I/O Stats
Paramiko (2.7.2) - Used to grab remote harvester stats
@@ -838,6 +838,25 @@ strategy above, it is super easy to add more drives.
### Changelog
+V0.95 2021-09-03
+ - Replaces glances with sysstat commands to check and verify network traffic
+ - Update install script to create new network check script based on install directory
+ - Created failsafe to allow free drive space to be filled when 'replace_plots' has been
+ set to yes and there are no more old plots to replace. Now falls back to filling empty
+ drive space after that happens and will notify you when both fail.
+ - Various bug fixes and enhancements.
+
+V0.94 2021-08-08
+ - Automatic plot removal and replacement of older style plots with new style pooling
+ plots done on a one-by-one basis to keep as many plots farming as possible during
+ the plot replacement process.
+ - Various bug fixes and other small enhancements
+ - Updated Farming Wide reports to report on number of portable plots vs old plots
+ and if we are replacing old plots or not on a per-server basis.
+ - Updated nas_export to include the number of old plots and if server is set to replace
+ old plots, the total number of plot space available on server is now the total of the
+ number of old plots to replace along with how many plots we can store on remaining
+ free space.
V0.93 2021-07-08
- Added ability to identify plots as `portable` (set `pooling: True` in config file)
diff --git a/chianas/config_file_updater.py b/chianas/config_file_updater.py
index c0b6978..75843af 100644
--- a/chianas/config_file_updater.py
+++ b/chianas/config_file_updater.py
@@ -4,7 +4,7 @@
"""
Part of drive_manager. Checks to see if our config file needs updating and updated it for us.
"""
-VERSION = "V0.94 (2021-08-08)"
+VERSION = "V0.95 (2021-09-03)"
import os
import yaml
diff --git a/chianas/drive_manager.py b/chianas/drive_manager.py
index e93a026..de80c28 100644
--- a/chianas/drive_manager.py
+++ b/chianas/drive_manager.py
@@ -3,7 +3,7 @@
# -*- coding: utf-8 -*-
__author__ = 'Richard J. Sears'
-VERSION = "0.94 (2021-08-08)"
+VERSION = "0.95 (2021-09-03)"
"""
NOTE NOTE NOTE NOTE NOTE NOTE NOTE
@@ -186,6 +186,9 @@
plot_size_g = 101.3623551
receive_script = script_path.joinpath('receive_plot.sh')
replace_plots_receive_script = script_path.joinpath('replace_plots_receive_plot.sh')
+remote_transfer_active_file = script_path.joinpath('remote_transfer_is_active')
+network_check = script_path.joinpath('check_network_io.sh')
+network_check_output = script_path.joinpath('network_stats.io')
# Date and Time Stuff
current_military_time = datetime.now().strftime('%Y%m%d%H%M%S')
@@ -663,6 +666,7 @@ def get_plot_drive_to_use():
return (natsorted(available_drives)[0])
except IndexError:
log.debug("ERROR: No Drives Found, Please add drives, run auto_drive.py and try again!")
+ notify('OUT OF DRIVE SPACE!', 'You have run out of drive space on your Harvester and we can no longer accept any new plots!')
exit()
@@ -696,9 +700,6 @@ def get_internal_plot_drive_to_use():
log.debug('this issue is you are able.')
notify('Drive Overlap!', 'Internal and External plotting drives now overlap! Suggest fixing to prevent drive bus contention and slow transfers. If you have selected plot replacement, we will attempt to convert to replacement now.')
return get_plot_drive_to_use()
- # log.debug("ERROR: No Additional Internal Drives Found, Please add drives, run auto_drive.py and try again!")
- # exit()
-
def get_sorted_drive_list():
@@ -866,6 +867,7 @@ def update_receive_plot():
here. See TODO: Update to use netcat native to python.
"""
log.debug("update_receive_plot() Started")
+ remote_transfer_active = check_for_active_remote_transfer()
if not chianas.replace_non_pool_plots: # If we are not replacing old plots with new portable plots, run the following code
log.debug('Replace Plots has NOT been set in config, will call build script for normal operation.')
drive = get_plot_drive_to_use()[0]
@@ -873,7 +875,7 @@ def update_receive_plot():
log.debug(f'{receive_script} not found. Building it now...')
build_receive_plot('normal', drive)
else:
- if os.path.isfile(script_path.joinpath('remote_transfer_is_active')):
+ if remote_transfer_active:
log.debug('Remote Transfer in Progress, will try again soon!')
quit() # TODO Think about what we really want to do here. If we are running a remote transfer, can we still do other things?
else:
@@ -901,7 +903,7 @@ def update_receive_plot():
log.debug(f'{receive_script} not found. Building it now...')
build_receive_plot('normal', drive)
else:
- if os.path.isfile(script_path.joinpath('remote_transfer_is_active')):
+ if remote_transfer_active:
log.debug('Remote Transfer in Progress, will try again soon!')
quit() # TODO Think about what we really want to do here. If we are running a remote transfer, can we still do other things?
else:
@@ -912,8 +914,7 @@ def update_receive_plot():
log.debug(f'No changes necessary to {receive_script}')
log.debug(f'Plots left available on configured plotting drive: {get_drive_info("space_free_plots_by_mountpoint", chianas.current_plotting_drive)}')
else:
- send_new_plot_disk_email() # This is the full Plot drive report. This is in addition to the generic email sent by the
- # notify() function.
+ send_new_plot_disk_email() # This is the full Plot drive report. This is in addition to the generic email sent by the notify() function.
notify('Plot Drive Updated', f'Plot Drive Updated: Was: {chianas.current_plotting_drive}, Now: {drive}')
build_receive_plot('normal', drive)
else:
@@ -927,14 +928,18 @@ def update_receive_plot():
log.debug(f'{receive_script} not found. Building it now...')
build_receive_plot('portable', drive)
else:
- if chianas.current_plot_replacement_drive == drive:
- log.debug(f'Currently Configured Replacement Drive: {chianas.current_plot_replacement_drive}')
- log.debug(f'System Selected Replacement Drive: {drive}')
- log.debug('Configured and Selected Drives Match!')
- log.debug(f'No changes necessary to {receive_script}')
+ if remote_transfer_active:
+ log.debug('Remote Transfer in Progress, will try again soon!')
+ quit() # TODO Think about what we really want to do here. If we are running a remote transfer, can we still do other things?
else:
- notify('Plot Replacement Drive Updated', f'Plot Drive Updated: Was: {chianas.current_plot_replacement_drive}, Now: {drive}')
- build_receive_plot('portable', drive)
+ if chianas.current_plot_replacement_drive == drive:
+ log.debug(f'Currently Configured Replacement Drive: {chianas.current_plot_replacement_drive}')
+ log.debug(f'System Selected Replacement Drive: {drive}')
+ log.debug('Configured and Selected Drives Match!')
+ log.debug(f'No changes necessary to {receive_script}')
+ else:
+ notify('Plot Replacement Drive Updated', f'Plot Drive Updated: Was: {chianas.current_plot_replacement_drive}, Now: {drive}')
+ build_receive_plot('portable', drive)
else:
log.debug(f'ERROR: Replace Plots Configured, but no old plots exist!')
quit()
@@ -948,18 +953,47 @@ def update_receive_plot():
log.debug(f'{receive_script} not found. Building it now...')
build_receive_plot('portable', drive)
else:
- if chianas.current_plot_replacement_drive == drive:
- log.debug(f'Currently Configured Replacement Drive: {chianas.current_plot_replacement_drive}')
- log.debug(f'System Selected Replacement Drive: {drive}')
- log.debug('Configured and Selected Drives Match!')
- log.debug(f'No changes necessary to {receive_script}')
+ if remote_transfer_active:
+ log.debug('Remote Transfer in Progress, will try again soon!')
+ quit() # TODO Think about what we really want to do here. If we are running a remote transfer, can we still do other things?
else:
- # send_new_plot_disk_email() # This is the full Plot drive report. This is in addition to the generic email sent by the
- # notify() function. - TODO Do we need to send this here or do we need to update the function?
- notify('Plot Replacement Drive Updated', f'Plot Drive Updated: Was: {chianas.current_plot_replacement_drive}, Now: {drive}')
- build_receive_plot('portable', drive)
+ if chianas.current_plot_replacement_drive == drive:
+ log.debug(f'Currently Configured Replacement Drive: {chianas.current_plot_replacement_drive}')
+ log.debug(f'System Selected Replacement Drive: {drive}')
+ log.debug('Configured and Selected Drives Match!')
+ log.debug(f'No changes necessary to {receive_script}')
+ else:
+ notify('Plot Replacement Drive Updated', f'Plot Drive Updated: Was: {chianas.current_plot_replacement_drive}, Now: {drive}')
+ build_receive_plot('portable', drive)
else:
- log.debug(f'ERROR: Replace Plots Configured, but no old plots exist!')
+ log.debug(f'ERROR: Replace Plots Configured, but no old plots exist! Defaulting to filling our empty drives...')
+ if (get_all_available_system_space("free")[1]) > chianas.empty_drives_low_water_mark:
+ log.debug('Found Empty Drive Space!')
+ log.debug(f'Low Water Mark: {chianas.empty_drives_low_water_mark} and we have {get_all_available_system_space("free")[1]} available')
+ drive = get_plot_drive_to_use()[0]
+ if not os.path.isfile(receive_script):
+ log.debug(f'{receive_script} not found. Building it now...')
+ build_receive_plot('normal', drive)
+ else:
+ if remote_transfer_active:
+ log.debug('Remote Transfer in Progress, will try again soon!')
+ quit() # TODO Think about what we really want to do here. If we are running a remote transfer, can we still do other things?
+ else:
+ if chianas.current_plotting_drive == drive:
+ log.debug(f'Currently Configured Plot Drive: {chianas.current_plotting_drive}')
+ log.debug(f'System Selected Plot Drive: {drive}')
+ log.debug('Configured and Selected Drives Match!')
+ log.debug(f'No changes necessary to {receive_script}')
+ log.debug(
+ f'Plots left available on configured plotting drive: {get_drive_info("space_free_plots_by_mountpoint", chianas.current_plotting_drive)}')
+ else:
+ send_new_plot_disk_email() # This is the full Plot drive report. This is in addition to the generic email sent by the notify() function.
+ notify('Plot Drive Updated',
+ f'Plot Drive Updated: Was: {chianas.current_plotting_drive}, Now: {drive}')
+ build_receive_plot('normal', drive)
+ else:
+ log.debug('We have a problem. There are no old plots to replace and we have run out of empty drive space, not sure what to do now but phone home.....')
+ notify('No Old Plots & No Drive Space Available', 'We have a problem. There are no old plots to replace and we have run out of empty drive space, I need your help!')
quit()
def build_receive_plot(type, drive):
@@ -982,6 +1016,57 @@ def build_receive_plot(type, drive):
log.info(f'Was: {chianas.current_plotting_drive}, Now: {drive}')
+def check_for_active_remote_transfer():
+ """
+ Function to check and verify if we have a remote transfer active to prevent
+ overloading drives during local and remote copies of plots.
+ """
+ log.debug('check_for_active_remote_transfer() called')
+ if os.path.isfile(remote_transfer_active_file):
+ log.debug(f'A Remote Transfer appears to be in Progress, checking for network traffic on interface: {chianas.plot_receive_interface}.')
+ if check_network_activity():
+ log.debug('Network traffic has been detected, a Remote Transfer is in progress.')
+ return True
+ else:
+ log.debug(f'Remote Transfer file present but there is no network traffic on interface: {chianas.plot_receive_interface}')
+ log.debug('Resetting Remote Transfer file now......')
+ os.remove(remote_transfer_active_file)
+ return False
+ else:
+ log.debug('Remote Transfer File does not exist, lets check for network traffic to verify....')
+ if check_network_activity():
+ log.debug('Network traffic has been detected, a Remote Transfer is in progress.')
+ return True
+ else:
+ log.debug('No Current Remote Transfers are taking place.')
+ return False
+
+def check_network_activity():
+ """
+ Here we are checking network activity on the network interface we are receiving plots on from our plotter. If there is
+ network activity, then we are most likely receiving a plot and don't want to make any changes.
+ """
+ log.debug('check_network_activity() called')
+ try:
+ subprocess.call([network_check, chianas.plot_receive_interface])
+ except subprocess.CalledProcessError as e:
+ log.warning(e.output)
+ with open(network_check_output, 'rb') as f:
+ f.seek(-2, os.SEEK_END)
+ while f.read(1) != b'\n':
+ f.seek(-2, os.SEEK_CUR)
+ last_line = f.readline().decode()
+ network_traffic_load = float((str.split(last_line)[9]))
+ if network_traffic_load >= chianas.plot_receive_interface_threshold:
+ log.debug(f'Network Activity detected on {chianas.plot_receive_interface}')
+ os.remove(network_check_output)
+ return True
+ else:
+ log.debug(f'No Network Activity detected on {chianas.plot_receive_interface}')
+ os.remove(network_check_output)
+ return False
+
+
def send_new_plot_disk_email():
"""
This is the function that we call when we want to send an email letting you know that a new
@@ -1580,7 +1665,6 @@ def main():
send_new_plot_notification()
update_receive_plot()
-
-
if __name__ == '__main__':
main()
+
diff --git a/chianas/drivemanager_classes.py b/chianas/drivemanager_classes.py
index 2318198..551e2c6 100644
--- a/chianas/drivemanager_classes.py
+++ b/chianas/drivemanager_classes.py
@@ -6,7 +6,7 @@
config file.
"""
-VERSION = "V0.94 (2021-08-08)"
+VERSION = "V0.95 (2021-09-03)"
import os
import yaml
@@ -40,40 +40,6 @@
log = logging.getLogger('drivemanager_classes.py')
log.setLevel(level)
-def config_file_update():
- """
- Function to determine if we need to update our yaml configuration file after an upgrade.
- """
- log.debug('config_file_update() Started....')
- if os.path.isfile(skel_config_file):
- with open(config_file, 'r') as current_config:
- current_config = yaml.safe_load(current_config)
- with open(skel_config_file, 'r') as temp_config:
- temp_config = yaml.safe_load(temp_config)
- temp_current_config = flatten(current_config)
- temp_temp_config = flatten(temp_config)
- updates = (dict((k, v) for k, v in temp_temp_config.items() if k not in temp_current_config))
- if updates != {}:
- copyfile(skel_config_file, (str(Path.home()) + '/.config/plot_manager/Config_Instructions.yaml'))
- copyfile(config_file, (str(Path.home()) + f'/.config/plot_manager/plot_manager.yaml.{current_military_time}'))
- temp_current_config.update(updates)
- new_config = (dict((k, v) for k, v in temp_current_config.items() if k in temp_temp_config))
- else:
- new_config = (dict((k, v) for k, v in temp_current_config.items() if k not in temp_temp_config))
- if new_config != {}:
- new_config = (dict((k, v) for k, v in temp_current_config.items() if k in temp_temp_config))
- current_config = unflatten(new_config)
- current_config.update({'configured': False})
- with open((str(Path.home()) + '/.config/plot_manager/plot_manager.yaml'), 'w') as f:
- yaml.safe_dump(current_config, f)
- log.debug(f'Config File: {config_file} updated. Update as necessary to run this script.')
- exit()
- else:
- log.debug('No config file changes necessary! No changes made.')
- else:
- log.debug('New configuration file not found. No changes made.')
-
-
class DriveManager:
if not os.path.isfile(config_file):
log.debug(f'Plot_Manager config file does not exist at: {config_file}')
@@ -84,8 +50,8 @@ def __init__(self, configured, hostname, pools, replace_non_pool_plots, fill_emp
remote_harvester_reports, remote_harvesters, notifications, pb, email, sms, daily_update, new_plot_drive, per_plot,
local_plotter, temp_dirs, temp_dirs_critical, temp_dirs_critical_alert_sent, dst_dirs, dst_dirs_critical,
dst_dirs_critical_alert_sent, warnings, emails, phones, twilio_from, twilio_account, twilio_token, pb_api,
- current_internal_drive, current_plotting_drive, total_plot_highwater_warning, total_plots_alert_sent,
- current_total_plots_midnight, current_total_plots_daily, offlined_drives, logging, log_level,
+ current_internal_drive, current_plotting_drive, total_plot_highwater_warning, total_plots_alert_sent, plot_receive_interface_threshold,
+ current_total_plots_midnight, current_total_plots_daily, offlined_drives, logging, log_level, plot_receive_interface,
current_portable_plots_midnight, current_portable_plots_daily, current_plot_replacement_drive, local_move_error, local_move_error_alert_sent):
self.configured = configured
self.hostname = hostname
@@ -129,6 +95,8 @@ def __init__(self, configured, hostname, pools, replace_non_pool_plots, fill_emp
self.offlined_drives = offlined_drives
self.logging = logging
self.log_level = log_level
+ self.plot_receive_interface = plot_receive_interface
+ self.plot_receive_interface_threshold = plot_receive_interface_threshold
self.current_plot_replacement_drive = current_plot_replacement_drive
self.local_move_error = local_move_error
self.local_move_error_alert_sent = local_move_error_alert_sent
@@ -180,6 +148,8 @@ def read_configs(cls):
offlined_drives=server['harvester']['offlined_drives'],
logging=server['logging'],
log_level=server['log_level'],
+ plot_receive_interface=server['plot_receive_interface'],
+ plot_receive_interface_threshold=server['plot_receive_interface_threshold'],
current_plot_replacement_drive=server['pools']['current_plot_replacement_drive'],
local_move_error=server['local_plotter']['local_move_error'],
local_move_error_alert_sent=server['local_plotter']['local_move_error_alert_sent'])
diff --git a/chianas/move_local_plots.py b/chianas/move_local_plots.py
index d941468..2c5b5e3 100644
--- a/chianas/move_local_plots.py
+++ b/chianas/move_local_plots.py
@@ -3,7 +3,7 @@
# -*- coding: utf-8 -*-
__author__ = 'Richard J. Sears'
-VERSION = "0.94 (2021-08-08)"
+VERSION = "0.95 (2021-09-03)"
# This script is part of my plot management set of tools. This
# script is used to move plots from one location to another on
@@ -21,7 +21,7 @@
from system_logging import setup_logging
import shutil
from timeit import default_timer as timer
-from drive_manager import get_drive_info, notify, check_space_available, get_all_available_system_space, get_internal_plot_drive_to_use
+from drive_manager import notify, check_space_available, get_all_available_system_space, get_internal_plot_drive_to_use
import subprocess
import pathlib
from drivemanager_classes import DriveManager, config_file, PlotManager
@@ -292,7 +292,6 @@ def process_plot():
return
-
def verify_plot_move(plot_source, plot_destination):
log.debug('verify_plot_move() Started')
log.debug (f'Verifing: {plot_source}')
diff --git a/chianas/plot_manager.skel.yaml b/chianas/plot_manager.skel.yaml
index bf3e1c9..228e9a6 100644
--- a/chianas/plot_manager.skel.yaml
+++ b/chianas/plot_manager.skel.yaml
@@ -1,4 +1,4 @@
-# v0.94 2021-08-08
+# v0.95 2021-09-03
# Once you have made the necessary modifications to this file, change this to
# True.
configured: False
@@ -6,6 +6,20 @@ configured: False
# Enter the hostname of this server:
hostname: chianas01
+# Enter the name (as shown by ifconfig) of the interface that you RECEIVE plots on
+# from your plotters. This is used to check for network traffic to prevent multiple
+# plots from being transferred at the same time.
+plot_receive_interface: eth0
+
+# This is a number that represents at what percentage overall utilization of the above
+# interface we will assume that a plot transfer is taking place. You should really TEST
+# this to make sure it works for your needs. If you have a dedicated interface to move
+# plots, then it can be set very low (1 to 2), however if you have a shared interface,
+# you should test while a plot transfer is running and set it to what number makes sense.
+# To test simply run the following command and look at the very last number:
+# /usr/bin/sar -n DEV 1 50 | egrep eth0
+plot_receive_interface_threshold: 2
+
# Are we plotting for pools? This has nothing to do with the actual plotting of
# plots but rather just naming of the new plots and eventually the replacing of
# old plots with portable plots.
diff --git a/chianas/readme.md b/chianas/readme.md
index 2f11dc6..bf3f9e2 100644
--- a/chianas/readme.md
+++ b/chianas/readme.md
@@ -1,7 +1,7 @@
- Chia Plot, Drive Manager, Coin Monitor & Auto Drive (V0.94 - August 8th, 2021)
+ Chia Plot, Drive Manager, Coin Monitor & Auto Drive (V0.95 - September 3th, 2021)
Multi Server Chia Plot and Drive Management Solution
@@ -44,6 +44,8 @@ be. This is a standard YAML file, so leave the formatting as you see it or you w
- configured (set this to true)
- hostname (set this to the hostname of the system - should match IP address used by all other systems to comunicate to this box)
+ - plot_receive_interface (set this tot he name of the interface as shown by ifconfig that you use to receive plots)
+ - plot_receive_interface_threshold (set this the the percentage utilization on the bove inteface that indicates that we are receiving plot)
- pools (set these setting according to your configuration, see notes)
- chia_log_file (full path to your chia log file (usually debug.log) (Make sure to set logging level to INFO or DEBIG in your Chia Config)
- chia_config_file (same as above)
@@ -57,7 +59,7 @@ be. This is a standard YAML file, so leave the formatting as you see it or you w
```
-# v0.94 2021-08-08
+# v0.95 2021-09-03
# Once you have made the necessary modifications to this file, change this to
# True.
configured: False
@@ -65,6 +67,20 @@ configured: False
# Enter the hostname of this server:
hostname: chianas01
+# Enter the name (as shown by ifconfig) of the interface that you RECEIVE plots on
+# from your plotters. This is used to check for network traffic to prevent multiple
+# plots from being transferred at the same time.
+plot_receive_interface: eth0
+
+# This is a number that represents at what percentage overall utilization of the above
+# interface we will assume that a plot transfer is taking place. You should really TEST
+# this to make sure it works for your needs. If you have a dedicated interface to move
+# plots, then it can be set very low (1 to 2), however if you have a shared interface,
+# you should test while a plot transfer is running and set it to what number makes sense.
+# To test simply run the following command and look at the very last number:
+# /usr/bin/sar -n DEV 1 50 | egrep eth0
+plot_receive_interface_threshold: 2
+
# Are we plotting for pools? This has nothing to do with the actual plotting of
# plots but rather just naming of the new plots and eventually the replacing of
# old plots with portable plots.
diff --git a/chiaplot/config_file_updater.py b/chiaplot/config_file_updater.py
new file mode 100644
index 0000000..75843af
--- /dev/null
+++ b/chiaplot/config_file_updater.py
@@ -0,0 +1,91 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+
+"""
+Part of drive_manager. Checks to see if our config file needs updating and updated it for us.
+"""
+VERSION = "V0.95 (2021-09-03)"
+
+import os
+import yaml
+from pathlib import Path
+import logging
+from system_logging import setup_logging
+from shutil import copyfile
+from flatten_dict import flatten
+from flatten_dict import unflatten
+from datetime import datetime
+
+script_path = Path(__file__).parent.resolve()
+
+# Date and Time Stuff
+current_military_time = datetime.now().strftime('%Y%m%d%H%M%S')
+
+config_file = (str(Path.home()) + '/.config/plot_manager/plot_manager.yaml')
+skel_config_file = script_path.joinpath('plot_manager.skel.yaml')
+
+# Define some colors for our help message
+red='\033[0;31m'
+yellow='\033[0;33m'
+green='\033[0;32m'
+white='\033[0;37m'
+blue='\033[0;34m'
+nc='\033[0m'
+
+
+# Setup Module logging. Main logging is configured in system_logging.py
+setup_logging()
+with open(config_file, 'r') as config:
+ server = yaml.safe_load(config)
+level = server['log_level']
+level = logging._checkLevel(level)
+log = logging.getLogger('config_file_updater.py')
+log.setLevel(level)
+
+
+def config_file_update():
+ """
+ Function to determine if we need to update our yaml configuration file after an upgrade.
+ """
+ log.debug('config_file_update() Started....')
+ if os.path.isfile(skel_config_file):
+ log.debug('New config SKEL file located, checking to see if an update is needed:')
+ with open(config_file, 'r') as current_config:
+ current_config = yaml.safe_load(current_config)
+ with open(skel_config_file, 'r') as temp_config:
+ temp_config = yaml.safe_load(temp_config)
+ temp_current_config = flatten(current_config)
+ temp_temp_config = flatten(temp_config)
+ updates = (dict((k, v) for k, v in temp_temp_config.items() if k not in temp_current_config))
+ if updates != {}:
+ copyfile(skel_config_file, (str(Path.home()) + '/.config/plot_manager/Config_Instructions.yaml'))
+ copyfile(config_file, (str(Path.home()) + f'/.config/plot_manager/plot_manager.yaml.{current_military_time}'))
+ temp_current_config.update(updates)
+ new_config = (dict((k, v) for k, v in temp_current_config.items() if k in temp_temp_config))
+ else:
+ new_config = (dict((k, v) for k, v in temp_current_config.items() if k not in temp_temp_config))
+ if new_config != {}:
+ new_config = (dict((k, v) for k, v in temp_current_config.items() if k in temp_temp_config))
+ current_config = unflatten(new_config)
+ current_config.update({'configured': False})
+ with open((str(Path.home()) + '/.config/plot_manager/plot_manager.yaml'), 'w') as f:
+ yaml.safe_dump(current_config, f)
+ log.debug('Your config files needs updating!')
+ log.debug(f'Config File: {config_file} updated. Update as necessary to run chia_plot_manager.')
+ exit()
+ else:
+ log.debug('No config file changes necessary! No changes made.')
+ log.debug(f'{skel_config_file} has been deleted!')
+ os.remove(skel_config_file)
+ else:
+ log.debug('New configuration file not found. No changes made.')
+
+
+
+def main():
+ print(f'Welcome to {green}config_file_updater.py{nc} {blue}VERSION: {nc}{VERSION}')
+ config_file_update()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/chiaplot/plot_manager.py b/chiaplot/plot_manager.py
index 6a34a85..0a6b608 100644
--- a/chiaplot/plot_manager.py
+++ b/chiaplot/plot_manager.py
@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-
__author__ = 'Richard J. Sears'
-VERSION = "0.94 (2021-08-08)"
+VERSION = "0.95 (2021-09-03)"
"""
Simple python script that helps to move my chia plots from my plotter to
@@ -47,7 +47,6 @@
from system_logging import setup_logging
import pathlib
import json
-import urllib.request
import psutil
from pushbullet import Pushbullet, errors as pb_errors
from twilio.rest import Client
@@ -59,7 +58,7 @@
script_path = pathlib.Path(__file__).parent.resolve()
from plotmanager_classes import PlotManager, config_file
chiaplot = PlotManager.read_configs()
-
+import time
# Are We Testing?
testing = False
@@ -105,10 +104,11 @@ def remote_harvesters_check():
transfer your plots over to your Harvester. We utilize this to determine is there is
network traffic flowing across it during a transfer.
"""
-network_interface = chiaplot.network_interface
-
-
+#network_interface = chiaplot.network_interface
remote_checkfile = script_path.joinpath('remote_transfer_is_active')
+network_check = script_path.joinpath('check_network_io.sh')
+network_check_output = script_path.joinpath('network_stats.io')
+
# Setup Module logging. Main logging is configured in system_logging.py
@@ -139,17 +139,19 @@ def process_plot():
if not process_control('check_status', 0):
plot_to_process = get_list_of_plots()
if plot_to_process and not testing:
- process_control('set_status', 'start')
plot_path = plot_dir + plot_to_process
log.info(f'Processing Plot: {plot_path}')
log.debug(f'{nas_server} reports remote mount as {remote_mount}')
if replace_plots:
+ log.debug(f'Executing "drive_manager.py -rp" on {nas_server}.')
result = (subprocess.run(['ssh', nas_server, f'{script_path.joinpath("drive_manager.py -rp")}'],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL))
if result.returncode != 0:
+ log.debug(result.returncode)
log.debug(f'replace_plot() Script on {nas_server} FAILED! Fix Immediately!')
- notify('URGENT', f'replace_plot() Script on {nas_server} FAILED! Fix Immediately!')
+ #notify('URGENT', f'replace_plot() Script on {nas_server} FAILED! Fix Immediately!')
quit()
+ process_control('set_status', 'start')
if chiaplot.pools:
plot_to_process = 'portable.'+plot_to_process
subprocess.call([f'{script_path.joinpath("send_plot.sh")}', plot_path, plot_to_process, nas_server])
@@ -190,31 +192,44 @@ def process_control(command, action):
if action == "start":
if os.path.isfile(status_file):
log.debug(f'Status File: [{status_file}] already exists!')
+ try:
+ log.debug(f'Remote Checkfile is: {remote_checkfile}')
+ subprocess.check_output(['ssh', nas_server, 'touch %s' % f'{remote_checkfile}'])
+ except subprocess.CalledProcessError as e:
+ log.warning(e.output) #Nothing to add here yet as we are not using this function remotely (yet)
return
else:
os.open(status_file, os.O_CREAT)
+ log.debug(f'Remote Checkfile is: {remote_checkfile}')
try:
- subprocess.check_output(['ssh', nas_server, 'touch %s' % remote_checkfile])
+ subprocess.check_output(['ssh', nas_server, 'touch %s' % f'{remote_checkfile}'])
except subprocess.CalledProcessError as e:
log.warning(e.output) #Nothing to add here yet as we are not using this function remotely (yet)
+ return
if action == "stop":
if os.path.isfile(status_file):
os.remove(status_file)
+ log.debug(f'Removing remote checkfile from {nas_server}')
try:
- subprocess.check_output(['ssh', nas_server, 'rm %s' % remote_checkfile])
+ subprocess.check_output(['ssh', nas_server, 'rm %s' % f'{remote_checkfile}'])
except subprocess.CalledProcessError as e:
log.warning(e.output) #Nothing to add here yet as we are not using this function remotely (yet)
else:
log.debug(f'Status File: [{status_file}] does not exist!')
- return
+ log.debug(f'Removing remote checkfile from {nas_server}')
+ try:
+ subprocess.check_output(['ssh', nas_server, 'rm %s' % f'{remote_checkfile}'])
+ except subprocess.CalledProcessError as e:
+ log.warning(e.output) #Nothing to add here yet as we are not using this function remotely (yet)
elif command == 'check_status':
if checkIfProcessRunning('nc') and check_transfer():
log.debug(f'NC is running and Network Traffic Exists, We are currently Running a Transfer, Exiting')
return True
elif checkIfProcessRunning('nc') and not check_transfer():
log.debug('WARNING! - NC is running but there is no network traffic! Forcing Reset')
+ log.debug(f'Removing remote checkfile from {nas_server}')
try:
- subprocess.check_output(['ssh', nas_server, 'rm %s' % remote_checkfile])
+ subprocess.check_output(['ssh', nas_server, 'rm %s' % f'{remote_checkfile}'])
except subprocess.CalledProcessError as e:
log.warning(e.output)
try:
@@ -230,7 +245,7 @@ def process_control(command, action):
return
else:
log.debug(f'WARNING: {nas_server} is OFFLINE! We Cannot Continue......')
- notify(f'{nas_server} OFFLINE', f'Your NAS Server: {nas_server} cannot be reached. Plots cannot move! Please Correct IMMEDIATELY!')
+ notify(f'{nas_server}.{chiaplot.domain_name} OFFLINE', f'Your NAS Server: {nas_server} cannot be reached. Plots cannot move! Please Correct IMMEDIATELY!')
exit()
@@ -246,10 +261,6 @@ def verify_plot_move(remote_mount, plot_path, plot_to_process):
local_plot_size = os.path.getsize(plot_path)
log.debug(f'Local Plot Size Reported as: {local_plot_size}')
if remote_plot_size == local_plot_size:
- try:
- subprocess.check_output(['ssh', nas_server, 'touch %s' % script_path.joinpath("new_plot_received")])
- except subprocess.CalledProcessError as e:
- log.warning(e.output)
return True
else:
log.debug(f'Plot Size Mismatch!')
@@ -257,17 +268,29 @@ def verify_plot_move(remote_mount, plot_path, plot_to_process):
def check_transfer():
+ """
+ Here we are checking network activity on the network interface we are sending plots to from our plotter. If there is
+ network activity, then we are most likely receiving a plot and don't want to make any changes.
+ """
+ log.debug('check_transfer() called')
try:
- with urllib.request.urlopen(f"http://localhost:61208/api/3/network/interface_name/{network_interface}") as url:
- data = json.loads(url.read().decode())
- current_transfer_speed = (data[network_interface][0]['tx']/1000000)
- if current_transfer_speed < 5:
- return False
- else:
- return True
- except urllib.error.URLError as e:
- print (e.reason)
- exit()
+ subprocess.call([network_check, chiaplot.network_interface])
+ except subprocess.CalledProcessError as e:
+ log.warning(e.output)
+ with open(network_check_output, 'rb') as f:
+ f.seek(-2, os.SEEK_END)
+ while f.read(1) != b'\n':
+ f.seek(-2, os.SEEK_CUR)
+ last_line = f.readline().decode()
+ network_traffic_load = float((str.split(last_line)[9]))
+ if network_traffic_load >= chiaplot.network_interface_threshold:
+ log.debug(f'Network Activity detected on {chiaplot.network_interface}')
+ os.remove(network_check_output)
+ return True
+ else:
+ log.debug(f'No Network Activity detected on {chiaplot.network_interface}')
+ os.remove(network_check_output)
+ return False
def checkIfProcessRunning(processName):
@@ -294,17 +317,6 @@ def host_check(host):
stdout=subprocess.DEVNULL)
return proc.returncode == 0
-
-def verify_glances_is_running():
- log.debug('verify_glances_is_running() Started')
- if not checkIfProcessRunning('glances'):
- log.debug('WARNING - This script requires the Glances API to operate properly.')
- log.debug('We were unable to determine if Glances is running. Please verify and try again!')
- exit()
- else:
- return True
-
-
def notify(title, message):
""" Notify system for email, pushbullet and sms (via Twilio)"""
log.debug(f'notify() called with Title: {title} and Message: {message}')
@@ -433,7 +445,7 @@ def check_temp_drive_utilization():
log.debug('check_temp_drive_utilization() started')
if chiaplot.get_critical_temp_dir_usage() != {}:
if not chiaplot.temp_dirs_critical_alert_sent:
- chiaplot.toggle_alert_sent('temp_dirs_critical_alert_sent')
+ chianas.toggle_alert_sent('temp_dirs_critical_alert_sent')
for dirs in chiaplot.get_critical_temp_dir_usage().keys():
log.debug(f'WARNING: {dirs} is nearing capacity. Sending Alert!')
notify('WARNING: Directory Utilization Nearing Capacity',
@@ -472,6 +484,17 @@ def check_dst_drive_utilization():
log.debug('DST Drive(s) check complete. All OK!')
+def exists_remote(host, path):
+ """Test if a file exists at path on a host accessible with SSH."""
+ status = subprocess.call(
+ ['ssh', host, 'test -f {}'.format(pipes.quote(path))])
+ if status == 0:
+ return True
+ if status == 1:
+ return False
+ raise Exception('SSH failed')
+
+
def system_checks():
"""
These are the various system checks. Limits are set in the
@@ -483,15 +506,10 @@ def system_checks():
def main():
log.debug(f'Welcome to plot_manager.py Version: {VERSION}')
- if verify_glances_is_running():
- system_checks()
- remote_harvesters_check()
- process_plot()
- else:
- print('Glances is Required for this script!')
- print('Please install and restart this script.')
- exit()
-
+ system_checks()
+ remote_harvesters_check()
+ process_plot()
if __name__ == '__main__':
main()
+
diff --git a/chiaplot/plot_manager.skel.yaml b/chiaplot/plot_manager.skel.yaml
index 3fc5667..92b1db9 100644
--- a/chiaplot/plot_manager.skel.yaml
+++ b/chiaplot/plot_manager.skel.yaml
@@ -1,18 +1,20 @@
# Once you have made the necessary modifications to this file, change this to
# True.
+
configured: False
# Enter the hostname of this server:
hostname: chiaplot01
+domain_name: mydomain.com
-# Are we plotting for pools (prepends `portable.` to the plot name)
+#Are we plotting for pools (prepends `portable.` to the plot name)
pools: True
# Enter Logging Information
logging: True
log_level: DEBUG
-# I use MadMax to do my plotting, but this should work for anything. This
+# I use Plotman to do my plotting, but this should work for anything. This
# has NOTHING to do with setting up your plotting configuration and is
# only used for monitoring drive space for notifications. Set to True if
# locally plotting and configure the rest of the settings.
@@ -40,7 +42,7 @@ local_plotter:
# At what % utilization do we send an error?
critical: 95
- critical_alert_sent: False
+ critical_alert_sent: True
# If you are running multiple remote harvesters,
# and enter their hostnames below.These hostnames
@@ -55,8 +57,19 @@ remote_harvesters:
- chianas02
- chianas03
-# This is our high speed, internal Network interface name that we send plots over
-network_interface: eno1
+# Enter the name (as shown by ifconfig) of the interface that you SEND plots on
+# to your plotters. This is used to check for network traffic to prevent multiple
+# plots from being transferred at the same time.
+network_interface: eth0
+
+# This is a number that represents at what percentage overall utilization of the above
+# interface we will assume that a plot transfer is taking place. You should really TEST
+# this to make sure it works for your needs. If you have a dedicated interface to move
+# plots, then it can be set very low (1 to 2), however if you have a shared interface,
+# you should test while a plot transfer is running and set it to what number makes sense.
+# To test simply run the following command and look at the very last number:
+# /usr/bin/sar -n DEV 1 50 | egrep eth0
+network_interface_threshold: 2
# This is where we set up our notifications
diff --git a/chiaplot/plotmanager_classes.py b/chiaplot/plotmanager_classes.py
index a63a50d..0e87f4a 100644
--- a/chiaplot/plotmanager_classes.py
+++ b/chiaplot/plotmanager_classes.py
@@ -6,7 +6,7 @@
config file.
"""
-VERSION = "V0.93 (2021-07-08)"
+VERSION = "V0.95 (2021-09-03)"
import os
import yaml
@@ -14,11 +14,18 @@
import logging
from system_logging import setup_logging
import psutil
+from flatten_dict import flatten
+from flatten_dict import unflatten
+from datetime import datetime
+script_path = Path(__file__).parent.resolve()
+#user_home_dir = str(Path.home())
+config_file = (str(Path.home()) + '/.config/plot_manager/plot_manager.yaml')
+#config_file = (user_home_dir + '/.config/plot_manager/plot_manager.yaml')
+skel_config_file = script_path.joinpath('plot_manager.skel.yaml')
-user_home_dir = str(Path.home())
-config_file = (user_home_dir + '/.config/plot_manager/plot_manager.yaml')
-
+# Date and Time Stuff
+current_military_time = datetime.now().strftime('%Y%m%d%H%M%S')
# Setup Module logging. Main logging is configured in system_logging.py
setup_logging()
@@ -30,22 +37,59 @@
log.setLevel(level)
+def config_file_update():
+ """
+ Function to determine if we need to update our yaml configuration file after an upgrade.
+ """
+ log.debug('config_file_update() Started....')
+ if os.path.isfile(skel_config_file):
+ with open(config_file, 'r') as current_config:
+ current_config = yaml.safe_load(current_config)
+ with open(skel_config_file, 'r') as temp_config:
+ temp_config = yaml.safe_load(temp_config)
+ temp_current_config = flatten(current_config)
+ temp_temp_config = flatten(temp_config)
+ updates = (dict((k, v) for k, v in temp_temp_config.items() if k not in temp_current_config))
+ if updates != {}:
+ copyfile(skel_config_file, (str(Path.home()) + '/.config/plot_manager/Config_Instructions.yaml'))
+ copyfile(config_file, (str(Path.home()) + f'/.config/plot_manager/plot_manager.yaml.{current_military_time}'))
+ temp_current_config.update(updates)
+ new_config = (dict((k, v) for k, v in temp_current_config.items() if k in temp_temp_config))
+ else:
+ new_config = (dict((k, v) for k, v in temp_current_config.items() if k not in temp_temp_config))
+ if new_config != {}:
+ new_config = (dict((k, v) for k, v in temp_current_config.items() if k in temp_temp_config))
+ current_config = unflatten(new_config)
+ current_config.update({'configured': False})
+ with open((str(Path.home()) + '/.config/plot_manager/plot_manager.yaml'), 'w') as f:
+ yaml.safe_dump(current_config, f)
+ log.debug(f'Config File: {config_file} updated. Update as necessary to run this script.')
+ exit()
+ else:
+ log.debug('No config file changes necessary! No changes made.')
+ else:
+ log.debug('New configuration file not found. No changes made.')
+
+
+
class PlotManager:
if not os.path.isfile(config_file):
log.debug(f'Plot_Manager config file does not exist at: {config_file}')
log.debug("Please check file path and try again.")
exit()
else:
- def __init__(self, configured, hostname, pools, remote_harvesters,
+ def __init__(self, configured, hostname, pools, remote_harvesters, network_interface_threshold, domain_name,
notifications, pb, email, sms, temp_dirs, temp_dirs_critical, network_interface,
dst_dirs, dst_dirs_critical, dst_dirs_critical_alert_sent,temp_dirs_critical_alert_sent,
warnings, emails, phones, twilio_from, twilio_account,
twilio_token, pb_api, logging, log_level):
self.configured = configured
self.hostname = hostname
+ self.domain_name = domain_name
self.pools = pools
self.remote_harvesters = remote_harvesters
self.network_interface = network_interface
+ self.network_interface_threshold = network_interface_threshold
self.notifications = notifications
self.pb = pb
self.email = email
@@ -73,9 +117,11 @@ def read_configs(cls):
return cls(
configured=server['configured'],
hostname=server['hostname'],
+ domain_name=server['domain_name'],
pools=server['pools'],
remote_harvesters=server['remote_harvesters'],
network_interface=server['network_interface'],
+ network_interface_threshold=server['network_interface_threshold'],
notifications=server['notifications']['active'],
pb=server['notifications']['methods']['pb'],
email=server['notifications']['methods']['email'],
diff --git a/chiaplot/send_plot.sh b/chiaplot/send_plot.sh
deleted file mode 100644
index f3b50c9..0000000
--- a/chiaplot/send_plot.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/bash
-#
-ssh root@$3 "nohup /root/plot_manager/receive_plot.sh $2 > foo.out 2> foo.err < /dev/null &"
-sudo /usr/bin/pv "$1" | sudo /usr/bin/nc -q 5 $3 4040
-exit
diff --git a/install.sh b/install.sh
index bc87e80..3f13079 100644
--- a/install.sh
+++ b/install.sh
@@ -1,6 +1,6 @@
#! /bin/bash
-# Version V0.94 2021-08-08
+# Version V0.95 2021-09-03
# Simple Install script for NEW clean Ubuntu 20.04 install, updates
# the system with various tools and tings required to run the various
@@ -213,7 +213,7 @@ nuke_snap (){
update_software_and_system(){
echo -e "\n\n${green}Updating System Software and Installing Required Packages.........${nc}\n"
apt update && apt upgrade -y # Let's do the basic update of our software before we do anything else
- apt install locate vim wget smartmontools tree unzip net-tools tmux glances python3-pip pv nmap sysstat postfix mailutils -y
+ apt install locate vim wget smartmontools tree unzip net-tools tmux python3-pip pv nmap sysstat postfix mailutils -y
if [ $(dpkg-query -W -f='${Status}' openssh-server 2>/dev/null | grep -c "ok installed") -eq 0 ]; then
apt install openssh-server -y
systemctl enable openssh
@@ -400,6 +400,509 @@ set_cpu_performance(){
fi
}
+
+create_check_network_io_script(){
+ echo -e "\nCreating ${green}$current_directory/check_network_io.sh${nc}....."
+ cat <>$current_directory/check_network_io.sh
+#! /bin/bash
+/usr/bin/sar -n DEV 1 3 | egrep \$1 > $current_directory/network_stats.io
+EOF
+}
+
+## Share some final notes...
+final_notes(){
+ get_current_directory
+ echo -e "\n\nThank you for choosing to try ${green}plot_manager${nc}, I hope it works well for you. If you"
+ echo -e "Have any trouble or issues, please feel free to reach me on my github page.\n"
+ echo -e "Before you go, please remember to check your settings to make sure they"
+ echo -e "agree with your configuration, otherwise you will have issues with the script(s)."
+ echo -e "By default, the file paths assume installation at /root/plot_manager\n\n"
+ echo -e "Please edit ${yellow}/root/.config/plot_manager/plot_manager.yaml${nc} and verify all"
+ echo -e "of the settings. Failure to do so will prevent the script from running properly.\n"
+
+ echo -e "To view this again at any time run ${yellow}$current_directory/install.sh notes${nc}\n"
+}
+
+thank_you(){
+ echo -e "\n\nInstall Process Complete - Thank You and have a ${red}G${yellow}R${white}E${green}A${blue}T${nc} Day!\n\n"
+}
+
+## Print out help when needed
+help (){
+echo -e "\nWelcome to ${green}Chia Plot Manager${nc} and associated utilities!\n"
+echo -e "Options:"
+echo -e " ${yellow}nas${nc} Starts the install process for a NAS/Harvester."
+echo -e " ${yellow}plot${nc} Starts the install process for a dedicated Plotter."
+echo -e " ${yellow}coin${nc} Starts the install process for the Coin Monitor on a Full Node."
+echo -e " ${yellow}auto_drive${nc} Starts the install process for a stand alone version of Auto_Drive"
+echo -e " ${yellow}network${nc} Only install network performance updates and exits."
+echo -e " ${yellow}notes${nc} Shows after-installation notes."
+echo -e " ${yellow}help${nc} Shows this help message.\n"
+echo -e "For additional help, please open an issue on my github page.\n"
+}
+
+## Here is where we start our install....
+start_install_nas(){
+ must_run_as_root
+ welcome_message_nas
+ get_current_directory_nas_plot
+ nuke_snap
+ update_software_and_system
+ create_example_directory_structure
+ create_check_network_io_script
+ improve_network_performance
+ set_cpu_performance
+ update_crontab_nas
+ clean_up_nas_directory
+ final_notes
+ thank_you
+}
+
+start_install_plot(){
+ must_run_as_root
+ welcome_message_plot
+ get_current_directory_nas_plot
+ nuke_snap
+ update_software_and_system
+ create_check_network_io_script
+ improve_network_performance
+ set_cpu_performance
+ update_crontab_plot
+ clean_up_plot_directory
+ final_notes
+ thank_you
+}
+
+start_install_coin(){
+ must_run_as_root
+ welcome_message_coin#! /bin/bash
+
+# Version V0.95 2021-09-03
+
+# Simple Install script for NEW clean Ubuntu 20.04 install, updates
+# the system with various tools and tings required to run the various
+# parts of chia_plot_manager.
+
+# I use this to create new NAS/Plotter servers.
+
+red='\033[0;31m'
+yellow='\033[0;33m'
+green='\033[0;32m'
+white='\033[0;37m'
+blue='\033[0;34m'
+nc='\033[0m'
+
+## Due to the nature of this script, it must be run as root or via sudo/su.
+must_run_as_root(){
+ if [[ $(id -u) -ne 0 ]]; then
+ echo
+ echo -e "${red}ERROR${nc}: Must run as root!"
+ echo "sudo install.sh option"
+ echo "or su- first."
+ echo
+ exit 1
+ fi
+}
+
+## Welcome everyone....
+welcome_message_nas() {
+ echo -e "\n\n * * * * * * * * Welcome to the ${red}P${yellow}l${green}o${white}t ${blue}M${red}a${yellow}n${green}g${white}e${blue}r${nc} Install Script * * * * * * * *\n"
+ echo -e "${red}WARNING!${nc} - This script assumes you are installing it on a ${yellow}NAS/Harvester${nc}. Some of the items (such as cron entries)"
+ echo -e "${red}WARNING!${nc} - may not be suitable for all installations. If you are ${yellow}UNSURE${nc} what options to choose"
+ echo -e "${red}WARNING!${nc} - during the install (crontab, network performance, creating directories), I suggest that you do not"
+ echo -e "${red}WARNING!${nc} - utilize those options until you understand them completely!."
+ echo
+ echo -e "${red}WARNING!${nc} - ${yellow}I have designed this install script for a Fresh, Clean installation of Ubuntu Linux!${nc}"
+ echo
+ echo -e "${red}WARNING!${nc} - Please ${yellow}CAREFULLY${nc} read the notes at the end (or run ./install.sh notes) at any time."
+ echo -e "${red}WARNING!${nc} - There are a ${yellow}LOT${nc} of configuration changes and path updates that need to be done to"
+ echo -e "${red}WARNING!${nc} - make these scripts your own!\n"
+ echo -e -n "Should we ${green}CONTINUE${nc}? "
+ read -n 1 -r
+ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
+ echo -e "\n${yellow}GOODBYE${nc}"
+ exit 1
+ fi
+}
+
+welcome_message_plot() {
+ echo -e "\n\n * * * * * * * * Welcome to the ${red}P${yellow}l${green}o${white}t ${blue}M${red}a${yellow}n${green}g${white}e${blue}r${nc} Install Script * * * * * * * *\n"
+ echo -e "${red}WARNING!${nc} - This script assumes you are installing it on a ${yellow}Dedicated Plotter${nc}. Some of the items (such as cron entries)"
+ echo -e "${red}WARNING!${nc} - may not be suitable for all installations. If you are ${yellow}UNSURE${nc} what options to choose"
+ echo -e "${red}WARNING!${nc} - during the install (crontab, network performance, creating directories), I suggest that you do not"
+ echo -e "${red}WARNING!${nc} - utilize those options until you understand them completely!."
+ echo
+ echo -e "${red}WARNING!${nc} - ${yellow}I have designed this install script for a Fresh, Clean installation of Ubuntu Linux!${nc}"
+ echo
+ echo -e "${red}WARNING!${nc} - Please ${yellow}CAREFULLY${nc} read the notes at the end (or run ./install.sh notes) at any time."
+ echo -e "${red}WARNING!${nc} - There are a ${yellow}LOT${nc} of configuration changes and path updates that need to be done to"
+ echo -e "${red}WARNING!${nc} - make these scripts your own!\n"
+ echo -e -n "Should we ${green}CONTINUE${nc}? "
+ read -n 1 -r
+ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
+ echo -e "\n${yellow}GOODBYE${nc}"
+ exit 1
+ fi
+}
+
+welcome_message_coin() {
+ echo -e "\n\n * * * * * * * * Welcome to the ${red}P${yellow}l${green}o${white}t ${blue}M${red}a${yellow}n${green}g${white}e${blue}r${nc} Install Script * * * * * * * *\n"
+ echo -e "${red}WARNING!${nc} - This script assumes you are installing it on a ${yellow}Full Node${nc}. Some of the items (such as cron entries)"
+ echo -e "${red}WARNING!${nc} - may not be suitable for all installations. If you are ${yellow}UNSURE${nc} what options to choose"
+ echo -e "${red}WARNING!${nc} - during the install (crontab, network performance, creating directories), I suggest that you do not"
+ echo -e "${red}WARNING!${nc} - utilize those options until you understand them completely!."
+ echo
+ echo -e "${red}WARNING!${nc} - ${yellow}I have designed this install script for a Fresh, Clean installation of Ubuntu Linux!${nc}"
+ echo
+ echo -e "${red}WARNING!${nc} - Please ${yellow}CAREFULLY${nc} read the notes at the end (or run ./install.sh notes) at any time."
+ echo -e "${red}WARNING!${nc} - There are a ${yellow}LOT${nc} of configuration changes and path updates that need to be done to"
+ echo -e "${red}WARNING!${nc} - make these scripts your own!\n"
+ echo -e -n "Should we ${green}CONTINUE${nc}? "
+ read -n 1 -r
+ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
+ echo -e "\n${yellow}GOODBYE${nc}"
+ exit 1
+ fi
+}
+
+
+## Creates our entire plot directory structure based on predefined layouts
+create_example_directory_structure(){
+ echo -e -n "\nShould we ${yellow}CREATE${nc} mount points using example directory structure? "
+ read -n 1 -r
+ echo
+ if [[ $REPLY =~ ^[Yy]$ ]]; then
+ nl $current_directory/extras/drive_structures/drive_structures
+ count="$(wc -l $current_directory/extras/drive_structures/drive_structures | cut -f 1 -d' ')"
+ n=""
+ while true; do
+ echo
+ read -p "Please Select Drive Structure to CREATE: " n
+ if [ "$n" -eq "$n" ] && [ "$n" -gt 0 ] && [ "$n" -le "$count" ]
+ then
+ break
+ fi
+ done
+ structure_selected="$(sed -n "${n}p" $current_directory/extras/drive_structures/drive_structures)"
+ echo
+ echo -e -n "You selected ${yellow}$structure_selected${nc}. Is this ${yellow}CORRECT${nc}? "
+ read -n 1 -r
+ echo
+ if [[ $REPLY =~ ^[Yy]$ ]]
+ then
+ echo -e "We will duplicate ${blue}$structure_selected${nc}"
+ xargs mkdir -p < $current_directory/extras/drive_structures/$structure_selected
+ tree -d /mnt
+ else
+ create_example_directory_structure
+ fi
+ fi
+}
+
+
+### Clean Up Directories
+clean_up_nas_directory(){
+ echo -e "\n\n${green}Cleaning Up Directory Structure & Setting File Permissions.........${nc}\n"
+ mv $current_directory/chianas/* $current_directory/
+ mv $current_directory/utilities/auto_drive.py $current_directory/
+ mv $current_directory/utilities/get_drive_uuid.sh $current_directory/
+ rm -rf $current_directory/chianas
+ rm -rf $current_directory/chiaplot
+ rm -rf $current_directory/coin_monitor
+ rm -rf $current_directory/auto_drive
+ rm -rf $current_directory/images
+ chmod +x $current_directory/*.py
+ chmod +x $current_directory/*.sh
+ chmod +x $current_directory/utilities/*.py
+ chmod +x $current_directory/utilities/*.sh
+ mkdir -p /root/.config/plot_manager
+ if test -f "/root/.config/plot_manager/plot_manager.yaml"; then
+ echo -e "/root/.config/plot_manager/plot_manager.yaml already exists....\n"
+ echo -e "Launching our ${blue}Configuration File Updater${nc} to see if updates are needed.......\n"
+ $current_directory/config_file_updater.py
+ else
+ cp $current_directory/plot_manager.skel.yaml /root/.config/plot_manager/plot_manager.yaml
+ cp $current_directory/plot_manager.skel.yaml /root/.config/plot_manager/INSTRUCTIONS.yaml
+ rm $current_directory/plot_manager.skel.yaml
+ fi
+}
+
+### Clean Up Directories
+clean_up_plot_directory(){
+ echo -e "\n\n${green}Cleaning Up Directory Structure & Setting File Permissions.........${nc}\n"
+ mv $current_directory/chiaplot/* $current_directory/
+ cp $current_directory/chianas/config_file_updater.py $current_directory
+ rm -rf $current_directory/chiaplot
+ rm -rf $current_directory/chianas
+ rm -rf $current_directory/coin_monitor
+ rm -rf $current_directory/auto_drive
+ rm -rf $current_directory/images
+ chmod +x $current_directory/*.py
+ chmod +x $current_directory/*.sh
+ mkdir -p /root/.config/plot_manager
+ if test -f "/root/.config/plot_manager/plot_manager.yaml"; then
+ echo -e "/root/.config/plot_manager/plot_manager.yaml already exists....\n"
+ echo -e "Launching our ${blue}Configuration File Updater${nc} to see if updates are needed.......\n"
+ $current_directory/config_file_updater.py
+ else
+ cp $current_directory/plot_manager.skel.yaml /root/.config/plot_manager/plot_manager.yaml
+ cp $current_directory/plot_manager.skel.yaml /root/.config/plot_manager/INSTRUCTIONS.yaml
+ rm $current_directory/plot_manager.skel.yaml
+ fi
+}
+
+### Clean Up Directories
+clean_up_coin_directory(){
+ echo -e "\n\n${green}Cleaning Up Directory Structure & Setting File Permissions.........${nc}\n"
+ mv $current_directory/coin_monitor/* $current_directory/
+ rm -rf $current_directory/chianas
+ rm -rf $current_directory/chiaplot
+ rm -rf $current_directory/coin_monitor
+ rm -rf $current_directory/auto_drive
+ rm -rf $current_directory/images
+ chmod +x $current_directory/*.py
+ chmod +x $current_directory/*.sh
+}
+
+## Let's get rid of SNAP, shall we.....
+nuke_snap (){
+ if [ $(dpkg-query -W -f='${Status}' snapd 2>/dev/null | grep -c "ok installed") -eq 1 ];
+ then
+ echo -e -n "\nShould we [${yellow}UNINSTALL${nc}] SNAP? "
+ read -n 1 -r
+ echo
+ if [[ $REPLY =~ ^[Yy]$ ]]; then
+ echo -e "\n\n${green}Uninstalling SNAP.........${nc}\n"
+ systemctl stop snapd && systemctl disable snapd
+ apt purge snapd
+ rm -rf ~/snap
+ rm -rf /snap /var/snap /var/lib/snapd /var/cache/snapd /usr/lib/snapd
+ mv $current_directory/extras/no-snap.pref /etc/apt/preferences.d/
+ chown root:root /etc/apt/preferences.d/no-snap.pref
+ echo -e "${green}DONE${nc}\n"
+ else
+ echo -e -n "\nSNAP ${yellow}PRESERVED${nc}!"
+ fi
+ fi
+}
+
+## Here is where we do all of the software updating that we need to do to
+update_software_and_system(){
+ echo -e "\n\n${green}Updating System Software and Installing Required Packages.........${nc}\n"
+ apt update && apt upgrade -y # Let's do the basic update of our software before we do anything else
+ apt install locate vim wget smartmontools tree unzip net-tools tmux python3-pip pv nmap sysstat postfix mailutils -y
+ if [ $(dpkg-query -W -f='${Status}' openssh-server 2>/dev/null | grep -c "ok installed") -eq 0 ]; then
+ apt install openssh-server -y
+ systemctl enable openssh
+ systemctl start openssh
+ fi
+ pip3 install -r $current_directory/chianas/requirements.txt
+ cd $current_directory
+ git clone https://github.com/truenas/py-SMART.git
+ cd py-SMART
+ python3 setup.py install
+ cd ..
+ rm -rf $current_directory/py-SMART
+ apt autoremove -y
+ echo -e "${green}DONE${nc}\n"
+}
+
+
+## Figure out exactly what directory we are in so we can make decisions...
+get_current_directory(){
+ SOURCE="${BASH_SOURCE[0]}"
+while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
+ DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
+ SOURCE="$(readlink "$SOURCE")"
+ [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
+done
+current_directory="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
+}
+
+## Figure out exactly what directory we are in so we can make decisions...
+get_current_directory_nas_plot(){
+ SOURCE="${BASH_SOURCE[0]}"
+while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
+ DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
+ SOURCE="$(readlink "$SOURCE")"
+ [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
+done
+current_directory="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
+if [ $current_directory != '/root/plot_manager' ]; then
+ echo -e "\n ${red}* * * * *${white} IMPORTANT ${red}* * * * *${nc}${white} IMPORTANT ${red}* * * * *${nc}${white} IMPORTANT ${red}* * * * *${nc}"
+ echo -e "${green}All scripts assume that they have been installed at ${white}/root/plot_manager${green}"
+ echo -e "and are configured as such. If you are changing the install directory,"
+ echo -e "please review all scripts for the proper paths or they ${red}will${green} fail${nc}.\n\n"
+else
+ echo
+fi
+}
+
+## Figure out exactly what directory we are in so we can make decisions...
+get_current_directory_coin(){
+ SOURCE="${BASH_SOURCE[0]}"
+while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
+ DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
+ SOURCE="$(readlink "$SOURCE")"
+ [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
+done
+current_directory="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
+if [ $current_directory != '/root/coin_manager' ]; then
+ echo -e "\n ${red}* * * * *${white} IMPORTANT ${red}* * * * *${nc}${white} IMPORTANT ${red}* * * * *${nc}${white} IMPORTANT ${red}* * * * *${nc}"
+ echo -e "${green}These scripts assume that they have been installed at ${white}/root/coin_manager${green}"
+ echo -e "and are configured as such. If you are changing the install directory,"
+ echo -e "please review all scripts for the proper paths or they ${red}will${green} fail${nc}.\n\n"
+else
+ echo
+fi
+}
+
+## Update our crontab with the necessary entries
+update_crontab_nas(){
+ get_current_directory
+ echo -e "This will update your root crontab to add the following entries If you need something"
+ echo -e "different, remember to make the necessary changes after the installation has completed."
+ echo -e ""
+ echo -e "PATH=$current_directory:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
+ echo -e "01 00 * * * cd $current_directory && /usr/bin/python3 $current_directory/drive_manager.py -ud >/dev/null 2>&1"
+ echo -e "02 00 * * * cd $current_directory && /usr/bin/python3 $current_directory/drive_manager.py -dr >/dev/null 2>&1"
+ echo -e "*/1 * * * * cd $current_directory && /usr/bin/python3 $current_directory/drive_manager.py >/dev/null 2>&1"
+ echo -e "*/2 * * * * cd $current_directory && /usr/bin/python3 $current_directory/move_local_plots.py >/dev/null 2>&1"
+ echo -e -n "\nShould we ${yellow}UPDATE${nc} Crontab with these entries? "
+ read -n 1 -r
+ if [[ $REPLY =~ ^[Yy]$ ]]; then
+ (crontab -l ; echo "PATH=$current_directory:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin")| crontab -
+ (crontab -l ; echo "01 00 * * * cd $current_directory && /usr/bin/python3 $current_directory/drive_manager.py -ud >/dev/null 2>&1")| crontab -
+ (crontab -l ; echo "02 00 * * * cd $current_directory && /usr/bin/python3 $current_directory/drive_manager.py -dr >/dev/null 2>&1")| crontab -
+ (crontab -l ; echo "*/1 * * * * cd $current_directory && /usr/bin/python3 $current_directory/drive_manager.py >/dev/null 2>&1")| crontab -
+ (crontab -l ; echo "*/2 * * * * cd $current_directory && /usr/bin/python3 $current_directory/move_local_plots.py -ud >/dev/null 2>&1")| crontab -
+ echo -e "\nCrontab has been ${yellow}UPDATED${nc}!\n"
+ else
+ echo -e "\nCrontab has ${red}NOT${nc} been ${yellow}UPDATED${nc}!\n"
+ fi
+}
+
+## Update our crontab with the necessary entries
+update_crontab_plot(){
+ get_current_directory
+ echo -e "This will update your root crontab to add the following entries If you need something"
+ echo -e "different, remember to make the necessary changes after the installation has completed."
+ echo -e ""
+ echo -e "PATH=$current_directory:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
+ echo -e "*/10 * * * * cd $current_directory && /usr/bin/python3 $current_directory/plot_manager.py >/dev/null 2>&1"
+ echo -e -n "\nShould we ${yellow}UPDATE${nc} Crontab with these entries? "
+ read -n 1 -r
+ if [[ $REPLY =~ ^[Yy]$ ]]; then
+ (crontab -l ; echo "PATH=$current_directory:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin")| crontab -
+ (crontab -l ; echo "*/10 * * * * cd $current_directory && /usr/bin/python3 $current_directory/plot_manager.py >/dev/null 2>&1")| crontab -
+ echo -e "\nCrontab has been ${yellow}UPDATED${nc}!\n"
+ else
+ echo -e "\nCrontab has ${red}NOT${nc} been ${yellow}UPDATED${nc}!\n"
+ fi
+}
+
+## Update our crontab with the necessary entries
+update_crontab_coin(){
+ get_current_directory
+ echo -e "This will update your root crontab to add the following entries If you need something"
+ echo -e "different, remember to make the necessary changes after the installation has completed."
+ echo -e ""
+ echo -e "PATH=$current_directory:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
+ echo -e "*/1 * * * * /usr/bin/python3 $current_directory/coin_monitor.py >/dev/null 2>&1"
+ echo -e -n "\nShould we ${yellow}UPDATE${nc} Crontab with these entries? "
+ read -n 1 -r
+ if [[ $REPLY =~ ^[Yy]$ ]]; then
+ (crontab -l ; echo "PATH=$current_directory:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin")| crontab -
+ (crontab -l ; echo "*/1 * * * * /usr/bin/python3 $current_directory/coin_monitor.py >/dev/null 2>&1")| crontab -
+ echo -e "\nCrontab has been ${yellow}UPDATED${nc}!\n"
+ else
+ echo -e "\nCrontab has ${red}NOT${nc} been ${yellow}UPDATED${nc}!\n"
+ fi
+}
+
+## Add entries into sysctl to improve network performance
+improve_network_performance(){
+ must_run_as_root
+ echo -e "\nNetwork Performance settings that >>> ${blue}I${nc} <<< use on my 10Gbe connected"
+ echo -e "plotters, harvesters, and farmers. Your performance may vary from mine!\n"
+ echo -e -n "\nShould we ${yellow}UPDATE${nc} Network Performance Configuration? "
+ read -n 1 -r
+ echo
+ if [[ $REPLY =~ ^[Yy]$ ]]; then
+ echo "vm.swappiness=1" >> /etc/sysctl.conf
+ echo "net.core.wmem_max=134217728" >> /etc/sysctl.conf
+ echo "net.ipv4.tcp_wmem=2097152 16777216 33554432" >> /etc/sysctl.conf
+ echo "net.core.rmem_max=134217728" >> /etc/sysctl.conf
+ echo "net.ipv4.tcp_rmem=2097152 16777216 33554432" >> /etc/sysctl.conf
+ echo "net.ipv4.tcp_window_scaling = 1" >> /etc/sysctl.conf
+ echo "net.ipv4.tcp_timestamps=1" >> /etc/sysctl.conf
+ echo "net.ipv4.tcp_no_metrics_save=1" >> /etc/sysctl.conf
+ echo "net.ipv4.tcp_low_latency=0" >> /etc/sysctl.conf
+ echo "net.ipv4.tcp_mtu_probing=1" >> /etc/sysctl.conf
+ echo "net.ipv4.tcp_sack = 0" >> /etc/sysctl.conf
+ echo "net.ipv4.tcp_dsack = 0" >> /etc/sysctl.conf
+ echo "net.ipv4.tcp_fack = 0" >> /etc/sysctl.conf
+ echo "net.ipv4.tcp_slow_start_after_idle = 0 " >> /etc/sysctl.conf
+ echo "net.ipv4.ipfrag_high_threshold = 8388608" >> /etc/sysctl.conf
+ echo "net.core.netdev_max_backlog = 30000" >> /etc/sysctl.conf
+ sysctl -e -p /etc/sysctl.conf
+ echo -e "${green}DONE${nc}\n"
+ else
+ echo -e "${green}DONE${nc} - We will ${red}NOT${nc} update performance settings.\n"
+ fi
+}
+
+## Set CPU scaling governor to Performance
+set_cpu_performance(){
+ echo -e -n "\nShould we set our CPU Scaling Governor to ${yellow}PERFORMANCE${nc}? "
+ read -n 1 -r
+ echo
+ if [[ $REPLY =~ ^[Yy]$ ]]; then
+ for file in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do echo "performance" > $file; done
+ echo -e "${yellow}PERFORMANCE${nc} mode set for all cores!\n"
+ echo -e -n "\nAdd to ${yellow}Crontab${nc} for persistence across reboots? "
+ read -n 1 -r
+ echo
+ if [[ $REPLY =~ ^[Yy]$ ]]; then
+ (crontab -l ; echo "@reboot /root/set_cpu_to_performance.sh")| crontab -
+ echo -e "\n${yellow}@reboot /root/set_cpu_to_performance.sh${nc} added to root crontab"
+ mv $current_directory/extras/set_cpu_to_performance.sh /root/
+ chmod +x /root/set_cpu_to_performance.sh
+ echo -e "${red}IMPORTANT${nc}: Recommend ${yellow}rebooting${nc} to ensure proper operation!"
+ else
+ echo -e "${yellow}set_cpu_to_performance.sh${nc} was ${red}NOT${nc} added to crontab"
+ fi
+ else
+ echo -e "CPU Scaling Governor ${yellow}NOT${nc} adjusted.\n"
+ fi
+}
+
+
+create_check_network_io_script(){
+ echo -e "\nCreating ${green}$current_directory/check_network_io.sh${nc}....."
+ cat <>$current_directory/check_network_io.sh
+#! /bin/bash
+/usr/bin/sar -n DEV 1 3 | egrep \$1 > $current_directory/network_stats.io
+EOF
+}
+
+create_send_plot_script(){
+ cat <>$current_directory/send_plot.sh
+#!/bin/bash
+
+# Script that we call to send our plots to the selected NAS. Created by the install script
+# in order to grab the current working directory. If you have installed you script in a location
+# other than $current_directory on you NAS, you will need to update this script to match that
+# directory location!
+
+ssh root@\$3 "nohup $current_directory/receive_plot.sh \$2 > foo.out 2> foo.err < /dev/null &"
+sudo /usr/bin/pv "\$1" | sudo /usr/bin/nc -q 5 \$3 4040
+exit
+EOF
+}
+
+
+
## Share some final notes...
final_notes(){
get_current_directory
@@ -440,6 +943,7 @@ start_install_nas(){
nuke_snap
update_software_and_system
create_example_directory_structure
+ create_check_network_io_script
improve_network_performance
set_cpu_performance
update_crontab_nas
@@ -454,6 +958,8 @@ start_install_plot(){
get_current_directory_nas_plot
nuke_snap
update_software_and_system
+ create_check_network_io_script
+ create_send_plot_script
improve_network_performance
set_cpu_performance
update_crontab_plot
@@ -476,6 +982,29 @@ start_install_coin(){
thank_you
}
+## And we're off....
+case "$1" in
+ nas) start_install_nas ;;
+ plot) start_install_plot ;;
+ coin) start_install_coin ;;
+ help) help ;;
+ network) improve_network_performance ;;
+ notes) final_notes ;;
+ *) echo -e "\n${yellow}Usage${nc}: $0 [ nas | plot | coin | network | notes | help ]\n" >&2
+ exit 1
+ ;;
+ esac
+ get_current_directory_coin
+ nuke_snap
+ update_software_and_system
+ improve_network_performance
+ set_cpu_performance
+ update_crontab_coin
+ clean_up_coin_directory
+ final_notes
+ thank_you
+}
+
## And we're off....
case "$1" in
nas) start_install_nas ;;