pwrkap-7.30/ 0000775 0000000 0000000 00000000000 11144346405 0012751 5 ustar 00root root 0000000 0000000 pwrkap-7.30/Makefile 0000664 0000000 0000000 00000001550 11144346405 0014412 0 ustar 00root root 0000000 0000000 # Main build script
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
include Rules.mk
SUBDIRS=data bin pwrkap docs man
ALL_SUBDIRS=$(addsuffix _all,$(SUBDIRS))
CLEAN_SUBDIRS=$(addsuffix _clean,$(SUBDIRS))
INSTALL_SUBDIRS=$(addsuffix _install,$(SUBDIRS))
.PHONY: $(SUBDIRS) $(ALL_SUBDIRS) $(CLEAN_SUBDIRS) $(INSTALL_SUBDIRS)
all: $(ALL_SUBDIRS)
$(ALL_SUBDIRS):
$(MAKE) all -C $(subst _all,,$@)
clean: $(CLEAN_SUBDIRS)
$(CLEAN_SUBDIRS):
$(MAKE) clean -C $(subst _clean,,$@)
install: $(INSTALL_SUBDIRS)
$(INSTALL_SUBDIRS):
$(MAKE) install -C $(subst _install,,$@)
dist:
@if test "`git describe`" != "$(PWRKAP_VERSION)" ; then \
echo 'Update PWRKAP_VERSION in the Rules.mk file before running "make dist".' ; \
exit 1 ; \
fi
git archive --format=tar --prefix=pwrkap-$(PWRKAP_VERSION)/ HEAD^{tree} | gzip -9 > pwrkap-$(PWRKAP_VERSION).tar.gz
pwrkap-7.30/Rules.mk 0000664 0000000 0000000 00000000261 11144346405 0014373 0 ustar 00root root 0000000 0000000 # Build script variables
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
PWRKAP_VERSION=7.30
PWRKAP_DATE=2009-01-12
PREFIX?=/usr/local
INSTALL=/usr/bin/install
pwrkap-7.30/bin/ 0000775 0000000 0000000 00000000000 11144346405 0013521 5 ustar 00root root 0000000 0000000 pwrkap-7.30/bin/Makefile 0000664 0000000 0000000 00000001436 11144346405 0015165 0 ustar 00root root 0000000 0000000 # Main build script
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
include ../Rules.mk
BUILD_FILES=pwrkap_aggregate pwrkap_cli pwrkap_gtk pwrkap_main
all: $(BUILD_FILES)
clean:;
rm -rf $(BUILD_FILES)
pwrkap_aggregate: pwrkap.sh.template
sed -e 's|%PREFIX%|$(PREFIX)|g' -e 's/%PROGRAM%/$@/g' < $^ > $@
chmod a+x $@
pwrkap_cli: pwrkap.sh.template
sed -e 's|%PREFIX%|$(PREFIX)|g' -e 's/%PROGRAM%/$@/g' < $^ > $@
chmod a+x $@
pwrkap_gtk: pwrkap.sh.template
sed -e 's|%PREFIX%|$(PREFIX)|g' -e 's/%PROGRAM%/$@/g' < $^ > $@
chmod a+x $@
pwrkap_main: pwrkap.sh.template
sed -e 's|%PREFIX%|$(PREFIX)|g' -e 's/%PROGRAM%/$@/g' < $^ > $@
chmod a+x $@
install: all
mkdir -p $(INST_PREFIX)$(PREFIX)/bin
$(INSTALL) $(BUILD_FILES) -o root -g root -t $(INST_PREFIX)$(PREFIX)/bin
pwrkap-7.30/bin/pwrkap.sh.template 0000664 0000000 0000000 00000000266 11144346405 0017177 0 ustar 00root root 0000000 0000000 #!/bin/sh
# Start a pwrkap program
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
export PYTHONPATH=%PREFIX%/lib/pwrkap
exec python "$PYTHONPATH/%PROGRAM%.py" $*
pwrkap-7.30/data/ 0000775 0000000 0000000 00000000000 11144346405 0013662 5 ustar 00root root 0000000 0000000 pwrkap-7.30/data/Makefile 0000664 0000000 0000000 00000001210 11144346405 0015314 0 ustar 00root root 0000000 0000000 # Main build script
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
include ../Rules.mk
BUILD_FILES=pwrkap.desktop
OTHER_FILES=pwrkap_aggregate.conf ui.glade pwrkap.svg
INSTALL_FILES=$(BUILD_FILES) $(OTHER_FILES)
pwrkap.desktop: pwrkap.desktop.template
sed -e 's|%PREFIX%|$(PREFIX)|g' < $^ > $@
all: $(INSTALL_FILES)
clean:
install: $(INSTALL_FILES)
mkdir -p $(INST_PREFIX)$(PREFIX)/share/pwrkap $(INST_PREFIX)$(PREFIX)/share/applications
$(INSTALL) $(OTHER_FILES) -o root -g root -m 644 -t $(INST_PREFIX)$(PREFIX)/share/pwrkap
$(INSTALL) pwrkap.desktop -o root -g root -m 644 -t $(INST_PREFIX)$(PREFIX)/share/applications
pwrkap-7.30/data/pwrkap.desktop.template 0000664 0000000 0000000 00000000371 11144346405 0020374 0 ustar 00root root 0000000 0000000 [Desktop Entry]
Type=Application
Name=pwrkap
Comment=pwrkap GTK client
Comment[fr]=Un client GTK pour pwrkap
Comment[es]=Un cliente GTK para pwrkap
Exec=pwrkap_gtk
Terminal=false
Icon=%PREFIX%/share/pwrkap/pwrkap.svg
Categories=GNOME;GTK;Utility;
pwrkap-7.30/data/pwrkap.svg 0000664 0000000 0000000 00000112114 11144346405 0015707 0 ustar 00root root 0000000 0000000
pwrkap-7.30/data/pwrkap_aggregate.conf 0000664 0000000 0000000 00000000306 11144346405 0020042 0 ustar 00root root 0000000 0000000 domain 0.0.0.0 9410 agg0 consists of:
system 9.47.66.254 9000 pwrdom0
system 9.47.66.254 9001 pwrdom0
system 9.47.66.254 9002 pwrdom0
system 9.47.66.254 9003 pwrdom0
system 9.47.66.254 9004 pwrdom0
pwrkap-7.30/data/ui.glade 0000664 0000000 0000000 00000047202 11144346405 0015302 0 ustar 00root root 0000000 0000000
GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK700500pwrkap.svg640480TrueTrueGDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK228TrueGDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK1311TrueTrueGDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASKGTK_POLICY_AUTOMATICGTK_POLICY_AUTOMATICGTK_SHADOW_INTrueTrueGDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASKTrueFalseTrueTrueGDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK1213TrueGDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK52139TrueGDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK1245GTK_EXPANDGTK_EXPANDTrueGDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASKEnergy Use:45GTK_EXPANDGTK_EXPANDTrueGDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK10TrueGDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASKFalseFalse11TrueFalseTrueGDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASKTrue1 1 10000000 1 10 10TrueTrueGTK_UPDATE_IF_VALIDFalse190TrueFalseTrueTrueTrueTrueGDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK_Set CapTrue0FalseFalse212235TrueGDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK1212GTK_EXPANDTrueGDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK12TrueGDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASKUtilization:GTK_JUSTIFY_RIGHTTrueGDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASKPower Use:12GTK_EXPANDTrueGDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASKPower _Cap:TrueGTK_JUSTIFY_RIGHTpower_cap_field23TrueGDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASKDiscard samples _after:TrueGTK_JUSTIFY_RIGHTmax_sample_age34TrueFalseGDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK25 Minutes
15 Minutes
30 Minutes
45 Minutes
1 Hour
2 Hours
6 Hours
1 Day1234FalseFalse1TrueGDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASKFalse2TrueGDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASKTrueGDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK<b>Cap exceeded by %dW!</b>TrueTrueTrueGDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASKShow _Graph of Power UseTrue0TrueFalse1FalseFalse3TrueGDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASKGTK_BUTTONBOX_ENDTrueTrueTrueGDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASKClose0FalseFalse4TrueTrue
pwrkap-7.30/docs/ 0000775 0000000 0000000 00000000000 11144346405 0013701 5 ustar 00root root 0000000 0000000 pwrkap-7.30/docs/COPYING 0000664 0000000 0000000 00000043103 11144346405 0014735 0 ustar 00root root 0000000 0000000 GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C)
This program 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 2 of the License, or
(at your option) any later version.
This program 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.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
pwrkap-7.30/docs/DESIGN.template 0000664 0000000 0000000 00000031055 11144346405 0016413 0 ustar 00root root 0000000 0000000 pwrkap -- Energy Use Monitor and Power Cap Enforcement Tools
version %VERSION% (%DATE%)
Written by Darrick J. Wong.
(C) Copyright IBM Corp. 2008-2009
This software is covered under the GNU GPL v2; see COPYING for details.
Overview
--------
This document attempts to describe the structure of the the pwrkap software.
There are two big parts to this program--device and power meter enumeration
and the grouping of those devices into power domains; and the part that builds
a table describing the effects of device power-state changes on the power
consumption of that power domain.
Power Meters
------------
All power meter objects must implement the methods described in the power_meter
class in pwrkap_data.py. For a reference implementation, see syfs_meter.py.
Obviously, power meter driver implementations will vary.
Managed Devices
---------------
All devices that are to be managed by pwrkap must implement the methods of
the device class in pwrkap_data.py. For a reference implementation, see
the file cpu_device.py. Obviously, device driver code will vary.
Discovery
---------
Code module, power meter, device and power domain discovery are all handled
through discovery.py. Discovery for pwrkap is done in a much different
way than it has been done in the past. All code modules to be loaded should
be listed in PWRKAP_DRIVERS; every module listed in that array is imported when
the program starts. Each code module should have a non-indented code snippet
that adds discovery functions to the lists PWRKAP_DEVICE_DISCOVERY,
PWRKAP_METER_DISCOVERY, or PWRKAP_POWER_DOMAIN_DISCOVERY.
When discovery is done, all functions registered in those lists will be called
in succession. First devices are discovered, then power meters, and finally
power domains are created to map power meters to the devices that the meter
measures.
Device discovery has two parts. First, the devices should be enumerated and
put into PWRKAP_DEVICES. Second, device control domain information must be
discovered. This means that all devices with their own set of controls should
be put into a device_domain, one domain per device. For sets of devices that
share the same controls, all the devices should be put into one device_domain.
Finally, all device_domain objects should be put into the PWRKAP_DOMAIN_DEVICES
list.
Power meters are discovered and should be put into PWRKAP_POWER_METERS.
Energy meters are discovered and should be put into PWRKAP_ENERGY_METERS.
Finally, meters and device control domains are united in the power domain
discovery function. Each power domain is created with links to three data
structures--the list of device_domain objects that are bound to the power
meter, something called an identical device profile domain, and the power meter
itself. The fourth argument is the initial power cap for the domain. All
power domains should be put in PWRKAP_POWER_DOMAINS; all devices, device control
domains, and meters claimed by the power domain should be removed from
PWRKAP_DEVICES, PWRKAP_DEVICE_DOMAINS, PWRKAP_ENERGY_METERS, and PWRKAP_POWER_METERS,
respectively.
The identical device profile domain, or idomain, identifies devices that have
identical power use profiles. This enables some extra flexibility in both the
training program as well as the cap enforcement algorithm. Instead of having
to iterate through all power states of all devices in a domain for training,
the program uses the idomain data to test one device in the idomain and apply
its observations to all other devices in the idomain. This drastically reduces
training time as well as shrinking the size of the power use effects table.
Please also note that even a device with a unique profile needs to be placed
in an idomain by itself.
The power domain discovery code are critical to correct operation of pwrkap!
Currently, there are two discovery paths--one for certain IBM systems, and a
simple one that lumps everything it finds into one power domain provided there
is only one power meter. Systems with multiple domains will need to provide
their own power domain discovery logic.
Inventories and Snapshots
-------------------------
Nearly all the data objects involved with pwrkap have two methods that have
not yet been discussed--inventory() and snapshot(). These two functions are
described below. However, a discussion of call flow may be useful.
Generically, the inventory() function returns a description of the static
characteristics of the system--a hardware identifier of the power meter, the
list of supported CPU frequency states, etc. It is assumed that changes in
inventory do not happen within the life of the daemon; thus, a change of this
sort should result in the daemon discarding all training data and starting
anew. Typically this data are used to estabish machine capabilities.
The snapshot() function, then, returns a picture of dynamic system state at
the time of invocation. These items should be fairly volatile and are used to
compute the current state of the machine and where it should go next.
pwrkap's controller object (discussed later) will call the inventory() or
snapshot() methods of power domain objects; it is the duty of these objects to
make the appropriate calls to the power meter and the control domain objects;
the control domain objects will (eventually) call the methods in the device
drivers. After that, the power domain object will compile the subordinate
objects' inventory or snapshot data into a single report and return it.
Relating Power Use to Device Utilization and Power States
---------------------------------------------------------
The heart of pwrkap is a four-dimensional table that enables pwrkap to
guess what kind of impact a change in a device's power state will have on the
power domain's power use. This large-ish array is indexed like this:
transition_table[idomain][dev_use][p_state][p_state] = power_change
The idomain field describes an identical device profile domain as outlined
above. The dev_use index cuts the rest of the table into utilization buckets,
because changes between power states of certain devices (CPUs in particular)
have different effects on power use depending on the device's utilization.
The last two fields are used to index the current power state of the device
and a proposed new power state. The value stored in the table is the average
impact on power use given the four indexing factors.
This table can contain empty cells. By convention, the two p-state indices
are always ordered with the lower of the two coming first, as it is assumed
that transitions are commutative, i.e. a->b => c and b->a => -c.
Training
--------
In the event that pwrkap finds itself lacking data relating power use to
device utilization and power state, a training algorithm is needed to observe
the necessary data to enforce the cap effectively. The code to do this can be
found in transitions.py.
The training algorithm is quite simple:
1. Load down the system so that the "100% Utilization" buckets are filled.
This probably requires outside attention.
2. Set all devices to their lowest power state. This is done partly to avoid
overloading the power mains and partly to work around cpufreq bugs in Linux.
3. In each power domain, identify one device from each idomain.
4. For all possible combinations of device and power state, take a snapshot
of the power domain and process the snapshot. See Processing a Snapshot
below for details.
When the daemon is offline, the power domains and their transition tables are
written to disk via python's cPickle mechanism. When the program loads, the
inventory of the saved data is compared to what is discovered; if there is a
mismatch, the saved data are discarded and the training algorithm is started.
Processing a Snapshot
---------------------
When snapshots are taken, either during training or during normal operation of
the pwrkap daemon, it is useful to augment the transition database with the
data that are being collected, thereby enabling the software to adapt to an
environment that changes over time. Each power domain remembers the past few
snapshots that were taken of that domain. When a new snapshot is taken, it is
compared to every snapshot currently in the retention buffer. If it can be
shown that the only difference between the old and new snapshots is a change
in power state one device in an idomain, the differences in states and in power
use are noted in the transition table.
Enforcing Caps
--------------
Each power domain gets its own thread to run a control loop. The operation of
this control loop is as follows:
1. No more than once every MEASUREMENT_PERIOD seconds, take a snapshot of the
power domain.
2. If less than ENFORCEMENT_INTERVAL seconds have passed since the last
enforcement attempt, go to sleep and restart at step 1.
3. Compute the difference between the domain's power cap and power use.
4. If use is less than cap, find all transitions that increase performance.
If use is more than cap, find all transitions that decrease use.
if they are equal, go back to step 1.
5. Find the transition with the most positive performance increase for the
most negative increase in power use.
6. Implement the power state change specified by the transition and return
to step 3.
Talking to Clients
------------------
Communication with the pwrkap client is achieved through three components--
controller, sockmux and lazy_log. The controller accepts incoming commands
from sockmux and modifies the power domains as necessary. The lazy log receives
new snapshots from the domains as they run through their control loops. While
forwarding new log entries to the sockmux, the lazy log also retains the last
few log entries, which are sent to new clients. Finally, the sockmux dispatches
incoming commands to the controller, sends data from the lazy log to all
clients, and deals with the underlying socket plumbing.
Communications across sockets are done with Python objects in cPickle format.
When a client first connects to a pwrkap server, it is sent a list of
power domain inventories and the log entries retained in the lazy log.
The pwrkap Client
-------------------
The pwrkap client connects to the server and receives a list of power domain
inventories and recent log entries. After that, the client should listen for
incoming power domain snapshots; it may also send commands to the pwrkap
daemon.
The CLI client dumps all incoming data to the console in raw format. Anything
typed into it is cPickle'd and send to the pwrkap server.
The GTK client is a bit more sophisticated. In addition to being able to
watch multiple pwrkap daemons simultaneously, it employs GTK controls to
switch the view between power domains (or aggregates of power domains). It
also interfaces with matplotlib to draw a graph of the past few minutes' of
power cap, power use, and overall domain utilization. Most of the client code
is fairly straightforward and not worth mentioning here, with the exception of
the domain aggregator.
Domain Aggregator
-----------------
The domain aggregator (found in ui_data.py) is used by the GTK client to roll
the power cap, use and domain utilization data of many domains into a single
report. It can also be used to set the power cap of a large number of
machines; in that case, the power cap of each machine is scaled up or down
to maintain the same proportion between domain power cap and overall power cap.
To aggregate data for graphing, the historical data of several power domains
must be blended together. Individual samples are taken in timestamp order from
each domain; the graphing interval is then split into small ranges of time.
For each time range, the power use and cap of each domain are added and the
utilizations are averaged to create the aggregate's profile for that time stamp.
This much smaller set of data is then graphed.
Wire Protocols
--------------
The wire protocol in use between the server and client software is a simple one;
both encode python objects in pickle format and send that across the wire. The
handshake protocol looks approximately like this:
1. Management client connects to pwrkap daemon.
2. pwrkap daemon sends a list of tuples of the format (domain name, inventory).
3. pwrkap daemon replays the last few log entries one by one. Log entries
are of the format (UTC timestamp, (domain name, snapshot))
4. pwrkap daemon sends (UTC timestamp, "live") to indicate that all data after
this point are live. The server's UTC timestamp can be used to synchronize
with the client's clock, since all log entries have UTC timestamps.
5. pwrkap daemon sends live snapshots of the form (UTC timestamp, (domain name,
snapshot))
6. Client software can send commands as an array of strings at any time.
pwrkap-7.30/docs/Makefile 0000664 0000000 0000000 00000001106 11144346405 0015337 0 ustar 00root root 0000000 0000000 # Main build script
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
include ../Rules.mk
BUILD_FILES=README DESIGN
all: $(BUILD_FILES)
clean:;
rm -rf *.pyc *.pyo pwrkap*.bin $(BUILD_FILES)
DESIGN: DESIGN.template
sed -e 's/%VERSION%/$(PWRKAP_VERSION)/g' -e 's/%DATE%/$(PWRKAP_DATE)/g' < $^ > $@
README: README.template
sed -e 's/%VERSION%/$(PWRKAP_VERSION)/g' -e 's/%DATE%/$(PWRKAP_DATE)/g' < $^ > $@
install: all
mkdir -p $(INST_PREFIX)$(PREFIX)/share/pwrkap
$(INSTALL) COPYING DESIGN README -o root -g root -m 644 -t $(INST_PREFIX)$(PREFIX)/share/pwrkap
pwrkap-7.30/docs/README.template 0000664 0000000 0000000 00000012400 11144346405 0016370 0 ustar 00root root 0000000 0000000 pwrkap -- Energy Use Monitor and Power Cap Enforcement Tools
version %VERSION% (%DATE%)
Written by Darrick J. Wong.
(C) Copyright IBM Corp. 2008-2009
This software is covered under the GNU GPL v2; see COPYING for details.
Overview
--------
pwrkap is a set of utilities that attempt to enforce an upper limit on the
amount of power consumed by a set of devices at any given time. The intent
of the program is ensure that *power* use does not go above a given level;
it is NOT to optimize the amount of *energy* consumed for a particular
workload, though it may or may not end up doing that too. There are two
components to this program--a self-training daemon that manages power domains
and a GUI program (that can run from a separate monitoring station) that
plots the power cap and use over time to show the effects of the program.
Requirements
------------
Your computer needs to have at least one power meter and some devices with
power management capabilities. Obviously, the power meter should measure the
devices' power consumption. A few more specifications:
The systems that you want to run the scripts on...
...must have Python 2.4 or newer.
...must have python bindings for GTK+ and matplotlib installed if you want
to use the GUI program.
The power meter...
...must be readable by a user-space app. Currently, pwrkap supports the
IPMI sensor interface via ipmitool and the IBM PowerExecutive sensors
via the ibmpex/ibmaem hwmon drivers. The software can also use discharging
ACPI batteries as a makeshift power meter, though this has not been as well
tested. Support for more devices should not be too difficult to add, but
the author does not have any such devices.
...must not be too terribly slow to read. The software was written under the
assumption that a power meter can be read in under 15 seconds.
Power managed devices...
...must be controllable from user-space. At the moment, pwrkap only knows
how to talk to CPUs that implement CPU frequency control. There are more
devices (network cards, displays, disks, PCI devices, etc) that could be
supported.
...must export utilization data. pwrkap's new power allocation algorithm
takes device utilization into account when figuring out what to do to try
to match the cap.
Specific Requirements
---------------------
- Red Hat Enterprise Linux 5, SUSE Linux Enterprise Server 10, Ubuntu 8.04.
- Linux kernel 2.6 or newer.
- CPU that supports CPU frequency changing via sysfs.
Installation
------------
# tar -xzf pwrkap.tar.gz
# cd pwrkap
# make install
You may also want to start pwrkap whenever the system is booted. The
To run pwrkap when the system is started up, create an init script:
* Debian/Ubuntu:
1. Copy debian/init-script from the source tarball to /etc/init.d/pwrkap
2. Run update-rc.d pwrkap defaults to set up the init script
* Red Hat/SUSE:
1. Copy redhat/init-script from the source tarball to /etc/init.d/pwrkap
2. Run chkconfig pwrkap on to set up the init script.
Usage
-----
Upon starting up for the first time, pwrkap does not know what the power
use profiles of the devices in the system, and will attempt to train itself.
During operation, the pwrkap daemon runs in closed-loop mode, using system
state snapshots to supplement the profile seed data.
#
# pwrkap_main [-d] [-w]
-d: Debug mode, i.e. don't run as a daemon
-w: Start the web server (runs on port 8036)
-f: Simulate a power-managed system
The database will be saved in /etc/pwrkap_$HOSTNAME.bin when the program
exits. By default, the daemon listens for the pwrkap client on port 9410.
To watch and control pwrkap, there are two clients. The first is a command
line client that started its life as a debugging tool. As such, it dumps
power snapshot data directly to standard output whenever the daemon sends data;
its one command is " cap ".
If you have a lot of machines, you may want to use the aggregator to reduce
the load on the monitoring workstation. The aggregator reads a configuration
file to determine how to assemble domains; the format of the config file is:
domain $ip_to_listen_on $port_to_listen $name_of_aggregate_domain consists of:
system $pwrkap_server $pwrkap_server_port $pwrkap_domain_name
Then start the aggregator by running this:
$ pwrkap_aggregate config_file
You can then connect to the aggregator with a client to monitor the aggregated
systems as if they were one system.
$ pwrkap_cli machine_name[:port]
Command format: $domain_name command args
$domain_name is the name of the domain to alter.
command = {dump, cap}
dump: Dump transition tables to daemon console.
cap $new_cap: Set a new power cap.
The preferred method of interacting with pwrkap, however, is the gtk client.
It can connect to a large number of systems and can plot power cap, power use,
and device utilization data.
$ pwrkap_gtk machine_name[:port] [machine_name[:port]...]
Be forewarned that the graph gets a bit slow if there are a lot of machines
connected (>200), so the display of it is disabled by default. If enabled,
the graphs show you the last few minutes' worth of power cap, power use, and
device utilization.
Wrap-Up
-------
I hope you enjoy pwrkap! Details about the design and innards are in the
file DESIGN.
pwrkap-7.30/man/ 0000775 0000000 0000000 00000000000 11144346405 0013524 5 ustar 00root root 0000000 0000000 pwrkap-7.30/man/Makefile 0000664 0000000 0000000 00000001571 11144346405 0015170 0 ustar 00root root 0000000 0000000 # Main build script
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
include ../Rules.mk
DAEMON_MANPAGES=pwrkap_main.8.gz pwrkap_aggregate.8.gz
TOOL_MANPAGES=pwrkap_gtk.1.gz pwrkap_cli.1.gz
CONFIGFILE_MANPAGES=pwrkap_aggregate.5.gz
BUILD_FILES=$(DAEMON_MANPAGES) $(TOOL_MANPAGES) $(CONFIGFILE_MANPAGES)
all: $(BUILD_FILES)
clean:;
rm -rf $(BUILD_FILES)
%.8.gz: %.8
gzip -9 < $^ > $@
%.1.gz: %.1
gzip -9 < $^ > $@
%.5.gz: %.5
gzip -9 < $^ > $@
install: $(BUILD_FILES)
mkdir -p $(INST_PREFIX)$(PREFIX)/share/man/man1 $(INST_PREFIX)$(PREFIX)/share/man/man8 $(INST_PREFIX)$(PREFIX)/share/man/man5
$(INSTALL) $(CONFIGFILE_MANPAGES) -o root -g root -t $(INST_PREFIX)$(PREFIX)/share/man/man5
$(INSTALL) $(DAEMON_MANPAGES) -o root -g root -t $(INST_PREFIX)$(PREFIX)/share/man/man8
$(INSTALL) $(TOOL_MANPAGES) -o root -g root -t $(INST_PREFIX)$(PREFIX)/share/man/man1
pwrkap-7.30/man/pwrkap_aggregate.5 0000664 0000000 0000000 00000002044 11144346405 0017124 0 ustar 00root root 0000000 0000000 .\" Process this file with
.\" groff -man -Tascii foo.1
.\"
.TH pwrkap_aggregate 5 "January 2009" Linux "User Manuals"
.SH NAME
pwrkap_aggregate \- Configuration file for pwrkap_aggregate
.SH DESCRIPTION
Lines in the configuration file should have this format:
.B domain
.I listen_interface
.I listen_port
.I domain_name
.B consists of:
.B system
.I pwrkap_server
.I pwrkap_port
.I pwrkap_server_domain
[more
.B domain
or
.B system
lines...]
.SH OPTIONS
.IP listen_interface
The IP address where the aggregate server should listen for clients.
.IP listen_port
The TCP port where the aggregate server should listen for clients.
.IP domain_name
The power control domain name that the server should present to clients.
.IP pwrkap_server
The IP address of a pwrkap server that should be aggregated.
.IP pwrkap_port
The TCP port of a pwrkap server that should be aggregated.
.IP pwrkap_server_domain
The domain name of a pwrkap server that should be aggregated.
.SH AUTHOR
Darrick J. Wong
.SH "SEE ALSO"
.BR pwrkap_aggregate (8),
pwrkap-7.30/man/pwrkap_aggregate.8 0000664 0000000 0000000 00000002010 11144346405 0017120 0 ustar 00root root 0000000 0000000 .\" Process this file with
.\" groff -man -Tascii foo.1
.\"
.TH pwrkap_aggregate 8 "January 2009" Linux "User Manuals"
.SH NAME
pwrkap_aggregate \- Present a large number of pwrkap servers as if they were one
.SH SYNOPSIS
.B pwrkap_aggregate
.I config_file
.SH DESCRIPTION
.B pwrkap_aggregate
is a daemon that connects to a multitude of pwrkap servers and power control
domains, and presents them as if they were one server with a smaller number of
domains. By aggregating pwrkap server instances, it is possible to monitor
many thousands of servers without overworking the command console. It is also
possible to give identical class designations to a group of pwrkap servers and
manage them as an aggregate.
.SH OPTIONS
.IP config_file
Text file mapping pwrkap servers to aggregated domains. See
.BR pwrkap_aggregate (5)
for information about the config file format.
.SH AUTHOR
Darrick J. Wong
.SH "SEE ALSO"
.BR pwrkap_gtk (1),
.BR pwrkap_cli (1),
.BR pwrkap_aggregate (5),
.BR pwrkap_main (8)
pwrkap-7.30/man/pwrkap_cli.1 0000664 0000000 0000000 00000002303 11144346405 0015737 0 ustar 00root root 0000000 0000000 .\" Process this file with
.\" groff -man -Tascii foo.1
.\"
.TH pwrkap_cli 1 "January 2009" Linux "User Manuals"
.SH NAME
pwrkap_cli \- Command line monitor and control program for pwrkap
.SH SYNOPSIS
.B pwrkap_cli
.I host:port
.SH DESCRIPTION
.B pwrkap_cli
is a command-line program that connects to a pwrkap server and downloads the
hardware configuration and power configuration domain data from the server.
Power use, energy use, and system status are dumped to the screen when the
server sends a status update, and raw commands can be sent to the server.
.SH OPTIONS
.IP "host:port"
A hostname (or IP address) and a port to which this client should connect.
.SH COMMAND FORMAT
The command format looks like this:
.I domain_name
.B [ help | dump | cap
.I new_cap
.B ]
.SH COMMANDS
.IP domain_name
The name of the power configuration domain that the user wants to control.
.IP help
Displays a short help screen.
.IP dump
Displays the current hardware configuration and state of the server.
.IP "cap new_cap"
Sets a new power cap and reconfigures the hardware to meet the cap.
.SH AUTHOR
Darrick J. Wong
.SH "SEE ALSO"
.BR pwrkap_gtk (1),
.BR pwrkap_aggregate (8),
.BR pwrkap_main (8)
pwrkap-7.30/man/pwrkap_gtk.1 0000664 0000000 0000000 00000001642 11144346405 0015762 0 ustar 00root root 0000000 0000000 .\" Process this file with
.\" groff -man -Tascii foo.1
.\"
.TH pwrkap_gtk 1 "January 2009" Linux "User Manuals"
.SH NAME
pwrkap_gtk \- Graphical monitor and control program for pwrkap
.SH SYNOPSIS
.B pwrkap_gtk
.I host:port
[
.I host:port
]
.SH DESCRIPTION
.B pwrkap_gtk
is a GTK graphical program that connects to any number of pwrkap servers,
and downloads the hardware configuration and power configuration domain
data from each machine. It displays the list of power domains and provides
the ability to drill down into each domain to see the current power and
energy use of each domain, the historical power use of the domain, and
provides a facility to set the domain's power cap.
.SH OPTIONS
.IP "host:port"
A hostname (or IP address) and a port to which this client should connect.
.SH AUTHOR
Darrick J. Wong
.SH "SEE ALSO"
.BR pwrkap_cli (1),
.BR pwrkap_aggregate (8),
.BR pwrkap_main (8)
pwrkap-7.30/man/pwrkap_main.8 0000664 0000000 0000000 00000001664 11144346405 0016134 0 ustar 00root root 0000000 0000000 .\" Process this file with
.\" groff -man -Tascii foo.1
.\"
.TH pwrkap_main 8 "January 2009" Linux "User Manuals"
.SH NAME
pwrkap_main \- Main pwrkap system daemon
.SH SYNOPSIS
.B pwrkap_main [-d] [-w] [-f] [-t]
.I listen_port
[
.I log_age
]
.SH DESCRIPTION
.B pwrkap_main
starts the main pwrkap control daemon. This piece of software observes
power use for a given hardware configuration and system load, and tries
to enforce a power cap by raising or lowering each device's performance
capabilities.
.SH OPTIONS
.IP -d
Run in the foreground, for debugging.
.IP -w
Start web service on port 8036.
.IP -f
Simulate a system full of power-managed system.
.IP -t
Do not reuse saved data.
.IP listen_port
Listen for pwrkap clients on this TCP port.
.IP log_age
Keep old status data around for
.I log_age
seconds.
.SH AUTHOR
Darrick J. Wong
.SH "SEE ALSO"
.BR pwrkap_gtk (1),
.BR pwrkap_cli (1),
.BR pwrkap_aggregate (8)
pwrkap-7.30/old-debian-files/ 0000775 0000000 0000000 00000000000 11144346405 0016047 5 ustar 00root root 0000000 0000000 pwrkap-7.30/old-debian-files/init-script 0000775 0000000 0000000 00000001770 11144346405 0020247 0 ustar 00root root 0000000 0000000 #!/bin/bash
# Start and stop pwrkap service
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
### BEGIN INIT INFO
# Provides: pwrkap
# Required-Start:
# Required-Stop:
# Default-Start: S
# Default-Stop:
# Short-Description: start pwrkap
### END INIT INFO
PATH="/sbin:/bin:/usr/sbin:/usr/bin"
[ -x /usr/bin/pwrkap_main ] || exit 0
. /lib/lsb/init-functions
case "$1" in
start)
/usr/bin/pwrkap_main
;;
stop)
ps ax | grep -v grep | grep pwrkap_main.py | while read pid junk; do kill $pid; done
;;
restart|force-reload)
$0 stop
$0 start
;;
status)
PWRKAPS=`ps ax | grep -v grep | grep pwrkap_main.py -c`
if [ $PWRKAPS -lt 1 ]; then
echo "pwrkap is not running."
elif [ $PWRKAPS -gt 1 ]; then
echo "Multiple copies of pwrkap are running. Turn some of them off."
else
echo "pwrkap is running."
fi
;;
*)
echo "Usage: $0 {start|stop|restart|force-reload|status}"
exit 1
;;
esac
exit 0
pwrkap-7.30/old-debian-files/postinst 0000775 0000000 0000000 00000000204 11144346405 0017654 0 ustar 00root root 0000000 0000000 #!/bin/bash
# Enable pwrkap on bootup
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
update-rc.d pwrkap defaults
pwrkap-7.30/old-debian-files/prerm 0000775 0000000 0000000 00000000205 11144346405 0017117 0 ustar 00root root 0000000 0000000 #!/bin/bash
# Disable pwrkap on boot.
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
update-rc.d -f pwrkap remove
pwrkap-7.30/pwrkap/ 0000775 0000000 0000000 00000000000 11144346405 0014255 5 ustar 00root root 0000000 0000000 pwrkap-7.30/pwrkap/Makefile 0000664 0000000 0000000 00000000634 11144346405 0015720 0 ustar 00root root 0000000 0000000 # Main build script
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
include ../Rules.mk
BUILD_FILES=ui_controller.py
all: $(BUILD_FILES)
clean:;
rm -rf $(BUILD_FILES)
ui_controller.py: ui_controller.py.template
sed -e 's|%PREFIX%|$(PREFIX)|g' < $^ > $@
install: all
mkdir -p $(INST_PREFIX)$(PREFIX)/lib/pwrkap
$(INSTALL) *.py -o root -g root -m 644 -t $(INST_PREFIX)$(PREFIX)/lib/pwrkap
pwrkap-7.30/pwrkap/acpi_meter.py 0000664 0000000 0000000 00000003716 11144346405 0016746 0 ustar 00root root 0000000 0000000 #!/usr/bin/python
"""Read power meters available via ACPI."""
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
import discovery
import pwrkap_data
import datetime
import os
import dircache
import util
class acpi_power_meter(pwrkap_data.power_meter):
"""Driver for battery power meters available via ACPI."""
def __init__(self, sensor_filename):
self.sensor_filename = sensor_filename
self.latency = None
def read(self):
def is_discharging(acpi_data):
"""Return true if the battery is discharging."""
discharge_data = ["charging", "state:", "discharging"]
for line in acpi_data:
if line == discharge_data:
return True
return False
def extract_rate(acpi_data):
"""Return discharge rate."""
for line in acpi_data:
if line[0] == "present" and line[1] == "rate:":
return int(line[2]) / 1000.0
return None
try:
before = datetime.datetime.utcnow()
x = util.read_file_as_array(self.sensor_filename)
if x == None:
return None
if not is_discharging(x):
return None
return extract_rate(x)
finally:
after = datetime.datetime.utcnow()
if self.latency == None:
self.latency = (after - before)
else:
self.latency = (8 * self.latency + 2 * (after - before)) / 10
def get_latency(self):
return self.latency
def inventory(self):
return ("acpimeter", {"name": self.sensor_filename})
SYSFS_ACPI_DIR = "/proc/acpi/battery/"
def acpi_meter_discover():
"""Discover ACPI meters."""
global SYSFS_ACPI_DIR
os.system("modprobe -q battery")
# Now look for /proc/acpi/battery/BAT*/state
for hwmon_dev in dircache.listdir(SYSFS_ACPI_DIR):
if not hwmon_dev.startswith("BAT"):
continue
hwmon_dev_dir = SYSFS_ACPI_DIR + hwmon_dev + "/state"
meter = acpi_power_meter(hwmon_dev_dir)
discovery.PWRKAP_POWER_METERS.append(meter)
def acpi_meter_init():
"""Set up acpi meter discovery functions."""
discovery.PWRKAP_METER_DISCOVERY.append(acpi_meter_discover)
return True
acpi_meter_init()
pwrkap-7.30/pwrkap/controller.py 0000664 0000000 0000000 00000010137 11144346405 0017014 0 ustar 00root root 0000000 0000000 #!/usr/bin/python
"""Main controller for pwrkap."""
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
import discovery
import lazy_log
import sockmux
import thread
import atexit
import pwrkap_pickle
import sys
import traceback
import time
import cPickle as pickle
import datetime
import pwrkap_train
import http_listener
def save_and_exit():
"""Tear down control threads and save data."""
print "Saving power use observations..."
for dm in discovery.PWRKAP_POWER_DOMAINS:
dm.exit_control_loop()
pwrkap_pickle.save_domains(discovery.PWRKAP_POWER_DOMAINS)
print "...done."
class controller:
"""Main control loop."""
def __init__(self, port, log_age, log_size, start_httpd = False):
"""Create controller."""
discovery.load_pwrkap_drivers()
discovery.discover_devices()
discovery.discover_meters()
discovery.discover_power_domains()
if start_httpd:
self.http_listener = http_listener.http_listener(self)
self.sockmux = sockmux.sockmux(self, port)
lazy_log.logger = lazy_log.lazy_log(self.sockmux, log_age, log_size)
self.pwrkap_events = {
"dump": self.dump_command,
"cap": self.cap_command}
def train(self):
"""Initialize the system with some data."""
# Speed things up if we're using fakedomain
if "fake_cpudomain" in discovery.PWRKAP_DRIVERS:
pwrkap_train.STABILIZE_TIME = 0
pwrkap_train.MAX_MEASUREMENTS = 10
print "Determining power use profile."
pwrkap_train.train()
def prepare(self, load_saved = True):
"""Prepare system for power capping."""
# Read in what we saved last time (if anything)
saved_data = None
if load_saved:
pwrkap_pickle.load_domains(discovery.PWRKAP_POWER_DOMAINS)
if saved_data == None:
self.train()
pwrkap_pickle.save_domains(discovery.PWRKAP_POWER_DOMAINS)
else:
discovery.PWRKAP_POWER_DOMAINS = saved_data
atexit.register(save_and_exit)
def run(self):
"""Run control methods."""
if len(discovery.PWRKAP_POWER_DOMAINS) < 1:
print "No power domains found."
return
# Try to limit stack consumption.
try:
thread.stack_size(65536)
except:
pass
# Start control methods
for dm in discovery.PWRKAP_POWER_DOMAINS:
thread.start_new_thread(dm.control_loop, ())
while True:
time.sleep(1)
def run_cli(self):
# Start command line interface
data = sys.stdin.readline()
while len(data) > 0:
components = data.strip().split()
self.command(components)
data = sys.stdin.readline()
def connect(self, socket, queue):
"""Connect a new client."""
try:
self.do_connect(socket, queue)
except Exception, e:
print e
traceback.print_exc(file=sys.stdout)
return False
return True
def do_connect(self, socket, queue):
"""Really connect a socket."""
# Dump the list of power domains.
inventories = []
for dm in discovery.PWRKAP_POWER_DOMAINS:
inventories.append(dm.inventory())
pickled = pickle.dumps(inventories, protocol = pickle.HIGHEST_PROTOCOL)
queue.append(pickled)
# Dump some cached snapshots
pickles = lazy_log.logger.dump_log()
for apickle in pickles:
queue.append(apickle)
# Display a "data now live" flag
now = datetime.datetime.utcnow()
tuple = (now, "live")
pickled = pickle.dumps(tuple, protocol = pickle.HIGHEST_PROTOCOL)
queue.append(pickled)
def command(self, components):
"""Process commands."""
try:
domain_name = components[0]
command = components[1]
domain = discovery.find_domain_by_name(domain_name)
handler = self.pwrkap_events[command]
handler(domain, components[2:])
except Exception, e:
print e
traceback.print_exc()
print "Ignoring bogus input '%s'." % components
def dump_command(self, domain, args):
"""Handle a dump command."""
print domain.trans_store.trans_table
def cap_command(self, domain, args):
"""Handle a cap command."""
cap = int(args[0])
if cap < 1:
return
domain.set_cap(cap)
def dump(self):
"""Dump current state."""
print ("devices", discovery.PWRKAP_DEVICES)
print ("pmeters", discovery.PWRKAP_POWER_METERS)
print ("emeters", discovery.PWRKAP_ENERGY_METERS)
print ("devdomains", discovery.PWRKAP_DEVICE_DOMAINS)
print ("pwrdomains", discovery.PWRKAP_POWER_DOMAINS)
pwrkap-7.30/pwrkap/cpu_device.py 0000664 0000000 0000000 00000025275 11144346405 0016750 0 ustar 00root root 0000000 0000000 #!/usr/bin/python
"""Device to represent CPU control."""
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
import discovery
import os
import subprocess
import pwrkap_data
import util
import datetime
import dircache
SYSFS_CPU_DIR = "/sys/devices/system/cpu/"
SYSFS_CPUFREQ_DIR = "/cpufreq/"
PROC_CPUINFO = "/proc/cpuinfo"
SYSFS_CPU_PACKAGE_ID_FILE = "/topology/physical_package_id"
SYSFS_CPU_CPUFREQ_DIR = "cpufreq"
SYSFS_CPU_AFFINITY_FILE = "/" + SYSFS_CPU_CPUFREQ_DIR + "/affected_cpus"
SYSFS_CPU_RELATED_FILE = "/" + SYSFS_CPU_CPUFREQ_DIR + "/related_cpus"
SYSFS_CPU_HOTPLUG_FILE = "/online"
SYSFS_CPU_GOVERNOR_FILE = "/" + SYSFS_CPU_CPUFREQ_DIR + "/scaling_governor"
DEFAULT_CPU_GOVERNOR = "ondemand"
class cpu_device(pwrkap_data.device):
"""Driver for a real CPU, as controlled by /proc and /sys."""
def __init__(self, id, stat_reader):
"""Create a CPU device."""
global SYSFS_CPU_DIR, SYSFS_CPUFREQ_DIR
self.id = id
self.cpufreq_dir = SYSFS_CPU_DIR + "cpu" + str(id) + SYSFS_CPUFREQ_DIR
self.stat_reader = stat_reader
self.last_util = None
self.last_util_time = None
self.load_process = None
def get_power_states(self):
"""Return the power states supported by this CPU."""
res = []
states = util.read_line_as_array(self.cpufreq_dir + "scaling_available_frequencies")
if states == None:
return []
states.sort(cmp = util.cmp_string_as_number)
max_state = int(states[len(states) - 1])
for state in states:
res.append((int(state), float(state) / max_state))
return res
def get_max_power_state(self):
"""Return the maximum power state."""
data = util.read_line_as_array(self.cpufreq_dir + "scaling_max_freq")
return int(data[0])
def set_max_power_state(self, max_pstate):
"""Set the maximum power state."""
pstates = self.get_power_states()
for (pstate, junk) in pstates:
if pstate == max_pstate:
util.write_file(self.cpufreq_dir + "scaling_max_freq", str(max_pstate))
return True
return False
def get_current_power_state(self):
"""Return the current power state."""
data = util.read_line_as_array(self.cpufreq_dir + "scaling_cur_freq")
return int(data[0])
def get_power_state_performance_potential(self, state):
"""Return the performance potential of a given state."""
for (freq, potential) in self.get_power_states():
if freq == state:
return potential
return None
def get_utilization_details(self):
"""Return details of the device's utilization."""
now = datetime.datetime.utcnow()
if self.last_util_time == None or \
self.last_util == None or \
(now - self.last_util_time) > pwrkap_data.ONE_SECOND:
(u, i) = self.stat_reader.read(self.id)
if u + i == 0:
i = 1
pot = self.get_power_state_performance_potential(self.get_current_power_state())
if pot == None:
pot = 1.0
self.last_util = (float(u) / (i + u)) * pot
self.last_util_time = now
return {self.get_name(): self.last_util}
def inventory(self):
key = self.get_name()
obj = {"states": self.get_power_states()}
return (key, obj)
def get_prefix(self):
return "cpu"
def get_name(self):
return self.get_prefix() + str(self.id)
def start_load(self):
def try_run_process(args):
"""Try to run a process."""
try:
self.load_process = subprocess.Popen(args)
except:
return False
return True
if self.load_process != None:
return True
if try_run_process("burnP6"):
return True
if try_run_process(["dd", "if=/dev/zero", "of=/dev/null", "bs=1"]):
return True
return False
def stop_load(self):
if self.load_process == None:
return
os.kill(self.load_process.pid, 9)
self.load_process.wait()
self.load_process = None
PROC_CPUINFO = "/proc/cpuinfo"
class fixed_cpu_device(cpu_device):
"""Driver for a fixed-frequency CPU, as controlled by /proc."""
def __init__(self, id, stat_reader):
"""Create a CPU device."""
def extract_cpu_speed(data):
"""Figure out the CPU's speed."""
# Skip down to the processor entry
for line in data:
if len(line) > 2 and line[0] == "procesor" and int(line[2]) == id:
break;
for line in data:
if len(line) > 3 and line[0] == "cpu" and line[1] == "MHz":
return int(float(line[3]) * 1000)
return None
global PROC_CPUINFO
cpu_device.__init__(self, id, stat_reader)
self.speed = extract_cpu_speed(util.read_file_as_array(PROC_CPUINFO))
def get_power_states(self):
"""Return the power states supported by this CPU."""
return [(self.speed, 1.0)]
def get_max_power_state(self):
"""Return the maximum power state."""
return self.speed
def set_max_power_state(self, max_pstate):
"""Set the maximum power state."""
if max_pstate == self.speed:
return True
return False
def get_prefix(self):
return "fixedcpu"
def get_current_power_state(self):
"""Return the current power state."""
return self.speed
PROC_STAT_UPDATE_INTERVAL = 500
class proc_stat_reader:
"""Common reader to cache accesses to /proc/stat."""
def __init__(self):
self.old_lines = None
self.lines = None
self.next_update = None
self.update()
self.update()
def update(self):
"""Update data."""
global PROC_STAT_UPDATE_INTERVAL
self.old_lines = self.lines
self.lines = util.read_file_as_array("/proc/stat")
x = datetime.datetime.utcnow()
self.next_update = x + datetime.timedelta(milliseconds = PROC_STAT_UPDATE_INTERVAL)
def read(self, cpu):
"""Read usage data for a given CPU."""
def find_cpu_use(lines, cpukey):
"""Find the array corresponding to a CPU."""
for line in lines:
if line[0] == cpukey:
return line
return None
now = datetime.datetime.utcnow()
if now > self.next_update:
self.update()
cpukey = "cpu" + str(cpu)
old = find_cpu_use(self.old_lines, cpukey)
new = find_cpu_use(self.lines, cpukey)
assert len(old) == len(new)
# Column 4 is idle time; the rest indicate use.
use = 0
idle = 0
for i in range(1, len(old)):
if i == 4:
idle = int(new[i]) - int(old[i])
else:
use = use + int(new[i]) - int(old[i])
return (use, idle)
def cpus_reset():
"""Bring all CPUs online and set them to default cpufreq control."""
global SYSFS_CPU_DIR, SYSFS_CPU_HOTPLUG_FILE, SYSFS_CPU_GOVERNOR_FILE, DEFAULT_CPU_GOVERNOR
for cpu in dircache.listdir(SYSFS_CPU_DIR):
if not cpu.startswith("cpu"):
continue
try:
util.write_file(SYSFS_CPU_DIR + cpu + SYSFS_CPU_HOTPLUG_FILE, "1")
util.write_file(SYSFS_CPU_DIR + cpu + SYSFS_CPU_GOVERNOR_FILE, DEFAULT_CPU_GOVERNOR)
except:
pass
def get_proc_cpu_info(cpuid, attributes):
"""Search /proc/cpuinfo for data about a particular CPU."""
global PROC_CPUINFO
proc_cpuinfo = util.read_file_as_array(PROC_CPUINFO, ":")
# Find start of this cpu's section
cpu_start = None
for i in range(0, len(proc_cpuinfo)):
if len(proc_cpuinfo[i]) < 2:
continue
if proc_cpuinfo[i][0].strip() == "processor" and \
int(proc_cpuinfo[i][1]) == cpuid:
cpu_start = i
break
if cpu_start == None:
return {}
# Now loop through attributes
res = {}
for i in range(cpu_start + 1, len(proc_cpuinfo)):
key = proc_cpuinfo[i][0].strip()
if key == "processor":
break
if key in attributes:
res[key] = proc_cpuinfo[i][1].strip()
return res
seen_cpu_affinity_bug = False
def get_cpu_affinities(cpuid):
"""Determine CPU affinity data."""
global SYSFS_CPU_DIR, SYSFS_CPU_AFFINITY_FILE, SYSFS_CPU_PACKAGE_ID_FILE
global seen_cpu_affinity_bug, SYSFS_CPU_RELATED_FILE
set_all = False
# Grab data from cpufreq sysfs attributes
affinity = util.read_line_as_array(SYSFS_CPU_DIR + "cpu" + \
str(cpuid) + \
SYSFS_CPU_RELATED_FILE)
if affinity == None:
affinity = util.read_line_as_array(SYSFS_CPU_DIR + "cpu" + \
str(cpuid) + \
SYSFS_CPU_AFFINITY_FILE)
# Errata report: If we have a Core2 (family 6, model 15+) system with
# no coordination reported via sysfs, we have to calculate our own
# affinity data because the kernel misreports hw coordinated cores as
# if they were totally independent (there are only half as many power
# planes as cores). For now we'll simply assume that all cores on a
# package must run at the same speed--for quad-cores this isn't true;
# they're dual dual-cores and if we could figure out which cores map
# to which planes we could enable more fine-toothed control.
cpuinfo = get_proc_cpu_info(cpuid, ["cpu family", "model"])
if len(affinity) == 1 and int(cpuinfo["cpu family"]) == 6 and \
int(cpuinfo["model"]) >= 15:
if not seen_cpu_affinity_bug:
print "Kernel coordination bug found, applying workaround."
seen_cpu_affinity_bug = True
set_all = True
this_cpu_package = util.read_line_as_array(SYSFS_CPU_DIR + "cpu" + \
str(cpuid) + \
SYSFS_CPU_PACKAGE_ID_FILE)
affinity = [cpuid]
for dir in dircache.listdir(SYSFS_CPU_DIR):
if not dir.startswith("cpu"):
continue
try:
other_cpu_id = int(dir[3:])
except:
continue
if other_cpu_id == cpuid:
continue
cpu_package = util.read_line_as_array(SYSFS_CPU_DIR + dir + SYSFS_CPU_PACKAGE_ID_FILE)
if cpu_package == this_cpu_package:
affinity.append(other_cpu_id)
# Sort data so that the lowest number CPU comes first
affinity.sort(cmp = util.cmp_string_as_number)
return (set_all, affinity)
def cpu_discover():
"""Discover system CPUs."""
global SYSFS_CPU_DIR, SYSFS_CPU_AFFINITY_FILE, SYSFS_CPU_CPUFREQ_DIR
psr = proc_stat_reader()
cpu_map = {}
cpus = []
domains = []
fixed_cpus = []
# Bring all CPUs online
cpus_reset()
# Find CPUs
for cpu in dircache.listdir(SYSFS_CPU_DIR):
if not cpu.startswith("cpu"):
continue
# Check for the presence of "cpufreq" file
cpufreq_found = False
try:
cpu_id = int(cpu[3:])
except:
continue
for handle in dircache.listdir(SYSFS_CPU_DIR + cpu + "/"):
if handle.startswith(SYSFS_CPU_CPUFREQ_DIR):
cpufreq_found = True
break
if not cpufreq_found:
cpu_dev = fixed_cpu_device(cpu_id, psr)
fixed_cpus.append(cpu_dev)
discovery.PWRKAP_DEVICES.append(cpu_dev)
continue
# Proceed with cpu device construction
cpu_dev = cpu_device(cpu_id, psr)
cpu_map[cpu_id] = cpu_dev
discovery.PWRKAP_DEVICES.append(cpu_dev)
cpus.append(cpu_dev)
# Map CPU affinities into domains
for cpu_dev in cpus:
(set_all, affinity) = get_cpu_affinities(cpu_dev.id)
if int(affinity[0]) != cpu_dev.id:
continue
domain_list = []
for cpu_id in affinity:
domain_list.append(cpu_map[int(cpu_id)])
domain = pwrkap_data.device_domain(domain_list)
domain.must_set_all = set_all
discovery.PWRKAP_DEVICE_DOMAINS.append(domain)
# Put all the fixed CPUs into a separate domain
if len(fixed_cpus) > 0:
domain = pwrkap_data.device_domain(fixed_cpus)
discovery.PWRKAP_DEVICE_DOMAINS.append(domain)
def cpu_init():
"""Set up CPU device discovery function."""
discovery.PWRKAP_DEVICE_DISCOVERY.append(cpu_discover)
return True
cpu_init()
pwrkap-7.30/pwrkap/daemon.py 0000664 0000000 0000000 00000002454 11144346405 0016077 0 ustar 00root root 0000000 0000000 #!/usr/bin/python
"""Helper function to make daemons out of normal processes."""
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
import sys
import os
import time
OUTPUT_FILE = "/dev/null"
INPUT_FILE = "/dev/null"
def daemonize():
"""Fork and detach this process."""
global OUTPUT_FILE, INPUT_FILE
#print "parent pid %d\n" % os.getpid()
# fork
try:
pid = os.fork()
if pid > 0:
#print "parent %d exiting" % os.getpid()
sys.exit(0)
except OSError, e:
print >>sys.stderr, "Fork failed %d (%s)" % (e.errno, e.strerror)
sys.exit(1)
#print "child1 pid %d" % os.getpid()
# detach
os.setsid()
os.umask(0)
# do second fork?
try:
pid = os.fork()
if pid > 0:
#print "child1 exiting %d" % os.getpid()
sys.exit(0)
except OSError, e:
print >>sys.stderr, "Fork2 failed %d" % e.errno
print "Backgrounding as process %d" % os.getpid()
fp1 = file(INPUT_FILE, "rb")
if fp1 == None:
print "Can't open %s?" % OUTPUT_FILE
sys.exit(1)
fp2 = file(OUTPUT_FILE, "wb+", 1)
if fp2 == None:
print "Can't open %s?" % OUTPUT_FILE
sys.exit(1)
fp3 = file(OUTPUT_FILE, "wb+", 0)
if fp3 == None:
print "Can't open %s?" % OUTPUT_FILE
sys.exit(1)
os.dup2(fp1.fileno(), sys.stdin.fileno())
os.dup2(fp2.fileno(), sys.stdout.fileno())
os.dup2(fp3.fileno(), sys.stderr.fileno())
pwrkap-7.30/pwrkap/default_domain.py 0000664 0000000 0000000 00000003112 11144346405 0017577 0 ustar 00root root 0000000 0000000 #!/usr/bin/python
"""Create power domains for one-meter systems."""
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
import popen2
import discovery
import pwrkap_data
import sys
# Note: This code does not autodetect systems with multiple power domains!
def default_system_discover():
"""Configure power domain on single-meter systems."""
if len(discovery.PWRKAP_POWER_METERS) > 1 or \
len(discovery.PWRKAP_ENERGY_METERS) > 1 or \
(len(discovery.PWRKAP_POWER_METERS) == 0 and \
len(discovery.PWRKAP_ENERGY_METERS) == 0):
return
# Take the sensor
pmeter = discovery.PWRKAP_POWER_METERS[0]
emeter = discovery.PWRKAP_POWER_METERS[0]
# No devices?
if len(discovery.PWRKAP_DEVICES) == 0:
return
# Assume all CPUs have identical power curves
idomain = []
for device in discovery.PWRKAP_DEVICES:
(name, data) = device.inventory()
if not name.startswith("cpu"):
continue
idomain.append(device)
if len(idomain) < 1:
print "No power-manageable devices found."
sys.exit(1)
# Take all device domains for this power domain
domains = discovery.PWRKAP_DEVICE_DOMAINS
# Remove all devices, domains, and meters that we intend to use.
discovery.PWRKAP_DEVICES = []
discovery.PWRKAP_DEVICE_DOMAINS = []
discovery.PWRKAP_POWER_METERS = []
discovery.PWRKAP_ENERGY_METERS = []
# Create power domain
pd = pwrkap_data.power_domain(domains, [idomain], pmeter, emeter, 1000)
discovery.PWRKAP_POWER_DOMAINS.append(pd)
def default_init():
"""Set up default system discovery functions."""
discovery.PWRKAP_POWER_DOMAIN_DISCOVERY.append(default_system_discover)
pwrkap-7.30/pwrkap/discovery.py 0000664 0000000 0000000 00000003502 11144346405 0016636 0 ustar 00root root 0000000 0000000 #!/usr/bin/python
"""Routines to manage pwrkap device/meter discovery."""
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
import default_domain
import traceback
import sys
# List of drivers and discovery functions
PWRKAP_DRIVERS = ["acpi_meter", "sysfs_meter", "cpu_device", "ibm_domain", "ipmi_meter"]
PWRKAP_DEVICE_DISCOVERY = []
PWRKAP_METER_DISCOVERY = []
PWRKAP_POWER_DOMAIN_DISCOVERY = []
# List of devices
PWRKAP_DEVICES = []
PWRKAP_POWER_METERS = []
PWRKAP_ENERGY_METERS = []
PWRKAP_DEVICE_DOMAINS = []
PWRKAP_POWER_DOMAINS = []
def load_pwrkap_drivers():
"""Load all known pwrkap drivers."""
global PWRKAP_DRIVERS
for driver in PWRKAP_DRIVERS:
__import__(driver)
def discover_devices():
"""Discover power-managed devices."""
global PWRKAP_DEVICE_DISCOVERY
for func in PWRKAP_DEVICE_DISCOVERY:
func()
def discover_meters():
"""Discover power meters."""
global PWRKAP_METER_DISCOVERY
for func in PWRKAP_METER_DISCOVERY:
try:
func()
except Exception, e:
traceback.print_exc()
pass
def discover_power_domains():
"""Discover power domains."""
global PWRKAP_POWER_DOMAIN_DISCOVERY, PWRKAP_POWER_DOMAINS
for func in PWRKAP_POWER_DOMAIN_DISCOVERY:
func()
default_domain.default_system_discover()
if len(PWRKAP_POWER_DOMAINS) < 1:
print "No power domains found."
sys.exit(1)
# Now remove domains that aren't reporting power use
dead_domains = []
for pdom in PWRKAP_POWER_DOMAINS:
if pdom.get_energy_use() == None or \
pdom.get_power_use() == None:
dead_domains.append(pdom)
for pdom in dead_domains:
PWRKAP_POWER_DOMAINS.remove(pdom)
if len(PWRKAP_POWER_DOMAINS) < 1:
print "No working power domains found."
sys.exit(1)
def find_domain_by_name(name):
"""Find a domain by name."""
for dom in PWRKAP_POWER_DOMAINS:
if dom.name() == name:
return dom
return None
pwrkap-7.30/pwrkap/fake_cpudomain.py 0000664 0000000 0000000 00000015702 11144346405 0017601 0 ustar 00root root 0000000 0000000 #!/usr/bin/python
"""Fake pwrkap driver that simulates CPUs and a power meter for them."""
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
import pwrkap_data
import discovery
import random
import datetime
import thread
import threading
import time
import traceback
cpu_domains = None
cpus = None
idomains = None
pm = None
em = None
class fake_energy_meter(pwrkap_data.energy_meter):
def __init__(self, pm_devices):
self.pm_devices = pm_devices
self.power_use = None
self.energy_use = None
self.last_update = None
self.keep_running = True
for pmdev in self.pm_devices:
assert pmdev.get_prefix() == "fakecpu"
# These must run last
self.update_energy_use()
thread.start_new_thread(self.run, ())
def __del__(self):
self.keep_running = False
def __setstate__(self, data):
self.__dict__ = data
self.energy_use = 0
thread.start_new_thread(self.run, ())
def run(self):
"""Periodically update energy use."""
self.keep_running = True
while self.keep_running:
try:
self.update_energy_use()
except Exception, e:
print e
traceback.print_exc()
time.sleep(1)
def update_energy_use(self):
"""Update the energy use counter."""
now = datetime.datetime.utcnow()
if self.last_update == None:
self.energy_use = 0
self.power_use = 0
self.last_update = now
return
td = now - self.last_update
time_delta = td.microseconds + \
(td.seconds * 1000000) + \
(td.days * 86400000000)
if time_delta == 0:
time_delta = 1
time_delta = float(time_delta) / 1000000
# Baseline this system uses about 150W.
delta_e = 0 #time_delta * (150 + random.randint(-5, 20))
# CPU energy costs are partially based on the current frequency
# and partly on the number of clocks used. Scales are designed
# such that a 3GHz CPU consumes 120W at full use.
for cpu in self.pm_devices:
nr_clocks = (0.4 + 0.6 * cpu.get_utilization()) * cpu.get_current_power_state()
nr_clocks = nr_clocks * 0.00004
delta_e = delta_e + time_delta * nr_clocks
self.energy_use = self.energy_use + delta_e
self.power_use = (0.666 * self.power_use) + (0.333 * (delta_e / time_delta))
self.last_update = now
def read(self):
return self.energy_use
def get_latency(self):
return 0;
def inventory(self):
return ("fakemeter", {})
class fake_power_meter(pwrkap_data.power_meter):
def __init__(self, energy_meter):
self.energy_meter = energy_meter
def read(self):
return self.energy_meter.power_use
def get_latency(self):
return 0;
def inventory(self):
return ("fakemeter", {})
def build_pstate_table(min, max, step):
"""Build a fake power state table."""
states = []
for mhz in range(min, max + 1, step):
states.append((mhz, (1.0 * mhz) / max))
return states
MIN_FAKE_CPU_SPEED = 2000000
MAX_FAKE_CPU_SPEED = 4000000
FAKE_CPU_SPEED_STEP = 1000000
class fake_cpu(pwrkap_data.device):
def __init__(self, id):
global MAX_FAKE_CPU_SPEED
self.max_pstate = MAX_FAKE_CPU_SPEED
self.id = id
self.cps_reads = 1
self.last_cps = MAX_FAKE_CPU_SPEED
self.master_cpu = None
self.last_util_read_time = datetime.datetime.utcnow()
self.last_util_read = 0.5
self.pstates = build_pstate_table(
MIN_FAKE_CPU_SPEED,
MAX_FAKE_CPU_SPEED,
FAKE_CPU_SPEED_STEP
)
random.seed()
def get_power_states(self):
if self.master_cpu != None:
return self.master_cpu.get_power_states()
return self.pstates
def get_max_power_state(self):
if self.master_cpu != None:
return self.master_cpu.get_max_power_state()
return self.max_pstate
def set_max_power_state(self, max_pstate):
if self.master_cpu != None:
return self.master_cpu.set_max_power_state(max_pstate)
valid_state = False
for (state, potential) in self.pstates:
if max_pstate == state:
valid_state = True
assert valid_state
self.max_pstate = max_pstate
if self.last_cps > self.max_pstate:
self.last_cps = self.max_pstate
def find_index_of_pstate(self, pstate):
for i in range(0, len(self.pstates)):
if self.pstates[i][0] == pstate:
return i
return None
def get_current_power_state(self):
if self.master_cpu != None:
return self.master_cpu.last_cps
self.cps_reads = self.cps_reads + 1
if self.cps_reads > 8 or self.last_cps > self.max_pstate:
self.cps_reads = 0
max_pstate = self.find_index_of_pstate(self.get_max_power_state())
pstate = random.randint(0, max_pstate)
(speed, junk) = self.pstates[pstate]
self.last_cps = speed
assert self.last_cps <= self.max_pstate
return self.last_cps
def get_utilization(self):
now = datetime.datetime.utcnow()
if (now - self.last_util_read_time) < pwrkap_data.ONE_SECOND:
return self.last_util
self.last_util = (90.0 + random.uniform(-80, 10)) / 100
self.last_util_read_time = now
return self.last_util
def get_utilization_details(self):
return {self.get_name(): self.get_utilization()}
def get_id(self):
return self.id
def get_name(self):
return self.get_prefix() + str(self.id)
def inventory(self):
key = self.get_prefix() + str(self.id)
obj = {"states": self.get_power_states()}
return (key, obj)
def get_prefix(self):
return "fakecpu"
def start_load(self):
return False
def stop_load(self):
# XXX Implement me
pass
def fake_cpu_discover_old():
"""Discover fake system CPUs."""
global cpu_domains, cpus, idomains
cpu_domains = []
cpus = []
idomains = [[], [], []]
# Simulate 4 dual-core CPUs
for cpuid in range(0, 4):
core0 = fake_cpu(2 * cpuid)
core1 = fake_cpu(2 * cpuid + 1)
core1.master_cpu = core0
domain = pwrkap_data.device_domain([core0, core1])
cpu_domains.append(domain)
cpus.append(core0)
cpus.append(core1)
idomains[cpuid / 2].append(core0)
idomains[cpuid / 2].append(core1)
# And a spurious 9th core for fun
core0 = fake_cpu(8)
domain = pwrkap_data.device_domain([core0])
cpu_domains.append(domain)
cpus.append(core0)
idomains[2].append(core0)
def fake_cpu_discover():
"""Discover fake system CPUs."""
global cpu_domains, cpus, idomains
cpu_domains = []
cpus = []
idomains = [[]]
# Simulate a single quad-core clovertown
core0 = fake_cpu(0)
core1 = fake_cpu(1)
core2 = fake_cpu(2)
core3 = fake_cpu(3)
core1.master_cpu = core0
core3.master_cpu = core2
domain0 = pwrkap_data.device_domain([core0, core1])
domain1 = pwrkap_data.device_domain([core2, core3])
cpu_domains.append(domain0)
cpu_domains.append(domain1)
cpus = [core0, core1, core2, core3]
idomains = [[core0, core1, core2, core3]]
def fake_meter_discover():
"""Discover fake power meters."""
global cpus, pm, em
em = fake_energy_meter(cpus)
pm = fake_power_meter(em)
def fake_system_discover():
"""Discover fake power domains."""
global cpu_domains, idomains, pm, em
for i in range(0, 1):
fake_cpu_discover()
fake_meter_discover()
dm = pwrkap_data.power_domain(cpu_domains, idomains, pm, em, 1000)
dm.snapshot()
discovery.PWRKAP_POWER_DOMAINS.append(dm)
def fake_init():
"""Set up discovery functions."""
discovery.PWRKAP_POWER_DOMAIN_DISCOVERY.append(fake_system_discover)
return True
fake_init()
pwrkap-7.30/pwrkap/http_listener.py 0000664 0000000 0000000 00000010477 11144346405 0017524 0 ustar 00root root 0000000 0000000 #!/usr/bin/python
"""Listen on a TCP/IP port for incoming HTTP requests."""
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
import BaseHTTPServer
import thread
import discovery
from httplib import OK, NOT_FOUND
from urlparse import urlparse
from cgi import parse_qs
from urllib import unquote
HOST_NAME = '0.0.0.0'
PORT_NUMBER = 8036
XML_ROOT = 'ReturnValues'
PWRKAP_CMD = 'setpowercap'
PWRKAP_PARAM_OLD= 'pcap'
PWRKAP_PARAM = 'pwrkap'
PWRDOM_PARAM = 'pwrdom'
# The controller variable should be set before starting a new HTTP listener.
# The controller contains the method that actually sets a pwrkap.
pwrkap_controller = None
class http_server(BaseHTTPServer.HTTPServer):
"""Extends the base HTTP server with the ability to invoke termination."""
def serve_terminable(self):
self.terminating = False
while not self.terminating:
self.handle_request()
def server_close(self):
BaseHTTPServer.HTTPServer.server_close(self)
self.terminating = True
class http_handler(BaseHTTPServer.BaseHTTPRequestHandler):
"""Defines methods to handle HTTP requests."""
def do_HEAD(self):
"""Respond to an HTTP HEAD request."""
parsedURL = urlparse(self.path)
if (unquote(parsedURL.path) != "/" + PWRKAP_CMD):
self.send_error(NOT_FOUND, "The specified path or command does not exist.")
return
self.send_response(OK)
self.send_header("Content-type", "text/html")
self.end_headers()
def do_GET(self):
"""Respond to an HTTP GET request."""
parsedURL = urlparse(self.path)
if (unquote(parsedURL.path) != "/" + PWRKAP_CMD):
self.send_error(NOT_FOUND, "The specified path or command does not exist.")
return
self.send_response(OK)
self.send_header("Content-type", "text/xml")
self.end_headers()
print "Received request from", self.client_address
params = parse_qs(parsedURL.query)
pwrkap = -1
powerDomainName = ""
# Get the specified pwrkap value if it can be obtained from the parameters.
# Use old parameter if specified, to avoid breaking older clients
if (params.has_key(PWRKAP_PARAM_OLD) and len(params[PWRKAP_PARAM_OLD]) > 0):
try:
pwrkap = int(params[PWRKAP_PARAM_OLD][0])
except ValueError:
pwrkap = -1
# Get the specified pwrkap value if it can be obtained from the parameters.
if (params.has_key(PWRKAP_PARAM) and len(params[PWRKAP_PARAM]) > 0):
try:
pwrkap = int(params[PWRKAP_PARAM][0])
except ValueError:
pwrkap = -1
if (pwrkap >= 0):
# Get the specified power domain name if it can be obtained from
# the parameters. Otherwise, attempt to obtain the first power
# domain name that can be found in the discovery module.
if (params.has_key(PWRDOM_PARAM) and len(params[PWRDOM_PARAM]) > 0):
powerDomainName = params[PWRDOM_PARAM][0]
elif (len(discovery.PWRKAP_POWER_DOMAINS) > 0):
print "Found at least one entry in PWRKAP_POWER_DOMAINS"
powerDomainName = discovery.PWRKAP_POWER_DOMAINS[0].name()
print "Power domain name:", powerDomainName
if (discovery.find_domain_by_name(powerDomainName) != None):
print "Attempting to set pwrkap to", pwrkap, "on power domain", \
powerDomainName, "..."
if (pwrkap_controller != None):
pwrkap_controller.command([powerDomainName, "cap", pwrkap])
else:
print "The pwrkap Controller was not defined. An HTTP", \
"server should only be started via the", \
"http_listener class."
pwrkap = -1
else:
print "The specified power domain name could not be", \
"identified. Power cap will not be set."
pwrkap = -1
self.wfile.write("")
self.wfile.write("<" + XML_ROOT + ">")
self.wfile.write("<" + PWRKAP_CMD + ">")
self.wfile.write("<" + PWRKAP_PARAM + ">" + str(pwrkap) + "" + PWRKAP_PARAM + ">")
self.wfile.write("<" + PWRDOM_PARAM + ">" + powerDomainName + "" + PWRDOM_PARAM + ">")
self.wfile.write("" + PWRKAP_CMD + ">")
self.wfile.write("" + XML_ROOT + ">\n")
class http_listener:
""" Defines the HTTP listener object that sets the global pwrkap controller
to handle incoming capping requests and starts the HTTP server. """
def __init__(self, controller):
global pwrkap_controller
pwrkap_controller = controller
self.httpd = http_server((HOST_NAME, PORT_NUMBER), http_handler)
thread.start_new_thread(self.httpd.serve_terminable, ())
def stop(self):
self.httpd.server_close()
pwrkap-7.30/pwrkap/ibm_domain.py 0000664 0000000 0000000 00000021002 11144346405 0016720 0 ustar 00root root 0000000 0000000 #!/usr/bin/python
"""Create power domains for IBM System X machines with PowerExecutive."""
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
import popen2
import time
import discovery
import pwrkap_data
import util
import dircache
SYSFS_NODE_DIR = "/sys/devices/system/node/"
def cmp_meter(x, y):
"""Compare two meters by sysfs name."""
xi = x.inventory()
yi = y.inventory()
(xt, xd) = xi
(yt, yd) = yi
assert xt == "sysfsmeter" and yt == "sysfsmeter"
xn = xd["name"][22:]
yn = yd["name"][22:]
xs = xn.find("/")
ys = yn.find("/")
assert xs >= 0 and ys >= 0
return util.cmp_string_as_number(xn[:xs], yn[:ys])
def find_device_in_list(list, devname):
for listdev in list:
x = listdev.inventory()
if devname == x[0]:
return listdev
return None
def find_domain_with_dev(domains, dev):
for dom in domains:
if dev in dom.devices:
return dom
return None
def ibm_aem_discover():
"""Configure power domains on AEM systems."""
global SYSFS_NODE_DIR
def find_aem_energy_meters():
"""Find AEM energy meters."""
meters = []
# energy2 meter is usually more accurate, but if it's
# zero then it's not connected
for meter in discovery.PWRKAP_ENERGY_METERS:
(name, data) = meter.inventory()
if not name.startswith("sysfsmeter"):
continue
if not data["chip"] == "aem2":
continue
if data["name"].endswith("energy2_input"):
if meter.read() != 0:
meters.append(meter)
if len(meters) > 0:
return meters
# Try for energy1 if there aren't any energy2s.
for meter in discovery.PWRKAP_ENERGY_METERS:
(name, data) = meter.inventory()
if not name.startswith("sysfsmeter"):
continue
if not data["chip"] == "aem2":
continue
if data["name"].endswith("energy1_input"):
meters.append(meter)
return meters
def find_aem_power_meters():
"""Find AEM power meters."""
meters = []
# power2 meter is usually more accurate, but if it's
# zero then it's not connected
for meter in discovery.PWRKAP_POWER_METERS:
(name, data) = meter.inventory()
if not name.startswith("sysfsmeter"):
continue
if not data["chip"] == "aem2":
continue
if data["name"].endswith("power2_average"):
if meter.read() != 0:
meters.append(meter)
if len(meters) > 0:
return meters
# Try for power1 if there aren't any power1s.
for meter in discovery.PWRKAP_POWER_METERS:
(name, data) = meter.inventory()
if not name.startswith("sysfsmeter"):
continue
if not data["chip"] == "aem2":
continue
if data["name"].endswith("power1_average"):
meters.append(meter)
return meters
# Go look for meters
emeters = find_aem_energy_meters()
pmeters = find_aem_power_meters()
if len(emeters) == 0 or len(pmeters) == 0:
return
emeters.sort(cmp = cmp_meter)
pmeters.sort(cmp = cmp_meter)
# Some AEM systems are actually NUMA systems. For this to work,
# we must grab node info from /sys/devices/system/node/nodeX/.
# Each node tells us which CPUs are in that node; to find the
# power/energy meter we'll simply sort them by /sys/class/hwmon/hwmonX
# order and assign them to the nodes.
# (This system should work for the non-NUMA systems too.)
i = 0
for node in dircache.listdir(SYSFS_NODE_DIR):
if not node.startswith("node"):
continue
cpus = []
emeter = emeters[i]
pmeter = pmeters[i]
dev_domains = []
devs = []
for cpu in dircache.listdir(SYSFS_NODE_DIR + node):
if not cpu.startswith("cpu"):
continue
cpu_device = find_device_in_list(discovery.PWRKAP_DEVICES, cpu)
if cpu_device == None:
continue
cpu_domain = find_domain_with_dev(discovery.PWRKAP_DEVICE_DOMAINS, cpu_device)
assert cpu_domain != None
for dev in cpu_domain.devices:
discovery.PWRKAP_DEVICES.remove(dev)
devs.append(dev)
discovery.PWRKAP_DEVICE_DOMAINS.remove(cpu_domain)
dev_domains.append(cpu_domain)
discovery.PWRKAP_POWER_METERS.remove(pmeter)
discovery.PWRKAP_ENERGY_METERS.remove(emeter)
idomains = pwrkap_data.detect_idomains_for_devices(devs)
pd = pwrkap_data.power_domain(dev_domains, idomains, pmeter, emeter, 1000)
discovery.PWRKAP_POWER_DOMAINS.append(pd)
i = i + 1
def ibm_simple_pex_discover():
"""Configure power domain on PEx systems."""
def find_ibmaem_energy_sensor():
"""Return an appropriate ibmaem energy meter."""
p2 = p1 = None
for meter in discovery.PWRKAP_ENERGY_METERS:
(name, data) = meter.inventory()
if not name.startswith("sysfsmeter"):
continue
if not data["chip"] == "aem2":
continue
if data["name"].endswith("energy2_input"):
p2 = meter
elif data["name"].endswith("energy1_input"):
p1 = meter
if p2 != None:
return p2
return p1
def find_ibmaem_power_sensor():
"""Return an appropriate ibmaem power meter."""
p2 = p1 = None
for meter in discovery.PWRKAP_POWER_METERS:
(name, data) = meter.inventory()
if not name.startswith("sysfsmeter"):
continue
if not data["chip"] == "aem2":
continue
if data["name"].endswith("power2_average"):
p2 = meter
elif data["name"].endswith("power1_average"):
p1 = meter
if p2 != None:
return p2
return p1
def find_ibmpex_sensor():
"""Return an appropriate ibmpex power meter."""
for meter in discovery.PWRKAP_POWER_METERS:
(name, data) = meter.inventory()
if not name.startswith("sysfsmeter"):
continue
if not data["chip"] == "ibmpex":
continue
if not data["name"].endswith("power11_average"):
continue
return meter
return None
def find_ipmi_sensor():
"""Return an appropriate ipmi power meter."""
for meter in discovery.PWRKAP_POWER_METERS:
(name, data) = meter.inventory()
if not name.startswith("ipmimeter"):
continue
if data["name"] == "AVG Power":
return meter
elif data["name"] == "AvgPwrIns1":
return meter
return None
# First see if we can find an ibmaem meter
pmeter = find_ibmaem_power_sensor()
emeter = find_ibmaem_energy_sensor()
# Then try to find an ibmpex power meter
if pmeter == None:
pmeter = find_ibmpex_sensor()
# If none found, go for IPMI sensor
if pmeter == None:
pmeter = find_ipmi_sensor()
# No meter?
if pmeter == None:
return
form_domain_from_all_devices(pmeter, emeter)
def ibm_x3_discover():
"""Configure power domain on simple X3 systems."""
def find_ipmi_sensor():
"""Return an appropriate ipmi power meter."""
for meter in discovery.PWRKAP_POWER_METERS:
(name, data) = meter.inventory()
if not name.startswith("ipmimeter"):
continue
if not data["name"] == "RFG 1PS 220":
continue
return meter
return None
# If none found, go for IPMI sensor
meter = find_ipmi_sensor()
# No meter?
if meter == None:
return
form_domain_from_all_devices(meter, None)
def form_domain_from_all_devices(pmeter, emeter):
"""Assume system has one meter for all devices and create domain."""
# No devices?
if len(discovery.PWRKAP_DEVICE_DOMAINS) == 0:
return
# Assume all CPUs have identical power curves
idomains = pwrkap_data.detect_idomains_for_devices(discovery.PWRKAP_DEVICES)
# Take all device domains for this power domain
domains = discovery.PWRKAP_DEVICE_DOMAINS
# Remove all devices, domains, and meters that we intend to use.
discovery.PWRKAP_DEVICES = []
discovery.PWRKAP_DEVICE_DOMAINS = []
discovery.PWRKAP_POWER_METERS.remove(pmeter)
if emeter != None:
discovery.PWRKAP_ENERGY_METERS.remove(emeter)
# Create power domain
pd = pwrkap_data.power_domain(domains, idomains, pmeter, emeter, 1000)
discovery.PWRKAP_POWER_DOMAINS.append(pd)
known_systems = [
(["IBM System x3650",
"IBM System x3655",
"IBM System x3755",
"IBM System x3350",
"BladeCenter LS21",
"IBM eServer BladeCenter HS21",
"IBM System x3550"], ibm_simple_pex_discover),
(["eserver xSeries 366",
"IBM x3850",
"IBM x3800"], ibm_x3_discover),
(["IBM 3850 M2 / x3950 M2"], ibm_aem_discover),
]
def ibm_system_discover():
"""Configure power domains on IBM systems."""
def get_dmi_system_name():
"""Retrieve system name from DMI."""
proc = popen2.Popen4("dmidecode -s system-product-name")
input = proc.fromchild
while proc.poll() == -1:
time.sleep(0.1)
return input.readline().strip()
global known_systems
# First determine if this is one of the known systems.
sys_name = get_dmi_system_name()
if sys_name == None:
return
sys_func = None
for (sys_list, func) in known_systems:
for sys in sys_list:
if sys_name.startswith(sys):
sys_func = func
break
if sys_func != None:
break;
if sys_func == None:
return
return func()
def ibm_init():
"""Set up IBM system discovery functions."""
discovery.PWRKAP_POWER_DOMAIN_DISCOVERY.append(ibm_system_discover)
ibm_init()
pwrkap-7.30/pwrkap/ipmi_meter.py 0000664 0000000 0000000 00000003574 11144346405 0016772 0 ustar 00root root 0000000 0000000 #!/usr/bin/python
"""Read power meters available via IPMI."""
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
import discovery
import pwrkap_data
import datetime
import os
import popen2
class ipmi_power_meter(pwrkap_data.power_meter):
"""Driver for power meters available via IPMI."""
def __init__(self, sensor_name, ipmitool_args = ""):
self.sensor_name = sensor_name
self.latency = None
self.ipmitool_args = ipmitool_args
self.meter_name = "ipmitool"
def read(self):
try:
before = datetime.datetime.utcnow()
proc = popen2.Popen4("ipmitool %s sensor get '%s'" % \
(self.ipmitool_args, self.sensor_name), 512)
res = proc.wait()
if res != 0:
return None
input = proc.fromchild
for line in input:
if line.strip().startswith("Sensor Reading"):
foo = line.split()
if foo[3] == "na":
return None
else:
return float(foo[3])
return None
finally:
after = datetime.datetime.utcnow()
if self.latency == None:
self.latency = (after - before)
else:
self.latency = (8 * self.latency + 2 * (after - before)) / 10
def get_latency(self):
return self.latency
def inventory(self):
return (self.meter_name, {"name": self.sensor_name})
def ipmi_meter_discover():
"""Discover IPMI meters."""
os.system("modprobe -q ipmi-si")
os.system("modprobe -q ipmi-devintf")
proc = popen2.Popen4("ipmitool sensor", 512)
# Don't wait for output; apparently RHEL5 drops all pipe data
# after the process terminates.
#res = proc.wait()
#if res != 0:
# return
input = proc.fromchild
for line in input:
foo = line.split("|")
if len(foo) > 2 and foo[2].strip() == "Watts":
meter = ipmi_power_meter(foo[0].strip())
discovery.PWRKAP_POWER_METERS.append(meter)
def ipmi_init():
"""Set up IPMI discovery functions."""
discovery.PWRKAP_METER_DISCOVERY.append(ipmi_meter_discover)
return True
ipmi_init()
pwrkap-7.30/pwrkap/lazy_log.py 0000664 0000000 0000000 00000002451 11144346405 0016451 0 ustar 00root root 0000000 0000000 #!/usr/bin/python
"""A log that retains events for a certain amount of time."""
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
import datetime
import cPickle as pickle
class lazy_log:
"""A log of pickled objects that retains log data for a given amount of time."""
def __init__(self, writer, max_age, max_size):
"""Create a log object."""
self.writer = writer
self.messages = []
self.max_age = max_age
self.max_size = max_size
def log(self, object):
"""Pickle an object, save it to the log, and write it out."""
time = datetime.datetime.utcnow()
tuple = (time, object)
pickled = pickle.dumps(tuple, protocol = pickle.HIGHEST_PROTOCOL)
self.messages.append((time, pickled))
self.writer.write(pickled)
print tuple
self.scrape_log()
def scrape_log(self):
"""Remove expired messages from the log."""
now = datetime.datetime.utcnow()
for i in range(0, len(self.messages)):
if (now - self.messages[i][0]).seconds <= self.max_age:
del self.messages[0:i]
break
if len(self.messages) > self.max_size:
self.messages = self.messages[len(self.messages) - self.max_size:]
def dump_log(self):
"""Retrieve log contents."""
pickles = []
self.scrape_log()
for (time, pickle) in self.messages:
pickles.append(pickle)
return pickles
logger = None
pwrkap-7.30/pwrkap/power_energy_meter.py 0000664 0000000 0000000 00000006655 11144346405 0020544 0 ustar 00root root 0000000 0000000 #!/usr/bin/python
"""Fake energy meter that calculates energy use by integrating power use."""
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
import pwrkap_data
import thread
import datetime
import time
import traceback
class power_energy_meter(pwrkap_data.energy_meter):
"""Fake energy meter that uses power meter to estimate energy use."""
def __init__(self, pmeter, threaded):
self.power_meter = pmeter
self.energy_use = None
self.last_update = None
self.keep_running = True
self.threaded = threaded
# This must run last
if self.threaded:
thread.start_new_thread(self.run, ())
def __del__(self):
self.keep_running = False
def __setstate__(self, data):
self.__dict__ = data
self.last_update = None
if self.threaded:
thread.start_new_thread(self.run, ())
def run(self):
"""Periodically update energy use."""
self.keep_running = True
while self.keep_running:
try:
self.update_energy_use()
except Exception, e:
print e
traceback.print_exc()
time.sleep(1)
def update_energy_use(self):
"""Update the energy use counter."""
now = datetime.datetime.utcnow()
if self.last_update == None:
self.energy_use = 0
self.last_update = now
return
td = now - self.last_update
time_delta = td.microseconds + \
(td.seconds * 1000000) + \
(td.days * 86400000000)
if time_delta == 0:
time_delta = 1
time_delta = float(time_delta) / 1000000
rate = self.power_meter.read()
self.energy_use = self.energy_use + (rate * time_delta)
self.last_update = now
def read(self):
if not self.threaded:
self.update_energy_use()
time.sleep(2)
self.update_energy_use()
return self.energy_use
def get_latency(self):
return 0;
def inventory(self):
return ("power_energy_meter", {})
class energy_power_meter(pwrkap_data.power_meter):
"""Fake power meter that uses energy meter to estimate energy use."""
def __init__(self, emeter, threaded):
self.energy_meter = emeter
self.last_energy_use = None
self.last_update = None
self.power_use = None
self.keep_running = True
self.threaded = threaded
# This must run last
if self.threaded:
thread.start_new_thread(self.run, ())
def __del__(self):
self.keep_running = False
def __setstate__(self, data):
self.__dict__ = data
self.last_update = None
if self.threaded:
thread.start_new_thread(self.run, ())
def run(self):
"""Periodically update energy use."""
self.keep_running = True
while self.keep_running:
try:
self.update_energy_use()
except Exception, e:
print e
traceback.print_exc()
time.sleep(1)
def update_power_use(self):
"""Update the power use counter."""
now = datetime.datetime.utcnow()
if self.last_update == None:
self.last_energy_use = self.energy_meter.read()
self.power_use = 0
self.last_update = now
return
td = now - self.last_update
time_delta = td.microseconds + \
(td.seconds * 1000000) + \
(td.days * 86400000000)
if time_delta == 0:
time_delta = 1
time_delta = float(time_delta) / 1000000
energy_now = self.energy_meter.read()
self.power_use = float(energy_now - self.last_energy_use) / time_delta
self.last_energy_use = energy_now
self.last_update = now
def read(self):
if not self.threaded:
self.update_power_use()
time.sleep(2)
self.update_power_use()
return self.power_use
def get_latency(self):
return 0;
def inventory(self):
return ("energy_power_meter", {})
pwrkap-7.30/pwrkap/pwrkap_aggregate.py 0000664 0000000 0000000 00000025603 11144346405 0020147 0 ustar 00root root 0000000 0000000 #!/usr/bin/python
"""Aggregate pwrkap servers into one virtual pwrkap server."""
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
import asyncore
import socket
import cPickle as pickle
import StringIO
import lazy_log
import traceback
import datetime
import thread
import time
import sys
import pwrkap_data
AGGREGATE_REFRESH = 15
def read_objs_from_string(str):
"""Unpickle commands from a string."""
sio = StringIO.StringIO(str)
objs = []
pos = 0
while True:
try:
obj = pickle.load(sio)
pos = sio.tell()
objs.append(obj)
except EOFError:
break
return (pos, objs)
class listen_dispatcher(asyncore.dispatcher):
"""Dispatch incoming connections."""
def __init__(self, socket, controller):
"""Set up server socket."""
asyncore.dispatcher.__init__(self, socket)
self.controller = controller
self.set_reuse_addr()
self.listen(5)
def handle_accept(self):
"""Dispatch incoming connection attempt."""
conn, addr = self.accept()
client_dispatcher(conn, self.controller)
class base_dispatcher(asyncore.dispatcher):
"""Writable dispatcher."""
def __init__(self, socket = None, map = None):
"""Create a writable dispatcher."""
asyncore.dispatcher.__init__(self, socket, map)
self.out_buf = ""
self.in_buf = ""
def handle_write(self):
"""Push data to client."""
sent = self.send(self.out_buf[0])
self.out_buf = self.out_buf[sent:]
def writable(self):
"""Determine if there are data to write to the client."""
return (len(self.out_buf) > 0)
def write(self, buffer):
"""Send some data to be written."""
self.out_buf = self.out_buf + buffer
def read_objs_from_socket(self):
"""Read objects from socket."""
try:
buf = self.recv(4096)
except:
self.handle_close()
return []
self.in_buf = self.in_buf + buf
while len(buf) > 0:
try:
buf = self.recv(4096)
self.in_buf = self.in_buf + buf
except:
break
pos, objs = read_objs_from_string(self.in_buf)
self.in_buf = self.in_buf[pos:]
return objs
class client_dispatcher(base_dispatcher):
"""Talk to a pwrkap client."""
def __init__(self, socket, controller):
"""Create a client dispatcher."""
base_dispatcher.__init__(self, socket)
self.controller = controller
self.controller.add_client(self)
def handle_read(self):
"""Read and dispatch commands."""
for obj in self.read_objs_from_socket():
self.controller.command(obj)
def handle_close(self):
"""Remove ourself."""
self.controller.remove_client(self)
asyncore.dispatcher.close(self)
self.connected = False
class server_dispatcher(base_dispatcher):
"""Talk to a pwrkap server."""
def __init__(self, controller, sock_func, connect_opts):
"""Create a server dispatcher."""
base_dispatcher.__init__(self)
self.controller = controller
self.connect_opts = connect_opts
self.sock_func = sock_func
self.usock = None
self.ignore_next = True
def handle_read(self):
"""Read and dispatch status."""
for obj in self.read_objs_from_socket():
if self.ignore_next:
self.ignore_next = False
continue
try:
self.controller.status(self, obj)
except:
traceback.print_exc()
def handle_close(self):
"""Handle connection closing."""
self.controller.remove_server(self)
self.close()
self.usock = None
self.connected = False
self.peer = None
def try_connect(self):
"""Try to connect."""
assert not self.connected
if self.usock == None:
self.usock = self.sock_func()
self.usock.setblocking(0)
self.set_socket(self.usock)
try:
print "Connecting to %s:%d..." % self.connect_opts
x = self.socket.connect_ex(self.connect_opts)
except:
return False
return True
def handle_connect(self):
"""Handle a successful connection."""
self.ignore_next = True
try:
self.peer = self.socket.getpeername()
except:
self.peer = None
pass
self.controller.add_server(self)
class aggregate_controller:
"""Aggregate a bunch of pwrkap servers to pwrkap clients."""
def __init__(self, name, server_socket_info, domains):
"""Create a pwrkap aggregator with given name and a list of (host, port, domain) tuples."""
assert len(domains) > 0
self.clients = []
self.servers = []
self.name = name
self.logger = lazy_log.lazy_log(self, 3600, 2)
self.command_table = {
"cap": self.cap_command}
self.ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.ssock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.ssock.bind(server_socket_info)
self.listen_dispatcher = listen_dispatcher(self.ssock, self)
# Contains domain info
self.domains = {}
for hpd in domains:
self.domains[hpd] = {"cap": None, "energy": None,
"power": None, "utilization": None}
self.dispatchers = {}
self.hostportdispatcher = {}
hostport_seen = []
self.inactive_dispatchers = []
self.active_dispatchers = []
for host, port, dom in domains:
if (host, port) in hostport_seen:
continue
dispatcher = server_dispatcher(self,
lambda: socket.socket(socket.AF_INET, socket.SOCK_STREAM),
(host, port))
self.dispatchers[dispatcher] = (host, port)
self.hostportdispatcher[(host, port)] = dispatcher
self.inactive_dispatchers.append(dispatcher)
hostport_seen.append((host, port))
print ("dispatchers ready" , self.dispatchers)
self.try_to_activate_dispatchers()
def try_to_activate_dispatchers(self):
"""Try to connect inactive dispatchers."""
connected = []
for d in self.inactive_dispatchers:
if not d.try_connect():
continue
connected.append(d)
for c in connected:
self.inactive_dispatchers.remove(c)
self.active_dispatchers.extend(connected)
def add_client(self, client_dispatcher):
"""Add a client."""
self.clients.append(client_dispatcher)
# Write fake inventory
data = [(self.name, {"domains": [], "meter": {"aggregate": {}}})]
datastr = pickle.dumps(data, pickle.HIGHEST_PROTOCOL)
client_dispatcher.write(datastr)
# Write old snapshots
pickles = self.logger.dump_log()
for apickle in pickles:
client_dispatcher.write(apickle)
# Write live stamp
data = (datetime.datetime.utcnow(), "live")
datastr = pickle.dumps(data, pickle.HIGHEST_PROTOCOL)
client_dispatcher.write(datastr)
def remove_client(self, client_dispatcher):
"""Remove a client."""
self.clients.remove(client_dispatcher)
def add_server(self, server_dispatcher):
"""Add a server."""
self.servers.append(server_dispatcher)
def remove_server(self, server_dispatcher):
"""Remove a server."""
self.servers.remove(server_dispatcher)
self.active_dispatchers.remove(server_dispatcher)
self.inactive_dispatchers.append(server_dispatcher)
def write(self, buffer):
"""Write data to all clients."""
for client in self.clients:
client.write(buffer)
def cap_command(self, command):
"""Handle the cap command."""
new_cap = float(command[2])
total_cap = 0.0
for key in self.domains.keys():
dom = self.domains[key]
if dom["cap"] != None:
total_cap = total_cap + dom["cap"]
for key in self.domains.keys():
(host, port, domname) = key
dom = self.domains[key]
command = [domname, "cap", (dom["cap"] / total_cap) * new_cap]
cmdstr = pickle.dumps(command, pickle.HIGHEST_PROTOCOL)
dispatcher = self.hostportdispatcher[(host, port)]
dispatcher.write(cmdstr)
def command(self, command):
"""Handle commands."""
if command[0] != self.name:
return
self.command_table[command[1]](command)
def status(self, dispatcher, status):
"""Handle status reports."""
(timestamp, some_data) = status
if some_data == "live":
return
(domname, domstatus) = some_data
peer = dispatcher.peer
if peer == None:
peer = self.dispatchers[dispatcher]
(host, port) = peer
# Are we watching this domain?
domkey = (host, port, domname)
if not self.domains.has_key(domkey):
return
# Collect status data
dom = self.domains[domkey]
dom["cap"] = domstatus["cap"]
dom["power"] = domstatus["power"]
if domstatus.has_key("energy"):
energy = domstatus["energy"]
else:
energy = None
dom["energy"] = energy
dom["utilization"] = domstatus["utilization"]
if domstatus.has_key("util_details"):
ud = {}
for detail in domstatus["util_details"].keys():
ud["%s:%d:%s:%s" % (host, port, domname, detail)] = domstatus["util_details"][detail]
else:
ud = {domname: dom["utilization"]}
dom["util_details"] = ud
def run(self):
"""Start this controller."""
thread.start_new_thread(self.do_periodic_updates, ())
def do_periodic_updates(self):
"""Periodically aggregate data and send to clients."""
while True:
print "Refresh"
self.try_to_activate_dispatchers()
self.update_clients()
time.sleep(AGGREGATE_REFRESH)
def update_clients(self):
"""Send status update to clients."""
total_cap = total_energy = total_power = total_utilization = 0.0
util_details = {}
doms_found = len(self.domains)
for key in self.domains.keys():
dom = self.domains[key]
try:
total_cap = total_cap + dom["cap"]
total_power = total_power + dom["power"]
if "energy" in dom.keys():
total_energy = total_energy + dom["energy"]
total_utilization = total_utilization + dom["utilization"]
util_details.update(dom["util_details"])
except:
doms_found = doms_found - 1
if doms_found == 0:
return
avg_utilization = pwrkap_data.average_utilization(util_details)
old_avg_util = total_utilization / doms_found
data = (self.name, {"domains": [],
"cap": total_cap,
"power": total_power,
"energy": total_energy,
"utilization": avg_utilization,
"old_avg_util": old_avg_util,
"util_details": util_details})
self.logger.log(data)
#ac = aggregate_controller("agg0", ('0.0.0.0', 9410), [
# ('9.47.66.63', 9410, 'pwrdom0'),
# ('9.47.66.254', 9410, 'pwrdom0')
#])
#ac.run()
def read_config_file(file):
"""Read config file and set up aggregates.
config file format:
domain $listen_addr $port $aggregate_name consists of:
system $hostname $port $domain
"""
listen_addr = None
port = None
domain = None
systems = []
controllers = []
for line in file:
components = line.split()
if len(components) < 1 or components[0][0] == "#":
continue
if port == None and components[0] != "domain":
continue
if components[0] == "domain":
if port != None:
print ("C", domain, listen_addr, port)
ac = aggregate_controller(domain, (listen_addr, port), systems)
controllers.append(ac)
port = None
systems = []
listen_addr = components[1]
port = int(components[2])
domain = components[3]
elif components[0] == "system":
system_hostname = components[1]
system_port = int(components[2])
system_domain = components[3]
systems.append((system_hostname, system_port, system_domain))
if port != None:
print ("D", domain, listen_addr, port)
ac = aggregate_controller(domain, (listen_addr, port), systems)
controllers.append(ac)
return controllers
fname = "./pwrkap_aggregate.conf"
if len(sys.argv) > 1:
fname = sys.argv[1]
ctrls = read_config_file(file(fname))
for ac in ctrls:
ac.run()
asyncore.loop()
pwrkap-7.30/pwrkap/pwrkap_cli.py 0000664 0000000 0000000 00000002570 11144346405 0016766 0 ustar 00root root 0000000 0000000 #!/usr/bin/python
"""Command-line client for pwrkap."""
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
import socket
import sys
import cPickle as pickle
import traceback
import thread
host = "localhost"
port = 9410
if len(sys.argv) > 1:
host = sys.argv[1]
if len(sys.argv) > 2:
port = int(sys.argv[2])
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host, port))
fp = sock.makefile()
def dump_input(file):
"""Read objects from a socket and dump them."""
try:
obj = pickle.load(file)
while True:
print ("recv'd", obj)
obj = pickle.load(file)
except Exception, e:
print e
traceback.print_exc()
sys.exit(1)
def print_help():
"""Print command help."""
print "Command format: domain_name command args"
print "domain_name is the name of the domain to alter."
print "command = {dump, cap}"
print " dump: Dump transition tables to daemon console."
print " cap new_cap: Set a new power cap."
def send_console(file):
"""Read console and send to server."""
data = sys.stdin.readline()
while data != "":
commands = data.split()
if commands[0] == "help":
print_help()
data = sys.stdin.readline()
continue
pickled = pickle.dumps(commands, protocol = pickle.HIGHEST_PROTOCOL)
file.write(pickled)
file.flush()
data = sys.stdin.readline()
thread.start_new_thread(dump_input, (fp,))
send_console(fp)
pwrkap-7.30/pwrkap/pwrkap_data.py 0000664 0000000 0000000 00000036071 11144346405 0017133 0 ustar 00root root 0000000 0000000 #!/usr/bin/python
"""Common data types for pwrkap drivers."""
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
import math
import transitions
import datetime
import threading
import traceback
import lazy_log
ONE_SECOND = datetime.timedelta(0, 0, 1)
def average_utilization(util_map):
"""Calculate the average utilization from a map of device -> utilization."""
assert len(util_map) > 0
sum = 0.0
for key in util_map.keys():
sum = sum + util_map[key]
return float(sum) / len(util_map)
class meter:
"""Abstract base class for meter implementations."""
def read(self):
"""Read the meter."""
pass
def inventory(self):
"""Return an inventory of the meter capabilities."""
pass
def get_latency(self):
"""Return the average latency of the meter in seconds."""
pass
class power_meter(meter):
"""Abstract base class for power meters. read() returns Watts."""
pass
class energy_meter(meter):
"""Abstract base class for energy meters. read() returns Joules."""
pass
import power_energy_meter
class device:
"""Abstract base class for power-managed devices."""
def get_prefix(self):
"""Return the type of this device."""
pass
def get_power_states(self):
"""Return a mapping of all possible power states to the device's ability to perform while in that state (in percent)."""
pass
def get_max_power_state(self):
"""Return the maximum power state."""
pass
def set_max_power_state(self, max_pstate):
"""Set the maximum power state."""
pass
def get_current_power_state(self):
"""Return the current power state."""
pass
def snapshot(self):
"""Return a snapshot of the current state of the device."""
pass
def inventory(self):
"""Return an inventory of the device capabilities."""
pass
def get_utilization_details(self):
"""Return a dictionary containing {subdevice: utilization} pairs. If there are no subdevices, return a single {device: utilization} pair."""
pass
def get_name(self):
"""Return the name of this device."""
pass
def snapshot(self):
"""Take a snapshot of this device."""
key = self.get_name()
obj = { "state": self.get_current_power_state(), \
"max_state": self.get_max_power_state(), \
"util_details": self.get_utilization_details()}
return (key, obj)
def start_load(self):
"""Start a load for training purposes."""
pass
def stop_load(self):
"""Stop the training load."""
pass
class device_domain(device):
"""A collection of devices that must be power-managed together."""
def __init__(self, devices):
"""Create a domain of power-managed devices."""
assert len(devices) > 0
self.devices = devices
self.must_set_all = False
def get_prefix(self):
"""Return the prefix of the domain."""
return "domain"
def get_current_power_state(self):
"""Return the current power state."""
highest_seen = None
for dev in self.devices:
cps = dev.get_current_power_state()
if highest_seen == None or highest_seen < cps:
highest_seen = cps
return highest_seen
def get_power_states(self):
"""Return a list of all possible power states."""
return self.devices[0].get_power_states()
def get_max_power_state(self):
"""Return the maximum power state."""
return self.devices[0].get_max_power_state()
def set_max_power_state(self, max_pstate):
"""Set the maximum power state."""
if not self.must_set_all:
return self.devices[0].set_max_power_state(max_pstate)
res = True
for dev in self.devices:
res = res and dev.set_max_power_state(max_pstate)
return res
def get_utilization_details(self):
"""Merge and return maps of device utilization."""
dom_util_map = {}
for dev in self.devices:
dev_util_map = dev.get_utilization_details()
assert dev_util_map != None
dom_util_map.update(dev_util_map)
return dom_util_map
def snapshot(self):
"""Take a snapshot of this device domain."""
snap_list = {}
for dev in self.devices:
(k, v) = dev.snapshot()
snap_list[k] = v
return snap_list
def inventory(self):
"""Take an inventory of this device domain."""
inv_list = {}
for dev in self.devices:
(k, v) = dev.inventory()
inv_list[k] = v
return inv_list
def get_device(self):
"""Return a device that represents this domain."""
return self.devices[0]
def start_load(self):
loaded = []
for dev in self.devices:
if dev.start_load():
loaded.append(dev)
else:
self.stop_load_for(loaded)
return False
return True
def stop_load_for(self, devices):
for dev in devices:
dev.stop_load()
def stop_load(self):
self.stop_load_for(self.devices)
class IllegalDomain(Exception): pass
class power_domain_volatile_data:
"""Dummy class to isolate power domain data that can't be preserved."""
def __init__(self):
self.signal = threading.Condition()
def __getstate__(self):
pass
def __setstate__(self, state):
self.signal = threading.Condition()
NUM_UTIL_BUCKETS = 4
ENFORCEMENT_INTERVAL = 30
MEASUREMENT_PERIOD = 15
ENFORCEMENT_SNAPSHOTS = 2
DEFAULT_SNAPSHOTS_TO_KEEP = 1000
class power_domain:
"""A collection of power-managed device domains, a power
meter, and various routines to manage them."""
def __init__(self, domains, idomains, power_meter, energy_meter, cap):
"""Create a power domain."""
global NUM_UTIL_BUCKETS, DEFAULT_SNAPSHOTS_TO_KEEP
assert len(domains) > 0
self.power_meter = power_meter
self.energy_meter = energy_meter
if self.energy_meter == None:
self.energy_meter = power_energy_meter.power_energy_meter(self.power_meter)
self.domains = domains
self.id = next_power_domain_id()
self.cap = cap
self.inter_domains = idomains
self.snap_store = transitions.snapshot_store(DEFAULT_SNAPSHOTS_TO_KEEP)
self.trans_store = transitions.transition_store(self.snap_store, self.inter_domains, NUM_UTIL_BUCKETS)
self.check_idomain()
self.control_loop_active = False
self.need_enforcement = False
self.volatile = power_domain_volatile_data()
self.control_loop_should_exit = False
self.last_enforcement = datetime.datetime.utcnow()
def choose_domains_for_training(self):
"""Return the smallest set of domains that are needed to
collect training data."""
training = []
for dom in self.domains:
dev = dom.get_device()
already_covered = False
for idom in self.inter_domains:
if idom[0] == dev:
training.append(dom)
already_covered = True
break
elif dev in idom:
already_covered = True
if not already_covered:
training.append(dom)
return training
def check_idomain(self):
"""Check interchangeable domains for problems."""
# No empty idoms
for idom in self.inter_domains:
assert len(idom) > 0
# All devices must be part of an idom.
devs = set()
for dom in self.domains:
for dev in dom.devices:
devs.add(dev)
for idom in self.inter_domains:
for dev in idom:
assert dev in devs
devs.remove(dev)
assert len(devs) == 0
def get_utilization_details(self):
"""Merge and return maps of domain utilization."""
pdom_util_map = {}
for dom in self.domains:
dom_util_map = dom.get_utilization_details()
assert dom_util_map != None
pdom_util_map.update(dom_util_map)
return pdom_util_map
def get_energy_use(self):
"""Return the power domain's energy use, in Joules."""
return self.energy_meter.read()
def get_power_use(self):
"""Return the power domain's power use, in Watts."""
return self.power_meter.read()
def get_cap(self):
"""Return the power domain's maximum usage."""
return self.cap
def set_cap(self, cap):
"""Set a new cap for this domain."""
self.cap = cap
if self.control_loop_active:
self.need_enforcement = True
# Signal the control loop
self.volatile.signal.acquire()
self.volatile.signal.notify()
self.volatile.signal.release()
else:
self.enforce_cap()
return True
def exit_control_loop(self):
"""Terminate the control loop."""
if not self.control_loop_active:
return
self.control_loop_should_exit = True
self.volatile.signal.acquire()
self.volatile.signal.notify()
self.volatile.signal.release()
def control_loop(self):
try:
self.do_control_loop()
except Exception, ex:
traceback.print_exc()
def do_control_loop(self):
"""Control loop for power use regulation."""
global ENFORCEMENT_INTERVAL, MEASUREMENT_PERIOD, ENFORCEMENT_SNAPSHOTS
snapshots_since_enforcement = 0
self.control_loop_should_exit = False
self.control_loop_active = True
while True:
# Are we being told to exit?
if self.control_loop_should_exit:
self.control_loop_active = False
return
before = datetime.datetime.utcnow()
# Take snapshot
(name, props) = self.process_snapshot()
usage = props["power"]
lazy_log.logger.log((name, props))
snapshots_since_enforcement = snapshots_since_enforcement + 1
# Figure out if we need to run the enforcement loop
nowtime = datetime.datetime.utcnow()
if self.need_enforcement or \
(nowtime - self.last_enforcement).seconds > ENFORCEMENT_INTERVAL or \
snapshots_since_enforcement >= ENFORCEMENT_SNAPSHOTS:
snapshots_since_enforcement = 0
self.need_enforcement = False
self.do_enforce_cap(usage)
self.last_enforcement = datetime.datetime.utcnow()
# Now sleep for a bit?
after = datetime.datetime.utcnow()
if (after - before).seconds < MEASUREMENT_PERIOD:
self.volatile.signal.acquire()
self.volatile.signal.wait(MEASUREMENT_PERIOD - (after - before).seconds)
self.volatile.signal.release()
def snapshot(self):
"""Return a (key, obj) representation of the power domain."""
def snapshot_domains(list):
"""Snapshot a list of domains."""
snap_list = []
for domain in list:
snap_list.append(domain.snapshot())
return snap_list
ud = self.get_utilization_details()
aud = average_utilization(ud)
key = self.name()
obj = { "utilization": aud, \
"power": self.get_power_use(), \
"energy": self.get_energy_use(), \
"domains": snapshot_domains(self.domains), \
"cap": self.get_cap(), \
"util_details": ud}
return (key, obj)
def process_snapshot(self):
"""Capture and record a snapshot."""
snap = self.snapshot()
self.trans_store.consider_snapshot(snap[1])
return snap
def inventory(self):
"""Return a (key, obj) representation of the power domain's \
capabilities."""
def inventory_domains(list):
"""Inventory a list of domains."""
inv_list = []
for domain in list:
inv_list.append(domain.inventory())
return inv_list
(pmeter_name, pmeter_data) = self.power_meter.inventory()
(emeter_name, emeter_data) = self.energy_meter.inventory()
key = self.name()
obj = { "domains": inventory_domains(self.domains), \
"pmeter": {pmeter_name: pmeter_data},
"emeter": {emeter_name: emeter_data}}
return (key, obj)
def name(self):
"""Return the domain's name."""
return "pwrdom" + str(self.id)
def enforce_cap(self):
"""Try to enforce the power cap on a one-off basis."""
return self.do_enforce_cap(self.get_power_use())
def do_enforce_cap(self, power_use):
"""Try to enforce the power cap given a power usage reading."""
recent_transitions = set()
recent_devs = set()
# Make it so that we don't go back to where we started from
for domain in self.domains:
state = domain.get_current_power_state()
recent_transitions.add((domain, state))
# XXX: Fudge things a bit here--give ourselves 10W of headroom
remaining_delta = self.get_cap() - power_use - 10
original_delta = remaining_delta
print "***********"
print "delta=%(delta)d cap=%(cap)d usage=%(use)d" % {"delta": remaining_delta, "cap": self.get_cap(), "use": power_use}
delta = self.change_power_target(remaining_delta, recent_transitions, recent_devs)
while delta != None:
remaining_delta = remaining_delta - delta
if remaining_delta * original_delta < 0:
break
delta = self.change_power_target(remaining_delta, recent_transitions, recent_devs)
print "Remaining delta=%(rem)dW" % {"rem": remaining_delta}
if remaining_delta < 0:
return remaining_delta
return 0
def change_power_target(self, delta, recent, recent_devs):
"""Take one step towards changing the power use target."""
proposals = []
if delta == 0:
return None
# For each device domain, construct a set of transitions
# from the (current state, utilization) to another state
# for which we know the power cost. XXX: If there is no
# data for (c0, u) -> (c1), can we use (c0, u') -> (c1)
# instead?
for domain in self.domains:
dev_proposals = self.trans_store.propose_transitions(domain)
for prop in dev_proposals:
# Don't monkey around with CPUs we've already modified
if prop.device in recent_devs:
continue
# Ignore proposals that don't change power use.
if prop.power_impact == 0:
continue
# If we have to decrease power use, eliminate
# options that consume more energy.
if delta < 0 and prop.power_impact > 0:
continue
# If delta positive, do not pick any option that results
# in a performance decrease. This greedy algorithm only
# cares about now; it does not look ahead.
if delta > 0 and prop.performance_impact < 0:
continue
# Eliminate transitions that exceed the desired delta
# if the delta is positive.
if delta > 0 and prop.power_impact > delta:
continue
# Else, add to proposal list. Note that it is
# quite valid to have options that increase
# peformance and decrease energy use!
proposals.append(prop)
# If there are no transitions left, we're stuck; exit
if len(proposals) == 0:
return None
# Sort transitions in order of goodness.
proposals.sort(cmp = transitions.compare_proposals)
if True:
print "------------------"
for x in proposals:
print (x.device.get_device().inventory()[0], x.new_state, 100*x.performance_impact, \
x.power_impact, x.performance_impact / x.power_impact)
# Pick the best one
proposal = None
for prop in proposals:
if (prop.device, prop.new_state) in recent:
continue
proposal = prop
break
if proposal == None:
return None
print ("I CHOOSE", proposal)
# Implement it.
proposal.device.set_max_power_state(proposal.new_state)
# Remember that fact.
recent.add((proposal.device, proposal.new_state))
recent_devs.add(proposal.device)
return proposal.power_impact
def start_load(self):
loaded = []
for dom in self.domains:
if dom.start_load():
loaded.append(dom)
else:
self.stop_load_for(loaded)
return False
return True
def stop_load_for(self, domains):
for dom in domains:
dom.stop_load()
def stop_load(self):
self.stop_load_for(self.domains)
def detect_idomains_for_devices(devices):
"""Compute identical-domains for a list of devices."""
idomains = []
for device in devices:
(name, data) = device.inventory()
prefix = device.get_prefix()
dev_idomain = []
for idomain in idomains:
(idom_name, idom_data) = idomain[0].inventory()
idom_prefix = idomain[0].get_prefix()
if idom_prefix == prefix and idom_data == data:
dev_idomain = idomain
break
if len(dev_idomain) == 0:
idomains.append(dev_idomain)
dev_idomain.append(device)
return idomains
next_dom_id = 0
def next_power_domain_id():
"""Return a unique power domain identifier."""
global next_dom_id
next_dom_id = next_dom_id + 1
return next_dom_id - 1
pwrkap-7.30/pwrkap/pwrkap_gtk.py 0000664 0000000 0000000 00000001316 11144346405 0017001 0 ustar 00root root 0000000 0000000 #!/usr/bin/python
"""GTK user interface for pwrkap."""
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
import ui_controller
import gtk
import sys
import ui_data
import thread
import gobject
hosts = []
if len(sys.argv) == 1:
host = ui_data.pwrkap_host("localhost", 9410)
hosts.append(host)
else:
for arg in sys.argv[1:]:
stuff = arg.split(":")
hostname = stuff[0]
if len(stuff) == 1:
port = 9410
else:
port = int(stuff[1])
host = ui_data.pwrkap_host(hostname, port)
hosts.append(host)
sys.setcheckinterval(10)
gtk.gdk.threads_init()
uc = ui_controller.ui_controller(hosts)
gtk.gdk.threads_enter()
thread.start_new_thread(uc.connect_hosts, ())
gtk.main()
gtk.gdk.threads_leave()
pwrkap-7.30/pwrkap/pwrkap_main.py 0000664 0000000 0000000 00000003077 11144346405 0017146 0 ustar 00root root 0000000 0000000 #!/usr/bin/python
"""Main pwrkap daemon."""
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
import discovery
import controller
import pwrkap_train
import sys
import daemon
def print_help():
"""Print command line usage."""
print "Usage: %s [-d] [-w] [-f] [-t] [listen_port] [log_age]" % sys.argv[0]
print "-d: Run in debug mode, i.e. don't run as a daemon."
print "-w: Start HTTP web server."
print "-f: Use fake devices."
print "-t: Retrain system."
print "listen_port: Listen for clients on this port."
print "log_age: Keep old snapshots for this many seconds."
def main():
"""Run main program."""
optargs = []
# Interpret arguments
daemonize = True
http = False
load_saved = True
for i in range(1, len(sys.argv)):
arg = sys.argv[i]
if arg == "-d":
daemonize = False
elif arg == "-w":
http = True
elif arg == "-f":
print "Using fake devices."
discovery.PWRKAP_DRIVERS = ["fake_cpudomain"]
elif arg == "-h" or arg == "--help":
print_help()
return
elif arg == "-t":
load_saved = False
else:
optargs.append(sys.argv[i])
if daemonize:
daemon.daemonize()
# Now gather arguments
port = 9410
log_age = 3600
log_size = 2
if len(optargs) > 0:
port = int(optargs[0])
if len(optargs) > 1:
log_age = int(optargs[1])
ctrl = controller.controller(port, log_age, log_size, http)
# Speed things up if we're using fakedomain
if "fake_cpudomain" in discovery.PWRKAP_DRIVERS:
pwrkap_train.STABILIZE_TIME = 0
pwrkap_train.MAX_MEASUREMENTS = 10
ctrl.prepare(load_saved)
ctrl.dump()
ctrl.run()
# We never return
main()
pwrkap-7.30/pwrkap/pwrkap_pickle.py 0000664 0000000 0000000 00000003401 11144346405 0017460 0 ustar 00root root 0000000 0000000 #!/usr/bin/python
"""Helper to save and restore power domains."""
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
import cPickle as pickle
import socket
import os
# Search for power cap data files in these locations:
# ./pwrkap_$HOSTNAME.bin
# /etc/pwrkap_$HOSTNAME.bin
# /etc/pwrkap.bin
def find_data_file_name():
"""Construct the name of the saved data file."""
hostname = socket.gethostname()
files = []
files.append("./pwrkap_%s.bin" % hostname)
files.append("/etc/pwrkap_%s.bin" % hostname)
files.append("/etc/pwrkap.bin")
for file in files:
if os.path.exists(file):
return file
def default_data_file_name():
"""Default location for saved data."""
hostname = socket.gethostname()
files = []
files.append("/etc/pwrkap_%s.bin" % hostname)
files.append("./pwrkap_%s.bin" % hostname)
for file in files:
try:
fp = open(file, "wb")
fp.close()
return file
except:
continue
return None
def save_domains(domains):
"""Save domain data to a file."""
file = open(default_data_file_name(), "wb")
pickle.dump(domains, file, pickle.HIGHEST_PROTOCOL)
file.close()
def load_domains(domains):
"""Load domain data from a file and return it if the inventory of
the restored domains match the inventory of the given domains."""
inventories = []
fname = find_data_file_name()
if fname == None:
return None
file = open(fname, "rb")
try:
loaded = pickle.load(file)
except:
file.close()
return None
file.close()
for pd in loaded:
inventories.append(pd.inventory())
for pd in domains:
inventory = pd.inventory()
if inventory not in inventories:
print "Configuration change, discarding old data."
return None
inventories.remove(pd.inventory())
if len(inventories) > 0:
print "err2"
return None
return loaded
pwrkap-7.30/pwrkap/pwrkap_test.py 0000664 0000000 0000000 00000006360 11144346405 0017177 0 ustar 00root root 0000000 0000000 #!/usr/bin/python
"""pwrkap test routines."""
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
import discovery
import time
import pwrkap_pickle
import sys
import pwrkap_train
def discover_world():
"""Discovery and assemble power domains."""
print "Probing hardware..."
discovery.load_pwrkap_drivers()
discovery.discover_devices()
discovery.discover_meters()
discovery.discover_power_domains()
discover_world()
print ("devices", discovery.PWRKAP_DEVICES)
print ("pmeters", discovery.PWRKAP_POWER_METERS)
print ("emeters", discovery.PWRKAP_ENERGY_METERS)
print ("devdomains", discovery.PWRKAP_DEVICE_DOMAINS)
print ("pwrdomains", discovery.PWRKAP_POWER_DOMAINS)
# Speed things up if we're using fakedomain
def train():
if "fake_cpudomain" in discovery.PWRKAP_DRIVERS:
pwrkap_train.STABILIZE_TIME = 0
pwrkap_train.MAX_MEASUREMENTS = 10
pwrkap_train.train()
def quick_simulate():
if len(discovery.PWRKAP_POWER_DOMAINS) == 0:
return
for pd in discovery.PWRKAP_POWER_DOMAINS:
print pd.inventory()
while True:
for pd in discovery.PWRKAP_POWER_DOMAINS:
print pd.snapshot()
time.sleep(1)
def pickle_test():
for i in range(0, 500):
for dm in discovery.PWRKAP_POWER_DOMAINS:
dm.process_snapshot()
count = 0
for dm in discovery.PWRKAP_POWER_DOMAINS:
count = count + 1
fname = "/tmp/pdom%d" % count
print "Saving to '%s'..." % fname
fp = open(fname, "w")
dm.save_snapshots(fp)
fp.close()
def pickle2():
for i in range(0, 500):
for dm in discovery.PWRKAP_POWER_DOMAINS:
dm.process_snapshot()
pwrkap_pickle.save_domains(discovery.PWRKAP_POWER_DOMAINS)
fubar = pwrkap_pickle.load_domains(discovery.PWRKAP_POWER_DOMAINS)
if fubar == None:
print "Screw up?!"
return False
print "INVENTORY"
for dm in discovery.PWRKAP_POWER_DOMAINS:
print dm.inventory()
print "INVENTORY PICKLED"
for dm in fubar:
print dm.inventory()
print "SNAPSHOT"
for dm in fubar:
print dm.snapshot()
def pickle3():
pwrkap_pickle.save_domains(discovery.PWRKAP_POWER_DOMAINS)
fubar = pwrkap_pickle.load_domains(discovery.PWRKAP_POWER_DOMAINS)
if fubar == None:
print "Screw up?!"
return False
print "INVENTORY"
for dm in discovery.PWRKAP_POWER_DOMAINS:
print dm.inventory()
print "INVENTORY PICKLED"
for dm in fubar:
print dm.inventory()
print "SNAPSHOT"
for dm in fubar:
print dm.snapshot()
def pickle4():
fubar = pwrkap_pickle.load_domains(discovery.PWRKAP_POWER_DOMAINS)
if fubar == None:
print "Screw up?!"
return False
print "INVENTORY"
for dm in discovery.PWRKAP_POWER_DOMAINS:
print dm.inventory()
print "INVENTORY PICKLED"
for dm in fubar:
print dm.inventory()
print "SNAPSHOT"
for dm in fubar:
print dm.snapshot()
def simulate():
for i in range(0, 300):
for dm in discovery.PWRKAP_POWER_DOMAINS:
dm.process_snapshot()
for dm in discovery.PWRKAP_POWER_DOMAINS:
print dm.trans_store.trans_table
x = dm.get_cap()
x = x * 0.75
while x > 10:
y = dm.set_cap(x)
if y < 0:
print "Missed cap %(cap)dW by %(miss)dW usage %(use)dW" \
% {"miss": y, "cap": x, "use": dm.get_power_use()}
x = dm.get_cap()
x = x * 0.75
tests = []
if len(sys.argv) < 2:
tests = ["train", "pickle3"]
else:
tests = sys.argv[1:]
for test in tests:
print "Executing %s()" % test
exec "%s()" % test
pwrkap-7.30/pwrkap/pwrkap_train.py 0000664 0000000 0000000 00000004274 11144346405 0017337 0 ustar 00root root 0000000 0000000 #!/usr/bin/python
"""Train the system by cycling devices through all known power states."""
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
import discovery
import time
import datetime
STABILIZE_TIME = 5
MAX_MEASUREMENTS = 10
MAX_MEASUREMENT_TIME = 60
def set_and_test(power_domain, domains):
"""Given a list of domains, loop through the power states of
the first domain in the list and recursively call ourself with the
rest of the list. If the domain list is empty, collect data."""
global STABILIZE_TIME, MAX_MEASUREMENTS, MAX_MEASUREMENT_TIME
if len(domains) == 0:
# Sleep a bit to let the system stabilize
time.sleep(STABILIZE_TIME)
before = datetime.datetime.utcnow()
after = datetime.datetime.utcnow()
count = 0
while count < MAX_MEASUREMENTS and (after - before).seconds < MAX_MEASUREMENT_TIME:
power_domain.process_snapshot()
count = count + 1
after = datetime.datetime.utcnow()
return
for state in domains[0].get_power_states():
print ("Set device", domains[0].inventory().keys()[0], state[0])
domains[0].set_max_power_state(state[0])
set_and_test(power_domain, domains[1:])
def train():
"""Train the system."""
def stop_load_for(devices):
"""Stop load for a selection of devices."""
for dev in devices:
dev.stop_load()
def start_load(devices):
loaded = []
for pd in devices:
if pd.start_load():
loaded.append(pd)
else:
stop_load_for(loaded)
return False
return True
if len(discovery.PWRKAP_POWER_DOMAINS) == 0:
return
print "Training power cap database..."
# Set all devices to the lowest power state
for pd in discovery.PWRKAP_POWER_DOMAINS:
for dd in pd.domains:
min_state = dd.get_power_states()[0]
dd.set_max_power_state(min_state[0])
# Start load here.
if not start_load(discovery.PWRKAP_POWER_DOMAINS):
print "Could not start load for testing, transition table will be less effective."
return
# Record load
for pd in discovery.PWRKAP_POWER_DOMAINS:
doms = pd.choose_domains_for_training()
if len(doms) > 0:
set_and_test(pd, doms)
# End load here.
stop_load_for(discovery.PWRKAP_POWER_DOMAINS)
print "...done."
for pd in discovery.PWRKAP_POWER_DOMAINS:
print pd.trans_store.trans_table
pwrkap-7.30/pwrkap/sockmux.py 0000664 0000000 0000000 00000011037 11144346405 0016322 0 ustar 00root root 0000000 0000000 #!/usr/bin/python
"""Multiplex various sockets to a single command channel."""
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
import select
import socket
import sys
import thread
import struct
import traceback
import os
import cPickle as pickle
def res_unavailable_error(err):
"""Determine if the error is due to lack of resource availability."""
if isinstance(err, socket.error) and err[0] == 11:
return True
return False
READ_BUFFER_SIZE = 1
class sockmux:
"""Multiplexer that sends objects from an input channel to sockets
and from sockets to the controller."""
def __init__(self, controller, listen_port):
"""Create a socket mux with a controller and a given listening port."""
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
print "Listening on port %d" % listen_port
self.server_socket.bind(('0.0.0.0', listen_port))
self.server_socket.listen(5)
self.controller = controller
self.should_close = False
self.queues = {}
self.in_buffers = {}
self.pipe = os.pipe()
self.pipe_read = os.fdopen(self.pipe[0], "r")
self.pipe_write = os.fdopen(self.pipe[1], "w")
# This MUST come last!
thread.start_new_thread(self.run, ())
def run(self):
"""Run the mux."""
self.should_close = False
while not self.should_close:
try:
self.run_once()
except Exception, e:
print e
self.server_socket.shutdown(socket.SHUT_RDWR)
self.server_socket.close()
for cs in self.queues.keys():
cs.shutdown(socket.SHUT_RDWR)
cs.close()
self.pipe_read.close()
self.pipe_write.close()
def shut_down(self):
"""Deactivate the mux."""
self.should_close = True
def write(self, object):
"""Write an object to all outputs."""
for cs in self.queues.keys():
queue = self.queues[cs]
queue.append(object)
# Wake up select()
self.pipe_write.write("0")
self.pipe_write.flush()
def kill_socket(self, cs):
"""Terminate a socket."""
del self.queues[cs]
del self.in_buffers[cs]
cs.shutdown(socket.SHUT_RDWR)
cs.close()
def run_once(self):
"""Shovel objects between controller and sockets."""
def write_socket(cs, queue):
"""Write something to a socket."""
try:
assert len(queue) > 0
obj = queue[0]
objlen = len(obj)
sent = cs.send(obj)
while sent != 0:
if objlen == sent:
del queue[0]
else:
queue[0] = obj[sent:]
if len(queue) == 0:
return
obj = queue[0]
objlen = len(obj)
sent = cs.send(obj)
except Exception, e:
# Don't fault on -EAGAIN
if res_unavailable_error(e):
return
print e
traceback.print_exc()
self.kill_socket(cs)
def read_socket(cs):
"""Read something from a socket."""
global READ_BUFFER_SIZE
try:
buffer = self.in_buffers[cs]
str = cs.recv(1)
# Readable but no data == EOF
if len(str) == 0:
raise EOFError()
while len(str) > 0:
buffer = buffer + str
try:
obj = pickle.loads(buffer)
self.in_buffers[cs] = ""
self.controller.command(obj)
except:
self.in_buffers[cs] = buffer
pass
str = cs.recv(1)
except Exception, e:
# Don't kill socket if -EAGAIN
if res_unavailable_error(e):
return
print e
traceback.print_exc(file=sys.stdout)
self.kill_socket(cs)
readers = [self.pipe_read, self.server_socket]
writers = []
exceptions = []
# Nominate all sockets with pending writes for select, and
# all sockets for reads
for cs in self.queues.keys():
queue = self.queues[cs]
readers.append(cs)
exceptions.append(cs)
if len(queue) > 0:
writers.append(cs)
# Find sockets that aren't blocked
#print ("before: ", readers, writers, exceptions)
(r, w, x) = select.select(readers, writers, exceptions)
#print ("after: ", r, w, x)
assert len(x) == 0
# If someone connects, tell the controller and add the
# socket to our list.
if self.server_socket in r:
try:
(cs, addr) = self.server_socket.accept()
print (cs, addr)
cs.setblocking(0)
queue = []
res = self.controller.connect(cs, queue)
if not res:
cs.shutdown(socket.SHUT_RDWR)
cs.close()
else:
self.queues[cs] = queue
self.in_buffers[cs] = ""
except socket.error:
pass
# For all writers that can be written, write queued data
for cs in w:
queue = self.queues[cs]
write_socket(cs, queue)
# For all reader sockets...
for cs in r:
if cs == self.pipe_read:
x = self.pipe_read.read(1)
continue
if cs == self.server_socket:
continue
read_socket(cs)
pwrkap-7.30/pwrkap/sysfs_meter.py 0000664 0000000 0000000 00000007553 11144346405 0017204 0 ustar 00root root 0000000 0000000 #!/usr/bin/python
"""Read power meters available via sysfs."""
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
import discovery
import pwrkap_data
import datetime
import os
import dircache
import util
class sysfs_energy_meter(pwrkap_data.energy_meter):
"""Driver for energy meters available via sysfs."""
def __init__(self, sensor_filename):
self.sensor_filename = sensor_filename
self.latency = None
self.initial_value = self.__read()
def __getstate__(self):
state = {}
for key in self.__dict__.keys():
value = self.__dict__[key]
state[key] = value
del state["initial_value"]
return state
def __setstate__(self, state):
self.__dict__ = state
self.initial_value = self.__read()
def get_chip_name(self):
"""Determine the name of the chip that controls this sensor."""
last_slash = self.sensor_filename.rfind("/")
if last_slash == -1:
return None
base_dir = self.sensor_filename[:last_slash + 1]
base_dir = base_dir + "/name"
x = util.read_line_as_array(base_dir)
if x == None:
return None
return x[0]
def __read(self):
"""Read the energy meter."""
x = util.read_line_as_array(self.sensor_filename)
if x == None:
return None
return float(x[0]) / 1000000
def read(self):
try:
before = datetime.datetime.utcnow()
return self.__read() - self.initial_value
finally:
after = datetime.datetime.utcnow()
if self.latency == None:
self.latency = (after - before)
else:
self.latency = (8 * self.latency + 2 * (after - before)) / 10
def get_latency(self):
return self.latency
def inventory(self):
return ("sysfsmeter", {
"name": self.sensor_filename, \
"chip": self.get_chip_name(), \
})
class sysfs_power_meter(pwrkap_data.power_meter):
"""Driver for power meters available via sysfs."""
def __init__(self, sensor_filename):
self.sensor_filename = sensor_filename
self.latency = None
def get_chip_name(self):
"""Determine the name of the chip that controls this sensor."""
last_slash = self.sensor_filename.rfind("/")
if last_slash == -1:
return None
base_dir = self.sensor_filename[:last_slash + 1]
base_dir = base_dir + "/name"
x = util.read_line_as_array(base_dir)
if x == None:
return None
return x[0]
def read(self):
try:
before = datetime.datetime.utcnow()
x = util.read_line_as_array(self.sensor_filename)
if x == None:
return None
return float(x[0]) / 1000000
finally:
after = datetime.datetime.utcnow()
if self.latency == None:
self.latency = (after - before)
else:
self.latency = (8 * self.latency + 2 * (after - before)) / 10
def get_latency(self):
return self.latency
def inventory(self):
return ("sysfsmeter", {
"name": self.sensor_filename, \
"chip": self.get_chip_name(), \
})
SYSFS_HWMON_DIR = "/sys/class/hwmon/"
def sysfs_meter_discover():
"""Discover sysfs meters."""
global SYSFS_HWMON_DIR
# This will load ibmpex/ibmaem drivers
os.system("modprobe -q hwmon")
os.system("modprobe -q ipmi-si")
os.system("modprobe -q ibmpex")
os.system("modprobe -q ibmaem")
# Now look for /sys/class/hwmon/hwmon*/device/power*_input
for hwmon_dev in dircache.listdir(SYSFS_HWMON_DIR):
if not hwmon_dev.startswith("hwmon"):
continue
hwmon_dev_dir = SYSFS_HWMON_DIR + hwmon_dev + "/device/"
for sensor_dev in dircache.listdir(hwmon_dev_dir):
if sensor_dev.startswith("power") \
and (sensor_dev.endswith("_input") or sensor_dev.endswith("_average")):
meter = sysfs_power_meter(hwmon_dev_dir + sensor_dev)
discovery.PWRKAP_POWER_METERS.append(meter)
if sensor_dev.startswith("energy") \
and sensor_dev.endswith("_input"):
meter = sysfs_energy_meter(hwmon_dev_dir + sensor_dev)
discovery.PWRKAP_ENERGY_METERS.append(meter)
def sysfs_meter_init():
"""Set up sysfs meter discovery functions."""
discovery.PWRKAP_METER_DISCOVERY.append(sysfs_meter_discover)
return True
sysfs_meter_init()
pwrkap-7.30/pwrkap/transitions.py 0000664 0000000 0000000 00000023503 11144346405 0017207 0 ustar 00root root 0000000 0000000 #!/usr/bin/python
"""Remember power domain snapshots and calculate expected transition costs."""
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
import math
import pwrkap_data
NEW_TRANSITION_WEIGHT = 0.2
class transition_store:
"""Compute and store power-managed device transitions."""
def __init__(self, snapshot_store, inter_domains, num_util_buckets):
"""Create a transition store with a snapshot store."""
self.snapshot_store = snapshot_store
self.trans_table = {}
self.num_util_buckets = num_util_buckets
self.inter_domains = inter_domains
for i in range(0, len(inter_domains)):
util_buckets = []
for j in range(0, num_util_buckets):
p_states = {}
for (p_state, potential) in inter_domains[i][0].get_power_states():
p_state_dests = {}
for (p_state_dest, potential_dest) in inter_domains[i][0].get_power_states():
if p_state >= p_state_dest:
continue
p_state_dests[p_state_dest] = (None, potential_dest - potential)
if len(p_state_dests) != 0:
p_states[p_state] = p_state_dests
util_buckets.append(p_states) #[(j + 1.00) / num_util_buckets] = p_states
self.trans_table["idom" + str(i)] = util_buckets
self.stat_cache = []
def consider_snapshot(self, snap):
"""Add a snapshot and try to determine some transitions."""
global NEW_TRANSITION_WEIGHT
self.snapshot_store.append(snap)
a = self.genericize_snapshot(snap)
a_hist = self.calc_snapshot_stats(a)
for (old_hist, old_power) in self.stat_cache:
# Find an interchangable-domain with just two changes
key = self.find_idom_with_two_changes(a_hist, old_hist)
if key == None:
continue
delta_power = old_power - a["power"]
# Store this result in our transition table
idom = key[0]
bucket = key[1][1]
p0 = key[1][0]
p1 = key[2][0]
if p0 > p1:
pt = p0
p0 = p1
p1 = pt
delta_power = -delta_power
(power, perf) = self.trans_table[idom][bucket][p0][p1]
if power == None:
power = delta_power
self.trans_table[idom][bucket][p0][p1] = \
(NEW_TRANSITION_WEIGHT * delta_power + \
(1 - NEW_TRANSITION_WEIGHT) * power, \
perf)
if len(self.stat_cache) > self.snapshot_store.max_size:
self.stat_cache.pop()
self.stat_cache.append( (a_hist, a["power"]) )
def find_idom_with_two_changes(self, a_hist, b_hist):
"""See if we can find only one idom where one power state \
loses a core and one power state gains a core."""
res = None
# Same idoms?
assert a_hist.keys() == b_hist.keys()
for idom in a_hist.keys():
a_idom_hist = a_hist[idom]
b_idom_hist = b_hist[idom]
a_set = set(a_idom_hist.keys())
b_set = set(b_idom_hist.keys())
ab_set = a_set.union(b_set)
# No transitions? Ignore this idom.
if a_idom_hist == b_idom_hist:
continue
# A and B differ. If we've already found a solution,
# it's now invalid as A and B have two different idoms.
if res != None:
return None
# Now scan A and B for changes
a_state = None
b_state = None
for key in ab_set:
if not b_idom_hist.has_key(key):
b_val = 0
else:
b_val = b_idom_hist[key]
if not a_idom_hist.has_key(key):
a_val = 0
else:
a_val = a_idom_hist[key]
diff = b_val - a_val # b_idom_hist[key] - a_idom_hist[key]
# More than 1 device entered/exited this state;
# this sample cannot be used.
if diff > 1 or diff < -1:
return None
# No change; ignore
if diff == 0:
continue
# This is a -1/+1 transition.
if diff == 1:
if b_state != None:
return None
b_state = key
elif diff == -1:
if a_state != None:
return None
a_state = key
# Ignore transitions that don't involve speed changes
if a_state != None and b_state != None and a_state[0] == b_state[0]:
return None
assert (a_state == None and b_state == None) or (a_state != None and b_state != None)
if a_state != None and b_state != None:
res = (idom, a_state, b_state)
return res
def calc_snapshot_stats(self, pseudo_snap):
"""Construct a histogram of the number of idoms in a given \
(pwr_state, util_bucket)."""
histogram = {}
domains = pseudo_snap["domains"]
for (domain, state) in domains:
if histogram.has_key(domain) == False:
histogram[domain] = {}
hist_key = (state["state"], state["util_bucket"])
if histogram[domain].has_key(hist_key) == False:
histogram[domain][hist_key] = 1
else:
histogram[domain][hist_key] = histogram[domain][hist_key] + 1
return histogram
def find_idomain_for_dev(self, dev_name):
"""Find an identical-domain for a device."""
for i in range(0, len(self.inter_domains)):
domain = self.inter_domains[i]
for domain_dev in domain:
if domain_dev.inventory()[0] == dev_name:
return "idom" + str(i)
return dev_name
def find_util_bucket(self, util):
"""Change utilization to utilization bucket number."""
return min(self.num_util_buckets - 1, int(util * self.num_util_buckets))
def genericize_snapshot(self, snap):
"""Construct a pseudo-snapshot from a real snapshot with device \
name changed to identical-domain ID, domain hierarchy flattened, devices \
within a domain collapsed into one, and utilization changed to utilization \
bucket number."""
# Copy non-domain properties to new snapshot
new_snap = {}
for key in snap.keys():
if key != "domains":
new_snap[key] = snap[key]
# Now copy domains but with a few changes.
# XXX: We'll be in trouble if a domain isn't a strict subset
# of an idomain!
new_domains = []
for domain in snap["domains"]:
new_state = {}
dev0 = domain.keys()[0]
new_name = self.find_idomain_for_dev(dev0)
#new_state["old_name"] = dev0
dev0_state = domain[dev0]
for key in dev0_state.keys():
if key != "utilization":
new_state[key] = dev0_state[key]
sum = 0.0
for device in domain.keys():
assert new_name == self.find_idomain_for_dev(device)
device_state = domain[device]
sum = sum + pwrkap_data.average_utilization(device_state["util_details"])
new_state["util_bucket"] = self.find_util_bucket(sum / len(domain.keys()))
new_domains.append((new_name, new_state))
new_snap["domains"] = new_domains
return new_snap
def propose_transitions(self, domain):
"""Propose power state transitions that can be executed for a domain."""
def try_to_find_transition(idom_table, bucket, a, b):
"""Try to find a transition for the current utilization. If none
found, try adjoining buckets."""
(x, y) = idom_table[bucket][a][b]
if not x == None:
return (x, y)
print ("Guessing!", bucket, a, b)
for delta in range(1, max(len(idom_table) - bucket, bucket)):
if bucket + delta < len(idom_table):
(x, y) = idom_table[bucket + delta][a][b]
if not x == None:
return (x, y)
if bucket - delta >= 0:
(x, y) = idom_table[bucket - delta][a][b]
if not x == None:
return (x, y)
return None
curr_state = domain.get_current_power_state()
curr_util = pwrkap_data.average_utilization(domain.get_utilization_details())
possible_states = domain.get_power_states()
device = domain.get_device()
idom = self.find_idomain_for_dev(device.inventory()[0])
bucket = self.find_util_bucket(curr_util)
props = []
for (new_state, junk) in possible_states:
if curr_state == new_state:
continue
if curr_state > new_state:
a = new_state
b = curr_state
else:
a = curr_state
b = new_state
# XXX: What if there's no entry for this bucket?
#(power, perf) = self.trans_table[idom][bucket][a][b]
x = try_to_find_transition(self.trans_table[idom], bucket, a, b)
if x == None:
print ("What do we do with this?", self.trans_table, idom, bucket, a, b)
continue
(power, perf) = x
if curr_state > new_state:
power = -power
perf = -perf
prop = proposed_transition(domain, new_state, power, perf, curr_state, curr_util)
props.append(prop)
return props
class snapshot_store:
"""Store power domain snapshots."""
def __init__(self, max_size):
"""Create a snapshot store device."""
self.max_size = max_size
self.records = []
def append(self, snapshot):
"""Append a snapshot record, deleting old ones if needed."""
if len(self.records) >= self.max_size:
self.records.pop()
self.records.append(snapshot)
class proposed_transition:
"""A proposal to change the power controls of a device."""
def __init__(self, device, new_state, power_impact, performance_impact, curr_state, curr_util):
"""Create a proposal."""
self.device = device
self.new_state = new_state
self.performance_impact = performance_impact
self.power_impact = power_impact
# XXX: This class does not currently use utilization!
self.curr_state = curr_state
self.curr_util = curr_util
def __repr__(self):
"""Return string representation of object."""
return str({"device": self.device.get_device().inventory()[0], "new_state": self.new_state, \
"perf": self.performance_impact, "power": self.power_impact, \
"state": self.curr_state, "util": self.curr_util})
def compare_proposals(self, other):
"""Compare one proposal to another."""
# This routine can be used to sort a list of proposals by "goodness".
# Assumptions: (1) no proposal has zero power impact. (2) if the cap
# is increasing, all proposals increase performance.
# (3) if the cap is decreasing, all proposals cut power.
# We therefore employ two factors to determine proposal ranking.
# The first is dP / abs(dW) because we always want the most positive
# change in performance for _any_ change in power budget. In the
# event of a tie, the proposal with the most negative dW wins.
dPdW_a = self.performance_impact / abs(self.power_impact)
dPdW_b = other.performance_impact / abs(other.power_impact)
if dPdW_a > dPdW_b:
return -1
elif dPdW_a < dPdW_b:
return 1
if self.power_impact < other.power_impact:
return -1
elif self.power_impact > other.power_impact:
return 1
return 0
pwrkap-7.30/pwrkap/ui_controller.py.template 0000664 0000000 0000000 00000042223 11144346405 0021324 0 ustar 00root root 0000000 0000000 #!/usr/bin/python
"""GTK UI controller for pwrkap."""
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
import pygtk
pygtk.require("2.0")
import gtk
import gtk.glade
import gobject
import thread
import ui_data
import datetime
import time
import threading
import traceback
import pwrkap_data
import sys
HAVE_MATPLOTLIB = True
try:
from matplotlib.axes import Subplot
from matplotlib.figure import Figure
# uncomment to select /GTK/GTKAgg/GTKCairo
#from matplotlib.backends.backend_gtk import FigureCanvasGTK as FigureCanvas
from matplotlib.backends.backend_gtkagg import FigureCanvasGTKAgg as FigureCanvas
#from matplotlib.backends.backend_gtkcairo import FigureCanvasGTKCairo as FigureCanvas
except:
HAVE_MATPLOTLIB = False
DEFAULT_TITLE = "Energy Consumption"
GRAPH_DRAW_INTERVAL = 5
UI_DRAW_INTERVAL = 2
DRAW_GRAPH = False
class ui_controller:
"""Binding between gtk controls and various actions."""
def __init__(self, hosts):
"""Create a UI."""
global DEFAULT_TITLE
self.window_name = "monitor_window"
try:
self.window_tree = gtk.glade.XML("ui.glade", self.window_name)
except:
try:
self.window_tree = gtk.glade.XML("%PREFIX%/share/pwrkap/ui.glade", self.window_name)
except:
print "Could not find UI file; is pwrkap installed correctly?"
sys.exit(2)
self.window = self.window_tree.get_widget(self.window_name)
self.window.set_title(DEFAULT_TITLE)
self.hosts = hosts
# Set up the machine list model
self.tree = self.window_tree.get_widget("host_names")
model = gtk.TreeStore(gobject.TYPE_PYOBJECT, gobject.TYPE_STRING, gobject.TYPE_STRING)
renderer = gtk.CellRendererText()
column = gtk.TreeViewColumn("Machine", renderer, text = 1)
self.tree.append_column(column)
column = gtk.TreeViewColumn("Domain", renderer, text = 2)
self.tree.append_column(column)
self.tree.set_model(model)
self.all_iter = model.insert_before(None, None)
model.set_value(self.all_iter, 0, (None, None))
model.set_value(self.all_iter, 1, "All Systems")
# GTK Event handling
events = {
"on_close_button_clicked": self.close_app,
"on_set_cap_button_clicked": self.set_cap,
"on_max_sample_age_changed": self.set_max_sample_age,
"on_host_names_cursor_changed": self.select_power_object_later,
"on_show_graph_toggled": self.show_graph_toggle}
self.window_tree.signal_autoconnect(events)
self.window.connect("destroy", self.close_app)
# pwrkap event handling
self.pwrkap_events = {
ui_data.HOST_CONNECTED: self.host_connected,
ui_data.HOST_DISCONNECTED: self.host_disconnected,
ui_data.HOST_DOMAIN_DATA_RECEIVED: self.data_seen}
# Create the graph
graphs = self.create_graph()
self.graph_lock = threading.Lock()
vbox = self.window_tree.get_widget("main_vbox")
if graphs != None:
(self.fig, self.canvas) = graphs
vbox.pack_start(self.canvas)
vbox.reorder_child(self.canvas, 0)
else:
graph_checkbox = self.window_tree.get_widget("show_graph")
graph_checkbox.set_sensitive(False)
label = gtk.Label("Graphs unavailable because matplotlib is not installed.")
vbox.pack_start(label)
vbox.reorder_child(label, 0)
# Other housekeeping variables
self.cur_host = None
self.cur_dom = None
self.tree.set_cursor("0")
self.selection_did_change = True
self.graph_changed = True
self.last_graph_draw_time = datetime.datetime.utcnow()
self.last_ui_draw_time = datetime.datetime.utcnow()
self.in_gtk_update = False
self.in_draw_graph = False
self.dom_cap = None
self.dom_power = None
self.dom_energy = None
self.dom_util = None
#self.window.set_size_request(1024, 768)
self.window.show_all()
self.connect_signal = threading.Condition()
def close_app(self, widget):
"""Close the program."""
gtk.main_quit()
def show_graph_toggle(self, widget):
"""Enable or disable the graph via check box."""
global DRAW_GRAPH
new_value = widget.get_active()
if new_value:
dialog = gtk.Dialog("Slow graph warning", None, \
gtk.DIALOG_MODAL, # | gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_NO, gtk.RESPONSE_NO, gtk.STOCK_YES, gtk.RESPONSE_YES))
label = gtk.Label("Graphing power use of a large number of power domains can\nbe very processor intensive! Are you sure you want to proceed?")
dialog.vbox.pack_start(label, padding = 15)
dialog.vbox.set_border_width(15)
label.show()
res = dialog.run()
dialog.hide()
if res == gtk.RESPONSE_NO:
widget.set_active(False)
return
DRAW_GRAPH = widget.get_active()
thread.start_new_thread(self.selection_changed, ())
def connect_hosts(self):
"""Connect all hosts."""
model = self.tree.get_model()
for host in self.hosts:
host.add_listener(self)
iter = model.insert_after(None, self.all_iter)
model.set_value(iter, 0, (host, None))
model.set_value(iter, 1, "%s:%d" % (host.host, host.port))
try:
#thread.stack_size(65536)
x = 3
except:
pass
for host in self.hosts:
thread.start_new_thread(host.control_loop, ())
self.connect_signal.acquire()
self.connect_signal.wait(10)
self.connect_signal.release()
def find_tree_iter_from_host(self, host):
"""Return a tree iterator given a host object."""
model = self.tree.get_model()
root_iter = model.get_iter_root()
while root_iter != None and model.get_value(root_iter, 0)[0] != host:
root_iter = model.iter_next(root_iter)
return root_iter
def pwrkap_event(self, event_type, affected):
"""Acknowledge a pwrkap host event."""
handler = self.pwrkap_events[event_type]
handler(affected)
def host_connected(self, affected):
"""Handle a host connection."""
print "Host connected"
self.connect_signal.acquire()
self.connect_signal.notify()
self.connect_signal.release()
gtk.gdk.threads_enter()
model = self.tree.get_model()
host_iter = self.find_tree_iter_from_host(affected)
print (affected, affected.host, affected.port)
doms = []
for domain_name in affected.domains.keys():
domain_object = affected.domains[domain_name]
doms.append(domain_object)
iter = model.insert_after(host_iter, None)
model.set_value(iter, 0, (affected, domain_object))
model.set_value(iter, 2, domain_name)
gtk.gdk.threads_leave()
agg = ui_data.pwrkap_aggregate_domain(doms, "All Domains")
gtk.gdk.threads_enter()
(x, y) = model.get_value(host_iter, 0)
model.set_value(host_iter, 0, (x, agg))
gtk.gdk.threads_leave()
self.update_all_aggregate()
print "end processing new host"
def host_disconnected(self, affected):
"""Handle a host disconnection."""
model = self.tree.get_model()
host_iter = self.find_tree_iter_from_host(affected)
dom_iter = model.iter_children(host_iter)
gtk.gdk.threads_enter()
while dom_iter != None:
next = model.iter_next(dom_iter)
model.remove(dom_iter)
dom_iter = next
(x, y) = model.get_value(host_iter, 0)
model.set_value(host_iter, 0, (x, None))
gtk.gdk.threads_leave()
self.update_all_aggregate()
def update_all_aggregate(self):
"""Update the 'All Systems' aggregate."""
doms = []
model = self.tree.get_model()
for host in self.hosts:
for dom_name in host.domains.keys():
dom = host.domains[dom_name]
doms.append(dom)
agg = None
if len(doms) != 0:
agg = ui_data.pwrkap_aggregate_domain(doms, "All Systems")
gtk.gdk.threads_enter()
(x, y) = model.get_value(self.all_iter, 0)
model.set_value(self.all_iter, 0, (x, agg))
gtk.gdk.threads_leave()
self.select_power_object()
def data_seen(self, affected):
"""Handle incoming snapshot data."""
if self.cur_dom != None and self.cur_dom.is_affected_by(affected):
self.update_view()
def create_graph(self):
"""Connect matplotlib graph to our gtk/glade window."""
global HAVE_MATPLOTLIB
if not HAVE_MATPLOTLIB:
return None
# Extract GTK background color
gtk_bgcolors = gtk.Style().bg
bg_color_str = "#%X%X%X" % (round(float(gtk_bgcolors[2].red) / 65536 * 256),
round(float(gtk_bgcolors[2].green) / 65536 * 256),
round(float(gtk_bgcolors[2].blue) / 65536 * 256))
# Create figure
fig = Figure()
fig.set_facecolor(bg_color_str)
fig.set_edgecolor(bg_color_str)
canvas = FigureCanvas(fig)
return (fig, canvas)
def select_power_object_later(self, widget):
"""Select new object... later."""
thread.start_new_thread(self.select_power_object, ())
def select_power_object(self):
"""Slide a new power-manageable object into view. \
NOTE: The GDK lock must be held before calling this function."""
model = self.tree.get_model()
(tree, iter) = self.tree.get_selection().get_selected()
try:
obj = model.get_value(iter, 0)
except:
return
self.cur_host = obj[0]
self.cur_dom = obj[1]
self.selection_changed()
def gtk_update_view(self, draw_graph):
"""Update all GTK controls."""
def time_to_age_string(time):
"""Convert seconds to age string."""
if time >= 86400:
str = "%d Day" % (time / 86400)
elif time >= 3600:
str = "%d Hour" % (time / 3600)
else:
str = "%d Minute" % (time / 60)
if str[0] != '1':
str = str + "s"
return str
def fmt_energy(e):
"""Render energy use as a string."""
e = e / 3600
if e == None:
return "Unknown"
elif e < 1000:
return "%.3fWh" % e
elif e < 1000000:
return "%.3fKWH" % (e / 1000)
elif e < 1000000000:
return "%.3fMWH" % (e / 1000000)
else:
return "%.3fGWH" % (e / 1000000000)
global DEFAULT_TITLE, DRAW_GRAPH, HAVE_MATPLOTLIB
self.in_gtk_update = True
# Update GTK control enablement and values
if self.cur_dom != None:
# Enable controls
self.window_tree.get_widget("power_cap_field").set_sensitive(True)
self.window_tree.get_widget("max_sample_age").set_sensitive(True)
self.window_tree.get_widget("set_cap_button").set_sensitive(True)
# Set max cap
if self.cur_dom.get_cap() != None and self.selection_did_change:
new_val = 0
if self.dom_cap != None:
new_val = self.dom_cap
self.window_tree.get_widget("power_cap_field").set_value(new_val)
# Set max record age
widget = self.window_tree.get_widget("max_sample_age")
if self.cur_dom.get_max_sample_age() == None:
# Assume 30 minutes is ok (?)
widget.set_active(2)
else:
age_str = time_to_age_string(self.cur_dom.get_max_sample_age())
for i in range(0, len(widget.get_model())):
if widget.get_model()[i][0] == age_str:
widget.set_active(i)
break
else:
# Disable controls
self.window_tree.get_widget("power_cap_field").set_sensitive(False)
self.window_tree.get_widget("max_sample_age").set_sensitive(False)
self.window_tree.get_widget("set_cap_button").set_sensitive(False)
# Update window title
title = " - " + DEFAULT_TITLE
if self.cur_host != None:
title = self.cur_host.get_name() + title
if self.cur_dom != None:
sep = ""
if self.cur_host != None:
sep = " : "
title = self.cur_dom.get_name() + sep + title
self.window.set_title(title)
# Update more controls
if self.dom_cap == None:
self.window_tree.get_widget("power_cap_label").set_text("")
self.window_tree.get_widget("power_use_label").set_text("")
self.window_tree.get_widget("energy_use_label").set_text("")
self.window_tree.get_widget("domain_use_label").set_text("")
self.window_tree.get_widget("overcap_label").set_text("")
else:
self.window_tree.get_widget("power_cap_label").set_text("%dW" % int(self.dom_cap))
self.window_tree.get_widget("power_use_label").set_text("%dW" % int(self.dom_power))
self.window_tree.get_widget("energy_use_label").set_text(fmt_energy(self.dom_energy))
self.window_tree.get_widget("domain_use_label").set_text("%d%%" % self.dom_util)
if self.dom_cap < self.dom_power:
self.window_tree.get_widget("overcap_label").set_markup("Cap exceeded by %dW!" % (self.dom_power - self.dom_cap))
else:
self.window_tree.get_widget("overcap_label").set_text("")
# Draw graph
if DRAW_GRAPH and self.graph_changed:
atime=datetime.datetime.utcnow()
self.canvas.draw()
btime=datetime.datetime.utcnow()
print ("elapsed draw time", (btime-atime))
elif not DRAW_GRAPH and HAVE_MATPLOTLIB:
self.fig.clear()
self.canvas.draw()
self.selection_did_change = False
self.graph_changed = False
self.in_gtk_update = False
def selection_changed(self):
"""Update the UI because the selection changed.
NOTE: The GDK lock must be held before calling this function."""
self.selection_did_change = True
self.update_view()
def update_view(self):
"""Update the view.
NOTE: The GDK lock must be held before calling this function."""
global UI_DRAW_INTERVAL
now = datetime.datetime.utcnow()
if not self.selection_did_change and (now - self.last_ui_draw_time).seconds < UI_DRAW_INTERVAL:
return
self.draw_graph()
if self.cur_dom != None:
self.dom_cap = self.cur_dom.get_cap()
self.dom_power = self.cur_dom.get_power()
self.dom_energy = self.cur_dom.get_energy()
self.dom_util = pwrkap_data.average_utilization(self.cur_dom.get_utilization_details())
else:
self.dom_cap = None
self.dom_power = None
self.dom_energy = None
self.dom_util = None
if not self.in_gtk_update:
gobject.idle_add(self.gtk_update_view, ())
self.last_ui_draw_time = datetime.datetime.utcnow()
def set_cap(self, widget):
"""Set a power cap due as a result of UI stimulus."""
widget = self.window_tree.get_widget("power_cap_field")
if self.cur_dom == None:
return
self.cur_dom.set_cap(widget.get_value_as_int())
def set_max_sample_age(self, widget):
"""Set max sample age due as a result of UI stimulus."""
def age_to_time(age_string):
"""Convert age string to seconds."""
items = age_string.split()
time = int(items[0])
units = items[1]
if units.startswith("Minute"):
time = time * 60
elif units.startswith("Hour"):
time = time * 3600
elif units.startswith("Day"):
time = time * 86400
return time
if self.cur_dom == None:
return
age_str = widget.get_model()[widget.get_active()][0]
age = age_to_time(age_str)
self.cur_dom.set_max_sample_age(age)
def draw_graph(self):
"""Draw the graph. NOTE: GDK lock must be held."""
if self.in_draw_graph:
return
self.graph_lock.acquire()
self.in_draw_graph = True
self.__draw_graph()
self.in_draw_graph = False
self.graph_lock.release()
def __draw_graph(self):
"""Draw the graph. NOTE: Graph and GDK lock must be held."""
def time_to_age(time_list):
"""Convert a list of absolute times to ages."""
new_list = []
now = datetime.datetime.utcnow()
nows = time.mktime(now.timetuple())
for t in time_list:
new_list.append((nows - t) / 60)
return new_list
global GRAPH_DRAW_INTERVAL, DRAW_GRAPH, HAVE_MATPLOTLIB
if not HAVE_MATPLOTLIB:
return
if self.cur_dom == None:
self.fig.clear()
return
# Don't draw the graph too frequently if we can help it;
# matplotlib is rather slow.
now = datetime.datetime.utcnow()
if not self.selection_did_change and (now - self.last_graph_draw_time).seconds < GRAPH_DRAW_INTERVAL:
return
self.last_graph_draw_time = now
if not DRAW_GRAPH:
return
a_plot = datetime.datetime.utcnow()
print "Start extract"
res = self.cur_dom.get_historical_data_as_lists()
if res == None:
print "No results to plot?"
return
(times, cap, power, energy, util_details) = res
times = time_to_age(times)
b_plot = datetime.datetime.utcnow()
print ("elapsed extract time", (b_plot - a_plot))
# Add plot
self.fig.clear()
self.fig.subplots_adjust(hspace = 0.4)
power_axes = self.fig.add_subplot(2, 1, 1)
power_axes.grid(True)
power_axes.axis('auto')
# Plot power data
cap_line = power_axes.plot(times, cap, 'r-o', linewidth = 2, aa = True)
power_line = power_axes.plot(times, power, 'b-o', linewidth = 2, aa = True)
(ymin, ymax) = power_axes.set_ylim()
power_axes.set_ylim(ymin = ymin - 5, ymax = ymax + 5)
power_axes.set_ylabel("Watts")
power_axes.set_title("Power Use vs. Time")
power_axes.yaxis.tick_left()
# Plot utilization data
util_axes = self.fig.add_subplot(2, 1, 2)
util_axes.grid(True)
colors = ["#7F0000", "#007F00", "#00007F", "#C0C0C0", "#CCCC00", "#00CCCC", "#CC00CC"]
color_index = 0
tr = times * 1
tr.reverse()
times_reversed = (times * 1).reverse()
last_y = [0] * len(times)
t = times + tr
num_plots = len(util_details.keys())
num_samples = len(times)
for dev in util_details.keys():
curr_y = []
for i in range(0, num_samples):
curr_y.append((util_details[dev][i] / num_plots) + last_y[i])
last_y.reverse()
u = curr_y + last_y
util_axes.fill(t, u, facecolor = colors[color_index], \
linewidth = 0, alpha = 0.8)
last_y = curr_y
color_index = color_index + 1
if color_index >= len(colors):
color_index = 0
util_line = util_axes.plot(times, last_y, '-o', color = "black", \
linewidth = 2, aa = True)
util_axes.set_ylim(ymin = -5, ymax = 105)
util_axes.set_title("Device Use vs. Time")
util_axes.set_ylabel("Use (%)")
util_axes.set_xlabel("Minutes Ago")
# Labels for the plots
power_axes.legend((cap_line, power_line), ('Cap', 'Usage'), 'best')
util_axes.legend((util_line,), ('Overall Use',), 'best')
c_plot = datetime.datetime.utcnow()
print ("elapsed plot time", (c_plot - b_plot))
#fp = file("/tmp/goo", "w")
#for i in range(0, len(times)):
# fp.write("%f %d %d %d\n" % (times[i], cap[i], power[i], util[i]))
#fp.close()
#self.canvas.draw()
self.graph_changed = True
pwrkap-7.30/pwrkap/ui_data.py 0000664 0000000 0000000 00000040324 11144346405 0016240 0 ustar 00root root 0000000 0000000 #!/usr/bin/python
"""Various data objects for the UI."""
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
import cPickle as pickle
import socket
import datetime
import traceback
import time
import pwrkap_data
import threading
# Event types
HOST_CONNECTED = 1
HOST_DISCONNECTED = 2
HOST_DOMAIN_DATA_RECEIVED = 3
DEFAULT_MAX_AGE = 1800
class historical_data:
"""Storage for power cap/use data."""
def __init__(self, max_age):
"""Create storage."""
self.clear()
self.max_age = max_age
def clear(self):
"""Clear all data."""
self.time = []
self.seconds = []
self.cap = []
self.power = []
self.energy = []
self.util_details = {}
def get_max_sample_age(self):
"""Retrieve the maximum record age."""
return self.max_age
def set_max_sample_age(self, new_max_age):
"""Set the maximum age that records are kept."""
self.max_age = new_max_age
self.scrub_samples()
def scrub_samples(self):
"""Remove old records."""
now = datetime.datetime.utcnow()
for i in range(0, len(self.time)):
if (now - self.time[i]).seconds <= self.max_age:
del self.time[0:i]
del self.seconds[0:i]
del self.cap[0:i]
del self.power[0:i]
del self.energy[0:i]
for dev in self.util_details.keys():
del self.util_details[dev][0:i]
break
def retrieve_as_lists(self, since = None):
"""Retrieve all data as a tuple of lists."""
if since == None:
return (self.seconds, self.cap, self.power, \
self.energy, self.util_details)
for i in range(0, len(self.time)):
if self.time[i] > since:
x = {}
for dev in self.util_details.keys():
x[dev] = self.util_details[dev][i:]
return (self.seconds[i:], self.cap[i:], \
self.power[i:], self.energy[i:], x)
return None
def store(self, time_stamp, cap, power, energy, util_details):
"""Store a snapshot of time, cap, power and utilization."""
if len(self.time) > 0:
last_time = self.time[-1]
if time_stamp <= last_time:
return
self.time.append(time_stamp)
self.seconds.append(time.mktime(time_stamp.timetuple()))
self.cap.append(cap)
self.power.append(power)
self.energy.append(energy)
for dev in util_details.keys():
if not self.util_details.has_key(dev):
self.util_details[dev] = [0] * (len(self.time) - 1)
self.util_details[dev].append(util_details[dev])
self.scrub_samples()
def get_most_recent(self):
"""Retrieve most recent samples."""
if len(self.time) == 0:
return None
idx = len(self.time) - 1
ud = {}
for dev in self.util_details.keys():
ud[dev] = self.util_details[dev][-1]
return (self.time[-1], self.cap[-1], self.power[-1], \
self.energy[-1], ud)
class pwrkap_host:
"""Represents a connection to a pwrkap server."""
def __init__(self, host, port):
"""Connect to a given host:port for pwrkap data."""
self.host = host
self.port = port
self.domains = {}
self.socket = None
self.sockfile = None
self.exit_loop = False
self.event_listeners = []
def get_name(self):
"""Create name."""
return "%s:%d" % (self.host, self.port)
def add_listener(self, obj):
"""Add an event listener."""
self.event_listeners.append(obj)
def remove_listener(self, obj):
"""Remove an event listener."""
self.event_listeners.remove(obj)
def connect(self):
"""Connect to the server."""
assert self.socket == None
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print "Connecting to %s:%d" % (self.host, self.port)
self.socket.connect((self.host, self.port))
self.sockfile = self.socket.makefile()
def set_up_socket(self):
"""Prepare socket for use."""
# Read list of (domain, domain_descr) tuples
domains = pickle.load(self.sockfile)
for (name, descr) in domains:
phd = pwrkap_host_domain(self, self.port, name)
self.domains[name] = phd
# Read list of old snapshots (time, (domain, snapshot)) tuples
# being careful to look for (time, 'live')
live_time = None
old_data = []
print (self, 1, datetime.datetime.utcnow())
while True:
(time, obj) = pickle.load(self.sockfile)
if obj == 'live':
live_time = time
break;
old_data.append((time, obj))
print (self, 2, datetime.datetime.utcnow())
# Now recalibrate the timestamps.
assert live_time != None
now = datetime.datetime.utcnow()
time_delta = now - live_time
#1-2 and 3-4 are slow
# Recalibrate the historical data and store
for (time, obj) in old_data:
(domain, snap) = obj
x = (time + time_delta, snap)
if "energy" in snap.keys():
energy = snap["energy"]
else:
energy = None
src_ud = {}
dst_ud = {}
if snap.has_key("util_details"):
src_ud = snap["util_details"]
else:
src_ud = {"dev": snap["utilization"]}
for dev in src_ud:
dst_ud["%s:%s:%s:%s" % (self.host, self.port, domain, dev)] = 100.0 * src_ud[dev]
self.domains[domain].historical_data.store(time + time_delta, \
snap["cap"], snap["power"], energy, dst_ud)
# Set initial values of cap/power/util to the last snapshot
# regardless of how old it is
for dom_name in self.domains.keys():
self.domains[dom_name].fudge_data()
print (self, 3, datetime.datetime.utcnow())
def disconnect(self):
"""Disconnect from server and erase all data."""
assert self.socket != None
self.sockfile.close()
self.sockfile = None
self.socket.shutdown(socket.SHUT_RDWR)
self.socket = None
self.domains = {}
def dispatch(self):
"""Dispatch incoming data."""
while True:
(time, obj) = pickle.load(self.sockfile)
(dom, snap) = obj
time = datetime.datetime.utcnow()
self.domains[dom].receive_snapshot(time, snap)
def exit_control_loop(self):
"""Exit control loop."""
assert self.socket != None
self.exit_loop = True
self.sockfile.close()
self.socket.shutdown(socket.SHUT_RDWR)
def control_loop(self):
"""Main control loop."""
self.exit_loop = False
while not self.exit_loop:
try:
self.connect()
except Exception, e:
print e
traceback.print_exc()
self.socket = None
time.sleep(1)
continue
try:
self.set_up_socket()
except Exception, e:
print e
traceback.print_exc()
self.sockfile.close()
self.socket.shutdown(socket.SHUT_RDWR)
self.socket = None
continue
self.send_event(HOST_CONNECTED)
try:
self.dispatch()
except Exception, e:
print e
traceback.print_exc()
self.disconnect()
self.send_event(HOST_DISCONNECTED)
def send_command(self, command):
"""Send a command."""
assert self.sockfile != None
pickled = pickle.dump(command, self.sockfile, pickle.HIGHEST_PROTOCOL)
self.sockfile.flush()
def send_event(self, event_type, affected_object = None):
"""Notify listeners of an event."""
if affected_object == None:
affected_object = self
for listener in self.event_listeners:
try:
listener.pwrkap_event(event_type, affected_object)
except Exception, e:
traceback.print_exc()
print e
class pwrkap_client_domain:
"""Abstract base class for power cap client domains."""
def receive_snapshot(self, time, snap):
"""Receive a snapshot."""
pass
def send_command(self, command):
"""Send a command."""
pass
def set_cap(self, new_cap):
"""Set a power cap."""
pass
def set_max_sample_age(self, new_max_age):
"""Set the maximum sample age."""
pass
def get_cap(self):
"""Retrieve this domain's power cap."""
pass
def get_power(self):
"""Retrieve this domain's power usage."""
pass
def get_energy(self):
"""Retrieve this domain's energy usage."""
pass
def get_utilization_details(self):
"""Retrieve this domain's utilization data."""
pass
def get_max_sample_age(self):
"""Retrieve this domain's maximum record age."""
pass
def is_affected_by(self, other):
"""Return true if a change to the other domain affects this one."""
pass
def get_historical_data_as_lists(self, since = None):
"""Retrieve sample data as a series of lists."""
pass
def get_name(self):
"""Create name."""
pass
class pwrkap_aggregate_domain(pwrkap_client_domain):
"""Aggregate of multiple power domains."""
def __init__(self, domains, name):
"""Create an aggregate domain of domains."""
self.domains = domains
self.historical_data = historical_data(DEFAULT_MAX_AGE)
self.domain_profiles = {}
for dom in self.domains:
self.domain_profiles[dom] = None
self.last_aggregation = None
self.aggregation_lock = threading.Lock()
self.name = name
def get_name(self):
"""Create name."""
return self.name
def unfilled_domain_profiles(self):
"""Determine if we can use caching mechanism."""
for dom in self.domains:
if self.domain_profiles[dom] == None:
return True
return False
def aggregate_snapshots(self, since):
"""Aggregate snapshots together."""
self.aggregation_lock.acquire()
self.__aggregate_snapshots_a(since)
self.aggregation_lock.release()
def __aggregate_snapshots_a(self, since):
"""Aggregate snapshots together (no locking)."""
def find_next_sample(data):
"""Find the next sample to process."""
lowest_timestamp = None
domain = None
for dom in domain_data_keys:
x = data[dom]
if x[0] >= len(x[1]):
continue
if lowest_timestamp == None or x[1][x[0]] < lowest_timestamp:
lowest_timestamp = x[1][x[0]]
domain = dom
if domain == None:
return None
dom_info = data[domain]
cursor = dom_info[0]
dom_info[0] = cursor + 1
# Pull out the utilization data
ud = {}
for dev in dom_info[5].keys():
ud[dev] = dom_info[5][dev][cursor]
# return domain, timestamp, cap, power, energy, utilization
return (domain, lowest_timestamp, dom_info[2][cursor], \
dom_info[3][cursor], dom_info[4][cursor], ud)
# Figure out if we need a full scan or if we can re-use
# previously calculated data
last_time = self.last_aggregation
if last_time == None or self.unfilled_domain_profiles():
self.historical_data.clear()
last_time = since
# Prepare our lists of items
domain_data = {}
to_read = 0
ztime = datetime.datetime.utcnow()
for dom in self.domains:
x = dom.get_historical_data_as_lists(last_time)
if x == None:
continue
(t, c, p, e, ud) = x
to_read = to_read + len(t)
domain_data[dom] = [0, t, c, p, e, ud]
print "Ugh, I have %d samples to read." % to_read
# Process all samples in order
atime = datetime.datetime.utcnow()
print ("listmake time", (atime-ztime))
doms = self.domain_profiles.keys()
domain_data_keys = domain_data.keys()
sample = find_next_sample(domain_data)
while sample != None:
# Update the domain's profile
d = sample[0]
xtime = sample[1]
self.domain_profiles[d] = sample[2:]
sample = find_next_sample(domain_data)
if sample != None:
assert xtime <= sample[1]
# Coalesce all profiles with the same timestamp.
xtime = xtime - (xtime % 15)
if sample != None:
new_xtime = sample[1] - (sample[1] % 15)
if new_xtime == xtime:
continue
# Otherwise, snapshot the world.
cap = 0
power = 0
energy = 0
util_details = {}
num_doms = len(doms)
if num_doms == 0:
continue
for dom in doms:
res = self.domain_profiles[dom]
if res == None:
num_doms = num_doms - 1
continue
(c, p, e, ud) = res
cap = cap + c
power = power + p
if e != None:
energy = energy + e
util_details.update(ud)
utilization = pwrkap_data.average_utilization(util_details)
utctime = datetime.datetime.fromtimestamp(xtime)
self.historical_data.store(utctime, cap, power, \
energy, util_details)
btime = datetime.datetime.utcnow()
print ("aggloop", (btime-atime))
self.last_aggregation = datetime.datetime.utcnow()
return
def receive_snapshot(self, time, snap):
"""Receive a snapshot."""
assert False
def send_command(self, command):
"""Send a command."""
assert False
def set_cap(self, new_cap):
"""Set a power cap."""
# Figure out old caps so we can redistribute proportionally
old_cap = 0
missing_domains = False
for dom in self.domains:
c = dom.get_cap()
if c == None:
missing_domains = True
old_cap = old_cap + c
if missing_domains:
print "BOO! Some domain is missing a cap!"
return
# Now reallocate cap
for dom in self.domains:
dom.set_cap(new_cap * dom.get_cap() / old_cap)
def set_max_sample_age(self, new_max_age):
"""Set the maximum sample age."""
for dom in self.domains:
dom.set_max_sample_age(new_max_age)
def get_cap(self):
"""Retrieve this domain's power cap."""
cap = 0
for dom in self.domains:
c = dom.get_cap()
if c != None:
cap = cap + c
return cap
def get_power(self):
"""Retrieve this domain's power usage."""
power = 0
for dom in self.domains:
p = dom.get_power()
if p != None:
power = power + p
return power
def get_energy(self):
"""Retrieve this domain's energy usage."""
energy = 0
for dom in self.domains:
e = dom.get_energy()
if e != None:
energy = energy + e
return energy
def get_utilization_details(self):
"""Retrieve this domain's utilization."""
ud = {}
for dom in self.domains:
ud.update(dom.get_utilization_details())
return ud
def get_max_sample_age(self):
"""Retrieve this domain's maximum record age."""
if len(self.domains) == 0:
return None
return self.domains[0].get_max_sample_age()
def is_affected_by(self, other):
"""Return true if a change to the other domain affects this one."""
if other in self.domains:
return True
return False
def get_historical_data_as_lists(self, since = None):
"""Retrieve sample data as a series of lists."""
if len(self.domains) == 1:
return self.domains[0].get_historical_data_as_lists(since)
if since == None:
max_age = self.get_max_sample_age()
td = datetime.timedelta(0, max_age)
since = (datetime.datetime.utcnow() - td)
self.aggregate_snapshots(since)
return self.historical_data.retrieve_as_lists(since)
class pwrkap_host_domain(pwrkap_client_domain):
"""Represents a power domain on a pwrkap server."""
def __init__(self, pwrkap_host, port, name):
"""Create a power domain."""
global DEFAULT_MAX_AGE
self.historical_data = historical_data(DEFAULT_MAX_AGE)
self.name = name
self.host = pwrkap_host
self.port = port
self.cap = None
self.power = None
self.energy = None
self.utilization_details = None
self.last_event_trigger = datetime.datetime.utcnow()
def get_name(self):
"""Create name."""
return self.name
def receive_snapshot(self, time, snap):
"""Receive a snapshot."""
if snap.has_key("energy"):
energy = snap["energy"]
else:
energy = None
if snap.has_key("util_details"):
ud = snap["util_details"]
else:
ud = {"dev": 100.0 * snap["utilization"]}
dst_ud = {}
for dev in ud:
dst_ud["%s:%s:%s:%s" % (self.host.host, self.port, self.name, dev)] = 100.0 * ud[dev]
self.historical_data.store(time, snap["cap"], snap["power"], \
energy, dst_ud)
self.cap = snap["cap"]
self.power = snap["power"]
self.energy = energy
self.utilization_details = dst_ud
self.host.send_event(HOST_DOMAIN_DATA_RECEIVED, self)
def get_historical_data_as_lists(self, since = None):
"""Retrieve sample data as a series of lists."""
return self.historical_data.retrieve_as_lists(since)
def send_command(self, command):
"""Send a command."""
cmd = [self.name]
for x in command:
cmd.append(x)
self.host.send_command(cmd)
def set_cap(self, new_cap):
"""Set a power cap."""
self.cap = new_cap
self.send_command(["cap", "%d" % new_cap])
def set_max_sample_age(self, new_max_age):
"""Set the maximum age that snapshots are kept."""
self.historical_data.set_max_sample_age(new_max_age)
def fudge_data(self):
"""Set initial values of cached data to the last snapshot."""
res = self.historical_data.get_most_recent()
if res == None:
return
(time, self.cap, self.power, self.energy, self.utilization_details) = res
def get_cap(self):
"""Retrieve this domain's power cap."""
return self.cap
def get_power(self):
"""Retrieve this domain's power usage."""
return self.power
def get_energy(self):
"""Retrieve this domain's energy usage."""
return self.energy
def get_utilization_details(self):
"""Return utilization details."""
return self.utilization_details
def get_max_sample_age(self):
"""Retrieve this domain's maximum record age."""
return self.historical_data.get_max_sample_age()
def is_affected_by(self, other):
"""Return true if a change to the other domain affects this one."""
if other == self:
return True
return False
pwrkap-7.30/pwrkap/util.py 0000664 0000000 0000000 00000001665 11144346405 0015614 0 ustar 00root root 0000000 0000000 #!/usr/bin/python
"""Helper routines."""
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
import datetime
def read_file_as_array(filename, delim = None):
"""Return the contents of a file as an array of arrays."""
lines = []
fp = None
try:
fp = open(filename)
except:
return None
try:
try:
for line in fp:
lines.append(line.split(delim))
except:
return None
return lines
finally:
fp.close()
def read_line_as_array(filename):
"""Return the first line of a file as an array."""
x = read_file_as_array(filename)
if x == None:
return None
return x[0]
def write_file(filename, data):
"""Write some data to a sysfs file."""
fp = None
try:
fp = open(filename, "w")
except:
return False
try:
try:
fp.write(data)
except:
return False
return True
finally:
fp.close()
def cmp_string_as_number(xs, ys):
"""Compare two strings numerically."""
x = int(xs)
y = int(ys)
return x - y
pwrkap-7.30/redhat/ 0000775 0000000 0000000 00000000000 11144346405 0014220 5 ustar 00root root 0000000 0000000 pwrkap-7.30/redhat/init-script 0000775 0000000 0000000 00000002411 11144346405 0016411 0 ustar 00root root 0000000 0000000 #!/bin/bash
#
# pwrkap This shell script takes care of starting and stopping
# pwrkap
#
# chkconfig: - 20 70
# description: pwrkap monitors energy use and controls power use.
# processname: pwrkap_main.py
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
### BEGIN INIT INFO
# Provides: pwrkap
# Required-Start:
# Should-Start:
# Required-Stop:
# Default-Start: 3 5
# Default-Stop:
# Description: start pwrkap
### END INIT INFO
# Source function library.
[ -e /etc/init.d/functions ] && . /etc/init.d/functions #rhel5
[ -e /etc/rc.status ] && . /etc/rc.status #sles10
PATH="/sbin:/bin:/usr/sbin:/usr/bin"
[ -x /usr/bin/pwrkap_main ] || exit 0
case "$1" in
start)
/usr/bin/pwrkap_main
;;
stop)
ps ax | grep -v grep | grep pwrkap_main.py | while read pid junk; do kill $pid; done
;;
restart|force-reload)
$0 stop
$0 start
;;
status)
PWRKAPS=`ps ax | grep -v grep | grep pwrkap_main.py -c`
if [ $PWRKAPS -lt 1 ]; then
echo "pwrkap is not running."
elif [ $PWRKAPS -gt 1 ]; then
echo "Multiple copies of pwrkap are running. Turn some of them off."
else
echo "pwrkap is running."
fi
;;
*)
echo "Usage: $0 {start|stop|restart|force-reload|status}"
exit 1
;;
esac
exit 0
pwrkap-7.30/redhat/pwrkap.spec 0000664 0000000 0000000 00000002620 11144346405 0016400 0 ustar 00root root 0000000 0000000 # spec file for package pwrkap
# (C) Copyright IBM Corp. 2008-2009
# Licensed under the GPLv2.
%define is_suse %(test -e /etc/SuSE-release && echo 1 || echo 0)
%define is_rhel %(test -e /etc/redhat-release && echo 1 || echo 0)
Name: pwrkap
Summary: Energy Monitor and Power Capping software for Linux.
Version: 7.20
Release: 1
License: GPL
Vendor: Darrick J. Wong
BuildArch: noarch
Group: Administration
Source0: %{name}-%version.tar.gz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-build
%if %is_suse
Requires: ipmitool python python-gtk
%endif
%if %is_rhel
Requires: OpenIPMI-tools python pygtk2
%endif
%description
pwrkap is a set of utilities that monitor computer energy
consumption and enforces an upper limit on the amount of power consumed
by the computer at any given time.
%prep
echo Building %{name}-%{version}-%{release}
%setup -q -n %{name}
%build
make PREFIX=%_prefix all
%install
make INST_PREFIX=%buildroot PREFIX=%_prefix install
mkdir -p %buildroot/etc/init.d/
cp -pRdu redhat/init-script %buildroot/etc/init.d/pwrkap
%clean
make clean
%post
chkconfig pwrkap on
%preun
chkconfig pwrkap off
%files
%defattr(-,root,root)
%dir %_prefix/lib/pwrkap/
%dir %_prefix/share/pwrkap/
/etc/init.d/pwrkap
%_prefix/lib/pwrkap/*
%_prefix/share/pwrkap/*
%_prefix/bin/pwrkap_*
%changelog
* Mon May 21 2008 - djwong (at) us.ibm.com
- Initial package creation