2019-10-31 14:32:46 -04:00
// Copyright (C) 2018 spdfg
2018-11-08 20:07:06 -05:00
//
2018-10-06 20:03:14 -07:00
// This file is part of Elektron.
2018-11-08 20:07:06 -05:00
//
2018-10-06 20:03:14 -07:00
// Elektron is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
2018-11-08 20:07:06 -05:00
//
2018-10-06 20:03:14 -07:00
// Elektron is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
2018-11-08 20:07:06 -05:00
//
2018-10-06 20:03:14 -07:00
// You should have received a copy of the GNU General Public License
// along with Elektron. If not, see <http://www.gnu.org/licenses/>.
2018-11-08 20:07:06 -05:00
//
2018-10-06 20:03:14 -07:00
2019-10-31 14:32:46 -04:00
package main // import github.com/spdfg/elektron
2016-09-08 02:06:24 -04:00
import (
"flag"
"fmt"
"os"
2016-10-07 20:47:59 -04:00
"os/signal"
2018-01-19 21:20:43 +00:00
"strings"
2016-10-13 17:15:09 -04:00
"time"
2018-09-30 18:23:38 -07:00
"github.com/golang/protobuf/proto"
mesos "github.com/mesos/mesos-go/api/v0/mesosproto"
sched "github.com/mesos/mesos-go/api/v0/scheduler"
2019-12-09 20:15:33 -05:00
log "github.com/sirupsen/logrus"
2019-10-31 14:32:46 -04:00
"github.com/spdfg/elektron/def"
2019-12-09 20:15:33 -05:00
elekLog "github.com/spdfg/elektron/logging"
. "github.com/spdfg/elektron/logging/types"
2019-10-31 14:32:46 -04:00
"github.com/spdfg/elektron/pcp"
"github.com/spdfg/elektron/powerCap"
"github.com/spdfg/elektron/schedulers"
2016-09-08 02:06:24 -04:00
)
2017-10-03 20:10:44 -04:00
var master = flag . String ( "master" , "" , "Location of leading Mesos master -- <mesos-master>:<port>" )
2016-09-27 18:12:50 -04:00
var tasksFile = flag . String ( "workload" , "" , "JSON file containing task definitions" )
2018-01-19 21:20:43 +00:00
var wattsAsAResource = flag . Bool ( "wattsAsAResource" , false , "Enable Watts as a Resource" )
2018-10-04 19:21:45 -04:00
var pcpConfigFile = flag . String ( "pcpConfigFile" , "config" , "PCP config file name (if file not " +
2018-10-04 13:57:35 -04:00
"present in the same directory, then provide path)." )
2018-01-19 21:20:43 +00:00
var pcplogPrefix = flag . String ( "logPrefix" , "" , "Prefix for pcplog" )
2018-10-04 13:38:55 -04:00
var powerCapPolicy = flag . String ( "powercap" , "" , "Power Capping policy. (default (''), extrema, prog-extrema)." )
2018-01-19 21:20:43 +00:00
var hiThreshold = flag . Float64 ( "hiThreshold" , 0.0 , "Upperbound for when we should start capping" )
var loThreshold = flag . Float64 ( "loThreshold" , 0.0 , "Lowerbound for when we should start uncapping" )
var classMapWatts = flag . Bool ( "classMapWatts" , false , "Enable mapping of watts to power class of node" )
var schedPolicyName = flag . String ( "schedPolicy" , "first-fit" , "Name of the scheduling policy to be used.\n\tUse option -listSchedPolicies to get the names of available scheduling policies" )
var listSchedPolicies = flag . Bool ( "listSchedPolicies" , false , "List the names of the pluaggable scheduling policies." )
var enableSchedPolicySwitch = flag . Bool ( "switchSchedPolicy" , false , "Enable switching of scheduling policies at runtime." )
2018-02-26 01:59:09 +00:00
var schedPolConfigFile = flag . String ( "schedPolConfig" , "" , "Config file that contains information for each scheduling policy." )
2018-04-17 20:09:35 +00:00
var fixFirstSchedPol = flag . String ( "fixFirstSchedPol" , "" , "Name of the scheduling policy to be deployed first, regardless of the distribution of tasks, provided switching is enabled." )
2018-04-17 20:12:33 +00:00
var fixSchedWindow = flag . Bool ( "fixSchedWindow" , false , "Fix the size of the scheduling window that every deployed scheduling policy should schedule, provided switching is enabled." )
var schedWindowSize = flag . Int ( "schedWindowSize" , 200 , "Size of the scheduling window if fixSchedWindow is set." )
2018-04-17 23:44:36 +00:00
var schedPolSwitchCriteria = flag . String ( "schedPolSwitchCriteria" , "taskDist" , "Scheduling policy switching criteria." )
2019-12-09 20:15:33 -05:00
var logConfigFilename = flag . String ( "logConfigFilename" , "logConfig.yaml" , "Log Configuration file name" )
2018-01-19 21:20:43 +00:00
// Short hand args
2016-10-13 17:15:09 -04:00
func init ( ) {
2017-10-03 20:10:44 -04:00
flag . StringVar ( master , "m" , "" , "Location of leading Mesos master (shorthand)" )
2016-09-27 19:15:32 -04:00
flag . StringVar ( tasksFile , "w" , "" , "JSON file containing task definitions (shorthand)" )
2018-01-19 21:20:43 +00:00
flag . BoolVar ( wattsAsAResource , "waar" , false , "Enable Watts as a Resource (shorthand)" )
2018-10-04 19:21:45 -04:00
flag . StringVar ( pcpConfigFile , "pcpCF" , "config" , "PCP config file name (if not present in" +
2018-10-04 13:57:35 -04:00
" the same directory, then provide path) (shorthand)." )
2018-01-19 21:20:43 +00:00
flag . StringVar ( pcplogPrefix , "p" , "" , "Prefix for pcplog (shorthand)" )
2018-10-04 19:21:45 -04:00
flag . StringVar ( powerCapPolicy , "pc" , "" , "Power Capping policy. (default (''), extrema, prog-extrema) (shorthand)." )
2018-01-19 21:20:43 +00:00
flag . Float64Var ( hiThreshold , "ht" , 700.0 , "Upperbound for when we should start capping (shorthand)" )
flag . Float64Var ( loThreshold , "lt" , 400.0 , "Lowerbound for when we should start uncapping (shorthand)" )
flag . BoolVar ( classMapWatts , "cmw" , false , "Enable mapping of watts to power class of node (shorthand)" )
flag . StringVar ( schedPolicyName , "sp" , "first-fit" , "Name of the scheduling policy to be used.\n Use option -listSchedPolicies to get the names of available scheduling policies (shorthand)" )
2017-09-26 13:17:47 -04:00
flag . BoolVar ( listSchedPolicies , "lsp" , false , "Names of the pluaggable scheduling policies. (shorthand)" )
2018-01-19 21:20:43 +00:00
flag . BoolVar ( enableSchedPolicySwitch , "ssp" , false , "Enable switching of scheduling policies at runtime." )
2018-02-26 01:59:09 +00:00
flag . StringVar ( schedPolConfigFile , "spConfig" , "" , "Config file that contains information for each scheduling policy (shorthand)." )
2018-04-17 20:12:33 +00:00
flag . StringVar ( fixFirstSchedPol , "fxFstSchedPol" , "" , "Name of the scheduling gpolicy to be deployed first, regardless of the distribution of tasks, provided switching is enabled (shorthand)." )
flag . BoolVar ( fixSchedWindow , "fixSw" , false , "Fix the size of the scheduling window that every deployed scheduling policy should schedule, provided switching is enabled (shorthand)." )
flag . IntVar ( schedWindowSize , "swSize" , 200 , "Size of the scheduling window if fixSchedWindow is set (shorthand)." )
2018-04-17 23:44:36 +00:00
flag . StringVar ( schedPolSwitchCriteria , "spsCriteria" , "taskDist" , "Scheduling policy switching criteria (shorthand)." )
2019-12-09 20:15:33 -05:00
flag . StringVar ( logConfigFilename , "lgCfg" , "logConfig.yaml" , "Log Configuration file name (shorthand)." )
2017-09-26 13:17:47 -04:00
}
func listAllSchedulingPolicies ( ) {
fmt . Println ( "Scheduling Policies" )
fmt . Println ( "-------------------" )
2018-10-04 14:11:38 -04:00
for policyName := range schedulers . SchedPolicies {
2017-09-26 13:17:47 -04:00
fmt . Println ( policyName )
}
2016-09-27 18:12:50 -04:00
}
2016-09-08 02:06:24 -04:00
func main ( ) {
flag . Parse ( )
2018-10-04 19:24:16 -04:00
// Checking to see if we need to just list the pluggable scheduling policies
2017-09-26 13:17:47 -04:00
if * listSchedPolicies {
listAllSchedulingPolicies ( )
os . Exit ( 1 )
}
2018-10-04 19:24:16 -04:00
// First we need to build the scheduler using scheduler options.
var schedOptions [ ] schedulers . SchedulerOptions = make ( [ ] schedulers . SchedulerOptions , 0 , 10 )
// OPTIONAL PARAMETERS
// Scheduling Policy Name
// If non-default scheduling policy given, checking if name exists.
2017-09-26 13:17:47 -04:00
if * schedPolicyName != "first-fit" {
2018-01-19 21:20:43 +00:00
if _ , ok := schedulers . SchedPolicies [ * schedPolicyName ] ; ! ok {
// invalid scheduling policy
2017-09-26 13:17:47 -04:00
log . Println ( "Invalid scheduling policy given. The possible scheduling policies are:" )
listAllSchedulingPolicies ( )
os . Exit ( 1 )
}
}
2018-10-04 19:24:16 -04:00
// CHANNELS AND FLAGS.
shutdown := make ( chan struct { } )
done := make ( chan struct { } )
pcpLog := make ( chan struct { } )
recordPCP := false
2016-09-16 19:06:53 -04:00
2018-10-04 19:24:16 -04:00
// Shutdown indicator channels.
// These channels are used to notify,
// 1. scheduling is complete.
// 2. all scheduled tasks have completed execution and framework can shutdown.
schedOptions = append ( schedOptions , schedulers . WithShutdown ( shutdown ) )
schedOptions = append ( schedOptions , schedulers . WithDone ( done ) )
2016-09-16 19:06:53 -04:00
2018-10-04 19:24:16 -04:00
// If here, then valid scheduling policy name provided.
schedOptions = append ( schedOptions , schedulers . WithSchedPolicy ( * schedPolicyName ) )
2016-09-17 18:55:35 -04:00
2018-10-04 19:24:16 -04:00
// Scheduling Policy Switching.
2018-02-26 01:59:09 +00:00
if * enableSchedPolicySwitch {
2018-10-04 19:24:16 -04:00
// Scheduling policy config file required.
2018-02-26 01:59:09 +00:00
if spcf := * schedPolConfigFile ; spcf == "" {
2018-11-08 20:07:06 -05:00
log . Fatal ( "Scheduling policy characteristics file not provided." )
2018-02-26 01:59:09 +00:00
} else {
// Initializing the characteristics of the scheduling policies.
schedulers . InitSchedPolicyCharacteristics ( spcf )
2018-10-04 19:24:16 -04:00
schedOptions = append ( schedOptions , schedulers . WithSchedPolSwitchEnabled ( * enableSchedPolicySwitch , * schedPolSwitchCriteria ) )
// Fix First Scheduling Policy.
schedOptions = append ( schedOptions , schedulers . WithNameOfFirstSchedPolToFix ( * fixFirstSchedPol ) )
// Fix Scheduling Window.
schedOptions = append ( schedOptions , schedulers . WithFixedSchedulingWindow ( * fixSchedWindow , * schedWindowSize ) )
2018-02-26 01:59:09 +00:00
}
}
2018-10-04 19:24:16 -04:00
// Watts as a Resource (WaaR) and ClassMapWatts (CMW).
// If WaaR and CMW is enabled then for each task the class_to_watts mapping is used to
// fit tasks into offers.
// If CMW is disabled, then the Median of Medians Max Peak Power Usage value is used
// as the watts value for each task.
if * wattsAsAResource {
2018-11-08 20:07:06 -05:00
log . Println ( "WaaR enabled..." )
2018-10-04 19:24:16 -04:00
schedOptions = append ( schedOptions , schedulers . WithWattsAsAResource ( * wattsAsAResource ) )
schedOptions = append ( schedOptions , schedulers . WithClassMapWatts ( * classMapWatts ) )
2016-09-08 02:06:24 -04:00
}
2018-10-04 19:21:45 -04:00
// REQUIRED PARAMETERS.
// PCP logging, Power capping and High and Low thresholds.
schedOptions = append ( schedOptions , schedulers . WithRecordPCP ( & recordPCP ) )
schedOptions = append ( schedOptions , schedulers . WithPCPLog ( pcpLog ) )
var noPowercap bool
var extrema bool
var progExtrema bool
var powercapValues map [ string ] struct { } = map [ string ] struct { } {
"" : { } ,
"extrema" : { } ,
"prog-extrema" : { } ,
2018-10-04 13:38:55 -04:00
}
if _ , ok := powercapValues [ * powerCapPolicy ] ; ! ok {
2018-11-08 20:07:06 -05:00
log . Fatal ( "Incorrect power-capping algorithm specified." )
2018-10-04 13:38:55 -04:00
} else {
2018-10-04 19:21:45 -04:00
// Indicating which power capping algorithm to use, if any.
// The pcp-logging with/without power capping will be run after the
// scheduler has been configured.
2018-10-04 13:38:55 -04:00
if * powerCapPolicy == "" {
noPowercap = true
} else {
if * powerCapPolicy == "extrema" {
extrema = true
2018-10-04 20:10:46 -04:00
} else if * powerCapPolicy == "prog-extrema" {
2018-10-04 13:38:55 -04:00
progExtrema = true
}
2018-10-05 00:41:43 -04:00
// High and Low thresholds are currently only needed for extrema and
2018-10-04 20:10:46 -04:00
// progressive extrema.
if extrema || progExtrema {
// High and Low Thresholds.
// These values are not used to configure the scheduler.
// hiThreshold and loThreshold are passed to the powercappers.
if * hiThreshold < * loThreshold {
2018-11-08 20:07:06 -05:00
log . Fatal ( "High threshold is of a lower value than low " +
"threshold." )
2018-10-04 20:10:46 -04:00
}
2018-10-04 13:38:55 -04:00
}
}
}
2018-10-04 19:24:16 -04:00
// Tasks
// If httpServer is disabled, then path of file containing workload needs to be provided.
if * tasksFile == "" {
2018-11-08 20:07:06 -05:00
log . Fatal ( "Tasks specifications file not provided." )
2018-10-04 19:24:16 -04:00
}
tasks , err := def . TasksFromJSON ( * tasksFile )
if err != nil || len ( tasks ) == 0 {
2019-11-26 03:08:12 -05:00
log . Fatal ( err )
2018-10-04 19:24:16 -04:00
}
schedOptions = append ( schedOptions , schedulers . WithTasks ( tasks ) )
// Scheduler.
scheduler := schedulers . SchedFactory ( schedOptions ... )
// Scheduler driver.
driver , err := sched . NewMesosSchedulerDriver ( sched . DriverConfig {
Master : * master ,
Framework : & mesos . FrameworkInfo {
Name : proto . String ( "Elektron" ) ,
User : proto . String ( "" ) ,
} ,
Scheduler : scheduler ,
} )
if err != nil {
2018-11-08 20:07:06 -05:00
log . Fatal ( fmt . Sprintf ( "Unable to create scheduler driver: %s" , err ) )
}
// Checking if prefix contains any special characters.
if strings . Contains ( * pcplogPrefix , "/" ) {
log . Fatal ( "log file prefix should not contain '/'." )
2018-10-04 13:57:35 -04:00
}
2019-12-09 20:15:33 -05:00
// Build Logger.
if err := elekLog . BuildLogger ( * pcplogPrefix , * logConfigFilename ) ; err != nil {
log . Fatal ( err )
}
2018-10-04 13:57:35 -04:00
2018-10-04 19:24:16 -04:00
// Starting PCP logging.
2018-10-04 13:38:55 -04:00
if noPowercap {
2019-12-09 20:15:33 -05:00
go pcp . Start ( pcpLog , & recordPCP , * pcpConfigFile )
2018-10-04 13:38:55 -04:00
} else if extrema {
go powerCap . StartPCPLogAndExtremaDynamicCap ( pcpLog , & recordPCP , * hiThreshold ,
2019-12-09 20:15:33 -05:00
* loThreshold , * pcpConfigFile )
2018-10-04 13:38:55 -04:00
} else if progExtrema {
go powerCap . StartPCPLogAndProgressiveExtremaCap ( pcpLog , & recordPCP , * hiThreshold ,
2019-12-09 20:15:33 -05:00
* loThreshold , * pcpConfigFile )
2018-10-04 13:38:55 -04:00
}
2018-10-04 19:24:16 -04:00
// Take a second between starting PCP log and continuing.
time . Sleep ( 1 * time . Second )
2016-09-26 19:14:51 -04:00
2017-09-28 15:36:47 -04:00
// Attempt to handle SIGINT to not leave pmdumptext running.
// Catch interrupt.
2016-10-07 20:47:59 -04:00
go func ( ) {
c := make ( chan os . Signal , 1 )
signal . Notify ( c , os . Interrupt , os . Kill )
s := <- c
if s != os . Interrupt {
2017-09-26 13:17:47 -04:00
close ( pcpLog )
2016-10-07 20:47:59 -04:00
return
}
2018-10-04 19:24:16 -04:00
log . Println ( "Received SIGINT... stopping" )
2017-09-26 13:17:47 -04:00
close ( done )
2016-10-07 20:47:59 -04:00
} ( )
2016-09-08 02:06:24 -04:00
go func ( ) {
2018-10-04 19:24:16 -04:00
// Signals we have scheduled every task we have
2016-09-19 20:25:10 -04:00
select {
2017-09-26 13:17:47 -04:00
case <- shutdown :
2017-01-28 17:48:15 -05:00
//case <-time.After(shutdownTimeout):
2016-09-17 18:55:35 -04:00
}
2016-09-08 02:06:24 -04:00
2018-10-04 19:24:16 -04:00
// All tasks have finished
2016-09-08 02:06:24 -04:00
select {
2017-09-26 13:17:47 -04:00
case <- done :
close ( pcpLog )
2016-09-26 19:14:51 -04:00
time . Sleep ( 5 * time . Second ) //Wait for PCP to log a few more seconds
2018-10-04 19:24:16 -04:00
// Closing logging channels.
2017-01-28 17:48:15 -05:00
//case <-time.After(shutdownTimeout):
2016-09-08 02:06:24 -04:00
}
2018-10-04 19:24:16 -04:00
// Done shutting down
2016-09-08 02:06:24 -04:00
driver . Stop ( false )
2016-09-17 18:55:35 -04:00
2016-09-08 02:06:24 -04:00
} ( )
2018-10-04 19:24:16 -04:00
// Starting the scheduler driver.
2016-09-08 02:06:24 -04:00
if status , err := driver . Run ( ) ; err != nil {
2019-12-09 20:15:33 -05:00
elekLog . WithFields ( log . Fields {
"status" : status . String ( ) ,
"error" : err . Error ( ) ,
} ) . Log ( CONSOLE , log . ErrorLevel , "Framework stopped " )
2016-09-08 02:06:24 -04:00
}
2019-12-09 20:15:33 -05:00
elekLog . Log ( CONSOLE , log . InfoLevel , "Exiting..." )
2017-08-22 12:56:36 -04:00
}