Merge branch 'dev' into 1201
This commit is contained in:
commit
5658b19cac
6 changed files with 645 additions and 57 deletions
|
|
@ -1,9 +1,23 @@
|
||||||
# Wizard Kit: Functions - HW Diagnostics
|
# Wizard Kit: Functions - HW Diagnostics
|
||||||
|
|
||||||
|
import base64
|
||||||
|
import Gnuplot
|
||||||
import json
|
import json
|
||||||
|
import math
|
||||||
|
import mysql.connector as mariadb
|
||||||
|
import requests
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from functions.common import *
|
from functions.common import *
|
||||||
|
from numpy import *
|
||||||
|
|
||||||
|
# Database connection
|
||||||
|
ost_db = {
|
||||||
|
'Connection': None,
|
||||||
|
'Cursor': None,
|
||||||
|
'Errors': False,
|
||||||
|
'Tunnel': None,
|
||||||
|
}
|
||||||
|
|
||||||
# STATIC VARIABLES
|
# STATIC VARIABLES
|
||||||
ATTRIBUTES = {
|
ATTRIBUTES = {
|
||||||
|
|
@ -29,13 +43,14 @@ ATTRIBUTES = {
|
||||||
}
|
}
|
||||||
IO_VARS = {
|
IO_VARS = {
|
||||||
'Block Size': 512*1024,
|
'Block Size': 512*1024,
|
||||||
'Chunk Size': 16*1024**2,
|
'Chunk Size': 32*1024**2,
|
||||||
'Minimum Dev Size': 8*1024**3,
|
'Minimum Dev Size': 8*1024**3,
|
||||||
'Minimum Test Size': 10*1024**3,
|
'Minimum Test Size': 10*1024**3,
|
||||||
'Alt Test Size Factor': 0.01,
|
'Alt Test Size Factor': 0.01,
|
||||||
'Progress Refresh Rate': 5,
|
'Progress Refresh Rate': 5,
|
||||||
'Scale 16': [2**(0.6*x)+(16*x) for x in range(1,17)],
|
'Scale 8': [2**(0.56*(x+1))+(16*(x+1)) for x in range(8)],
|
||||||
'Scale 32': [2**(0.6*x/2)+(16*x/2) for x in range(1,33)],
|
'Scale 16': [2**(0.56*(x+1))+(16*(x+1)) for x in range(16)],
|
||||||
|
'Scale 32': [2**(0.56*(x+1)/2)+(16*(x+1)/2) for x in range(32)],
|
||||||
'Threshold Fail': 65*1024**2,
|
'Threshold Fail': 65*1024**2,
|
||||||
'Threshold Warn': 135*1024**2,
|
'Threshold Warn': 135*1024**2,
|
||||||
'Threshold Great': 750*1024**2,
|
'Threshold Great': 750*1024**2,
|
||||||
|
|
@ -51,6 +66,16 @@ IO_VARS = {
|
||||||
'███▏', '███▎', '███▍', '███▌',
|
'███▏', '███▎', '███▍', '███▌',
|
||||||
'███▋', '███▊', '███▉', '████'),
|
'███▋', '███▊', '███▉', '████'),
|
||||||
}
|
}
|
||||||
|
OST_STAFF_ID = '23'
|
||||||
|
OST_STAFF_NAME = 'Wizard Kit'
|
||||||
|
OST_SQL_SET_HOLD = "UPDATE `{db_name}`.`ost_ticket` SET `hold` = '{hold_type}' WHERE `ost_ticket`.`ticket_id` = {ticket_id};"
|
||||||
|
OST_SQL_SET_FLAG = "UPDATE `{db_name}`.`ost_ticket` SET `{flag}` = '{value}' WHERE `ost_ticket`.`ticket_id` = {ticket_id};"
|
||||||
|
OST_SQL_POST_REPLY = ("INSERT INTO `{db_name}`.`ost_ticket_response` (ticket_id, staff_id, staff_name, response, created) "
|
||||||
|
"VALUES ('{ticket_id}', '{staff_id}', '{staff_name}', '{response}', '{created}');")
|
||||||
|
OST_DRIVE_FLAG = 'zHDTune'
|
||||||
|
OST_DRIVE_PASSED = 1
|
||||||
|
OST_DRIVE_FAILED = 2
|
||||||
|
OST_NEEDS_ATTENTION = 4
|
||||||
TESTS = {
|
TESTS = {
|
||||||
'Prime95': {
|
'Prime95': {
|
||||||
'Enabled': False,
|
'Enabled': False,
|
||||||
|
|
@ -68,17 +93,90 @@ TESTS = {
|
||||||
},
|
},
|
||||||
'iobenchmark': {
|
'iobenchmark': {
|
||||||
'Enabled': False,
|
'Enabled': False,
|
||||||
|
'Data': {},
|
||||||
'Results': {},
|
'Results': {},
|
||||||
'Status': {},
|
'Status': {},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
def generate_horizontal_graph(rates):
|
def connect_to_db():
|
||||||
|
"""Connect to osTicket database via SSH tunnel."""
|
||||||
|
cmd = [
|
||||||
|
'ssh', '-N', '-p', SSH_PORT, '-L3306:127.0.0.1:3306',
|
||||||
|
'{user}@{host}'.format(user=SSH_USER, host=DB_HOST),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Establish SSH tunnel unless one already exists
|
||||||
|
if not ost_db['Tunnel']:
|
||||||
|
ost_db['Tunnel'] = popen_program(cmd)
|
||||||
|
|
||||||
|
# Establish SQL connection (try a few times in case SSH is slow)
|
||||||
|
for x in range(5):
|
||||||
|
sleep(3)
|
||||||
|
try:
|
||||||
|
ost_db['Connection'] = mariadb.connect(
|
||||||
|
user=DB_USER, password=DB_PASS, database=DB_NAME)
|
||||||
|
except:
|
||||||
|
# Just try again
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
ost_db['Cursor'] = ost_db['Connection'].cursor()
|
||||||
|
ost_db['Errors'] = False
|
||||||
|
|
||||||
|
def disconnect_from_db():
|
||||||
|
"""Disconnect from SQL DB and close SSH tunnel."""
|
||||||
|
if ost_db['Cursor']:
|
||||||
|
ost_db['Cursor'].close()
|
||||||
|
if ost_db['Connection']:
|
||||||
|
ost_db['Connection'].close()
|
||||||
|
if ost_db['Tunnel']:
|
||||||
|
ost_db['Tunnel'].kill()
|
||||||
|
|
||||||
|
def export_png_graph(name, dev):
|
||||||
|
"""Exports PNG graph using gnuplot, returns file path as str."""
|
||||||
|
max_rate = max(TESTS['iobenchmark']['Data'][name]['Read Rates'])
|
||||||
|
max_rate /= (1024**2)
|
||||||
|
max_rate = max(800, max_rate)
|
||||||
|
out_path = '{}/iobenchmark-{}.png'.format(global_vars['LogDir'], name)
|
||||||
|
plot_data = '{}/iobenchmark-{}-raw.log'.format(global_vars['LogDir'], name)
|
||||||
|
|
||||||
|
# Adjust Y-axis range to either 1000 or roughly max rate + 200
|
||||||
|
## Round up to the nearest 100 and then add 200
|
||||||
|
y_range = (math.ceil(max_rate/100)*100) + 200
|
||||||
|
|
||||||
|
# Run gnuplot commands
|
||||||
|
g = Gnuplot.Gnuplot()
|
||||||
|
g('reset')
|
||||||
|
g('set output "{}"'.format(out_path))
|
||||||
|
g('set terminal png large size 660,300 truecolor font "Noto Sans,11"')
|
||||||
|
g('set title "I/O Benchmark"')
|
||||||
|
g('set yrange [0:{}]'.format(y_range))
|
||||||
|
g('set style data lines')
|
||||||
|
g('plot "{data}" title "{size} ({tran}) {model} {serial}"'.format(
|
||||||
|
data=plot_data,
|
||||||
|
size=dev['lsblk'].get('size', '???b'),
|
||||||
|
tran=dev['lsblk'].get('tran', '???'),
|
||||||
|
model=dev['lsblk'].get('model', 'Unknown Model'),
|
||||||
|
serial=dev['lsblk'].get('serial', 'Unknown Serial'),
|
||||||
|
))
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
g.close()
|
||||||
|
del(g)
|
||||||
|
|
||||||
|
return out_path
|
||||||
|
|
||||||
|
def generate_horizontal_graph(rates, oneline=False):
|
||||||
"""Generate two-line horizontal graph from rates, returns str."""
|
"""Generate two-line horizontal graph from rates, returns str."""
|
||||||
line_top = ''
|
line_1 = ''
|
||||||
line_bottom = ''
|
line_2 = ''
|
||||||
|
line_3 = ''
|
||||||
|
line_4 = ''
|
||||||
for r in rates:
|
for r in rates:
|
||||||
step = get_graph_step(r, scale=16)
|
step = get_graph_step(r, scale=32)
|
||||||
|
if oneline:
|
||||||
|
step = get_graph_step(r, scale=8)
|
||||||
|
|
||||||
# Set color
|
# Set color
|
||||||
r_color = COLORS['CLEAR']
|
r_color = COLORS['CLEAR']
|
||||||
|
|
@ -90,15 +188,35 @@ def generate_horizontal_graph(rates):
|
||||||
r_color = COLORS['GREEN']
|
r_color = COLORS['GREEN']
|
||||||
|
|
||||||
# Build graph
|
# Build graph
|
||||||
if step < 8:
|
full_block = '{}{}'.format(r_color, IO_VARS['Graph Horizontal'][-1])
|
||||||
line_top += ' '
|
if step >= 24:
|
||||||
line_bottom += '{}{}'.format(r_color, IO_VARS['Graph Horizontal'][step])
|
line_1 += '{}{}'.format(r_color, IO_VARS['Graph Horizontal'][step-24])
|
||||||
|
line_2 += full_block
|
||||||
|
line_3 += full_block
|
||||||
|
line_4 += full_block
|
||||||
|
elif step >= 16:
|
||||||
|
line_1 += ' '
|
||||||
|
line_2 += '{}{}'.format(r_color, IO_VARS['Graph Horizontal'][step-16])
|
||||||
|
line_3 += full_block
|
||||||
|
line_4 += full_block
|
||||||
|
elif step >= 8:
|
||||||
|
line_1 += ' '
|
||||||
|
line_2 += ' '
|
||||||
|
line_3 += '{}{}'.format(r_color, IO_VARS['Graph Horizontal'][step-8])
|
||||||
|
line_4 += full_block
|
||||||
else:
|
else:
|
||||||
line_top += '{}{}'.format(r_color, IO_VARS['Graph Horizontal'][step-8])
|
line_1 += ' '
|
||||||
line_bottom += '{}{}'.format(r_color, IO_VARS['Graph Horizontal'][-1])
|
line_2 += ' '
|
||||||
line_top += COLORS['CLEAR']
|
line_3 += ' '
|
||||||
line_bottom += COLORS['CLEAR']
|
line_4 += '{}{}'.format(r_color, IO_VARS['Graph Horizontal'][step])
|
||||||
return '{}\n{}'.format(line_top, line_bottom)
|
line_1 += COLORS['CLEAR']
|
||||||
|
line_2 += COLORS['CLEAR']
|
||||||
|
line_3 += COLORS['CLEAR']
|
||||||
|
line_4 += COLORS['CLEAR']
|
||||||
|
if oneline:
|
||||||
|
return line_4
|
||||||
|
else:
|
||||||
|
return '\n'.join([line_1, line_2, line_3, line_4])
|
||||||
|
|
||||||
def get_graph_step(rate, scale=16):
|
def get_graph_step(rate, scale=16):
|
||||||
"""Get graph step based on rate and scale, returns int."""
|
"""Get graph step based on rate and scale, returns int."""
|
||||||
|
|
@ -112,6 +230,30 @@ def get_graph_step(rate, scale=16):
|
||||||
break
|
break
|
||||||
return step
|
return step
|
||||||
|
|
||||||
|
def get_osticket_number():
|
||||||
|
"""Get ticket number and confirm with name from osTicket DB."""
|
||||||
|
ticket_number = None
|
||||||
|
if not ost_db['Cursor']:
|
||||||
|
# No DB access, return None to disable integration
|
||||||
|
return None
|
||||||
|
while ticket_number is None:
|
||||||
|
print_standard(' ')
|
||||||
|
_input = input('Enter ticket number (or leave blank to disable): ')
|
||||||
|
if re.match(r'^\s*$', _input):
|
||||||
|
if ask('Disable osTicket integration for this run?'):
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
if not re.match(r'^([0-9]+)$', _input):
|
||||||
|
continue
|
||||||
|
_name = osticket_get_ticket_name(_input)
|
||||||
|
if _name:
|
||||||
|
print_standard('You have selected ticket #{} {}'.format(
|
||||||
|
_input, _name))
|
||||||
|
if ask('Is this correct?'):
|
||||||
|
ticket_number = _input
|
||||||
|
return ticket_number
|
||||||
|
|
||||||
def get_read_rate(s):
|
def get_read_rate(s):
|
||||||
"""Get read rate in bytes/s from dd progress output."""
|
"""Get read rate in bytes/s from dd progress output."""
|
||||||
real_rate = None
|
real_rate = None
|
||||||
|
|
@ -200,15 +342,22 @@ def menu_diags(*args):
|
||||||
spacer = '──────────────────────────')
|
spacer = '──────────────────────────')
|
||||||
if selection.isnumeric():
|
if selection.isnumeric():
|
||||||
if diag_modes[int(selection)-1]['Name'] != 'Quick drive test':
|
if diag_modes[int(selection)-1]['Name'] != 'Quick drive test':
|
||||||
|
clear_screen()
|
||||||
|
print_standard(' ')
|
||||||
|
try_and_print(
|
||||||
|
message='Connecting to osTicket database...',
|
||||||
|
function=connect_to_db,
|
||||||
|
width=40)
|
||||||
# Save log for non-quick tests
|
# Save log for non-quick tests
|
||||||
ticket_number = get_ticket_number()
|
ticket_number = get_osticket_number()
|
||||||
global_vars['LogDir'] = '{}/Logs/{}'.format(
|
global_vars['LogDir'] = '{}/Logs/{}_{}'.format(
|
||||||
global_vars['Env']['HOME'],
|
global_vars['Env']['HOME'],
|
||||||
ticket_number if ticket_number else global_vars['Date-Time'])
|
ticket_number,
|
||||||
|
global_vars['Date-Time'])
|
||||||
os.makedirs(global_vars['LogDir'], exist_ok=True)
|
os.makedirs(global_vars['LogDir'], exist_ok=True)
|
||||||
global_vars['LogFile'] = '{}/Hardware Diagnostics.log'.format(
|
global_vars['LogFile'] = '{}/Hardware Diagnostics.log'.format(
|
||||||
global_vars['LogDir'])
|
global_vars['LogDir'])
|
||||||
run_tests(diag_modes[int(selection)-1]['Tests'])
|
run_tests(diag_modes[int(selection)-1]['Tests'], ticket_number)
|
||||||
elif selection == 'A':
|
elif selection == 'A':
|
||||||
run_program(['hw-diags-audio'], check=False, pipe=False)
|
run_program(['hw-diags-audio'], check=False, pipe=False)
|
||||||
pause('Press Enter to return to main menu... ')
|
pause('Press Enter to return to main menu... ')
|
||||||
|
|
@ -230,7 +379,277 @@ def menu_diags(*args):
|
||||||
elif selection == 'Q':
|
elif selection == 'Q':
|
||||||
break
|
break
|
||||||
|
|
||||||
def run_badblocks():
|
# Done
|
||||||
|
disconnect_from_db()
|
||||||
|
|
||||||
|
def osticket_get_ticket_name(ticket_id):
|
||||||
|
"""Lookup ticket and return name as str."""
|
||||||
|
ticket_name = 'Unknown'
|
||||||
|
if not ticket_id:
|
||||||
|
raise GenericError
|
||||||
|
if not ost_db['Cursor']:
|
||||||
|
# Skip section
|
||||||
|
return
|
||||||
|
|
||||||
|
# Set command
|
||||||
|
sql_cmd = "SELECT name FROM `ost_ticket` WHERE `ticket_id` = {}".format(
|
||||||
|
ticket_id)
|
||||||
|
|
||||||
|
# Run command
|
||||||
|
try:
|
||||||
|
ost_db['Cursor'].execute(sql_cmd)
|
||||||
|
for name in ost_db['Cursor']:
|
||||||
|
ticket_name = name[0]
|
||||||
|
return ticket_name
|
||||||
|
except:
|
||||||
|
ost_db['Errors'] = True
|
||||||
|
|
||||||
|
def osticket_needs_attention(ticket_id):
|
||||||
|
"""[DISABLED] Marks the ticket as "NEEDS ATTENTION" in osTicket."""
|
||||||
|
return # This function has been DISABLED due to a repurposing of that flag
|
||||||
|
if not ticket_id:
|
||||||
|
raise GenericError
|
||||||
|
if not ost_db['Cursor']:
|
||||||
|
# Skip section
|
||||||
|
return
|
||||||
|
|
||||||
|
# Set command
|
||||||
|
sql_cmd = OST_SQL_SET_HOLD.format(
|
||||||
|
db_name=DB_NAME,
|
||||||
|
hold_type=OST_NEEDS_ATTENTION,
|
||||||
|
ticket_id=ticket_id)
|
||||||
|
|
||||||
|
# Run command
|
||||||
|
try:
|
||||||
|
ost_db['Cursor'].execute(sql_cmd)
|
||||||
|
except:
|
||||||
|
ost_db['Errors'] = True
|
||||||
|
|
||||||
|
def osticket_post_reply(ticket_id, response):
|
||||||
|
"""Post a reply to a ticket in osTicket."""
|
||||||
|
if not ticket_id:
|
||||||
|
raise GenericError
|
||||||
|
if not ost_db['Cursor']:
|
||||||
|
# Skip section
|
||||||
|
return
|
||||||
|
|
||||||
|
# Set command
|
||||||
|
sql_cmd = OST_SQL_POST_REPLY.format(
|
||||||
|
db_name=DB_NAME,
|
||||||
|
ticket_id=ticket_id,
|
||||||
|
staff_id=OST_STAFF_ID,
|
||||||
|
staff_name=OST_STAFF_NAME,
|
||||||
|
response=response,
|
||||||
|
created=time.strftime("%Y-%m-%d %H:%M:%S"))
|
||||||
|
|
||||||
|
# Run command
|
||||||
|
try:
|
||||||
|
ost_db['Cursor'].execute(sql_cmd)
|
||||||
|
except:
|
||||||
|
ost_db['Errors'] = True
|
||||||
|
|
||||||
|
def osticket_set_drive_result(ticket_id, passed):
|
||||||
|
"""Marks the pass/fail box for the drive(s) in osTicket."""
|
||||||
|
if not ticket_id:
|
||||||
|
raise GenericError
|
||||||
|
if not ost_db['Cursor']:
|
||||||
|
# Skip section
|
||||||
|
return
|
||||||
|
|
||||||
|
# Set command
|
||||||
|
sql_cmd = OST_SQL_SET_FLAG.format(
|
||||||
|
db_name=DB_NAME,
|
||||||
|
flag=OST_DRIVE_FLAG,
|
||||||
|
value=OST_DRIVE_PASSED if passed else OST_DRIVE_FAILED,
|
||||||
|
ticket_id=ticket_id)
|
||||||
|
|
||||||
|
# Run command
|
||||||
|
try:
|
||||||
|
ost_db['Cursor'].execute(sql_cmd)
|
||||||
|
except:
|
||||||
|
ost_db['Errors'] = True
|
||||||
|
|
||||||
|
def pad_with_dots(s, left_pad=True):
|
||||||
|
"""Replace ' ' padding with '..' for osTicket posts."""
|
||||||
|
s = str(s).replace(' ', '..')
|
||||||
|
if '.' in s:
|
||||||
|
if left_pad:
|
||||||
|
s = '.' + s
|
||||||
|
else:
|
||||||
|
s = s + '.'
|
||||||
|
return s
|
||||||
|
|
||||||
|
def post_drive_results(ticket_number):
|
||||||
|
"""Post drive test results to osTicket."""
|
||||||
|
tested = False
|
||||||
|
|
||||||
|
# Check if test(s) were run
|
||||||
|
for t in ['NVMe/SMART', 'badblocks', 'iobenchmark']:
|
||||||
|
tested |= TESTS[t]['Enabled']
|
||||||
|
if not tested or TESTS['NVMe/SMART']['Quick']:
|
||||||
|
# No tests were run so no post necessary
|
||||||
|
return
|
||||||
|
|
||||||
|
# Build reports for all tested devices
|
||||||
|
for name, dev in sorted(TESTS['NVMe/SMART']['Devices'].items()):
|
||||||
|
dev_failed = False
|
||||||
|
dev_passed = True
|
||||||
|
dev_unknown = False
|
||||||
|
report = []
|
||||||
|
|
||||||
|
# Check all test results for dev
|
||||||
|
for t in ['NVMe/SMART', 'badblocks', 'iobenchmark']:
|
||||||
|
if not TESTS[t]['Enabled']:
|
||||||
|
continue
|
||||||
|
status = TESTS[t]['Status'].get(name, 'Unknown')
|
||||||
|
dev_failed |= status == 'NS'
|
||||||
|
dev_passed &= status == 'CS'
|
||||||
|
dev_unknown |= status in ('Working', 'Unknown')
|
||||||
|
if not dev_failed and not dev_passed and not dev_unknown:
|
||||||
|
# Assuming drive was skipped so no reply is needed
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Start drive report
|
||||||
|
if dev_failed:
|
||||||
|
report.append('Drive hardware diagnostics tests: FAILED')
|
||||||
|
elif dev_unknown:
|
||||||
|
report.append('Drive hardware diagnostics tests: UNKNOWN')
|
||||||
|
elif dev_passed:
|
||||||
|
report.append('Drive hardware diagnostics tests: Passed')
|
||||||
|
report.append('')
|
||||||
|
|
||||||
|
# Drive description
|
||||||
|
report.append('{size} ({tran}) {model} {serial}'.format(
|
||||||
|
size=dev['lsblk'].get('size', '???b'),
|
||||||
|
tran=dev['lsblk'].get('tran', '???'),
|
||||||
|
model=dev['lsblk'].get('model', 'Unknown Model'),
|
||||||
|
serial=dev['lsblk'].get('serial', 'Unknown Serial'),
|
||||||
|
))
|
||||||
|
report.append('')
|
||||||
|
|
||||||
|
# Warnings (if any)
|
||||||
|
if dev.get('NVMe Disk', False):
|
||||||
|
if dev['Quick Health Ok']:
|
||||||
|
report.append('WARNING: NVMe support is still experimental')
|
||||||
|
else:
|
||||||
|
report.append('ERROR: NVMe disk is reporting critical warnings')
|
||||||
|
report.append('')
|
||||||
|
elif not dev['SMART Support']:
|
||||||
|
report.append('ERROR: Unable to retrieve SMART data')
|
||||||
|
report.append('')
|
||||||
|
elif not dev['SMART Pass']:
|
||||||
|
report.append('ERROR: SMART overall-health assessment result: FAILED')
|
||||||
|
report.append('')
|
||||||
|
|
||||||
|
# NVMe/SMART Attributes
|
||||||
|
if dev.get('NVMe Disk', False):
|
||||||
|
report.append('NVMe Attributes:')
|
||||||
|
for attrib in sorted(ATTRIBUTES['NVMe'].keys()):
|
||||||
|
if attrib in dev['nvme-cli']:
|
||||||
|
report.append('{attrib:30}{value}'.format(
|
||||||
|
attrib=attrib,
|
||||||
|
value=dev['nvme-cli'][attrib],
|
||||||
|
))
|
||||||
|
report[-1] = report[-1].strip().replace(' ', '.')
|
||||||
|
report[-1] = report[-1].replace('_', ' ')
|
||||||
|
elif dev['smartctl'].get('ata_smart_attributes', None):
|
||||||
|
report.append('SMART Attributes:')
|
||||||
|
s_table = dev['smartctl'].get('ata_smart_attributes', {}).get(
|
||||||
|
'table', {})
|
||||||
|
s_table = {a.get('id', 'Unknown'): a for a in s_table}
|
||||||
|
for attrib in sorted(ATTRIBUTES['SMART'].keys()):
|
||||||
|
if attrib in s_table:
|
||||||
|
# Pad attributewith dots for osTicket
|
||||||
|
hex_str = str(hex(int(attrib))).upper()[2:]
|
||||||
|
hex_str = pad_with_dots('{:>2}'.format(hex_str))
|
||||||
|
dec_str = pad_with_dots('{:>3}'.format(attrib))
|
||||||
|
val_str = '{:<20}'.format(s_table[attrib]['raw']['string'])
|
||||||
|
val_str = pad_with_dots(val_str, left_pad=False)
|
||||||
|
report.append('{:>2}/{:>3}: {} ({})'.format(
|
||||||
|
hex_str,
|
||||||
|
dec_str,
|
||||||
|
val_str,
|
||||||
|
s_table[attrib]['name'],
|
||||||
|
))
|
||||||
|
report[-1] = report[-1].replace('_', ' ')
|
||||||
|
report.append('')
|
||||||
|
|
||||||
|
# badblocks
|
||||||
|
bb_status = TESTS['badblocks']['Status'].get(name, None)
|
||||||
|
if TESTS['badblocks']['Enabled'] and bb_status not in ['Denied', 'Skipped']:
|
||||||
|
report.append('badblocks:')
|
||||||
|
bb_result = TESTS['badblocks']['Results'].get(
|
||||||
|
name,
|
||||||
|
'ERROR: Failed to read log.')
|
||||||
|
for line in bb_result.splitlines():
|
||||||
|
line = line.strip()
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
if re.search('Pass completed', line, re.IGNORECASE):
|
||||||
|
line = re.sub(
|
||||||
|
r'Pass completed,?\s+',
|
||||||
|
r'',
|
||||||
|
line,
|
||||||
|
re.IGNORECASE)
|
||||||
|
report.append(line)
|
||||||
|
report.append('')
|
||||||
|
|
||||||
|
# I/O Benchmark
|
||||||
|
io_status = TESTS['iobenchmark']['Status'].get(name, None)
|
||||||
|
if TESTS['iobenchmark']['Enabled'] and io_status not in ['Denied', 'Skipped']:
|
||||||
|
one_line_graph = generate_horizontal_graph(
|
||||||
|
rates=TESTS['iobenchmark']['Data'][name]['Merged Rates'],
|
||||||
|
oneline=True)
|
||||||
|
for c in COLORS.values():
|
||||||
|
one_line_graph = one_line_graph.replace(c, '')
|
||||||
|
report.append('I/O Benchmark:')
|
||||||
|
report.append(one_line_graph)
|
||||||
|
report.append('{}'.format(
|
||||||
|
TESTS['iobenchmark']['Data'][name]['Avg/Min/Max']))
|
||||||
|
|
||||||
|
# Export PNG
|
||||||
|
try:
|
||||||
|
png_path = export_png_graph(name, dev)
|
||||||
|
except:
|
||||||
|
png_path = None
|
||||||
|
|
||||||
|
# imgur
|
||||||
|
try:
|
||||||
|
url = upload_to_imgur(png_path)
|
||||||
|
report.append('Imgur: {}'.format(url))
|
||||||
|
except:
|
||||||
|
# Oh well
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Nextcloud
|
||||||
|
try:
|
||||||
|
url = upload_to_nextcloud(png_path, ticket_number, name)
|
||||||
|
report.append('Nextcloud: {}'.format(url))
|
||||||
|
except:
|
||||||
|
# Oh well
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Post reply for drive
|
||||||
|
osticket_post_reply(
|
||||||
|
ticket_id=ticket_number,
|
||||||
|
response='\n'.join(report))
|
||||||
|
|
||||||
|
# Mark ticket HDD/SSD pass/fail checkbox (as needed)
|
||||||
|
if dev_failed:
|
||||||
|
osticket_set_drive_result(
|
||||||
|
ticket_id=ticket_number,
|
||||||
|
passed=False)
|
||||||
|
elif dev_unknown:
|
||||||
|
pass
|
||||||
|
elif dev_passed:
|
||||||
|
osticket_set_drive_result(
|
||||||
|
ticket_id=ticket_number,
|
||||||
|
passed=True)
|
||||||
|
|
||||||
|
# Mark ticket as NEEDS ATTENTION
|
||||||
|
osticket_needs_attention(ticket_id=ticket_number)
|
||||||
|
|
||||||
|
def run_badblocks(ticket_number):
|
||||||
"""Run a read-only test for all detected disks."""
|
"""Run a read-only test for all detected disks."""
|
||||||
aborted = False
|
aborted = False
|
||||||
clear_screen()
|
clear_screen()
|
||||||
|
|
@ -292,7 +711,7 @@ def run_badblocks():
|
||||||
run_program('tmux kill-pane -a'.split(), check=False)
|
run_program('tmux kill-pane -a'.split(), check=False)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def run_iobenchmark():
|
def run_iobenchmark(ticket_number):
|
||||||
"""Run a read-only test for all detected disks."""
|
"""Run a read-only test for all detected disks."""
|
||||||
aborted = False
|
aborted = False
|
||||||
clear_screen()
|
clear_screen()
|
||||||
|
|
@ -385,14 +804,16 @@ def run_iobenchmark():
|
||||||
|
|
||||||
# Run dd read tests
|
# Run dd read tests
|
||||||
offset = 0
|
offset = 0
|
||||||
read_rates = []
|
TESTS['iobenchmark']['Data'][name] = {
|
||||||
|
'Graph': [],
|
||||||
|
'Read Rates': []}
|
||||||
for i in range(test_chunks):
|
for i in range(test_chunks):
|
||||||
i += 1
|
i += 1
|
||||||
s = skip_count
|
s = skip_count
|
||||||
c = int(IO_VARS['Chunk Size'] / IO_VARS['Block Size'])
|
c = int(IO_VARS['Chunk Size'] / IO_VARS['Block Size'])
|
||||||
if skip_extra and i % skip_extra == 0:
|
if skip_extra and i % skip_extra == 0:
|
||||||
s += 1
|
s += 1
|
||||||
cmd = 'sudo dd bs={b} skip={s} count={c} if=/dev/{n} of={o}'.format(
|
cmd = 'sudo dd bs={b} skip={s} count={c} if=/dev/{n} of={o} iflag=direct'.format(
|
||||||
b=IO_VARS['Block Size'],
|
b=IO_VARS['Block Size'],
|
||||||
s=offset+s,
|
s=offset+s,
|
||||||
c=c,
|
c=c,
|
||||||
|
|
@ -400,12 +821,18 @@ def run_iobenchmark():
|
||||||
o='/dev/null')
|
o='/dev/null')
|
||||||
result = run_program(cmd.split())
|
result = run_program(cmd.split())
|
||||||
result_str = result.stderr.decode().replace('\n', '')
|
result_str = result.stderr.decode().replace('\n', '')
|
||||||
read_rates.append(get_read_rate(result_str))
|
cur_rate = get_read_rate(result_str)
|
||||||
|
TESTS['iobenchmark']['Data'][name]['Read Rates'].append(
|
||||||
|
cur_rate)
|
||||||
|
TESTS['iobenchmark']['Data'][name]['Graph'].append(
|
||||||
|
'{percent:0.1f} {rate}'.format(
|
||||||
|
percent=i/test_chunks*100,
|
||||||
|
rate=int(cur_rate/(1024**2))))
|
||||||
if i % IO_VARS['Progress Refresh Rate'] == 0:
|
if i % IO_VARS['Progress Refresh Rate'] == 0:
|
||||||
# Update vertical graph
|
# Update vertical graph
|
||||||
update_io_progress(
|
update_io_progress(
|
||||||
percent=i/test_chunks*100,
|
percent=i/test_chunks*100,
|
||||||
rate=read_rates[-1],
|
rate=cur_rate,
|
||||||
progress_file=progress_file)
|
progress_file=progress_file)
|
||||||
# Update offset
|
# Update offset
|
||||||
offset += s + c
|
offset += s + c
|
||||||
|
|
@ -415,24 +842,29 @@ def run_iobenchmark():
|
||||||
run_program(['tmux', 'kill-pane', '-t', bottom_pane])
|
run_program(['tmux', 'kill-pane', '-t', bottom_pane])
|
||||||
|
|
||||||
# Build report
|
# Build report
|
||||||
h_graph_rates = []
|
avg_min_max = 'Average read speed: {:3.1f} MB/s (Min: {:3.1f}, Max: {:3.1f})'.format(
|
||||||
|
sum(TESTS['iobenchmark']['Data'][name]['Read Rates'])/len(
|
||||||
|
TESTS['iobenchmark']['Data'][name]['Read Rates'])/(1024**2),
|
||||||
|
min(TESTS['iobenchmark']['Data'][name]['Read Rates'])/(1024**2),
|
||||||
|
max(TESTS['iobenchmark']['Data'][name]['Read Rates'])/(1024**2))
|
||||||
|
TESTS['iobenchmark']['Data'][name]['Avg/Min/Max'] = avg_min_max
|
||||||
|
TESTS['iobenchmark']['Data'][name]['Merged Rates'] = []
|
||||||
pos = 0
|
pos = 0
|
||||||
width = int(test_chunks / IO_VARS['Graph Horizontal Width'])
|
width = int(test_chunks / IO_VARS['Graph Horizontal Width'])
|
||||||
for i in range(IO_VARS['Graph Horizontal Width']):
|
for i in range(IO_VARS['Graph Horizontal Width']):
|
||||||
# Append average rate for WIDTH number of rates to new array
|
# Append average rate for WIDTH number of rates to new array
|
||||||
h_graph_rates.append(sum(read_rates[pos:pos+width])/width)
|
TESTS['iobenchmark']['Data'][name]['Merged Rates'].append(sum(
|
||||||
|
TESTS['iobenchmark']['Data'][name]['Read Rates'][pos:pos+width])/width)
|
||||||
pos += width
|
pos += width
|
||||||
report = generate_horizontal_graph(h_graph_rates)
|
report = generate_horizontal_graph(
|
||||||
report += '\nRead speed: {:3.1f} MB/s (Min: {:3.1f}, Max: {:3.1f})'.format(
|
TESTS['iobenchmark']['Data'][name]['Merged Rates'])
|
||||||
sum(read_rates)/len(read_rates)/(1024**2),
|
report += '\n{}'.format(avg_min_max)
|
||||||
min(read_rates)/(1024**2),
|
|
||||||
max(read_rates)/(1024**2))
|
|
||||||
TESTS['iobenchmark']['Results'][name] = report
|
TESTS['iobenchmark']['Results'][name] = report
|
||||||
|
|
||||||
# Set CS/NS
|
# Set CS/NS
|
||||||
if min(read_rates) <= IO_VARS['Threshold Fail']:
|
if min(TESTS['iobenchmark']['Data'][name]['Read Rates']) <= IO_VARS['Threshold Fail']:
|
||||||
TESTS['iobenchmark']['Status'][name] = 'NS'
|
TESTS['iobenchmark']['Status'][name] = 'NS'
|
||||||
elif min(read_rates) <= IO_VARS['Threshold Warn']:
|
elif min(TESTS['iobenchmark']['Data'][name]['Read Rates']) <= IO_VARS['Threshold Warn']:
|
||||||
TESTS['iobenchmark']['Status'][name] = 'Unknown'
|
TESTS['iobenchmark']['Status'][name] = 'Unknown'
|
||||||
else:
|
else:
|
||||||
TESTS['iobenchmark']['Status'][name] = 'CS'
|
TESTS['iobenchmark']['Status'][name] = 'CS'
|
||||||
|
|
@ -441,15 +873,14 @@ def run_iobenchmark():
|
||||||
dest_filename = '{}/iobenchmark-{}.log'.format(global_vars['LogDir'], name)
|
dest_filename = '{}/iobenchmark-{}.log'.format(global_vars['LogDir'], name)
|
||||||
shutil.move(progress_file, dest_filename)
|
shutil.move(progress_file, dest_filename)
|
||||||
with open(dest_filename.replace('.', '-raw.'), 'a') as f:
|
with open(dest_filename.replace('.', '-raw.'), 'a') as f:
|
||||||
for rate in read_rates:
|
f.write('\n'.join(TESTS['iobenchmark']['Data'][name]['Graph']))
|
||||||
f.write('{} MB/s\n'.format(rate/(1024**2)))
|
|
||||||
update_progress()
|
update_progress()
|
||||||
|
|
||||||
# Done
|
# Done
|
||||||
run_program('tmux kill-pane -a'.split(), check=False)
|
run_program('tmux kill-pane -a'.split(), check=False)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def run_mprime():
|
def run_mprime(ticket_number):
|
||||||
"""Run Prime95 for MPRIME_LIMIT minutes while showing the temps."""
|
"""Run Prime95 for MPRIME_LIMIT minutes while showing the temps."""
|
||||||
aborted = False
|
aborted = False
|
||||||
print_log('\nStart Prime95 test')
|
print_log('\nStart Prime95 test')
|
||||||
|
|
@ -469,8 +900,10 @@ def run_mprime():
|
||||||
try:
|
try:
|
||||||
for i in range(int(MPRIME_LIMIT)):
|
for i in range(int(MPRIME_LIMIT)):
|
||||||
clear_screen()
|
clear_screen()
|
||||||
print_standard('Running Prime95 ({} minutes left)'.format(
|
min_left = int(MPRIME_LIMIT) - i
|
||||||
int(MPRIME_LIMIT)-i))
|
print_standard('Running Prime95 ({} minute{} left)'.format(
|
||||||
|
min_left,
|
||||||
|
's' if min_left != 1 else ''))
|
||||||
print_warning('If running too hot, press CTRL+c to abort the test')
|
print_warning('If running too hot, press CTRL+c to abort the test')
|
||||||
sleep(60)
|
sleep(60)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
|
|
@ -540,10 +973,45 @@ def run_mprime():
|
||||||
TESTS['Prime95']['Status'] = 'Unknown'
|
TESTS['Prime95']['Status'] = 'Unknown'
|
||||||
update_progress()
|
update_progress()
|
||||||
|
|
||||||
|
# Build osTicket report
|
||||||
|
if TESTS['Prime95']['Status'] not in ['Unknown', 'Aborted']:
|
||||||
|
report = ['System {} Prime95 testing.'.format(
|
||||||
|
'FAILED' if TESTS['Prime95']['NS'] else 'passed')]
|
||||||
|
report.append('')
|
||||||
|
report.append('Prime95 log:')
|
||||||
|
log_path = '{}/prime.log'.format(global_vars['LogDir'])
|
||||||
|
try:
|
||||||
|
with open(log_path, 'r') as f:
|
||||||
|
for line in f.readlines():
|
||||||
|
line = line.strip()
|
||||||
|
r = re.search('(completed \d+ tests.*)', line, re.IGNORECASE)
|
||||||
|
if r:
|
||||||
|
report.append(r.group(1))
|
||||||
|
except:
|
||||||
|
report.append('ERROR: Failed to read log.')
|
||||||
|
report.append('')
|
||||||
|
report.append('Final temps:')
|
||||||
|
log_path = '{}/Final Temps.log'.format(global_vars['LogDir'])
|
||||||
|
try:
|
||||||
|
with open(log_path, 'r') as f:
|
||||||
|
for line in f.readlines():
|
||||||
|
line = line.strip()
|
||||||
|
if not line:
|
||||||
|
# Stop after CPU temp(s)
|
||||||
|
break
|
||||||
|
report.append(line)
|
||||||
|
except:
|
||||||
|
report.append('ERROR: Failed to read log.')
|
||||||
|
|
||||||
|
# Upload osTicket report
|
||||||
|
osticket_post_reply(
|
||||||
|
ticket_id=ticket_number,
|
||||||
|
response='\n'.join(report))
|
||||||
|
|
||||||
# Done
|
# Done
|
||||||
run_program('tmux kill-pane -a'.split())
|
run_program('tmux kill-pane -a'.split())
|
||||||
|
|
||||||
def run_nvme_smart():
|
def run_nvme_smart(ticket_number):
|
||||||
"""Run the built-in NVMe or SMART test for all detected disks."""
|
"""Run the built-in NVMe or SMART test for all detected disks."""
|
||||||
aborted = False
|
aborted = False
|
||||||
clear_screen()
|
clear_screen()
|
||||||
|
|
@ -638,10 +1106,12 @@ def run_nvme_smart():
|
||||||
# Done
|
# Done
|
||||||
run_program('tmux kill-pane -a'.split(), check=False)
|
run_program('tmux kill-pane -a'.split(), check=False)
|
||||||
|
|
||||||
def run_tests(tests):
|
def run_tests(tests, ticket_number=None):
|
||||||
"""Run selected hardware test(s)."""
|
"""Run selected hardware test(s)."""
|
||||||
print_log('Starting Hardware Diagnostics')
|
clear_screen()
|
||||||
print_log('\nRunning tests: {}'.format(', '.join(tests)))
|
print_standard('Starting Hardware Diagnostics')
|
||||||
|
print_standard(' ')
|
||||||
|
print_standard('Running tests: {}'.format(', '.join(tests)))
|
||||||
# Enable selected tests
|
# Enable selected tests
|
||||||
for t in ['Prime95', 'NVMe/SMART', 'badblocks', 'iobenchmark']:
|
for t in ['Prime95', 'NVMe/SMART', 'badblocks', 'iobenchmark']:
|
||||||
TESTS[t]['Enabled'] = t in tests
|
TESTS[t]['Enabled'] = t in tests
|
||||||
|
|
@ -650,7 +1120,6 @@ def run_tests(tests):
|
||||||
# Initialize
|
# Initialize
|
||||||
if TESTS['NVMe/SMART']['Enabled'] or TESTS['badblocks']['Enabled'] or TESTS['iobenchmark']['Enabled']:
|
if TESTS['NVMe/SMART']['Enabled'] or TESTS['badblocks']['Enabled'] or TESTS['iobenchmark']['Enabled']:
|
||||||
print_standard(' ')
|
print_standard(' ')
|
||||||
print_standard('Scanning disks...')
|
|
||||||
scan_disks()
|
scan_disks()
|
||||||
update_progress()
|
update_progress()
|
||||||
|
|
||||||
|
|
@ -658,18 +1127,19 @@ def run_tests(tests):
|
||||||
mprime_aborted = False
|
mprime_aborted = False
|
||||||
if TESTS['Prime95']['Enabled']:
|
if TESTS['Prime95']['Enabled']:
|
||||||
try:
|
try:
|
||||||
run_mprime()
|
run_mprime(ticket_number)
|
||||||
except GenericError:
|
except GenericError:
|
||||||
mprime_aborted = True
|
mprime_aborted = True
|
||||||
if not mprime_aborted:
|
if not mprime_aborted:
|
||||||
if TESTS['NVMe/SMART']['Enabled']:
|
if TESTS['NVMe/SMART']['Enabled']:
|
||||||
run_nvme_smart()
|
run_nvme_smart(ticket_number)
|
||||||
if TESTS['badblocks']['Enabled']:
|
if TESTS['badblocks']['Enabled']:
|
||||||
run_badblocks()
|
run_badblocks(ticket_number)
|
||||||
if TESTS['iobenchmark']['Enabled']:
|
if TESTS['iobenchmark']['Enabled']:
|
||||||
run_iobenchmark()
|
run_iobenchmark(ticket_number)
|
||||||
|
|
||||||
# Show results
|
# Show results
|
||||||
|
post_drive_results(ticket_number)
|
||||||
show_results()
|
show_results()
|
||||||
|
|
||||||
# Open log
|
# Open log
|
||||||
|
|
@ -683,7 +1153,6 @@ def run_tests(tests):
|
||||||
|
|
||||||
def scan_disks(full_paths=False, only_path=None):
|
def scan_disks(full_paths=False, only_path=None):
|
||||||
"""Scan for disks eligible for hardware testing."""
|
"""Scan for disks eligible for hardware testing."""
|
||||||
clear_screen()
|
|
||||||
|
|
||||||
# Get eligible disk list
|
# Get eligible disk list
|
||||||
cmd = ['lsblk', '-J', '-O']
|
cmd = ['lsblk', '-J', '-O']
|
||||||
|
|
@ -772,7 +1241,7 @@ def scan_disks(full_paths=False, only_path=None):
|
||||||
if ask('Run tests on this device anyway?'):
|
if ask('Run tests on this device anyway?'):
|
||||||
TESTS['NVMe/SMART']['Status'][dev_name] = 'OVERRIDE'
|
TESTS['NVMe/SMART']['Status'][dev_name] = 'OVERRIDE'
|
||||||
else:
|
else:
|
||||||
TESTS['NVMe/SMART']['Status'][dev_name] = 'NS'
|
TESTS['NVMe/SMART']['Status'][dev_name] = 'Skipped'
|
||||||
TESTS['badblocks']['Status'][dev_name] = 'Denied'
|
TESTS['badblocks']['Status'][dev_name] = 'Denied'
|
||||||
TESTS['iobenchmark']['Status'][dev_name] = 'Denied'
|
TESTS['iobenchmark']['Status'][dev_name] = 'Denied'
|
||||||
print_standard(' ') # In case there's more than one "OVERRIDE" disk
|
print_standard(' ') # In case there's more than one "OVERRIDE" disk
|
||||||
|
|
@ -1020,6 +1489,64 @@ def update_progress():
|
||||||
with open(TESTS['Progress Out'], 'w') as f:
|
with open(TESTS['Progress Out'], 'w') as f:
|
||||||
f.writelines(output)
|
f.writelines(output)
|
||||||
|
|
||||||
|
def upload_to_imgur(image_path):
|
||||||
|
"""Upload image to Imgur and return image url as str."""
|
||||||
|
image_data = None
|
||||||
|
image_link = None
|
||||||
|
|
||||||
|
# Bail early
|
||||||
|
if not image_path:
|
||||||
|
raise GenericError
|
||||||
|
|
||||||
|
# Read image file and convert to base64 then convert to str
|
||||||
|
with open(image_path, 'rb') as f:
|
||||||
|
image_data = base64.b64encode(f.read()).decode()
|
||||||
|
|
||||||
|
# POST image
|
||||||
|
url = "https://api.imgur.com/3/image"
|
||||||
|
boundary = '----WebKitFormBoundary7MA4YWxkTrZu0gW'
|
||||||
|
payload = ('--{boundary}\r\nContent-Disposition: form-data; '
|
||||||
|
'name="image"\r\n\r\n{data}\r\n--{boundary}--')
|
||||||
|
payload = payload.format(boundary=boundary, data=image_data)
|
||||||
|
headers = {
|
||||||
|
'content-type': 'multipart/form-data; boundary={}'.format(boundary),
|
||||||
|
'Authorization': 'Client-ID {}'.format(IMGUR_CLIENT_ID),
|
||||||
|
}
|
||||||
|
response = requests.request("POST", url, data=payload, headers=headers)
|
||||||
|
|
||||||
|
# Return image link
|
||||||
|
if response.ok:
|
||||||
|
json_data = json.loads(response.text)
|
||||||
|
image_link = json_data['data']['link']
|
||||||
|
return image_link
|
||||||
|
|
||||||
|
def upload_to_nextcloud(image_path, ticket_number, dev_name):
|
||||||
|
"""Upload image to Nextcloud server and return folder url as str."""
|
||||||
|
image_data = None
|
||||||
|
|
||||||
|
# Bail early
|
||||||
|
if not image_path:
|
||||||
|
raise GenericError
|
||||||
|
|
||||||
|
# Read image file and convert to base64
|
||||||
|
with open(image_path, 'rb') as f:
|
||||||
|
image_data = f.read()
|
||||||
|
|
||||||
|
# PUT image
|
||||||
|
url = '{base_url}/{ticket_number}_iobenchmark_{dev_name}_{date}.png'.format(
|
||||||
|
base_url=BENCHMARK_SERVER['Url'],
|
||||||
|
ticket_number=ticket_number,
|
||||||
|
dev_name=dev_name,
|
||||||
|
date=global_vars.get('Date-Time', 'Unknown Date-Time'))
|
||||||
|
requests.put(
|
||||||
|
url,
|
||||||
|
data=image_data,
|
||||||
|
headers = {'X-Requested-With': 'XMLHttpRequest'},
|
||||||
|
auth = (BENCHMARK_SERVER['User'], BENCHMARK_SERVER['Pass']))
|
||||||
|
|
||||||
|
# Return folder link
|
||||||
|
return BENCHMARK_SERVER['Short Url']
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
print("This file is not meant to be called directly.")
|
print("This file is not meant to be called directly.")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
# Features
|
# Features
|
||||||
ENABLED_OPEN_LOGS = False
|
ENABLED_OPEN_LOGS = False
|
||||||
ENABLED_TICKET_NUMBERS = False
|
ENABLED_TICKET_NUMBERS = False
|
||||||
ENABLED_UPLOAD_DATA = False
|
ENABLED_UPLOAD_DATA = True
|
||||||
|
|
||||||
# STATIC VARIABLES (also used by BASH and BATCH files)
|
# STATIC VARIABLES (also used by BASH and BATCH files)
|
||||||
## NOTE: There are no spaces around the = for easier parsing in BASH and BATCH
|
## NOTE: There are no spaces around the = for easier parsing in BASH and BATCH
|
||||||
|
|
@ -12,6 +12,15 @@ ARCHIVE_PASSWORD='Sorted1201'
|
||||||
KIT_NAME_FULL='1201-WizardKit'
|
KIT_NAME_FULL='1201-WizardKit'
|
||||||
KIT_NAME_SHORT='1201'
|
KIT_NAME_SHORT='1201'
|
||||||
SUPPORT_MESSAGE='Please let support know by opening an issue on Gogs'
|
SUPPORT_MESSAGE='Please let support know by opening an issue on Gogs'
|
||||||
|
# osTicket
|
||||||
|
DB_HOST='osticket.1201.com'
|
||||||
|
DB_NAME='osticket'
|
||||||
|
DB_USER='wizardkit'
|
||||||
|
DB_PASS='U9bJnF9eamVkfsVw'
|
||||||
|
SSH_PORT='22'
|
||||||
|
SSH_USER='sql_tunnel'
|
||||||
|
# imgur
|
||||||
|
IMGUR_CLIENT_ID='3d1ee1d38707b85'
|
||||||
# Live Linux
|
# Live Linux
|
||||||
MPRIME_LIMIT='7' # of minutes to run Prime95 during hw-diags
|
MPRIME_LIMIT='7' # of minutes to run Prime95 during hw-diags
|
||||||
ROOT_PASSWORD='1201 loves computers!'
|
ROOT_PASSWORD='1201 loves computers!'
|
||||||
|
|
@ -41,17 +50,24 @@ BACKUP_SERVERS = [
|
||||||
'RW-Pass': '1201 loves computers!',
|
'RW-Pass': '1201 loves computers!',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
BENCHMARK_SERVER = {
|
||||||
|
'Name': 'Nextcloud',
|
||||||
|
'Short Url': 'https://1201north.ddns.net:8001/index.php/f/27892',
|
||||||
|
'Url': 'https://1201north.ddns.net:8001/public.php/webdav/Benchmarks',
|
||||||
|
'User': 'RAE7ajRk25CBnW6',
|
||||||
|
'Pass': '',
|
||||||
|
}
|
||||||
CRASH_SERVER = {
|
CRASH_SERVER = {
|
||||||
'Name': "2Shirt's Nextcloud Server",
|
'Name': 'Nextcloud',
|
||||||
'Url': 'https://nextcloud.2shirt.work/public.php/webdav/WK_Issues',
|
'Url': 'https://1201north.ddns.net:8001/public.php/webdav/WizardKit_Issues',
|
||||||
'User': 'hgoTCWIL28oGWqJ',
|
'User': 'LoQ97J3r6CFGT2T',
|
||||||
'Pass': '',
|
'Pass': '',
|
||||||
}
|
}
|
||||||
OFFICE_SERVER = {
|
OFFICE_SERVER = {
|
||||||
'IP': OFFICE_SERVER_IP,
|
'IP': OFFICE_SERVER_IP,
|
||||||
'Name': 'Anaconda',
|
'Name': 'Anaconda',
|
||||||
'Mounted': False,
|
'Mounted': False,
|
||||||
'Share': 'Office',
|
'Share': r'Public\Office\MS Office',
|
||||||
'User': 'cx',
|
'User': 'cx',
|
||||||
'Pass': 'cx',
|
'Pass': 'cx',
|
||||||
'RW-User': 'backup',
|
'RW-User': 'backup',
|
||||||
|
|
@ -61,7 +77,7 @@ QUICKBOOKS_SERVER = {
|
||||||
'IP': QUICKBOOKS_SERVER_IP,
|
'IP': QUICKBOOKS_SERVER_IP,
|
||||||
'Name': 'Anaconda',
|
'Name': 'Anaconda',
|
||||||
'Mounted': False,
|
'Mounted': False,
|
||||||
'Share': 'QuickBooks',
|
'Share': r'Public\QuickBooks',
|
||||||
'User': 'cx',
|
'User': 'cx',
|
||||||
'Pass': 'cx',
|
'Pass': 'cx',
|
||||||
'RW-User': 'backup',
|
'RW-User': 'backup',
|
||||||
|
|
@ -71,7 +87,7 @@ WINDOWS_SERVER = {
|
||||||
'IP': '10.11.1.20',
|
'IP': '10.11.1.20',
|
||||||
'Name': 'Anaconda',
|
'Name': 'Anaconda',
|
||||||
'Mounted': False,
|
'Mounted': False,
|
||||||
'Share': 'Windows',
|
'Share': r'Public\Windows',
|
||||||
'User': 'cx',
|
'User': 'cx',
|
||||||
'Pass': 'cx',
|
'Pass': 'cx',
|
||||||
'RW-User': 'backup',
|
'RW-User': 'backup',
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
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
|
||||||
|
-----END CERTIFICATE-----
|
||||||
2
.linux_items/include/airootfs/etc/skel/.ssh/known_hosts
Normal file
2
.linux_items/include/airootfs/etc/skel/.ssh/known_hosts
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
osticket.1201.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJDDXtNvh4Vd3q3qZkZbIcnDWWO
|
||||||
|
fJPZb6LVCFptr4awYjlZNL5ieWIUW080IUgtnzWNR7UvetQRtGDsyGu65L+4=
|
||||||
|
|
@ -43,6 +43,7 @@ libinput
|
||||||
linux-firmware
|
linux-firmware
|
||||||
lm_sensors
|
lm_sensors
|
||||||
lzip
|
lzip
|
||||||
|
mariadb-clients
|
||||||
mdadm
|
mdadm
|
||||||
mediainfo
|
mediainfo
|
||||||
mesa-demos
|
mesa-demos
|
||||||
|
|
@ -62,6 +63,8 @@ p7zip
|
||||||
papirus-icon-theme
|
papirus-icon-theme
|
||||||
progsreiserfs
|
progsreiserfs
|
||||||
python
|
python
|
||||||
|
python-gnuplot
|
||||||
|
python-mysql-connector
|
||||||
python-psutil
|
python-psutil
|
||||||
python-requests
|
python-requests
|
||||||
qemu-guest-agent
|
qemu-guest-agent
|
||||||
|
|
|
||||||
|
|
@ -253,6 +253,10 @@ function update_live_env() {
|
||||||
echo "ln -sf '/usr/share/zoneinfo/$LINUX_TIME_ZONE' '/etc/localtime'" >> "$LIVE_DIR/airootfs/root/customize_airootfs.sh"
|
echo "ln -sf '/usr/share/zoneinfo/$LINUX_TIME_ZONE' '/etc/localtime'" >> "$LIVE_DIR/airootfs/root/customize_airootfs.sh"
|
||||||
echo 'sed -i "s/#FallbackNTP/NTP/" /etc/systemd/timesyncd.conf' >> "$LIVE_DIR/airootfs/root/customize_airootfs.sh"
|
echo 'sed -i "s/#FallbackNTP/NTP/" /etc/systemd/timesyncd.conf' >> "$LIVE_DIR/airootfs/root/customize_airootfs.sh"
|
||||||
|
|
||||||
|
# Trust root CA(s)
|
||||||
|
echo "trust extract" >> "$LIVE_DIR/airootfs/root/customize_airootfs.sh"
|
||||||
|
echo "trust extract-compat" >> "$LIVE_DIR/airootfs/root/customize_airootfs.sh"
|
||||||
|
|
||||||
# udevil fix
|
# udevil fix
|
||||||
echo "mkdir /media" >> "$LIVE_DIR/airootfs/root/customize_airootfs.sh"
|
echo "mkdir /media" >> "$LIVE_DIR/airootfs/root/customize_airootfs.sh"
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue