diff options
63 files changed, 5711 insertions, 0 deletions
diff --git a/trunk/src/ChangeLog b/trunk/src/ChangeLog new file mode 100644 index 00000000..92947fd9 --- /dev/null +++ b/trunk/src/ChangeLog @@ -0,0 +1,248 @@ +2007-06-15 Navid Sheikhol-Eslami <navid@redhat.com> + + * [BZ#241282] initial port to RHEL4 :) sos core now runs happily, plugins will probably need to be fixed as well. + * Initial commit of XML reporting to gather details about commands executed and files gathered. + * Exceptions in plugin.analyse() were not catched allowing a bad plugin to break sosreport. + +2007-06-15 Eugene Teo <eteo@redhat.com> + + * lib/sos/plugins/apache.py, lib/sos/plugins/nfsserver.py, lib/sos/plugins/selinux.py, lib/sos/plugins/xinetd.py, lib/sos/plugins/ssh.py, lib/sos/plugins/sendmail.py, lib/sos/plugins/samba.py, lib/sos/plugins/named.py, lib/sos/plugins/cluster.py: + - Edited apache.py to gather /var/log/httpd/* log files + - Added nfsserver.py to gather NFS server-related debugging information + - Edited selinux.py to gather /etc/selinux/* configuration files + - Added xinetd.py to gather xinetd-related information + - Added ssh.py to gather ssh-related information + - Added sendmail.py to gather sendmail information + - Edited samba.py to gather /var/log/samba/* log files + - Edited named.py to gather /etc/sysconfig/named + - Edited cluster.py to gather the output of fdisk -l to show the + shared storage devices that should be available to each system + +2007-05-28 Eugene Teo <eteo@redhat.com> + + * lib/sos/plugins/systemtap.py: + - Added systemtap.py to gather SystemTap pre-requisites information + +2007-05-28 Eugene Teo <eteo@redhat.com> + + * lib/sos/plugins/amd.py: + - Added amd.py to gather Amd automounter information + +2007-05-25 Eugene Teo <eteo@redhat.com> + + * lib/sos/plugins/xen.py, lib/sos/plugins/pam.py, lib/sos/plugins/memory.py: + - Edited xen.py to determine if CPU has PAE/Intel VT/AMD-V support + - Edited pam.py to gather configurations in /etc/security, and files + listing of /lib/security/pam_*so + - Edited memory.py to gather /proc/{vmstat,slabinfo}, and free -m + output + +2007-04-23 Navid Sheikhol-Eslami <navid@redhat.com> + + * Running "multipath" without arguments might change the device-mapper maps, which we want to avoid if things are broken. + +2007-04-02 Navid Sheikhol-Eslami <navid@redhat.com> + + * Replaced xen plugin with (better) version from Chris Lalancette <clalance@redhat.com> + +2007-03-29 Navid Sheikhol-Eslami <navid@redhat.com> + + * Added a checkenabled() function which can be used to disable a plugin at run-time. + * Disable the progress-bar if verbosity is enabled. + +2007-03-27 Navid Sheikhol-Eslami <navid@redhat.com> + + * Fixed hardware plugin to use modules.pcimap instead of deprecated pcitable. + * Added a random suffix to sosreport tree to avoid overwriting an existing tree with same name. + * Better logging using python's logging module. + * Verbose logs included in sosreport (sos_logs/sos.log) + +2007-03-15 Navid Sheikhol-Eslami <navid@redhat.com> + + * Implemented a progress bar (RFE BZ#219672) which can be disabled from the command line. + * Added check to see if the loaded module matches the copy on the file-system + +2007-03-14 Navid Sheikhol-Eslami <navid@redhat.com> + + * fixed BZ#219877 (ncurses "Cancel" button makes sosreport exit) + +2007-03-07 Navid Sheikhol-Eslami <navid@redhat.com> + + * Allow passing multiple comma-separated plugin names to -n (--noplugin) and -o (--onlyplugin) options. + * Added further commands' output to gather from lvm_dump + + +2007-02-20 Navid Sheikhol-Eslami <navid@redhat.com> + + * Added a specialized plugin for device-mapper related configuration files and command outputs (device-mapper, LVM and multipath) + * Added --onlyplugin option (-o) to selectively choose which plugins to load (complementary to existing --noplugin) + * Exit if no valid plugin was selected (rather than building an empty sosreport). + +2007-02-16 Navid Sheikhol-Eslami <navid@redhat.com> + + * Strip out the shared secret (bindpw) from /etc/ldap.conf + * Collect parsed configuration tree directly from ccsd (useful for troubleshooting parsing issues) + * Scamble password information for fencing devices. + +2007-01-26 Navid Sheikhol-Eslami <navid@redhat.com> + + * Added doRegexSub() to be called in postproc() to apply a regexp substitution to files + * Added radius plugin for freeradius data collection + * Ask full name to prevent errors when moving the sos tree before packaging + * Reformat tar file name is no ticket number is given + +2006-06-19 Steve Conklin <sconklin@tintin> + + * ChangeLog, LICENSE, setup.py, sos.spec: + Added License file and bumped release + +2006-06-08 dlehman <dlehman@tintin> + + * example_plugins/example.py, example_plugins/fsusage.py, example_plugins/release.py, example_plugins/template.py, lib/sos/helpers.py, lib/sos/plugins/apache.py, lib/sos/plugins/bootloader.py, lib/sos/plugins/cluster.py, lib/sos/plugins/filesys.py, lib/sos/plugins/ftp.py, lib/sos/plugins/general.py, lib/sos/plugins/hardware.py, lib/sos/plugins/kernel.py, lib/sos/plugins/ldap.py, lib/sos/plugins/libraries.py, lib/sos/plugins/mail.py, lib/sos/plugins/memory.py, lib/sos/plugins/named.py, lib/sos/plugins/networking.py, lib/sos/plugins/pam.py, lib/sos/plugins/process.py, lib/sos/plugins/rhn.py, lib/sos/plugins/rpm.py, lib/sos/plugins/samba.py, lib/sos/plugins/selinux.py, lib/sos/plugins/squid.py, lib/sos/plugins/startup.py, lib/sos/plugins/system.py, lib/sos/plugins/tarball.py, lib/sos/plugins/x11.py, lib/sos/plugintools.py, lib/sos/policyredhat.py, setup.py, sosreport: + - Flesh out rhn plugin to handle Proxy or Satellite + - Add package queries to policyredhat.py (allPkgsByName, pkgByName, pkgNVRA) + - Add policy instance to the commons dict for access from plugins + - Use string objects' methods instead of the string module where possible + - Remove imports of unused string module + - Cleanup some typos, redundant initializations, &c + +2006-06-08 dlehman <dlehman@tintin> + + * example_plugins/example.py, example_plugins/fsusage.py, example_plugins/release.py, example_plugins/template.py, lib/sos/helpers.py, lib/sos/plugins/apache.py, lib/sos/plugins/bootloader.py, lib/sos/plugins/cluster.py, lib/sos/plugins/filesys.py, lib/sos/plugins/ftp.py, lib/sos/plugins/general.py, lib/sos/plugins/hardware.py, lib/sos/plugins/kernel.py, lib/sos/plugins/ldap.py, lib/sos/plugins/libraries.py, lib/sos/plugins/mail.py, lib/sos/plugins/memory.py, lib/sos/plugins/named.py, lib/sos/plugins/networking.py, lib/sos/plugins/pam.py, lib/sos/plugins/process.py, lib/sos/plugins/rhn.py, lib/sos/plugins/rpm.py, lib/sos/plugins/samba.py, lib/sos/plugins/selinux.py, lib/sos/plugins/squid.py, lib/sos/plugins/startup.py, lib/sos/plugins/system.py, lib/sos/plugins/tarball.py, lib/sos/plugins/x11.py, lib/sos/plugintools.py, lib/sos/policyredhat.py, setup.py, sosreport: + - Flesh out rhn plugin to handle Proxy or Satellite + - Add package queries to policyredhat.py (allPkgsByName, pkgByName, pkgNVRA) + - Add policy instance to the commons dict for access from plugins + - Use string objects' methods instead of the string module where possible + - Remove imports of unused string module + - Cleanup some typos, redundant initializations, &c + +2006-06-05 jwhiter <jwhiter@tintin> + + * lib/sos/plugins/system.py: + adding the abilit to capture the autofs maps to system.py + +2006-05-31 Steve Conklin <sconklin@tintin> + + * ChangeLog, Makefile: New Makefile and ChangeLog (autogenerated) + + * Changelog, setup.py, sos.spec: + Removed old Changelog file and sync'd version and Changelog in spec file + + * lib/sos/plugins/networking.py, TODO, setup.py, sos.spec: + Final patches and version change before submission to Fedora + +2006-05-31 Steve Conklin <sconklin@tintin> + + * Changelog, setup.py, sos.spec: + Removed old Changelog file and sync'd version and Changelog in spec file + + * lib/sos/plugins/networking.py, TODO, setup.py, sos.spec: + Final patches and version change before submission to Fedora + +2006-05-26 Steve Conklin <sconklin@tintin> + + * Changelog, lib/sos/helpers.py, lib/sos/plugintools.py, setup.py, sosreport: + Added pamadio's curses UI for selecting plugin options + Added flushing stdout after informational messages + +2006-05-26 jwhiter <jwhiter@tintin> + + * lib/sos/plugins/filesys.py: + - making the filesys.py plugin call 'blkid' when running at the request of L1, this will allow us to map labels with actual devices. + + * lib/sos/plugins/tarball.py, lib/sos/plugintools.py, sosreport: + - Adding tarball.py to create a tarball of the report after it is run + - Updated sosreport to call postproc() in all the plugins, which handles the post run + - Added runExeInd() which will just run the exe and return the status to plugintools.py + - Added postproc() to plugintools.py so that plugins can implement it + +2006-05-25 Steve Conklin <sconklin@tintin> + + * lib/sos/plugintools.py, sosreport: + Fixed file naming for commands to eliminate special chars and prevent + name collisions. Added sorting of plugins by name before reporting. + + * TODO, lib/sos/plugins/kernel.py, lib/sos/plugintools.py, setup.py: + Fixed option handling + +2006-05-23 jwhiter <jwhiter@tintin> + + * Changelog, lib/sos/plugins/kernel.py, setup.py: + Adding jwb's patch to have sosreport grab sysrq data. + +2006-05-22 Steve Conklin <sconklin@tintin> + + * lib/sos/plugins/apache.py: oops, forgot this file + + * lib/sos/plugins/bootloader.py, lib/sos/plugins/filesys.py, lib/sos/plugins/ftp.py, lib/sos/plugins/general.py, lib/sos/plugins/hardware.py, lib/sos/plugins/kernel.py, lib/sos/plugins/ldap.py, lib/sos/plugins/mail.py, lib/sos/plugins/memory.py, lib/sos/plugins/named.py, lib/sos/plugins/samba.py, lib/sos/plugins/squid.py, lib/sos/plugins/x11.py, sos.spec: + Patch from jwb + + * lib/sos/plugins/filesys.py, lib/sos/plugins/kernel.py: + jwb's patches for kernel.py and filesys.py + + * sosreport: minor fix to the dir perms patch + + * lib/sos/plugins/bootloader.py, lib/sos/plugins/cluster.py, lib/sos/plugins/filesys.py, lib/sos/plugins/general.py, lib/sos/plugins/hardware.py, lib/sos/plugins/kernel.py, lib/sos/plugins/libraries.py, lib/sos/plugins/memory.py, lib/sos/plugins/networking.py, lib/sos/plugins/pam.py, lib/sos/plugins/process.py, lib/sos/plugins/rhn.py, lib/sos/plugins/rpm.py, lib/sos/plugins/selinux.py, lib/sos/plugins/startup.py, lib/sos/plugins/system.py, lib/sos/plugins/template.py, lib/sos/plugins/x11.py, Changelog, example_plugins/template.py, lib/sos/plugintools.py, setup.py, sosreport: + Merged all of jwb's weekend patches. Make output dirs world readable, check before + executing executables, and added a lot of plugins. + +2006-05-19 Steve Conklin <sconklin@tintin> + + * lib/sos/plugins/template.py: Removed unneeded variabled from plugin. + + * Changelog, TODO, example_plugins/example.py, example_plugins/fsusage.py, example_plugins/release.py, example_plugins/runcommand.py, lib/sos/plugins/template.py, lib/sos/plugintools.py, setup.py, sosreport: + Applied jwb's fix, added his examples, improved html output + +2006-05-18 Steve Conklin <sconklin@tintin> + + * example_plugins/example.py, example_plugins/runcommand.py, lib/sos/plugins/template.py: + Put instance variables in plugins in addition to base class + + * example_plugins/example.py, example_plugins/runcommand.py, lib/sos/plugins/template.py, lib/sos/plugintools.py, setup.py, sosreport: + Removed separate pit class, and put everything having to do with the plugin in + pluginBase. Still incorrectly aggregates data across all plugins. + + * README: Added Jwb as a contributor + + * Changelog, TODO, lib/sos/plugins/template.py, lib/sos/plugintools.py, sosreport: + Implemented a base class for plugins + +2006-05-17 Steve Conklin <sconklin@tintin> + + * Changelog, TODO, lib/sos/helpers.py, lib/sos/plugins/template.py, lib/sos/plugintools.py, sosreport, tests/maketesttree.sh: + Cleaned up code, added comments, fixed dir copying bug, changed option + handling. See Changelog + +2006-05-16 Steve Conklin <sconklin@tintin> + + * lib/sos/helpers.py, lib/sos/plugintools.py: + Missed an edit to change log file descriptor to the dictionary. Fixed. + + * TODO: Added documentation to list + + * TODO: Added need for example plugin + +2006-05-15 Steve Conklin <sconklin@tintin> + + * README, TODO, lib/sos/plugins/template.py, lib/sos/plugintools.py, setup.py, sosreport: + Added a dictionary of things that need to be known to all, like paths for + reports. + + Added Pierre Amadio's command line arg handling + + Fixed incorrect handling of command completion status + + Added html generation + +2006-05-09 Steve Conklin <sconklin@tintin> + + * TODO: changed it + + * setup.cfg: removed RFC file + + * MANIFEST.in, Notes.txt, README, TODO, lib/sos/__init__.py, lib/sos/helpers.py, lib/sos/plugins/__init__.py, lib/sos/plugins/template.py, lib/sos/plugintools.py, lib/sos/policyredhat.py, setup.cfg, setup.py, sosreport: + Initial checkin of the sos project + + * MANIFEST.in, Notes.txt, README, TODO, lib/sos/__init__.py, lib/sos/helpers.py, lib/sos/plugins/__init__.py, lib/sos/plugins/template.py, lib/sos/plugintools.py, lib/sos/policyredhat.py, setup.cfg, setup.py, sosreport: + New file. + diff --git a/trunk/src/LICENSE b/trunk/src/LICENSE new file mode 100644 index 00000000..7a8e8abf --- /dev/null +++ b/trunk/src/LICENSE @@ -0,0 +1,340 @@ + 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 Library 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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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. + + <signature of Ty Coon>, 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. diff --git a/trunk/src/Makefile b/trunk/src/Makefile new file mode 100644 index 00000000..99d2ed5d --- /dev/null +++ b/trunk/src/Makefile @@ -0,0 +1,95 @@ +# +# Makefile for sos system support tools +# + +NAME = sos +VERSION = $(shell awk '/^%define version / { print $$3 }' sos.spec) +REPO = https://sos.108.redhat.com/svn/sos +SVNTAG = r$(subst .,-,$(VERSION)) +SRCDIR = $(PWD) +TOPDIR = $(PWD)/build/rpm-$(NAME)-$(VERSION) + + +all: + +.PHONY: tag-release tarball release install version clean + +diff-tag: + svn diff $(REPO)/trunk/src $(REPO)/tags/$(SVNTAG) + +tag: + @if ( svn list $(REPO)/tags/$(SVNTAG)/Makefile &> /dev/null ); then \ + echo "The repository already contains a tag for version $(VERSION)"; \ + exit 1; \ + fi + @svn copy $(REPO)/trunk/src $(REPO)/tags/$(SVNTAG) \ + -m "Tagging the $(SVNTAG) release of the sos project" + @echo "Tagged as $(SVNTAG)" + +tag-force: + @echo svn del $(REPO)/tags/$(SVNTAG) + @echo make diff-tag + +tarball: clean + @echo "Creating an archive from HEAD of development" + @rm -rf /tmp/$(NAME) + @svn export -q $(REPO)/trunk/src /tmp/$(NAME) \ + || echo GRRRrrrrr -- ignore [export aborted] + @mv /tmp/$(NAME) /tmp/$(NAME)-$(VERSION) + @cd /tmp; tar --bzip2 -cSpf $(NAME)-$(VERSION).tar.bz2 $(NAME)-$(VERSION) + @rm -rf /tmp/$(NAME)-$(VERSION) + @mv /tmp/$(NAME)-$(VERSION).tar.bz2 . + @echo " " + @echo "The final archive is ./$(NAME)-$(VERSION).tar.bz2." + +release: clean + @if ( ! svn list $(REPO)/tags/$(SVNTAG)/Makefile &> /dev/null ); then \ + echo "There is no tag in the repository for this version, must be tagged before release"; \ + exit 1; \ + fi + @echo "Creating an archive from tag $(SVNTAG)" + @rm -rf /tmp/$(NAME) + @svn export -q $(REPO)/tags/$(SVNTAG) /tmp/$(NAME) \ + || echo GRRRrrrrr -- ignore [export aborted] + @mv /tmp/$(NAME) /tmp/$(NAME)-$(VERSION) + @cd /tmp; tar --bzip2 -cSpf $(NAME)-$(VERSION).tar.bz2 $(NAME)-$(VERSION) + @rm -rf /tmp/$(NAME)-$(VERSION) + @cp /tmp/$(NAME)-$(VERSION).tar.bz2 . + @rm -f /tmp/$(NAME)-$(VERSION).tar.bz2 + @echo " " + @echo "The final archive is ./$(NAME)-$(VERSION).tar.bz2." + +install:mo + python setup.py install + +version: + @echo "The version is $(NAME)-$(VERSION)" + +clean: + @rm -fv *~ .*~ changenew ChangeLog.old $(NAME)-$(VERSION).tar.bz2 sosreport.1.gz + +rpm: mo + @test -f sos.spec + @mkdir -p $(TOPDIR)/SOURCES $(TOPDIR)/SRPMS $(TOPDIR)/RPMS $(TOPDIR)/BUILD + +# this builds an RPM from the current working copy + @cd $(TOPDIR)/BUILD ; \ + rm -rf $(NAME)-$(VERSION) ; \ + ln -s $(SRCDIR) $(NAME)-$(VERSION) ; \ + tar --gzip --exclude=.svn --exclude=svn-commit.tmp --exclude=$(NAME)-$(VERSION)/build --exclude=$(NAME)-$(VERSION)/dist \ + -chSpf $(TOPDIR)/SOURCES/$(NAME)-$(VERSION).tar.gz $(NAME)-$(VERSION) ; \ + rm -f $(NAME)-$(VERSION) + +# this builds an RPM from HEAD +# @rm -rf $(TOPDIR)/BUILD/$(NAME)-$(VERSION) +# @svn export -q $(REPO)/trunk/src $(TOPDIR)/BUILD/$(NAME)-$(VERSION) +# @cd $(TOPDIR)/BUILD ; tar --gzip -cSpf $(TOPDIR)/SOURCES/$(NAME)-$(VERSION).tar.gz $(NAME)-$(VERSION); rm -rf $(NAME)-$(VERSION) + + rpmbuild -ba --define="_topdir $(TOPDIR)" sos.spec + @mv $(TOPDIR)/RPMS/noarch/$(NAME)-$(VERSION)*.rpm $(TOPDIR)/SRPMS/$(NAME)-$(VERSION)*.rpm $(TOPDIR)/SOURCES/$(NAME)-$(VERSION).tar.gz dist/ + +pot: + python tools/pygettext.py -o locale/sos.pot sosreport + +mo: + python tools/msgfmt.py locale/*/LC_MESSAGES/sos.po diff --git a/trunk/src/README b/trunk/src/README new file mode 100644 index 00000000..b65b07ed --- /dev/null +++ b/trunk/src/README @@ -0,0 +1,24 @@ +This set of tools is designed to provide information to support +organizations in an extensible manner, allowing third parties, +package maintainers, and anyone else to provide plugins that will +collect, analyze, and report information that is useful for supporting +software packages. + +This project is hosted at http://sos.108.redhat.com. For the latest version, +to contribute, and for more information, please visit there. + +To access to the public source code repository for this project run: + + svn checkout https://sos.108.redhat.com/svn/sos/trunk sos + +(all the following as root) +to install locally ==> python setup.py install +to build an rpm ==> make rpm + +See the Makefile. + +Contributors: +Steve Conklin <sconklin@redhat.com> +Pierre Amadio <pamadio@redhat.com> +John Berninger <jwb@redhat.com> +Navid Sheikhol-Eslami <navid@redhat.com> diff --git a/trunk/src/TODO b/trunk/src/TODO new file mode 100644 index 00000000..2f2d323f --- /dev/null +++ b/trunk/src/TODO @@ -0,0 +1,21 @@ +To Do List: + + * Gather statistics (time, files collected, commands run) per plugin + + * Choose sane defaults to satisfy Red Hat support requirements (ie. what + plugins should be loaded by default, with what options, etc) + + * Display on screen what files/commands are being gathered. + + * Make sosreport a drop-in replacement for sysreport + + * Allow to use a different rootdir than / + + * Add support for distributions other than Fedora. + + * Make it easier to select a policy module and perhaps optionally include + plugins from the library, to allow per-distribution customization. + + * Documentation - plugin howto doc, inline doc in plugin template + + * Review and test error handling for things like a full tmp file system diff --git a/trunk/src/example_plugins/example.py b/trunk/src/example_plugins/example.py new file mode 100755 index 00000000..1e650417 --- /dev/null +++ b/trunk/src/example_plugins/example.py @@ -0,0 +1,94 @@ +## example.py +## An example sos plugin + +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +# Class name must be the same as file name and method names must not change +class example(sos.plugintools.PluginBase): + """This is an example plugin for sos. Plugins gather, analyze, and report on various aspects + of system operation that are of interest. plugins are based on the PluginBase class, which + you should inspect if you wish to override any methods in your plugin. The methods of use + to plugin developers are: + collect() - use the functions sosCp and sosRunExe to gether information + analyze() - perform any special analysis you require beyond just saving the + information gathered by collect(). Use sosAlert() and sosAddCustomText() + to add information to the report. + report() - override this method if you wish to replace the default reporting + + All plugins will use collect(), some will use analyze(), few will override report() + """ + + # Add your options here, indicate whether they are slow to run, and set whether they are enabled by default + # Options are python dictionaries that conatin a short name, long description, speed, and whether they are enabled by default + optionList = [("init.d", 'Gathers the init.d directory', 'slow', 0), + ('follicles', 'Gathers information about each follicle on every toe', 'slow', 0), + ('color', 'Gathers toenail polish color', 'fast', 0)] + + def setup(self): + ''' First phase - Collect all the information we need. + Directories are copied recursively. arbitrary commands may be + executed using the susRunExe method. Information is automatically saved, and + links are presented in the report to each file or directory which has been + copied to the saved tree. Also, links are provided to the output from each command. + ''' + # Here's how to copy files and directory trees + self.addCopySpec("/etc/hosts") + # this one saves a file path to the copy for later analysis + # FIXME: Need to figure out how to do this + # self.fooFilePath = self.copyFileOrDir("/proc/cpuinfo") + + # Here's how to test your options and execute if enabled + if self.isOptionEnabled("init.d"): + self.addCopySpec("/etc/init.d") # copies a whole directory tree + + # Here's how to execute a command + # you can save the path to the copied file for later analysis if desired + # FIXME: Need to figure out how to do this + self.psCmdDstFileName = self.runExe("/bin/ps -ef") + return + + def analyze(self): + ''' This is optional and need not be defined. + If you wish to perform some analysis on either files + that were gathered or on the output of commands, then save the filenames on the + destination file system when gathering that information in the collect() method + and use them here + ''' + # This is an example of opening and reading the output of a command that + # was run in the collect() method. Note that the output of the command is + # included in the report anyway + fd = open(self.fooFilePath) + lines = fd.readlines() + fd.close() + for line in lines: + if line.count("vendor_id"): + self.addCustomText("Vendor ID string is: %s <br>\n" % line) + # + # Alerts can optionally be generated, and will be included in the + # report automatically + # + self.addAlert("This is an alert") + return + +# def report(self): +# """ Usually, this doesn't even need to be defined, and you can inherit the +# base class, unless you want to replace what sosGetResults() +# does with your own custom report generator. If you are going to do that, have a good +# look at that method to see what it does. Custom text and alerts can still be added +# here using sosAddCustomText() and sosAddAlert() +# This method returns html that will be included inline in the report +# """ diff --git a/trunk/src/example_plugins/fsusage.py b/trunk/src/example_plugins/fsusage.py new file mode 100644 index 00000000..2532c5f7 --- /dev/null +++ b/trunk/src/example_plugins/fsusage.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +# Class name must be the same as file name and method names must not change +class fsusage(sos.plugintools.PluginBase): + def setup(self): + self.psCmdDstFileName = self.collectExtOutput("/bin/df -al") + return diff --git a/trunk/src/example_plugins/release.py b/trunk/src/example_plugins/release.py new file mode 100644 index 00000000..db4d7581 --- /dev/null +++ b/trunk/src/example_plugins/release.py @@ -0,0 +1,21 @@ +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +# Class name must be the same as file name and method names must not change +class release(sos.plugintools.PluginBase): + def setup(self): + self.addCopySpec("/etc/redhat-release") + return diff --git a/trunk/src/example_plugins/runcommand.py b/trunk/src/example_plugins/runcommand.py new file mode 100755 index 00000000..df8951d2 --- /dev/null +++ b/trunk/src/example_plugins/runcommand.py @@ -0,0 +1,45 @@ +## runcommand.py +## An example plugin for sos + +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +# Class name must be the same as file name and method names must not change +class runcommand(sos.plugintools.PluginBase): + """This is a very simple example plugin that just runs one command in a shell. That + command could be a script supplied with your package which outputs information of interest + to support. When the script or command is run, stdout is collected and included in the + report generated by sosreport, and stderr is collected and copied into the sos log. + + This is useful for people who have minimal knowledge of python, who wish to write + collection tools in other languages, or who have existing tools. + + If your script or command generates output files that you want included in the sosreport + collection of files, include sosCp calls to include them. + + The only method required for this simple plugin is collect(). + + Your finished plugin should be included with your package and installed in python's + site-packages/sos/plugins/ directory + """ + def setup(self): + ''' Run a command. Output is automatically included in the report. + ''' + self.collectExtOutput("/path/to/my/script --myoption --anotheroption") + + # if (for example) that command created files /foo/bar/baz.txt and + # we want to include that in our report, we include the next line: + #self.pit.sosCp("/foo/bar/baz.txt") diff --git a/trunk/src/lib/sos/__init__.py b/trunk/src/lib/sos/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/trunk/src/lib/sos/__init__.py diff --git a/trunk/src/lib/sos/helpers.py b/trunk/src/lib/sos/helpers.py new file mode 100755 index 00000000..54a86dba --- /dev/null +++ b/trunk/src/lib/sos/helpers.py @@ -0,0 +1,120 @@ +## helpers.py +## Implement policies required for the sos system support tool + +## Copyright (C) 2006 Steve Conklin <sconklin@redhat.com> + +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +## Some code adapted from "Python Cookbook, 2nd ed", by Alex +## Martelli, Anna Martelli Ravenscroft, and David Ascher +## (O'Reilly Media, 2005) 0-596-00797-3 +## + +""" +helper functions used by sosreport and plugins +""" +import os, popen2, fcntl, select, itertools, sys, commands +from time import time +from tempfile import mkdtemp + +def importPlugin(pluginname, name): + """ Import a plugin to extend capabilities of sosreport + """ + try: + plugin = __import__(pluginname, globals(), locals(), [name]) + except ImportError: + return None + return getattr(plugin, name) + + +def sosFindTmpDir(): + """Find a temp directory to form the root for our gathered information + and reports. + """ + workingBase = mkdtemp("","sos_") + return workingBase + + +def makeNonBlocking(afd): + """ Make the file descriptor non-blocking. This prevents deadlocks. + """ + fl = fcntl.fcntl(afd, fcntl.F_GETFL) + try: + fcntl.fcntl(afd, fcntl.F_SETFL, fl | os.O_NDELAY) + except AttributeError: + fcntl.fcntl(afd, fcntl.F_SETFL, fl | os.FNDELAY) + + +def sosGetCommandOutput(command): + """ Execute a command and gather stdin, stdout, and return status. + Adapted from Python Cookbook - O'Reilly + """ + stime = time() + errdata = '' + status,outdata=commands.getstatusoutput(command) + return (status, outdata, time()-stime) + + +# FIXME: this needs to be made clean and moved to the plugin tools, so +# that it prints nice color output like sysreport if the progress bar +# is not enabled. +def sosStatus(stat): + """ Complete a status line that has been output to the console, + providing pass/fail indication. + """ + if not stat: + print " [ OK ]" + else: + print " [ FAILED ]" + sys.stdout.flush() + return + + +def allEqual(elements): + ''' return True if all the elements are equal, otherwise False. ''' + first_element = elements[0] + for other_element in elements[1:]: + if other_element != first_element: + return False + return True + + +def commonPrefix(*sequences): + ''' return a list of common elements at the start of all sequences, + then a list of lists that are the unique tails of each sequence. ''' + # if there are no sequences at all, we're done + if not sequences: + return [], [] + # loop in parallel on the sequences + common = [] + for elements in itertools.izip(*sequences): + # unless all elements are equal, bail out of the loop + if not allEqual(elements): + break + # got one more common element, append it and keep looping + common.append(elements[0]) + # return the common prefix and unique tails + return common, [ sequence[len(common):] for sequence in sequences ] + +def sosRelPath(path1, path2, sep=os.path.sep, pardir=os.path.pardir): + ''' return a relative path from path1 equivalent to path path2. + In particular: the empty string, if path1 == path2; + path2, if path1 and path2 have no common prefix. + ''' + common, (u1, u2) = commonPrefix(path1.split(sep), path2.split(sep)) + if not common: + return path2 # leave path absolute if nothing at all in common + return sep.join( [pardir]*len(u1) + u2 ) + diff --git a/trunk/src/lib/sos/plugins/__init__.py b/trunk/src/lib/sos/plugins/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/trunk/src/lib/sos/plugins/__init__.py diff --git a/trunk/src/lib/sos/plugins/amd.py b/trunk/src/lib/sos/plugins/amd.py new file mode 100644 index 00000000..1b1375cc --- /dev/null +++ b/trunk/src/lib/sos/plugins/amd.py @@ -0,0 +1,31 @@ +## Copyright (C) 2007 Red Hat, Inc., Eugene Teo <eteo@redhat.com> + +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class amd(sos.plugintools.PluginBase): + """Amd automounter information + """ + def setup(self): + self.addCopySpec("/etc/amd.*") + self.addCopySpec("/etc/rc.d/init.d/amd") + self.addCopySpec("/etc/sysconfig/amd") + self.collectExtOutput("/bin/rpm -qV am-utils") + self.collectExtOutput("/bin/egrep -e 'automount|pid.*nfs' /proc/mounts") + self.collectExtOutput("/bin/mount | egrep -e 'automount|pid.*nfs'") + self.collectExtOutput("/sbin/chkconfig --list amd") + return + diff --git a/trunk/src/lib/sos/plugins/apache.py b/trunk/src/lib/sos/plugins/apache.py new file mode 100644 index 00000000..c5250fd9 --- /dev/null +++ b/trunk/src/lib/sos/plugins/apache.py @@ -0,0 +1,26 @@ +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools +from threading import Thread + +class apache(sos.plugintools.PluginBase): + """Apache related information + """ + def setup(self): + self.addCopySpec("/etc/httpd/conf/httpd.conf") + self.addCopySpec("/etc/httpd/conf.d/*.conf") + self.addCopySpec("/var/log/httpd/*") + return + diff --git a/trunk/src/lib/sos/plugins/bootloader.py b/trunk/src/lib/sos/plugins/bootloader.py new file mode 100644 index 00000000..a820069e --- /dev/null +++ b/trunk/src/lib/sos/plugins/bootloader.py @@ -0,0 +1,31 @@ +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class bootloader(sos.plugintools.PluginBase): + """Bootloader information + """ + def setup(self): + self.addCopySpec("/etc/lilo.conf") + self.addCopySpec("/etc/milo.conf") + self.addCopySpec("/etc/silo.conf") + self.addCopySpec("/boot/grub/grub.conf") + self.addCopySpec("/boot/grub/device.map") + self.addCopySpec("/boot/efi/elilo.conf") + self.addCopySpec("/boot/yaboot.conf") + + self.collectExtOutput("/sbin/lilo -q") + return + diff --git a/trunk/src/lib/sos/plugins/cluster.py b/trunk/src/lib/sos/plugins/cluster.py new file mode 100644 index 00000000..595d940a --- /dev/null +++ b/trunk/src/lib/sos/plugins/cluster.py @@ -0,0 +1,78 @@ +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools +import commands, os + +class cluster(sos.plugintools.PluginBase): + """cluster suite and GFS related information + """ + def checkenabled(self): + # enable if any related package is installed + for pkg in [ "ccs", "cman", "cman-kernel", "magma", "magma-plugins", + "rgmanager", "fence", "dlm", "dlm-kernel", "gulm", + "GFS", "GFS-kernel", "lvm2-cluster" ]: + if self.cInfo["policy"].pkgByName(pkg) != None: + return True + + # enable if any related file is present + for fname in [ "/etc/cluster/cluster.conf" ]: + try: os.stat(fname) + except:pass + else: return True + + # no data related to RHCS/GFS exists + return False + + def diagnose(self): + rhelver = self.cInfo["policy"].pkgDictByName("fedora-release")[0] + if rhelver == "6": + # check if the minimum set of packages is installed + # for RHEL4 RHCS(ccs, cman, cman-kernel, magma, magma-plugins, (dlm, dlm-kernel) || gulm, perl-Net-Telnet, rgmanager, fence) + # RHEL4 GFS (GFS, GFS-kernel, ccs, lvm2-cluster, fence) + for pkg in [ "ccs", "cman", "cman-kernel", "magma", "magma-plugins", "perl-Net-Telnet", "rgmanager", "fence" ]: + if self.cInfo["policy"].pkgByName(pkg) == None: + self.addDiagnose("required package is missing: %s" % pkg) + + # check if all the needed daemons are active at sosreport time + # check if they are started at boot time in RHEL4 RHCS (cman, ccsd, rgmanager, fenced) + # and GFS (gfs, ccsd, clvmd, fenced) + for service in [ "cman", "ccsd", "rgmanager", "fence" ]: + if commands.getstatus("/sbin/service %s status" % service): + self.addDiagnose("service %s is not running" % service) + + if not self.cInfo["policy"].runlevelDefault() in self.cInfo["policy"].runlevelByService(service): + self.addDiagnose("service %s is not started in default runlevel" % service) + + # FIXME: what locking are we using ? check if packages exist +# if self.cInfo["policy"].pkgByName(pkg) and self.cInfo["policy"].pkgByName(pkg) and not self.cInfo["policy"].pkgByName(pkg) + + def setup(self): + self.collectExtOutput("/sbin/fdisk -l") + self.addCopySpec("/etc/cluster.conf") + self.addCopySpec("/etc/cluster.xml") + self.addCopySpec("/etc/cluster") + self.collectExtOutput("/usr/sbin/rg_test test /etc/cluster/cluster.conf") + self.addCopySpec("/proc/cluster") + self.collectExtOutput("/usr/bin/cman_tool status") + self.collectExtOutput("/usr/bin/cman_tool services") + self.collectExtOutput("/usr/bin/cman_tool -af nodes") + self.collectExtOutput("/usr/bin/ccs_tool lsnode") + self.collectExtOutput("/usr/bin/openais-cfgtool -s") + self.collectExtOutput("/usr/bin/clustat") + return + + def postproc(self): + self.doRegexSub("/etc/cluster/cluster.conf", r"(\s*\<fencedevice\s*.*\s*passwd\s*=\s*)\S+(\")", r"\1***") + return diff --git a/trunk/src/lib/sos/plugins/devicemapper.py b/trunk/src/lib/sos/plugins/devicemapper.py new file mode 100644 index 00000000..e517aa3d --- /dev/null +++ b/trunk/src/lib/sos/plugins/devicemapper.py @@ -0,0 +1,38 @@ +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class devicemapper(sos.plugintools.PluginBase): + """device-mapper related information (dm, lvm, multipath) + """ + def setup(self): + self.collectExtOutput("/sbin/dmsetup info -c") + self.collectExtOutput("/sbin/dmsetup table") + self.collectExtOutput("/sbin/dmsetup status") + + self.collectExtOutput("/usr/sbin/vgscan -vvv") + self.collectExtOutput("/usr/sbin/vgdisplay -vv", root_symlink = "vgdisplay") + self.collectExtOutput("/usr/sbin/pvscan -v") + self.collectExtOutput("/usr/sbin/lvs -a -o +devices") + self.collectExtOutput("/usr/sbin/pvs -a -v") + self.collectExtOutput("/usr/sbin/vgs -v") + + self.addCopySpec("/etc/lvm/lvm.conf") + + self.addCopySpec("/etc/multipath.conf") + self.addCopySpec("/var/lib/multipath/bindings") + self.collectExtOutput("/sbin/multipath -v4 -ll") + + return diff --git a/trunk/src/lib/sos/plugins/filesys.py b/trunk/src/lib/sos/plugins/filesys.py new file mode 100644 index 00000000..02bde2eb --- /dev/null +++ b/trunk/src/lib/sos/plugins/filesys.py @@ -0,0 +1,47 @@ +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools +import commands + +class filesys(sos.plugintools.PluginBase): + """information on filesystems + """ + def setup(self): + self.addCopySpec("/proc/filesystems") + self.addCopySpec("/etc/fstab") + self.addCopySpec("/proc/self/mounts") + self.addCopySpec("/proc/mounts") + self.addCopySpec("/proc/mdstat") + self.addCopySpec("/etc/raidtab") + self.addCopySpec("/etc/mdadm.conf") + self.addCopySpec("/etc/auto.master") + self.addCopySpec("/etc/auto.misc") + self.addCopySpec("/etc/auto.net") + + self.collectExtOutput("/bin/df -al", root_symlink = "df") + self.collectExtOutput("/usr/sbin/lsof -b +M -n -l", root_symlink = "lsof") + self.collectExtOutput("/bin/mount -l", root_symlink = "mount") + self.collectExtOutput("/sbin/blkid") + + raiddevs = commands.getoutput("/bin/cat /proc/partitions | /bin/egrep -v \"^major|^$\" | /bin/awk '{print $4}' | /bin/grep \/ | /bin/egrep -v \"p[0123456789]$\"") + disks = commands.getoutput("/bin/cat /proc/partitions | /bin/egrep -v \"^major|^$\" | /bin/awk '{print $4}' | /bin/grep -v / | /bin/egrep -v \"[0123456789]$\"") + for disk in raiddevs.split('\n'): + if '' != disk.strip(): + self.collectExtOutput("/sbin/fdisk -l /dev/%s" % (disk,)) + for disk in disks.split('\n'): + if '' != disk.strip(): + self.collectExtOutput("/sbin/fdisk -l /dev/%s" % (disk,)) + return + diff --git a/trunk/src/lib/sos/plugins/ftp.py b/trunk/src/lib/sos/plugins/ftp.py new file mode 100644 index 00000000..026954c0 --- /dev/null +++ b/trunk/src/lib/sos/plugins/ftp.py @@ -0,0 +1,24 @@ +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class ftp(sos.plugintools.PluginBase): + """FTP server related information + """ + def setup(self): + self.addCopySpec("/etc/ftp*") + self.addCopySpec("/etc/vsftpd") + return + diff --git a/trunk/src/lib/sos/plugins/general.py b/trunk/src/lib/sos/plugins/general.py new file mode 100644 index 00000000..06f55228 --- /dev/null +++ b/trunk/src/lib/sos/plugins/general.py @@ -0,0 +1,43 @@ +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools +import glob + +class general(sos.plugintools.PluginBase): + """very basic system information + """ + + optionList = [("syslogsize", "maximum size (in MiB) of logs to collect per syslog file", "", 15)] + + def setup(self): + self.addCopySpec("/etc/redhat-release") + self.addCopySpec("/etc/sysconfig") + self.addCopySpec("/proc/stat") + self.addCopySpec("/var/log/dmesg") + self.addCopySpec("/var/log/messages") + self.addCopySpecLimit("/var/log/messages.*", sizelimit = self.isOptionEnabled("syslogsize")) + self.addCopySpec("/var/log/secure") + self.addCopySpecLimit("/var/log/secure.*", sizelimit = self.isOptionEnabled("syslogsize")) + self.addCopySpec("/var/log/sa") + self.addCopySpec("/var/log/up2date") + self.addCopySpec("/etc/exports") + self.collectExtOutput("/bin/hostname", root_symlink = "hostname") + self.collectExtOutput("/bin/date", root_symlink = "date") + self.collectExtOutput("/usr/bin/uptime", root_symlink = "uptime") + return + + def postproc(self): + self.doRegexSub("/etc/sysconfig/rhn/up2date", r"(\s*proxyPassword\s*=\s*)\S+", r"\1***") + return diff --git a/trunk/src/lib/sos/plugins/hardware.py b/trunk/src/lib/sos/plugins/hardware.py new file mode 100644 index 00000000..f8eeda88 --- /dev/null +++ b/trunk/src/lib/sos/plugins/hardware.py @@ -0,0 +1,55 @@ +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools +import commands + +class hardware(sos.plugintools.PluginBase): + """hardware related information + """ + def setup(self): + self.addCopySpec("/proc/partitions") + self.addCopySpec("/proc/cpuinfo") + self.addCopySpec("/proc/meminfo") + self.addCopySpec("/proc/ioports") + self.addCopySpec("/proc/interrupts") + self.addCopySpec("/proc/scsi") + self.addCopySpec("/proc/dma") + self.addCopySpec("/proc/devices") + self.addCopySpec("/proc/rtc") + self.addCopySpec("/proc/ide") + self.addCopySpec("/proc/bus") + self.addCopySpec("/etc/stinit.def") + self.addCopySpec("/etc/sysconfig/hwconf") + self.addCopySpec("/proc/chandev") + self.addCopySpec("/proc/dasd") + self.addCopySpec("/proc/s390dbf/tape") + self.collectExtOutput("/usr/share/rhn/up2dateclient/hardware.py") + self.collectExtOutput("""/bin/echo "lspci" ; /bin/echo ; /sbin/lspci ; /bin/echo ; /bin/echo ; /bin/echo "lspci -nvv" ; /bin/echo ; /sbin/lspci -nvv""", suggest_filename = "lspci", root_symlink = "lspci") + + # FIXME: what is this for? + self.collectExtOutput("/bin/dmesg | /bin/grep -e 'e820.' -e 'agp.'") + + # FIXME: what is this for? + tmpreg = "" + for hwmodule in commands.getoutput('cat /lib/modules/$(uname -r)/modules.pcimap | cut -d " " -f 1 | grep "[:alpha:]" | sort -u').split("\n"): + hwmodule = hwmodule.strip() + if len(hwmodule): + tmpreg = tmpreg + "|" + hwmodule + self.collectExtOutput("/bin/dmesg | /bin/egrep '(%s)'" % tmpreg[1:]) + + self.collectExtOutput("/sbin/lsusb") + self.collectExtOutput("/usr/bin/lshal") + return + diff --git a/trunk/src/lib/sos/plugins/initrd.py b/trunk/src/lib/sos/plugins/initrd.py new file mode 100644 index 00000000..83356548 --- /dev/null +++ b/trunk/src/lib/sos/plugins/initrd.py @@ -0,0 +1,28 @@ +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools +import glob + +class initrd(sos.plugintools.PluginBase): + """initrd related information + """ + def setup(self): + for initrd in glob.glob('/boot/initrd-*.img'): + self.collectExtOutput("/bin/zcat "+initrd+" | /bin/cpio "+ + "--extract --to-stdout init" ) + return + + def defaultenabled(self): + return False diff --git a/trunk/src/lib/sos/plugins/kernel.py b/trunk/src/lib/sos/plugins/kernel.py new file mode 100644 index 00000000..09381b38 --- /dev/null +++ b/trunk/src/lib/sos/plugins/kernel.py @@ -0,0 +1,131 @@ +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools +import commands, os, re + +class kernel(sos.plugintools.PluginBase): + """kernel related information + """ + optionList = [("modinfo", 'Gathers module information on all modules', 'fast', 1), + ('sysrq', 'Trigger SysRq dumps', 'fast', 0)] + moduleFile = "" + taintList = [ + {'regex':'mvfs*', 'description':'Clearcase module'}, + {'regex':'vnode*', 'description':'Clearcase module'}, + {'regex':'vxfs*', 'description':'Veritas file system module'}, + {'regex':'vxportal*', 'description':'Veritas module'}, + {'regex':'vxdmp*', 'description':'Veritas dynamic multipathing module'}, + {'regex':'vxio*', 'description':'Veritas module'}, + {'regex':'vxspec*"', 'description':'Veritas module'}, + {'regex':'dcd*', 'description':'Dell OpenManage Server Administrator module'}, + {'regex':'ocfs', 'description':'Oracle cluster filesystem module'}, + {'regex':'oracle*', 'description':'Oracle module'}, + {'regex':'vmnet*', 'description':'VMware module'}, + {'regex':'vmmon*', 'description':'VMware module'}, + {'regex':'egenera*', 'description':'Egenera module'}, + {'regex':'emcp*', 'description':'EMC module'}, + {'regex':'ocfs*', 'description':'OCFS module'}, + {'regex':'nvidia', 'description':'NVidia module'}, + {'regex':'ati-', 'description':'ATI module'} + ] + + # HP + # + # + + + def setup(self): + self.collectExtOutput("/bin/uname -a", root_symlink = "uname") + self.moduleFile = self.collectOutputNow("/sbin/lsmod", root_symlink = "lsmod") + if self.isOptionEnabled('modinfo'): + runcmd = "" + for kmod in commands.getoutput('/sbin/lsmod | /bin/cut -f1 -d" " 2>/dev/null | /bin/grep -v Module 2>/dev/null').split('\n'): + if '' != kmod.strip(): + runcmd = runcmd + " " + kmod + if len(runcmd): + self.collectExtOutput("/sbin/modinfo " + runcmd) + self.collectExtOutput("/sbin/ksyms") + self.addCopySpec("/proc/filesystems") + self.addCopySpec("/proc/ksyms") + self.addCopySpec("/proc/slabinfo") + kver = commands.getoutput('/bin/uname -r') + depfile = "/lib/modules/%s/modules.dep" % (kver,) + self.addCopySpec(depfile) + self.addCopySpec("/etc/conf.modules") + self.addCopySpec("/etc/modules.conf") + self.addCopySpec("/etc/modprobe.conf") + self.collectExtOutput("/usr/sbin/dmidecode", root_symlink = "dmidecode") + self.collectExtOutput("/usr/sbin/dkms status") + self.addCopySpec("/proc/cmdline") + self.addCopySpec("/proc/driver") + self.addCopySpec("/proc/sys/kernel/tainted") + # trigger some sysrq's. I'm not sure I like doing it this way, but + # since we end up with the sysrq dumps in syslog whether we run the + # syslog report before or after this, I suppose I can live with it. + if self.isOptionEnabled('sysrq') and os.access("/proc/sysrq-trigger", os.W_OK) and os.access("/proc/sys/kernel/sysrq", os.R_OK): + sysrq_state = commands.getoutput("/bin/cat /proc/sys/kernel/sysrq") + commands.getoutput("/bin/echo 1 > /proc/sys/kernel/sysrq") + for key in ['m', 'p', 't']: + commands.getoutput("/bin/echo %s > /proc/sysrq-trigger" % (key,)) + commands.getoutput("/bin/echo %s > /proc/sys/kernel/sysrq" % (sysrq_state,)) + # No need to grab syslog here if we can't trigger sysrq, so keep this + # inside the if + self.addCopySpec("/var/log/messages") + + return + + def analyze(self): + infd = open("/proc/modules", "r") + modules = infd.readlines() + infd.close() + + for modname in modules: + modname=modname.split(" ")[0] + modinfo_srcver = commands.getoutput("/sbin/modinfo -F srcversion %s" % modname) + if not os.access("/sys/module/%s/srcversion" % modname, os.R_OK): + continue + infd = open("/sys/module/%s/srcversion" % modname, "r") + sys_srcver = infd.read().strip("\n") + infd.close() + if modinfo_srcver != sys_srcver: + self.addAlert("Loaded module %s differs from the one present on the file-system") + + # this would be a good moment to check the module's signature + # but at the moment there's no easy way to do that outside of + # the kernel. i will probably need to write a C lib (derived from + # the kernel sources to do this verification. + + savedtaint = os.path.join(self.cInfo['dstroot'], "/proc/sys/kernel/tainted") + infd = open(savedtaint, "r") + line = infd.read() + infd.close() + line = line.strip() + if (line != "0"): + self.addAlert("Kernel taint flag is <%s>\n" % line) + + + infd = open(self.moduleFile, "r") + modules = infd.readlines() + infd.close() + + #print(modules) + for tainter in self.taintList: + p = re.compile(tainter['regex']) + for line in modules: + if p.match(line) != None: + # found a taint match, create an alert + moduleName = line.split()[0] + self.addAlert("Check for tainted kernel by module %s, which is %s" % (moduleName, tainter['description'])) + return diff --git a/trunk/src/lib/sos/plugins/ldap.py b/trunk/src/lib/sos/plugins/ldap.py new file mode 100644 index 00000000..318a3ba9 --- /dev/null +++ b/trunk/src/lib/sos/plugins/ldap.py @@ -0,0 +1,27 @@ +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class ldap(sos.plugintools.PluginBase): + """LDAP related information + """ + def setup(self): + self.addCopySpec("/etc/ldap.conf") + self.addCopySpec("/etc/openldap") + return + + def postproc(self): + self.doRegexSub("/etc/ldap.conf", r"(\s*bindpw\s*)\S+", r"\1***") + return diff --git a/trunk/src/lib/sos/plugins/libraries.py b/trunk/src/lib/sos/plugins/libraries.py new file mode 100644 index 00000000..70b63dba --- /dev/null +++ b/trunk/src/lib/sos/plugins/libraries.py @@ -0,0 +1,24 @@ +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class libraries(sos.plugintools.PluginBase): + """information on shared libraries + """ + def setup(self): + self.addCopySpec("/etc/ld.so.conf") + self.addCopySpec("/etc/ld.so.conf.d") + return + diff --git a/trunk/src/lib/sos/plugins/mail.py b/trunk/src/lib/sos/plugins/mail.py new file mode 100644 index 00000000..0d4dda29 --- /dev/null +++ b/trunk/src/lib/sos/plugins/mail.py @@ -0,0 +1,25 @@ +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class mail(sos.plugintools.PluginBase): + """mail server related information + """ + def setup(self): + self.addCopySpec("/etc/mail") + self.addCopySpec("/etc/postfix/main.cf") + self.addCopySpec("/etc/postfix/master.cf") + return + diff --git a/trunk/src/lib/sos/plugins/memory.py b/trunk/src/lib/sos/plugins/memory.py new file mode 100644 index 00000000..7fbe39c3 --- /dev/null +++ b/trunk/src/lib/sos/plugins/memory.py @@ -0,0 +1,30 @@ +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class memory(sos.plugintools.PluginBase): + """memory usage information + """ + def setup(self): + self.addCopySpec("/proc/pci") + self.addCopySpec("/proc/meminfo") + self.addCopySpec("/proc/vmstat") + self.addCopySpec("/proc/slabinfo") + + self.collectExtOutput("/bin/dmesg | grep -e 'e820.' -e 'aperature.'") + self.collectExtOutput("/usr/bin/free", root_symlink = "free") + self.collectExtOutput("/usr/bin/free -m") + return + diff --git a/trunk/src/lib/sos/plugins/named.py b/trunk/src/lib/sos/plugins/named.py new file mode 100644 index 00000000..68d56ca0 --- /dev/null +++ b/trunk/src/lib/sos/plugins/named.py @@ -0,0 +1,36 @@ +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools +import commands +import os + +class named(sos.plugintools.PluginBase): + """named related information + """ + def setup(self): + dnsdir = "" + self.addCopySpec("/etc/named.boot") + self.addCopySpec("/etc/named.conf") + self.addCopySpec("/etc/sysconfig/named") + if os.access("/etc/named.conf", os.R_OK): + dnsdir = commands.getoutput("/bin/grep -i directory /etc/named.conf | /bin/gawk '{print $2}' | /bin/sed 's/\\\"//g' | /bin/sed 's/\;//g'") + if os.access("/etc/named.boot", os.R_OK): + dnsdir = commands.getoutput("/bin/grep -i directory /etc/named.boot | /bin/gawk '{print $2}' | /bin/sed 's/\\\"//g' | /bin/sed 's/\;//g'") + if '' != dnsdir.strip(): + self.addCopySpec(dnsdir) + self.addForbiddenPath('/var/named/chroot/proc') + self.addForbiddenPath('/var/named/chroot/dev') + return + diff --git a/trunk/src/lib/sos/plugins/networking.py b/trunk/src/lib/sos/plugins/networking.py new file mode 100644 index 00000000..54723663 --- /dev/null +++ b/trunk/src/lib/sos/plugins/networking.py @@ -0,0 +1,75 @@ +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools +import os,re,commands + +class networking(sos.plugintools.PluginBase): + """network related information + """ + optionList = [("traceroute", "collects a traceroute to rhn.redhat.com", "slow", 0)] + + def get_interface_name(self,ifconfigFile): + """Return a dictionary for which key are interface name according to the + output of ifconifg-a stored in ifconfigFile. + """ + out={} + if(os.path.isfile(ifconfigFile)): + f=open(ifconfigFile,'r') + content=f.read() + f.close() + reg=re.compile(r"^(eth\d+)\D",re.MULTILINE) + for name in reg.findall(content): + out[name]=1 + return out + + def collectIPTable(self,tablename): + """ When running the iptables command, it unfortunately auto-loads + the modules before trying to get output. Some people explicitly + don't want this, so check if the modules are loaded before running + the command. If they aren't loaded, there can't possibly be any + relevant rules in that table """ + + cmd = "/sbin/iptables -t "+tablename+" -nvL" + + (status, output) = commands.getstatusoutput("/sbin/lsmod | grep -q "+tablename) + if status == 0: + self.collectExtOutput(cmd) + else: + self.writeTextToCommand(cmd,"IPTables module "+tablename+" not loaded\n") + + def setup(self): + self.addCopySpec("/etc/nsswitch.conf") + self.addCopySpec("/etc/yp.conf") + self.addCopySpec("/etc/inetd.conf") + self.addCopySpec("/etc/xinetd.conf") + self.addCopySpec("/etc/xinetd.d") + self.addCopySpec("/etc/host*") + self.addCopySpec("/etc/resolv.conf") + ifconfigFile=self.collectExtOutput("/sbin/ifconfig -a", root_symlink = "ifconfig") + self.collectExtOutput("/sbin/route -n", root_symlink = "route") + self.collectExtOutput("/sbin/ipchains -nvL") + self.collectIPTable("filter") + self.collectIPTable("nat") + self.collectIPTable("mangle") + self.collectExtOutput("/bin/netstat -nap") + if ifconfigFile: + for eth in self.get_interface_name(ifconfigFile): + self.collectExtOutput("/sbin/ethtool "+eth) + if self.isOptionEnabled("traceroute"): + # The semicolon prevents the browser from thinking this is a link when viewing the report + self.collectExtOutput("/bin/traceroute rhn.redhat.com;") + + return + diff --git a/trunk/src/lib/sos/plugins/nfsserver.py b/trunk/src/lib/sos/plugins/nfsserver.py new file mode 100644 index 00000000..6c894177 --- /dev/null +++ b/trunk/src/lib/sos/plugins/nfsserver.py @@ -0,0 +1,27 @@ +## Copyright (C) 2007 Red Hat, Inc., Eugene Teo <eteo@redhat.com> + +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class nfsserver(sos.plugintools.PluginBase): + """NFS server-related information + """ + def setup(self): + self.addCopySpec("/etc/exports") + self.collectExtOutput("/usr/sbin/rpcinfo -p localhost") + self.collectExtOutput("/usr/sbin/nfsstat") + return + diff --git a/trunk/src/lib/sos/plugins/pam.py b/trunk/src/lib/sos/plugins/pam.py new file mode 100644 index 00000000..8164bba3 --- /dev/null +++ b/trunk/src/lib/sos/plugins/pam.py @@ -0,0 +1,25 @@ +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class pam(sos.plugintools.PluginBase): + """PAM related information + """ + def setup(self): + self.addCopySpec("/etc/pam.d") + self.addCopySpec("/etc/security") + self.collectExtOutput("/bin/ls -laF /lib/security/pam_*so") + return + diff --git a/trunk/src/lib/sos/plugins/printing.py b/trunk/src/lib/sos/plugins/printing.py new file mode 100644 index 00000000..76a476eb --- /dev/null +++ b/trunk/src/lib/sos/plugins/printing.py @@ -0,0 +1,24 @@ +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class printing(sos.plugintools.PluginBase): + """printing related information (cups) + """ + def setup(self): + self.addCopySpec("/etc/cups/*.conf") + self.addCopySpec("/var/log/cups") + return + diff --git a/trunk/src/lib/sos/plugins/process.py b/trunk/src/lib/sos/plugins/process.py new file mode 100644 index 00000000..7ed13a5e --- /dev/null +++ b/trunk/src/lib/sos/plugins/process.py @@ -0,0 +1,24 @@ +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class process(sos.plugintools.PluginBase): + """process information + """ + def setup(self): + self.collectExtOutput("/bin/ps auxww", root_symlink = "ps") + self.collectExtOutput("/usr/bin/pstree", root_symlink = "pstree") + return + diff --git a/trunk/src/lib/sos/plugins/radius.py b/trunk/src/lib/sos/plugins/radius.py new file mode 100644 index 00000000..7ab53810 --- /dev/null +++ b/trunk/src/lib/sos/plugins/radius.py @@ -0,0 +1,29 @@ +## Copyright (C) 2007 Navid Sheikhol-Eslami <navid@redhat.com> + +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class radius(sos.plugintools.PluginBase): + """radius related information + """ + def setup(self): + self.addCopySpec("/etc/raddb") + self.addCopySpec("/etc/pam.d/radiusd") + return + + def postproc(self): + self.doRegexSub("/etc/raddb/sql.conf", r"(\s*password\s*=\s*)\S+", r"\1***") + return diff --git a/trunk/src/lib/sos/plugins/rhn.py b/trunk/src/lib/sos/plugins/rhn.py new file mode 100644 index 00000000..049e51bc --- /dev/null +++ b/trunk/src/lib/sos/plugins/rhn.py @@ -0,0 +1,91 @@ +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +from sos.plugintools import PluginBase + +class rhn(PluginBase): + """RHN server related information + """ + def checkenabled(self): + # XXX check for the presence of requisite packages + satellite = self.cInfo["policy"].pkgByName("rhns-satellite-tools") + proxy = self.cInfo["policy"].pkgByName("rhns-proxy-tools") + if not satellite and not proxy: + return False + return True + + def setup(self): + # + # First, grab things needed from both Satellite and Proxy systems + # + # TODO: add chain load so we can use existing modules for httpd, &c. + # + + # basic RHN logs and configs + self.addCopySpec("/var/log/rhn*") + self.addCopySpec("/etc/rhn") + self.collectExtOutput("/usr/share/rhn/up2date_client/hardware.py") + + # httpd + self.addCopySpec("/etc/httpd/conf") + self.addCopySpec("/var/log/httpd") + + # RPM manifests + self.collectExtOutput("/bin/rpm -qa --last | sort") + + # monitoring scout logs + self.addCopySpec("/home/nocpulse/var/*.log*") + self.addCopySpec("/home/nocpulse/var/commands/*.log*") + + satellite = self.cInfo["policy"].pkgByName("rhns-satellite-tools") + proxy = self.cInfo["policy"].pkgByName("rhns-proxy-tools") + + # + # Now, go for product-specific data + # + if satellite: + self.setupSatellite(satellite) + + if proxy: + self.setupProxy(proxy) + + def setupSatellite(self, satellite): + self.collectExtOutput("/usr/bin/rhn-schema-version") + self.collectExtOutput("/usr/bin/rhn-charsets") + + # oracle + self.addCopySpec("/etc/tnsnames.ora") + + # tomcat (4.x and newer satellites only) + if not self.cInfo["policy"].pkgNVRA(satellite)[1].startswith("3."): + self.addCopySpec("/etc/tomcat5") + self.addCopySpec("/var/log/tomcat5") + + # jabberd + # - logs to /var/log/messages + self.addCopySpec("/etc/jabberd") + + # SSL build + self.addCopySpec("/root/ssl-build") + + # monitoring logs + self.addCopySpec("/opt/notification/var/*.log*") + self.addCopySpec("/var/tmp/ack_handler.log*") + self.addCopySpec("/var/tmp/enqueue.log*") + + def setupProxy(self, proxy): + # squid + self.addCopySpec("/etc/squid") + self.addCopySpec("/var/log/squid") + diff --git a/trunk/src/lib/sos/plugins/rpm.py b/trunk/src/lib/sos/plugins/rpm.py new file mode 100644 index 00000000..b6fdb699 --- /dev/null +++ b/trunk/src/lib/sos/plugins/rpm.py @@ -0,0 +1,33 @@ +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class rpm(sos.plugintools.PluginBase): + """RPM information + """ + optionList = [("rpmq", "Queries for package information via rpm -q", "fast", 1), + ("rpmva", "Runs a verify on all packages", "slow", 1)] + + def setup(self): + self.addCopySpec("/var/log/rpmpkgs") + + if self.isOptionEnabled("rpmq"): + self.collectExtOutput("/bin/rpm -qa --qf \"%{NAME}-%{VERSION}-%{RELEASE}-%{ARCH}\n\"", root_symlink = "installed-rpms") + + if self.isOptionEnabled("rpmva"): + self.eta_weight += 800 # this plugins takes 200x longer (for ETA) + self.collectExtOutput("/bin/rpm -Va", root_symlink = "rpm-Va") + return + diff --git a/trunk/src/lib/sos/plugins/samba.py b/trunk/src/lib/sos/plugins/samba.py new file mode 100644 index 00000000..ec65370e --- /dev/null +++ b/trunk/src/lib/sos/plugins/samba.py @@ -0,0 +1,26 @@ +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class samba(sos.plugintools.PluginBase): + """Samba related information + """ + def setup(self): + self.addCopySpec("/etc/samba") + self.addCopySpec("/var/log/samba/*") + self.collectExtOutput("/usr/bin/wbinfo -g") + self.collectExtOutput("/usr/bin/wbinfo -u") + return + diff --git a/trunk/src/lib/sos/plugins/satellite.py b/trunk/src/lib/sos/plugins/satellite.py new file mode 100644 index 00000000..930d11b0 --- /dev/null +++ b/trunk/src/lib/sos/plugins/satellite.py @@ -0,0 +1,53 @@ +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class satellite(sos.plugintools.PluginBase): + """RHN Satellite related information + """ + + def defaultenabled(self): + return False + + def setup(self): + self.addCopySpec("/etc/httpd/conf") + self.addCopySpec("/etc/rhn") + self.addCopySpec("/etc/sysconfig/rhn") + self.addCopySpec("/etc/tnsnames.ora") + self.addCopySpec("/var/log/httpd") # httpd-logs + self.addCopySpec("/var/log/rhn*") # rhn-logs + self.addCopySpec("/var/log/rhn/rhn-database-installation.log") + self.addCopySpec("/etc/jabberd") + + # tomcat for satellite 400+ + self.addCopySpec("/etc/tomcat5") + self.addCopySpec("/var/log/tomcat5") + + # all these used to go in $DIR/mon-logs + self.addCopySpec("/opt/notification/var/*.log*") + self.addCopySpec("/var/tmp/ack_handler.log*") + self.addCopySpec("/var/tmp/enqueue.log*") + + self.addCopySpec("/home/nocpulse/var/*.log*") + self.addCopySpec("/home/nocpulse/var/commands/*.log*") + self.addCopySpec("/var/tmp/ack_handler.log*") + self.addCopySpec("/var/tmp/enqueue.log*") + + self.addCopySpec("/root/ssl-build") + self.addCopySpec("rpm -qa --last") # $DIR/rpm-manifest + self.addCopySpec("/usr/bin/rhn-schema-version") # $DIR/database-schema-version + self.addCopySpec("/usr/bin/rhn-charsets") # $DIR/database-character-sets + + return diff --git a/trunk/src/lib/sos/plugins/selinux.py b/trunk/src/lib/sos/plugins/selinux.py new file mode 100644 index 00000000..897c3991 --- /dev/null +++ b/trunk/src/lib/sos/plugins/selinux.py @@ -0,0 +1,36 @@ +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools +import commands + +class selinux(sos.plugintools.PluginBase): + """selinux related information + """ + def setup(self): + self.addCopySpec("/etc/selinux/*") + self.collectExtOutput("/usr/bin/selinuxconfig") + self.collectExtOutput("/usr/sbin/sestatus", root_symlink = "sestatus") + self.collectExtOutput("/bin/rpm -q -V selinux-policy-targeted") + self.collectExtOutput("/bin/rpm -q -V selinux-policy-strict") + return + + def checkenabled(self): + # is selinux enabled ? + try: + if commands.getoutput("/usr/sbin/sestatus").split(":")[1].strip() == "disabled": + return False + except: + pass + return True diff --git a/trunk/src/lib/sos/plugins/sendmail.py b/trunk/src/lib/sos/plugins/sendmail.py new file mode 100644 index 00000000..d95b5d7b --- /dev/null +++ b/trunk/src/lib/sos/plugins/sendmail.py @@ -0,0 +1,27 @@ +## Copyright (C) 2007 Red Hat, Inc., Eugene Teo <eteo@redhat.com> + +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class sendmail(sos.plugintools.PluginBase): + """sendmail information + """ + def setup(self): + self.addCopySpec("/etc/mail/*") + self.addCopySpec("/var/log/maillog") + self.collectExtOutput("/sbin/chkconfig --list sendmail") + return + diff --git a/trunk/src/lib/sos/plugins/squid.py b/trunk/src/lib/sos/plugins/squid.py new file mode 100644 index 00000000..4544ef9a --- /dev/null +++ b/trunk/src/lib/sos/plugins/squid.py @@ -0,0 +1,23 @@ +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class squid(sos.plugintools.PluginBase): + """squid related information + """ + def setup(self): + self.addCopySpec("/etc/squid/squid.conf") + return + diff --git a/trunk/src/lib/sos/plugins/ssh.py b/trunk/src/lib/sos/plugins/ssh.py new file mode 100644 index 00000000..92a4c9e5 --- /dev/null +++ b/trunk/src/lib/sos/plugins/ssh.py @@ -0,0 +1,27 @@ +## Copyright (C) 2007 Red Hat, Inc., Eugene Teo <eteo@redhat.com> + +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class ssh(sos.plugintools.PluginBase): + """ssh-related information + """ + def setup(self): + self.addCopySpec("/etc/ssh/ssh_config") + self.addCopySpec("/etc/ssh/sshd_config") + self.collectExtOutput("/sbin/chkconfig --list sshd") + return + diff --git a/trunk/src/lib/sos/plugins/startup.py b/trunk/src/lib/sos/plugins/startup.py new file mode 100644 index 00000000..02361393 --- /dev/null +++ b/trunk/src/lib/sos/plugins/startup.py @@ -0,0 +1,25 @@ +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class startup(sos.plugintools.PluginBase): + """startup information + """ + def setup(self): + self.addCopySpec("/etc/rc.d") + + self.collectExtOutput("/sbin/chkconfig --list", root_symlink = "chkconfig") + return + diff --git a/trunk/src/lib/sos/plugins/system.py b/trunk/src/lib/sos/plugins/system.py new file mode 100644 index 00000000..19d7859e --- /dev/null +++ b/trunk/src/lib/sos/plugins/system.py @@ -0,0 +1,31 @@ +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class system(sos.plugintools.PluginBase): + """core system related information + """ + def setup(self): + self.addCopySpec("/proc/sys") + self.addCopySpec("/etc/sysctl.conf") + self.addCopySpec("/etc/cron*") + self.addCopySpec("/etc/syslog.conf") + self.addCopySpec("/etc/ntp.conf") + self.addCopySpec("/etc/ntp/step-tickers") + self.addCopySpec("/etc/ntp/ntpservers") + self.addCopySpec("/etc/auto.*") + + return + diff --git a/trunk/src/lib/sos/plugins/systemtap.py b/trunk/src/lib/sos/plugins/systemtap.py new file mode 100644 index 00000000..b99ce0cf --- /dev/null +++ b/trunk/src/lib/sos/plugins/systemtap.py @@ -0,0 +1,29 @@ +## Copyright (C) 2007 Red Hat, Inc., Eugene Teo <eteo@redhat.com> + +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class systemtap(sos.plugintools.PluginBase): + """SystemTap pre-requisites information + """ + def setup(self): + # requires systemtap, systemtap-runtime, kernel-devel, + # kernel-debuginfo, kernel-debuginfo-common + self.collectExtOutput("/bin/rpm -qa | /bin/egrep -e kernel.*`uname -r` -e systemtap -e elfutils | sort") + self.collectExtOutput("/usr/bin/stap -V 2") + self.collectExtOutput("/bin/uname -r") + return + diff --git a/trunk/src/lib/sos/plugins/x11.py b/trunk/src/lib/sos/plugins/x11.py new file mode 100644 index 00000000..9b7b7ac9 --- /dev/null +++ b/trunk/src/lib/sos/plugins/x11.py @@ -0,0 +1,26 @@ +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class x11(sos.plugintools.PluginBase): + """X related information + """ + def setup(self): + self.addCopySpec("/etc/X11") + self.addCopySpec("/var/log/Xorg.*.log") + self.addCopySpec("/var/log/XFree86.*.log") + self.collectExtOutput("/bin/dmesg | grep -e 'agpgart.'") + return + diff --git a/trunk/src/lib/sos/plugins/xen.py b/trunk/src/lib/sos/plugins/xen.py new file mode 100644 index 00000000..d6daec63 --- /dev/null +++ b/trunk/src/lib/sos/plugins/xen.py @@ -0,0 +1,82 @@ +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools +import os, commands +from stat import * + +class xen(sos.plugintools.PluginBase): + """Xen related information + """ + def determineXenHost(self): + if os.access("/proc/acpi/dsdt", os.R_OK): + (status, output) = commands.getstatusoutput("/usr/bin/strings /proc/acpi/dsdt | grep -q int-xen") + if status == 0: + return "hvm" + + if os.access("/proc/xen/capabilities", os.R_OK): + (status, output) = commands.getstatusoutput("grep -q control_d /proc/xen/capabilities") + if status == 0: + return "dom0" + else: + return "domU" + return "baremetal" + + def checkenabled(self): + if self.determineXenHost() == "baremetal": + return False + return True + + def domCollectProc(self): + self.addCopySpec("/proc/xen/balloon") + self.addCopySpec("/proc/xen/capabilities") + self.addCopySpec("/proc/xen/xsd_kva") + self.addCopySpec("/proc/xen/xsd_port") + # determine if CPU has PAE support + self.collectExtOutput("/bin/grep pae /proc/cpuinfo") + # determine if CPU has Intel-VT or AMD-V support + self.collectExtOutput("/bin/egrep -e 'vmx|svm' /proc/cpuinfo") + + def setup(self): + host_type = self.determineXenHost() + if host_type == "domU": + # we should collect /proc/xen and /sys/hypervisor + self.domCollectProc() + # determine if hardware virtualization support is enabled + # in BIOS: /sys/hypervisor/properties/capabilities + self.addCopySpec("/sys/hypervisor") + elif host_type == "hvm": + # what do we collect here??? + pass + elif host_type == "dom0": + # default of dom0, collect lots of system information + self.addCopySpec("/var/log/xen") + self.addCopySpec("/etc/xen") + self.collectExtOutput("/usr/bin/xenstore-ls") + self.collectExtOutput("/usr/sbin/xm dmesg") + self.collectExtOutput("/usr/sbin/xm info") + self.collectExtOutput("/usr/sbin/brctl show") + self.domCollectProc() + self.addCopySpec("/sys/hypervisor") + # FIXME: we *might* want to collect things in /sys/bus/xen*, + # /sys/class/xen*, /sys/devices/xen*, /sys/modules/blk*, + # /sys/modules/net*, but I've never heard of them actually being + # useful, so I'll leave it out for now + else: + # for bare-metal, we don't have to do anything special + return + + self.addCustomText("Xen hostType: "+host_type) + return + diff --git a/trunk/src/lib/sos/plugins/xinetd.py b/trunk/src/lib/sos/plugins/xinetd.py new file mode 100644 index 00000000..715c831f --- /dev/null +++ b/trunk/src/lib/sos/plugins/xinetd.py @@ -0,0 +1,27 @@ +## Copyright (C) 2007 Red Hat, Inc., Eugene Teo <eteo@redhat.com> + +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class xinetd(sos.plugintools.PluginBase): + """xinetd information + """ + def setup(self): + self.addCopySpec("/etc/xinetd.conf") + self.addCopySpec("/etc/xinetd.d/*") + self.collectExtOutput("/sbin/chkconfig --list xinetd") + return + diff --git a/trunk/src/lib/sos/plugins/yum.py b/trunk/src/lib/sos/plugins/yum.py new file mode 100644 index 00000000..89102c38 --- /dev/null +++ b/trunk/src/lib/sos/plugins/yum.py @@ -0,0 +1,37 @@ +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import sos.plugintools + +class yum(sos.plugintools.PluginBase): + """yum information + """ + + def defaultenabled(self): + # enable with -e or -o + return False + + def setup(self): + # Pull all yum related information + self.addCopySpec("/etc/yum") + self.addCopySpec("/etc/yum.repos.d") + self.addCopySpec("/etc/yum.conf") + self.addCopySpec("/var/log/yum.log") + + # Get a list of channels the machine is subscribed to. + self.collectExtOutput("/bin/echo \"repo list\" | /usr/bin/yum shell") + # List various information about available packages + self.collectExtOutput("/usr/bin/yum list") + + return diff --git a/trunk/src/lib/sos/plugintools.py b/trunk/src/lib/sos/plugintools.py new file mode 100644 index 00000000..238036ce --- /dev/null +++ b/trunk/src/lib/sos/plugintools.py @@ -0,0 +1,568 @@ +## plugintools.py +## This exports methods available for use by plugins for sos + +## Copyright (C) 2006 Steve Conklin <sconklin@redhat.com> + +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +# pylint: disable-msg = R0902 +# pylint: disable-msg = R0904 +# pylint: disable-msg = W0702 +# pylint: disable-msg = W0703 +# pylint: disable-msg = R0201 +# pylint: disable-msg = W0611 +# pylint: disable-msg = W0613 + +""" +This is the base class for sosreport plugins +""" +from sos.helpers import * +from threading import Thread, activeCount +import os, os.path, sys, string, itertools, glob, re, traceback +import logging +from stat import * +from time import time + +class PluginBase: + """ + Base class for plugins + """ + def __init__(self, pluginname, commons): + # pylint: disable-msg = E0203 + try: + len(self.optionList) + except: + self.optionList = [] + # pylint: enable-msg = E0203 + self.copiedFiles = [] + self.copiedDirs = [] + self.executedCommands = [] + self.diagnose_msgs = [] + self.alerts = [] + self.customText = "" + self.optNames = [] + self.optParms = [] + self.piName = pluginname + self.cInfo = commons + self.forbiddenPaths = [] + self.copyPaths = [] + self.collectProgs = [] + self.thread = None + self.pid = None + self.eta_weight = 1 + self.time_start = None + self.time_stop = None + + self.soslog = logging.getLogger('sos') + + # get the option list into a dictionary + for opt in self.optionList: + self.optNames.append(opt[0]) + self.optParms.append({'desc':opt[1], 'speed':opt[2], 'enabled':opt[3]}) + + # Method for applying regexp substitutions + def doRegexSub(self, srcpath, regexp, subst): + '''Apply a regexp substitution to a file archived by sosreport. + ''' + if len(self.copiedFiles): + for afile in self.copiedFiles: + if afile['srcpath'] == srcpath: + abspath = os.path.join(self.cInfo['dstroot'], srcpath.lstrip(os.path.sep)) + try: + fp = open(abspath, 'r') + tmpout, occurs = re.subn( regexp, subst, fp.read() ) + fp.close() + if occurs > 0: + fp = open(abspath,'w') + fp.write(tmpout) + fp.close() + return occurs + except SystemExit: + raise SystemExit + except KeyboardInterrupt: + raise KeyboardInterrupt + except Exception, e: + self.soslog.log(logging.VERBOSE, "Problem at path %s (%s)" % (abspath,e)) + break + return False + + # Methods for copying files and shelling out + def doCopyFileOrDir(self, srcpath): + # pylint: disable-msg = R0912 + # pylint: disable-msg = R0915 + ''' Copy file or directory to the destination tree. If a directory, then everything + below it is recursively copied. A list of copied files are saved for use later + in preparing a report + ''' + copyProhibited = 0 + for path in self.forbiddenPaths: + if ( srcpath.count(path) > 0 ): + copyProhibited = 1 + + if copyProhibited: + return '' + + if os.path.islink(srcpath): + # This is a symlink - We need to also copy the file that it points to + # file and dir symlinks ar ehandled the same + link = os.readlink(srcpath) + if os.path.isabs(link): + # the link was an absolute path, and will not point to the new + # tree. We must adjust it. + + # What's the name of the symlink on the dest tree? + dstslname = os.path.join(self.cInfo['dstroot'], srcpath.lstrip(os.path.sep)) + + # make sure the dst dir exists + if not (os.path.exists(os.path.dirname(dstslname)) and os.path.isdir(os.path.dirname(dstslname))): + # create the directory + os.makedirs(os.path.dirname(dstslname)) + + dstsldir = os.path.join(self.cInfo['dstroot'], link.lstrip(os.path.sep)) + # Create the symlink on the dst tree + rpth = sosRelPath(os.path.dirname(dstslname), dstsldir) + os.symlink(rpth, dstslname) + else: + # no adjustment, symlink is the relative path + dstslname = link + + if os.path.isdir(srcpath): + for afile in os.listdir(srcpath): + if afile == '.' or afile == '..': + pass + else: + try: + abspath = self.doCopyFileOrDir(srcpath+'/'+afile) + except SystemExit: + raise SystemExit + except KeyboardInterrupt: + raise KeyboardInterrupt + except Exception, e: + self.soslog.warning(traceback.format_exc()) + + # if on forbidden list, abspath is null + if not abspath == '': + dstslname = sosRelPath(self.cInfo['rptdir'], abspath) + self.copiedDirs.append({'srcpath':srcpath, 'dstpath':dstslname, 'symlink':"yes", 'pointsto':link}) + else: + try: + dstslname, abspath = self.__copyFile(srcpath) + self.copiedFiles.append({'srcpath':srcpath, 'dstpath':dstslname, 'symlink':"yes", 'pointsto':link}) + self.cInfo['xmlreport'].add_file(srcpath,os.stat(srcpath)) + except SystemExit: + raise SystemExit + except KeyboardInterrupt: + raise KeyboardInterrupt + except Exception, e: + self.soslog.log(logging.VERBOSE, "Problem at path %s (%s)" % (srcpath, e)) + + return abspath + + else: + if not os.path.exists(srcpath): + self.soslog.debug("File or directory %s does not exist\n" % srcpath) + elif os.path.isdir(srcpath): + for afile in os.listdir(srcpath): + if afile == '.' or afile == '..': + pass + else: + self.doCopyFileOrDir(srcpath+'/'+afile) + else: + # This is not a directory or a symlink + tdstpath, abspath = self.__copyFile(srcpath) + self.copiedFiles.append({'srcpath':srcpath, 'dstpath':tdstpath, 'symlink':"no"}) # save in our list + return abspath + + def __copyFile(self, src): + """ call cp to copy a file, collect return status and output. Returns the + destination file name. + """ + try: + # pylint: disable-msg = W0612 + status, shout, runtime = sosGetCommandOutput("/bin/cp --parents -P --preserve=mode,ownership,timestamps,links " + src +" " + self.cInfo['dstroot']) + if status: + self.soslog.debug(shout) + abspath = os.path.join(self.cInfo['dstroot'], src.lstrip(os.path.sep)) + relpath = sosRelPath(self.cInfo['rptdir'], abspath) + return relpath, abspath + except SystemExit: + raise SystemExit + except KeyboardInterrupt: + raise KeyboardInterrupt + except Exception,e: + self.soslog.warning("Problem copying file %s (%s)" % (src, e)) + + def addForbiddenPath(self, forbiddenPath): + """Specify a path to not copy, even if it's part of a copyPaths[] entry. + Note: do NOT use globs here. + """ + self.forbiddenPaths.append(forbiddenPath) + + def getAllOptions(self): + """ + return a list of all options selected + """ + return (self.optNames, self.optParms) + + def setOption(self, optionname, enable): + ''' enable or disable the named option. + ''' + for name, parms in zip(self.optNames, self.optParms): + if name == optionname: + parms['enabled'] = enable + + def isOptionEnabled(self, optionname): + ''' see whether the named option is enabled. + ''' + for name, parms in zip(self.optNames, self.optParms): + if name == optionname: + return parms['enabled'] + # nonexistent options aren't enabled. + return 0 + + def addCopySpecLimit(self,fname,sizelimit = None): + """Add a file specification (with limits) + """ + files = glob.glob(fname) + files.sort() + cursize = 0 + for flog in files: + cursize += os.stat(flog)[ST_SIZE] + if sizelimit and (cursize / 1024 / 1024) > sizelimit: + break + self.addCopySpec(flog) + + def addCopySpec(self, copyspec): + """ Add a file specification (can be file, dir,or shell glob) to be + copied into the sosreport by this module + """ + # Glob case handling is such that a valid non-glob is a reduced glob + for filespec in glob.glob(copyspec): + self.copyPaths.append(filespec) + + def copyFileGlob(self, srcglob): + """ Deprecated - please modify modules to use addCopySpec() + """ + sys.stderr.write("Warning: thecopyFileGlob() function has been deprecated. Please") + sys.stderr.write("use addCopySpec() instead. Calling addCopySpec() now.") + self.addCopySpec(srcglob) + + def copyFileOrDir(self, srcpath): + """ Deprecated - please modify modules to use addCopySpec() + """ + sys.stderr.write("Warning: the copyFileOrDir() function has been deprecated. Please\n") + sys.stderr.write("use addCopySpec() instead. Calling addCopySpec() now.\n") + raise ValueError + #self.addCopySpec(srcpath) + + def runExeInd(self, exe): + """ Deprecated - use callExtProg() + """ + sys.stderr.write("Warning: the runExeInd() function has been deprecated. Please use\n") + sys.stderr.write("the callExtProg() function. This should only be called\n") + sys.stderr.write("if collect() is overridden.") + pass + + def callExtProg(self, prog): + """ Execute a command independantly of the output gathering part of + sosreport + """ + # Log if binary is not runnable or does not exist + if not os.access(prog.split()[0], os.X_OK): + self.soslog.log(logging.VERBOSE, "binary '%s' does not exist or is not runnable" % prog.split()[0]) + + # pylint: disable-msg = W0612 + status, shout, runtime = sosGetCommandOutput(prog) + return status + + def runExe(self, exe): + """ Deprecated - use collectExtOutput() + """ + sys.stderr.write("Warning: the runExe() function has been deprecated. Please use\n") + sys.stderr.write("the collectExtOutput() function.\n") + pass + + def collectExtOutput(self, exe, suggest_filename = None, root_symlink = None): + """ + Run a program and collect the output + """ + self.collectProgs.append( (exe,suggest_filename,root_symlink) ) + + def makeCommandFilename(self, exe): + """ The internal function to build up a filename based on a command """ + + mangledname = re.sub(r"[^\w\-\.\/]+", "_", exe) + mangledname = re.sub(r"/", ".", mangledname).strip(" ._-")[0:64] + + outfn = self.cInfo['cmddir'] + "/" + self.piName + "/" + mangledname + + # check for collisions + inc = 0 + if os.path.exists(outfn): + inc = 2 + while True: + newfn = outfn + "_" + inc + if not os.path.exists(newfn): + break + inc +=1 + + return outfn + + def collectOutputNow(self, exe, suggest_filename = None, root_symlink = False): + """ Execute a command and save the output to a file for inclusion in + the report + """ + # First check to make sure the binary exists and is runnable. + if not os.access(exe.split()[0], os.X_OK): + self.soslog.log(logging.VERBOSE, "binary '%s' does not exist or is not runnable, trying anyways" % exe.split()[0]) + + # pylint: disable-msg = W0612 + status, shout, runtime = sosGetCommandOutput(exe) + + if suggest_filename: + outfn = self.makeCommandFilename(suggest_filename) + else: + outfn = self.makeCommandFilename(exe) + + if not os.path.isdir(os.path.dirname(outfn)): + os.mkdir(os.path.dirname(outfn)) + + outfd = open(outfn, "w") + if status == 127: outfd.write("# the command returned exit status 127, this normally means that the command was not found.\n\n") + if len(shout): outfd.write(shout+"\n") + outfd.close() + + if root_symlink: + curdir = os.getcwd() + os.chdir(self.cInfo['dstroot']) + os.symlink(outfn[len(self.cInfo['dstroot'])+1:], root_symlink.strip("/.")) + os.chdir(curdir) + + outfn = outfn[len(self.cInfo['cmddir'])+1:] + + # sosStatus(status) + # save info for later + self.executedCommands.append({'exe': exe, 'file':outfn}) # save in our list + self.cInfo['xmlreport'].add_command(cmdline=exe,exitcode=status,f_stdout=outfn,runtime=runtime) + return outfn + + def writeTextToCommand(self, exe, text): + """ A function that allows you to write a random text string to the + command output location referenced by exe; this is useful if you want + to conditionally collect information, but still want the output file + to exist so as not to confuse readers """ + + outfn = self.makeCommandFilename(exe) + + if not os.path.isdir(os.path.dirname(outfn)): + os.mkdir(os.path.dirname(outfn)) + + outfd = open(outfn, "w") + outfd.write(text) + outfd.close() + + self.executedCommands.append({'exe': exe, 'file': outfn}) # save in our list + return outfn + + # For adding warning messages regarding configuration sanity + def addDiagnose(self, alertstring): + """ Add a configuration sanity warning for this plugin. These + will be displayed on-screen before collection and in the report as well. + """ + self.diagnose_msgs.append(alertstring) + return + + # For adding output + def addAlert(self, alertstring): + """ Add an alert to the collection of alerts for this plugin. These + will be displayed in the report + """ + self.alerts.append(alertstring) + return + + + def addCustomText(self, text): + """ Append text to the custom text that is included in the report. This + is freeform and can include html. + """ + self.customText = self.customText + text + return + + def doCollect(self): + """ This function has been replaced with copyStuff(threaded = True). Please change your + module calls. Calling setup() now. + """ + return self.copyStuff(threaded = True) + + def isRunning(self): + """ + if threaded, is thread running ? + """ + if self.thread: return self.thread.isAlive() + return None + + def wait(self,timeout=None): + """ + wait for a thread to complete - only called for threaded execution + """ + self.thread.join(timeout) + return self.thread.isAlive() + + def copyStuff(self, threaded = False, semaphore = None): + """ + Collect the data for a plugin + """ + if threaded and self.thread == None: + self.thread = Thread(target=self.copyStuff, name=self.piName+'-thread', args = [True, semaphore] ) + self.thread.start() + return self.thread + + if semaphore: semaphore.acquire() + + self.soslog.log(logging.VERBOSE, "starting threaded plugin %s" % self.piName) + + self.time_start = time() + self.time_stop = None + + for path in self.copyPaths: + self.soslog.debug("copying pathspec %s" % path) + try: + self.doCopyFileOrDir(path) + except SystemExit: + raise SystemExit + except KeyboardInterrupt: + raise KeyboardInterrupt + except Exception, e: + self.soslog.log(logging.VERBOSE, "error copying from pathspec %s (%s), traceback follows:" % (path,e)) + self.soslog.log(logging.VERBOSE, traceback.format_exc()) + for (prog,suggest_filename,root_symlink) in self.collectProgs: + self.soslog.debug("collecting output of '%s'" % prog) + try: + self.collectOutputNow(prog, suggest_filename, root_symlink) + except SystemExit: + raise SystemExit + except KeyboardInterrupt: + raise KeyboardInterrupt + except: + self.soslog.log(logging.VERBOSE, "error collection output of '%s', traceback follows:" % prog) + self.soslog.log(logging.VERBOSE, traceback.format_exc()) + + self.time_stop = time() + + if semaphore: semaphore.release() + self.soslog.log(logging.VERBOSE, "plugin %s returning" % self.piName) + + def get_description(self): + """ This function will return the description for the plugin""" + try: + return self.__doc__.strip() + except: + return "<no description available>" + + def checkenabled(self): + """ This function can be overidden to let the plugin decide whether + it should run or not. + """ + return True + + def defaultenabled(self): + """This devices whether a plugin should be automatically loaded or + only if manually specified in the command line.""" + return True + + def collect(self): + """ This function has been replaced with setup(). Please change your + module calls. Calling setup() now. + """ + self.setup() + + def diagnose(self): + """This class must be overridden to check the sanity of the system's + configuration before the collection begins. + """ + pass + + def setup(self): + """This class must be overridden to add the copyPaths, forbiddenPaths, + and external programs to be collected at a minimum. + """ + pass + + def analyze(self): + """ + perform any analysis. To be replaced by a plugin if desired + """ + pass + + def postproc(self): + """ + perform any postprocessing. To be replaced by a plugin if desired + """ + pass + + def report(self): + """ Present all information that was gathered in an html file that allows browsing + the results. + """ + # make this prettier + html = '<hr/><a name="%s"></a>\n' % self.piName + + # Intro + html = html + "<h2> Plugin <em>" + self.piName + "</em></h2>\n" + + # Files + if len(self.copiedFiles): + html = html + "<p>Files copied:<br><ul>\n" + for afile in self.copiedFiles: + html = html + '<li><a href="%s">%s</a>' % (afile['dstpath'], afile['srcpath']) + if (afile['symlink'] == "yes"): + html = html + " (symlink to %s)" % afile['pointsto'] + html = html + '</li>\n' + html = html + "</ul></p>\n" + + # Dirs + if len(self.copiedDirs): + html = html + "<p>Directories Copied:<br><ul>\n" + for adir in self.copiedDirs: + html = html + '<li><a href="%s">%s</a>\n' % (adir['dstpath'], adir['srcpath']) + if (adir['symlink'] == "yes"): + html = html + " (symlink to %s)" % adir['pointsto'] + html = html + '</li>\n' + html = html + "</ul></p>\n" + + # Command Output + if len(self.executedCommands): + html = html + "<p>Commands Executed:<br><ul>\n" + # convert file name to relative path from our root + for cmd in self.executedCommands: + cmdOutRelPath = sosRelPath(self.cInfo['rptdir'], cmd['file']) + html = html + '<li><a href="%s">%s</a></li>\n' % (cmdOutRelPath, cmd['exe']) + html = html + "</ul></p>\n" + + # Alerts + if len(self.alerts): + html = html + "<p>Alerts:<br><ul>\n" + for alert in self.alerts: + html = html + '<li>%s</li>\n' % alert + html = html + "</ul></p>\n" + + # Custom Text + if (self.customText != ""): + html = html + "<p>Additional Information:<br>\n" + html = html + self.customText + "</p>\n" + + return html + + diff --git a/trunk/src/lib/sos/policyredhat.py b/trunk/src/lib/sos/policyredhat.py new file mode 100755 index 00000000..d4a4074d --- /dev/null +++ b/trunk/src/lib/sos/policyredhat.py @@ -0,0 +1,151 @@ +## policy-redhat.py +## Implement policies required for the sos system support tool + +## Copyright (C) Steve Conklin <sconklin@redhat.com> + +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +import os +import commands +import sys +import string +from tempfile import gettempdir +from sos.helpers import * +import random + +SOME_PATH = "/tmp/SomePath" + +#class SosError(Exception): +# def __init__(self, code, message): +# self.code = code +# self.message = message +# +# def __str__(self): +# return 'Sos Error %s: %s' % (self.code, self.message) + + +class SosPolicy: + "This class implements various policies for sos" + def __init__(self): + #print "Policy init" + return + + def setCommons(self, commons): + self.cInfo = commons + return + + def validatePlugin(self, pluginpath): + "Validates the plugin as being acceptable to run" + # return value + # TODO implement this + #print "validating %s" % pluginpath + return True + + def allPkgsByName(self, name): + # FIXME: we're relying on rpm to sort the output list + cmd = "/bin/rpm --qf '%%{N}-%%{V}-%%{R}-%%{ARCH}\n' -q %s" % (name,) + pkgs = os.popen(cmd).readlines() + return [pkg[:-1] for pkg in pkgs if pkg.startswith(name)] + + def pkgByName(self, name): + # TODO: do a full NEVRA compare and return newest version, best arch + try: + # lame attempt at locating newest + pkg = self.allPkgsByName(name)[-1] + except IndexError: + pkg = None + + return pkg + + def pkgDictByName(self, name): + pkgName = self.pkgByName(name) + if pkgName and len(pkgName) > len(name): + return pkgName[len(name)+1:].split("-") + else: + return None + + def runlevelByService(self, name): + ret = [] + try: + for tabs in commands.getoutput("/sbin/chkconfig --list %s" % name).split(): + (runlevel, onoff) = tabs.split(":") + if onoff == "on": + ret.append(int(runlevel)) + except: + pass + return ret + + def runlevelDefault(self): + # FIXME: get this from /etc/inittab + return 3 + + def pkgNVRA(self, pkg): + fields = pkg.split("-") + version, release, arch = fields[-3:] + name = "-".join(fields[:-3]) + return (name, version, release, arch) + + def packageResults(self): + localname = commands.getoutput("/bin/uname -n").split(".")[0] + + try: + name = raw_input("Please enter your first initial and last name [%s]: " % localname) + if len(name) == 0: name = localname + + ticketNumber = raw_input("Please enter the case number that you are generating this report for: ") + except KeyboardInterrupt: + print _("<interrupted>") + print _("Temporary files have been stored in ") % self.cInfo['dstroot'] + return + + if len(ticketNumber): + namestr = name + "." + ticketNumber + else: + namestr = name + + ourtempdir = gettempdir() + tarballName = os.path.join(ourtempdir, "sosreport-" + namestr + ".tar.bz2") + + namestr = namestr + "-" + str(random.randint(1, 999999)) + + aliasdir = os.path.join(ourtempdir, namestr) + + tarcmd = "/bin/tar -jcf %s %s" % (tarballName, namestr) + + print + print "Creating compressed tar archive..." + if not os.access(string.split(tarcmd)[0], os.X_OK): + print "Unable to create tarball" + return + + # FIXME: gotta be a better way... + os.system("/bin/mv %s %s" % (self.cInfo['dstroot'], aliasdir)) + curwd = os.getcwd() + os.chdir(ourtempdir) + oldmask = os.umask(077) + # pylint: disable-msg = W0612 + status, shout, runtime = sosGetCommandOutput(tarcmd) + os.umask(oldmask) + os.chdir(curwd) + # FIXME: use python internal command + os.system("/bin/mv %s %s" % (aliasdir, self.cInfo['dstroot'])) + + sys.stdout.write("\n") + print "Your sosreport has been generated and saved in %s" % tarballName + print "Please send this file to your support representative." + sys.stdout.write("\n") + + return + diff --git a/trunk/src/locale/en/LC_MESSAGES/sos.po b/trunk/src/locale/en/LC_MESSAGES/sos.po new file mode 100644 index 00000000..c103a495 --- /dev/null +++ b/trunk/src/locale/en/LC_MESSAGES/sos.po @@ -0,0 +1,99 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2007-07-14 11:57\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: utf-8\n" +"Generated-By: pygettext.py 1.5\n" + + +#: sosreport:399 +msgid "sosreport (version %s)" +msgstr "" + +#: sosreport:417 +msgid "plugin %s does not validate, skipping" +msgstr "" + +#: sosreport:421 +msgid "plug %s skipped (noplugins)" +msgstr "" + +#: sosreport:425 +msgid "plugin %s is inactive (use -e or -o to enable)." +msgstr "" + +#: sosreport:433 +msgid "plugin %s not specified in --onlyplugin list" +msgstr "" + +#: sosreport:438 +msgid "Plugin %s does not install, skipping" +msgstr "" + +#: sosreport:441 +msgid "plugin load failed for %s" +msgstr "" + +#: sosreport:447 +msgid "processing options from plugin: %s" +msgstr "" + +#: sosreport:454 +msgid "no valid plugins found" +msgstr "" + +#: sosreport:457 +msgid "The following plugins are currently enabled:" +msgstr "" + +#: sosreport:463 +msgid "The following plugin options are available:" +msgstr "" + +#: sosreport:469 +msgid "The following plugins are currently disabled:" +msgstr "" + +#: sosreport:480 +msgid "sosreport requires root permissions to run." +msgstr "" + +#: sosreport:487 +msgid "no valid plugins were enabled" +msgstr "" + +#: sosreport:491 +msgid "" +"This utility will collect some detailed information about the\n" +"hardware and setup of your Red Hat Enterprise Linux system.\n" +"This information will be used to diagnose problems with your \n" +"system and will be considered confidential information.\n" +"Red Hat will use this information for diagnostic purposes ONLY.\n" +"\n" +"This process may take a while to complete.\n" +"No changes will be made to your system.\n" +"\n" +"Press ENTER to continue, or CTRL-C to quit.\n" +msgstr "" + +#: sosreport:529 +msgid "Exiting." +msgstr "" + +#: sosreport:676 +msgid "Collected information is in " +msgstr "" + +#: sosreport:677 +msgid "Your html report is in " +msgstr "" + diff --git a/trunk/src/locale/it/LC_MESSAGES/sos.po b/trunk/src/locale/it/LC_MESSAGES/sos.po new file mode 100644 index 00000000..0b5e2b32 --- /dev/null +++ b/trunk/src/locale/it/LC_MESSAGES/sos.po @@ -0,0 +1,99 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"POT-Creation-Date: 2007-07-14 12:17\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: utf-8\n" +"Generated-By: pygettext.py 1.5\n" + + +#: sosreport:399 +msgid "sosreport (version %s)" +msgstr "sosreport (versione %s)" + +#: sosreport:417 +msgid "plugin %s does not validate, skipping" +msgstr "" + +#: sosreport:421 +msgid "plug %s skipped (noplugins)" +msgstr "" + +#: sosreport:425 +msgid "plugin %s is inactive (use -e or -o to enable)." +msgstr "" + +#: sosreport:433 +msgid "plugin %s not specified in --onlyplugin list" +msgstr "" + +#: sosreport:438 +msgid "Plugin %s does not install, skipping" +msgstr "" + +#: sosreport:441 +msgid "plugin load failed for %s" +msgstr "" + +#: sosreport:447 +msgid "processing options from plugin: %s" +msgstr "" + +#: sosreport:454 +msgid "no valid plugins found" +msgstr "" + +#: sosreport:457 +msgid "The following plugins are currently enabled:" +msgstr "" + +#: sosreport:463 +msgid "The following plugin options are available:" +msgstr "" + +#: sosreport:469 +msgid "The following plugins are currently disabled:" +msgstr "" + +#: sosreport:480 +msgid "sosreport requires root permissions to run." +msgstr "" + +#: sosreport:487 +msgid "no valid plugins were enabled" +msgstr "" + +#: sosreport:491 +msgid "" +"This utility will collect some detailed information about the\n" +"hardware and setup of your Red Hat Enterprise Linux system.\n" +"This information will be used to diagnose problems with your \n" +"system and will be considered confidential information.\n" +"Red Hat will use this information for diagnostic purposes ONLY.\n" +"\n" +"This process may take a while to complete.\n" +"No changes will be made to your system.\n" +"\n" +"Press ENTER to continue, or CTRL-C to quit.\n" +msgstr "" + +#: sosreport:529 +msgid "Exiting." +msgstr "" + +#: sosreport:676 +msgid "Collected information is in " +msgstr "" + +#: sosreport:677 +msgid "Your html report is in " +msgstr "" + diff --git a/trunk/src/pylintrc b/trunk/src/pylintrc new file mode 100644 index 00000000..934f5255 --- /dev/null +++ b/trunk/src/pylintrc @@ -0,0 +1,354 @@ +# lint Python modules using external checkers. +# +# This is the main checker controling the other ones and the reports +# generation. It is itself both a raw checker and an astng checker in order +# to: +# * handle message activation / deactivation at the module level +# * handle some basic but necessary stats'data (number of classes, methods...) +# +# This checker also defines the following reports: +# * R0001: Total errors / warnings +# * R0002: % errors / warnings by module +# * R0003: Messages +# * R0004: Global evaluation +[MASTER] + +# Profiled execution. +profile=no + +# Add <file or directory> to the black list. It should be a base name, not a +# path. You may set this option multiple times. +ignore=CVS + +# Pickle collected data for later comparisons. +persistent=yes + +# Set the cache size for astng objects. +cache-size=500 + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + + +[REPORTS] + +# Tells wether to display a full report or only the messages +reports=yes + +# Use HTML as output format instead of text +html=no + +# Use a parseable text output format, so your favorite text editor will be able +# to jump to the line corresponding to a message. +parseable=yes + +# Colorizes text output using ansi escape codes +color=no + +# Put messages in a separate file for each module / package specified on the +# command line instead of printing them on stdout. Reports (if any) will be +# written in a file name "pylint_global.[txt|html]". +files-output=no + +# Python expression which should return a note less than 10 (10 is the highest +# note).You have access to the variables errors warning, statement which +# respectivly contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (R0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Add a comment according to your evaluation note. This is used by the global +# evaluation report (R0004). +comment=no + +# Include message's id in output +include-ids=yes + + +# checks for +# * unused variables / imports +# * undefined variables +# * redefinition of variable from builtins or from an outer scope +# * use of variable before assigment +# +[VARIABLES] + +# Enable / disable this checker +enable-variables=yes + +# Tells wether we should check for unused import in __init__ files. +init-import=no + +# A regular expression matching names used for dummy variables (i.e. not used). +dummy-variables-rgx=_|dummy + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + + +# try to find bugs in the code using type inference +# +[TYPECHECK] + +# Enable / disable this checker +enable-typecheck=yes + +# Tells wether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# When zope mode is activated, consider the acquired-members option to ignore +# access to some undefined attributes. +zope=no + +# List of members which are usually get through zope's acquisition mecanism and +# so shouldn't trigger E0201 when accessed (need zope=yes to be considered. +acquired-members=REQUEST,acl_users,aq_parent + + +# checks for : +# * doc strings +# * modules / classes / functions / methods / arguments / variables name +# * number of arguments, local variables, branchs, returns and statements in +# functions, methods +# * required module attributes +# * dangerous default values as arguments +# * redefinition of function / method / class +# * uses of the global statement +# +# This checker also defines the following reports: +# * R0101: Statistics by type +[BASIC] + +# Enable / disable this checker +enable-basic=yes + +#disable-msg=C0121 + +# Required attributes for module, separated by a comma +required-attributes= + +# Regular expression which should only match functions or classes name which do +# not require a docstring +no-docstring-rgx=__.*__ + +# Regular expression which should only match correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Regular expression which should only match correct module level names +const-rgx=(([A-Z_][A-Z1-9_]*)|(__.*__))$ + +# Regular expression which should only match correct class names +class-rgx=[A-Z_][a-zA-Z0-9]+$ + +# Regular expression which should only match correct function names +function-rgx=[a-z_][A-Za-z0-9_]{2,30}$ + +# Regular expression which should only match correct method names +method-rgx=[a-z_][A-Za-z0-9_]{2,30}$ + +# Regular expression which should only match correct instance attribute names +attr-rgx=[a-z_][A-Za-z0-9_]{2,30}$ + +# Regular expression which should only match correct argument names +argument-rgx=[a-z_][A-Za-z0-9_]{2,30}$ + +# Regular expression which should only match correct variable names +variable-rgx=[a-z_][A-Za-z0-9_]{0,30}$ + +# Regular expression which should only match correct list comprehension / +# generator expression variable names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Good variable names which should always be accepted, separated by a comma +good-names=i,j,k,ex,Run,_ + +# Bad variable names which should always be refused, separated by a comma +bad-names=foo,bar,baz,toto,tutu,tata + +# List of builtins function names that should not be used, separated by a comma +bad-functions=map,filter,apply,input + + +# checks for sign of poor/misdesign: +# * number of methods, attributes, local variables... +# * size, complexity of functions, methods +# +[DESIGN] + +# Enable / disable this checker +enable-design=yes + +# Maximum number of arguments for function / method +max-args=5 + +# Maximum number of locals for function / method body +max-locals=15 + +# Maximum number of return / yield for function / method body +max-returns=6 + +# Maximum number of branch for function / method body +max-branchs=12 + +# Maximum number of statements in function / method body +max-statements=50 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + + +# checks for : +# * methods without self as first argument +# * overriden methods signature +# * access only to existant members via self +# * attributes not defined in the __init__ method +# * supported interfaces implementation +# * unreachable code +# +[CLASSES] + +# Enable / disable this checker +enable-classes=yes + +# List of interface methods to ignore, separated by a comma. This is used for +# instance to not check methods defines in Zope's Interface base class. +ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp + + +# checks for +# * external modules dependencies +# * relative / wildcard imports +# * cyclic imports +# * uses of deprecated modules +# +# This checker also defines the following reports: +# * R0401: External dependencies +# * R0402: Modules dependencies graph +[IMPORTS] + +# Enable / disable this checker +enable-imports=no + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=regsub,string,TERMIOS,Bastion,rexec + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report R0402 must not be disabled) +import-graph= + +# Create a graph of external dependencies in the given file (report R0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of internal dependencies in the given file (report R0402 must +# not be disabled) +int-import-graph= + + +# checks for usage of new style capabilities on old style classes and +# other new/old styles conflicts problems +# * use of property, __slots__, super +# * "super" usage +# * raising a new style class as exception +# +[NEWSTYLE] + +# Enable / disable this checker +enable-newstyle=yes + + +# checks for +# * excepts without exception filter +# * string exceptions +# +[EXCEPTIONS] + +# Enable / disable this checker +enable-exceptions=yes + + +# checks for : +# * unauthorized constructions +# * strict indentation +# * line length +# * use of <> instead of != +# +[FORMAT] + +# Enable / disable this checker +enable-format=yes + +# Maximum number of characters on a single line. +max-line-length=132 + +# Maximum number of lines in a module +max-module-lines=1000 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + + +# checks for similarities and duplicated code. This computation may be +# memory / CPU intensive, so you should disable it if you experiments some +# problems. +# +# This checker also defines the following reports: +# * R0801: Duplication +[SIMILARITIES] + +# Enable / disable this checker +enable-similarities=yes + +# Minimum lines number of a similarity. +min-similarity-lines=4 + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + + +# checks for: +# * warning notes in the code like FIXME, XXX +# * PEP 263: source code with non ascii character but no encoding declaration +# +[MISCELLANEOUS] + +# Enable / disable this checker +enable-miscellaneous=yes + +# List of note tags to take in consideration, separated by a comma. Default to +# FIXME, XXX, TODO +notes=FIXME,XXX,TODO + + +# does not check anything but gives some raw metrics : +# * total number of lines +# * total number of code lines +# * total number of docstring lines +# * total number of comments lines +# * total number of empty lines +# +# This checker also defines the following reports: +# * R0701: Raw metrics +[METRICS] + +# Enable / disable this checker +enable-metrics=no diff --git a/trunk/src/setup.py b/trunk/src/setup.py new file mode 100644 index 00000000..26504147 --- /dev/null +++ b/trunk/src/setup.py @@ -0,0 +1,14 @@ +""" +setup.py - Setup package with the help from Python's DistUtils +""" + +from distutils.core import setup + +setup( + name = 'sos', + packages = ['sos', 'sos.plugins'], + scripts = [], + package_dir = {'': 'lib',}, + data_files = [ ('/usr/sbin', ['sosreport']), ('/usr/share/man/man1', ['sosreport.1']), ('/usr/share/locale/en', []), ('/usr/share/locale/it', []), ('/usr/share/locale/en/LC_MESSAGES', ['locale/en/LC_MESSAGES/sos.mo']), ('/usr/share/locale/it/LC_MESSAGES', ['locale/it/LC_MESSAGES/sos.mo']) + ] +) diff --git a/trunk/src/sos.spec b/trunk/src/sos.spec new file mode 100644 index 00000000..4b4237d7 --- /dev/null +++ b/trunk/src/sos.spec @@ -0,0 +1,145 @@ +%{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")} + +%define name sos +%define version 1.7 +%define release 1 + +%define _localedir %_datadir/locale + +Summary: A set of tools to gather troubleshooting information from a system +Name: %{name} +Version: %{version} +Release: %{release}%{?dist} +# The source for this package was pulled from upstream's svn. Use the +# following commands to generate the tarball: +# svn --username guest export https://sos.108.redhat.com/svn/sos/tags/r1-6 sos-1.6 +# tar -czvf sos-1.6.tar.gz sos-1.6 +Source0: %{name}-%{version}.tar.gz +License: GPL +Group: Development/Libraries +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot +BuildArch: noarch +Url: http://sos.108.redhat.com/ +BuildRequires: python-devel + +%description +Sos is a set of tools that gathers information about system +hardware and configuration. The information can then be used for +diagnostic purposes and debugging. Sos is commonly used to help +support technicians and developers. + +%prep +%setup -q + +%build +python setup.py build + +%install +rm -rf ${RPM_BUILD_ROOT} +python setup.py install --optimize 1 --root=$RPM_BUILD_ROOT + +%clean +rm -rf ${RPM_BUILD_ROOT} + +%files +%defattr(-,root,root,-) +%{_sbindir}/sosreport +%{python_sitelib}/sos/ +%{_mandir}/man1/sosreport.1* +%{_localedir}/*/LC_MESSAGES/sos.mo +%doc README TODO LICENSE ChangeLog + +%changelog +* Mon Jul 5 2007 Navid Sheikhol-Eslami <navid at redhat dot com> - 1.6-5 +- Yet more fixes to make package Fedora compliant. + +* Mon Jul 5 2007 Navid Sheikhol-Eslami <navid at redhat dot com> - 1.6-4 +- More fixes to make package Fedora compliant. + +* Mon Jul 2 2007 Navid Sheikhol-Eslami <navid at redhat dot com> - 1.6-3 +- Other fixes to make package Fedora compliant. + +* Mon Jul 2 2007 Navid Sheikhol-Eslami <navid at redhat dot com> - 1.6-2 +- Minor fixes. + +* Mon Jul 2 2007 Navid Sheikhol-Eslami <navid at redhat dot com> - 1.6-1 +- Beautified output of --list-plugins. +- GPL licence is now included in the package. +- added python-devel requirement for building package +- fixed incompatibility with python from RHEL4 + +* Fri May 25 2007 Steve Conklin <sconklin at redhat dot com> - 1.5-1 +- Bumped version + +* Fri May 25 2007 Steve Conklin <sconklin at redhat dot com> - 1.4-2 +- Fixed a backtrace on nonexistent file in kernel plugin (thanks, David Robinson) + +* Mon Apr 30 2007 Steve Conklin <sconklin at redhat dot com> - 1.4-1 +- Fixed an error in option handling +- Forced the file generated by traceroute to not end in .com +- Fixed a problem with manpage +- Added optional traceroute collection to networking plugin +- Added clalance's patch to gather iptables info. +- Fixes to the device-mapper plugin +- Fixed a problem with installation of man page + +* Mon Apr 16 2007 Steve Conklin <sconklin at redhat dot com> - 1.3-3 +- including patches to fix the following: +- Resolves: bz219745 sosreport needs a man page +- Resolves: bz219667 sosreport does not terminate cleanly on ^C +- Resolves: bz233375 Make SOS flag the situation when running on a fully virtu... +- Resolves: bz234873 rhel5 sos needs to include rpm-va by default +- Resolves: bz219669 sosreport multi-threaded option sometimes fails +- Resolves: bz219671 RFE for sosreport - allow specification of plugins to be run +- Resolves: bz219672 RFE - show progress while sosreport is running +- Resolves: bz219673 Add xen information gathering to sosreport +- Resolves: bz219675 Collect information related to the new driver update model +- Resolves: bz219877 'Cancel' button during option selection only cancels sele... + +* Tue Feb 20 2007 John Berninger <jwb at redhat dot com> - 1.3-2 +- Add man page + +* Fri Dec 15 2006 Steve Conklin <sconklin at redhat dot com> - 1.3-1 +- really fixed bz_219654 + +* Fri Dec 15 2006 Steve Conklin <sconklin at redhat dot com> - 1.2-1 +- fixed a build problem + +* Fri Dec 15 2006 Steve Conklin <sconklin at redhat dot com> - 1.1-1 +- Tighten permissions of tmp directory so only readable by creator bz_219657 +- Don't print message 'Problem at path ...' bz_219654 +- Removed useless message bz_219670 +- Preserve file modification times bz_219674 +- Removed unneeded message about files on copyProhibitedList bz_219712 + +* Wed Aug 30 2006 Steve Conklin <sconklin at redhat dot com> - 1.0-1 +- Seperated upstream and RPM versioning + +* Mon Aug 21 2006 Steve Conklin <sconklin at redhat dot com> - 0.1-11 +- Code cleanup, fixed a regression in threading + +* Mon Aug 14 2006 Steve Conklin <sconklin at redhat dot com> - 0.1-10 +- minor bugfixes, added miltithreading option, setup now quiet + +* Mon Jul 17 2006 Steve Conklin <sconklin at redhat dot com> - 0.1-9 +- migrated to svn on 108.redhat.com, fixed a problem with command output linking in report + +* Mon Jun 19 2006 Steve Conklin <sconklin at redhat dot com> - 0.1-6 +- Added LICENSE file containing GPL + +* Wed May 31 2006 Steve Conklin <sconklin at redhat dot com> - 0.1-5 +- Added fixes to network plugin and prepped for Fedora submission + +* Wed May 31 2006 John Berninger <jwb at redhat dot com> - 0.1-4 +- Reconsolidated subpackages into one package per discussion with sconklin + +* Mon May 22 2006 John Berninger <jwb at redhat dot com> - 0.1-3 +- Added ftp, ldap, mail, named, samba, squid SOS plugins +- Fixed various errors in kernel and hardware plugins + +* Mon May 22 2006 John Benringer <jwb at redhat dot com> - 0.1-2 +- split off cluster plugin into subpackage +- correct file payload lists + +* Mon May 22 2006 John Berninger <jwb at redhat dot com> - 0.1-1 +- initial package build diff --git a/trunk/src/sosreport b/trunk/src/sosreport new file mode 100755 index 00000000..86d66b4d --- /dev/null +++ b/trunk/src/sosreport @@ -0,0 +1,725 @@ +#!/usr/bin/env python +""" +Gather information about a system and report it using plugins +supplied for application-specific information +""" +## sosreport.py +## gather information about a system and report it + +## Copyright (C) 2006 Steve Conklin <sconklin@redhat.com> + +### 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +# pylint: disable-msg = W0611 +# pylint: disable-msg = W0702 + +import sys +import os +#import curses +from optparse import OptionParser, Option +import sos.policyredhat +from sos.helpers import * +from snack import * +from threading import Thread, activeCount, enumerate +import signal +import logging +from stat import * +from time import strftime, localtime, time +from pwd import getpwuid +import gettext +from threading import Semaphore + +__version__ = 1.7 + +__breakHits__ = 0 # Use this to track how many times we enter the exit routine + +## Set up routines to be linked to signals for termination handling +def exittermhandler(signum, frame): + doExitCode() + +def doExitCode(): + global __breakHits__ + __breakHits__ += 1 + if ( ( activeCount() > 1 ) and ( __breakHits__ == 1 ) ): + print "SIGTERM received, multiple threads detected, waiting for all threads to exit" + for thread in enumerate(): + if thread.getName() != "MainThread": + thread.join() + print "All threads ended, cleaning up." + if ( ( activeCount() > 1 ) and ( __breakHits__ > 1 ) ): + print "Multiple SIGTERMs, multiple threads, attempting to signal threads to die immediately" + ## FIXME: Add thread-kill code (see FIXME below) + print "Threads dead, cleaning up." + if ( ( activeCount() == 1 ) and ( __breakHits__ > 2 ) ): + print "Multiple SIGTERMs, single thread, exiting without cleaning up." + sys.exit(3) + + # FIXME: Add code here to clean up /tmp + sys.exit("Abnormal exit") + +# Handle any sort of exit signal cleanly +# Currently, we intercept only sig 15 (TERM) +signal.signal(signal.SIGTERM, exittermhandler) + +## FIXME: Need to figure out how to IPC with child threads in case of +## multiple SIGTERMs. +## FIXME: Need to figure out how to handle SIGKILL - we can't intercept it. + +# for debugging +__raisePlugins__ = 1 + +class SosOption (Option): + """Allow to specify comma delimited list of plugins""" + ACTIONS = Option.ACTIONS + ("extend",) + STORE_ACTIONS = Option.STORE_ACTIONS + ("extend",) + TYPED_ACTIONS = Option.TYPED_ACTIONS + ("extend",) + + def take_action(self, action, dest, opt, value, values, parser): + if action == "extend": + try: lvalue = value.split(",") + except: pass + else: values.ensure_value(dest, []).extend(lvalue) + else: + Option.take_action(self, action, dest, opt, value, values, parser) + +__cmdParser__ = OptionParser(option_class=SosOption) +__cmdParser__.add_option("-a", "--alloptions", action="store_true", \ + dest="usealloptions", default=False, \ + help="Use all options for loaded plugins") +__cmdParser__.add_option("-f", "--fastoptions", action="store_true", \ + dest="fastoptions", default=False, \ + help="Use only fast options for loaded plugins") +__cmdParser__.add_option("-g", "--gatheronly", action="store_true", \ + dest="gatheronly", default=False, \ + help="Gather information locally but don't package or submit") +__cmdParser__.add_option("-l", "--list-plugins", action="store_true", \ + dest="listPlugins", default=False, \ + help="list existing plugins") +__cmdParser__.add_option("-n", "--noplugin", action="extend", \ + dest="noplugins", type="string", \ + help="skip these plugins", default = []) +__cmdParser__.add_option("-o", "--onlyplugin", action="extend", \ + dest="onlyplugins", type="string", \ + help="enable these plugins only", default = []) +__cmdParser__.add_option("-e", "--enableplugin", action="extend", \ + dest="enableplugins", type="string", \ + help="list of inactive plugins to be enabled", default = []) +__cmdParser__.add_option("-k", "--pluginopts", action="extend", \ + dest="plugopts", type="string", \ + help="plugin options in plugin_name.option=value format") +__cmdParser__.add_option("-v", "--verbose", action="count", \ + dest="verbosity", \ + help="How obnoxious we're being about telling the user what we're doing.") +__cmdParser__.add_option("-c", "--curses", action="store_true", \ + dest="use_curses", default=False, \ + help="Display a text GUI menu to modify plugin options.") +__cmdParser__.add_option("--no-progressbar", action="store_false", \ + dest="progressbar", default=True, \ + help="Do not display a progress bar.") +__cmdParser__.add_option("--no-multithread", action="store_true", \ + dest="nomultithread", \ + help="Disable multi-threaded gathering mode (slower)", default=False) +(__cmdLineOpts__, __cmdLineArgs__)=__cmdParser__.parse_args() + +def textcolor(text, fg, bg=None, raw=0): + colors = { "black":"30", "red":"31", "green":"32", "brown":"33", "blue":"34", + "purple":"35", "cyan":"36", "lgray":"37", "gray":"1;30", "lred":"1;31", + "lgreen":"1;32", "yellow":"1;33", "lblue":"1;34", "pink":"1;35", + "lcyan":"1;36", "white":"1;37" } + opencol = "\033[" + closecol = "m" + clear = opencol + "0" + closecol + f = opencol + colors[fg] + closecol + return "%s%s%s" % (f, text, clear) + +def get_curse_options(alloptions): + """ + use curses to enable the user to select some options + """ + # alloptions is an array of (plug, plugname, optname, parms(dictionary)) tuples + plugName = [] + out = [] + + # get a sorted list of all plugin names + for rrr in alloptions: + if rrr[1] not in plugName: + plugName.append(rrr[1]) + + plugName.sort() + plugCbox = CheckboxTree(height=5, scroll=1) + + countOpt = -1 + + optDic = {} + optDicCounter = 0 + + # iterate over all plugins with options + for curPlugName in plugName: + plugCbox.addItem(curPlugName, (snackArgs['append'],)) + countOpt = countOpt+1 + + for opt in alloptions: + if opt[1] != curPlugName: + continue + + snt = opt[2] + " ("+opt[3]['desc']+") is " + opt[3]['speed'] + plugCbox.addItem(snt, (countOpt, snackArgs['append']), item = optDicCounter, selected = opt[3]['enabled']) + optDic[optDicCounter] = opt + optDicCounter += 1 + + + screen = SnackScreen() + bb = ButtonBar(screen, (("Ok", "ok"), ("Cancel", "cancel"))) + g = GridForm(screen, "Select Sosreport Options", 1, 10) + g.add(plugCbox, 0, 0) + g.add(bb, 0, 1, growx = 1) + result = g.runOnce() + + screen.finish() + + if bb.buttonPressed(result) == "cancel": + raise "Cancelled" + + for rrr in range(0, optDicCounter): + optDic[rrr][3]['enabled'] = plugCbox.getEntryValue(rrr)[1] + out.append((optDic[rrr])) + + return out + +class progressBar: + def __init__(self, minValue = 0, maxValue = 10, totalWidth=40): + self.progBar = "[]" # This holds the progress bar string + self.min = minValue + self.max = maxValue + self.width = totalWidth + self.amount = 0 # When amount == max, we are 100% done + self.time_start = time() + self.eta = 0 + self.last_amount_update = time() + self.update() + + def updateAmount(self, newAmount = 0): + if newAmount < self.min: newAmount = self.min + if newAmount > self.max: newAmount = self.max + if self.amount != newAmount: + self.last_amount_update = time() + self.amount = newAmount + last_update_relative = round(self.last_amount_update - self.time_start) + self.eta = round(last_update_relative * self.max / self.amount) + + # generate ETA + timeElapsed = round(time() - self.time_start) + last_update_relative = round(self.last_amount_update - self.time_start) + if timeElapsed >= 10 and self.amount > 0: + percentDone = round(timeElapsed * 100 / self.eta) + if percentDone > 100: + percentDone = 100 + ETA = timeElapsed + elif self.eta < timeElapsed: + ETA = timeElapsed + else: + ETA = self.eta + ETA = "[%02d:%02d/%02d:%02d]" % (round(timeElapsed/60), timeElapsed % 60, round(ETA/60), ETA % 60) + else: + ETA = "[%02d:%02d/--:--]" % (round(timeElapsed/60), timeElapsed % 60) + if self.amount < self.max: + percentDone = 0 + else: + percentDone = 100 + + # Figure out how many hash bars the percentage should be + allFull = self.width - 2 + numHashes = (percentDone / 100.0) * allFull + numHashes = int(round(numHashes)) + + # build a progress bar with hashes and spaces + self.progBar = " [" + '#'*numHashes + ' '*(allFull-numHashes) + "]" + + # figure out where to put the percentage, roughly centered + percentPlace = (len(self.progBar) / 2) - len(str(percentDone)) + percentString = str(percentDone) + "%" + + # slice the percentage into the bar + self.progBar = " Progress" + self.progBar[0:percentPlace] + percentString + self.progBar[percentPlace+len(percentString):] + ETA + + def incAmount(self, toInc = 1): + self.updateAmount(self.amount+toInc) + + def finished(self): + self.updateAmount(self.max) + sys.stdout.write(self.progBar + '\n') + sys.stdout.flush() + + def update(self): + self.updateAmount(self.amount) + sys.stdout.write(self.progBar + '\r') + sys.stdout.flush() + +class XmlReport: + def __init__(self): + try: + import libxml2 + except: + self.enabled = False + return + else: + self.enabled = True + self.doc = libxml2.newDoc("1.0") + self.root = self.doc.newChild(None, "sos", None) + self.commands = self.root.newChild(None, "commands", None) + self.files = self.root.newChild(None, "files", None) + + def add_command(self,cmdline,exitcode,stdout = None,stderr = None,f_stdout=None,f_stderr=None, runtime=None): + if not self.enabled: return + + cmd = self.commands.newChild(None, "cmd", None) + + cmd.setNsProp(None, "cmdline", cmdline) + + cmdchild = cmd.newChild(None, "exitcode", str(exitcode)) + + if runtime: + cmd.newChild(None, "runtime", str(runtime)) + + if stdout or f_stdout: + cmdchild = cmd.newChild(None, "stdout", stdout) + if f_stdout: + cmdchild.setNsProp(None, "file", f_stdout) + + if stderr or f_stderr: + cmdchild = cmd.newChild(None, "stderr", stderr) + if f_stderr: + cmdchild.setNsProp(None, "file", f_stderr) + + def add_file(self,fname,stats): + if not self.enabled: return + + cfile = self.files.newChild(None,"file",None) + + cfile.setNsProp(None, "fname", fname) + + cchild = cfile.newChild(None, "uid", str(stats[ST_UID])) + cchild.setNsProp(None,"name", getpwuid(stats[ST_UID])[0]) + cchild = cfile.newChild(None, "gid", str(stats[ST_GID])) + cchild.setNsProp(None,"name", getpwuid(stats[ST_GID])[0]) + cfile.newChild(None, "mode", str(oct(S_IMODE(stats[ST_MODE])))) + cchild = cfile.newChild(None, "ctime", strftime('%a %b %d %H:%M:%S %Y', localtime(stats[ST_CTIME]))) + cchild.setNsProp(None,"tstamp", str(stats[ST_CTIME])) + cchild = cfile.newChild(None, "atime", strftime('%a %b %d %H:%M:%S %Y', localtime(stats[ST_ATIME]))) + cchild.setNsProp(None,"tstamp", str(stats[ST_ATIME])) + cchild = cfile.newChild(None, "mtime", strftime('%a %b %d %H:%M:%S %Y', localtime(stats[ST_MTIME]))) + cchild.setNsProp(None,"tstamp", str(stats[ST_MTIME])) + + def serialize(self): + if not self.enabled: return + + print self.doc.serialize(None, 1) + + def serialize_to_file(self,fname): + if not self.enabled: return + + outfn = open(fname,"w") + outfn.write(self.doc.serialize(None,1)) + outfn.close() + +def sosreport(): + # pylint: disable-msg = R0912 + # pylint: disable-msg = R0914 + # pylint: disable-msg = R0915 + """ + This is the top-level function that gathers and processes all sosreport information + """ + loadedplugins = [] + skippedplugins = [] + alloptions = [] + + # perhaps we should automatically locate the policy module?? + policy = sos.policyredhat.SosPolicy() + + # find the plugins path + paths = sys.path + for path in paths: + if path.strip()[-len("site-packages"):] == "site-packages": + pluginpath = path + "/sos/plugins" + reporterpath = path + "/sos/reporters" + + # Set up common info and create destinations + + dstroot = sosFindTmpDir() + cmddir = os.path.join(dstroot, "sos_commands") + logdir = os.path.join(dstroot, "sos_logs") + rptdir = os.path.join(dstroot, "sos_reports") + os.mkdir(cmddir, 0755) + os.mkdir(logdir, 0755) + os.mkdir(rptdir, 0755) + + # initialize i18n language localization + gettext.install('sos', '/usr/share/locale', unicode=False) + + # initialize logging + soslog = logging.getLogger('sos') + soslog.setLevel(logging.DEBUG) + + # log to a file + flog = logging.FileHandler(logdir + "/sos.log") + flog.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s')) + flog.setLevel(logging.DEBUG) + soslog.addHandler(flog) + + # define a Handler which writes INFO messages or higher to the sys.stderr + console = logging.StreamHandler(sys.stderr) + if __cmdLineOpts__.verbosity > 0: + console.setLevel(20 - __cmdLineOpts__.verbosity) + __cmdLineOpts__.progressbar = False + else: + console.setLevel(logging.INFO) + console.setFormatter(logging.Formatter('%(message)s')) + soslog.addHandler(console) + + logging.VERBOSE = logging.INFO - 1 + logging.VERBOSE2 = logging.INFO - 2 + logging.VERBOSE3 = logging.INFO - 3 + logging.addLevelName(logging.VERBOSE, "verbose") + logging.addLevelName(logging.VERBOSE2,"verbose2") + logging.addLevelName(logging.VERBOSE3,"verbose3") + + xmlrep = XmlReport() + + # set up dict so everyone can share the following + commons = {'dstroot': dstroot, 'cmddir': cmddir, 'logdir': logdir, 'rptdir': rptdir, + 'soslog': soslog, 'policy': policy, 'verbosity' : __cmdLineOpts__.verbosity, + 'xmlreport' : xmlrep } + + # Make policy aware of the commons + policy.setCommons(commons) + + print + soslog.info ( _("sosreport (version %s)") % __version__) + print + + # generate list of available plugins + plugins = os.listdir(pluginpath) + plugins.sort() + + # validate and load plugins + for plug in plugins: + plugbase = plug[:-3] + if not plug[-3:] == '.py' or plugbase == "__init__": + continue + try: + #print "importing plugin: %s" % plugbase + try: + if policy.validatePlugin(pluginpath + plug): + pluginClass = importPlugin("sos.plugins." + plugbase, plugbase) + else: + soslog.warning(_("plugin %s does not validate, skipping") % plug) + skippedplugins.append((plugbase, pluginClass(plugbase, commons))) + continue + if plugbase in __cmdLineOpts__.noplugins: + soslog.log(logging.VERBOSE, _("plug %s skipped (noplugins)") % plugbase) + skippedplugins.append((plugbase, pluginClass(plugbase, commons))) + continue + if not pluginClass(plugbase, commons).checkenabled() and not plugbase in __cmdLineOpts__.enableplugins and not plugbase in __cmdLineOpts__.onlyplugins: + soslog.log(logging.VERBOSE, _("plugin %s is inactive (use -e or -o to enable).") % plug) + skippedplugins.append((plugbase, pluginClass(plugbase, commons))) + continue + if not pluginClass(plugbase, commons).defaultenabled() and not plugbase in __cmdLineOpts__.enableplugins and not plugbase in __cmdLineOpts__.onlyplugins: + soslog.log(logging.VERBOSE, "plugin %s not loaded by default (use -e or -o to enable)." % plug) + skippedplugins.append((plugbase, pluginClass(plugbase, commons))) + continue + if __cmdLineOpts__.onlyplugins and not plugbase in __cmdLineOpts__.onlyplugins: + soslog.log(logging.VERBOSE, _("plugin %s not specified in --onlyplugin list") % plug) + skippedplugins.append((plugbase, pluginClass(plugbase, commons))) + continue + loadedplugins.append((plugbase, pluginClass(plugbase, commons))) + except: + soslog.warning(_("plugin %s does not install, skipping") % plug) + raise + except: + soslog.warning(_("could not load plugin %s") % plug) + if __raisePlugins__: + raise + + # First, gather and process options + for plugname, plug in loadedplugins: + soslog.log(logging.VERBOSE3, _("processing options from plugin: %s") % plugname) + names, parms = plug.getAllOptions() + for optname, optparm in zip(names, parms): + alloptions.append((plug, plugname, optname, optparm)) + + if __cmdLineOpts__.listPlugins: + if not len(loadedplugins) and not len(skippedplugins): + soslog.error(_("no valid plugins found")) + sys.exit(1) + + if len(loadedplugins): + print _("The following plugins are currently enabled:") + print + for (plugname,plug) in loadedplugins: + print " %-25s %s" % (textcolor(plugname,"lblue"),plug.get_description()) + else: + print _("No plugin enabled.") + print + + if len(alloptions): + print _("The following plugin options are available:") + print + for (plug, plugname, optname, optparm) in alloptions: + print " %-25s %s [%d]" % (plugname + "." + optname, optparm["desc"], optparm["enabled"]) + else: + print _("No plugin options available.") + + if len(skippedplugins): + print + print _("The following plugins are currently disabled:") + print + for (plugname,plugclass) in skippedplugins: + print " %-25s %s" % (textcolor(plugname,"blue"),plugclass.get_description()) + + print + sys.exit() + + # to go anywhere further than listing the plugins we will need root permissions. + # + if os.getuid() != 0: + print _('sosreport requires root permissions to run.') + sys.exit(1) + + # we don't need to keep in memory plugins we are not going to use + del skippedplugins + + if not len(loadedplugins): + soslog.error(_("no valid plugins were enabled")) + sys.exit(1) + + try: + raw_input(_("""This utility will collect some detailed information about the +hardware and setup of your Red Hat Enterprise Linux system. +This information will be used to diagnose problems with your +system and will be considered confidential information. +Red Hat will use this information for diagnostic purposes ONLY. + +This process may take a while to complete. +No changes will be made to your system. + +Press ENTER to continue, or CTRL-C to quit. +""")) + except KeyboardInterrupt: + print + sys.exit(0) + + # setup plugin options + if __cmdLineOpts__.plugopts: + opts = {} + for opt in __cmdLineOpts__.plugopts: + try: opt, val = opt.split("=") + except: val=1 + plug, opt = opt.split(".") + try: val = int(val) # try to convert string "val" to int() + except: pass + try: opts[plug] + except KeyError: opts[plug] = [] + opts[plug].append( (opt,val) ) + for plugname, plug in loadedplugins: + if opts.has_key(plugname): + for opt,val in opts[plugname]: + soslog.log(logging.VERBOSE, "setting option %s for plugin %s to %s" % (plugname,opt,val)) + plug.setOption(opt,val) + del opt,opts,val + elif not __cmdLineOpts__.fastoptions and not __cmdLineOpts__.usealloptions: + if len(alloptions) and __cmdLineOpts__.use_curses: + try: + get_curse_options(alloptions) + except "Cancelled": + sys.exit(_("Exiting.")) + elif __cmdLineOpts__.fastoptions: + for i in range(len(alloptions)): + for plug, plugname, optname, optparm in alloptions: + if optparm['speed'] == 'fast': + plug.setOption(optname, 1) + else: + plug.setOption(optname, 0) + elif __cmdLineOpts__.usealloptions: + for i in range(len(alloptions)): + for plug, plugname, optname, optparm in alloptions: + plug.setOption(optname, 1) + + # Call the diagnose() method for each plugin + tmpcount = 0 + for plugname, plug in loadedplugins: + soslog.log(logging.VERBOSE2, "Performing sanity check for plugin %s" % plugname) + plug.diagnose() + tmpcount += len(plug.diagnose_msgs) + if tmpcount > 0: + print _("One or more plugin has detected a problem in your configuration.") + print _("Please review the following messages:") + print + for plugname, plug in loadedplugins: + for msg in plug.diagnose_msgs: + soslog.warning(" * %s: %s", plugname, msg) + print + try: + raw_input( _("Press ENTER to continue, or CTRL-C to quit.\n") ) + except KeyboardInterrupt: + print + sys.exit(0) + + # Call the setup() method for each plugin + for plugname, plug in loadedplugins: + soslog.log(logging.VERBOSE2, "Preloading files and commands to be gathered by plugin %s" % plugname) + plug.setup() + + # Setup the progress bar + if __cmdLineOpts__.progressbar: + # gather information useful for generating ETA + eta_weight = len(loadedplugins) + for plugname, plug in loadedplugins: + eta_weight += plug.eta_weight + pbar = progressBar(minValue = 0, maxValue = eta_weight) + # pbar.max = number_of_plugins + weight (default 1 per plugin) + + if __cmdLineOpts__.nomultithread: + soslog.log(logging.VERBOSE, "using single-threading") + else: + soslog.log(logging.VERBOSE, "using multi-threading") + + # Call the collect method for each plugin + plugrunning = Semaphore(2) + for plugname, plug in loadedplugins: + soslog.log(logging.VERBOSE, "executing plugin %s" % plugname) + if not __cmdLineOpts__.nomultithread: + plug.copyStuff(threaded = True, semaphore = plugrunning) + else: + plug.copyStuff() + if __cmdLineOpts__.progressbar: + pbar.incAmount(plug.eta_weight) + pbar.update() + del plugrunning + + # Wait for all the collection threads to exit + if not __cmdLineOpts__.nomultithread: + finishedplugins = [] + while len(loadedplugins) > 0: + plugname, plug = loadedplugins.pop(0) + if not plug.wait(0.5): + finishedplugins.append((plugname,plug)) + soslog.log(logging.VERBOSE2, "plugin %s has returned" % plugname) + if __cmdLineOpts__.progressbar: + pbar.incAmount(plug.eta_weight) + else: + soslog.log(logging.VERBOSE3, "plugin %s still hasn't returned" % plugname) + loadedplugins.append((plugname,plug)) + if __cmdLineOpts__.progressbar: + pbar.update() + loadedplugins = finishedplugins + del finishedplugins + + xmlrep.serialize_to_file(rptdir + "/" + "sosreport.xml") + + # Call the analyze method for each plugin + for plugname, plug in loadedplugins: + soslog.log(logging.VERBOSE2, "Analyzing results of plugin %s" % plugname,) + try: + plug.analyze() + except: + # catch exceptions in analyse() and keep working + pass + if __cmdLineOpts__.progressbar: + pbar.incAmount() + pbar.update() + + if __cmdLineOpts__.progressbar: + pbar.finished() + sys.stdout.write("\n") + + # Generate the header for the html output file + rfd = open(rptdir + "/" + "sosreport.html", "w") + rfd.write(""" + <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> + <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> + <head> + <link rel="stylesheet" type="text/css" media="screen" href="donot.css" /> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> + <title>Sos System Report</title> + </head> + + <body> + """) + + + # Make a pass to gather Alerts and a list of module names + allAlerts = [] + plugNames = [] + for plugname, plug in loadedplugins: + for alert in plug.alerts: + allAlerts.append('<a href="#%s">%s</a>: %s' % (plugname, plugname, alert)) + plugNames.append(plugname) + + + + # Create a table of links to the module info + rfd.write("<hr/><h3>Loaded Plugins:</h3>") + rfd.write("<table><tr>\n") + rr = 0 + for i in range(len(plugNames)): + rfd.write('<td><a href="#%s">%s</a></td>\n' % (plugNames[i], plugNames[i])) + rr = divmod(i, 4)[1] + if (rr == 3): + rfd.write('</tr>') + if not (rr == 3): + rfd.write('</tr>') + rfd.write('</table>\n') + + rfd.write('<hr/><h3>Alerts:</h3>') + rfd.write('<ul>') + for alert in allAlerts: + rfd.write('<li>%s</li>' % alert) + rfd.write('</ul>') + + + # Call the report method for each plugin + for plugname, plug in loadedplugins: + html = plug.report() + rfd.write(html) + + rfd.write("</body></html>") + + rfd.close() + + # Collect any needed user information (name, etc) + + # Call the postproc method for each plugin + for plugname, plug in loadedplugins: + plug.postproc() + + if __cmdLineOpts__.gatheronly: + soslog.info(_("Collected information is in ") + dstroot) + soslog.info(_("Your html report is in ") + rptdir + "/" + "sosreport.html") + else: + # package up the results for the support organization + policy.packageResults() + # delete gathered files + os.system("/bin/rm -rf %s" % dstroot) + # automated submission will go here + + # Close all log files and perform any cleanup + logging.shutdown() + + +if __name__ == '__main__': + try: + sosreport() + except KeyboardInterrupt: + doExitCode() diff --git a/trunk/src/sosreport.1 b/trunk/src/sosreport.1 new file mode 100644 index 00000000..cf76afdb --- /dev/null +++ b/trunk/src/sosreport.1 @@ -0,0 +1,55 @@ +.TH SOSREPORT 1 "Tue Feb 20 2007" +.SH NAME +sosreport \- Generate debugging information for this system +.SH SYNOPSIS +.B sosreport +[-a|--alloptions] [-f|--fastoptions] [-g|--gatheronly] + [-l|--list-plugins] [-n|--noplugin \fIplugin-name\fR] + [-o|--onlyplugin \fIplugin-name\fR] + [-v|--verbose [...]] [-m|--multithreaded] +.SH DESCRIPTION +\fBsosreport\fR generates a compressed tarball of debugging information +for the system it is run on that can be sent to technical support +reps that will give them a more complete view of the overall system +status. +.SH OPTIONS +.TP +.B \-a, \--alloptions +Enable all options for all loaded plugins +.TP +.B \-f, \--fastoptions +Enable all options marked as "fast" for loaded plugins. This will +reduce running time while still gathering helpful information, but +you may be asked to re-run later with some or all "slow" options +enabled depending on the specific issue. +.TP +.B \-g, \--gatheronly +Gather the diagnostic data and bundle it up, but do not attempt to +send it to any support site. This option currently has no effect as +sosreport currently does not support report transmission. +.TP +.B \-l, \--list-plugins +List available plugins +.TP +.B \-n, \--noplugin +Do not load specified plugin(s) +.TP +.B \-o, \--onlyplugin +Load only the specified plugin(s), all otherplugins should be disabled +.TP +.B \-v, \--verbose +Increase the verbosity of the output as sosreport is running. This option +can be specified more than once. +.TP +.B \-m, \--multithreaded +Enable a multithreaded collection and analysis of the sosreport data. Please +note that this option is experimental and is known to have intermittent issues. +.SH BUGS +The multithreaded option can fail intermittently, please use it with care. +.SH AUTHORS +.nf +Steve Conklin <sconklin@redhat.com> +John Berninger <jwb@redhat.com> +Navid Sheikhol-Eslami <navid@redhat.com> +Pierre Amadio <pamadio@redhat.com> +.fi diff --git a/trunk/src/tools/msgfmt.py b/trunk/src/tools/msgfmt.py new file mode 100644 index 00000000..8a2d4e66 --- /dev/null +++ b/trunk/src/tools/msgfmt.py @@ -0,0 +1,203 @@ +#! /usr/bin/env python +# -*- coding: iso-8859-1 -*- +# Written by Martin v. Löwis <loewis@informatik.hu-berlin.de> + +"""Generate binary message catalog from textual translation description. + +This program converts a textual Uniforum-style message catalog (.po file) into +a binary GNU catalog (.mo file). This is essentially the same function as the +GNU msgfmt program, however, it is a simpler implementation. + +Usage: msgfmt.py [OPTIONS] filename.po + +Options: + -o file + --output-file=file + Specify the output file to write to. If omitted, output will go to a + file named filename.mo (based off the input file name). + + -h + --help + Print this message and exit. + + -V + --version + Display version information and exit. +""" + +import sys +import os +import getopt +import struct +import array + +__version__ = "1.1" + +MESSAGES = {} + + + +def usage(code, msg=''): + print >> sys.stderr, __doc__ + if msg: + print >> sys.stderr, msg + sys.exit(code) + + + +def add(id, str, fuzzy): + "Add a non-fuzzy translation to the dictionary." + global MESSAGES + if not fuzzy and str: + MESSAGES[id] = str + + + +def generate(): + "Return the generated output." + global MESSAGES + keys = MESSAGES.keys() + # the keys are sorted in the .mo file + keys.sort() + offsets = [] + ids = strs = '' + for id in keys: + # For each string, we need size and file offset. Each string is NUL + # terminated; the NUL does not count into the size. + offsets.append((len(ids), len(id), len(strs), len(MESSAGES[id]))) + ids += id + '\0' + strs += MESSAGES[id] + '\0' + output = '' + # The header is 7 32-bit unsigned integers. We don't use hash tables, so + # the keys start right after the index tables. + # translated string. + keystart = 7*4+16*len(keys) + # and the values start after the keys + valuestart = keystart + len(ids) + koffsets = [] + voffsets = [] + # The string table first has the list of keys, then the list of values. + # Each entry has first the size of the string, then the file offset. + for o1, l1, o2, l2 in offsets: + koffsets += [l1, o1+keystart] + voffsets += [l2, o2+valuestart] + offsets = koffsets + voffsets + output = struct.pack("Iiiiiii", + 0x950412deL, # Magic + 0, # Version + len(keys), # # of entries + 7*4, # start of key index + 7*4+len(keys)*8, # start of value index + 0, 0) # size and offset of hash table + output += array.array("i", offsets).tostring() + output += ids + output += strs + return output + + + +def make(filename, outfile): + ID = 1 + STR = 2 + + # Compute .mo name from .po name and arguments + if filename.endswith('.po'): + infile = filename + else: + infile = filename + '.po' + if outfile is None: + outfile = os.path.splitext(infile)[0] + '.mo' + + try: + lines = open(infile).readlines() + except IOError, msg: + print >> sys.stderr, msg + sys.exit(1) + + section = None + fuzzy = 0 + + # Parse the catalog + lno = 0 + for l in lines: + lno += 1 + # If we get a comment line after a msgstr, this is a new entry + if l[0] == '#' and section == STR: + add(msgid, msgstr, fuzzy) + section = None + fuzzy = 0 + # Record a fuzzy mark + if l[:2] == '#,' and l.find('fuzzy'): + fuzzy = 1 + # Skip comments + if l[0] == '#': + continue + # Now we are in a msgid section, output previous section + if l.startswith('msgid'): + if section == STR: + add(msgid, msgstr, fuzzy) + section = ID + l = l[5:] + msgid = msgstr = '' + # Now we are in a msgstr section + elif l.startswith('msgstr'): + section = STR + l = l[6:] + # Skip empty lines + l = l.strip() + if not l: + continue + # XXX: Does this always follow Python escape semantics? + l = eval(l) + if section == ID: + msgid += l + elif section == STR: + msgstr += l + else: + print >> sys.stderr, 'Syntax error on %s:%d' % (infile, lno), \ + 'before:' + print >> sys.stderr, l + sys.exit(1) + # Add last entry + if section == STR: + add(msgid, msgstr, fuzzy) + + # Compute output + output = generate() + + try: + open(outfile,"wb").write(output) + except IOError,msg: + print >> sys.stderr, msg + + + +def main(): + try: + opts, args = getopt.getopt(sys.argv[1:], 'hVo:', + ['help', 'version', 'output-file=']) + except getopt.error, msg: + usage(1, msg) + + outfile = None + # parse options + for opt, arg in opts: + if opt in ('-h', '--help'): + usage(0) + elif opt in ('-V', '--version'): + print >> sys.stderr, "msgfmt.py", __version__ + sys.exit(0) + elif opt in ('-o', '--output-file'): + outfile = arg + # do it + if not args: + print >> sys.stderr, 'No input file given' + print >> sys.stderr, "Try `msgfmt --help' for more information." + return + + for filename in args: + make(filename, outfile) + + +if __name__ == '__main__': + main() diff --git a/trunk/src/tools/pygettext.py b/trunk/src/tools/pygettext.py new file mode 100644 index 00000000..040b5c7f --- /dev/null +++ b/trunk/src/tools/pygettext.py @@ -0,0 +1,762 @@ +#! /usr/bin/env python +# -*- coding: iso-8859-1 -*- +# Originally written by Barry Warsaw <barry@zope.com> +# +# Minimally patched to make it even more xgettext compatible +# by Peter Funk <pf@artcom-gmbh.de> +# +# 2002-11-22 Jürgen Hermann <jh@web.de> +# Added checks that _() only contains string literals, and +# command line args are resolved to module lists, i.e. you +# can now pass a filename, a module or package name, or a +# directory (including globbing chars, important for Win32). +# Made docstring fit in 80 chars wide displays using pydoc. +# + +import codecs + +# for selftesting +import re +try: + import fintl + _ = fintl.gettext +except ImportError: + _ = lambda s: s + +__doc__ = _("""pygettext -- Python equivalent of xgettext(1) + +Many systems (Solaris, Linux, Gnu) provide extensive tools that ease the +internationalization of C programs. Most of these tools are independent of +the programming language and can be used from within Python programs. +Martin von Loewis' work[1] helps considerably in this regard. + +There's one problem though; xgettext is the program that scans source code +looking for message strings, but it groks only C (or C++). Python +introduces a few wrinkles, such as dual quoting characters, triple quoted +strings, and raw strings. xgettext understands none of this. + +Enter pygettext, which uses Python's standard tokenize module to scan +Python source code, generating .pot files identical to what GNU xgettext[2] +generates for C and C++ code. From there, the standard GNU tools can be +used. + +A word about marking Python strings as candidates for translation. GNU +xgettext recognizes the following keywords: gettext, dgettext, dcgettext, +and gettext_noop. But those can be a lot of text to include all over your +code. C and C++ have a trick: they use the C preprocessor. Most +internationalized C source includes a #define for gettext() to _() so that +what has to be written in the source is much less. Thus these are both +translatable strings: + + gettext("Translatable String") + _("Translatable String") + +Python of course has no preprocessor so this doesn't work so well. Thus, +pygettext searches only for _() by default, but see the -k/--keyword flag +below for how to augment this. + + [1] http://www.python.org/workshops/1997-10/proceedings/loewis.html + [2] http://www.gnu.org/software/gettext/gettext.html + +NOTE: pygettext attempts to be option and feature compatible with GNU +xgettext where ever possible. However some options are still missing or are +not fully implemented. Also, xgettext's use of command line switches with +option arguments is broken, and in these cases, pygettext just defines +additional switches. + +Usage: pygettext [options] inputfile ... + +Options: + + -a + --extract-all + Extract all strings. + + -d name + --default-domain=name + Rename the default output file from messages.pot to name.pot. + + -E + --escape + Replace non-ASCII characters with octal escape sequences. + + -D + --docstrings + Extract module, class, method, and function docstrings. These do + not need to be wrapped in _() markers, and in fact cannot be for + Python to consider them docstrings. (See also the -X option). + + -h + --help + Print this help message and exit. + + -k word + --keyword=word + Keywords to look for in addition to the default set, which are: + %(DEFAULTKEYWORDS)s + + You can have multiple -k flags on the command line. + + -K + --no-default-keywords + Disable the default set of keywords (see above). Any keywords + explicitly added with the -k/--keyword option are still recognized. + + --no-location + Do not write filename/lineno location comments. + + -n + --add-location + Write filename/lineno location comments indicating where each + extracted string is found in the source. These lines appear before + each msgid. The style of comments is controlled by the -S/--style + option. This is the default. + + -o filename + --output=filename + Rename the default output file from messages.pot to filename. If + filename is `-' then the output is sent to standard out. + + -p dir + --output-dir=dir + Output files will be placed in directory dir. + + -S stylename + --style stylename + Specify which style to use for location comments. Two styles are + supported: + + Solaris # File: filename, line: line-number + GNU #: filename:line + + The style name is case insensitive. GNU style is the default. + + -v + --verbose + Print the names of the files being processed. + + -V + --version + Print the version of pygettext and exit. + + -w columns + --width=columns + Set width of output to columns. + + -x filename + --exclude-file=filename + Specify a file that contains a list of strings that are not be + extracted from the input files. Each string to be excluded must + appear on a line by itself in the file. + + -X filename + --no-docstrings=filename + Specify a file that contains a list of files (one per line) that + should not have their docstrings extracted. This is only useful in + conjunction with the -D option above. + +If `inputfile' is -, standard input is read. +""") + +import os +import imp +import sys +import glob +import time +import getopt +import token +import tokenize +import operator +import codecs + +from elementtree.ElementTree import ElementTree, XML + +__version__ = '1.5' + +default_keywords = ['_'] +DEFAULTKEYWORDS = ', '.join(default_keywords) + +EMPTYSTRING = '' + + + +# The normal pot-file header. msgmerge and Emacs's po-mode work better if it's +# there. +pot_header = _('''\ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR ORGANIZATION +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\\n" +"POT-Creation-Date: %(time)s\\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n" +"Language-Team: LANGUAGE <LL@li.org>\\n" +"MIME-Version: 1.0\\n" +"Content-Type: text/plain; charset=%(charset)s\\n" +"Content-Transfer-Encoding: %(charset)s\\n" +"Generated-By: pygettext.py %(version)s\\n" + +''') + + +def usage(code, msg=''): + print >> sys.stderr, __doc__ % globals() + if msg: + print >> sys.stderr, msg + sys.exit(code) + + + +escapes = [] + +def make_escapes(pass_iso8859): + global escapes + if pass_iso8859: + # Allow iso-8859 characters to pass through so that e.g. 'msgid + # "Höhe"' would result not result in 'msgid "H\366he"'. Otherwise we + # escape any character outside the 32..126 range. + mod = 128 + else: + mod = 256 + for i in range(256): + if 32 <= (i % mod) <= 126: + escapes.append(chr(i)) + else: + escapes.append("\\%03o" % i) + escapes[ord('\\')] = '\\\\' + escapes[ord('\t')] = '\\t' + escapes[ord('\r')] = '\\r' + escapes[ord('\n')] = '\\n' + escapes[ord('\"')] = '\\"' + + +def escape_ascii(s): + "Escapes all text outside of 7bit ASCII plus control characters and Python literals." + global escapes + s = list(s) + for i in range(len(s)): + s[i] = escapes[ord(s[i])] + return EMPTYSTRING.join(s) + +def escape_unicode(s): + "Escapes control characters and Python literals only leaving non-ascii text intact." + #for sp in ('\t', '\r', '\n', '\"', '\\'): + s = s.replace('\\', '\\\\') + s = s.replace('\t', '\\t') + s = s.replace('\r', '\\r') + s = s.replace('\n', '\\n') + s = s.replace('\"', '\\"') + # escape control chars + def repl(m): return "\\%03o" % ord(m.group(0)) + s = re.sub('[\001-\037]', repl, s) + return s + +def safe_eval(s): + # unwrap quotes, safely + return eval(s, {'__builtins__':{}}, {}) + + +def normalize(s, escape=False): + # This converts the various Python string types into a format that is + # appropriate for .po files, namely much closer to C style. + lines = s.split('\n') + if len(lines) == 1: + s = '"' + escape_unicode(s) + '"' + else: + if not lines[-1]: + del lines[-1] + lines[-1] = lines[-1] + '\n' + for i in range(len(lines)): + lines[i] = escape_unicode(lines[i]) + lineterm = '\\n"\n"' + s = '""\n"' + lineterm.join(lines) + '"' + if isinstance(s, unicode): + s = s.encode('utf-8') + if escape: + def repl(m): return "\\%03o" % ord(m.group(0)) + s = re.sub('[\200-\377]', repl, s) + return s + + +def containsAny(str, set): + """Check whether 'str' contains ANY of the chars in 'set'""" + return 1 in [c in str for c in set] + + +def _visit_pyfiles(list, dirname, names): + """Helper for getFilesForName().""" + # get extension for python source files + if not globals().has_key('_py_ext'): + global _py_ext + _py_ext = [triple[0] for triple in imp.get_suffixes() + if triple[2] == imp.PY_SOURCE][0] + + # don't recurse into CVS directories + if 'CVS' in names: + names.remove('CVS') + if '.svn' in names: + names.remove('.svn') + + # add all *.py files to list + list.extend( + [os.path.join(dirname, file) for file in names + if os.path.splitext(file)[1] == _py_ext] + ) + + +def _get_modpkg_path(dotted_name, pathlist=None): + """Get the filesystem path for a module or a package. + + Return the file system path to a file for a module, and to a directory for + a package. Return None if the name is not found, or is a builtin or + extension module. + """ + # split off top-most name + parts = dotted_name.split('.', 1) + + if len(parts) > 1: + # we have a dotted path, import top-level package + try: + file, pathname, description = imp.find_module(parts[0], pathlist) + if file: file.close() + except ImportError: + return None + + # check if it's indeed a package + if description[2] == imp.PKG_DIRECTORY: + # recursively handle the remaining name parts + pathname = _get_modpkg_path(parts[1], [pathname]) + else: + pathname = None + else: + # plain name + try: + file, pathname, description = imp.find_module( + dotted_name, pathlist) + if file: + file.close() + if description[2] not in [imp.PY_SOURCE, imp.PKG_DIRECTORY]: + pathname = None + except ImportError: + pathname = None + + return pathname + + +def getFilesForName(name): + """Get a list of module files for a filename, a module or package name, + or a directory. + """ + if not os.path.exists(name): + # check for glob chars + if containsAny(name, "*?[]"): + files = glob.glob(name) + list = [] + for file in files: + list.extend(getFilesForName(file)) + return list + + # try to find module or package + name = _get_modpkg_path(name) + if not name: + return [] + + if os.path.isdir(name): + # find all python files in directory + list = [] + os.path.walk(name, _visit_pyfiles, list) + return list + elif os.path.exists(name): + # a single file + return [name] + + return [] + + +class TokenEater: + def __init__(self, options): + self.__options = options + self.__messages = {} + self.__state = self.__waiting + self.__data = [] + self.__lineno = -1 + self.__freshmodule = 1 + self.__curfile = None + self.__encoding = None + + def __call__(self, ttype, tstring, stup, etup, line): + # dispatch +## import token +## print >> sys.stderr, 'ttype:', token.tok_name[ttype], \ +## 'tstring:', tstring + self.__state(ttype, tstring, stup[0]) + + def __waiting(self, ttype, tstring, lineno): + opts = self.__options + # Do docstring extractions, if enabled + if opts.docstrings and not opts.nodocstrings.get(self.__curfile): + # module docstring? + if self.__freshmodule: + if ttype == tokenize.STRING: + self.__addentry(safe_eval(tstring), lineno, isdocstring=1) + self.__freshmodule = 0 + elif ttype not in (tokenize.COMMENT, tokenize.NL): + self.__freshmodule = 0 + return + # class docstring? + if ttype == tokenize.NAME and tstring in ('class', 'def'): + self.__state = self.__suiteseen + return + if ttype == tokenize.NAME and tstring in opts.keywords: + self.__state = self.__keywordseen + + def __suiteseen(self, ttype, tstring, lineno): + # ignore anything until we see the colon + if ttype == tokenize.OP and tstring == ':': + self.__state = self.__suitedocstring + + def __suitedocstring(self, ttype, tstring, lineno): + # ignore any intervening noise + if ttype == tokenize.STRING: + self.__addentry(safe_eval(tstring), lineno, isdocstring=1) + self.__state = self.__waiting + elif ttype not in (tokenize.NEWLINE, tokenize.INDENT, + tokenize.COMMENT): + # there was no class docstring + self.__state = self.__waiting + + def __keywordseen(self, ttype, tstring, lineno): + if ttype == tokenize.OP and tstring == '(': + self.__data = [] + self.__lineno = lineno + self.__state = self.__openseen + else: + self.__state = self.__waiting + + def __openseen(self, ttype, tstring, lineno): + if ttype == tokenize.OP and tstring == ')': + # We've seen the last of the translatable strings. Record the + # line number of the first line of the strings and update the list + # of messages seen. Reset state for the next batch. If there + # were no strings inside _(), then just ignore this entry. + if self.__data: + self.__addentry(EMPTYSTRING.join(self.__data)) + self.__state = self.__waiting + elif ttype == tokenize.STRING: + self.__data.append(safe_eval(tstring)) + elif ttype not in [tokenize.COMMENT, token.INDENT, token.DEDENT, + token.NEWLINE, tokenize.NL]: + # warn if we see anything else than STRING or whitespace + print >> sys.stderr, _( + '*** %(file)s:%(lineno)s: Seen unexpected token "%(token)s"' + ) % { + 'token': tstring, + 'file': self.__curfile, + 'lineno': self.__lineno + } + self.__state = self.__waiting + + def __addentry(self, msg, lineno=None, isdocstring=0, iskidstring=0): + # tokenize module always return unicode strings + # even when they are in fact coded string instances + # to deal with this we use a hack: + # evaluate string's representation without leading "u" + # to force interpration as coded string + # then we decode it using already known file's encoding + if not iskidstring: + if type(msg) is str: + msg = eval(repr(msg)) + else: + msg = eval(repr(msg)[1:]) + msg = msg.decode(self.__encoding) + if lineno is None: + lineno = self.__lineno + if not msg in self.__options.toexclude: + entry = (self.__curfile, lineno) + self.__messages.setdefault(msg, {})[entry] = isdocstring + + def set_filename(self, filename): + self.__curfile = filename + self.__freshmodule = 1 + + def set_file_encoding(self, fp): + """Searches for -*- coding: -*- magic comment to find out file encoding.""" + self.__encoding = 'utf-8' # reset to default for each new file + for line in fp.readlines()[:5]: + m = re.match('#\s*-\*-\s+coding:\s+(\w+)\s+-\*-', line) + if m: + self.__encoding = m.group(1) + break + fp.seek(0) + + def contains_inline_python(self,msg): + if '${' in msg and not '$${' in msg: return True + return False + + def strip_namespace_uri(self,tag): + return tag.split('}')[-1] + + def get_text_node(self,node): + tag = re.sub('({[^}]+})?(\w+)', '\\2', node.tag) + + if node.text: + msg = node.text.strip() + if msg and not self.contains_inline_python(msg): + if tag not in ['script','style']: + self.__addentry(msg,self.strip_namespace_uri(node.tag), iskidstring=1) + + if node.getchildren(): + for child in node: self.get_text_node(child) + + if node.tail: + msg = node.tail.strip() + if msg and not self.contains_inline_python(msg): + self.__addentry(msg,self.strip_namespace_uri(node.tag), iskidstring=1) + + def extract_kid_strings(self): + if not self.__curfile: return + f = None + try: + file = open(self.__curfile) + f = ElementTree(XML( fixentities(file.read() ))) + except Exception, e: + print 'Skip %s: %s' % (self.__curfile, e) + return + + node = f.getroot() + self.get_text_node(node) + + def write(self, fp): + options = self.__options + # format without tz information + # because %Z is timezone's name, not offset + # and, say, on localized Windows XP this is non-ascii string + timestamp = time.strftime('%Y-%m-%d %H:%M') + # The time stamp in the header doesn't have the same format as that + # generated by xgettext... + t = {'time': timestamp, 'version': __version__, 'charset':'utf-8'} + print >> fp, pot_header % t + # Sort the entries. First sort each particular entry's keys, then + # sort all the entries by their first item. + reverse = {} + for k, v in self.__messages.items(): + keys = v.keys() + keys.sort() + reverse.setdefault(tuple(keys), []).append((k, v)) + rkeys = reverse.keys() + rkeys.sort() + for rkey in rkeys: + rentries = reverse[rkey] + rentries.sort() + for k, v in rentries: + isdocstring = 0 + # If the entry was gleaned out of a docstring, then add a + # comment stating so. This is to aid translators who may wish + # to skip translating some unimportant docstrings. + if reduce(operator.__add__, v.values()): + isdocstring = 1 + # k is the message string, v is a dictionary-set of (filename, + # lineno) tuples. We want to sort the entries in v first by + # file name and then by line number. + v = v.keys() + v.sort() + if not options.writelocations: + pass + # location comments are different b/w Solaris and GNU: + elif options.locationstyle == options.SOLARIS: + for filename, lineno in v: + d = {'filename': filename, 'lineno': lineno} + print >>fp, _( + '# File: %(filename)s, line: %(lineno)s') % d + elif options.locationstyle == options.GNU: + # fit as many locations on one line, as long as the + # resulting line length doesn't exceeds 'options.width' + locline = '#:' + for filename, lineno in v: + d = {'filename': filename, 'lineno': lineno} + s = _(' %(filename)s:%(lineno)s') % d + if len(locline) + len(s) <= options.width: + locline = locline + s + else: + print >> fp, locline + locline = "#:" + s + if len(locline) > 2: + print >> fp, locline + if isdocstring: + print >> fp, '#, docstring' + if k: # do not output empty msgid + print >> fp, 'msgid', normalize(k, options.escape) + print >> fp, 'msgstr ""\n' + + +def main(): + global default_keywords + try: + opts, args = getopt.getopt( + sys.argv[1:], + 'ad:UDEhk:Kno:p:S:Vvw:x:X:', + ['extract-all', 'default-domain=', 'escape', 'help', + 'keyword=', 'no-default-keywords', + 'add-location', 'no-location', 'output=', 'output-dir=', + 'style=', 'verbose', 'version', 'width=', 'exclude-file=', + 'docstrings', 'no-docstrings', 'support-unicode', + ]) + except getopt.error, msg: + usage(1, msg) + + # for holding option values + class Options: + # constants + GNU = 1 + SOLARIS = 2 + # defaults + extractall = 0 # FIXME: currently this option has no effect at all. + escape = 0 + keywords = [] + outpath = '' + outfile = 'messages.pot' + writelocations = 1 + locationstyle = GNU + verbose = 0 + width = 78 + excludefilename = '' + docstrings = 0 + nodocstrings = {} + + options = Options() + locations = {'gnu' : options.GNU, + 'solaris' : options.SOLARIS, + } + + # parse options + for opt, arg in opts: + if opt in ('-h', '--help'): + usage(0) + elif opt in ('-a', '--extract-all'): + options.extractall = 1 + elif opt in ('-d', '--default-domain'): + options.outfile = arg + '.pot' + elif opt in ('-E', '--escape'): + options.escape = 1 + elif opt in ('-D', '--docstrings'): + options.docstrings = 1 + elif opt in ('-k', '--keyword'): + options.keywords.append(arg) + elif opt in ('-K', '--no-default-keywords'): + default_keywords = [] + elif opt in ('-n', '--add-location'): + options.writelocations = 1 + elif opt in ('--no-location',): + options.writelocations = 0 + elif opt in ('-S', '--style'): + options.locationstyle = locations.get(arg.lower()) + if options.locationstyle is None: + usage(1, _('Invalid value for --style: %s') % arg) + elif opt in ('-o', '--output'): + options.outfile = arg + elif opt in ('-p', '--output-dir'): + options.outpath = arg + elif opt in ('-v', '--verbose'): + options.verbose = 1 + elif opt in ('-V', '--version'): + print _('pygettext.py (xgettext for Python) %s') % __version__ + sys.exit(0) + elif opt in ('-w', '--width'): + try: + options.width = int(arg) + except ValueError: + usage(1, _('--width argument must be an integer: %s') % arg) + elif opt in ('-x', '--exclude-file'): + options.excludefilename = arg + elif opt in ('-X', '--no-docstrings'): + fp = open(arg) + try: + while 1: + line = fp.readline() + if not line: + break + options.nodocstrings[line[:-1]] = 1 + finally: + fp.close() + + # calculate escapes + make_escapes(0) + + # calculate all keywords + options.keywords.extend(default_keywords) + + # initialize list of strings to exclude + if options.excludefilename: + try: + fp = open(options.excludefilename) + options.toexclude = fp.readlines() + fp.close() + except IOError: + print >> sys.stderr, _( + "Can't read --exclude-file: %s") % options.excludefilename + sys.exit(1) + else: + options.toexclude = [] + + # resolve args to module lists + expanded = [] + for arg in args: + if arg == '-': + expanded.append(arg) + else: + expanded.extend(getFilesForName(arg)) + args = expanded + + # slurp through all the files + eater = TokenEater(options) + for filename in args: + if filename == '-': + if options.verbose: + print _('Reading standard input') + fp = sys.stdin + closep = 0 + else: + if options.verbose: + print _('Working on %s') % filename + fp = open(filename) + eater.set_file_encoding(fp) + closep = 1 + try: + eater.set_filename(filename) + try: + tokenize.tokenize(fp.readline, eater) + except tokenize.TokenError, e: + print >> sys.stderr, '%s: %s, line %d, column %d' % ( + e[0], filename, e[1][0], e[1][1]) + finally: + if closep: + fp.close() + + if os.path.splitext(filename)[-1].lower() == '.kid': eater.extract_kid_strings() + + # write the output + if options.outfile == '-': + fp = sys.stdout + closep = 0 + else: + if options.outpath: + options.outfile = os.path.join(options.outpath, options.outfile) + fp = open(options.outfile, 'wt') + closep = 1 + try: + eater.write(fp) + finally: + if closep: + fp.close() + + +if __name__ == '__main__': + main() + # some more test strings + _(u'a unicode string') + # this one creates a warning + _('*** Seen unexpected token "%(token)s"') % {'token': 'test'} + _('more' 'than' 'one' 'string') diff --git a/trunk/www/alm-process/snippets/page.xml b/trunk/www/alm-process/snippets/page.xml new file mode 100644 index 00000000..0bfbb429 --- /dev/null +++ b/trunk/www/alm-process/snippets/page.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<page visibility="1">
+ <component_list>
+ <component visibility="1" type="ProjectMetadata" order="1">
+ <title localize="false"></title>
+ </component>
+ <component visibility="1" type="Html" order="2">
+ <title localize="false"></title>
+ <filename>index.html</filename>
+ </component>
+ <component visibility="1" type="Subproject" order="3">
+ <title localize="false">Subprojects</title>
+ </component>
+ </component_list>
+</page>
+
diff --git a/trunk/www/index.html b/trunk/www/index.html new file mode 100644 index 00000000..598d71a2 --- /dev/null +++ b/trunk/www/index.html @@ -0,0 +1,38 @@ +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <h3>sos (son of sysreport)</h3> <h1>Sos Project Home</h1> The SOS project provides tools for gathering information and running tools. <ul> <li>Improve on the capabilities of sysreport by adding some analytic abilities</li> <li>Allow applications to deliver modules that integrate with sos to gather support information</li> <li>To put more tools into administrator's hands to help them solve their problems</li> <li>To provide a set of tools to improve the abilities of support organizations to solve customer problems</li> </ul> <h3>Sos will be delivered with Red Hat Enterprise Linux 5.</h3>
+<p>
+If you find bugs in that version, please report them against the sos component of version rhel5 at <a target="_blank" title="bugzilla.redhat.com" href="https://bugzilla.redhat.com/bugzilla/enter_bug.cgi?product=Red%20Hat%20Enterprise%20Linux&version=5&component=sos">bugzilla.redhat.com</a>.
+</p>
+
+<p>
+Although this project was begun by people in Red Hat's support organization, it is released under the GPL , with all Red Hat specific parts isolated in plugins so that it will be generally useful to others.
+</p>
+
+<p>
+Contributors to this project are welcome, especially if you are interested in writing plugins or in helping make this tool useful across many linux distributions. All development and testing to date has occurred on Fedora and RHEL5 , and contributors working with other distributions are welcome. Writing plugins in easy - see the example_plugins directory in the source tree.
+</p>
+
+<p>
+The first stage of the project, sosreport, has a first implementation, and is available now. If you maintain linux software, please consider delivering a plugin for sos that will gather the information needed to help troubleshoot or debug your application.
+</p>
+ This project will provide a number of tools, in the following areas:<br />
+ <h2>1. System Information Gathering (released)</h2>
+<p>
+This application (sosreport) performs a function similar to what sysreport does now, but has a pluggable architecture that will allow the inclusion of sos plugins to be delivered with any package. These plugins can gather information needed for troubleshooting that package, provide the ability to turn on other debugging or reporting that is of interest for that package, provide some analysis to be done to notify the customer of improper configuration or operation, and integrate the reporting from the plugin with other system information.
+</p>
+
+<p>
+Reports are provided in html. The report integrates reports from various plugins, along with the results of analysis performed by those plugins. In addition, future policy plugins may provide links in the output for additional operations, such as opening a ticket with a Support CRM system, submitting the system information to a web hosted tool for further analysis, or submitting the information for attachment to a ticket already open within a support CRM system.
+</p>
+<h2>2. Integration of additional debugging tools (future)</h2>
+<p>
+An API will be provided that allows delivery of additional tools which may be enabled and disabled through a simple unified interface by the end user. For example, we expect to provide scripts that will use sar, systemtap, enable performance counters, etc. For all tools that are enabled, reports will be included in subsequent system information reports and integrated in the heirarchical result navigation.
+</p>
+
+<p>
+This will allow Support Organizations to easily walk the customer (by phone or email or chat) through a simple interface to enable the gathering of troubleshooting information specific to the problem being addressed.
+</p>
+ <h2>3. Integration with Support CRM and other ticketing systems</h2>
+<p>
+The tools in the package will allow simplified interaction with Support CRM systems. Once system information is gathered, the customer will have an option to open an issue with support. Verification of entitlement for support and filing the ticket with the system information and problem description attached will be handled by policy plugins that can be customized for integration with different CRM systems.
+</p>
+ <hr /> <em>sos is the international distress call, and also stands for "son of sysreport"</em> <hr />
\ No newline at end of file diff --git a/trunk/www/silho_neutral_1.gif b/trunk/www/silho_neutral_1.gif Binary files differnew file mode 100644 index 00000000..47c1524b --- /dev/null +++ b/trunk/www/silho_neutral_1.gif |