#!/usr/bin/env python3
# Copyright (c) 2007-2011 Arastra, Inc.  All rights reserved.
# Arastra, Inc. Confidential and Proprietary.

# pylint: disable=consider-using-f-string

import Tac, Tracing
import sys, re

from PowerDiagLib import OptionParser
from PowerDiagLib import exitWithError, envPowerStatusDir
from PyClient import PyClient

t0 = Tracing.trace0

parser = OptionParser()

parser.add_option("--ignorePowerLoss", dest="ignorePowerLoss", action="store_true",
                  default = False,
                  help="Ignore power loss to check power sharing for pairs of PS")

parser.add_option("--testSlots", dest="testSlots", action="store_true",
                  default = False,
                  help="Set this option to only test certain slots, and " + \
                  "pass them in as arguments (regex)" )

options, args = parser.parse_args()

if args and not options.testSlots:
   parser.error( "unexpected arguments" )

if options.testSlots and not args:
   parser.error( "Require slots to test" )

slots = []

if options.testSlots:
   slots = args
   t0( "Slots: %s" % slots )
   if len(slots) < 2:
      sys.exit("Need redundant configuration") 

sysdbRoot = PyClient( options.sysname, "Sysdb" ).agentRoot()

powerStatusDir = envPowerStatusDir( sysdbRoot )

supplyStatusesRegistered = list( powerStatusDir.values() )

if not supplyStatusesRegistered:
   exitWithError( "No power supplies found" )
elif len(supplyStatusesRegistered) == 1:
   exitWithError( "Only one power supply found. Test requires " \
                     " a redundant configuration." )

supplyStatuses = []
for supplyStatus in sorted( supplyStatusesRegistered ):
   # If some test slot is entered, then assume first that this slot will be skipped
   skip = options.testSlots
   for slot in slots:
      t0( f'Slot is: {slot} Supply Status Name is: {supplyStatus.name}' )
      # If we find that this is one of the test slots to test, then dont skip
      if re.search( slot, supplyStatus.name ):
         skip = False
         break
   if skip:
      t0( 'Skipping %s' % supplyStatus.name ) 
      continue
   print( "loadSharingTest: checking power supply", supplyStatus.name )

   # pylint: disable-next=cell-var-from-loop
   Tac.waitFor( lambda: supplyStatus.state != "unknown",
                description="Power supply to be detected and initialized",
                timeout=60  # The default timeout of 10 minutes is too long -we can't
                            # allow our manufacturing scripts to site waiting for
                            # something that will never happen, as we're paying by
                            # the hour.
                )

   # Also, if we get power loss but we want to ignore power loss, 
   # then skip this as well   
   if options.ignorePowerLoss and supplyStatus.state == "powerLoss":
      t0( 'Skipping %s' % supplyStatus.name )
      continue

   if supplyStatus.state == "powerLoss":
      if not options.ignorePowerLoss:
         exitWithError( 
            "Power loss detected. Is %s plugged in?" % supplyStatus.name )
      else:
         supplyStatuses.append(supplyStatus)
   elif supplyStatus.state != "ok":
      exitWithError( "{} failed, state is {}".format( supplyStatus.name, 
                                                  supplyStatus.state ) )
   else:
      supplyStatuses.append(supplyStatus)

if supplyStatuses == []: # pylint: disable=use-implicit-booleaness-not-comparison
   exitWithError( "No power supplies to run test on" )
print( "Checking current sharing between the power supplies" )

totalPowerUsage = 0
totalPowerSupplyCapacity = 0
for supplyStatus in supplyStatuses:
   totalPowerUsage += supplyStatus.outputPower
   totalPowerSupplyCapacity += supplyStatus.capacity
print( "Total system power usage is", totalPowerUsage,
       "Watts out of", totalPowerSupplyCapacity, "Watts" )

# How well the power supplies current share has a lot to do with
# the load on the supply. At high enough load (~60% load on the
# supplies), we expect very good power sharing ( about 10/9 ). At
# lower usage levels, sharing is less perfect ( we expect better
# than 5/3 ) and at very low levels, < 10%, we don't expect any 
# significant level of power sharing.

percentageLoad =  totalPowerUsage / ( totalPowerSupplyCapacity * 1.0 )
if percentageLoad < 0.1:
   requiredShareRatio = 100.0
   powerSupplyLoad = "very low"
elif percentageLoad < 0.3:
   requiredShareRatio = 9.0
   powerSupplyLoad = "low and load sharing is very unpredictable"
elif percentageLoad < 0.6:
   requiredShareRatio = 5.0/3
   powerSupplyLoad = "low and load sharing is slightly unpredictable"
else:
   requiredShareRatio = 10.0/9
   powerSupplyLoad = "high and load sharing should be very predictable"

print( "Power supply load is {}. Expecting {:.02f} power sharing ratio".format(
   powerSupplyLoad, requiredShareRatio ) )

outputPower = {}
for supplyStatus in supplyStatuses:
   outputPower[ supplyStatus.name ] = supplyStatus.outputPower

minOutputPower = min( outputPower.values() )
maxOutputPower = max( outputPower.values() )
# pylint: disable-next=consider-using-ternary
shareRatio = minOutputPower and ( 1.0 * maxOutputPower / minOutputPower ) or 0

print( "Share ratio is %s." % shareRatio )

if shareRatio > requiredShareRatio:
   exitWithError( "Power supplies are not current sharing sufficiently. Output "
                  "powers are %s" %(
      ", ".join( [ "%s: %sW" %( k, p ) for ( k, p ) in outputPower.items() ] ) ) )
   
print( "powerLoadSharingTest: power supplies are current sharing appropriately" )
