From 888ab01903bc4c47c86d758cce27bb7b576c966e Mon Sep 17 00:00:00 2001 From: huertas Date: Wed, 7 Oct 2015 16:32:41 +0200 Subject: [PATCH] feature(g5k-sensor): implement the external powermeter OmegaWatt (from Grid5000) feature(core): add handling for generic external powermeters --- .../scala/org/powerapi/app/PowerAPI.scala | 10 +- .../org/powerapi/core/ExternalPMeter.scala | 2 +- .../ExtPMeterChannel.scala} | 33 +++--- .../ExtPMeterFormula.scala} | 25 ++-- .../ExtPMeterFormulaConfiguration.scala} | 5 +- .../ExtPMeterModule.scala} | 15 +-- .../ExtPMeterSensor.scala} | 19 ++-- .../extPMeter/g5k/G5kOmegaWattModule.scala | 35 ++++++ .../module/extPMeter/g5k/G5kPMeter.scala | 101 +++++++++++++++++ .../g5k/G5kPMeterConfiguration.scala | 44 +++++++ .../extPMeter/powerspy/PowerSpyModule.scala | 35 ++++++ .../powerspy/PowerSpyPMeter.scala | 13 ++- .../PowerSpyPMeterConfiguration.scala | 8 +- .../test/resources/configuration-suite.conf | 5 +- .../scala/org/powerapi/PowerMeterSuite.scala | 18 ++- .../ExtPMeterFormulaConfigurationSuite.scala} | 10 +- .../ExtPMeterFormulaSuite.scala} | 18 +-- .../ExtPMeterModulesSuite.scala} | 24 ++-- .../extPMeter/ExtPMeterSensorSuite.scala | 107 ++++++++++++++++++ .../extPMeter/g5k/G5kConfigurationSuite.scala | 47 ++++++++ .../PowerSpyConfigurationSuite.scala} | 6 +- .../module/powerspy/PowerSpySensorSuite.scala | 95 ---------------- .../scala/org/powerapi/daemon/PowerAPId.scala | 6 +- .../org/powerapi/sampling/Sampling.scala | 4 +- 24 files changed, 492 insertions(+), 193 deletions(-) rename powerapi-core/src/main/scala/org/powerapi/module/{powerspy/PowerSpyChannel.scala => extPMeter/ExtPMeterChannel.scala} (63%) rename powerapi-core/src/main/scala/org/powerapi/module/{powerspy/PowerSpyFormula.scala => extPMeter/ExtPMeterFormula.scala} (81%) rename powerapi-core/src/main/scala/org/powerapi/module/{powerspy/PowerSpyFormulaConfiguration.scala => extPMeter/ExtPMeterFormulaConfiguration.scala} (87%) rename powerapi-core/src/main/scala/org/powerapi/module/{powerspy/PowerSpyModule.scala => extPMeter/ExtPMeterModule.scala} (67%) rename powerapi-core/src/main/scala/org/powerapi/module/{powerspy/PowerSpySensor.scala => extPMeter/ExtPMeterSensor.scala} (68%) create mode 100644 powerapi-core/src/main/scala/org/powerapi/module/extPMeter/g5k/G5kOmegaWattModule.scala create mode 100644 powerapi-core/src/main/scala/org/powerapi/module/extPMeter/g5k/G5kPMeter.scala create mode 100644 powerapi-core/src/main/scala/org/powerapi/module/extPMeter/g5k/G5kPMeterConfiguration.scala create mode 100644 powerapi-core/src/main/scala/org/powerapi/module/extPMeter/powerspy/PowerSpyModule.scala rename powerapi-core/src/main/scala/org/powerapi/module/{ => extPMeter}/powerspy/PowerSpyPMeter.scala (86%) rename powerapi-core/src/main/scala/org/powerapi/module/{ => extPMeter}/powerspy/PowerSpyPMeterConfiguration.scala (78%) rename powerapi-core/src/test/scala/org/powerapi/module/{powerspy/PowerSpyFormulaConfigurationSuite.scala => extPMeter/ExtPMeterFormulaConfigurationSuite.scala} (77%) rename powerapi-core/src/test/scala/org/powerapi/module/{powerspy/PowerSpyFormulaSuite.scala => extPMeter/ExtPMeterFormulaSuite.scala} (91%) rename powerapi-core/src/test/scala/org/powerapi/module/{powerspy/PowerSpyModulesSuite.scala => extPMeter/ExtPMeterModulesSuite.scala} (77%) create mode 100644 powerapi-core/src/test/scala/org/powerapi/module/extPMeter/ExtPMeterSensorSuite.scala create mode 100644 powerapi-core/src/test/scala/org/powerapi/module/extPMeter/g5k/G5kConfigurationSuite.scala rename powerapi-core/src/test/scala/org/powerapi/module/{powerspy/PowerSpyPMeterConfigurationSuite.scala => extPMeter/powerspy/PowerSpyConfigurationSuite.scala} (89%) delete mode 100644 powerapi-core/src/test/scala/org/powerapi/module/powerspy/PowerSpySensorSuite.scala diff --git a/powerapi-cli/src/main/scala/org/powerapi/app/PowerAPI.scala b/powerapi-cli/src/main/scala/org/powerapi/app/PowerAPI.scala index 5163a14..572a4e6 100644 --- a/powerapi-cli/src/main/scala/org/powerapi/app/PowerAPI.scala +++ b/powerapi-cli/src/main/scala/org/powerapi/app/PowerAPI.scala @@ -31,7 +31,8 @@ import org.powerapi.core.power._ import org.powerapi.module.cpu.dvfs.CpuDvfsModule import org.powerapi.module.cpu.simple.{SigarCpuSimpleModule, ProcFSCpuSimpleModule} import org.powerapi.module.libpfm.{LibpfmModule, LibpfmHelper, LibpfmCoreProcessModule, LibpfmCoreModule, LibpfmProcessModule} -import org.powerapi.module.powerspy.PowerSpyModule +import org.powerapi.module.extPMeter.powerspy.PowerSpyModule +import org.powerapi.module.extPMeter.g5k.G5kOmegaWattModule import scala.concurrent.duration.DurationInt import scala.sys import scala.sys.process.stringSeqToProcess @@ -43,7 +44,7 @@ import scala.sys.process.stringSeqToProcess * @author Loïc Huertas */ object PowerAPI extends App { - val modulesR = """(procfs-cpu-simple|sigar-cpu-simple|cpu-dvfs|libpfm|libpfm-process|libpfm-core|libpfm-core-process|powerspy|rapl)(,(procfs-cpu-simple|sigar-cpu-simple|cpu-dvfs|libpfm|libpfm-process|libpfm-core|libpfm-core-process|powerspy|rapl))*""".r + val modulesR = """(procfs-cpu-simple|sigar-cpu-simple|cpu-dvfs|libpfm|libpfm-process|libpfm-core|libpfm-core-process|powerspy|g5k-omegawatt|rapl)(,(procfs-cpu-simple|sigar-cpu-simple|cpu-dvfs|libpfm|libpfm-process|libpfm-core|libpfm-core-process|powerspy|g5k-omegawatt|rapl))*""".r val aggR = """max|min|geomean|logsum|mean|median|stdev|sum|variance""".r val durationR = """\d+""".r val pidR = """(\d+)""".r @@ -113,7 +114,7 @@ object PowerAPI extends App { |You can use different settings per software-defined power meter for some modules by using the optional prefix option. |Please, refer to the documentation inside the GitHub wiki for further details. | - |usage: ./powerapi modules [procfs-cpu-simple|sigar-cpu-simple|cpu-dvfs|libpfm|libpfm-process|libpfm-core|libpfm-core-proces|powerspy|rapl,...] *--prefix [name]* \ + |usage: ./powerapi modules [procfs-cpu-simple|sigar-cpu-simple|cpu-dvfs|libpfm|libpfm-process|libpfm-core|libpfm-core-proces|powerspy|g5k-omegawatt|rapl,...] *--prefix [name]* \ | monitor --frequency [ms] --targets [pid, ..., app, ...|all] --agg [max|min|geomean|logsum|mean|median|stdev|sum|variance] --[console,file [filepath],chart] \ | duration [s] | @@ -180,7 +181,8 @@ object PowerAPI extends App { case "libpfm-process" => LibpfmProcessModule(powerMeterConf('prefix).asInstanceOf[Option[String]], libpfmHelper.get) case "libpfm-core" => LibpfmCoreModule(powerMeterConf('prefix).asInstanceOf[Option[String]], libpfmHelper.get) case "libpfm-core-process" => LibpfmCoreProcessModule(powerMeterConf('prefix).asInstanceOf[Option[String]], libpfmHelper.get) - case "powerspy" => PowerSpyModule() + case "powerspy" => PowerSpyModule(powerMeterConf('prefix).asInstanceOf[Option[String]]) + case "g5k-omegawatt" => G5kOmegaWattModule(powerMeterConf('prefix).asInstanceOf[Option[String]]) case "rapl" => RAPLModule() } }).toSeq diff --git a/powerapi-core/src/main/scala/org/powerapi/core/ExternalPMeter.scala b/powerapi-core/src/main/scala/org/powerapi/core/ExternalPMeter.scala index 29b51ad..47cf25f 100644 --- a/powerapi-core/src/main/scala/org/powerapi/core/ExternalPMeter.scala +++ b/powerapi-core/src/main/scala/org/powerapi/core/ExternalPMeter.scala @@ -28,7 +28,7 @@ package org.powerapi.core * @author Maxime Colmant */ trait ExternalPMeter { - def init(): Unit + def init(bus: MessageBus): Unit def start(): Unit def stop(): Unit } diff --git a/powerapi-core/src/main/scala/org/powerapi/module/powerspy/PowerSpyChannel.scala b/powerapi-core/src/main/scala/org/powerapi/module/extPMeter/ExtPMeterChannel.scala similarity index 63% rename from powerapi-core/src/main/scala/org/powerapi/module/powerspy/PowerSpyChannel.scala rename to powerapi-core/src/main/scala/org/powerapi/module/extPMeter/ExtPMeterChannel.scala index a2d27e0..e1e81ad 100644 --- a/powerapi-core/src/main/scala/org/powerapi/module/powerspy/PowerSpyChannel.scala +++ b/powerapi-core/src/main/scala/org/powerapi/module/extPMeter/ExtPMeterChannel.scala @@ -20,59 +20,60 @@ * * If not, please consult http://www.gnu.org/licenses/agpl-3.0.html. */ -package org.powerapi.module.powerspy +package org.powerapi.module.extPMeter import akka.actor.ActorRef import org.powerapi.core.{Channel, Message, MessageBus} import org.powerapi.core.power.Power /** - * PowerSpyChannel channel and messages. + * ExtPMeterChannel channel and messages. * * @author Maxime Colmant + * @author Loïc Huertas */ -object PowerSpyChannel extends Channel { +object ExtPMeterChannel extends Channel { - type M = PowerSpyPower + type M = ExtPMeterPower /** - * PowerSpyPower is represented as a dedicated type of message. + * ExtPMeterPower is represented as a dedicated type of message. * * @param topic: subject used for routing the message. * @param power: power consumption got by an external device. */ - case class PowerSpyPower(topic: String, - power: Power) extends Message + case class ExtPMeterPower(topic: String, + power: Power) extends Message /** * Topic for communicating with the Sensor actor. */ - private val pMeterTopic = "powerspy:power" + private val pMeterTopic = "extpmeter:power" /** * Topic for communicating with the Formula actor. */ - private val topic = "sensor:powerspy" + private val topic = "sensor:extpmeter" /** - * Publish a PowerSpyPower in the event bus. + * Publish a ExtPMeterPower in the event bus. */ - def publishExternalPowerSpyPower(power: Power): MessageBus => Unit = { - publish(PowerSpyPower(pMeterTopic, power)) + def publishExternalPMeterPower(power: Power): MessageBus => Unit = { + publish(ExtPMeterPower(pMeterTopic, power)) } - def publishPowerSpyPower(power: Power): MessageBus => Unit = { - publish(PowerSpyPower(topic, power)) + def publishPMeterPower(power: Power): MessageBus => Unit = { + publish(ExtPMeterPower(topic, power)) } /** * External methods used for interacting with the bus. */ - def subscribeExternalPowerSpyPower: MessageBus => ActorRef => Unit = { + def subscribeExternalPMeterPower: MessageBus => ActorRef => Unit = { subscribe(pMeterTopic) } - def subscribePowerSpyPower: MessageBus => ActorRef => Unit = { + def subscribePMeterPower: MessageBus => ActorRef => Unit = { subscribe(topic) } } diff --git a/powerapi-core/src/main/scala/org/powerapi/module/powerspy/PowerSpyFormula.scala b/powerapi-core/src/main/scala/org/powerapi/module/extPMeter/ExtPMeterFormula.scala similarity index 81% rename from powerapi-core/src/main/scala/org/powerapi/module/powerspy/PowerSpyFormula.scala rename to powerapi-core/src/main/scala/org/powerapi/module/extPMeter/ExtPMeterFormula.scala index f3ebce0..b18fbef 100644 --- a/powerapi-core/src/main/scala/org/powerapi/module/powerspy/PowerSpyFormula.scala +++ b/powerapi-core/src/main/scala/org/powerapi/module/extPMeter/ExtPMeterFormula.scala @@ -20,7 +20,7 @@ * * If not, please consult http://www.gnu.org/licenses/agpl-3.0.html. */ -package org.powerapi.module.powerspy +package org.powerapi.module.extPMeter import akka.event.LoggingReceive import org.powerapi.core.{OSHelper, APIComponent, MessageBus} @@ -29,7 +29,7 @@ import org.powerapi.core.power._ import org.powerapi.core.target.{Application, All, Process, TargetUsageRatio} import org.powerapi.module.{Cache, CacheKey} import org.powerapi.module.PowerChannel.publishRawPowerReport -import org.powerapi.module.powerspy.PowerSpyChannel.{PowerSpyPower, subscribePowerSpyPower} +import org.powerapi.module.extPMeter.ExtPMeterChannel.{ExtPMeterPower, subscribePMeterPower} import scala.reflect.ClassTag /** @@ -39,20 +39,21 @@ import scala.reflect.ClassTag * The simple CpuSensor is used for getting the Target cpu usage ratio (UsageReport). * * @author Maxime Colmant + * @author Loïc Huertas */ -class PowerSpyFormula(eventBus: MessageBus, osHelper: OSHelper, idlePower: Power) extends APIComponent { +class ExtPMeterFormula(eventBus: MessageBus, osHelper: OSHelper, idlePower: Power) extends APIComponent { override def preStart(): Unit = { - subscribePowerSpyPower(eventBus)(self) + subscribePMeterPower(eventBus)(self) subscribeMonitorTick(eventBus)(self) super.preStart() } def receive: PartialFunction[Any, Unit] = running(None) - def running(pspyPower: Option[PowerSpyPower]): PartialFunction[Any, Unit] = LoggingReceive { - case msg: PowerSpyPower => context.become(running(Some(msg))) - case msg: MonitorTick => compute(pspyPower, msg) + def running(epmPower: Option[ExtPMeterPower]): PartialFunction[Any, Unit] = LoggingReceive { + case msg: ExtPMeterPower => context.become(running(Some(msg))) + case msg: MonitorTick => compute(epmPower, msg) } orElse default // In order to compute the target's ratio @@ -98,17 +99,17 @@ class PowerSpyFormula(eventBus: MessageBus, osHelper: OSHelper, idlePower: Power } } - def compute(pSpyPower: Option[PowerSpyPower], monitorTick: MonitorTick): Unit = { - pSpyPower match { + def compute(epmPower: Option[ExtPMeterPower], monitorTick: MonitorTick): Unit = { + epmPower match { case Some(pPower) => { lazy val dynamicP = if(pPower.power.toMilliWatts - idlePower.toMilliWatts > 0) pPower.power - idlePower else 0.W monitorTick.target match { - case All => publishRawPowerReport(monitorTick.muid, monitorTick.target, pPower.power, "PowerSpy", monitorTick.tick)(eventBus) - case _ => publishRawPowerReport(monitorTick.muid, monitorTick.target, dynamicP * targetCpuUsageRatio(monitorTick).ratio, "PowerSpy", monitorTick.tick)(eventBus) + case All => publishRawPowerReport(monitorTick.muid, monitorTick.target, pPower.power, "ExtPMeter", monitorTick.tick)(eventBus) + case _ => publishRawPowerReport(monitorTick.muid, monitorTick.target, dynamicP * targetCpuUsageRatio(monitorTick).ratio, "ExtPMeter", monitorTick.tick)(eventBus) } } - case _ => log.debug("{}", "no PowerSpyPower message received") + case _ => log.debug("{}", "no ExtPMeterPower message received") } } } diff --git a/powerapi-core/src/main/scala/org/powerapi/module/powerspy/PowerSpyFormulaConfiguration.scala b/powerapi-core/src/main/scala/org/powerapi/module/extPMeter/ExtPMeterFormulaConfiguration.scala similarity index 87% rename from powerapi-core/src/main/scala/org/powerapi/module/powerspy/PowerSpyFormulaConfiguration.scala rename to powerapi-core/src/main/scala/org/powerapi/module/extPMeter/ExtPMeterFormulaConfiguration.scala index 397cd96..bf6bc84 100644 --- a/powerapi-core/src/main/scala/org/powerapi/module/powerspy/PowerSpyFormulaConfiguration.scala +++ b/powerapi-core/src/main/scala/org/powerapi/module/extPMeter/ExtPMeterFormulaConfiguration.scala @@ -20,7 +20,7 @@ * * If not, please consult http://www.gnu.org/licenses/agpl-3.0.html. */ -package org.powerapi.module.powerspy +package org.powerapi.module.extPMeter import org.powerapi.core.{Configuration, ConfigValue} import org.powerapi.core.power._ @@ -29,8 +29,9 @@ import org.powerapi.core.power._ * Main configuration. * * @author Maxime Colmant + * @author Loïc Huertas */ -trait PowerSpyFormulaConfiguration extends Configuration { +trait ExtPMeterFormulaConfiguration extends Configuration { lazy val idlePower = load { _.getDouble(s"powerapi.hardware.idle-power") } match { case ConfigValue(idle) => idle.W case _ => 0.W diff --git a/powerapi-core/src/main/scala/org/powerapi/module/powerspy/PowerSpyModule.scala b/powerapi-core/src/main/scala/org/powerapi/module/extPMeter/ExtPMeterModule.scala similarity index 67% rename from powerapi-core/src/main/scala/org/powerapi/module/powerspy/PowerSpyModule.scala rename to powerapi-core/src/main/scala/org/powerapi/module/extPMeter/ExtPMeterModule.scala index 4133b69..27310b8 100644 --- a/powerapi-core/src/main/scala/org/powerapi/module/powerspy/PowerSpyModule.scala +++ b/powerapi-core/src/main/scala/org/powerapi/module/extPMeter/ExtPMeterModule.scala @@ -20,17 +20,17 @@ * * If not, please consult http://www.gnu.org/licenses/agpl-3.0.html. */ -package org.powerapi.module.powerspy +package org.powerapi.module.extPMeter import org.powerapi.PowerModule -import org.powerapi.core.{OSHelper, LinuxHelper} +import org.powerapi.core.{ExternalPMeter, OSHelper} import org.powerapi.core.power.Power import scala.concurrent.duration.FiniteDuration -class PowerSpyModule(osHelper: OSHelper, mac: String, interval: FiniteDuration, idlePower: Power) extends PowerModule { +class ExtPMeterModule(osHelper: OSHelper, extPMeter: ExternalPMeter, idlePower: Power) extends PowerModule { lazy val underlyingClasses = eventBus match { case Some(bus) => { - (Seq((classOf[PowerSpySensor], Seq(new PowerSpyPMeter(bus, mac, interval)))), Seq((classOf[PowerSpyFormula], Seq(osHelper, idlePower)))) + (Seq((classOf[ExtPMeterSensor], Seq(extPMeter))), Seq((classOf[ExtPMeterFormula], Seq(osHelper, idlePower)))) } case _ => (Seq(), Seq()) } @@ -39,10 +39,3 @@ class PowerSpyModule(osHelper: OSHelper, mac: String, interval: FiniteDuration, lazy val underlyingFormulaeClasses = underlyingClasses._2 } -object PowerSpyModule extends PowerSpyFormulaConfiguration with PowerSpyPMeterConfiguration { - def apply(): PowerSpyModule = { - val linuxHelper = new LinuxHelper - - new PowerSpyModule(linuxHelper, mac, interval, idlePower) - } -} diff --git a/powerapi-core/src/main/scala/org/powerapi/module/powerspy/PowerSpySensor.scala b/powerapi-core/src/main/scala/org/powerapi/module/extPMeter/ExtPMeterSensor.scala similarity index 68% rename from powerapi-core/src/main/scala/org/powerapi/module/powerspy/PowerSpySensor.scala rename to powerapi-core/src/main/scala/org/powerapi/module/extPMeter/ExtPMeterSensor.scala index 2df2469..4d9e094 100644 --- a/powerapi-core/src/main/scala/org/powerapi/module/powerspy/PowerSpySensor.scala +++ b/powerapi-core/src/main/scala/org/powerapi/module/extPMeter/ExtPMeterSensor.scala @@ -20,22 +20,23 @@ * * If not, please consult http://www.gnu.org/licenses/agpl-3.0.html. */ -package org.powerapi.module.powerspy +package org.powerapi.module.extPMeter import akka.event.LoggingReceive import org.powerapi.core.{ExternalPMeter, MessageBus, APIComponent} -import org.powerapi.module.powerspy.PowerSpyChannel.{PowerSpyPower, publishPowerSpyPower, subscribeExternalPowerSpyPower} +import org.powerapi.module.extPMeter.ExtPMeterChannel.{ExtPMeterPower, publishPMeterPower, subscribeExternalPMeterPower} /** - * PowerSpySensor's implementation by using an helper. + * ExtPMeterSensor's implementation by using an helper. * * @author Maxime Colmant + * @author Loïc Huertas */ -class PowerSpySensor(eventBus: MessageBus, pMeter: ExternalPMeter) extends APIComponent { +class ExtPMeterSensor(eventBus: MessageBus, pMeter: ExternalPMeter) extends APIComponent { override def preStart(): Unit = { - subscribeExternalPowerSpyPower(eventBus)(self) - pMeter.init() + subscribeExternalPMeterPower(eventBus)(self) + pMeter.init(eventBus) pMeter.start() super.preStart() } @@ -46,10 +47,10 @@ class PowerSpySensor(eventBus: MessageBus, pMeter: ExternalPMeter) extends APICo } def receive: PartialFunction[Any, Unit] = LoggingReceive { - case msg: PowerSpyPower => sense(msg) + case msg: ExtPMeterPower => sense(msg) } orElse default - def sense(pSpyPower: PowerSpyPower): Unit = { - publishPowerSpyPower(pSpyPower.power)(eventBus) + def sense(epmPower: ExtPMeterPower): Unit = { + publishPMeterPower(epmPower.power)(eventBus) } } diff --git a/powerapi-core/src/main/scala/org/powerapi/module/extPMeter/g5k/G5kOmegaWattModule.scala b/powerapi-core/src/main/scala/org/powerapi/module/extPMeter/g5k/G5kOmegaWattModule.scala new file mode 100644 index 0000000..38bd6cc --- /dev/null +++ b/powerapi-core/src/main/scala/org/powerapi/module/extPMeter/g5k/G5kOmegaWattModule.scala @@ -0,0 +1,35 @@ +/* + * This software is licensed under the GNU Affero General Public License, quoted below. + * + * This file is a part of PowerAPI. + * + * Copyright (C) 2011-2015 Inria, University of Lille 1. + * + * PowerAPI is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * PowerAPI 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with PowerAPI. + * + * If not, please consult http://www.gnu.org/licenses/agpl-3.0.html. + */ +package org.powerapi.module.extPMeter.g5k + +import org.powerapi.core.LinuxHelper +import org.powerapi.module.extPMeter.{ExtPMeterFormulaConfiguration, ExtPMeterModule} + +object G5kOmegaWattModule extends ExtPMeterFormulaConfiguration { + def apply(prefixConfig: Option[String] = None): ExtPMeterModule = { + val conf = new G5kPMeterConfiguration(prefixConfig) + val linuxHelper = new LinuxHelper + + new ExtPMeterModule(linuxHelper, new G5kPMeter(conf.probe, conf.interval), idlePower) + } +} diff --git a/powerapi-core/src/main/scala/org/powerapi/module/extPMeter/g5k/G5kPMeter.scala b/powerapi-core/src/main/scala/org/powerapi/module/extPMeter/g5k/G5kPMeter.scala new file mode 100644 index 0000000..3017bac --- /dev/null +++ b/powerapi-core/src/main/scala/org/powerapi/module/extPMeter/g5k/G5kPMeter.scala @@ -0,0 +1,101 @@ +/* + * This software is licensed under the GNU Affero General Public License, quoted below. + * + * This file is a part of PowerAPI. + * + * Copyright (C) 2011-2015 Inria, University of Lille 1. + * + * PowerAPI is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * PowerAPI 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with PowerAPI. + * + * If not, please consult http://www.gnu.org/licenses/agpl-3.0.html. + */ +package org.powerapi.module.extPMeter.g5k + +import org.powerapi.core.{MessageBus, ExternalPMeter} +import org.powerapi.core.power._ +import org.powerapi.module.extPMeter.ExtPMeterChannel.publishExternalPMeterPower +import org.apache.logging.log4j.LogManager +import scala.concurrent.duration.{FiniteDuration, DurationInt} +import spray.json._ +import DefaultJsonProtocol._ + + +case class PowerR(integrated: Double, timestamp: Long, `type`: String, unit: String, value: Double) +case class Probe(power: PowerR) + +object PowerJsonProtocol extends DefaultJsonProtocol { + implicit val powerrFormat = jsonFormat5(PowerR) + implicit def probeFormat = jsonFormat1(Probe) +} + +/** + * Powermeter (Omegawatt) from Grid5000 Lyon site special helper. + * @see https://intranet.grid5000.fr/supervision/lyon/wattmetre/ + * + * @author Loïc Huertas + */ +class G5kPMeter(probeUrl: String, interval: FiniteDuration) extends ExternalPMeter { + + @volatile private var running = true + @volatile private var thread: Option[java.lang.Thread] = None + + protected var eventBus: Option[MessageBus] = None + + private val log = LogManager.getLogger + + def init(bus: MessageBus): Unit = { + eventBus = Some(bus) + running = true + } + + def start(): Unit = { + thread match { + case None => { + val threadInst = new java.lang.Thread { + override def run(): Unit = { + while(running) { + readRealTime() match { + case Some(rtValue) if eventBus.get != None => publishExternalPMeterPower(Power(rtValue.value, rtValue.unit))(eventBus.get) + case _ => {} + } + Thread.sleep(interval.toMillis) + } + } + } + + threadInst.start() + thread = Some(threadInst) + } + case _ => log.debug("Connexion already established") + } + } + + def stop(): Unit = { + running = false + + thread match { + case Some(thr) => thr.join(1.seconds.toMillis) + case _ => log.debug("Call the method start() before stopping.") + } + + thread = None + } + + private def readRealTime(): Option[PowerR] = { + import PowerJsonProtocol._ + val json = scala.io.Source.fromURL(probeUrl).mkString.parseJson + val result = json.asJsObject.fields.head._2.convertTo[Probe] + Some(result.power) + } +} diff --git a/powerapi-core/src/main/scala/org/powerapi/module/extPMeter/g5k/G5kPMeterConfiguration.scala b/powerapi-core/src/main/scala/org/powerapi/module/extPMeter/g5k/G5kPMeterConfiguration.scala new file mode 100644 index 0000000..1e231b0 --- /dev/null +++ b/powerapi-core/src/main/scala/org/powerapi/module/extPMeter/g5k/G5kPMeterConfiguration.scala @@ -0,0 +1,44 @@ +/* + * This software is licensed under the GNU Affero General Public License, quoted below. + * + * This file is a part of PowerAPI. + * + * Copyright (C) 2011-2015 Inria, University of Lille 1. + * + * PowerAPI is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * PowerAPI 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with PowerAPI. + * + * If not, please consult http://www.gnu.org/licenses/agpl-3.0.html. + */ +package org.powerapi.module.extPMeter.g5k + +import java.util.concurrent.TimeUnit +import org.powerapi.core.{ConfigValue, Configuration} +import scala.concurrent.duration.{FiniteDuration, DurationLong} + +/** + * Main configuration. + * + * @author Loïc Huertas + */ +class G5kPMeterConfiguration(prefix: Option[String]) extends Configuration(prefix) { + lazy val probe: String = load { _.getString(s"${configurationPath}g5k.probe") } match { + case ConfigValue(address) => address + case _ => "" + } + + lazy val interval: FiniteDuration = load { _.getDuration(s"${configurationPath}g5k.interval", TimeUnit.NANOSECONDS) } match { + case ConfigValue(value) => value.nanoseconds + case _ => 1l.seconds + } +} diff --git a/powerapi-core/src/main/scala/org/powerapi/module/extPMeter/powerspy/PowerSpyModule.scala b/powerapi-core/src/main/scala/org/powerapi/module/extPMeter/powerspy/PowerSpyModule.scala new file mode 100644 index 0000000..2f72bc2 --- /dev/null +++ b/powerapi-core/src/main/scala/org/powerapi/module/extPMeter/powerspy/PowerSpyModule.scala @@ -0,0 +1,35 @@ +/* + * This software is licensed under the GNU Affero General Public License, quoted below. + * + * This file is a part of PowerAPI. + * + * Copyright (C) 2011-2015 Inria, University of Lille 1. + * + * PowerAPI is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * PowerAPI 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with PowerAPI. + * + * If not, please consult http://www.gnu.org/licenses/agpl-3.0.html. + */ +package org.powerapi.module.extPMeter.powerspy + +import org.powerapi.core.LinuxHelper +import org.powerapi.module.extPMeter.{ExtPMeterFormulaConfiguration, ExtPMeterModule} + +object PowerSpyModule extends ExtPMeterFormulaConfiguration { + def apply(prefixConfig: Option[String] = None): ExtPMeterModule = { + val conf = new PowerSpyPMeterConfiguration(prefixConfig) + val linuxHelper = new LinuxHelper + + new ExtPMeterModule(linuxHelper, new PowerSpyPMeter(conf.mac, conf.interval), idlePower) + } +} diff --git a/powerapi-core/src/main/scala/org/powerapi/module/powerspy/PowerSpyPMeter.scala b/powerapi-core/src/main/scala/org/powerapi/module/extPMeter/powerspy/PowerSpyPMeter.scala similarity index 86% rename from powerapi-core/src/main/scala/org/powerapi/module/powerspy/PowerSpyPMeter.scala rename to powerapi-core/src/main/scala/org/powerapi/module/extPMeter/powerspy/PowerSpyPMeter.scala index 7274e49..6e1d19b 100644 --- a/powerapi-core/src/main/scala/org/powerapi/module/powerspy/PowerSpyPMeter.scala +++ b/powerapi-core/src/main/scala/org/powerapi/module/extPMeter/powerspy/PowerSpyPMeter.scala @@ -20,12 +20,12 @@ * * If not, please consult http://www.gnu.org/licenses/agpl-3.0.html. */ -package org.powerapi.module.powerspy +package org.powerapi.module.extPMeter.powerspy import fr.inria.powerspy.core.PowerSpy import org.powerapi.core.{MessageBus, ExternalPMeter} import org.powerapi.core.power._ -import org.powerapi.module.powerspy.PowerSpyChannel.publishExternalPowerSpyPower +import org.powerapi.module.extPMeter.ExtPMeterChannel.publishExternalPMeterPower import org.apache.logging.log4j.LogManager import scala.concurrent.duration.{FiniteDuration, DurationInt} @@ -35,16 +35,19 @@ import scala.concurrent.duration.{FiniteDuration, DurationInt} * * @author Maxime Colmant */ -class PowerSpyPMeter(eventBus: MessageBus, mac: String, interval: FiniteDuration) extends ExternalPMeter { +class PowerSpyPMeter(mac: String, interval: FiniteDuration) extends ExternalPMeter { @volatile private var running = true @volatile private var thread: Option[java.lang.Thread] = None @volatile private var powerspy: Option[PowerSpy] = None + protected var eventBus: Option[MessageBus] = None + private val log = LogManager.getLogger - def init(): Unit = { + def init(bus: MessageBus): Unit = { powerspy = PowerSpy.init(mac) + eventBus = Some(bus) running = true } @@ -66,7 +69,7 @@ class PowerSpyPMeter(eventBus: MessageBus, mac: String, interval: FiniteDuration override def run(): Unit = { while(running) { pSpy.readRealTime() match { - case Some(rtValue) => publishExternalPowerSpyPower(rtValue.power.W)(eventBus) + case Some(rtValue) if eventBus.get != None => publishExternalPMeterPower(rtValue.power.W)(eventBus.get) case _ => {} } } diff --git a/powerapi-core/src/main/scala/org/powerapi/module/powerspy/PowerSpyPMeterConfiguration.scala b/powerapi-core/src/main/scala/org/powerapi/module/extPMeter/powerspy/PowerSpyPMeterConfiguration.scala similarity index 78% rename from powerapi-core/src/main/scala/org/powerapi/module/powerspy/PowerSpyPMeterConfiguration.scala rename to powerapi-core/src/main/scala/org/powerapi/module/extPMeter/powerspy/PowerSpyPMeterConfiguration.scala index 712084f..b502089 100644 --- a/powerapi-core/src/main/scala/org/powerapi/module/powerspy/PowerSpyPMeterConfiguration.scala +++ b/powerapi-core/src/main/scala/org/powerapi/module/extPMeter/powerspy/PowerSpyPMeterConfiguration.scala @@ -20,7 +20,7 @@ * * If not, please consult http://www.gnu.org/licenses/agpl-3.0.html. */ -package org.powerapi.module.powerspy +package org.powerapi.module.extPMeter.powerspy import java.util.concurrent.TimeUnit import org.powerapi.core.{ConfigValue, Configuration} @@ -31,13 +31,13 @@ import scala.concurrent.duration.{FiniteDuration, DurationLong} * * @author Maxime Colmant */ -trait PowerSpyPMeterConfiguration extends Configuration { - lazy val mac = load { _.getString("powerspy.mac") } match { +class PowerSpyPMeterConfiguration(prefix: Option[String]) extends Configuration(prefix) { + lazy val mac: String = load { _.getString(s"${configurationPath}powerspy.mac") } match { case ConfigValue(address) => address case _ => "" } - lazy val interval: FiniteDuration = load { _.getDuration("powerspy.interval", TimeUnit.NANOSECONDS) } match { + lazy val interval: FiniteDuration = load { _.getDuration(s"${configurationPath}powerspy.interval", TimeUnit.NANOSECONDS) } match { case ConfigValue(value) => value.nanoseconds case _ => 1l.seconds } diff --git a/powerapi-core/src/test/resources/configuration-suite.conf b/powerapi-core/src/test/resources/configuration-suite.conf index bf345dc..467e0fd 100644 --- a/powerapi-core/src/test/resources/configuration-suite.conf +++ b/powerapi-core/src/test/resources/configuration-suite.conf @@ -86,9 +86,12 @@ powerapi.libpfm.NR-perf-event-open = 128 powerapi.hardware.idle-power = 87.50 -powerspy.mac = "00:0B:CE:07:1E:9B" +powerspy.mac = "00:06:66:4D:F4:BB" powerspy.interval = 250ms +g5k.probe = "http://kwapi.lyon.grid5000.fr:5000/probes/lyon.taurus-1/power/" +g5k.interval = 250ms + powerapi.procfs.cpu-info-path = "p1" powerapi.cpu.msr-path = "p2" diff --git a/powerapi-core/src/test/scala/org/powerapi/PowerMeterSuite.scala b/powerapi-core/src/test/scala/org/powerapi/PowerMeterSuite.scala index 722c308..39fecae 100644 --- a/powerapi-core/src/test/scala/org/powerapi/PowerMeterSuite.scala +++ b/powerapi-core/src/test/scala/org/powerapi/PowerMeterSuite.scala @@ -25,14 +25,21 @@ package org.powerapi import akka.actor.{Props, ActorSystem} import akka.testkit.{TestActorRef, TestKit} import akka.util.Timeout -import org.powerapi.core.MessageBus +import org.powerapi.core.{ExternalPMeter, MessageBus} import org.powerapi.module.cpu.dvfs.CpuDvfsModule import org.powerapi.module.cpu.simple.{SigarCpuSimpleModule, ProcFSCpuSimpleModule} import org.powerapi.module.libpfm.{LibpfmCoreProcessModule, LibpfmCoreSensorModule, LibpfmHelper, LibpfmCoreModule, LibpfmModule, LibpfmProcessModule} -import org.powerapi.module.powerspy.PowerSpyModule +import org.powerapi.module.extPMeter.powerspy.PowerSpyModule +import org.powerapi.module.extPMeter.g5k.G5kOmegaWattModule import org.powerapi.module.rapl.RAPLModule import scala.concurrent.duration.DurationInt +class MockPMeter extends ExternalPMeter { + def init(bus: MessageBus): Unit = {} + def start(): Unit = {} + def stop(): Unit = {} +} + class PowerMeterSuite(system: ActorSystem) extends UnitTest(system) { implicit val timeout = Timeout(1.seconds) @@ -102,7 +109,12 @@ class PowerMeterSuite(system: ActorSystem) extends UnitTest(system) { } it should "load the PowerSpyModule" ignore new EventBus { - val actor = TestActorRef(Props(classOf[PowerMeterActor], eventBus, Seq(PowerSpyModule()), Timeout(1.seconds)))(system) + val actor = TestActorRef(Props(classOf[PowerMeterActor], eventBus, Seq(PowerSpyModule(None)), Timeout(1.seconds)))(system) + actor.children.size should equal(4) + } + + it should "load the G5kOmegaWattModule" ignore new EventBus { + val actor = TestActorRef(Props(classOf[PowerMeterActor], eventBus, Seq(G5kOmegaWattModule(None)), Timeout(1.seconds)))(system) actor.children.size should equal(4) } diff --git a/powerapi-core/src/test/scala/org/powerapi/module/powerspy/PowerSpyFormulaConfigurationSuite.scala b/powerapi-core/src/test/scala/org/powerapi/module/extPMeter/ExtPMeterFormulaConfigurationSuite.scala similarity index 77% rename from powerapi-core/src/test/scala/org/powerapi/module/powerspy/PowerSpyFormulaConfigurationSuite.scala rename to powerapi-core/src/test/scala/org/powerapi/module/extPMeter/ExtPMeterFormulaConfigurationSuite.scala index 4eb097f..efa642b 100644 --- a/powerapi-core/src/test/scala/org/powerapi/module/powerspy/PowerSpyFormulaConfigurationSuite.scala +++ b/powerapi-core/src/test/scala/org/powerapi/module/extPMeter/ExtPMeterFormulaConfigurationSuite.scala @@ -20,7 +20,7 @@ * * If not, please consult http://www.gnu.org/licenses/agpl-3.0.html. */ -package org.powerapi.module.powerspy +package org.powerapi.module.extPMeter import akka.actor.ActorSystem import akka.testkit.TestKit @@ -29,18 +29,18 @@ import org.powerapi.UnitTest import org.powerapi.core.power._ import scala.concurrent.duration.DurationInt -class PowerSpyFormulaConfigurationSuite(system: ActorSystem) extends UnitTest(system) { +class ExtPMeterFormulaConfigurationSuite(system: ActorSystem) extends UnitTest(system) { implicit val timeout = Timeout(1.seconds) - def this() = this(ActorSystem("PowerSpyFormulaConfigurationSuite")) + def this() = this(ActorSystem("ExtPMeterFormulaConfigurationSuite")) override def afterAll() = { TestKit.shutdownActorSystem(system) } - "The PowerSpyFormulaConfiguration" should "read correctly the values from a resource file" in { - val configuration = new PowerSpyFormulaConfiguration {} + "The ExtPMeterFormulaConfiguration" should "read correctly the values from a resource file" in { + val configuration = new ExtPMeterFormulaConfiguration {} configuration.idlePower should equal(87.50.W) } diff --git a/powerapi-core/src/test/scala/org/powerapi/module/powerspy/PowerSpyFormulaSuite.scala b/powerapi-core/src/test/scala/org/powerapi/module/extPMeter/ExtPMeterFormulaSuite.scala similarity index 91% rename from powerapi-core/src/test/scala/org/powerapi/module/powerspy/PowerSpyFormulaSuite.scala rename to powerapi-core/src/test/scala/org/powerapi/module/extPMeter/ExtPMeterFormulaSuite.scala index f91ba81..391e256 100644 --- a/powerapi-core/src/test/scala/org/powerapi/module/powerspy/PowerSpyFormulaSuite.scala +++ b/powerapi-core/src/test/scala/org/powerapi/module/extPMeter/ExtPMeterFormulaSuite.scala @@ -20,7 +20,7 @@ * * If not, please consult http://www.gnu.org/licenses/agpl-3.0.html. */ -package org.powerapi.module.powerspy +package org.powerapi.module.extPMeter import akka.actor.{ActorSystem, Props} import akka.testkit.{TestActorRef, TestKit} @@ -32,18 +32,18 @@ import org.powerapi.core.{OSHelper, MessageBus, Thread, TimeInStates} import org.powerapi.core.MonitorChannel.publishMonitorTick import org.powerapi.core.target.{All, Application, intToProcess, Process, Target, TargetUsageRatio} import org.powerapi.module.PowerChannel.{RawPowerReport, subscribeRawPowerReport} -import org.powerapi.module.powerspy.PowerSpyChannel.publishPowerSpyPower +import org.powerapi.module.extPMeter.ExtPMeterChannel.publishPMeterPower import scala.concurrent.duration.DurationInt -class PowerSpyFormulaSuite(system: ActorSystem) extends UnitTest(system) { +class ExtPMeterFormulaSuite(system: ActorSystem) extends UnitTest(system) { - def this() = this(ActorSystem("PowerSpyFormulaSuite")) + def this() = this(ActorSystem("ExtPMeterFormulaSuite")) override def afterAll() = { TestKit.shutdownActorSystem(system) } - "A PowerSpyFormula" should "listen PowerSpyPower/UsageReport messages and produce PowerReport messages" in { + "A ExtPMeterFormula" should "listen ExtPMeterPower/UsageReport messages and produce PowerReport messages" in { val eventBus = new MessageBus val globalElapsedTime1: Long = 43171 + 1 + 24917 + 25883594 + 1160 + 19 + 1477 + 0 @@ -66,7 +66,7 @@ class PowerSpyFormulaSuite(system: ActorSystem) extends UnitTest(system) { val processRatio1 = TargetUsageRatio((p1ElapsedTime - oldP1ElapsedTime).toDouble / (activeElapsedTime1 - oldActiveElapsedTime1)) val processRatio2 = TargetUsageRatio((p2ElapsedTime - oldP2ElapsedTime).toDouble / (activeElapsedTime2 - oldActiveElapsedTime2)) - TestActorRef(Props(classOf[PowerSpyFormula], eventBus, new OSHelper { + TestActorRef(Props(classOf[ExtPMeterFormula], eventBus, new OSHelper { import org.powerapi.core.GlobalCpuTime private var targetTimes = Map[Target, List[Long]]( @@ -120,7 +120,7 @@ class PowerSpyFormulaSuite(system: ActorSystem) extends UnitTest(system) { publishMonitorTick(muid1, 1, tickMock)(eventBus) expectNoMsg(3.seconds) - publishPowerSpyPower(120.W)(eventBus) + publishPMeterPower(120.W)(eventBus) publishMonitorTick(muid1, 1, tickMock)(eventBus) var ret = expectMsgClass(classOf[RawPowerReport]) @@ -143,7 +143,7 @@ class PowerSpyFormulaSuite(system: ActorSystem) extends UnitTest(system) { ret.power should equal(120.W) ret.tick should equal(tickMock) - publishPowerSpyPower(150.W)(eventBus) + publishPMeterPower(150.W)(eventBus) publishMonitorTick(muid1, 1, tickMock)(eventBus) ret = expectMsgClass(classOf[RawPowerReport]) @@ -158,7 +158,7 @@ class PowerSpyFormulaSuite(system: ActorSystem) extends UnitTest(system) { ret.power should equal((processRatio2.ratio * (150 - 90)).W) ret.tick should equal(tickMock) - publishPowerSpyPower(140.W)(eventBus) + publishPMeterPower(140.W)(eventBus) publishMonitorTick(muid3, All, tickMock)(eventBus) ret = expectMsgClass(classOf[RawPowerReport]) diff --git a/powerapi-core/src/test/scala/org/powerapi/module/powerspy/PowerSpyModulesSuite.scala b/powerapi-core/src/test/scala/org/powerapi/module/extPMeter/ExtPMeterModulesSuite.scala similarity index 77% rename from powerapi-core/src/test/scala/org/powerapi/module/powerspy/PowerSpyModulesSuite.scala rename to powerapi-core/src/test/scala/org/powerapi/module/extPMeter/ExtPMeterModulesSuite.scala index d8f1966..979a591 100644 --- a/powerapi-core/src/test/scala/org/powerapi/module/powerspy/PowerSpyModulesSuite.scala +++ b/powerapi-core/src/test/scala/org/powerapi/module/extPMeter/ExtPMeterModulesSuite.scala @@ -20,29 +20,29 @@ * * If not, please consult http://www.gnu.org/licenses/agpl-3.0.html. */ -package org.powerapi.module.powerspy +package org.powerapi.module.extPMeter import java.util.UUID import akka.actor.ActorSystem import akka.testkit.TestKit import akka.util.Timeout import org.powerapi.UnitTest -import org.powerapi.core.{MessageBus, GlobalCpuTime, TimeInStates, OSHelper, Thread} +import org.powerapi.core.{ExternalPMeter, MessageBus, GlobalCpuTime, TimeInStates, OSHelper, Thread} import org.powerapi.core.target.{Application, TargetUsageRatio, Process} import org.powerapi.core.power._ import scala.concurrent.duration.DurationInt -class PowerSpyModulesSuite(system: ActorSystem) extends UnitTest(system) { +class ExtPMeterModulesSuite(system: ActorSystem) extends UnitTest(system) { implicit val timeout = Timeout(1.seconds) - def this() = this(ActorSystem("PowerSpyModulesSuite")) + def this() = this(ActorSystem("ExtPMeterModulesSuite")) override def afterAll() = { TestKit.shutdownActorSystem(system) } - "The PowerSpyModule class" should "create the underlying classes (sensors/formulae)" in { + "The ExtPMeterModule class" should "create the underlying classes (sensors/formulae)" in { val osHelper = new OSHelper { override def getCPUFrequencies: Set[Long] = Set() override def getThreads(process: Process): Set[Thread] = Set() @@ -53,17 +53,23 @@ class PowerSpyModulesSuite(system: ActorSystem) extends UnitTest(system) { override def getGlobalCpuTime: GlobalCpuTime = GlobalCpuTime(0, 0) override def getProcesses(application: Application): Set[Process] = Set() } + + val mockPMeter = new ExternalPMeter { + def init(bus: MessageBus): Unit = {} + def start(): Unit = {} + def stop(): Unit = {} + } - val module = new PowerSpyModule(osHelper, "00:00:00:00:00:00", 10.seconds, 1.W) { + val module = new ExtPMeterModule(osHelper, mockPMeter, 1.W) { eventBus = Some(new MessageBus) } module.underlyingSensorsClasses.size should equal(1) - module.underlyingSensorsClasses(0)._1 should equal(classOf[PowerSpySensor]) + module.underlyingSensorsClasses(0)._1 should equal(classOf[ExtPMeterSensor]) module.underlyingSensorsClasses(0)._2.size should equal(1) - module.underlyingSensorsClasses(0)._2(0).getClass should equal(classOf[PowerSpyPMeter]) + module.underlyingSensorsClasses(0)._2(0).getClass should equal(mockPMeter.getClass) module.underlyingFormulaeClasses.size should equal(1) - module.underlyingFormulaeClasses(0)._1 should equal(classOf[PowerSpyFormula]) + module.underlyingFormulaeClasses(0)._1 should equal(classOf[ExtPMeterFormula]) module.underlyingFormulaeClasses(0)._2.size should equal(2) module.underlyingFormulaeClasses(0)._2(0) should equal(osHelper) module.underlyingFormulaeClasses(0)._2(1) should equal(1.W) diff --git a/powerapi-core/src/test/scala/org/powerapi/module/extPMeter/ExtPMeterSensorSuite.scala b/powerapi-core/src/test/scala/org/powerapi/module/extPMeter/ExtPMeterSensorSuite.scala new file mode 100644 index 0000000..0329eba --- /dev/null +++ b/powerapi-core/src/test/scala/org/powerapi/module/extPMeter/ExtPMeterSensorSuite.scala @@ -0,0 +1,107 @@ +/* + * This software is licensed under the GNU Affero General Public License, quoted below. + * + * This file is a part of PowerAPI. + * + * Copyright (C) 2011-2015 Inria, University of Lille 1. + * + * PowerAPI is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * PowerAPI 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with PowerAPI. + * + * If not, please consult http://www.gnu.org/licenses/agpl-3.0.html. + */ +package org.powerapi.module.extPMeter + +import akka.actor.{Actor, ActorSystem, Props} +import akka.pattern.gracefulStop +import akka.testkit.{TestActorRef, TestKit} +import akka.util.Timeout +import org.powerapi.UnitTest +import org.powerapi.core.{MessageBus, ExternalPMeter} +import org.powerapi.module.extPMeter.ExtPMeterChannel.publishExternalPMeterPower +import org.powerapi.core.power._ +import org.powerapi.module.extPMeter.ExtPMeterChannel.{ExtPMeterPower, subscribePMeterPower} +import org.powerapi.module.extPMeter.powerspy.PowerSpyPMeter +import org.powerapi.module.extPMeter.g5k.G5kPMeter +import scala.concurrent.duration.DurationInt + +class MockPMeter(eventBus: MessageBus) extends ExternalPMeter { + def init(bus: MessageBus): Unit = {} + def start(): Unit = { + publishExternalPMeterPower(10.W)(eventBus) + publishExternalPMeterPower(20.W)(eventBus) + publishExternalPMeterPower(14.W)(eventBus) + } + def stop(): Unit = {} +} + +class ExtPMeterPowerListener(eventBus: MessageBus) extends Actor { + override def preStart(): Unit = { + subscribePMeterPower(eventBus)(self) + } + + def receive = { + case msg: ExtPMeterPower => println(msg) + } +} + +class ExtPMeterSensorSuite(system: ActorSystem) extends UnitTest(system) { + implicit val timeout = Timeout(1.seconds) + + def this() = this(ActorSystem("ExtPMeterSensorSuite")) + + override def afterAll() = { + TestKit.shutdownActorSystem(system) + } + + trait EventBus { + val eventBus = new MessageBus + } + + "A ExtPMeterSensor" should "listen ExtPMeterPower messages, build a new message and then publish it" in new EventBus { + subscribePMeterPower(eventBus)(testActor) + val extpmSensor = TestActorRef(Props(classOf[ExtPMeterSensor], eventBus, new MockPMeter(eventBus)), "extpmSensor")(system) + + expectMsgClass(classOf[ExtPMeterPower]) match { + case ExtPMeterPower(_, power) => power should equal(10.W) + } + expectMsgClass(classOf[ExtPMeterPower]) match { + case ExtPMeterPower(_, power) => power should equal(20.W) + } + expectMsgClass(classOf[ExtPMeterPower]) match { + case ExtPMeterPower(_, power) => power should equal(14.W) + } + + gracefulStop(extpmSensor, 15.seconds) + } + + it should "open the connection with the PowerSpy, build new messages and then publish them" ignore new EventBus { + val listener = TestActorRef(Props(classOf[ExtPMeterPowerListener], eventBus), "listener")(system) + val extpmSensor = TestActorRef(Props(classOf[ExtPMeterSensor], eventBus, new PowerSpyPMeter("00:06:66:4D:F4:BB", 1.seconds)), "pSpySensor")(system) + + Thread.sleep(15.seconds.toMillis) + + gracefulStop(extpmSensor, 15.seconds) + gracefulStop(listener, 15.seconds) + } + + it should "open the connection with the grid5000 OmegaWatt, build new messages and then publish them" ignore new EventBus { + val listener = TestActorRef(Props(classOf[ExtPMeterPowerListener], eventBus), "listener")(system) + val extpmSensor = TestActorRef(Props(classOf[ExtPMeterSensor], eventBus, new G5kPMeter("http://kwapi.lyon.grid5000.fr:5000/probes/lyon.taurus-1/power/", 1.seconds)), "g5kowSensor")(system) + + Thread.sleep(15.seconds.toMillis) + + gracefulStop(extpmSensor, 15.seconds) + gracefulStop(listener, 15.seconds) + } +} diff --git a/powerapi-core/src/test/scala/org/powerapi/module/extPMeter/g5k/G5kConfigurationSuite.scala b/powerapi-core/src/test/scala/org/powerapi/module/extPMeter/g5k/G5kConfigurationSuite.scala new file mode 100644 index 0000000..48a9745 --- /dev/null +++ b/powerapi-core/src/test/scala/org/powerapi/module/extPMeter/g5k/G5kConfigurationSuite.scala @@ -0,0 +1,47 @@ +/* + * This software is licensed under the GNU Affero General Public License, quoted below. + * + * This file is a part of PowerAPI. + * + * Copyright (C) 2011-2015 Inria, University of Lille 1. + * + * PowerAPI is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * PowerAPI 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with PowerAPI. + * + * If not, please consult http://www.gnu.org/licenses/agpl-3.0.html. + */ +package org.powerapi.module.extPMeter.g5k + +import akka.actor.ActorSystem +import akka.testkit.TestKit +import akka.util.Timeout +import org.powerapi.UnitTest +import scala.concurrent.duration.DurationInt + +class G5kPMeterConfigurationSuite(system: ActorSystem) extends UnitTest(system) { + + implicit val timeout = Timeout(1.seconds) + + def this() = this(ActorSystem("G5kPMeterConfigurationSuite")) + + override def afterAll() = { + TestKit.shutdownActorSystem(system) + } + + "The G5kPMeterConfiguration" should "read correctly the values from a resource file" in { + val configuration = new G5kPMeterConfiguration(None) + + configuration.probe should equal("http://kwapi.lyon.grid5000.fr:5000/probes/lyon.taurus-1/power/") + configuration.interval should equal(250.milliseconds) + } +} diff --git a/powerapi-core/src/test/scala/org/powerapi/module/powerspy/PowerSpyPMeterConfigurationSuite.scala b/powerapi-core/src/test/scala/org/powerapi/module/extPMeter/powerspy/PowerSpyConfigurationSuite.scala similarity index 89% rename from powerapi-core/src/test/scala/org/powerapi/module/powerspy/PowerSpyPMeterConfigurationSuite.scala rename to powerapi-core/src/test/scala/org/powerapi/module/extPMeter/powerspy/PowerSpyConfigurationSuite.scala index 1c4f7f2..ea27a91 100644 --- a/powerapi-core/src/test/scala/org/powerapi/module/powerspy/PowerSpyPMeterConfigurationSuite.scala +++ b/powerapi-core/src/test/scala/org/powerapi/module/extPMeter/powerspy/PowerSpyConfigurationSuite.scala @@ -20,7 +20,7 @@ * * If not, please consult http://www.gnu.org/licenses/agpl-3.0.html. */ -package org.powerapi.module.powerspy +package org.powerapi.module.extPMeter.powerspy import akka.actor.ActorSystem import akka.testkit.TestKit @@ -39,9 +39,9 @@ class PowerSpyPMeterConfigurationSuite(system: ActorSystem) extends UnitTest(sys } "The PowerSpyPMeterConfiguration" should "read correctly the values from a resource file" in { - val configuration = new PowerSpyPMeterConfiguration {} + val configuration = new PowerSpyPMeterConfiguration(None) - configuration.mac should equal("00:0B:CE:07:1E:9B") + configuration.mac should equal("00:06:66:4D:F4:BB") configuration.interval should equal(250.milliseconds) } } diff --git a/powerapi-core/src/test/scala/org/powerapi/module/powerspy/PowerSpySensorSuite.scala b/powerapi-core/src/test/scala/org/powerapi/module/powerspy/PowerSpySensorSuite.scala deleted file mode 100644 index 4693a8c..0000000 --- a/powerapi-core/src/test/scala/org/powerapi/module/powerspy/PowerSpySensorSuite.scala +++ /dev/null @@ -1,95 +0,0 @@ -/* - * This software is licensed under the GNU Affero General Public License, quoted below. - * - * This file is a part of PowerAPI. - * - * Copyright (C) 2011-2015 Inria, University of Lille 1. - * - * PowerAPI is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * PowerAPI 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 Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with PowerAPI. - * - * If not, please consult http://www.gnu.org/licenses/agpl-3.0.html. - */ -package org.powerapi.module.powerspy - -import akka.actor.{Actor, ActorSystem, Props} -import akka.pattern.gracefulStop -import akka.testkit.{TestActorRef, TestKit} -import akka.util.Timeout -import org.powerapi.UnitTest -import org.powerapi.core.{MessageBus, ExternalPMeter} -import org.powerapi.module.powerspy.PowerSpyChannel.publishExternalPowerSpyPower -import org.powerapi.core.power._ -import org.powerapi.module.powerspy.PowerSpyChannel.{PowerSpyPower, subscribePowerSpyPower} -import scala.concurrent.duration.DurationInt - -class MockPMeter(eventBus: MessageBus) extends ExternalPMeter { - def init(): Unit = {} - def start(): Unit = { - publishExternalPowerSpyPower(10.W)(eventBus) - publishExternalPowerSpyPower(20.W)(eventBus) - publishExternalPowerSpyPower(14.W)(eventBus) - } - def stop(): Unit = {} -} - -class PowerSpyPowerListener(eventBus: MessageBus) extends Actor { - override def preStart(): Unit = { - subscribePowerSpyPower(eventBus)(self) - } - - def receive = { - case msg: PowerSpyPower => println(msg) - } -} - -class PowerSpySensorSuite(system: ActorSystem) extends UnitTest(system) { - implicit val timeout = Timeout(1.seconds) - - def this() = this(ActorSystem("PowerSpySensorSuite")) - - override def afterAll() = { - TestKit.shutdownActorSystem(system) - } - - trait EventBus { - val eventBus = new MessageBus - } - - "A PowerSpySensor" should "listen PowerSpyPower messages, build a new message and then publish it" in new EventBus { - subscribePowerSpyPower(eventBus)(testActor) - val pSpySensor = TestActorRef(Props(classOf[PowerSpySensor], eventBus, new MockPMeter(eventBus)), "pSpySensor")(system) - - expectMsgClass(classOf[PowerSpyPower]) match { - case PowerSpyPower(_, power) => power should equal(10.W) - } - expectMsgClass(classOf[PowerSpyPower]) match { - case PowerSpyPower(_, power) => power should equal(20.W) - } - expectMsgClass(classOf[PowerSpyPower]) match { - case PowerSpyPower(_, power) => power should equal(14.W) - } - - gracefulStop(pSpySensor, 15.seconds) - } - - it should "open the connection with the power meter, build new messages and then publish them" ignore new EventBus { - val listener = TestActorRef(Props(classOf[PowerSpyPowerListener], eventBus), "listener")(system) - val pSpySensor = TestActorRef(Props(classOf[PowerSpySensor], eventBus, new PowerSpyPMeter(eventBus, "00:0b:ce:07:1e:9b", 1.seconds)), "pSpySensor")(system) - - Thread.sleep(15.seconds.toMillis) - - gracefulStop(pSpySensor, 15.seconds) - gracefulStop(listener, 15.seconds) - } -} diff --git a/powerapi-daemon/src/main/scala/org/powerapi/daemon/PowerAPId.scala b/powerapi-daemon/src/main/scala/org/powerapi/daemon/PowerAPId.scala index 297ff6e..474a6c3 100644 --- a/powerapi-daemon/src/main/scala/org/powerapi/daemon/PowerAPId.scala +++ b/powerapi-daemon/src/main/scala/org/powerapi/daemon/PowerAPId.scala @@ -40,7 +40,8 @@ import org.powerapi.core.power._ import org.powerapi.module.cpu.dvfs.CpuDvfsModule import org.powerapi.module.cpu.simple.{SigarCpuSimpleModule, ProcFSCpuSimpleModule} import org.powerapi.module.libpfm.{LibpfmHelper, LibpfmCoreProcessModule, LibpfmCoreModule} -import org.powerapi.module.powerspy.PowerSpyModule +import org.powerapi.module.extPMeter.powerspy.PowerSpyModule +import org.powerapi.module.extPMeter.g5k.G5kOmegaWattModule /** * PowerAPI daemon. @@ -113,7 +114,8 @@ class PowerAPId extends Daemon { libpfmHelper.get.init() LibpfmCoreProcessModule(None, libpfmHelper.get) } - case "powerspy" => PowerSpyModule() + case "powerspy" => PowerSpyModule(None) + case "g5k-omegawatt" => G5kOmegaWattModule(None) case "rapl" => RAPLModule() } }).toSeq diff --git a/powerapi-sampling/src/main/scala/org/powerapi/sampling/Sampling.scala b/powerapi-sampling/src/main/scala/org/powerapi/sampling/Sampling.scala index 2345807..357dbcc 100644 --- a/powerapi-sampling/src/main/scala/org/powerapi/sampling/Sampling.scala +++ b/powerapi-sampling/src/main/scala/org/powerapi/sampling/Sampling.scala @@ -32,7 +32,7 @@ import org.powerapi.module.libpfm.{LibpfmHelper, LibpfmCoreSensorModule} import org.powerapi.module.libpfm.PerformanceCounterChannel.{subscribePCReport, PCReport} import org.powerapi.core.power._ import org.powerapi.core.target.All -import org.powerapi.module.powerspy.PowerSpyModule +import org.powerapi.module.extPMeter.powerspy.PowerSpyModule import scala.concurrent.duration.DurationInt import scala.sys.process.{ProcessLogger, stringSeqToProcess} import scala.concurrent.ExecutionContext.Implicits.global @@ -356,7 +356,7 @@ object Sampling { def apply(outputPath: String, configuration: SamplingConfiguration, libpfmHelper: LibpfmHelper): Sampling = { libpfmHelper.init() powerapi = Some(PowerMeter.loadModule(LibpfmCoreSensorModule(None, libpfmHelper, configuration.events))) - externalPMeter = Some(PowerMeter.loadModule(PowerSpyModule())) + externalPMeter = Some(PowerMeter.loadModule(PowerSpyModule(None))) new Sampling(outputPath, configuration, libpfmHelper, powerapi.get, externalPMeter.get) } }